فهرست منبع

Switching to legacy controls
Legacy controls are controls which customize JavaFX ones and that have a counterpart which is built from scratch by me (see the new combo box or the upcoming table view).

MFXListCell: added support for background radius and insets (currently limited to only one value).
MFXTreeItemSkin: clear the animation on dispose.
NodeUtils: added updateBackground method which accepts fill, radius and insets.

Added new font resources

Signed-off-by: PAlex404 <alessandro.parisi406@gmail.com>

PAlex404 4 سال پیش
والد
کامیت
56958cf197
23فایلهای تغییر یافته به همراه981 افزوده شده و 462 حذف شده
  1. 7 7
      demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ComboBoxesDemoController.java
  2. 16 15
      demo/src/main/resources/io/github/palexdev/materialfx/demo/combo_boxes_demo.fxml
  3. 1 1
      materialfx/gradle.properties
  4. 2 3
      materialfx/src/main/java/io/github/palexdev/materialfx/beans/MFXSnapshotWrapper.java
  5. 42 274
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXComboBox.java
  6. 57 5
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXListCell.java
  7. 34 0
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/enums/ComboBoxStyles.java
  8. 342 0
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/legacy/MFXLegacyComboBox.java
  9. 14 14
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/legacy/MFXLegacyTableRow.java
  10. 8 9
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/legacy/MFXLegacyTableView.java
  11. 4 0
      materialfx/src/main/java/io/github/palexdev/materialfx/font/FontResources.java
  12. 107 128
      materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXComboBoxSkin.java
  13. 5 0
      materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXTreeItemSkin.java
  14. 192 0
      materialfx/src/main/java/io/github/palexdev/materialfx/skins/legacy/MFXLegacyComboBoxSkin.java
  15. 3 3
      materialfx/src/main/java/io/github/palexdev/materialfx/skins/legacy/MFXLegacyTableViewSkin.java
  16. 14 0
      materialfx/src/main/java/io/github/palexdev/materialfx/utils/NodeUtils.java
  17. 2 3
      materialfx/src/main/java/module-info.java
  18. 0 0
      materialfx/src/main/resources/io/github/palexdev/materialfx/css/legacy/mfx-combobox.css
  19. 0 0
      materialfx/src/main/resources/io/github/palexdev/materialfx/css/legacy/mfx-tablerow.css
  20. 0 0
      materialfx/src/main/resources/io/github/palexdev/materialfx/css/legacy/mfx-tableview.css
  21. 65 0
      materialfx/src/main/resources/io/github/palexdev/materialfx/css/mfx-combobox-style1.css
  22. 66 0
      materialfx/src/main/resources/io/github/palexdev/materialfx/css/mfx-combobox-style2.css
  23. BIN
      materialfx/src/main/resources/io/github/palexdev/materialfx/fonts/materialfx-resources.ttf

+ 7 - 7
demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ComboBoxesDemoController.java

@@ -1,7 +1,7 @@
 package io.github.palexdev.materialfx.demo.controllers;
 
 import io.github.palexdev.materialfx.controls.MFXCheckbox;
-import io.github.palexdev.materialfx.controls.MFXComboBox;
+import io.github.palexdev.materialfx.controls.legacy.MFXLegacyComboBox;
 import javafx.collections.FXCollections;
 import javafx.collections.ObservableList;
 import javafx.fxml.FXML;
@@ -17,22 +17,22 @@ import java.util.ResourceBundle;
 public class ComboBoxesDemoController implements Initializable {
 
     @FXML
-    private MFXComboBox<String> standard;
+    private MFXLegacyComboBox<String> standard;
 
     @FXML
-    private MFXComboBox<String> lineColors;
+    private MFXLegacyComboBox<String> lineColors;
 
     @FXML
-    private MFXComboBox<String> editable;
+    private MFXLegacyComboBox<String> editable;
 
     @FXML
-    private MFXComboBox<Label> labels;
+    private MFXLegacyComboBox<Label> labels;
 
     @FXML
-    private MFXComboBox<String> validated;
+    private MFXLegacyComboBox<String> validated;
 
     @FXML
-    private MFXComboBox<String> customized;
+    private MFXLegacyComboBox<String> customized;
 
     @FXML
     private MFXCheckbox checkbox;

+ 16 - 15
demo/src/main/resources/io/github/palexdev/materialfx/demo/combo_boxes_demo.fxml

@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
-<?import io.github.palexdev.materialfx.controls.*?>
+<?import io.github.palexdev.materialfx.controls.legacy.*?>
+<?import io.github.palexdev.materialfx.controls.MFXCheckbox?>
 <?import javafx.geometry.*?>
 <?import javafx.scene.control.Label?>
 <?import javafx.scene.layout.*?>
@@ -10,46 +11,46 @@
          <Insets top="20.0" />
       </StackPane.margin>
    </Label>
-   <MFXComboBox fx:id="standard" animateLines="false" prefHeight="23.0" prefWidth="90.0" promptText="Standard" StackPane.alignment="TOP_CENTER">
+   <MFXLegacyComboBox fx:id="standard" animateLines="false" prefHeight="23.0" prefWidth="90.0" promptText="Standard" StackPane.alignment="TOP_CENTER">
       <StackPane.margin>
          <Insets right="280.0" top="60.0" />
       </StackPane.margin>
-   </MFXComboBox>
-   <MFXComboBox fx:id="lineColors" animateLines="false" lineColor="#58d726" prefHeight="23.0" prefWidth="90.0" promptText="Lines Colors" unfocusedLineColor="#b91212" StackPane.alignment="TOP_CENTER">
+   </MFXLegacyComboBox>
+   <MFXLegacyComboBox fx:id="lineColors" animateLines="false" lineColor="#58d726" prefHeight="23.0" prefWidth="90.0" promptText="Lines Colors" unfocusedLineColor="#b91212" StackPane.alignment="TOP_CENTER">
       <StackPane.margin>
          <Insets top="60.0" />
       </StackPane.margin>
-   </MFXComboBox>
-   <MFXComboBox disable="true" prefHeight="23.0" prefWidth="90.0" promptText="Disabled" StackPane.alignment="TOP_CENTER">
+   </MFXLegacyComboBox>
+   <MFXLegacyComboBox disable="true" prefHeight="23.0" prefWidth="90.0" promptText="Disabled" StackPane.alignment="TOP_CENTER">
       <StackPane.margin>
          <Insets left="280.0" top="60.0" />
       </StackPane.margin>
-   </MFXComboBox>
+   </MFXLegacyComboBox>
    <Label id="customLabel" alignment="CENTER" prefHeight="26.0" prefWidth="266.0" text="Customization" StackPane.alignment="TOP_CENTER">
       <StackPane.margin>
          <Insets top="125.0" />
       </StackPane.margin>
    </Label>
-   <MFXComboBox fx:id="editable" prefHeight="23.0" prefWidth="90.0" promptText="Editable" StackPane.alignment="TOP_CENTER">
+   <MFXLegacyComboBox fx:id="editable" prefHeight="23.0" prefWidth="90.0" promptText="Editable" StackPane.alignment="TOP_CENTER">
       <StackPane.margin>
          <Insets right="120.0" top="170.0" />
       </StackPane.margin>
-   </MFXComboBox>
-   <MFXComboBox fx:id="labels" prefHeight="23.0" prefWidth="90.0" promptText="Labels" StackPane.alignment="TOP_CENTER">
+   </MFXLegacyComboBox>
+   <MFXLegacyComboBox fx:id="labels" prefHeight="23.0" prefWidth="90.0" promptText="Labels" StackPane.alignment="TOP_CENTER">
       <StackPane.margin>
          <Insets right="350.0" top="170.0" />
       </StackPane.margin>
-   </MFXComboBox>
-   <MFXComboBox fx:id="validated" lineColor="#ffdc00" prefHeight="23.0" prefWidth="90.0" promptText="Validated" validated="true" StackPane.alignment="TOP_CENTER">
+   </MFXLegacyComboBox>
+   <MFXLegacyComboBox fx:id="validated" lineColor="#ffdc00" prefHeight="23.0" prefWidth="90.0" promptText="Validated" validated="true" StackPane.alignment="TOP_CENTER">
       <StackPane.margin>
          <Insets left="120.0" top="170.0" />
       </StackPane.margin>
-   </MFXComboBox>
-   <MFXComboBox id="custom" fx:id="customized" prefHeight="23.0" prefWidth="90.0" promptText="CSS" StackPane.alignment="TOP_CENTER">
+   </MFXLegacyComboBox>
+   <MFXLegacyComboBox id="custom" fx:id="customized" prefHeight="23.0" prefWidth="90.0" promptText="CSS" StackPane.alignment="TOP_CENTER">
       <StackPane.margin>
          <Insets left="350.0" top="170.0" />
       </StackPane.margin>
-   </MFXComboBox>
+   </MFXLegacyComboBox>
    <MFXCheckbox fx:id="checkbox" text="Validation!" StackPane.alignment="BOTTOM_LEFT">
       <StackPane.margin>
          <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />

+ 1 - 1
materialfx/gradle.properties

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

+ 2 - 3
materialfx/src/main/java/io/github/palexdev/materialfx/beans/MFXSnapshotWrapper.java

@@ -18,7 +18,6 @@
 
 package io.github.palexdev.materialfx.beans;
 
-import io.github.palexdev.materialfx.controls.MFXComboBox;
 import javafx.scene.Node;
 import javafx.scene.SnapshotParameters;
 import javafx.scene.image.ImageView;
@@ -26,9 +25,9 @@ import javafx.scene.image.WritableImage;
 import javafx.scene.paint.Color;
 
 /**
- * Class used in {@link MFXComboBox}, workaround for showing the item graphic if is is a node.
+ * Class used in various controls as a workaround for showing a node two or more times on the scene graph.
  * <p>
- * Makes a screenshot of the graphic node with transparent background.
+ * Makes a screenshot of a node with transparent background.
  * Then {@link #getGraphic()} should be used to get an ImageView node which contains the screenshot.
  * <p></p>
  * A little side note: since it is a screenshot the image may appear a little blurry compared to the real

+ 42 - 274
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXComboBox.java

@@ -19,312 +19,85 @@
 package io.github.palexdev.materialfx.controls;
 
 import io.github.palexdev.materialfx.MFXResourcesLoader;
-import io.github.palexdev.materialfx.beans.MFXSnapshotWrapper;
-import io.github.palexdev.materialfx.controls.cell.MFXListCell;
+import io.github.palexdev.materialfx.controls.enums.ComboBoxStyles;
 import io.github.palexdev.materialfx.skins.MFXComboBoxSkin;
-import io.github.palexdev.materialfx.validation.MFXDialogValidator;
-import javafx.beans.property.BooleanProperty;
-import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.collections.FXCollections;
 import javafx.collections.ObservableList;
-import javafx.css.*;
-import javafx.scene.SnapshotParameters;
-import javafx.scene.control.ComboBox;
-import javafx.scene.control.Labeled;
-import javafx.scene.control.ListCell;
+import javafx.scene.control.Control;
 import javafx.scene.control.Skin;
-import javafx.scene.image.WritableImage;
-import javafx.scene.paint.Color;
-import javafx.scene.paint.Paint;
 
-import java.util.List;
-
-/**
- * This is the implementation of a combo box following Google's material design guidelines in JavaFX.
- * <p>
- * Extends {@code ComboBox}, redefines the style class to "mfx-combo-box" for usage in CSS and
- * includes a {@link MFXDialogValidator}.
- * <p></p>
- * A few notes on features and usage:
- * <p>
- * If you check {@link ComboBox} documentation you will see a big warning about using nodes as content
- * because the scenegraph only allows for Nodes to be in one place at a time.
- * I found a workaround to this issue using {@link #snapshot(SnapshotParameters, WritableImage)}.
- * Basically I make a "screenshot" of the graphic and then I use an {@code ImageView} to show it.
- * <p>
- * So let's say you have a combo box of labels with icons as graphic, when you select an item, it won't disappear anymore
- * from the list because what you are seeing it's not the real graphic but a screenshot of it.
- * <p>
- * I recommend to use only nodes which are instances of {@code Labeled} since the {@code toString()} method is overridden
- * to return the control's text.
- * @see MFXSnapshotWrapper
- */
-public class MFXComboBox<T> extends ComboBox<T> {
-    //================================================================================
-    // Properties
-    //================================================================================
-    private static final StyleablePropertyFactory<MFXComboBox<?>> FACTORY = new StyleablePropertyFactory<>(ComboBox.getClassCssMetaData());
+public class MFXComboBox<T> extends Control {
     private final String STYLE_CLASS = "mfx-combo-box";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-combobox.css").toString();
-
-    private MFXDialogValidator validator;
-
-    //================================================================================
-    // Constructors
-    //================================================================================
-    public MFXComboBox() {
-        initialize();
-    }
-
-    public MFXComboBox(ObservableList<T> observableList) {
-        super(observableList);
-        initialize();
-    }
-
-    //================================================================================
-    // Methods
-    //================================================================================
-    private void initialize() {
-        getStyleClass().add(STYLE_CLASS);
-        setCellFactory(listCell -> new MFXListCell<>() {
-            @Override
-            protected void updateItem(T item, boolean empty) {
-                super.updateItem(item, empty);
+    private final String STYLESHEET;
 
-                getChildren().remove(lookup(".ripple-generator"));
-            }
-        });
-
-        setButtonCell(new ListCell<>() {
-            {
-                valueProperty().addListener(observable -> {
-                    if (getValue() == null) {
-                        updateItem(null, true);
-                    }
-                });
-            }
-
-            @Override
-            protected void updateItem(T item, boolean empty) {
-                updateComboItem(this, item, empty);
-            }
-        });
-
-        setupValidator();
-    }
-
-    /**
-     * Defines the behavior of the button cell.
-     * <p>
-     * If it's empty or the item is null, shows the prompt text.
-     * <p>
-     * If the item is instanceof {@code Labeled} makes a "screenshot" of the graphic if not null,
-     * and gets item's text. Otherwise calls {@code toString()} on the item.
-     */
-    protected void updateComboItem(ListCell<T> cell, T item, boolean empty) {
-
-        if (empty || item == null) {
-            cell.setGraphic(null);
-            cell.setText(getPromptText());
-            return;
-        }
-
-        if (item instanceof Labeled) {
-            Labeled nodeItem = (Labeled) item;
-            if (nodeItem.getGraphic() != null) {
-                cell.setGraphic(new MFXSnapshotWrapper(nodeItem.getGraphic()).getGraphic());
-            }
-            cell.setText(nodeItem.getText());
-        } else {
-            cell.setText(item.toString());
-        }
-    }
-
-    /**
-     * Configures the validator. If {@link #isValidated()} is true, by default shows a warning
-     * if no item is selected. The warning is showed as soon as the control is out of focus.
-     */
-    private void setupValidator() {
-        BooleanProperty validIndex = new SimpleBooleanProperty(false);
-        validIndex.bind(getSelectionModel().selectedIndexProperty().isNotEqualTo(-1));
-        validator = new MFXDialogValidator("Warning");
-        validator.add(validIndex, "Selected index is not valid");
-    }
-
-    /**
-     * Returns the validator instance of this control.
-     */
-    public MFXDialogValidator getValidator() {
-        return validator;
-    }
+    private final ObjectProperty<T> selectedValue = new SimpleObjectProperty<>();
+    private final ObjectProperty<ObservableList<T>> items = new SimpleObjectProperty<>(FXCollections.observableArrayList());
 
-    //================================================================================
-    // Styleable Properties
-    //================================================================================
+    private final DoubleProperty maxPopupHeight = new SimpleDoubleProperty(200);
 
-    /**
-     * Specifies the line's color when the control is focused.
-     */
-    private final StyleableObjectProperty<Paint> lineColor = new SimpleStyleableObjectProperty<>(
-            StyleableProperties.LINE_COLOR,
-            this,
-            "lineColor",
-            Color.rgb(50, 120, 220)
-    );
-
-    /**
-     * Specifies the line's color when the control is not focused.
-     */
-    private final StyleableObjectProperty<Paint> unfocusedLineColor = new SimpleStyleableObjectProperty<>(
-            StyleableProperties.UNFOCUSED_LINE_COLOR,
-            this,
-            "unfocusedLineColor",
-            Color.rgb(77, 77, 77)
-    );
-
-    /**
-     * Specifies the lines' width.
-     */
-    private final StyleableDoubleProperty lineStrokeWidth = new SimpleStyleableDoubleProperty(
-            StyleableProperties.LINE_STROKE_WIDTH,
-            this,
-            "lineStrokeWidth",
-            1.5
-    );
-
-    /**
-     * Specifies if the lines switch between focus/un-focus should be animated.
-     */
-    private final StyleableBooleanProperty animateLines = new SimpleStyleableBooleanProperty(
-            StyleableProperties.ANIMATE_LINES,
-            this,
-            "animateLines",
-            true
-    );
-
-    /**
-     * Specifies if validation is required for the control.
-     */
-    private final StyleableBooleanProperty isValidated = new SimpleStyleableBooleanProperty(
-            StyleableProperties.IS_VALIDATED,
-            this,
-            "isValidated",
-            false
-    );
-
-    public Paint getLineColor() {
-        return lineColor.get();
-    }
-
-    public StyleableObjectProperty<Paint> lineColorProperty() {
-        return lineColor;
-    }
-
-    public void setLineColor(Paint lineColor) {
-        this.lineColor.set(lineColor);
+    public MFXComboBox() {
+        this(FXCollections.observableArrayList());
     }
 
-    public Paint getUnfocusedLineColor() {
-        return unfocusedLineColor.get();
+    public MFXComboBox(ComboBoxStyles style) {
+        this(FXCollections.observableArrayList(), style);
     }
 
-    public StyleableObjectProperty<Paint> unfocusedLineColorProperty() {
-        return unfocusedLineColor;
+    public MFXComboBox(ObservableList<T> items) {
+        this(items, ComboBoxStyles.STYLE1);
     }
 
-    public void setUnfocusedLineColor(Paint unfocusedLineColor) {
-        this.unfocusedLineColor.set(unfocusedLineColor);
-    }
+    public MFXComboBox(ObservableList<T> items, ComboBoxStyles style) {
+        this.STYLESHEET = MFXResourcesLoader.load(style.getStyleSheetPath()).toString();
+        this.items.set(items);
 
-    public double getLineStrokeWidth() {
-        return lineStrokeWidth.get();
+        initialize();
     }
 
-    public StyleableDoubleProperty lineStrokeWidthProperty() {
-        return lineStrokeWidth;
+    private void initialize() {
+        getStyleClass().add(STYLE_CLASS);
     }
 
-    public void setLineStrokeWidth(double lineStrokeWidth) {
-        this.lineStrokeWidth.set(lineStrokeWidth);
+    public T getSelectedValue() {
+        return selectedValue.get();
     }
 
-    public boolean isAnimateLines() {
-        return animateLines.get();
+    public ObjectProperty<T> selectedValueProperty() {
+        return selectedValue;
     }
 
-    public StyleableBooleanProperty animateLinesProperty() {
-        return animateLines;
+    public void setSelectedValue(T selectedValue) {
+        this.selectedValue.set(selectedValue);
     }
 
-    public void setAnimateLines(boolean animateLines) {
-        this.animateLines.set(animateLines);
+    public ObservableList<T> getItems() {
+        return items.get();
     }
 
-    public boolean isValidated() {
-        return isValidated.get();
+    public ObjectProperty<ObservableList<T>> itemsProperty() {
+        return items;
     }
 
-    public StyleableBooleanProperty isValidatedProperty() {
-        return isValidated;
+    public void setItems(ObservableList<T> items) {
+        this.items.set(items);
     }
 
-    public void setValidated(boolean isValidated) {
-        this.isValidated.set(isValidated);
+    public double getMaxPopupHeight() {
+        return maxPopupHeight.get();
     }
 
-    //================================================================================
-    // CssMetaData
-    //================================================================================
-    private static class StyleableProperties {
-        private static final List<CssMetaData<? extends Styleable, ?>> cssMetaDataList;
-
-        private static final CssMetaData<MFXComboBox<?>, Paint> LINE_COLOR =
-                FACTORY.createPaintCssMetaData(
-                        "-mfx-line-color",
-                        MFXComboBox::lineColorProperty,
-                        Color.rgb(50, 150, 205)
-                );
-
-        private static final CssMetaData<MFXComboBox<?>, Paint> UNFOCUSED_LINE_COLOR =
-                FACTORY.createPaintCssMetaData(
-                        "-mfx-unfocused-line-color",
-                        MFXComboBox::unfocusedLineColorProperty,
-                        Color.rgb(77, 77, 77)
-                );
-
-        private final static CssMetaData<MFXComboBox<?>, Number> LINE_STROKE_WIDTH =
-                FACTORY.createSizeCssMetaData(
-                        "-mfx-line-stroke-width",
-                        MFXComboBox::lineStrokeWidthProperty,
-                        1.5
-                );
-
-        private static final CssMetaData<MFXComboBox<?>, Boolean> ANIMATE_LINES =
-                FACTORY.createBooleanCssMetaData(
-                        "-mfx-animate-lines",
-                        MFXComboBox::animateLinesProperty,
-                        true
-                );
-
-        private static final CssMetaData<MFXComboBox<?>, Boolean> IS_VALIDATED =
-                FACTORY.createBooleanCssMetaData(
-                        "-mfx-validate",
-                        MFXComboBox::isValidatedProperty,
-                        false
-                );
-
-        static {
-            cssMetaDataList = List.of(LINE_COLOR, UNFOCUSED_LINE_COLOR, LINE_STROKE_WIDTH, IS_VALIDATED);
-        }
-
+    public DoubleProperty maxPopupHeightProperty() {
+        return maxPopupHeight;
     }
 
-    public static List<CssMetaData<? extends Styleable, ?>> getControlCssMetaDataList() {
-        return StyleableProperties.cssMetaDataList;
+    public void setMaxPopupHeight(double maxPopupHeight) {
+        this.maxPopupHeight.set(maxPopupHeight);
     }
 
-    //================================================================================
-    // Override Methods
-    //================================================================================
     @Override
     protected Skin<?> createDefaultSkin() {
         return new MFXComboBoxSkin<>(this);
@@ -334,9 +107,4 @@ public class MFXComboBox<T> extends ComboBox<T> {
     public String getUserAgentStylesheet() {
         return STYLESHEET;
     }
-
-    @Override
-    public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
-        return MFXComboBox.getControlCssMetaDataList();
-    }
 }

+ 57 - 5
materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXListCell.java

@@ -89,9 +89,9 @@ public class MFXListCell<T> extends ListCell<T> {
 
         selectedProperty().addListener((observable, oldValue, newValue) -> {
             if (newValue) {
-                NodeUtils.updateBackground(MFXListCell.this, getSelectedColor());
+                NodeUtils.updateBackground(MFXListCell.this, getSelectedColor(), new CornerRadii(getCornerRadius()), new Insets(getBackgroundInsets()));
             } else {
-                NodeUtils.updateBackground(MFXListCell.this, Color.WHITE);
+                NodeUtils.updateBackground(MFXListCell.this, Color.WHITE, new CornerRadii(getCornerRadius()), new Insets(getBackgroundInsets()));
             }
         });
 
@@ -104,10 +104,10 @@ public class MFXListCell<T> extends ListCell<T> {
                 if (getIndex() == 0) {
                     setBackground(new Background(new BackgroundFill(getHoverColor(), CornerRadii.EMPTY, Insets.EMPTY)));
                 } else {
-                    NodeUtils.updateBackground(MFXListCell.this, getHoverColor());
+                    NodeUtils.updateBackground(MFXListCell.this, getHoverColor(), new CornerRadii(getCornerRadius()), new Insets(getBackgroundInsets()));
                 }
             } else {
-                NodeUtils.updateBackground(MFXListCell.this, Color.WHITE);
+                NodeUtils.updateBackground(MFXListCell.this, Color.WHITE, new CornerRadii(getCornerRadius()), new Insets(getBackgroundInsets()));
             }
         });
 
@@ -151,6 +151,20 @@ public class MFXListCell<T> extends ListCell<T> {
             Color.rgb(50, 150, 255, 0.2)
     );
 
+    private final StyleableDoubleProperty cornerRadius = new SimpleStyleableDoubleProperty(
+            StyleableProperties.CORNER_RADIUS,
+            this,
+            "cornerRadius",
+            0.0
+    );
+
+    private final StyleableDoubleProperty backgroundInsets = new SimpleStyleableDoubleProperty(
+            StyleableProperties.BACKGROUND_INSETS,
+            this,
+            "backgroundInsets",
+            0.0
+    );
+
     public Paint getSelectedColor() {
         return selectedColor.get();
     }
@@ -175,6 +189,30 @@ public class MFXListCell<T> extends ListCell<T> {
         this.hoverColor.set(hoverColor);
     }
 
+    public double getCornerRadius() {
+        return cornerRadius.get();
+    }
+
+    public StyleableDoubleProperty cornerRadiusProperty() {
+        return cornerRadius;
+    }
+
+    public void setCornerRadius(double cornerRadius) {
+        this.cornerRadius.set(cornerRadius);
+    }
+
+    public double getBackgroundInsets() {
+        return backgroundInsets.get();
+    }
+
+    public StyleableDoubleProperty backgroundInsetsProperty() {
+        return backgroundInsets;
+    }
+
+    public void setBackgroundInsets(double backgroundInsets) {
+        this.backgroundInsets.set(backgroundInsets);
+    }
+
     //================================================================================
     // CssMetaData
     //================================================================================
@@ -195,8 +233,22 @@ public class MFXListCell<T> extends ListCell<T> {
                         Color.rgb(50, 150, 255, 0.2)
                 );
 
+        private static final CssMetaData<MFXListCell<?>, Number> CORNER_RADIUS =
+                FACTORY.createSizeCssMetaData(
+                        "-mfx-corner-radius",
+                        MFXListCell::cornerRadiusProperty,
+                        0
+                );
+
+        private static final CssMetaData<MFXListCell<?>, Number> BACKGROUND_INSETS =
+                FACTORY.createSizeCssMetaData(
+                        "-mfx-background-insets",
+                        MFXListCell::backgroundInsetsProperty,
+                        0
+                );
+
         static {
-            cssMetaDataList = List.of(SELECTED_COLOR, HOVER_COLOR);
+            cssMetaDataList = List.of(SELECTED_COLOR, HOVER_COLOR, CORNER_RADIUS, BACKGROUND_INSETS);
         }
 
     }

+ 34 - 0
materialfx/src/main/java/io/github/palexdev/materialfx/controls/enums/ComboBoxStyles.java

@@ -0,0 +1,34 @@
+/*
+ *     Copyright (C) 2021 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 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 General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with MaterialFX.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.materialfx.controls.enums;
+
+public enum ComboBoxStyles {
+    STYLE1("css/mfx-combobox-style1.css"),
+    STYLE2("css/mfx-combobox-style2.css");
+
+    private final String styleSheetPath;
+
+    ComboBoxStyles(String styleSheetPath) {
+        this.styleSheetPath = styleSheetPath;
+    }
+
+    public String getStyleSheetPath() {
+        return styleSheetPath;
+    }
+}

+ 342 - 0
materialfx/src/main/java/io/github/palexdev/materialfx/controls/legacy/MFXLegacyComboBox.java

@@ -0,0 +1,342 @@
+/*
+ *     Copyright (C) 2021 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 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 General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with MaterialFX.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.materialfx.controls.legacy;
+
+import io.github.palexdev.materialfx.MFXResourcesLoader;
+import io.github.palexdev.materialfx.beans.MFXSnapshotWrapper;
+import io.github.palexdev.materialfx.controls.cell.MFXListCell;
+import io.github.palexdev.materialfx.skins.legacy.MFXLegacyComboBoxSkin;
+import io.github.palexdev.materialfx.validation.MFXDialogValidator;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.collections.ObservableList;
+import javafx.css.*;
+import javafx.scene.SnapshotParameters;
+import javafx.scene.control.ComboBox;
+import javafx.scene.control.Labeled;
+import javafx.scene.control.ListCell;
+import javafx.scene.control.Skin;
+import javafx.scene.image.WritableImage;
+import javafx.scene.paint.Color;
+import javafx.scene.paint.Paint;
+
+import java.util.List;
+
+/**
+ * This is the implementation of a combo box following Google's material design guidelines in JavaFX.
+ * <p>
+ * Extends {@code ComboBox}, redefines the style class to "mfx-combo-box" for usage in CSS and
+ * includes a {@link MFXDialogValidator}.
+ * <p></p>
+ * A few notes on features and usage:
+ * <p>
+ * If you check {@link ComboBox} documentation you will see a big warning about using nodes as content
+ * because the scenegraph only allows for Nodes to be in one place at a time.
+ * I found a workaround to this issue using {@link #snapshot(SnapshotParameters, WritableImage)}.
+ * Basically I make a "screenshot" of the graphic and then I use an {@code ImageView} to show it.
+ * <p>
+ * So let's say you have a combo box of labels with icons as graphic, when you select an item, it won't disappear anymore
+ * from the list because what you are seeing it's not the real graphic but a screenshot of it.
+ * <p>
+ * I recommend to use only nodes which are instances of {@code Labeled} since the {@code toString()} method is overridden
+ * to return the control's text.
+ * @see MFXSnapshotWrapper
+ */
+public class MFXLegacyComboBox<T> extends ComboBox<T> {
+    //================================================================================
+    // Properties
+    //================================================================================
+    private static final StyleablePropertyFactory<MFXLegacyComboBox<?>> FACTORY = new StyleablePropertyFactory<>(ComboBox.getClassCssMetaData());
+    private final String STYLE_CLASS = "mfx-combo-box";
+    private final String STYLESHEET = MFXResourcesLoader.load("css/legacy/mfx-combobox.css").toString();
+
+    private MFXDialogValidator validator;
+
+    //================================================================================
+    // Constructors
+    //================================================================================
+    public MFXLegacyComboBox() {
+        initialize();
+    }
+
+    public MFXLegacyComboBox(ObservableList<T> observableList) {
+        super(observableList);
+        initialize();
+    }
+
+    //================================================================================
+    // Methods
+    //================================================================================
+    private void initialize() {
+        getStyleClass().add(STYLE_CLASS);
+        setCellFactory(listCell -> new MFXListCell<>() {
+            @Override
+            protected void updateItem(T item, boolean empty) {
+                super.updateItem(item, empty);
+
+                getChildren().remove(lookup(".ripple-generator"));
+            }
+        });
+
+        setButtonCell(new ListCell<>() {
+            {
+                valueProperty().addListener(observable -> {
+                    if (getValue() == null) {
+                        updateItem(null, true);
+                    }
+                });
+            }
+
+            @Override
+            protected void updateItem(T item, boolean empty) {
+                updateComboItem(this, item, empty);
+            }
+        });
+
+        setupValidator();
+    }
+
+    /**
+     * Defines the behavior of the button cell.
+     * <p>
+     * If it's empty or the item is null, shows the prompt text.
+     * <p>
+     * If the item is instanceof {@code Labeled} makes a "screenshot" of the graphic if not null,
+     * and gets item's text. Otherwise calls {@code toString()} on the item.
+     */
+    protected void updateComboItem(ListCell<T> cell, T item, boolean empty) {
+
+        if (empty || item == null) {
+            cell.setGraphic(null);
+            cell.setText(getPromptText());
+            return;
+        }
+
+        if (item instanceof Labeled) {
+            Labeled nodeItem = (Labeled) item;
+            if (nodeItem.getGraphic() != null) {
+                cell.setGraphic(new MFXSnapshotWrapper(nodeItem.getGraphic()).getGraphic());
+            }
+            cell.setText(nodeItem.getText());
+        } else {
+            cell.setText(item.toString());
+        }
+    }
+
+    /**
+     * Configures the validator. If {@link #isValidated()} is true, by default shows a warning
+     * if no item is selected. The warning is showed as soon as the control is out of focus.
+     */
+    private void setupValidator() {
+        BooleanProperty validIndex = new SimpleBooleanProperty(false);
+        validIndex.bind(getSelectionModel().selectedIndexProperty().isNotEqualTo(-1));
+        validator = new MFXDialogValidator("Warning");
+        validator.add(validIndex, "Selected index is not valid");
+    }
+
+    /**
+     * Returns the validator instance of this control.
+     */
+    public MFXDialogValidator getValidator() {
+        return validator;
+    }
+
+    //================================================================================
+    // Styleable Properties
+    //================================================================================
+
+    /**
+     * Specifies the line's color when the control is focused.
+     */
+    private final StyleableObjectProperty<Paint> lineColor = new SimpleStyleableObjectProperty<>(
+            StyleableProperties.LINE_COLOR,
+            this,
+            "lineColor",
+            Color.rgb(50, 120, 220)
+    );
+
+    /**
+     * Specifies the line's color when the control is not focused.
+     */
+    private final StyleableObjectProperty<Paint> unfocusedLineColor = new SimpleStyleableObjectProperty<>(
+            StyleableProperties.UNFOCUSED_LINE_COLOR,
+            this,
+            "unfocusedLineColor",
+            Color.rgb(77, 77, 77)
+    );
+
+    /**
+     * Specifies the lines' width.
+     */
+    private final StyleableDoubleProperty lineStrokeWidth = new SimpleStyleableDoubleProperty(
+            StyleableProperties.LINE_STROKE_WIDTH,
+            this,
+            "lineStrokeWidth",
+            1.5
+    );
+
+    /**
+     * Specifies if the lines switch between focus/un-focus should be animated.
+     */
+    private final StyleableBooleanProperty animateLines = new SimpleStyleableBooleanProperty(
+            StyleableProperties.ANIMATE_LINES,
+            this,
+            "animateLines",
+            true
+    );
+
+    /**
+     * Specifies if validation is required for the control.
+     */
+    private final StyleableBooleanProperty isValidated = new SimpleStyleableBooleanProperty(
+            StyleableProperties.IS_VALIDATED,
+            this,
+            "isValidated",
+            false
+    );
+
+    public Paint getLineColor() {
+        return lineColor.get();
+    }
+
+    public StyleableObjectProperty<Paint> lineColorProperty() {
+        return lineColor;
+    }
+
+    public void setLineColor(Paint lineColor) {
+        this.lineColor.set(lineColor);
+    }
+
+    public Paint getUnfocusedLineColor() {
+        return unfocusedLineColor.get();
+    }
+
+    public StyleableObjectProperty<Paint> unfocusedLineColorProperty() {
+        return unfocusedLineColor;
+    }
+
+    public void setUnfocusedLineColor(Paint unfocusedLineColor) {
+        this.unfocusedLineColor.set(unfocusedLineColor);
+    }
+
+    public double getLineStrokeWidth() {
+        return lineStrokeWidth.get();
+    }
+
+    public StyleableDoubleProperty lineStrokeWidthProperty() {
+        return lineStrokeWidth;
+    }
+
+    public void setLineStrokeWidth(double lineStrokeWidth) {
+        this.lineStrokeWidth.set(lineStrokeWidth);
+    }
+
+    public boolean isAnimateLines() {
+        return animateLines.get();
+    }
+
+    public StyleableBooleanProperty animateLinesProperty() {
+        return animateLines;
+    }
+
+    public void setAnimateLines(boolean animateLines) {
+        this.animateLines.set(animateLines);
+    }
+
+    public boolean isValidated() {
+        return isValidated.get();
+    }
+
+    public StyleableBooleanProperty isValidatedProperty() {
+        return isValidated;
+    }
+
+    public void setValidated(boolean isValidated) {
+        this.isValidated.set(isValidated);
+    }
+
+    //================================================================================
+    // CssMetaData
+    //================================================================================
+    private static class StyleableProperties {
+        private static final List<CssMetaData<? extends Styleable, ?>> cssMetaDataList;
+
+        private static final CssMetaData<MFXLegacyComboBox<?>, Paint> LINE_COLOR =
+                FACTORY.createPaintCssMetaData(
+                        "-mfx-line-color",
+                        MFXLegacyComboBox::lineColorProperty,
+                        Color.rgb(50, 150, 205)
+                );
+
+        private static final CssMetaData<MFXLegacyComboBox<?>, Paint> UNFOCUSED_LINE_COLOR =
+                FACTORY.createPaintCssMetaData(
+                        "-mfx-unfocused-line-color",
+                        MFXLegacyComboBox::unfocusedLineColorProperty,
+                        Color.rgb(77, 77, 77)
+                );
+
+        private final static CssMetaData<MFXLegacyComboBox<?>, Number> LINE_STROKE_WIDTH =
+                FACTORY.createSizeCssMetaData(
+                        "-mfx-line-stroke-width",
+                        MFXLegacyComboBox::lineStrokeWidthProperty,
+                        1.5
+                );
+
+        private static final CssMetaData<MFXLegacyComboBox<?>, Boolean> ANIMATE_LINES =
+                FACTORY.createBooleanCssMetaData(
+                        "-mfx-animate-lines",
+                        MFXLegacyComboBox::animateLinesProperty,
+                        true
+                );
+
+        private static final CssMetaData<MFXLegacyComboBox<?>, Boolean> IS_VALIDATED =
+                FACTORY.createBooleanCssMetaData(
+                        "-mfx-validate",
+                        MFXLegacyComboBox::isValidatedProperty,
+                        false
+                );
+
+        static {
+            cssMetaDataList = List.of(LINE_COLOR, UNFOCUSED_LINE_COLOR, LINE_STROKE_WIDTH, IS_VALIDATED);
+        }
+
+    }
+
+    public static List<CssMetaData<? extends Styleable, ?>> getControlCssMetaDataList() {
+        return StyleableProperties.cssMetaDataList;
+    }
+
+    //================================================================================
+    // Override Methods
+    //================================================================================
+    @Override
+    protected Skin<?> createDefaultSkin() {
+        return new MFXLegacyComboBoxSkin<>(this);
+    }
+
+    @Override
+    public String getUserAgentStylesheet() {
+        return STYLESHEET;
+    }
+
+    @Override
+    public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
+        return MFXLegacyComboBox.getControlCssMetaDataList();
+    }
+}

+ 14 - 14
materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/tableview/MFXTableRow.java → materialfx/src/main/java/io/github/palexdev/materialfx/controls/legacy/MFXLegacyTableRow.java

@@ -16,7 +16,7 @@
  *     along with MaterialFX.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-package io.github.palexdev.materialfx.controls.cell.tableview;
+package io.github.palexdev.materialfx.controls.legacy;
 
 import io.github.palexdev.materialfx.MFXResourcesLoader;
 import io.github.palexdev.materialfx.effects.RippleGenerator;
@@ -34,13 +34,13 @@ import javafx.util.Duration;
 
 import java.util.List;
 
-public class MFXTableRow<T> extends TableRow<T> {
-    private static final StyleablePropertyFactory<MFXTableRow<?>> FACTORY = new StyleablePropertyFactory<>(TableRow.getClassCssMetaData());
+public class MFXLegacyTableRow<T> extends TableRow<T> {
+    private static final StyleablePropertyFactory<MFXLegacyTableRow<?>> FACTORY = new StyleablePropertyFactory<>(TableRow.getClassCssMetaData());
     private final String STYLE_CLASS = "mfx-table-row";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/tableview/mfx-tablerow.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/legacy/mfx-tablerow.css").toString();
     private final RippleGenerator rippleGenerator;
 
-    public MFXTableRow() {
+    public MFXLegacyTableRow() {
         rippleGenerator = new RippleGenerator(this);
         rippleGenerator.setRippleColor(Color.rgb(50, 150, 255));
         rippleGenerator.setInDuration(Duration.millis(400));
@@ -69,9 +69,9 @@ public class MFXTableRow<T> extends TableRow<T> {
 
         selectedProperty().addListener((observable, oldValue, newValue) -> {
             if (newValue) {
-                NodeUtils.updateBackground(MFXTableRow.this, getSelectedColor());
+                NodeUtils.updateBackground(MFXLegacyTableRow.this, getSelectedColor());
             } else {
-                NodeUtils.updateBackground(MFXTableRow.this, Color.WHITE);
+                NodeUtils.updateBackground(MFXLegacyTableRow.this, Color.WHITE);
             }
         });
 
@@ -84,16 +84,16 @@ public class MFXTableRow<T> extends TableRow<T> {
                 if (getIndex() == 0) {
                     setBackground(new Background(new BackgroundFill(getHoverColor(), CornerRadii.EMPTY, Insets.EMPTY)));
                 } else {
-                    NodeUtils.updateBackground(MFXTableRow.this, getHoverColor());
+                    NodeUtils.updateBackground(MFXLegacyTableRow.this, getHoverColor());
                 }
             } else {
-                NodeUtils.updateBackground(MFXTableRow.this, Color.WHITE);
+                NodeUtils.updateBackground(MFXLegacyTableRow.this, Color.WHITE);
             }
         });
 
         selectedColor.addListener((observableValue, oldValue, newValue) -> {
             if (!newValue.equals(oldValue) && isSelected()) {
-                NodeUtils.updateBackground(MFXTableRow.this, newValue);
+                NodeUtils.updateBackground(MFXLegacyTableRow.this, newValue);
             }
         });
     }
@@ -152,17 +152,17 @@ public class MFXTableRow<T> extends TableRow<T> {
     private static class StyleableProperties {
         private static final List<CssMetaData<? extends Styleable, ?>> cssMetaDataList;
 
-        private static final CssMetaData<MFXTableRow<?>, Paint> SELECTED_COLOR =
+        private static final CssMetaData<MFXLegacyTableRow<?>, Paint> SELECTED_COLOR =
                 FACTORY.createPaintCssMetaData(
                         "-mfx-selected-color",
-                        MFXTableRow::selectedColorProperty,
+                        MFXLegacyTableRow::selectedColorProperty,
                         Color.rgb(180, 180, 255)
                 );
 
-        private static final CssMetaData<MFXTableRow<?>, Paint> HOVER_COLOR =
+        private static final CssMetaData<MFXLegacyTableRow<?>, Paint> HOVER_COLOR =
                 FACTORY.createPaintCssMetaData(
                         "-mfx-hover-color",
-                        MFXTableRow::hoverColorProperty,
+                        MFXLegacyTableRow::hoverColorProperty,
                         Color.rgb(50, 150, 255, 0.15)
                 );
 

+ 8 - 9
materialfx/src/main/java/io/github/palexdev/materialfx/controls/tableview/MFXTableView.java → materialfx/src/main/java/io/github/palexdev/materialfx/controls/legacy/MFXLegacyTableView.java

@@ -16,37 +16,36 @@
  *     along with MaterialFX.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-package io.github.palexdev.materialfx.controls.tableview;
+package io.github.palexdev.materialfx.controls.legacy;
 
 import io.github.palexdev.materialfx.MFXResourcesLoader;
-import io.github.palexdev.materialfx.controls.cell.tableview.MFXTableRow;
-import io.github.palexdev.materialfx.skins.tableview.MFXTableViewSkin;
+import io.github.palexdev.materialfx.skins.legacy.MFXLegacyTableViewSkin;
 import javafx.collections.ObservableList;
 import javafx.scene.control.Skin;
 import javafx.scene.control.TableView;
 
-public class MFXTableView<S> extends TableView<S> {
+public class MFXLegacyTableView<S> extends TableView<S> {
     private final String STYLE_CLASS = "mfx-table-view";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/tableview/mfx-tableview.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/legacy/mfx-tableview.css").toString();
 
-    public MFXTableView() {
+    public MFXLegacyTableView() {
         initialize();
     }
 
-    public MFXTableView(ObservableList<S> items) {
+    public MFXLegacyTableView(ObservableList<S> items) {
         super(items);
         initialize();
     }
 
     private void initialize() {
         getStyleClass().add(STYLE_CLASS);
-        setRowFactory(row -> new MFXTableRow<>());
+        setRowFactory(row -> new MFXLegacyTableRow<>());
         setFixedCellSize(27);
     }
 
     @Override
     protected Skin<?> createDefaultSkin() {
-        return new MFXTableViewSkin<>(this);
+        return new MFXLegacyTableViewSkin<>(this);
     }
 
     @Override

+ 4 - 0
materialfx/src/main/java/io/github/palexdev/materialfx/font/FontResources.java

@@ -29,6 +29,10 @@ public enum FontResources {
     CALENDAR_BLACK("mfx-calendar-black", '\uE904'),
     CALENDAR_SEMI_BLACK("mfx-calendar-semi-black", '\uE905'),
     CALENDAR_WHITE("mfx-calendar-white", '\uE906'),
+    CARET_DOWN("mfx-caret-down", '\uE91f'),
+    CARET_LEFT("mfx-caret-left", '\uE920'),
+    CARET_RIGHT("mfx-caret-right", '\uE921'),
+    CARET_UP("mfx-caret-up", '\uE922'),
     CASPIAN_MARK("mfx-caspian-mark", '\uE90b'),
     CHEVRON_DOWN("mfx-chevron-down", '\uE902'),
     CHEVRON_LEFT("mfx-chevron-left", '\uE903'),

+ 107 - 128
materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXComboBoxSkin.java

@@ -20,173 +20,152 @@ package io.github.palexdev.materialfx.skins;
 
 import io.github.palexdev.materialfx.controls.MFXComboBox;
 import io.github.palexdev.materialfx.controls.MFXIconWrapper;
-import io.github.palexdev.materialfx.controls.factories.MFXAnimationFactory;
+import io.github.palexdev.materialfx.controls.MFXListView;
+import io.github.palexdev.materialfx.effects.RippleGenerator;
 import io.github.palexdev.materialfx.font.MFXFontIcon;
-import io.github.palexdev.materialfx.validation.MFXDialogValidator;
-import javafx.animation.ScaleTransition;
+import io.github.palexdev.materialfx.utils.NodeUtils;
+import javafx.animation.KeyFrame;
+import javafx.animation.KeyValue;
+import javafx.animation.Timeline;
+import javafx.event.EventHandler;
+import javafx.geometry.HPos;
+import javafx.geometry.Point2D;
+import javafx.geometry.Pos;
+import javafx.geometry.VPos;
 import javafx.scene.control.Label;
-import javafx.scene.control.skin.ComboBoxListViewSkin;
+import javafx.scene.control.PopupControl;
+import javafx.scene.control.SkinBase;
 import javafx.scene.input.MouseEvent;
-import javafx.scene.paint.Color;
-import javafx.scene.shape.Line;
-import javafx.scene.text.Font;
+import javafx.scene.layout.HBox;
 import javafx.util.Duration;
 
-/**
- * This is the implementation of the {@code Skin} associated with every {@code MFXComboBox}.
- */
-public class MFXComboBoxSkin<T> extends ComboBoxListViewSkin<T> {
-    //================================================================================
-    // Properties
-    //================================================================================
-    private final double padding = 11;
-
-    private final Line line;
-    private final Line focusLine;
-    private final Label validate;
-
-    //================================================================================
-    // Constructors
-    //================================================================================
+public class MFXComboBoxSkin<T> extends SkinBase<MFXComboBox<T>> {
+    private final HBox container;
+    private final Label valueLabel;
+    private final MFXIconWrapper icon;
+    private final PopupControl popup;
+    private final MFXListView<T> listView;
+    private final EventHandler<MouseEvent> popupHandler;
+
+    private Timeline arrowAnimation;
+
     public MFXComboBoxSkin(MFXComboBox<T> comboBox) {
         super(comboBox);
 
-        line = new Line();
-        line.getStyleClass().add("unfocused-line");
-        line.setStroke(comboBox.getUnfocusedLineColor());
-        line.setStrokeWidth(comboBox.getLineStrokeWidth());
-        line.setSmooth(true);
-
-        focusLine = new Line();
-        focusLine.getStyleClass().add("focused-line");
-        focusLine.setStroke(comboBox.getLineColor());
-        focusLine.setStrokeWidth(comboBox.getLineStrokeWidth());
-        focusLine.setSmooth(true);
-        focusLine.setScaleX(0.0);
+        valueLabel = new Label();
 
-        line.endXProperty().bind(comboBox.widthProperty());
-        focusLine.endXProperty().bind(comboBox.widthProperty());
+        MFXFontIcon fontIcon = new MFXFontIcon("mfx-caret-down", 12);
+        icon = new MFXIconWrapper(fontIcon, 24).addRippleGenerator();
+        icon.getStylesheets().addAll(comboBox.getUserAgentStylesheet());
+        NodeUtils.makeRegionCircular(icon, 10);
 
-        MFXFontIcon warnIcon = new MFXFontIcon("mfx-exclamation-triangle", Color.RED);
-        MFXIconWrapper warnWrapper = new MFXIconWrapper(warnIcon, 10);
+        container = new HBox(20, valueLabel, icon);
+        container.setAlignment(Pos.CENTER_LEFT);
 
-        validate = new Label("", warnWrapper);
-        validate.getStyleClass().add("validate-label");
-        validate.textProperty().bind(comboBox.getValidator().validatorMessageProperty());
-        validate.setFont(Font.font(padding));
-        validate.setGraphicTextGap(padding / 2);
-        validate.setVisible(false);
+        listView = new MFXListView<>();
+        listView.getStylesheets().add(comboBox.getUserAgentStylesheet());
+        popup = new PopupControl();
+        buildPopup();
 
-        getChildren().addAll(line, focusLine, validate);
+        popupHandler = event -> {
+            if (popup.isShowing() && !NodeUtils.inHierarchy(event.getPickResult().getIntersectedNode(), comboBox)) {
+                popup.hide();
+            }
+        };
 
+        getChildren().add(container);
         setListeners();
     }
 
-    //================================================================================
-    // Methods
-    //================================================================================
-
-    /**
-     * Adds listeners for: line, focus, disabled and validator properties.
-     * <p>
-     * Validator: when the control is not focused, and of course if {@code isValidated} is true,
-     * all the conditions in the validator are evaluated and if one is false the {@code validate} label is shown.
-     * The label text is bound to the {@code validatorMessage} property so if you want to change it you can do it
-     * by getting the instance with {@code getValidator()}.
-     * <p>
-     * There's also another listener to keep track of validator changes and an event handler to show a dialog if you click
-     * on the warning label.
-     */
     private void setListeners() {
-        MFXComboBox<T> comboBox = (MFXComboBox<T>) getSkinnable();
-        MFXDialogValidator validator = comboBox.getValidator();
+        MFXComboBox<T> comboBox = getSkinnable();
+        RippleGenerator rg = icon.getRippleGenerator();
+        rg.setRippleRadius(8);
 
-        comboBox.lineColorProperty().addListener((observable, oldValue, newValue) -> {
-            if (!newValue.equals(oldValue)) {
-                focusLine.setStroke(newValue);
-            }
-        });
+        comboBox.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> comboBox.requestFocus());
 
-        comboBox.unfocusedLineColorProperty().addListener((observable, oldValue, newValue) -> {
-            if (!newValue.equals(oldValue)) {
-                line.setStroke(newValue);
-            }
+        icon.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
+            rg.setGeneratorCenterX(icon.getWidth() / 2);
+            rg.setGeneratorCenterY(icon.getHeight() / 2);
+            rg.createRipple();
         });
 
-        comboBox.lineStrokeWidthProperty().addListener((observable, oldValue, newValue) -> {
-            if (newValue.doubleValue() != oldValue.doubleValue()) {
-                line.setStrokeWidth(newValue.doubleValue());
-                focusLine.setStrokeWidth(newValue.doubleValue() * 1.3);
+        icon.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
+            if (!popup.isShowing()) {
+                Point2D point = NodeUtils.pointRelativeTo(comboBox, listView, HPos.CENTER, VPos.BOTTOM, 0, 2, false);
+                popup.show(comboBox, snapPositionX(point.getX()), snapPositionY(point.getY()));
+            } else {
+                popup.hide();
             }
         });
 
-        comboBox.focusedProperty().addListener((observable, oldValue, newValue) -> {
-            if (!newValue && comboBox.isValidated()) {
-                validate.setVisible(!validator.isValid());
-            }
-
-            if (comboBox.isAnimateLines()) {
-                buildAndPlayAnimation(newValue);
-                return;
-            }
-
-            if (newValue) {
-                focusLine.setScaleX(1.0);
+        comboBox.selectedValueProperty().bind(listView.getSelectionModel().selectedItemProperty());
+        comboBox.selectedValueProperty().addListener((observable, oldValue, newValue) -> {
+            if (newValue != null) {
+                valueLabel.setText(newValue.toString());
             } else {
-                focusLine.setScaleX(0.0);
+                valueLabel.setText("");
             }
         });
 
-        comboBox.isValidatedProperty().addListener((observable, oldValue, newValue) -> {
-            if (!newValue) {
-                validate.setVisible(false);
+        listView.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
+            if (popup.isShowing()) {
+                popup.hide();
             }
         });
-
-        comboBox.disabledProperty().addListener((observable, oldValue, newValue) -> {
-            if (newValue) {
-                validate.setVisible(false);
+        listView.maxHeightProperty().bind(comboBox.maxPopupHeightProperty());
+
+        comboBox.getScene().addEventFilter(MouseEvent.MOUSE_PRESSED, popupHandler);
+        comboBox.sceneProperty().addListener((observable, oldValue, newValue) -> {
+            if (newValue != oldValue) {
+                oldValue.removeEventFilter(MouseEvent.MOUSE_PRESSED, popupHandler);
+                if (newValue != null) {
+                    newValue.addEventFilter(MouseEvent.MOUSE_PRESSED, popupHandler);
+                }
             }
         });
+    }
 
-        validator.addChangeListener((observable, oldValue, newValue) -> {
-            if (comboBox.isValidated()) {
-                validate.setVisible(!newValue);
-            }
-        });
-        validate.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> validator.showModal(comboBox.getScene().getWindow()));
+    protected void buildPopup() {
+        MFXComboBox<T> comboBox = getSkinnable();
+
+        listView.itemsProperty().bind(comboBox.itemsProperty());
+        popup.getScene().setRoot(listView);
+        popup.setOnShowing(event -> buildAnimation(true).play());
+        popup.setOnHiding(event -> buildAnimation(false).play());
     }
 
-    /**
-     * Builds and play the lines animation if {@code animateLines} is true.
-     */
-    private void buildAndPlayAnimation(boolean focused) {
-        ScaleTransition scaleTransition = new ScaleTransition(Duration.millis(400), focusLine);
-        if (focused) {
-            scaleTransition.setFromX(0.0);
-            scaleTransition.setToX(1.0);
-        } else {
-            scaleTransition.setFromX(1.0);
-            scaleTransition.setToX(0.0);
-        }
-        scaleTransition.setInterpolator(MFXAnimationFactory.getInterpolator());
-        scaleTransition.play();
+    private Timeline buildAnimation(boolean isShowing) {
+        KeyFrame kf0 = new KeyFrame(Duration.millis(150),
+                new KeyValue(icon.rotateProperty(), (isShowing ? 180 : 0))
+        );
+        arrowAnimation = new Timeline(kf0);
+        return arrowAnimation;
+    }
+
+    @Override
+    protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
+        double value = leftInset + 50 + rightInset;
+        valueLabel.setMinWidth(value);
+        return value;
     }
 
-    //================================================================================
-    // Override Methods
-    //================================================================================
     @Override
-    protected void layoutChildren(double x, double y, double w, double h) {
-        super.layoutChildren(x, y, w, h);
+    protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
+        return computePrefWidth(height, topInset, rightInset, bottomInset, leftInset);
+    }
 
-        final double size = padding / 2.5;
-        final double tx = -((w - line.getEndX()) / 2);
+    @Override
+    protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
+        return computePrefHeight(width, topInset, rightInset, bottomInset, leftInset);
+    }
 
-        focusLine.setTranslateY(h);
-        line.setTranslateY(h);
-        validate.resize(w * 1.5, h - size);
-        validate.setTranslateY(focusLine.getTranslateY() + size);
-        validate.setTranslateX(tx);
+    @Override
+    public void dispose() {
+        super.dispose();
+
+        if (arrowAnimation != null) {
+            arrowAnimation = null;
+        }
     }
 }

+ 5 - 0
materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXTreeItemSkin.java

@@ -399,6 +399,11 @@ public class MFXTreeItemSkin<T> extends SkinBase<MFXTreeItem<T>> {
     public void dispose() {
         if (getSkinnable() == null) return;
         getSkinnable().getItems().removeListener(itemsListener);
+
+        if (animation != null) {
+            animation.getChildren().clear();
+            animation = null;
+        }
         super.dispose();
     }
 }

+ 192 - 0
materialfx/src/main/java/io/github/palexdev/materialfx/skins/legacy/MFXLegacyComboBoxSkin.java

@@ -0,0 +1,192 @@
+/*
+ *     Copyright (C) 2021 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 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 General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with MaterialFX.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.materialfx.skins.legacy;
+
+import io.github.palexdev.materialfx.controls.MFXIconWrapper;
+import io.github.palexdev.materialfx.controls.factories.MFXAnimationFactory;
+import io.github.palexdev.materialfx.controls.legacy.MFXLegacyComboBox;
+import io.github.palexdev.materialfx.font.MFXFontIcon;
+import io.github.palexdev.materialfx.validation.MFXDialogValidator;
+import javafx.animation.ScaleTransition;
+import javafx.scene.control.Label;
+import javafx.scene.control.skin.ComboBoxListViewSkin;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Line;
+import javafx.scene.text.Font;
+import javafx.util.Duration;
+
+/**
+ * This is the implementation of the {@code Skin} associated with every {@code MFXLegacyComboBox}.
+ */
+public class MFXLegacyComboBoxSkin<T> extends ComboBoxListViewSkin<T> {
+    //================================================================================
+    // Properties
+    //================================================================================
+    private final double padding = 11;
+
+    private final Line line;
+    private final Line focusLine;
+    private final Label validate;
+
+    //================================================================================
+    // Constructors
+    //================================================================================
+    public MFXLegacyComboBoxSkin(MFXLegacyComboBox<T> comboBox) {
+        super(comboBox);
+
+        line = new Line();
+        line.getStyleClass().add("unfocused-line");
+        line.setStroke(comboBox.getUnfocusedLineColor());
+        line.setStrokeWidth(comboBox.getLineStrokeWidth());
+        line.setSmooth(true);
+
+        focusLine = new Line();
+        focusLine.getStyleClass().add("focused-line");
+        focusLine.setStroke(comboBox.getLineColor());
+        focusLine.setStrokeWidth(comboBox.getLineStrokeWidth());
+        focusLine.setSmooth(true);
+        focusLine.setScaleX(0.0);
+
+        line.endXProperty().bind(comboBox.widthProperty());
+        focusLine.endXProperty().bind(comboBox.widthProperty());
+
+        MFXFontIcon warnIcon = new MFXFontIcon("mfx-exclamation-triangle", Color.RED);
+        MFXIconWrapper warnWrapper = new MFXIconWrapper(warnIcon, 10);
+
+        validate = new Label("", warnWrapper);
+        validate.getStyleClass().add("validate-label");
+        validate.textProperty().bind(comboBox.getValidator().validatorMessageProperty());
+        validate.setFont(Font.font(padding));
+        validate.setGraphicTextGap(padding / 2);
+        validate.setVisible(false);
+
+        getChildren().addAll(line, focusLine, validate);
+
+        setListeners();
+    }
+
+    //================================================================================
+    // Methods
+    //================================================================================
+
+    /**
+     * Adds listeners for: line, focus, disabled and validator properties.
+     * <p>
+     * Validator: when the control is not focused, and of course if {@code isValidated} is true,
+     * all the conditions in the validator are evaluated and if one is false the {@code validate} label is shown.
+     * The label text is bound to the {@code validatorMessage} property so if you want to change it you can do it
+     * by getting the instance with {@code getValidator()}.
+     * <p>
+     * There's also another listener to keep track of validator changes and an event handler to show a dialog if you click
+     * on the warning label.
+     */
+    private void setListeners() {
+        MFXLegacyComboBox<T> comboBox = (MFXLegacyComboBox<T>) getSkinnable();
+        MFXDialogValidator validator = comboBox.getValidator();
+
+        comboBox.lineColorProperty().addListener((observable, oldValue, newValue) -> {
+            if (!newValue.equals(oldValue)) {
+                focusLine.setStroke(newValue);
+            }
+        });
+
+        comboBox.unfocusedLineColorProperty().addListener((observable, oldValue, newValue) -> {
+            if (!newValue.equals(oldValue)) {
+                line.setStroke(newValue);
+            }
+        });
+
+        comboBox.lineStrokeWidthProperty().addListener((observable, oldValue, newValue) -> {
+            if (newValue.doubleValue() != oldValue.doubleValue()) {
+                line.setStrokeWidth(newValue.doubleValue());
+                focusLine.setStrokeWidth(newValue.doubleValue() * 1.3);
+            }
+        });
+
+        comboBox.focusedProperty().addListener((observable, oldValue, newValue) -> {
+            if (!newValue && comboBox.isValidated()) {
+                validate.setVisible(!validator.isValid());
+            }
+
+            if (comboBox.isAnimateLines()) {
+                buildAndPlayAnimation(newValue);
+                return;
+            }
+
+            if (newValue) {
+                focusLine.setScaleX(1.0);
+            } else {
+                focusLine.setScaleX(0.0);
+            }
+        });
+
+        comboBox.isValidatedProperty().addListener((observable, oldValue, newValue) -> {
+            if (!newValue) {
+                validate.setVisible(false);
+            }
+        });
+
+        comboBox.disabledProperty().addListener((observable, oldValue, newValue) -> {
+            if (newValue) {
+                validate.setVisible(false);
+            }
+        });
+
+        validator.addChangeListener((observable, oldValue, newValue) -> {
+            if (comboBox.isValidated()) {
+                validate.setVisible(!newValue);
+            }
+        });
+        validate.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> validator.showModal(comboBox.getScene().getWindow()));
+    }
+
+    /**
+     * Builds and play the lines animation if {@code animateLines} is true.
+     */
+    private void buildAndPlayAnimation(boolean focused) {
+        ScaleTransition scaleTransition = new ScaleTransition(Duration.millis(400), focusLine);
+        if (focused) {
+            scaleTransition.setFromX(0.0);
+            scaleTransition.setToX(1.0);
+        } else {
+            scaleTransition.setFromX(1.0);
+            scaleTransition.setToX(0.0);
+        }
+        scaleTransition.setInterpolator(MFXAnimationFactory.getInterpolator());
+        scaleTransition.play();
+    }
+
+    //================================================================================
+    // Override Methods
+    //================================================================================
+    @Override
+    protected void layoutChildren(double x, double y, double w, double h) {
+        super.layoutChildren(x, y, w, h);
+
+        final double size = padding / 2.5;
+        final double tx = -((w - line.getEndX()) / 2);
+
+        focusLine.setTranslateY(h);
+        line.setTranslateY(h);
+        validate.resize(w * 1.5, h - size);
+        validate.setTranslateY(focusLine.getTranslateY() + size);
+        validate.setTranslateX(tx);
+    }
+}

+ 3 - 3
materialfx/src/main/java/io/github/palexdev/materialfx/skins/tableview/MFXTableViewSkin.java → materialfx/src/main/java/io/github/palexdev/materialfx/skins/legacy/MFXLegacyTableViewSkin.java

@@ -16,7 +16,7 @@
  *     along with MaterialFX.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-package io.github.palexdev.materialfx.skins.tableview;
+package io.github.palexdev.materialfx.skins.legacy;
 
 import io.github.palexdev.materialfx.controls.factories.MFXAnimationFactory;
 import javafx.animation.KeyFrame;
@@ -36,7 +36,7 @@ import javafx.util.Duration;
 
 import java.util.Set;
 
-public class MFXTableViewSkin<T> extends TableViewSkin<T> {
+public class MFXLegacyTableViewSkin<T> extends TableViewSkin<T> {
     //================================================================================
     // Properties
     //================================================================================
@@ -49,7 +49,7 @@ public class MFXTableViewSkin<T> extends TableViewSkin<T> {
     private final Timeline hideBars;
     private final Timeline showBars;
 
-    public MFXTableViewSkin(TableView<T> tableView) {
+    public MFXLegacyTableViewSkin(TableView<T> tableView) {
         super(tableView);
 
         virtualFlow = (VirtualFlow<?>) tableView.lookup(".virtual-flow");

+ 14 - 0
materialfx/src/main/java/io/github/palexdev/materialfx/utils/NodeUtils.java

@@ -86,6 +86,20 @@ public class NodeUtils {
         region.setBackground(new Background(fills.toArray(BackgroundFill[]::new)));
     }
 
+    public static void updateBackground(Region region, Paint fill, CornerRadii cornerRadii, Insets backgroundInsets) {
+        final Background background = region.getBackground();
+        if (background == null || background.getFills().isEmpty()) {
+            return;
+        }
+
+        final List<BackgroundFill> fills = new ArrayList<>();
+        for (BackgroundFill bf : background.getFills()) {
+            fills.add(new BackgroundFill(fill, cornerRadii, backgroundInsets));
+        }
+
+        region.setBackground(new Background(fills.toArray(BackgroundFill[]::new)));
+    }
+
     /**
      * Sets the background of the given region to the given color.
      */

+ 2 - 3
materialfx/src/main/java/module-info.java

@@ -12,15 +12,14 @@ module MaterialFX.materialfx.main {
     exports io.github.palexdev.materialfx.controls;
     exports io.github.palexdev.materialfx.controls.base;
     exports io.github.palexdev.materialfx.controls.cell;
-    exports io.github.palexdev.materialfx.controls.cell.tableview;
     exports io.github.palexdev.materialfx.controls.enums;
     exports io.github.palexdev.materialfx.controls.factories;
-    exports io.github.palexdev.materialfx.controls.tableview;
+    exports io.github.palexdev.materialfx.controls.legacy;
     exports io.github.palexdev.materialfx.effects;
     exports io.github.palexdev.materialfx.font;
     exports io.github.palexdev.materialfx.notifications;
     exports io.github.palexdev.materialfx.skins;
-    exports io.github.palexdev.materialfx.skins.tableview;
+    exports io.github.palexdev.materialfx.skins.legacy;
     exports io.github.palexdev.materialfx.utils;
     exports io.github.palexdev.materialfx.validation;
     exports io.github.palexdev.materialfx.validation.base;

+ 0 - 0
materialfx/src/main/resources/io/github/palexdev/materialfx/css/mfx-combobox.css → materialfx/src/main/resources/io/github/palexdev/materialfx/css/legacy/mfx-combobox.css


+ 0 - 0
materialfx/src/main/resources/io/github/palexdev/materialfx/css/tableview/mfx-tablerow.css → materialfx/src/main/resources/io/github/palexdev/materialfx/css/legacy/mfx-tablerow.css


+ 0 - 0
materialfx/src/main/resources/io/github/palexdev/materialfx/css/tableview/mfx-tableview.css → materialfx/src/main/resources/io/github/palexdev/materialfx/css/legacy/mfx-tableview.css


+ 65 - 0
materialfx/src/main/resources/io/github/palexdev/materialfx/css/mfx-combobox-style1.css

@@ -0,0 +1,65 @@
+/*
+ *     Copyright (C) 2021 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 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 General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with MaterialFX.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+@import "fonts.css";
+
+.mfx-combo-box {
+    -fx-background-color: rgb(232, 232, 232);
+    -fx-background-radius: 3 3 0 0;
+    -fx-border-color: transparent transparent rgb(159, 159, 159) transparent;
+    -fx-border-width: 1.5;
+}
+
+.mfx-combo-box:focused {
+    -fx-border-color: transparent transparent rgb(76, 0, 225) transparent;
+}
+
+.mfx-combo-box .label {
+    -fx-padding: 0 0 0 5;
+    -fx-font-family: "Open Sans Regular";
+    -fx-font-smoothing-type: grey;
+}
+
+.mfx-combo-box .mfx-icon-wrapper .ripple-generator {
+    -mfx-ripple-color: rgba(76, 0, 225, 0.3);
+}
+
+.mfx-combo-box .mfx-icon-wrapper .mfx-font-icon {
+    -mfx-color: black;
+}
+
+.mfx-combo-box:focused .mfx-icon-wrapper .mfx-font-icon {
+    -mfx-color: rgb(76, 0, 225);
+}
+
+.mfx-list-view {
+    -fx-background-color: white;
+    -fx-background-insets: 2;
+    -fx-background-radius: 2px;
+
+    -mfx-track-color: transparent;
+    -mfx-thumb-color: rgba(76, 0, 225, 0.1);
+    -mfx-thumb-hover-color: rgb(158, 107, 255);
+}
+
+.mfx-list-view .mfx-list-cell {
+    -mfx-hover-color: rgb(242, 242, 242);
+    -mfx-selected-color: rgb(185, 152, 255);
+
+    -mfx-corner-radius: 2px;
+}

+ 66 - 0
materialfx/src/main/resources/io/github/palexdev/materialfx/css/mfx-combobox-style2.css

@@ -0,0 +1,66 @@
+/*
+ *     Copyright (C) 2021 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 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 General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with MaterialFX.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+@import "fonts.css";
+
+.mfx-combo-box {
+    -fx-background-color: white;
+    -fx-background-radius: 2px;
+    -fx-border-color: rgb(159, 159, 159);
+    -fx-border-radius: 2px;
+    -fx-border-width: 1.2;
+}
+
+.mfx-combo-box:focused {
+    -fx-border-color: rgb(76, 0, 225);
+}
+
+.mfx-combo-box .label {
+    -fx-padding: 0 0 0 8;
+    -fx-font-family: "Open Sans Regular";
+    -fx-font-smoothing-type: grey;
+}
+
+.mfx-combo-box .mfx-icon-wrapper .ripple-generator {
+    -mfx-ripple-color: rgba(76, 0, 225, 0.3);
+}
+
+.mfx-combo-box .mfx-icon-wrapper .mfx-font-icon {
+    -mfx-color: black;
+}
+
+.mfx-combo-box:focused .mfx-icon-wrapper .mfx-font-icon {
+    -mfx-color: rgb(76, 0, 225);
+}
+
+.mfx-list-view {
+    -fx-background-color: white;
+    -fx-background-insets: 2;
+    -fx-background-radius: 2px;
+
+    -mfx-track-color: transparent;
+    -mfx-thumb-color: rgba(76, 0, 225, 0.1);
+    -mfx-thumb-hover-color: rgb(158, 107, 255);
+}
+
+.mfx-list-view .mfx-list-cell {
+    -mfx-hover-color: rgb(242, 242, 242);
+    -mfx-selected-color: rgb(185, 152, 255);
+
+    -mfx-corner-radius: 2px;
+}

BIN
materialfx/src/main/resources/io/github/palexdev/materialfx/fonts/materialfx-resources.ttf