diff --git a/.gitattributes b/.gitattributes
new file mode 100755
index 00000000..f6b1c4ce
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,34 @@
+# Java sources
+*.java text diff=java
+*.kt text diff=java
+*.groovy text diff=java
+*.scala text diff=java
+*.gradle text diff=java
+*.gradle.kts text diff=java
+
+# These files are text and should be normalized (Convert crlf => lf)
+*.css text diff=css
+*.scss text diff=css
+*.sass text
+*.df text
+*.htm text diff=html
+*.html text diff=html
+*.js text
+*.jsp text
+*.jspf text
+*.jspx text
+*.properties text
+*.tld text
+*.tag text
+*.tagx text
+*.xml text
+
+# These files are binary and should be left untouched
+# (binary is a macro for -text -diff)
+*.class binary
+*.dll binary
+*.ear binary
+*.jar binary
+*.so binary
+*.war binary
+*.jks binary
\ No newline at end of file
diff --git a/.github/workflows/FUNDING.yml b/.github/workflows/FUNDING.yml
new file mode 100644
index 00000000..2a7c2148
--- /dev/null
+++ b/.github/workflows/FUNDING.yml
@@ -0,0 +1,13 @@
+# These are supported funding model platforms
+
+github: palexdev
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
old mode 100644
new mode 100755
diff --git a/.gitignore b/.gitignore
old mode 100644
new mode 100755
index 4771c16e..e19d7bd5
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ out/
# Others
materialfx/src/test
demo/scenicView.properties
+scenicView.properties
diff --git a/.run/MaterialFX [build].run.xml b/.run/MaterialFX [build].run.xml
old mode 100644
new mode 100755
diff --git a/.run/MaterialFX [clean build].run.xml b/.run/MaterialFX [clean build].run.xml
new file mode 100644
index 00000000..b2a0a586
--- /dev/null
+++ b/.run/MaterialFX [clean build].run.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+
+
+
\ No newline at end of file
diff --git a/.run/MaterialFX [clean].run.xml b/.run/MaterialFX [clean].run.xml
old mode 100644
new mode 100755
diff --git a/.run/MaterialFX [jlinkZip].run.xml b/.run/MaterialFX [jlinkZip].run.xml
old mode 100644
new mode 100755
diff --git a/.run/MaterialFX [materialfx_uploadArchives].run.xml b/.run/MaterialFX [materialfx_uploadArchives].run.xml
old mode 100644
new mode 100755
diff --git a/.run/MaterialFX [run].run.xml b/.run/MaterialFX [run].run.xml
old mode 100644
new mode 100755
diff --git a/.run/MaterialFX [testrun].run.xml b/.run/MaterialFX [testrun].run.xml
old mode 100644
new mode 100755
diff --git a/.run/Run Tests.run.xml b/.run/Run Tests.run.xml
deleted file mode 100644
index a834b2fb..00000000
--- a/.run/Run Tests.run.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..e2780305
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,67 @@
+# Changelog
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+(Date format is dd-MM-yyyy)
+
+## Type of Changes
+- **Added** for new features.
+- **Changed** for changes in existing functionality.
+- **Deprecated** for soon-to-be removed features.
+- **Removed** for now removed features.
+- **Fixed** for any bug fixes.
+
+## [11.13.0] - 22-01-2022
+_This version won't follow the above scheme as the amount of changes and commits is simply too huge and there would be no
+way to correctly show all the changes without making mistakes (duplicates, "overlapping" changes...), for this reason
+I'll try to sum up only the major changes below._
+
+- The demo has been completely remade
+- Added new beans and properties
+- Added new mechanisms for bindings
+- Added new collections, in particular an ObservableList that combines the capabilities of JavaFX's FilteredList and SortedList.
+Also those two are read-only, but MaterialFX also offers a version that allows to directly make changes to the source list
+- ReactFX, Flowless removed in favor of my own Virtual Flow implementation, VirtualizedFX. As a result all controls having lists have been reworked.
+- The table view has been reworked as well to use a Virtual Flow (scrollable) so it's efficiency is now on a whole new level. There's also a paginated
+version of the table (like before) but it still uses a Virtual Flow, which of course makes it efficient
+- MFXLabels have been deprecated and removed as now MaterialFX follows Material Design's text fields. The new MFXTextFields are the best thing you're gonna see
+on JavaFX. They have all the features defined by Material Design principles and also more. They now offer a floating label that can have 4 states: disabled, above, border, inline.
+Oh, they can also be set to behave like Labels, no need to have duplicate controls (Label and TextFields), since they offer the same functionalities duh.
+- Many controls (such as combo boxes, date pickers) now extends MFXTextField, so they inherit all its features.
+- Almost all controls have been reviewed/remade to make them fully functional (there were a lot of issues with CSS not working properly).
+- The Filter API has been reviewed and now it's more powerful than ever with the new MFXFilterPane.
+- The Validation API has been reviewed as well, it is as powerful as before thanks to JavaFX properties and observables, but it's much more flexible. It's up to the user now
+to decide when and how to validate a control. Also an important design choice has been made here. Many validation frameworks for JavaFX also offer a way to decorate a control,
+but I decided to not to that as it would violate the Single Responsibility Principle! Validation has nothing to do with UI, plus depending on the fanciness of your App it's up to
+you to decide how the validation controls will look like!
+- Dialogs and Notifications have been reviewed as well. The dialogs have been simplified, and for notifications there are now two separate systems.
+- The date picker is now on a whole new level, it's been remade from scratch and it's simply beautiful, powerful and versatile.
+- There are also new components! MFXPopup is a PopupControl that actually works. Ever tried to style a PopupControl but no matter what the CSS would not work?
+Do not worry about that never again, just use MFXPopup it's super easy thanks to my custom MFXCSSBridge (check documentation would be too much to write here haha).
+MFXPagination has been made for MFXPaginatedTableViews, remade from scratch (meaning that doesn't extend Pagination) with a stunning modern look.
+MFXTooltip, an alternative to JavaFX's tooltip, much more versatile!
+- MFXRippleGenerator has been deprecated. The ripple generation is organized to be a new API, meaning that there are now interfaces and a base abstract class from which
+you can implement new ripple generators. The new default implementation is MFXCircleRippleGenerator. The new API also allows you to create new Ripples by implementing the IRipple interface.
+It's a rather advanced API tbh, but hey, it's there, who knows maybe someday I'll need it to be like this.
+- MFXHLoader and MFXVLoader are no more. The loading API has been "extracted" to be independent from UI. MFXLoader has the same capabilities as the aforementioned controls
+but it's not a Node. It's up to the user to decide how to manage the loaded views, and how to translate the loaded beans to a Node. (see the documentation and the DemoController for an example
+on how to easily create a nav-bar even with the new API)
+- The Selection API has been reviewed as well. It also supports the "extend selection" behavior when "Shift" is pressed, like you would expect from a file manager.
+- There is an insane amount of new utilities, for JavaFX as well as for Java
+
+Again, let me **apologize** for this messy changelist, but I promise from next version changes will be tracked properly!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LICENSE b/LICENSE
old mode 100644
new mode 100755
diff --git a/README.md b/README.md
old mode 100644
new mode 100755
index 6d8455de..b14c2b2b
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
-
+
@@ -18,7 +18,7 @@
MaterialFX
- MaterialFX is an open source Java library which provides material components for JavaFX
+ MaterialFX is an open source Java library which provides material design components for JavaFX
Explore the wiki »
@@ -35,12 +35,14 @@
## Table of Contents
* [About the Project and History of JavaFX](#about-the-project-and-history-of-javafx)
+* [About the Logo](#about-the-logo)
* [Some GIFs](#preview-gifs)
* [Getting Started](#getting-started)
* [Build](#build)
* [Usage](#usage)
* [Gradle](#gradle)
* [Maven](#maven)
+* [Changelog](#changelog)
* [Roadmap](#roadmap)
* [Contributing](#contributing)
* [License](#license)
@@ -74,104 +76,102 @@ That's where this project comes in. The aim of my project is to bring components
Google's material design guidelines to JavaFX. The second purpose is to provide a successor to the already
available [JFoenix](https://github.com/jfoenixadmin/JFoenix) library, which is a bit old and has a lot of issues.
-In recent months the project has evolved a lot, to the point that it is no longer a simple substitute.
-To date MaterialFX offers not only restyled controls, but also: new and unique controls such as the Stepper, controls completely redone from scratch such as ComboBoxes or TableViews (and many others), and many utilities for JavaFX and Java (NodeUtils, ColorUtils, StringUtils ...).
+In recent months the project has evolved a lot, to the point that it is no longer a simple substitute.
+To date MaterialFX offers not only restyled controls, but also: new and unique controls such as the Stepper,
+controls completely redone from scratch such as ComboBoxes or TableViews (and many others),
+and many utilities for JavaFX and Java (NodeUtils, ColorUtils, StringUtils ...).
+
+
+
+## About The Logo
+MaterialFX v11.13.0 brought a lot of fixes and new features, but it also brought a new logo, something that is more
+meaningful for me and that somewhat represents the new version.
+The new logo is a Phoenix, the immortal bird from Greek mythology, associated to regeneration/rebirth.
+When a Phoenix dies it obtains new life by raising from its ashes.
+MaterialFX v11.13.0 fixed many critical bugs and broken features, I like to think that it is reborn from
+the previous version, so I thought a new logo would have been a good idea.
## Preview GIFs
-#### Imgur Link: [Gallery](https://gifyu.com/album/cU3)
+#### Imgur Link: [Gallery](https://imgur.com/a/IrDirnI)
Buttons
-
+
-CheckBoxes
+Check Boxes, Radio Buttons and Toggles
-
+
-ComboBoxes
+Combo Boxes
-
-
-
-
-
-DatePickers
-
-
+
Dialogs
-
+
-
-
-Labels
-
-
-
-
-ListViews
+Fields
-
+
-Notifications
+Lists
-
+
-ProgressBars
+Notifications
-
+
-ProgressSpinners
+Pickers
-
+
-RadioButtons
+Progress
-
+
-ScrollPanes
+Scroll Panes
-
+
Sliders
-
+
@@ -179,39 +179,17 @@ To date MaterialFX offers not only restyled controls, but also: new and unique c
Stepper
-
+
-TableViews
+Tables
-
+
-
-TextFields
-
-
-
-
-
-
-Toggles
-
-
-
-
-
-
-FontResources
-
-
-
-
-
-
## Getting Started
@@ -229,7 +207,10 @@ To run the main demo, execute the following command:
gradlew run
-**NOTE** : MaterialFX requires **Java 11** and above.
+**NOTE**: MaterialFX requires **Java 11** and above.
+
+**NOTE**: Starting from version 11.14.0 (next major version), MaterialFX will transition to
+Java 17 and bump version to 17.x.x. What will happen to version 11 is still to be decided
### Usage
@@ -241,7 +222,7 @@ repositories {
}
dependencies {
-implementation 'io.github.palexdev:materialfx:11.12.0'
+implementation 'io.github.palexdev:materialfx:11.13.0'
}
```
@@ -251,15 +232,20 @@ implementation 'io.github.palexdev:materialfx:11.12.0'
io.github.palexdev
materialfx
- 11.12.0
+ 11.13.0
```
+## Changelog
+See the [CHANGELOG](https://github.com/palexdev/MaterialFX/blob/main/CHANGELOG.md) file for a list of changes per version.
+
+
+
## Roadmap
-See the [open issues](https://github.com/palexdev/MaterialFX/issues) for a list of proposed features (and known issues).
+See the [Open Issues](https://github.com/palexdev/MaterialFX/issues) for a list of proposed features (and known issues).
See the [ROADMAP](https://github.com/palexdev/MaterialFX/blob/main/ROADMAP.md) for a list of implemented and upcoming features.
@@ -294,10 +280,14 @@ Project Link: [https://github.com/palexdev/MaterialFX](https://github.com/palexd
#### Donation
-Ever since I was a kid I have always liked programming, I find it interesting and often funny too, however it can also
-be a difficult and stressful job at times. This is my first public project, and I'm dedicating a lot of time to it. This
-is an open source library of course and everyone can use it for free, but if you feel like it you can make a small
-donation here. [![Donate](https://img.shields.io/badge/$-support-green.svg?style=flat-square)](https://bit.ly/31XB8zD)
+It's been more than a year since I started developing MaterialFX. Implementing cool looking, fully functional controls,
+introducing new components and features as well as providing many utilities for JavaFX and Java is really hard,
+especially considering that developing for JavaFX also means to deal with its closeness, its bugs, its annoying
+design decisions. Many times I've honestly been on the verge of giving up because sometimes it's really too much
+stress to handle.
+**But**, today MaterialFX is a great library, supported by many people and I'm proud of it.
+If you are using MaterialFX in your projects and feel like it, I recently activated [GitHub Sponsors](https://github.com/sponsors/palexdev) so
+you can easily donate/sponsor.
@@ -308,6 +298,10 @@ donation here. [![Donate](https://img.shields.io/badge/$-support-green.svg?style
- Yiding He
- Alaa Abu Zidan
- Ultraviolet-Ninja
-- *Your name can be here by supporting me at this link, [Support](https://bit.ly/31XB8zD)*
+- Sourabh Bhat
+- Mohammad Chaudhry (thank you very much for the huge donation, YOU are the legend)
+- Yahia Rehab
+- Mauro de Wit
+- *Your name can be here by supporting me at this link, [GitHub Sponsors](https://github.com/sponsors/palexdev)*
Thank you very very much to all supporters, to all people who contribute to the project, to all people that thanked me, you really made my day
diff --git a/ROADMAP.md b/ROADMAP.md
old mode 100644
new mode 100755
index 88977509..cf9c491b
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -14,4 +14,5 @@
- [ ] *Scrollable MFXTableView*
- [ ] *MFXToastNotification*
-- [ ] *Improve Notification System(?)*
\ No newline at end of file
+- [ ] *Improve Notification System(?)*
+- [ ] *Introduce StringConverters for listviews' cells too?*
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
old mode 100644
new mode 100755
index a682079c..d531ed2b
--- a/build.gradle
+++ b/build.gradle
@@ -1,22 +1,31 @@
+buildscript {
+ repositories {
+ gradlePluginPortal()
+ }
+
+ dependencies {
+ classpath 'org.javamodularity:moduleplugin:1.8.10' // Workaround for broken javafxplugin
+ }
+}
+
plugins {
id 'java-library'
- id 'org.openjfx.javafxplugin' version '0.0.10' apply false
+ id 'org.openjfx.javafxplugin' version '0.0.11' apply false
}
group 'io.github.palexdev'
-version '11.12.0'
+version '11.13.0'
repositories {
mavenCentral()
- jcenter()
}
subprojects {
apply plugin: 'org.openjfx.javafxplugin'
javafx {
- version = "16"
- modules = [ 'javafx.controls', 'javafx.fxml', 'javafx.media', 'javafx.swing', 'javafx.web' ]
+ version = "17.0.1"
+ modules = ['javafx.controls', 'javafx.fxml', 'javafx.media', 'javafx.swing', 'javafx.web']
}
}
diff --git a/demo/build.gradle b/demo/build.gradle
old mode 100644
new mode 100755
index 284ea628..c51e3d0d
--- a/demo/build.gradle
+++ b/demo/build.gradle
@@ -1,11 +1,10 @@
plugins {
id 'application'
- id 'org.beryx.jlink' version '2.23.3'
+ id 'org.beryx.jlink' version '2.24.4'
}
repositories {
mavenCentral()
- jcenter()
flatDir {
dirs "${project(':demo').projectDir}/libs"
@@ -14,14 +13,18 @@ repositories {
dependencies {
testImplementation "org.testfx:testfx-core:4.0.16-alpha"
- testImplementation "junit:junit:4.13.1"
- testImplementation "org.testfx:testfx-junit:4.0.16-alpha"
+ testImplementation "org.testfx:testfx-junit5:4.0.16-alpha"
+
+ testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
+ testImplementation 'org.junit.platform:junit-platform-suite-api:1.8.1'
+ testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
implementation name: 'scenicview'
implementation "fr.brouillard.oss:cssfx:11.4.0"
implementation 'org.kordamp.ikonli:ikonli-core:12.2.0'
implementation 'org.kordamp.ikonli:ikonli-javafx:12.2.0'
implementation 'org.kordamp.ikonli:ikonli-fontawesome5-pack:12.2.0'
+ implementation 'io.github.palexdev:virtualizedfx:11.2.4'
implementation project(':materialfx')
}
@@ -43,4 +46,8 @@ jlink {
jpackage {
imageOptions = ['--icon', 'src/main/resources/logo.ico']
}
+}
+
+test {
+ useJUnitPlatform()
}
\ No newline at end of file
diff --git a/demo/libs/scenicview.jar b/demo/libs/scenicview.jar
old mode 100644
new mode 100755
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/Demo.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/Demo.java
index de660103..478d86ed 100644
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/Demo.java
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/Demo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Parisi Alessandro
+ * Copyright (C) 2022 Parisi Alessandro
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
*
* MaterialFX is free software: you can redistribute it and/or modify
@@ -22,45 +22,26 @@
import io.github.palexdev.materialfx.demo.controllers.DemoController;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
+import javafx.scene.Parent;
import javafx.scene.Scene;
-import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
-import java.io.IOException;
-
public class Demo extends Application {
- private double xOffset;
- private double yOffset;
-
- @Override
- public void start(Stage primaryStage) throws IOException {
- CSSFX.start();
-
- FXMLLoader fxmlLoader = new FXMLLoader(MFXDemoResourcesLoader.loadURL("Demo.fxml"));
- fxmlLoader.setControllerFactory(controller -> new DemoController(primaryStage, getHostServices()));
- StackPane demoPane = fxmlLoader.load();
-
- demoPane.setOnMousePressed(event -> {
- xOffset = primaryStage.getX() - event.getScreenX();
- yOffset = primaryStage.getY() - event.getScreenY();
- });
- demoPane.setOnMouseDragged(event -> {
- primaryStage.setX(event.getScreenX() + xOffset);
- primaryStage.setY(event.getScreenY() + yOffset);
- });
-
- primaryStage.setTitle("MaterialFX Demo - Features Preview");
- primaryStage.initStyle(StageStyle.TRANSPARENT);
- Scene scene = new Scene(demoPane);
- scene.setFill(Color.TRANSPARENT);
- primaryStage.setScene(scene);
- primaryStage.show();
- }
-
- public static void main(String[] args) {
- launch(args);
- }
+ @Override
+ public void start(Stage primaryStage) throws Exception {
+ CSSFX.start();
+
+ FXMLLoader loader = new FXMLLoader(MFXDemoResourcesLoader.loadURL("fxml/Demo.fxml"));
+ loader.setControllerFactory(c -> new DemoController(primaryStage));
+ Parent root = loader.load();
+ Scene scene = new Scene(root);
+ scene.setFill(Color.TRANSPARENT);
+ primaryStage.initStyle(StageStyle.TRANSPARENT);
+ primaryStage.setScene(scene);
+ primaryStage.setTitle("MaterialFX Demo");
+ primaryStage.show();
+ }
}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/MFXDemoResourcesLoader.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/MFXDemoResourcesLoader.java
old mode 100644
new mode 100755
index a446942b..41dde53e
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/MFXDemoResourcesLoader.java
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/MFXDemoResourcesLoader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Parisi Alessandro
+ * Copyright (C) 2022 Parisi Alessandro
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
*
* MaterialFX is free software: you can redistribute it and/or modify
@@ -27,19 +27,19 @@
*/
public class MFXDemoResourcesLoader {
- private MFXDemoResourcesLoader() {
- }
+ private MFXDemoResourcesLoader() {
+ }
- public static URL loadURL(String path) {
- return MFXDemoResourcesLoader.class.getResource(path);
- }
+ public static URL loadURL(String path) {
+ return MFXDemoResourcesLoader.class.getResource(path);
+ }
- public static String load(String path) {
- return loadURL(path).toString();
- }
+ public static String load(String path) {
+ return loadURL(path).toString();
+ }
- public static InputStream loadStream(String name) {
- return MFXDemoResourcesLoader.class.getResourceAsStream(name);
- }
+ public static InputStream loadStream(String name) {
+ return MFXDemoResourcesLoader.class.getResourceAsStream(name);
+ }
}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/TestDemo.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/TestDemo.java
deleted file mode 100644
index 5413c49e..00000000
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/TestDemo.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-package io.github.palexdev.materialfx.demo;
-
-import io.github.palexdev.materialfx.controls.MFXButton;
-import io.github.palexdev.materialfx.controls.MFXIconWrapper;
-import io.github.palexdev.materialfx.controls.MFXRectangleToggleNode;
-import io.github.palexdev.materialfx.controls.MFXTableView;
-import io.github.palexdev.materialfx.controls.cell.MFXTableColumn;
-import io.github.palexdev.materialfx.controls.cell.MFXTableRowCell;
-import io.github.palexdev.materialfx.demo.model.FilterablePerson;
-import io.github.palexdev.materialfx.font.FontResources;
-import io.github.palexdev.materialfx.font.MFXFontIcon;
-import io.github.palexdev.materialfx.utils.ColorUtils;
-import javafx.application.Application;
-import javafx.geometry.Pos;
-import javafx.scene.Scene;
-import javafx.scene.layout.HBox;
-import javafx.scene.layout.VBox;
-import javafx.stage.Stage;
-import org.scenicview.ScenicView;
-
-import java.util.Random;
-
-@SuppressWarnings("All")
-public class TestDemo extends Application {
- private final Random random = new Random(System.currentTimeMillis());
-
- @Override
- public void start(Stage primaryStage) {
- VBox box = new VBox(100);
- box.setAlignment(Pos.CENTER);
-
- HBox bbox = new HBox(20);
- bbox.setAlignment(Pos.CENTER);
-
- MFXButton b1 = new MFXButton("Set Leading");
- MFXButton b2 = new MFXButton("Set Trailing");
- MFXButton b3 = new MFXButton("Set Graphic");
- MFXButton b4 = new MFXButton("Remove Graphic");
- MFXButton b5 = new MFXButton("Remove Label Graphic");
- MFXRectangleToggleNode rtn = new MFXRectangleToggleNode("");
- rtn.setPrefSize(32, 32);
- rtn.setAlignment(Pos.CENTER);
-
- b1.setOnAction(event -> {
- rtn.setLabelLeadingIcon(MFXFontIcon.getRandomIcon(12, ColorUtils.getRandomColor()));
- });
- b2.setOnAction(event -> {
- rtn.setLabelTrailingIcon(MFXFontIcon.getRandomIcon(12, ColorUtils.getRandomColor()));
- });
- b3.setOnAction(event -> {
- rtn.setGraphic(MFXFontIcon.getRandomIcon(12, ColorUtils.getRandomColor()));
- });
- b4.setOnAction(event -> {
- rtn.setGraphic(null);
- });
- b5.setOnAction(event -> {
- rtn.setLabelLeadingIcon(null);
- rtn.setLabelTrailingIcon(null);
- });
-
- bbox.getChildren().addAll(b1, b2, b3, b4, b5);
-
- box.getChildren().addAll(rtn, bbox);
- box.getStylesheets().add(MFXDemoResourcesLoader.load("css/TestDemo.css"));
-
- Scene scene = new Scene(box, 800, 600);
- primaryStage.setScene(scene);
- primaryStage.show();
-
- ScenicView.show(scene);
- }
-
- public static void main(String[] args) {
- launch(args);
- }
-
- private static boolean isInvalidCharacter(char c) {
- if (c == 0x7F) return true;
- if (c == 0xA) return true;
- if (c == 0x9) return true;
- return c < 0x20;
- }
-
- public MFXIconWrapper getRandomIcon(double size) {
- FontResources[] resources = FontResources.values();
- String desc = resources[random.nextInt(resources.length)].getDescription();
- return new MFXIconWrapper(new MFXFontIcon(desc, size, ColorUtils.getRandomColor()), size * 1.5);
- }
-
- private void setupTable(MFXTableView tableView) {
- MFXTableColumn firstName = new MFXTableColumn<>("FName");
- MFXTableColumn lastName = new MFXTableColumn<>("LName");
- MFXTableColumn address = new MFXTableColumn<>("Address");
- MFXTableColumn age = new MFXTableColumn<>("age");
-
- firstName.setRowCellFunction(person -> new MFXTableRowCell(person.firstNameProperty()));
- lastName.setRowCellFunction(person -> new MFXTableRowCell(person.lastNameProperty()));
- address.setRowCellFunction(person -> new MFXTableRowCell(person.addressProperty()));
- age.setRowCellFunction(person -> new MFXTableRowCell(person.ageProperty().asString()));
-
- tableView.getTableColumns().addAll(firstName, lastName, address, age);
- //tableView.setItems(people);
- }
-
- public String randStr() {
- int leftLimit = 97; // letter 'a'
- int rightLimit = 122; // letter 'z'
- int targetStringLength = 30;
- return random.ints(leftLimit, rightLimit + 1)
- .limit(targetStringLength)
- .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
- .toString();
- }
-}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TogglesController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ChecksRadiosToggleController.java
similarity index 53%
rename from demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TogglesController.java
rename to demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ChecksRadiosToggleController.java
index 6088647f..8826ead2 100644
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TogglesController.java
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ChecksRadiosToggleController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Parisi Alessandro
+ * Copyright (C) 2022 Parisi Alessandro
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
*
* MaterialFX is free software: you can redistribute it and/or modify
@@ -22,34 +22,43 @@
import io.github.palexdev.materialfx.controls.MFXToggleButton;
import io.github.palexdev.materialfx.font.MFXFontIcon;
import io.github.palexdev.materialfx.utils.ColorUtils;
+import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
+import javafx.scene.paint.Color;
import java.net.URL;
import java.util.ResourceBundle;
-public class TogglesController implements Initializable {
+public class ChecksRadiosToggleController implements Initializable {
- @FXML
- private MFXToggleButton toggleButton;
+ @FXML
+ private MFXToggleButton customToggle;
- @FXML
- private MFXRectangleToggleNode rec1;
+ @FXML
+ private MFXRectangleToggleNode r1;
- @FXML
- private MFXRectangleToggleNode rec2;
+ @FXML
+ private MFXRectangleToggleNode r2;
- @Override
- public void initialize(URL location, ResourceBundle resources) {
- rec1.setLabelLeadingIcon(MFXFontIcon.getRandomIcon(16, ColorUtils.getRandomColor()));
- rec1.setLabelTrailingIcon(MFXFontIcon.getRandomIcon(16, ColorUtils.getRandomColor()));
- rec2.setLabelLeadingIcon(MFXFontIcon.getRandomIcon(16, ColorUtils.getRandomColor()));
- rec2.setLabelTrailingIcon(MFXFontIcon.getRandomIcon(16, ColorUtils.getRandomColor()));
- }
+ @FXML
+ private MFXRectangleToggleNode r3;
- @FXML
- private void handleButtonClick() {
- toggleButton.setToggleColor(ColorUtils.getRandomColor());
- toggleButton.setSelected(false);
- }
+ @FXML
+ private void changeColors(ActionEvent event) {
+ customToggle.setColors(ColorUtils.getRandomColor(), ColorUtils.getRandomColor());
+ customToggle.setSelected(false);
+ }
+
+ @Override
+ public void initialize(URL location, ResourceBundle resources) {
+ r1.setLabelLeadingIcon(MFXFontIcon.getRandomIcon(16, Color.BLACK));
+ r1.setLabelTrailingIcon(MFXFontIcon.getRandomIcon(16, Color.BLACK));
+
+ r2.setLabelLeadingIcon(MFXFontIcon.getRandomIcon(16, Color.BLACK));
+ r2.setLabelTrailingIcon(MFXFontIcon.getRandomIcon(16, Color.BLACK));
+
+ r3.setLabelLeadingIcon(MFXFontIcon.getRandomIcon(16, Color.BLACK));
+ r3.setLabelTrailingIcon(MFXFontIcon.getRandomIcon(16, Color.BLACK));
+ }
}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ComboBoxesController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ComboBoxesController.java
new file mode 100644
index 00000000..21ae3b2b
--- /dev/null
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ComboBoxesController.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+package io.github.palexdev.materialfx.demo.controllers;
+
+import io.github.palexdev.materialfx.controls.MFXComboBox;
+import io.github.palexdev.materialfx.controls.MFXFilterComboBox;
+import io.github.palexdev.materialfx.controls.MFXTooltip;
+import io.github.palexdev.materialfx.controls.legacy.MFXLegacyComboBox;
+import io.github.palexdev.materialfx.demo.model.Model;
+import io.github.palexdev.materialfx.demo.model.Person;
+import io.github.palexdev.materialfx.utils.StringUtils;
+import io.github.palexdev.materialfx.utils.others.FunctionalStringConverter;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.util.StringConverter;
+
+import java.net.URL;
+import java.util.ResourceBundle;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public class ComboBoxesController implements Initializable {
+
+ @FXML
+ private MFXLegacyComboBox lCombo;
+
+ @FXML
+ private MFXLegacyComboBox lCustCombo;
+
+ @FXML
+ private MFXComboBox nBFCombo;
+
+ @FXML
+ private MFXComboBox nCombo;
+
+ @FXML
+ private MFXComboBox nCustCombo;
+
+ @FXML
+ private MFXComboBox nEditCombo;
+
+ @FXML
+ private MFXComboBox nNFCombo;
+
+ @FXML
+ private MFXFilterComboBox filterCombo;
+
+ @FXML
+ private MFXFilterComboBox custFilterCombo;
+
+ @Override
+ public void initialize(URL location, ResourceBundle resources) {
+ ObservableList strings = Model.strings;
+ ObservableList people = Model.people;
+
+ lCombo.setItems(strings);
+ lCustCombo.setItems(strings);
+
+ nCombo.setItems(strings);
+ nCustCombo.setItems(strings);
+ nEditCombo.setItems(strings);
+ nBFCombo.setItems(strings);
+ nNFCombo.setItems(strings);
+
+ nEditCombo.setOnCancel(s -> nEditCombo.setText(nEditCombo.getSelectedItem()));
+ nEditCombo.setOnCommit(s -> {
+ if (!strings.contains(s)) {
+ strings.add(s);
+ }
+ nEditCombo.selectItem(s);
+ });
+
+ MFXTooltip.of(
+ nEditCombo,
+ """
+ This combo box allows you to add new items to the list (no duplicates allowed) when pressing Enter.
+ It also allows to restore the previous selected item by pressing Ctrl+Shift+Z.
+ Both key strokes are default for all MFXComboBoxes but the action to perform must be configured by the user.
+ This combo box is also set to scroll to the selected item when opening the popup.
+ """
+ ).install();
+
+ StringConverter converter = FunctionalStringConverter.converter(
+ s -> {
+ throw new UnsupportedOperationException();
+ },
+ person -> person.getName() + " " + person.getSurname()
+ );
+ Function> filterFunction = s -> person -> StringUtils.containsIgnoreCase(converter.toString(person), s);
+ filterCombo.setItems(people);
+ filterCombo.setConverter(converter);
+ filterCombo.setFilterFunction(filterFunction);
+ custFilterCombo.setItems(people);
+ custFilterCombo.setConverter(converter);
+ custFilterCombo.setFilterFunction(filterFunction);
+ custFilterCombo.setResetOnPopupHidden(false);
+ }
+}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ComboBoxesDemoController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ComboBoxesDemoController.java
deleted file mode 100644
index 86321626..00000000
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ComboBoxesDemoController.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-package io.github.palexdev.materialfx.demo.controllers;
-
-import io.github.palexdev.materialfx.controls.MFXCheckbox;
-import io.github.palexdev.materialfx.controls.MFXComboBox;
-import io.github.palexdev.materialfx.controls.MFXFilterComboBox;
-import io.github.palexdev.materialfx.controls.legacy.MFXLegacyComboBox;
-import io.github.palexdev.materialfx.demo.model.SimplePerson;
-import io.github.palexdev.materialfx.utils.BindingUtils;
-import javafx.collections.FXCollections;
-import javafx.collections.ObservableList;
-import javafx.fxml.FXML;
-import javafx.fxml.Initializable;
-import javafx.scene.control.Label;
-import javafx.scene.paint.Color;
-import org.kordamp.ikonli.javafx.FontIcon;
-
-import java.net.URL;
-import java.util.List;
-import java.util.ResourceBundle;
-
-public class ComboBoxesDemoController implements Initializable {
-
- @FXML
- private MFXLegacyComboBox standard;
-
- @FXML
- private MFXLegacyComboBox lineColors;
-
- @FXML
- private MFXLegacyComboBox editable;
-
- @FXML
- private MFXLegacyComboBox labels;
-
- @FXML
- private MFXLegacyComboBox validated;
-
- @FXML
- private MFXLegacyComboBox customized;
-
- @FXML
- private MFXCheckbox checkbox;
-
- @FXML
- private MFXComboBox style1;
-
- @FXML
- private MFXComboBox style2;
-
- @FXML
- private MFXComboBox style3;
-
- @FXML
- private MFXComboBox validatedNew;
-
- @FXML
- private MFXFilterComboBox filters1;
-
- @FXML
- private MFXFilterComboBox filters2;
-
- @FXML
- private MFXFilterComboBox filters3;
-
- @FXML
- private MFXFilterComboBox filtersValidated;
-
- @Override
- public void initialize(URL location, ResourceBundle resources) {
- ObservableList stringList = FXCollections.observableArrayList(List.of(
- "String 0",
- "String 1",
- "String 2",
- "String 3",
- "String 4",
- "String 5",
- "String 6",
- "String 7"
- ));
-
- ObservableList personList = FXCollections.observableArrayList(
- new SimplePerson("Jack Nicholson"),
- new SimplePerson("Marlon Brando"),
- new SimplePerson("Robert De Niro"),
- new SimplePerson("Al Pacino"),
- new SimplePerson("Daniel Day-Lewis"),
- new SimplePerson("Dustin Hoffman"),
- new SimplePerson("Tom Hanks"),
- new SimplePerson("Anthony Hopkins"),
- new SimplePerson("Paul Newman"),
- new SimplePerson("Denzel Washington"),
- new SimplePerson("Spencer Tracy"),
- new SimplePerson("Laurence Olivier"),
- new SimplePerson("Jack Lemmon"),
- new SimplePerson("Jeff Bridges"),
- new SimplePerson("James Stewart"),
- new SimplePerson("Sean Penn"),
- new SimplePerson("Michael Caine"),
- new SimplePerson("Morgan Freeman"),
- new SimplePerson("Robert Duvall"),
- new SimplePerson("Gene Hackman"),
- new SimplePerson("Clint Eastwood"),
- new SimplePerson("Gregory Peck"),
- new SimplePerson("Robin Williams"),
- new SimplePerson("Ben Kingsley"),
- new SimplePerson("Philip Seymour Hoffman")
- );
-
- ObservableList labelsList = FXCollections.observableArrayList(List.of(
- new Label("Label 0", createIcon("fas-home")),
- new Label("Label 1", createIcon("fas-star")),
- new Label("Label 2", createIcon("fas-heart")),
- new Label("Label 3", createIcon("fas-cocktail")),
- new Label("Label 4", createIcon("fas-anchor")),
- new Label("Label 5", createIcon("fas-bolt")),
- new Label("Label 6", createIcon("fas-bug")),
- new Label("Label 7", createIcon("fas-beer"))
- ));
-
- standard.setItems(stringList);
- lineColors.setItems(stringList);
- labels.setItems(labelsList);
- editable.setItems(stringList);
- validated.setItems(stringList);
- customized.setItems(stringList);
-
- editable.setEditable(true);
- validated.getValidator().add(checkbox.selectedProperty(), "Checkbox is not selected!");
-
- style1.setItems(stringList);
- style2.setItems(stringList);
- style3.setItems(labelsList);
- validatedNew.setItems(personList);
- validatedNew.setValidated(true);
- validatedNew.getValidator().add(BindingUtils.toProperty(validatedNew.getSelectionModel().selectedIndexProperty().isNotEqualTo(-1)), "A value must be selected");
- validatedNew.getValidator().add(checkbox.selectedProperty(), "Checkbox must be selected");
-
- filters1.setItems(personList);
- filters2.setItems(personList);
- filters3.setItems(stringList);
- filtersValidated.setItems(personList);
- filtersValidated.setValidated(true);
- filtersValidated.getValidator().add(BindingUtils.toProperty(filtersValidated.getSelectionModel().selectedIndexProperty().isNotEqualTo(-1)), "A value must be selected");
- filtersValidated.getValidator().add(checkbox.selectedProperty(), "Checkbox must be selected");
-
- filters3.setForceFieldFocusOnShow(true);
- }
-
- private FontIcon createIcon(String s) {
- FontIcon icon = new FontIcon(s);
- icon.setIconColor(Color.PURPLE);
- icon.setIconSize(13);
- return icon;
- }
-}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/DemoController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/DemoController.java
index bb540808..c3218944 100644
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/DemoController.java
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/DemoController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Parisi Alessandro
+ * Copyright (C) 2022 Parisi Alessandro
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
*
* MaterialFX is free software: you can redistribute it and/or modify
@@ -18,279 +18,135 @@
package io.github.palexdev.materialfx.demo.controllers;
-import io.github.palexdev.materialfx.beans.MFXLoaderBean.Builder;
-import io.github.palexdev.materialfx.controls.*;
-import io.github.palexdev.materialfx.demo.MFXDemoResourcesLoader;
+import io.github.palexdev.materialfx.controls.MFXIconWrapper;
+import io.github.palexdev.materialfx.controls.MFXRectangleToggleNode;
+import io.github.palexdev.materialfx.controls.MFXScrollPane;
import io.github.palexdev.materialfx.font.MFXFontIcon;
-import io.github.palexdev.materialfx.utils.AnimationUtils;
-import io.github.palexdev.materialfx.utils.AnimationUtils.KeyFrames;
-import io.github.palexdev.materialfx.utils.NodeUtils;
import io.github.palexdev.materialfx.utils.ScrollUtils;
-import javafx.animation.KeyFrame;
-import javafx.animation.KeyValue;
-import javafx.animation.ParallelTransition;
-import javafx.application.HostServices;
+import io.github.palexdev.materialfx.utils.ToggleButtonsUtil;
+import io.github.palexdev.materialfx.utils.others.loader.MFXLoader;
+import io.github.palexdev.materialfx.utils.others.loader.MFXLoaderBean;
+import javafx.application.Platform;
+import javafx.css.PseudoClass;
import javafx.fxml.FXML;
-import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
-import javafx.geometry.Insets;
import javafx.geometry.Pos;
-import javafx.scene.Scene;
-import javafx.scene.control.Label;
-import javafx.scene.image.ImageView;
-import javafx.scene.input.KeyCode;
-import javafx.scene.input.KeyEvent;
+import javafx.scene.control.ToggleButton;
+import javafx.scene.control.ToggleGroup;
import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
-import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
-import javafx.scene.media.Media;
-import javafx.scene.media.MediaPlayer;
-import javafx.scene.paint.Color;
-import javafx.scene.text.TextFlow;
-import javafx.stage.Modality;
import javafx.stage.Stage;
-import javafx.util.Duration;
-import java.io.IOException;
import java.net.URL;
+import java.util.List;
import java.util.ResourceBundle;
-public class DemoController implements Initializable {
- private final Stage primaryStage;
- private final HostServices hostServices;
-
- private MFXButton opNavButton;
- private ParallelTransition openNav;
- private ParallelTransition closeNav;
- private boolean isNavShown = false;
-
- private final MediaPlayer m1;
- private final MediaPlayer m2;
-
- @FXML
- private StackPane demoPane;
-
- @FXML
- private HBox windowButtons;
-
- @FXML
- private StackPane navBar;
-
- @FXML
- private MFXScrollPane scrollPane;
-
- @FXML
- private MFXVLoader vLoader;
-
- @FXML
- private StackPane contentPane;
-
- @FXML
- private VBox logoPane;
-
- @FXML
- private ImageView logo;
-
- @FXML
- private Label splashLabel1;
-
- @FXML
- private Label splashLabel2;
-
- @FXML
- private Label splashLabel3;
-
- @FXML
- private TextFlow version;
-
- public DemoController(Stage primaryStage, HostServices hostServices) {
- m1 = new MediaPlayer(new Media(MFXDemoResourcesLoader.load("assets/welcome1.wav")));
- m2 = new MediaPlayer(new Media(MFXDemoResourcesLoader.load("assets/welcome2.wav")));
-
- m1.setVolume(0.3);
- m2.setVolume(0.2);
-
- this.primaryStage = primaryStage;
- this.hostServices = hostServices;
- }
-
- @Override
- public void initialize(URL location, ResourceBundle resources) {
- // Icons
- MFXFontIcon xIcon = new MFXFontIcon("mfx-x-circle", 16);
- MFXFontIcon minusIcon = new MFXFontIcon("mfx-minus-circle", 16);
- MFXFontIcon expandIcon = new MFXFontIcon("mfx-expand", 12.5);
- MFXFontIcon infoIcon = new MFXFontIcon("mfx-info-circle", 30, Color.rgb(75, 181, 255));
- MFXFontIcon angleIcon = new MFXFontIcon("mfx-angle-right", 20);
-
- // Buttons
- MFXIconWrapper closeButton = new MFXIconWrapper(xIcon, 22);
- closeButton.setId("closeButton");
- closeButton.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> primaryStage.close());
-
- MFXIconWrapper minimizeButton = new MFXIconWrapper(minusIcon, 22);
- minimizeButton.setId("minimizeButton");
- minimizeButton.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> primaryStage.setIconified(true));
-
- MFXIconWrapper expandButton = new MFXIconWrapper(expandIcon, 22);
- expandButton.setId("expandButton");
- expandButton.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> primaryStage.setFullScreen(!primaryStage.isFullScreen()));
-
- MFXIconWrapper infoButton = new MFXIconWrapper(infoIcon, 30).defaultRippleGeneratorBehavior();
- infoButton.setId("infoButton");
- infoButton.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> showInfo());
-
- opNavButton = new MFXButton("");
- opNavButton.setOpacity(0.0);
- opNavButton.setDisable(true);
- opNavButton.setId("navButton");
- opNavButton.setPrefSize(25, 25);
- opNavButton.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
- opNavButton.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> animate());
-
- // Graphics
- opNavButton.setGraphic(angleIcon);
-
- // Layout and Utils
- StackPane.setAlignment(infoButton, Pos.BOTTOM_RIGHT);
- StackPane.setMargin(infoButton, new Insets(0, 8, 8, 0));
- StackPane.setAlignment(opNavButton, Pos.CENTER_LEFT);
- StackPane.setMargin(opNavButton, new Insets(0, 0, 0, 4));
-
- NodeUtils.makeRegionCircular(closeButton);
- NodeUtils.makeRegionCircular(minimizeButton);
- NodeUtils.makeRegionCircular(expandButton);
- NodeUtils.makeRegionCircular(infoButton);
- NodeUtils.makeRegionCircular(opNavButton);
-
- // Add all
- windowButtons.getChildren().addAll(expandButton, minimizeButton, closeButton);
- demoPane.getChildren().addAll(infoButton, opNavButton);
-
- // VLoader
- vLoader.setContentPane(contentPane);
- vLoader.addItem("BUTTONS", Builder.build(new MFXRectangleToggleNode("BUTTONS"), MFXDemoResourcesLoader.loadURL("ButtonsDemo.fxml")).setDefaultRoot(true));
- vLoader.addItem("CHECKBOXES", Builder.build(new MFXRectangleToggleNode("CHECKBOXES"), MFXDemoResourcesLoader.loadURL("CheckBoxesDemo.fxml")));
- vLoader.addItem("COMBOBOXES", Builder.build(new MFXRectangleToggleNode("COMBOBOXES"), MFXDemoResourcesLoader.loadURL("ComboBoxesDemo.fxml")));
- vLoader.addItem("DATEPICKERS", Builder.build(new MFXRectangleToggleNode("DATEPICKERS"), MFXDemoResourcesLoader.loadURL("DatePickersDemo.fxml")));
- vLoader.addItem("DIALOGS", Builder.build(new MFXRectangleToggleNode("DIALOGS"), MFXDemoResourcesLoader.loadURL("DialogsDemo.fxml")).setControllerFactory(controller -> new DialogsController(demoPane)));
- vLoader.addItem("LABELS", Builder.build(new MFXRectangleToggleNode("LABELS"), MFXDemoResourcesLoader.loadURL("LabelsDemo.fxml")));
- vLoader.addItem("LISTVIEWS", Builder.build(new MFXRectangleToggleNode("LISTVIEWS"), MFXDemoResourcesLoader.loadURL("ListViewsDemo.fxml")));
- vLoader.addItem("NOTIFICATIONS", Builder.build(new MFXRectangleToggleNode("NOTIFICATIONS"), MFXDemoResourcesLoader.loadURL("NotificationsDemo.fxml")));
- vLoader.addItem("PROGRESS_BARS", Builder.build(new MFXRectangleToggleNode("PROGRESS BARS"), MFXDemoResourcesLoader.loadURL("ProgressBarsDemo.fxml")));
- vLoader.addItem("PROGRESS_SPINNERS", Builder.build(new MFXRectangleToggleNode("PROGRESS SPINNERS"), MFXDemoResourcesLoader.loadURL("ProgressSpinnersDemo.fxml")));
- vLoader.addItem("RADIOBUTTONS", Builder.build(new MFXRectangleToggleNode("RADIOBUTTONS"), MFXDemoResourcesLoader.loadURL("RadioButtonsDemo.fxml")));
- vLoader.addItem("SCROLLPANES", Builder.build(new MFXRectangleToggleNode("SCROLLPANES"), MFXDemoResourcesLoader.loadURL("ScrollPanesDemo.fxml")));
- vLoader.addItem("SLIDERS", Builder.build(new MFXRectangleToggleNode("SLIDERS"), MFXDemoResourcesLoader.loadURL("SlidersDemo.fxml")));
- vLoader.addItem("STEPPER", Builder.build(new MFXRectangleToggleNode("STEPPER"), MFXDemoResourcesLoader.loadURL("StepperDemo.fxml")));
- vLoader.addItem("TABLEVIEWS", Builder.build(new MFXRectangleToggleNode("TABLEVIEWS"), MFXDemoResourcesLoader.loadURL("TableViewsDemo.fxml")));
- vLoader.addItem("TEXTFIELDS", Builder.build(new MFXRectangleToggleNode("TEXTFIELDS"), MFXDemoResourcesLoader.loadURL("TextFieldsDemo.fxml")));
- vLoader.addItem("TOGGLES", Builder.build(new MFXRectangleToggleNode("TOGGLES"), MFXDemoResourcesLoader.loadURL("ToggleButtonsDemo.fxml")));
- vLoader.addItem("TREEVIEWS", Builder.build(new MFXRectangleToggleNode("TREEVIEWS"), MFXDemoResourcesLoader.loadURL("TreeViewsDemo.fxml")));
- vLoader.addItem("FONTRESOURCES", Builder.build(new MFXRectangleToggleNode("FONTRESOURCES"), MFXDemoResourcesLoader.loadURL("FontResourcesDemo.fxml")));
- vLoader.start();
-
- // Others
- ScrollUtils.addSmoothScrolling(scrollPane, 2);
- ScrollUtils.animateScrollBars(scrollPane, 500, 500);
- primaryStage.sceneProperty().addListener((observable, oldValue, newValue) -> {
- if (newValue != null) {
- Scene scene = primaryStage.getScene();
- scene.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
- if (event.getCode() == KeyCode.F11) {
- primaryStage.setFullScreen(!primaryStage.isFullScreen());
- }
- });
- scene.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
- if (isNavShown) {
- animate();
- }
- });
- }
- });
- navBar.setVisible(false);
- initAnimations();
-
- demoPane.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> demoPane.requestFocus());
-
- primaryStage.setOnShown(event -> presentation());
- }
-
- private void presentation() {
- AnimationUtils.SequentialBuilder.build()
- .add(KeyFrames.of(Duration.ONE, event -> m1.play()))
- .add(AnimationUtils.ParallelBuilder.build().show(1000, logo, version).getAnimation())
- .add(AnimationUtils.TimelineBuilder.build().show(450, splashLabel1).setDelay(200).getAnimation())
- .add(AnimationUtils.TimelineBuilder.build().show(450, splashLabel2).setDelay(50).getAnimation())
- .add(AnimationUtils.TimelineBuilder.build().show(450, splashLabel3).setDelay(50).getAnimation())
- .setOnFinished(event -> AnimationUtils.SequentialBuilder.build()
- .add(KeyFrames.of(300, end -> m2.play()))
- .add(AnimationUtils.TimelineBuilder.build().hide(300, logoPane).setOnFinished(end -> logoPane.setVisible(false)).getAnimation())
- .add(AnimationUtils.ParallelBuilder.build().show(800, contentPane, opNavButton).setOnFinished(end -> opNavButton.setDisable(false)).getAnimation())
- .setDelay(750)
- .getAnimation().play())
- .setDelay(750)
- .getAnimation().play();
-
- }
-
- private void initAnimations() {
- openNav = (ParallelTransition) AnimationUtils.ParallelBuilder.build()
- .show(400, navBar)
- .add(new KeyFrame(Duration.millis(300), new KeyValue(navBar.translateXProperty(), 5)))
- .add(new KeyFrame(Duration.millis(200), new KeyValue(opNavButton.rotateProperty(), -180)))
- .setOnFinished(event -> isNavShown = true)
- .getAnimation();
-
- closeNav = (ParallelTransition) AnimationUtils.ParallelBuilder.build()
- .hide(50, navBar)
- .add(new KeyFrame(Duration.millis(300), new KeyValue(navBar.translateXProperty(), -240)))
- .add(new KeyFrame(Duration.millis(200), new KeyValue(opNavButton.rotateProperty(), 0)))
- .setOnFinished(event -> isNavShown = false)
- .getAnimation();
- }
-
- private void animate() {
- if (!isNavShown) {
- navBar.setVisible(true);
- openNav.play();
- } else {
- closeNav.play();
- }
- }
-
- private void showInfo() {
- MFXDialog infoDialog;
- MFXStageDialog stageDialog;
- try {
- FXMLLoader loader = new FXMLLoader(MFXDemoResourcesLoader.loadURL("InfoDialog.fxml"));
- loader.setControllerFactory(controller -> new InfoController(hostServices));
- infoDialog = loader.load();
- } catch (IOException e) {
- e.printStackTrace();
- return;
- }
-
- // Close Button
- StackPane header = (StackPane) infoDialog.lookup("#headerNode");
-
- MFXFontIcon xIcon = new MFXFontIcon("mfx-x", 8);
- MFXIconWrapper closeButton = new MFXIconWrapper(xIcon, 22).defaultRippleGeneratorBehavior();
- closeButton.setId("closeButton");
- StackPane.setAlignment(closeButton, Pos.TOP_RIGHT);
- StackPane.setMargin(closeButton, new Insets(4, 4, 0, 0));
- NodeUtils.makeRegionCircular(closeButton);
- header.getChildren().add(closeButton);
-
- stageDialog = new MFXStageDialog(infoDialog);
- stageDialog.setScrimBackground(true);
- stageDialog.setOwner(primaryStage);
- stageDialog.setModality(Modality.APPLICATION_MODAL);
- closeButton.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> stageDialog.close());
- stageDialog.setCenterInOwner(true);
- stageDialog.show();
- }
+import static io.github.palexdev.materialfx.demo.MFXDemoResourcesLoader.loadURL;
+public class DemoController implements Initializable {
+ private final Stage stage;
+ private double xOffset;
+ private double yOffset;
+ private final ToggleGroup toggleGroup;
+
+ @FXML
+ private HBox windowHeader;
+
+ @FXML
+ private MFXFontIcon closeIcon;
+
+ @FXML
+ private MFXFontIcon minimizeIcon;
+
+ @FXML
+ private MFXFontIcon alwaysOnTopIcon;
+
+ @FXML
+ private AnchorPane rootPane;
+
+ @FXML
+ private MFXScrollPane scrollPane;
+
+ @FXML
+ private VBox navBar;
+
+ @FXML
+ private StackPane contentPane;
+
+ public DemoController(Stage stage) {
+ this.stage = stage;
+ this.toggleGroup = new ToggleGroup();
+ ToggleButtonsUtil.addAlwaysOneSelectedSupport(toggleGroup);
+ }
+
+ @Override
+ public void initialize(URL location, ResourceBundle resources) {
+ closeIcon.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> Platform.exit());
+ minimizeIcon.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> ((Stage) rootPane.getScene().getWindow()).setIconified(true));
+ alwaysOnTopIcon.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
+ boolean newVal = !stage.isAlwaysOnTop();
+ alwaysOnTopIcon.pseudoClassStateChanged(PseudoClass.getPseudoClass("always-on-top"), newVal);
+ stage.setAlwaysOnTop(newVal);
+ });
+
+ windowHeader.setOnMousePressed(event -> {
+ xOffset = stage.getX() - event.getScreenX();
+ yOffset = stage.getY() - event.getScreenY();
+ });
+ windowHeader.setOnMouseDragged(event -> {
+ stage.setX(event.getScreenX() + xOffset);
+ stage.setY(event.getScreenY() + yOffset);
+ });
+
+ initializeLoader();
+
+ ScrollUtils.addSmoothScrolling(scrollPane);
+ }
+
+ private void initializeLoader() {
+ MFXLoader loader = new MFXLoader();
+ loader.addView(MFXLoaderBean.of("BUTTONS", loadURL("fxml/Buttons.fxml")).setBeanToNodeMapper(() -> createToggle("mfx-circle-dot", "Buttons")).setDefaultRoot(true).get());
+ loader.addView(MFXLoaderBean.of("CHECKS_RADIOS_TOGGLES", loadURL("fxml/ChecksRadiosToggles.fxml")).setBeanToNodeMapper(() -> createToggle("mfx-toggle-on", "Checks, Radios, Toggles")).get());
+ loader.addView(MFXLoaderBean.of("COMBOS", loadURL("fxml/ComboBoxes.fxml")).setBeanToNodeMapper(() -> createToggle("mfx-list-dropdown", "ComboBoxes")).get());
+ loader.addView(MFXLoaderBean.of("DIALOGS", loadURL("fxml/Dialogs.fxml")).setBeanToNodeMapper(() -> createToggle("mfx-messages", "Dialogs")).setControllerFactory(c -> new DialogsController(stage)).get());
+ loader.addView(MFXLoaderBean.of("TEXT-FIELDS", loadURL("fxml/TextFields.fxml")).setBeanToNodeMapper(() -> createToggle("mfx-input-pipe-alt", "Fields")).get());
+ loader.addView(MFXLoaderBean.of("LISTS", loadURL("fxml/ListViews.fxml")).setBeanToNodeMapper(() -> createToggle("mfx-square-list", "Lists")).get());
+ loader.addView(MFXLoaderBean.of("NOTIFICATIONS", loadURL("fxml/Notifications.fxml")).setBeanToNodeMapper(() -> createToggle("mfx-bell", "Notifications")).setControllerFactory(c -> new NotificationsController(stage)).get());
+ loader.addView(MFXLoaderBean.of("PICKERS", loadURL("fxml/Pickers.fxml")).setBeanToNodeMapper(() -> createToggle("mfx-calendars", "Pickers")).get());
+ loader.addView(MFXLoaderBean.of("PROGRESS", loadURL("fxml/Progress.fxml")).setBeanToNodeMapper(() -> createToggle("mfx-progress-bars", "Progress")).get());
+ loader.addView(MFXLoaderBean.of("SCROLL-PANES", loadURL("fxml/ScrollPanes.fxml")).setBeanToNodeMapper(() -> createToggle("mfx-scroll-bar", "Scroll Panes")).get());
+ loader.addView(MFXLoaderBean.of("SLIDERS", loadURL("fxml/Sliders.fxml")).setBeanToNodeMapper(() -> createToggle("mfx-sliders", "Sliders")).get());
+ loader.addView(MFXLoaderBean.of("STEPPER", loadURL("fxml/Stepper.fxml")).setBeanToNodeMapper(() -> createToggle("mfx-stepper", "Stepper")).get());
+ loader.addView(MFXLoaderBean.of("TABLES", loadURL("fxml/TableViews.fxml")).setBeanToNodeMapper(() -> createToggle("mfx-table", "Tables")).get());
+ loader.addView(MFXLoaderBean.of("FONT-RESOURCES", loadURL("fxml/FontResources.fxml")).setBeanToNodeMapper(() -> createToggle("mfx-fonticons", "Font Resources")).get());
+ loader.setOnLoadedAction(beans -> {
+ List nodes = beans.stream()
+ .map(bean -> {
+ ToggleButton toggle = (ToggleButton) bean.getBeanToNodeMapper().get();
+ toggle.setOnAction(event -> contentPane.getChildren().setAll(bean.getRoot()));
+ if (bean.isDefaultView()) {
+ contentPane.getChildren().setAll(bean.getRoot());
+ toggle.setSelected(true);
+ }
+ return toggle;
+ })
+ .toList();
+ navBar.getChildren().setAll(nodes);
+ });
+ loader.start();
+ }
+
+ private ToggleButton createToggle(String icon, String text) {
+ MFXIconWrapper wrapper = new MFXIconWrapper(icon, 24, 32);
+
+ MFXRectangleToggleNode toggleNode = new MFXRectangleToggleNode(text, wrapper);
+ toggleNode.setAlignment(Pos.CENTER_LEFT);
+ toggleNode.setMaxWidth(Double.MAX_VALUE);
+ toggleNode.setToggleGroup(toggleGroup);
+
+ return toggleNode;
+ }
}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/DialogsController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/DialogsController.java
index 5537917f..8d89e79c 100644
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/DialogsController.java
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/DialogsController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Parisi Alessandro
+ * Copyright (C) 2022 Parisi Alessandro
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
*
* MaterialFX is free software: you can redistribute it and/or modify
@@ -19,276 +19,97 @@
package io.github.palexdev.materialfx.demo.controllers;
import io.github.palexdev.materialfx.controls.MFXButton;
-import io.github.palexdev.materialfx.controls.MFXNotification;
-import io.github.palexdev.materialfx.controls.MFXStageDialog;
-import io.github.palexdev.materialfx.controls.SimpleMFXNotificationPane;
-import io.github.palexdev.materialfx.controls.base.AbstractMFXDialog;
-import io.github.palexdev.materialfx.controls.enums.ButtonType;
-import io.github.palexdev.materialfx.controls.enums.DialogType;
-import io.github.palexdev.materialfx.controls.factories.MFXAnimationFactory;
-import io.github.palexdev.materialfx.controls.factories.MFXDialogFactory;
-import io.github.palexdev.materialfx.effects.DepthLevel;
-import io.github.palexdev.materialfx.notifications.NotificationPos;
-import io.github.palexdev.materialfx.notifications.NotificationsManager;
+import io.github.palexdev.materialfx.demo.model.Model;
+import io.github.palexdev.materialfx.dialogs.MFXGenericDialog;
+import io.github.palexdev.materialfx.dialogs.MFXGenericDialogBuilder;
+import io.github.palexdev.materialfx.dialogs.MFXStageDialog;
+import io.github.palexdev.materialfx.enums.ScrimPriority;
+import io.github.palexdev.materialfx.font.MFXFontIcon;
import javafx.application.Platform;
+import javafx.event.ActionEvent;
import javafx.fxml.FXML;
-import javafx.fxml.Initializable;
-import javafx.geometry.Insets;
-import javafx.geometry.Pos;
-import javafx.scene.layout.HBox;
-import javafx.scene.layout.Pane;
-import javafx.scene.layout.Region;
+import javafx.scene.layout.GridPane;
import javafx.stage.Modality;
-import javafx.util.Duration;
-
-import java.net.URL;
-import java.util.ResourceBundle;
-
-public class DialogsController implements Initializable {
- private final Pane pane;
-
- @FXML
- private MFXButton pError;
-
- @FXML
- private MFXButton pWarning;
-
- @FXML
- private MFXButton pInfo;
-
- @FXML
- private MFXButton pGeneric;
-
- @FXML
- private MFXButton pGenericActions;
-
- @FXML
- private MFXButton pFade;
-
- @FXML
- private MFXButton pSlideLR;
-
- @FXML
- private MFXButton pSlideTB;
-
- @FXML
- private MFXButton pMix;
-
- @FXML
- private MFXButton sError;
-
- @FXML
- private MFXButton sWarning;
-
- @FXML
- private MFXButton sInfo;
-
- @FXML
- private MFXButton sGeneric;
-
- @FXML
- private MFXButton pDraggable;
-
- @FXML
- private MFXButton pOverlayClose;
-
- @FXML
- private MFXButton sModal;
-
- private final AbstractMFXDialog dialog;
- private final AbstractMFXDialog animateDialog;
- private final String text =
- "Lorem Ipsum is simply dummy text of the printing and typesetting industry. " +
- "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, " +
- "when an unknown printer took a galley of type and scrambled it to make a type specimen book. " +
- "It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. " +
- "It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, " +
- "and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
-
- public DialogsController(Pane pane) {
- this.pane = pane;
-
- dialog = MFXDialogFactory.buildDialog(DialogType.INFO, "MFXDialog - Generic Dialog", text);
-
- animateDialog = MFXDialogFactory.buildDialog(DialogType.INFO, "", text);
- animateDialog.setAnimateIn(true);
- animateDialog.setAnimateOut(true);
-
- animateDialog.setOnBeforeOpen(event -> System.out.println("BEFORE OPEN"));
- animateDialog.setOnOpened(event -> System.out.println("OPENED"));
- animateDialog.setOnBeforeClose(event -> System.out.println("BEFORE CLOSING"));
- animateDialog.setOnClosed(event -> System.out.println("CLOSED"));
-
- Platform.runLater(() -> this.pane.getChildren().addAll(dialog, animateDialog));
- }
-
- @Override
- public void initialize(URL location, ResourceBundle resources) {
- dialog.setVisible(false);
- animateDialog.setVisible(false);
-
- pError.setOnAction(event -> {
- resetDialog();
- MFXDialogFactory.convertToSpecific(DialogType.ERROR, dialog);
- dialog.setTitle("MFXDialog - Error Dialog");
- dialog.show();
- });
-
- pWarning.setOnAction(event -> {
- resetDialog();
- MFXDialogFactory.convertToSpecific(DialogType.WARNING, dialog);
- dialog.setTitle("MFXDialog - Warning Dialog");
- dialog.show();
- });
-
- pInfo.setOnAction(event -> {
- resetDialog();
- MFXDialogFactory.convertToSpecific(DialogType.INFO, dialog);
- dialog.setTitle("MFXDialog - Info Dialog");
- dialog.show();
- });
-
- pGeneric.setOnAction(event -> {
- AbstractMFXDialog genericDialog = MFXDialogFactory.buildGenericDialog("MFXDialog - Generic Dialog", text);
- genericDialog.setCloseHandler(c -> {
- genericDialog.close();
- DialogsController.this.pane.getChildren().remove(genericDialog);
- });
- genericDialog.setVisible(false);
- this.pane.getChildren().add(genericDialog);
- genericDialog.show();
- });
-
- pGenericActions.setOnAction(event -> {
- AbstractMFXDialog genericDialog = MFXDialogFactory.buildGenericDialog("MFXDialog - Generic Dialog", text);
- genericDialog.setCloseHandler(c -> {
- genericDialog.close();
- DialogsController.this.pane.getChildren().remove(genericDialog);
- });
- genericDialog.setVisible(false);
- this.pane.getChildren().add(genericDialog);
- genericDialog.setActions(createActionsBar(genericDialog));
- genericDialog.show();
- });
-
- pFade.setOnAction(event -> {
- resetDialog();
- animateDialog.setTitle("MFXDialog - Fade Dialog");
- animateDialog.setInAnimationType(MFXAnimationFactory.FADE_IN);
- animateDialog.setOutAnimationType(MFXAnimationFactory.FADE_OUT);
- animateDialog.show();
- });
-
- pSlideLR.setOnAction(event -> {
- resetDialog();
- animateDialog.setTitle("MFXDialog - Slide Left/Right Dialog");
- animateDialog.setInAnimationType(MFXAnimationFactory.SLIDE_IN_LEFT);
- animateDialog.setOutAnimationType(MFXAnimationFactory.SLIDE_OUT_RIGHT);
- animateDialog.show();
- });
-
- pSlideTB.setOnAction(event -> {
- resetDialog();
- animateDialog.setTitle("MFXDialog - Slide Top/Bottom Dialog");
- animateDialog.setInAnimationType(MFXAnimationFactory.SLIDE_IN_TOP);
- animateDialog.setOutAnimationType(MFXAnimationFactory.SLIDE_OUT_BOTTOM);
- animateDialog.show();
- });
-
- pMix.setOnAction(event -> {
- resetDialog();
- animateDialog.setTitle("MFXDialog - Mix Animation Dialog");
- animateDialog.setInAnimationType(MFXAnimationFactory.SLIDE_IN_TOP);
- animateDialog.setOutAnimationType(MFXAnimationFactory.SLIDE_OUT_RIGHT);
- animateDialog.show();
- });
-
- pDraggable.setOnAction(event -> {
- resetDialog();
- MFXDialogFactory.convertToSpecific(DialogType.INFO, dialog);
- dialog.setTitle("MFXDialog - Draggable Dialog");
- dialog.setIsDraggable(true);
- dialog.show();
- });
-
- pOverlayClose.setOnAction(event -> {
- resetDialog();
- MFXDialogFactory.convertToSpecific(DialogType.INFO, dialog);
- dialog.setTitle("MFXDialog - Overlay Close Dialog");
- dialog.setOverlayClose(true);
- dialog.show();
- });
-
- sError.setOnAction(event -> {
- MFXStageDialog dialog = new MFXStageDialog(DialogType.ERROR, "MFXStageDialog - Error Dialog", text);
- dialog.show();
- });
-
- sWarning.setOnAction(event -> {
- MFXStageDialog dialog = new MFXStageDialog(DialogType.WARNING, "MFXStageDialog - Warning Dialog", text);
- dialog.show();
- });
-
- sInfo.setOnAction(event -> {
- MFXStageDialog dialog = new MFXStageDialog(DialogType.INFO, "MFXStageDialog - Info Dialog", text);
- dialog.show();
- });
-
- sGeneric.setOnAction(event -> {
- MFXStageDialog dialog = new MFXStageDialog(DialogType.GENERIC, "MFXStageDialog - Generic Dialog", text);
- dialog.show();
- });
-
- sModal.setOnAction(event -> {
- MFXStageDialog dialog = new MFXStageDialog(DialogType.INFO, "MFXStageDialog - Modal Dialog", text);
- dialog.setOwner(pane.getScene().getWindow());
- dialog.setModality(Modality.APPLICATION_MODAL);
- dialog.setScrimBackground(true);
- dialog.setCenterInOwner(true);
- dialog.show();
- });
- }
-
- private void resetDialog() {
- dialog.setOverlayClose(false);
- dialog.setIsDraggable(false);
- }
-
- private HBox createActionsBar(AbstractMFXDialog dialog) {
- MFXButton action1 = new MFXButton("Perform Action 1");
- MFXButton action2 = new MFXButton("Perform Action 2");
- MFXButton action3 = new MFXButton("Perform Action 3");
- MFXButton close = new MFXButton("Close");
-
- action1.setButtonType(ButtonType.RAISED);
- action2.setButtonType(ButtonType.RAISED);
- action3.setButtonType(ButtonType.RAISED);
- close.setButtonType(ButtonType.RAISED);
-
- action1.setDepthLevel(DepthLevel.LEVEL1);
- action2.setDepthLevel(DepthLevel.LEVEL1);
- action3.setDepthLevel(DepthLevel.LEVEL1);
- close.setDepthLevel(DepthLevel.LEVEL1);
-
- action1.setOnAction(event -> NotificationsManager.send(NotificationPos.BOTTOM_RIGHT, createNotification("Action 1 Performed")));
- action2.setOnAction(event -> NotificationsManager.send(NotificationPos.BOTTOM_RIGHT, createNotification("Action 2 Performed")));
- action3.setOnAction(event -> NotificationsManager.send(NotificationPos.BOTTOM_RIGHT, createNotification("Action 3 Performed")));
- dialog.addCloseButton(close);
-
- HBox box = new HBox(20, action1, action2, action3, close);
- box.setAlignment(Pos.CENTER);
- box.setPadding(new Insets(20, 5, 20, 5));
- return box;
- }
-
- private MFXNotification createNotification(String text) {
- Region notificationPane = new SimpleMFXNotificationPane(
- "Dialogs Actions Test",
- "",
- text
- );
- MFXNotification notification = new MFXNotification(notificationPane, true, true);
- notification.setHideAfterDuration(Duration.seconds(3));
- return notification;
- }
+import javafx.stage.Stage;
+
+import java.util.Map;
+
+public class DialogsController {
+ private MFXGenericDialog dialogContent;
+ private MFXStageDialog dialog;
+
+ @FXML
+ private GridPane grid;
+
+ public DialogsController(Stage stage) {
+
+ Platform.runLater(() -> {
+ this.dialogContent = MFXGenericDialogBuilder.build()
+ .setContentText(Model.ipsum)
+ .makeScrollable(true)
+ .get();
+ this.dialog = MFXGenericDialogBuilder.build(dialogContent)
+ .toStageDialogBuilder()
+ .initOwner(stage)
+ .initModality(Modality.APPLICATION_MODAL)
+ .setDraggable(true)
+ .setTitle("Dialogs Preview")
+ .setOwnerNode(grid)
+ .setScrimPriority(ScrimPriority.WINDOW)
+ .setScrimOwner(true)
+ .get();
+
+ dialogContent.addActions(
+ Map.entry(new MFXButton("Confirm"), event -> {
+ }),
+ Map.entry(new MFXButton("Cancel"), event -> dialog.close())
+ );
+
+ dialogContent.setMaxSize(400, 200);
+ });
+ }
+
+ @FXML
+ private void openInfo(ActionEvent event) {
+ MFXFontIcon infoIcon = new MFXFontIcon("mfx-info-circle-filled", 18);
+ dialogContent.setHeaderIcon(infoIcon);
+ dialogContent.setHeaderText("This is a generic info dialog");
+ convertDialogTo("mfx-info-dialog");
+ dialog.showDialog();
+ }
+
+ @FXML
+ private void openWarning(ActionEvent event) {
+ MFXFontIcon warnIcon = new MFXFontIcon("mfx-do-not-enter-circle", 18);
+ dialogContent.setHeaderIcon(warnIcon);
+ dialogContent.setHeaderText("This is a warning info dialog");
+ convertDialogTo("mfx-warn-dialog");
+ dialog.showDialog();
+ }
+
+ @FXML
+ private void openError(ActionEvent event) {
+ MFXFontIcon errorIcon = new MFXFontIcon("mfx-exclamation-circle-filled", 18);
+ dialogContent.setHeaderIcon(errorIcon);
+ dialogContent.setHeaderText("This is a error info dialog");
+ convertDialogTo("mfx-error-dialog");
+ dialog.showDialog();
+ }
+
+ @FXML
+ private void openGeneric(ActionEvent event) {
+ dialogContent.setHeaderIcon(null);
+ dialogContent.setHeaderText("This is a generic dialog");
+ convertDialogTo(null);
+ dialog.showDialog();
+ }
+
+ private void convertDialogTo(String styleClass) {
+ dialogContent.getStyleClass().removeIf(
+ s -> s.equals("mfx-info-dialog") || s.equals("mfx-warn-dialog") || s.equals("mfx-error-dialog")
+ );
+
+ if (styleClass != null)
+ dialogContent.getStyleClass().add(styleClass);
+ }
}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/FontResourcesController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/FontResourcesController.java
new file mode 100644
index 00000000..a0cb41fd
--- /dev/null
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/FontResourcesController.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+package io.github.palexdev.materialfx.demo.controllers;
+
+import io.github.palexdev.materialfx.controls.MFXTableColumn;
+import io.github.palexdev.materialfx.controls.MFXTableRow;
+import io.github.palexdev.materialfx.controls.MFXTableView;
+import io.github.palexdev.materialfx.controls.cell.MFXTableRowCell;
+import io.github.palexdev.materialfx.filter.StringFilter;
+import io.github.palexdev.materialfx.font.FontResources;
+import io.github.palexdev.materialfx.font.MFXFontIcon;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.Label;
+
+import java.net.URL;
+import java.util.Comparator;
+import java.util.ResourceBundle;
+
+public class FontResourcesController implements Initializable {
+ private final ObservableList fontResources;
+
+ @FXML
+ private Label header;
+
+ @FXML
+ private MFXTableView tableView;
+
+ public FontResourcesController() {
+ fontResources = FXCollections.observableArrayList(FontResources.values());
+ }
+
+ @Override
+ public void initialize(URL location, ResourceBundle resources) {
+ MFXTableColumn iconColumn = new MFXTableColumn<>("Icon", false, Comparator.comparing(FontResources::getDescription));
+ MFXTableColumn descriptionColumn = new MFXTableColumn<>("Description", false, Comparator.comparing(FontResources::getDescription));
+ MFXTableColumn codeColumn = new MFXTableColumn<>("Code", false, Comparator.comparing(FontResources::getCode));
+
+ iconColumn.setRowCellFactory(resource -> new MFXTableRowCell<>(FontResources::getDescription) {
+ final MFXFontIcon icon = new MFXFontIcon("mfx-logo", 32);
+
+ @Override
+ public void update(FontResources item) {
+ icon.setDescription(item.getDescription());
+ setGraphic(icon);
+ }
+ });
+ descriptionColumn.setRowCellFactory(resource -> new MFXTableRowCell<>(FontResources::getDescription) {
+ @Override
+ public double computePrefWidth(double height) {
+ return 300;
+ }
+ });
+ codeColumn.setRowCellFactory(resource -> new MFXTableRowCell<>(FontResources::getCode, character -> Integer.toHexString(character | 0x10000).substring(1).toUpperCase()));
+
+ tableView.setTableRowFactory(resource -> new MFXTableRow<>(tableView, resource) {{
+ setPrefHeight(48);
+ }});
+ tableView.getTableColumns().addAll(iconColumn, descriptionColumn, codeColumn);
+ tableView.getFilters().add(new StringFilter<>("Description", FontResources::getDescription));
+ tableView.setItems(fontResources);
+ tableView.features().enableBounceEffect();
+ tableView.features().enableSmoothScrolling(0.7);
+ tableView.autosizeColumnsOnInitialization();
+
+ header.setText("MaterialFX Font Resources (" + fontResources.size() + ")");
+ }
+}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/FontResourcesDemoController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/FontResourcesDemoController.java
deleted file mode 100644
index 255cf4dc..00000000
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/FontResourcesDemoController.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-package io.github.palexdev.materialfx.demo.controllers;
-
-import io.github.palexdev.materialfx.controls.MFXFlowlessListView;
-import io.github.palexdev.materialfx.controls.MFXIconWrapper;
-import io.github.palexdev.materialfx.controls.MFXLabel;
-import io.github.palexdev.materialfx.controls.cell.MFXFlowlessListCell;
-import io.github.palexdev.materialfx.font.FontResources;
-import io.github.palexdev.materialfx.font.MFXFontIcon;
-import io.github.palexdev.materialfx.utils.ScrollUtils;
-import javafx.fxml.FXML;
-import javafx.fxml.Initializable;
-import javafx.geometry.Insets;
-import javafx.geometry.Orientation;
-import javafx.geometry.Pos;
-import javafx.scene.control.Separator;
-import javafx.scene.layout.HBox;
-import javafx.scene.paint.Color;
-
-import java.net.URL;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.List;
-import java.util.ResourceBundle;
-import java.util.stream.Collectors;
-
-public class FontResourcesDemoController implements Initializable {
-
- @FXML
- private MFXFlowlessListView list;
-
- @FXML
- private MFXLabel count;
-
- @Override
- public void initialize(URL location, ResourceBundle resources) {
- list.setCellFactory(hBox -> {
- MFXFlowlessListCell cell = new MFXFlowlessListCell<>(list, hBox);
- cell.setFixedCellHeight(48);
- return cell;
- });
- ScrollUtils.addSmoothScrolling(list, 5);
- populateList();
- count.setText(list.getItems().size() + " Icons");
- }
-
- private void populateList() {
- List fontResources = Arrays.asList(FontResources.values());
- fontResources.sort(Comparator.comparing(FontResources::name));
-
-
- List resBoxes = fontResources.stream().map(this::buildNode).collect(Collectors.toList());
- list.setItems(resBoxes);
- }
-
- private HBox buildNode(FontResources fontResource) {
- MFXFontIcon icon = new MFXFontIcon(fontResource.getDescription(), 20);
- MFXLabel l1 = new MFXLabel();
- l1.setLineColor(Color.TRANSPARENT);
- l1.setUnfocusedLineColor(Color.TRANSPARENT);
- l1.setStyle("-fx-background-color: transparent");
- l1.setText("Description: " + fontResource.getDescription());
- l1.setMinWidth(300);
- l1.setAlignment(Pos.CENTER_LEFT);
-
- MFXLabel l2 = new MFXLabel();
- l2.setLineColor(Color.TRANSPARENT);
- l2.setUnfocusedLineColor(Color.TRANSPARENT);
- l2.setStyle("-fx-background-color: transparent");
- l2.setText("Code: " + Integer.toHexString(fontResource.getCode() | 0x10000).substring(1).toUpperCase());
- l2.setMinWidth(300);
- l2.setAlignment(Pos.CENTER_LEFT);
-
- Separator s1 = new Separator(Orientation.VERTICAL);
- s1.setStyle("-fx-fill: white");
- Separator s2 = new Separator(Orientation.VERTICAL);
- s2.setStyle("-fx-fill: white");
-
-
- HBox box = new HBox(10, new MFXIconWrapper(icon, 24), s1, l1, s2, l2);
- box.setPadding(new Insets(5));
- box.setAlignment(Pos.CENTER_LEFT);
- return box;
- }
-}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/InfoController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/InfoController.java
deleted file mode 100644
index 7052930d..00000000
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/InfoController.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-package io.github.palexdev.materialfx.demo.controllers;
-
-import javafx.application.HostServices;
-import javafx.fxml.FXML;
-import javafx.fxml.Initializable;
-import javafx.scene.control.Hyperlink;
-import javafx.scene.control.Label;
-
-import java.net.URL;
-import java.util.ResourceBundle;
-
-public class InfoController implements Initializable {
- private final HostServices hostServices;
-
- @FXML
- private Label githubL;
-
- @FXML
- private Hyperlink githubH;
-
- @FXML
- private Label mavenL;
-
- @FXML
- private Hyperlink mavenH;
-
- @FXML
- private Label contactL;
-
- @FXML
- private Hyperlink contactH;
-
- @FXML
- private Label emailL;
-
- @FXML
- private Hyperlink emailH;
-
- @FXML
- private Label paypalL;
-
- @FXML
- private Hyperlink paypalH;
-
- public InfoController(HostServices hostServices) {
- this.hostServices = hostServices;
- }
-
- @Override
- public void initialize(URL location, ResourceBundle resources) {
- githubL.getGraphic().setOnMousePressed(event -> hostServices.showDocument(githubH.getTooltip().getText()));
- mavenL.getGraphic().setOnMousePressed(event -> hostServices.showDocument(mavenH.getTooltip().getText()));
- contactL.getGraphic().setOnMousePressed(event -> hostServices.showDocument(contactH.getTooltip().getText()));
- emailL.getGraphic().setOnMousePressed(event -> hostServices.showDocument(emailH.getTooltip().getText()));
- paypalL.getGraphic().setOnMousePressed(event -> hostServices.showDocument(paypalH.getTooltip().getText()));
- }
-}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/LabelsDemoController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/LabelsDemoController.java
deleted file mode 100644
index 01e1ce16..00000000
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/LabelsDemoController.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-package io.github.palexdev.materialfx.demo.controllers;
-
-import io.github.palexdev.materialfx.controls.MFXIconWrapper;
-import io.github.palexdev.materialfx.controls.MFXLabel;
-import io.github.palexdev.materialfx.effects.ripple.MFXCircleRippleGenerator;
-import io.github.palexdev.materialfx.font.MFXFontIcon;
-import io.github.palexdev.materialfx.utils.NodeUtils;
-import javafx.fxml.FXML;
-import javafx.fxml.Initializable;
-import javafx.scene.paint.Color;
-
-import java.net.URL;
-import java.util.ResourceBundle;
-
-public class LabelsDemoController implements Initializable {
-
- @FXML
- private MFXLabel custom;
-
- @Override
- public void initialize(URL location, ResourceBundle resources) {
- MFXIconWrapper leading = new MFXIconWrapper(new MFXFontIcon("mfx-filter", 15), 22).defaultRippleGeneratorBehavior();
- MFXIconWrapper trailing = new MFXIconWrapper(new MFXFontIcon("mfx-info-circle", 15), 22).defaultRippleGeneratorBehavior();
-
- NodeUtils.makeRegionCircular(leading);
- NodeUtils.makeRegionCircular(trailing);
-
- MFXCircleRippleGenerator lrg = leading.getRippleGenerator();
- lrg.setRippleColor(Color.web("#849ED7"));
-
- MFXCircleRippleGenerator trg = trailing.getRippleGenerator();
- trg.setRippleColor(Color.web("#849ED7"));
-
- custom.setLeadingIcon(leading);
- custom.setTrailingIcon(trailing);
- }
-}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ListViewsController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ListViewsController.java
new file mode 100644
index 00000000..5a0b2c09
--- /dev/null
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ListViewsController.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+package io.github.palexdev.materialfx.demo.controllers;
+
+import io.github.palexdev.materialfx.controls.MFXCheckListView;
+import io.github.palexdev.materialfx.controls.MFXListView;
+import io.github.palexdev.materialfx.controls.cell.MFXListCell;
+import io.github.palexdev.materialfx.demo.model.Model;
+import io.github.palexdev.materialfx.demo.model.Person;
+import io.github.palexdev.materialfx.effects.DepthLevel;
+import io.github.palexdev.materialfx.font.MFXFontIcon;
+import io.github.palexdev.materialfx.utils.ColorUtils;
+import io.github.palexdev.materialfx.utils.others.FunctionalStringConverter;
+import javafx.collections.ObservableList;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.util.StringConverter;
+
+import java.net.URL;
+import java.util.ResourceBundle;
+
+public class ListViewsController implements Initializable {
+
+ @FXML
+ private MFXListView list;
+
+ @FXML
+ private MFXListView custList;
+
+ @FXML
+ private MFXCheckListView checkList;
+
+ @FXML
+ private MFXListView legacyList;
+
+ @Override
+ public void initialize(URL location, ResourceBundle resources) {
+ ObservableList strings = Model.strings;
+ ObservableList people = Model.people;
+ StringConverter personConverter = FunctionalStringConverter.converter(
+ s -> {
+ throw new UnsupportedOperationException();
+ },
+ person -> (person == null) ? "" : person.getName() + " " + person.getSurname()
+ );
+
+ list.setItems(strings);
+ custList.setItems(people);
+ checkList.setItems(strings);
+ custList.setConverter(personConverter);
+ custList.setCellFactory(person -> new PersonCellFactory(custList, person));
+ custList.features().enableBounceEffect();
+ custList.features().enableSmoothScrolling(0.5);
+
+ legacyList.setItems(people);
+ legacyList.setConverter(personConverter);
+ }
+
+ @FXML
+ void changeColors(ActionEvent event) {
+ custList.setTrackColor(ColorUtils.getRandomColor());
+ custList.setThumbColor(ColorUtils.getRandomColor());
+ custList.setThumbHoverColor(ColorUtils.getRandomColor());
+ }
+
+ @FXML
+ void changeDepth(ActionEvent event) {
+ DepthLevel newLevel = (custList.getDepthLevel() == DepthLevel.LEVEL0) ? DepthLevel.LEVEL2 : DepthLevel.LEVEL0;
+ custList.setDepthLevel(newLevel);
+ }
+
+ private static class PersonCellFactory extends MFXListCell {
+ private final MFXFontIcon userIcon;
+
+ public PersonCellFactory(MFXListView listView, Person data) {
+ super(listView, data);
+
+ userIcon = new MFXFontIcon("mfx-user", 18);
+ userIcon.getStyleClass().add("user-icon");
+ render(data);
+ }
+
+ @Override
+ protected void render(Person data) {
+ super.render(data);
+ if (userIcon != null) getChildren().add(0, userIcon);
+ }
+ }
+}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ListViewsDemoController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ListViewsDemoController.java
deleted file mode 100644
index 58a2b2f5..00000000
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ListViewsDemoController.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-package io.github.palexdev.materialfx.demo.controllers;
-
-import io.github.palexdev.materialfx.controls.*;
-import io.github.palexdev.materialfx.effects.DepthLevel;
-import io.github.palexdev.materialfx.utils.ColorUtils;
-import javafx.beans.property.ObjectProperty;
-import javafx.beans.property.SimpleObjectProperty;
-import javafx.collections.FXCollections;
-import javafx.collections.ObservableList;
-import javafx.fxml.FXML;
-import javafx.fxml.Initializable;
-import javafx.geometry.Insets;
-import javafx.scene.control.Label;
-import javafx.scene.input.MouseEvent;
-import javafx.scene.layout.HBox;
-import javafx.scene.paint.Color;
-import org.kordamp.ikonli.javafx.FontIcon;
-
-import java.net.URL;
-import java.util.List;
-import java.util.Random;
-import java.util.ResourceBundle;
-
-public class ListViewsDemoController implements Initializable {
- private final Random random = new Random(System.currentTimeMillis());
-
- private enum State {
- LEGACY, NEW
- }
-
- private final ObjectProperty state = new SimpleObjectProperty<>(State.NEW);
-
- @FXML
- private HBox legacyBox;
-
- @FXML
- private HBox newBox;
-
- @FXML
- private MFXListView stringView;
-
- @FXML
- private MFXListView labelView;
-
- @FXML
- private MFXListView hBoxView;
-
- @FXML
- private MFXListView cssView;
-
- @FXML
- private MFXFlowlessListView stringViewNew;
-
- @FXML
- private MFXFlowlessListView labelViewNew;
-
- @FXML
- private MFXFlowlessListView hBoxViewNew;
-
- @FXML
- private MFXFlowlessCheckListView checkList;
-
- @FXML
- private MFXFlowlessListView cssViewNew;
-
- @FXML
- private MFXButton swapButton;
-
- @FXML
- private MFXButton depthButton;
-
- @FXML
- private MFXButton colorsButton;
-
- @FXML
- private Label mulLabel;
-
- @FXML
- private MFXCheckbox allowSelection;
-
- private ObservableList stringList;
- private ObservableList labelsList;
- private ObservableList hBoxesList;
-
- private ObservableList labelsListNew;
- private ObservableList hBoxesListNew;
-
- @Override
- public void initialize(URL location, ResourceBundle resources) {
- initLists();
-
- state.addListener((observable, oldValue, newValue) -> {
- if (newValue == State.NEW) {
- mulLabel.setVisible(true);
- legacyBox.setVisible(false);
- newBox.setVisible(true);
- } else {
- mulLabel.setVisible(false);
- legacyBox.setVisible(true);
- newBox.setVisible(false);
- }
- });
-
- checkList.getSelectionModel().allowsSelectionProperty().bind(allowSelection.selectedProperty());
-
- // LEGACY //
- stringView.setItems(stringList);
- labelView.setItems(labelsList);
- hBoxView.setItems(hBoxesList);
- cssView.setItems(stringList);
-
- // NEW //
- stringViewNew.setItems(stringList);
- labelViewNew.setItems(labelsListNew);
- hBoxViewNew.setItems(hBoxesListNew);
- checkList.setItems(stringList);
- cssViewNew.setItems(stringList);
-
- swapButton.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> updateState());
- depthButton.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> updateDepth());
- colorsButton.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> updateColors());
-
- stringViewNew.getSelectionModel().setAllowsMultipleSelection(true);
- }
-
- private void initLists() {
- stringList = FXCollections.observableArrayList(List.of(
- "String 0",
- "String 1",
- "String 2",
- "String 3",
- "String 4",
- "String 5",
- "String 6",
- "String 7"
- ));
-
- // LEGACY //
- labelsList = FXCollections.observableArrayList(List.of(
- createLegacyLabel("Label 0", "fas-home"),
- createLegacyLabel("Label 1", "fas-star"),
- createLegacyLabel("Label 2", "fas-heart"),
- createLegacyLabel("Label 3", "fas-cocktail"),
- createLegacyLabel("Label 4", "fas-anchor"),
- createLegacyLabel("Label 5", "fas-apple-alt"),
- createLegacyLabel("Label 6", "fas-bug"),
- createLegacyLabel("Label 7", "fas-beer")
- ));
- hBoxesList = FXCollections.observableArrayList(List.of(
- createHBox(0),
- createHBox(1),
- createHBox(2),
- createHBox(3),
- createHBox(4),
- createHBox(5),
- createHBox(6),
- createHBox(7)
- ));
-
- // NEW //
- labelsListNew = FXCollections.observableArrayList(List.of(
- createLabel("Label 0", "fas-home"),
- createLabel("Label 1", "fas-star"),
- createLabel("Label 2", "fas-heart"),
- createLabel("Label 3", "fas-cocktail"),
- createLabel("Label 4", "fas-anchor"),
- createLabel("Label 5", "fas-apple-alt"),
- createLabel("Label 6", "fas-bug"),
- createLabel("Label 7", "fas-beer")
- ));
- hBoxesListNew = FXCollections.observableArrayList(List.of(
- createHBox(0),
- createHBox(1),
- createHBox(2),
- createHBox(3),
- createHBox(4),
- createHBox(5),
- createHBox(6),
- createHBox(7)
- ));
- }
-
- private MFXLabel createLabel(String text, String iconDescription) {
- FontIcon icon = new FontIcon(iconDescription);
- icon.setIconColor(Color.PURPLE);
- icon.setIconSize(14);
-
- MFXLabel label = new MFXLabel(text);
- label.setLineColor(Color.TRANSPARENT);
- label.setUnfocusedLineColor(Color.TRANSPARENT);
- label.setStyle("-fx-background-color: transparent");
- label.setLeadingIcon(icon);
- label.setGraphicTextGap(10);
- return label;
- }
-
- private Label createLegacyLabel(String text, String iconDescription) {
- FontIcon icon = new FontIcon(iconDescription);
- icon.setIconColor(Color.PURPLE);
- icon.setIconSize(14);
-
- Label label = new Label(text);
- label.setStyle("-fx-background-color: transparent");
- label.setGraphic(icon);
- label.setGraphicTextGap(10);
- return label;
- }
-
- private HBox createHBox(int index) {
- HBox hBox = new HBox(20);
- hBox.setPadding(new Insets(0, 10, 0, 10));
- hBox.setPrefSize(200, 30);
-
- FontIcon city = new FontIcon("fas-city");
- city.setIconColor(Color.GOLD);
- city.setIconSize(12);
- Label label1 = new Label("City " + index, city);
-
- FontIcon people = new FontIcon("fas-users");
- people.setIconColor(Color.GOLD);
- people.setIconSize(12);
- Label label2 = new Label("Count: " + random.nextInt(2000000), people);
-
- hBox.getChildren().addAll(label1, label2);
- return hBox;
- }
-
- private void updateState() {
- State curr = state.get();
- swapButton.setText(curr == State.LEGACY ? "Switch to Legacy" : "Switch to New");
- state.set(curr == State.LEGACY ? State.NEW : State.LEGACY);
- }
-
- private void updateDepth() {
- if (state.get() == State.LEGACY) {
- DepthLevel level = cssView.getDepthLevel();
- cssView.setDepthLevel(level.equals(DepthLevel.LEVEL0) ? DepthLevel.LEVEL2 : DepthLevel.LEVEL0);
- } else {
- DepthLevel level = cssViewNew.getDepthLevel();
- cssViewNew.setDepthLevel(level.equals(DepthLevel.LEVEL0) ? DepthLevel.LEVEL2 : DepthLevel.LEVEL0);
- }
- }
-
- private void updateColors() {
- if (state.get() == State.LEGACY) {
- cssView.setTrackColor(ColorUtils.getRandomColor());
- cssView.setThumbColor(ColorUtils.getRandomColor());
- cssView.setThumbHoverColor(ColorUtils.getRandomColor());
- } else {
- cssViewNew.setTrackColor(ColorUtils.getRandomColor());
- cssViewNew.setThumbColor(ColorUtils.getRandomColor());
- cssViewNew.setThumbHoverColor(ColorUtils.getRandomColor());
- }
- }
-}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/NotificationsController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/NotificationsController.java
index bacca117..60a76df2 100644
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/NotificationsController.java
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/NotificationsController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Parisi Alessandro
+ * Copyright (C) 2022 Parisi Alessandro
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
*
* MaterialFX is free software: you can redistribute it and/or modify
@@ -18,131 +18,213 @@
package io.github.palexdev.materialfx.demo.controllers;
-import io.github.palexdev.materialfx.controls.MFXDialog;
-import io.github.palexdev.materialfx.controls.MFXNotification;
-import io.github.palexdev.materialfx.controls.SimpleMFXNotificationPane;
-import io.github.palexdev.materialfx.controls.base.AbstractMFXDialog;
-import io.github.palexdev.materialfx.controls.enums.DialogType;
-import io.github.palexdev.materialfx.controls.factories.MFXDialogFactory;
-import io.github.palexdev.materialfx.notifications.NotificationPos;
-import io.github.palexdev.materialfx.notifications.NotificationsManager;
+import io.github.palexdev.materialfx.controls.MFXButton;
+import io.github.palexdev.materialfx.controls.MFXIconWrapper;
+import io.github.palexdev.materialfx.controls.MFXNotificationCenter;
+import io.github.palexdev.materialfx.controls.MFXSimpleNotification;
+import io.github.palexdev.materialfx.controls.cell.MFXNotificationCell;
+import io.github.palexdev.materialfx.demo.MFXDemoResourcesLoader;
+import io.github.palexdev.materialfx.demo.model.Model;
+import io.github.palexdev.materialfx.enums.NotificationPos;
+import io.github.palexdev.materialfx.enums.NotificationState;
+import io.github.palexdev.materialfx.factories.InsetsFactory;
+import io.github.palexdev.materialfx.font.MFXFontIcon;
+import io.github.palexdev.materialfx.notifications.MFXNotificationCenterSystem;
+import io.github.palexdev.materialfx.notifications.MFXNotificationSystem;
+import io.github.palexdev.materialfx.notifications.base.INotification;
+import io.github.palexdev.materialfx.utils.RandomUtils;
+import javafx.application.Platform;
+import javafx.beans.binding.Bindings;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.event.ActionEvent;
import javafx.fxml.FXML;
-import javafx.scene.layout.Region;
-import javafx.scene.paint.Color;
-import javafx.util.Duration;
-import org.kordamp.ikonli.javafx.FontIcon;
-
-import java.util.Random;
+import javafx.geometry.Pos;
+import javafx.scene.control.Label;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Priority;
+import javafx.scene.layout.StackPane;
+import javafx.stage.Stage;
public class NotificationsController {
- private final Random random = new Random(System.currentTimeMillis());
-
- private final String title = "MaterialFX Notification System";
- private final String dummy =
- "Lorem Ipsum is simply dummy text of the printing and typesetting industry. " +
- "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, " +
- "when an unknown printer took a galley of type and scrambled it to make a type specimen book. " +
- "It has survived not only five centuries, but also the leap into electronic typesetting, " +
- "remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, " +
- "and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
-
- @FXML
- void showTopLeft() {
- NotificationPos pos = NotificationPos.TOP_LEFT;
- showNotification(pos);
- }
-
- @FXML
- void showTopCenter() {
- NotificationPos pos = NotificationPos.TOP_CENTER;
- showNotification(pos);
- }
-
- @FXML
- void showTopRight() {
- NotificationPos pos = NotificationPos.TOP_RIGHT;
- showNotification(pos);
- }
-
- @FXML
- void showBottomLeft() {
- NotificationPos pos = NotificationPos.BOTTOM_LEFT;
- showNotification(pos);
- }
-
- @FXML
- void showBottomCenter() {
- NotificationPos pos = NotificationPos.BOTTOM_CENTER;
- showNotification(pos);
- }
-
- @FXML
- void showBottomRight() {
- NotificationPos pos = NotificationPos.BOTTOM_RIGHT;
- showNotification(pos);
- }
-
- private void showNotification(NotificationPos pos) {
- MFXNotification notification = buildNotification();
- NotificationsManager.send(pos, notification);
- }
-
- private MFXNotification buildNotification() {
- Region template = getRandomTemplate();
- MFXNotification notification = new MFXNotification(template, true, true);
- notification.setHideAfterDuration(Duration.seconds(3));
-
- if (template instanceof SimpleMFXNotificationPane) {
- SimpleMFXNotificationPane pane = (SimpleMFXNotificationPane) template;
- pane.setCloseHandler(closeEvent -> notification.hideNotification());
- } else {
- MFXDialog dialog = (MFXDialog) template;
- dialog.setCloseHandler(closeEvent -> notification.hideNotification());
- }
-
- return notification;
- }
-
- private Region getRandomTemplate() {
- final int rand = random.nextInt(4);
-
- switch (rand) {
- case 0:
- FontIcon icon1 = new FontIcon("fas-info-circle");
- icon1.setIconColor(Color.LIGHTBLUE);
- icon1.setIconSize(15);
- return new SimpleMFXNotificationPane(
- icon1,
- "Dummy Notification",
- title,
- dummy
- );
- case 1:
- FontIcon icon2 = new FontIcon("fas-cocktail");
- icon2.setIconColor(Color.GREEN);
- icon2.setIconSize(15);
- return new SimpleMFXNotificationPane(
- icon2,
- "Fast Food",
- title,
- "Hello username, your order is on the way!"
- );
- case 2:
- FontIcon icon3 = new FontIcon("fab-whatsapp");
- icon3.setIconColor(Color.GREEN);
- icon3.setIconSize(15);
- return new SimpleMFXNotificationPane(
- icon3,
- "Whatsapp Notification",
- title,
- "Hi Mark, it's been ages since we last spoke!\nHow are you?"
- );
- case 3:
- AbstractMFXDialog dialog = MFXDialogFactory.buildDialog(DialogType.WARNING, "Warning Dialog as Notification", "Disk space is running low, better watch out...");
- dialog.setVisible(true);
- return dialog;
- default:
- return null;
- }
- }
+
+ public NotificationsController(Stage stage) {
+ Platform.runLater(() -> {
+ MFXNotificationSystem.instance().initOwner(stage);
+ MFXNotificationCenterSystem.instance().initOwner(stage);
+
+ MFXNotificationCenter center = MFXNotificationCenterSystem.instance().getCenter();
+ center.setCellFactory(notification -> new MFXNotificationCell(center, notification) {
+ {
+ setPrefHeight(400);
+ }
+ });
+ });
+ }
+
+ @FXML
+ void showTopLeft(ActionEvent event) {
+ MFXNotificationSystem.instance()
+ .setPosition(NotificationPos.TOP_LEFT)
+ .publish(createNotification());
+ }
+
+ @FXML
+ void showTopCenter(ActionEvent event) {
+ MFXNotificationSystem.instance()
+ .setPosition(NotificationPos.TOP_CENTER)
+ .publish(createNotification());
+ }
+
+ @FXML
+ void showTopRight(ActionEvent event) {
+ MFXNotificationSystem.instance()
+ .setPosition(NotificationPos.TOP_RIGHT)
+ .publish(createNotification());
+ }
+
+ @FXML
+ void showBottomLeft(ActionEvent event) {
+ MFXNotificationSystem.instance()
+ .setPosition(NotificationPos.BOTTOM_LEFT)
+ .publish(createNotification());
+ }
+
+ @FXML
+ void showBottomCenter(ActionEvent event) {
+ MFXNotificationSystem.instance()
+ .setPosition(NotificationPos.BOTTOM_LEFT)
+ .publish(createNotification());
+ }
+
+ @FXML
+ void showBottomRight(ActionEvent event) {
+ MFXNotificationSystem.instance()
+ .setPosition(NotificationPos.BOTTOM_RIGHT)
+ .publish(createNotification());
+ }
+
+ @FXML
+ void showTopLeftNC(ActionEvent event) {
+ MFXNotificationCenterSystem.instance()
+ .setPosition(NotificationPos.TOP_LEFT)
+ .publish(createNotification());
+ }
+
+ @FXML
+ void showTopCenterNC(ActionEvent event) {
+ MFXNotificationCenterSystem.instance()
+ .setPosition(NotificationPos.TOP_CENTER)
+ .publish(createNotification());
+ }
+
+ @FXML
+ void showTopRightNC(ActionEvent event) {
+ MFXNotificationCenterSystem.instance()
+ .setPosition(NotificationPos.TOP_RIGHT)
+ .publish(createNotification());
+ }
+
+ @FXML
+ void showBottomLeftNC(ActionEvent event) {
+ MFXNotificationCenterSystem.instance()
+ .setPosition(NotificationPos.BOTTOM_LEFT)
+ .publish(createNotification());
+ }
+
+ @FXML
+ void showBottomCenterNC(ActionEvent event) {
+ MFXNotificationCenterSystem.instance()
+ .setPosition(NotificationPos.BOTTOM_CENTER)
+ .publish(createNotification());
+ }
+
+ @FXML
+ void showBottomRightNC(ActionEvent event) {
+ MFXNotificationCenterSystem.instance()
+ .setPosition(NotificationPos.BOTTOM_RIGHT)
+ .publish(createNotification());
+ }
+
+ private INotification createNotification() {
+ ExampleNotification notification = new ExampleNotification();
+ notification.setContentText(RandomUtils.randFromArray(Model.randomText));
+ return notification;
+ }
+
+ private static class ExampleNotification extends MFXSimpleNotification {
+ private final StringProperty headerText = new SimpleStringProperty("Notification Header");
+ private final StringProperty contentText = new SimpleStringProperty();
+
+ public ExampleNotification() {
+
+ MFXIconWrapper icon = new MFXIconWrapper(RandomUtils.randFromArray(Model.notificationsIcons).getDescription(), 16, 32);
+ Label headerLabel = new Label();
+ headerLabel.textProperty().bind(headerText);
+ MFXIconWrapper readIcon = new MFXIconWrapper("mfx-eye", 16, 32);
+ ((MFXFontIcon) readIcon.getIcon()).descriptionProperty().bind(Bindings.createStringBinding(
+ () -> (getState() == NotificationState.READ) ? "mfx-eye" : "mfx-eye-slash",
+ notificationStateProperty()
+ ));
+ StackPane.setAlignment(readIcon, Pos.CENTER_RIGHT);
+ StackPane placeHolder = new StackPane(readIcon);
+ placeHolder.setMaxWidth(Double.MAX_VALUE);
+ HBox.setHgrow(placeHolder, Priority.ALWAYS);
+ HBox header = new HBox(10, icon, headerLabel, placeHolder);
+ header.setAlignment(Pos.CENTER_LEFT);
+ header.setPadding(InsetsFactory.of(5, 0, 5, 0));
+ header.setMaxWidth(Double.MAX_VALUE);
+
+
+ Label contentLabel = new Label();
+ contentLabel.getStyleClass().add("content");
+ contentLabel.textProperty().bind(contentText);
+ contentLabel.setWrapText(true);
+ contentLabel.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
+ contentLabel.setAlignment(Pos.TOP_LEFT);
+
+ MFXButton action1 = new MFXButton("Action 1");
+ MFXButton action2 = new MFXButton("Action 2");
+ HBox actionsBar = new HBox(15, action1, action2);
+ actionsBar.getStyleClass().add("actions-bar");
+ actionsBar.setAlignment(Pos.CENTER_RIGHT);
+ actionsBar.setPadding(InsetsFactory.all(5));
+
+ BorderPane container = new BorderPane();
+ container.getStyleClass().add("notification");
+ container.setTop(header);
+ container.setCenter(contentLabel);
+ container.setBottom(actionsBar);
+ container.getStylesheets().add(MFXDemoResourcesLoader.load("css/ExampleNotification.css"));
+ container.setMinHeight(200);
+ container.setMaxWidth(400);
+
+ setContent(container);
+ }
+
+ public String getHeaderText() {
+ return headerText.get();
+ }
+
+ public StringProperty headerTextProperty() {
+ return headerText;
+ }
+
+ public void setHeaderText(String headerText) {
+ this.headerText.set(headerText);
+ }
+
+ public String getContentText() {
+ return contentText.get();
+ }
+
+ public StringProperty contentTextProperty() {
+ return contentText;
+ }
+
+ public void setContentText(String contentText) {
+ this.contentText.set(contentText);
+ }
+ }
}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/DatePickersDemoController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/PickersController.java
similarity index 54%
rename from demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/DatePickersDemoController.java
rename to demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/PickersController.java
index 07680733..2d431c53 100644
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/DatePickersDemoController.java
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/PickersController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Parisi Alessandro
+ * Copyright (C) 2022 Parisi Alessandro
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
*
* MaterialFX is free software: you can redistribute it and/or modify
@@ -19,32 +19,22 @@
package io.github.palexdev.materialfx.demo.controllers;
import io.github.palexdev.materialfx.controls.MFXDatePicker;
-import io.github.palexdev.materialfx.demo.MFXDemoResourcesLoader;
+import io.github.palexdev.materialfx.utils.DateTimeUtils;
+import io.github.palexdev.materialfx.utils.others.dates.DateStringConverter;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
-import javafx.geometry.Insets;
-import javafx.scene.layout.StackPane;
import java.net.URL;
-import java.time.LocalDate;
import java.util.ResourceBundle;
-public class DatePickersDemoController implements Initializable {
+public class PickersController implements Initializable {
- @FXML
- private MFXDatePicker customPicker;
+ @FXML
+ private MFXDatePicker custDatePicker;
- @FXML
- private StackPane pane;
-
- @Override
- public void initialize(URL location, ResourceBundle resources) {
- String css = MFXDemoResourcesLoader.load("css/CustomDatePicker.css");
- customPicker.getContent().getStylesheets().add(css);
-
- MFXDatePicker initialized = new MFXDatePicker(LocalDate.now());
- initialized.setColorText(true);
- pane.getChildren().add(initialized);
- StackPane.setMargin(initialized, new Insets(10, 0, 0, 0));
- }
-}
+ @Override
+ public void initialize(URL location, ResourceBundle resources) {
+ custDatePicker.setGridAlgorithm(DateTimeUtils::partialIntMonthMatrix);
+ custDatePicker.setConverterSupplier(() -> new DateStringConverter("dd/MM/yyyy", custDatePicker.getLocale()));
+ }
+}
\ No newline at end of file
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ProgressBarsDemoController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ProgressBarsDemoController.java
deleted file mode 100644
index 4e255931..00000000
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ProgressBarsDemoController.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-package io.github.palexdev.materialfx.demo.controllers;
-
-import io.github.palexdev.materialfx.beans.NumberRange;
-import io.github.palexdev.materialfx.controls.MFXProgressBar;
-import io.github.palexdev.materialfx.controls.factories.MFXAnimationFactory;
-import io.github.palexdev.materialfx.utils.AnimationUtils;
-import io.github.palexdev.materialfx.utils.AnimationUtils.KeyFrames;
-import javafx.animation.Animation;
-import javafx.beans.binding.Bindings;
-import javafx.fxml.FXML;
-import javafx.fxml.Initializable;
-import javafx.scene.control.Label;
-import javafx.scene.paint.Color;
-import javafx.util.Duration;
-
-import java.net.URL;
-import java.util.Formatter;
-import java.util.ResourceBundle;
-
-public class ProgressBarsDemoController implements Initializable {
-
- @FXML
- private MFXProgressBar determinate;
-
- @FXML
- private Label progressLabel;
-
- @Override
- public void initialize(URL location, ResourceBundle resources) {
- progressLabel.textProperty().bind(Bindings.createStringBinding(
- () -> new Formatter().format("%.2f", determinate.getProgress()).toString().replace(",", "."),
- determinate.progressProperty()
- ));
- progressLabel.textFillProperty().bind(Bindings.createObjectBinding(
- () -> progressLabel.getText().equals("1.00") ? Color.web("#85CB33") : Color.BLACK,
- progressLabel.textProperty()
- ));
-
- Animation a1 = AnimationUtils.TimelineBuilder.build()
- .add(
- KeyFrames.of(2000, determinate.progressProperty(), 0.3, MFXAnimationFactory.getInterpolatorV1()),
- KeyFrames.of(4000, determinate.progressProperty(), 0.6, MFXAnimationFactory.getInterpolatorV1()),
- KeyFrames.of(6000, determinate.progressProperty(), 1.0, MFXAnimationFactory.getInterpolatorV1())
- )
- .getAnimation();
-
- Animation a2 = AnimationUtils.TimelineBuilder.build()
- .add(
- KeyFrames.of(1000, determinate.progressProperty(), 0, MFXAnimationFactory.getInterpolatorV2())
- )
- .getAnimation();
-
- a1.setOnFinished(end -> AnimationUtils.PauseBuilder.build()
- .setDuration(Duration.seconds(1))
- .setOnFinished(event -> a2.playFromStart())
- .getAnimation()
- .play()
- );
- a2.setOnFinished(end -> AnimationUtils.PauseBuilder.build()
- .setDuration(Duration.seconds(1))
- .setOnFinished(event -> a1.playFromStart())
- .getAnimation()
- .play()
- );
-
- a1.play();
-
- determinate.getRanges1().add(NumberRange.of(0.0, 0.30));
- determinate.getRanges2().add(NumberRange.of(0.31, 0.60));
- determinate.getRanges3().add(NumberRange.of(0.61, 1.0));
- }
-}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ProgressController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ProgressController.java
new file mode 100644
index 00000000..689c88fe
--- /dev/null
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ProgressController.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+package io.github.palexdev.materialfx.demo.controllers;
+
+import io.github.palexdev.materialfx.beans.NumberRange;
+import io.github.palexdev.materialfx.controls.MFXProgressBar;
+import io.github.palexdev.materialfx.controls.MFXProgressSpinner;
+import io.github.palexdev.materialfx.effects.Interpolators;
+import io.github.palexdev.materialfx.utils.AnimationUtils.KeyFrames;
+import io.github.palexdev.materialfx.utils.AnimationUtils.PauseBuilder;
+import io.github.palexdev.materialfx.utils.AnimationUtils.TimelineBuilder;
+import javafx.animation.Animation;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.ProgressIndicator;
+import javafx.util.Duration;
+
+import java.net.URL;
+import java.util.ResourceBundle;
+
+public class ProgressController implements Initializable {
+
+ @FXML
+ private MFXProgressBar determinateBar;
+
+ @FXML
+ private MFXProgressSpinner determinateSpinner;
+
+
+ @Override
+ public void initialize(URL location, ResourceBundle resources) {
+ determinateBar.getRanges1().add(NumberRange.of(0.0, 0.30));
+ determinateBar.getRanges2().add(NumberRange.of(0.31, 0.60));
+ determinateBar.getRanges3().add(NumberRange.of(0.61, 1.0));
+
+ determinateSpinner.getRanges1().add(NumberRange.of(0.0, 0.30));
+ determinateSpinner.getRanges2().add(NumberRange.of(0.31, 0.60));
+ determinateSpinner.getRanges3().add(NumberRange.of(0.61, 1.0));
+
+ createAndPlayAnimation(determinateBar);
+ createAndPlayAnimation(determinateSpinner);
+ }
+
+ private void createAndPlayAnimation(ProgressIndicator indicator) {
+ Animation a1 = TimelineBuilder.build()
+ .add(
+ KeyFrames.of(2000, indicator.progressProperty(), 0.3, Interpolators.INTERPOLATOR_V1),
+ KeyFrames.of(4000, indicator.progressProperty(), 0.6, Interpolators.INTERPOLATOR_V1),
+ KeyFrames.of(6000, indicator.progressProperty(), 1.0, Interpolators.INTERPOLATOR_V1)
+ )
+ .getAnimation();
+
+ Animation a2 = TimelineBuilder.build()
+ .add(
+ KeyFrames.of(1000, indicator.progressProperty(), 0, Interpolators.INTERPOLATOR_V2)
+ )
+ .getAnimation();
+
+ a1.setOnFinished(end -> PauseBuilder.build()
+ .setDuration(Duration.seconds(1))
+ .setOnFinished(event -> a2.playFromStart())
+ .getAnimation()
+ .play()
+ );
+ a2.setOnFinished(end -> PauseBuilder.build()
+ .setDuration(Duration.seconds(1))
+ .setOnFinished(event -> a1.playFromStart())
+ .getAnimation()
+ .play()
+ );
+
+ a1.play();
+ }
+}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ProgressSpinnersDemoController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ProgressSpinnersDemoController.java
deleted file mode 100644
index a71a3da7..00000000
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ProgressSpinnersDemoController.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-package io.github.palexdev.materialfx.demo.controllers;
-
-import io.github.palexdev.materialfx.beans.NumberRange;
-import io.github.palexdev.materialfx.controls.MFXProgressSpinner;
-import io.github.palexdev.materialfx.utils.AnimationUtils;
-import io.github.palexdev.materialfx.utils.AnimationUtils.KeyFrames;
-import javafx.animation.KeyValue;
-import javafx.animation.Timeline;
-import javafx.fxml.FXML;
-import javafx.fxml.Initializable;
-import javafx.util.Duration;
-
-import java.net.URL;
-import java.util.ResourceBundle;
-
-public class ProgressSpinnersDemoController implements Initializable {
-
- @FXML
- private MFXProgressSpinner progress1;
-
- @FXML
- private MFXProgressSpinner progress2;
-
- @Override
- public void initialize(URL location, ResourceBundle resources) {
- AnimationUtils.TimelineBuilder.build()
- .add(
- KeyFrames.of(
- Duration.ZERO,
- new KeyValue(progress1.progressProperty(), 0),
- new KeyValue(progress2.progressProperty(), 0)
- ),
- KeyFrames.of(
- Duration.seconds(0.5),
- new KeyValue(progress1.progressProperty(), 0.5)
- ),
- KeyFrames.of(
- Duration.seconds(2),
- new KeyValue(progress1.progressProperty(), 1),
- new KeyValue(progress2.progressProperty(), 1)
- )
- )
- .setCycleCount(Timeline.INDEFINITE)
- .getAnimation()
- .play();
-
- progress1.getRanges1().add(NumberRange.of(0.0, 0.30));
- progress1.getRanges2().add(NumberRange.of(0.31, 0.60));
- progress1.getRanges3().add(NumberRange.of(0.61, 1.0));
- }
-}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ScrollPaneDemoController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ScrollPaneDemoController.java
deleted file mode 100644
index 3ac3d504..00000000
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ScrollPaneDemoController.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-package io.github.palexdev.materialfx.demo.controllers;
-
-
-import io.github.palexdev.materialfx.controls.MFXScrollPane;
-import io.github.palexdev.materialfx.utils.ColorUtils;
-import io.github.palexdev.materialfx.utils.ScrollUtils;
-import javafx.fxml.FXML;
-import javafx.fxml.Initializable;
-
-import java.net.URL;
-import java.util.ResourceBundle;
-
-public class ScrollPaneDemoController implements Initializable {
-
- @FXML
- private MFXScrollPane scrollPaneV;
-
- @FXML
- private MFXScrollPane scrollPaneVH;
-
- @Override
- public void initialize(URL location, ResourceBundle resources) {
- ScrollUtils.addSmoothScrolling(scrollPaneV);
- ScrollUtils.addSmoothScrolling(scrollPaneVH);
- ScrollUtils.animateScrollBars(scrollPaneVH);
- }
-
- @FXML
- void setRandomTrackColor() {
- scrollPaneV.setTrackColor(ColorUtils.getRandomColor());
- scrollPaneVH.setTrackColor(ColorUtils.getRandomColor());
- }
-
- @FXML
- void setRandomThumbColor() {
- scrollPaneV.setThumbColor(ColorUtils.getRandomColor());
- scrollPaneVH.setThumbColor(ColorUtils.getRandomColor());
- }
-
- @FXML
- void setRandomThumbHoverColor() {
- scrollPaneV.setThumbHoverColor(ColorUtils.getRandomColor());
- scrollPaneVH.setThumbHoverColor(ColorUtils.getRandomColor());
- }
-}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ScrollPanesController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ScrollPanesController.java
new file mode 100644
index 00000000..8543be11
--- /dev/null
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/ScrollPanesController.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+package io.github.palexdev.materialfx.demo.controllers;
+
+import io.github.palexdev.materialfx.controls.MFXScrollPane;
+import io.github.palexdev.materialfx.demo.model.Model;
+import io.github.palexdev.materialfx.utils.ColorUtils;
+import io.github.palexdev.materialfx.utils.ScrollUtils;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.Label;
+
+import java.net.URL;
+import java.util.ResourceBundle;
+
+public class ScrollPanesController implements Initializable {
+
+ @FXML
+ private MFXScrollPane scroll1;
+
+ @FXML
+ private MFXScrollPane scroll2;
+
+ @Override
+ public void initialize(URL location, ResourceBundle resources) {
+ Label l1 = new Label(Model.ipsum);
+ l1.setWrapText(true);
+ Label l2 = new Label(Model.ipsum);
+ l2.setMaxSize(400, Double.MAX_VALUE);
+ l2.setWrapText(true);
+
+ scroll1.setContent(l1);
+ scroll2.setContent(l2);
+
+ ScrollUtils.addSmoothScrolling(scroll1);
+ ScrollUtils.addSmoothScrolling(scroll2);
+ }
+
+ @FXML
+ private void setRandomTrackColor() {
+ scroll1.setTrackColor(ColorUtils.getRandomColor());
+ scroll2.setTrackColor(ColorUtils.getRandomColor());
+ }
+
+ @FXML
+ private void setRandomThumbColor() {
+ scroll1.setThumbColor(ColorUtils.getRandomColor());
+ scroll2.setThumbColor(ColorUtils.getRandomColor());
+ }
+
+ @FXML
+ private void setRandomThumbHoverColor() {
+ scroll1.setThumbHoverColor(ColorUtils.getRandomColor());
+ scroll2.setThumbHoverColor(ColorUtils.getRandomColor());
+ }
+}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/SlidersDemoController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/SlidersController.java
similarity index 68%
rename from demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/SlidersDemoController.java
rename to demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/SlidersController.java
index 8ee6f9bb..2f831f5e 100644
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/SlidersDemoController.java
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/SlidersController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Parisi Alessandro
+ * Copyright (C) 2022 Parisi Alessandro
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
*
* MaterialFX is free software: you can redistribute it and/or modify
@@ -26,15 +26,15 @@
import java.net.URL;
import java.util.ResourceBundle;
-public class SlidersDemoController implements Initializable {
+public class SlidersController implements Initializable {
- @FXML
- private MFXSlider customSlider;
+ @FXML
+ private MFXSlider customSlider;
- @Override
- public void initialize(URL location, ResourceBundle resources) {
- customSlider.getRanges1().add(NumberRange.of(customSlider.getMin(), 33.0));
- customSlider.getRanges2().add(NumberRange.of(34.0, 66.0));
- customSlider.getRanges3().add(NumberRange.of(67.0, customSlider.getMax()));
- }
+ @Override
+ public void initialize(URL location, ResourceBundle resources) {
+ customSlider.getRanges1().add(NumberRange.of(customSlider.getMin(), 33.0));
+ customSlider.getRanges2().add(NumberRange.of(34.0, 66.0));
+ customSlider.getRanges3().add(NumberRange.of(67.0, customSlider.getMax()));
+ }
}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/StepperController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/StepperController.java
new file mode 100644
index 00000000..86e25f2b
--- /dev/null
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/StepperController.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+package io.github.palexdev.materialfx.demo.controllers;
+
+import io.github.palexdev.materialfx.controls.*;
+import io.github.palexdev.materialfx.controls.MFXStepper.MFXStepperEvent;
+import io.github.palexdev.materialfx.font.MFXFontIcon;
+import io.github.palexdev.materialfx.validation.Constraint;
+import io.github.palexdev.materialfx.validation.MFXValidator;
+import io.github.palexdev.materialfx.validation.Validated;
+import javafx.beans.binding.Bindings;
+import javafx.collections.FXCollections;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Region;
+import javafx.scene.layout.StackPane;
+import javafx.scene.layout.VBox;
+import javafx.scene.paint.Color;
+
+import java.net.URL;
+import java.util.List;
+import java.util.ResourceBundle;
+
+public class StepperController implements Initializable {
+ private final MFXTextField loginField;
+ private final MFXPasswordField passwordField;
+ private final MFXTextField firstNameField;
+ private final MFXTextField lastNameField;
+ private final MFXComboBox genderCombo;
+ private final MFXCheckbox checkbox;
+
+ @FXML
+ private MFXButton unlock;
+
+ @FXML
+ private MFXStepper stepper;
+
+ public StepperController() {
+ loginField = new MFXTextField();
+ passwordField = new MFXPasswordField();
+ firstNameField = new MFXTextField();
+ lastNameField = new MFXTextField();
+ genderCombo = new MFXComboBox<>();
+ checkbox = new MFXCheckbox("Confirm Data?");
+ }
+
+
+ @Override
+ public void initialize(URL location, ResourceBundle resources) {
+ loginField.setPromptText("Username...");
+ loginField.getValidator().constraint("The username must be at least 6 characters long", loginField.textProperty().length().greaterThanOrEqualTo(6));
+ loginField.setLeadingIcon(new MFXIconWrapper("mfx-user", 16, Color.web("#4D4D4D"), 24));
+ passwordField.getValidator().constraint("The password must be at least 8 characters long", passwordField.textProperty().length().greaterThanOrEqualTo(8));
+ passwordField.setPromptText("Password...");
+
+ firstNameField.setPromptText("First Name...");
+ lastNameField.setPromptText("Last Name...");
+ genderCombo.setItems(FXCollections.observableArrayList("Male", "Female", "Other"));
+
+ List stepperToggles = createSteps();
+ stepper.getStepperToggles().addAll(stepperToggles);
+
+ unlock.visibleProperty().bind(stepper.mouseTransparentProperty());
+ unlock.setOnAction(event -> stepper.setMouseTransparent(false));
+ }
+
+ private List createSteps() {
+ MFXStepperToggle step1 = new MFXStepperToggle("Step 1", new MFXFontIcon("mfx-lock", 16, Color.web("#f1c40f")));
+ VBox step1Box = new VBox(20, wrapNodeForValidation(loginField), wrapNodeForValidation(passwordField));
+ step1Box.setAlignment(Pos.CENTER);
+ step1.setContent(step1Box);
+ step1.getValidator().dependsOn(loginField.getValidator()).dependsOn(passwordField.getValidator());
+
+ MFXStepperToggle step2 = new MFXStepperToggle("Step 2", new MFXFontIcon("mfx-user", 16, Color.web("#49a6d7")));
+ VBox step2Box = new VBox(20, firstNameField, lastNameField, genderCombo);
+ step2Box.setAlignment(Pos.CENTER);
+ step2.setContent(step2Box);
+
+ MFXStepperToggle step3 = new MFXStepperToggle("Step 3", new MFXFontIcon("mfx-variant7-mark", 16, Color.web("#85CB33")));
+ Node step3Grid = createGrid();
+ step3.setContent(step3Grid);
+ step3.getValidator().constraint("Data must be confirmed", checkbox.selectedProperty());
+
+ return List.of(step1, step2, step3);
+ }
+
+ private Node wrapNodeForValidation(T node) {
+ Label errorLabel = new Label();
+ errorLabel.getStyleClass().add("error-label");
+ errorLabel.setManaged(false);
+ stepper.addEventHandler(MFXStepperEvent.VALIDATION_FAILED_EVENT, event -> {
+ MFXValidator validator = node.getValidator();
+ List validate = validator.validate();
+ if (!validate.isEmpty()) {
+ errorLabel.setText(validate.get(0).getMessage());
+ }
+ });
+ stepper.addEventHandler(MFXStepperEvent.NEXT_EVENT, event -> errorLabel.setText(""));
+ VBox wrap = new VBox(3, node, errorLabel) {
+ @Override
+ protected void layoutChildren() {
+ super.layoutChildren();
+
+ double x = node.getBoundsInParent().getMinX();
+ double y = node.getBoundsInParent().getMaxY() + getSpacing();
+ double width = getWidth();
+ double height = errorLabel.prefHeight(-1);
+ errorLabel.resizeRelocate(x, y, width, height);
+ }
+
+ @Override
+ protected double computePrefHeight(double width) {
+ return super.computePrefHeight(width) + errorLabel.getHeight() + getSpacing();
+ }
+ };
+ wrap.setAlignment(Pos.CENTER);
+ return wrap;
+ }
+
+ private Node createGrid() {
+ MFXTextField usernameLabel1 = createLabel("Username: ");
+ MFXTextField usernameLabel2 = createLabel("");
+ usernameLabel2.textProperty().bind(loginField.textProperty());
+
+ MFXTextField firstNameLabel1 = createLabel("First Name: ");
+ MFXTextField firstNameLabel2 = createLabel("");
+ firstNameLabel2.textProperty().bind(firstNameField.textProperty());
+
+ MFXTextField lastNameLabel1 = createLabel("Last Name: ");
+ MFXTextField lastNameLabel2 = createLabel("");
+ lastNameLabel2.textProperty().bind(lastNameField.textProperty());
+
+ MFXTextField genderLabel1 = createLabel("Gender: ");
+ MFXTextField genderLabel2 = createLabel("");
+ genderLabel2.textProperty().bind(Bindings.createStringBinding(
+ () -> genderCombo.getValue() != null ? genderCombo.getValue() : "",
+ genderCombo.valueProperty()
+ ));
+
+ usernameLabel1.getStyleClass().add("header-label");
+ firstNameLabel1.getStyleClass().add("header-label");
+ lastNameLabel1.getStyleClass().add("header-label");
+ genderLabel1.getStyleClass().add("header-label");
+
+ MFXTextField completedLabel = MFXTextField.asLabel("Completed!");
+ completedLabel.getStyleClass().add("completed-label");
+
+ HBox b1 = new HBox(usernameLabel1, usernameLabel2);
+ HBox b2 = new HBox(firstNameLabel1, firstNameLabel2);
+ HBox b3 = new HBox(lastNameLabel1, lastNameLabel2);
+ HBox b4 = new HBox(genderLabel1, genderLabel2);
+
+ b1.setMaxWidth(Region.USE_PREF_SIZE);
+ b2.setMaxWidth(Region.USE_PREF_SIZE);
+ b3.setMaxWidth(Region.USE_PREF_SIZE);
+ b4.setMaxWidth(Region.USE_PREF_SIZE);
+
+ VBox box = new VBox(10, b1, b2, b3, b4, checkbox);
+ box.setAlignment(Pos.CENTER);
+ StackPane.setAlignment(box, Pos.CENTER);
+
+ stepper.setOnLastNext(event -> {
+ box.getChildren().setAll(completedLabel);
+ stepper.setMouseTransparent(true);
+ });
+ stepper.setOnBeforePrevious(event -> {
+ if (stepper.isLastToggle()) {
+ checkbox.setSelected(false);
+ box.getChildren().setAll(b1, b2, b3, b4, checkbox);
+ }
+ });
+
+ return box;
+ }
+
+ private MFXTextField createLabel(String text) {
+ MFXTextField label = MFXTextField.asLabel(text);
+ label.setAlignment(Pos.CENTER_LEFT);
+ label.setPrefWidth(200);
+ label.setMinWidth(Region.USE_PREF_SIZE);
+ label.setMaxWidth(Region.USE_PREF_SIZE);
+ return label;
+ }
+}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/StepperDemoController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/StepperDemoController.java
deleted file mode 100644
index 408ca1e9..00000000
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/StepperDemoController.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-package io.github.palexdev.materialfx.demo.controllers;
-
-import io.github.palexdev.materialfx.controls.*;
-import io.github.palexdev.materialfx.demo.MFXDemoResourcesLoader;
-import io.github.palexdev.materialfx.font.MFXFontIcon;
-import io.github.palexdev.materialfx.utils.BindingUtils;
-import javafx.beans.binding.Bindings;
-import javafx.collections.FXCollections;
-import javafx.fxml.FXML;
-import javafx.fxml.Initializable;
-import javafx.geometry.Pos;
-import javafx.scene.Node;
-import javafx.scene.layout.HBox;
-import javafx.scene.layout.Region;
-import javafx.scene.layout.StackPane;
-import javafx.scene.layout.VBox;
-import javafx.scene.paint.Color;
-import javafx.scene.text.Font;
-import org.kordamp.ikonli.javafx.FontIcon;
-
-import java.net.URL;
-import java.util.List;
-import java.util.ResourceBundle;
-
-public class StepperDemoController implements Initializable {
- private final MFXTextField loginField;
- private final MFXPasswordField passwordField;
- private final MFXTextField firstNameField;
- private final MFXTextField lastNameField;
- private final MFXComboBox genderCombo;
- private final MFXCheckbox checkbox;
-
- @FXML
- private MFXStepper stepper;
-
- @FXML
- private MFXButton unlock;
-
- public StepperDemoController() {
- loginField = new MFXTextField();
- passwordField = new MFXPasswordField();
- firstNameField = new MFXTextField();
- lastNameField = new MFXTextField();
- genderCombo = new MFXComboBox<>();
- checkbox = new MFXCheckbox("Confirm Data?");
- }
-
- @Override
- public void initialize(URL location, ResourceBundle resources) {
- loginField.setPromptText("Username...");
- loginField.getValidator().add(BindingUtils.toProperty(loginField.textProperty().length().greaterThanOrEqualTo(6)), "The username must be at least 6 characters long");
- loginField.setValidated(true);
- loginField.setIcon(new MFXIconWrapper(new MFXFontIcon("mfx-user", 16, Color.web("#4D4D4D")), 24));
- passwordField.setPromptText("Password...");
- passwordField.getValidator().add(BindingUtils.toProperty(passwordField.passwordProperty().length().greaterThanOrEqualTo(8)), "The password must be at least 8 characters long");
- passwordField.setValidated(true);
-
- firstNameField.setPromptText("First Name...");
- lastNameField.setPromptText("Last Name...");
- genderCombo.setItems(FXCollections.observableArrayList("Male", "Female", "Other"));
-
- checkbox.setMarkType("mfx-variant7-mark");
-
- List stepperToggles = createSteps();
- stepper.getStepperToggles().addAll(stepperToggles);
-
- unlock.visibleProperty().bind(stepper.mouseTransparentProperty());
- unlock.setOnAction(event -> stepper.setMouseTransparent(false));
- }
-
- private List createSteps() {
- FontIcon key = new FontIcon("fas-key");
- key.setIconSize(16);
- key.setIconColor(Color.web("#f1c40f"));
- MFXStepperToggle step1 = new MFXStepperToggle("Step 1", key);
- VBox step1Box = new VBox(40, loginField, passwordField);
- step1Box.setAlignment(Pos.CENTER);
- step1.setContent(step1Box);
- step1.getValidator().addDependencies(loginField.getValidator(), passwordField.getValidator());
-
- MFXStepperToggle step2 = new MFXStepperToggle("Step 2", new MFXFontIcon("mfx-user", 16, Color.web("#49a6d7")));
- VBox step2Box = new VBox(40, firstNameField, lastNameField, genderCombo);
- step2Box.setAlignment(Pos.CENTER);
- step2.setContent(step2Box);
-
- MFXStepperToggle step3 = new MFXStepperToggle("Step 3", new MFXFontIcon("mfx-variant7-mark", 16, Color.web("#85CB33")));
- Node step3Grid = createGrid();
- step3.setContent(step3Grid);
- step3.getValidator().add(checkbox.selectedProperty(), "Data must be confirmed");
-
- return List.of(step1, step2, step3);
- }
-
- private Node createGrid() {
- MFXLabel usernameLabel1 = createLabel("Username: ");
- MFXLabel usernameLabel2 = createLabel("");
- usernameLabel2.textProperty().bind(loginField.textProperty());
-
- MFXLabel firstNameLabel1 = createLabel("First Name: ");
- MFXLabel firstNameLabel2 = createLabel("");
- firstNameLabel2.textProperty().bind(firstNameField.textProperty());
-
- MFXLabel lastNameLabel1 = createLabel("Last Name: ");
- MFXLabel lastNameLabel2 = createLabel("");
- lastNameLabel2.textProperty().bind(lastNameField.textProperty());
-
- MFXLabel genderLabel1 = createLabel("Gender: ");
- MFXLabel genderLabel2 = createLabel("");
- genderLabel2.textProperty().bind(Bindings.createStringBinding(
- () -> genderCombo.getSelectedValue() != null ? genderCombo.getSelectedValue() : "",
- genderCombo.selectedValueProperty()
- ));
-
- MFXLabel completedLabel = new MFXLabel("Completed!!");
- completedLabel.setFont(Font.font("Open Sans Bold", 24));
- completedLabel.setTextFill(Color.web("#85CB33"));
-
- HBox b1 = new HBox(usernameLabel1, usernameLabel2);
- HBox b2 = new HBox(firstNameLabel1, firstNameLabel2);
- HBox b3 = new HBox(lastNameLabel1, lastNameLabel2);
- HBox b4 = new HBox(genderLabel1, genderLabel2);
-
- b1.setMaxWidth(Region.USE_PREF_SIZE);
- b2.setMaxWidth(Region.USE_PREF_SIZE);
- b3.setMaxWidth(Region.USE_PREF_SIZE);
- b4.setMaxWidth(Region.USE_PREF_SIZE);
-
- VBox box = new VBox(10, b1, b2, b3, b4, checkbox);
- box.getStylesheets().setAll(MFXDemoResourcesLoader.load("css/StepperDemo.css"));
- box.setAlignment(Pos.CENTER);
- StackPane.setAlignment(box, Pos.CENTER);
-
- stepper.setOnLastNext(event -> {
- box.getChildren().setAll(completedLabel);
- stepper.setMouseTransparent(true);
- });
- stepper.setOnBeforePrevious(event -> {
- if (stepper.isLastToggle()) {
- checkbox.setSelected(false);
- box.getChildren().setAll(b1, b2, b3, b4, checkbox);
- }
- });
-
- return box;
- }
-
- private MFXLabel createLabel(String text) {
- MFXLabel label = new MFXLabel(text);
- label.setAlignment(Pos.CENTER_LEFT);
- label.setPrefWidth(200);
- label.setMinWidth(Region.USE_PREF_SIZE);
- label.setMaxWidth(Region.USE_PREF_SIZE);
- return label;
- }
-}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TableViewsController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TableViewsController.java
new file mode 100644
index 00000000..54f0593c
--- /dev/null
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TableViewsController.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+package io.github.palexdev.materialfx.demo.controllers;
+
+import io.github.palexdev.materialfx.controls.MFXPaginatedTableView;
+import io.github.palexdev.materialfx.controls.MFXTableColumn;
+import io.github.palexdev.materialfx.controls.MFXTableView;
+import io.github.palexdev.materialfx.controls.cell.MFXTableRowCell;
+import io.github.palexdev.materialfx.demo.model.Device;
+import io.github.palexdev.materialfx.demo.model.Model;
+import io.github.palexdev.materialfx.demo.model.Person;
+import io.github.palexdev.materialfx.filter.EnumFilter;
+import io.github.palexdev.materialfx.filter.IntegerFilter;
+import io.github.palexdev.materialfx.filter.StringFilter;
+import io.github.palexdev.materialfx.utils.others.observables.When;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.geometry.Pos;
+
+import java.net.URL;
+import java.util.Comparator;
+import java.util.ResourceBundle;
+
+public class TableViewsController implements Initializable {
+
+ @FXML
+ private MFXTableView table;
+
+ @FXML
+ private MFXPaginatedTableView paginated;
+
+ @Override
+ public void initialize(URL location, ResourceBundle resources) {
+ setupTable();
+ setupPaginated();
+
+ table.autosizeColumnsOnInitialization();
+ paginated.autosizeColumnsOnInitialization();
+
+ When.onChanged(paginated.currentPageProperty())
+ .then((oldValue, newValue) -> paginated.autosizeColumns())
+ .listen();
+ }
+
+ private void setupTable() {
+ MFXTableColumn nameColumn = new MFXTableColumn<>("Name", true, Comparator.comparing(Person::getName));
+ MFXTableColumn surnameColumn = new MFXTableColumn<>("Surname", true, Comparator.comparing(Person::getSurname));
+ MFXTableColumn ageColumn = new MFXTableColumn<>("Age", true, Comparator.comparing(Person::getAge));
+
+ nameColumn.setRowCellFactory(person -> new MFXTableRowCell<>(Person::getName));
+ surnameColumn.setRowCellFactory(person -> new MFXTableRowCell<>(Person::getSurname));
+ ageColumn.setRowCellFactory(person -> new MFXTableRowCell<>(Person::getAge) {{
+ setAlignment(Pos.CENTER_RIGHT);
+ }});
+ ageColumn.setAlignment(Pos.CENTER_RIGHT);
+
+ table.getTableColumns().addAll(nameColumn, surnameColumn, ageColumn);
+ table.getFilters().addAll(
+ new StringFilter<>("Name", Person::getName),
+ new StringFilter<>("Surname", Person::getSurname),
+ new IntegerFilter<>("Age", Person::getAge)
+ );
+ table.setItems(Model.people);
+ }
+
+ private void setupPaginated() {
+ MFXTableColumn idColumn = new MFXTableColumn<>("ID", false, Comparator.comparing(Device::getID));
+ MFXTableColumn nameColumn = new MFXTableColumn<>("Name", false, Comparator.comparing(Device::getName));
+ MFXTableColumn ipColumn = new MFXTableColumn<>("IP", false, Comparator.comparing(Device::getIP));
+ MFXTableColumn ownerColumn = new MFXTableColumn<>("Owner", false, Comparator.comparing(Device::getOwner));
+ MFXTableColumn stateColumn = new MFXTableColumn<>("State", false, Comparator.comparing(Device::getState));
+
+ idColumn.setRowCellFactory(device -> new MFXTableRowCell<>(Device::getID));
+ nameColumn.setRowCellFactory(device -> new MFXTableRowCell<>(Device::getName));
+ ipColumn.setRowCellFactory(device -> new MFXTableRowCell<>(Device::getIP) {{
+ setAlignment(Pos.CENTER_RIGHT);
+ }});
+ ownerColumn.setRowCellFactory(device -> new MFXTableRowCell<>(Device::getOwner));
+ stateColumn.setRowCellFactory(device -> new MFXTableRowCell<>(Device::getState));
+ ipColumn.setAlignment(Pos.CENTER_RIGHT);
+
+ paginated.getTableColumns().addAll(idColumn, nameColumn, ipColumn, ownerColumn, stateColumn);
+ paginated.getFilters().addAll(
+ new IntegerFilter<>("ID", Device::getID),
+ new StringFilter<>("Name", Device::getName),
+ new StringFilter<>("IP", Device::getIP),
+ new StringFilter<>("Owner", Device::getOwner),
+ new EnumFilter<>("State", Device::getState, Device.State.class)
+ );
+ paginated.setItems(Model.devices);
+ }
+}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TableViewsDemoController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TableViewsDemoController.java
deleted file mode 100644
index 396e7186..00000000
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TableViewsDemoController.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-package io.github.palexdev.materialfx.demo.controllers;
-
-import io.github.palexdev.materialfx.controls.MFXButton;
-import io.github.palexdev.materialfx.controls.MFXTableView;
-import io.github.palexdev.materialfx.controls.cell.MFXTableColumn;
-import io.github.palexdev.materialfx.controls.cell.MFXTableRowCell;
-import io.github.palexdev.materialfx.controls.legacy.MFXLegacyTableView;
-import io.github.palexdev.materialfx.demo.model.Machine;
-import io.github.palexdev.materialfx.demo.model.Person;
-import io.github.palexdev.materialfx.font.MFXFontIcon;
-import javafx.application.Platform;
-import javafx.beans.binding.Bindings;
-import javafx.beans.property.ObjectProperty;
-import javafx.beans.property.SimpleObjectProperty;
-import javafx.collections.FXCollections;
-import javafx.collections.ObservableList;
-import javafx.fxml.FXML;
-import javafx.fxml.Initializable;
-import javafx.geometry.Insets;
-import javafx.geometry.Pos;
-import javafx.scene.Scene;
-import javafx.scene.control.TableColumn;
-import javafx.scene.input.MouseEvent;
-import javafx.scene.layout.*;
-import javafx.scene.paint.Color;
-import javafx.scene.paint.Paint;
-import javafx.stage.Modality;
-import javafx.stage.Stage;
-
-import java.net.URL;
-import java.util.Comparator;
-import java.util.List;
-import java.util.ResourceBundle;
-import java.util.concurrent.Callable;
-
-import static io.github.palexdev.materialfx.demo.model.Machine.State.OFFLINE;
-import static io.github.palexdev.materialfx.demo.model.Machine.State.ONLINE;
-
-public class TableViewsDemoController implements Initializable {
- private final ObjectProperty tableStage = new SimpleObjectProperty<>();
- private final MFXLegacyTableView legacyTable;
- private final MFXTableView tableView;
- private final StackPane stackPane = new StackPane();
- private final Scene scene = new Scene(stackPane, 800, 600);
-
- @FXML
- private MFXButton showLegacy;
-
- @FXML
- private MFXButton showNew;
-
- public TableViewsDemoController() {
- tableStage.addListener((observable, oldValue, newValue) -> {
- getTableStage().initOwner(showLegacy.getScene().getWindow());
- getTableStage().initModality(Modality.WINDOW_MODAL);
- });
-
- Platform.runLater(() -> tableStage.set(new Stage()));
-
- legacyTable = new MFXLegacyTableView<>();
- tableView = new MFXTableView<>();
- }
-
- @Override
- public void initialize(URL location, ResourceBundle resources) {
- showLegacy.setOnAction(event -> {
- getTableStage().close();
- stackPane.getChildren().setAll(legacyTable);
- getTableStage().setScene(scene);
- getTableStage().setTitle("Legacy TableView - Preview");
- getTableStage().show();
- });
-
- showNew.setOnAction(event -> {
- getTableStage().close();
- stackPane.getChildren().setAll(tableView);
- getTableStage().setScene(scene);
- getTableStage().setTitle("New TableView - Preview");
- getTableStage().show();
- });
-
- populateLegacy();
- populateTable();
- }
-
- @SuppressWarnings("unchecked")
- private void populateLegacy() {
- ObservableList people = FXCollections.observableArrayList(
- List.of(
- new Person("Shaw", "Readdie", "972 Campfire St. Hopkins, MN 55343", 24),
- new Person("Lonnie", "Dane", "30 Walnut St. Galloway, OH 43119", 27),
- new Person("Tia", "Pilgrim", "8141 N. Edgewater Street Cumberland, RI 02864", 45),
- new Person("Liberty", "Ward", "483 East Grand St. Stafford, VA 22554", 34),
- new Person("Aria", "Watkins", "85 Sunnyslope Dr. Vincentown, NJ 08088", 53),
- new Person("Jervis", "Kitchens", "813 Oklahoma Street West Roxbury, MA 02132", 77),
- new Person("Dominick", "Church", "99 E. Alton Ave. Canfield, OH 44406", 29),
- new Person("Forrest", "Davis", "840 Pilgrim Street Lake Villa, IL 60046", 67),
- new Person("Nathaniel", "Crewe", "9407 South 10th Road Wenatchee, WA 98801", 19)
- )
- );
-
- TableColumn firstNameColumn = new TableColumn<>("First Name");
- firstNameColumn.setCellValueFactory(fName -> fName.getValue().firstNameProperty());
- TableColumn lastNameColumn = new TableColumn<>("Last Name");
- lastNameColumn.setCellValueFactory(lName -> lName.getValue().lastNameProperty());
- TableColumn addressColumn = new TableColumn<>("Address");
- addressColumn.setCellValueFactory(addr -> addr.getValue().addressProperty());
- TableColumn ageColumn = new TableColumn<>("Age");
- ageColumn.setCellValueFactory(age -> age.getValue().ageProperty());
-
- legacyTable.setItems(people);
- legacyTable.getColumns().addAll(firstNameColumn, lastNameColumn, addressColumn, ageColumn);
- }
-
- @SuppressWarnings("unchecked")
- private void populateTable() {
- ObservableList people = FXCollections.observableArrayList(
- List.of(
- new Machine("MainPC", "192.144.1.5", "Me", ONLINE),
- new Machine("SecondaryPC", "192.144.1.6", "Me", OFFLINE),
- new Machine("GamingLaptop", "192.144.1.44", "My Sons", OFFLINE),
- new Machine("OfficeLaptop", "192.144.1.98", "Me", ONLINE),
- new Machine("OfficeNAS", "192.144.1.2", "Me", ONLINE),
- new Machine("OfficeAlexa", "192.144.1.34", "", ONLINE),
- new Machine("OfficeSmartTV", "192.144.1.72", "", OFFLINE),
- new Machine("KidsTablet", "192.144.1.11", "My Sons", OFFLINE),
- new Machine("WifeKindle", "192.144.1.35", "My Wife", OFFLINE),
- new Machine("SmartWasher", "192.144.1.78", "", ONLINE),
- new Machine("SmartWatch", "192.144.1.18", "", ONLINE),
- new Machine("GenericSmartphone", "192.144.54", "Me", ONLINE)
- )
- );
-
- MFXTableColumn nameColumn = new MFXTableColumn<>("Name", Comparator.comparing(Machine::getName));
- MFXTableColumn ipColumn = new MFXTableColumn<>("IP", Comparator.comparing(Machine::getIp));
- MFXTableColumn ownerColumn = new MFXTableColumn<>("Owner", Comparator.comparing(Machine::getOwner));
- MFXTableColumn stateColumn = new MFXTableColumn<>("State", Comparator.comparing(Machine::getState));
-
- nameColumn.setRowCellFunction(machine -> new MFXTableRowCell(machine.nameProperty()));
- ipColumn.setRowCellFunction(machine -> {
- MFXTableRowCell cell = new MFXTableRowCell(machine.ipProperty());
- cell.setRowAlignment(Pos.CENTER_RIGHT);
- return cell;
- });
- ownerColumn.setRowCellFunction(machine -> new MFXTableRowCell(machine.ownerProperty()));
- stateColumn.setRowCellFunction(machine -> {
- MFXTableRowCell rowCell = new MFXTableRowCell(machine.stateProperty().asString().concat(" - Click Me"));
- rowCell.setGraphicTextGap(4);
- MFXFontIcon icon = new MFXFontIcon("mfx-circle", 6);
- icon.colorProperty().bind(Bindings.createObjectBinding(
- (Callable) () -> machine.getState() == ONLINE ? Color.LIMEGREEN : Color.SALMON,
- machine.stateProperty())
- );
- rowCell.setLeadingGraphic(icon);
- rowCell.borderProperty().bind(Bindings.createObjectBinding(
- () -> {
- Color borderColor = machine.getState() == ONLINE ? Color.LIMEGREEN : Color.SALMON;
- return new Border(new BorderStroke(borderColor, BorderStrokeStyle.SOLID, new CornerRadii(5), new BorderWidths(1)));
- }, machine.stateProperty()
- ));
- rowCell.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> machine.setState(machine.getState() == ONLINE ? OFFLINE : ONLINE));
- rowCell.setPadding(new Insets(0, 5, 0, 5));
- return rowCell;
- });
-
- ipColumn.setColumnAlignment(Pos.CENTER_RIGHT);
-
- tableView.setItems(people);
- tableView.getTableColumns().addAll(nameColumn, ipColumn, ownerColumn, stateColumn);
- }
-
- public Stage getTableStage() {
- return tableStage.get();
- }
-
- public void setTableStage(Stage tableStage) {
- this.tableStage.set(tableStage);
- }
-}
-
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TextFieldsController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TextFieldsController.java
new file mode 100644
index 00000000..31024670
--- /dev/null
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TextFieldsController.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+package io.github.palexdev.materialfx.demo.controllers;
+
+import io.github.palexdev.materialfx.controls.MFXPasswordField;
+import io.github.palexdev.materialfx.controls.MFXTextField;
+import io.github.palexdev.materialfx.validation.Constraint;
+import io.github.palexdev.materialfx.validation.Severity;
+import javafx.beans.binding.Bindings;
+import javafx.css.PseudoClass;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.Label;
+
+import java.net.URL;
+import java.util.List;
+import java.util.ResourceBundle;
+
+import static io.github.palexdev.materialfx.utils.StringUtils.containsAny;
+
+public class TextFieldsController implements Initializable {
+ private static final PseudoClass INVALID_PSEUDO_CLASS = PseudoClass.getPseudoClass("invalid");
+ // Because fuck regex, stupid shit
+ private static final String[] upperChar = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z".split(" ");
+ private static final String[] lowerChar = "a b c d e f g h i j k l m n o p q r s t u v w x y z".split(" ");
+ private static final String[] digits = "0 1 2 3 4 5 6 7 8 9".split(" ");
+ private static final String[] specialCharacters = "! @ # & ( ) – [ { } ]: ; ' , ? / * ~ $ ^ + = < > -".split(" ");
+
+ @FXML
+ private MFXTextField textField;
+
+ @FXML
+ private MFXPasswordField passwordField;
+
+ @FXML
+ private Label validationLabel;
+
+ @Override
+ public void initialize(URL location, ResourceBundle resources) {
+ textField.setTextLimit(10);
+
+ Constraint lengthConstraint = Constraint.Builder.build()
+ .setSeverity(Severity.ERROR)
+ .setMessage("Password must be at least 8 characters long")
+ .setCondition(passwordField.textProperty().length().greaterThanOrEqualTo(8))
+ .get();
+
+ Constraint digitConstraint = Constraint.Builder.build()
+ .setSeverity(Severity.ERROR)
+ .setMessage("Password must contain at least one digit")
+ .setCondition(Bindings.createBooleanBinding(
+ () -> containsAny(passwordField.getText(), "", digits),
+ passwordField.textProperty()
+ ))
+ .get();
+
+ Constraint charactersConstraint = Constraint.Builder.build()
+ .setSeverity(Severity.ERROR)
+ .setMessage("Password must contain at least one lowercase and one uppercase characters")
+ .setCondition(Bindings.createBooleanBinding(
+ () -> containsAny(passwordField.getText(), "", upperChar) && containsAny(passwordField.getText(), "", lowerChar),
+ passwordField.textProperty()
+ ))
+ .get();
+
+ Constraint specialCharactersConstraint = Constraint.Builder.build()
+ .setSeverity(Severity.ERROR)
+ .setMessage("Password must contain at least one special character")
+ .setCondition(Bindings.createBooleanBinding(
+ () -> containsAny(passwordField.getText(), "", specialCharacters),
+ passwordField.textProperty()
+ ))
+ .get();
+
+ passwordField.getValidator()
+ .constraint(digitConstraint)
+ .constraint(charactersConstraint)
+ .constraint(specialCharactersConstraint)
+ .constraint(lengthConstraint);
+
+ passwordField.getValidator().validProperty().addListener((observable, oldValue, newValue) -> {
+ if (newValue) {
+ validationLabel.setVisible(false);
+ passwordField.pseudoClassStateChanged(INVALID_PSEUDO_CLASS, false);
+ }
+ });
+
+ passwordField.delegateFocusedProperty().addListener((observable, oldValue, newValue) -> {
+ if (oldValue && !newValue) {
+ List constraints = passwordField.validate();
+ if (!constraints.isEmpty()) {
+ passwordField.pseudoClassStateChanged(INVALID_PSEUDO_CLASS, true);
+ validationLabel.setText(constraints.get(0).getMessage());
+ validationLabel.setVisible(true);
+ }
+ }
+ });
+ }
+}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TextFieldsDemoController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TextFieldsDemoController.java
deleted file mode 100644
index 9f06982e..00000000
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TextFieldsDemoController.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-package io.github.palexdev.materialfx.demo.controllers;
-
-import io.github.palexdev.materialfx.controls.MFXCheckbox;
-import io.github.palexdev.materialfx.controls.MFXDatePicker;
-import io.github.palexdev.materialfx.controls.MFXPasswordField;
-import io.github.palexdev.materialfx.controls.MFXTextField;
-import io.github.palexdev.materialfx.font.MFXFontIcon;
-import io.github.palexdev.materialfx.utils.BindingUtils;
-import io.github.palexdev.materialfx.utils.StringUtils;
-import javafx.beans.binding.Bindings;
-import javafx.beans.property.BooleanProperty;
-import javafx.fxml.FXML;
-import javafx.fxml.Initializable;
-import javafx.scene.paint.Color;
-
-import java.net.URL;
-import java.time.LocalDate;
-import java.time.Month;
-import java.util.ResourceBundle;
-
-public class TextFieldsDemoController implements Initializable {
-
- @FXML
- private MFXTextField validated;
-
- @FXML
- private MFXCheckbox checkbox;
-
- @FXML
- private MFXDatePicker picker;
-
- @FXML
- private MFXPasswordField passwordValidated;
-
- @Override
- public void initialize(URL location, ResourceBundle resources) {
- BooleanProperty checkboxValidation = BindingUtils.toProperty(
- Bindings.createBooleanBinding(
- () -> checkbox.isSelected(),
- checkbox.selectedProperty()
- )
- );
- BooleanProperty datePickerValidation = BindingUtils.toProperty(
- Bindings.createBooleanBinding(
- () -> {
- LocalDate value = picker.getDatePicker().getValue();
- if (value != null) {
- return value.equals(LocalDate.of(1911, Month.OCTOBER, 3));
- } else {
- return false;
- }
- },
- picker.getDatePicker().valueProperty()
- )
- );
- validated.getValidator().add(checkboxValidation, "Checkbox must be selected");
- validated.getValidator().add(datePickerValidation, "Selected date must be 03/10/1911");
- validated.setValidated(true);
- validated.setIcon(new MFXFontIcon("mfx-variant7-mark", 16, Color.web("#8FF7A7")));
- validated.getIcon().visibleProperty().bind(validated.getValidator().validProperty());
-
- passwordValidated.setValidated(true);
- passwordValidated.getValidator().add(
- BindingUtils.toProperty(passwordValidated.passwordProperty().length().greaterThanOrEqualTo(8)),
- "Must be at least 8 characters long"
- );
- passwordValidated.getValidator().add(BindingUtils.toProperty(
- Bindings.createBooleanBinding(() -> passwordValidated.getPassword().matches(".*\\d.*"), passwordValidated.passwordProperty())),
- "Must contain at least one digit"
- );
- passwordValidated.getValidator().add(BindingUtils.toProperty(
- Bindings.createBooleanBinding(() -> StringUtils.containsAny(passwordValidated.getPassword(), "", "?", "!", "@", "(", ")", "[", "]", "{", "}", "-", "_"), passwordValidated.passwordProperty())),
- "Must contain at least one among these: ?!@()[]{}-_"
- );
- }
-}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TreeviewsDemoController.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TreeviewsDemoController.java
deleted file mode 100644
index a24967ac..00000000
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/controllers/TreeviewsDemoController.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-package io.github.palexdev.materialfx.demo.controllers;
-
-import io.github.palexdev.materialfx.controls.MFXCheckTreeItem;
-import io.github.palexdev.materialfx.controls.MFXCheckTreeView;
-import io.github.palexdev.materialfx.controls.MFXTreeItem;
-import io.github.palexdev.materialfx.controls.MFXTreeView;
-import io.github.palexdev.materialfx.font.MFXFontIcon;
-import io.github.palexdev.materialfx.utils.ColorUtils;
-import io.github.palexdev.materialfx.utils.ScrollUtils;
-import javafx.fxml.FXML;
-import javafx.fxml.Initializable;
-import javafx.geometry.Pos;
-import javafx.scene.control.Label;
-import javafx.scene.layout.HBox;
-import javafx.scene.text.Text;
-
-import java.net.URL;
-import java.util.List;
-import java.util.ResourceBundle;
-
-public class TreeviewsDemoController implements Initializable {
-
- @FXML
- private MFXTreeView treeView;
-
- @FXML
- private MFXTreeView treeViewHide;
-
- @FXML
- private MFXCheckTreeView checkTreeView;
-
- @FXML
- private Text text1;
-
- @FXML
- private Text text2;
-
- @FXML
- private Text text3;
-
- @Override
- public void initialize(URL location, ResourceBundle resources) {
- treeView.setRoot(createRoot());
-
- treeViewHide.setRoot(createNodeRoot());
- treeViewHide.setShowRoot(false);
-
- checkTreeView.setRoot(createCheckRoot());
-
- ScrollUtils.addSmoothScrolling(treeView);
- ScrollUtils.addSmoothScrolling(treeViewHide);
- ScrollUtils.addSmoothScrolling(checkTreeView);
-
- treeView.getSelectionModel().getSelectedItems().addListener(
- (observable, oldValue, newValue) -> text1.setText("Selected Items Count: " + treeView.getSelectionModel().getSelectedItems().size()));
- treeViewHide.getSelectionModel().getSelectedItems().addListener(
- (observable, oldValue, newValue) -> text2.setText("Selected Items Count: " + newValue.size()));
- checkTreeView.getCheckModel().getCheckedItems().addListener(
- (observable, oldValue, newValue) -> text3.setText("Checked Items Count: " + newValue.size()));
- }
-
- public MFXTreeItem createRoot() {
- MFXTreeItem root = new MFXTreeItem<>("Tree View Root");
-
- MFXTreeItem item1 = new MFXTreeItem<>("ITEM1");
- item1.getItems().addAll(List.of(
- new MFXTreeItem<>("ITEM1-Sub1"),
- new MFXTreeItem<>("ITEM1-Sub2")
- )
- );
-
- MFXTreeItem item2 = new MFXTreeItem<>("ITEM2");
- item2.getItems().addAll(List.of(
- new MFXTreeItem<>("ITEM2-Sub1"),
- new MFXTreeItem<>("ITEM2-Sub2"),
- new MFXTreeItem<>("ITEM2-Sub3"),
- new MFXTreeItem<>("ITEM2-Sub4")
- )
- );
-
- MFXTreeItem item3 = new MFXTreeItem<>("ITEM3");
-
- MFXTreeItem item4 = new MFXTreeItem<>("ITEM4");
- item2.getItems().add(
- new MFXTreeItem<>("ITEM4-Sub1")
- );
-
- MFXTreeItem item5 = new MFXTreeItem<>("ITEM5");
- item2.getItems().addAll(List.of(
- new MFXTreeItem<>("ITEM5-Sub1"),
- new MFXTreeItem<>("ITEM5-Sub2"),
- new MFXTreeItem<>("ITEM5-Sub3")
- )
- );
-
- root.getItems().addAll(List.of(item1, item2, item3, item4, item5));
- return root;
- }
-
- public MFXTreeItem createNodeRoot() {
- MFXTreeItem root = new MFXTreeItem<>(createBox("mfx-google", "Google Root"));
-
- MFXTreeItem item1 = new MFXTreeItem<>(createBox("mfx-google", "ITEM1"));
- item1.getItems().addAll(List.of(
- new MFXTreeItem<>(createBox("mfx-google", "ITEM1-Sub1")),
- new MFXTreeItem<>(createBox("mfx-google", "ITEM1-Sub2"))
- )
- );
-
- MFXTreeItem item2 = new MFXTreeItem<>(createBox("mfx-calendar-black", "ITEM2"));
- item2.getItems().addAll(List.of(
- new MFXTreeItem<>(createBox("mfx-calendar-black", "ITEM2-Sub1")),
- new MFXTreeItem<>(createBox("mfx-calendar-black", "ITEM2-Sub2")),
- new MFXTreeItem<>(createBox("mfx-calendar-black", "ITEM2-Sub3")),
- new MFXTreeItem<>(createBox("mfx-calendar-black", "ITEM2-Sub4"))
- )
- );
-
- MFXTreeItem item3 = new MFXTreeItem<>(createBox("mfx-exclamation-triangle", "ITEM3"));
-
- MFXTreeItem item4 = new MFXTreeItem<>(createBox("mfx-circle", "ITEM4"));
- item4.getItems().add(
- new MFXTreeItem<>(createBox("mfx-info-circle", "ITEM4-Sub1"))
- );
-
- MFXTreeItem item5 = new MFXTreeItem<>(createBox("mfx-circle", "ITEM5"));
- item5.getItems().addAll(List.of(
- new MFXTreeItem<>(createBox("mfx-circle", "ITEM5-Sub1")),
- new MFXTreeItem<>(createBox("mfx-circle", "ITEM5-Sub2")),
- new MFXTreeItem<>(createBox("mfx-circle", "ITEM5-Sub3"))
- )
- );
-
- root.getItems().addAll(List.of(item1, item2, item3, item4, item5));
- return root;
- }
-
- public MFXCheckTreeItem createCheckRoot() {
- MFXCheckTreeItem root = new MFXCheckTreeItem<>("ROOT");
-
- MFXCheckTreeItem i1 = new MFXCheckTreeItem<>("I1");
- MFXCheckTreeItem i1a = new MFXCheckTreeItem<>("I1A");
- i1a.getItems().add(new MFXCheckTreeItem<>("I11A"));
-
- MFXCheckTreeItem i1b = new MFXCheckTreeItem<>("I1B");
- i1.getItems().addAll(List.of(i1a, i1b));
-
- MFXCheckTreeItem i2 = new MFXCheckTreeItem<>("I2");
- MFXCheckTreeItem i2a = new MFXCheckTreeItem<>("I2A");
- i2.getItems().add(i2a);
-
- MFXCheckTreeItem i3 = new MFXCheckTreeItem<>("I3");
- MFXCheckTreeItem i3a = new MFXCheckTreeItem<>("I3A");
- MFXCheckTreeItem i3b = new MFXCheckTreeItem<>("I3B");
- i3.getItems().addAll(List.of(i3a, i3b));
-
- MFXCheckTreeItem i4 = new MFXCheckTreeItem<>("I4");
- MFXCheckTreeItem i4a = new MFXCheckTreeItem<>("I4A");
- i4.getItems().add(i4a);
-
- root.getItems().addAll(List.of(i1, i2, i3, i4));
- return root;
- }
-
- private HBox createBox(String iconDescription, String text) {
- MFXFontIcon icon = new MFXFontIcon(iconDescription, ColorUtils.getRandomColor());
- HBox hBox = new HBox(10, icon, new Label(text));
- hBox.setAlignment(Pos.CENTER_LEFT);
- return hBox;
- }
-}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/model/Device.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/model/Device.java
new file mode 100644
index 00000000..dbd04e93
--- /dev/null
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/model/Device.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+package io.github.palexdev.materialfx.demo.model;
+
+import io.github.palexdev.materialfx.utils.RandomUtils;
+import javafx.beans.property.*;
+
+public class Device {
+ public enum State {
+ ONLINE, OFFLINE
+ }
+
+ private final IntegerProperty id = new SimpleIntegerProperty();
+ private final StringProperty name = new SimpleStringProperty("");
+ private final StringProperty ip = new SimpleStringProperty("");
+ private final StringProperty owner = new SimpleStringProperty("");
+ private final ObjectProperty state = new SimpleObjectProperty<>();
+
+ public Device(int id, String name, String ip, String owner, State state) {
+ setID(id);
+ setName(name);
+ setIP(ip);
+ setOwner(owner);
+ setState(state);
+ }
+
+ public int getID() {
+ return id.get();
+ }
+
+ public IntegerProperty idProperty() {
+ return id;
+ }
+
+ public void setID(int id) {
+ this.id.set(id);
+ }
+
+ public String getName() {
+ return name.get();
+ }
+
+ public StringProperty nameProperty() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name.set(name);
+ }
+
+ public String getIP() {
+ return ip.get();
+ }
+
+ public StringProperty ipProperty() {
+ return ip;
+ }
+
+ public void setIP(String ip) {
+ this.ip.set(ip);
+ }
+
+ public String getOwner() {
+ return owner.get();
+ }
+
+ public StringProperty ownerProperty() {
+ return owner;
+ }
+
+ public void setOwner(String owner) {
+ this.owner.set(owner);
+ }
+
+ public State getState() {
+ return state.get();
+ }
+
+ public ObjectProperty stateProperty() {
+ return state;
+ }
+
+ public void setState(State state) {
+ this.state.set(state);
+ }
+
+ public static int randomID() {
+ return RandomUtils.random.nextInt(100000, 1000000);
+ }
+}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/model/FilterablePerson.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/model/FilterablePerson.java
deleted file mode 100644
index c598f4eb..00000000
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/model/FilterablePerson.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-package io.github.palexdev.materialfx.demo.model;
-
-import io.github.palexdev.materialfx.filter.IFilterable;
-import javafx.beans.property.IntegerProperty;
-import javafx.beans.property.SimpleIntegerProperty;
-import javafx.beans.property.SimpleStringProperty;
-import javafx.beans.property.StringProperty;
-
-public class FilterablePerson implements IFilterable {
- private final StringProperty firstName = new SimpleStringProperty();
- private final StringProperty lastName = new SimpleStringProperty();
- private final StringProperty address = new SimpleStringProperty();
- private final IntegerProperty age = new SimpleIntegerProperty();
-
- public FilterablePerson(String firstName, String lastName, String address, Integer age) {
- setFirstName(firstName);
- setLastName(lastName);
- setAddress(address);
- setAge(age);
- }
-
- public String getFirstName() {
- return firstName.get();
- }
-
- public StringProperty firstNameProperty() {
- return firstName;
- }
-
- public void setFirstName(String firstName) {
- this.firstName.set(firstName);
- }
-
- public String getLastName() {
- return lastName.get();
- }
-
- public StringProperty lastNameProperty() {
- return lastName;
- }
-
- public void setLastName(String lastName) {
- this.lastName.set(lastName);
- }
-
- public String getAddress() {
- return address.get();
- }
-
- public StringProperty addressProperty() {
- return address;
- }
-
- public void setAddress(String address) {
- this.address.set(address);
- }
-
- public int getAge() {
- return age.get();
- }
-
- public IntegerProperty ageProperty() {
- return age;
- }
-
- public void setAge(int age) {
- this.age.set(age);
- }
-
- @Override
- public String toFilterString() {
- return getFirstName() + getLastName() + getAddress() + getAge();
- }
-
- @Override
- public String toString() {
- return getFirstName() + " " + getLastName();
- }
-}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/model/Machine.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/model/Machine.java
deleted file mode 100644
index 9799f153..00000000
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/model/Machine.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-package io.github.palexdev.materialfx.demo.model;
-
-import io.github.palexdev.materialfx.filter.IFilterable;
-import javafx.beans.property.ObjectProperty;
-import javafx.beans.property.SimpleObjectProperty;
-import javafx.beans.property.SimpleStringProperty;
-import javafx.beans.property.StringProperty;
-
-public class Machine implements IFilterable {
-
- public enum State {
- ONLINE, OFFLINE
- }
-
- private final StringProperty name = new SimpleStringProperty("");
- private final StringProperty ip = new SimpleStringProperty("");
- private final StringProperty owner = new SimpleStringProperty("");
- private final ObjectProperty state = new SimpleObjectProperty<>();
-
- public Machine(String name, String ip, String owner, State state) {
- setName(name);
- setIp(ip);
- setOwner(owner);
- setState(state);
- }
-
- public String getName() {
- return name.get();
- }
-
- public StringProperty nameProperty() {
- return name;
- }
-
- public void setName(String name) {
- this.name.set(name);
- }
-
- public String getIp() {
- return ip.get();
- }
-
- public StringProperty ipProperty() {
- return ip;
- }
-
- public void setIp(String ip) {
- this.ip.set(ip);
- }
-
- public String getOwner() {
- return owner.get();
- }
-
- public StringProperty ownerProperty() {
- return owner;
- }
-
- public void setOwner(String owner) {
- this.owner.set(owner);
- }
-
- public State getState() {
- return state.get();
- }
-
- public ObjectProperty stateProperty() {
- return state;
- }
-
- public void setState(State state) {
- this.state.set(state);
- }
-
- @Override
- public String toFilterString() {
- return getName() + " " + getIp() + " " + getOwner() + " " + getState().name();
- }
-}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/model/Model.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/model/Model.java
new file mode 100644
index 00000000..40147258
--- /dev/null
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/model/Model.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+package io.github.palexdev.materialfx.demo.model;
+
+import io.github.palexdev.materialfx.font.FontResources;
+import io.github.palexdev.materialfx.utils.FXCollectors;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+
+import java.util.stream.IntStream;
+
+import static io.github.palexdev.materialfx.demo.model.Device.State.OFFLINE;
+import static io.github.palexdev.materialfx.demo.model.Device.State.ONLINE;
+import static io.github.palexdev.materialfx.demo.model.Device.randomID;
+
+public class Model {
+ public static final String ipsum =
+ """
+ Lorem Ipsum is simply dummy text of the printing and typesetting industry.
+ Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
+ It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.
+ It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
+ """;
+ public static final FontResources[] notificationsIcons;
+ public static final String[] randomText;
+ public static final ObservableList strings;
+ public static final ObservableList people;
+ public static final ObservableList devices;
+
+ static {
+ notificationsIcons = new FontResources[]{
+ FontResources.BELL, FontResources.BELL_ALT,
+ FontResources.CALENDAR_ALT_DARK, FontResources.CALENDAR_ALT_SEMI_DARK,
+ FontResources.CHART_PIE, FontResources.CIRCLE, FontResources.CIRCLE_EMPTY,
+ FontResources.EXCLAMATION_CIRCLE, FontResources.EXCLAMATION_TRIANGLE,
+ FontResources.GEAR, FontResources.GOOGLE_DRIVE, FontResources.HOME,
+ FontResources.INFO_CIRCLE, FontResources.MUSIC,
+ FontResources.USER, FontResources.USERS, FontResources.VIDEO,
+ FontResources.X_CIRCLE
+ };
+
+ randomText = new String[]{
+ """
+ In the United States there are about 350 spoken languages. But coding has us beat with over 700 coding languages in use today! Only two countries speak more languages, Papua New Guinea (836) and Indonesia (710).
+ Some programming languages, like Java, Python, and HTML, are more common, but others, like Rust and Kotlin, are used in very specific situations.
+ The good news for coders? Once you learn the big ones, the more niche languages come easily.
+ """,
+ """
+ Have you ever encountered a computer bug? How about a real bug in your computer? In 1947 a technician at Harvard had an issue with the performance of their Mark II computer.
+ Once they investigated, they discovered that a moth had gotten into a relay – an actual real live bug. In the logbook, it was noted as “First actual case of bug being found."
+ While it is oft-repeated that this is where the term “bug" came to refer to errors that impacted the performance of programs, this is not the case.
+ The term “bug" was already in fairly widespread use in technical circles in 1947. Thomas Edison used it in 1869 to describe problems in his own inventions.
+ """,
+ """
+ Coding and STEM fields may seem like it’s built for boys, but the first person to write our modern understanding of a program was Ada Lovelace.
+ Being the only legitimate daughter of the poet, Lord Byron, Ada’s mother feared her daughter would suffer the same madness as her father.
+ To stave off the madness as long as possible, she dedicated her daughter to studying math and science.
+ While working with a peer on a mechanical general-purpose computer known as the Analytical Engine, she recognized that the machine could go way beyond simple and pure calculations, publishing then the first algorithm intended to be carried out by a machine like this one.
+ """,
+ """
+ The idea of a computer virus was published in the essay “Theory of self-reproducing automata" by John von Neumann in 1949, but the first replicating computer program was not written until 1971.
+ The program was not actively malicious software as it caused no damage to data,
+ the only effect being a message it output to the teletype reading “I’M THE CREEPER; CATCH ME IF YOU CAN".
+ """,
+ """
+ You may be fluent in Javascript or C++ but what NASA engineers really need to know is ADA and HAL/S.
+ Up through 2005, NASA was still using a computer language from 1973 specifically designed for their needs called HAL/S (or High-order Assembly Language/Shuttle).
+ Although HAL/S is designed primarily for programming on-board computers, it is general enough for almost any application and is used widely across NASA’s projects.
+ Newer projects, such as the International Space Station, operate on a programming language called ADA, developed in 1980 and accepted as an international standard programming language in 1995.
+ """,
+ """
+ In 1972, Steve Wozniak and Steve Jobs collaborated on an arcade game, Breakout, for Atari.
+ In 2018, Apple Inc became the first US Trillion dollar company. There’s no doubt, there is big money to be had in coding. And by big money, we mean billions.
+ The average salary of a data scientist is up to $100,000. Enjoy computer games? Markus Persson, a Swedish programmer, created and launched the computer game Minecraft in 2009.
+ By 2014, Microsoft bought it for $2.5 billion.
+ """,
+ """
+ Computers operate on what is called a “binary code." All of the software that runs them is written using only 0’s and 1’s,
+ and there are infinite combinations of these two digits. That’s why new software can be written all the time.
+ """,
+ """
+ As of the end of 2020, 70% of coding jobs are in career fields not connected with technology.
+ Those who learn to code early and well will have a choice of many careers in almost every industry imaginable.
+ """,
+ """
+ The first-ever computer game made zero profit for its team of creators.
+ The game, titled Spacewar, was built from the ground up by Steve Russel, a young computer programmer, and his passionate team of fellow developers.
+ Incredibly, Steve and his team chose not to charge people to play Spacewar, and instead, happily shared their creation with anyone who wanted to try it out.
+ """
+ };
+
+ strings = IntStream.rangeClosed(1, 25)
+ .mapToObj(i -> "String " + i)
+ .collect(FXCollectors.toList());
+
+ people = FXCollections.observableArrayList(
+ Person.ofSplit("Turner Romero", " ").randomAge(),
+ Person.ofSplit("Harley Hays", " ").randomAge(),
+ Person.ofSplit("Jeffrey Cannon", " ").randomAge(),
+ Person.ofSplit("Simeon Huang", " ").randomAge(),
+ Person.ofSplit("Jennifer Donovan", " ").randomAge(),
+ Person.ofSplit("Hezekiah Stout", " ").randomAge(),
+ Person.ofSplit("Roberto Evans", " ").randomAge(),
+ Person.ofSplit("Braxton Watts", " ").randomAge(),
+ Person.ofSplit("Jayvon Wilkinson", " ").randomAge(),
+ Person.ofSplit("Anabelle Chang", " ").randomAge(),
+ Person.ofSplit("Abigayle Christensen", " ").randomAge(),
+ Person.ofSplit("Fletcher May", " ").randomAge(),
+ Person.ofSplit("Marisol Morris", " ").randomAge(),
+ Person.ofSplit("Grant Wilson", " ").randomAge(),
+ Person.ofSplit("Hayden Baldwin", " ").randomAge(),
+ Person.ofSplit("Markus Davidson", " ").randomAge(),
+ Person.ofSplit("Madelyn Farmer", " ").randomAge(),
+ Person.ofSplit("Deandre Crosby", " ").randomAge(),
+ Person.ofSplit("Casey Hardy", " ").randomAge(),
+ Person.ofSplit("Carmelo Velazquez", " ").randomAge(),
+ Person.ofSplit("Phillip Hays", " ").randomAge(),
+ Person.ofSplit("Damari Mcfarland", " ").randomAge(),
+ Person.ofSplit("Selina Norton", " ").randomAge(),
+ Person.ofSplit("Lukas Vaughan", " ").randomAge(),
+ Person.ofSplit("Charlie Carney", " ").randomAge()
+ );
+
+ devices = FXCollections.observableArrayList(
+ new Device(randomID(), "HP Gaming Laptop", "144.156.1.1", "Me", ONLINE),
+ new Device(randomID(), "Own Gaming Desktop", "", "Me", OFFLINE),
+ new Device(randomID(), "Pear fPhone X", "144.156.1.98", "Me", ONLINE),
+ new Device(randomID(), "Alexa Echo Dot", "144.156.1.71", "Me", ONLINE),
+ new Device(randomID(), "Surface Pro", "", "Wife", OFFLINE),
+ new Device(randomID(), "Pear sPhone S", "144.156.1.70", "Wife", ONLINE),
+ new Device(randomID(), "Chromebook", "", "Wife", OFFLINE),
+ new Device(randomID(), "Wife Gaming Desktop", "144.156.1.69", "Wife", ONLINE),
+ new Device(randomID(), "Chromecast", "", "", OFFLINE),
+ new Device(randomID(), "Smart Dishwasher", "144.156.1.7", "", ONLINE),
+ new Device(randomID(), "Samsung Smart TV", "", "", OFFLINE),
+ new Device(randomID(), "Google Home Mini", "144.156.1.58", "", ONLINE),
+ new Device(randomID(), "Roomba Killer 2000", "144.156.1.42", "", ONLINE),
+ new Device(randomID(), "Smart WC", "144.156.1.99", "", ONLINE),
+ new Device(randomID(), "Kids Gaming Desktop", "144.156.1.127", "Kids", ONLINE),
+ new Device(randomID(), "Kids Gaming Laptop", "144.156.1.153", "Kids", ONLINE),
+ new Device(randomID(), "XBox 360 No Scope Edition", "", "Kids", OFFLINE),
+ new Device(randomID(), "PS5", "", "Kids", OFFLINE),
+ new Device(randomID(), "PS6", "", "My cousin", OFFLINE),
+ new Device(randomID(), "Smart Watch", "144.156.1.155", "Kids", ONLINE),
+ new Device(randomID(), "Smart Watch", "144.156.1.154", "Kids", ONLINE),
+ new Device(randomID(), "Kids' Smartphone", "144.156.1.221", "Kids", ONLINE),
+ new Device(randomID(), "Kids Smartphone", "", "Kids", OFFLINE),
+ new Device(randomID(), "Home Lights Controller", "144.156.1.91", "", ONLINE),
+ new Device(randomID(), "WiFi Extender", "144.156.1.10", "", ONLINE)
+ );
+ }
+}
diff --git a/demo/src/main/java/io/github/palexdev/materialfx/demo/model/Person.java b/demo/src/main/java/io/github/palexdev/materialfx/demo/model/Person.java
index 80d20304..2c5e43a5 100644
--- a/demo/src/main/java/io/github/palexdev/materialfx/demo/model/Person.java
+++ b/demo/src/main/java/io/github/palexdev/materialfx/demo/model/Person.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Parisi Alessandro
+ * Copyright (C) 2022 Parisi Alessandro
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
*
* MaterialFX is free software: you can redistribute it and/or modify
@@ -18,74 +18,47 @@
package io.github.palexdev.materialfx.demo.model;
-import javafx.beans.property.IntegerProperty;
-import javafx.beans.property.SimpleIntegerProperty;
-import javafx.beans.property.SimpleStringProperty;
-import javafx.beans.property.StringProperty;
+import io.github.palexdev.materialfx.utils.RandomUtils;
public class Person {
- private final StringProperty firstName = new SimpleStringProperty();
- private final StringProperty lastName = new SimpleStringProperty();
- private final StringProperty address = new SimpleStringProperty();
- private final IntegerProperty age = new SimpleIntegerProperty();
-
- public Person(String firstName, String lastName, String address, Integer age) {
- setFirstName(firstName);
- setLastName(lastName);
- setAddress(address);
- setAge(age);
- }
-
- public String getFirstName() {
- return firstName.get();
- }
-
- public StringProperty firstNameProperty() {
- return firstName;
- }
-
- public void setFirstName(String firstName) {
- this.firstName.set(firstName);
- }
-
- public String getLastName() {
- return lastName.get();
- }
-
- public StringProperty lastNameProperty() {
- return lastName;
- }
-
- public void setLastName(String lastName) {
- this.lastName.set(lastName);
- }
-
- public String getAddress() {
- return address.get();
- }
-
- public StringProperty addressProperty() {
- return address;
- }
-
- public void setAddress(String address) {
- this.address.set(address);
- }
-
- public int getAge() {
- return age.get();
- }
-
- public IntegerProperty ageProperty() {
- return age;
- }
-
- public void setAge(int age) {
- this.age.set(age);
- }
-
- @Override
- public String toString() {
- return getFirstName() + " " + getLastName();
- }
+ private final String name;
+ private final String surname;
+ private int age;
+
+ public Person(String name, String surname) {
+ this.name = name;
+ this.surname = surname;
+ }
+
+ public Person(String name, String surname, int age) {
+ this.name = name;
+ this.surname = surname;
+ this.age = age;
+ }
+
+ public static Person ofSplit(String fullName, String split) {
+ String[] fNameArray = fullName.split(split);
+ return new Person(fNameArray[0], fNameArray[1]);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getSurname() {
+ return surname;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public Person randomAge() {
+ setAge(RandomUtils.random.nextInt(18, 81));
+ return this;
+ }
}
diff --git a/demo/src/main/java/module-info.java b/demo/src/main/java/module-info.java
old mode 100644
new mode 100755
index b482923a..a3f637f8
--- a/demo/src/main/java/module-info.java
+++ b/demo/src/main/java/module-info.java
@@ -1,18 +1,18 @@
module MaterialFX.Demo {
- requires MaterialFX;
+ requires MaterialFX;
- requires jdk.localedata;
+ requires jdk.localedata;
- requires javafx.controls;
- requires javafx.fxml;
- requires javafx.graphics;
- requires javafx.media;
+ requires javafx.controls;
+ requires javafx.fxml;
+ requires javafx.graphics;
+ requires javafx.media;
- requires fr.brouillard.oss.cssfx;
- requires org.kordamp.ikonli.javafx;
- requires org.kordamp.ikonli.fontawesome5;
- requires org.scenicview.scenicview;
+ requires fr.brouillard.oss.cssfx;
+ requires org.kordamp.ikonli.javafx;
+ requires org.kordamp.ikonli.fontawesome5;
+ requires org.scenicview.scenicview;
- opens io.github.palexdev.materialfx.demo;
- opens io.github.palexdev.materialfx.demo.controllers;
+ opens io.github.palexdev.materialfx.demo;
+ opens io.github.palexdev.materialfx.demo.controllers;
}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/ButtonsDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/ButtonsDemo.fxml
deleted file mode 100644
index f8e56143..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/ButtonsDemo.fxml
+++ /dev/null
@@ -1,82 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/CheckBoxesDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/CheckBoxesDemo.fxml
deleted file mode 100644
index 456b8ed2..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/CheckBoxesDemo.fxml
+++ /dev/null
@@ -1,95 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/ComboBoxesDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/ComboBoxesDemo.fxml
deleted file mode 100644
index 8e1dc580..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/ComboBoxesDemo.fxml
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/DatePickersDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/DatePickersDemo.fxml
deleted file mode 100644
index 4e4d7798..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/DatePickersDemo.fxml
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/Demo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/Demo.fxml
deleted file mode 100644
index af70bad2..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/Demo.fxml
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/DialogsDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/DialogsDemo.fxml
deleted file mode 100644
index 38f379da..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/DialogsDemo.fxml
+++ /dev/null
@@ -1,94 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/FontResourcesDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/FontResourcesDemo.fxml
deleted file mode 100644
index c7993f00..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/FontResourcesDemo.fxml
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/InfoDialog.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/InfoDialog.fxml
deleted file mode 100644
index 055169ad..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/InfoDialog.fxml
+++ /dev/null
@@ -1,115 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/LabelsDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/LabelsDemo.fxml
deleted file mode 100644
index 8fbb49b4..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/LabelsDemo.fxml
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/ListViewsDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/ListViewsDemo.fxml
deleted file mode 100644
index 43c7dd21..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/ListViewsDemo.fxml
+++ /dev/null
@@ -1,108 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/NotificationsDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/NotificationsDemo.fxml
deleted file mode 100644
index aed78d23..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/NotificationsDemo.fxml
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/ProgressBarsDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/ProgressBarsDemo.fxml
deleted file mode 100644
index 18b10eb7..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/ProgressBarsDemo.fxml
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/ProgressSpinnersDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/ProgressSpinnersDemo.fxml
deleted file mode 100644
index 83cefb22..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/ProgressSpinnersDemo.fxml
+++ /dev/null
@@ -1,81 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/RadioButtonsDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/RadioButtonsDemo.fxml
deleted file mode 100644
index ac3dcb60..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/RadioButtonsDemo.fxml
+++ /dev/null
@@ -1,78 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/ScrollPanesDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/ScrollPanesDemo.fxml
deleted file mode 100644
index 623d62ff..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/ScrollPanesDemo.fxml
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/SlidersDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/SlidersDemo.fxml
deleted file mode 100644
index 92f6c79c..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/SlidersDemo.fxml
+++ /dev/null
@@ -1,113 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/StepperDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/StepperDemo.fxml
deleted file mode 100644
index e5141f88..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/StepperDemo.fxml
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/TableViewsDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/TableViewsDemo.fxml
deleted file mode 100644
index b532a7e4..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/TableViewsDemo.fxml
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/TextFieldsDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/TextFieldsDemo.fxml
deleted file mode 100644
index ba452708..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/TextFieldsDemo.fxml
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/ToggleButtonsDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/ToggleButtonsDemo.fxml
deleted file mode 100644
index 52ea92b9..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/ToggleButtonsDemo.fxml
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/TreeViewsDemo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/TreeViewsDemo.fxml
deleted file mode 100644
index ba781888..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/TreeViewsDemo.fxml
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/assets/logo.png b/demo/src/main/resources/io/github/palexdev/materialfx/demo/assets/logo.png
deleted file mode 100644
index f57b8e7a..00000000
Binary files a/demo/src/main/resources/io/github/palexdev/materialfx/demo/assets/logo.png and /dev/null differ
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/assets/welcome1.wav b/demo/src/main/resources/io/github/palexdev/materialfx/demo/assets/welcome1.wav
deleted file mode 100644
index 06222d4d..00000000
Binary files a/demo/src/main/resources/io/github/palexdev/materialfx/demo/assets/welcome1.wav and /dev/null differ
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/assets/welcome2.wav b/demo/src/main/resources/io/github/palexdev/materialfx/demo/assets/welcome2.wav
deleted file mode 100644
index 4c68715e..00000000
Binary files a/demo/src/main/resources/io/github/palexdev/materialfx/demo/assets/welcome2.wav and /dev/null differ
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Buttons.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Buttons.css
new file mode 100644
index 00000000..bd16212f
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Buttons.css
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+@import '../fonts/Fonts.css';
+@import 'Common.css';
+@import 'MFXColors.css';
+
+.mfx-button {
+ -fx-font-family: 'Visby Round CF Medium';
+ -fx-text-fill: -mfx-text-he;
+}
+
+.mfx-button .mfx-ripple-generator {
+ -mfx-ripple-radius: 40;
+ -mfx-auto-clip: true;
+}
+
+.mfx-button .text {
+ -fx-font-smoothing-type: lcd;
+}
+
+#custom {
+ -fx-background-color: -mfx-purple;
+ -fx-text-fill: white
+}
+
+.outline-button {
+ -fx-background-color: transparent;
+ -fx-border-color: -mfx-purple;
+ -fx-border-radius: 3;
+
+ -fx-text-fill: -mfx-purple;
+}
+
+.outline-button:hover,
+.outline-button:focused {
+ -fx-background-color: -mfx-purple;
+ -fx-text-fill: white;
+}
+
+
+.link-button {
+ -fx-background-color: transparent;
+ -fx-border-radius: 3;
+ -fx-text-fill: #0096FF;
+ -fx-underline: true;
+ -fx-cursor: hand;
+}
+
+.link-button:armed,
+.link-button:focused {
+ -fx-border-color: #006BFF;
+ -fx-text-fill: #006BFF;
+}
+
+.link-button .mfx-ripple-generator {
+ -mfx-ripple-color: #D9E9FF;
+}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ButtonsDemo.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ButtonsDemo.css
deleted file mode 100644
index f3c5e869..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ButtonsDemo.css
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-.flat-button {
- -fx-background-color: transparent;
-}
-
-.flat-button .mfx-ripple-generator {
- -mfx-ripple-color: #E1E1E1;
-}
-
-.flat-button .text {
- -fx-font-family: Comfortaa-SemiBold;
- -fx-font-size: 12;
- -fx-font-weight: bold;
-}
-
-.raised-button {
- -fx-background-color: white;
- -mfx-button-type: raised;
-}
-
-.raised-button .mfx-ripple-generator {
- -mfx-ripple-color: #E1E1E1;
-}
-
-.raised-button .text {
- -fx-font-family: Comfortaa-SemiBold;
- -fx-font-size: 12;
- -fx-font-weight: bold;
-}
-
-#colored-raised {
- -fx-background-color: #b700ff;
- -fx-text-fill: white;
-}
-
-#colored-raised .mfx-ripple-generator {
- -mfx-ripple-color: rgba(255, 255, 255, 0.4);
-}
-
-#custom-ripple {
- -fx-background-color: #55bbff;
- -fx-text-fill: white;
-}
-
-#custom-ripple .mfx-ripple-generator {
- -mfx-animation-speed: 1.5;
- -mfx-ripple-color: #1900ff;
-}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/CheckBoxesDemo.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/CheckBoxesDemo.css
deleted file mode 100644
index cd040869..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/CheckBoxesDemo.css
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-#custom-ripple .ripple-container .mfx-ripple-generator {
- -mfx-ripple-color: rgb(60, 255, 45);
-}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ChecksRadiosToggles.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ChecksRadiosToggles.css
new file mode 100644
index 00000000..e7344a0d
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ChecksRadiosToggles.css
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+@import '../fonts/Fonts.css';
+@import 'Common.css';
+@import 'MFXColors.css';
+
+
+/**************************************************
+ * Checkboxes
+**************************************************/
+.mfx-checkbox {
+ -fx-font-family: 'Visby Round CF Medium';
+ -fx-text-fill: -mfx-text-he;
+}
+
+.mfx-checkbox .text {
+ -fx-font-smoothing-type: lcd;
+}
+
+#customCheck {
+ -mfx-main: -common-gradient;
+ -mfx-gray: #5E0BA8;
+ -fx-text-fill: #5E0BA8;
+}
+
+#customCheck .box .mark {
+ -mfx-description: "mfx-x-alt";
+}
+
+#customCheck:indeterminate .box .mark {
+ -mfx-description: "mfx-hyphen";
+}
+
+#customCheck .mfx-ripple-generator {
+ -mfx-ripple-color: -common-gradient;
+ -fx-opacity: 0.4;
+}
+
+/**************************************************
+ * Radios
+**************************************************/
+.mfx-radio-button {
+ -fx-font-family: 'Visby Round CF Medium';
+ -fx-text-fill: -mfx-text-he;
+}
+
+.mfx-radio-button .text {
+ -fx-font-smoothing-type: lcd;
+}
+
+#customRadio {
+ -mfx-main: -common-gradient;
+ -mfx-gray: #5E0BA8;
+ -fx-text-fill: #5E0BA8;
+}
+
+#customRadio .mfx-ripple-generator {
+ -mfx-ripple-color: -common-gradient;
+ -fx-opacity: 0.5;
+}
+
+/**************************************************
+ * Toggle Buttons
+**************************************************/
+.mfx-toggle-button {
+ -fx-font-family: 'Visby Round CF Medium';
+ -fx-text-fill: -mfx-text-he;
+}
+
+.mfx-toggle-button .text {
+ -fx-font-smoothing-type: lcd;
+}
+
+#customToggleButton {
+ -mfx-main: -common-gradient;
+ -mfx-secondary: #5E0BA8;
+ -fx-text-fill: #5E0BA8;
+}
+
+#customToggleButton .mfx-ripple-generator {
+ -mfx-ripple-color: -common-gradient;
+ -fx-opacity: 0.5;
+}
+
+/**************************************************
+ * Toggle Nodes
+**************************************************/
+.mfx-circle-toggle-node {
+ -fx-font-family: 'Visby Round CF Medium';
+ -fx-text-fill: -mfx-text-he;
+
+}
+
+.mfx-rectangle-toggle-node {
+ -fx-font-family: 'Visby Round CF Medium';
+ -fx-text-fill: -mfx-text-he;
+ -fx-min-height: 40;
+}
+
+#customCircle,
+#customRectangle {
+ -mfx-selected-color: -common-gradient;
+ -mfx-selected-border-color: transparent;
+ -fx-text-fill: #5E0BA8;
+}
+
+#customCircle:selected .mfx-font-icon,
+#customRectangle:selected .mfx-font-icon {
+ -mfx-color: white;
+}
+
+#customCircle:selected .mfx-ripple-generator,
+#customRectangle:selected .mfx-ripple-generator {
+ -mfx-paused: true;
+}
+
+#customRectangle:selected {
+ -fx-text-fill: white;
+}
+
+/**************************************************
+ * Others
+**************************************************/
+.mfx-button {
+ -fx-font-family: 'Visby Round CF Medium';
+ -fx-text-fill: -mfx-text-he;
+}
+
+.mfx-button .text {
+ -fx-font-smoothing-type: lcd;
+}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ComboBoxes.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ComboBoxes.css
new file mode 100644
index 00000000..9da059bb
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ComboBoxes.css
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+@import '../fonts/Fonts.css';
+@import 'Common.css';
+@import 'MFXColors.css';
+
+/**************************************************
+ * Legacy Combos
+**************************************************/
+.mfx-legacy-combo-box {
+ -fx-min-width: 120;
+}
+
+/**************************************************
+ * New Combos
+**************************************************/
+.mfx-combo-box {
+ -fx-min-width: 150;
+ -fx-font-family: 'Visby Round CF Medium';
+}
+
+.mfx-combo-box .floating-text {
+ -fx-font-family: 'Visby Round CF Demi Bold';
+}
+
+#customNCombo {
+ -fx-border-color: transparent;
+ -fx-border-radius: 0;
+ -fx-background-color: -mfx-purple;
+ -fx-background-radius: 30;
+ -fx-text-fill: white;
+
+ -fx-padding: 3 3 3 6;
+ -fx-background-insets: 0 0 0 -3;
+}
+
+#customNCombo .floating-text {
+ -fx-background-color: transparent;
+ -fx-text-fill: white;
+}
+
+#customNCombo .caret .mfx-font-icon {
+ -mfx-color: white;
+}
+
+#customNCombo .caret .mfx-ripple-generator {
+ -mfx-ripple-color: rgba(255, 255, 255, 0.5);
+}
+
+#customNCombo2 {
+ -fx-border-color: transparent transparent gray transparent;
+ -fx-border-radius: 0;
+ -fx-padding: 3 -3 0 3;
+}
+
+#customNCombo2:focused {
+ -fx-border-color: transparent transparent -common-gradient transparent;
+}
+
+#customNCombo2:focused .caret .mfx-font-icon {
+ -mfx-color: #ff5d53;
+}
+
+#customNCombo2 .virtual-flow {
+ -track-color: derive(#ff5d53, 90%);
+ -thumb-color: derive(#ff5d53, 50%);
+ -thumb-hover-color: derive(#ff5d53, 25%);
+}
+
+#customNCombo2 .virtual-flow .mfx-combo-box-cell:hover {
+ -fx-background-color: derive(#ff5d53, 90%);
+}
+
+/**************************************************
+ * Filter Combos
+**************************************************/
+#customFilter {
+ -fx-border-color: transparent;
+ -fx-border-radius: 0;
+ -fx-background-color: #007BF6;
+ -fx-text-fill: white;
+
+ -fx-padding: 4;
+ -fx-background-insets: 0;
+}
+
+#customFilter .text-field {
+ -fx-prompt-text-fill: white;
+}
+
+#customFilter .combo-popup .text-field {
+ -fx-prompt-text-fill: -mfx-text-me;
+}
+
+#customFilter .caret .mfx-font-icon {
+ -mfx-color: #FF794B;
+ -mfx-description: "mfx-angle-down";
+ -mfx-size: 16;
+}
+
+#customFilter .caret .mfx-ripple-generator {
+ -mfx-ripple-color: rgba(255, 255, 255, 0.5);
+}
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ComboBoxesDemo.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ComboBoxesDemo.css
deleted file mode 100644
index 80ebdec4..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ComboBoxesDemo.css
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-#custom {
- -mfx-line-color: #7F0FFF;
- -mfx-unfocused-line-color: #E2CAFF;
- -mfx-line-stroke-width: 1px;
- -mfx-animate-lines: false;
-}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Common.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Common.css
index dfef748c..8aa77960 100644
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Common.css
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Common.css
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Parisi Alessandro
+ * Copyright (C) 2022 Parisi Alessandro
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
*
* MaterialFX is free software: you can redistribute it and/or modify
@@ -16,231 +16,34 @@
* along with MaterialFX. If not, see .
*/
-@font-face {
- font-family: 'Comfortaa Bold';
- font-weight: bold;
- font-style: normal;
- font-display: swap;
- src: url('../fonts/Comfortaa/Comfortaa-Bold.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Comfortaa Light';
- font-weight: 300px;
- font-style: normal;
- font-display: swap;
- src: url('../fonts/Comfortaa/Comfortaa-Light.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Comfortaa Medium';
- font-weight: 500px;
- font-style: normal;
- font-display: swap;
- src: url('../fonts/Comfortaa/Comfortaa-Medium.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Comfortaa Regular';
- font-weight: normal;
- font-style: normal;
- font-display: swap;
- src: url('../fonts/Comfortaa/Comfortaa-Regular.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Comfortaa SemiBold';
- font-weight: 600px;
- font-style: normal;
- font-display: swap;
- src: url('../fonts/Comfortaa/Comfortaa-SemiBold.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Open Sans Bold';
- font-weight: bold;
- font-style: normal;
- font-display: swap;
- src: url('../fonts/OpenSans/OpenSans-Bold.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Open Sans BoldItalic';
- font-weight: bold;
- font-style: italic;
- font-display: swap;
- src: url('../fonts/OpenSans/OpenSans-BoldItalic.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Open Sans ExtraBold';
- font-weight: 800px;
- font-style: normal;
- font-display: swap;
- src: url('../fonts/OpenSans/OpenSans-ExtraBold.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Open Sans ExtraBoldItalic';
- font-weight: 800px;
- font-style: italic;
- font-display: swap;
- src: url('../fonts/OpenSans/OpenSans-ExtraBoldItalic.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Open Sans Light';
- font-weight: 300px;
- font-style: normal;
- font-display: swap;
- src: url('../fonts/OpenSans/OpenSans-Light.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Open Sans LightItalic';
- font-weight: 300px;
- font-style: italic;
- font-display: swap;
- src: url('../fonts/OpenSans/OpenSans-LightItalic.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Open Sans Italic';
- font-weight: normal;
- font-style: italic;
- font-display: swap;
- src: url('../fonts/OpenSans/OpenSans-Italic.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Open Sans SemiBold';
- font-weight: 600px;
- font-style: normal;
- font-display: swap;
- src: url('../fonts/OpenSans/OpenSans-SemiBold.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Open Sans Regular';
- font-weight: normal;
- font-style: normal;
- font-display: swap;
- src: url('../fonts/OpenSans/OpenSans-Regular.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Open Sans SemiBoldItalic';
- font-weight: 600px;
- font-style: italic;
- font-display: swap;
- src: url('../fonts/OpenSans/OpenSans-SemiBoldItalic.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Roboto Black';
- font-weight: 900px;
- font-style: normal;
- font-display: swap;
- src: url('../fonts/Roboto/Roboto-Black.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Roboto BlackItalic';
- font-weight: 900px;
- font-style: italic;
- font-display: swap;
- src: url('../fonts/Roboto/Roboto-BlackItalic.ttf') format('truetype');
-}
+@import '../fonts/Fonts.css';
+@import 'MFXColors.css';
-@font-face {
- font-family: 'Roboto Bold';
- font-weight: bold;
- font-style: normal;
- font-display: swap;
- src: url('../fonts/Roboto/Roboto-Bold.ttf') format('truetype');
+.header-label {
+ -fx-font-family: 'Visby Round CF Bold';
+ -fx-font-size: 14;
+ -fx-text-fill: -mfx-text-he;
}
-@font-face {
- font-family: 'Roboto BoldItalic';
- font-weight: bold;
- font-style: italic;
- font-display: swap;
- src: url('../fonts/Roboto/Roboto-BoldItalic.ttf') format('truetype');
+.header-label .text {
+ -fx-font-smoothing-type: lcd;
}
-@font-face {
- font-family: 'Roboto Light';
- font-weight: 300px;
- font-style: normal;
- font-display: swap;
- src: url('../fonts/Roboto/Roboto-Light.ttf') format('truetype');
+.sub-header-label {
+ -fx-font-family: 'Visby Round CF Demi Bold';
+ -fx-text-fill: -mfx-text-he;
}
-@font-face {
- font-family: 'Roboto Italic';
- font-weight: normal;
- font-style: italic;
- font-display: swap;
- src: url('../fonts/Roboto/Roboto-Italic.ttf') format('truetype');
+.sub-header-label .text {
+ -fx-font-smoothing-type: lcd;
}
-@font-face {
- font-family: 'Roboto LightItalic';
- font-weight: 300px;
- font-style: italic;
- font-display: swap;
- src: url('../fonts/Roboto/Roboto-LightItalic.ttf') format('truetype');
+.grid-pane {
+ -fx-background-color: transparent;
}
-@font-face {
- font-family: 'Roboto Medium';
- font-weight: 500px;
- font-style: normal;
- font-display: swap;
- src: url('../fonts/Roboto/Roboto-Medium.ttf') format('truetype');
+.grid-background {
+ -fx-background-color: white;
+ -fx-background-radius: 10;
+ -fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.2), 10, 0.12, -1.0, 2.0);
}
-
-@font-face {
- font-family: 'Roboto MediumItalic';
- font-weight: 500px;
- font-style: italic;
- font-display: swap;
- src: url('../fonts/Roboto/Roboto-MediumItalic.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Roboto Regular';
- font-weight: normal;
- font-style: normal;
- font-display: swap;
- src: url('../fonts/Roboto/Roboto-Regular.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Roboto ThinItalic';
- font-weight: 100px;
- font-style: italic;
- font-display: swap;
- src: url('../fonts/Roboto/Roboto-ThinItalic.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Roboto Thin';
- font-weight: 100px;
- font-style: normal;
- font-display: swap;
- src: url('../fonts/Roboto/Roboto-Thin.ttf') format('truetype');
-}
-
-#customLabel {
- -fx-background-color: linear-gradient(to bottom right, #C01ADD 0%, #6A6AF8 100%);
- -fx-background-radius: 6;
- -fx-border-color: transparent;
-}
-
-#customLabel .text {
- -fx-font-smoothing-type: gray;
- -fx-font-family: "Open Sans Bold";
- -fx-font-size: 12.5;
- -fx-fill: white;
-}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/DatePickersDemo.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/DatePickersDemo.css
deleted file mode 100644
index 45c27bc9..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/DatePickersDemo.css
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-#customPicker {
- -mfx-main-color: salmon;
- -mfx-line-color: salmon;
- -mfx-color-text: true;
- -mfx-close-on-day-selected: false;
- -mfx-animate-calendar: false;
-}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Demo.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Demo.css
index 9adbb08f..8ad6ed91 100644
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Demo.css
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Demo.css
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Parisi Alessandro
+ * Copyright (C) 2022 Parisi Alessandro
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
*
* MaterialFX is free software: you can redistribute it and/or modify
@@ -16,141 +16,117 @@
* along with MaterialFX. If not, see .
*/
-@import "Common.css";
+@import '../fonts/Fonts.css';
+@import 'MFXColors.css';
-#demoPane {
- -fx-background-color: #FAFAFA;
- -fx-background-radius: 10;
- -fx-border-radius: 10;
+/**************************************************
+ * Root
+**************************************************/
+.rootPane {
+ -fx-background-color: white;
+ -fx-background-radius: 10;
+ -fx-border-color: #ebebeb;
+ -fx-border-radius: 10;
}
-#closeButton:hover {
- -fx-background-color: #ffdcdc;
+/**************************************************
+ * Window Header
+**************************************************/
+.rootPane .close-icon {
+ -mfx-color: -mfx-red;
+ -fx-opacity: 0.5;
}
-#minimizeButton:hover {
- -fx-background-color: #dce2ff;
+.rootPane .close-icon:hover {
+ -fx-opacity: 1.0;
}
-#expandButton:hover {
- -fx-background-color: #fff3cc;
+.rootPane .minimize-icon {
+ -mfx-color: #ffbf37;
+ -fx-opacity: 0.5;
}
-#infoButton {
- -fx-background-radius: 16.4, 15;
- -fx-background-insets: -1.4, 0;
- -fx-border-radius: 15;
- -fx-border-width: 2;
- -fx-border-color: rgb(75, 181, 255);
+.rootPane .minimize-icon:hover {
+ -fx-opacity: 1.0;
}
-#infoButton .mfx-ripple-generator {
- -mfx-ripple-radius: 15px;
- -mfx-ripple-color: #d3d8ff;
+.rootPane .always-on-top-icon {
+ -mfx-color: -mfx-purple;
+ -fx-opacity: 0.5;
}
-#contentPane {
- -fx-background-color: #FAFAFA;
- -fx-background-insets: 0 2 2 2;
- -fx-background-radius: 10;
+.rootPane .always-on-top-icon:hover {
+ -fx-opacity: 1.0;
}
-#navButton {
- -fx-background-color: white, white;
- -fx-background-radius: 16.4, 15;
- -fx-background-insets: -1.4, 0;
- -fx-border-radius: 15;
- -fx-border-width: 1.2;
- -fx-border-color: rgba(127, 15, 255, 0.35);
- -fx-padding: 0;
- -fx-font-size: 0;
+.rootPane .always-on-top-icon:always-on-top {
+ -fx-opacity: 1.0;
}
-#navButton .mfx-ripple-generator {
- -mfx-ripple-color: #e2caff;
+/**************************************************
+ * Sidebar
+**************************************************/
+.rootPane .sidebar {
+ -fx-background-color: -mfx-purple;
+ -fx-background-insets: -1 -5 -1 -1;
+ -fx-background-radius: 0 20 20 0;
}
-#navButton .mfx-font-icon {
- -mfx-color: #6e0ede;
- -fx-opacity: 0.5;
+.rootPane .sidebar .header {
+ -fx-font-family: 'Visby Round CF Heavy';
+ -fx-font-size: 24;
+ -fx-text-fill: white;
}
-#navButton .mfx-font-icon:hover {
- -mfx-color: #6e0ede;
- -fx-opacity: 1.0;
+.rootPane .sidebar .header .mfx-font-icon {
+ -mfx-color: white;
}
-#navBar {
- -fx-background-radius: 10;
- -fx-border-color: #7F0FFF;
- -fx-border-width: 0.2;
- -fx-border-radius: 10;
-}
-
-#vLoader .mfx-toggle-node {
- -fx-background-color: white;
- -fx-background-radius: 6;
- -fx-background-insets: -1;
- -fx-border-color: #7F0FFF;
- -fx-border-radius: 6;
- -fx-border-insets: -1;
- -fx-pref-width: 160;
-}
-
-#vLoader .mfx-toggle-node .mfx-label {
- -mfx-font-family: 'Open Sans Bold';
- -mfx-text-fill: #7F0FFF;
- -mfx-font-size: 13px;
- -fx-opacity: 0.7;
-}
-
-#vLoader .mfx-toggle-node:hover,
-#vLoader .mfx-toggle-node:hover .mfx-label {
- -fx-opacity: 1.0;
-}
+.rootPane .sidebar .mfx-scroll-pane {
+ -fx-background-color: transparent;
-#vLoader .mfx-toggle-node:selected {
- -fx-background-color: derive(#7F0FFF, 150%);
- -fx-opacity: 1;
+ -track-color: rgba(255, 255, 255, 0.3);
+ -thumb-color: white;
+ -thumb-hover-color: white;
}
-#vLoader .mfx-toggle-node:selected .text {
- -fx-opacity: 1.0;
+.rootPane .sidebar .menu-label {
+ -fx-font-family: 'Visby Round CF Bold';
+ -fx-font-size: 14;
+ -fx-text-fill: white;
}
-#vLoader .mfx-toggle-node .mfx-ripple-generator {
- -mfx-ripple-radius: 40px;
- -mfx-ripple-color: #e2caff;
- -mfx-animate-background: true;
-}
+.rootPane .sidebar .navbar .mfx-rectangle-toggle-node {
+ -fx-background-color: transparent;
+ -fx-background-radius: 0;
+ -fx-border-color: transparent;
+ -fx-border-radius: 0;
+ -fx-pref-height: 40;
-#loaderScroll {
- -mfx-track-color: #e6d7ff;
- -mfx-thumb-color: #b885ff;
- -mfx-thumb-hover-color: #9e47ff;
+ -fx-font-family: 'Visby Round CF Demi Bold';
+ -fx-font-size: 13;
+ -fx-text-fill: white;
}
-#splashLabel {
- -fx-font-family: "Open Sans Bold";
- -fx-font-size: 36;
- -fx-text-fill: #6314A8;
+.rootPane .sidebar .navbar .mfx-rectangle-toggle-node .mfx-ripple-generator {
+ -mfx-paused: true;
}
-#splashLabel .text {
- -fx-font-smoothing-type: gray;
+.rootPane .sidebar .navbar .mfx-rectangle-toggle-node .mfx-icon-wrapper .mfx-font-icon {
+ -mfx-color: white;
}
-#textVer1,
-#textVer2 {
- -fx-font-smoothing-type: gray;
- -fx-font-family: "Open Sans Bold";
- -fx-fill: #6314A8;
+.rootPane .sidebar .navbar .mfx-rectangle-toggle-node:selected {
+ -fx-background-color: white;
+ -fx-background-radius: 5;
+ -fx-text-fill: #601cbe;
}
-#textVer1 {
- -fx-font-size: 12;
+.rootPane .sidebar .navbar .mfx-rectangle-toggle-node:selected .selection-rectangle {
+ -fx-stroke: transparent;
+ -fx-fill: #601cbe;
}
-#textVer2 {
- -fx-font-size: 24;
+.rootPane .sidebar .navbar .mfx-rectangle-toggle-node:selected .mfx-icon-wrapper .mfx-font-icon {
+ -mfx-color: #601cbe;
}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ScrollPanesDemo.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ExampleNotification.css
similarity index 56%
rename from demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ScrollPanesDemo.css
rename to demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ExampleNotification.css
index f330bd30..0ff3e969 100644
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ScrollPanesDemo.css
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ExampleNotification.css
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Parisi Alessandro
+ * Copyright (C) 2022 Parisi Alessandro
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
*
* MaterialFX is free software: you can redistribute it and/or modify
@@ -16,16 +16,33 @@
* along with MaterialFX. If not, see .
*/
-.label {
- -fx-background-color: white;
- -fx-background-radius: 0;
- -fx-border-color: transparent;
- -fx-border-radius: 0;
- -fx-border-width: 0;
+@import 'MFXColors.css';
+
+.notification {
+ -fx-background-color: white;
+ -fx-background-radius: 5;
+ -fx-border-color: #ebebeb;
+ -fx-border-radius: 5;
+}
+
+.notification .label {
+ -fx-font-family: 'Visby Round CF Bold';
+ -fx-text-fill: -mfx-text-he;
+}
+
+.notification .content {
+ -fx-padding: 10;
+ -fx-font-family: 'Visby Round CF Medium';
+}
+
+.notification .mfx-button {
+ -fx-min-height: 27;
+ -fx-border-color: -mfx-purple;
+ -fx-border-radius: 3;
+ -fx-text-fill: -mfx-purple;
}
-.label .text {
- -fx-font-family: "Open Sans";
- -fx-font-size: 12;
- -fx-fill: black;
+.notification .mfx-button .mfx-ripple-generator {
+ -mfx-auto-clip: true;
+ -mfx-ripple-color: derive(#7a0ed9, 125%);
}
\ No newline at end of file
diff --git a/materialfx/src/main/resources/io/github/palexdev/materialfx/css/MFXTableRow.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/FontResources.css
similarity index 73%
rename from materialfx/src/main/resources/io/github/palexdev/materialfx/css/MFXTableRow.css
rename to demo/src/main/resources/io/github/palexdev/materialfx/demo/css/FontResources.css
index 0daab810..31574031 100644
--- a/materialfx/src/main/resources/io/github/palexdev/materialfx/css/MFXTableRow.css
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/FontResources.css
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Parisi Alessandro
+ * Copyright (C) 2022 Parisi Alessandro
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
*
* MaterialFX is free software: you can redistribute it and/or modify
@@ -16,12 +16,11 @@
* along with MaterialFX. If not, see .
*/
-.mfx-table-row {
- -fx-background-insets: 0 -5 0 -5;
- -fx-background-color: white;
- -fx-border-color: transparent;
+.mfx-table-view .virtual-flow .mfx-table-row:hover,
+.mfx-table-view .virtual-flow .mfx-table-row:selected {
+ -fx-background-color: transparent;
}
-.mfx-table-row:selected {
- -fx-background-color: #F4F4F4;
+.mfx-table-view .virtual-flow .mfx-table-row .mfx-ripple-generator {
+ -mfx-paused: true;
}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/InfoDialog.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/InfoDialog.css
deleted file mode 100644
index eea581cd..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/InfoDialog.css
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-.mfx-dialog {
- -fx-background-color: transparent;
- -fx-border-color: transparent;
-}
-
-.label .text {
- -fx-font-family: "Comfortaa Regular";
-}
-
-#materialfxLabel {
- -fx-background-color: #7F0FFF;
- -fx-background-radius: 6px;
-}
-
-#materialfxLabel .text {
- -fx-font-family: "Comfortaa Bold";
- -fx-font-size: 40px;
- -fx-fill: white;
-}
-
-#titleLabel .text {
- -fx-font-family: "Comfortaa Bold";
- -fx-font-size: 15px;
-}
-
-
-.mfx-icon-wrapper {
- -fx-background-color: white;
- -fx-background-radius: 16.4, 15;
- -fx-border-radius: 15;
- -fx-border-width: 2;
- -fx-border-color: rgba(127, 15, 255, 0.55);
-}
-
-.mfx-icon-wrapper .mfx-ripple-generator {
- -mfx-ripple-color: #c4acff;
-}
-
-#contentNode {
- -fx-background-color: white;
- -fx-background-radius: 5px;
- -fx-background-insets: 0px 11px 11px 11px;
- -fx-border-color: #7F0FFF;
- -fx-border-radius: 5px;
- -fx-border-insets: 0px 11px 11px 11px;
- -fx-border-width: 2px;
-}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ListViews.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ListViews.css
new file mode 100644
index 00000000..00e14959
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ListViews.css
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+@import '../fonts/Fonts.css';
+@import 'Common.css';
+@import 'MFXColors.css';
+
+.mfx-list-view .virtual-flow .mfx-list-cell .data-label {
+ -fx-font-family: 'Visby Round CF Medium';
+}
+
+.mfx-list-view .virtual-flow .mfx-list-cell:selected .data-label {
+ -fx-font-family: 'Visby Round CF Demi Bold';
+}
+
+.mfx-check-list-view .virtual-flow .mfx-check-list-cell .data-label {
+ -fx-font-family: 'Visby Round CF Medium';
+}
+
+.mfx-check-list-view .virtual-flow .mfx-check-list-cell:selected .data-label {
+ -fx-font-family: 'Visby Round CF Demi Bold';
+}
+
+#custList {
+ -fx-background-color: -mfx-purple;
+ -fx-background-radius: 10;
+ -fx-border-color: -mfx-purple;
+ -fx-border-radius: 10;
+ -fx-padding: 4;
+}
+
+#custList .virtual-flow {
+ -fx-background-color: transparent;
+}
+
+#custList:focused {
+ -fx-border-color: -mfx-purple;
+}
+
+#custList .virtual-flow .mfx-list-cell {
+ -fx-background-color: transparent;
+ -fx-border-color: transparent;
+ -fx-background-radius: 10;
+ -fx-border-radius: 10;
+}
+
+#custList .virtual-flow .mfx-list-cell .data-label {
+ -fx-text-fill: white;
+}
+
+#custList .virtual-flow .mfx-list-cell .user-icon {
+ -mfx-color: white;
+}
+
+#custList .virtual-flow .mfx-list-cell:hover,
+#custList .virtual-flow .mfx-list-cell:selected {
+ -fx-background-color: rgba(255, 255, 255, 0.3);
+}
+
+#custList .virtual-flow .mfx-list-cell .mfx-ripple-generator {
+ -mfx-auto-clip: true;
+}
+
+#custList .virtual-flow .mfx-list-cell:selected .mfx-ripple-generator {
+ -mfx-paused: true;
+}
+
+.mfx-button {
+ -fx-background-color: -mfx-purple;
+ -fx-border-color: -mfx-purple;
+ -fx-border-radius: 3;
+ -fx-text-fill: white;
+ -fx-font-family: 'Visby Round CF Demi Bold';
+ -fx-font-size: 14;
+}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ListViewsDemo.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ListViewsDemo.css
deleted file mode 100644
index da3053ad..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ListViewsDemo.css
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-#customView {
- -mfx-hide-scrollbars: true;
-
- -mfx-track-color: transparent;
- -mfx-thumb-color: derive(salmon, 30%);
- -mfx-thumb-hover-color: red;
-}
-
-#customView .mfx-list-cell {
- -mfx-selected-color: salmon;
- -mfx-hover-color: derive(salmon, 70%);
-}
-
-#customView .mfx-list-cell .mfx-ripple-generator {
- -mfx-ripple-color: #f55c49;
-}
-
-#customViewNew {
- -mfx-hide-scrollbars: true;
-
- -mfx-track-color: transparent;
- -mfx-thumb-color: derive(salmon, 30%);
- -mfx-thumb-hover-color: red;
-}
-
-#customViewNew .mfx-list-cell:hover {
- -fx-background-color: derive(salmon, 70%);
- -fx-border-color: derive(salmon, 70%);
-}
-
-#customViewNew .mfx-list-cell:selected {
- -fx-background-color: salmon;
- -fx-border-color: salmon;
-}
-
-#customViewNew .mfx-list-cell .mfx-ripple-generator {
- -mfx-ripple-color: #f55c49;
-}
-
-#label {
- -fx-background-color: linear-gradient(to bottom right, #C01ADD 0%, #6A6AF8 100%);
- -fx-background-radius: 7;
- -fx-border-color: #7F0FFF;
- -fx-border-radius: 5;
- -fx-border-width: 2;
- -fx-min-height: 32;
-}
-
-#label .text {
- -fx-font-family: "Open Sans Bold";
- -fx-font-size: 10;
- -fx-fill: white;
-}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/MFXColors.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/MFXColors.css
old mode 100644
new mode 100755
index 6afd20c0..22c3788d
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/MFXColors.css
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/MFXColors.css
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Parisi Alessandro
+ * Copyright (C) 2022 Parisi Alessandro
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
*
* MaterialFX is free software: you can redistribute it and/or modify
@@ -17,9 +17,20 @@
*/
* {
- -mfx-blue: #2196f3;
- -mfx-charcoal: #445055;
- -mfx-green: #4caf50;
- -mfx-purple: #673AB7;
- -mfx-red: #EF6E6B;
+ -mfx-blue: #2196f3;
+ -mfx-charcoal: #445055;
+ -mfx-green: #4caf50;
+ -mfx-onyx: #353935;
+ -mfx-orange: #ff9800;
+ -mfx-purple: #7a0ed9;
+ -mfx-red: #EF6E6B;
+ -mfx-yellow: #FFEA00;
+
+
+ -mfx-text-he: rgba(0, 0, 0, 0.87); /* High Emphasis */
+ -mfx-text-me: rgba(0, 0, 0, 0.60); /* Medium Emphasis*/
+ -mfx-text-disabled: rgba(0, 0, 0, 0.38); /* Disabled */
+
+ -common-gradient: linear-gradient(to right bottom, #c4008b, #d00085, #dc007f, #e60b79, #ef1872, #f5276b, #fa3564, #fe415e, #ff5158, #ff5f52, #ff6c4e, #ff794b);
+ -secondary-gradient: linear-gradient(to right bottom, #1ec400, #00bd3a, #00b654, #00ae67, #00a574, #00a280, #009e8a, #009a91, #009b9d, #009ba8, #009bb2, #009abb);
}
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Pickers.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Pickers.css
new file mode 100644
index 00000000..cf1a7387
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Pickers.css
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+@import '../fonts/Fonts.css';
+@import 'Common.css';
+@import 'MFXColors.css';
+
+/**************************************************
+ * Date Pickers
+**************************************************/
+#custDatePicker {
+ -fx-background-color: #007BF6;
+ -fx-border-color: transparent;
+ -fx-border-radius: 0;
+
+ -fx-font-family: "Visby Round CF Medium";
+ -fx-text-fill: white;
+}
+
+#custDatePicker .floating-text {
+ -fx-background-color: transparent;
+
+ -fx-font-family: "Visby Round CF Demi Bold";
+ -fx-text-fill: white;
+}
+
+#custDatePicker .icon {
+ -mfx-color: white;
+}
+
+#custDatePicker .date-picker-popup .content {
+ -fx-background-color: #007BF6;
+ -fx-border-color: transparent;
+ -fx-border-radius: 0;
+}
+
+#custDatePicker .date-picker-popup .left-arrow:disabled,
+#custDatePicker .date-picker-popup .right-arrow:disabled {
+ -fx-opacity: 0.8;
+}
+
+#custDatePicker .date-picker-popup .left-arrow .mfx-font-icon,
+#custDatePicker .date-picker-popup .right-arrow .mfx-font-icon {
+ -mfx-color: white;
+}
+
+#custDatePicker .date-picker-popup .months-combo,
+#custDatePicker .date-picker-popup .years-combo {
+ -fx-border-color: transparent;
+ -fx-border-radius: 0;
+ -fx-background-color: white;
+ -fx-text-fill: -mfx-text-he;
+
+ -fx-padding: 4;
+ -fx-background-insets: 0;
+ -fx-font-family: "Visby Round CF Medium";
+}
+
+#custDatePicker .date-picker-popup .months-combo:focused,
+#custDatePicker .date-picker-popup .years-combo:focused {
+ -fx-text-fill: #007BF6;
+}
+
+#custDatePicker .date-picker-popup .months-combo:focused .caret .mfx-font-icon,
+#custDatePicker .date-picker-popup .years-combo:focused .caret .mfx-font-icon {
+ -mfx-color: #007BF6;
+}
+
+#custDatePicker .date-picker-popup .months-combo .virtual-flow,
+#custDatePicker .date-picker-popup .years-combo .virtual-flow {
+ -track-color: derive(#007BF6, 100%);
+ -thumb-color: derive(#007BF6, 70%);
+ -thumb-hover-color: derive(#007BF6, 45%);
+}
+
+#custDatePicker .date-picker-popup .months-combo .virtual-flow .mfx-combo-box-cell,
+#custDatePicker .date-picker-popup .years-combo .virtual-flow .mfx-combo-box-cell {
+ -fx-font-family: 'Visby Round CF Medium';
+}
+
+#custDatePicker .date-picker-popup .months-combo .virtual-flow .mfx-combo-box-cell:selected .label,
+#custDatePicker .date-picker-popup .years-combo .virtual-flow .mfx-combo-box-cell:selected .label {
+ -fx-text-fill: #007BF6;
+}
+
+#custDatePicker .date-picker-popup .months-combo .virtual-flow .mfx-combo-box-cell:hover,
+#custDatePicker .date-picker-popup .years-combo .virtual-flow .mfx-combo-box-cell:hover,
+#custDatePicker .date-picker-popup .months-combo .virtual-flow .mfx-combo-box-cell:selected,
+#custDatePicker .date-picker-popup .years-combo .virtual-flow .mfx-combo-box-cell:selected {
+ -fx-background-color: derive(#007BF6, 130%);
+}
+
+#custDatePicker .date-picker-popup .week-day {
+ -fx-font-family: 'Visby Round CF Bold';
+ -fx-text-fill: white;
+}
+
+#custDatePicker .date-picker-popup .mfx-date-cell {
+ -fx-font-family: 'Visby Round CF Medium';
+ -fx-text-fill: white;
+}
+
+#custDatePicker .date-picker-popup .mfx-date-cell:extra {
+ -fx-text-fill: rgba(255, 255, 255, 0.5);
+}
+
+#custDatePicker .date-picker-popup .mfx-date-cell:current {
+ -fx-background-color: transparent;
+ -fx-border-color: white;
+ -fx-border-width: 2;
+ -fx-font-family: 'Visby Round CF Demi Bold';
+}
+
+#custDatePicker .date-picker-popup .mfx-date-cell:selected {
+ -fx-background-color: white;
+ -fx-font-family: 'Visby Round CF Bold';
+ -fx-text-fill: #007BF6;
+}
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Progress.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Progress.css
new file mode 100644
index 00000000..19673a68
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Progress.css
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+@import '../fonts/Fonts.css';
+@import 'Common.css';
+@import 'MFXColors.css';
+
+/**************************************************
+ * Progress Bars
+**************************************************/
+#customBar1 .bar1 {
+ -fx-fill: -common-gradient;
+}
+
+#customBar1 .bar2 {
+ -fx-fill: -secondary-gradient;
+}
+
+#customBar2 .track {
+ -fx-fill: -mfx-purple;
+ -fx-stroke: -mfx-purple;
+ -fx-stroke-width: 2;
+}
+
+#customBar2 .bar1,
+#customBar2 .bar2 {
+ -fx-stroke: -mfx-purple;
+ -fx-stroke-width: 1;
+ -fx-fill: white;
+}
+
+#determinateBar:range1 .bar1 {
+ -fx-fill: -mfx-red;
+}
+
+#determinateBar:range1 .track {
+ -fx-fill: derive(-mfx-red, 85%)
+}
+
+#determinateBar:range2 .bar1 {
+ -fx-fill: -mfx-blue;
+}
+
+#determinateBar:range2 .track {
+ -fx-fill: derive(-mfx-blue, 85%)
+}
+
+#determinateBar:range3 .bar1 {
+ -fx-fill: -mfx-green;
+}
+
+#determinateBar:range3 .track {
+ -fx-fill: derive(-mfx-green, 85%);
+}
+
+
+/**************************************************
+ * Progress Spinners
+**************************************************/
+#customSpinner1 {
+ -mfx-color1: #7A0ED9;
+ -mfx-color2: #EB00A6;
+ -mfx-color3: #FF1471;
+ -mfx-color4: #FF7C4A;
+}
+
+#customSpinner1 .track {
+ -fx-stroke: #ebebeb;
+}
+
+#determinateSpinner .text {
+ -fx-font-family: 'Visby Round CF Demi Bold';
+ -fx-font-size: 18;
+ -fx-fill: black;
+}
+
+#determinateSpinner:range1 .arc {
+ -fx-fill: #FF6088;
+}
+
+#determinateSpinner:range2 .arc {
+ -fx-fill: #00BCFF;
+}
+
+#determinateSpinner:range3 .arc {
+ -fx-fill: #1EC400;
+}
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ProgressSpinnersDemo.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ProgressSpinnersDemo.css
deleted file mode 100644
index 7f290998..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/ProgressSpinnersDemo.css
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-@import "MFXColors.css";
-
-.progress2 .text {
- visibility: false;
-}
-
-.progress2 .arc {
- -fx-stroke: orange;
-}
-
-#cssSpinner1 {
- -fx-stroke: #4285f4;
-}
-
-#cssSpinner1 .track {
- -fx-stroke: #d0d0d0;
-}
-
-#cssSpinner2 .track {
- -fx-stroke: #ffccc5;
-}
-
-#determinate:range1 .text {
- -fx-fill: -mfx-red;
-}
-
-#determinate:range1 .arc{
- -fx-stroke: -mfx-red;
-}
-
-#determinate:range1 .track{
- -fx-stroke: derive(-mfx-red, 85%);
-}
-
-#determinate:range2 .text {
- -fx-fill: -mfx-blue;
-}
-
-#determinate:range2 .arc{
- -fx-stroke: -mfx-blue;
-}
-
-#determinate:range2 .track{
- -fx-stroke: derive(-mfx-blue, 85%);
-}
-
-#determinate:range3 .text {
- -fx-fill: -mfx-green;
-}
-
-#determinate:range3 .arc{
- -fx-stroke: -mfx-green;
-}
-
-#determinate:range3 .track{
- -fx-stroke: derive(-mfx-green, 85%);
-}
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/RadioButtonsDemo.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/RadioButtonsDemo.css
deleted file mode 100644
index adfbbf27..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/RadioButtonsDemo.css
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-#cssButton {
- -mfx-selected-color: green;
- -mfx-unselected-color: darkred;
- -mfx-selected-text-color: blue;
- -mfx-unselected-text-color: darkred;
-}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Sliders.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Sliders.css
new file mode 100644
index 00000000..b9642e9d
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Sliders.css
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+@import '../fonts/Fonts.css';
+@import 'Common.css';
+@import 'MFXColors.css';
+
+#customSlider:range1 .thumb,
+#customSlider:range1 .track {
+ -mfx-color: -mfx-red;
+ -fx-fill: derive(-mfx-red, 85%)
+}
+
+#customSlider:range1 .bar {
+ -fx-fill: -mfx-red;
+}
+
+#customSlider:range2 .thumb,
+#customSlider:range2 .track {
+ -mfx-color: -mfx-blue;
+ -fx-fill: derive(-mfx-blue, 85%)
+}
+
+#customSlider:range2 .bar {
+ -fx-fill: -mfx-blue;
+}
+
+#customSlider:range3 .bar,
+#customSlider:range3 .thumb {
+ -mfx-color: -mfx-green;
+ -fx-fill: -mfx-green;
+}
+
+#customSlider:range3 .track {
+ -fx-fill: derive(-mfx-green, 85%);
+}
+
+#customSlider:range1 .thumb-container:hover .thumb-radius,
+#customSlider:range1 .thumb-container:pressed .thumb-radius {
+ -mfx-color: derive(-mfx-red, 85%);
+ -fx-opacity: 0.5;
+}
+
+#customSlider:range2 .thumb-container:hover .thumb-radius,
+#customSlider:range2 .thumb-container:pressed .thumb-radius {
+ -mfx-color: derive(-mfx-blue, 85%);
+ -fx-opacity: 0.5;
+}
+
+#customSlider:range3 .thumb-container:hover .thumb-radius,
+#customSlider:range3 .thumb-container:pressed .thumb-radius {
+ -mfx-color: derive(-mfx-green, 70%);
+ -fx-opacity: 0.5;
+}
+
+#customSlider:range1 .thumb-container .mfx-ripple-generator {
+ -mfx-ripple-color: -mfx-red;
+}
+
+#customSlider:range2 .thumb-container .mfx-ripple-generator {
+ -mfx-ripple-color: -mfx-blue;
+}
+
+#customSlider:range3 .thumb-container .mfx-ripple-generator {
+ -mfx-ripple-color: -mfx-green;
+}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/SlidersDemo.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/SlidersDemo.css
deleted file mode 100644
index 23b0b8ed..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/SlidersDemo.css
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-@import "MFXColors.css";
-
-#customSlider2 .track {
- -fx-fill: linear-gradient(to bottom right, #6A6AF8 0%, #C850C0 30%, darkorange 100%);
-}
-
-#customSlider2 .bar {
- -fx-fill: linear-gradient(to bottom right, #F4D03F 0%, #10d7b4 100%);
-}
-
-#customSlider:range1 .thumb,
-#customSlider:range1 .track {
- -mfx-color: -mfx-red;
- -fx-fill: derive(-mfx-red, 85%)
-}
-
-#customSlider:range1 .bar {
- -fx-fill: -mfx-red;
-}
-
-#customSlider:range2 .thumb,
-#customSlider:range2 .track {
- -mfx-color: -mfx-blue;
- -fx-fill: derive(-mfx-blue, 85%)
-}
-
-#customSlider:range2 .bar {
- -fx-fill: -mfx-blue;
-}
-
-#customSlider:range3 .bar,
-#customSlider:range3 .thumb {
- -mfx-color: -mfx-green;
- -fx-fill: -mfx-green;
-}
-
-#customSlider:range3 .track {
- -fx-fill: derive(-mfx-green, 85%);
-}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Stepper.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Stepper.css
new file mode 100644
index 00000000..78e89a34
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/Stepper.css
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+@import '../fonts/Fonts.css';
+@import 'Common.css';
+@import 'MFXColors.css';
+
+.mfx-stepper .mfx-text-field {
+ -mfx-float-mode: disabled;
+ -fx-font-family: 'Visby Round CF Medium';
+ -fx-pref-width: 200;
+}
+
+.mfx-stepper .mfx-stepper-toggle .mfx-text-field {
+ -fx-border-color: transparent;
+ -fx-font-family: 'Visby Round CF Bold';
+ -fx-pref-width: 1;
+}
+
+.mfx-stepper .error-label {
+ -fx-font-family: 'Visby Round CF Medium';
+ -fx-text-fill: -mfx-red;
+}
+
+.mfx-stepper .header-label {
+ -fx-border-color: transparent;
+ -fx-font-family: 'Visby Round CF Demi Bold';
+}
+
+.mfx-stepper .completed-label {
+ -fx-border-color: transparent;
+ -fx-font-family: 'Visby Round CF Bold';
+ -fx-font-size: 24;
+ -fx-alignment: center;
+ -fx-text-fill: -mfx-green;
+}
+
+.outline-button {
+ -fx-background-color: transparent;
+ -fx-border-color: -mfx-purple;
+ -fx-border-radius: 3;
+
+ -fx-text-fill: -mfx-purple;
+}
+
+.outline-button:hover,
+.outline-button:focused {
+ -fx-background-color: -mfx-purple;
+ -fx-text-fill: white;
+}
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/TextFields.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/TextFields.css
new file mode 100644
index 00000000..574369c1
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/TextFields.css
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+@import '../fonts/Fonts.css';
+@import 'Common.css';
+@import 'MFXColors.css';
+
+/**************************************************
+ * Text Fields
+**************************************************/
+.mfx-text-field {
+ -fx-min-width: 130;
+ -fx-font-family: 'Visby Round CF Medium';
+}
+
+.mfx-text-field .floating-text {
+ -fx-font-family: 'Visby Round CF Medium';
+}
+
+
+#customField {
+ -fx-border-color: -common-gradient;
+}
+
+#customField .floating-text {
+ -fx-text-fill: -mfx-red;
+}
+
+/**************************************************
+ * Password Fields
+**************************************************/
+#customPassword {
+ -fx-border-color: -secondary-gradient;
+}
+
+#customPassword:masked {
+ -fx-text-fill: -mfx-blue;
+}
+
+#customPassword .mfx-font-icon {
+ -mfx-color: -mfx-blue;
+}
+
+#customPassword .mfx-context-menu .mfx-menu-item:disabled .mfx-font-icon {
+ -mfx-color: #bcbcbc;
+}
+
+#customPassword .floating-text {
+ -fx-text-fill: -mfx-blue;
+}
+
+#validatedField {
+ -mfx-hide-character: '?';
+}
+
+#validatedField:invalid {
+ -fx-border-color: -mfx-red;
+}
+
+#validatedField:invalid .floating-text {
+ -fx-text-fill: -mfx-red;
+}
+
+#validatedField:invalid .mfx-font-icon {
+ -mfx-color: -mfx-red;
+}
+
+#validationLabel {
+ -fx-font-family: 'Visby Round CF Demi Bold';
+ -fx-font-size: 11;
+}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/TextFieldsDemo.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/TextFieldsDemo.css
deleted file mode 100644
index 273adaf3..00000000
--- a/demo/src/main/resources/io/github/palexdev/materialfx/demo/css/TextFieldsDemo.css
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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 Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see .
- */
-
-@import "MFXColors.css";
-
-.mfx-text-field {
- -fx-font-family: "Open Sans Regular";
-}
-
-.label {
- -fx-font-family: "Open Sans SemiBold";
-}
-
-#colors {
- -fx-prompt-text-fill: #000e8b;
- -fx-text-fill: #000e8b;
- -mfx-unfocused-line-color: #000e8b;
- -mfx-line-color: darkorange;
-
- -mfx-text-limit: 10;
-}
-
-#colors:focused {
- -fx-prompt-text-fill: darkorange;
- -fx-text-fill: darkorange;
-}
-
-#customPassword {
- -fx-prompt-text-fill: -mfx-purple;
- -fx-text-fill: -mfx-purple;
- -mfx-unfocused-line-color: -mfx-purple;
- -mfx-line-color: darkorange;
-}
-
-#customPassword:focused {
- -fx-prompt-text-fill: darkorange;
- -fx-text-fill: darkorange;
-}
-
-#customPassword .mfx-icon-wrapper .mfx-font-icon {
- -mfx-color: -mfx-purple;
-}
-
-#customPassword:focused .mfx-icon-wrapper .mfx-font-icon {
- -mfx-color: darkorange
-}
-
-#customPassword {
- -fx-prompt-text-fill: -mfx-purple;
- -fx-text-fill: -mfx-purple;
- -mfx-unfocused-line-color: -mfx-purple;
- -mfx-line-color: darkorange;
-}
-
-#customPassword:focused {
- -fx-prompt-text-fill: darkorange;
- -fx-text-fill: darkorange;
-}
-
-#customPassword .mfx-icon-wrapper .mfx-font-icon {
- -mfx-color: -mfx-purple;
-}
-
-#customPassword:focused .mfx-icon-wrapper .mfx-font-icon {
- -mfx-color: darkorange
-}
-
-#customPassword {
- -fx-prompt-text-fill: -mfx-purple;
- -fx-text-fill: -mfx-purple;
- -mfx-unfocused-line-color: linear-gradient(to bottom right, #C01ADD 0%, #6A6AF8 100%);
- -mfx-line-color: linear-gradient(to bottom right, #6A6AF8 0%, #C850C0 46%, darkorange 100%);
-}
-
-#customPassword:focused {
- -fx-prompt-text-fill: darkorange;
- -fx-text-fill: darkorange;
-}
-
-#customPassword .mfx-icon-wrapper .mfx-font-icon {
- -mfx-color: -mfx-purple;
-}
-
-#customPassword:focused .mfx-icon-wrapper .mfx-font-icon {
- -mfx-color: darkorange
-}
\ No newline at end of file
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Comfortaa/Comfortaa-Bold.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Comfortaa/Comfortaa-Bold.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Comfortaa/Comfortaa-Light.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Comfortaa/Comfortaa-Light.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Comfortaa/Comfortaa-Medium.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Comfortaa/Comfortaa-Medium.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Comfortaa/Comfortaa-Regular.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Comfortaa/Comfortaa-Regular.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Comfortaa/Comfortaa-SemiBold.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Comfortaa/Comfortaa-SemiBold.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Comfortaa/Comfortaa-VariableFont_wght.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Comfortaa/Comfortaa-VariableFont_wght.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Fonts.css b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Fonts.css
new file mode 100644
index 00000000..5e17978b
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Fonts.css
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
+ *
+ * MaterialFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see .
+ */
+
+@font-face {
+ font-family: 'Comfortaa Bold';
+ font-weight: bold;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Comfortaa/Comfortaa-Bold.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Comfortaa Light';
+ font-weight: 300px;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Comfortaa/Comfortaa-Light.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Comfortaa Medium';
+ font-weight: 500px;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Comfortaa/Comfortaa-Medium.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Comfortaa Regular';
+ font-weight: normal;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Comfortaa/Comfortaa-Regular.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Comfortaa SemiBold';
+ font-weight: 600px;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Comfortaa/Comfortaa-SemiBold.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Open Sans Bold';
+ font-weight: bold;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/OpenSans/OpenSans-Bold.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Open Sans BoldItalic';
+ font-weight: bold;
+ font-style: italic;
+ font-display: swap;
+ src: url('../fonts/OpenSans/OpenSans-BoldItalic.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Open Sans ExtraBold';
+ font-weight: 800px;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/OpenSans/OpenSans-ExtraBold.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Open Sans ExtraBoldItalic';
+ font-weight: 800px;
+ font-style: italic;
+ font-display: swap;
+ src: url('../fonts/OpenSans/OpenSans-ExtraBoldItalic.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Open Sans Light';
+ font-weight: 300px;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/OpenSans/OpenSans-Light.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Open Sans LightItalic';
+ font-weight: 300px;
+ font-style: italic;
+ font-display: swap;
+ src: url('../fonts/OpenSans/OpenSans-LightItalic.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Open Sans Italic';
+ font-weight: normal;
+ font-style: italic;
+ font-display: swap;
+ src: url('../fonts/OpenSans/OpenSans-Italic.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Open Sans SemiBold';
+ font-weight: 600px;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/OpenSans/OpenSans-SemiBold.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Open Sans Regular';
+ font-weight: normal;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/OpenSans/OpenSans-Regular.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Open Sans SemiBoldItalic';
+ font-weight: 600px;
+ font-style: italic;
+ font-display: swap;
+ src: url('../fonts/OpenSans/OpenSans-SemiBoldItalic.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Roboto Black';
+ font-weight: 900px;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Roboto/Roboto-Black.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Roboto BlackItalic';
+ font-weight: 900px;
+ font-style: italic;
+ font-display: swap;
+ src: url('../fonts/Roboto/Roboto-BlackItalic.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Roboto Bold';
+ font-weight: bold;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Roboto/Roboto-Bold.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Roboto BoldItalic';
+ font-weight: bold;
+ font-style: italic;
+ font-display: swap;
+ src: url('../fonts/Roboto/Roboto-BoldItalic.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Roboto Light';
+ font-weight: 300px;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Roboto/Roboto-Light.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Roboto Italic';
+ font-weight: normal;
+ font-style: italic;
+ font-display: swap;
+ src: url('../fonts/Roboto/Roboto-Italic.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Roboto LightItalic';
+ font-weight: 300px;
+ font-style: italic;
+ font-display: swap;
+ src: url('../fonts/Roboto/Roboto-LightItalic.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Roboto Medium';
+ font-weight: 500px;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Roboto/Roboto-Medium.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Roboto MediumItalic';
+ font-weight: 500px;
+ font-style: italic;
+ font-display: swap;
+ src: url('../fonts/Roboto/Roboto-MediumItalic.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Roboto Regular';
+ font-weight: normal;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Roboto/Roboto-Regular.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Roboto ThinItalic';
+ font-weight: 100px;
+ font-style: italic;
+ font-display: swap;
+ src: url('../fonts/Roboto/Roboto-ThinItalic.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Roboto Thin';
+ font-weight: 100px;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Roboto/Roboto-Thin.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Varela Round';
+ font-weight: normal;
+ font-display: swap;
+ src: url('../fonts/Varela/VarelaRound-Regular.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Visby Round CF Demi';
+ font-weight: 600;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Visby/VisbyRoundCF-DemiBold.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Visby Round CF Extra';
+ font-weight: bold;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Visby/VisbyRoundCF-ExtraBold.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Visby Round CF';
+ font-weight: bold;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Visby/VisbyRoundCF-Bold.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Visby Round CF';
+ font-weight: 900;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Visby/VisbyRoundCF-Heavy.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Visby Round CF Extra';
+ font-weight: 200;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Visby/VisbyRoundCF-ExtraLight.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Visby Round CF';
+ font-weight: 300;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Visby/VisbyRoundCF-Light.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Visby Round CF';
+ font-weight: 500;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Visby/VisbyRoundCF-Medium.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Visby Round CF';
+ font-weight: normal;
+ font-style: normal;
+ font-display: swap;
+ src: url('../fonts/Visby/VisbyRoundCF-Regular.ttf') format('truetype');
+}
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-Bold.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-Bold.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-BoldItalic.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-BoldItalic.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-ExtraBold.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-ExtraBold.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-ExtraBoldItalic.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-ExtraBoldItalic.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-Italic.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-Italic.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-Light.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-Light.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-LightItalic.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-LightItalic.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-Regular.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-Regular.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-SemiBold.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-SemiBold.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-SemiBoldItalic.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/OpenSans/OpenSans-SemiBoldItalic.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-Black.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-Black.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-BlackItalic.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-BlackItalic.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-Bold.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-Bold.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-BoldItalic.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-BoldItalic.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-Italic.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-Italic.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-Light.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-Light.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-LightItalic.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-LightItalic.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-Medium.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-Medium.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-MediumItalic.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-MediumItalic.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-Regular.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-Regular.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-Thin.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-Thin.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-ThinItalic.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Roboto/Roboto-ThinItalic.ttf
old mode 100644
new mode 100755
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Varela/VarelaRound-Regular.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Varela/VarelaRound-Regular.ttf
new file mode 100644
index 00000000..bd68d515
Binary files /dev/null and b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Varela/VarelaRound-Regular.ttf differ
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-Bold.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-Bold.ttf
new file mode 100644
index 00000000..a05328dc
Binary files /dev/null and b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-Bold.ttf differ
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-DemiBold.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-DemiBold.ttf
new file mode 100644
index 00000000..da814e4f
Binary files /dev/null and b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-DemiBold.ttf differ
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-ExtraBold.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-ExtraBold.ttf
new file mode 100644
index 00000000..bd029d0f
Binary files /dev/null and b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-ExtraBold.ttf differ
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-ExtraLight.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-ExtraLight.ttf
new file mode 100644
index 00000000..2beb5799
Binary files /dev/null and b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-ExtraLight.ttf differ
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-Heavy.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-Heavy.ttf
new file mode 100644
index 00000000..9013cf97
Binary files /dev/null and b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-Heavy.ttf differ
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-Light.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-Light.ttf
new file mode 100644
index 00000000..de01b934
Binary files /dev/null and b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-Light.ttf differ
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-Medium.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-Medium.ttf
new file mode 100644
index 00000000..b1835d50
Binary files /dev/null and b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-Medium.ttf differ
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-Regular.ttf b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-Regular.ttf
new file mode 100644
index 00000000..346c068d
Binary files /dev/null and b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fonts/Visby/VisbyRoundCF-Regular.ttf differ
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Buttons.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Buttons.fxml
new file mode 100644
index 00000000..dad31c10
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Buttons.fxml
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/ChecksRadiosToggles.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/ChecksRadiosToggles.fxml
new file mode 100644
index 00000000..76a6bf86
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/ChecksRadiosToggles.fxml
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/ComboBoxes.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/ComboBoxes.fxml
new file mode 100644
index 00000000..a328b662
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/ComboBoxes.fxml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Demo.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Demo.fxml
new file mode 100644
index 00000000..8e6a8343
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Demo.fxml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Dialogs.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Dialogs.fxml
new file mode 100644
index 00000000..64fea327
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Dialogs.fxml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/FontResources.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/FontResources.fxml
new file mode 100644
index 00000000..0949f7fa
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/FontResources.fxml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/ListViews.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/ListViews.fxml
new file mode 100644
index 00000000..3485daff
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/ListViews.fxml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Notifications.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Notifications.fxml
new file mode 100644
index 00000000..e7243433
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Notifications.fxml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Pickers.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Pickers.fxml
new file mode 100644
index 00000000..700f0845
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Pickers.fxml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Progress.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Progress.fxml
new file mode 100644
index 00000000..e8a3cbff
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Progress.fxml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/ScrollPanes.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/ScrollPanes.fxml
new file mode 100644
index 00000000..58435ff7
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/ScrollPanes.fxml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Sliders.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Sliders.fxml
new file mode 100644
index 00000000..8593dc97
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Sliders.fxml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Stepper.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Stepper.fxml
new file mode 100644
index 00000000..adbcf150
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/Stepper.fxml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/TableViews.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/TableViews.fxml
new file mode 100644
index 00000000..783e62b8
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/TableViews.fxml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/TextFields.fxml b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/TextFields.fxml
new file mode 100644
index 00000000..a9668c5d
--- /dev/null
+++ b/demo/src/main/resources/io/github/palexdev/materialfx/demo/fxml/TextFields.fxml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/main/resources/logo.ico b/demo/src/main/resources/logo.ico
deleted file mode 100644
index 04347e6f..00000000
Binary files a/demo/src/main/resources/logo.ico and /dev/null differ
diff --git a/demo/src/main/resources/logo.png b/demo/src/main/resources/logo.png
new file mode 100644
index 00000000..edefd3be
Binary files /dev/null and b/demo/src/main/resources/logo.png differ
diff --git a/demo/src/test/java/BorderWithGap.java b/demo/src/test/java/BorderWithGap.java
new file mode 100755
index 00000000..cb65b4d6
--- /dev/null
+++ b/demo/src/test/java/BorderWithGap.java
@@ -0,0 +1,34 @@
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.Region;
+import javafx.stage.Stage;
+
+public class BorderWithGap extends Application {
+
+ // Background
+ @Override
+ public void start(Stage primaryStage) {
+ AnchorPane pane = new AnchorPane();
+
+ Label label = new Label("Border with gap");
+ AnchorPane.setTopAnchor(label, 50.0);
+ AnchorPane.setLeftAnchor(label, 50.0);
+ label.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
+ pane.getChildren().add(label);
+
+ label.setStyle(
+ """
+ -fx-background-color: blue, -fx-background, -fx-background;
+ -fx-background-insets: 0, 0 10 38 10, 1;
+ -fx-background-radius: 5, 0, 3;
+ -fx-padding: 10;
+ """
+ );
+
+ Scene scene = new Scene(pane, 600, 600);
+ primaryStage.setScene(scene);
+ primaryStage.show();
+ }
+}
diff --git a/demo/src/test/java/ContextTest.java b/demo/src/test/java/ContextTest.java
new file mode 100644
index 00000000..56231d78
--- /dev/null
+++ b/demo/src/test/java/ContextTest.java
@@ -0,0 +1,49 @@
+import io.github.palexdev.materialfx.controls.MFXButton;
+import io.github.palexdev.materialfx.controls.MFXContextMenu;
+import io.github.palexdev.materialfx.controls.MFXContextMenuItem;
+import io.github.palexdev.materialfx.factories.InsetsFactory;
+import io.github.palexdev.materialfx.font.MFXFontIcon;
+import io.github.palexdev.materialfx.utils.ColorUtils;
+import io.github.palexdev.materialfx.utils.StringUtils;
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.scene.layout.BorderPane;
+import javafx.stage.Stage;
+
+public class ContextTest extends Application {
+
+ @Override
+ public void start(Stage primaryStage) throws Exception {
+ BorderPane bp = new BorderPane();
+
+ MFXButton button = new MFXButton("Show");
+ MFXContextMenu menu = new MFXContextMenu(button);
+ populateMenu(menu, 5);
+
+ Label labelSeparator = new Label("Separator");
+ labelSeparator.setPadding(InsetsFactory.of(5, 3, 5, 0));
+ menu.addSeparator(labelSeparator);
+ menu.addItem(new MFXContextMenuItem("Separated Item", MFXFontIcon.getRandomIcon(12, ColorUtils.getRandomColor())));
+
+ menu.addLineSeparator(MFXContextMenu.Builder.getLineSeparator());
+ menu.addItem(new MFXContextMenuItem("LSeparated Item", MFXFontIcon.getRandomIcon(12, ColorUtils.getRandomColor())));
+
+ menu.install();
+
+ bp.setCenter(button);
+ Scene scene = new Scene(bp, 800, 600);
+ primaryStage.setScene(scene);
+ primaryStage.show();
+ }
+
+ private void populateMenu(MFXContextMenu menu, int num) {
+ MFXContextMenuItem[] items = new MFXContextMenuItem[num];
+ for (int i = 0; i < num; i++) {
+ MFXContextMenuItem item = new MFXContextMenuItem("Menu Item " + (i + 1), MFXFontIcon.getRandomIcon(12, ColorUtils.getRandomColor()));
+ item.setAccelerator("Alt + " + StringUtils.randAlphabetic(1).toUpperCase());
+ items[i] = item;
+ }
+ menu.addItems(items);
+ }
+}
diff --git a/demo/src/test/java/FilterPaneTest.java b/demo/src/test/java/FilterPaneTest.java
new file mode 100755
index 00000000..6e147c4f
--- /dev/null
+++ b/demo/src/test/java/FilterPaneTest.java
@@ -0,0 +1,94 @@
+import fr.brouillard.oss.cssfx.CSSFX;
+import io.github.palexdev.materialfx.MFXResourcesLoader;
+import io.github.palexdev.materialfx.controls.BoundTextField;
+import io.github.palexdev.materialfx.controls.MFXButton;
+import io.github.palexdev.materialfx.controls.MFXFilterPane;
+import io.github.palexdev.materialfx.controls.MFXTextField;
+import io.github.palexdev.materialfx.demo.model.Machine;
+import io.github.palexdev.materialfx.dialogs.MFXDialogs;
+import io.github.palexdev.materialfx.dialogs.MFXStageDialog;
+import io.github.palexdev.materialfx.factories.InsetsFactory;
+import io.github.palexdev.materialfx.filter.EnumFilter;
+import io.github.palexdev.materialfx.filter.IntegerFilter;
+import io.github.palexdev.materialfx.utils.NodeUtils;
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.text.Text;
+import javafx.stage.Stage;
+import org.scenicview.ScenicView;
+
+public class FilterPaneTest extends Application {
+ private String text =
+ """
+ Lorem Ipsum is simply dummy text of the printing and typesetting industry.
+ Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
+ when an unknown printer took a galley of type and scrambled it to make a type specimen book.
+ It has survived not only five centuries, but also the leap into electronic typesetting,
+ remaining essentially unchanged.
+ It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages,
+ and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
+ """;
+ private MFXStageDialog dialog;
+
+ @Override
+ public void start(Stage primaryStage) throws Exception {
+ CSSFX.start();
+
+ BorderPane borderPane = new BorderPane();
+ borderPane.setPadding(InsetsFactory.all(20));
+
+ dialog = MFXDialogs.info()
+ .setHeaderText("Info Dialog")
+ //.setContentText("This is a simple test for the new MaterialFX dialogs!")
+ .setContentText(text)
+ .addActions(new MFXButton("Cancel"))
+ .addActions(new MFXButton("Ok"))
+ .makeScrollable(true)
+ .addStylesheets(MFXResourcesLoader.load("css/MFXDialogs.css"))
+ .toStageDialogBuilder()
+ .setTitle("MaterialFX - Dialogs Test")
+ .initOwner(primaryStage)
+ .setOwnerNode(borderPane)
+ .setScrimOwner(true)
+ .setDraggable(true)
+ .setOverlayClose(true)
+ .get();
+
+ MFXFilterPane filterPane = new MFXFilterPane<>();
+ filterPane.getStylesheets().add(MFXResourcesLoader.load("css/MFXFilterPane.css"));
+ filterPane.getFilters().add(new EnumFilter<>("State", Machine::getState, Machine.State.class));
+ filterPane.getFilters().add(new IntegerFilter<>("Machine ID", Machine::getID));
+
+ MFXButton button = new MFXButton("Show Dialog");
+ button.setOnAction(event -> {
+ //dialog.showDialog();
+ MFXStageDialog dialog = MFXDialogs.filter(filterPane)
+ .makeScrollable(false)
+ .addStylesheets(MFXResourcesLoader.load("css/MFXDialogs.css"))
+ .toStageDialogBuilder()
+ .setTitle("MFXFilterDialog - Test")
+ .setAlwaysOnTop(true)
+ .setDraggable(true)
+ .get();
+ dialog.showDialog();
+ ScenicView.show(dialog.getScene());
+ });
+
+ MFXTextField textField = new MFXTextField("", "", "Float");
+ NodeUtils.waitForSkin(textField, () -> {
+ BoundTextField node = (BoundTextField) textField.lookup(".text-field");
+ NodeUtils.waitForSkin(node, () -> {
+ Text text = (Text) node.lookup(".text");
+ text.wrappingWidthProperty().bind(textField.widthProperty());
+ }, false, true);
+ }, false, true);
+
+ borderPane.setTop(button);
+ //borderPane.setCenter(filterPane);
+ borderPane.setBottom(textField);
+ Scene scene = new Scene(borderPane, 800, 600);
+ primaryStage.setScene(scene);
+ primaryStage.show();
+ }
+}
diff --git a/demo/src/test/java/FloatingStyle.java b/demo/src/test/java/FloatingStyle.java
new file mode 100755
index 00000000..f6ad05ca
--- /dev/null
+++ b/demo/src/test/java/FloatingStyle.java
@@ -0,0 +1,3 @@
+public enum FloatingStyle {
+ FLOATING, IFTA
+}
diff --git a/demo/src/test/java/Launcher.java b/demo/src/test/java/Launcher.java
new file mode 100755
index 00000000..588c086f
--- /dev/null
+++ b/demo/src/test/java/Launcher.java
@@ -0,0 +1,9 @@
+import javafx.application.Application;
+
+public class Launcher {
+
+ public static void main(String[] args) {
+ System.setProperty("prism.text", "t2k");
+ Application.launch(Playground.class, args);
+ }
+}
diff --git a/demo/src/test/java/MFXLabelRework.java b/demo/src/test/java/MFXLabelRework.java
new file mode 100755
index 00000000..e718d4d3
--- /dev/null
+++ b/demo/src/test/java/MFXLabelRework.java
@@ -0,0 +1,166 @@
+import io.github.palexdev.materialfx.utils.StyleablePropertiesUtils;
+import javafx.beans.property.*;
+import javafx.css.*;
+import javafx.scene.Node;
+import javafx.scene.control.Labeled;
+import javafx.scene.control.Skin;
+
+import java.util.List;
+
+public class MFXLabelRework extends Labeled {
+ private final String STYLE_CLASS = "mfx-label";
+ private final String STYLESHEET = "";
+
+ private final StringProperty floatingText = new SimpleStringProperty("");
+ private final ObjectProperty trailingIcon = new SimpleObjectProperty<>();
+
+ protected static final PseudoClass EDITING_PSEUDO_CLASS = PseudoClass.getPseudoClass("editing");
+ private final BooleanProperty editing = new SimpleBooleanProperty();
+
+ public MFXLabelRework() {
+ this("");
+ }
+
+ public MFXLabelRework(String text) {
+ this(text, null);
+ }
+
+ public MFXLabelRework(String text, String floatingText) {
+ this(text, floatingText, null);
+ }
+
+ public MFXLabelRework(String text, String floatingText, Node graphic) {
+ super(text, graphic);
+ setFloatingText(floatingText);
+ initialize();
+ }
+
+ private void initialize() {
+ getStyleClass().add(STYLE_CLASS);
+ editing.addListener(invalidated -> pseudoClassStateChanged(EDITING_PSEUDO_CLASS, editing.get()));
+ }
+
+ public String getFloatingText() {
+ return floatingText.get();
+ }
+
+ public StringProperty floatingTextProperty() {
+ return floatingText;
+ }
+
+ public void setFloatingText(String floatingText) {
+ this.floatingText.set(floatingText);
+ }
+
+ public Node getTrailingIcon() {
+ return trailingIcon.get();
+ }
+
+ public ObjectProperty trailingIconProperty() {
+ return trailingIcon;
+ }
+
+ public void setTrailingIcon(Node trailingIcon) {
+ this.trailingIcon.set(trailingIcon);
+ }
+
+ public boolean isEditing() {
+ return editing.get();
+ }
+
+ public BooleanProperty editingProperty() {
+ return editing;
+ }
+
+ public void setEditing(boolean editing) {
+ this.editing.set(editing);
+ }
+
+ private final StyleableBooleanProperty editable = new SimpleStyleableBooleanProperty(
+ StyleableProperties.EDITABLE,
+ this,
+ "editable",
+ false
+ ) {
+ @Override
+ public StyleOrigin getStyleOrigin() {
+ return StyleOrigin.USER_AGENT;
+ }
+ };
+
+ private final StyleableDoubleProperty gap = new SimpleStyleableDoubleProperty(
+ StyleableProperties.GAP,
+ this,
+ "gap",
+ 3.0
+ ) {
+ @Override
+ public StyleOrigin getStyleOrigin() {
+ return StyleOrigin.USER_AGENT;
+ }
+ };
+
+ public boolean isEditable() {
+ return editable.get();
+ }
+
+ public StyleableBooleanProperty editableProperty() {
+ return editable;
+ }
+
+ public void setEditable(boolean editable) {
+ this.editable.set(editable);
+ }
+
+ public double getGap() {
+ return gap.get();
+ }
+
+ public StyleableDoubleProperty gapProperty() {
+ return gap;
+ }
+
+ public void setGap(double gap) {
+ this.gap.set(gap);
+ }
+
+ private static class StyleableProperties {
+ private static final StyleablePropertyFactory FACTORY = new StyleablePropertyFactory<>(Labeled.getClassCssMetaData());
+ private static final List> cssMetaDataList;
+
+ private static final CssMetaData EDITABLE =
+ FACTORY.createBooleanCssMetaData(
+ "-mfx-editable",
+ MFXLabelRework::editableProperty,
+ false
+ );
+
+ private static final CssMetaData GAP =
+ FACTORY.createSizeCssMetaData(
+ "-mfx-gap",
+ MFXLabelRework::gapProperty,
+ 3.0
+ );
+
+ static {
+ cssMetaDataList = StyleablePropertiesUtils.cssMetaDataList(
+ Labeled.getClassCssMetaData(),
+ EDITABLE, GAP
+ );
+ }
+ }
+
+ public static List> getControlCssMetaDataList() {
+ return StyleableProperties.cssMetaDataList;
+ }
+
+ @Override
+ protected Skin> createDefaultSkin() {
+ return new MFXLabelSkinRework(this);
+ }
+
+ @Override
+ public List> getControlCssMetaData() {
+ return MFXLabelRework.getControlCssMetaDataList();
+ }
+}
diff --git a/demo/src/test/java/MFXLabelSkinRework.java b/demo/src/test/java/MFXLabelSkinRework.java
new file mode 100755
index 00000000..eca7b150
--- /dev/null
+++ b/demo/src/test/java/MFXLabelSkinRework.java
@@ -0,0 +1,170 @@
+import io.github.palexdev.materialfx.controls.BoundLabel;
+import javafx.scene.Node;
+import javafx.scene.control.SkinBase;
+import javafx.scene.control.TextField;
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.text.Text;
+
+public class MFXLabelSkinRework extends SkinBase {
+ private final BoundLabel text;
+ private final Text floatingText;
+
+ private final TextField editor;
+ private boolean commitChanges = false;
+
+ public MFXLabelSkinRework(MFXLabelRework label) {
+ super(label);
+
+ text = new BoundLabel(label);
+ text.graphicProperty().unbind();
+ text.setGraphic(null);
+ text.setMaxWidth(Double.MAX_VALUE);
+
+ floatingText = new Text();
+ floatingText.textProperty().bind(label.floatingTextProperty());
+
+ editor = new TextField();
+ editor.getStyleClass().add("editor");
+ editor.setManaged(false);
+ editor.setVisible(false);
+
+ getChildren().addAll(text, floatingText, editor);
+ if (label.getGraphic() != null) getChildren().add(0, label.getGraphic());
+ if (label.getTrailingIcon() != null) getChildren().add(0, label.getTrailingIcon());
+ addListeners();
+ }
+
+ private void addListeners() {
+ MFXLabelRework label = getSkinnable();
+
+ label.graphicProperty().addListener((observable, oldValue, newValue) -> {
+ if (oldValue != null) getChildren().remove(oldValue);
+ if (newValue != null) getChildren().add(0, newValue);
+ });
+ label.trailingIconProperty().addListener((observable, oldValue, newValue) -> {
+ if (oldValue != null) getChildren().remove(oldValue);
+ if (newValue != null) getChildren().remove(newValue);
+ });
+ label.gapProperty().addListener(invalidated -> label.requestLayout());
+
+ label.editingProperty().addListener((observable, oldValue, newValue) -> showEditor(newValue));
+ label.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
+ if (!label.isEditable()) return;
+ if (event.getClickCount() >= 2 && event.getClickCount() % 2 == 0 && !label.isEditing()) {
+ label.setEditing(true);
+ }
+ });
+
+ label.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
+ if (event.getCode() == KeyCode.ENTER) {
+ commitChanges = true;
+ label.setEditing(false);
+ } else if (event.getCode() == KeyCode.ESCAPE) {
+ commitChanges = false;
+ label.setEditing(false);
+ }
+ });
+ editor.focusedProperty().addListener((observable, oldValue, newValue) -> {
+ if (!newValue && label.isEditing()) {
+ commitChanges = true;
+ label.setEditing(false);
+ }
+ });
+
+ }
+
+ protected void showEditor(boolean show) {
+ MFXLabelRework label = getSkinnable();
+ if (show) {
+ text.setVisible(false);
+ editor.setText(label.getText());
+ editor.setVisible(true);
+ editor.requestFocus();
+ editor.positionCaret(editor.getText().length());
+ } else {
+ if (commitChanges) label.setText(editor.getText());
+ editor.setVisible(false);
+ text.setVisible(true);
+ }
+ commitChanges = false;
+ }
+
+ @Override
+ protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
+ return topInset +
+ floatingText.prefHeight(-1) +
+ getSkinnable().getGap() +
+ text.prefHeight(-1) +
+ bottomInset;
+ }
+
+ @Override
+ protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
+ MFXLabelRework label = getSkinnable();
+ Node graphic = label.getGraphic();
+ Node trailing = label.getTrailingIcon();
+ return leftInset +
+ ((graphic != null) ? graphic.prefWidth(-1) + label.getGraphicTextGap() : 0) +
+ Math.max(floatingText.prefWidth(-1), text.prefWidth(-1)) +
+ ((trailing != null) ? label.getGraphicTextGap() + trailing.prefWidth(-1) : 0) +
+ rightInset;
+ }
+
+ @Override
+ protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
+ return getSkinnable().prefWidth(-1);
+ }
+
+ @Override
+ protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
+ return getSkinnable().prefHeight(-1);
+ }
+
+ @Override
+ protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {
+ MFXLabelRework label = getSkinnable();
+ text.autosize();
+ floatingText.autosize();
+
+ Node graphic = label.getGraphic();
+ Node trailingIcon = label.getTrailingIcon();
+
+ // Graphic
+ double graphicW = 0;
+ double graphicH;
+ double graphicX = snappedLeftInset();
+ double graphicY;
+ if (graphic != null) {
+ graphic.autosize();
+ graphicW = graphic.prefWidth(-1);
+ graphicH = graphic.prefHeight(-1);
+ graphicY = (contentHeight / 2) - (graphicH / 2) + snappedTopInset();
+ graphic.relocate(snapPositionX(graphicX), snapPositionY(graphicY));
+ }
+
+ // Texts and Editor
+ double floatW = floatingText.prefWidth(-1);
+ double floatH = floatingText.prefHeight(-1);
+ double floatX = graphicX + graphicW + ((graphic != null) ? label.getGraphicTextGap() : 0);
+ double floatY = snappedTopInset();
+ floatingText.relocate(snapPositionX(floatX), snapPositionY(floatY));
+
+ double textW = text.prefWidth(-1);
+ double textY = floatY + label.getGap() + floatH;
+ text.relocate(snapPositionX(floatX), snapPositionY(textY));
+
+ double editorH = text.prefHeight(-1);
+ editor.resizeRelocate(snapPositionX(floatX), snapPositionY(textY), textW, editorH);
+
+ // Trailing
+ if (trailingIcon != null) {
+ trailingIcon.autosize();
+ double trailingH = trailingIcon.prefHeight(-1);
+ double trailingX = floatX + Math.max(textW, floatW) + label.getGraphicTextGap();
+ double trailingY = (contentHeight / 2) - (trailingH / 2) + snappedTopInset();
+ trailingIcon.relocate(snapPositionX(trailingX), snapPositionY(trailingY));
+ }
+ }
+}
diff --git a/demo/src/test/java/NotificationsTest.java b/demo/src/test/java/NotificationsTest.java
new file mode 100755
index 00000000..256c5885
--- /dev/null
+++ b/demo/src/test/java/NotificationsTest.java
@@ -0,0 +1,84 @@
+import io.github.palexdev.materialfx.MFXResourcesLoader;
+import io.github.palexdev.materialfx.controls.MFXIconWrapper;
+import io.github.palexdev.materialfx.controls.MFXNotificationCenter;
+import io.github.palexdev.materialfx.controls.MFXSimpleNotification;
+import io.github.palexdev.materialfx.controls.MFXTextField;
+import io.github.palexdev.materialfx.enums.NotificationPos;
+import io.github.palexdev.materialfx.factories.InsetsFactory;
+import io.github.palexdev.materialfx.font.MFXFontIcon;
+import io.github.palexdev.materialfx.notifications.MFXNotificationCenterSystem;
+import io.github.palexdev.materialfx.notifications.MFXNotificationSystem;
+import io.github.palexdev.materialfx.notifications.base.INotification;
+import io.github.palexdev.materialfx.utils.ColorUtils;
+import io.github.palexdev.materialfx.utils.RandomUtils;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Priority;
+import javafx.scene.layout.StackPane;
+import javafx.stage.Stage;
+import org.scenicview.ScenicView;
+
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+
+public class NotificationsTest extends Application {
+
+ @Override
+ public void start(Stage primaryStage) throws Exception {
+ StackPane stackPane = new StackPane();
+
+ MFXNotificationCenter notificationCenter = new MFXNotificationCenter();
+ notificationCenter.getStylesheets().add(MFXResourcesLoader.load("css/MFXNotificationCenter.css"));
+ IntStream.range(0, 100).forEach(i -> notificationCenter.getNotifications().add(createDummyNotification()));
+ stackPane.getChildren().add(notificationCenter);
+
+ MFXNotificationCenterSystem.instance()
+ .initOwner(primaryStage)
+ .setOpenOnNew(false)
+ .setCloseAutomatically(true)
+ .setPosition(NotificationPos.TOP_LEFT);
+ MFXNotificationSystem.instance()
+ .initOwner(primaryStage)
+ .setPosition(NotificationPos.TOP_RIGHT);
+ stackPane.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
+ switch (event.getCode()) {
+ case A -> MFXNotificationCenterSystem.instance().publish(createDummyNotification());
+ case C -> notificationCenter.stopNotificationsUpdater();
+ case S -> notificationCenter.startNotificationsUpdater(60, TimeUnit.SECONDS);
+ case T -> MFXNotificationSystem.instance().publish(createDummyNotification());
+ case P -> MFXNotificationCenterSystem.instance().delaySetPosition(NotificationPos.TOP_LEFT);
+ }
+ });
+
+ Scene scene = new Scene(stackPane, 800, 600);
+ primaryStage.setScene(scene);
+ primaryStage.show();
+
+ ScenicView.show(notificationCenter.getScene());
+ }
+
+ private INotification createDummyNotification() {
+ MFXTextField label = MFXTextField.asLabel("Random Label n." + RandomUtils.random.nextInt());
+ label.setLeadingIcon(new MFXIconWrapper(MFXFontIcon.getRandomIcon(18, ColorUtils.getRandomColor()), 24));
+ label.setAlignment(Pos.CENTER_LEFT);
+ label.setMaxWidth(Double.MAX_VALUE);
+ HBox.setHgrow(label, Priority.ALWAYS);
+
+ MFXTextField time = MFXTextField.asLabel();
+ time.setAlignment(Pos.CENTER_RIGHT);
+
+ HBox box = new HBox(label, time);
+ box.setMinSize(450, -1);
+ box.setStyle("-fx-background-color: white");
+ box.setAlignment(Pos.CENTER_LEFT);
+ box.setPadding(InsetsFactory.right(20));
+ MFXSimpleNotification notification = new MFXSimpleNotification(box);
+ notification.setOnUpdateElapsed((longElapsed, stringElapsed) -> Platform.runLater(() -> time.setText(stringElapsed)));
+ time.setText(notification.getTimeToStringConverter().apply(notification.getElapsedTime()));
+ return notification;
+ }
+}
diff --git a/demo/src/test/java/Playground.java b/demo/src/test/java/Playground.java
new file mode 100755
index 00000000..7b5909f2
--- /dev/null
+++ b/demo/src/test/java/Playground.java
@@ -0,0 +1,43 @@
+import fr.brouillard.oss.cssfx.CSSFX;
+import io.github.palexdev.materialfx.controls.MFXButton;
+import io.github.palexdev.materialfx.controls.MFXPasswordField;
+import io.github.palexdev.materialfx.controls.MFXRectangleToggleNode;
+import io.github.palexdev.materialfx.factories.InsetsFactory;
+import io.github.palexdev.materialfx.font.MFXFontIcon;
+import javafx.application.Application;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.Region;
+import javafx.stage.Stage;
+import org.scenicview.ScenicView;
+
+public class Playground extends Application {
+
+ @Override
+ public void start(Stage primaryStage) {
+ CSSFX.start();
+ BorderPane borderPane = new BorderPane();
+
+ MFXPasswordField textField = new MFXPasswordField("", "Prompt", "Floating Text");
+
+ MFXRectangleToggleNode toggleNode = new MFXRectangleToggleNode("This should be a long text");
+ toggleNode.setLabelLeadingIcon(new MFXFontIcon("mfx-google", 48));
+ toggleNode.setLabelTrailingIcon(new MFXFontIcon("mfx-google", 24));
+
+ MFXButton button = new MFXButton("Click Me!");
+ button.setOnAction(event -> textField.setShowPassword(!textField.isShowPassword()));
+ button.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
+ BorderPane.setAlignment(button, Pos.TOP_CENTER);
+ BorderPane.setMargin(button, InsetsFactory.top(10));
+
+ borderPane.getStylesheets().add(Playground.class.getResource("CustomField.css").toString());
+ borderPane.setTop(button);
+ borderPane.setCenter(toggleNode);
+ Scene scene = new Scene(borderPane, 800, 600);
+ primaryStage.setScene(scene);
+ primaryStage.setOnShown(event -> button.requestFocus());
+ primaryStage.show();
+ ScenicView.show(scene);
+ }
+}
diff --git a/demo/src/test/java/PopupTest.java b/demo/src/test/java/PopupTest.java
new file mode 100755
index 00000000..a6658b87
--- /dev/null
+++ b/demo/src/test/java/PopupTest.java
@@ -0,0 +1,85 @@
+import io.github.palexdev.materialfx.beans.Alignment;
+import io.github.palexdev.materialfx.controls.MFXButton;
+import io.github.palexdev.materialfx.controls.MFXPopup;
+import io.github.palexdev.materialfx.controls.MFXSimpleNotification;
+import io.github.palexdev.materialfx.controls.MFXTextField;
+import io.github.palexdev.materialfx.effects.DepthLevel;
+import io.github.palexdev.materialfx.enums.ButtonType;
+import io.github.palexdev.materialfx.font.MFXFontIcon;
+import io.github.palexdev.materialfx.notifications.base.INotification;
+import io.github.palexdev.materialfx.utils.ColorUtils;
+import io.github.palexdev.materialfx.utils.RandomUtils;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.geometry.HPos;
+import javafx.geometry.Pos;
+import javafx.geometry.VPos;
+import javafx.scene.Scene;
+import javafx.scene.layout.BackgroundFill;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Priority;
+import javafx.scene.layout.StackPane;
+import javafx.scene.paint.Paint;
+import javafx.stage.Stage;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class PopupTest extends Application {
+
+ @Override
+ public void start(Stage primaryStage) throws Exception {
+ StackPane stackPane = new StackPane() {
+ @Override
+ public String getUserAgentStylesheet() {
+ return Launcher.class.getResource("PopupTest.css").toExternalForm();
+ }
+ };
+ //stackPane.getStylesheets().add(Launcher.class.getResource("PopupTest.css").toExternalForm());
+
+ MFXButton button = new MFXButton("SHOW");
+ button.backgroundProperty().addListener((observable, oldValue, newValue) -> {
+ List fills = newValue.getFills();
+ Paint[] colors = fills.stream().map(BackgroundFill::getFill).toArray(Paint[]::new);
+ System.out.println(Arrays.toString(colors));
+ });
+ button.setPrefSize(180, 36);
+ button.setButtonType(ButtonType.RAISED);
+ button.setDepthLevel(DepthLevel.LEVEL1);
+
+ button.setOnAction(event -> {
+ MFXButton hello = new MFXButton("Hello Button");
+ MFXPopup popup = new MFXPopup(hello);
+ popup.setPopupStyleableParent(stackPane);
+ popup.show(button, Alignment.of(HPos.LEFT, VPos.BOTTOM), -0, -0);
+ });
+
+ stackPane.getChildren().add(button);
+ Scene scene = new Scene(stackPane, 800, 800);
+ primaryStage.setScene(scene);
+ primaryStage.show();
+ }
+
+ private INotification createDummyNotification() {
+ MFXTextField label = MFXTextField.asLabel("Random Label n." + RandomUtils.random.nextInt());
+ label.setLeadingIcon(MFXFontIcon.getRandomIcon(32, ColorUtils.getRandomColor()));
+ label.setAlignment(Pos.CENTER_LEFT);
+ label.setMaxWidth(Double.MAX_VALUE);
+ HBox.setHgrow(label, Priority.ALWAYS);
+
+ MFXTextField time = MFXTextField.asLabel();
+ time.setAlignment(Pos.CENTER_RIGHT);
+
+ HBox box = new HBox(label, time);
+ box.setMinSize(450, 100);
+ box.setStyle("-fx-background-color: white");
+ box.setAlignment(Pos.CENTER_LEFT);
+ MFXSimpleNotification notification = new MFXSimpleNotification(box);
+ notification.setOnUpdateElapsed((longElapsed, stringElapsed) -> Platform.runLater(() -> time.setText(stringElapsed)));
+ time.setText(notification.getTimeToStringConverter().apply(notification.getElapsedTime()));
+ box.setStyle("" +
+ "-fx-background-color: transparent;\n" +
+ "-fx-border-color: red");
+ return notification;
+ }
+}
diff --git a/demo/src/test/java/binding/BiBindingsTest.java b/demo/src/test/java/binding/BiBindingsTest.java
new file mode 100755
index 00000000..2c382f82
--- /dev/null
+++ b/demo/src/test/java/binding/BiBindingsTest.java
@@ -0,0 +1,309 @@
+package binding;
+
+import io.github.palexdev.materialfx.bindings.BiBindingHelper;
+import io.github.palexdev.materialfx.bindings.BiBindingManager;
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.beans.value.ObservableValue;
+import org.junit.jupiter.api.Test;
+
+import java.lang.ref.WeakReference;
+import java.util.LinkedList;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class BiBindingsTest {
+ private final BiBindingManager biBindingManager = BiBindingManager.instance();
+
+ @Test
+ public void biBindingTest1() {
+ assertTrue(biBindingManager.isEmpty());
+
+ IntegerProperty i1 = new SimpleIntegerProperty();
+ IntegerProperty i2 = new SimpleIntegerProperty();
+
+ biBindingManager.bindBidirectional(i1)
+ .with((oldValue, newValue) -> i1.setValue(newValue))
+ .to(i2, (oldValue, newValue) -> i2.setValue(newValue))
+ .create();
+
+ i1.set(10);
+ assertEquals(10, i1.get());
+ assertEquals(10, i2.get());
+
+ i2.set(20);
+ assertEquals(20, i1.get());
+ assertEquals(20, i2.get());
+
+ biBindingManager.unbind(i1, i2);
+ }
+
+ @Test
+ public void biBindingTest2() {
+ assertTrue(biBindingManager.isEmpty());
+
+ IntegerProperty iA = new SimpleIntegerProperty();
+ IntegerProperty iB = new SimpleIntegerProperty();
+ IntegerProperty iC = new SimpleIntegerProperty();
+
+ biBindingManager.bindBidirectional(iA)
+ .with((oldValue, newValue) -> iA.setValue(newValue))
+ .to(iB, (oldValue, newValue) -> iB.setValue(newValue))
+ .create();
+ biBindingManager.bindBidirectional(iA)
+ .with((oldValue, newValue) -> iA.setValue(newValue))
+ .to(iC, (oldValue, newValue) -> iC.setValue(newValue))
+ .create();
+
+ iA.set(8); // All properties must be 8
+ assertEquals(8, iA.get());
+ assertEquals(8, iB.get());
+ assertEquals(8, iC.get());
+
+ iB.set(10); // Only A and B will be 10, C will remain 8
+ assertEquals(10, iA.get());
+ assertEquals(10, iB.get());
+ assertEquals(10, iC.get());
+
+ iC.set(12); // Only A and C will be 12, B will remain 10
+ assertEquals(12, iA.get());
+ assertEquals(12, iB.get());
+ assertEquals(12, iC.get());
+
+ biBindingManager.clear(iA);
+ }
+
+ @Test
+ public void testEagerBinding() {
+ assertTrue(biBindingManager.isEmpty());
+
+ IntegerProperty i1 = new SimpleIntegerProperty(5);
+ IntegerProperty i2 = new SimpleIntegerProperty(10);
+ IntegerProperty i3 = new SimpleIntegerProperty(15);
+
+ biBindingManager.bindBidirectional(i1)
+ .with((oldValue, newValue) -> i1.setValue(newValue))
+ .to(
+ Map.entry(i2, (oldValue, newValue) -> i2.setValue(newValue)),
+ Map.entry(i3, (oldValue, newValue) -> i3.setValue(newValue))
+ )
+ .create();
+
+ assertEquals(15, i1.get());
+ assertEquals(15, i2.get());
+ assertEquals(15, i3.get());
+
+ biBindingManager.clear(i1);
+ assertTrue(biBindingManager.isEmpty());
+ }
+
+ @Test
+ public void testLazyBinding() {
+ assertTrue(biBindingManager.isEmpty());
+
+ IntegerProperty i1 = new SimpleIntegerProperty(5);
+ IntegerProperty i2 = new SimpleIntegerProperty(10);
+ IntegerProperty i3 = new SimpleIntegerProperty(15);
+
+ biBindingManager.bindBidirectional(i1)
+ .with((oldValue, newValue) -> i1.setValue(newValue))
+ .to(
+ Map.entry(i2, (oldValue, newValue) -> i2.setValue(newValue)),
+ Map.entry(i3, (oldValue, newValue) -> i3.setValue(newValue))
+ )
+ .lazy()
+ .create();
+
+ assertEquals(5, i1.get());
+ assertEquals(10, i2.get());
+ assertEquals(15, i3.get());
+
+ i1.set(20);
+ assertEquals(20, i1.get());
+ assertEquals(20, i2.get());
+ assertEquals(20, i3.get());
+
+ biBindingManager.clear(i1);
+ }
+
+ // Performance Debug //
+ @Test
+ public void testPerformance1() {
+ assertTrue(biBindingManager.isEmpty());
+
+ IntegerProperty i1 = new SimpleIntegerProperty();
+ IntegerProperty i2 = new SimpleIntegerProperty();
+ IntegerProperty i3 = new SimpleIntegerProperty();
+
+ DebuggableBiBindingHelper helper = new DebuggableBiBindingHelper<>();
+ biBindingManager.bindBidirectional(i1)
+ .to(i2)
+ .withHelper(helper)
+ .create();
+
+ assertEquals(0, helper.getTargetCounter());
+ assertEquals(0, helper.getSourcesCounter());
+
+ i1.set(5);
+ assertEquals(5, i1.get());
+ assertEquals(5, i2.get());
+ assertEquals(0, helper.getTargetCounter());
+ assertEquals(1, helper.getSourcesCounter());
+
+ i2.set(10);
+ assertEquals(10, i1.get());
+ assertEquals(10, i2.get());
+ assertEquals(1, helper.getTargetCounter());
+ assertEquals(1, helper.getSourcesCounter());
+
+ BiBindingManager.instance().clear(i1);
+ assertTrue(biBindingManager.isEmpty());
+ }
+
+ @Test
+ public void testPerformance2() {
+ assertTrue(biBindingManager.isEmpty());
+
+ IntegerProperty i1 = new SimpleIntegerProperty();
+ IntegerProperty i2 = new SimpleIntegerProperty();
+ IntegerProperty i3 = new SimpleIntegerProperty();
+
+ DebuggableBiBindingHelper helper = new DebuggableBiBindingHelper<>();
+ biBindingManager.bindBidirectional(i1)
+ .with((oldValue, newValue) -> i1.setValue(newValue))
+ .to(
+ Map.entry(i2, (oldValue, newValue) -> i2.setValue(newValue)),
+ Map.entry(i3, (oldValue, newValue) -> i3.setValue(newValue))
+ )
+ .withHelper(helper)
+ .create();
+
+ assertEquals(0, helper.getTargetCounter());
+ assertEquals(0, helper.getSourcesCounter());
+
+ i1.set(5);
+ assertEquals(5, i1.get());
+ assertEquals(5, i2.get());
+ assertEquals(5, i3.get());
+ assertEquals(0, helper.getTargetCounter());
+ assertEquals(2, helper.getSourcesCounter());
+
+ i2.set(10);
+ assertEquals(10, i1.get());
+ assertEquals(10, i2.get());
+ assertEquals(10, i3.get());
+ assertEquals(1, helper.getTargetCounter());
+ assertEquals(3, helper.getSourcesCounter());
+
+ i3.set(20);
+ assertEquals(20, i1.get());
+ assertEquals(20, i2.get());
+ assertEquals(20, i3.get());
+ assertEquals(2, helper.getTargetCounter());
+ assertEquals(4, helper.getSourcesCounter());
+
+ biBindingManager.clear(i1);
+ assertTrue(biBindingManager.isEmpty());
+ }
+
+ @Test
+ public void biBindingTest3() {
+ assertTrue(biBindingManager.isEmpty());
+
+ IntegerProperty iA = new SimpleIntegerProperty();
+ IntegerProperty iB = new SimpleIntegerProperty();
+ IntegerProperty iC = new SimpleIntegerProperty();
+
+ DebuggableBiBindingHelper helper = new DebuggableBiBindingHelper<>();
+ biBindingManager.bindBidirectional(iA)
+ .with((oldValue, newValue) -> iA.setValue(newValue))
+ .to(iB, (oldValue, newValue) -> iB.setValue(newValue))
+ .withHelper(helper)
+ .create();
+ biBindingManager.bindBidirectional(iA)
+ .with((oldValue, newValue) -> iA.setValue(newValue))
+ .to(iC, (oldValue, newValue) -> iC.setValue(newValue))
+ .withHelper(helper)
+ .create();
+
+ assertEquals(0, helper.getTargetCounter());
+ assertEquals(0, helper.getSourcesCounter());
+
+ iA.set(8); // All properties must be 8
+ assertEquals(8, iA.get());
+ assertEquals(8, iB.get());
+ assertEquals(8, iC.get());
+ assertEquals(0, helper.getTargetCounter());
+ assertEquals(2, helper.getSourcesCounter());
+
+ iB.set(10); // B updates A updates C
+ assertEquals(10, iA.get());
+ assertEquals(10, iB.get());
+ assertEquals(10, iC.get());
+ assertEquals(1, helper.getTargetCounter());
+ assertEquals(3, helper.getSourcesCounter());
+
+ iC.set(12); // C updates A updates B
+ assertEquals(12, iA.get());
+ assertEquals(12, iB.get());
+ assertEquals(12, iC.get());
+ assertEquals(2, helper.getTargetCounter());
+ assertEquals(4, helper.getSourcesCounter());
+
+ biBindingManager.clear(iA);
+ }
+
+ @Test
+ public void testCombineHelpers() {
+ IntegerProperty target = new SimpleIntegerProperty();
+ IntegerProperty iA = new SimpleIntegerProperty();
+ IntegerProperty iB = new SimpleIntegerProperty();
+ IntegerProperty iC = new SimpleIntegerProperty();
+
+ BiBindingHelper helper1 = new BiBindingHelper<>();
+ BiBindingHelper helper2 = new BiBindingHelper<>();
+
+ helper1.bind(target);
+ helper1.addSource(iA, (oldValue, newValue) -> iA.setValue(newValue));
+ helper1.addSource(iB, (oldValue, newValue) -> iB.setValue(newValue));
+
+ helper2.bind(target);
+ helper2.addSource(iA, (oldValue, newValue) -> iA.setValue(newValue));
+ helper2.addSource(iC, (oldValue, newValue) -> iC.setValue(newValue));
+
+ BiBindingHelper combined = BiBindingHelper.newFor(helper1, helper2, false);
+ assertEquals(3, combined.size());
+
+ LinkedList>> keys = combined.getUnmodifiableSources();
+ assertEquals(3, keys.size());
+ assertEquals(keys.getFirst().get(), iA);
+ assertEquals(keys.getLast().get(), iC);
+ }
+
+ @Test
+ public void testJavaFXBinding() {
+ IntegerProperty iA = new SimpleIntegerProperty();
+ IntegerProperty iB = new SimpleIntegerProperty();
+ IntegerProperty iC = new SimpleIntegerProperty();
+
+ iA.bindBidirectional(iB);
+ iA.bindBidirectional(iC);
+
+ iA.set(8); // All properties must be 8
+ assertEquals(8, iA.get());
+ assertEquals(8, iB.get());
+ assertEquals(8, iC.get());
+
+ iB.set(10); // B updates A updates C
+ assertEquals(10, iA.get());
+ assertEquals(10, iB.get());
+ assertEquals(10, iC.get());
+
+ iC.set(12); // C updates A updates B
+ assertEquals(12, iA.get());
+ assertEquals(12, iB.get());
+ assertEquals(12, iC.get());
+ }
+}
diff --git a/demo/src/test/java/binding/BindingsTest.java b/demo/src/test/java/binding/BindingsTest.java
new file mode 100755
index 00000000..c970d53f
--- /dev/null
+++ b/demo/src/test/java/binding/BindingsTest.java
@@ -0,0 +1,299 @@
+package binding;
+
+import io.github.palexdev.materialfx.bindings.BindingManager;
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.ReadOnlyIntegerWrapper;
+import javafx.beans.property.SimpleIntegerProperty;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class BindingsTest {
+ private final BindingManager bindingManager = BindingManager.instance();
+
+ @Test
+ public void testBinding1() {
+ assertTrue(bindingManager.isEmpty());
+
+ IntegerProperty source = new SimpleIntegerProperty();
+ IntegerProperty target = new SimpleIntegerProperty() {
+ @Override
+ public boolean isBound() {
+ return bindingManager.isBound(this) && !bindingManager.isIgnoreBinding(this);
+ }
+ };
+
+ bindingManager.bind(target)
+ .to(source)
+ .with((oldValue, newValue) -> target.setValue(newValue))
+ .create();
+
+ source.set(1);
+ assertEquals(1, source.get());
+ assertEquals(1, target.get());
+
+ assertThrows(RuntimeException.class, () -> target.set(5));
+ assertEquals(1, source.get());
+ assertEquals(1, target.get());
+
+ bindingManager.unbind(target);
+ }
+
+ @Test
+ public void testBinding2() {
+ assertTrue(bindingManager.isEmpty());
+
+ ReadOnlyIntegerWrapper source = new ReadOnlyIntegerWrapper();
+ IntegerProperty target = new SimpleIntegerProperty() {
+ @Override
+ public boolean isBound() {
+ return bindingManager.isBound(this) && !bindingManager.isIgnoreBinding(this);
+ }
+ };
+
+ bindingManager.bind(target)
+ .to(source)
+ .with((oldValue, newValue) -> target.setValue(newValue))
+ .create();
+
+ source.set(1);
+ assertEquals(1, source.get());
+ assertEquals(1, target.get());
+
+ assertThrows(RuntimeException.class, () -> target.set(5));
+ assertEquals(1, source.get());
+ assertEquals(1, target.get());
+
+ bindingManager.unbind(target);
+ }
+
+ @Test
+ public void testBindAndUnbind() {
+ assertTrue(bindingManager.isEmpty());
+
+ IntegerProperty source = new SimpleIntegerProperty();
+ IntegerProperty target = new SimpleIntegerProperty() {
+ @Override
+ public boolean isBound() {
+ return bindingManager.isBound(this) && !bindingManager.isIgnoreBinding(this);
+ }
+ };
+
+ bindingManager.bind(target)
+ .to(source)
+ .with((oldValue, newValue) -> target.setValue(newValue))
+ .create();
+
+ source.set(1);
+ assertEquals(1, source.get());
+ assertEquals(1, target.get());
+
+ bindingManager.unbind(target);
+ assertFalse(bindingManager.isBound(target));
+
+ target.set(5);
+ assertEquals(1, source.get());
+ assertEquals(5, target.get());
+
+ bindingManager.unbind(target);
+ }
+
+ @Test
+ public void testReadOnly1() {
+ assertTrue(bindingManager.isEmpty());
+
+ IntegerProperty source = new SimpleIntegerProperty();
+ ReadOnlyIntegerWrapper target = new ReadOnlyIntegerWrapper() {
+ @Override
+ public boolean isBound() {
+ return (bindingManager.isBound(this) || bindingManager.isBound(getReadOnlyProperty())) && !bindingManager.isIgnoreBinding(getReadOnlyProperty());
+ }
+ };
+
+ bindingManager.bind(target.getReadOnlyProperty())
+ .to(source)
+ .with((oldValue, newValue) -> target.setValue(newValue))
+ .create();
+ assertTrue(target.isBound());
+
+ source.set(1);
+ assertEquals(1, source.get());
+ assertEquals(1, target.get());
+
+ bindingManager.unbindReadOnly(target);
+ assertFalse(target.isBound());
+
+ target.set(5);
+ assertEquals(1, source.get());
+ assertEquals(5, target.get());
+
+ bindingManager.unbindReadOnly(target);
+ }
+
+ @Test
+ public void testReadOnly2() {
+ assertTrue(bindingManager.isEmpty());
+
+ ReadOnlyIntegerWrapper source = new ReadOnlyIntegerWrapper();
+ ReadOnlyIntegerWrapper target = new ReadOnlyIntegerWrapper() {
+ @Override
+ public boolean isBound() {
+ return (bindingManager.isBound(this) || bindingManager.isBound(getReadOnlyProperty())) && !bindingManager.isIgnoreBinding(getReadOnlyProperty());
+ }
+ };
+
+ bindingManager.bind(target.getReadOnlyProperty())
+ .to(source.getReadOnlyProperty())
+ .with((oldValue, newValue) -> target.setValue(newValue))
+ .create();
+ assertTrue(target.isBound());
+
+ source.set(1);
+ assertEquals(1, source.get());
+ assertEquals(1, target.get());
+
+ bindingManager.unbindReadOnly(target);
+ assertFalse(target.isBound());
+
+ target.set(5);
+ assertEquals(1, source.get());
+ assertEquals(5, target.get());
+
+ bindingManager.unbindReadOnly(target);
+ }
+
+ @Test
+ public void testMultipleBindings() {
+ assertTrue(bindingManager.isEmpty());
+
+ IntegerProperty i1 = new SimpleIntegerProperty() {
+ @Override
+ public boolean isBound() {
+ return bindingManager.isBound(this) && !bindingManager.isIgnoreBinding(this);
+ }
+ };
+ IntegerProperty i2 = new SimpleIntegerProperty() {
+ @Override
+ public boolean isBound() {
+ return bindingManager.isBound(this) && !bindingManager.isIgnoreBinding(this);
+ }
+ };
+ IntegerProperty i3 = new SimpleIntegerProperty() {
+ @Override
+ public boolean isBound() {
+ return bindingManager.isBound(this) && !bindingManager.isIgnoreBinding(this);
+ }
+ };
+
+ bindingManager.bind(i3)
+ .to(i2)
+ .with((oldValue, newValue) -> i3.setValue(newValue))
+ .create();
+ bindingManager.bind(i2)
+ .to(i1)
+ .with((oldValue, newValue) -> i2.setValue(newValue))
+ .create();
+
+ i1.set(5);
+ assertEquals(5, i1.get());
+ assertEquals(5, i2.get());
+ assertEquals(5, i3.get());
+
+ assertThrows(RuntimeException.class, () -> i2.set(3));
+ assertThrows(RuntimeException.class, () -> i3.set(3));
+
+ bindingManager.dispose();
+ assertTrue(bindingManager.isEmpty());
+
+ bindingManager.bind(i3)
+ .to(i2)
+ .with((oldValue, newValue) -> i3.setValue(newValue))
+ .create();
+ bindingManager.bind(i1)
+ .to(i2)
+ .with((oldValue, newValue) -> i1.setValue(newValue))
+ .create();
+
+ i2.set(10);
+ assertEquals(10, i1.get());
+ assertEquals(10, i2.get());
+ assertEquals(10, i3.get());
+
+ bindingManager.dispose();
+ }
+
+ @Test
+ public void testOverrideBinding() {
+ assertTrue(bindingManager.isEmpty());
+
+ IntegerProperty i1 = new SimpleIntegerProperty() {
+ @Override
+ public boolean isBound() {
+ return bindingManager.isBound(this) && !bindingManager.isIgnoreBinding(this);
+ }
+ };
+ IntegerProperty i2 = new SimpleIntegerProperty() {
+ @Override
+ public boolean isBound() {
+ return bindingManager.isBound(this) && !bindingManager.isIgnoreBinding(this);
+ }
+ };
+ IntegerProperty i3 = new SimpleIntegerProperty() {
+ @Override
+ public boolean isBound() {
+ return bindingManager.isBound(this) && !bindingManager.isIgnoreBinding(this);
+ }
+ };
+
+ bindingManager.bind(i2)
+ .to(i1)
+ .with((oldValue, newValue) -> i2.set(newValue.intValue() * 2))
+ .create();
+ bindingManager.bind(i2)
+ .to(i3)
+ .with((oldValue, newValue) -> i2.set(newValue.intValue() * 3))
+ .create();
+
+ i1.set(50);
+ assertEquals(50, i1.get());
+ assertEquals(0, i2.get());
+ assertEquals(0, i3.get());
+
+ assertThrows(RuntimeException.class, () -> i2.set(100));
+
+ i3.set(10);
+ assertEquals(50, i1.get());
+ assertEquals(30, i2.get());
+ assertEquals(10, i3.get());
+
+ bindingManager.dispose();
+ }
+
+ @Test
+ public void testWithGeneric() {
+ assertTrue(bindingManager.isEmpty());
+
+ IntegerProperty source = new SimpleIntegerProperty();
+ IntegerProperty target = new SimpleIntegerProperty() {
+ @Override
+ public boolean isBound() {
+ return bindingManager.isBound(this) && !bindingManager.isIgnoreBinding(this);
+ }
+ };
+
+ bindingManager.bind(target)
+ .to(source)
+ .with((oldValue, newValue) -> target.setValue(newValue))
+ .create();
+
+ source.set(1);
+ assertEquals(1, source.get());
+ assertEquals(1, target.get());
+
+ assertThrows(RuntimeException.class, () -> target.set(5));
+ assertEquals(1, source.get());
+ assertEquals(1, target.get());
+
+ bindingManager.unbind(target);
+ }
+}
diff --git a/demo/src/test/java/binding/DebuggableBiBindingHelper.java b/demo/src/test/java/binding/DebuggableBiBindingHelper.java
new file mode 100755
index 00000000..2ae90989
--- /dev/null
+++ b/demo/src/test/java/binding/DebuggableBiBindingHelper.java
@@ -0,0 +1,35 @@
+package binding;
+
+import io.github.palexdev.materialfx.bindings.BiBindingHelper;
+import javafx.beans.value.ObservableValue;
+
+import java.util.function.BiConsumer;
+
+public class DebuggableBiBindingHelper extends BiBindingHelper {
+ private int targetCounter = 0;
+ private int sourcesCounter = 0;
+
+ @Override
+ protected void beforeUpdateTarget() {
+ targetCounter++;
+ }
+
+ @Override
+ protected void updateSource(ObservableValue extends T> source, BiConsumer updater, T oldValue, T newValue) {
+ sourcesCounter++;
+ super.updateSource(source, updater, oldValue, newValue);
+ }
+
+ public int getTargetCounter() {
+ return targetCounter;
+ }
+
+ public int getSourcesCounter() {
+ return sourcesCounter;
+ }
+
+ public void resetCounters() {
+ targetCounter = 0;
+ sourcesCounter = 0;
+ }
+}
diff --git a/demo/src/test/java/collections/TransformableListTest.java b/demo/src/test/java/collections/TransformableListTest.java
new file mode 100755
index 00000000..60203ad2
--- /dev/null
+++ b/demo/src/test/java/collections/TransformableListTest.java
@@ -0,0 +1,69 @@
+package collections;
+
+import io.github.palexdev.materialfx.collections.TransformableList;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.collections.transformation.FilteredList;
+import javafx.collections.transformation.SortedList;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.testfx.framework.junit5.ApplicationExtension;
+
+import java.util.Comparator;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@ExtendWith(ApplicationExtension.class)
+public class TransformableListTest {
+ private final ObservableList source = FXCollections.observableArrayList("A", "B", "C", "D", "E");
+
+ @Test
+ public void sortTest1() {
+ TransformableList transformed = new TransformableList<>(source);
+ transformed.setComparator(Comparator.reverseOrder(), true);
+
+ assertEquals(transformed.get(4), "A");
+ assertEquals(transformed.indexOf("E"), 0);
+ assertEquals(transformed.viewToSource(0), 4);
+ assertEquals(transformed.sourceToView(0), 4);
+ }
+
+ @Test
+ public void sortAndFilterTest1() {
+ TransformableList transformed = new TransformableList<>(source);
+ transformed.setComparator(Comparator.reverseOrder(), true);
+ transformed.setPredicate(s -> s.equals("A") || s.equals("C") || s.equals("E"));
+
+ assertThrows(IndexOutOfBoundsException.class, () -> transformed.get(4));
+ assertEquals(transformed.get(1), "C");
+ assertEquals(transformed.indexOf("E"), 0);
+ assertEquals(transformed.viewToSource(1), 2);
+ assertEquals(transformed.sourceToView(1), -1);
+ }
+
+ @Test
+ public void testJavaFX1() {
+ SortedList sorted = new SortedList<>(source);
+ sorted.setComparator(Comparator.reverseOrder());
+
+ assertEquals(sorted.get(4), "A");
+ assertEquals(sorted.indexOf("E"), 0);
+ assertEquals(sorted.getSourceIndex(0), 4);
+ assertEquals(sorted.getViewIndex(0), 4);
+ }
+
+ @Test
+ public void testJavaFX2() {
+ SortedList sorted = new SortedList<>(source);
+ sorted.setComparator(Comparator.reverseOrder());
+
+ FilteredList filtered = new FilteredList<>(sorted);
+ filtered.setPredicate(s -> s.equals("A") || s.equals("C") || s.equals("E"));
+
+ assertThrows(IndexOutOfBoundsException.class, () -> filtered.get(4));
+ assertEquals(filtered.get(1), "C");
+ assertEquals(filtered.indexOf("E"), 0);
+ assertEquals(filtered.getSourceIndex(1), 2);
+ assertTrue(filtered.getViewIndex(1) < 0);
+ }
+}
diff --git a/demo/src/test/java/combobox/ComboBoxTest.java b/demo/src/test/java/combobox/ComboBoxTest.java
new file mode 100755
index 00000000..e6211c26
--- /dev/null
+++ b/demo/src/test/java/combobox/ComboBoxTest.java
@@ -0,0 +1,46 @@
+package combobox;
+
+import io.github.palexdev.materialfx.controls.MFXFilterComboBox;
+import javafx.application.Application;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.layout.HBox;
+import javafx.stage.Stage;
+import org.scenicview.ScenicView;
+
+import java.util.stream.IntStream;
+
+public class ComboBoxTest extends Application {
+
+ @Override
+ public void start(Stage primaryStage) {
+ HBox hBox = new HBox(50);
+ hBox.setAlignment(Pos.TOP_CENTER);
+ hBox.setPadding(new Insets(20, 0, 0, 0));
+
+ ObservableList s1 = FXCollections.observableArrayList();
+ ObservableList s2 = FXCollections.observableArrayList();
+ ObservableList s3 = FXCollections.observableArrayList();
+
+ IntStream.rangeClosed(0, 10).forEach(i -> s1.add("String " + i));
+ IntStream.rangeClosed(30, 40).forEach(i -> s2.add("String " + i));
+ IntStream.rangeClosed(55, 65).forEach(i -> s3.add("String " + i));
+
+ MFXFilterComboBox c1 = new MFXFilterComboBox<>(s1);
+ MFXFilterComboBox c2 = new MFXFilterComboBox<>(s2);
+ MFXFilterComboBox c3 = new MFXFilterComboBox<>(s3);
+
+ c2.valueProperty().bind(c1.valueProperty());
+ c3.getSelectionModel().bindIndexBidirectional(c1.getSelectionModel());
+
+ hBox.getChildren().addAll(c1, c2, c3);
+ Scene scene = new Scene(hBox, 800, 800);
+ primaryStage.setScene(scene);
+ primaryStage.show();
+
+ ScenicView.show(scene);
+ }
+}
diff --git a/demo/src/test/java/properties/SynchronizedBooleanTests.java b/demo/src/test/java/properties/SynchronizedBooleanTests.java
new file mode 100755
index 00000000..f8d7fd1d
--- /dev/null
+++ b/demo/src/test/java/properties/SynchronizedBooleanTests.java
@@ -0,0 +1,101 @@
+package properties;
+
+import io.github.palexdev.materialfx.beans.properties.synced.SynchronizedBooleanProperty;
+import io.github.palexdev.materialfx.utils.ExecutionUtils;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.testfx.framework.junit5.ApplicationExtension;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@ExtendWith(ApplicationExtension.class)
+public class SynchronizedBooleanTests {
+ private final BooleanProperty booleanProperty = new SimpleBooleanProperty();
+
+ @BeforeEach
+ public void setUp() {
+ booleanProperty.set(false);
+ }
+
+ @Test
+ public void testSync() {
+ SynchronizedBooleanProperty synced = new SynchronizedBooleanProperty();
+ synced.setAndWait(true, booleanProperty);
+ booleanProperty.set(true);
+ assertTrue(synced.get());
+ assertTrue(booleanProperty.get());
+ }
+
+ @Test
+ public void testBind1() {
+ AtomicBoolean changed = new AtomicBoolean(false);
+ SynchronizedBooleanProperty synced = new SynchronizedBooleanProperty();
+ synced.bind(booleanProperty);
+ synced.addListener((observable, oldValue, newValue) -> changed.set(true));
+ booleanProperty.set(true);
+ assertTrue(changed.get());
+ }
+
+ @Test
+ public void testBind2() {
+ Throwable th = null;
+
+ SynchronizedBooleanProperty synced = new SynchronizedBooleanProperty();
+ synced.bind(booleanProperty);
+
+ try {
+ synced.setAndWait(true, booleanProperty);
+ } catch (Exception ex) {
+ th = ex;
+ }
+
+ assertNotNull(th);
+ assertEquals("A bound value cannot be set!", th.getMessage());
+ assertFalse(synced.isWaiting());
+ }
+
+ @Test
+ public void testBindBidirectional() {
+ AtomicBoolean aValue = new AtomicBoolean();
+ AtomicBoolean bValue = new AtomicBoolean();
+
+ SynchronizedBooleanProperty synced = new SynchronizedBooleanProperty();
+ synced.bindBidirectional(booleanProperty);
+
+ ExecutionUtils.executeWhen(
+ synced,
+ (oldValue, newValue) -> aValue.set(newValue),
+ false,
+ (oldValue, newValue) -> newValue != null,
+ true
+ );
+ booleanProperty.set(true);
+ assertTrue(aValue.get());
+ assertTrue(booleanProperty.get());
+ synced.set(false); // Need to reset in order to fire change event
+
+ ExecutionUtils.executeWhen(
+ booleanProperty,
+ (oldValue, newValue) -> bValue.set(newValue),
+ false,
+ (oldValue, newValue) -> newValue != null,
+ true
+ );
+ synced.set(true);
+ assertTrue(bValue.get());
+ assertTrue(synced.get());
+ }
+
+ @Test
+ public void testFailSync() {
+ SynchronizedBooleanProperty synced1 = new SynchronizedBooleanProperty();
+ SynchronizedBooleanProperty synced2 = new SynchronizedBooleanProperty();
+ synced1.setAndWait(true, synced2);
+ assertThrows(IllegalArgumentException.class, () -> synced2.setAndWait(true, synced1));
+ }
+}
diff --git a/demo/src/test/java/properties/SynchronizedDoubleTests.java b/demo/src/test/java/properties/SynchronizedDoubleTests.java
new file mode 100755
index 00000000..6c67c35b
--- /dev/null
+++ b/demo/src/test/java/properties/SynchronizedDoubleTests.java
@@ -0,0 +1,82 @@
+package properties;
+
+import io.github.palexdev.materialfx.beans.properties.synced.SynchronizedDoubleProperty;
+import io.github.palexdev.materialfx.utils.ExecutionUtils;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.testfx.framework.junit5.ApplicationExtension;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@ExtendWith(ApplicationExtension.class)
+public class SynchronizedDoubleTests {
+ private final DoubleProperty doubleProperty = new SimpleDoubleProperty();
+
+ @BeforeEach
+ public void setUp() {
+ doubleProperty.set(0.0);
+ }
+
+ @Test
+ public void testSync() {
+ SynchronizedDoubleProperty synced = new SynchronizedDoubleProperty();
+ synced.setAndWait(9.9, doubleProperty);
+ doubleProperty.set(7.5);
+ assertEquals(9.9, synced.get());
+ assertEquals(7.5, doubleProperty.get());
+ }
+
+ @Test
+ public void testBind() {
+ SynchronizedDoubleProperty synced = new SynchronizedDoubleProperty();
+ synced.bind(doubleProperty);
+ doubleProperty.set(8.8);
+ assertEquals(8.8, synced.get());
+ assertEquals(8.8, doubleProperty.get());
+ }
+
+ @Test
+ public void testBindBidirectional() {
+ AtomicReference aValue = new AtomicReference<>();
+ AtomicReference bValue = new AtomicReference<>();
+
+ SynchronizedDoubleProperty synced = new SynchronizedDoubleProperty();
+ synced.bindBidirectional(doubleProperty);
+
+ ExecutionUtils.executeWhen(
+ synced,
+ (oldValue, newValue) -> aValue.set(newValue.doubleValue()),
+ false,
+ (oldValue, newValue) -> newValue != null,
+ true
+ );
+ doubleProperty.set(8.5);
+ assertEquals(8.5, aValue.get());
+ assertEquals(8.5, doubleProperty.get());
+
+ ExecutionUtils.executeWhen(
+ doubleProperty,
+ (oldValue, newValue) -> bValue.set(newValue.doubleValue()),
+ false,
+ (oldValue, newValue) -> newValue != null,
+ true
+ );
+ synced.set(7.5);
+ assertEquals(7.5, bValue.get());
+ assertEquals(7.5, synced.get());
+ }
+
+ @Test
+ public void testFailSync() {
+ SynchronizedDoubleProperty synced1 = new SynchronizedDoubleProperty();
+ SynchronizedDoubleProperty synced2 = new SynchronizedDoubleProperty();
+ synced1.setAndWait(0.36, synced2);
+ assertThrows(IllegalArgumentException.class, () -> synced2.setAndWait(0.56, synced1));
+ }
+}
diff --git a/demo/src/test/java/properties/SynchronizedFloatTests.java b/demo/src/test/java/properties/SynchronizedFloatTests.java
new file mode 100755
index 00000000..fd3b2d84
--- /dev/null
+++ b/demo/src/test/java/properties/SynchronizedFloatTests.java
@@ -0,0 +1,79 @@
+package properties;
+
+import io.github.palexdev.materialfx.beans.properties.synced.SynchronizedFloatProperty;
+import io.github.palexdev.materialfx.utils.ExecutionUtils;
+import javafx.beans.property.FloatProperty;
+import javafx.beans.property.SimpleFloatProperty;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class SynchronizedFloatTests {
+ private final FloatProperty floatProperty = new SimpleFloatProperty();
+
+ @BeforeEach
+ public void setUp() {
+ floatProperty.set(0.0F);
+ }
+
+ @Test
+ public void testSync() {
+ SynchronizedFloatProperty synced = new SynchronizedFloatProperty();
+ synced.setAndWait(0.9F, floatProperty);
+ floatProperty.set(0.7F);
+ assertEquals(0.9F, synced.get());
+ assertEquals(0.7F, floatProperty.get());
+ }
+
+ @Test
+ public void testBind() {
+ SynchronizedFloatProperty synced = new SynchronizedFloatProperty();
+ synced.bind(floatProperty);
+ floatProperty.set(0.8F);
+ assertEquals(0.8F, synced.get());
+ assertEquals(0.8F, floatProperty.get());
+ }
+
+ @Test
+ public void testBindBidirectional() {
+ AtomicReference aValue = new AtomicReference<>();
+ AtomicReference bValue = new AtomicReference<>();
+
+ SynchronizedFloatProperty synced = new SynchronizedFloatProperty();
+ synced.bindBidirectional(floatProperty);
+
+ ExecutionUtils.executeWhen(
+ synced,
+ (oldValue, newValue) -> aValue.set(newValue.floatValue()),
+ false,
+ (oldValue, newValue) -> newValue != null,
+ true
+ );
+ floatProperty.set(0.8F);
+ assertEquals(0.8F, aValue.get());
+ assertEquals(0.8F, floatProperty.get());
+
+ ExecutionUtils.executeWhen(
+ floatProperty,
+ (oldValue, newValue) -> bValue.set(newValue.floatValue()),
+ false,
+ (oldValue, newValue) -> newValue != null,
+ true
+ );
+ synced.set(0.7F);
+ assertEquals(0.7F, bValue.get());
+ assertEquals(0.7F, synced.get());
+ }
+
+ @Test
+ public void testFailSync() {
+ SynchronizedFloatProperty synced1 = new SynchronizedFloatProperty();
+ SynchronizedFloatProperty synced2 = new SynchronizedFloatProperty();
+ synced1.setAndWait(0.1F, synced2);
+ assertThrows(IllegalArgumentException.class, () -> synced2.setAndWait(0.2F, synced1));
+ }
+}
diff --git a/demo/src/test/java/properties/SynchronizedIntegerTests.java b/demo/src/test/java/properties/SynchronizedIntegerTests.java
new file mode 100755
index 00000000..f9e355c5
--- /dev/null
+++ b/demo/src/test/java/properties/SynchronizedIntegerTests.java
@@ -0,0 +1,131 @@
+package properties;
+
+import io.github.palexdev.materialfx.beans.properties.synced.SynchronizedIntegerProperty;
+import io.github.palexdev.materialfx.utils.ExecutionUtils;
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class SynchronizedIntegerTests {
+ private final IntegerProperty integerProperty = new SimpleIntegerProperty();
+
+ @BeforeEach
+ public void setUp() {
+ integerProperty.set(0);
+ }
+
+ @Test
+ public void testSync() {
+ SynchronizedIntegerProperty synced = new SynchronizedIntegerProperty();
+ synced.setAndWait(9, integerProperty);
+ integerProperty.set(7);
+ assertEquals(9, synced.get());
+ assertEquals(7, integerProperty.get());
+ }
+
+ @Test
+ public void testBind() {
+ SynchronizedIntegerProperty synced = new SynchronizedIntegerProperty();
+ synced.bind(integerProperty);
+ integerProperty.set(8);
+ assertEquals(8, synced.get());
+ assertEquals(8, integerProperty.get());
+ }
+
+ @Test
+ public void testBindBidirectional() {
+ AtomicInteger aValue = new AtomicInteger();
+ AtomicInteger bValue = new AtomicInteger();
+
+ SynchronizedIntegerProperty synced = new SynchronizedIntegerProperty();
+ synced.bindBidirectional(integerProperty);
+
+ ExecutionUtils.executeWhen(
+ synced,
+ (oldValue, newValue) -> aValue.set(newValue.intValue()),
+ false,
+ (oldValue, newValue) -> newValue != null,
+ true
+ );
+ integerProperty.set(8);
+ assertEquals(8, aValue.get());
+ assertEquals(8, integerProperty.get());
+
+ ExecutionUtils.executeWhen(
+ integerProperty,
+ (oldValue, newValue) -> bValue.set(newValue.intValue()),
+ false,
+ (oldValue, newValue) -> newValue != null,
+ true
+ );
+ synced.set(7);
+ assertEquals(7, bValue.get());
+ assertEquals(7, synced.get());
+ }
+
+ @Test
+ public void testFailSync() {
+ SynchronizedIntegerProperty synced1 = new SynchronizedIntegerProperty();
+ SynchronizedIntegerProperty synced2 = new SynchronizedIntegerProperty();
+ synced1.setAndWait(1, synced2);
+ assertThrows(IllegalArgumentException.class, () -> synced2.setAndWait(2, synced1));
+ }
+
+ @Test
+ public void testChain1() {
+ AtomicInteger a1 = new AtomicInteger();
+ AtomicInteger a2 = new AtomicInteger();
+ AtomicInteger a3 = new AtomicInteger();
+
+ SynchronizedIntegerProperty synced1 = new SynchronizedIntegerProperty();
+ SynchronizedIntegerProperty synced2 = new SynchronizedIntegerProperty();
+ synced1.addListener((observable, oldValue, newValue) -> {
+ a1.set(newValue.intValue());
+ System.out.println(newValue);
+ });
+ synced2.addListener((observable, oldValue, newValue) -> {
+ a2.set(newValue.intValue());
+ System.out.println(newValue);
+ });
+ integerProperty.addListener(new ChangeListener<>() {
+ @Override
+ public void changed(ObservableValue extends Number> observable, Number oldValue, Number newValue) {
+ a3.set(newValue.intValue());
+ System.out.println(newValue);
+ integerProperty.removeListener(this);
+ }
+ });
+
+ synced1.setAndWait(8, synced2);
+ synced2.setAndWait(10, integerProperty);
+ integerProperty.set(12);
+
+ assertEquals(8, a1.get());
+ assertEquals(10, a2.get());
+ assertEquals(12, a3.get());
+ }
+
+ @Test
+ public void testChain2() {
+ SynchronizedIntegerProperty synced1 = new SynchronizedIntegerProperty();
+ SynchronizedIntegerProperty synced2 = new SynchronizedIntegerProperty();
+ synced1.setAndWait(8, synced2);
+ assertThrows(IllegalArgumentException.class, () -> synced2.setAndWait(10, synced1));
+ }
+
+ @Test
+ public void testOverrideWait() {
+ SynchronizedIntegerProperty synced1 = new SynchronizedIntegerProperty();
+ SynchronizedIntegerProperty synced2 = new SynchronizedIntegerProperty();
+ synced1.setAndWait(8, synced2);
+ assertThrows(IllegalStateException.class, () -> synced1.setAndWait(10, integerProperty));
+ }
+}
diff --git a/demo/src/test/java/properties/SynchronizedLongTests.java b/demo/src/test/java/properties/SynchronizedLongTests.java
new file mode 100755
index 00000000..7ef2aedf
--- /dev/null
+++ b/demo/src/test/java/properties/SynchronizedLongTests.java
@@ -0,0 +1,79 @@
+package properties;
+
+import io.github.palexdev.materialfx.beans.properties.synced.SynchronizedLongProperty;
+import io.github.palexdev.materialfx.utils.ExecutionUtils;
+import javafx.beans.property.LongProperty;
+import javafx.beans.property.SimpleLongProperty;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class SynchronizedLongTests {
+ private final LongProperty longProperty = new SimpleLongProperty();
+
+ @BeforeEach
+ public void setUp() {
+ longProperty.set(0L);
+ }
+
+ @Test
+ public void testSync() {
+ SynchronizedLongProperty synced = new SynchronizedLongProperty();
+ synced.setAndWait(9L, longProperty);
+ longProperty.set(7L);
+ assertEquals(9L, synced.get());
+ assertEquals(7L, longProperty.get());
+ }
+
+ @Test
+ public void testBind() {
+ SynchronizedLongProperty synced = new SynchronizedLongProperty();
+ synced.bind(longProperty);
+ longProperty.set(8L);
+ assertEquals(8L, synced.get());
+ assertEquals(8L, longProperty.get());
+ }
+
+ @Test
+ public void testBindBidirectional() {
+ AtomicLong aValue = new AtomicLong();
+ AtomicLong bValue = new AtomicLong();
+
+ SynchronizedLongProperty synced = new SynchronizedLongProperty();
+ synced.bindBidirectional(longProperty);
+
+ ExecutionUtils.executeWhen(
+ synced,
+ (oldValue, newValue) -> aValue.set(newValue.longValue()),
+ false,
+ (oldValue, newValue) -> newValue != null,
+ true
+ );
+ longProperty.set(8L);
+ assertEquals(8L, aValue.get());
+ assertEquals(8L, longProperty.get());
+
+ ExecutionUtils.executeWhen(
+ longProperty,
+ (oldValue, newValue) -> bValue.set(newValue.longValue()),
+ false,
+ (oldValue, newValue) -> newValue != null,
+ true
+ );
+ synced.set(7L);
+ assertEquals(7L, bValue.get());
+ assertEquals(7L, synced.get());
+ }
+
+ @Test
+ public void testFailSync() {
+ SynchronizedLongProperty synced1 = new SynchronizedLongProperty();
+ SynchronizedLongProperty synced2 = new SynchronizedLongProperty();
+ synced1.setAndWait(1L, synced2);
+ assertThrows(IllegalArgumentException.class, () -> synced2.setAndWait(2L, synced1));
+ }
+}
diff --git a/demo/src/test/java/properties/SynchronizedObjectTests.java b/demo/src/test/java/properties/SynchronizedObjectTests.java
new file mode 100755
index 00000000..c4945a41
--- /dev/null
+++ b/demo/src/test/java/properties/SynchronizedObjectTests.java
@@ -0,0 +1,79 @@
+package properties;
+
+import io.github.palexdev.materialfx.beans.properties.synced.SynchronizedObjectProperty;
+import io.github.palexdev.materialfx.demo.model.SimplePerson;
+import io.github.palexdev.materialfx.utils.ExecutionUtils;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class SynchronizedObjectTests {
+ private final ObjectProperty objectProperty = new SimpleObjectProperty<>();
+
+ @BeforeEach
+ public void setUp() {
+ objectProperty.set(null);
+ }
+
+ @Test
+ public void testSync() {
+ SynchronizedObjectProperty synced = new SynchronizedObjectProperty<>();
+ synced.setAndWait(new SimplePerson("Jack"), objectProperty);
+ objectProperty.set(new SimplePerson("Rose"));
+ assertEquals("Jack", synced.get().getName());
+ assertEquals("Rose", objectProperty.get().getName());
+ }
+
+ @Test
+ public void testBind() {
+ SynchronizedObjectProperty synced = new SynchronizedObjectProperty<>();
+ synced.bind(objectProperty);
+ objectProperty.set(new SimplePerson("Mark"));
+ assertEquals("Mark", synced.get().getName());
+ assertEquals("Mark", objectProperty.get().getName());
+ }
+
+ @Test
+ public void testBindBidirectional() {
+ AtomicReference aValue = new AtomicReference<>();
+ AtomicReference bValue = new AtomicReference<>();
+
+ SynchronizedObjectProperty synced = new SynchronizedObjectProperty<>();
+ synced.bindBidirectional(objectProperty);
+
+ ExecutionUtils.executeWhen(
+ synced,
+ (oldValue, newValue) -> aValue.set(newValue),
+ false,
+ (oldValue, newValue) -> newValue != null,
+ true
+ );
+ objectProperty.set(new SimplePerson("Jack"));
+ assertEquals("Jack", aValue.get().getName());
+ assertEquals("Jack", objectProperty.get().getName());
+
+ ExecutionUtils.executeWhen(
+ objectProperty,
+ (oldValue, newValue) -> bValue.set(newValue),
+ false,
+ (oldValue, newValue) -> newValue != null,
+ true
+ );
+ synced.set(new SimplePerson("Rose"));
+ assertEquals("Rose", bValue.get().getName());
+ assertEquals("Rose", synced.get().getName());
+ }
+
+ @Test
+ public void testFailSync() {
+ SynchronizedObjectProperty synced1 = new SynchronizedObjectProperty<>();
+ SynchronizedObjectProperty synced2 = new SynchronizedObjectProperty<>();
+ synced1.setAndWait(new SimplePerson("Mark"), synced2);
+ assertThrows(IllegalArgumentException.class, () -> synced2.setAndWait(new SimplePerson("Leia"), synced1));
+ }
+}
diff --git a/demo/src/test/java/properties/SynchronizedStringTests.java b/demo/src/test/java/properties/SynchronizedStringTests.java
new file mode 100755
index 00000000..396b8170
--- /dev/null
+++ b/demo/src/test/java/properties/SynchronizedStringTests.java
@@ -0,0 +1,79 @@
+package properties;
+
+import io.github.palexdev.materialfx.beans.properties.synced.SynchronizedStringProperty;
+import io.github.palexdev.materialfx.utils.ExecutionUtils;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class SynchronizedStringTests {
+ private final StringProperty stringProperty = new SimpleStringProperty();
+
+ @BeforeEach
+ public void setUp() {
+ stringProperty.set(null);
+ }
+
+ @Test
+ public void testSync() {
+ SynchronizedStringProperty synced = new SynchronizedStringProperty();
+ synced.setAndWait("SString", stringProperty);
+ stringProperty.set("PString");
+ assertEquals("SString", synced.get());
+ assertEquals("PString", stringProperty.get());
+ }
+
+ @Test
+ public void testBind() {
+ SynchronizedStringProperty synced = new SynchronizedStringProperty();
+ synced.bind(stringProperty);
+ stringProperty.set("BString");
+ assertEquals("BString", synced.get());
+ assertEquals("BString", stringProperty.get());
+ }
+
+ @Test
+ public void testBindBidirectional() {
+ AtomicReference aValue = new AtomicReference<>();
+ AtomicReference bValue = new AtomicReference<>();
+
+ SynchronizedStringProperty synced = new SynchronizedStringProperty();
+ synced.bindBidirectional(stringProperty);
+
+ ExecutionUtils.executeWhen(
+ synced,
+ (oldValue, newValue) -> aValue.set(newValue),
+ false,
+ (oldValue, newValue) -> newValue != null,
+ true
+ );
+ stringProperty.set("PString");
+ assertEquals("PString", aValue.get());
+ assertEquals("PString", stringProperty.get());
+
+ ExecutionUtils.executeWhen(
+ stringProperty,
+ (oldValue, newValue) -> bValue.set(newValue),
+ false,
+ (oldValue, newValue) -> newValue != null,
+ true
+ );
+ synced.set("SString");
+ assertEquals("SString", bValue.get());
+ assertEquals("SString", synced.get());
+ }
+
+ @Test
+ public void testFailSync() {
+ SynchronizedStringProperty synced1 = new SynchronizedStringProperty();
+ SynchronizedStringProperty synced2 = new SynchronizedStringProperty();
+ synced1.setAndWait("SS1", synced2);
+ assertThrows(IllegalArgumentException.class, () -> synced2.setAndWait("SS2", synced1));
+ }
+}
diff --git a/demo/src/test/java/prototipes/FloatingTextPrototype.java b/demo/src/test/java/prototipes/FloatingTextPrototype.java
new file mode 100755
index 00000000..bddd9c8f
--- /dev/null
+++ b/demo/src/test/java/prototipes/FloatingTextPrototype.java
@@ -0,0 +1,135 @@
+package prototipes;
+
+import io.github.palexdev.materialfx.controls.BoundLabel;
+import io.github.palexdev.materialfx.effects.ConsumerTransition;
+import io.github.palexdev.materialfx.effects.Interpolators;
+import io.github.palexdev.materialfx.factories.InsetsFactory;
+import io.github.palexdev.materialfx.utils.AnimationUtils.KeyFrames;
+import io.github.palexdev.materialfx.utils.AnimationUtils.ParallelBuilder;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.control.Labeled;
+import javafx.scene.control.Skin;
+import javafx.scene.control.SkinBase;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.VBox;
+import javafx.scene.transform.Scale;
+
+public class FloatingTextPrototype extends Labeled {
+ private final StringProperty promptText = new SimpleStringProperty("Floating Text");
+
+ public FloatingTextPrototype() {
+ this("");
+ }
+
+ public FloatingTextPrototype(String text) {
+ this(text, null);
+ }
+
+ public FloatingTextPrototype(String text, Node graphic) {
+ super(text, graphic);
+ initialize();
+ }
+
+ private void initialize() {
+ setPadding(InsetsFactory.of(5, 3, 5, 3));
+ }
+
+ public String getPromptText() {
+ return promptText.get();
+ }
+
+ public StringProperty promptTextProperty() {
+ return promptText;
+ }
+
+ public void setPromptText(String promptText) {
+ this.promptText.set(promptText);
+ }
+
+ @Override
+ protected Skin> createDefaultSkin() {
+ return new FloatingTextPrototypeSkin(this);
+ }
+}
+
+class FloatingTextPrototypeSkin extends SkinBase {
+ private final VBox container;
+ private final BoundLabel text;
+ private final Label promptText;
+
+ private final double scaleMultiplier = 0.85;
+ private final Scale scale = new Scale(1, 1, 0, 0);
+
+ private boolean floating = true;
+
+ public FloatingTextPrototypeSkin(FloatingTextPrototype control) {
+ super(control);
+
+ text = new BoundLabel(control);
+ text.setStyle("-fx-border-color: red");
+
+ promptText = new Label("Look! It's floating!");
+ promptText.setStyle("-fx-border-color: blue");
+ promptText.getTransforms().add(scale);
+
+ container = new VBox(promptText, text);
+ container.setAlignment(Pos.CENTER_LEFT);
+ getChildren().setAll(container);
+
+ setBehavior();
+ }
+
+ private void setBehavior() {
+ FloatingTextPrototype control = getSkinnable();
+
+ control.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> {
+ if (floating) {
+ double promptH = promptText.getHeight();
+ double y = (control.getHeight() / 2) - (promptH / 2) + snappedTopInset();
+ double translateY = y - promptText.getBoundsInParent().getMinY();
+ double mul = scale.getY();
+ ParallelBuilder.build()
+ .add(
+ ConsumerTransition.of(frac -> promptText.setTranslateY((translateY / 2) * frac * mul), 250)
+ .setOnFinishedFluent(end -> promptText.setTranslateY(snapPositionY(promptText.getTranslateY())))
+ .setInterpolatorFluent(Interpolators.INTERPOLATOR_V1)
+ )
+ .add(
+ KeyFrames.of(250, scale.xProperty(), 1, Interpolators.INTERPOLATOR_V1),
+ KeyFrames.of(250, scale.yProperty(), 1, Interpolators.INTERPOLATOR_V1)
+ )
+ .setOnFinished(end -> System.out.println("Y: " + promptText.getBoundsInParent().getMinY()))
+ .getAnimation().play();
+ floating = false;
+ } else {
+ double initialTranslateY = promptText.getTranslateY();
+ ParallelBuilder.build()
+ .add(
+ ConsumerTransition.of(frac -> promptText.setTranslateY(initialTranslateY - (initialTranslateY * frac)), 250)
+ .setInterpolatorFluent(Interpolators.INTERPOLATOR_V1)
+ )
+ .add(
+ KeyFrames.of(250, scale.xProperty(), scaleMultiplier, Interpolators.INTERPOLATOR_V1),
+ KeyFrames.of(250, scale.yProperty(), scaleMultiplier, Interpolators.INTERPOLATOR_V1)
+ )
+ .setOnFinished(end -> System.out.println("Scaled Y: " + promptText.getBoundsInParent().getMinY()))
+ .getAnimation().play();
+ floating = true;
+ }
+ });
+ }
+
+ @Override
+ protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
+ return getSkinnable().prefWidth(-1);
+ }
+
+ @Override
+ protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
+ return getSkinnable().prefHeight(-1);
+ }
+}
diff --git a/demo/src/test/java/selection/SingleSelectionModelTests.java b/demo/src/test/java/selection/SingleSelectionModelTests.java
new file mode 100755
index 00000000..9d2728a9
--- /dev/null
+++ b/demo/src/test/java/selection/SingleSelectionModelTests.java
@@ -0,0 +1,530 @@
+package selection;
+
+import io.github.palexdev.materialfx.bindings.BiBindingManager;
+import io.github.palexdev.materialfx.bindings.BindingManager;
+import io.github.palexdev.materialfx.demo.model.SimplePerson;
+import io.github.palexdev.materialfx.selection.SingleSelectionModel;
+import javafx.beans.property.*;
+import javafx.collections.FXCollections;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.testfx.framework.junit5.ApplicationExtension;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@ExtendWith(ApplicationExtension.class)
+public class SingleSelectionModelTests {
+ private final ListProperty people1 = new SimpleListProperty<>(
+ FXCollections.observableArrayList(
+ new SimplePerson("Jack"),
+ new SimplePerson("Mark"),
+ new SimplePerson("Linda"),
+ new SimplePerson("Marty"),
+ new SimplePerson("Lily"),
+ new SimplePerson("Sam"))
+ );
+
+ private final ListProperty people2 = new SimpleListProperty<>(
+ FXCollections.observableArrayList(
+ new SimplePerson("Mark"),
+ new SimplePerson("Roberto"),
+ new SimplePerson("Alex"),
+ new SimplePerson("Samantha"),
+ new SimplePerson("Elyse"),
+ new SimplePerson("Mark"),
+ new SimplePerson("Sam"),
+ new SimplePerson("Jennifer"),
+ new SimplePerson("Alex"),
+ new SimplePerson("Rocky"),
+ new SimplePerson("Phil"))
+ );
+
+ private final SingleSelectionModel model1 = new SingleSelectionModel<>(people1);
+ private final SingleSelectionModel model2 = new SingleSelectionModel<>(people2);
+
+ @AfterEach
+ public void checkManagers() {
+ BindingManager.instance().dispose();
+ BiBindingManager.instance().dispose();
+ }
+
+ @AfterAll
+ static void dispose() {
+ assertEquals(0, BindingManager.instance().size());
+ assertEquals(0, BiBindingManager.instance().size());
+ }
+
+ @Test
+ public void testIndexSelection1() {
+ model1.selectIndex(0);
+ assertEquals(0, model1.getSelectedIndex());
+ assertEquals("Jack", model1.getSelectedItem().getName());
+ }
+
+ @Test
+ public void testIndexSelection2() {
+ assertThrows(IndexOutOfBoundsException.class, () -> model1.selectIndex(10));
+ assertEquals(-1, model1.getSelectedIndex());
+ }
+
+ @Test
+ public void testIndexSelection3() {
+ assertThrows(IndexOutOfBoundsException.class, () -> model1.selectIndex(-1));
+ assertEquals(-1, model1.getSelectedIndex());
+ }
+
+ @Test
+ public void testItemSelection1() {
+ model1.selectItem(new SimplePerson("Mark"));
+ assertEquals(1, model1.getSelectedIndex());
+ assertEquals("Mark", model1.getSelectedItem().getName());
+ }
+
+ @Test
+ public void testItemSelection2() {
+ assertThrows(IllegalArgumentException.class, () -> model1.selectItem(new SimplePerson("Unexisting")));
+ }
+
+ @Test
+ public void testBindIndexProperty1() {
+ IntegerProperty property = new SimpleIntegerProperty();
+ model1.bindIndex(property, i -> model1.getUnmodifiableItems().get(i));
+ property.set(2);
+ assertEquals(2, model1.getSelectedIndex());
+ assertEquals("Linda", model1.getSelectedItem().getName());
+ }
+
+ @Test
+ public void testBindIndexProperty2() {
+ IntegerProperty property = new SimpleIntegerProperty();
+ model1.bindIndex(property, i -> model1.getUnmodifiableItems().get(i));
+ assertThrows(IllegalStateException.class, () -> model1.selectIndex(2));
+ }
+
+ @Test
+ public void testBindIndexProperty3() {
+ AtomicReference ex = new AtomicReference<>();
+ IntegerProperty property = new SimpleIntegerProperty();
+ model1.bindIndex(property, i -> {
+ try {
+ return model1.getUnmodifiableItems().get(i);
+ } catch (Exception exception) {
+ ex.set(exception);
+ }
+ return null;
+ });
+ property.set(6);
+ assertNotNull(ex.get());
+ }
+
+ @Test
+ public void testBindIndexProperty4() {
+ model1.selectIndex(0);
+
+ AtomicReference ex = new AtomicReference<>();
+ Thread.setDefaultUncaughtExceptionHandler((t, e) -> ex.set(e));
+
+ IntegerProperty property = new SimpleIntegerProperty();
+ model1.bindIndex(property, i -> model1.getUnmodifiableItems().get(i));
+ property.set(-1);
+
+ assertNotNull(ex.get());
+ assertEquals(0, model1.getSelectedIndex());
+ assertEquals("Jack", model1.getSelectedItem().getName());
+ }
+
+ @Test
+ public void testBindIndex1() {
+ model1.bindIndex(model2);
+ model2.selectIndex(2);
+
+ assertEquals(2, model1.getSelectedIndex());
+ assertEquals(2, model2.getSelectedIndex());
+
+ assertEquals("Linda", model1.getSelectedItem().getName());
+ assertEquals("Alex", model2.getSelectedItem().getName());
+ }
+
+ @Test
+ public void testBindIndex2() {
+ model1.bindIndex(model2);
+ assertThrows(IllegalStateException.class, () -> model1.selectIndex(2));
+ }
+
+ @Test
+ public void testBindIndex3() {
+ AtomicReference ex = new AtomicReference<>();
+ Thread.setDefaultUncaughtExceptionHandler((t, e) -> ex.set(e));
+
+ model1.bindIndex(model2);
+ model2.selectIndex(8);
+
+ assertEquals(-1, model1.getSelectedIndex());
+ assertNull(model1.getSelectedItem());
+ assertEquals(8, model2.getSelectedIndex());
+ assertEquals("Alex", model2.getSelectedItem().getName());
+
+ assertNotNull(ex.get());
+ }
+
+ @Test
+ public void testBindIndex4() {
+ model1.bindIndex(model2);
+ model2.selectIndex(0);
+ assertThrows(IndexOutOfBoundsException.class, () -> model2.selectIndex(-1));
+
+ assertEquals(0, model1.getSelectedIndex());
+ assertEquals("Jack", model1.getSelectedItem().getName());
+ assertEquals(0, model2.getSelectedIndex());
+ assertEquals("Mark", model2.getSelectedItem().getName());
+ }
+
+ @Test
+ public void testBindIndexBidirectionalProperty1() {
+ IntegerProperty property = new SimpleIntegerProperty();
+ model1.bindIndexBidirectional(property, i -> model1.getUnmodifiableItems().get(i), (clearing, i, other) -> other.setValue(i));
+
+ property.set(1);
+ assertEquals(1, model1.getSelectedIndex());
+ assertEquals("Mark", model1.getSelectedItem().getName());
+
+ model1.selectIndex(5);
+ assertEquals("Sam", model1.getSelectedItem().getName());
+ assertEquals(5, property.get());
+
+ model1.selectItem(new SimplePerson("Lily"));
+ assertEquals(4, model1.getSelectedIndex());
+ assertEquals(4, property.get());
+ }
+
+ @Test
+ public void testBindIndexBidirectionalProperty2() {
+ IntegerProperty property = new SimpleIntegerProperty();
+ model1.bindIndexBidirectional(property, i -> model1.getUnmodifiableItems().get(i), (clearing, i, other) -> other.setValue(i));
+
+ property.set(1);
+ assertEquals(1, model1.getSelectedIndex());
+ assertEquals("Mark", model1.getSelectedItem().getName());
+
+ property.set(-1);
+ assertEquals(-1, property.get());
+ assertEquals(-1, model1.getSelectedIndex());
+ assertNull(model1.getSelectedItem());
+ }
+
+ @Test
+ public void testBindIndexBidirectionalProperty3() {
+ IntegerProperty property = new SimpleIntegerProperty();
+ model1.bindIndexBidirectional(property, i -> model1.getUnmodifiableItems().get(i), (clearing, i, other) -> other.setValue(i));
+
+ assertThrows(IndexOutOfBoundsException.class, () -> model1.selectIndex(8));
+ assertEquals(0, property.get());
+ assertEquals(0, model1.getSelectedIndex());
+ assertEquals("Jack", model1.getSelectedItem().getName());
+ }
+
+ @Test
+ public void testBindIndexBidirectional1() {
+ model1.bindIndexBidirectional(model2);
+
+ model2.selectIndex(1);
+ assertEquals("Mark", model1.getSelectedItem().getName());
+ assertEquals("Roberto", model2.getSelectedItem().getName());
+
+ model1.selectIndex(3);
+ assertEquals("Marty", model1.getSelectedItem().getName());
+ assertEquals("Samantha", model2.getSelectedItem().getName());
+ }
+
+ @Test
+ public void testBindIndexBidirectional2() {
+ AtomicReference reference = new AtomicReference<>();
+ Thread.setDefaultUncaughtExceptionHandler((t, e) -> reference.set(e));
+ model1.bindIndexBidirectional(model2);
+
+ model2.selectIndex(8);
+ assertNotNull(reference.get());
+ assertEquals("Alex", model2.getSelectedItem().getName());
+ assertEquals(-1, model1.getSelectedIndex());
+ assertNull(model1.getSelectedItem());
+
+ model1.selectIndex(0);
+ assertEquals("Jack", model1.getSelectedItem().getName());
+ assertEquals("Mark", model2.getSelectedItem().getName());
+ }
+
+ @Test
+ public void testBindIndexBidirectional3() {
+ AtomicReference reference = new AtomicReference<>();
+ Thread.setDefaultUncaughtExceptionHandler((t, e) -> reference.set(e));
+ model2.bindIndexBidirectional(model1);
+
+ model1.selectIndex(0);
+ assertEquals("Jack", model1.getSelectedItem().getName());
+ assertEquals("Mark", model2.getSelectedItem().getName());
+
+ model2.selectIndex(8);
+ assertNotNull(reference.get());
+ assertEquals("Alex", model2.getSelectedItem().getName());
+ assertEquals(0, model1.getSelectedIndex());
+ assertEquals("Jack", model1.getSelectedItem().getName());
+ }
+
+ @Test
+ public void testBindItemProperty1() {
+ ObjectProperty property = new SimpleObjectProperty<>();
+ model1.bindItem(property, simplePerson -> model1.getUnmodifiableItems().indexOf(simplePerson));
+
+ property.set(new SimplePerson("Mark"));
+ assertEquals(1, model1.getSelectedIndex());
+ assertEquals("Mark", model1.getSelectedItem().getName());
+ }
+
+ @Test
+ public void testBindItemProperty2() {
+ ObjectProperty property = new SimpleObjectProperty<>();
+ model2.bindItem(property, simplePerson -> model2.getUnmodifiableItems().indexOf(simplePerson));
+
+ property.set(new SimplePerson("Alex"));
+ assertEquals(2, model2.getSelectedIndex());
+ assertEquals("Alex", model2.getSelectedItem().getName());
+ }
+
+ @Test
+ public void testBindItemProperty3() {
+ AtomicReference reference = new AtomicReference<>();
+ Thread.setDefaultUncaughtExceptionHandler((t, e) -> reference.set(e));
+
+ ObjectProperty property = new SimpleObjectProperty<>();
+ model2.bindItem(property, simplePerson -> model2.getUnmodifiableItems().indexOf(simplePerson));
+
+ property.set(new SimplePerson("Unexisting"));
+ assertEquals("Unexisting", property.get().getName());
+ assertEquals(-1, model2.getSelectedIndex());
+ assertNull(model2.getSelectedItem());
+ assertNotNull(reference.get());
+ }
+
+ @Test
+ public void testBindItem1() {
+ model1.bindItem(model2);
+ model2.selectItem(new SimplePerson("Mark"));
+ assertEquals(1, model1.getSelectedIndex());
+ assertEquals(0, model2.getSelectedIndex());
+ }
+
+ @Test
+ public void testBindItem2() {
+ model1.bindItem(model2);
+ assertThrows(IllegalStateException.class, () -> model1.selectItem(new SimplePerson("Mark")));
+ }
+
+ @Test
+ public void testBindItem3() {
+ AtomicReference reference = new AtomicReference<>();
+ Thread.setDefaultUncaughtExceptionHandler((t, e) -> reference.set(e));
+
+ model1.bindItem(model2);
+ model2.selectItem(new SimplePerson("Alex"));
+ assertEquals(-1, model1.getSelectedIndex());
+ assertNull(model1.getSelectedItem());
+ assertEquals(2, model2.getSelectedIndex());
+ assertEquals("Alex", model2.getSelectedItem().getName());
+ assertNotNull(reference.get());
+ }
+
+ @Test
+ public void testBindItemPropertyBidirectional1() {
+ ObjectProperty property = new SimpleObjectProperty<>();
+ model1.bindItemBidirectional(property, simplePerson -> model1.getUnmodifiableItems().indexOf(simplePerson), (clearing, simplePerson, other) -> other.setValue(simplePerson));
+
+ property.set(new SimplePerson("Linda"));
+ assertEquals(2, model1.getSelectedIndex());
+ assertEquals("Linda", model1.getSelectedItem().getName());
+ }
+
+ @Test
+ public void testBindItemPropertyBidirectional2() {
+ AtomicReference reference = new AtomicReference<>();
+ Thread.setDefaultUncaughtExceptionHandler((t, e) -> reference.set(e));
+
+ ObjectProperty property = new SimpleObjectProperty<>();
+ model1.bindItemBidirectional(property, simplePerson -> model1.getUnmodifiableItems().indexOf(simplePerson), (clearing, simplePerson, other) -> other.setValue(simplePerson));
+
+ property.set(new SimplePerson("Unexisting"));
+ assertNotNull(reference.get());
+ }
+
+ @Test
+ public void testBindItemPropertyBidirectional3() {
+ ObjectProperty property = new SimpleObjectProperty<>();
+ model1.bindItemBidirectional(property, simplePerson -> model1.getUnmodifiableItems().indexOf(simplePerson), (clearing, simplePerson, other) -> other.setValue(simplePerson));
+
+ property.set(new SimplePerson("Mark"));
+ assertEquals(1, model1.getSelectedIndex());
+
+ model1.selectItem(new SimplePerson("Linda"));
+ assertEquals(2, model1.getSelectedIndex());
+ assertEquals("Linda", model1.getSelectedItem().getName());
+ assertEquals("Linda", property.get().getName());
+ }
+
+ @Test
+ public void testBindItemBidirectional1() {
+ model1.bindItemBidirectional(model2);
+
+ model2.selectItem(new SimplePerson("Mark"));
+ assertEquals(1, model1.getSelectedIndex());
+ assertEquals(0, model2.getSelectedIndex());
+
+ model1.selectItem(new SimplePerson("Sam"));
+ assertEquals(5, model1.getSelectedIndex());
+ assertEquals(6, model2.getSelectedIndex());
+ }
+
+ @Test
+ public void testBindItemBidirectional2() {
+ AtomicReference reference = new AtomicReference<>();
+ Thread.setDefaultUncaughtExceptionHandler((t, e) -> reference.set(e));
+ model1.bindItemBidirectional(model2);
+
+ model1.selectItem(new SimplePerson("Sam"));
+ assertEquals(5, model1.getSelectedIndex());
+ assertEquals(6, model2.getSelectedIndex());
+
+ model2.selectItem(new SimplePerson("Alex"));
+ assertNotNull(reference.get());
+ assertEquals(5, model1.getSelectedIndex());
+ assertEquals("Sam", model1.getSelectedItem().getName());
+ assertEquals(2, model2.getSelectedIndex());
+ assertEquals("Alex", model2.getSelectedItem().getName());
+ }
+
+ @Test
+ public void testBindItemBidirectional3() {
+ AtomicReference reference = new AtomicReference<>();
+ Thread.setDefaultUncaughtExceptionHandler((t, e) -> reference.set(e));
+ model1.bindItemBidirectional(model2);
+
+ model2.selectItem(new SimplePerson("Alex"));
+ assertNotNull(reference.get());
+ assertEquals(-1, model1.getSelectedIndex());
+ assertNull(model1.getSelectedItem());
+ assertEquals(2, model2.getSelectedIndex());
+ assertEquals("Alex", model2.getSelectedItem().getName());
+
+ model1.selectItem(new SimplePerson("Sam"));
+ assertEquals(5, model1.getSelectedIndex());
+ assertEquals(6, model2.getSelectedIndex());
+ }
+
+ @Test
+ public void testClearSelection1() {
+ model1.selectIndex(0);
+ model1.clearSelection();
+ assertEquals(-1, model1.getSelectedIndex());
+ assertNull(model1.getSelectedItem());
+ }
+
+ @Test
+ public void testClearSelection2() {
+ model1.bindIndexBidirectional(model2);
+ model1.selectIndex(0);
+ model1.clearSelection();
+
+ assertEquals(-1, model1.getSelectedIndex());
+ assertNull(model1.getSelectedItem());
+ assertEquals(-1, model2.getSelectedIndex());
+ assertNull(model2.getSelectedItem());
+ }
+
+ @Test
+ public void testClearSelection3() {
+ model1.bindIndex(model2);
+ model2.selectIndex(0);
+ assertThrows(IllegalStateException.class, model1::clearSelection);
+ }
+
+ @Test
+ public void testClearSelection4() {
+ model1.bindItemBidirectional(model2);
+ model2.selectItem(new SimplePerson("Mark"));
+
+ assertEquals(1, model1.getSelectedIndex());
+ assertEquals("Mark", model1.getSelectedItem().getName());
+ assertEquals(0, model2.getSelectedIndex());
+ assertEquals("Mark", model2.getSelectedItem().getName());
+
+ model1.clearSelection();
+
+ assertEquals(-1, model1.getSelectedIndex());
+ assertNull(model1.getSelectedItem());
+ assertEquals(-1, model2.getSelectedIndex());
+ assertNull(model2.getSelectedItem());
+ }
+
+ @Test
+ public void testClearSelection5() {
+ IntegerProperty property = new SimpleIntegerProperty();
+ model1.bindIndexBidirectional(property, i -> model1.getUnmodifiableItems().get(i), (clearing, i, otherProperty) -> property.set(i));
+ model1.selectIndex(1);
+
+ assertEquals(1, property.get());
+ assertEquals("Mark", model1.getSelectedItem().getName());
+
+ model1.clearSelection();
+ assertEquals(-1, property.get());
+ assertEquals(-1, model1.getSelectedIndex());
+ assertNull(model1.getSelectedItem());
+
+ model1.selectIndex(1);
+ property.set(-1);
+ assertEquals(-1, property.get());
+ assertEquals(-1, model1.getSelectedIndex());
+ assertNull(model1.getSelectedItem());
+ }
+
+ @Test
+ public void testClearSelection6() {
+ ObjectProperty property = new SimpleObjectProperty<>();
+ model1.bindItemBidirectional(property, v -> model1.getUnmodifiableItems().indexOf(v), (clearing, v, otherProperty) -> property.set(v));
+ model1.selectIndex(1);
+
+ assertEquals("Mark", property.get().getName());
+ assertEquals(1, model1.getSelectedIndex());
+
+ model1.clearSelection();
+ assertNull(property.get());
+ assertEquals(-1, model1.getSelectedIndex());
+ assertNull(model1.getSelectedItem());
+
+ model1.selectIndex(1);
+ property.set(null);
+ assertNull(property.get());
+ assertEquals(-1, model1.getSelectedIndex());
+ assertNull(model1.getSelectedItem());
+ }
+
+ @Test
+ public void testClearSelection7() {
+ IntegerProperty property = new SimpleIntegerProperty();
+ model1.bindIndexBidirectional(
+ property,
+ i -> model1.getUnmodifiableItems().get(i),
+ (clearing, i, otherProperty) -> property.set(i)
+ );
+ model1.selectIndex(1);
+
+ assertEquals(1, property.get());
+ assertEquals("Mark", model1.getSelectedItem().getName());
+
+ property.set(-1);
+ assertEquals(-1, property.get());
+ assertEquals(-1, model1.getSelectedIndex());
+ assertNull(model1.getSelectedItem());
+ }
+}
diff --git a/demo/src/test/java/treeview/NumberUtilsTest.java b/demo/src/test/java/treeview/NumberUtilsTest.java
deleted file mode 100644
index f351584c..00000000
--- a/demo/src/test/java/treeview/NumberUtilsTest.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package treeview;
-
-import io.github.palexdev.materialfx.beans.NumberRange;
-import io.github.palexdev.materialfx.utils.NumberUtils;
-import junit.framework.TestCase;
-
-public class NumberUtilsTest extends TestCase {
- private double val;
- private NumberRange fromRange;
- private NumberRange toRange;
-
- public void testMap1() {
- val = 0;
- fromRange = NumberRange.of(-50.0, 100.0);
- toRange = NumberRange.of(0.0, 100.0);
-
- double mapped = NumberUtils.mapOneRangeToAnother(val, fromRange, toRange, 1);
- assertEquals(33.3, mapped);
- }
-
- public void testMap2() {
- val = -50;
- fromRange = NumberRange.of(-50.0, 100.0);
- toRange = NumberRange.of(0.0, 100.0);
-
- double mapped = NumberUtils.mapOneRangeToAnother(val, fromRange, toRange, 1);
- assertEquals(0.0, mapped);
- }
-
- public void testMap3() {
- val = 100;
- fromRange = NumberRange.of(-50.0, 100.0);
- toRange = NumberRange.of(0.0, 100.0);
-
- double mapped = NumberUtils.mapOneRangeToAnother(val, fromRange, toRange, 1);
- assertEquals(100.0, mapped);
- }
-
- public void testMap4() {
- val = -10;
- fromRange = NumberRange.of(-50.0, 100.0);
- toRange = NumberRange.of(0.0, 100.0);
-
- double mapped = NumberUtils.mapOneRangeToAnother(val, fromRange, toRange, 1);
- assertEquals(26.7, mapped);
- }
-
- public void testMap5() {
- val = 0;
- fromRange = NumberRange.of(-100.0, 100.0);
- toRange = NumberRange.of(0.0, 100.0);
-
- double mapped = NumberUtils.mapOneRangeToAnother(val, fromRange, toRange, 1);
- assertEquals(50.0, mapped);
- }
-
- public void testClamp1() {
- val = 10;
-
- double clamped = NumberUtils.clamp(val, 0, 100);
- assertEquals(10.0, clamped);
- }
-
- public void testClamp2() {
- val = 50;
-
- double clamped = NumberUtils.clamp(val, 0, 100);
- assertEquals(50.0, clamped);
- }
-
- public void testClamp3() {
- val = 102;
-
- double clamped = NumberUtils.clamp(val, 0, 100);
- assertEquals(100.0, clamped);
- }
-
- public void testClamp4() {
- val = -9;
-
- double clamped = NumberUtils.clamp(val, 0, 100);
- assertEquals(0.0, clamped);
- }
-}
diff --git a/demo/src/test/java/treeview/NumberUtilsTests.java b/demo/src/test/java/treeview/NumberUtilsTests.java
new file mode 100755
index 00000000..8b8af3ad
--- /dev/null
+++ b/demo/src/test/java/treeview/NumberUtilsTests.java
@@ -0,0 +1,95 @@
+package treeview;
+
+import io.github.palexdev.materialfx.beans.NumberRange;
+import io.github.palexdev.materialfx.utils.NumberUtils;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class NumberUtilsTests {
+ private double val;
+ private NumberRange fromRange;
+ private NumberRange toRange;
+
+ @Test
+ public void testMap1() {
+ val = 0;
+ fromRange = NumberRange.of(-50.0, 100.0);
+ toRange = NumberRange.of(0.0, 100.0);
+
+ double mapped = NumberUtils.mapOneRangeToAnother(val, fromRange, toRange, 1);
+ assertEquals(33.3, mapped);
+ }
+
+ @Test
+ public void testMap2() {
+ val = -50;
+ fromRange = NumberRange.of(-50.0, 100.0);
+ toRange = NumberRange.of(0.0, 100.0);
+
+ double mapped = NumberUtils.mapOneRangeToAnother(val, fromRange, toRange, 1);
+ assertEquals(0.0, mapped);
+ }
+
+ @Test
+ public void testMap3() {
+ val = 100;
+ fromRange = NumberRange.of(-50.0, 100.0);
+ toRange = NumberRange.of(0.0, 100.0);
+
+ double mapped = NumberUtils.mapOneRangeToAnother(val, fromRange, toRange, 1);
+ assertEquals(100.0, mapped);
+ }
+
+ @Test
+ public void testMap4() {
+ val = -10;
+ fromRange = NumberRange.of(-50.0, 100.0);
+ toRange = NumberRange.of(0.0, 100.0);
+
+ double mapped = NumberUtils.mapOneRangeToAnother(val, fromRange, toRange, 1);
+ assertEquals(26.7, mapped);
+ }
+
+ @Test
+ public void testMap5() {
+ val = 0;
+ fromRange = NumberRange.of(-100.0, 100.0);
+ toRange = NumberRange.of(0.0, 100.0);
+
+ double mapped = NumberUtils.mapOneRangeToAnother(val, fromRange, toRange, 1);
+ assertEquals(50.0, mapped);
+ }
+
+ @Test
+ public void testClamp1() {
+ val = 10;
+
+ double clamped = NumberUtils.clamp(val, 0, 100);
+ assertEquals(10.0, clamped);
+ }
+
+ @Test
+ public void testClamp2() {
+ val = 50;
+
+ double clamped = NumberUtils.clamp(val, 0, 100);
+ assertEquals(50.0, clamped);
+ }
+
+ @Test
+ public void testClamp3() {
+ val = 102;
+
+ double clamped = NumberUtils.clamp(val, 0, 100);
+ assertEquals(100.0, clamped);
+ }
+
+ @Test
+ public void testClamp4() {
+ val = -9;
+
+ double clamped = NumberUtils.clamp(val, 0, 100);
+ assertEquals(0.0, clamped);
+ }
+}
diff --git a/demo/src/test/java/treeview/TreeViewTests.java b/demo/src/test/java/treeview/TreeViewTests.java
old mode 100644
new mode 100755
index 6f12ee90..e3372dd7
--- a/demo/src/test/java/treeview/TreeViewTests.java
+++ b/demo/src/test/java/treeview/TreeViewTests.java
@@ -8,9 +8,11 @@
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.testfx.api.FxRobot;
-import org.testfx.framework.junit.ApplicationTest;
+import org.testfx.framework.junit5.ApplicationExtension;
+import org.testfx.framework.junit5.Start;
import java.io.File;
import java.io.IOException;
@@ -19,366 +21,367 @@
import java.nio.file.Paths;
import java.util.List;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-public class TreeViewTests extends ApplicationTest {
- private final FxRobot robot = new FxRobot();
- private final String desktopPath = System.getProperty("user.home") + "/Desktop";
- private MFXTreeView treeView;
- private MFXTreeView expandedTreeView;
- private MFXTreeView complexTreeView;
-
- @Override
- public void start(Stage stage) {
- buildTreeViews();
- StackPane stackPane = new StackPane(
- treeView, expandedTreeView, complexTreeView
- );
- Scene scene = new Scene(stackPane, 100, 100);
- stage.setScene(scene);
- stage.show();
- stage.toBack();
- }
-
- @SuppressWarnings("ConstantConditions")
- @Test
- public void testItemsCountRoot() {
- long start = System.nanoTime();
- AbstractMFXTreeItem root = treeView.getRoot();
-
- long count = root.getItemsCount();
- assertEquals(12, count);
-
- AbstractMFXTreeItem i1 = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I1"))
- .findFirst().orElse(null);
- count = i1.getItemsCount();
- assertEquals(4, count);
- long end = System.nanoTime();
- System.out.println("TimeCountRoot:" + ((double) (end - start) / 1000000) + "ms");
- }
-
- @SuppressWarnings("ConstantConditions")
- @Test
- public void testItemsCountItem() {
- long start = System.nanoTime();
- AbstractMFXTreeItem root = treeView.getRoot();
- AbstractMFXTreeItem i3 = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I3"))
- .findFirst().orElse(null);
- long count = i3.getItemsCount();
- assertEquals(3, count);
- long end = System.nanoTime();
- System.out.println("TimeCountItem:" + ((double) (end - start) / 1000000) + "ms");
- }
-
- @Test
- public void testItemCountComplex() throws IOException {
- long start = System.nanoTime();
- AbstractMFXTreeItem root = complexTreeView.getRoot();
- long expectedCount = fileCount();
- long count = root.getItemsCount();
- assertEquals(expectedCount, count);
- long end = System.nanoTime();
- System.out.println("TimeCountComplex:" + ((double) (end - start) / 1000000) + "ms");
- }
-
- @SuppressWarnings("ConstantConditions")
- @Test
- public void testItemIndex() {
- long start = System.nanoTime();
- AbstractMFXTreeItem root = treeView.getRoot();
- AbstractMFXTreeItem i1 = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I1"))
- .findFirst().orElse(null);
- AbstractMFXTreeItem i1b = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I1B"))
- .findFirst().orElse(null);
- AbstractMFXTreeItem i2a = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I2A"))
- .findFirst().orElse(null);
- AbstractMFXTreeItem i4a = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I4A"))
- .findFirst().orElse(null);
-
- assertEquals(0, root.getIndex());
- assertEquals(1, i1.getIndex());
- assertEquals(4, i1b.getIndex());
- assertEquals(6, i2a.getIndex());
- assertEquals(11, i4a.getIndex());
- long end = System.nanoTime();
- System.out.println("TimeIndex:" + ((double) (end - start) / 1000000) + "ms");
- }
-
- @SuppressWarnings("ConstantConditions")
- @Test
- public void testItemLevel() {
- long start = System.nanoTime();
- AbstractMFXTreeItem root = treeView.getRoot();
- AbstractMFXTreeItem i1 = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I1"))
- .findFirst().orElse(null);
- AbstractMFXTreeItem i1b = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I1B"))
- .findFirst().orElse(null);
- AbstractMFXTreeItem i2a = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I2A"))
- .findFirst().orElse(null);
- AbstractMFXTreeItem i11a = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I11A"))
- .findFirst().orElse(null);
-
- assertEquals(0, root.getLevel());
- assertEquals(1, i1.getLevel());
- assertEquals(2, i1b.getLevel());
- assertEquals(2, i2a.getLevel());
- assertEquals(3, i11a.getLevel());
- long end = System.nanoTime();
- System.out.println("TimeLevel:" + ((double) (end - start) / 1000000) + "ms");
- }
-
- @Test
- public void testTreeViewGet() {
- long start = System.nanoTime();
- AbstractMFXTreeItem root = treeView.getRoot();
- AbstractMFXTreeItem complexRoot = complexTreeView.getRoot();
-
- TreeItemStream.stream(root).forEach(item -> assertEquals(treeView, item.getTreeView()));
- TreeItemStream.stream(complexRoot).forEach(item -> assertEquals(complexTreeView, item.getTreeView()));
- long end = System.nanoTime();
- System.out.println("TimeTreeViewGet:" + ((double) (end - start) / 1000000) + "ms");
- }
-
- @SuppressWarnings("ConstantConditions")
- @Test
- public void testNextSiblings() {
- long start = System.nanoTime();
- AbstractMFXTreeItem root = treeView.getRoot();
- AbstractMFXTreeItem i1 = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I1"))
- .findFirst().orElse(null);
- AbstractMFXTreeItem i1b = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I1B"))
- .findFirst().orElse(null);
- AbstractMFXTreeItem i2a = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I2A"))
- .findFirst().orElse(null);
- AbstractMFXTreeItem i3a = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I3A"))
- .findFirst().orElse(null);
- AbstractMFXTreeItem i4 = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I4"))
- .findFirst().orElse(null);
-
- assertNull(root.getNextSibling());
- assertEquals("I2", i1.getNextSibling().getData());
- assertNull(i1b.getNextSibling());
- assertNull(i2a.getNextSibling());
- assertEquals("I3B", i3a.getNextSibling().getData());
- assertNull(i4.getNextSibling());
- long end = System.nanoTime();
- System.out.println("TimeNextSiblings:" + ((double) (end - start) / 1000000) + "ms");
- }
-
- @SuppressWarnings("ConstantConditions")
- @Test
- public void testPreviousSiblings() {
- long start = System.nanoTime();
- AbstractMFXTreeItem root = treeView.getRoot();
- AbstractMFXTreeItem i1 = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I1"))
- .findFirst().orElse(null);
- AbstractMFXTreeItem i1b = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I1B"))
- .findFirst().orElse(null);
- AbstractMFXTreeItem i2a = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I2A"))
- .findFirst().orElse(null);
- AbstractMFXTreeItem i3a = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I3A"))
- .findFirst().orElse(null);
- AbstractMFXTreeItem i4 = TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I4"))
- .findFirst().orElse(null);
-
- assertNull(root.getPreviousSibling());
- assertNull(i1.getPreviousSibling());
- assertEquals("I1A", i1b.getPreviousSibling().getData());
- assertNull(i2a.getPreviousSibling());
- assertNull(i3a.getPreviousSibling());
- assertEquals("I3", i4.getPreviousSibling().getData());
- long end = System.nanoTime();
- System.out.println("TimePreviousSiblings:" + ((double) (end - start) / 1000000) + "ms");
- }
-
- @SuppressWarnings("ConstantConditions")
- @Test
- public void testSelected() {
- long start = System.nanoTime();
- MFXTreeItem root = (MFXTreeItem) treeView.getRoot();
- treeView.getSelectionModel().setAllowsMultipleSelection(false);
- MFXTreeItem i1 = (MFXTreeItem) TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I1"))
- .findFirst().orElse(null);
- MFXTreeItem i1b = (MFXTreeItem) TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I1B"))
- .findFirst().orElse(null);
- MFXTreeItem i2a = (MFXTreeItem) TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I2A"))
- .findFirst().orElse(null);
- MFXTreeItem i3 = (MFXTreeItem) TreeItemStream.stream(root)
- .filter(i -> i.getData().equals("I3"))
- .findFirst().orElse(null);
-
- root.setSelected(true);
- i1.setSelected(true);
- i1b.setSelected(true);
- i2a.setSelected(true);
- i3.setSelected(true);
-
- assertEquals(1, treeView.getSelectionModel().getSelectedItems().size());
- assertEquals(i3, treeView.getSelectionModel().getSelectedItem());
- long end = System.nanoTime();
- System.out.println("TimeSelected:" + ((double) (end - start) / 1000000) + "ms");
- }
-
- @SuppressWarnings("ConstantConditions")
- @Test
- public void testSelectedMultiple() {
- long start = System.nanoTime();
- MFXTreeItem