Browse Source

Add new material control: MFXCheckbox
Updated demo to use a TabbedPane, each tab contains a control preview
The ripple generator has been modified for better consistency:
-the generator accepts all nodes that are instances of Region
-it's possible to decide the ripple's clip type
-it's possible to decide to animate the background color of the Region or not
-it's possible to decide to animate the shadow or not
Added documentation for RippleGenerator
Added convenience classes: HexToRGBColor, NodeUtils

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

PAlex404 5 years ago
parent
commit
c342849c59
41 changed files with 1036 additions and 61 deletions
  1. 3 2
      demo/build.gradle
  2. 3 3
      demo/src/main/java/it/paprojects/materialfx/demo/Demo.java
  3. 2 1
      demo/src/main/java/it/paprojects/materialfx/demo/MFXResources.java
  4. 56 0
      demo/src/main/resources/it/paprojects/materialfx/demo/buttons_demo.css
  5. 70 0
      demo/src/main/resources/it/paprojects/materialfx/demo/buttons_demo.fxml
  6. 5 0
      demo/src/main/resources/it/paprojects/materialfx/demo/checkboxes_demo.css
  7. 97 0
      demo/src/main/resources/it/paprojects/materialfx/demo/checkboxes_demo.fxml
  8. 25 0
      demo/src/main/resources/it/paprojects/materialfx/demo/common.css
  9. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Comfortaa/Comfortaa-Bold.ttf
  10. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Comfortaa/Comfortaa-Light.ttf
  11. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Comfortaa/Comfortaa-Medium.ttf
  12. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Comfortaa/Comfortaa-Regular.ttf
  13. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Comfortaa/Comfortaa-SemiBold.ttf
  14. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Comfortaa/Comfortaa-VariableFont_wght.ttf
  15. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-Black.ttf
  16. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-BlackItalic.ttf
  17. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-Bold.ttf
  18. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-BoldItalic.ttf
  19. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-Italic.ttf
  20. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-Light.ttf
  21. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-LightItalic.ttf
  22. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-Medium.ttf
  23. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-MediumItalic.ttf
  24. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-Regular.ttf
  25. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-Thin.ttf
  26. BIN
      demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-ThinItalic.ttf
  27. 37 0
      demo/src/main/resources/it/paprojects/materialfx/demo/main_demo.css
  28. 20 0
      demo/src/main/resources/it/paprojects/materialfx/demo/main_demo.fxml
  29. 0 1
      materialfx/src/main/java/it/paprojects/materialfx/controls/MFXButton.java
  30. 182 0
      materialfx/src/main/java/it/paprojects/materialfx/controls/MFXCheckbox.java
  31. 6 0
      materialfx/src/main/java/it/paprojects/materialfx/controls/enums/ButtonType.java
  32. 26 0
      materialfx/src/main/java/it/paprojects/materialfx/controls/enums/MarkType.java
  33. 38 0
      materialfx/src/main/java/it/paprojects/materialfx/effects/RippleClipType.java
  34. 86 46
      materialfx/src/main/java/it/paprojects/materialfx/effects/RippleGenerator.java
  35. 1 2
      materialfx/src/main/java/it/paprojects/materialfx/skins/MFXButtonSkin.java
  36. 220 0
      materialfx/src/main/java/it/paprojects/materialfx/skins/MFXCheckboxSkin.java
  37. 0 6
      materialfx/src/main/java/it/paprojects/materialfx/utils/ButtonType.java
  38. 24 0
      materialfx/src/main/java/it/paprojects/materialfx/utils/HexToRGBColor.java
  39. 89 0
      materialfx/src/main/java/it/paprojects/materialfx/utils/NodeUtils.java
  40. 1 0
      materialfx/src/main/java/module-info.java
  41. 45 0
      materialfx/src/main/resources/it/paprojects/materialfx/css/mfx-checkbox.css

+ 3 - 2
demo/build.gradle

@@ -13,5 +13,6 @@ dependencies {
     implementation "fr.brouillard.oss:cssfx:11.4.0"
     implementation project(':materialfx')
 }
-
-mainClassName = 'it.paprojects.materialfx.demo.Demo'
+application {
+    mainClassName = 'MaterialFX.demo.main/it.paprojects.materialfx.demo.Demo'
+}

+ 3 - 3
demo/src/main/java/it/paprojects/materialfx/demo/Demo.java

@@ -15,10 +15,10 @@ public class Demo extends Application {
     public void start(Stage primaryStage) throws IOException {
         CSSFX.start();
 
-        AnchorPane anchorPane = FXMLLoader.load(MFXResources.load("buttons_demo.fxml"));
+        AnchorPane demo = FXMLLoader.load(MFXResources.load("main_demo.fxml"));
 
-        primaryStage.setTitle("HELLO THERE");
-        primaryStage.setScene(new Scene(anchorPane));
+        primaryStage.setTitle("MaterialFX Demo - Features Preview");
+        primaryStage.setScene(new Scene(demo));
         primaryStage.show();
     }
 

+ 2 - 1
demo/src/main/java/it/paprojects/materialfx/demo/MFXResources.java

@@ -8,7 +8,8 @@ import java.net.URL;
  */
 public class MFXResources {
 
-    private MFXResources() {}
+    private MFXResources() {
+    }
 
     public static URL load(String path) {
         return MFXResources.class.getResource(path);

+ 56 - 0
demo/src/main/resources/it/paprojects/materialfx/demo/buttons_demo.css

@@ -0,0 +1,56 @@
+@import url("common.css");
+
+.flat-button {
+    -fx-background-color: transparent;
+}
+
+.flat-button .ripple-generator {
+    -mfx-ripple-color: lightgray;
+}
+
+.flat-button .text {
+    -fx-font-family: Comfortaa-SemiBold;
+    -fx-font-size: 12;
+    -fx-font-weight: bold;
+}
+
+.raised-button {
+    -fx-background-color: white;
+    -mfx-button-type: raised;
+}
+
+.raised-button .ripple-generator {
+    -mfx-ripple-color: lightgray;
+}
+
+.raised-button .text {
+    -fx-font-family: Comfortaa-SemiBold;
+    -fx-font-size: 12;
+    -fx-font-weight: bold;
+}
+
+#colored-raised {
+    -fx-background-color: #b700ff;
+    -fx-text-fill: white;
+}
+
+#colored-raised .text {
+    -fx-fill: white;
+}
+
+#colored-raised .ripple-generator {
+    -mfx-ripple-color: rgba(255, 255, 255, 0.4);
+}
+
+#custom-ripple {
+    -fx-background-color: #55bbff;
+    -fx-text-fill: white;
+}
+
+#custom-ripple .text {
+    -fx-fill: white;
+}
+
+#custom-ripple .ripple-generator {
+    -mfx-ripple-color: #1900ff;
+}

+ 70 - 0
demo/src/main/resources/it/paprojects/materialfx/demo/buttons_demo.fxml

@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+<?import it.paprojects.materialfx.controls.*?>
+<?import javafx.geometry.*?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.layout.*?>
+<?import javafx.scene.text.Font?>
+<StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0"
+           prefWidth="600.0" stylesheets="@buttons_demo.css" xmlns="http://javafx.com/javafx/11.0.1">
+    <MFXButton rippleRadius="40.0" styleClass="flat-button" StackPane.alignment="TOP_CENTER">
+        <font>
+            <Font name="Comic Sans MS" size="14.0"/>
+        </font>
+        <StackPane.margin>
+            <Insets right="180.0" top="65.0"/>
+        </StackPane.margin>
+    </MFXButton>
+    <MFXButton rippleColor="#d5d5d5" rippleRadius="40.0" styleClass="raised-button" text="Button"
+               StackPane.alignment="TOP_CENTER">
+        <StackPane.margin>
+            <Insets right="180.0" top="165.0"/>
+        </StackPane.margin>
+    </MFXButton>
+    <MFXButton rippleRadius="40.0" styleClass="flat-button" textFill="#d400ff" StackPane.alignment="TOP_CENTER">
+        <font>
+            <Font name="Comic Sans MS" size="14.0"/>
+        </font>
+        <StackPane.margin>
+            <Insets top="65.0"/>
+        </StackPane.margin>
+    </MFXButton>
+    <MFXButton id="colored-raised" prefHeight="26.0" prefWidth="55.0" rippleColor="#d5d5d5" rippleRadius="40.0"
+               styleClass="raised-button" StackPane.alignment="TOP_CENTER">
+        <StackPane.margin>
+            <Insets top="165.0"/>
+        </StackPane.margin>
+    </MFXButton>
+    <MFXButton disable="true" rippleRadius="40.0" styleClass="flat-button" StackPane.alignment="TOP_CENTER">
+        <font>
+            <Font name="Comic Sans MS" size="14.0"/>
+        </font>
+        <StackPane.margin>
+            <Insets left="180.0" top="65.0"/>
+        </StackPane.margin>
+    </MFXButton>
+    <MFXButton disable="true" rippleColor="#d5d5d5" rippleRadius="40.0" styleClass="raised-button"
+               StackPane.alignment="TOP_CENTER">
+        <StackPane.margin>
+            <Insets left="180.0" top="165.0"/>
+        </StackPane.margin>
+    </MFXButton>
+    <MFXButton id="custom-ripple" prefHeight="26.0" prefWidth="150.0" rippleRadius="50.0" styleClass="raised-button"
+               text="Custom Ripple" StackPane.alignment="CENTER">
+        <StackPane.margin>
+            <Insets top="60.0"/>
+        </StackPane.margin>
+    </MFXButton>
+    <Label alignment="CENTER" prefHeight="26.0" prefWidth="266.0" text="Flat buttons" StackPane.alignment="TOP_CENTER">
+        <StackPane.margin>
+            <Insets top="20.0"/>
+        </StackPane.margin>
+    </Label>
+    <Label alignment="CENTER" prefHeight="26.0" prefWidth="266.0" text="Raised buttons"
+           StackPane.alignment="TOP_CENTER">
+        <StackPane.margin>
+            <Insets top="120.0"/>
+        </StackPane.margin>
+    </Label>
+</StackPane>

+ 5 - 0
demo/src/main/resources/it/paprojects/materialfx/demo/checkboxes_demo.css

@@ -0,0 +1,5 @@
+@import url("common.css");
+
+#custom-ripple .ripple-container .ripple-generator {
+    -mfx-ripple-color: rgb(195, 255, 190);
+}

+ 97 - 0
demo/src/main/resources/it/paprojects/materialfx/demo/checkboxes_demo.fxml

@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import it.paprojects.materialfx.controls.*?>
+<?import javafx.geometry.*?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.layout.*?>
+<?import javafx.scene.paint.LinearGradient?>
+<?import javafx.scene.paint.Stop?>
+<StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0"
+           prefWidth="600.0" stylesheets="@checkboxes_demo.css" xmlns="http://javafx.com/javafx/11.0.1">
+    <Label alignment="CENTER" prefHeight="26.0" prefWidth="266.0" text="Checkboxes color"
+           StackPane.alignment="TOP_CENTER">
+        <StackPane.margin>
+            <Insets top="20.0"/>
+        </StackPane.margin>
+    </Label>
+    <MFXCheckbox allowIndeterminate="true" StackPane.alignment="TOP_CENTER">
+        <StackPane.margin>
+            <Insets right="200.0" top="65.0"/>
+        </StackPane.margin>
+    </MFXCheckbox>
+    <MFXCheckbox allowIndeterminate="true" checkedColor="#b200ff" markType="VARIANT6" uncheckedColor="RED"
+                 StackPane.alignment="TOP_CENTER">
+        <StackPane.margin>
+            <Insets top="65.0"/>
+        </StackPane.margin>
+    </MFXCheckbox>
+    <MFXCheckbox allowIndeterminate="true" uncheckedColor="#008bff" StackPane.alignment="TOP_CENTER">
+        <StackPane.margin>
+            <Insets left="200.0" top="65.0"/>
+        </StackPane.margin>
+        <checkedColor>
+            <LinearGradient endX="1.0" endY="1.0">
+                <stops>
+                    <Stop color="#0098ff"/>
+                    <Stop color="#00ffcb" offset="0.8748091603053434"/>
+                    <Stop color="#00ffcb" offset="1.0"/>
+                </stops>
+            </LinearGradient>
+        </checkedColor>
+    </MFXCheckbox>
+    <MFXCheckbox id="custom-ripple" prefHeight="30.0" prefWidth="164.0" text="CheckBox Custom Ripple"
+                 StackPane.alignment="TOP_CENTER">
+        <StackPane.margin>
+            <Insets top="110.0"/>
+        </StackPane.margin>
+        <opaqueInsets>
+            <Insets/>
+        </opaqueInsets>
+    </MFXCheckbox>
+    <Label alignment="CENTER" prefHeight="26.0" prefWidth="266.0" text="Checkboxes Mark Types"/>
+    <MFXCheckbox markType="CASPIAN">
+        <StackPane.margin>
+            <Insets right="300.0" top="65.0"/>
+        </StackPane.margin>
+    </MFXCheckbox>
+    <MFXCheckbox>
+        <StackPane.margin>
+            <Insets right="100.0" top="65.0"/>
+        </StackPane.margin>
+    </MFXCheckbox>
+    <MFXCheckbox markType="VARIANT3">
+        <StackPane.margin>
+            <Insets left="100.0" top="65.0"/>
+        </StackPane.margin>
+    </MFXCheckbox>
+    <MFXCheckbox markType="VARIANT4">
+        <StackPane.margin>
+            <Insets left="300.0" top="65.0"/>
+        </StackPane.margin>
+    </MFXCheckbox>
+    <MFXCheckbox markType="VARIANT5">
+        <StackPane.margin>
+            <Insets right="300.0" top="130.0"/>
+        </StackPane.margin>
+    </MFXCheckbox>
+    <MFXCheckbox markType="VARIANT6">
+        <StackPane.margin>
+            <Insets right="100.0" top="130.0"/>
+        </StackPane.margin>
+    </MFXCheckbox>
+    <MFXCheckbox markType="VARIANT7">
+        <StackPane.margin>
+            <Insets left="100.0" top="130.0"/>
+        </StackPane.margin>
+    </MFXCheckbox>
+    <MFXCheckbox markType="VARIANT8">
+        <StackPane.margin>
+            <Insets left="300.0" top="130.0"/>
+        </StackPane.margin>
+    </MFXCheckbox>
+    <MFXCheckbox markType="VARIANT9">
+        <StackPane.margin>
+            <Insets top="195.0"/>
+        </StackPane.margin>
+    </MFXCheckbox>
+</StackPane>

+ 25 - 0
demo/src/main/resources/it/paprojects/materialfx/demo/common.css

@@ -0,0 +1,25 @@
+@font-face {
+    font-family: 'Comfortaa';
+    src: url('fonts/Comfortaa/Comfortaa-VariableFont_wght.ttf');
+}
+
+@font-face {
+    font-family: "Comfortaa SemiBold";
+    src: url('fonts/Comfortaa/Comfortaa-SemiBold.ttf');
+}
+
+@font-face {
+    font-family: Roboto;
+    src: url('fonts/Roboto/Roboto-Regular.ttf');
+}
+
+.label {
+    -fx-background-color: linear-gradient(to bottom right, #0093E9 0%, #7be4bc 100%);
+    -fx-background-radius: 7;
+    -fx-border-color: lightgray;
+    -fx-border-radius: 5;
+    -fx-border-width: 2;
+    -fx-font-family: Comfortaa-SemiBold;
+    -fx-font-size: 12.5;
+    -fx-text-fill: white;
+}

BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Comfortaa/Comfortaa-Bold.ttf


BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Comfortaa/Comfortaa-Light.ttf


BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Comfortaa/Comfortaa-Medium.ttf


BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Comfortaa/Comfortaa-Regular.ttf


BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Comfortaa/Comfortaa-SemiBold.ttf


BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Comfortaa/Comfortaa-VariableFont_wght.ttf


BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-Black.ttf


BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-BlackItalic.ttf


BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-Bold.ttf


BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-BoldItalic.ttf


BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-Italic.ttf


BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-Light.ttf


BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-LightItalic.ttf


BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-Medium.ttf


BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-MediumItalic.ttf


BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-Regular.ttf


BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-Thin.ttf


BIN
demo/src/main/resources/it/paprojects/materialfx/demo/fonts/Roboto/Roboto-ThinItalic.ttf


+ 37 - 0
demo/src/main/resources/it/paprojects/materialfx/demo/main_demo.css

@@ -0,0 +1,37 @@
+.tab {
+    -fx-background-color: #fbfbfb;
+    -fx-border-width: 0 0 1 0;
+    -fx-border-color: #c2c2c2 #c2c2c2 #c2c2c2 #c2c2c2
+}
+
+.tab:selected {
+    -fx-background-radius: 0;
+    -fx-background-insets: 0;
+    -fx-background-color: #fbfbfb;
+    -fx-border-width: 0 0 5 0;
+    -fx-border-color: #c2c2c2 #c2c2c2 linear-gradient(to bottom right, #0093E9 0%, #7be4bc 100%) #c2c2c2
+}
+
+.tab-pane *.tab-header-background {
+    -fx-background-color: #fbfbfb, #fbfbfb, #fbfbfb;
+    -fx-border-width: 1 0 1 0;
+    -fx-border-color: #c2c2c2 #c2c2c2 #c2c2c2 #c2c2c2
+}
+
+.tab-pane {
+    -fx-background-color: WHITE;
+}
+
+/*.tab-pane .tab {
+    -fx-background-color: WHITE;
+    -fx-background-radius: 6;
+    -fx-border-color: gray;
+    -fx-border-radius: 6;
+    -fx-border-width: 2.5;
+    -fx-background-insets: 0 3 0 0;
+    -fx-border-insets: 0 3 0 0;
+}*/
+
+.tab-pane:focused > .tab-header-area > .headers-region > .tab:selected .focus-indicator {
+    -fx-border-color: transparent;
+}

+ 20 - 0
demo/src/main/resources/it/paprojects/materialfx/demo/main_demo.fxml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Tab?>
+<?import javafx.scene.control.TabPane?>
+<?import javafx.scene.layout.*?>
+<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="360.0"
+            prefWidth="600.0" stylesheets="@main_demo.css" xmlns="http://javafx.com/javafx/11.0.1"
+            xmlns:fx="http://javafx.com/fxml/1">
+    <TabPane prefHeight="430.0" prefWidth="600.0" tabClosingPolicy="UNAVAILABLE" AnchorPane.bottomAnchor="0.0"
+             AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
+        <Tab text="Buttons demo">
+            <StackPane prefHeight="258.0" prefWidth="600.0">
+                <fx:include maxWidth="-Infinity" source="buttons_demo.fxml" StackPane.alignment="CENTER"/>
+            </StackPane>
+        </Tab>
+        <Tab text="Checkbox demo">
+            <fx:include source="checkboxes_demo.fxml"/>
+        </Tab>
+    </TabPane>
+</AnchorPane>

+ 0 - 1
materialfx/src/main/java/it/paprojects/materialfx/controls/MFXButton.java

@@ -219,7 +219,6 @@ public class MFXButton extends Button {
     //================================================================================
     // Override Methods
     //================================================================================
-
     @Override
     protected Skin<?> createDefaultSkin() {
         MFXButtonSkin skin = new MFXButtonSkin(this, depthLevel.get());

+ 182 - 0
materialfx/src/main/java/it/paprojects/materialfx/controls/MFXCheckbox.java

@@ -0,0 +1,182 @@
+package it.paprojects.materialfx.controls;
+
+import it.paprojects.materialfx.MFXResources;
+import it.paprojects.materialfx.controls.enums.MarkType;
+import it.paprojects.materialfx.effects.RippleClipType;
+import it.paprojects.materialfx.effects.RippleGenerator;
+import it.paprojects.materialfx.skins.MFXCheckboxSkin;
+import javafx.css.*;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.Skin;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.Pane;
+import javafx.scene.paint.Color;
+import javafx.scene.paint.Paint;
+import javafx.util.Duration;
+
+import java.util.List;
+
+public class MFXCheckbox extends CheckBox {
+    //================================================================================
+    // Properties
+    //================================================================================
+    private static final StyleablePropertyFactory<MFXCheckbox> FACTORY = new StyleablePropertyFactory<>(CheckBox.getClassCssMetaData());
+    private final String STYLE_CLASS = "mfx-checkbox";
+    private final String STYLESHEET = MFXResources.load("css/mfx-checkbox.css").toString();
+    private RippleGenerator rippleGenerator;
+
+    //================================================================================
+    // Constructors
+    //================================================================================
+    public MFXCheckbox() {
+        setText("CheckBox");
+        init();
+    }
+
+    public MFXCheckbox(String text) {
+        super(text);
+        init();
+    }
+
+    //================================================================================
+    // Methods
+    //================================================================================
+    private void init() {
+        getStyleClass().add(STYLE_CLASS);
+    }
+
+    //================================================================================
+    // Stylesheet properties
+    //================================================================================
+    private final StyleableObjectProperty<Paint> checkedColor = new SimpleStyleableObjectProperty<>(
+            StyleableProperties.CHECKED_COLOR,
+            this,
+            "checkedColor",
+            Color.rgb(15, 157, 88)
+    );
+
+    private final StyleableObjectProperty<Paint> uncheckedColor = new SimpleStyleableObjectProperty<>(
+            StyleableProperties.UNCHECKED_COLOR,
+            this,
+            "uncheckedColor",
+            Color.rgb(90, 90, 90)
+    );
+
+    private final StyleableObjectProperty<MarkType> markType = new SimpleStyleableObjectProperty<>(
+            StyleableProperties.MARK_TYPE,
+            this,
+            "markType",
+            MarkType.MODENA
+    );
+
+    public Paint getCheckedColor() {
+        return checkedColor.get();
+    }
+
+    public StyleableObjectProperty<Paint> checkedColorProperty() {
+        return checkedColor;
+    }
+
+    public void setCheckedColor(Paint checkedColor) {
+        this.checkedColor.set(checkedColor);
+    }
+
+    public Paint getUncheckedColor() {
+        return uncheckedColor.get();
+    }
+
+    public StyleableObjectProperty<Paint> uncheckedColorProperty() {
+        return uncheckedColor;
+    }
+
+    public void setUncheckedColor(Paint uncheckedColor) {
+        this.uncheckedColor.set(uncheckedColor);
+    }
+
+    public MarkType getMarkType() {
+        return markType.get();
+    }
+
+    public StyleableObjectProperty<MarkType> markTypeProperty() {
+        return markType;
+    }
+
+    public void setMarkType(MarkType markType) {
+        this.markType.set(markType);
+    }
+
+    //================================================================================
+    // CssMetaData
+    //================================================================================
+    private static class StyleableProperties {
+        private static final List<CssMetaData<? extends Styleable, ?>> cssMetaDataList;
+
+        private static final CssMetaData<MFXCheckbox, Paint> CHECKED_COLOR =
+                FACTORY.createPaintCssMetaData(
+                        "-mfx-checked-color",
+                        MFXCheckbox::checkedColorProperty,
+                        Color.rgb(15, 157, 88)
+                );
+
+        private static final CssMetaData<MFXCheckbox, Paint> UNCHECKED_COLOR =
+                FACTORY.createPaintCssMetaData(
+                        "-mfx-unchecked-color",
+                        MFXCheckbox::uncheckedColorProperty,
+                        Color.rgb(90, 90, 90)
+                );
+
+        private static final CssMetaData<MFXCheckbox, MarkType> MARK_TYPE =
+                FACTORY.createEnumCssMetaData(
+                        MarkType.class,
+                        "-mfx-mark-type",
+                        MFXCheckbox::markTypeProperty,
+                        MarkType.MODENA
+                );
+
+        static {
+            cssMetaDataList = List.of(CHECKED_COLOR, UNCHECKED_COLOR, MARK_TYPE);
+        }
+    }
+
+    public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaDataList() {
+        return MFXCheckbox.StyleableProperties.cssMetaDataList;
+    }
+
+    //================================================================================
+    // Override Methods
+    //================================================================================
+
+    @Override
+    protected Skin<?> createDefaultSkin() {
+        MFXCheckboxSkin skin = new MFXCheckboxSkin(this);
+        Pane rippleContainer = skin.getRippleContainer();
+
+        rippleGenerator = new RippleGenerator(rippleContainer, RippleClipType.NOCLIP);
+        rippleGenerator.setRippleRadius(18);
+        rippleGenerator.setInDuration(Duration.millis(400));
+        rippleGenerator.setAnimateBackground(false);
+        rippleContainer.getChildren().add(0, rippleGenerator);
+
+        /* Listener on control but if the coordinates of the event are greater than then ripple container size
+         * then the center of the ripple is set to the width and/or height of container
+         */
+        this.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
+            rippleGenerator.setGeneratorCenterX(Math.min(event.getX(), rippleContainer.getWidth()));
+            rippleGenerator.setGeneratorCenterY(Math.min(event.getY(), rippleContainer.getHeight()));
+            rippleGenerator.createRipple();
+        });
+
+        return skin;
+    }
+
+    @Override
+    public String getUserAgentStylesheet() {
+        return STYLESHEET;
+    }
+
+    @Override
+    public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
+        return this.getControlCssMetaDataList();
+    }
+
+}

+ 6 - 0
materialfx/src/main/java/it/paprojects/materialfx/controls/enums/ButtonType.java

@@ -0,0 +1,6 @@
+package it.paprojects.materialfx.controls.enums;
+
+public enum ButtonType {
+    FLAT,
+    RAISED
+}

+ 26 - 0
materialfx/src/main/java/it/paprojects/materialfx/controls/enums/MarkType.java

@@ -0,0 +1,26 @@
+package it.paprojects.materialfx.controls.enums;
+
+/**
+ * Convenience enum to keep and get SVG paths for different types of check marks
+ */
+public enum MarkType {
+    CASPIAN("M0,4H2L3,6L6,0H8L4,8H2Z"),
+    MODENA("M-0.25,6.083c0.843-0.758,4.583,4.833,5.75,4.833S14.5-1.5,15.917-0.917c1.292,0.532-8.75,17.083-10.5,17.083C3,16.167-1.083,6.833-0.25,6.083z"),
+    VARIANT3("M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"),
+    VARIANT4("M0 11c2.761.575 6.312 1.688 9 3.438 3.157-4.23 8.828-8.187 15-11.438-5.861 5.775-10.711 12.328-14 18.917-2.651-3.766-5.547-7.271-10-10.917z"),
+    VARIANT5("M20.303 4.846l.882.882-12.22 12.452-6.115-5.93.902-.902 5.303 5.028 11.248-11.53zm-.018-2.846l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285-3.715-3.715z"),
+    VARIANT6("M0 12.116l2.053-1.897c2.401 1.162 3.924 2.045 6.622 3.969 5.073-5.757 8.426-8.678 14.657-12.555l.668 1.536c-5.139 4.484-8.902 9.479-14.321 19.198-3.343-3.936-5.574-6.446-9.679-10.251z"),
+    VARIANT7("M0 11.522l1.578-1.626 7.734 4.619 13.335-12.526 1.353 1.354-14 18.646z"),
+    VARIANT8("M0 11.386l1.17-1.206c1.951.522 5.313 1.731 8.33 3.597 3.175-4.177 9.582-9.398 13.456-11.777l1.044 1.073-14 18.927-10-10.614z"),
+    VARIANT9("M24 6.278l-11.16 12.722-6.84-6 1.319-1.49 5.341 4.686 9.865-11.196 1.475 1.278zm-22.681 5.232l6.835 6.01-1.314 1.48-6.84-6 1.319-1.49zm9.278.218l5.921-6.728 1.482 1.285-5.921 6.756-1.482-1.313z");
+
+    private final String svhPath;
+
+    MarkType(String svhPath) {
+        this.svhPath = svhPath;
+    }
+
+    public String getSvhPath() {
+        return svhPath;
+    }
+}

+ 38 - 0
materialfx/src/main/java/it/paprojects/materialfx/effects/RippleClipType.java

@@ -0,0 +1,38 @@
+package it.paprojects.materialfx.effects;
+
+import javafx.scene.layout.Region;
+import javafx.scene.shape.Circle;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.shape.Shape;
+
+public enum RippleClipType {
+    CIRCLE {
+        @Override
+        public Shape buildClip(Region region) {
+            double radius = Math.sqrt(Math.pow(region.getWidth(), 2) + Math.pow(region.getHeight(), 2)) / 2;
+            return new Circle(radius);
+        }
+    },
+    RECTANGLE {
+        @Override
+        public Shape buildClip(Region region) {
+            return new Rectangle(region.getWidth(), region.getHeight());
+        }
+    },
+    NOCLIP {
+        @Override
+        public Shape buildClip(Region region) {
+            return null;
+        }
+    };
+
+    public static Circle buildClip(double radius) {
+        return new Circle(radius);
+    }
+
+    public static Rectangle buildClip(double width, double height) {
+        return new Rectangle(width, height);
+    }
+
+    public abstract Shape buildClip(Region region);
+}

+ 86 - 46
materialfx/src/main/java/it/paprojects/materialfx/effects/RippleGenerator.java

@@ -1,6 +1,5 @@
 package it.paprojects.materialfx.effects;
 
-import it.paprojects.materialfx.controls.MFXButton;
 import javafx.animation.*;
 import javafx.beans.property.DoubleProperty;
 import javafx.beans.property.ObjectProperty;
@@ -10,6 +9,7 @@ import javafx.css.*;
 import javafx.scene.Group;
 import javafx.scene.control.Control;
 import javafx.scene.effect.DropShadow;
+import javafx.scene.layout.Region;
 import javafx.scene.paint.Color;
 import javafx.scene.shape.Circle;
 import javafx.scene.shape.Rectangle;
@@ -26,31 +26,31 @@ public class RippleGenerator extends Group {
     private final String STYLE_CLASS = "ripple-generator";
     private static final StyleablePropertyFactory<RippleGenerator> FACTORY = new StyleablePropertyFactory<>(Group.getClassCssMetaData());
 
-    private final Control control;
+    private final Region region;
 
+    private RippleClipType rippleClipType = RippleClipType.RECTANGLE;
+    private DepthLevel level = null;
+    private boolean animateBackground = true;
     private final Interpolator rippleInterpolator = Interpolator.SPLINE(0.0825, 0.3025, 0.0875, 0.9975);
     //private final Interpolator rippleInterpolator = Interpolator.SPLINE(0.1, 0.50, 0.3, 0.85);
-    private final Interpolator easeIn = Interpolator.EASE_IN;
-    private final Interpolator easeOut = Interpolator.EASE_OUT;
-    private final Interpolator easeBoth = Interpolator.EASE_BOTH;
-    private  ObjectProperty<Color> rippleColor = new StyleableObjectProperty<>(Color.ROYALBLUE)
+    private final ObjectProperty<Color> rippleColor = new StyleableObjectProperty<>(Color.ROYALBLUE)
     {
         @Override public CssMetaData getCssMetaData() { return StyleableProperties.RIPPLE_COLOR; }
         @Override public Object getBean() { return this; }
-        @Override public String getName() { return "ledColor"; }
+        @Override public String getName() { return "rippleColor"; }
     };
     private final DoubleProperty rippleRadius = new SimpleDoubleProperty(10.0);
     private final ObjectProperty<Duration> inDuration = new SimpleObjectProperty<>(Duration.millis(700));
     private final ObjectProperty<Duration> outDuration = new SimpleObjectProperty<>(inDuration.get().divide(2));
 
-    private double generatorCenterX = 100.0;
-    private double generatorCenterY = 100.0;
+    private double generatorCenterX = 0.0;
+    private double generatorCenterY = 0.0;
 
     //================================================================================
     // Constructors
     //================================================================================
-    public RippleGenerator(Control control) {
-        this.control = control;
+    public RippleGenerator(Region region) {
+        this.region = region;
         getStyleClass().add(STYLE_CLASS);
         inDuration.addListener((observable, oldValue, newValue) -> {
             if (!newValue.equals(oldValue)) {
@@ -64,24 +64,47 @@ public class RippleGenerator extends Group {
         });
     }
 
+    public RippleGenerator(Region region, DepthLevel shadowLevel) {
+        this(region);
+        this.level = shadowLevel;
+    }
+
+    public RippleGenerator(Region region, RippleClipType rippleClipType) {
+        this(region);
+        this.rippleClipType = rippleClipType;
+    }
+
+    public RippleGenerator(Region region, DepthLevel shadowLevel, RippleClipType rippleClipType) {
+        this(region, shadowLevel);
+        this.rippleClipType = rippleClipType;
+    }
+
     //================================================================================
     // Methods
     //================================================================================
+
+    /**
+     * Creates a new {@code Ripple} at the specified coordinates.
+     * <p>
+     * Each {@code Ripple} is a new instance, this allows multiple ripples to be generated at the same time.
+     */
     public void createRipple() {
         final Ripple ripple = new Ripple(generatorCenterX, generatorCenterY);
         getChildren().add(ripple);
 
-        Rectangle fillRect = new Rectangle(control.getWidth(), control.getHeight());
-        fillRect.setFill(rippleColor.get());
-        fillRect.setOpacity(0);
-        getChildren().add(0, fillRect);
-
-        KeyValue keyValueIn = new KeyValue(fillRect.opacityProperty(), 0.3);
-        KeyValue keyValueOut = new KeyValue(fillRect.opacityProperty(), 0);
-        KeyFrame keyFrameIn = new KeyFrame(inDuration.get(), keyValueIn);
-        KeyFrame keyFrameOut = new KeyFrame(outDuration.get(), keyValueOut);
-        ripple.inAnimation.getKeyFrames().add(keyFrameIn);
-        ripple.outAnimation.getKeyFrames().add(keyFrameOut);
+        if (animateBackground) {
+            Rectangle fillRect = new Rectangle(region.getWidth(), region.getHeight());
+            fillRect.setFill(rippleColor.get());
+            fillRect.setOpacity(0);
+            getChildren().add(0, fillRect);
+
+            KeyValue keyValueIn = new KeyValue(fillRect.opacityProperty(), 0.3);
+            KeyValue keyValueOut = new KeyValue(fillRect.opacityProperty(), 0);
+            KeyFrame keyFrameIn = new KeyFrame(inDuration.get(), keyValueIn);
+            KeyFrame keyFrameOut = new KeyFrame(outDuration.get(), keyValueOut);
+            ripple.inAnimation.getKeyFrames().add(keyFrameIn);
+            ripple.outAnimation.getKeyFrames().add(keyFrameOut);
+        }
 
         ripple.parallelTransition.setOnFinished(event -> getChildren().remove(ripple));
         ripple.parallelTransition.play();
@@ -96,6 +119,14 @@ public class RippleGenerator extends Group {
         this.generatorCenterY = generatorCenterY;
     }
 
+    public void setAnimateBackground(boolean animateBackground) {
+        this.animateBackground = animateBackground;
+    }
+
+    public void setRippleClipType(RippleClipType rippleClipType) {
+        this.rippleClipType = rippleClipType;
+    }
+
     public Color getRippleColor() {
         return rippleColor.get();
     }
@@ -144,11 +175,15 @@ public class RippleGenerator extends Group {
         this.outDuration.set(outDuration);
     }
 
+    /**
+     * This class defines a ripple as a {@code Circle} and contains all it's properties, mainly
+     * it builds the animation of the ripple.
+     */
     private class Ripple extends Circle {
         //================================================================================
         // Properties
         //================================================================================
-        private final int shadowDelta = 2;
+        private final int shadowDelta = 1;
 
         private final Timeline inAnimation = new Timeline();
         private final Timeline outAnimation = new Timeline();
@@ -162,20 +197,18 @@ public class RippleGenerator extends Group {
         private Ripple(double centerX, double centerY) {
             super(centerX, centerY, 0, Color.TRANSPARENT);
             setFill(rippleColor.get());
-            Rectangle rectangle = new Rectangle(control.getWidth(), control.getHeight());
-            setClip(rectangle);
+            setClip(rippleClipType.buildClip(region));
             buildAnimation();
         }
 
         //================================================================================
         // Methods
         //================================================================================
-        private void buildAnimation() {
-            DropShadow buttonShadow = (DropShadow) ((MFXButton) control.getSkin().getSkinnable()).getEffect();
-            DepthLevel depthLevel = ((MFXButton) control).getDepthLevel();
-            DropShadow startShadow = shadowOf(depthLevel);
-            DropShadow endShadow = shadowOf(depthLevel, shadowDelta);
 
+        /**
+         * Build the ripple's animation
+         */
+        private void buildAnimation() {
             KeyValue keyValue1 = new KeyValue(radiusProperty(), rippleRadius.get());
             KeyValue keyValue2 = new KeyValue(opacityProperty(), 1.0);
             KeyFrame keyFrame1 = new KeyFrame(inDuration.get(), keyValue1);
@@ -188,25 +221,32 @@ public class RippleGenerator extends Group {
             KeyFrame keyFrame4 = new KeyFrame(outDuration.get(), keyValue4);
             outAnimation.getKeyFrames().addAll(keyFrame3, keyFrame4);
 
-            // Button shadow
-            // Spread
-            KeyValue keyValue5 = new KeyValue(buttonShadow.spreadProperty(), endShadow.getSpread(), easeBoth);
-            KeyValue keyValue6 = new KeyValue(buttonShadow.spreadProperty(), startShadow.getSpread(), easeBoth);
-            //Radius
-            KeyValue keyValue7 = new KeyValue(buttonShadow.radiusProperty(), endShadow.getRadius(), easeBoth);
-            KeyValue keyValue8 = new KeyValue(buttonShadow.radiusProperty(), startShadow.getRadius(), easeBoth);
-            // Offsets
-            KeyValue keyValue9 = new KeyValue(buttonShadow.offsetXProperty(), endShadow.getOffsetX(), easeBoth);
-            KeyValue keyValue10 = new KeyValue(buttonShadow.offsetXProperty(), startShadow.getOffsetX(), easeBoth);
-            KeyValue keyValue11 = new KeyValue(buttonShadow.offsetYProperty(), endShadow.getOffsetY(), easeBoth);
-            KeyValue keyValue12 = new KeyValue(buttonShadow.offsetYProperty(), startShadow.getOffsetY(), easeBoth);
-            KeyFrame keyFrame5 = new KeyFrame(Duration.ZERO, keyValue5, keyValue7, keyValue9, keyValue11);
-            KeyFrame keyFrame6 = new KeyFrame(inDuration.get(), keyValue6, keyValue8, keyValue10, keyValue12);
-            shadowAnimation.getKeyFrames().addAll(keyFrame5, keyFrame6);
+            if (level != null) {
+                Control control = (Control) region;
+                DropShadow shadowEffect = (DropShadow) ((Control) control.getSkin().getSkinnable()).getEffect();
+                DropShadow startShadow = shadowOf(level);
+                DropShadow endShadow = shadowOf(level, shadowDelta);
+
+                // Spread
+                KeyValue keyValue5 = new KeyValue(shadowEffect.spreadProperty(), endShadow.getSpread(), Interpolator.LINEAR);
+                KeyValue keyValue6 = new KeyValue(shadowEffect.spreadProperty(), startShadow.getSpread(), Interpolator.LINEAR);
+                //Radius
+                KeyValue keyValue7 = new KeyValue(shadowEffect.radiusProperty(), endShadow.getRadius(), Interpolator.LINEAR);
+                KeyValue keyValue8 = new KeyValue(shadowEffect.radiusProperty(), startShadow.getRadius(), Interpolator.LINEAR);
+                // Offsets
+                KeyValue keyValue9 = new KeyValue(shadowEffect.offsetXProperty(), endShadow.getOffsetX(), Interpolator.LINEAR);
+                KeyValue keyValue10 = new KeyValue(shadowEffect.offsetXProperty(), startShadow.getOffsetX(), Interpolator.LINEAR);
+                KeyValue keyValue11 = new KeyValue(shadowEffect.offsetYProperty(), endShadow.getOffsetY(), Interpolator.LINEAR);
+                KeyValue keyValue12 = new KeyValue(shadowEffect.offsetYProperty(), startShadow.getOffsetY(), Interpolator.LINEAR);
+                KeyFrame keyFrame5 = new KeyFrame(Duration.ZERO, keyValue5, keyValue7, keyValue9, keyValue11);
+                KeyFrame keyFrame6 = new KeyFrame(inDuration.get(), keyValue6, keyValue8, keyValue10, keyValue12);
+                shadowAnimation.getKeyFrames().addAll(keyFrame5, keyFrame6);
+                parallelTransition.getChildren().add(0, shadowAnimation);
+            }
 
             sequentialTransition.getChildren().addAll(inAnimation, outAnimation);
             parallelTransition.setInterpolator(rippleInterpolator);
-            parallelTransition.getChildren().addAll(shadowAnimation, sequentialTransition);
+            parallelTransition.getChildren().add(sequentialTransition);
         }
     }
 

+ 1 - 2
materialfx/src/main/java/it/paprojects/materialfx/skins/MFXButtonSkin.java

@@ -15,9 +15,8 @@ public class MFXButtonSkin extends ButtonSkin {
     public MFXButtonSkin(MFXButton button, DepthLevel depthLevel) {
         super(button);
 
-
         button.buttonTypeProperty().addListener(
-                ((observable, oldValue, newValue) -> updateButtonType(button, depthLevel)));
+                (observable, oldValue, newValue) -> updateButtonType(button, depthLevel));
 
         updateButtonType(button, depthLevel);
     }

+ 220 - 0
materialfx/src/main/java/it/paprojects/materialfx/skins/MFXCheckboxSkin.java

@@ -0,0 +1,220 @@
+package it.paprojects.materialfx.skins;
+
+import it.paprojects.materialfx.controls.MFXCheckbox;
+import it.paprojects.materialfx.utils.NodeUtils;
+import javafx.geometry.Insets;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.skin.CheckBoxSkin;
+import javafx.scene.layout.*;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Circle;
+import javafx.scene.shape.SVGPath;
+
+/**
+ *  This is the implementation of the {@code Skin} associated with every {@code MFXCheckbox}.
+ */
+public class MFXCheckboxSkin extends CheckBoxSkin {
+    //================================================================================
+    // Properties
+    //================================================================================
+    private final AnchorPane rippleContainer;
+    private final StackPane box;
+    private final StackPane mark;
+
+    private final double rippleContainerWidth = 30;
+    private final double rippleContainerHeight = 30;
+    private final double boxWidth = 26;
+    private final double boxHeight = 26;
+
+    private final double labelOffset = 2;
+
+    //================================================================================
+    // Constructors
+    //================================================================================
+    public MFXCheckboxSkin(MFXCheckbox control) {
+        super(control);
+
+        // Contains the ripple generator and the box
+        rippleContainer = new AnchorPane();
+        rippleContainer.setPrefSize(rippleContainerWidth, rippleContainerHeight);
+        rippleContainer.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
+        rippleContainer.getStyleClass().setAll("ripple-container");
+
+        // To make ripple container appear like a Circle
+        Circle circle = new Circle();
+        circle.setCenterX(rippleContainerWidth / 2);
+        circle.setCenterY(rippleContainerHeight / 2);
+        circle.setRadius(rippleContainerWidth * 0.6);
+        rippleContainer.setClip(circle);
+
+        // Contains the mark which is a SVG path defined in css
+        box = new StackPane();
+        box.setPrefSize(boxWidth, boxHeight);
+        box.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
+        box.getStyleClass().setAll("box");
+        box.setBorder(new Border(new BorderStroke(
+                control.getUncheckedColor(),
+                BorderStrokeStyle.SOLID,
+                new CornerRadii(2),
+                new BorderWidths(2.2)
+        )));
+        box.setBackground(new Background(new BackgroundFill(
+                Color.TRANSPARENT,
+                new CornerRadii(2),
+                Insets.EMPTY
+                )));
+
+        mark = new StackPane();
+        mark.getStyleClass().setAll("mark");
+        box.getChildren().add(mark);
+
+        rippleContainer.getChildren().add(box);
+
+        updateChildren();
+        updateMarkType(control);
+        setListeners(control);
+    }
+
+    //================================================================================
+    // Methods
+    //================================================================================
+
+    /**
+     * Adds listeners for: markType, selected, indeterminate, checked and unchecked coloros properties.
+     * @param control The MFXCheckbox associated to this skin
+     */
+    private void setListeners(MFXCheckbox control) {
+        control.markTypeProperty().addListener(
+                (observable, oldValue, newValue) -> updateMarkType(control));
+
+        control.selectedProperty().addListener(
+                (observable, oldValue, newValue) -> updateColors(control)
+        );
+
+        control.indeterminateProperty().addListener(
+                (observable, oldValue, newValue) -> updateColors(control)
+        );
+
+        control.checkedColorProperty().addListener(
+                (observable, oldValue, newValue) -> updateColors(control)
+        );
+
+        control.uncheckedColorProperty().addListener(
+                (observable, oldValue, newValue) -> updateColors(control)
+        );
+    }
+
+    /**
+     * This method is called whenever one of the following properties changes:
+     * {@code selectedProperty}, {@code indeterminateProperty}, {@code checkedColor} and {@code uncheckedColor} properties
+     * @param control The MFXCheckbox associated to this skin
+     * @see NodeUtils
+     */
+    private void updateColors(MFXCheckbox control) {
+        final BorderStroke borderStroke = box.getBorder().getStrokes().get(0);
+        if (control.isIndeterminate()) {
+            NodeUtils.updateBackground(box, control.getCheckedColor(), new Insets(4));
+        } else if (control.isSelected()) {
+            NodeUtils.updateBackground(box, control.getCheckedColor(), Insets.EMPTY);
+            box.setBorder(new Border(new BorderStroke(
+                    control.getCheckedColor(),
+                    borderStroke.getTopStyle(),
+                    borderStroke.getRadii(),
+                    borderStroke.getWidths()
+            )));
+        } else {
+            NodeUtils.updateBackground(box, Color.TRANSPARENT);
+            box.setBorder(new Border(new BorderStroke(
+                    control.getUncheckedColor(),
+                    borderStroke.getTopStyle(),
+                    borderStroke.getRadii(),
+                    borderStroke.getWidths()
+            )));
+        }
+    }
+
+    /**
+     * This method is called whenever the {@code markType} property changes.
+     * @param control The MFXCheckbox associated to this skin
+     */
+    private void updateMarkType(MFXCheckbox control) {
+        SVGPath svgPath = new SVGPath();
+        svgPath.setContent(control.getMarkType().getSvhPath());
+        mark.setShape(svgPath);
+    }
+
+    /**
+     * Centers the box in the ripple container
+     */
+    private void centerBox() {
+        final double offsetPercentage = 3;
+        final double vInset = ((rippleContainerHeight - boxHeight) / 2) * offsetPercentage;
+        final double hInset = ((rippleContainerWidth - boxWidth) / 2) * offsetPercentage;
+        AnchorPane.setTopAnchor(box, vInset);
+        AnchorPane.setRightAnchor(box, hInset);
+        AnchorPane.setBottomAnchor(box, vInset);
+        AnchorPane.setLeftAnchor(box, hInset);
+    }
+
+    public Pane getRippleContainer() {
+        return rippleContainer;
+    }
+
+    //================================================================================
+    // Override Methods
+    //================================================================================
+    @Override
+    protected void updateChildren() {
+        super.updateChildren();
+        if (rippleContainer != null) {
+            getChildren().remove(1);
+            getChildren().add(rippleContainer);
+        }
+    }
+
+    @Override
+    protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
+        return super.computeMinWidth(height, topInset, rightInset, bottomInset, leftInset) +
+                snapSizeX(rippleContainer.minWidth(-1));
+    }
+
+    @Override
+    protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
+        return super.computePrefWidth(height, topInset, rightInset, bottomInset, leftInset) +
+                snapSizeX(rippleContainer.prefWidth(-1));
+    }
+
+    @Override
+    protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
+        return Math.max(super.computeMinHeight(width - rippleContainer.minWidth(-1), topInset, rightInset, bottomInset, leftInset),
+                topInset + rippleContainer.minHeight(-1) + bottomInset) + topInset;
+    }
+
+    @Override
+    protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
+        return Math.max(super.computePrefHeight(width - rippleContainer.prefWidth(-1), topInset, rightInset, bottomInset, leftInset),
+                topInset + rippleContainer.prefHeight(-1) + bottomInset);
+    }
+
+    @Override
+    protected void layoutChildren(double x, double y, double w, double h) {
+        final CheckBox checkBox = getSkinnable();
+
+        final double boxWidth = snapSizeX(rippleContainer.prefWidth(-1));
+        final double boxHeight = snapSizeY(rippleContainer.prefHeight(-1));
+        final double computeWidth = Math.max(checkBox.prefWidth(-1), checkBox.minWidth(-1));
+        final double labelWidth = Math.min( computeWidth - boxWidth, w - snapSizeX(boxWidth));
+        final double labelHeight = Math.min(checkBox.prefHeight(labelWidth), h);
+        final double maxHeight = Math.max(boxHeight, labelHeight);
+        final double xOffset = NodeUtils.computeXOffset(w, labelWidth + boxWidth, checkBox.getAlignment().getHpos()) + x;
+        final double yOffset = NodeUtils.computeYOffset(h, maxHeight, checkBox.getAlignment().getVpos()) + y;
+
+        layoutLabelInArea(xOffset + boxWidth + labelOffset, yOffset, labelWidth, maxHeight, checkBox.getAlignment());
+        rippleContainer.resize(boxWidth, boxHeight);
+        positionInArea(rippleContainer, xOffset, yOffset, boxWidth, maxHeight, 0, checkBox.getAlignment().getHpos(), checkBox.getAlignment().getVpos());
+
+        centerBox();
+    }
+}
+
+

+ 0 - 6
materialfx/src/main/java/it/paprojects/materialfx/utils/ButtonType.java

@@ -1,6 +0,0 @@
-package it.paprojects.materialfx.utils;
-
-public enum ButtonType {
-    FLAT,
-    RAISED
-}

+ 24 - 0
materialfx/src/main/java/it/paprojects/materialfx/utils/HexToRGBColor.java

@@ -0,0 +1,24 @@
+package it.paprojects.materialfx.utils;
+
+import javafx.scene.paint.Color;
+
+public class HexToRGBColor {
+
+    private HexToRGBColor() {
+    }
+
+    public static String rgb(Color color) {
+        return String.format("#%02x%02x%02x",
+                (int) (255 * color.getRed()),
+                (int) (255 * color.getGreen()),
+                (int) (255 * color.getBlue()));
+    }
+
+    public static String rgba(Color color) {
+        return String.format("rgba(%d, %d, %d, %f)",
+                (int) (255 * color.getRed()),
+                (int) (255 * color.getGreen()),
+                (int) (255 * color.getBlue()),
+                color.getOpacity());
+    }
+}

+ 89 - 0
materialfx/src/main/java/it/paprojects/materialfx/utils/NodeUtils.java

@@ -0,0 +1,89 @@
+package it.paprojects.materialfx.utils;
+
+import javafx.geometry.HPos;
+import javafx.geometry.Insets;
+import javafx.geometry.VPos;
+import javafx.scene.layout.Background;
+import javafx.scene.layout.BackgroundFill;
+import javafx.scene.layout.Region;
+import javafx.scene.paint.Paint;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class which provides convenience methods for working with Nodes
+ */
+public class NodeUtils {
+
+    private NodeUtils() {
+    }
+
+    /**
+     * Changes the background color of a {@code Region} to the desired one.
+     * @param region The region to change the background color to
+     * @param fill The desired color
+     */
+    public static void updateBackground(Region region, Paint fill) {
+        final Background background = region.getBackground();
+        if (background == null || background.getFills().isEmpty()) {
+            return;
+        }
+
+        final List<BackgroundFill> fills = new ArrayList<>();
+        for (BackgroundFill bf : background.getFills()) {
+            fills.add(new BackgroundFill(fill, bf.getRadii(), bf.getInsets()));
+        }
+
+        region.setBackground(new Background(fills.toArray(BackgroundFill[]::new)));
+    }
+
+    /**
+     * Changes the background color of a {@code Region} to the desired one and lets specify the background insets.
+     * @param region The region to change the background color to
+     * @param fill The desired color
+     * @param backgroundInsets The background insets to use
+     */
+    public static void updateBackground(Region region, Paint fill, Insets backgroundInsets) {
+        final Background background = region.getBackground();
+        if (background == null || background.getFills().isEmpty()) {
+            return;
+        }
+
+        final List<BackgroundFill> fills = new ArrayList<>();
+        for (BackgroundFill bf : background.getFills()) {
+            fills.add(new BackgroundFill(fill, bf.getRadii(), backgroundInsets));
+        }
+
+        region.setBackground(new Background(fills.toArray(BackgroundFill[]::new)));
+    }
+
+    /* The next two methods are copied from com.sun.javafx.scene.control.skin.Utils class
+     * It's a private module, so to avoid adding exports and opens I copied them
+     */
+    public static double computeXOffset(double width, double contentWidth, HPos hpos) {
+        switch (hpos) {
+            case LEFT:
+                return 0;
+            case CENTER:
+                return (width - contentWidth) / 2;
+            case RIGHT:
+                return width - contentWidth;
+        }
+        return 0;
+    }
+
+    public static double computeYOffset(double height, double contentHeight, VPos vpos) {
+
+        switch (vpos) {
+            case TOP:
+                return 0;
+            case CENTER:
+                return (height - contentHeight) / 2;
+            case BOTTOM:
+                return height - contentHeight;
+            default:
+                return 0;
+        }
+    }
+}

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

@@ -5,6 +5,7 @@ module MaterialFX.materialfx.main {
     requires java.desktop;
 
     exports it.paprojects.materialfx.controls;
+    exports it.paprojects.materialfx.controls.enums;
     exports it.paprojects.materialfx.effects;
     exports it.paprojects.materialfx.skins;
     exports it.paprojects.materialfx.utils;

+ 45 - 0
materialfx/src/main/resources/it/paprojects/materialfx/css/mfx-checkbox.css

@@ -0,0 +1,45 @@
+/*================================================================================*/
+/* Ripple
+/*================================================================================*/
+.mfx-checkbox .ripple-container {
+    -fx-pref-width: 30;
+    -fx-pref-height: 30;
+}
+
+.mfx-checkbox .ripple-container .ripple-generator {
+    -mfx-ripple-color: rgb(190, 190, 190);
+}
+
+/*================================================================================*/
+/* Box
+/*================================================================================*/
+.mfx-checkbox .box {
+    -fx-border-width: 2.2;
+    -fx-border-radius: 2;
+    -fx-background-radius: 2;
+}
+
+.mfx-checkbox:indeterminate .box {
+    -fx-background-insets: 4;
+}
+
+/* CIRCULAR BOX*/
+/*
+.mfx-checkbox .box {
+    -fx-shape: "M 400 100 L 400 100 A 50 50 0 1 1 400 250 A 50 50 0 1 1 400 100 ";
+}
+*/
+
+/*================================================================================*/
+/* Mark
+/*================================================================================*/
+.mfx-checkbox:selected .mark {
+    -fx-max-width: 12;
+    -fx-max-height: 9;
+    -fx-background-color: WHITE;
+    -fx-border-color: WHITE;
+    -fx-border-width: 5;
+    -fx-border-radius: 2;
+}
+
+