Browse Source

Upgrade components, core, release and resources modules

Core Module
:sparkles: Introduce convenience API for components developers that want to easily integrate with SceneBuilder

Components Module
:recycle: Make buttons use the new SceneBuilder integration API mentioned above

Release Module
:sparkles: This new module allows to pack all MaterialFX dependencies in a single artifact. This allows easy utilization in SceneBuilder as well as in non-modular projects. For modular projects it's still recommended to use the build system to download and import all the needed dependencies

Resources Module
:recycle: Fix wrongly formatted stylesheet

Signed-off-by: palexdev <alessandro.parisi406@gmail.com>
palexdev 2 years ago
parent
commit
5f4ed75b00

+ 1 - 0
.idea/gradle.xml

@@ -13,6 +13,7 @@
             <option value="$PROJECT_DIR$/modules/core" />
             <option value="$PROJECT_DIR$/modules/core" />
             <option value="$PROJECT_DIR$/modules/effects" />
             <option value="$PROJECT_DIR$/modules/effects" />
             <option value="$PROJECT_DIR$/modules/localization" />
             <option value="$PROJECT_DIR$/modules/localization" />
+            <option value="$PROJECT_DIR$/modules/release" />
             <option value="$PROJECT_DIR$/modules/resources" />
             <option value="$PROJECT_DIR$/modules/resources" />
           </set>
           </set>
         </option>
         </option>

+ 10 - 4
build.gradle

@@ -52,18 +52,24 @@ subprojects {
         targetCompatibility = "$jdk"
         targetCompatibility = "$jdk"
     }
     }
 
 
+    // The ':all' module is ignored since it is a placeholder module and there is no module-info.java,
+    // making moduleOptions fail
     compileTestJava {
     compileTestJava {
         sourceCompatibility = "$testJdk"
         sourceCompatibility = "$testJdk"
         targetCompatibility = "$testJdk"
         targetCompatibility = "$testJdk"
-        moduleOptions {
-            compileOnClasspath = true
+        if (project.name != 'release') {
+            moduleOptions {
+                compileOnClasspath = true
+            }
         }
         }
     }
     }
 
 
     test {
     test {
         useJUnitPlatform()
         useJUnitPlatform()
-        moduleOptions {
-            runOnClasspath = true
+        if (project.name != 'release') {
+            moduleOptions {
+                runOnClasspath = true
+            }
         }
         }
     }
     }
 }
 }

+ 1 - 0
gradle.properties

@@ -31,6 +31,7 @@ jfxPlugin=0.0.13
 jlink=2.25.0
 jlink=2.25.0
 mavenPublishPlugin=0.23.2
 mavenPublishPlugin=0.23.2
 gradleSass=1.4.2
 gradleSass=1.4.2
+shadowJarPlugin=7.1.2
 
 
 # Dependencies
 # Dependencies
 jfx=19
 jfx=19

+ 1 - 0
modules/components/build.gradle

@@ -4,6 +4,7 @@ version = "$mfx"
 
 
 dependencies {
 dependencies {
     api(project(":core"))
     api(project(":core"))
+    api(project(':effects'))
     api(project(":localization"))
     api(project(":localization"))
     api(project(":resources"))
     api(project(":resources"))
     api "io.github.palexdev:virtualizedfx:$vfx"
     api "io.github.palexdev:virtualizedfx:$vfx"

+ 13 - 0
modules/components/gradle.properties

@@ -0,0 +1,13 @@
+#--------------------------------------#
+# Maven                                #
+#--------------------------------------#
+POM_ARTIFACT_ID=mfxcomponents
+VERSION_NAME=11.15.1
+
+POM_NAME=mfxcomponents
+POM_DESCRIPTION=Material Design/Modern components for JavaFX
+POM_INCEPTION_YEAR=2023
+
+POM_LICENCE_NAME=GNU LGPLv3
+POM_LICENCE_URL=https://www.gnu.org/licenses/lgpl-3.0.html
+POM_LICENCE_DIST=repo

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

@@ -23,6 +23,9 @@ import io.github.palexdev.materialfx.controls.base.MFXLabeled;
 import io.github.palexdev.materialfx.skins.MFXButtonSkin;
 import io.github.palexdev.materialfx.skins.MFXButtonSkin;
 import io.github.palexdev.mfxcore.base.properties.EventHandlerProperty;
 import io.github.palexdev.mfxcore.base.properties.EventHandlerProperty;
 import io.github.palexdev.mfxcore.controls.SkinBase;
 import io.github.palexdev.mfxcore.controls.SkinBase;
+import io.github.palexdev.mfxcore.observables.When;
+import io.github.palexdev.mfxcore.utils.fx.SceneBuilderIntegration;
+import io.github.palexdev.mfxresources.MFXResources;
 import javafx.event.ActionEvent;
 import javafx.event.ActionEvent;
 import javafx.event.EventHandler;
 import javafx.event.EventHandler;
 import javafx.scene.Node;
 import javafx.scene.Node;
@@ -119,6 +122,18 @@ public class MFXButton extends MFXLabeled<MFXButtonBehavior> {
 	private void initialize() {
 	private void initialize() {
 		getStyleClass().setAll(defaultStyleClasses());
 		getStyleClass().setAll(defaultStyleClasses());
 		setDefaultBehaviorProvider();
 		setDefaultBehaviorProvider();
+
+		// SceneBuilder integration
+		SceneBuilderIntegration.ifInSceneBuilder(() -> setText("Button"));
+		SceneBuilderIntegration.ifInSceneBuilder(() -> {
+			String theme = MFXResources.load("sass/md3/mfx-light.css");
+			When.onChanged(sceneProperty())
+					.condition((o, n) -> n != null && !n.getStylesheets().contains(theme))
+					.then((o, n) -> n.getStylesheets().add(theme))
+					.oneShot()
+					.listen();
+		});
+		// TODO theme integration with SceneBuilder will change once base themes and MFXThemeManager are implemented
 	}
 	}
 
 
 	/**
 	/**

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

@@ -42,11 +42,10 @@ public class MFXElevatedButton extends MFXButton {
 	// Constructors
 	// Constructors
 	//================================================================================
 	//================================================================================
 	public MFXElevatedButton() {
 	public MFXElevatedButton() {
-		this("");
 	}
 	}
 
 
 	public MFXElevatedButton(String text) {
 	public MFXElevatedButton(String text) {
-		this(text, null);
+		super(text, null);
 	}
 	}
 
 
 	public MFXElevatedButton(String text, Node icon) {
 	public MFXElevatedButton(String text, Node icon) {

+ 96 - 0
modules/core/src/main/java/io/github/palexdev/mfxcore/utils/fx/SceneBuilderIntegration.java

@@ -0,0 +1,96 @@
+/*
+ * 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.mfxcore.utils.fx;
+
+import java.lang.StackWalker.Option;
+import java.lang.StackWalker.StackFrame;
+import java.util.Set;
+
+/**
+ * This utility can be used by libraries that implement custom controls to allow them to detect whether they
+ * are being loaded by the SceneBuilder app. This allows to perform whatever action the user desires in such case.
+ * <p>
+ * For example MaterialFX uses this to set the buttons text to "Button" only if they are used in SceneBuilder, the caveat
+ * of such trick is that for some reason properties in the right pane are not updated, the Text property will appear blank,
+ * if you want to set a empty text, just use a whitespace character.
+ * <p></p>
+ * MaterialFX also does another interesting thing with this, since SceneBuilder doesn't offer any way to add custom themes,
+ * this utility helps with that by detecting the app and adding the stylesheet to the root Scene.
+ * <p></p>
+ * The utility is optimized to not run the detection more than one time, the result is cached after the first time.
+ * The 'search' can become invalid and thus repeated if the user changes the 'search' depth. What is the depth?
+ * This uses the {@link StackWalker} API to detect the caller of constructors/methods, the depth is the estimate number
+ * of {@link StackFrame}s to process.
+ */
+public class SceneBuilderIntegration {
+	//================================================================================
+	// Static Properties
+	//================================================================================
+	private static Boolean inSceneBuilder = null;
+	private static int depth = 10;
+	private static boolean isDepthInvalid = false;
+
+	//================================================================================
+	// Constructors
+	//================================================================================
+	private SceneBuilderIntegration() {
+	}
+
+	//================================================================================
+	// Static Methods
+	//================================================================================
+
+	/**
+	 * If {@link #isInSceneBuilder()} returns true executes the given action.
+	 */
+	public static void ifInSceneBuilder(Runnable action) {
+		if (isInSceneBuilder()) action.run();
+	}
+
+	/**
+	 * This is the core method responsible for detecting the caller of a constructor/method.
+	 * The {@link StackWalker} API used to do so, will detect all the classes involved in the calling.
+	 * SceneBuilder can be detected because classes loading custom controls are in a package containing the
+	 * following string: ".javafx.scenebuilder.kit."
+	 * <p></p>
+	 * Subsequent calls to this will return the cached result (if it was called at least once of course), so there
+	 * should be little to no performance impact in the app. If the search depth is changed the cached result is ignored
+	 * and the research is repeated.
+	 */
+	public static boolean isInSceneBuilder() {
+		if (inSceneBuilder == null || isDepthInvalid) {
+			StackWalker sw = StackWalker.getInstance(
+					Set.of(Option.RETAIN_CLASS_REFERENCE, Option.SHOW_REFLECT_FRAMES),
+					depth
+			);
+			inSceneBuilder = sw.walk(sfs -> sfs.anyMatch(sf ->
+					sf.getClassName() != null && sf.getClassName().contains(".javafx.scenebuilder.kit.")));
+			isDepthInvalid = false;
+		}
+		return inSceneBuilder;
+	}
+
+	/**
+	 * Sets the 'search' depth used by the {@link StackWalker} API.
+	 */
+	public static void setDepth(int depth) {
+		SceneBuilderIntegration.depth = depth;
+		SceneBuilderIntegration.isDepthInvalid = true;
+	}
+}

+ 8 - 0
modules/release/PLACEHOLDER.md

@@ -0,0 +1,8 @@
+#### This module is intended to be used in two particular occasions:
+
+1) SceneBuilder: all this module does is producing a Jar that contains all the dependencies of MaterialFX,
+   this makes usage in SceneBuilder really easy as all you have to do is include this artifact instead of importing
+   all the modules one by one
+2) Non-modular projects. Usage in modular projects is discouraged, and you will probably encounter problems otherwise.
+   The Jar building process removed the 'module-info.java' file from the modules, so it cannot be used anymore with
+   the Java Module System as intended.

+ 42 - 0
modules/release/build.gradle

@@ -0,0 +1,42 @@
+import com.vanniktech.maven.publish.SonatypeHost
+
+plugins {
+    id 'com.github.johnrengelman.shadow' version "$shadowJarPlugin"
+}
+
+version = "$mfx"
+
+dependencies {
+    api(project(':core'))
+    api(project(':components'))
+    api(project(":effects"))
+    api(project(':localization'))
+    api(project(':resources'))
+    api "io.github.palexdev:virtualizedfx:$vfx"
+}
+
+java {
+    tasks.withType(Jar).each { it.archiveBaseName.set("materialfx-all") }
+}
+
+// TODO rename materialfx package to mfxcomponents
+shadowJar {
+    mergeServiceFiles()
+    dependencies {
+        include(project(':core'))
+        include(project(':components'))
+        include(project(":effects"))
+        include(project(':localization'))
+        include(project(':resources'))
+    }
+}
+
+mavenPublishing {
+    publishToMavenCentral(SonatypeHost.S01)
+    signAllPublications()
+}
+
+jar {
+    enabled = false
+    dependsOn(shadowJar { classifier = null })
+}

+ 13 - 0
modules/release/gradle.properties

@@ -0,0 +1,13 @@
+#--------------------------------------#
+# Maven                                #
+#--------------------------------------#
+POM_ARTIFACT_ID=materialfx-all
+VERSION_NAME=11.15.1
+
+POM_NAME=materialfx-all
+POM_DESCRIPTION=Material Design/Modern components for JavaFX, now packed as a single Jar
+POM_INCEPTION_YEAR=2023
+
+POM_LICENCE_NAME=GNU LGPLv3
+POM_LICENCE_URL=https://www.gnu.org/licenses/lgpl-3.0.html
+POM_LICENCE_DIST=repo

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

@@ -1,4 +1,5 @@
-// noinspection
+// @noinspection
+// @formatter:off
 @use "../../abstracts/theme_utils" as *;
 @use "../../abstracts/theme_utils" as *;
 @use "buttons_base";
 @use "buttons_base";
 
 
@@ -11,17 +12,9 @@ $icon: 'primary';
 $ripple: 'primary';
 $ripple: 'primary';
 
 
 .mfx-button.text {
 .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;
+  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.base($surface, $surface, $text, $surface, 0);
   @include buttons_base.disabled();
   @include buttons_base.disabled();
   @include buttons_base.hover(state_layer($surface, 'primary', 'hover'), $surface, 2);
   @include buttons_base.hover(state_layer($surface, 'primary', 'hover'), $surface, 2);

+ 1 - 0
settings.gradle

@@ -2,6 +2,7 @@ include 'components'
 include 'core'
 include 'core'
 include 'effects'
 include 'effects'
 include 'localization'
 include 'localization'
+include 'release'
 include 'resources'
 include 'resources'
 
 
 rootProject.name = 'MaterialFX'
 rootProject.name = 'MaterialFX'