Browse Source

:bookmark: Upgrade components, core, effects and resources modules

Components Module
:bookmark: Version 11.15.1
:recycle: Implemented MFXButtonBehavior
:sparkles: Implemented all variants of MFXButton
:recycle: MFXButtonSkin: add ripple effect, adapt to changes made in SkinBase

Core Module
:bookmark: Version 11.2.2

Effects Module
:bookmark: 11.0.3
:dizzy: Slight change to shadow transitions

Resources Module
:bookmark: Version 11.1.1
:recycle: MFXFontIcon: use old properties name in CSS
:recycle: MFXIconWrapper: use the new ripple generator
:recycle: Fix fonts path and add a single CSS file which imports all of them
:recycle: Added some new utils to MD3 sass, some were moved
:sparkles: Added styles for all the button variants
:sparkles: Expose all the tokens in the generated CSS theme, so that user can easily extend a theme and have the palette and color scheme as look-ups

:memo: Add/Update documentation where needed

Signed-off-by: Alessadro Parisi <alessandro.parisi406@gmail.com>
Alessadro Parisi 2 years ago
parent
commit
fe47fd0477
53 changed files with 1492 additions and 382 deletions
  1. 4 2
      .idea/inspectionProfiles/Project_Default.xml
  2. 4 4
      gradle.properties
  3. 11 0
      modules/components/CHANGELOG.md
  4. 43 1
      modules/components/src/main/java/io/github/palexdev/materialfx/behaviors/MFXButtonBehavior.java
  5. 13 5
      modules/components/src/main/java/io/github/palexdev/materialfx/controls/base/MFXControl.java
  6. 11 0
      modules/components/src/main/java/io/github/palexdev/materialfx/controls/base/MFXLabeled.java
  7. 3 0
      modules/components/src/main/java/io/github/palexdev/materialfx/controls/base/MFXStyleable.java
  8. 64 23
      modules/components/src/main/java/io/github/palexdev/materialfx/controls/buttons/MFXButton.java
  9. 153 0
      modules/components/src/main/java/io/github/palexdev/materialfx/controls/buttons/MFXElevatedButton.java
  10. 54 0
      modules/components/src/main/java/io/github/palexdev/materialfx/controls/buttons/MFXFilledButton.java
  11. 53 0
      modules/components/src/main/java/io/github/palexdev/materialfx/controls/buttons/MFXOutlinedButton.java
  12. 53 0
      modules/components/src/main/java/io/github/palexdev/materialfx/controls/buttons/MFXTextButton.java
  13. 54 0
      modules/components/src/main/java/io/github/palexdev/materialfx/controls/buttons/MFXTonalFilledButton.java
  14. 48 12
      modules/components/src/main/java/io/github/palexdev/materialfx/skins/MFXButtonSkin.java
  15. 3 0
      modules/components/src/main/java/io/github/palexdev/materialfx/theming/PseudoClasses.java
  16. 11 1
      modules/components/src/main/java/module-info.java
  17. 4 3
      modules/components/src/test/java/app/ComponentsLauncher.java
  18. 147 0
      modules/components/src/test/java/app/buttons/ButtonsPlayground.java
  19. 34 0
      modules/components/src/test/java/app/others/ui/MultipleViewApp.java
  20. 91 0
      modules/components/src/test/java/app/others/ui/TitledFlowPane.java
  21. 55 0
      modules/components/src/test/java/app/others/ui/ViewSwitcher.java
  22. 0 62
      modules/components/src/test/java/interactive/ButtonsTest.java
  23. 2 2
      modules/components/src/test/java/interactive/TestButton.java
  24. 6 0
      modules/core/CHANGELOG.md
  25. 15 0
      modules/core/src/main/java/io/github/palexdev/mfxcore/behavior/WithBehavior.java
  26. 42 0
      modules/core/src/main/java/io/github/palexdev/mfxcore/controls/SkinBase.java
  27. 6 0
      modules/effects/CHANGELOG.md
  28. 6 5
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/enums/ElevationLevel.java
  29. 1 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/ripple/MFXRippleGenerator.java
  30. 15 0
      modules/resources/CHANGELOG.md
  31. 25 0
      modules/resources/build.gradle
  32. 6 6
      modules/resources/src/main/java/io/github/palexdev/mfxresources/fonts/MFXFontIcon.java
  33. 7 7
      modules/resources/src/main/java/io/github/palexdev/mfxresources/fonts/MFXIconWrapper.java
  34. 25 25
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/fonts/Comfortaa/Confortaa.css
  35. 3 9
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/fonts/Fonts.css
  36. BIN
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/fonts/MFXResources.ttf
  37. 50 50
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/fonts/OpenSans/OpenSans.css
  38. 61 61
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/fonts/Roboto/Roboto.css
  39. 17 0
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/abstracts/_common_utils.scss
  40. 6 17
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/abstracts/_elevation_utils.scss
  41. 15 0
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/abstracts/_ripple_utils.scss
  42. 19 0
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/abstracts/_theme_utils.scss
  43. 1 1
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/components/_components.scss
  44. 5 0
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/components/buttons/_buttons.scss
  45. 91 0
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/components/buttons/_buttons_base.scss
  46. 14 55
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/components/buttons/_elevated_button.scss
  47. 20 0
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/components/buttons/_filled_button.scss
  48. 22 0
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/components/buttons/_outlined_button.scss
  49. 31 0
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/components/buttons/_text_button.scss
  50. 20 0
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/components/buttons/_tonal_filled_button.scss
  51. 11 8
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/mfx-light.scss
  52. 34 15
      modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/tokens/_tokens.scss
  53. 3 8
      modules/resources/src/test/java/interactive/IconsTests.java

+ 4 - 2
.idea/inspectionProfiles/Project_Default.xml

@@ -867,11 +867,13 @@
     <inspection_tool class="CssReplaceWithShorthandSafely" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
     <inspection_tool class="CssReplaceWithShorthandUnsafely" enabled="true" level="INFORMATION" enabled_by_default="true" />
     <inspection_tool class="CssUnknownProperty" enabled="true" level="WARNING" enabled_by_default="true">
-      <option name="myCustomPropertiesEnabled" value="false" />
+      <option name="myCustomPropertiesEnabled" value="true" />
       <option name="myIgnoreVendorSpecificProperties" value="false" />
       <option name="myCustomPropertiesList">
         <value>
-          <list size="0" />
+          <list size="1">
+            <item index="0" class="java.lang.String" itemvalue="-mfx-elevation" />
+          </list>
         </value>
       </option>
     </inspection_tool>

+ 4 - 4
gradle.properties

@@ -20,11 +20,11 @@ jdk=11
 testJdk=17
 
 # Modules
-mfx=11.15.0
-mfxcore=11.2.1
-mfxeffects=11.0.2
+mfx=11.15.1
+mfxcore=11.2.2
+mfxeffects=11.0.3
 mfxlocalization=11.0.1
-mfxresources=11.1.0
+mfxresources=11.1.1
 
 # Plugins
 jfxPlugin=0.0.13

+ 11 - 0
modules/components/CHANGELOG.md

@@ -16,6 +16,17 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 [//]: ##[Unreleased]
 
+## [11.15.1] - 02-02-2023
+
+## Added
+
+- Implemented all variants of MFXButton
+
+## Changed
+
+- Implemented MFXButtonBehavior
+- MFXButtonSkin: add ripple effect, adapt to changes made in SkinBase
+
 ## [11.15.0] - 26-01-2023
 
 ## Added

+ 43 - 1
modules/components/src/main/java/io/github/palexdev/materialfx/behaviors/MFXButtonBehavior.java

@@ -18,12 +18,54 @@
 
 package io.github.palexdev.materialfx.behaviors;
 
-import io.github.palexdev.materialfx.controls.MFXButton;
+import io.github.palexdev.materialfx.controls.buttons.MFXButton;
+import io.github.palexdev.materialfx.skins.MFXButtonSkin;
 import io.github.palexdev.mfxcore.behavior.BehaviorBase;
+import io.github.palexdev.mfxeffects.ripple.MFXRippleGenerator;
+import javafx.scene.input.MouseEvent;
 
+/**
+ * This is the default behavior used by all {@link MFXButton}s.
+ * <p>
+ * Defines the actions to:
+ * <p> - generate ripples
+ * <p> - handle mouse press
+ * <p> - handle mouse click
+ */
 public class MFXButtonBehavior extends BehaviorBase<MFXButton> {
 
+	//================================================================================
+	// Constructors
+	//================================================================================
 	public MFXButtonBehavior(MFXButton node) {
 		super(node);
 	}
+
+	//================================================================================
+	// Methods
+	//================================================================================
+
+	/**
+	 * Instructs the given {@link MFXRippleGenerator} to generate a ripple for the given
+	 * {@link MouseEvent}.
+	 * <p></p>
+	 * The parameters are given by the default skin, {@link MFXButtonSkin}, associated to each button.
+	 */
+	public void generateRipple(MFXRippleGenerator rg, MouseEvent me) {
+		rg.generate(me);
+	}
+
+	/**
+	 * Requests focus on mouse pressed.
+	 */
+	public void mousePressed() {
+		getNode().requestFocus();
+	}
+
+	/**
+	 * Calls {@link MFXButton#fire()} on mouse clicked.
+	 */
+	public void mouseClicked() {
+		getNode().fire();
+	}
 }

+ 13 - 5
modules/components/src/main/java/io/github/palexdev/materialfx/controls/base/MFXControl.java

@@ -27,17 +27,22 @@ import javafx.scene.control.Control;
 
 import java.util.function.Supplier;
 
+/**
+ * Base class for MaterialFX controls. The idea is to have a separate hierarchy of components from the JavaFX one,
+ * that perfectly integrates with the new Behavior and Theming APIs.
+ * <p>
+ * Extends {@link Control} and implements both {@link WithBehavior} and {@link MFXStyleable}.
+ * <p></p>
+ * Components that primarily deal with text should extend {@link MFXLabeled} instead.
+ *
+ * @param <B> the behavior type used by the control
+ */
 public abstract class MFXControl<B extends BehaviorBase<? extends Node>> extends Control implements WithBehavior<B>, MFXStyleable {
 	//================================================================================
 	// Properties
 	//================================================================================
 	private final SupplierProperty<B> behaviorProvider = new SupplierProperty<>();
 
-	//================================================================================
-	// Abstract Methods
-	//================================================================================
-	public abstract void defaultBehaviorFactory();
-
 	//================================================================================
 	// Overridden Methods
 	//================================================================================
@@ -49,14 +54,17 @@ public abstract class MFXControl<B extends BehaviorBase<? extends Node>> extends
 	//================================================================================
 	// Getters/Setters
 	//================================================================================
+	@Override
 	public Supplier<B> getBehaviorProvider() {
 		return behaviorProvider.get();
 	}
 
+	@Override
 	public SupplierProperty<B> behaviorProviderProperty() {
 		return behaviorProvider;
 	}
 
+	@Override
 	public void setBehaviorProvider(Supplier<B> behaviorProvider) {
 		this.behaviorProvider.set(behaviorProvider);
 	}

+ 11 - 0
modules/components/src/main/java/io/github/palexdev/materialfx/controls/base/MFXLabeled.java

@@ -27,6 +27,14 @@ import javafx.scene.control.Labeled;
 
 import java.util.function.Supplier;
 
+/**
+ * Base class for MaterialFX controls that are text based. The idea is to have a separate hierarchy of components from the JavaFX one,
+ * * that perfectly integrates with the new Behavior and Theming APIs.
+ * <p>
+ * Extends {@link Labeled} and implements both {@link WithBehavior} and {@link MFXStyleable}.
+ *
+ * @param <B> the behavior type used by the control
+ */
 public abstract class MFXLabeled<B extends BehaviorBase<? extends Node>> extends Labeled implements WithBehavior<B>, MFXStyleable {
 	//================================================================================
 	// Properties
@@ -58,14 +66,17 @@ public abstract class MFXLabeled<B extends BehaviorBase<? extends Node>> extends
 	//================================================================================
 	// Getters/Setters
 	//================================================================================
+	@Override
 	public Supplier<B> getBehaviorProvider() {
 		return behaviorProvider.get();
 	}
 
+	@Override
 	public SupplierProperty<B> behaviorProviderProperty() {
 		return behaviorProvider;
 	}
 
+	@Override
 	public void setBehaviorProvider(Supplier<B> behaviorProvider) {
 		this.behaviorProvider.set(behaviorProvider);
 	}

+ 3 - 0
modules/components/src/main/java/io/github/palexdev/materialfx/controls/base/MFXStyleable.java

@@ -20,6 +20,9 @@ package io.github.palexdev.materialfx.controls.base;
 
 import java.util.List;
 
+/**
+ * Public API for all MaterialFX components that are styleable and integrated with the new Theming system.
+ */
 public interface MFXStyleable {
 
 	List<String> defaultStyleClasses();

+ 64 - 23
modules/components/src/main/java/io/github/palexdev/materialfx/controls/MFXButton.java → modules/components/src/main/java/io/github/palexdev/materialfx/controls/buttons/MFXButton.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
  * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
  *
  * MaterialFX is free software: you can redistribute it and/or
@@ -16,7 +16,7 @@
  * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package io.github.palexdev.materialfx.controls;
+package io.github.palexdev.materialfx.controls.buttons;
 
 import io.github.palexdev.materialfx.behaviors.MFXButtonBehavior;
 import io.github.palexdev.materialfx.controls.base.MFXLabeled;
@@ -30,6 +30,22 @@ import javafx.scene.Node;
 import java.util.List;
 import java.util.function.Supplier;
 
+/**
+ * Custom implementation of a button which extends {@link MFXLabeled}, has its own skin
+ * {@link MFXButtonSkin} and its own behavior {@link MFXButtonBehavior}.
+ * <p></p>
+ * {@code MFXButton} has 5 variants that mainly override the {@link #defaultStyleClasses()} method
+ * to allow themes to style them according to their style classes:
+ * <p> - {@link MFXElevatedButton}
+ * <p> - {@link MFXFilledButton}
+ * <p> - {@link MFXTonalFilledButton}
+ * <p> - {@link MFXOutlinedButton}
+ * <p> - {@link MFXTextButton}
+ * <p></p>
+ * <b>This base class is un-styled by the default official themes, a perfect start to implement custom styled buttons.</b>
+ * <p>
+ * The default style class of this component is: '.mfx-button'.
+ */
 public class MFXButton extends MFXLabeled<MFXButtonBehavior> {
 	//================================================================================
 	// Properties
@@ -58,6 +74,45 @@ public class MFXButton extends MFXLabeled<MFXButtonBehavior> {
 		initialize();
 	}
 
+	//================================================================================
+	// Variants
+	//================================================================================
+
+	/**
+	 * @return a new {@link MFXElevatedButton}
+	 */
+	public static MFXElevatedButton elevated() {
+		return new MFXElevatedButton();
+	}
+
+	/**
+	 * @return a new {@link MFXFilledButton}
+	 */
+	public static MFXFilledButton filled() {
+		return new MFXFilledButton();
+	}
+
+	/**
+	 * @return a new {@link MFXTonalFilledButton}
+	 */
+	public static MFXTonalFilledButton tonalFilled() {
+		return new MFXTonalFilledButton();
+	}
+
+	/**
+	 * @return a new {@link MFXOutlinedButton}
+	 */
+	public static MFXOutlinedButton outlined() {
+		return new MFXOutlinedButton();
+	}
+
+	/**
+	 * @return a new {@link MFXTextButton}
+	 */
+	public static MFXTextButton text() {
+		return new MFXTextButton();
+	}
+
 	//================================================================================
 	// Methods
 	//================================================================================
@@ -66,6 +121,10 @@ public class MFXButton extends MFXLabeled<MFXButtonBehavior> {
 		setDefaultBehaviorProvider();
 	}
 
+	/**
+	 * If not disabled, fires a new {@link ActionEvent}, triggering the {@link EventHandler} specified
+	 * by the {@link #onActionProperty()}.
+	 */
 	public void fire() {
 		if (!isDisabled()) fireEvent(new ActionEvent());
 	}
@@ -88,27 +147,6 @@ public class MFXButton extends MFXLabeled<MFXButtonBehavior> {
 		return new MFXButtonSkin(this);
 	}
 
-	//================================================================================
-	// Variants
-	//================================================================================
-	public static class MFXElevatedButton extends MFXButton {
-		public MFXElevatedButton() {
-		}
-
-		public MFXElevatedButton(String text) {
-			super(text);
-		}
-
-		public MFXElevatedButton(String text, Node icon) {
-			super(text, icon);
-		}
-
-		@Override
-		public List<String> defaultStyleClasses() {
-			return List.of("mfx-button", "elevated");
-		}
-	}
-
 	//================================================================================
 	// Getters/Setters
 	//================================================================================
@@ -116,6 +154,9 @@ public class MFXButton extends MFXLabeled<MFXButtonBehavior> {
 		return onAction.get();
 	}
 
+	/**
+	 * Specifies the action to execute when an {@link ActionEvent} is fired on this button.
+	 */
 	public EventHandlerProperty<ActionEvent> onActionProperty() {
 		return onAction;
 	}

+ 153 - 0
modules/components/src/main/java/io/github/palexdev/materialfx/controls/buttons/MFXElevatedButton.java

@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.materialfx.controls.buttons;
+
+import io.github.palexdev.mfxcore.base.properties.styleable.StyleableObjectProperty;
+import io.github.palexdev.mfxcore.utils.fx.StyleUtils;
+import io.github.palexdev.mfxeffects.enums.ElevationLevel;
+import javafx.css.CssMetaData;
+import javafx.css.Styleable;
+import javafx.css.StyleablePropertyFactory;
+import javafx.scene.Node;
+import javafx.scene.effect.DropShadow;
+import javafx.scene.effect.Effect;
+
+import java.util.List;
+
+/**
+ * Extension and variant of {@link MFXButton}, redefines the default style class to: '.mfx-button.elevated'
+ * and implements the mechanism needed to 'elevate' the button, see {@link #elevationProperty()}.
+ *
+ * @see <a href="https://www.w3schools.com/cssref/css_selectors.php">CSS Selectors</a>
+ */
+public class MFXElevatedButton extends MFXButton {
+
+	//================================================================================
+	// Constructors
+	//================================================================================
+	public MFXElevatedButton() {
+		this("");
+	}
+
+	public MFXElevatedButton(String text) {
+		this(text, null);
+	}
+
+	public MFXElevatedButton(String text, Node icon) {
+		super(text, icon);
+		initialize();
+	}
+
+	//================================================================================
+	// Methods
+	//================================================================================
+	private void initialize() {
+		setPickOnBounds(false);
+	}
+
+	//================================================================================
+	// Overridden Methods
+	//================================================================================
+	@Override
+	public List<String> defaultStyleClasses() {
+		return List.of("mfx-button", "elevated");
+	}
+
+	//================================================================================
+	// Styleable Properties
+	//================================================================================
+	private final StyleableObjectProperty<ElevationLevel> elevation = new StyleableObjectProperty<>(
+			StyleableProperties.ELEVATION,
+			this,
+			"elevation"
+	) {
+		@Override
+		public void set(ElevationLevel newValue) {
+			if (newValue == ElevationLevel.LEVEL0) {
+				setEffect(null);
+				super.set(newValue);
+				return;
+			}
+
+			Effect effect = getEffect();
+			if (effect == null) {
+				setEffect(newValue.toShadow());
+				super.set(newValue);
+				return;
+			}
+			if (!(effect instanceof DropShadow)) {
+				return;
+			}
+
+			ElevationLevel oldValue = get();
+			if (oldValue != newValue)
+				oldValue.animateTo((DropShadow) effect, newValue);
+			super.set(newValue);
+		}
+	};
+
+	public ElevationLevel getElevation() {
+		return elevation.get();
+	}
+
+	/**
+	 * Specifies the emphasis of the button' shadow. In other words, this property will apply a {@link DropShadow}
+	 * to the button when the specified level is greater than 0.
+	 * <p>
+	 * Can be set in CSS via the property: '-mfx-elevation'
+	 */
+	public StyleableObjectProperty<ElevationLevel> elevationProperty() {
+		return elevation;
+	}
+
+	public void setElevation(ElevationLevel elevation) {
+		this.elevation.set(elevation);
+	}
+
+	//================================================================================
+	// CssMetaData
+	//================================================================================
+	private static class StyleableProperties {
+		private static final StyleablePropertyFactory<io.github.palexdev.materialfx.controls.buttons.MFXElevatedButton> FACTORY = new StyleablePropertyFactory<>(MFXButton.getClassCssMetaData());
+		private static final List<CssMetaData<? extends Styleable, ?>> cssMetaDataList;
+
+		private static final CssMetaData<io.github.palexdev.materialfx.controls.buttons.MFXElevatedButton, ElevationLevel> ELEVATION =
+				FACTORY.createEnumCssMetaData(
+						ElevationLevel.class,
+						"-mfx-elevation",
+						io.github.palexdev.materialfx.controls.buttons.MFXElevatedButton::elevationProperty
+				);
+
+		static {
+			cssMetaDataList = StyleUtils.cssMetaDataList(
+					MFXButton.getClassCssMetaData(),
+					ELEVATION
+			);
+		}
+	}
+
+	public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
+		return StyleableProperties.cssMetaDataList;
+	}
+
+	@Override
+	public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
+		return getClassCssMetaData();
+	}
+}

+ 54 - 0
modules/components/src/main/java/io/github/palexdev/materialfx/controls/buttons/MFXFilledButton.java

@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.materialfx.controls.buttons;
+
+import javafx.scene.Node;
+
+import java.util.List;
+
+/**
+ * Extension and variant of {@link MFXButton}, redefines the default style class to: '.mfx-button.filled'.
+ *
+ * @see <a href="https://www.w3schools.com/cssref/css_selectors.php">CSS Selectors</a>
+ */
+public class MFXFilledButton extends MFXButton {
+
+	//================================================================================
+	// Constructors
+	//================================================================================
+	public MFXFilledButton() {
+	}
+
+	public MFXFilledButton(String text) {
+		super(text);
+	}
+
+	public MFXFilledButton(String text, Node icon) {
+		super(text, icon);
+	}
+
+	//================================================================================
+	// Overridden Methods
+	//================================================================================
+
+	@Override
+	public List<String> defaultStyleClasses() {
+		return List.of("mfx-button", "filled");
+	}
+}

+ 53 - 0
modules/components/src/main/java/io/github/palexdev/materialfx/controls/buttons/MFXOutlinedButton.java

@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.materialfx.controls.buttons;
+
+import javafx.scene.Node;
+
+import java.util.List;
+
+/**
+ * Extension and variant of {@link MFXButton}, redefines the default style class to: '.mfx-button.outlined'.
+ *
+ * @see <a href="https://www.w3schools.com/cssref/css_selectors.php">CSS Selectors</a>
+ */
+public class MFXOutlinedButton extends MFXButton {
+
+	//================================================================================
+	// Constructors
+	//================================================================================
+	public MFXOutlinedButton() {
+	}
+
+	public MFXOutlinedButton(String text) {
+		super(text);
+	}
+
+	public MFXOutlinedButton(String text, Node icon) {
+		super(text, icon);
+	}
+
+	//================================================================================
+	// Overridden Methods
+	//================================================================================
+	@Override
+	public List<String> defaultStyleClasses() {
+		return List.of("mfx-button", "outlined");
+	}
+}

+ 53 - 0
modules/components/src/main/java/io/github/palexdev/materialfx/controls/buttons/MFXTextButton.java

@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.materialfx.controls.buttons;
+
+import javafx.scene.Node;
+
+import java.util.List;
+
+/**
+ * Extension and variant of {@link MFXButton}, redefines the default style class to: '.mfx-button.text'.
+ *
+ * @see <a href="https://www.w3schools.com/cssref/css_selectors.php">CSS Selectors</a>
+ */
+public class MFXTextButton extends MFXButton {
+
+	//================================================================================
+	// Constructors
+	//================================================================================
+	public MFXTextButton() {
+	}
+
+	public MFXTextButton(String text) {
+		super(text);
+	}
+
+	public MFXTextButton(String text, Node icon) {
+		super(text, icon);
+	}
+
+	//================================================================================
+	// Overridden Methods
+	//================================================================================
+	@Override
+	public List<String> defaultStyleClasses() {
+		return List.of("mfx-button", "text");
+	}
+}

+ 54 - 0
modules/components/src/main/java/io/github/palexdev/materialfx/controls/buttons/MFXTonalFilledButton.java

@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.materialfx.controls.buttons;
+
+import javafx.scene.Node;
+
+import java.util.List;
+
+/**
+ * Extension and variant of {@link MFXButton}, redefines the default style class to: '.mfx-button.tonal-filled'.
+ *
+ * @see <a href="https://www.w3schools.com/cssref/css_selectors.php">CSS Selectors</a>
+ */
+public class MFXTonalFilledButton extends MFXButton {
+
+	//================================================================================
+	// Contractors
+	//================================================================================
+
+	public MFXTonalFilledButton() {
+	}
+
+	public MFXTonalFilledButton(String text) {
+		super(text);
+	}
+
+	public MFXTonalFilledButton(String text, Node icon) {
+		super(text, icon);
+	}
+
+	//================================================================================
+	// Overridden Methods
+	//================================================================================
+	@Override
+	public List<String> defaultStyleClasses() {
+		return List.of("mfx-button", "tonal-filled");
+	}
+}

+ 48 - 12
modules/components/src/main/java/io/github/palexdev/materialfx/skins/MFXButtonSkin.java

@@ -19,26 +19,35 @@
 package io.github.palexdev.materialfx.skins;
 
 import io.github.palexdev.materialfx.behaviors.MFXButtonBehavior;
-import io.github.palexdev.materialfx.controls.MFXButton;
+import io.github.palexdev.materialfx.controls.buttons.MFXButton;
 import io.github.palexdev.materialfx.theming.PseudoClasses;
 import io.github.palexdev.mfxcore.controls.BoundLabel;
 import io.github.palexdev.mfxcore.controls.SkinBase;
 import io.github.palexdev.mfxcore.utils.fx.LayoutUtils;
 import io.github.palexdev.mfxcore.utils.fx.TextUtils;
+import io.github.palexdev.mfxeffects.ripple.MFXRippleGenerator;
 import javafx.scene.Node;
 import javafx.scene.control.ContentDisplay;
 import javafx.scene.input.MouseEvent;
-
-import java.util.function.Supplier;
+import javafx.scene.paint.Color;
 
 import static io.github.palexdev.mfxcore.observables.When.disposeFor;
 import static io.github.palexdev.mfxcore.observables.When.onChanged;
 
+/**
+ * Default skin implementation for {@link MFXButton}s.
+ * <p>
+ * Extends {@link SkinBase} allowing seamless integration with the new Behavior API. This
+ * skin uses behaviors of type {@link MFXButtonBehavior}.
+ * <p></p>
+ * The layout is simple, there are just the label to show the text and the {@link MFXRippleGenerator} to generate ripples.
+ */
 public class MFXButtonSkin extends SkinBase<MFXButton, MFXButtonBehavior> {
 	//================================================================================
 	// Properties
 	//================================================================================
 	private final BoundLabel label;
+	private final MFXRippleGenerator rg;
 
 	//================================================================================
 	// Constructors
@@ -48,22 +57,35 @@ public class MFXButtonSkin extends SkinBase<MFXButton, MFXButtonBehavior> {
 
 		// Init nodes
 		label = new BoundLabel(button);
-
-		// Init behavior
-		Supplier<MFXButtonBehavior> bp = button.getBehaviorProvider();
-		MFXButtonBehavior behavior = bp.get();
-		setBehavior(behavior);
+		rg = new MFXRippleGenerator(button);
+		rg.setManaged(false);
+		rg.setAnimateBackground(false);
+		rg.setAutoClip(true);
+		rg.setRipplePrefSize(50);
+		rg.setRippleColor(Color.web("d7d1e7"));
 
 		// Finalize init
-		getChildren().add(label);
+		getChildren().addAll(rg, label);
 		addListeners();
 	}
 
 	//================================================================================
 	// Methods
 	//================================================================================
+
+	/**
+	 * Adds the following listeners:
+	 * <p> - A listener on the {@link MFXButton#behaviorProviderProperty()} to update the control's behavior when the provider changes
+	 * <p> - A listener on the {@link MFXButton#graphicProperty()} to update the graphic node when it changes
+	 * <p> - A listener on the {@link MFXButton#contentDisplayProperty()} to activate/disable the pseudo classes
+	 * {@link PseudoClasses#WITH_ICON_LEFT} and {@link PseudoClasses#WITH_ICON_RIGHT} accordingly
+	 */
 	private void addListeners() {
 		MFXButton button = getSkinnable();
+		onChanged(button.behaviorProviderProperty())
+				.then((o, n) -> setBehavior(n.get()))
+				.executeNow()
+				.listen();
 		onChanged(button.graphicProperty())
 				.then((o, n) -> {
 					if (o != null) getChildren().remove(o);
@@ -87,11 +109,17 @@ public class MFXButtonSkin extends SkinBase<MFXButton, MFXButtonBehavior> {
 	//================================================================================
 	// Overridden Methods
 	//================================================================================
+
+	/**
+	 * Initializes the given {@link MFXButtonBehavior} to handle events such as: {@link MouseEvent#MOUSE_PRESSED},
+	 * {@link MouseEvent#MOUSE_CLICKED}.
+	 */
 	@Override
-	protected void initBehavior() {
+	protected void initBehavior(MFXButtonBehavior behavior) {
 		MFXButton button = getSkinnable();
-		handle(button, MouseEvent.MOUSE_PRESSED, e -> button.requestFocus());
-		handle(button, MouseEvent.MOUSE_CLICKED, e -> button.fire());
+		handle(button, MouseEvent.MOUSE_PRESSED, e -> behavior.generateRipple(rg, e));
+		handle(button, MouseEvent.MOUSE_PRESSED, e -> behavior.mousePressed());
+		handle(button, MouseEvent.MOUSE_CLICKED, e -> behavior.mouseClicked());
 	}
 
 	@Override
@@ -123,6 +151,14 @@ public class MFXButtonSkin extends SkinBase<MFXButton, MFXButtonBehavior> {
 		return getSkinnable().prefHeight(width);
 	}
 
+	@Override
+	protected void layoutChildren(double x, double y, double w, double h) {
+		super.layoutChildren(x, y, w, h);
+
+		MFXButton button = getSkinnable();
+		rg.resizeRelocate(0, 0, button.getWidth(), button.getHeight());
+	}
+
 	@Override
 	public void dispose() {
 		MFXButton button = getSkinnable();

+ 3 - 0
modules/components/src/main/java/io/github/palexdev/materialfx/theming/PseudoClasses.java

@@ -21,6 +21,9 @@ package io.github.palexdev.materialfx.theming;
 import javafx.css.PseudoClass;
 import javafx.scene.Node;
 
+/**
+ * This enumerator keeps references to custom {@link PseudoClass}es needed by MaterialFX components.
+ */
 public enum PseudoClasses {
 	WITH_ICON_LEFT(PseudoClass.getPseudoClass("with-icon-left")),
 	WITH_ICON_RIGHT(PseudoClass.getPseudoClass("with-icon-right")),

+ 11 - 1
modules/components/src/main/java/module-info.java

@@ -2,9 +2,19 @@ module mfx.components {
 	requires transitive javafx.controls;
 
 	requires transitive mfx.core;
+	requires transitive mfx.effects;
 	requires transitive mfx.localization;
 	requires transitive mfx.resources;
 
+	// Behaviors
 	exports io.github.palexdev.materialfx.behaviors;
-	exports io.github.palexdev.materialfx.controls;
+
+	// Controls
+	exports io.github.palexdev.materialfx.controls.buttons;
+
+	// Skins
+	exports io.github.palexdev.materialfx.skins;
+
+	// Theming
+	exports io.github.palexdev.materialfx.theming;
 }

+ 4 - 3
modules/components/src/test/java/interactive/Launcher.java → modules/components/src/test/java/app/ComponentsLauncher.java

@@ -16,13 +16,14 @@
  * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package interactive;
+package app;
 
+import app.buttons.ButtonsPlayground;
 import javafx.application.Application;
 
-public class Launcher {
+public class ComponentsLauncher {
 
 	public static void main(String[] args) {
-		Application.launch(ButtonsTest.class);
+		Application.launch(ButtonsPlayground.class, args);
 	}
 }

+ 147 - 0
modules/components/src/test/java/app/buttons/ButtonsPlayground.java

@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package app.buttons;
+
+import app.others.ui.MultipleViewApp;
+import app.others.ui.TitledFlowPane;
+import app.others.ui.ViewSwitcher;
+import io.github.palexdev.materialfx.controls.buttons.*;
+import io.github.palexdev.mfxresources.MFXResources;
+import io.github.palexdev.mfxresources.fonts.IconsProviders;
+import javafx.application.Application;
+import javafx.collections.FXCollections;
+import javafx.css.PseudoClass;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.scene.control.ComboBox;
+import javafx.scene.control.ContentDisplay;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.paint.Color;
+import javafx.stage.Stage;
+
+import java.util.function.BiFunction;
+
+import static io.github.palexdev.mfxresources.fonts.IconsProviders.FONTAWESOME_SOLID;
+
+public class ButtonsPlayground extends Application implements MultipleViewApp<String> {
+	//================================================================================
+	// Properties
+	//================================================================================
+	private final ViewSwitcher<String> switcher = new ViewSwitcher<>();
+
+	//================================================================================
+	// Overridden Methods
+	//================================================================================
+	@Override
+	public void start(Stage stage) {
+		registerViews();
+
+		BorderPane root = new BorderPane();
+		ComboBox<String> header = new ComboBox<>(FXCollections.observableArrayList(switcher.views().keySet()));
+		header.valueProperty().addListener((observable, oldValue, newValue) -> root.setCenter(switcher.load(newValue)));
+		root.setTop(header);
+		BorderPane.setAlignment(header, Pos.CENTER);
+		BorderPane.setMargin(header, new Insets(30, 0, 60, 0));
+		root.setStyle("-fx-background-color: -md-sys-color-background");
+
+		header.getSelectionModel().selectFirst();
+
+		Scene scene = new Scene(root, 800, 800);
+		loadStyleSheet(scene);
+		stage.setScene(scene);
+		stage.setTitle("Buttons Playground");
+		stage.show();
+
+		//ScenicView.show(scene);
+	}
+
+	@Override
+	public void registerViews() {
+		switcher.register(defaultView(), s -> ebView());
+		switcher.register("filled-buttons", s -> fbView());
+		switcher.register("tonal-filled-buttons", s -> tfbView());
+		switcher.register("outlined-buttons", s -> obView());
+		switcher.register("text-buttons", s -> tbView());
+	}
+
+	@Override
+	public String defaultView() {
+		return "elevated-buttons";
+	}
+
+	@Override
+	public String getStylesheet() {
+		return MFXResources.load("sass/md3/mfx-light.css");
+	}
+
+	//================================================================================
+	// Methods
+	//================================================================================
+
+	private Node ebView() {
+		return createButtonsView(MFXElevatedButton::new);
+	}
+
+	private Node fbView() {
+		return createButtonsView(MFXFilledButton::new);
+	}
+
+	private Node tfbView() {
+		return createButtonsView(MFXTonalFilledButton::new);
+	}
+
+	private Node obView() {
+		return createButtonsView(MFXOutlinedButton::new);
+	}
+
+	private Node tbView() {
+		return createButtonsView(600, MFXTextButton::new);
+	}
+
+	private Node createButtonsView(BiFunction<String, Node, MFXButton> generator) {
+		return createButtonsView(700, generator);
+	}
+
+	private Node createButtonsView(double length, BiFunction<String, Node, MFXButton> generator) {
+		TitledFlowPane tfp = new TitledFlowPane("Filled Buttons");
+		tfp.setMaxWidth(length);
+
+		MFXButton btn0 = generator.apply("Enabled", null);
+		MFXButton btn1 = generator.apply("Disabled", null);
+		MFXButton btn2 = generator.apply("Hovered", null);
+		MFXButton btn3 = generator.apply("Focused", null);
+		MFXButton btn4 = generator.apply("Pressed", null);
+		MFXButton btn5 = generator.apply("Icon Left", IconsProviders.randomIcon(FONTAWESOME_SOLID, 24.0, Color.TRANSPARENT));
+		MFXButton btn6 = generator.apply("Icon Right", IconsProviders.randomIcon(FONTAWESOME_SOLID, 24.0, Color.TRANSPARENT));
+		btn6.setContentDisplay(ContentDisplay.RIGHT);
+
+		btn1.setDisable(true);
+		btn2.setMouseTransparent(true);
+		btn2.pseudoClassStateChanged(PseudoClass.getPseudoClass("hover"), true);
+		btn3.setMouseTransparent(true);
+		btn3.pseudoClassStateChanged(PseudoClass.getPseudoClass("focused"), true);
+		btn4.setMouseTransparent(true);
+		btn4.pseudoClassStateChanged(PseudoClass.getPseudoClass("pressed"), true);
+
+		tfp.add(btn0, btn1, btn2, btn3, btn4, btn5, btn6);
+		return tfp;
+	}
+}

+ 34 - 0
modules/components/src/test/java/app/others/ui/MultipleViewApp.java

@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package app.others.ui;
+
+import javafx.scene.Scene;
+
+public interface MultipleViewApp<T> {
+
+	void registerViews();
+
+	T defaultView();
+
+	String getStylesheet();
+
+	default void loadStyleSheet(Scene scene) {
+		scene.getStylesheets().add(getStylesheet());
+	}
+}

+ 91 - 0
modules/components/src/test/java/app/others/ui/TitledFlowPane.java

@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package app.others.ui;
+
+import javafx.collections.ObservableList;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.layout.FlowPane;
+import javafx.scene.layout.VBox;
+
+import java.util.Collection;
+
+public class TitledFlowPane extends VBox {
+	//================================================================================
+	// Properties
+	//================================================================================
+	private final String STYLE_CLASS = "titled-flow-pane";
+	private final Label label;
+	private final FlowPane pane;
+
+	//================================================================================
+	// Constructors
+	//================================================================================
+	public TitledFlowPane() {
+		this("");
+	}
+
+	public TitledFlowPane(String title) {
+		this.label = new Label(title);
+		this.pane = new FlowPane();
+		pane.setAlignment(Pos.CENTER);
+		pane.setVgap(30);
+		pane.setHgap(30);
+
+		super.getChildren().addAll(label, pane);
+		setAlignment(Pos.TOP_CENTER);
+		setSpacing(30);
+	}
+
+	//================================================================================
+	// Methods
+	//================================================================================
+	public TitledFlowPane add(Node node) {
+		pane.getChildren().add(node);
+		return this;
+	}
+
+	public TitledFlowPane add(Node... nodes) {
+		pane.getChildren().addAll(nodes);
+		return this;
+	}
+
+	public TitledFlowPane add(Collection<Node> nodes) {
+		pane.getChildren().addAll(nodes);
+		return this;
+	}
+
+	public String getTitle() {
+		return label.getText();
+	}
+
+	public TitledFlowPane setTitle(String title) {
+		label.setText(title);
+		return this;
+	}
+
+	//================================================================================
+	// Overridden Methods
+	//================================================================================
+	@Override
+	public ObservableList<Node> getChildren() {
+		return getChildrenUnmodifiable();
+	}
+}

+ 55 - 0
modules/components/src/test/java/app/others/ui/ViewSwitcher.java

@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package app.others.ui;
+
+import io.github.palexdev.mfxcore.utils.Memoizer;
+import javafx.scene.Node;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Function;
+
+// TODO implement animated switching for fun?
+public class ViewSwitcher<T> {
+	//================================================================================
+	// Properties
+	//================================================================================
+	private final Map<T, Function<T, Node>> views = new LinkedHashMap<>();
+
+	//================================================================================
+	// Methods
+	//================================================================================
+	public ViewSwitcher<T> register(T id, Function<T, Node> sceneSupplier) {
+		views.put(id, Memoizer.memoize(sceneSupplier));
+		return this;
+	}
+
+	public ViewSwitcher<T> unregister(T id) {
+		views.remove(id);
+		return this;
+	}
+
+	public Node load(T id) {
+		return views.get(id).apply(id);
+	}
+
+	public Map<T, Function<T, Node>> views() {
+		return views;
+	}
+}

+ 0 - 62
modules/components/src/test/java/interactive/ButtonsTest.java

@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
- * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
- *
- * MaterialFX is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 3 of the License,
- * or (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package interactive;
-
-import io.github.palexdev.materialfx.controls.MFXButton;
-import io.github.palexdev.materialfx.controls.MFXButton.MFXElevatedButton;
-import io.github.palexdev.mfxcore.builders.bindings.StringBindingBuilder;
-import io.github.palexdev.mfxresources.MFXResources;
-import javafx.application.Application;
-import javafx.css.SizeUnits;
-import javafx.geometry.Pos;
-import javafx.scene.Scene;
-import javafx.scene.control.Button;
-import javafx.scene.layout.VBox;
-import javafx.scene.text.Font;
-import javafx.stage.Stage;
-
-import java.util.Arrays;
-
-public class ButtonsTest extends Application {
-
-	@Override
-	public void start(Stage primaryStage) throws Exception {
-		String theme = MFXResources.load("sass/md3/mfx-light.css");
-
-		MFXButton button = new MFXElevatedButton("Button");
-		Button btn = new Button();
-		btn.textProperty().bind(StringBindingBuilder.build()
-				.setMapper(() -> Arrays.toString(button.getPseudoClassStates().toArray()))
-				.addSources(button.getPseudoClassStates())
-				.get()
-		);
-
-		VBox pane = new VBox(30, button, btn);
-		pane.setAlignment(Pos.CENTER);
-		pane.setStyle("-fx-background-color: white");
-		Scene scene = new Scene(pane, 800, 800);
-		scene.getStylesheets().add(theme);
-
-		double px = SizeUnits.PX.points(14, 1, Font.font("Roboto Medium"));
-		System.out.println("Size: " + px);
-
-		primaryStage.setScene(scene);
-		primaryStage.show();
-	}
-}

+ 2 - 2
modules/components/src/test/java/unit/buttons/TestButton.java → modules/components/src/test/java/interactive/TestButton.java

@@ -16,10 +16,10 @@
  * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package unit.buttons;
+package interactive;
 
 import com.sun.javafx.tk.Toolkit;
-import io.github.palexdev.materialfx.controls.MFXButton;
+import io.github.palexdev.materialfx.controls.buttons.MFXButton;
 import javafx.scene.Scene;
 import javafx.scene.layout.Region;
 import javafx.scene.layout.StackPane;

+ 6 - 0
modules/core/CHANGELOG.md

@@ -16,6 +16,12 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 [//]: ##[Unreleased]
 
+## [11.2.2] - 02-02-2023
+
+## Changed
+
+- Added/Updated documentation where needed
+
 ## [11.2.1] - 31-01-2023
 
 ## Changed

+ 15 - 0
modules/core/src/main/java/io/github/palexdev/mfxcore/behavior/WithBehavior.java

@@ -23,16 +23,31 @@ import javafx.scene.Node;
 
 import java.util.function.Supplier;
 
+/**
+ * Public API for all components that want to integrate with the new Behavior API.
+ *
+ * @param <B> the type of behavior the component will use
+ */
 public interface WithBehavior<B extends BehaviorBase<? extends Node>> {
 
+	/**
+	 * @return a {@link Supplier} that is the provider for the default behavior used by the component.
+	 */
 	Supplier<B> defaultBehaviorProvider();
 
 	void setBehaviorProvider(Supplier<B> factory);
 
+	/**
+	 * Specifies the {@link Supplier} used to produce a behavior object for the component.
+	 */
 	SupplierProperty<B> behaviorProviderProperty();
 
 	Supplier<B> getBehaviorProvider();
 
+	/**
+	 * Restores the components behavior to the default one using {@link #defaultBehaviorProvider()}
+	 * and {@link #setBehaviorProvider(Supplier)}.
+	 */
 	default void setDefaultBehaviorProvider() {
 		setBehaviorProvider(defaultBehaviorProvider());
 	}

+ 42 - 0
modules/core/src/main/java/io/github/palexdev/mfxcore/controls/SkinBase.java

@@ -19,6 +19,7 @@
 package io.github.palexdev.mfxcore.controls;
 
 import io.github.palexdev.mfxcore.behavior.BehaviorBase;
+import io.github.palexdev.mfxcore.behavior.WithBehavior;
 import javafx.beans.InvalidationListener;
 import javafx.beans.Observable;
 import javafx.beans.value.ChangeListener;
@@ -29,6 +30,18 @@ import javafx.event.EventType;
 import javafx.scene.Node;
 import javafx.scene.control.Control;
 
+/**
+ * Extension of {@link javafx.scene.control.SkinBase} used by components that what a seamless integration with the new
+ * Behavior API.
+ * <p>
+ * The skin in responsible for keeping an instance of the behavior object and initializing it.
+ * <p>
+ * For implementation of this, the flow should be this:
+ * <p> - The skin creates the behavior, in case of controls implementing {@link WithBehavior}, it's possible to use {@link WithBehavior#getBehaviorProvider()}
+ * <p> - The skin sets the behavior with {@link #setBehavior(BehaviorBase)}
+ * <p> - The above method will automatically call {@link #initBehavior(BehaviorBase)}, which is an abstract method every skin
+ * should implement, it's responsible for initializing the behavior every time it changes
+ */
 public abstract class SkinBase<C extends Control, B extends BehaviorBase<C>> extends javafx.scene.control.SkinBase<C> {
 	//================================================================================
 	// Properties
@@ -45,24 +58,41 @@ public abstract class SkinBase<C extends Control, B extends BehaviorBase<C>> ext
 	//================================================================================
 	// Abstract Methods
 	//================================================================================
+
+	/**
+	 * This is responsible for initializing the behavior every time it changes, the given parameter
+	 * is the current uninitialized behavior.
+	 */
 	protected abstract void initBehavior(B behavior);
 
 	//================================================================================
 	// Delegate Methods
 	//================================================================================
 
+	/**
+	 * Delegate for {@link BehaviorBase#register(ObservableValue, ChangeListener)}.
+	 */
 	public <T> void register(ObservableValue<T> observable, ChangeListener<T> listener) {
 		behavior.register(observable, listener);
 	}
 
+	/**
+	 * Delegate for {@link BehaviorBase#register(Observable, InvalidationListener)}.
+	 */
 	public void register(Observable observable, InvalidationListener listener) {
 		behavior.register(observable, listener);
 	}
 
+	/**
+	 * Delegate for {@link BehaviorBase#handler(Node, EventType, EventHandler)}.
+	 */
 	public <E extends Event> void handle(Node node, EventType<E> eventType, EventHandler<E> handler) {
 		behavior.handler(node, eventType, handler);
 	}
 
+	/**
+	 * Delegate for {@link BehaviorBase#filter(Node, EventType, EventHandler)}.
+	 */
 	public <E extends Event> void handleAsFilter(Node node, EventType<E> eventType, EventHandler<E> handler) {
 		behavior.filter(node, eventType, handler);
 	}
@@ -73,6 +103,8 @@ public abstract class SkinBase<C extends Control, B extends BehaviorBase<C>> ext
 
 	/**
 	 * {@inheritDoc}
+	 * <p></p>
+	 * It's also responsible for disposing the current behavior.
 	 */
 	@Override
 	public void dispose() {
@@ -84,10 +116,20 @@ public abstract class SkinBase<C extends Control, B extends BehaviorBase<C>> ext
 	//================================================================================
 	// Getters/Setters
 	//================================================================================
+
+	/**
+	 * @return the current behavior instance used by the control
+	 */
 	protected B getBehavior() {
 		return behavior;
 	}
 
+	/**
+	 * This should be used by implementations when the behavior needs to change.
+	 * Will automatically call {@link #initBehavior(BehaviorBase)}.
+	 *
+	 * @throws IllegalArgumentException if the given behavior is null
+	 */
 	protected void setBehavior(B behavior) {
 		if (this.behavior != null) this.behavior.dispose();
 		if (behavior == null)

+ 6 - 0
modules/effects/CHANGELOG.md

@@ -16,6 +16,12 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 [//]: ##[Unreleased]
 
+## [11.0.3] - 02-02-2023
+
+## Changed
+
+- Slight change to shadow transitions
+
 ## [11.0.2] - 31-01-2023
 
 ## Changed

+ 6 - 5
modules/effects/src/main/java/io/github/palexdev/mfxeffects/enums/ElevationLevel.java

@@ -20,6 +20,7 @@ package io.github.palexdev.mfxeffects.enums;
 
 import io.github.palexdev.mfxeffects.animations.Animations.KeyFrames;
 import io.github.palexdev.mfxeffects.animations.Animations.TimelineBuilder;
+import io.github.palexdev.mfxeffects.animations.BezierEasing;
 import javafx.animation.Interpolator;
 import javafx.scene.effect.BlurType;
 import javafx.scene.effect.DropShadow;
@@ -98,12 +99,12 @@ public enum ElevationLevel {
 	}
 
 	public void animateTo(DropShadow current, ElevationLevel next) {
-		Interpolator i = Interpolators.INTERPOLATOR_V2.toInterpolator();
+		Interpolator i = BezierEasing.EASE;
 		TimelineBuilder.build()
-				.add(KeyFrames.of(200, current.radiusProperty(), next.getRadius(), i))
-				.add(KeyFrames.of(200, current.spreadProperty(), next.getSpread(), i))
-				.add(KeyFrames.of(150, current.offsetXProperty(), next.getOffsetX(), i))
-				.add(KeyFrames.of(150, current.offsetYProperty(), next.getOffsetY(), i))
+				.add(KeyFrames.of(50, current.offsetXProperty(), next.getOffsetX(), i))
+				.add(KeyFrames.of(50, current.offsetYProperty(), next.getOffsetY(), i))
+				.add(KeyFrames.of(50, current.radiusProperty(), next.getRadius(), i))
+				.add(KeyFrames.of(50, current.spreadProperty(), next.getSpread(), i))
 				.getAnimation().play();
 	}
 

+ 1 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/ripple/MFXRippleGenerator.java

@@ -87,6 +87,7 @@ import static javafx.util.Duration.ZERO;
  * <p> 2) You can set the visibility to hidden, {@link #visibleProperty()}
  * <p> 3) You can set the ripples opacity to 0, {@link #rippleOpacityProperty()}
  */
+// TODO next improvement is to keep the ripple until the mouse is released (?)
 public class MFXRippleGenerator extends Region implements RippleGenerator {
 	//================================================================================
 	// Properties

+ 15 - 0
modules/resources/CHANGELOG.md

@@ -16,6 +16,21 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 [//]: ##[Unreleased]
 
+## [11.1.1] - 02-02-2023
+
+## Added
+
+- Added styles for all the button variants
+- Expose all the tokens in the generated CSS theme, so that user can easily extend a theme and have the palette and
+  color scheme as look-ups
+
+## Changed
+
+- MFXFontIcon: use old properties name in CSS
+- MFXIconWrapper: use the new ripple generator
+- Fix fonts path and add a single CSS file which imports all of them
+- Added some new utils to MD3 sass, some were moved
+
 ## [11.1.0] - 26-01-2023
 
 ## Added

+ 25 - 0
modules/resources/build.gradle

@@ -1,5 +1,8 @@
 import com.vanniktech.maven.publish.SonatypeHost
 
+import java.nio.file.Files
+import java.nio.file.StandardOpenOption
+
 plugins {
     id 'io.miret.etienne.sass' version "$gradleSass"
 }
@@ -40,9 +43,31 @@ compileSass {
     sourceMap = none
 }
 
+tasks.register('postProcessCss') {
+    doLast {
+        String baseDir = "io/github/palexdev/mfxresources/sass"
+        File outputDir = project.file("src/main/resources/" + baseDir)
+        outputDir.eachFileRecurse(groovy.io.FileType.FILES) {
+            if (it.name.endsWith('.css')) {
+                String newText = it.text.replaceAll('[}]\n(?=\\S)', '}' + System.lineSeparator().repeat(2))
+                        .replaceAll('[;]\n(?=[/])', ';' + System.lineSeparator().repeat(2))
+                        .replaceAll("(, )(?=(.mfx))", "," + System.lineSeparator())
+                Files.writeString(
+                        it.toPath(),
+                        newText,
+                        StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING
+                )
+            }
+        }
+    }
+    mustRunAfter compileSass
+    doNotTrackState("CSS must be post processed always to make sure it's correct")
+}
+
 processResources {
     // This makes gradle compile sass to css before copying the resources
     dependsOn compileSass
+    dependsOn postProcessCss
 }
 
 mavenPublishing {

+ 6 - 6
modules/resources/src/main/java/io/github/palexdev/mfxresources/fonts/MFXFontIcon.java

@@ -232,7 +232,7 @@ public class MFXFontIcon extends Text {
 	/**
 	 * Specifies the color of the icon.
 	 * <p></p>
-	 * Settable in CSS via the property: '-color'.
+	 * Settable in CSS via the property: '-mfx-color'.
 	 */
 	public StyleableObjectProperty<Color> colorProperty() {
 		return color;
@@ -249,7 +249,7 @@ public class MFXFontIcon extends Text {
 	/**
 	 * Specifies the icon's description/name inside the icon font pack.
 	 * <p></p>
-	 * Settable in CSS via the property: '-description'.
+	 * Settable in CSS via the property: '-mfx-description'.
 	 */
 	public StyleableStringProperty descriptionProperty() {
 		return description;
@@ -269,7 +269,7 @@ public class MFXFontIcon extends Text {
 	 * <p>
 	 * On change this will automatically call {@link #setFontSize(double)}.
 	 * <p></p>
-	 * Settable in CSS via the property: '-size'.
+	 * Settable in CSS via the property: '-mfx-size'.
 	 */
 	public StyleableDoubleProperty sizeProperty() {
 		return size;
@@ -289,20 +289,20 @@ public class MFXFontIcon extends Text {
 
 		private static final CssMetaData<MFXFontIcon, Color> COLOR =
 				FACTORY.createColorCssMetaData(
-						"-color",
+						"-mfx-color",
 						MFXFontIcon::colorProperty,
 						Color.web("#454545")
 				);
 
 		private static final CssMetaData<MFXFontIcon, String> DESCRIPTION =
 				FACTORY.createStringCssMetaData(
-						"-description",
+						"-mfx-description",
 						MFXFontIcon::descriptionProperty
 				);
 
 		private static final CssMetaData<MFXFontIcon, Number> SIZE =
 				FACTORY.createSizeCssMetaData(
-						"-size",
+						"-mfx-size",
 						MFXFontIcon::sizeProperty,
 						16.0
 				);

+ 7 - 7
modules/resources/src/main/java/io/github/palexdev/mfxresources/fonts/MFXIconWrapper.java

@@ -19,7 +19,7 @@
 package io.github.palexdev.mfxresources.fonts;
 
 import io.github.palexdev.mfxeffects.beans.Position;
-import io.github.palexdev.mfxeffects.ripple.MFXCircleRippleGenerator;
+import io.github.palexdev.mfxeffects.ripple.MFXRippleGenerator;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.collections.ObservableList;
@@ -59,7 +59,7 @@ public class MFXIconWrapper extends StackPane {
 	private final String STYLE_CLASS = "mfx-icon-wrapper";
 
 	private final ObjectProperty<MFXFontIcon> icon = new SimpleObjectProperty<>();
-	private MFXCircleRippleGenerator rg;
+	private MFXRippleGenerator rg;
 	private EventHandler<MouseEvent> rHandler;
 
 	//================================================================================
@@ -94,7 +94,7 @@ public class MFXIconWrapper extends StackPane {
 		size.addListener((observable, oldValue, newValue) -> setPrefSize(newValue.doubleValue(), oldValue.doubleValue()));
 		rHandler = e -> {
 			if (e.getButton() == MouseButton.PRIMARY)
-				rg.generateRipple(e);
+				rg.generate(e);
 		};
 	}
 
@@ -130,8 +130,8 @@ public class MFXIconWrapper extends StackPane {
 		if (rg != null)
 			throw new IllegalStateException("Ripple generator has already been enabled for this icon!");
 
-		rg = new MFXCircleRippleGenerator(this);
-		rg.setRipplePositionFunction(positionFunction);
+		rg = new MFXRippleGenerator(this);
+		rg.setPositionFunction(positionFunction);
 		addEventHandler(MouseEvent.MOUSE_PRESSED, rHandler);
 		manageChildren();
 		setEnableRipple(true);
@@ -390,10 +390,10 @@ public class MFXIconWrapper extends StackPane {
 	//================================================================================
 
 	/**
-	 * @return the {@link MFXCircleRippleGenerator} instance for this wrapper, note that
+	 * @return the {@link MFXRippleGenerator} instance for this wrapper, note that
 	 * if the generator is not enabled this will return null
 	 */
-	public MFXCircleRippleGenerator getRippleGenerator() {
+	public MFXRippleGenerator getRippleGenerator() {
 		return rg;
 	}
 

+ 25 - 25
modules/resources/src/main/resources/io/github/palexdev/mfxresources/fonts/Comfortaa/Confortaa.css

@@ -17,41 +17,41 @@
  */
 
 @font-face {
-	font-family: 'Comfortaa Bold';
-	font-weight: bold;
-	font-style: normal;
-	font-display: swap;
-	src: url('../Comfortaa/Comfortaa-Bold.ttf') format('truetype');
+  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('../Comfortaa/Comfortaa-Light.ttf') format('truetype');
+  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('../Comfortaa/Comfortaa-Medium.ttf') format('truetype');
+  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('../Comfortaa/Comfortaa-Regular.ttf') format('truetype');
+  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('../Comfortaa/Comfortaa-SemiBold.ttf') format('truetype');
+  font-family: 'Comfortaa SemiBold';
+  font-weight: 600px;
+  font-style: normal;
+  font-display: swap;
+  src: url('../../fonts/Comfortaa/Comfortaa-SemiBold.ttf') format('truetype');
 }

+ 3 - 9
modules/core/src/main/java/io/github/palexdev/mfxcore/behavior/EmptyBehavior.java → modules/resources/src/main/resources/io/github/palexdev/mfxresources/fonts/Fonts.css

@@ -16,12 +16,6 @@
  * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package io.github.palexdev.mfxcore.behavior;
-
-import javafx.scene.Node;
-
-public class EmptyBehavior extends BehaviorBase<Node> {
-	public EmptyBehavior(Node node) {
-		super(node);
-	}
-}
+@import "Comfortaa/Confortaa.css";
+@import "OpenSans/OpenSans.css";
+@import "Roboto/Roboto.css";

BIN
modules/resources/src/main/resources/io/github/palexdev/mfxresources/fonts/MFXResources.ttf


+ 50 - 50
modules/resources/src/main/resources/io/github/palexdev/mfxresources/fonts/OpenSans/OpenSans.css

@@ -17,81 +17,81 @@
  */
 
 @font-face {
-	font-family: 'Open Sans Bold';
-	font-weight: bold;
-	font-style: normal;
-	font-display: swap;
-	src: url('../OpenSans/OpenSans-Bold.ttf') format('truetype');
+  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('../OpenSans/OpenSans-BoldItalic.ttf') format('truetype');
+  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('../OpenSans/OpenSans-ExtraBold.ttf') format('truetype');
+  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('../OpenSans/OpenSans-ExtraBoldItalic.ttf') format('truetype');
+  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('../OpenSans/OpenSans-Light.ttf') format('truetype');
+  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('../OpenSans/OpenSans-LightItalic.ttf') format('truetype');
+  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('../OpenSans/OpenSans-Italic.ttf') format('truetype');
+  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('../OpenSans/OpenSans-SemiBold.ttf') format('truetype');
+  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('../OpenSans/OpenSans-Regular.ttf') format('truetype');
+  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('../OpenSans/OpenSans-SemiBoldItalic.ttf') format('truetype');
+  font-family: 'Open Sans SemiBoldItalic';
+  font-weight: 600px;
+  font-style: italic;
+  font-display: swap;
+  src: url('../../fonts/OpenSans/OpenSans-SemiBoldItalic.ttf') format('truetype');
 }

+ 61 - 61
modules/resources/src/main/resources/io/github/palexdev/mfxresources/fonts/Roboto/Roboto.css

@@ -17,97 +17,97 @@
  */
 
 @font-face {
-	font-family: 'Roboto Black';
-	font-weight: 900px;
-	font-style: normal;
-	font-display: swap;
-	src: url('../Roboto/Roboto-Black.ttf') format('truetype');
+  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('../Roboto/Roboto-BlackItalic.ttf') format('truetype');
+  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('../Roboto/Roboto-Bold.ttf') format('truetype');
+  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('../Roboto/Roboto-BoldItalic.ttf') format('truetype');
+  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('../Roboto/Roboto-Light.ttf') format('truetype');
+  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('../Roboto/Roboto-Italic.ttf') format('truetype');
+  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('../Roboto/Roboto-LightItalic.ttf') format('truetype');
+  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('../Roboto/Roboto-Medium.ttf') format('truetype');
+  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('../Roboto/Roboto-MediumItalic.ttf') format('truetype');
+  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('../Roboto/Roboto-Regular.ttf') format('truetype');
+  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('../Roboto/Roboto-ThinItalic.ttf') format('truetype');
+  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('../Roboto/Roboto-Thin.ttf') format('truetype');
-}
+  font-family: 'Roboto Thin';
+  font-weight: 100px;
+  font-style: normal;
+  font-display: swap;
+  src: url('../../fonts/Roboto/Roboto-Thin.ttf') format('truetype');
+}

+ 17 - 0
modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/abstracts/_common_utils.scss

@@ -0,0 +1,17 @@
+@use "sass:string";
+
+/// Replace `$search` with `$replace` in `$string`
+/// @author Kitty Giraudel
+/// @param {String} $string - Initial string
+/// @param {String} $search - Substring to replace
+/// @param {String} $replace ('') - New value
+/// @return {String} - Updated string
+@function str-replace($string, $search, $replace: '') {
+  $index: string.index($string, $search);
+
+  @if $index {
+    @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
+  }
+
+  @return $string;
+}

+ 6 - 17
modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/abstracts/_elevation_utils.scss

@@ -10,12 +10,6 @@
   @return map.get($elevation, $cLevel);
 }
 
-/// Returns a percentage corresponding to the given layer state, as described by MD3 guidelines.
-/// Valid values are: 'hover', 'focus/focused', 'pressed', 'dragged'.
-@function get_percent_state_layer($state) {
-  @return map.get($interaction_states, $state);
-}
-
 /// Mixes two colors according to the levels described by MD3 guidelines, each level corresponds to a certain percentage.
 ///
 /// @param {color} $color1 this is the color that will be applied on the second, in MD3 this is the surface tint color
@@ -34,19 +28,14 @@
   @return mix($tint, $surface, $percent);
 }
 
-/// Shortcut function to compute the color of a surface given its state.
-/// MD3 components have many layers/containers that are used to simulate elevation and interaction states.
-/// This is not really feasible for a desktop UI, where using as less sub-components as possible is recommended for
-/// performance reasons.
-/// So the other way to compute the color given an interaction state, is to mix the two colors.
-/// The part that is not explicitly explained by the guidelines is that you can have both the elevation and interaction
-/// layers 'active' at the same time. This means that the resulting color is the result of: computing the elevation
-/// color (which itself is blend/mix operation) and then mix it with the interaction layer color.
-@function compute_state_layer($background, $layer_color, $state) {
-  @return mix($layer_color, $background, get_percent_state_layer($state));
+/// Shortcut function to compute the elevation for a 'color' with the provided
+/// tint/layer and given the level of the elevation.
+@function elevate($color, $layer, $level) {
+  $percent: get_percent_elevation($level);
+  @return mix(get_scheme_color($layer), get_scheme_color($color), $percent);
 }
 
-// TODO docs
+/// Returns the value associated with the given shape type as specified by the tokens module
 @function get_shape($type) {
   @return unquote(map.get($shapes, $type));
 }

+ 15 - 0
modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/abstracts/_ripple_utils.scss

@@ -0,0 +1,15 @@
+@use "theme_utils" as *;
+@use '../tokens/tokens' as *;
+
+/// Computes the color of the ripple layer given the base color and the interaction/layer state
+@function ripple_layer($color, $layer_state) {
+  @return rgba(get_scheme_color($color), get_percent_state_layer($layer_state));
+}
+
+/// Shortcut for the ripple_layer function with 'pressed' as the interaction state.
+/// On the web, the ripple layer is an actual container that changes its color based on the user interaction with the
+/// component. Here on the desktop the ripple is not a layer but just an effect, because of this, the ripple color
+/// will always refer to the 'pressed' state.
+@function ripple($color) {
+  @return ripple_layer($color, 'pressed');
+}

+ 19 - 0
modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/abstracts/_theme_utils.scss

@@ -25,4 +25,23 @@
 @function get_scheme_color($color_type) {
   $key: '--md-sys-color-' + $color_type;
   @return map.get($scheme, $key);
+}
+
+/// Returns a percentage corresponding to the given layer state, as described by MD3 guidelines.
+/// Valid values are: 'hover', 'focus/focused', 'pressed', 'dragged'.
+@function get_percent_state_layer($state) {
+  @return map.get($interaction_states, $state);
+}
+
+/// Shortcut function to compute the color of a surface given its state.
+/// MD3 components have many layers/containers that are used to simulate elevation and interaction states.
+/// This is not really feasible for a desktop UI, where using as less sub-components as possible is recommended for
+/// performance reasons.
+/// So the other way to compute the color given an interaction state, is to mix the two colors.
+/// The part that is not explicitly explained by the guidelines is that you can have both the elevation and interaction
+/// layers 'active' at the same time. This means that the resulting color is the result of: computing the elevation
+/// color (which itself is blend/mix operation) and then mix it with the interaction layer color.
+@function state_layer($surface, $overlay_color, $state) {
+  $percent: get_percent_state_layer($state);
+  @return $surface, rgba(get_scheme_color($overlay_color), $percent);
 }

+ 1 - 1
modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/components/_components.scss

@@ -1 +1 @@
-@use "buttons/elevated_button";
+@use "buttons/buttons";

+ 5 - 0
modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/components/buttons/_buttons.scss

@@ -0,0 +1,5 @@
+@use 'elevated_button';
+@use 'filled_button';
+@use 'tonal_filled_button';
+@use 'outlined_button';
+@use 'text_button';

+ 91 - 0
modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/components/buttons/_buttons_base.scss

@@ -0,0 +1,91 @@
+@use '../../abstracts/elevation_utils' as *;
+@use '../../abstracts/ripple_utils' as *;
+@use '../../abstracts/theme_utils' as *;
+@use '../../tokens/typography' as *;
+
+$padding: 0px 24px 0px 24px !default;
+$padding-i-left: 0px 24px 0px 16px !default;
+$padding-i-right: 0px 16px 0px 24px !default;
+
+@mixin base($surface, $onSurface, $text, $surfaceTint, $elevation) {
+  @include label_large();
+  -fx-text-fill: get_scheme_color($text);
+  @if (type-of($surface) == color) {
+    -fx-background-color: $surface;
+  } @else {
+    -fx-background-color: elevate($surface, $surfaceTint, $elevation);
+  }
+  -fx-background-radius: 20px;
+  -fx-pref-height: 40px;
+  -fx-padding: $padding;
+  -fx-graphic-text-gap: 8px;
+  -mfx-elevation: unquote("LEVEL" + $elevation);
+}
+
+@mixin outline() {
+  -fx-border-color: get_scheme_color('outline');
+  -fx-border-radius: 20px;
+
+  &:disabled {
+    -fx-border-color: rgba(get_scheme_color('outline'), 12%);
+  }
+}
+
+@mixin disabled() {
+  &:disabled {
+    -fx-text-fill: rgba(get_scheme_color('on-surface'), 38%);
+    -fx-background-color: rgba(get_scheme_color('on-surface'), 12%);
+    -mfx-elevation: LEVEL0;
+  }
+}
+
+@mixin hover($surface, $surfaceTint, $elevation) {
+  &:hover {
+    @if (type-of($surface) != string) {
+      -fx-background-color: $surface;
+    } @else {
+      $bcolor: elevate($surface, $surfaceTint, $elevation);
+      -fx-background-color: state_layer($bcolor, $surfaceTint, 'hover');
+    }
+    -mfx-elevation: unquote("LEVEL" + $elevation);
+  }
+}
+
+@mixin focus_press($surface, $surfaceTint, $elevation) {
+  &:focused,
+  &:pressed {
+    @if (type-of($surface) != string) {
+      -fx-background-color: $surface;
+    } @else {
+      $bcolor: elevate($surface, $surfaceTint, $elevation);
+      -fx-background-color: state_layer($bcolor, $surfaceTint, 'focused');
+    }
+    -mfx-elevation: unquote("LEVEL" + $elevation);
+  }
+}
+
+@mixin icon($color) {
+  .mfx-font-icon {
+    -mfx-color: get_scheme_color($color);
+    -mfx-size: 18px;
+  }
+
+  &:disabled .mfx-font-icon {
+    -mfx-color: rgba(get_scheme_color('on-surface'), 38%);
+  }
+
+  &:with-icon-left {
+    -fx-padding: $padding-i-left;
+  }
+
+  &:with-icon-right {
+    -fx-padding: $padding-i-right;
+  }
+
+}
+
+@mixin ripple($color) {
+  .mfx-ripple-generator {
+    -mfx-ripple-color: ripple($color);
+  }
+}

+ 14 - 55
modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/components/buttons/_elevated_button.scss

@@ -1,61 +1,20 @@
-@use '../../abstracts/elevation_utils' as *;
-@use '../../abstracts/theme_utils' as *;
-@use '../../tokens/typography' as *;
+@use 'buttons_base';
 
 /****************************************************************************************************
  * Elevated Buttons
  ****************************************************************************************************/
-.mfx-button.elevated {
-  @include label_large();
-  -fx-text-fill: get_scheme_color('primary');
-  -fx-background-color: elevate_surface(1);
-  -fx-background-radius: 20px;
-  // TODO add shadow
-  -fx-pref-height: 40px;
-  -fx-padding: 0px 24px 0px 24px;
-  -fx-graphic-text-gap: 8px;
-}
-
-.mfx-button.elevated:with-icon-left {
-  -fx-padding: 0px 16px 0px 24px;
-}
-
-.mfx-button.elevated:with-icon-right {
-  -fx-padding: 0px 24px 0px 16px;
-}
-
-.mfx-button.elevated .mfx-font-icon {
-  -mfx-color: get_scheme_color('primary');
-  -mfx-size: 18px;
-}
-
-.mfx-button.elevated:disabled {
-  -fx-background-color: rgba(get_scheme_color('on-surface'), 12%);
-  // TODO elevation goes to 0 (remove shadow)
-  -fx-text-fill: rgba(get_scheme_color('on-surface'), 38%);
-}
+$surface: 'surface';
+$onSurface: 'on-surface';
+$surfaceTint: 'surface-tint';
+$text: 'primary';
+$icon: 'primary';
+$ripple: 'primary';
 
-.mfx-button.elevated:disabled .mfx-font-icon {
-  -mfx-color: rgba(get_scheme_color('on-surface'), 38%);
-}
-
-.mfx-button.elevated:hover {
-  $bcolor: elevate_surface(2);
-  $layer_color: get_scheme_color('primary');
-  -fx-background-color: compute_state_layer($bcolor, $layer_color, 'hover');
-  // TODO elevation goes to 2
-}
-
-.mfx-button.elevated:focused {
-  $bcolor: elevate_surface(1);
-  $layer_color: get_scheme_color('primary');
-  -fx-background-color: compute_state_layer($bcolor, $layer_color, 'focused');
-  // TODO elevation goes to 1
-}
-
-.mfx-button.elevated:pressed {
-  $bcolor: elevate_surface(1);
-  $layer_color: get_scheme_color('primary');
-  -fx-background-color: compute_state_layer($bcolor, $layer_color, 'pressed');
-  // TODO elevation goes to 1
+.mfx-button.elevated {
+  @include buttons_base.base($surface, $onSurface, $text, $surfaceTint, 1);
+  @include buttons_base.disabled();
+  @include buttons_base.hover($surface, $surfaceTint, 2);
+  @include buttons_base.focus_press($surface, $surfaceTint, 1);
+  @include buttons_base.icon($icon);
+  @include buttons_base.ripple($ripple);
 }

+ 20 - 0
modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/components/buttons/_filled_button.scss

@@ -0,0 +1,20 @@
+@use 'buttons_base';
+
+/****************************************************************************************************
+ * Filled Buttons
+ ****************************************************************************************************/
+$surface: 'primary';
+$onSurface: 'on-primary';
+$surfaceTint: 'on-primary';
+$text: 'on-primary';
+$icon: 'on-primary';
+$ripple: 'on-primary';
+
+.mfx-button.filled {
+  @include buttons_base.base($surface, $onSurface, $text, $surfaceTint, 0);
+  @include buttons_base.disabled();
+  @include buttons_base.hover($surface, $surfaceTint, 1);
+  @include buttons_base.focus_press($surface, $surfaceTint, 0);
+  @include buttons_base.icon($icon);
+  @include buttons_base.ripple($ripple);
+}

+ 22 - 0
modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/components/buttons/_outlined_button.scss

@@ -0,0 +1,22 @@
+@use 'buttons_base';
+
+/****************************************************************************************************
+ * Outlined Buttons
+ ****************************************************************************************************/
+
+$surface: 'surface';
+$onSurface: 'primary';
+$surfaceTint: 'primary';
+$text: 'primary';
+$icon: 'primary';
+$ripple: 'primary';
+
+.mfx-button.outlined {
+  @include buttons_base.outline();
+  @include buttons_base.base($surface, $onSurface, $text, $surfaceTint, 0);
+  @include buttons_base.disabled();
+  @include buttons_base.hover($surface, $surfaceTint, 0);
+  @include buttons_base.focus_press($surface, $surfaceTint, 0);
+  @include buttons_base.icon($icon);
+  @include buttons_base.ripple($ripple);
+}

+ 31 - 0
modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/components/buttons/_text_button.scss

@@ -0,0 +1,31 @@
+// noinspection
+@use "../../abstracts/theme_utils" as *;
+@use "buttons_base";
+
+/****************************************************************************************************
+ * Text Buttons
+ ****************************************************************************************************/
+$surface: transparent;
+$text: 'primary';
+$icon: 'primary';
+$ripple: 'primary';
+
+.mfx-button.text {
+  buttons_base.$padding:
+
+  0px 12px 0px 12px;
+
+  buttons_base.$padding-i-left:
+
+  0px 16px 0px 12px;
+
+  buttons_base.$padding-i-right:
+
+  0px 12px 0px 16px;
+  @include buttons_base.base($surface, $surface, $text, $surface, 0);
+  @include buttons_base.disabled();
+  @include buttons_base.hover(state_layer($surface, 'primary', 'hover'), $surface, 2);
+  @include buttons_base.focus_press(state_layer($surface, 'primary', 'pressed'), $surface, 1);
+  @include buttons_base.icon($icon);
+  @include buttons_base.ripple($ripple);
+}

+ 20 - 0
modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/components/buttons/_tonal_filled_button.scss

@@ -0,0 +1,20 @@
+@use 'buttons_base';
+
+/****************************************************************************************************
+ * Tonal Filled Buttons
+ ****************************************************************************************************/
+$surface: 'secondary-container';
+$onSurface: 'on-secondary-container';
+$surfaceTint: 'on-secondary-container';
+$text: 'on-secondary-container';
+$icon: 'on-secondary-container';
+$ripple: 'on-secondary-container';
+
+.mfx-button.tonal-filled {
+  @include buttons_base.base($surface, $onSurface, $text, $surfaceTint, 0);
+  @include buttons_base.disabled();
+  @include buttons_base.hover($surface, $surfaceTint, 1);
+  @include buttons_base.focus_press($surface, $surfaceTint, 0);
+  @include buttons_base.icon($icon);
+  @include buttons_base.ripple($ripple);
+}

+ 11 - 8
modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/mfx-light.scss

@@ -123,13 +123,16 @@ $scheme: (
   --md-sys-color-on-surface: #1C1B1F,
   --md-sys-color-surface-variant: #E7E0EC,
   --md-sys-color-on-surface-variant: #49454F,
-  --md-sys-color-inverse-surface: #313033,
-  --md-sys-color-inverse-on-surface: #F4EFF4,
-  --md-sys-color-inverse-primary: #D0BCFF,
-  --md-sys-color-shadow: #000000,
-  --md-sys-color-surface-tint: #6750A4,
-  --md-sys-color-outline-variant: #CAC4D0,
-  --md-sys-color-scrim: #000000,
+        --md-sys-color-inverse-surface: #313033,
+        --md-sys-color-inverse-on-surface: #F4EFF4,
+        --md-sys-color-inverse-primary: #D0BCFF,
+        --md-sys-color-shadow: #000000,
+        --md-sys-color-surface-tint: #6750A4,
+        --md-sys-color-outline-variant: #CAC4D0,
+        --md-sys-color-scrim: #000000,
 );
 
-@use 'components/components';
+@use 'components/components';
+@import "../../fonts/Fonts.css";
+
+@include expose_tokens();

+ 34 - 15
modules/resources/src/main/resources/io/github/palexdev/mfxresources/sass/md3/tokens/_tokens.scss

@@ -1,12 +1,14 @@
+@use '../abstracts/common_utils' as *;
+
 $variant: 'light' !default;
 $palette: () !default;
 $scheme: () !default;
 $elevation: (
-  0: 0%,
-  1: 5%,
-  2: 8%,
-  3: 11%,
-  4: 12%,
+        0: 0%,
+        1: 5%,
+        2: 8%,
+        3: 11%,
+        4: 12%,
   5: 14%,
 ) !default;
 $interaction_states: (
@@ -19,13 +21,30 @@ $interaction_states: (
 $shapes: (
   'none': '0px',
   'extra-small': '4px',
-  'extra-small-top': '4px 4px 0px 0px',
-  'small': '8px',
-  'medium': '12px',
-  'large': '16px',
-  'large-end': '0px 16px 16px 0px',
-  'large-top': '16px 16px 0px 0px',
-  'extra-large': '28px',
-  'extra-large-top': '28px 28px 0px 0px',
-  'full': '100%',
-);
+        'extra-small-top': '4px 4px 0px 0px',
+        'small': '8px',
+        'medium': '12px',
+        'large': '16px',
+        'large-end': '0px 16px 16px 0px',
+        'large-top': '16px 16px 0px 0px',
+        'extra-large': '28px',
+        'extra-large-top': '28px 28px 0px 0px',
+        'full': '100%',
+);
+
+@mixin expose_tokens() {
+  * {
+    /* Palette */
+    @each $token, $value in $palette {
+      @if (str-slice($token, 1, 1) != '-') {
+        #{'-' + $token}: $value;
+      } @else {
+        #{str-replace($token, '--', '-')}: $value;
+      }
+    }
+    /* Scheme */
+    @each $token, $value in $scheme {
+      #{str-replace($token, '--', '-')}: $value;
+    }
+  }
+}

+ 3 - 8
modules/resources/src/test/java/interactive/IconsTests.java

@@ -41,11 +41,8 @@ import org.testfx.api.FxRobot;
 import org.testfx.api.FxToolkit;
 import org.testfx.framework.junit5.ApplicationExtension;
 import org.testfx.framework.junit5.Start;
-import org.testfx.util.WaitForAsyncUtils;
 
 import java.util.Optional;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
@@ -218,14 +215,12 @@ public class IconsTests {
 			wrapper.getIcon().setDescription(FontAwesomeRegular.SQUARE.getDescription());
 			wrapper.setStyle("-mfx-enable-ripple: true;\n-mfx-round: true;\n");
 		});
-		wrapper.getRippleGenerator().setRippleRadius(128.0);
-		wrapper.getRippleGenerator().setOnAnimationFinished(e -> ripple.set(true));
+		wrapper.getRippleGenerator().setRipplePrefSize(128.0);
 		assertNotNull(wrapper.getClip());
 		assertEquals(2, wrapper.getChildren().size());
 		robot.clickOn(wrapper);
-		WaitForAsyncUtils.waitFor(1, TimeUnit.SECONDS, CompletableFuture.supplyAsync(ripple::get));
-		Thread.sleep(sleep);
-		assertTrue(ripple.get());
+		Thread.sleep(1000);
+		assertEquals(0, wrapper.getRippleGenerator().getChildrenUnmodifiable().size());
 
 		robot.interact(() -> wrapper.setStyle(null));
 		assertNull(wrapper.getClip());