Jelajahi Sumber

New control MFXContextMenu, various fixes and improvements

AbstractFlowlessListView: changed the setItems method because in some situations it's necessary to not clean the selection model (filter combo box for example) so this should be done manually.

AbstractMFXFlowlessListCell: change event handler to filter, this assures that the mouse event is not consumed eventually by other nodes that may be in the cell.

MFXFlowlessListViewSkin: modified the code of VirtualizedScrollPane to make the vBar and hBar protected. MFXVirtualizedScrollPane added methods to get the scroll bars and modified the layoutChildren strategy. The skin now is similar to MFXListViewSkin.

ListSelectionModel: oops, forgot to check if single selection mode.

MFXComboBox, MFXFilterComboBox, MFXComboBoxSkin, MFXFilterComboBoxSkin: switched to MFXFlowlessListView.

ComboSelectionModelMock: clear requested flag is not necessary anymore, update the index too when selecting an item.

MFXButton: fixed ripple generator properties throwing exception because bounded. This is kind of a revert to v11.5.5 but the ripple generator is added in the skin to fix the bug listed in commit 044e6d76.

MFXTableView: fixed behavior when changing items. Allow to change the cells fixed height.
MFXTableViewSkin: big news actually. The layout has been reviewed, when the items list is empty the rows container height is set to a minimum computed by computeRowsContainerHeight(), this also happens when there aren't enough rows to fill the container. The default width is now 600. The pagination controls are now aligned to the left to make them always visible even when the columns are resized. All columns by default have a context menu with the following actions: reset width, reset width of all columns, auto size, auto size all columns.
MFXTableColumnCell: added new custom pseudo class to track drag events.

MFXResourcesLoader: meh, it's more convenient like this.
Signed-off-by: PAlex404 <alessandro.parisi406@gmail.com>
PAlex404 4 tahun lalu
induk
melakukan
da9250f6f3
56 mengubah file dengan 809 tambahan dan 311 penghapusan
  1. 1 0
      demo/src/main/java/module-info.java
  2. 5 1
      materialfx/src/main/java/io/github/palexdev/materialfx/MFXResourcesLoader.java
  3. 68 0
      materialfx/src/main/java/io/github/palexdev/materialfx/beans/MFXContextMenuItem.java
  4. 21 21
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXButton.java
  5. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXCheckTreeItem.java
  6. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXCheckbox.java
  7. 6 5
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXComboBox.java
  8. 235 0
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXContextMenu.java
  9. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXDatePicker.java
  10. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXDialog.java
  11. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXFilterComboBox.java
  12. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXFlowlessCheckListView.java
  13. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXFlowlessListView.java
  14. 2 2
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXLabel.java
  15. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXListView.java
  16. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXProgressBar.java
  17. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXProgressSpinner.java
  18. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXRadioButton.java
  19. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXScrollPane.java
  20. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXTableRow.java
  21. 14 15
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXTableView.java
  22. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXTextField.java
  23. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXToggleButton.java
  24. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXToggleNode.java
  25. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXTreeItem.java
  26. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXTreeView.java
  27. 0 3
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/base/AbstractFlowlessListView.java
  28. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/base/AbstractMFXFlowlessListCell.java
  29. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/base/AbstractMFXNotificationPane.java
  30. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXCheckTreeCell.java
  31. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXFlowlessCheckListCell.java
  32. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXFlowlessListCell.java
  33. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXListCell.java
  34. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXSimpleTreeCell.java
  35. 36 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXTableColumnCell.java
  36. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXTableRowCell.java
  37. 31 42
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/flowless/MFXVirtualizedScrollPane.java
  38. 3 3
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/flowless/VirtualizedScrollPane.java
  39. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/legacy/MFXLegacyComboBox.java
  40. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/legacy/MFXLegacyTableRow.java
  41. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/legacy/MFXLegacyTableView.java
  42. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/filter/MFXFilterDialog.java
  43. 7 11
      materialfx/src/main/java/io/github/palexdev/materialfx/selection/ComboSelectionModelMock.java
  44. 9 3
      materialfx/src/main/java/io/github/palexdev/materialfx/selection/ListSelectionModel.java
  45. 8 19
      materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXButtonSkin.java
  46. 19 26
      materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXComboBoxSkin.java
  47. 1 1
      materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXDatePickerContent.java
  48. 50 45
      materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXFilterComboBoxSkin.java
  49. 70 51
      materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXFlowlessListViewSkin.java
  50. 0 1
      materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXScrollPaneSkin.java
  51. 124 8
      materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXTableViewSkin.java
  52. 6 10
      materialfx/src/main/java/io/github/palexdev/materialfx/utils/LabelUtils.java
  53. 10 6
      materialfx/src/main/resources/io/github/palexdev/materialfx/css/mfx-combobox-style1.css
  54. 10 6
      materialfx/src/main/resources/io/github/palexdev/materialfx/css/mfx-combobox-style2.css
  55. 41 0
      materialfx/src/main/resources/io/github/palexdev/materialfx/css/mfx-contextmenu.css
  56. 2 1
      materialfx/src/main/resources/io/github/palexdev/materialfx/css/mfx-table-column-cell.css

+ 1 - 0
demo/src/main/java/module-info.java

@@ -7,6 +7,7 @@ module MaterialFX.demo.main {
 
     requires fr.brouillard.oss.cssfx;
     requires org.kordamp.ikonli.javafx;
+    requires org.scenicview.scenicview;
 
     opens io.github.palexdev.materialfx.demo;
     opens io.github.palexdev.materialfx.demo.controllers;

+ 5 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/MFXResourcesLoader.java

@@ -29,10 +29,14 @@ public class MFXResourcesLoader {
 
     private MFXResourcesLoader() {}
 
-    public static URL load(String path) {
+    public static URL loadURL(String path) {
         return MFXResourcesLoader.class.getResource(path);
     }
 
+    public static String load(String path) {
+        return loadURL(path).toString();
+    }
+
     public static InputStream loadStream(String name) {
         return MFXResourcesLoader.class.getResourceAsStream(name);
     }

+ 68 - 0
materialfx/src/main/java/io/github/palexdev/materialfx/beans/MFXContextMenuItem.java

@@ -0,0 +1,68 @@
+/*
+ *     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.beans;
+
+import javafx.event.EventHandler;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.input.MouseEvent;
+
+public class MFXContextMenuItem {
+    private final Node node;
+    private EventHandler<MouseEvent> action;
+
+    public MFXContextMenuItem(Node node) {
+        this.node = node;
+    }
+
+    public MFXContextMenuItem(Node node, EventHandler<MouseEvent> action) {
+        this.node = node;
+        this.action = action;
+        node.addEventHandler(MouseEvent.MOUSE_PRESSED, action);
+    }
+
+    public MFXContextMenuItem(String text) {
+        Label label = new Label(text);
+        label.setMaxWidth(Double.MAX_VALUE);
+        node = new Label(text);
+    }
+
+    public MFXContextMenuItem(String text, EventHandler<MouseEvent> action) {
+        Label label = new Label(text);
+        label.setMaxWidth(Double.MAX_VALUE);
+        node = label;
+        node.addEventHandler(MouseEvent.MOUSE_PRESSED, action);
+    }
+
+    public Node getNode() {
+        return node;
+    }
+
+    public EventHandler<MouseEvent> getAction() {
+        return action;
+    }
+
+    public void setAction(EventHandler<MouseEvent> action) {
+        if (this.action != null) {
+            node.removeEventHandler(MouseEvent.MOUSE_PRESSED, this.action);
+        }
+        node.addEventHandler(MouseEvent.MOUSE_PRESSED, action);
+        this.action = action;
+    }
+}

+ 21 - 21
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXButton.java

@@ -50,7 +50,8 @@ public class MFXButton extends Button {
     //================================================================================
     private static final StyleablePropertyFactory<MFXButton> FACTORY = new StyleablePropertyFactory<>(Button.getClassCssMetaData());
     private final String STYLE_CLASS = "mfx-button";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-button.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-button.css");
+    private final RippleGenerator rippleGenerator = new RippleGenerator(this);
 
     //================================================================================
     // Constructors
@@ -82,6 +83,13 @@ public class MFXButton extends Button {
     private void initialize() {
         getStyleClass().add(STYLE_CLASS);
         setAlignment(Pos.CENTER);
+
+        setRippleRadius(25);
+        setRippleColor(Color.rgb(190, 190, 190));
+    }
+
+    public RippleGenerator getRippleGenerator() {
+        return this.rippleGenerator;
     }
 
     //================================================================================
@@ -93,37 +101,37 @@ public class MFXButton extends Button {
      *
      * @see Color
      */
-    private final ObjectProperty<Paint> rippleColor = new SimpleObjectProperty<>(Color.rgb(190, 190, 190));
+    private final ObjectProperty<Paint> rippleColor = new SimpleObjectProperty<>();
 
     /**
      * Specifies the ripples radius of this control.
      */
-    private final DoubleProperty rippleRadius = new SimpleDoubleProperty(25);
+    private final DoubleProperty rippleRadius = new SimpleDoubleProperty();
 
     /**
      * Specifies the ripples in animation duration of this control.
      *
      * @see Duration
      */
-    private final ObjectProperty<Duration> rippleInDuration = new SimpleObjectProperty<>(Duration.millis(700));
+    private final ObjectProperty<Duration> rippleInDuration = new SimpleObjectProperty<>();
 
     /**
      * Specifies the ripples out animation duration of this control.
      *
      * @see Duration
      */
-    private final ObjectProperty<Duration> rippleOutDuration = new SimpleObjectProperty<>(getRippleInDuration().divide(2.0));
+    private final ObjectProperty<Duration> rippleOutDuration = new SimpleObjectProperty<>();
 
-    public Paint getRippleColor() {
+    public final Paint getRippleColor() {
         return rippleColor.get();
     }
 
-    public ObjectProperty<Paint> rippleColorProperty() {
-        return rippleColor;
+    public final ObjectProperty<Paint> rippleColorProperty() {
+        return this.rippleColor;
     }
 
-    public void setRippleColor(Paint rippleColor) {
-        this.rippleColor.set(rippleColor);
+    public final void setRippleColor(Paint rippleColor) {
+        rippleGenerator.setRippleColor(rippleColor);
     }
 
     public double getRippleRadius() {
@@ -135,7 +143,7 @@ public class MFXButton extends Button {
     }
 
     public void setRippleRadius(double rippleRadius) {
-        this.rippleRadius.set(rippleRadius);
+        rippleGenerator.setRippleRadius(rippleRadius);
     }
 
     public Duration getRippleInDuration() {
@@ -147,7 +155,7 @@ public class MFXButton extends Button {
     }
 
     public void setRippleInDuration(Duration rippleInDuration) {
-        this.rippleInDuration.set(rippleInDuration);
+        rippleGenerator.setInDuration(rippleInDuration);
     }
 
     public Duration getRippleOutDuration() {
@@ -159,15 +167,7 @@ public class MFXButton extends Button {
     }
 
     public void setRippleOutDuration(Duration rippleOutDuration) {
-        this.rippleOutDuration.set(rippleOutDuration);
-    }
-
-    public RippleGenerator getRippleGenerator() {
-        try {
-            return (RippleGenerator) lookup(".ripple-generator");
-        } catch (ClassCastException ex) {
-            return null;
-        }
+        rippleGenerator.setOutDuration(rippleOutDuration);
     }
 
     //================================================================================

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXCheckTreeItem.java

@@ -48,7 +48,7 @@ public class MFXCheckTreeItem<T> extends MFXTreeItem<T> {
     // Properties
     //================================================================================
     private final String STYLE_CLASS = "mfx-check-tree-item";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-treeitem.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-treeitem.css");
 
     private final BooleanProperty checked = new SimpleBooleanProperty(false);
     private final BooleanProperty indeterminate = new SimpleBooleanProperty(false);

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXCheckbox.java

@@ -40,7 +40,7 @@ public class MFXCheckbox extends CheckBox {
     //================================================================================
     private static final StyleablePropertyFactory<MFXCheckbox> FACTORY = new StyleablePropertyFactory<>(CheckBox.getClassCssMetaData());
     private final String STYLE_CLASS = "mfx-checkbox";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-checkbox.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-checkbox.css");
 
     //================================================================================
     // Constructors

+ 6 - 5
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXComboBox.java

@@ -58,10 +58,10 @@ public class MFXComboBox<T> extends Control {
     private final ObjectProperty<T> selectedValue = new SimpleObjectProperty<>();
     private final ObjectProperty<ObservableList<T>> items = new SimpleObjectProperty<>();
 
-    private final DoubleProperty maxPopupWidth = new SimpleDoubleProperty(150);
-    private final DoubleProperty maxPopupHeight = new SimpleDoubleProperty(200);
+    private final DoubleProperty maxPopupWidth = new SimpleDoubleProperty();
+    private final DoubleProperty maxPopupHeight = new SimpleDoubleProperty(190);
     private final DoubleProperty popupXOffset = new SimpleDoubleProperty(0);
-    private final DoubleProperty popupYOffset = new SimpleDoubleProperty(1);
+    private final DoubleProperty popupYOffset = new SimpleDoubleProperty(2);
 
     private final ComboSelectionModelMock<T> mockSelection;
 
@@ -73,7 +73,7 @@ public class MFXComboBox<T> extends Control {
     }
 
     public MFXComboBox(ObservableList<T> items) {
-        this.STYLESHEET = MFXResourcesLoader.load(getComboStyle().getStyleSheetPath()).toString();
+        this.STYLESHEET = MFXResourcesLoader.load(getComboStyle().getStyleSheetPath());
         this.items.set(items);
         this.mockSelection = new ComboSelectionModelMock<>(this);
 
@@ -92,10 +92,11 @@ public class MFXComboBox<T> extends Control {
          */
         comboStyle.addListener((observable, oldValue, newValue) -> {
             if (newValue != null && newValue != oldValue) {
-                STYLESHEET = MFXResourcesLoader.load(newValue.getStyleSheetPath()).toString();
+                STYLESHEET = MFXResourcesLoader.load(newValue.getStyleSheetPath());
                 getStylesheets().setAll(STYLESHEET);
             }
         });
+        maxPopupWidthProperty().bind(widthProperty());
     }
 
     public T getSelectedValue() {

+ 235 - 0
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXContextMenu.java

@@ -0,0 +1,235 @@
+/*
+ *     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;
+
+import io.github.palexdev.materialfx.MFXResourcesLoader;
+import io.github.palexdev.materialfx.beans.MFXContextMenuItem;
+import javafx.beans.value.ChangeListener;
+import javafx.event.EventHandler;
+import javafx.event.EventType;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.scene.control.PopupControl;
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.input.MouseButton;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.VBox;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Line;
+import javafx.stage.Window;
+import javafx.stage.WindowEvent;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MFXContextMenu extends VBox {
+    private final String STYLE_CLASS = "mfx-context-menu";
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-contextmenu.css");
+
+    private final List<Line> separators = new ArrayList<>();
+
+    private WeakReference<Node> nodeReference;
+    private final PopupControl popupControl;
+    private final ChangeListener<Scene> sceneListener;
+    private final ChangeListener<Boolean> windowFocusListener;
+    private final EventHandler<MouseEvent> sceneHandler;
+    private final EventHandler<MouseEvent> openHandler;
+    private final EventHandler<KeyEvent> keyHandler;
+
+    public MFXContextMenu() {
+        this(0);
+    }
+
+    public MFXContextMenu(double spacing) {
+        super(spacing);
+        setAlignment(Pos.TOP_CENTER);
+        getStyleClass().setAll(STYLE_CLASS);
+        popupControl = new PopupControl();
+        popupControl.getScene().setFill(Color.WHITE);
+        popupControl.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> hide());
+
+        sceneHandler = event -> {
+            if (popupControl.isShowing()) {
+                hide();
+            }
+        };
+        sceneListener = (observable, oldValue, newValue) -> {
+            if (oldValue != null) {
+                oldValue.removeEventFilter(MouseEvent.MOUSE_PRESSED, sceneHandler);
+            }
+            if (newValue != null) {
+                newValue.addEventFilter(MouseEvent.MOUSE_PRESSED, sceneHandler);
+            }
+        };
+        windowFocusListener = (observable, oldValue, newValue) -> {
+            if (!newValue && popupControl.isShowing()) {
+                hide();
+            }
+        };
+        openHandler = event -> {
+            if (event.getButton() == MouseButton.SECONDARY) {
+                popupControl.getScene().setRoot(MFXContextMenu.this);
+                show(nodeReference.get(), event.getScreenX(), event.getScreenY());
+            }
+        };
+        keyHandler = event -> {
+            if (event.getEventType() != KeyEvent.KEY_PRESSED) return;
+
+            final KeyCode code = event.getCode();
+            if (code == KeyCode.ENTER || code == KeyCode.SPACE) {
+                hide();
+            }
+        };
+    }
+
+    public void install(Node node) {
+        node.sceneProperty().addListener((observable, oldValue, newValue) -> {
+            if (newValue != null) {
+                newValue.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> hide());
+                Window window = newValue.getWindow();
+                if (window != null) {
+                    window.focusedProperty().addListener(windowFocusListener);
+                }
+            }
+        });
+        Node nR = nodeReference != null ? nodeReference.get() : null;
+        if (nR != null && nR == node) {
+            return;
+        }
+
+        dispose();
+        nodeReference = new WeakReference<>(node);
+        installBehavior();
+    }
+
+    private void installBehavior() {
+        if (nodeReference != null) {
+            Node node = nodeReference.get();
+            if (node != null) {
+                node.sceneProperty().addListener(sceneListener);
+                node.addEventHandler(MouseEvent.MOUSE_PRESSED, openHandler);
+                node.addEventHandler(KeyEvent.KEY_PRESSED, keyHandler);
+            }
+        }
+    }
+
+    private void dispose() {
+        if (nodeReference != null) {
+            Node node = nodeReference.get();
+            if (node != null) {
+                node.sceneProperty().removeListener(sceneListener);
+                node.getScene().removeEventFilter(MouseEvent.MOUSE_PRESSED, sceneHandler);
+                Window window = node.getScene().getWindow();
+                if (window != null) {
+                    window.focusedProperty().removeListener(windowFocusListener);
+                }
+                node.removeEventHandler(MouseEvent.MOUSE_PRESSED, openHandler);
+                node.removeEventHandler(KeyEvent.KEY_PRESSED, keyHandler);
+            }
+        }
+    }
+
+    void addSeparator(Line line) {
+        separators.add(line);
+    }
+
+    @Override
+    public String getUserAgentStylesheet() {
+        return STYLESHEET;
+    }
+
+    @Override
+    protected void layoutChildren() {
+        super.layoutChildren();
+
+        separators.forEach(line -> {
+            line.setStartX(0);
+            line.setEndX(getWidth() - (snappedRightInset() + snappedLeftInset()));
+        });
+    }
+
+    public void show(Node ownerNode, double anchorX, double anchorY) {
+        popupControl.show(ownerNode, anchorX, anchorY);
+    }
+
+    public void hide() {
+        popupControl.hide();
+    }
+
+    public void addPopupEventHandler(EventType<WindowEvent> eventType, EventHandler<WindowEvent> eventHandler) {
+        popupControl.addEventHandler(eventType, eventHandler);
+    }
+
+    public void addPopupEventFilter(EventType<WindowEvent> eventType, EventHandler<WindowEvent> eventHandler) {
+        popupControl.addEventFilter(eventType, eventHandler);
+    }
+
+    public void removePopupEventHandler(EventType<WindowEvent> eventType, EventHandler<WindowEvent> eventHandler) {
+        popupControl.removeEventHandler(eventType, eventHandler);
+    }
+
+    public void removePopupEventFilter(EventType<WindowEvent> eventType, EventHandler<WindowEvent> eventHandler) {
+        popupControl.removeEventFilter(eventType, eventHandler);
+    }
+
+    public static class Builder {
+        private final MFXContextMenu contextMenu;
+
+        public Builder() {
+            contextMenu = new MFXContextMenu();
+        }
+
+        public Builder(double spacing) {
+            contextMenu = new MFXContextMenu(spacing);
+        }
+
+        public Builder addMenuItem(Node node, EventHandler<MouseEvent> action) {
+            node.addEventHandler(MouseEvent.MOUSE_PRESSED, action);
+            contextMenu.getChildren().add(node);
+            return this;
+        }
+
+        public Builder addMenuItem(MFXContextMenuItem item) {
+            contextMenu.getChildren().add(item.getNode());
+            return this;
+        }
+
+        public Builder addSeparator() {
+            Line separator = new Line();
+            separator.getStyleClass().add("separator");
+            VBox.setMargin(separator, new Insets(4, 0, 3, 0));
+            contextMenu.addSeparator(separator);
+            contextMenu.getChildren().add(separator);
+            return this;
+        }
+
+        public MFXContextMenu get() {
+            return contextMenu;
+        }
+
+        public MFXContextMenu install(Node node) {
+            contextMenu.install(node);
+            return contextMenu;
+        }
+    }
+}

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXDatePicker.java

@@ -67,7 +67,7 @@ public class MFXDatePicker extends VBox {
     //================================================================================
     private static final StyleablePropertyFactory<MFXDatePicker> FACTORY = new StyleablePropertyFactory<>(VBox.getClassCssMetaData());
     private final String STYLE_CLASS = "mfx-date-picker";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-datepicker.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-datepicker.css");
 
     private final DatePicker datePicker;
     private final ObjectProperty<DateTimeFormatter> dateFormatter = new SimpleObjectProperty<>(DateTimeFormatter.ofPattern("dd/M/yyyy"));

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXDialog.java

@@ -42,7 +42,7 @@ public class MFXDialog extends AbstractMFXDialog {
     // Properties
     //================================================================================
     private final String STYLE_CLASS = "mfx-dialog";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-dialog.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-dialog.css");
 
     //================================================================================
     // Constructors

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXFilterComboBox.java

@@ -18,7 +18,7 @@ public class MFXFilterComboBox<T> extends MFXComboBox<T> {
     // Properties
     //================================================================================
     private final String STYLE_CLASS = "mfx-filter-combo-box";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-filter-combobox.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-filter-combobox.css");
 
     /**
      * When the popup is shown and the text field is added to the scene the text field is not focused,

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXFlowlessCheckListView.java

@@ -30,7 +30,7 @@ import javafx.scene.control.Skin;
 
 public class MFXFlowlessCheckListView<T> extends AbstractFlowlessListView<T, MFXFlowlessCheckListCell<T>, IListCheckModel<T>> {
     private final String STYLE_CLASS = "mfx-check-list-view";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-flowless-check-listview.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-flowless-check-listview.css");
 
     public MFXFlowlessCheckListView() {
         this(FXCollections.observableArrayList());

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXFlowlessListView.java

@@ -31,7 +31,7 @@ import javafx.scene.control.Skin;
 
 public class MFXFlowlessListView<T> extends AbstractFlowlessListView<T, AbstractMFXFlowlessListCell<T>, IListSelectionModel<T>> {
     private final String STYLE_CLASS = "mfx-list-view";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-flowless-listview.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-flowless-listview.css");
 
     public MFXFlowlessListView() {
         this(FXCollections.observableArrayList());

+ 2 - 2
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXLabel.java

@@ -73,7 +73,7 @@ public class MFXLabel extends Control {
 
     public MFXLabel(String text) {
         setText(text);
-        this.STYLESHEET = MFXResourcesLoader.load(getLabelStyle().getStyleSheetPath()).toString();
+        this.STYLESHEET = MFXResourcesLoader.load(getLabelStyle().getStyleSheetPath());
         initialize();
     }
 
@@ -89,7 +89,7 @@ public class MFXLabel extends Control {
          */
         labelStyle.addListener((observable, oldValue, newValue) -> {
             if (newValue != null && newValue != oldValue) {
-                STYLESHEET = MFXResourcesLoader.load(newValue.getStyleSheetPath()).toString();
+                STYLESHEET = MFXResourcesLoader.load(newValue.getStyleSheetPath());
                 getStylesheets().setAll(STYLESHEET);
             }
         });

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXListView.java

@@ -47,7 +47,7 @@ public class MFXListView<T> extends ListView<T> {
     //================================================================================
     private static final StyleablePropertyFactory<MFXListView<?>> FACTORY = new StyleablePropertyFactory<>(ListView.getClassCssMetaData());
     private final String STYLE_CLASS = "mfx-legacy-list-view";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-listview.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-listview.css");
 
     //================================================================================
     // Constructors

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXProgressBar.java

@@ -33,7 +33,7 @@ public class MFXProgressBar extends ProgressBar {
     // Properties
     //================================================================================
     private final String STYLE_CLASS = "mfx-progress-bar";
-    private final String STYLESHEETS = MFXResourcesLoader.load("css/mfx-progressbar.css").toString();
+    private final String STYLESHEETS = MFXResourcesLoader.load("css/mfx-progressbar.css");
 
     //================================================================================
     // Constructors

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXProgressSpinner.java

@@ -38,7 +38,7 @@ public class MFXProgressSpinner extends ProgressIndicator {
     //================================================================================
     private static final StyleablePropertyFactory<MFXProgressSpinner> FACTORY = new StyleablePropertyFactory<>(ProgressIndicator.getClassCssMetaData());
     private final String STYLE_CLASS = "mfx-progress-spinner";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-progress-spinner.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-progress-spinner.css");
 
     //================================================================================
     // Constructors

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXRadioButton.java

@@ -40,7 +40,7 @@ public class MFXRadioButton extends RadioButton {
     //================================================================================
     private static final StyleablePropertyFactory<MFXRadioButton> FACTORY = new StyleablePropertyFactory<>(RadioButton.getClassCssMetaData());
     private final String STYLE_CLASS = "mfx-radio-button";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-radiobutton.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-radiobutton.css");
 
     //================================================================================
     // Constructors

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXScrollPane.java

@@ -50,7 +50,7 @@ public class MFXScrollPane extends ScrollPane {
     // Properties
     //================================================================================
     private final String STYLE_CLASS = "mfx-scroll-pane";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-scrollpane.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-scrollpane.css");
 
     //================================================================================
     // Constructors

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXTableRow.java

@@ -37,7 +37,7 @@ public class MFXTableRow<T> extends HBox {
     // Properties
     //================================================================================
     private final String STYLE_CLASS = "mfx-table-row";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-table-row.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-table-row.css");
     private final T item;
 
     private static final PseudoClass SELECTED_PSEUDO_CLASS = PseudoClass.getPseudoClass("selected");

+ 14 - 15
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXTableView.java

@@ -23,10 +23,7 @@ import io.github.palexdev.materialfx.controls.cell.MFXTableColumnCell;
 import io.github.palexdev.materialfx.selection.TableSelectionModel;
 import io.github.palexdev.materialfx.selection.base.ITableSelectionModel;
 import io.github.palexdev.materialfx.skins.MFXTableViewSkin;
-import javafx.beans.property.IntegerProperty;
-import javafx.beans.property.ObjectProperty;
-import javafx.beans.property.SimpleIntegerProperty;
-import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.property.*;
 import javafx.collections.FXCollections;
 import javafx.collections.ObservableList;
 import javafx.event.Event;
@@ -46,30 +43,28 @@ public class MFXTableView<T> extends Control {
     // Properties
     //================================================================================
     private final String STYLE_CLASS = "mfx-table-view";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-tableview.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-tableview.css");
 
-    private final ObjectProperty<ObservableList<T>> items = new SimpleObjectProperty<>(FXCollections.observableArrayList());
+    private final ObservableList<T> items = FXCollections.observableArrayList();
     private final ObjectProperty<ITableSelectionModel<T>> selectionModel = new SimpleObjectProperty<>(null);
 
     private final ObservableList<MFXTableColumnCell<T>> columns = FXCollections.observableArrayList();
     private final IntegerProperty maxRows = new SimpleIntegerProperty(10);
     private final IntegerProperty maxRowsCombo = new SimpleIntegerProperty(20);
-    private final double fixedRowsHeight;
+    private final DoubleProperty fixedRowsHeight = new SimpleDoubleProperty(27);
 
     //================================================================================
     // Constructors
     //================================================================================
     public MFXTableView() {
         installSelectionModel();
-
-        fixedRowsHeight = 27;
         initialize();
     }
 
     public MFXTableView(double fixedRowsHeight) {
         installSelectionModel();
 
-        this.fixedRowsHeight = fixedRowsHeight;
+        setFixedRowsHeight(fixedRowsHeight);
         initialize();
     }
 
@@ -90,15 +85,11 @@ public class MFXTableView<T> extends Control {
     }
 
     public ObservableList<T> getItems() {
-        return items.get();
-    }
-
-    public ObjectProperty<ObservableList<T>> itemsProperty() {
         return items;
     }
 
     public void setItems(ObservableList<T> items) {
-        this.items.set(items);
+        this.items.setAll(items);
     }
 
     public ITableSelectionModel<T> getSelectionModel() {
@@ -144,9 +135,17 @@ public class MFXTableView<T> extends Control {
     }
 
     public double getFixedRowsHeight() {
+        return fixedRowsHeight.get();
+    }
+
+    public DoubleProperty fixedRowsHeightProperty() {
         return fixedRowsHeight;
     }
 
+    public void setFixedRowsHeight(double fixedRowsHeight) {
+        this.fixedRowsHeight.set(fixedRowsHeight);
+    }
+
     //================================================================================
     // Override Methods
     //================================================================================

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

@@ -43,7 +43,7 @@ public class MFXTextField extends TextField {
     //================================================================================
     private static final StyleablePropertyFactory<MFXTextField> FACTORY = new StyleablePropertyFactory<>(TextField.getClassCssMetaData());
     private final String STYLE_CLASS = "mfx-text-field";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-textfield.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-textfield.css");
 
     private MFXDialogValidator validator;
 

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXToggleButton.java

@@ -43,7 +43,7 @@ public class MFXToggleButton extends ToggleButton {
     //================================================================================
     private static final StyleablePropertyFactory<MFXToggleButton> FACTORY = new StyleablePropertyFactory<>(ToggleButton.getClassCssMetaData());
     private final String STYLE_CLASS = "mfx-toggle-button";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-togglebutton.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-togglebutton.css");
 
     //================================================================================
     // Constructors

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXToggleNode.java

@@ -52,7 +52,7 @@ public class MFXToggleNode extends ToggleButton {
     //================================================================================
     private static final StyleablePropertyFactory<MFXToggleNode> FACTORY = new StyleablePropertyFactory<>(ToggleButton.getClassCssMetaData());
     private final String STYLECLASS = "mfx-toggle-node";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-togglenode.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-togglenode.css");
     protected final RippleGenerator rippleGenerator = new RippleGenerator(this);
 
     //================================================================================

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXTreeItem.java

@@ -60,7 +60,7 @@ public class MFXTreeItem<T> extends AbstractMFXTreeItem<T> {
     //================================================================================
     private static final StyleablePropertyFactory<MFXTreeItem<?>> FACTORY = new StyleablePropertyFactory<>(MFXTreeItem.getClassCssMetaData());
     private final String STYLE_CLASS = "mfx-tree-item";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-treeitem.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-treeitem.css");
 
     private final BooleanProperty expanded = new SimpleBooleanProperty(false);
     private final ReadOnlyBooleanWrapper animationRunning = new ReadOnlyBooleanWrapper(false);

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXTreeView.java

@@ -43,7 +43,7 @@ public class MFXTreeView<T> extends MFXScrollPane {
     // Properties
     //================================================================================
     private final String STYLE_CLASS = "mfx-tree-view";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-treeview.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-treeview.css");
 
     private final ObjectProperty<AbstractMFXTreeItem<T>> root = new SimpleObjectProperty<>(null);
     private final ObjectProperty<ITreeSelectionModel<T>> selectionModel = new SimpleObjectProperty<>(null);

+ 0 - 3
materialfx/src/main/java/io/github/palexdev/materialfx/controls/base/AbstractFlowlessListView.java

@@ -102,9 +102,6 @@ public abstract class AbstractFlowlessListView<T, C extends AbstractMFXFlowlessL
 
     @Override
     public void setItems(ObservableList<T> items) {
-        if (getSelectionModel() != null) {
-            getSelectionModel().clearSelection();
-        }
         getItems().setAll(items);
     }
 

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/base/AbstractMFXFlowlessListCell.java

@@ -75,7 +75,7 @@ public abstract class AbstractMFXFlowlessListCell<T> extends HBox implements Cel
             }
         });
 
-        addEventHandler(MouseEvent.MOUSE_PRESSED, this::updateModel);
+        addEventFilter(MouseEvent.MOUSE_PRESSED, this::updateModel);
         listView.getSelectionModel().selectedItemsProperty().addListener((InvalidationListener) invalidated -> {
             if (!containsEqualsBoth() && isSelected()) {
                 setSelected(false);

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/base/AbstractMFXNotificationPane.java

@@ -33,7 +33,7 @@ public abstract class AbstractMFXNotificationPane extends VBox {
     // Properties
     //================================================================================
     protected final String STYLE_CLASS = "mfx-notification";
-    protected final String STYLESHEET = MFXResourcesLoader.load("css/mfx-notification.css").toString();
+    protected final String STYLESHEET = MFXResourcesLoader.load("css/mfx-notification.css");
 
     protected final StringProperty headerProperty = new SimpleStringProperty("");
     protected final StringProperty titleProperty = new SimpleStringProperty("");

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXCheckTreeCell.java

@@ -35,7 +35,7 @@ public class MFXCheckTreeCell<T> extends MFXSimpleTreeCell<T> {
     // Properties
     //================================================================================
     private final String STYLE_CLASS = "mfx-check-tree-cell";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-checktreecell.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-checktreecell.css");
     private final MFXCheckbox checkbox;
 
     private static final PseudoClass CHECKED_PSEUDO_CLASS = PseudoClass.getPseudoClass("checked");

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXFlowlessCheckListCell.java

@@ -35,7 +35,7 @@ import javafx.util.Duration;
 
 public class MFXFlowlessCheckListCell<T> extends AbstractMFXFlowlessListCell<T> {
     private final String STYLE_CLASS = "mfx-check-list-cell";
-    private final String STYLESHHET = MFXResourcesLoader.load("css/mfx-flowless-check-listcell.css").toString();
+    private final String STYLESHHET = MFXResourcesLoader.load("css/mfx-flowless-check-listcell.css");
     protected final RippleGenerator rippleGenerator = new RippleGenerator(this);
 
     protected final MFXCheckbox checkbox;

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXFlowlessListCell.java

@@ -29,7 +29,7 @@ import javafx.util.Duration;
 
 public class MFXFlowlessListCell<T> extends AbstractMFXFlowlessListCell<T> {
     private final String STYLE_CLASS = "mfx-list-cell";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-flowless-listcell.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-flowless-listcell.css");
     protected final RippleGenerator rippleGenerator = new RippleGenerator(this);
 
     public MFXFlowlessListCell(AbstractFlowlessListView<T, ?, ?> listView, T  data) {

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

@@ -48,7 +48,7 @@ public class MFXListCell<T> extends ListCell<T> {
     //================================================================================
     private static final StyleablePropertyFactory<MFXListCell<?>> FACTORY = new StyleablePropertyFactory<>(ListCell.getClassCssMetaData());
     private final String STYLE_CLASS = "mfx-list-cell";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-listcell.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-listcell.css");
     private final RippleGenerator rippleGenerator;
 
     //================================================================================

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXSimpleTreeCell.java

@@ -42,7 +42,7 @@ public class MFXSimpleTreeCell<T> extends AbstractMFXTreeCell<T> {
     // Properties
     //================================================================================
     private final String STYLE_CLASS = "mfx-tree-cell";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-treecell.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-treecell.css");
 
     //================================================================================
     // Constructors

+ 36 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXTableColumnCell.java

@@ -22,8 +22,12 @@ import io.github.palexdev.materialfx.MFXResourcesLoader;
 import io.github.palexdev.materialfx.controls.enums.SortState;
 import io.github.palexdev.materialfx.skins.MFXTableColumnCellSkin;
 import javafx.beans.property.*;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.css.PseudoClass;
 import javafx.scene.control.Label;
 import javafx.scene.control.Skin;
+import javafx.scene.input.MouseEvent;
 import javafx.util.Callback;
 
 import java.util.Comparator;
@@ -41,7 +45,9 @@ public class MFXTableColumnCell<T> extends Label {
     // Properties
     //================================================================================
     private final String STYLE_CLASS = "mfx-table-column-cell";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-table-column-cell.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-table-column-cell.css");
+
+    private final ReadOnlyDoubleWrapper initialWidth = new ReadOnlyDoubleWrapper();
 
     private final ObjectProperty<Callback<T, ? extends MFXTableRowCell>> rowCellFactory = new SimpleObjectProperty<>();
     private final StringProperty columnName = new SimpleStringProperty("");
@@ -51,6 +57,9 @@ public class MFXTableColumnCell<T> extends Label {
     private SortState sortState = SortState.UNSORTED;
     private Comparator<T> comparator;
 
+    private static final PseudoClass DRAG_PSEUDO_CLASS = PseudoClass.getPseudoClass("dragged");
+    private final BooleanProperty dragged = new SimpleBooleanProperty(false);
+
     //================================================================================
     // Constructors
     //================================================================================
@@ -73,6 +82,32 @@ public class MFXTableColumnCell<T> extends Label {
     private void initialize() {
         getStyleClass().add(STYLE_CLASS);
         setTooltipText(getColumnName());
+
+        dragged.addListener(invalidate -> pseudoClassStateChanged(DRAG_PSEUDO_CLASS, dragged.get()));
+        addEventFilter(MouseEvent.MOUSE_DRAGGED, event -> dragged.set(true));
+        addEventFilter(MouseEvent.MOUSE_RELEASED, event -> dragged.set(false));
+
+        widthProperty().addListener(new ChangeListener<>() {
+            @Override
+            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
+                if (newValue != null && newValue.doubleValue() > 0) {
+                    setInitialWidth(newValue.doubleValue());
+                    widthProperty().removeListener(this);
+                }
+            }
+        });
+    }
+
+    public double getInitialWidth() {
+        return initialWidth.get();
+    }
+
+    public ReadOnlyDoubleProperty initialWidthProperty() {
+        return initialWidth.getReadOnlyProperty();
+    }
+
+    protected void setInitialWidth(double initialWidth) {
+        this.initialWidth.set(initialWidth);
     }
 
     public Callback<T, ? extends MFXTableRowCell> getRowCellFactory() {

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXTableRowCell.java

@@ -50,7 +50,7 @@ public class MFXTableRowCell extends Label {
     // Properties
     //================================================================================
     private final String STYLE_CLASS = "mfx-table-row-cell";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-table-row-cell.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-table-row-cell.css");
 
     //================================================================================
     // Constructors

+ 31 - 42
materialfx/src/main/java/io/github/palexdev/materialfx/controls/flowless/MFXVirtualizedScrollPane.java

@@ -19,21 +19,10 @@
 package io.github.palexdev.materialfx.controls.flowless;
 
 
-import io.github.palexdev.materialfx.utils.NodeUtils;
-import javafx.beans.value.ChangeListener;
-import javafx.beans.value.ObservableValue;
+import javafx.geometry.Insets;
 import javafx.scene.Node;
-import javafx.scene.Scene;
 import javafx.scene.control.ScrollBar;
 import javafx.scene.control.ScrollPane;
-import javafx.scene.layout.BackgroundFill;
-import javafx.scene.paint.Color;
-import javafx.scene.paint.Paint;
-
-import java.lang.reflect.Field;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
 
 public class MFXVirtualizedScrollPane<V extends Node & Virtualized> extends VirtualizedScrollPane<V> {
 
@@ -48,40 +37,40 @@ public class MFXVirtualizedScrollPane<V extends Node & Virtualized> extends Virt
     }
 
     private void initialize() {
-        sceneProperty().addListener(new ChangeListener<>() {
-            @Override
-            public void changed(ObservableValue<? extends Scene> observable, Scene oldValue, Scene newValue) {
-                Set<ScrollBar> scrollBars = lookupAll(".scroll-bar").stream()
-                        .filter(node -> node instanceof ScrollBar)
-                        .map(node -> (ScrollBar) node)
-                        .collect(Collectors.toSet());
-                for (ScrollBar scrollBar : scrollBars) {
-                    manageScrollBarBackground(scrollBar);
-                    NodeUtils.updateBackground(scrollBar, Color.WHITE);
-                }
-                sceneProperty().removeListener(this);
-            }
-        });
+        setPadding(new Insets(0, 5, 0, 0));
+    }
 
-        try {
-            Field vbarField = VirtualizedScrollPane.class.getDeclaredField("vBar");
-            vbarField.setAccessible(true);
-            ScrollBar scrollBar = (ScrollBar) vbarField.get(this);
-            scrollBar.getStyleClass().add("vvbar");
-        } catch (Exception ignored) {
-        }
+    public ScrollBar getVBar() {
+        return vBar;
     }
 
-    private void manageScrollBarBackground(ScrollBar scrollBar) {
-        scrollBar.backgroundProperty().addListener((observable, oldValue, newValue) -> {
-            if (newValue != null && !containsFill(newValue.getFills(), Color.WHITE)) {
-                NodeUtils.updateBackground(scrollBar, Color.WHITE);
-            }
-        });
+    public ScrollBar getHBar() {
+        return hBar;
     }
 
-    private boolean containsFill(List<BackgroundFill> backgroundFills, Color fill) {
-        List<Paint> paints = backgroundFills.stream().map(BackgroundFill::getFill).collect(Collectors.toList());
-        return paints.contains(fill);
+    @Override
+    protected void layoutChildren() {
+        double layoutWidth = snapSizeX(getLayoutBounds().getWidth());
+        double layoutHeight = snapSizeY(getLayoutBounds().getHeight());
+        boolean vbarVisible = vBar.isVisible();
+        boolean hbarVisible = hBar.isVisible();
+        double vbarWidth = snapSizeX(vbarVisible ? vBar.prefWidth(-1) : 0);
+        double hbarHeight = snapSizeY(hbarVisible ? hBar.prefHeight(-1) : 0);
+
+        double w = layoutWidth - vbarWidth;
+        double h = layoutHeight - hbarHeight;
+
+        content.resize(w + vbarWidth, h);
+
+        hBar.setVisibleAmount(w);
+        vBar.setVisibleAmount(h);
+
+        if (vbarVisible) {
+            vBar.resizeRelocate(layoutWidth - vbarWidth - snappedRightInset(), 0, vbarWidth, h);
+        }
+
+        if (hbarVisible) {
+            hBar.resizeRelocate(0, layoutHeight - hbarHeight, w, hbarHeight);
+        }
     }
 }

+ 3 - 3
materialfx/src/main/java/io/github/palexdev/materialfx/controls/flowless/VirtualizedScrollPane.java

@@ -42,9 +42,9 @@ public class VirtualizedScrollPane<V extends Node & Virtualized> extends Region
 
     private static final PseudoClass CONTENT_FOCUSED = PseudoClass.getPseudoClass("content-focused");
 
-    private final ScrollBar hBar;
-    private final ScrollBar vBar;
-    private final V content;
+    protected final ScrollBar hBar;
+    protected final ScrollBar vBar;
+    protected final V content;
     private final ChangeListener<Boolean> contentFocusedListener;
 
     private final Var<Double> hBarValue;

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

@@ -67,7 +67,7 @@ public class MFXLegacyComboBox<T> extends ComboBox<T> {
     //================================================================================
     private static final StyleablePropertyFactory<MFXLegacyComboBox<?>> FACTORY = new StyleablePropertyFactory<>(ComboBox.getClassCssMetaData());
     private final String STYLE_CLASS = "mfx-legacy-combo-box";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/legacy/mfx-combobox.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/legacy/mfx-combobox.css");
 
     private MFXDialogValidator validator;
 

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/legacy/MFXLegacyTableRow.java

@@ -45,7 +45,7 @@ public class MFXLegacyTableRow<T> extends TableRow<T> {
     //================================================================================
     private static final StyleablePropertyFactory<MFXLegacyTableRow<?>> FACTORY = new StyleablePropertyFactory<>(TableRow.getClassCssMetaData());
     private final String STYLE_CLASS = "mfx-legacy-table-row";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/legacy/mfx-tablerow.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/legacy/mfx-tablerow.css");
     private final RippleGenerator rippleGenerator;
 
     //================================================================================

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/controls/legacy/MFXLegacyTableView.java

@@ -35,7 +35,7 @@ public class MFXLegacyTableView<S> extends TableView<S> {
     // Properties
     //================================================================================
     private final String STYLE_CLASS = "mfx-legacy-table-view";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/legacy/mfx-tableview.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/legacy/mfx-tableview.css");
 
     //================================================================================
     // Constructors

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/filter/MFXFilterDialog.java

@@ -77,7 +77,7 @@ public class MFXFilterDialog extends MFXDialog {
     // Properties
     //================================================================================
     private final String STYLE_CLASS = "mfx-filter-dialog";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-filter-dialog.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-filter-dialog.css");
 
     private final VBox container;
     private final VBox textFieldsContainer;

+ 7 - 11
materialfx/src/main/java/io/github/palexdev/materialfx/selection/ComboSelectionModelMock.java

@@ -19,7 +19,9 @@
 package io.github.palexdev.materialfx.selection;
 
 import io.github.palexdev.materialfx.controls.MFXComboBox;
+import javafx.beans.property.ReadOnlyIntegerProperty;
 import javafx.beans.property.ReadOnlyIntegerWrapper;
+import javafx.beans.property.ReadOnlyObjectProperty;
 import javafx.beans.property.ReadOnlyObjectWrapper;
 
 /**
@@ -37,7 +39,6 @@ public class ComboSelectionModelMock<T> {
     private final MFXComboBox<T> comboBox;
     private final ReadOnlyObjectWrapper<T> selectedItem = new ReadOnlyObjectWrapper<>(null);
     private final ReadOnlyIntegerWrapper selectedIndex = new ReadOnlyIntegerWrapper(-1);
-    private boolean isClearRequested = false;
 
     //================================================================================
     // Constructors
@@ -50,16 +51,15 @@ public class ComboSelectionModelMock<T> {
     // Methods
     //================================================================================
     public void clearSelection() {
-        isClearRequested = true;
         selectedItem.set(null);
         selectedIndex.set(-1);
-        isClearRequested = false;
     }
 
     public void selectItem(T item) {
         if (!comboBox.getItems().contains(item)) {
             return;
         }
+        selectedIndex.set(comboBox.getItems().indexOf(item));
         selectedItem.set(item);
     }
 
@@ -89,19 +89,15 @@ public class ComboSelectionModelMock<T> {
         return selectedIndex.get();
     }
 
-    public ReadOnlyIntegerWrapper selectedIndexProperty() {
-        return selectedIndex;
+    public ReadOnlyIntegerProperty selectedIndexProperty() {
+        return selectedIndex.getReadOnlyProperty();
     }
 
     public T getSelectedItem() {
         return selectedItem.get();
     }
 
-    public ReadOnlyObjectWrapper<T> selectedItemProperty() {
-        return selectedItem;
-    }
-
-    public boolean isClearRequested() {
-        return isClearRequested;
+    public ReadOnlyObjectProperty<T> selectedItemProperty() {
+        return selectedItem.getReadOnlyProperty();
     }
 }

+ 9 - 3
materialfx/src/main/java/io/github/palexdev/materialfx/selection/ListSelectionModel.java

@@ -50,12 +50,18 @@ public class ListSelectionModel<T> implements IListSelectionModel<T> {
             return;
         }
 
-        if (mouseEvent.isShiftDown() || mouseEvent.isControlDown()) {
-            selectedItems.put(index, data);
-        } else {
+        if (!allowsMultipleSelection) {
             ObservableMap<Integer, T> tmpMap = getObservableTreeMap();
             tmpMap.put(index, data);
             selectedItems.set(tmpMap);
+        } else {
+            if (mouseEvent.isShiftDown() || mouseEvent.isControlDown()) {
+                selectedItems.put(index, data);
+            } else {
+                ObservableMap<Integer, T> tmpMap = getObservableTreeMap();
+                tmpMap.put(index, data);
+                selectedItems.set(tmpMap);
+            }
         }
     }
 

+ 8 - 19
materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXButtonSkin.java

@@ -23,7 +23,6 @@ import io.github.palexdev.materialfx.controls.enums.ButtonType;
 import io.github.palexdev.materialfx.effects.DepthLevel;
 import io.github.palexdev.materialfx.effects.MFXDepthManager;
 import io.github.palexdev.materialfx.effects.RippleGenerator;
-import javafx.beans.binding.Bindings;
 import javafx.scene.control.skin.ButtonSkin;
 import javafx.scene.input.MouseEvent;
 
@@ -31,24 +30,17 @@ import javafx.scene.input.MouseEvent;
  * This is the implementation of the {@code Skin} associated with every {@code MFXButton}.
  */
 public class MFXButtonSkin extends ButtonSkin {
-    //================================================================================
-    // Properties
-    //================================================================================
-    private final RippleGenerator rippleGenerator;
-
     //================================================================================
     // Constructors
     //================================================================================
     public MFXButtonSkin(MFXButton button) {
         super(button);
 
-        rippleGenerator = new RippleGenerator(button);
-        setupRippleGenerator();
-
         setListeners();
         updateButtonType();
 
         updateChildren();
+        setupRippleGenerator();
     }
 
     //================================================================================
@@ -57,14 +49,12 @@ public class MFXButtonSkin extends ButtonSkin {
 
     protected void setupRippleGenerator() {
         MFXButton button = (MFXButton) getSkinnable();
+        RippleGenerator rippleGenerator = button.getRippleGenerator();
 
-        rippleGenerator.rippleColorProperty().bind(button.rippleColorProperty());
-        rippleGenerator.rippleRadiusProperty().bind(button.rippleRadiusProperty());
-        rippleGenerator.inDurationProperty().bind(button.rippleInDurationProperty());
-        rippleGenerator.outDurationProperty().bind(Bindings.createObjectBinding(
-                () -> button.getRippleInDuration().divide(2.0),
-                button.rippleInDurationProperty())
-        );
+        button.rippleColorProperty().bind(rippleGenerator.rippleColorProperty());
+        button.rippleRadiusProperty().bind(rippleGenerator.rippleRadiusProperty());
+        button.rippleInDurationProperty().bind(rippleGenerator.inDurationProperty());
+        button.rippleOutDurationProperty().bind(rippleGenerator.outDurationProperty());
     }
 
     /**
@@ -72,6 +62,7 @@ public class MFXButtonSkin extends ButtonSkin {
      */
     private void setListeners() {
         MFXButton button = (MFXButton) getSkinnable();
+        RippleGenerator rippleGenerator = button.getRippleGenerator();
 
         button.depthLevelProperty().addListener((observable, oldValue, newValue) -> {
             if (!newValue.equals(oldValue) && button.getButtonType().equals(ButtonType.RAISED)) {
@@ -116,8 +107,6 @@ public class MFXButtonSkin extends ButtonSkin {
     @Override
     protected void updateChildren() {
         super.updateChildren();
-        if (rippleGenerator != null) {
-            getChildren().add(0, rippleGenerator);
-        }
+        getChildren().add(0, ((MFXButton) getSkinnable()).getRippleGenerator());
     }
 }

+ 19 - 26
materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXComboBoxSkin.java

@@ -20,8 +20,8 @@ package io.github.palexdev.materialfx.skins;
 
 import io.github.palexdev.materialfx.beans.MFXSnapshotWrapper;
 import io.github.palexdev.materialfx.controls.MFXComboBox;
+import io.github.palexdev.materialfx.controls.MFXFlowlessListView;
 import io.github.palexdev.materialfx.controls.MFXIconWrapper;
-import io.github.palexdev.materialfx.controls.MFXListView;
 import io.github.palexdev.materialfx.controls.enums.Styles;
 import io.github.palexdev.materialfx.controls.factories.MFXAnimationFactory;
 import io.github.palexdev.materialfx.effects.RippleGenerator;
@@ -32,6 +32,7 @@ import javafx.animation.KeyFrame;
 import javafx.animation.KeyValue;
 import javafx.animation.ScaleTransition;
 import javafx.animation.Timeline;
+import javafx.collections.MapChangeListener;
 import javafx.event.EventHandler;
 import javafx.geometry.HPos;
 import javafx.geometry.Point2D;
@@ -59,7 +60,7 @@ public class MFXComboBoxSkin<T> extends SkinBase<MFXComboBox<T>> {
 
     private final MFXIconWrapper icon;
     private final PopupControl popup;
-    private final MFXListView<T> listView;
+    private final MFXFlowlessListView<T> listView;
     private final EventHandler<MouseEvent> popupHandler;
 
     private final Line unfocusedLine;
@@ -101,8 +102,9 @@ public class MFXComboBoxSkin<T> extends SkinBase<MFXComboBox<T>> {
         container = new HBox(20, valueLabel);
         container.setAlignment(Pos.CENTER_LEFT);
 
-        listView = new MFXListView<>();
+        listView = new MFXFlowlessListView<>();
         listView.getStylesheets().add(comboBox.getUserAgentStylesheet());
+        listView.getSelectionModel().setAllowsMultipleSelection(false);
         popup = buildPopup();
 
         popupHandler = event -> {
@@ -202,7 +204,19 @@ public class MFXComboBoxSkin<T> extends SkinBase<MFXComboBox<T>> {
         MFXComboBox<T> comboBox = getSkinnable();
         ComboSelectionModelMock<T> selectionModel = comboBox.getSelectionModel();
 
-        comboBox.selectedValueProperty().bind(listView.getSelectionModel().selectedItemProperty());
+        selectionModel.selectedItemProperty().addListener((observable, oldValue, newValue) -> {
+            if (oldValue != newValue) {
+                comboBox.setSelectedValue(newValue);
+            }
+        });
+
+        listView.getSelectionModel().selectedItemsProperty().addListener((MapChangeListener<? super Integer, ? super T>) change -> {
+            T item = change.getValueAdded();
+            if (item != null) {
+                selectionModel.selectItem(item);
+            }
+        });
+
         comboBox.selectedValueProperty().addListener((observable, oldValue, newValue) -> {
             if (newValue != null) {
                 setValueLabel(newValue);
@@ -211,27 +225,6 @@ public class MFXComboBoxSkin<T> extends SkinBase<MFXComboBox<T>> {
                 valueLabel.setGraphic(null);
             }
         });
-
-        listView.getSelectionModel().selectedIndexProperty().addListener((observable, oldValue, newValue) -> selectionModel.selectedIndexProperty().set(newValue.intValue()));
-        listView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> selectionModel.selectedItemProperty().set(newValue));
-        selectionModel.selectedIndexProperty().addListener((observable, oldValue, newValue) -> {
-            if (newValue.intValue() != oldValue.intValue()) {
-                if (newValue.intValue() == -1) {
-                    listView.getSelectionModel().clearSelection();
-                } else {
-                    listView.getSelectionModel().select(newValue.intValue());
-                }
-            }
-        });
-        selectionModel.selectedItemProperty().addListener((observable, oldValue, newValue) -> {
-            if (newValue != oldValue) {
-                if (newValue == null) {
-                    listView.getSelectionModel().clearSelection();
-                } else {
-                    listView.getSelectionModel().select(newValue);
-                }
-            }
-        });
     }
 
     /**
@@ -327,7 +320,7 @@ public class MFXComboBoxSkin<T> extends SkinBase<MFXComboBox<T>> {
 
         PopupControl popupControl = new PopupControl();
 
-        listView.itemsProperty().bind(comboBox.itemsProperty());
+        listView.setItems(comboBox.getItems());
         popupControl.getScene().setRoot(listView);
         popupControl.setOnShowing(event -> buildAnimation(true).play());
         popupControl.setOnHiding(event -> buildAnimation(false).play());

+ 1 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXDatePickerContent.java

@@ -84,7 +84,7 @@ public class MFXDatePickerContent extends VBox {
     // Properties
     //================================================================================
     private final String STYLE_CLASS = "mfx-datepicker-content";
-    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-datepicker-content.css").toString();
+    private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-datepicker-content.css");
 
     private final double DEFAULT_WIDTH = 300;
     private final double DEFAULT_HEIGHT = 380;

+ 50 - 45
materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXFilterComboBoxSkin.java

@@ -2,18 +2,20 @@ package io.github.palexdev.materialfx.skins;
 
 import io.github.palexdev.materialfx.beans.MFXSnapshotWrapper;
 import io.github.palexdev.materialfx.controls.MFXFilterComboBox;
+import io.github.palexdev.materialfx.controls.MFXFlowlessListView;
 import io.github.palexdev.materialfx.controls.MFXIconWrapper;
-import io.github.palexdev.materialfx.controls.MFXListView;
 import io.github.palexdev.materialfx.controls.MFXTextField;
+import io.github.palexdev.materialfx.controls.cell.MFXFlowlessListCell;
 import io.github.palexdev.materialfx.controls.enums.Styles;
 import io.github.palexdev.materialfx.controls.factories.MFXAnimationFactory;
 import io.github.palexdev.materialfx.effects.RippleGenerator;
 import io.github.palexdev.materialfx.font.MFXFontIcon;
 import io.github.palexdev.materialfx.selection.ComboSelectionModelMock;
+import io.github.palexdev.materialfx.selection.base.IListSelectionModel;
 import io.github.palexdev.materialfx.utils.NodeUtils;
 import io.github.palexdev.materialfx.utils.StringUtils;
 import javafx.animation.*;
-import javafx.beans.InvalidationListener;
+import javafx.collections.MapChangeListener;
 import javafx.collections.transformation.FilteredList;
 import javafx.event.EventHandler;
 import javafx.geometry.*;
@@ -26,6 +28,7 @@ import javafx.scene.input.MouseEvent;
 import javafx.scene.layout.HBox;
 import javafx.scene.paint.Color;
 import javafx.scene.shape.Line;
+import javafx.stage.WindowEvent;
 import javafx.util.Duration;
 
 import java.util.Arrays;
@@ -46,7 +49,7 @@ public class MFXFilterComboBoxSkin<T> extends SkinBase<MFXFilterComboBox<T>> {
 
     private final MFXIconWrapper icon;
     private final PopupControl popup;
-    private final MFXListView<T> listView;
+    private final MFXFlowlessListView<T> listView;
     private final EventHandler<MouseEvent> popupHandler;
 
     private final Line unfocusedLine;
@@ -55,7 +58,6 @@ public class MFXFilterComboBoxSkin<T> extends SkinBase<MFXFilterComboBox<T>> {
     private final HBox searchContainer;
     private final FilteredList<T> filteredList;
     private MFXTextField searchField;
-    private T previousSelected;
 
     private Timeline arrowAnimation;
 
@@ -103,7 +105,24 @@ public class MFXFilterComboBoxSkin<T> extends SkinBase<MFXFilterComboBox<T>> {
         searchContainer.setAlignment(Pos.CENTER_LEFT);
         searchContainer.setManaged(false);
 
-        listView = new MFXListView<>();
+        listView = new MFXFlowlessListView<>() {
+            {
+                setCellFactory(item -> new MFXFlowlessListCell<>(this, item) {
+                    @Override
+                    public void updateIndex(int index) {
+                        setIndex(index);
+                        if (containsEqualsBoth() && !isSelected()) {
+                            setSelected(true);
+                            return;
+                        }
+                        if (containsNotEqualsIndex()) {
+                            listView.getSelectionModel().updateIndex(getData(), index);
+                            setSelected(true);
+                        }
+                    }
+                });
+            }
+        };
         listView.getStylesheets().add(comboBox.getUserAgentStylesheet());
         popup = buildPopup();
         popupHandler = event -> {
@@ -197,51 +216,25 @@ public class MFXFilterComboBoxSkin<T> extends SkinBase<MFXFilterComboBox<T>> {
         MFXFilterComboBox<T> comboBox = getSkinnable();
         ComboSelectionModelMock<T> selectionModel = comboBox.getSelectionModel();
 
-        comboBox.selectedValueProperty().bind(listView.getSelectionModel().selectedItemProperty());
-        comboBox.selectedValueProperty().addListener((observable, oldValue, newValue) -> {
-            if (newValue != null) {
-                setValueLabel(newValue);
-            } else {
-                valueLabel.setText("");
-                valueLabel.setGraphic(null);
-            }
-        });
-        listView.getSelectionModel().selectedIndexProperty().addListener((observable, oldValue, newValue) -> selectionModel.selectedIndexProperty().set(newValue.intValue()));
-        listView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> selectionModel.selectedItemProperty().set(newValue));
-        selectionModel.selectedIndexProperty().addListener((observable, oldValue, newValue) -> {
-            if (newValue.intValue() != oldValue.intValue()) {
-                if (newValue.intValue() == -1) {
-                    listView.getSelectionModel().clearSelection();
-                } else {
-                    listView.getSelectionModel().select(newValue.intValue());
-                }
-            }
-        });
         selectionModel.selectedItemProperty().addListener((observable, oldValue, newValue) -> {
-            if (newValue != oldValue) {
-                if (newValue == null) {
-                    listView.getSelectionModel().clearSelection();
-                } else {
-                    listView.getSelectionModel().select(newValue);
-                }
+            if (oldValue != newValue) {
+                comboBox.setSelectedValue(newValue);
             }
         });
 
-        /*
-         * This is a workaround to make selection work. For some reason when the focus changes the selection
-         * is reset. To prevent that we store the last selected item in a temp variable, when the selection is reset
-         * we force the selection to that temp variable. To clear the selection use ComboSelectionModelMock#clearSelection.
-         */
-        filteredList.addListener((InvalidationListener) invalidated -> {
-            if (selectionModel.getSelectedItem() != null) {
-                previousSelected = selectionModel.getSelectedItem();
+        listView.getSelectionModel().selectedItemsProperty().addListener((MapChangeListener<? super Integer, ? super T>) change -> {
+            T item = change.getValueAdded();
+            if (item != null) {
+                selectionModel.selectItem(item);
             }
-            listView.setItems(filteredList);
-            selectionModel.selectItem(previousSelected);
         });
-        selectionModel.selectedItemProperty().addListener((observable, oldValue, newValue) -> {
-            if (newValue == null && !selectionModel.isClearRequested()) {
-                selectionModel.selectItem(previousSelected);
+
+        comboBox.selectedValueProperty().addListener((observable, oldValue, newValue) -> {
+            if (newValue != null) {
+                setValueLabel(newValue);
+            } else {
+                valueLabel.setText("");
+                valueLabel.setGraphic(null);
             }
         });
     }
@@ -278,6 +271,14 @@ public class MFXFilterComboBoxSkin<T> extends SkinBase<MFXFilterComboBox<T>> {
                 newValue.addEventFilter(MouseEvent.MOUSE_PRESSED, popupHandler);
             }
         });
+
+        popup.addEventHandler(WindowEvent.WINDOW_SHOWN, event -> {
+            ComboSelectionModelMock<T> selectionModelMock = comboBox.getSelectionModel();
+            IListSelectionModel<T> listSelectionModel = listView.getSelectionModel();
+            if (selectionModelMock.getSelectedItem() != null) {
+                listSelectionModel.select(selectionModelMock.getSelectedIndex(), selectionModelMock.getSelectedItem(), null);
+            }
+        });
     }
 
     /**
@@ -394,6 +395,7 @@ public class MFXFilterComboBoxSkin<T> extends SkinBase<MFXFilterComboBox<T>> {
         container.getChildren().remove(searchContainer);
         filteredList.setPredicate(null);
         valueLabel.setVisible(true);
+        listView.setItems(filteredList);
         comboBox.requestFocus();
     }
 
@@ -427,7 +429,10 @@ public class MFXFilterComboBoxSkin<T> extends SkinBase<MFXFilterComboBox<T>> {
         searchField.setLineColor(Color.TRANSPARENT);
         searchField.setFocusTraversable(false);
 
-        searchField.textProperty().addListener((observable, oldValue, newValue) -> filteredList.setPredicate(getPredicate(searchField)));
+        searchField.textProperty().addListener((observable, oldValue, newValue) -> {
+            filteredList.setPredicate(getPredicate(searchField));
+            listView.setItems(filteredList);
+        });
         searchField.focusedProperty().addListener((observable, oldValue, newValue) -> {
             if (!newValue && popup.isShowing()) {
                 reset();

+ 70 - 51
materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXFlowlessListViewSkin.java

@@ -36,6 +36,7 @@ import javafx.util.Duration;
 
 public class MFXFlowlessListViewSkin<T> extends SkinBase<AbstractFlowlessListView<T, ?, ?>> {
     private final ScrollBar vBar;
+    private final ScrollBar hBar;
     private Timeline hideBars;
     private Timeline showBars;
 
@@ -51,19 +52,23 @@ public class MFXFlowlessListViewSkin<T> extends SkinBase<AbstractFlowlessListVie
         virtualScrollPane.getStylesheets().setAll(listView.getUserAgentStylesheet());
         //MFXVirtualizedScrollPane.smoothVScrolling(virtualScrollPane); // Not working atm
 
-        vBar = (ScrollBar) virtualScrollPane.lookup(".vvbar");
-
-        if (vBar != null) {
-            hideBars = new Timeline(
-                    new KeyFrame(Duration.millis(400),
-                            new KeyValue(vBar.opacityProperty(), 0.0, MFXAnimationFactory.getInterpolatorV2())));
-            showBars = new Timeline(
-                    new KeyFrame(Duration.millis(400),
-                            new KeyValue(vBar.opacityProperty(), 1.0, MFXAnimationFactory.getInterpolatorV2())));
-        }
-
-        if (vBar != null && listView.isHideScrollBars()) {
+        vBar = virtualScrollPane.getVBar();
+        hBar = virtualScrollPane.getHBar();
+
+        hideBars = new Timeline(
+                new KeyFrame(Duration.millis(400),
+                        new KeyValue(vBar.opacityProperty(), 0.0, MFXAnimationFactory.getInterpolatorV1()),
+                        new KeyValue(hBar.opacityProperty(), 0.0, MFXAnimationFactory.getInterpolatorV1()))
+        );
+        showBars = new Timeline(
+                new KeyFrame(Duration.millis(400),
+                        new KeyValue(vBar.opacityProperty(), 1.0, MFXAnimationFactory.getInterpolatorV1()),
+                        new KeyValue(hBar.opacityProperty(), 1.0, MFXAnimationFactory.getInterpolatorV1()))
+        );
+
+        if (listView.isHideScrollBars()) {
             vBar.setOpacity(0.0);
+            hBar.setOpacity(0.0);
         }
 
         getChildren().add(virtualScrollPane);
@@ -84,51 +89,65 @@ public class MFXFlowlessListViewSkin<T> extends SkinBase<AbstractFlowlessListVie
     }
 
     private void setScrollBarHandlers() {
-        if (vBar != null) {
-            AbstractFlowlessListView<T, ?, ?> listView = getSkinnable();
-
-            listView.setOnMouseExited(event -> {
-                if (listView.isHideScrollBars()) {
-                    hideBars.setDelay(listView.getHideAfter());
-
-                    if (vBar.isPressed()) {
-                        vBar.pressedProperty().addListener(new ChangeListener<>() {
-                            @Override
-                            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
-                                if (!newValue) {
-                                    hideBars.play();
-                                }
-                                vBar.pressedProperty().removeListener(this);
-                            }
-                        });
-                        return;
-                    }
+        AbstractFlowlessListView<T, ?, ?> listView = getSkinnable();
+
+        listView.setOnMouseExited(event -> {
+            if (listView.isHideScrollBars()) {
+                hideBars.setDelay(listView.getHideAfter());
 
-                    hideBars.play();
+                if (hBar.isPressed()) {
+                    hBar.pressedProperty().addListener(new ChangeListener<>() {
+                        @Override
+                        public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+                            if (!newValue) {
+                                hideBars.play();
+                            }
+                            hBar.pressedProperty().removeListener(this);
+                        }
+                    });
+                    return;
                 }
-            });
 
-            listView.setOnMouseEntered(event -> {
-                if (hideBars.getStatus().equals(Animation.Status.RUNNING)) {
-                    hideBars.stop();
+                if (vBar.isPressed()) {
+                    vBar.pressedProperty().addListener(new ChangeListener<>() {
+                        @Override
+                        public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+                            if (!newValue) {
+                                hideBars.play();
+                            }
+                            vBar.pressedProperty().removeListener(this);
+                        }
+                    });
+                    return;
                 }
+
+                hideBars.play();
+            }
+        });
+
+        listView.setOnMouseEntered(event -> {
+            if (hideBars.getStatus().equals(Animation.Status.RUNNING)) {
+                hideBars.stop();
+            }
+            showBars.play();
+        });
+
+        listView.hideScrollBarsProperty().addListener((observable, oldValue, newValue) -> {
+            if (newValue) {
+                hideBars.play();
+            } else {
                 showBars.play();
-            });
+            }
+            if (newValue &&
+                    hideBars.getStatus() != Animation.Status.RUNNING ||
+                    vBar.getOpacity() != 0 ||
+                    hBar.getOpacity() != 0
+            ) {
+                vBar.setOpacity(0.0);
+                hBar.setOpacity(0.0);
+            }
+        });
 
-            listView.hideScrollBarsProperty().addListener((observable, oldValue, newValue) -> {
-                if (newValue) {
-                    hideBars.play();
-                } else {
-                    showBars.play();
-                }
-                if (newValue &&
-                        hideBars.getStatus() != Animation.Status.RUNNING ||
-                        vBar.getOpacity() != 0
-                ) {
-                    vBar.setOpacity(0.0);
-                }
-            });
-        }
     }
 
     protected AbstractMFXFlowlessListCell<T> createCell(T item) {

+ 0 - 1
materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXScrollPaneSkin.java

@@ -35,5 +35,4 @@ public class MFXScrollPaneSkin extends ScrollPaneSkin {
         StackPane viewPort = (StackPane) scrollPane.lookup(".viewport");
         viewPort.setCache(false);
     }
-
 }

+ 124 - 8
materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXTableViewSkin.java

@@ -18,6 +18,7 @@
 
 package io.github.palexdev.materialfx.skins;
 
+import io.github.palexdev.materialfx.beans.MFXContextMenuItem;
 import io.github.palexdev.materialfx.controls.*;
 import io.github.palexdev.materialfx.controls.cell.MFXTableColumnCell;
 import io.github.palexdev.materialfx.controls.cell.MFXTableRowCell;
@@ -30,11 +31,14 @@ import io.github.palexdev.materialfx.filter.MFXFilterDialog;
 import io.github.palexdev.materialfx.font.MFXFontIcon;
 import io.github.palexdev.materialfx.selection.base.ITableSelectionModel;
 import io.github.palexdev.materialfx.utils.DragResizer;
+import io.github.palexdev.materialfx.utils.LabelUtils;
 import io.github.palexdev.materialfx.utils.NodeUtils;
 import javafx.animation.KeyFrame;
 import javafx.animation.KeyValue;
 import javafx.animation.Timeline;
 import javafx.beans.InvalidationListener;
+import javafx.beans.binding.Bindings;
+import javafx.beans.binding.BooleanBinding;
 import javafx.beans.property.BooleanProperty;
 import javafx.beans.property.SimpleBooleanProperty;
 import javafx.beans.value.ChangeListener;
@@ -47,8 +51,10 @@ import javafx.geometry.Pos;
 import javafx.scene.Node;
 import javafx.scene.control.Skin;
 import javafx.scene.control.SkinBase;
+import javafx.scene.input.MouseButton;
 import javafx.scene.input.MouseEvent;
 import javafx.scene.layout.HBox;
+import javafx.scene.layout.Priority;
 import javafx.scene.layout.Region;
 import javafx.scene.layout.VBox;
 import javafx.stage.Modality;
@@ -60,6 +66,7 @@ import java.util.List;
 import java.util.stream.Collectors;
 
 import static io.github.palexdev.materialfx.controls.MFXTableView.TableViewEvent;
+import static javafx.scene.layout.Region.USE_PREF_SIZE;
 
 /**
  * This is the implementation of the Skin associated with every {@code MFXTableView}.
@@ -131,15 +138,17 @@ public class MFXTableViewSkin<T> extends SkinBase<MFXTableView<T>> {
 
         rowsContainer = new VBox();
         rowsContainer.getStyleClass().setAll("rows-container");
-        rowsContainer.setPadding(new Insets(3, 5, 3, 5));
+        rowsContainer.setMinHeight(USE_PREF_SIZE);
+        rowsContainer.setPadding(new Insets(-1, 5, -1, 4));
         rowsContainer.prefWidthProperty().bind(columnsContainer.widthProperty());
+        VBox.setVgrow(rowsContainer, Priority.ALWAYS);
 
         paginationControls = new HBox(10);
         paginationControls.getStyleClass().setAll("pagination");
         paginationControls.prefWidthProperty().bind(container.widthProperty());
         paginationControls.setPrefHeight(40);
         paginationControls.setMaxHeight(Region.USE_PREF_SIZE);
-        paginationControls.setAlignment(Pos.CENTER_RIGHT);
+        paginationControls.setAlignment(Pos.CENTER_LEFT);
         paginationControls.setPadding(new Insets(8, 5, 5, 5));
 
         filterIcon = buildFilterIcon();
@@ -155,7 +164,6 @@ public class MFXTableViewSkin<T> extends SkinBase<MFXTableView<T>> {
         rowsPerPageLabel.setLabelStyle(Styles.LabelStyles.STYLE2);
         rowsPerPageLabel.setStyle("-fx-border-color: transparent");
         rowsPerPageLabel.setLabelAlignment(Pos.CENTER);
-        rowsPerPageLabel.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> System.out.println(rowsPerPageLabel.getWidth()));
         HBox.setMargin(rowsPerPageLabel, new Insets(0, -10, 0, 0));
 
         rowsPerPageCombo = new MFXComboBox<>();
@@ -253,8 +261,13 @@ public class MFXTableViewSkin<T> extends SkinBase<MFXTableView<T>> {
                 buildRows();
             }
         });
-        filterIcon.disableProperty().bind(tableFiltered);
-        clearFilterIcon.disableProperty().bind(tableFiltered.not());
+
+        BooleanBinding listEmpty = Bindings.createBooleanBinding(
+                () -> tableView.getItems().isEmpty(),
+                tableView.getItems()
+        );
+        filterIcon.disableProperty().bind(tableFiltered.or(listEmpty));
+        clearFilterIcon.disableProperty().bind(tableFiltered.not().or(listEmpty));
     }
 
     /**
@@ -264,9 +277,14 @@ public class MFXTableViewSkin<T> extends SkinBase<MFXTableView<T>> {
         MFXTableView<T> tableView = getSkinnable();
 
         for (MFXTableColumnCell<T> column : tableView.getColumns()) {
+            addContextMenu(column);
             column.setMaxHeight(Double.MAX_VALUE);
             DragResizer.makeResizable(column, DragResizer.RIGHT);
-            column.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> sortColumn(column));
+            column.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
+                if (event.getButton() == MouseButton.PRIMARY) {
+                    sortColumn(column);
+                }
+            });
             column.rowCellFactoryProperty().addListener((observable, oldValue, newValue) -> {
                 if (newValue != oldValue) {
                     buildRows();
@@ -276,6 +294,77 @@ public class MFXTableViewSkin<T> extends SkinBase<MFXTableView<T>> {
         }
     }
 
+    @SuppressWarnings("unchecked")
+    protected void addContextMenu(MFXTableColumnCell<T> column) {
+        MFXContextMenuItem restoreWidthThis = new MFXContextMenuItem(
+                "Restore this column width",
+                event -> column.setMinWidth(column.getInitialWidth())
+        );
+
+        MFXContextMenuItem restoreWidthAll = new MFXContextMenuItem(
+                "Restore all columns width",
+                event -> columnsContainer.getChildren().stream()
+                        .filter(node -> node instanceof MFXTableColumnCell)
+                        .map(node -> (MFXTableColumnCell<T>) node)
+                        .forEach(c -> c.setMinWidth(c.getInitialWidth()))
+        );
+
+        MFXContextMenuItem autoSizeThis = new MFXContextMenuItem(
+                "Autosize this column",
+                event -> autoSizeColumn(column)
+        );
+
+        MFXContextMenuItem autoSizeAll = new MFXContextMenuItem(
+                "Autosize all columns",
+                event -> columnsContainer.getChildren().stream()
+                        .filter(node -> node instanceof MFXTableColumnCell)
+                        .map(node -> (MFXTableColumnCell<T>) node)
+                        .forEach(this::autoSizeColumn)
+        );
+
+        new MFXContextMenu.Builder()
+                .addMenuItem(autoSizeAll)
+                .addMenuItem(autoSizeThis)
+                .addSeparator()
+                .addMenuItem(restoreWidthAll)
+                .addMenuItem(restoreWidthThis)
+                .install(column);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected void autoSizeColumn(MFXTableColumnCell<T> column) {
+        int index = columnsContainer.getChildren().indexOf(column);
+        if (index > -1) {
+            List<MFXTableRow<T>> tableRows = rowsContainer.getChildren().stream()
+                    .filter(node -> node instanceof MFXTableRow)
+                    .map(node -> (MFXTableRow<T>) node)
+                    .collect(Collectors.toList());
+            List<MFXTableRowCell> rowCells = new ArrayList<>();
+            tableRows.forEach(row -> {
+                MFXTableRowCell rowCell = (MFXTableRowCell) row.getChildren().get(index);
+                if (LabelUtils.isLabelTruncated(rowCell)) {
+                    rowCells.add((MFXTableRowCell) row.getChildren().get(index));
+                }
+            });
+            double max = getMaxCellWidth(rowCells);
+            if (max != -1) {
+                column.setMinWidth(max);
+            }
+        }
+    }
+
+    protected double getMaxCellWidth(List<MFXTableRowCell> rowCells) {
+        double max = -1;
+        for (MFXTableRowCell rowCell : rowCells) {
+            double computed = LabelUtils.computeTextWidth(rowCell.getFont(), rowCell.getText());
+            computed += rowCell.snappedRightInset() + rowCell.snappedLeftInset();
+            if (computed > max) {
+                max = computed;
+            }
+        }
+        return max;
+    }
+
     /**
      * Builds a row HBox which will contain all the required cells to represent the model object
      * and allows selection.
@@ -285,7 +374,7 @@ public class MFXTableViewSkin<T> extends SkinBase<MFXTableView<T>> {
 
         MFXTableRow<T> row = new MFXTableRow<>(10, Pos.CENTER_LEFT, item);
         row.prefWidthProperty().bind(container.widthProperty());
-        row.setMinHeight(tableView.getFixedRowsHeight());
+        row.minHeightProperty().bind(tableView.fixedRowsHeightProperty());
         row.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> tableView.getSelectionModel().select(row, event));
         return row;
     }
@@ -304,7 +393,10 @@ public class MFXTableViewSkin<T> extends SkinBase<MFXTableView<T>> {
         List<MFXTableRow<T>> rows = new ArrayList<>();
         int i;
         int size = items.size();
-        for (i = index; i < (tableView.getMaxRows() + index) && size > 0 && i < size; i++) {
+        int pageIndex = tableView.getMaxRows() + index;
+        computeRowsContainerHeight(items.size() - index);
+
+        for (i = index; i < pageIndex && size > 0 && i < size; i++) {
             T item = items.get(i);
             MFXTableRow<T> row = buildRowBox(item);
             rows.add(row);
@@ -653,6 +745,20 @@ public class MFXTableViewSkin<T> extends SkinBase<MFXTableView<T>> {
         filterDialog.close();
     }
 
+    private void computeRowsContainerHeight(int index) {
+        MFXTableView<T> tableView = getSkinnable();
+
+        int nrows = rowsPerPageCombo.getSelectedValue() != null ? rowsPerPageCombo.getSelectedValue() : 5;
+        double value = -1;
+        if (tableView.getItems().isEmpty() || index < nrows) {
+            value = (rowsContainer.snappedTopInset() +
+                    tableView.getFixedRowsHeight() +
+                    rowsContainer.snappedBottomInset()) * nrows;
+        }
+        double finalValue = value == -1 ? value : snapSpaceY(value + ((rowsPerPageCombo.getSelectionModel().getSelectedIndex() + 1)));
+        rowsContainer.setMinHeight(finalValue);
+    }
+
     //================================================================================
     // Override Methods
     //================================================================================
@@ -670,4 +776,14 @@ public class MFXTableViewSkin<T> extends SkinBase<MFXTableView<T>> {
     protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
         return computePrefHeight(width, topInset, leftInset, bottomInset, rightInset);
     }
+
+    @Override
+    protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
+        return 600;
+    }
+
+    @Override
+    protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
+        return computePrefWidth(height, topInset, rightInset, bottomInset, leftInset);
+    }
 }

+ 6 - 10
materialfx/src/main/java/io/github/palexdev/materialfx/utils/LabelUtils.java

@@ -24,8 +24,6 @@ import javafx.scene.layout.Region;
 import javafx.scene.text.Font;
 import javafx.scene.text.Text;
 
-import java.util.concurrent.atomic.AtomicBoolean;
-
 /**
  * Utils class for JavaFX's {@code Label}s.
  */
@@ -40,15 +38,13 @@ public class LabelUtils {
      * @param label The specified label
      */
     public static boolean isLabelTruncated(Label label) {
-        AtomicBoolean isTruncated = new AtomicBoolean(false);
-
-        label.needsLayoutProperty().addListener((observable, oldValue, newValue) -> {
-            String originalString = label.getText();
-            Text textNode = (Text) label.lookup(".text");
+        String originalString = label.getText();
+        Text textNode = (Text) label.lookup(".text");
+        if (textNode != null) {
             String actualString = textNode.getText();
-            isTruncated.set(!actualString.isEmpty() && !originalString.equals(actualString));
-        });
-        return isTruncated.get();
+            return (!actualString.isEmpty() && !originalString.equals(actualString));
+        }
+        return false;
     }
 
     /**

+ 10 - 6
materialfx/src/main/resources/io/github/palexdev/materialfx/css/mfx-combobox-style1.css

@@ -44,17 +44,21 @@
 
 .mfx-list-view {
     -fx-background-color: white;
-    -fx-background-insets: 2;
-    -fx-background-radius: 2px;
+    -fx-border-color: #A4B1B6;
+    -fx-background-insets: 0 -1 0 -1;
+    -fx-border-insets: 0 -1 0 -1;
 
     -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-list-view .mfx-list-cell:hover {
+    -fx-background-color: rgb(242, 242, 242);
+    -fx-border-color: rgb(242, 242, 242);
+}
 
-    -mfx-corner-radius: 2px;
+.mfx-list-view .mfx-list-cell:selected {
+    -fx-background-color: rgb(185, 152, 255);
+    -fx-border-color: rgb(185, 152, 255);
 }

+ 10 - 6
materialfx/src/main/resources/io/github/palexdev/materialfx/css/mfx-combobox-style2.css

@@ -50,17 +50,21 @@
 
 .mfx-list-view {
     -fx-background-color: white;
-    -fx-background-insets: 2;
-    -fx-background-radius: 2px;
+    -fx-border-color: #A4B1B6;
+    -fx-background-insets: 0 -1 0 -1;
+    -fx-border-insets: 0 -1 0 -1;
 
     -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-list-view .mfx-list-cell:hover {
+    -fx-background-color: rgb(242, 242, 242);
+    -fx-border-color: rgb(242, 242, 242);
+}
 
-    -mfx-corner-radius: 2px;
+.mfx-list-view .mfx-list-cell:selected {
+    -fx-background-color: rgb(185, 152, 255);
+    -fx-border-color: rgb(185, 152, 255);
 }

+ 41 - 0
materialfx/src/main/resources/io/github/palexdev/materialfx/css/mfx-contextmenu.css

@@ -0,0 +1,41 @@
+/*
+ *     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/>.
+ */
+
+.mfx-context-menu {
+    -fx-background-radius: 4;
+    -fx-border-color: #A4B1B6;
+    -fx-border-radius: 4;
+    -fx-padding: 3 0 3 0;
+}
+
+.label {
+    -fx-min-height: 27;
+    -fx-padding: 8;
+    -fx-background-color: white;
+}
+
+.label:hover {
+    -fx-background-color: rgb(120, 150, 255);
+}
+
+.separator {
+    -fx-border-color: #A4B1B6;
+    -fx-stroke-width: 0.5;
+    -fx-opacity: 0.5;
+}
+

+ 2 - 1
materialfx/src/main/resources/io/github/palexdev/materialfx/css/mfx-table-column-cell.css

@@ -29,7 +29,8 @@
     -fx-border-color: transparent;
 }
 
-.mfx-table-column-cell:hover {
+.mfx-table-column-cell:hover,
+.mfx-table-column-cell:dragged {
     -fx-border-color: transparent rgba(46, 52, 64, 0.3) transparent transparent;
     -fx-border-width: 0.5;
 }