Эх сурвалжийг харах

Complete new combo box

MFXComboBo:  use a mock selection model to allow basic operations on the associated listview.
Allow customization of popup offset and max width.

MFXProgressSpinner: correct style class

StringUtils: added new method.

Selection classes moved to separate package.
Added new resources.

Signed-off-by: PAlex404 <alessandro.parisi406@gmail.com>
PAlex404 4 жил өмнө
parent
commit
9a87aa5beb
20 өөрчлөгдсөн 238 нэмэгдсэн , 53 устгасан
  1. 9 8
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXCheckTreeItem.java
  2. 9 7
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXCheckTreeView.java
  3. 47 0
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXComboBox.java
  4. 2 2
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXProgressSpinner.java
  5. 6 5
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXTreeItem.java
  6. 7 6
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXTreeView.java
  7. 3 2
      materialfx/src/main/java/io/github/palexdev/materialfx/controls/base/AbstractMFXTreeItem.java
  8. 6 0
      materialfx/src/main/java/io/github/palexdev/materialfx/font/FontResources.java
  9. 86 0
      materialfx/src/main/java/io/github/palexdev/materialfx/selection/ComboSelectionModelMock.java
  10. 2 2
      materialfx/src/main/java/io/github/palexdev/materialfx/selection/ITreeCheckModel.java
  11. 3 2
      materialfx/src/main/java/io/github/palexdev/materialfx/selection/ITreeSelectionModel.java
  12. 6 6
      materialfx/src/main/java/io/github/palexdev/materialfx/selection/TreeCheckModelTree.java
  13. 6 6
      materialfx/src/main/java/io/github/palexdev/materialfx/selection/TreeTreeSelectionModel.java
  14. 3 2
      materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXCheckTreeItemSkin.java
  15. 31 1
      materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXComboBoxSkin.java
  16. 2 1
      materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXTreeItemSkin.java
  17. 6 0
      materialfx/src/main/java/io/github/palexdev/materialfx/utils/StringUtils.java
  18. 1 0
      materialfx/src/main/java/module-info.java
  19. 3 3
      materialfx/src/main/resources/io/github/palexdev/materialfx/css/mfx-progress-spinner.css
  20. BIN
      materialfx/src/main/resources/io/github/palexdev/materialfx/fonts/materialfx-resources.ttf

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

@@ -21,8 +21,9 @@ package io.github.palexdev.materialfx.controls;
 import io.github.palexdev.materialfx.MFXResourcesLoader;
 import io.github.palexdev.materialfx.controls.base.AbstractMFXTreeCell;
 import io.github.palexdev.materialfx.controls.base.AbstractMFXTreeItem;
-import io.github.palexdev.materialfx.controls.base.ICheckModel;
 import io.github.palexdev.materialfx.controls.cell.MFXCheckTreeCell;
+import io.github.palexdev.materialfx.selection.ITreeCheckModel;
+import io.github.palexdev.materialfx.selection.TreeCheckModelTree;
 import io.github.palexdev.materialfx.skins.MFXCheckTreeItemSkin;
 import javafx.beans.property.BooleanProperty;
 import javafx.beans.property.SimpleBooleanProperty;
@@ -38,7 +39,7 @@ import java.lang.ref.WeakReference;
  * <p>
  * The default associated {@link Skin} is {@link MFXCheckTreeItemSkin<T>}.
  * @see MFXCheckTreeView
- * @see ICheckModel
+ * @see ITreeCheckModel
  * @param <T> The type of the data within TreeItem.
  */
 public class MFXCheckTreeItem<T> extends MFXTreeItem<T> {
@@ -72,15 +73,15 @@ public class MFXCheckTreeItem<T> extends MFXTreeItem<T> {
      * Sets the style class to "mfx-tree-view".
      * <p>
      * Adds a listener to {@link #treeViewProperty()} allowing item check before the Scene is shown
-     * by calling the CheckModel {@link CheckModel#scanTree(MFXCheckTreeItem)} )} method.
+     * by calling the TreeCheckModelTree {@link TreeCheckModelTree#scanTree(MFXCheckTreeItem)} )} method.
      */
     private void initialize() {
         getStyleClass().add(STYLE_CLASS);
 
         treeViewProperty().addListener((observable, oldValue, newValue) -> {
             if (newValue != null && isRoot()) {
-                CheckModel<T> checkModel = (CheckModel<T>) getSelectionModel();
-                checkModel.scanTree((MFXCheckTreeItem<T>) getRoot());
+                TreeCheckModelTree<T> treeCheckModel = (TreeCheckModelTree<T>) getSelectionModel();
+                treeCheckModel.scanTree((MFXCheckTreeItem<T>) getRoot());
             }
         });
     }
@@ -114,11 +115,11 @@ public class MFXCheckTreeItem<T> extends MFXTreeItem<T> {
     //================================================================================
 
     /**
-     * Overridden to return the ICheckModel instance of the MFXCheckTreeView.
+     * Overridden to return the ITreeCheckModel instance of the MFXCheckTreeView.
      */
     @Override
-    public ICheckModel<T> getSelectionModel() {
-        return (ICheckModel<T>) super.getSelectionModel();
+    public ITreeCheckModel<T> getSelectionModel() {
+        return (ITreeCheckModel<T>) super.getSelectionModel();
     }
 
     /**

+ 9 - 7
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXCheckTreeView.java

@@ -18,10 +18,12 @@
 
 package io.github.palexdev.materialfx.controls;
 
+import io.github.palexdev.materialfx.selection.TreeCheckModelTree;
+
 /**
  * This is the container for a tree made of MFXCheckTreeItems.
  * <p>
- * Note: this could also work with other item classes since the CheckModel extends SelectionModel,
+ * Note: this could also work with other item classes since the TreeCheckModelTree extends TreeTreeSelectionModel,
  * but of course it is not recommended to do so.
  * @param <T> The type of the data within the items.
  */
@@ -40,8 +42,8 @@ public class MFXCheckTreeView<T> extends MFXTreeView<T> {
     //================================================================================
     // Methods
     //================================================================================
-    public CheckModel<T> getCheckModel() {
-        return (CheckModel<T>) super.getSelectionModel();
+    public TreeCheckModelTree<T> getCheckModel() {
+        return (TreeCheckModelTree<T>) super.getSelectionModel();
     }
 
     //================================================================================
@@ -49,14 +51,14 @@ public class MFXCheckTreeView<T> extends MFXTreeView<T> {
     //================================================================================
 
     /**
-     * Overridden method to install a CheckModel.
+     * Overridden method to install a TreeCheckModelTree.
      * <p>
      * By default it is set to allow multiple selection.
      */
     @Override
     protected void installSelectionModel() {
-        CheckModel<T> checkModel = new CheckModel<>();
-        checkModel.setAllowsMultipleSelection(true);
-        setSelectionModel(checkModel);
+        TreeCheckModelTree<T> treeCheckModel = new TreeCheckModelTree<>();
+        treeCheckModel.setAllowsMultipleSelection(true);
+        setSelectionModel(treeCheckModel);
     }
 }

+ 47 - 0
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXComboBox.java

@@ -20,6 +20,7 @@ package io.github.palexdev.materialfx.controls;
 
 import io.github.palexdev.materialfx.MFXResourcesLoader;
 import io.github.palexdev.materialfx.controls.enums.ComboBoxStyles;
+import io.github.palexdev.materialfx.selection.ComboSelectionModelMock;
 import io.github.palexdev.materialfx.skins.MFXComboBoxSkin;
 import javafx.beans.property.DoubleProperty;
 import javafx.beans.property.ObjectProperty;
@@ -37,7 +38,12 @@ public class MFXComboBox<T> extends Control {
     private final ObjectProperty<T> selectedValue = new SimpleObjectProperty<>();
     private final ObjectProperty<ObservableList<T>> items = new SimpleObjectProperty<>(FXCollections.observableArrayList());
 
+    private final DoubleProperty maxPopupWidth = new SimpleDoubleProperty(150);
     private final DoubleProperty maxPopupHeight = new SimpleDoubleProperty(200);
+    private final DoubleProperty popupXOffset = new SimpleDoubleProperty(0);
+    private final DoubleProperty popupYOffset = new SimpleDoubleProperty(1);
+
+    private final ComboSelectionModelMock<T> mockSelection;
 
     public MFXComboBox() {
         this(FXCollections.observableArrayList());
@@ -54,6 +60,7 @@ public class MFXComboBox<T> extends Control {
     public MFXComboBox(ObservableList<T> items, ComboBoxStyles style) {
         this.STYLESHEET = MFXResourcesLoader.load(style.getStyleSheetPath()).toString();
         this.items.set(items);
+        this.mockSelection = new ComboSelectionModelMock<>(this);
 
         initialize();
     }
@@ -86,6 +93,18 @@ public class MFXComboBox<T> extends Control {
         this.items.set(items);
     }
 
+    public double getMaxPopupWidth() {
+        return maxPopupWidth.get();
+    }
+
+    public DoubleProperty maxPopupWidthProperty() {
+        return maxPopupWidth;
+    }
+
+    public void setMaxPopupWidth(double maxPopupWidth) {
+        this.maxPopupWidth.set(maxPopupWidth);
+    }
+
     public double getMaxPopupHeight() {
         return maxPopupHeight.get();
     }
@@ -98,6 +117,34 @@ public class MFXComboBox<T> extends Control {
         this.maxPopupHeight.set(maxPopupHeight);
     }
 
+    public double getPopupXOffset() {
+        return popupXOffset.get();
+    }
+
+    public DoubleProperty popupXOffsetProperty() {
+        return popupXOffset;
+    }
+
+    public void setPopupXOffset(double popupXOffset) {
+        this.popupXOffset.set(popupXOffset);
+    }
+
+    public double getPopupYOffset() {
+        return popupYOffset.get();
+    }
+
+    public DoubleProperty popupYOffsetProperty() {
+        return popupYOffset;
+    }
+
+    public void setPopupYOffset(double popupYOffset) {
+        this.popupYOffset.set(popupYOffset);
+    }
+
+    public ComboSelectionModelMock<T> getSelectionModel() {
+        return mockSelection;
+    }
+
     @Override
     protected Skin<?> createDefaultSkin() {
         return new MFXComboBoxSkin<>(this);

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

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

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

@@ -21,8 +21,9 @@ package io.github.palexdev.materialfx.controls;
 import io.github.palexdev.materialfx.MFXResourcesLoader;
 import io.github.palexdev.materialfx.controls.base.AbstractMFXTreeCell;
 import io.github.palexdev.materialfx.controls.base.AbstractMFXTreeItem;
-import io.github.palexdev.materialfx.controls.base.ISelectionModel;
 import io.github.palexdev.materialfx.controls.cell.MFXSimpleTreeCell;
+import io.github.palexdev.materialfx.selection.ITreeSelectionModel;
+import io.github.palexdev.materialfx.selection.TreeTreeSelectionModel;
 import io.github.palexdev.materialfx.skins.MFXTreeItemSkin;
 import javafx.beans.property.*;
 import javafx.collections.ListChangeListener;
@@ -49,7 +50,7 @@ import java.util.List;
  * To change it you have to override the method inline or by extending this class.
  * @see AbstractMFXTreeCell
  * @see MFXTreeView
- * @see ISelectionModel
+ * @see ITreeSelectionModel
  * @param <T> The type of the data within TreeItem.
  */
 public class MFXTreeItem<T> extends AbstractMFXTreeItem<T> {
@@ -89,7 +90,7 @@ public class MFXTreeItem<T> extends AbstractMFXTreeItem<T> {
      * Adds a listener to the items list to update the added/removed item parent accordingly.
      * <p>
      * Adds a listener to {@link #selectedProperty()} and the {@link #treeViewProperty()} allowing item selection before the Scene is shown
-     * by calling the SelectionModel {@link SelectionModel#scanTree(AbstractMFXTreeItem)} method.
+     * by calling the TreeTreeSelectionModel {@link TreeTreeSelectionModel#scanTree(AbstractMFXTreeItem)} method.
      * <p>
      * Adds a listener to {@link #childrenMarginProperty()} to request layout in case it changes.
      */
@@ -249,7 +250,7 @@ public class MFXTreeItem<T> extends AbstractMFXTreeItem<T> {
      * Retrieves the selection model instance from the TreeView which contains the tree.
      */
     @Override
-    public ISelectionModel<T> getSelectionModel() {
+    public ITreeSelectionModel<T> getSelectionModel() {
         if (getTreeView() != null) {
             return getTreeView().getSelectionModel();
         }
@@ -308,7 +309,7 @@ public class MFXTreeItem<T> extends AbstractMFXTreeItem<T> {
     @Override
     public String toString() {
         String className = getClass().getName();
-        String simpleName = className.substring(className.lastIndexOf('.')+1);
+        String simpleName = className.substring(className.lastIndexOf('.') + 1);
         StringBuilder sb = new StringBuilder();
         sb.append("[").append(simpleName);
         sb.append('@');

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

@@ -20,7 +20,8 @@ package io.github.palexdev.materialfx.controls;
 
 import io.github.palexdev.materialfx.MFXResourcesLoader;
 import io.github.palexdev.materialfx.controls.base.AbstractMFXTreeItem;
-import io.github.palexdev.materialfx.controls.base.ISelectionModel;
+import io.github.palexdev.materialfx.selection.ITreeSelectionModel;
+import io.github.palexdev.materialfx.selection.TreeTreeSelectionModel;
 import javafx.beans.property.BooleanProperty;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.SimpleBooleanProperty;
@@ -45,7 +46,7 @@ public class MFXTreeView<T> extends MFXScrollPane {
     private final String STYLESHEET = MFXResourcesLoader.load("css/mfx-treeview.css").toString();
 
     private final ObjectProperty<AbstractMFXTreeItem<T>> root = new SimpleObjectProperty<>(null);
-    private final ObjectProperty<ISelectionModel<T>> selectionModel = new SimpleObjectProperty<>(null);
+    private final ObjectProperty<ITreeSelectionModel<T>> selectionModel = new SimpleObjectProperty<>(null);
     private final BooleanProperty showRoot = new SimpleBooleanProperty(true);
 
     //================================================================================
@@ -120,7 +121,7 @@ public class MFXTreeView<T> extends MFXScrollPane {
      * By default it is set to allow multiple selection.
      */
     protected void installSelectionModel() {
-        ISelectionModel<T> selectionModel = new SelectionModel<>();
+        ITreeSelectionModel<T> selectionModel = new TreeTreeSelectionModel<>();
         selectionModel.setAllowsMultipleSelection(true);
         setSelectionModel(selectionModel);
     }
@@ -137,15 +138,15 @@ public class MFXTreeView<T> extends MFXScrollPane {
         this.root.set(root);
     }
 
-    public ISelectionModel<T> getSelectionModel() {
+    public ITreeSelectionModel<T> getSelectionModel() {
         return selectionModel.get();
     }
 
-    public ObjectProperty<ISelectionModel<T>> selectionModelProperty() {
+    public ObjectProperty<ITreeSelectionModel<T>> selectionModelProperty() {
         return selectionModel;
     }
 
-    public void setSelectionModel(ISelectionModel<T> selectionModel) {
+    public void setSelectionModel(ITreeSelectionModel<T> selectionModel) {
         this.selectionModel.set(selectionModel);
     }
 

+ 3 - 2
materialfx/src/main/java/io/github/palexdev/materialfx/controls/base/AbstractMFXTreeItem.java

@@ -19,6 +19,7 @@
 package io.github.palexdev.materialfx.controls.base;
 
 import io.github.palexdev.materialfx.controls.MFXTreeView;
+import io.github.palexdev.materialfx.selection.ITreeSelectionModel;
 import io.github.palexdev.materialfx.utils.TreeItemStream;
 import javafx.beans.property.*;
 import javafx.collections.FXCollections;
@@ -46,7 +47,7 @@ import java.util.List;
  * <p></p>
  * @see AbstractMFXTreeCell
  * @see MFXTreeView
- * @see ISelectionModel
+ * @see ITreeSelectionModel
  * @param <T> The type of the data within TreeItem.
  */
 public abstract class AbstractMFXTreeItem<T> extends Control {
@@ -73,7 +74,7 @@ public abstract class AbstractMFXTreeItem<T> extends Control {
     //================================================================================
     // Abstract Methods
     //================================================================================
-    public abstract ISelectionModel<T> getSelectionModel();
+    public abstract ITreeSelectionModel<T> getSelectionModel();
     protected abstract void defaultCellFactory();
     protected abstract void updateChildrenParent(List<? extends AbstractMFXTreeItem<T>> treeItems, final AbstractMFXTreeItem<T> newParent);
 

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

@@ -26,6 +26,8 @@ public enum FontResources {
     ANGLE_LEFT("mfx-angle-left", '\uE91c'),
     ANGLE_RIGHT("mfx-angle-right", '\uE91d'),
     ANGLE_UP("mfx-angle-up", '\uE91e'),
+    ARROW_BACK("mfx-arrow-back", '\uE925'),
+    ARROW_FORWARD("mfx-arrow-forward", '\uE926'),
     CALENDAR_BLACK("mfx-calendar-black", '\uE904'),
     CALENDAR_SEMI_BLACK("mfx-calendar-semi-black", '\uE905'),
     CALENDAR_WHITE("mfx-calendar-white", '\uE906'),
@@ -42,11 +44,15 @@ public enum FontResources {
     EXCLAMATION_CIRCLE("mfx-exclamation-circle", '\uE917'),
     EXCLAMATION_TRIANGLE("mfx-exclamation-triangle", '\uE918'),
     EXPAND("mfx-expand", '\uE919'),
+    FIRST_PAGE("mfx-first-page", '\uE927'),
     GOOGLE("mfx-google", '\uE90a'),
     INFO_CIRCLE("mfx-info-circle", '\uE91a'),
+    LAST_PAGE("mfx-last-page", '\uE928'),
     MINUS("mfx-minus", '\uE901'),
     MINUS_CIRCLE("mfx-minus-circle", '\uE90c'),
     MODENA_MARK("mfx-modena-mark", '\uE90d'),
+    STEP_BACKWARD("mfx-step-backward", '\uE923'),
+    STEP_FORWARD("mfx-step-forward", '\uE924'),
     VARIANT3_MARK("mfx-variant3-mark", '\uE90e'),
     VARIANT4_MARK("mfx-variant4-mark", '\uE90f'),
     VARIANT5_MARK("mfx-variant5-mark", '\uE910'),

+ 86 - 0
materialfx/src/main/java/io/github/palexdev/materialfx/selection/ComboSelectionModelMock.java

@@ -0,0 +1,86 @@
+/*
+ *     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.selection;
+
+import io.github.palexdev.materialfx.controls.MFXComboBox;
+import javafx.beans.property.ReadOnlyIntegerWrapper;
+import javafx.beans.property.ReadOnlyObjectWrapper;
+
+public class ComboSelectionModelMock<T> {
+    private final MFXComboBox<T> comboBox;
+    private final ReadOnlyObjectWrapper<T> selectedItem = new ReadOnlyObjectWrapper<>(null);
+    private final ReadOnlyIntegerWrapper selectedIndex = new ReadOnlyIntegerWrapper(-1);
+
+    public ComboSelectionModelMock(MFXComboBox<T> comboBox) {
+        this.comboBox = comboBox;
+
+        selectedItem.addListener((observable, oldValue, newValue) -> System.out.println(newValue));
+        selectedIndex.addListener((observable, oldValue, newValue) -> System.out.println(newValue));
+    }
+
+    public void clearSelection() {
+        selectedItem.set(null);
+        selectedIndex.set(-1);
+    }
+
+    public void selectItem(T item) {
+        if (!comboBox.getItems().contains(item)) {
+            return;
+        }
+        selectedItem.set(item);
+    }
+
+    public void selectFirst() {
+        selectedIndex.set(0);
+    }
+
+    public void selectNext() {
+        if (getSelectedIndex() == (comboBox.getItems().size() - 1)) {
+            return;
+        }
+        selectedIndex.add(1);
+    }
+
+    public void selectLast() {
+        selectedIndex.set(comboBox.getItems().size());
+    }
+
+    public void selectPrevious() {
+        if (getSelectedIndex() == -1) {
+            return;
+        }
+        selectedIndex.subtract(1);
+    }
+
+    public int getSelectedIndex() {
+        return selectedIndex.get();
+    }
+
+    public ReadOnlyIntegerWrapper selectedIndexProperty() {
+        return selectedIndex;
+    }
+
+    public T getSelectedItem() {
+        return selectedItem.get();
+    }
+
+    public ReadOnlyObjectWrapper<T> selectedItemProperty() {
+        return selectedItem;
+    }
+}

+ 2 - 2
materialfx/src/main/java/io/github/palexdev/materialfx/controls/base/ICheckModel.java → materialfx/src/main/java/io/github/palexdev/materialfx/selection/ITreeCheckModel.java

@@ -16,7 +16,7 @@
  *     along with MaterialFX.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-package io.github.palexdev.materialfx.controls.base;
+package io.github.palexdev.materialfx.selection;
 
 import io.github.palexdev.materialfx.controls.MFXCheckTreeItem;
 import javafx.beans.property.ListProperty;
@@ -26,7 +26,7 @@ import static io.github.palexdev.materialfx.controls.MFXCheckTreeItem.CheckTreeI
 /**
  * Public API used by any MFXCheckTreeView.
  */
-public interface ICheckModel<T> extends ISelectionModel<T> {
+public interface ITreeCheckModel<T> extends ITreeSelectionModel<T> {
     void scanTree(MFXCheckTreeItem<T> item);
     void check(MFXCheckTreeItem<T> item, CheckTreeItemEvent<?> event);
     void clearChecked();

+ 3 - 2
materialfx/src/main/java/io/github/palexdev/materialfx/controls/base/ISelectionModel.java → materialfx/src/main/java/io/github/palexdev/materialfx/selection/ITreeSelectionModel.java

@@ -16,15 +16,16 @@
  *     along with MaterialFX.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-package io.github.palexdev.materialfx.controls.base;
+package io.github.palexdev.materialfx.selection;
 
+import io.github.palexdev.materialfx.controls.base.AbstractMFXTreeItem;
 import javafx.beans.property.ListProperty;
 import javafx.scene.input.MouseEvent;
 
 /**
  * Public API used by any MFXTreeView.
  */
-public interface ISelectionModel<T> {
+public interface ITreeSelectionModel<T> {
     void scanTree(AbstractMFXTreeItem<T> item);
     void select(AbstractMFXTreeItem<T> item, MouseEvent mouseEvent);
     void clearSelection();

+ 6 - 6
materialfx/src/main/java/io/github/palexdev/materialfx/controls/CheckModel.java → materialfx/src/main/java/io/github/palexdev/materialfx/selection/TreeCheckModelTree.java

@@ -16,10 +16,10 @@
  *     along with MaterialFX.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-package io.github.palexdev.materialfx.controls;
+package io.github.palexdev.materialfx.selection;
 
+import io.github.palexdev.materialfx.controls.MFXCheckTreeItem;
 import io.github.palexdev.materialfx.controls.base.AbstractMFXTreeItem;
-import io.github.palexdev.materialfx.controls.base.ICheckModel;
 import io.github.palexdev.materialfx.utils.TreeItemStream;
 import javafx.beans.property.ListProperty;
 import javafx.beans.property.SimpleListProperty;
@@ -33,9 +33,9 @@ import java.util.stream.Collectors;
 import static io.github.palexdev.materialfx.controls.MFXCheckTreeItem.CheckTreeItemEvent;
 
 /**
- * Concrete implementation of the {@code ICheckModel} class.
+ * Concrete implementation of the {@code ITreeCheckModel} class.
  * <p>
- * This provides common methods for items check. Also, since it extends SelectionModel it also provides
+ * This provides common methods for items check. Also, since it extends TreeTreeSelectionModel it also provides
  * all the methods for items selection.
  * <p>
  * The check should be handled internally only. This is because the mechanism is kind of tricky.
@@ -43,7 +43,7 @@ import static io.github.palexdev.materialfx.controls.MFXCheckTreeItem.CheckTreeI
  * you can see that when the checkbox is fired, a CHECK_EVENT is fired and "travels" up to the root. Each item then calls
  * {@link #check(MFXCheckTreeItem, CheckTreeItemEvent)}.
  */
-public class CheckModel<T> extends SelectionModel<T> implements ICheckModel<T> {
+public class TreeCheckModelTree<T> extends TreeTreeSelectionModel<T> implements ITreeCheckModel<T> {
     //================================================================================
     // Properties
     //================================================================================
@@ -52,7 +52,7 @@ public class CheckModel<T> extends SelectionModel<T> implements ICheckModel<T> {
     //================================================================================
     // Constructors
     //================================================================================
-    public CheckModel() {
+    public TreeCheckModelTree() {
         super();
         checkedItems.addListener((ListChangeListener<MFXCheckTreeItem<T>>) change -> {
             List<MFXCheckTreeItem<T>> tmpRemoved = new ArrayList<>();

+ 6 - 6
materialfx/src/main/java/io/github/palexdev/materialfx/controls/SelectionModel.java → materialfx/src/main/java/io/github/palexdev/materialfx/selection/TreeTreeSelectionModel.java

@@ -16,10 +16,10 @@
  *     along with MaterialFX.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-package io.github.palexdev.materialfx.controls;
+package io.github.palexdev.materialfx.selection;
 
+import io.github.palexdev.materialfx.controls.MFXTreeItem;
 import io.github.palexdev.materialfx.controls.base.AbstractMFXTreeItem;
-import io.github.palexdev.materialfx.controls.base.ISelectionModel;
 import io.github.palexdev.materialfx.utils.TreeItemStream;
 import javafx.beans.property.ListProperty;
 import javafx.beans.property.SimpleListProperty;
@@ -31,16 +31,16 @@ import java.util.ArrayList;
 import java.util.List;
 
 /**
- * Concrete implementation of the {@code ISelectionModel} class.
+ * Concrete implementation of the {@code ITreeSelectionModel} class.
  * <p>
  * This provides common methods for items selection.
  * <p>
- * To select an item it should call the SelectionModel associated with the tree which contains the item
+ * To select an item it should call the TreeTreeSelectionModel associated with the tree which contains the item
  * with {@link AbstractMFXTreeItem#getSelectionModel()} and call the {@link #select(AbstractMFXTreeItem, MouseEvent)} method.
  * In the constructor a listener is added to the ListProperty of this class, which contains all the selected items, and
  * its role is to change the selected property of the item.
  */
-public class SelectionModel<T> implements ISelectionModel<T> {
+public class TreeTreeSelectionModel<T> implements ITreeSelectionModel<T> {
     //================================================================================
     // Properties
     //================================================================================
@@ -50,7 +50,7 @@ public class SelectionModel<T> implements ISelectionModel<T> {
     //================================================================================
     // Constructors
     //================================================================================
-    public SelectionModel() {
+    public TreeTreeSelectionModel() {
         selectedItems.addListener((ListChangeListener<AbstractMFXTreeItem<T>>) change -> {
             List<AbstractMFXTreeItem<T>> tmpRemoved = new ArrayList<>();
             List<AbstractMFXTreeItem<T>> tmpAdded = new ArrayList<>();

+ 3 - 2
materialfx/src/main/java/io/github/palexdev/materialfx/skins/MFXCheckTreeItemSkin.java

@@ -21,6 +21,7 @@ package io.github.palexdev.materialfx.skins;
 import io.github.palexdev.materialfx.controls.MFXCheckTreeItem;
 import io.github.palexdev.materialfx.controls.base.AbstractMFXTreeCell;
 import io.github.palexdev.materialfx.controls.cell.MFXCheckTreeCell;
+import io.github.palexdev.materialfx.selection.TreeCheckModelTree;
 import javafx.scene.control.CheckBox;
 
 import static io.github.palexdev.materialfx.controls.MFXCheckTreeItem.CheckTreeItemEvent;
@@ -28,7 +29,7 @@ import static io.github.palexdev.materialfx.controls.MFXCheckTreeItem.CheckTreeI
 /**
  * This is the implementation of the {@code Skin} associated with every {@link MFXCheckTreeItemSkin}.
  * @see MFXCheckTreeItem
- * @see io.github.palexdev.materialfx.controls.CheckModel
+ * @see TreeCheckModelTree
  */
 public class MFXCheckTreeItemSkin<T> extends MFXTreeItemSkin<T> {
     //================================================================================
@@ -45,7 +46,7 @@ public class MFXCheckTreeItemSkin<T> extends MFXTreeItemSkin<T> {
     //================================================================================
 
     /**
-     * Adds a listener for handling CHECK_EVENTs and call {@link io.github.palexdev.materialfx.controls.CheckModel#check(MFXCheckTreeItem, CheckTreeItemEvent)}.
+     * Adds a listener for handling CHECK_EVENTs and call {@link TreeCheckModelTree#check(MFXCheckTreeItem, CheckTreeItemEvent)}.
      */
     private void setListeners() {
         MFXCheckTreeItem<T> item = (MFXCheckTreeItem<T>) getSkinnable();

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

@@ -92,7 +92,15 @@ public class MFXComboBoxSkin<T> extends SkinBase<MFXComboBox<T>> {
 
         icon.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
             if (!popup.isShowing()) {
-                Point2D point = NodeUtils.pointRelativeTo(comboBox, listView, HPos.CENTER, VPos.BOTTOM, 0, 2, false);
+                Point2D point = NodeUtils.pointRelativeTo(
+                        comboBox,
+                        listView,
+                        HPos.CENTER,
+                        VPos.BOTTOM,
+                        comboBox.getPopupXOffset(),
+                        comboBox.getPopupYOffset(),
+                        false
+                );
                 popup.show(comboBox, snapPositionX(point.getX()), snapPositionY(point.getY()));
             } else {
                 popup.hide();
@@ -114,6 +122,28 @@ public class MFXComboBoxSkin<T> extends SkinBase<MFXComboBox<T>> {
             }
         });
         listView.maxHeightProperty().bind(comboBox.maxPopupHeightProperty());
+        listView.maxWidthProperty().bind(comboBox.maxPopupWidthProperty());
+
+        listView.getSelectionModel().selectedIndexProperty().addListener((observable, oldValue, newValue) -> comboBox.getSelectionModel().selectedIndexProperty().set(newValue.intValue()));
+        listView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> comboBox.getSelectionModel().selectedItemProperty().set(newValue));
+        comboBox.getSelectionModel().selectedIndexProperty().addListener((observable, oldValue, newValue) -> {
+            if (newValue.intValue() != oldValue.intValue()) {
+                if (newValue.intValue() == -1) {
+                    listView.getSelectionModel().clearSelection();
+                } else {
+                    listView.getSelectionModel().select(newValue.intValue());
+                }
+            }
+        });
+        comboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
+            if (newValue != oldValue) {
+                if (newValue == null) {
+                    listView.getSelectionModel().clearSelection();
+                } else {
+                    listView.getSelectionModel().select(newValue);
+                }
+            }
+        });
 
         comboBox.getScene().addEventFilter(MouseEvent.MOUSE_PRESSED, popupHandler);
         comboBox.sceneProperty().addListener((observable, oldValue, newValue) -> {

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

@@ -22,6 +22,7 @@ import io.github.palexdev.materialfx.controls.MFXTreeItem;
 import io.github.palexdev.materialfx.controls.base.AbstractMFXTreeCell;
 import io.github.palexdev.materialfx.controls.base.AbstractMFXTreeItem;
 import io.github.palexdev.materialfx.controls.factories.MFXAnimationFactory;
+import io.github.palexdev.materialfx.selection.TreeTreeSelectionModel;
 import io.github.palexdev.materialfx.utils.NodeUtils;
 import javafx.animation.*;
 import javafx.collections.FXCollections;
@@ -200,7 +201,7 @@ public class MFXTreeItemSkin<T> extends SkinBase<MFXTreeItem<T>> {
      * the behavior is to expand/collapse the item only if the mouse was pressed on the disclosure node.
      * <p>
      * If that is not the case then we trigger the selection, retrieve the selection model and select the item.
-     * @see io.github.palexdev.materialfx.controls.SelectionModel
+     * @see TreeTreeSelectionModel
      */
     private void setListeners() {
         MFXTreeItem<T> item = getSkinnable();

+ 6 - 0
materialfx/src/main/java/io/github/palexdev/materialfx/utils/StringUtils.java

@@ -86,6 +86,12 @@ public class StringUtils {
                 + string.substring(index + substring.length());
     }
 
+    public static String replaceIndex(String string, int startIndex, int endIndex, String replacement) {
+        StringBuilder sb = new StringBuilder(string);
+        sb.replace(startIndex, endIndex, replacement);
+        return sb.toString();
+    }
+
     public static String titleCaseWord(String str) {
         if (str.length() > 0) {
             int firstChar = str.codePointAt(0);

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

@@ -18,6 +18,7 @@ module MaterialFX.materialfx.main {
     exports io.github.palexdev.materialfx.effects;
     exports io.github.palexdev.materialfx.font;
     exports io.github.palexdev.materialfx.notifications;
+    exports io.github.palexdev.materialfx.selection;
     exports io.github.palexdev.materialfx.skins;
     exports io.github.palexdev.materialfx.skins.legacy;
     exports io.github.palexdev.materialfx.utils;

+ 3 - 3
materialfx/src/main/resources/io/github/palexdev/materialfx/css/mfx-spinner.css → materialfx/src/main/resources/io/github/palexdev/materialfx/css/mfx-progress-spinner.css

@@ -16,15 +16,15 @@
  *     along with MaterialFX.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-.mfx-spinner:determinate .arc {
+.mfx-progress-spinner:determinate .arc {
     -fx-stroke: #0F9D58;
 }
 
-.mfx-spinner .percentage {
+.mfx-progress-spinner .percentage {
     -fx-font-family: "Comfortaa Medium";
     -fx-font-smoothing-type: gray;
 }
 
-.mfx-spinner:determinate .percentage {
+.mfx-progress-spinner:determinate .percentage {
     -fx-fill: #4d4d4d;
 }

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