Kaynağa Gözat

:bookmark: Version 11.13.5

Controls Package
:bug: MFXTextField: fixed TextFormatter not working. It must be added on the BoundTextField, for this reason, added a delegate property (Fix for #174)

CSS Package
:recycle: MFXCSSBridge: do not take into account the Region's user agent stylesheet. If it's needed to specify the user agent the popup's getUserAgentStylesheet() method should be overridden inline, as well as for any other component that mey require it (see MFXComboBoxSkin as an example) (Fix for #173)

Selection Package
:recycle: Adds a method to retrieve the selection values as a List (rather than getSelection().values() which returns a generic Collection). Also make MultipleSelectionManager use LinkedHashSet and LinkedHashMap (TreeMap was used, oversight sorry) to keep insertion order for selection, this also ensures that building the values List returns the selected values in the same exact order as in the selection Map (Enhancement for #161)

Skins Package
:bug: MFXComboBoxSkin, MFXFilterComboBoxSkin: fixed an issue that prevented the combo box's popup from being fully customizable with CSS (Fix for #173)
:bug: MFXDatePickerSkin: initialize the picker's text if the initial date is not null (Fix for #172)

Signed-off-by: palexdev <alessandro.parisi406@gmail.com>
palexdev 3 yıl önce
ebeveyn
işleme
dee6071d70

+ 50 - 18
CHANGELOG.md

@@ -1,8 +1,9 @@
 # Changelog
+
 All notable changes to this project will be documented in this file.
 
-The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
-and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).  
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
+to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).  
 (Date format is dd-MM-yyyy)
 
 ## Type of Changes
@@ -15,6 +16,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 [//]: ##[Unreleased]
 
+## [11.13.5] - 11-04-2022
+
+## Changed
+
+- MFXCSSBridge: do not take into account the Region's user agent stylesheet. If it's needed to specify the user agent
+  the popup's getUserAgentStylesheet() method should be overridden inline, as well as for any other component that mey
+  require it (see MFXComboBoxSkin as an example) (Fix for #173)
+- Adds a method to retrieve the selection values as a List (rather than getSelection().values() which returns a generic
+  Collection). Also make MultipleSelectionManager use LinkedHashSet and LinkedHashMap (TreeMap was used, oversight
+  sorry) to keep insertion order for selection, this also ensures that building the values List returns the selected
+  values in the same exact order as in the selection Map (Enhancement for #161)
+
+## Fixed
+
+- MFXTextField: fixed TextFormatter not working. It must be added on the BoundTextField, for this reason, added a
+  delegate property (Fix for #174)
+- MFXComboBoxSkin, MFXFilterComboBoxSkin: fixed an issue that prevented the combo box's popup from being fully
+  customizable with CSS (Fix for #173)
+- MFXDatePickerSkin: initialize the picker's text if the initial date is not null (Fix for #172)
+
 ## [11.13.4] - 31-03-2022
 
 ### Fixed
@@ -35,6 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Improve ROADMAP
 
 ## [11.13.2] - 09-02-2022
+
 ### Added
 
 - New control MFXMagnifierPane
@@ -46,12 +68,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Added new control, MFXSpinner
 
 ### Changed
+
 - ColorUtils: changed some method to be null-safe
 - MFXFilterPaneSkin: properly compute the minimum width
 - MFXTableViewSkin: allow to drag the filter dialog
 - MFXIconWrapper: added handler to acquire focus
 
 ### Fixed
+
 - MFXComboBoxSkin: ensure the caret position is at 0 if the combo box is not selectable
 - MFXTableViewSkin: ensure the dialog is on foreground
 - MFXTextField and all subclasses: fixed an issue with CSS and :focused PseudoClass. It was being ignored in some cases,
@@ -68,6 +92,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - ListChangeProcessor: fix findShift() method by including the given index in the count too
 
 ## [11.13.0] - 22-01-2022
+
 _This version won't follow the above scheme as the amount of changes and commits is simply too huge and there would be
 no way to correctly show all the changes without making mistakes (duplicates, "overlapping" changes...), for this reason
 I'll try to sum up only the major changes below._
@@ -94,22 +119,29 @@ I'll try to sum up only the major changes below._
 - The Filter API has been reviewed and now it's more powerful than ever with the new MFXFilterPane.
 - The Validation API has been reviewed as well, it is as powerful as before thanks to JavaFX properties and observables,
   but it's much more flexible. It's up to the user now to decide when and how to validate a control. Also an important
-  design choice has been made here. Many validation frameworks for JavaFX also offer a way to decorate a control,
-but I decided to not to that as it would violate the Single Responsibility Principle! Validation has nothing to do with UI, plus depending on the fanciness of your App it's up to
-you to decide how the validation controls will look like!
-- Dialogs and Notifications have been reviewed as well. The dialogs have been simplified, and for notifications there are now two separate systems.
-- The date picker is now on a whole new level, it's been remade from scratch and it's simply beautiful, powerful and versatile.
-- There are also new components! MFXPopup is a PopupControl that actually works. Ever tried to style a PopupControl but no matter what the CSS would not work?
-Do not worry about that never again, just use MFXPopup it's super easy thanks to my custom MFXCSSBridge (check documentation would be too much to write here haha).
-MFXPagination has been made for MFXPaginatedTableViews, remade from scratch (meaning that doesn't extend Pagination) with a stunning modern look.
-MFXTooltip, an alternative to JavaFX's tooltip, much more versatile!
-- MFXRippleGenerator has been deprecated. The ripple generation is organized to be a new API, meaning that there are now interfaces and a base abstract class from which
-you can implement new ripple generators. The new default implementation is MFXCircleRippleGenerator. The new API also allows you to create new Ripples by implementing the IRipple interface.
-It's a rather advanced API tbh, but hey, it's there, who knows maybe someday I'll need it to be like this.
-- MFXHLoader and MFXVLoader are no more. The loading API has been "extracted" to be independent from UI. MFXLoader has the same capabilities as the aforementioned controls
-but it's not a Node. It's up to the user to decide how to manage the loaded views, and how to translate the loaded beans to a Node. (see the documentation and the DemoController for an example
-on how to easily create a nav-bar even with the new API)
-- The Selection API has been reviewed as well. It also supports the "extend selection" behavior when "Shift" is pressed, like you would expect from a file manager.
+  design choice has been made here. Many validation frameworks for JavaFX also offer a way to decorate a control, but I
+  decided to not to that as it would violate the Single Responsibility Principle! Validation has nothing to do with UI,
+  plus depending on the fanciness of your App it's up to you to decide how the validation controls will look like!
+- Dialogs and Notifications have been reviewed as well. The dialogs have been simplified, and for notifications there
+  are now two separate systems.
+- The date picker is now on a whole new level, it's been remade from scratch and it's simply beautiful, powerful and
+  versatile.
+- There are also new components! MFXPopup is a PopupControl that actually works. Ever tried to style a PopupControl but
+  no matter what the CSS would not work? Do not worry about that never again, just use MFXPopup it's super easy thanks
+  to my custom MFXCSSBridge (check documentation would be too much to write here haha). MFXPagination has been made for
+  MFXPaginatedTableViews, remade from scratch (meaning that doesn't extend Pagination) with a stunning modern look.
+  MFXTooltip, an alternative to JavaFX's tooltip, much more versatile!
+- MFXRippleGenerator has been deprecated. The ripple generation is organized to be a new API, meaning that there are now
+  interfaces and a base abstract class from which you can implement new ripple generators. The new default
+  implementation is MFXCircleRippleGenerator. The new API also allows you to create new Ripples by implementing the
+  IRipple interface. It's a rather advanced API tbh, but hey, it's there, who knows maybe someday I'll need it to be
+  like this.
+- MFXHLoader and MFXVLoader are no more. The loading API has been "extracted" to be independent from UI. MFXLoader has
+  the same capabilities as the aforementioned controls but it's not a Node. It's up to the user to decide how to manage
+  the loaded views, and how to translate the loaded beans to a Node. (see the documentation and the DemoController for
+  an example on how to easily create a nav-bar even with the new API)
+- The Selection API has been reviewed as well. It also supports the "extend selection" behavior when "Shift" is pressed,
+  like you would expect from a file manager.
 - There is an insane amount of new utilities, for JavaFX as well as for Java
 
 Again, let me **apologize** for this messy changelist, but I promise from next version changes will be tracked properly!

+ 2 - 2
README.md

@@ -224,7 +224,7 @@ repositories {
 }
 
 dependencies {
-  implementation 'io.github.palexdev:materialfx:11.13.4'
+  implementation 'io.github.palexdev:materialfx:11.13.5'
 }
 ```
 
@@ -235,7 +235,7 @@ dependencies {
 <dependency>
   <groupId>io.github.palexdev</groupId>
   <artifactId>materialfx</artifactId>
-  <version>11.13.4</version>
+  <version>11.13.5</version>
 </dependency>
 ```
 

+ 1 - 1
build.gradle

@@ -4,7 +4,7 @@ plugins {
 }
 
 group 'io.github.palexdev'
-version '11.13.4'
+version '11.13.5'
 
 repositories {
     mavenCentral()

+ 21 - 11
demo/src/test/java/Playground.java

@@ -1,31 +1,41 @@
-import io.github.palexdev.materialfx.controls.MFXButton;
 import io.github.palexdev.materialfx.controls.MFXTextField;
 import javafx.application.Application;
 import javafx.geometry.Pos;
 import javafx.scene.Scene;
+import javafx.scene.control.TextFormatter;
 import javafx.scene.layout.VBox;
 import javafx.stage.Stage;
 import org.scenicview.ScenicView;
 
+import java.text.DecimalFormat;
+import java.text.ParsePosition;
+
 public class Playground extends Application {
-	private final double w = 445;
-	private final double h = 270;
 
 	@Override
 	public void start(Stage primaryStage) {
 		VBox vBox = new VBox(10);
 		vBox.setAlignment(Pos.CENTER);
 
-		MFXTextField textField = new MFXTextField("15.0", "", "Pixels");
+		DecimalFormat format = new DecimalFormat("#.0");
+		MFXTextField field = new MFXTextField("", "", "Numbers");
+		field.delegateSetTextFormatter(new TextFormatter<>(c ->
+		{
+			if (c.getControlNewText().isEmpty()) {
+				return c;
+			}
+
+			ParsePosition parsePosition = new ParsePosition(0);
+			Object object = format.parse(c.getControlNewText(), parsePosition);
 
-		MFXButton button = new MFXButton("Change Measure Unit");
-		button.setOnAction(event -> {
-			String measureUnit = textField.getMeasureUnit();
-			measureUnit = (measureUnit == null || measureUnit.isEmpty()) ? "px" : "cm";
-			textField.setMeasureUnit(measureUnit);
-		});
+			if (object == null || parsePosition.getIndex() < c.getControlNewText().length()) {
+				return null;
+			} else {
+				return c;
+			}
+		}));
 
-		vBox.getChildren().addAll(button, textField);
+		vBox.getChildren().addAll(field);
 		Scene scene = new Scene(vBox, 800, 800);
 		primaryStage.setScene(scene);
 		primaryStage.show();

+ 73 - 0
demo/src/test/java/selection/MultipleSelectionModelTests.java

@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package selection;
+
+import io.github.palexdev.materialfx.selection.MultipleSelectionModel;
+import io.github.palexdev.materialfx.utils.FXCollectors;
+import javafx.collections.ObservableList;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.Set;
+import java.util.stream.IntStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class MultipleSelectionModelTests {
+	private final ObservableList<String> strings = IntStream.rangeClosed(0, 30)
+			.mapToObj(i -> "String " + i)
+			.collect(FXCollectors.toList());
+	private final MultipleSelectionModel<String> selectionModel = new MultipleSelectionModel<String>(strings);
+
+	@BeforeEach
+	public void setUp() {
+		selectionModel.clearSelection();
+	}
+
+	@Test
+	public void testOrder() {
+		Integer[] toSelect = {0, 6, 3, 9, 6};
+		selectionModel.selectIndexes(List.of(toSelect));
+
+		assertEquals(4, selectionModel.getSelection().size());
+
+		// Indexes
+		int i = 0;
+		Set<Integer> indexes = selectionModel.getSelection().keySet();
+		for (int val : indexes) {
+			assertEquals(toSelect[i], val);
+			i++;
+		}
+
+		// Values
+		i = 0;
+		String[] expected = {
+				"String 0",
+				"String 6",
+				"String 3",
+				"String 9"
+		};
+		List<String> values = selectionModel.getSelectedValues();
+		for (String value : values) {
+			assertEquals(expected[i], value);
+			i++;
+		}
+	}
+}

+ 72 - 0
demo/src/test/resources/CustomCombo.css

@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX.  If not, see <http://www.gnu.org/licenses/>.
+ */
+.mfx-filter-combo-box {
+	-fx-background-color: derive(#323232, 20%);
+	-fx-border-color: transparent;
+	-mfx-caret-visible: false;
+	-mfx-float-mode: disabled;
+	-fx-text-fill: white;
+}
+
+.mfx-filter-combo-box .caret .mfx-ripple-generator {
+	-mfx-ripple-color: rgba(255, 255, 255, 0.2);
+}
+
+.mfx-filter-combo-box .caret .mfx-font-icon {
+	-mfx-color: white
+}
+
+.mfx-filter-combo-box .combo-popup .search-container {
+	-fx-background-color: derive(#323232, 20%);
+	-fx-background-radius: 5;
+	-fx-border-color: transparent;
+	-fx-padding: 10 5 5 5;
+}
+
+.mfx-filter-combo-box .combo-popup .search-field {
+	-fx-background-color: derive(#323232, 20%);
+	-fx-text-fill: white;
+}
+
+.mfx-filter-combo-box .combo-popup .search-field .text-field {
+	-fx-prompt-text-fill: #ebebeb;
+}
+
+.mfx-filter-combo-box .combo-popup .virtual-flow {
+	-fx-background-color: transparent;
+}
+
+.mfx-filter-combo-box .combo-popup .virtual-flow .mfx-combo-box-cell .data-label {
+	-fx-text-fill: white;
+}
+
+.mfx-filter-combo-box .combo-popup .virtual-flow .mfx-combo-box-cell:hover {
+	-fx-background-color: white;
+}
+
+.mfx-filter-combo-box .combo-popup .virtual-flow .mfx-combo-box-cell:hover .data-label {
+	-fx-text-fill: rgba(0, 0, 0, 0.87);
+}
+
+.mfx-filter-combo-box .combo-popup .virtual-flow .mfx-combo-box-cell:selected {
+	-fx-background-color: white;
+}
+
+.mfx-filter-combo-box .combo-popup .virtual-flow .mfx-combo-box-cell:selected .data-label {
+	-fx-text-fill: rgb(0, 0, 0, 0.87);
+}

+ 1 - 1
materialfx/gradle.properties

@@ -1,6 +1,6 @@
 GROUP=io.github.palexdev
 POM_ARTIFACT_ID=materialfx
-VERSION_NAME=11.13.4
+VERSION_NAME=11.13.5
 
 POM_NAME=materialfx
 POM_DESCRIPTION=Material Desgin components for JavaFX

+ 16 - 0
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXTextField.java

@@ -41,6 +41,7 @@ import javafx.scene.Node;
 import javafx.scene.control.IndexRange;
 import javafx.scene.control.Skin;
 import javafx.scene.control.TextField;
+import javafx.scene.control.TextFormatter;
 import javafx.scene.input.ContextMenuEvent;
 import javafx.scene.paint.Color;
 
@@ -422,6 +423,21 @@ public class MFXTextField extends TextField implements Validated, MFXMenuControl
 		boundField.replaceSelection(replacement);
 	}
 
+	public TextFormatter<?> delegateGetTextFormatter() {
+		return boundField.getTextFormatter();
+	}
+
+	/**
+	 * Specifies the {@link BoundTextField} text formatter.
+	 */
+	public ObjectProperty<TextFormatter<?>> delegateTextFormatterProperty() {
+		return boundField.textFormatterProperty();
+	}
+
+	public void delegateSetTextFormatter(TextFormatter<?> textFormatter) {
+		boundField.setTextFormatter(textFormatter);
+	}
+
 	public int delegateGetAnchor() {
 		return boundField.getAnchor();
 	}

+ 0 - 7
materialfx/src/main/java/io/github/palexdev/materialfx/css/MFXCSSBridge.java

@@ -22,7 +22,6 @@ import javafx.beans.InvalidationListener;
 import javafx.collections.FXCollections;
 import javafx.collections.ObservableList;
 import javafx.scene.Parent;
-import javafx.scene.layout.Region;
 
 /**
  * Helper class which is responsible for parsing the stylesheets for a given {@link Parent}.
@@ -64,12 +63,6 @@ public class MFXCSSBridge {
 		stylesheets.clear();
 		if (parent == null) return;
 
-		if (parent instanceof Region) {
-			Region region = (Region) parent;
-			if (region.getUserAgentStylesheet() != null && !region.getUserAgentStylesheet().isEmpty()) {
-				stylesheets.add(region.getUserAgentStylesheet());
-			}
-		}
 		stylesheets.addAll(parent.getStylesheets());
 	}
 

+ 17 - 6
materialfx/src/main/java/io/github/palexdev/materialfx/selection/MultipleSelectionManager.java

@@ -145,10 +145,12 @@ public class MultipleSelectionManager<T> {
 		if (indexes.isEmpty()) return;
 
 		if (allowsMultipleSelection) {
-			Set<Integer> indexesSet = new HashSet<>(indexes);
+			Set<Integer> indexesSet = new LinkedHashSet<>(indexes);
 			Map<Integer, T> newSelection = indexesSet.stream().collect(Collectors.toMap(
 					i -> i,
-					i -> selectionModel.getItems().get(i)
+					i -> selectionModel.getItems().get(i),
+					(t, t2) -> t2,
+					LinkedHashMap::new
 			));
 			selection.putAll(newSelection);
 		} else {
@@ -254,17 +256,17 @@ public class MultipleSelectionManager<T> {
 	}
 
 	/**
-	 * Builds a new observable hash map backed by a {@link TreeMap}.
+	 * Builds a new observable hash map backed by a {@link LinkedHashMap}.
 	 */
 	protected ObservableMap<Integer, T> getMap() {
-		return FXCollections.observableMap(new TreeMap<>());
+		return FXCollections.observableMap(new LinkedHashMap<>());
 	}
 
 	/**
-	 * Builds a new observable hash map backed by a {@link TreeMap}, initialized with the given map.
+	 * Builds a new observable hash map backed by a {@link LinkedHashMap}, initialized with the given map.
 	 */
 	protected ObservableMap<Integer, T> getMap(Map<Integer, T> map) {
-		return FXCollections.observableMap(new TreeMap<>(map));
+		return FXCollections.observableMap(new LinkedHashMap<>(map));
 	}
 
 
@@ -297,6 +299,15 @@ public class MultipleSelectionManager<T> {
 		this.selection.set(selection);
 	}
 
+	/**
+	 * Returns an unmodifiable {@link List} containing all the selected values extracted from
+	 * {@link Map#values()}.
+	 * The values order is kept since the selection is backed by a {@link LinkedHashMap}.
+	 */
+	public List<T> getSelectedValues() {
+		return List.copyOf(selection.values());
+	}
+
 	/**
 	 * Specifies if this model allows multiple selection or should act like
 	 * a SingleSelectionModel.

+ 8 - 0
materialfx/src/main/java/io/github/palexdev/materialfx/selection/MultipleSelectionModel.java

@@ -171,6 +171,14 @@ public class MultipleSelectionModel<T> extends AbstractMultipleSelectionModel<T>
 		selectionManager.setSelection(newSelection);
 	}
 
+	/**
+	 * Delegate method for {@link MultipleSelectionManager#getSelectedValues()}.
+	 */
+	@Override
+	public List<T> getSelectedValues() {
+		return selectionManager.getSelectedValues();
+	}
+
 	/**
 	 * Delegate method for {@link MultipleSelectionManager#allowsMultipleSelection()}.
 	 */

+ 9 - 0
materialfx/src/main/java/io/github/palexdev/materialfx/selection/base/IMultipleSelectionModel.java

@@ -21,7 +21,9 @@ package io.github.palexdev.materialfx.selection.base;
 import javafx.beans.property.MapProperty;
 import javafx.collections.ObservableMap;
 
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Public API that every MultipleSelectionModel must implement.
@@ -108,6 +110,13 @@ public interface IMultipleSelectionModel<T> {
 	 */
 	void setSelection(ObservableMap<Integer, T> newSelection);
 
+	/**
+	 * Returns an unmodifiable {@link List} containing all the selected values extracted from
+	 * {@link Map#values()}.
+	 * The values order is kept since the selection is backed by a {@link LinkedHashMap}.
+	 */
+	List<T> getSelectedValues();
+
 	/**
 	 * Specifies if this model allows multiple selection or should act like
 	 * a SingleSelectionModel.

+ 30 - 16
materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXComboBoxSkin.java

@@ -58,17 +58,7 @@ public class MFXComboBoxSkin<T> extends MFXTextFieldSkin {
 	public MFXComboBoxSkin(MFXComboBox<T> comboBox, BoundTextField boundField) {
 		super(comboBox, boundField);
 
-		popup = new MFXPopup();
-		popup.getStyleClass().add("combo-popup");
-		popup.setPopupStyleableParent(comboBox);
-		popup.setAutoHide(true);
-		popup.setConsumeAutoHidingEvents(true);
-		popupManager = event -> {
-			if (comboBox.getItems().isEmpty()) return;
-			comboBox.show();
-		};
-
-		initialize();
+		popup = createPopup();
 		setBehavior();
 
 		T selectedItem = comboBox.getSelectedItem();
@@ -80,10 +70,6 @@ public class MFXComboBoxSkin<T> extends MFXTextFieldSkin {
 	//================================================================================
 	// Methods
 	//================================================================================
-	protected void initialize() {
-		popup.setContent(createPopupContent());
-	}
-
 	protected void setBehavior() {
 		comboBehavior();
 		selectionBehavior();
@@ -231,6 +217,29 @@ public class MFXComboBoxSkin<T> extends MFXTextFieldSkin {
 		comboBox.getAnimationProvider().apply(icon, showing).play();
 	}
 
+	/**
+	 * Responsible for creating the combo box's popup.
+	 */
+	protected MFXPopup createPopup() {
+		MFXComboBox<T> comboBox = getComboBox();
+		MFXPopup popup = new MFXPopup() {
+			@Override
+			public String getUserAgentStylesheet() {
+				return comboBox.getUserAgentStylesheet();
+			}
+		};
+		popup.getStyleClass().add("combo-popup");
+		popup.setPopupStyleableParent(comboBox);
+		popup.setAutoHide(true);
+		popup.setConsumeAutoHidingEvents(true);
+		popupManager = event -> {
+			if (comboBox.getItems().isEmpty()) return;
+			comboBox.show();
+		};
+		popup.setContent(createPopupContent());
+		return popup;
+	}
+
 	/**
 	 * Responsible for creating the popup's content.
 	 */
@@ -241,7 +250,12 @@ public class MFXComboBoxSkin<T> extends MFXTextFieldSkin {
 					comboBox.itemsProperty(),
 					comboBox.getCellFactory(),
 					Orientation.VERTICAL
-			);
+			) {
+				@Override
+				public String getUserAgentStylesheet() {
+					return comboBox.getUserAgentStylesheet();
+				}
+			};
 			virtualFlow.cellFactoryProperty().bind(comboBox.cellFactoryProperty());
 			virtualFlow.prefWidthProperty().bind(comboBox.widthProperty());
 		}

+ 32 - 3
materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXDatePickerSkin.java

@@ -22,6 +22,7 @@ import io.github.palexdev.materialfx.beans.NumberRange;
 import io.github.palexdev.materialfx.controls.*;
 import io.github.palexdev.materialfx.controls.cell.MFXDateCell;
 import io.github.palexdev.materialfx.utils.DateTimeUtils;
+import io.github.palexdev.materialfx.utils.ExecutionUtils;
 import io.github.palexdev.materialfx.utils.NodeUtils;
 import javafx.beans.binding.Bindings;
 import javafx.collections.FXCollections;
@@ -99,7 +100,12 @@ public class MFXDatePickerSkin extends MFXTextFieldSkin {
 	public MFXDatePickerSkin(MFXDatePicker datePicker, BoundTextField boundField) {
 		super(datePicker, boundField);
 
-		popup = new MFXPopup();
+		popup = new MFXPopup() {
+			@Override
+			public String getUserAgentStylesheet() {
+				return datePicker.getUserAgentStylesheet();
+			}
+		};
 		popup.getStyleClass().add("date-picker-popup");
 		popup.setPopupStyleableParent(datePicker);
 		popup.setAutoHide(true);
@@ -128,7 +134,20 @@ public class MFXDatePickerSkin extends MFXTextFieldSkin {
 	// Methods
 	//================================================================================
 	protected void initialize() {
+		MFXDatePicker datePicker = getDatePicker();
 		popup.setContent(createPopupContent());
+
+		LocalDate date = datePicker.getValue();
+		if (date != null) {
+			updateValue(date);
+			ExecutionUtils.executeWhen(
+					datePicker.delegateSelectedTextProperty(),
+					(oldValue, newValue) -> datePicker.positionCaret(newValue.length()),
+					false,
+					(oldValue, newValue) -> !newValue.isEmpty(),
+					true
+			);
+		}
 	}
 
 	protected void setBehavior() {
@@ -275,7 +294,12 @@ public class MFXDatePickerSkin extends MFXTextFieldSkin {
 	protected Node createPopupContent() {
 		MFXDatePicker datePicker = getDatePicker();
 
-		MFXComboBox<Month> monthCombo = new MFXComboBox<>(FXCollections.observableArrayList(Month.values()));
+		MFXComboBox<Month> monthCombo = new MFXComboBox<>(FXCollections.observableArrayList(Month.values())) {
+			@Override
+			public String getUserAgentStylesheet() {
+				return datePicker.getUserAgentStylesheet();
+			}
+		};
 		monthCombo.getStyleClass().add("months-combo");
 		monthCombo.converterProperty().bind(Bindings.createObjectBinding(
 				() -> datePicker.getMonthConverterSupplier().get(),
@@ -288,7 +312,12 @@ public class MFXDatePickerSkin extends MFXTextFieldSkin {
 			updateGrid();
 		});
 
-		MFXComboBox<Integer> yearCombo = new MFXComboBox<>(years);
+		MFXComboBox<Integer> yearCombo = new MFXComboBox<>(years) {
+			@Override
+			public String getUserAgentStylesheet() {
+				return datePicker.getUserAgentStylesheet();
+			}
+		};
 		yearCombo.getStyleClass().add("years-combo");
 		yearCombo.selectItem(currentYearMonth.getYear());
 		yearCombo.valueProperty().addListener((observable, oldValue, newValue) -> {

+ 6 - 7
materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXFilterComboBoxSkin.java

@@ -50,7 +50,6 @@ public class MFXFilterComboBoxSkin<T> extends MFXComboBoxSkin<T> {
 	//================================================================================
 	public MFXFilterComboBoxSkin(MFXFilterComboBox<T> comboBox, BoundTextField boundField) {
 		super(comboBox, boundField);
-		popup.setContent(createPopupContent());
 		addListeners();
 	}
 
@@ -87,9 +86,6 @@ public class MFXFilterComboBoxSkin<T> extends MFXComboBoxSkin<T> {
 	//================================================================================
 	// Overridden Methods
 	//================================================================================
-	@Override
-	protected void initialize() {
-	}
 
 	/**
 	 * {@inheritDoc}
@@ -108,7 +104,12 @@ public class MFXFilterComboBoxSkin<T> extends MFXComboBoxSkin<T> {
 		MFXFilterComboBox<T> comboBox = getComboBox();
 		TransformableList<T> filterList = comboBox.getFilterList();
 
-		MFXTextField searchField = new MFXTextField("", I18N.getOrDefault("filterCombo.search"));
+		MFXTextField searchField = new MFXTextField("", I18N.getOrDefault("filterCombo.search")) {
+			@Override
+			public String getUserAgentStylesheet() {
+				return comboBox.getUserAgentStylesheet();
+			}
+		};
 		searchField.getStyleClass().add("search-field");
 		searchField.textProperty().bindBidirectional(comboBox.searchTextProperty());
 		searchField.setMaxWidth(Double.MAX_VALUE);
@@ -142,6 +143,4 @@ public class MFXFilterComboBoxSkin<T> extends MFXComboBoxSkin<T> {
 	public MFXFilterComboBox<T> getComboBox() {
 		return (MFXFilterComboBox<T>) getSkinnable();
 	}
-
-
 }

+ 105 - 0
materialfx/src/main/resources/io/github/palexdev/materialfx/css/MFXComboBox.css

@@ -20,6 +20,10 @@
 @import "MFXTextField.css";
 
 .mfx-combo-box {
+	-mfx-track-color: rgb(230, 230, 230);
+	-mfx-thumb-color: rgb(137, 137, 137);
+	-mfx-thumb-hover-color: rgb(89, 88, 91);
+
 	-mfx-gap: 0;
 	-fx-padding: 3 5 3 5;
 }
@@ -42,4 +46,105 @@
 
 .mfx-combo-box:disabled .caret .mfx-font-icon {
 	-mfx-color: #bcbcbc;
+}
+
+/**************************************************
+* Virtual Flow Init
+**************************************************/
+.virtual-flow {
+	-fx-background-color: white;
+}
+
+/* Remove JavaFX crap */
+.scroll-bar,
+.scroll-bar .decrement-arrow,
+.scroll-bar .increment-arrow {
+	-fx-pref-width: 0;
+	-fx-pref-height: 0;
+}
+
+
+.scroll-bar:horizontal .increment-button,
+.scroll-bar:horizontal .decrement-button {
+	-fx-background-color: transparent;
+	-fx-background-radius: 0.0em;
+	-fx-padding: 0.0 0.0 10.0 0.0;
+}
+
+.scroll-bar:vertical .increment-button,
+.scroll-bar:vertical .decrement-button {
+	-fx-background-color: transparent;
+	-fx-background-radius: 0.0em;
+	-fx-padding: 0.0 10.0 0.0 0.0;
+
+}
+
+.scroll-bar .increment-arrow,
+.scroll-bar .decrement-arrow {
+	-fx-shape: " ";
+	-fx-padding: 0.15em 0.0;
+}
+
+.scroll-bar:horizontal .increment-arrow,
+.scroll-bar:horizontal .decrement-arrow {
+	-fx-shape: " ";
+	-fx-padding: 0.0 0.05em;
+}
+
+.scroll-bar:vertical .increment-arrow,
+.scroll-bar:vertical .decrement-arrow {
+	-fx-shape: " ";
+	-fx-padding: 0.0 0.05em;
+}
+
+/* Customize ScrollBars */
+
+.scroll-bar:horizontal .track {
+	-fx-background-color: -mfx-track-color;
+	-fx-border-color: transparent;
+	-fx-background-radius: 2.0em;
+	-fx-border-radius: 2.0em;
+	-fx-background-insets: 3;
+}
+
+.scroll-bar:vertical .track {
+	-fx-background-color: -mfx-track-color;
+	-fx-border-color: transparent;
+	-fx-background-radius: 2.0em;
+	-fx-border-radius: 2.0em;
+	-fx-background-insets: 3;
+}
+
+.scroll-bar .decrement-arrow,
+.scroll-bar .increment-arrow {
+	-fx-pref-width: 0;
+	-fx-pref-height: 0;
+}
+
+.scroll-bar:vertical {
+	-fx-background-color: transparent;
+	-fx-pref-width: 12;
+	-fx-pref-height: 12;
+	-fx-padding: 5 0.5 5 0.5;
+}
+
+.scroll-bar:horizontal {
+	-fx-background-color: transparent;
+	-fx-pref-width: 12;
+	-fx-pref-height: 12;
+	-fx-padding: 0.5 5 0.5 5;
+}
+
+.scroll-bar:horizontal .thumb,
+.scroll-bar:vertical .thumb {
+	-fx-background-color: -mfx-thumb-color;
+	-fx-background-insets: 2.0, 0.0, 0.0;
+	-fx-background-radius: 2.0em;
+}
+
+.scroll-bar:horizontal .thumb:hover,
+.scroll-bar:vertical .thumb:hover {
+	-fx-background-color: -mfx-thumb-hover-color;
+	-fx-background-insets: 1.5, 0.0, 0.0;
+	-fx-background-radius: 2.0em;
 }

+ 1 - 1
materialfx/src/main/resources/io/github/palexdev/materialfx/css/MFXDatePicker.css

@@ -18,7 +18,7 @@
 
 @import "Fonts.css";
 @import "MFXColors.css";
-@import "MFXTextField.css";
+@import "MFXComboBox.css";
 
 /********************
 Date Picker