├── .gitignore ├── src ├── test │ ├── resources │ │ ├── mockito-extensions │ │ │ └── org.mockito.plugins.MockMaker │ │ └── views │ │ │ ├── load_view_without_controller.fxml │ │ │ └── load_view_with_controller.fxml │ └── java │ │ └── com │ │ └── github │ │ └── spring │ │ └── boot │ │ └── javafx │ │ ├── controllers │ │ └── MyTestController.java │ │ ├── stage │ │ ├── BorderlessStageWrapperTest.java │ │ └── BorderlessStageTest.java │ │ ├── TestConfiguration.java │ │ ├── font │ │ ├── FontRegistryImplTest.java │ │ └── controls │ │ │ ├── IconSolidTest.java │ │ │ ├── IconRegularTest.java │ │ │ └── IconTest.java │ │ ├── JavaFxAutoConfigurationTest.java │ │ ├── view │ │ └── ViewLoaderImplTest.java │ │ └── ui │ │ └── scale │ │ └── ScaleAwareImplTest.java └── main │ ├── resources │ ├── META-INF │ │ └── spring │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── fonts │ │ ├── fontawesome-regular.ttf │ │ ├── fontawesome-solid.ttf │ │ └── fontawesome-webfont.woff │ └── java │ └── com │ └── github │ └── spring │ └── boot │ └── javafx │ ├── view │ ├── ViewManagerPolicy.java │ ├── ViewNotFoundException.java │ ├── StageNotFoundException.java │ ├── ViewException.java │ ├── ViewProperties.java │ ├── config │ │ └── ViewConfiguration.java │ ├── ViewManager.java │ ├── ViewLoader.java │ ├── ViewManagerImpl.java │ └── ViewLoaderImpl.java │ ├── text │ ├── Message.java │ ├── config │ │ └── LocaleTextConfiguration.java │ ├── LocaleText.java │ └── LocaleTextImpl.java │ ├── font │ ├── FontException.java │ ├── FontRegistry.java │ ├── config │ │ └── FontConfiguration.java │ ├── FontRegistryImpl.java │ └── controls │ │ ├── AbstractIcon.java │ │ ├── IconRegular.java │ │ └── Icon.java │ ├── ui │ ├── scale │ │ ├── MissingScaleAwarePropertyException.java │ │ ├── ScaleAware.java │ │ └── ScaleAwareImpl.java │ ├── stage │ │ └── StageAware.java │ └── size │ │ └── SizeAware.java │ ├── stage │ ├── InvalidStageException.java │ ├── BorderlessStage.java │ └── BorderlessStageWrapper.java │ ├── stereotype │ └── ViewController.java │ ├── JavaFxAutoConfiguration.java │ └── SpringJavaFXApplication.java ├── .github └── workflows │ └── build.yml ├── README.md ├── pom.xml └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target/ 3 | 4 | **/*.iml -------------------------------------------------------------------------------- /src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | com.github.spring.boot.javafx.JavaFxAutoConfiguration -------------------------------------------------------------------------------- /src/main/resources/fonts/fontawesome-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoep/spring-boot-starter-javafx/HEAD/src/main/resources/fonts/fontawesome-regular.ttf -------------------------------------------------------------------------------- /src/main/resources/fonts/fontawesome-solid.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoep/spring-boot-starter-javafx/HEAD/src/main/resources/fonts/fontawesome-solid.ttf -------------------------------------------------------------------------------- /src/main/resources/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoep/spring-boot-starter-javafx/HEAD/src/main/resources/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/view/ViewManagerPolicy.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.view; 2 | 3 | public enum ViewManagerPolicy { 4 | CLOSEABLE, 5 | BLOCKED 6 | } 7 | -------------------------------------------------------------------------------- /src/test/resources/views/load_view_without_controller.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/text/Message.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.text; 2 | 3 | /** 4 | * Defines a message resource for {@link LocaleText}. 5 | */ 6 | public interface Message { 7 | /** 8 | * Get the key of the message. 9 | * 10 | * @return Returns the message key. 11 | */ 12 | String getKey(); 13 | } 14 | -------------------------------------------------------------------------------- /src/test/resources/views/load_view_with_controller.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/font/FontException.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.font; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class FontException extends RuntimeException { 7 | private String filename; 8 | 9 | public FontException(String filename) { 10 | super("An error occurred while loading font file " + filename); 11 | this.filename = filename; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/ui/scale/MissingScaleAwarePropertyException.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.ui.scale; 2 | 3 | /** 4 | * Defines the exception that the scale aware scene is missing from the controller at initialization. 5 | */ 6 | public class MissingScaleAwarePropertyException extends RuntimeException { 7 | public MissingScaleAwarePropertyException() { 8 | super("Missing scene to execute scale aware logic"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/com/github/spring/boot/javafx/controllers/MyTestController.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.controllers; 2 | 3 | import com.github.spring.boot.javafx.stereotype.ViewController; 4 | import javafx.fxml.FXML; 5 | import javafx.scene.layout.Pane; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.ToString; 9 | 10 | @Data 11 | @ToString 12 | @EqualsAndHashCode 13 | @ViewController 14 | public class MyTestController { 15 | @FXML 16 | Pane root; 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/com/github/spring/boot/javafx/stage/BorderlessStageWrapperTest.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.stage; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.assertThrows; 6 | 7 | class BorderlessStageWrapperTest { 8 | @Test 9 | void testConstructor_whenStageArgumentIsNull_shouldThrowIllegalArgumentException() { 10 | assertThrows(NullPointerException.class, () -> new BorderlessStageWrapper(null), "stage cannot be null"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/view/ViewNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.view; 2 | 3 | /** 4 | * Exception indicating that the view doesn't exist or couldn't be found with the given name. 5 | */ 6 | public class ViewNotFoundException extends ViewException { 7 | public ViewNotFoundException(String view) { 8 | super(view, "View '" + view + "' not found"); 9 | } 10 | 11 | public ViewNotFoundException(String view, Exception ex) { 12 | super(view, "View '" + view + "' not found", ex); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/stage/InvalidStageException.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.stage; 2 | 3 | import javafx.stage.Stage; 4 | import lombok.Getter; 5 | 6 | @Getter 7 | public class InvalidStageException extends RuntimeException { 8 | private final Stage stage; 9 | 10 | public InvalidStageException(Stage stage, String message) { 11 | super(message); 12 | this.stage = stage; 13 | } 14 | 15 | public InvalidStageException(Stage stage, String message, Throwable cause) { 16 | super(message, cause); 17 | this.stage = stage; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/com/github/spring/boot/javafx/TestConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.ComponentScan; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.context.support.ResourceBundleMessageSource; 7 | 8 | @Configuration 9 | @ComponentScan({ 10 | "com.github.spring.boot.javafx.controllers" 11 | }) 12 | public class TestConfiguration { 13 | @Bean 14 | public ResourceBundleMessageSource resourceBundleMessageSource() { 15 | return new ResourceBundleMessageSource(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/ui/stage/StageAware.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.ui.stage; 2 | 3 | import javafx.stage.Stage; 4 | 5 | /** 6 | * Defines that a view controller is aware of the stage events, such as the stage being shown/closed. 7 | */ 8 | public interface StageAware { 9 | /** 10 | * Is triggered when the stage is shown. 11 | * 12 | * @param stage The stage that triggered the event. 13 | */ 14 | void onShown(Stage stage); 15 | 16 | /** 17 | * Is triggered when the stage is closed. 18 | * 19 | * @param stage The stage that triggered the event. 20 | */ 21 | void onClosed(Stage stage); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/view/StageNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.view; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class StageNotFoundException extends RuntimeException { 7 | private final String stageName; 8 | private final boolean primaryStage; 9 | 10 | public StageNotFoundException() { 11 | super("Primary stage couldn't be found"); 12 | this.stageName = "primary"; 13 | this.primaryStage = false; 14 | } 15 | 16 | public StageNotFoundException(String stageName) { 17 | super("Window '" + stageName + "' couldn't be found"); 18 | this.stageName = stageName; 19 | this.primaryStage = false; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/com/github/spring/boot/javafx/font/FontRegistryImplTest.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.font; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.assertNotNull; 6 | 7 | class FontRegistryImplTest { 8 | @Test 9 | void testGetInstance() { 10 | var result = FontRegistryImpl.getInstance(); 11 | 12 | assertNotNull(result, "expected a font registry instance to have been returned"); 13 | } 14 | 15 | @Test 16 | void testLoadFont() { 17 | var registry = FontRegistryImpl.getInstance(); 18 | 19 | var font = registry.loadFont("fontawesome-regular.ttf"); 20 | 21 | assertNotNull(font, "expected a font to have been returned"); 22 | } 23 | } -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Build 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | workflow_dispatch: 12 | inputs: {} 13 | 14 | jobs: 15 | test: 16 | name: Test 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: Set up JDK 17 22 | uses: actions/setup-java@v3 23 | with: 24 | distribution: 'adopt' 25 | java-version: 17 26 | cache: 'maven' 27 | - name: Maven test 28 | run: | 29 | xvfb-run -a mvn -B test -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/view/ViewException.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.view; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * Exception which occurred during the loading of a FXML view. 7 | */ 8 | @Getter 9 | public class ViewException extends RuntimeException { 10 | private String view; 11 | 12 | public ViewException(String message) { 13 | super(message); 14 | } 15 | 16 | public ViewException(String view, String message) { 17 | super(message); 18 | this.view = view; 19 | } 20 | 21 | public ViewException(String message, Throwable cause) { 22 | super(message, cause); 23 | } 24 | 25 | public ViewException(String view, String message, Throwable cause) { 26 | super(message, cause); 27 | this.view = view; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/stereotype/ViewController.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.stereotype; 2 | 3 | import org.springframework.core.annotation.AliasFor; 4 | import org.springframework.stereotype.Component; 5 | 6 | import java.lang.annotation.*; 7 | 8 | /** 9 | * Indicates that the annotated class is a "View Controller". 10 | */ 11 | @Target({ElementType.TYPE}) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Documented 14 | @Component 15 | public @interface ViewController { 16 | /** 17 | * The value may indicate a suggestion for a logical component name, 18 | * to be turned into a Spring bean in case of an autodetected component. 19 | * @return the suggested component name, if any (or empty String otherwise) 20 | */ 21 | @AliasFor(annotation = Component.class) 22 | String value() default ""; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/ui/size/SizeAware.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.ui.size; 2 | 3 | import javafx.stage.Stage; 4 | 5 | /** 6 | * Defines that the view controller is aware of the size of it's window and can handle size changes within the window. 7 | */ 8 | public interface SizeAware { 9 | /** 10 | * Set the initial size of the window. 11 | * 12 | * @param window Set the window to set the size on. 13 | */ 14 | void setInitialSize(Stage window); 15 | 16 | /** 17 | * Is triggered when the size of the window is changed. 18 | * 19 | * @param width The new width of the window. 20 | * @param height The new height of the window. 21 | * @param isMaximized The maximized state of the window. 22 | */ 23 | void onSizeChange(Number width, Number height, boolean isMaximized); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/JavaFxAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx; 2 | 3 | import com.github.spring.boot.javafx.font.config.FontConfiguration; 4 | import com.github.spring.boot.javafx.text.config.LocaleTextConfiguration; 5 | import com.github.spring.boot.javafx.view.config.ViewConfiguration; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.context.annotation.Import; 8 | 9 | /** 10 | * Configuration class for auto-configuring JavaFX related beans. 11 | * This class imports other configuration classes responsible for configuring 12 | * fonts, locale text, and views in a JavaFX application. 13 | */ 14 | @Configuration 15 | @Import({ 16 | FontConfiguration.class, 17 | LocaleTextConfiguration.class, 18 | ViewConfiguration.class 19 | }) 20 | public class JavaFxAutoConfiguration { 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/font/FontRegistry.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.font; 2 | 3 | import javafx.scene.text.Font; 4 | 5 | public interface FontRegistry { 6 | /** 7 | * The font directory location. 8 | */ 9 | String FONT_DIRECTORY = "/fonts/"; 10 | 11 | /** 12 | * Load the given font file and use the size of the system default font. 13 | * 14 | * @param filename The font filename to load from the {@link #FONT_DIRECTORY}. 15 | * @return Returns the loaded font. 16 | */ 17 | Font loadFont(String filename); 18 | 19 | /** 20 | * Load the given font file. 21 | * 22 | * @param filename The font filename to load from the {@link #FONT_DIRECTORY}. 23 | * @param size The size of the font. 24 | * @return Returns the loaded font. 25 | */ 26 | Font loadFont(String filename, double size); 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/com/github/spring/boot/javafx/font/controls/IconSolidTest.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.font.controls; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.junit.jupiter.api.extension.ExtendWith; 5 | import org.testfx.framework.junit5.ApplicationExtension; 6 | 7 | import static org.junit.jupiter.api.Assertions.assertEquals; 8 | import static org.junit.jupiter.api.Assertions.assertTrue; 9 | 10 | @ExtendWith(ApplicationExtension.class) 11 | class IconSolidTest { 12 | 13 | @Test 14 | void testNewInstance() { 15 | var result = new IconSolid(); 16 | 17 | assertTrue(result.getStyleClass().contains(AbstractIcon.STYLE_CLASS)); 18 | } 19 | 20 | @Test 21 | void testNewInstanceWithUnicode() { 22 | var unicode = IconSolid.AD_UNICODE; 23 | 24 | var result = new IconSolid(unicode); 25 | 26 | assertTrue(result.getStyleClass().contains(AbstractIcon.STYLE_CLASS)); 27 | assertEquals(unicode, result.getText()); 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/ui/scale/ScaleAware.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.ui.scale; 2 | 3 | import javafx.scene.Scene; 4 | import javafx.scene.layout.Region; 5 | 6 | /** 7 | * Defines that a view controller is aware of the scale factor and the view is able to be scaled accordingly. 8 | */ 9 | public interface ScaleAware { 10 | /** 11 | * Scale the given scene according to the scale factor. 12 | * This method is automatically invoked by the {@link com.github.spring.boot.javafx.view.ViewLoader} when the view is being loaded. 13 | * 14 | * @param scene The current scene to use for scaling. 15 | * @param root The root region of the FXML file. 16 | * @param scale Set the scale of the UI. 17 | */ 18 | void scale(Scene scene, Region root, float scale); 19 | 20 | /** 21 | * Invoked when the scene scale is being changed to a new scale factor. 22 | * 23 | * @param newValue The new scale factor. 24 | */ 25 | void onScaleChanged(float newValue); 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/com/github/spring/boot/javafx/JavaFxAutoConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx; 2 | 3 | import com.github.spring.boot.javafx.view.ViewLoader; 4 | import com.github.spring.boot.javafx.view.ViewManager; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertNotNull; 10 | 11 | @SpringBootTest( 12 | classes = { 13 | JavaFxAutoConfiguration.class, 14 | TestConfiguration.class 15 | } 16 | ) 17 | class JavaFxAutoConfigurationTest { 18 | @Autowired 19 | private ViewManager viewManager; 20 | @Autowired 21 | private ViewLoader viewLoader; 22 | 23 | @Test 24 | void testAutoConfiguration() { 25 | assertNotNull(viewManager, "expected a view manager to have been created"); 26 | assertNotNull(viewLoader, "expected a view loader to have been created"); 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/font/config/FontConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.font.config; 2 | 3 | import com.github.spring.boot.javafx.font.FontRegistry; 4 | import com.github.spring.boot.javafx.font.FontRegistryImpl; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | /** 10 | * Configuration class for configuring fonts in the application. 11 | * This class provides a bean definition for FontRegistry if one is not already present. 12 | */ 13 | @Configuration 14 | public class FontConfiguration { 15 | 16 | /** 17 | * Defines a bean for FontRegistry if no other bean of type FontRegistry is present. 18 | * 19 | * @return the FontRegistry bean instance 20 | */ 21 | @Bean 22 | @ConditionalOnMissingBean(FontRegistry.class) 23 | public FontRegistry fontRegistry() { 24 | return FontRegistryImpl.getInstance(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/view/ViewProperties.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.view; 2 | 3 | import javafx.scene.paint.Paint; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Data 10 | @Builder 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | public class ViewProperties { 14 | /** 15 | * The title of the window. 16 | */ 17 | private String title; 18 | /** 19 | * The icon of the window to show in the title bar or taskbar. 20 | */ 21 | private String icon; 22 | /** 23 | * The new window will be set as a dialog on top of the current window. 24 | */ 25 | private boolean dialog; 26 | /** 27 | * The indication if the new window should be resizable. 28 | */ 29 | @Builder.Default 30 | private boolean resizable = true; 31 | /** 32 | * The indication if the new window will be maximized by default. 33 | */ 34 | private boolean maximized; 35 | /** 36 | * The new window will be automatically centered on the primary screen. 37 | */ 38 | @Builder.Default 39 | private boolean centerOnScreen = true; 40 | /** 41 | * The default background fill color of the scene. 42 | */ 43 | private Paint background; 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/text/config/LocaleTextConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.text.config; 2 | 3 | import com.github.spring.boot.javafx.text.LocaleText; 4 | import com.github.spring.boot.javafx.text.LocaleTextImpl; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.support.ResourceBundleMessageSource; 9 | 10 | /** 11 | * Configuration class for managing locale-specific text resources. 12 | * This class provides a bean definition for LocaleText if one is not already present, 13 | * using a ResourceBundleMessageSource as the underlying message source. 14 | */ 15 | @Configuration 16 | public class LocaleTextConfiguration { 17 | 18 | /** 19 | * Defines a bean for LocaleText if no other bean of type LocaleText is present, 20 | * using the provided ResourceBundleMessageSource as the message source. 21 | * 22 | * @param messageSource the ResourceBundleMessageSource instance to use for managing message sources 23 | * @return the LocaleText bean instance 24 | */ 25 | @Bean 26 | @ConditionalOnMissingBean(LocaleText.class) 27 | public LocaleText localeText(ResourceBundleMessageSource messageSource) { 28 | return new LocaleTextImpl(messageSource); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/com/github/spring/boot/javafx/font/controls/IconRegularTest.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.font.controls; 2 | 3 | import javafx.geometry.Insets; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.extension.ExtendWith; 6 | import org.testfx.framework.junit5.ApplicationExtension; 7 | 8 | import static java.util.Arrays.asList; 9 | import static org.junit.jupiter.api.Assertions.*; 10 | 11 | @ExtendWith(ApplicationExtension.class) 12 | class IconRegularTest { 13 | 14 | @Test 15 | void testNewInstance() { 16 | var result = new IconRegular(); 17 | 18 | assertTrue(result.getStyleClass().contains(AbstractIcon.STYLE_CLASS)); 19 | } 20 | 21 | @Test 22 | void testNewInstanceWithUnicode() { 23 | var unicode = IconRegular.ANGRY_UNICODE; 24 | 25 | var result = new IconRegular(unicode); 26 | 27 | assertTrue(result.getStyleClass().contains(AbstractIcon.STYLE_CLASS)); 28 | assertEquals(unicode, result.getText()); 29 | } 30 | 31 | @Test 32 | void testNewInstanceWithBuilder() { 33 | var unicode = IconRegular.ANGRY_UNICODE; 34 | var padding = new Insets(12, 20, 30, 40); 35 | 36 | var result = IconRegular.builder() 37 | .unicode(unicode) 38 | .padding(padding) 39 | .visible(false) 40 | .styleClasses(asList("foo", "bar")) 41 | .build(); 42 | 43 | assertTrue(result.getStyleClass().contains(AbstractIcon.STYLE_CLASS)); 44 | assertEquals(unicode, result.getText()); 45 | assertEquals(padding, result.getPadding()); 46 | assertFalse(result.isVisible()); 47 | assertTrue(result.getStyleClass().contains("foo")); 48 | assertTrue(result.getStyleClass().contains("bar")); 49 | } 50 | } -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/font/FontRegistryImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.font; 2 | 3 | import javafx.scene.text.Font; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.Objects; 8 | import java.util.Optional; 9 | 10 | /** 11 | * Registry for loading additional fonts into JavaFX. 12 | */ 13 | public class FontRegistryImpl implements FontRegistry { 14 | private static final FontRegistryImpl INSTANCE = new FontRegistryImpl(); 15 | private final Map loadedFonts = new HashMap<>(); 16 | 17 | private FontRegistryImpl() { 18 | } 19 | 20 | /** 21 | * Get the instance of the {@link FontRegistryImpl}. 22 | * 23 | * @return Returns the instance. 24 | */ 25 | public static FontRegistryImpl getInstance() { 26 | return INSTANCE; 27 | } 28 | 29 | @Override 30 | public Font loadFont(String filename) { 31 | Objects.requireNonNull(filename, "filename cannot be null"); 32 | var defaultFont = Font.getDefault(); 33 | 34 | return loadFont(filename, defaultFont.getSize()); 35 | } 36 | 37 | @Override 38 | public Font loadFont(String filename, double size) { 39 | Objects.requireNonNull(filename, "filename cannot be null"); 40 | 41 | if (loadedFonts.containsKey(filename)) 42 | return createFontFromAlreadyLoadedFont(loadedFonts.get(filename), size); 43 | 44 | return loadFontResource(filename, size); 45 | } 46 | 47 | private Font loadFontResource(String filename, double size) { 48 | var resource = getClass().getResource(FONT_DIRECTORY + filename); 49 | var font = Optional.ofNullable(Font.loadFont(resource.toExternalForm(), size)) 50 | .orElseThrow(() -> new FontException(filename)); 51 | 52 | loadedFonts.put(filename, font); 53 | 54 | return font; 55 | } 56 | 57 | private Font createFontFromAlreadyLoadedFont(Font font, double size) { 58 | return Font.font(font.getFamily(), size); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/com/github/spring/boot/javafx/stage/BorderlessStageTest.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.stage; 2 | 3 | import javafx.application.Platform; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.extension.ExtendWith; 6 | import org.testfx.framework.junit5.ApplicationExtension; 7 | import org.testfx.util.WaitForAsyncUtils; 8 | 9 | import java.util.concurrent.atomic.AtomicReference; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertEquals; 12 | 13 | @ExtendWith(ApplicationExtension.class) 14 | class BorderlessStageTest { 15 | @Test 16 | void testSetHeader() { 17 | var expectedResult = 10.0; 18 | var stage = new AtomicReference(); 19 | Platform.runLater(() -> stage.set(new BorderlessStage())); 20 | WaitForAsyncUtils.waitForFxEvents(); 21 | 22 | stage.get().setHeader(expectedResult); 23 | 24 | assertEquals(expectedResult, stage.get().getHeader()); 25 | } 26 | 27 | @Test 28 | void testHeaderProperty() { 29 | var expectedResult = 15.0; 30 | var stage = new AtomicReference(); 31 | var headerHolder = new AtomicReference(); 32 | Platform.runLater(() -> stage.set(new BorderlessStage())); 33 | WaitForAsyncUtils.waitForFxEvents(); 34 | 35 | stage.get().headerProperty().addListener((observable, oldValue, newValue) -> { 36 | headerHolder.set(newValue.doubleValue()); 37 | }); 38 | stage.get().setHeader(expectedResult); 39 | WaitForAsyncUtils.waitForFxEvents(); 40 | 41 | assertEquals(expectedResult, headerHolder.get()); 42 | } 43 | 44 | @Test 45 | void testSetResizeBorder() { 46 | var expectedResult = 5.0; 47 | var stage = new AtomicReference(); 48 | Platform.runLater(() -> stage.set(new BorderlessStage())); 49 | WaitForAsyncUtils.waitForFxEvents(); 50 | 51 | stage.get().setResizeBorder(expectedResult); 52 | 53 | assertEquals(expectedResult, stage.get().getResizeBorder()); 54 | } 55 | } -------------------------------------------------------------------------------- /src/test/java/com/github/spring/boot/javafx/view/ViewLoaderImplTest.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.view; 2 | 3 | import com.github.spring.boot.javafx.JavaFxAutoConfiguration; 4 | import com.github.spring.boot.javafx.TestConfiguration; 5 | import com.github.spring.boot.javafx.controllers.MyTestController; 6 | import javafx.fxml.FXML; 7 | import javafx.scene.layout.Pane; 8 | import org.junit.jupiter.api.AfterEach; 9 | import org.junit.jupiter.api.Test; 10 | import org.junit.jupiter.api.extension.ExtendWith; 11 | import org.mockito.junit.jupiter.MockitoExtension; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.context.SpringBootTest; 14 | import org.testfx.framework.junit5.ApplicationExtension; 15 | 16 | import static org.junit.jupiter.api.Assertions.assertNotNull; 17 | 18 | @SpringBootTest( 19 | classes = { 20 | JavaFxAutoConfiguration.class, 21 | TestConfiguration.class 22 | }, 23 | webEnvironment = SpringBootTest.WebEnvironment.NONE 24 | ) 25 | @ExtendWith({MockitoExtension.class, ApplicationExtension.class}) 26 | public class ViewLoaderImplTest { 27 | @Autowired 28 | ViewLoader viewLoader; 29 | @Autowired 30 | MyTestController controller; 31 | 32 | @AfterEach 33 | void tearDown() { 34 | controller.setRoot(null); 35 | } 36 | 37 | @Test 38 | void testLoadWithoutController() { 39 | var result = viewLoader.load("load_view_with_controller.fxml"); 40 | 41 | assertNotNull(result); 42 | assertNotNull(controller.getRoot(), "expected a root node to have been wired"); 43 | } 44 | 45 | @Test 46 | void testLoadWithController() { 47 | var controller = new MyController(); 48 | 49 | var result = viewLoader.load("load_view_without_controller.fxml", controller); 50 | 51 | assertNotNull(result); 52 | assertNotNull(controller.root, "expected a root node to have been wired"); 53 | } 54 | 55 | public static class MyController { 56 | @FXML 57 | Pane root; 58 | } 59 | } -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/view/config/ViewConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.view.config; 2 | 3 | import com.github.spring.boot.javafx.text.LocaleText; 4 | import com.github.spring.boot.javafx.view.ViewLoader; 5 | import com.github.spring.boot.javafx.view.ViewLoaderImpl; 6 | import com.github.spring.boot.javafx.view.ViewManager; 7 | import com.github.spring.boot.javafx.view.ViewManagerImpl; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 9 | import org.springframework.context.ApplicationContext; 10 | import org.springframework.context.ConfigurableApplicationContext; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | 14 | /** 15 | * Configuration class for managing views in the application. 16 | * This class provides bean definitions for ViewLoader and ViewManager 17 | * if no beans of these types are already present. 18 | */ 19 | @Configuration 20 | public class ViewConfiguration { 21 | 22 | /** 23 | * Defines a bean for ViewLoader if no other bean of type ViewLoader is present. 24 | * 25 | * @param applicationContext the application context 26 | * @param viewManager the ViewManager instance 27 | * @param localeText the LocaleText instance 28 | * @return the ViewLoader bean instance 29 | */ 30 | @Bean 31 | @ConditionalOnMissingBean(ViewLoader.class) 32 | public ViewLoader viewLoader(ApplicationContext applicationContext, ViewManager viewManager, LocaleText localeText) { 33 | return new ViewLoaderImpl(applicationContext, viewManager, localeText); 34 | } 35 | 36 | /** 37 | * Defines a bean for ViewManager if no other bean of type ViewManager is present. 38 | * 39 | * @param applicationContext the configurable application context 40 | * @return the ViewManager bean instance 41 | */ 42 | @Bean 43 | @ConditionalOnMissingBean(ViewManager.class) 44 | public ViewManager viewManager(ConfigurableApplicationContext applicationContext) { 45 | return new ViewManagerImpl(applicationContext); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/view/ViewManager.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.view; 2 | 3 | import javafx.beans.property.Property; 4 | import javafx.beans.property.ReadOnlyProperty; 5 | import javafx.scene.Scene; 6 | import javafx.stage.Stage; 7 | 8 | import java.util.Optional; 9 | 10 | public interface ViewManager { 11 | /** 12 | * Get the current policy for the view manager. 13 | * 14 | * @return Returns the current policy. 15 | */ 16 | ViewManagerPolicy getPolicy(); 17 | 18 | /** 19 | * Get the policy property of the view manager. 20 | * 21 | * @return Returns the policy property. 22 | */ 23 | Property policyProperty(); 24 | 25 | /** 26 | * Set the policy for the view manager which it follows when all windows are closed. 27 | * 28 | * @param policy Set the policy that needs to be applied. 29 | */ 30 | void setPolicy(ViewManagerPolicy policy); 31 | 32 | /** 33 | * Get the primary window of the JavaFX application. 34 | * 35 | * @return Returns the primary window if present, else {@link Optional#empty()}. 36 | */ 37 | Optional getPrimaryStage(); 38 | 39 | /** 40 | * Get the primary stage property of the view manager. 41 | * 42 | * @return Returns the primary stage property. 43 | */ 44 | ReadOnlyProperty primaryStageProperty(); 45 | 46 | /** 47 | * Get the total amount of windows which are currently being shown. 48 | * 49 | * @return Returns the total amount of shown windows. 50 | */ 51 | int getTotalWindows(); 52 | 53 | /** 54 | * Get the JavaFX window by the given name. 55 | * 56 | * @param name Set the name of the window. 57 | * @return Returns the window if found, else {@link Optional#empty()}. 58 | */ 59 | Optional getStage(String name); 60 | 61 | /** 62 | * Register the primary stage of the JavaFX application. 63 | * 64 | * @param primaryStage The primary stage to register in this manager. 65 | */ 66 | void registerPrimaryStage(Stage primaryStage); 67 | 68 | /** 69 | * Add a new opened window to the manager. 70 | * 71 | * @param window Set the new window. 72 | * @param view Set the corresponding loaded view of the window. 73 | */ 74 | void addWindowView(Stage window, Scene view); 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/stage/BorderlessStage.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.stage; 2 | 3 | import javafx.beans.property.DoubleProperty; 4 | import javafx.stage.Stage; 5 | import javafx.stage.StageStyle; 6 | 7 | /** 8 | * The {@link BorderlessStage} uses the {@link StageStyle#UNDECORATED} style but allows the functionality of 9 | * moving- and resizing the stage by the user. 10 | */ 11 | public class BorderlessStage extends Stage { 12 | private final BorderlessStageWrapper stageWrapper; 13 | 14 | //region Constructors 15 | 16 | /** 17 | * Initialize a new instance of the borderless stage. 18 | */ 19 | public BorderlessStage() { 20 | this.stageWrapper = new BorderlessStageWrapper(this); 21 | } 22 | 23 | //endregion 24 | 25 | //region Properties 26 | 27 | /** 28 | * Get the header height of the pane which allows the stage to be dragged around. 29 | * 30 | * @return Returns the header height of this pane. 31 | */ 32 | public double getHeader() { 33 | return stageWrapper.getHeader(); 34 | } 35 | 36 | /** 37 | * Get the header height property. 38 | * 39 | * @return Return the property of the header height. 40 | */ 41 | public DoubleProperty headerProperty() { 42 | return stageWrapper.headerProperty(); 43 | } 44 | 45 | /** 46 | * Set the header height of the pane which allows the stage to be dragged around. 47 | * 48 | * @param header The height of the header. 49 | */ 50 | public void setHeader(double header) { 51 | stageWrapper.setHeader(header); 52 | } 53 | 54 | /** 55 | * Get the width of the resize border. 56 | * 57 | * @return Returns the width of the resize border. 58 | */ 59 | public double getResizeBorder() { 60 | return stageWrapper.getResizeBorder(); 61 | } 62 | 63 | /** 64 | * Get the resize border property. 65 | * 66 | * @return Returns the resize border property. 67 | */ 68 | public DoubleProperty resizeBorderProperty() { 69 | return stageWrapper.resizeBorderProperty(); 70 | } 71 | 72 | /** 73 | * Set the new width of the resize border. 74 | * 75 | * @param resizeBorder The new border resize width. 76 | */ 77 | public void setResizeBorder(double resizeBorder) { 78 | stageWrapper.setResizeBorder(resizeBorder); 79 | } 80 | 81 | //endregion 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/ui/scale/ScaleAwareImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.ui.scale; 2 | 3 | import javafx.scene.Scene; 4 | import javafx.scene.layout.Region; 5 | import javafx.scene.transform.Scale; 6 | import javafx.stage.Window; 7 | 8 | /** 9 | * Abstract implementation of {@link ScaleAware} for scaling the scene during initialization. 10 | */ 11 | public abstract class ScaleAwareImpl implements ScaleAware { 12 | protected float scaleFactor; 13 | 14 | private Scene scene; 15 | private Region root; 16 | private Scale scale; 17 | 18 | @Override 19 | public void scale(Scene scene, Region root, float scale) { 20 | if (scene == null) 21 | throw new MissingScaleAwarePropertyException(); 22 | 23 | this.scene = scene; 24 | this.root = root; 25 | this.scaleFactor = scale; 26 | 27 | initializeScaling(); 28 | } 29 | 30 | @Override 31 | public void onScaleChanged(float newValue) { 32 | // check if the initial scaling has already been applied before trying to rescale the scene 33 | if (scene == null) 34 | return; 35 | 36 | this.scaleFactor = newValue; 37 | scale(); 38 | } 39 | 40 | private void initializeScaling() { 41 | Window window = scene.getWindow(); 42 | 43 | // set initial window size 44 | window.setWidth(root.getPrefWidth() * scaleFactor); 45 | window.setHeight(root.getPrefHeight() * scaleFactor); 46 | 47 | // scale the scene by the given scale factor 48 | scene.widthProperty().addListener((observable, oldValue, newValue) -> scaleWidth(newValue.doubleValue())); 49 | scene.heightProperty().addListener((observable, oldValue, newValue) -> scaleHeight(newValue.doubleValue())); 50 | 51 | // apply a scale transformer to the root region 52 | scale = new Scale(scaleFactor, scaleFactor); 53 | scale.setPivotX(0); 54 | scale.setPivotY(0); 55 | root.getTransforms().setAll(scale); 56 | } 57 | 58 | private void scale() { 59 | scale.setX(this.scaleFactor); 60 | scale.setY(this.scaleFactor); 61 | 62 | scaleWidth(scene.getWidth()); 63 | scaleHeight(scene.getHeight()); 64 | } 65 | 66 | private void scaleWidth(Double newValue) { 67 | root.setPrefWidth(newValue * 1 / scaleFactor); 68 | } 69 | 70 | private void scaleHeight(Double newValue) { 71 | root.setPrefHeight(newValue * 1 / scaleFactor); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/text/LocaleText.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.text; 2 | 3 | import javafx.beans.property.ReadOnlyObjectProperty; 4 | import org.springframework.context.support.MessageSourceAccessor; 5 | 6 | import java.util.Locale; 7 | import java.util.ResourceBundle; 8 | 9 | /** 10 | * Defines a easy way of loading localized text messages. 11 | * This interface uses {@link Message} for easy localized text definitions. 12 | */ 13 | public interface LocaleText { 14 | /** 15 | * The property name of the locale text message source accessor. 16 | */ 17 | String MESSAGE_SOURCE_ACCESSOR_PROPERTY = "messageSourceAccessor"; 18 | 19 | /** 20 | * The property name of the locale text resource bundle. 21 | */ 22 | String RESOURCE_BUNDLE_PROPERTY = "resourceBundle"; 23 | 24 | /** 25 | * Get the message source that is used by this {@link LocaleText}. 26 | * 27 | * @return Returns the message source. 28 | */ 29 | MessageSourceAccessor getMessageSource(); 30 | 31 | /** 32 | * Get the message source accessor property of this locale text. 33 | * 34 | * @return Returns the read-only message source accessor property. 35 | */ 36 | ReadOnlyObjectProperty messageSourceProperty(); 37 | 38 | /** 39 | * Get the resource bundle that is used by this {@link LocaleText}. 40 | * 41 | * @return Returns the resource bundle. 42 | */ 43 | ResourceBundle getResourceBundle(); 44 | 45 | /** 46 | * Get the resource bundle property of this locale text. 47 | * 48 | * @return Returns the read-only instance of the resource bundle property. 49 | */ 50 | ReadOnlyObjectProperty resourceBundleProperty(); 51 | 52 | /** 53 | * Get the text for the given message key. 54 | * 55 | * @param message Set the message key. 56 | * @return Returns the formatted text. 57 | */ 58 | String get(Message message); 59 | 60 | /** 61 | * Get the text for the given message key. 62 | * 63 | * @param message Set the message key. 64 | * @param args Set the arguments to pass to the message. 65 | * @return Returns the formatted text. 66 | */ 67 | String get(Message message, Object... args); 68 | 69 | /** 70 | * Get the text for the given message. 71 | * 72 | * @param message Set the message. 73 | * @param args Set the arguments to pass to the message. 74 | * @return Returns the formatted text. 75 | */ 76 | String get(String message, Object... args); 77 | 78 | /** 79 | * Update the locale that needs to be used for the localized texts. 80 | * 81 | * @param locale The new locale to use. 82 | */ 83 | void updateLocale(Locale locale); 84 | } 85 | -------------------------------------------------------------------------------- /src/test/java/com/github/spring/boot/javafx/font/controls/IconTest.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.font.controls; 2 | 3 | import javafx.geometry.Insets; 4 | import javafx.scene.paint.Color; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import org.testfx.framework.junit5.ApplicationExtension; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | import static org.junit.jupiter.api.Assertions.assertTrue; 11 | 12 | @ExtendWith(ApplicationExtension.class) 13 | class IconTest { 14 | public static final String FONT_FAMILY = "FontAwesome"; 15 | 16 | @Test 17 | void testNewInstance() { 18 | var result = new Icon(); 19 | 20 | assertTrue(result.getStyleClass().contains(AbstractIcon.STYLE_CLASS)); 21 | assertEquals(FONT_FAMILY, result.getFont().getFamily()); 22 | } 23 | 24 | @Test 25 | void testNewInstanceWithUnicode() { 26 | var unicode = Icon.ADJUST_UNICODE; 27 | 28 | var result = new Icon(unicode); 29 | 30 | assertTrue(result.getStyleClass().contains(AbstractIcon.STYLE_CLASS)); 31 | assertEquals(unicode, result.getText()); 32 | } 33 | 34 | @Test 35 | void testNewInstanceWithBuilder() { 36 | var padding = new Insets(10, 20, 30, 40); 37 | 38 | var result = Icon.builder() 39 | .unicode(Icon.AMAZON_UNICODE) 40 | .padding(padding) 41 | .visible(true) 42 | .build(); 43 | 44 | assertTrue(result.getStyleClass().contains(AbstractIcon.STYLE_CLASS)); 45 | assertEquals(Icon.AMAZON_UNICODE, result.getText()); 46 | assertEquals(padding, result.getPadding()); 47 | assertTrue(result.isVisible()); 48 | assertEquals(FONT_FAMILY, result.getFont().getFamily(), "expected the font family to not have been changed"); 49 | } 50 | 51 | @Test 52 | void testSizeFactor() { 53 | var icon = new Icon(); 54 | assertEquals(0.0, icon.getSizeFactor(), "expected the initial size factor to be 0.0"); 55 | 56 | var newSizeFactor = 2.3; 57 | icon.setSizeFactor(newSizeFactor); 58 | assertEquals(newSizeFactor, icon.getSizeFactor(), "expected the size factor to be 2.3"); 59 | assertEquals(FONT_FAMILY, icon.getFont().getFamily(), "expected the font family to not have been changed"); 60 | } 61 | 62 | @Test 63 | void testTextColor() { 64 | var icon = new Icon(); 65 | assertEquals(Color.BLACK, icon.getTextFill(), "expected the initial text color to be black"); 66 | 67 | icon.setColor(Color.RED); 68 | assertEquals(Color.RED, icon.getTextFill(), "expected the text color to be red"); 69 | assertEquals(FONT_FAMILY, icon.getFont().getFamily(), "expected the font family to not have been changed"); 70 | } 71 | } -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/SpringJavaFXApplication.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx; 2 | 3 | import com.github.spring.boot.javafx.view.ViewManager; 4 | import javafx.application.Application; 5 | import javafx.application.Preloader; 6 | import javafx.stage.Stage; 7 | import org.springframework.boot.Banner; 8 | import org.springframework.boot.SpringApplication; 9 | import org.springframework.context.ConfigurableApplicationContext; 10 | 11 | /** 12 | * Abstract Spring Boot extension of {@link Application}. 13 | */ 14 | public abstract class SpringJavaFXApplication extends Application { 15 | /** 16 | * The application context created by the JavaFX starter. 17 | * This context will only be available once the {@link #init()} has been invoked by JavaFX. 18 | */ 19 | protected ConfigurableApplicationContext applicationContext; 20 | 21 | /** 22 | * Launch a JavaFX application with for the given class and program arguments. 23 | * 24 | * @param appClass The class to launch the JavaFX application for. 25 | * @param args The program arguments. 26 | */ 27 | @SuppressWarnings("unused") 28 | public static void launch(Class appClass, String... args) { 29 | Application.launch(appClass, args); 30 | } 31 | 32 | /** 33 | * Launch a JavaFX application with for the given class and program arguments. 34 | * 35 | * @param appClass The class to launch the JavaFX application for. 36 | * @param preloaderClass The class to use as the preloader of the JavaFX application. 37 | * @param args The program arguments. 38 | */ 39 | @SuppressWarnings("unused") 40 | public static void launch(Class appClass, Class preloaderClass, String... args) { 41 | System.setProperty("javafx.preloader", preloaderClass.getName()); 42 | Application.launch(appClass, args); 43 | } 44 | 45 | /** 46 | * Launch a JavaFX application with the given program arguments. 47 | * 48 | * @param args The program arguments. 49 | */ 50 | @SuppressWarnings("unused") 51 | public static void launch(String... args) { 52 | Application.launch(args); 53 | } 54 | 55 | @Override 56 | public void init() { 57 | Parameters parameters = getParameters(); 58 | SpringApplication application = new SpringApplication(this.getClass()); 59 | 60 | application.setBannerMode(Banner.Mode.OFF); 61 | application.setHeadless(false); 62 | 63 | applicationContext = application.run(parameters.getRaw().toArray(new String[0])); 64 | } 65 | 66 | @Override 67 | public void start(Stage primaryStage) throws Exception { 68 | ViewManager viewManager = applicationContext.getBean(ViewManager.class); 69 | viewManager.registerPrimaryStage(primaryStage); 70 | } 71 | 72 | @Override 73 | public void stop() throws Exception { 74 | applicationContext.close(); 75 | System.exit(0); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/view/ViewLoader.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.view; 2 | 3 | import javafx.scene.Node; 4 | import javafx.scene.layout.Pane; 5 | import javafx.stage.Stage; 6 | 7 | public interface ViewLoader { 8 | /** 9 | * The directory containing the FXML files. 10 | */ 11 | String VIEW_DIRECTORY = "/views"; 12 | /** 13 | * The directory contain 14 | */ 15 | String IMAGE_DIRECTORY = "/images"; 16 | 17 | /** 18 | * Set the UI scale of the views. 19 | * 20 | * @param scale The scale value of the ui. 21 | */ 22 | void setScale(float scale); 23 | 24 | /** 25 | * Load and show the given view. 26 | * 27 | * @param view Set the view to load and show. 28 | * @param properties The view properties. 29 | */ 30 | void show(String view, ViewProperties properties); 31 | 32 | /** 33 | * Show the primary scene on the given primary window. 34 | * 35 | * @param window The window. 36 | * @param view The view scene to load. 37 | * @param properties The view properties. 38 | */ 39 | void show(Stage window, String view, ViewProperties properties); 40 | 41 | /** 42 | * Show the given view in a new window. 43 | * 44 | * @param view The FXML file to load and show. 45 | * @param properties The properties of the window. 46 | */ 47 | void showWindow(String view, ViewProperties properties); 48 | 49 | /** 50 | * Show the given pane in a new window. 51 | * 52 | * @param pane The root pane to show in the window. 53 | * @param controller The controller of the root pane.µ 54 | * @param properties The properties of the window. 55 | */ 56 | void showWindow(Pane pane, Object controller, ViewProperties properties); 57 | 58 | /** 59 | * Load the given FXML view file from the classpath. 60 | * Spring will inject the correct controller based on the {@code fx:controller} attribute in the view. 61 | * 62 | * @param view The FXML file to load. 63 | * @param The root node of the view file. 64 | * @return Returns the root node of the loaded FXML view if successfully loaded, else null. 65 | */ 66 | T load(String view); 67 | 68 | /** 69 | * Load the given FXML view from the classpath and use the given controller within the loaded view. (discouraged) 70 | * Use Spring's {@link org.springframework.context.annotation.Scope} annotation on the {@link com.github.spring.boot.javafx.stereotype.ViewController} 71 | * instead. 72 | * This will create a new controller instance each time the view is loaded (and use {@link #load(String)} instead). 73 | * 74 | * @param view The FXML file to load. 75 | * @param controller The controller to wire into the view. 76 | * @param The root node of the view file. 77 | * @return Returns the root node of the loaded FXML view if successfully loaded, else null. 78 | */ 79 | T load(String view, Object controller); 80 | } 81 | -------------------------------------------------------------------------------- /src/test/java/com/github/spring/boot/javafx/ui/scale/ScaleAwareImplTest.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.ui.scale; 2 | 3 | import javafx.beans.property.SimpleDoubleProperty; 4 | import javafx.collections.ObservableList; 5 | import javafx.scene.Scene; 6 | import javafx.scene.layout.Region; 7 | import javafx.scene.transform.Scale; 8 | import javafx.scene.transform.Transform; 9 | import javafx.stage.Window; 10 | import org.junit.jupiter.api.BeforeEach; 11 | import org.junit.jupiter.api.Test; 12 | import org.junit.jupiter.api.extension.ExtendWith; 13 | import org.mockito.Mock; 14 | import org.mockito.junit.jupiter.MockitoExtension; 15 | 16 | import java.util.concurrent.atomic.AtomicReference; 17 | 18 | import static org.junit.jupiter.api.Assertions.*; 19 | import static org.mockito.ArgumentMatchers.isA; 20 | import static org.mockito.Mockito.lenient; 21 | import static org.mockito.Mockito.when; 22 | 23 | @ExtendWith(MockitoExtension.class) 24 | class ScaleAwareImplTest { 25 | @Mock 26 | private Scene scene; 27 | @Mock 28 | private Region root; 29 | @Mock 30 | private Window window; 31 | @Mock 32 | private ObservableList transforms; 33 | private MyController controller; 34 | 35 | @BeforeEach 36 | void setUp() { 37 | lenient().when(root.getTransforms()).thenReturn(transforms); 38 | lenient().when(scene.getWindow()).thenReturn(window); 39 | 40 | controller = new MyController(); 41 | } 42 | 43 | @Test 44 | void testScale_whenSceneIsMissing_shouldThrowMissingScaleAwarePropertyException() { 45 | assertThrows(MissingScaleAwarePropertyException.class, () -> controller.scale(null, root, 1.0f), "Missing scene to execute scale aware logic"); 46 | } 47 | 48 | @Test 49 | void testScale_whenInitialScaleIsGiven_shouldInitializeSceneWithTheGivenScale() { 50 | float scale = 1.5f; 51 | AtomicReference scaleHolder = new AtomicReference<>(); 52 | when(transforms.setAll(isA(Scale.class))).thenAnswer(invocation -> { 53 | scaleHolder.set(invocation.getArgument(0, Scale.class)); 54 | return true; 55 | }); 56 | when(scene.widthProperty()).thenReturn(new SimpleDoubleProperty()); 57 | when(scene.heightProperty()).thenReturn(new SimpleDoubleProperty()); 58 | 59 | controller.scale(scene, root, scale); 60 | Scale result = scaleHolder.get(); 61 | 62 | assertNotNull(result, "Expected a scale to have been set on the region"); 63 | assertEquals(scale, result.getX()); 64 | assertEquals(scale, result.getY()); 65 | assertEquals(0, result.getPivotX()); 66 | assertEquals(0, result.getPivotY()); 67 | } 68 | 69 | @Test 70 | void testOnScaleChanged_whenNewValueIsGiven_shouldUpdateTheScaleWithTheNewValue() { 71 | float initialScale = 1.0f; 72 | float expectedScale = 1.8f; 73 | AtomicReference scaleHolder = new AtomicReference<>(); 74 | when(transforms.setAll(isA(Scale.class))).thenAnswer(invocation -> { 75 | scaleHolder.set(invocation.getArgument(0, Scale.class)); 76 | return true; 77 | }); 78 | when(scene.widthProperty()).thenReturn(new SimpleDoubleProperty()); 79 | when(scene.heightProperty()).thenReturn(new SimpleDoubleProperty()); 80 | 81 | controller.scale(scene, root, initialScale); 82 | controller.onScaleChanged(expectedScale); 83 | Scale scale = scaleHolder.get(); 84 | 85 | assertNotNull(scale, "Expected the scale to have been set on the region"); 86 | assertEquals(expectedScale, scale.getX()); 87 | assertEquals(expectedScale, scale.getY()); 88 | } 89 | 90 | static class MyController extends ScaleAwareImpl { 91 | } 92 | } -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/font/controls/AbstractIcon.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.font.controls; 2 | 3 | import com.github.spring.boot.javafx.font.FontRegistryImpl; 4 | import javafx.beans.property.DoubleProperty; 5 | import javafx.beans.property.SimpleDoubleProperty; 6 | import javafx.scene.control.Label; 7 | import javafx.scene.paint.Color; 8 | import javafx.scene.text.Font; 9 | import javafx.scene.text.FontWeight; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.util.Assert; 12 | 13 | import java.util.Optional; 14 | import java.util.function.Consumer; 15 | 16 | /** 17 | * Abstract implementation of a Font Awesome icon that extends the {@link Label}. 18 | * This class loads the font file and prevents any changes to the initial loaded font family. 19 | */ 20 | @Slf4j 21 | @SuppressWarnings("unused") 22 | abstract class AbstractIcon extends Label { 23 | public static final String STYLE_CLASS ="icon"; 24 | 25 | private final DoubleProperty sizeFactorProperty = new SimpleDoubleProperty(); 26 | private String fontFamily; 27 | private boolean updating; 28 | 29 | AbstractIcon(String filename) { 30 | Assert.hasText(filename, "filename cannot be empty"); 31 | init(filename); 32 | } 33 | 34 | AbstractIcon(String filename, String text) { 35 | super(text); 36 | Assert.hasText(filename, "filename cannot be empty"); 37 | init(filename); 38 | } 39 | 40 | public double getSizeFactor() { 41 | return sizeFactorProperty.get(); 42 | } 43 | 44 | public void setSizeFactor(double factor) { 45 | sizeFactorProperty.set(factor); 46 | } 47 | 48 | public void setColor(Color color) { 49 | setTextFill(color); 50 | } 51 | 52 | void setProperty(T property, Consumer mapping) { 53 | Optional.ofNullable(property) 54 | .ifPresent(mapping); 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | return getText(); 60 | } 61 | 62 | private void init(String filename) { 63 | initializeFont(filename); 64 | initializeSizeFactor(); 65 | initializeFontFamilyListener(); 66 | initializeStyleClass(); 67 | } 68 | 69 | private void initializeFont(String filename) { 70 | var font = FontRegistryImpl.getInstance().loadFont(filename); 71 | 72 | fontFamily = font.getFamily(); 73 | setFont(font); 74 | } 75 | 76 | private void initializeSizeFactor() { 77 | sizeFactorProperty.addListener((observable, oldValue, newValue) -> { 78 | updating = true; 79 | var oldFont = getFont(); 80 | var fontSize = getActualSize(newValue.doubleValue(), oldFont.getSize()); 81 | 82 | setFont(Font.font(fontFamily, FontWeight.findByName(oldFont.getStyle()), fontSize)); 83 | updating = false; 84 | }); 85 | } 86 | 87 | private void initializeFontFamilyListener() { 88 | // this listener prevents any changes to the font family 89 | fontProperty().addListener((observable, oldValue, newValue) -> { 90 | if (newValue.getFamily().equals(fontFamily) || updating) 91 | return; 92 | 93 | updating = true; 94 | double fontSize = getActualSize(sizeFactorProperty.get(), newValue.getSize()); 95 | 96 | setFont(Font.font(fontFamily, FontWeight.findByName(newValue.getStyle()), fontSize)); 97 | updating = false; 98 | }); 99 | } 100 | 101 | private void initializeStyleClass() { 102 | this.getStyleClass().add(STYLE_CLASS); 103 | } 104 | 105 | private double getActualSize(double sizeFactor, double fontSize) { 106 | return sizeFactor <= 0 ? fontSize : sizeFactor * fontSize; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/text/LocaleTextImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.text; 2 | 3 | import javafx.beans.property.ObjectProperty; 4 | import javafx.beans.property.ReadOnlyObjectProperty; 5 | import javafx.beans.property.SimpleObjectProperty; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.apache.commons.lang3.ArrayUtils; 8 | import org.springframework.context.NoSuchMessageException; 9 | import org.springframework.context.support.MessageSourceAccessor; 10 | import org.springframework.context.support.MessageSourceResourceBundle; 11 | import org.springframework.context.support.ResourceBundleMessageSource; 12 | 13 | import java.util.Locale; 14 | import java.util.Objects; 15 | import java.util.ResourceBundle; 16 | 17 | /** 18 | * Get the localized text for the given message key. 19 | * This class uses Spring's {@link MessageSourceResourceBundle} for retrieving the locale strings. 20 | */ 21 | @Slf4j 22 | public class LocaleTextImpl implements LocaleText { 23 | private final ResourceBundleMessageSource messageSource; 24 | private final ObjectProperty messageSourceAccessor = new SimpleObjectProperty<>(this, MESSAGE_SOURCE_ACCESSOR_PROPERTY); 25 | private final ObjectProperty resourceBundle = new SimpleObjectProperty<>(this, RESOURCE_BUNDLE_PROPERTY); 26 | 27 | /** 28 | * Initialize a new instance of {@link LocaleTextImpl}. 29 | * 30 | * @param messageSource set the message source to use. 31 | */ 32 | public LocaleTextImpl(ResourceBundleMessageSource messageSource) { 33 | Objects.requireNonNull(messageSource, "messageSource cannot be null"); 34 | this.messageSource = messageSource; 35 | 36 | init(); 37 | } 38 | 39 | //region Properties 40 | 41 | @Override 42 | public MessageSourceAccessor getMessageSource() { 43 | return messageSourceAccessor.get(); 44 | } 45 | 46 | @Override 47 | public ObjectProperty messageSourceProperty() { 48 | return messageSourceAccessor; 49 | } 50 | 51 | private void setMessageSource(MessageSourceAccessor messageSourceAccessor) { 52 | this.messageSourceAccessor.set(messageSourceAccessor); 53 | } 54 | 55 | @Override 56 | public ResourceBundle getResourceBundle() { 57 | return resourceBundle.get(); 58 | } 59 | 60 | @Override 61 | public ReadOnlyObjectProperty resourceBundleProperty() { 62 | return resourceBundle; 63 | } 64 | 65 | private void setResourceBundle(ResourceBundle resourceBundle) { 66 | this.resourceBundle.set(resourceBundle); 67 | } 68 | 69 | //endregion 70 | 71 | //region Getters 72 | 73 | @Override 74 | public String get(Message message) { 75 | return get(message, ArrayUtils.EMPTY_OBJECT_ARRAY); 76 | } 77 | 78 | @Override 79 | public String get(Message message, Object... args) { 80 | return get(message.getKey(), args); 81 | } 82 | 83 | @Override 84 | public String get(String message, Object... args) { 85 | try { 86 | return getMessageSource().getMessage(message, args); 87 | } catch (NoSuchMessageException ex) { 88 | log.warn("Message key '" + message + "' not found", ex); 89 | return message; 90 | } 91 | } 92 | 93 | //endregion 94 | 95 | //region Methods 96 | 97 | @Override 98 | public void updateLocale(Locale locale) { 99 | setMessageSource(createMessageSourceAccessor(locale)); 100 | setResourceBundle(createResourceBundle(locale)); 101 | } 102 | 103 | //endregion 104 | 105 | //region Functions 106 | 107 | private void init() { 108 | setMessageSource(createMessageSourceAccessor(Locale.getDefault())); 109 | setResourceBundle(createResourceBundle(Locale.getDefault())); 110 | } 111 | 112 | private MessageSourceAccessor createMessageSourceAccessor(Locale locale) { 113 | return new MessageSourceAccessor(messageSource, locale); 114 | } 115 | 116 | private MessageSourceResourceBundle createResourceBundle(Locale locale) { 117 | return new MessageSourceResourceBundle(messageSource, locale); 118 | } 119 | 120 | //endregion 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/view/ViewManagerImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.view; 2 | 3 | import javafx.application.Platform; 4 | import javafx.beans.property.Property; 5 | import javafx.beans.property.ReadOnlyProperty; 6 | import javafx.beans.property.SimpleObjectProperty; 7 | import javafx.event.EventHandler; 8 | import javafx.scene.Scene; 9 | import javafx.stage.Stage; 10 | import javafx.stage.WindowEvent; 11 | import lombok.AllArgsConstructor; 12 | import lombok.EqualsAndHashCode; 13 | import lombok.Value; 14 | import lombok.extern.slf4j.Slf4j; 15 | import org.springframework.context.ConfigurableApplicationContext; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | import java.util.Objects; 20 | import java.util.Optional; 21 | 22 | @Slf4j 23 | @EqualsAndHashCode 24 | public class ViewManagerImpl implements ViewManager { 25 | public static final String PRIMARY_STAGE_PROPERTY = "primaryStage"; 26 | public static final String POLICY_PROPERTY = "policy"; 27 | 28 | private final ConfigurableApplicationContext applicationContext; 29 | private final List windows = new ArrayList<>(); 30 | private final Property primaryStage = new SimpleObjectProperty<>(this, PRIMARY_STAGE_PROPERTY); 31 | private final Property policy = new SimpleObjectProperty<>(this, POLICY_PROPERTY, ViewManagerPolicy.CLOSEABLE); 32 | 33 | //region Constructors 34 | 35 | public ViewManagerImpl(ConfigurableApplicationContext applicationContext) { 36 | this.applicationContext = applicationContext; 37 | } 38 | 39 | //endregion 40 | 41 | //region Properties 42 | 43 | @Override 44 | public Optional getPrimaryStage() { 45 | return Optional.ofNullable(primaryStage.getValue()); 46 | } 47 | 48 | @Override 49 | public ReadOnlyProperty primaryStageProperty() { 50 | return primaryStage; 51 | } 52 | 53 | @Override 54 | public Optional getStage(String name) { 55 | return windows.stream() 56 | .filter(e -> e.getStage().getTitle().equalsIgnoreCase(name)) 57 | .findFirst() 58 | .map(Window::getStage); 59 | } 60 | 61 | @Override 62 | public ViewManagerPolicy getPolicy() { 63 | return policy.getValue(); 64 | } 65 | 66 | @Override 67 | public Property policyProperty() { 68 | return policy; 69 | } 70 | 71 | @Override 72 | public void setPolicy(ViewManagerPolicy policy) { 73 | this.policy.setValue(policy); 74 | } 75 | 76 | @Override 77 | public int getTotalWindows() { 78 | return windows.size(); 79 | } 80 | 81 | //endregion 82 | 83 | //region Methods 84 | 85 | @Override 86 | public void registerPrimaryStage(Stage primaryStage) { 87 | Objects.requireNonNull(primaryStage, "primaryStage cannot be null"); 88 | if (getPrimaryStage().isPresent()) { 89 | log.warn("Ignoring primary stage register as one has already been registered"); 90 | return; 91 | } 92 | 93 | this.primaryStage.setValue(primaryStage); 94 | addWindowView(primaryStage, primaryStage.getScene(), true); 95 | } 96 | 97 | @Override 98 | public void addWindowView(Stage window, Scene view) { 99 | Objects.requireNonNull(window, "window cannot be null"); 100 | Objects.requireNonNull(view, "view cannot be null"); 101 | addWindowView(window, view, false); 102 | } 103 | 104 | //endregion 105 | 106 | //region Functions 107 | 108 | private void addWindowView(Stage window, Scene view, boolean isPrimaryStage) { 109 | window.setOnHiding(onWindowClosingEventHandler()); 110 | windows.add(new Window(window, view, isPrimaryStage)); 111 | log.debug("Currently showing " + getTotalWindows() + " window(s)"); 112 | } 113 | 114 | private EventHandler onWindowClosingEventHandler() { 115 | return event -> { 116 | Stage stage = (Stage) event.getSource(); 117 | Window window = this.windows.stream() 118 | .filter(e -> e.getStage() == stage) 119 | .findFirst() 120 | .orElseThrow(() -> new StageNotFoundException(stage.getTitle())); 121 | 122 | this.windows.remove(window); 123 | log.debug("Currently showing " + getTotalWindows() + " window(s)"); 124 | 125 | if (policy.getValue() == ViewManagerPolicy.CLOSEABLE) { 126 | if (window.isPrimaryWindow()) { 127 | log.debug("Application closing, primary window is closed"); 128 | exitApplication(); 129 | } else if (this.windows.size() == 0) { 130 | log.debug("All windows closed, exiting application"); 131 | exitApplication(); 132 | } 133 | } 134 | }; 135 | } 136 | 137 | private void exitApplication() { 138 | try { 139 | Platform.exit(); 140 | } catch (Exception ex) { 141 | log.error("Failed to stop application gracefully, " + ex.getMessage(), ex); 142 | System.exit(1); 143 | } 144 | } 145 | 146 | //endregion 147 | 148 | @Value 149 | @AllArgsConstructor 150 | private static class Window { 151 | private final Stage stage; 152 | private Scene scene; 153 | private final boolean primaryWindow; 154 | } 155 | } 156 | 157 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot JavaFX starter 2 | 3 | Spring Boot starter for easy integration with JavaFX. 4 | This library provides easy integration with JavaFX as well as additional helpers for 5 | loading & managing JavaFX views in Spring. 6 | 7 | ## Maven 8 | 9 | The library is available in the maven central repository and can be used by adding: 10 | 11 | _Spring Boot 3.X_ 12 | ```xml 13 | 14 | com.github.yoep 15 | spring-boot-starter-javafx 16 | 2.0.0 17 | 18 | ``` 19 | 20 | _Spring Boot 2.X_ 21 | ```xml 22 | 23 | com.github.yoep 24 | spring-boot-starter-javafx 25 | 1.0.12 26 | 27 | ``` 28 | 29 | ## Requirements 30 | 31 | ### Version 1.0.0+ 32 | 33 | This is the legacy version of the library which requires Spring Boot 2+ and Java 8+. 34 | 35 | - Spring Boot 2+ 36 | - Java 8+ 37 | 38 | ### Version 2.0.0+ 39 | 40 | This is the new major version of the library which requires Spring Boot 3+ and Java 17+. 41 | 42 | - Spring Boot 3+ 43 | - Java 17+ 44 | 45 | ## Usage 46 | 47 | Create a class which extends `SpringJavaFXApplication` and launch the JavaFX application from this class. 48 | 49 | _main entry example_ 50 | ```java 51 | @SpringBootApplication 52 | public class MySpringApplication extends SpringJavaFXApplication { 53 | 54 | public static void main(String[] args) { 55 | launch(MySpringApplication.class, args); 56 | } 57 | 58 | @Override 59 | public void start(Stage primaryStage) throws Exception { 60 | super.start(primaryStage); 61 | 62 | // YOUR CODE ON STARTUP HERE 63 | } 64 | } 65 | ``` 66 | 67 | Create a `ResourceBundle` bean which can be used by JavaFX within `.fxml` files. 68 | 69 | _resource bundle example_ 70 | ```java 71 | @Configuration 72 | public class LanguageConfig { 73 | @Bean 74 | public ResourceBundleMessageSource messageSource() { 75 | ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); 76 | messageSource.setBasenames("lang/example"); 77 | return messageSource; 78 | } 79 | } 80 | ``` 81 | 82 | ### Borderless stage 83 | 84 | You can create a borderless (undecorated) stage which is still resizable 85 | and draggable by using the `BorderlessStage` class. 86 | 87 | This functionality can also be achieved on the primary stage by using 88 | the `BorderlessStageWrapper` as follows: 89 | 90 | ```java 91 | 92 | @SpringBootApplication 93 | public class MySpringApplication extends SpringJavaFXApplication { 94 | @Override 95 | public void start(Stage stage) throws Exception { 96 | var myBorderlessStage = new BorderlessStageWrapper(stage); 97 | super.start(stage); 98 | 99 | // set the height of the header 100 | // this height is used within the BorderlessStage 101 | // to drag the window around 102 | myBorderlessStage.setHeader(20); 103 | // set the virtual border of the BorderlessStage 104 | // this width is used to determine when the user can grab 105 | // the non-existing border to resize the borderless stage 106 | myBorderlessStage.setResizeBorder(2); 107 | } 108 | } 109 | ``` 110 | 111 | ### View controllers 112 | 113 | Use the following stereotype to define a view controller. 114 | This is not required as the library will use any bean that is known within the 115 | `ApplicationContext` to bind them to views. 116 | 117 | ```java 118 | @ViewController 119 | public class MyViewController { 120 | } 121 | ``` 122 | 123 | ### Loading views 124 | 125 | There are 2 options when loading views, automatic controller selection through beans 126 | or manually defining the controller that needs to be used by JavaFX. 127 | 128 | - Automatically use a controller from the `ApplicationContext`. 129 | 130 | ```java 131 | private class Example { 132 | private ViewLoader viewLoader; 133 | 134 | private void loadView() { 135 | viewLoader.load("my-view-file.fxml"); 136 | } 137 | } 138 | ``` 139 | 140 | - Define a controller which needs to be used in the view. 141 | 142 | ```java 143 | private class Example { 144 | private ViewLoader viewLoader; 145 | 146 | private void loadView() { 147 | ViewController controller = new ViewController(); 148 | 149 | viewLoader.load("my-view-file.fxml", controller); 150 | } 151 | } 152 | ``` 153 | 154 | ## FAT jar packaging 155 | 156 | It is **recommended to not use** the `spring-boot-maven-plugin` if you want to package the JavaFX application into a fat jar. 157 | The reason behind this, is that the plugin will break JavaFX due to the JAR layout that is used by the plugin. 158 | 159 | For creating fat jar packages, use the `maven-shade-plugin` instead. 160 | 161 | ```xml 162 | 163 | org.apache.maven.plugins 164 | maven-shade-plugin 165 | 166 | false 167 | 168 | 169 | 170 | package 171 | 172 | shade 173 | 174 | 175 | 176 | 177 | META-INF/spring.handlers 178 | 179 | 180 | META-INF/spring.factories 181 | 182 | 183 | META-INF/spring.schemas 184 | 185 | 186 | 187 | ${start-class} 188 | 189 | 190 | 191 | 192 | 193 | 194 | ``` 195 | 196 | ## IntelliJ IDEA 197 | 198 | IntelliJ adds by default the `javafx.base` and `javafx.graphics` to the modules of Java 9+. 199 | This might be causing issues in Java 9 and above, as the `javafx.controls` and `javafx.fxml` are 200 | missing from the modules causing an `IllegalAccessException` when trying to run the application. 201 | 202 | Add the following options to the `VM Options` in the run configuration of IntelliJ to fix this issue. 203 | 204 | -p "\lib" --add-modules javafx.controls,javafx.fxml,javafx.swing 205 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.2.5 9 | 10 | 11 | com.github.yoep 12 | spring-boot-starter-javafx 13 | 2.0.1-SNAPSHOT 14 | jar 15 | 16 | Spring Boot starter JavaFX 17 | Spring Boot starter for JavaFX applications 18 | https://github.com/yoep/spring-boot-starter-javafx 19 | 20 | 21 | 22 | Apache License, Version 2.0 23 | http://www.apache.org/licenses/LICENSE-2.0.txt 24 | repo 25 | 26 | 27 | 28 | 29 | https://github.com/yoep/spring-boot-starter-javafx 30 | scm:git:git@github.com:yoep/spring-boot-starter-javafx.git 31 | 1.0.12 32 | 33 | 34 | 35 | 36 | ossrh 37 | https://oss.sonatype.org/content/repositories/snapshots 38 | 39 | 40 | ossrh 41 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 42 | 43 | 44 | 45 | 46 | 17 47 | 17 48 | 17 49 | 50 | 51 | 22.0.1 52 | 53 | 54 | 5.10.2 55 | 5.2.0 56 | 5.11.0 57 | 4.0.17 58 | 59 | 60 | 3.1.0 61 | 3.2.0 62 | 3.3.0 63 | 3.0.1 64 | 65 | 66 | 67 | 68 | 69 | org.apache.commons 70 | commons-lang3 71 | provided 72 | 73 | 74 | 75 | 76 | org.springframework.boot 77 | spring-boot 78 | provided 79 | 80 | 81 | org.springframework.boot 82 | spring-boot-autoconfigure 83 | provided 84 | 85 | 86 | org.springframework.boot 87 | spring-boot-starter-logging 88 | provided 89 | 90 | 91 | 92 | 93 | org.openjfx 94 | javafx-fxml 95 | ${javafx.version} 96 | provided 97 | 98 | 99 | 100 | 101 | org.projectlombok 102 | lombok 103 | provided 104 | 105 | 106 | 107 | 108 | org.junit.jupiter 109 | junit-jupiter-engine 110 | ${junit.jupiter.engine.version} 111 | test 112 | 113 | 114 | org.mockito 115 | mockito-inline 116 | ${mockito.inline.version} 117 | test 118 | 119 | 120 | org.mockito 121 | mockito-junit-jupiter 122 | ${mockito.junit.jupiter.version} 123 | test 124 | 125 | 126 | org.testfx 127 | testfx-junit5 128 | ${testfx.junit.version} 129 | test 130 | 131 | 132 | org.springframework.boot 133 | spring-boot-starter-test 134 | test 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | org.apache.maven.plugins 143 | maven-gpg-plugin 144 | ${maven.gpg.plugin.version} 145 | 146 | 147 | sign-artifacts 148 | verify 149 | 150 | sign 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | org.apache.maven.plugins 161 | maven-source-plugin 162 | ${maven.source.plugin.version} 163 | 164 | 165 | attach-sources 166 | verify 167 | 168 | jar-no-fork 169 | 170 | 171 | 172 | 173 | 174 | org.apache.maven.plugins 175 | maven-javadoc-plugin 176 | ${maven.javadoc.plugin.version} 177 | 178 | 179 | attach-javadocs 180 | 181 | jar 182 | 183 | 184 | 185 | 186 | 187 | org.apache.maven.plugins 188 | maven-release-plugin 189 | ${maven.release.plugin.version} 190 | 191 | @{project.version} 192 | clean deploy -Possrh 193 | 194 | 195 | 196 | 197 | 198 | 199 | maven_central 200 | Maven Central 201 | https://repo.maven.apache.org/maven2/ 202 | 203 | 204 | 205 | 206 | 207 | ossrh 208 | 209 | 210 | 211 | org.apache.maven.plugins 212 | maven-gpg-plugin 213 | 214 | 215 | 216 | 217 | 218 | 219 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/stage/BorderlessStageWrapper.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.stage; 2 | 3 | import javafx.beans.property.DoubleProperty; 4 | import javafx.beans.property.SimpleDoubleProperty; 5 | import javafx.event.EventHandler; 6 | import javafx.scene.Cursor; 7 | import javafx.scene.Scene; 8 | import javafx.scene.input.MouseEvent; 9 | import javafx.stage.Stage; 10 | import javafx.stage.StageStyle; 11 | import lombok.Getter; 12 | import org.springframework.lang.Nullable; 13 | 14 | import java.util.Objects; 15 | 16 | /** 17 | * The {@link BorderlessStageWrapper} wraps around an existing stage and converts it into a borderless stage. 18 | * The precondition of the stage is that {@link Stage#isShowing()} needs to be false so the style can be initialized for the stage. 19 | * If the stage is however already being show, a {@link InvalidStageException} will be thrown. 20 | */ 21 | public class BorderlessStageWrapper { 22 | public static final String HEADER_PROPERTY = "header"; 23 | public static final String RESIZE_BORDER_PROPERTY = "resizeBorder"; 24 | 25 | private final EventHandler mouseMovedEventHandler = this::onMouseMoved; 26 | private final EventHandler mousePressedEventHandler = this::onMousePressed; 27 | private final EventHandler mouseDraggedEventHandler = this::onMouseDragged; 28 | 29 | private final DoubleProperty header = new SimpleDoubleProperty(this, HEADER_PROPERTY, 0); 30 | private final DoubleProperty resizeBorder = new SimpleDoubleProperty(this, RESIZE_BORDER_PROPERTY, 4); 31 | @Getter 32 | private final Stage stage; 33 | 34 | private double xOffset; 35 | private double yOffset; 36 | private double xStart; 37 | private double yStart; 38 | private boolean windowDrag; 39 | private boolean windowResize; 40 | private Cursor cursor = Cursor.DEFAULT; 41 | 42 | //region Constructors 43 | 44 | public BorderlessStageWrapper(Stage stage) { 45 | Objects.requireNonNull(stage, "stage cannot be null"); 46 | this.stage = stage; 47 | init(); 48 | } 49 | 50 | //endregion 51 | 52 | //region Properties 53 | 54 | /** 55 | * Get the header height of the pane which allows the stage to be dragged around. 56 | * 57 | * @return Returns the header height of this pane. 58 | */ 59 | public double getHeader() { 60 | return header.get(); 61 | } 62 | 63 | /** 64 | * Get the header height property. 65 | * 66 | * @return Return the property of the header height. 67 | */ 68 | public DoubleProperty headerProperty() { 69 | return header; 70 | } 71 | 72 | /** 73 | * Set the header height of the pane which allows the stage to be dragged around. 74 | * 75 | * @param header The height of the header. 76 | */ 77 | public void setHeader(double header) { 78 | this.header.set(header); 79 | } 80 | 81 | /** 82 | * Get the width of the resize border. 83 | * 84 | * @return Returns the width of the resize border. 85 | */ 86 | public double getResizeBorder() { 87 | return resizeBorder.get(); 88 | } 89 | 90 | /** 91 | * Get the resize border property. 92 | * 93 | * @return Returns the resize border property. 94 | */ 95 | public DoubleProperty resizeBorderProperty() { 96 | return resizeBorder; 97 | } 98 | 99 | /** 100 | * Set the new width of the resize border. 101 | * 102 | * @param resizeBorder The new border resize width. 103 | */ 104 | public void setResizeBorder(double resizeBorder) { 105 | this.resizeBorder.set(resizeBorder); 106 | } 107 | 108 | //endregion 109 | 110 | //region Functions 111 | 112 | /** 113 | * Invoked when the scene of this {@link BorderlessStage} is being changed. 114 | * 115 | * @param oldValue The old scene of the {@link BorderlessStage}. 116 | * @param newValue The new scene of the {@link BorderlessStage}. 117 | */ 118 | protected void onSceneChanged(@Nullable Scene oldValue, @Nullable Scene newValue) { 119 | if (oldValue != null) 120 | removeSceneListeners(oldValue); 121 | 122 | if (newValue != null) 123 | addSceneListeners(newValue); 124 | } 125 | 126 | private void init() { 127 | initializeStage(); 128 | initializeListeners(); 129 | } 130 | 131 | private void initializeStage() { 132 | if (stage.isShowing()) { 133 | throw new InvalidStageException(stage, "Stage is in an invalid state, stage is already being showed"); 134 | } 135 | 136 | stage.initStyle(StageStyle.UNDECORATED); 137 | } 138 | 139 | private void initializeListeners() { 140 | stage.sceneProperty().addListener((observable, oldValue, newValue) -> onSceneChanged(oldValue, newValue)); 141 | } 142 | 143 | private void removeSceneListeners(Scene scene) { 144 | Objects.requireNonNull(scene, "scene cannot be null"); 145 | scene.removeEventHandler(MouseEvent.MOUSE_MOVED, mouseMovedEventHandler); 146 | scene.removeEventHandler(MouseEvent.MOUSE_PRESSED, mousePressedEventHandler); 147 | scene.removeEventHandler(MouseEvent.MOUSE_DRAGGED, mouseDraggedEventHandler); 148 | } 149 | 150 | private void addSceneListeners(Scene scene) { 151 | Objects.requireNonNull(scene, "scene cannot be null"); 152 | scene.addEventHandler(MouseEvent.MOUSE_MOVED, mouseMovedEventHandler); 153 | scene.addEventHandler(MouseEvent.MOUSE_PRESSED, mousePressedEventHandler); 154 | scene.addEventHandler(MouseEvent.MOUSE_DRAGGED, mouseDraggedEventHandler); 155 | } 156 | 157 | private void onMouseMoved(MouseEvent event) { 158 | // check if the stage is allowed to be resized 159 | // if not, ignore this event 160 | if (!isResizeAllowed()) 161 | return; 162 | 163 | double x = event.getSceneX(); 164 | double y = event.getSceneY(); 165 | double width = stage.getWidth(); 166 | double height = stage.getHeight(); 167 | double border = getResizeBorder(); 168 | 169 | if (x < border && y < border) { 170 | cursor = Cursor.NW_RESIZE; 171 | } else if (x > width - border && y < border) { 172 | cursor = Cursor.NE_RESIZE; 173 | } else if (x > width - border && y > height - border) { 174 | cursor = Cursor.SE_RESIZE; 175 | } else if (x < border && y > height - border) { 176 | cursor = Cursor.SW_RESIZE; 177 | } else if (y < border) { 178 | cursor = Cursor.N_RESIZE; 179 | } else if (x > width - border) { 180 | cursor = Cursor.E_RESIZE; 181 | } else if (y > height - border) { 182 | cursor = Cursor.S_RESIZE; 183 | } else if (x < border) { 184 | cursor = Cursor.W_RESIZE; 185 | } else { 186 | cursor = Cursor.DEFAULT; 187 | } 188 | 189 | windowResize = cursor != Cursor.DEFAULT; 190 | stage.getScene().setCursor(cursor); 191 | } 192 | 193 | private void onMousePressed(MouseEvent event) { 194 | // check if the stage is in fullscreen 195 | // if so, ignore this event 196 | if (stage.isFullScreen()) { 197 | return; 198 | } 199 | 200 | xOffset = stage.getX() - event.getScreenX(); 201 | yOffset = stage.getY() - event.getScreenY(); 202 | xStart = stage.getWidth() - event.getSceneX(); 203 | yStart = stage.getHeight() - event.getSceneY(); 204 | windowDrag = isValidWindowDragEvent(event); 205 | } 206 | 207 | private void onMouseDragged(MouseEvent event) { 208 | if (windowDrag) 209 | onWindowDrag(event); 210 | if (windowResize && isResizeAllowed()) 211 | onWindowResize(event); 212 | } 213 | 214 | private void onWindowDrag(MouseEvent event) { 215 | event.consume(); 216 | 217 | stage.setX(event.getScreenX() + xOffset); 218 | stage.setY(event.getScreenY() + yOffset); 219 | } 220 | 221 | private void onWindowResize(MouseEvent event) { 222 | event.consume(); 223 | 224 | if (cursor == Cursor.E_RESIZE || cursor == Cursor.NE_RESIZE || cursor == Cursor.SE_RESIZE) { 225 | double newWidth = event.getSceneX() + xStart; 226 | 227 | if (newWidth >= stage.getMinWidth()) 228 | stage.setWidth(newWidth); 229 | } 230 | 231 | if (cursor == Cursor.S_RESIZE || cursor == Cursor.SE_RESIZE || cursor == Cursor.SW_RESIZE) { 232 | double newHeight = event.getSceneY() + yStart; 233 | 234 | if (newHeight >= stage.getMinHeight()) 235 | stage.setHeight(newHeight); 236 | } 237 | 238 | if (cursor == Cursor.W_RESIZE || cursor == Cursor.SW_RESIZE || cursor == Cursor.NW_RESIZE) { 239 | double width = stage.getX() - event.getScreenX() + stage.getWidth(); 240 | 241 | if (width >= stage.getMinWidth()) { 242 | stage.setX(event.getScreenX()); 243 | stage.setWidth(width); 244 | } 245 | } 246 | 247 | if (cursor == Cursor.N_RESIZE || cursor == Cursor.NW_RESIZE || cursor == Cursor.NE_RESIZE) { 248 | double height = stage.getY() - event.getScreenY() + stage.getHeight(); 249 | 250 | if (height >= stage.getMinHeight()) { 251 | stage.setY(event.getScreenY()); 252 | stage.setHeight(height); 253 | } 254 | } 255 | } 256 | 257 | private boolean isResizeAllowed() { 258 | return stage.isResizable() && !stage.isFullScreen(); 259 | } 260 | 261 | private boolean isValidWindowDragEvent(MouseEvent event) { 262 | // check if the mouse event is a valid window drag event 263 | // the event should be within the header height and not a window resize event 264 | return !windowResize && event.getSceneY() <= getHeader(); 265 | } 266 | 267 | //endregion 268 | } 269 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/font/controls/IconRegular.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.font.controls; 2 | 3 | import javafx.event.EventHandler; 4 | import javafx.geometry.Insets; 5 | import javafx.scene.input.MouseEvent; 6 | import lombok.Builder; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Fontawesome v5 regular icon. 12 | */ 13 | public class IconRegular extends AbstractIcon { 14 | //region Unicode 15 | 16 | public static final String ADDRESS_BOOK_UNICODE = "\uf2b9"; 17 | public static final String ADDRESS_CARD_UNICODE = "\uf2bb"; 18 | public static final String ANGRY_UNICODE = "\uf556"; 19 | public static final String ARROW_ALT_CIRCLE_DOWN_UNICODE = "\uf358"; 20 | public static final String ARROW_ALT_CIRCLE_LEFT_UNICODE = "\uf359"; 21 | public static final String ARROW_ALT_CIRCLE_RIGHT_UNICODE = "\uf35a"; 22 | public static final String ARROW_ALT_CIRCLE_UP_UNICODE = "\uf35b"; 23 | public static final String BELL_UNICODE = "\uf0f3"; 24 | public static final String BELL_SLASH_UNICODE = "\uf1f6"; 25 | public static final String BOOKMARK_UNICODE = "\uf02e"; 26 | public static final String BUILDING_UNICODE = "\uf1ad"; 27 | public static final String CALENDAR_UNICODE = "\uf133"; 28 | public static final String CALENDAR_ALT_UNICODE = "\uf073"; 29 | public static final String CALENDAR_CHECK_UNICODE = "\uf274"; 30 | public static final String CALENDAR_MINUS_UNICODE = "\uf272"; 31 | public static final String CALENDAR_PLUS_UNICODE = "\uf271"; 32 | public static final String CALENDAR_TIMES_UNICODE = "\uf273"; 33 | public static final String CARET_SQUARE_DOWN_UNICODE = "\uf150"; 34 | public static final String CARET_SQUARE_LEFT_UNICODE = "\uf191"; 35 | public static final String CARET_SQUARE_RIGHT_UNICODE = "\uf152"; 36 | public static final String CARET_SQUARE_UP_UNICODE = "\uf151"; 37 | public static final String CHART_BAR_UNICODE = "\uf080"; 38 | public static final String CHECK_CIRCLE_UNICODE = "\uf058"; 39 | public static final String CHECK_SQUARE_UNICODE = "\uf14a"; 40 | public static final String CIRCLE_UNICODE = "\uf111"; 41 | public static final String CLIPBOARD_UNICODE = "\uf328"; 42 | public static final String CLOCK_UNICODE = "\uf017"; 43 | public static final String CLONE_UNICODE = "\uf24d"; 44 | public static final String CLOSED_CAPTIONING_UNICODE = "\uf20a"; 45 | public static final String COMMENT_UNICODE = "\uf075"; 46 | public static final String COMMENT_ALT_UNICODE = "\uf27a"; 47 | public static final String COMMENT_DOTS_UNICODE = "\uf4ad"; 48 | public static final String COMMENTS_UNICODE = "\uf086"; 49 | public static final String COMPASS_UNICODE = "\uf14e"; 50 | public static final String COPY_UNICODE = "\uf0c5"; 51 | public static final String COPYRIGHT_UNICODE = "\uf1f9"; 52 | public static final String CREDIT_CARD_UNICODE = "\uf09d"; 53 | public static final String DIZZY_UNICODE = "\uf567"; 54 | public static final String DOT_CIRCLE_UNICODE = "\uf192"; 55 | public static final String EDIT_UNICODE = "\uf044"; 56 | public static final String ENVELOPE_UNICODE = "\uf0e0"; 57 | public static final String ENVELOPE_OPEN_UNICODE = "\uf2b6"; 58 | public static final String EYE_UNICODE = "\uf06e"; 59 | public static final String EYE_SLASH_UNICODE = "\uf070"; 60 | public static final String FILE_UNICODE = "\uf15b"; 61 | public static final String FILE_ALT_UNICODE = "\uf15c"; 62 | public static final String FILE_ARCHIVE_UNICODE = "\uf1c6"; 63 | public static final String FILE_AUDIO_UNICODE = "\uf1c7"; 64 | public static final String FILE_CODE_UNICODE = "\uf1c9"; 65 | public static final String FILE_EXCEL_UNICODE = "\uf1c3"; 66 | public static final String FILE_IMAGE_UNICODE = "\uf1c5"; 67 | public static final String FILE_PDF_UNICODE = "\uf1c1"; 68 | public static final String FILE_POWERPOINT_UNICODE = "\uf1c4"; 69 | public static final String FILE_VIDEO_UNICODE = "\uf1c8"; 70 | public static final String FILE_WORD_UNICODE = "\uf1c2"; 71 | public static final String FLAG_UNICODE = "\uf024"; 72 | public static final String FLUSHED_UNICODE = "\uf579"; 73 | public static final String FOLDER_UNICODE = "\uf07b"; 74 | public static final String FOLDER_OPEN_UNICODE = "\uf07c"; 75 | public static final String FROWN_UNICODE = "\uf119"; 76 | public static final String FROWN_OPEN_UNICODE = "\uf57a"; 77 | public static final String FUTBOL_UNICODE = "\uf1e3"; 78 | public static final String GEM_UNICODE = "\uf3a5"; 79 | public static final String GRIMACE_UNICODE = "\uf57f"; 80 | public static final String GRIN_UNICODE = "\uf580"; 81 | public static final String GRIN_ALT_UNICODE = "\uf581"; 82 | public static final String GRIN_BEAM_UNICODE = "\uf582"; 83 | public static final String GRIN_BEAM_SWEAT_UNICODE = "\uf583"; 84 | public static final String GRIN_HEARTS_UNICODE = "\uf584"; 85 | public static final String GRIN_SQUINT_UNICODE = "\uf585"; 86 | public static final String GRIN_SQUINT_TEARS_UNICODE = "\uf586"; 87 | public static final String GRIN_STARS_UNICODE = "\uf587"; 88 | public static final String GRIN_TEARS_UNICODE = "\uf588"; 89 | public static final String GRIN_TONGUE_UNICODE = "\uf589"; 90 | public static final String GRIN_TONGUE_SQUINT_UNICODE = "\uf58a"; 91 | public static final String GRIN_TONGUE_WINK_UNICODE = "\uf58b"; 92 | public static final String GRIN_WINK_UNICODE = "\uf58c"; 93 | public static final String HAND_LIZARD_UNICODE = "\uf258"; 94 | public static final String HAND_PAPER_UNICODE = "\uf256"; 95 | public static final String HAND_PEACE_UNICODE = "\uf25b"; 96 | public static final String HAND_POINT_DOWN_UNICODE = "\uf0a7"; 97 | public static final String HAND_POINT_LEFT_UNICODE = "\uf0a5"; 98 | public static final String HAND_POINT_RIGHT_UNICODE = "\uf0a4"; 99 | public static final String HAND_POINT_UP_UNICODE = "\uf0a6"; 100 | public static final String HAND_POINTER_UNICODE = "\uf25a"; 101 | public static final String HAND_ROCK_UNICODE = "\uf255"; 102 | public static final String HAND_SCISSORS_UNICODE = "\uf257"; 103 | public static final String HAND_SPOCK_UNICODE = "\uf259"; 104 | public static final String HANDSHAKE_UNICODE = "\uf2b5"; 105 | public static final String HDD_UNICODE = "\uf0a0"; 106 | public static final String HEART_UNICODE = "\uf004"; 107 | public static final String HOSPITAL_UNICODE = "\uf0f8"; 108 | public static final String HOURGLASS_UNICODE = "\uf254"; 109 | public static final String ID_BADGE_UNICODE = "\uf2c1"; 110 | public static final String ID_CARD_UNICODE = "\uf2c2"; 111 | public static final String IMAGE_UNICODE = "\uf03e"; 112 | public static final String IMAGES_UNICODE = "\uf302"; 113 | public static final String KEYBOARD_UNICODE = "\uf11c"; 114 | public static final String KISS_UNICODE = "\uf596"; 115 | public static final String KISS_BEAM_UNICODE = "\uf597"; 116 | public static final String KISS_WINK_HEART_UNICODE = "\uf598"; 117 | public static final String LAUGH_UNICODE = "\uf599"; 118 | public static final String LAUGH_BEAM_UNICODE = "\uf59a"; 119 | public static final String LAUGH_SQUINT_UNICODE = "\uf59b"; 120 | public static final String LAUGH_WINK_UNICODE = "\uf59c"; 121 | public static final String LEMON_UNICODE = "\uf094"; 122 | public static final String LIFE_RING_UNICODE = "\uf1cd"; 123 | public static final String LIGHTBULB_UNICODE = "\uf0eb"; 124 | public static final String LIST_ALT_UNICODE = "\uf022"; 125 | public static final String MAP_UNICODE = "\uf279"; 126 | public static final String MEH_UNICODE = "\uf11a"; 127 | public static final String MEH_BLANK_UNICODE = "\uf5a4"; 128 | public static final String MEH_ROLLING_EYES_UNICODE = "\uf5a5"; 129 | public static final String MINUS_SQUARE_UNICODE = "\uf146"; 130 | public static final String MONEY_BILL_ALT_UNICODE = "\uf3d1"; 131 | public static final String MOON_UNICODE = "\uf186"; 132 | public static final String NEWSPAPER_UNICODE = "\uf1ea"; 133 | public static final String OBJECT_GROUP_UNICODE = "\uf247"; 134 | public static final String OBJECT_UNGROUP_UNICODE = "\uf248"; 135 | public static final String PAPER_PLANE_UNICODE = "\uf1d8"; 136 | public static final String PAUSE_CIRCLE_UNICODE = "\uf28b"; 137 | public static final String PLAY_CIRCLE_UNICODE = "\uf144"; 138 | public static final String PLUS_SQUARE_UNICODE = "\uf0fe"; 139 | public static final String QUESTION_CIRCLE_UNICODE = "\uf059"; 140 | public static final String REGISTERED_UNICODE = "\uf25d"; 141 | public static final String SAD_CRY_UNICODE = "\uf5b3"; 142 | public static final String SAD_TEAR_UNICODE = "\uf5b4"; 143 | public static final String SAVE_UNICODE = "\uf0c7"; 144 | public static final String SHARE_SQUARE_UNICODE = "\uf14d"; 145 | public static final String SMILE_UNICODE = "\uf118"; 146 | public static final String SMILE_BEAM_UNICODE = "\uf5b8"; 147 | public static final String SMILE_WINK_UNICODE = "\uf4da"; 148 | public static final String SNOWFLAKE_UNICODE = "\uf2dc"; 149 | public static final String SQUARE_UNICODE = "\uf0c8"; 150 | public static final String STAR_UNICODE = "\uf005"; 151 | public static final String STAR_HALF_UNICODE = "\uf089"; 152 | public static final String STICKY_NOTE_UNICODE = "\uf249"; 153 | public static final String STOP_CIRCLE_UNICODE = "\uf28d"; 154 | public static final String SUN_UNICODE = "\uf185"; 155 | public static final String SURPRISE_UNICODE = "\uf5c2"; 156 | public static final String THUMBS_DOWN_UNICODE = "\uf165"; 157 | public static final String THUMBS_UP_UNICODE = "\uf164"; 158 | public static final String TIMES_CIRCLE_UNICODE = "\uf057"; 159 | public static final String TIRED_UNICODE = "\uf5c8"; 160 | public static final String TRASH_ALT_UNICODE = "\uf2ed"; 161 | public static final String USER_UNICODE = "\uf007"; 162 | public static final String USER_CIRCLE_UNICODE = "\uf2bd"; 163 | public static final String WINDOW_CLOSE_UNICODE = "\uf410"; 164 | public static final String WINDOW_MAXIMIZE_UNICODE = "\uf2d0"; 165 | public static final String WINDOW_MINIMIZE_UNICODE = "\uf2d1"; 166 | public static final String WINDOW_RESTORE_UNICODE = "\uf2d2"; 167 | 168 | //endregion 169 | 170 | private static final String FILENAME = "fontawesome-regular.ttf"; 171 | 172 | public IconRegular() { 173 | super(FILENAME); 174 | } 175 | 176 | public IconRegular(String unicode) { 177 | super(FILENAME, unicode); 178 | } 179 | 180 | @Builder 181 | public IconRegular(String unicode, Insets padding, Boolean visible, EventHandler onMouseClicked, List styleClasses) { 182 | super(FILENAME); 183 | setProperty(unicode, this::setText); 184 | setProperty(padding, this::setPadding); 185 | setProperty(visible, this::setVisible); 186 | setProperty(onMouseClicked, this::setOnMouseClicked); 187 | setProperty(styleClasses, e -> this.getStyleClass().addAll(e)); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/view/ViewLoaderImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.view; 2 | 3 | import com.github.spring.boot.javafx.text.LocaleText; 4 | import com.github.spring.boot.javafx.ui.scale.ScaleAware; 5 | import com.github.spring.boot.javafx.ui.size.SizeAware; 6 | import com.github.spring.boot.javafx.ui.stage.StageAware; 7 | import javafx.application.Platform; 8 | import javafx.fxml.FXMLLoader; 9 | import javafx.scene.Group; 10 | import javafx.scene.Node; 11 | import javafx.scene.Scene; 12 | import javafx.scene.image.Image; 13 | import javafx.scene.layout.Pane; 14 | import javafx.scene.layout.Region; 15 | import javafx.stage.Modality; 16 | import javafx.stage.Screen; 17 | import javafx.stage.Stage; 18 | import lombok.extern.slf4j.Slf4j; 19 | import org.apache.commons.lang3.StringUtils; 20 | import org.springframework.context.ApplicationContext; 21 | import org.springframework.core.io.ClassPathResource; 22 | import org.springframework.util.Assert; 23 | 24 | import java.io.File; 25 | import java.io.IOException; 26 | import java.util.Objects; 27 | import java.util.Optional; 28 | 29 | @Slf4j 30 | public class ViewLoaderImpl implements ViewLoader { 31 | protected final ApplicationContext applicationContext; 32 | protected final ViewManager viewManager; 33 | protected final LocaleText localeText; 34 | 35 | protected float scale = 1f; 36 | 37 | //region Constructors 38 | 39 | /** 40 | * Initialize a new instance of {@link ViewLoaderImpl}. 41 | * 42 | * @param applicationContext Set the current application context. 43 | * @param viewManager Set the view manager to store the views in. 44 | * @param localeText Set the UI text manager. 45 | */ 46 | public ViewLoaderImpl(ApplicationContext applicationContext, ViewManager viewManager, LocaleText localeText) { 47 | Objects.requireNonNull(applicationContext, "applicationContext cannot be null"); 48 | Objects.requireNonNull(viewManager, "viewManager cannot be null"); 49 | Objects.requireNonNull(localeText, "localeText cannot be null"); 50 | this.applicationContext = applicationContext; 51 | this.viewManager = viewManager; 52 | this.localeText = localeText; 53 | } 54 | 55 | //endregion 56 | 57 | //region ViewLoader 58 | 59 | @Override 60 | public void setScale(float scale) { 61 | if (this.scale == scale) 62 | return; 63 | 64 | this.scale = scale; 65 | onScaleChanged(scale); 66 | } 67 | 68 | @Override 69 | public void show(String view, ViewProperties properties) { 70 | Assert.hasText(view, "view cannot be empty"); 71 | Objects.requireNonNull(properties, "properties cannot be null"); 72 | 73 | Stage stage = viewManager.getPrimaryStage() 74 | .orElseThrow(StageNotFoundException::new); 75 | showScene(stage, view, properties); 76 | } 77 | 78 | @Override 79 | public void show(Stage window, String view, ViewProperties properties) { 80 | Objects.requireNonNull(window, "window cannot be empty"); 81 | Assert.hasText(view, "view cannot be empty"); 82 | Objects.requireNonNull(properties, "properties cannot be null"); 83 | showScene(window, view, properties); 84 | } 85 | 86 | @Override 87 | public void showWindow(String view, ViewProperties properties) { 88 | Assert.hasText(view, "view cannot be empty"); 89 | Objects.requireNonNull(properties, "properties cannot be null"); 90 | Platform.runLater(() -> showScene(new Stage(), view, properties)); 91 | } 92 | 93 | @Override 94 | public void showWindow(Pane pane, Object controller, ViewProperties properties) { 95 | Objects.requireNonNull(pane, "pane cannot be null"); 96 | Objects.requireNonNull(controller, "controller cannot be null"); 97 | Objects.requireNonNull(properties, "properties cannot be null"); 98 | 99 | Platform.runLater(() -> showScene(new Stage(), new SceneInfo(new Scene(pane), pane, controller), properties)); 100 | } 101 | 102 | @Override 103 | public T load(String view) { 104 | Assert.hasText(view, "view cannot be empty"); 105 | var loader = loadResource(view); 106 | 107 | loader.setControllerFactory(applicationContext::getBean); 108 | return loadComponent(loader); 109 | } 110 | 111 | @Override 112 | public T load(String view, Object controller) { 113 | Assert.hasText(view, "view cannot be empty"); 114 | Objects.requireNonNull(controller, "controller cannot be null"); 115 | var loader = loadResource(view); 116 | 117 | loader.setController(controller); 118 | return loadComponent(loader); 119 | } 120 | 121 | //endregion 122 | 123 | //region Functions 124 | 125 | /** 126 | * Load the view component from the {@link FXMLLoader}. 127 | * This method attaches the available resources of the application to the loader before loading the actual component. 128 | * 129 | * @param loader The loader to load the component from. 130 | * @param The root node of the view. 131 | * @return Returns the loaded view component on success, else null when the loading failed. 132 | */ 133 | protected T loadComponent(FXMLLoader loader) { 134 | Objects.requireNonNull(loader, "loader cannot be null"); 135 | loader.setResources(localeText.getResourceBundle()); 136 | 137 | try { 138 | return loader.load(); 139 | } catch (IOException e) { 140 | log.error(e.getMessage(), e); 141 | return null; 142 | } 143 | } 144 | 145 | /** 146 | * Prepare the resource view to be loaded. 147 | * This method will load the resource file from the classpath and prepare the {@link FXMLLoader}. 148 | * 149 | * @param view The view file that needs to be loaded. 150 | * @return Returns the loader for the given view file. 151 | */ 152 | protected FXMLLoader loadResource(String view) { 153 | Objects.requireNonNull(view, "view cannot be null"); 154 | var componentResource = new ClassPathResource(ViewLoader.VIEW_DIRECTORY + File.separator + view); 155 | 156 | if (!componentResource.exists()) 157 | throw new ViewNotFoundException(view); 158 | 159 | try { 160 | return new FXMLLoader(componentResource.getURL()); 161 | } catch (IOException ex) { 162 | throw new ViewException(ex.getMessage(), ex); 163 | } 164 | } 165 | 166 | private SceneInfo loadView(String view, ViewProperties properties) throws ViewNotFoundException { 167 | Assert.hasText(view, "view cannot be empty"); 168 | var fxmlResourceFile = new ClassPathResource(ViewLoader.VIEW_DIRECTORY + File.separator + view); 169 | 170 | if (fxmlResourceFile.exists()) { 171 | FXMLLoader loader; 172 | 173 | try { 174 | loader = new FXMLLoader(fxmlResourceFile.getURL(), localeText.getResourceBundle()); 175 | } catch (IOException ex) { 176 | throw new ViewException(view, ex.getMessage(), ex); 177 | } 178 | 179 | loader.setControllerFactory(applicationContext::getBean); 180 | 181 | try { 182 | Region root = loader.load(); 183 | Object controller = loader.getController(); 184 | Scene scene; 185 | 186 | if (controller instanceof ScaleAware) { 187 | scene = new Scene(new Group(root)); 188 | } else { 189 | scene = new Scene(root); 190 | } 191 | 192 | // check if a background fill color has been defined 193 | // if so, set the fill color for the scene 194 | if (properties != null && properties.getBackground() != null) 195 | scene.setFill(properties.getBackground()); 196 | 197 | return new SceneInfo(scene, root, controller); 198 | } catch (IllegalStateException ex) { 199 | throw new ViewNotFoundException(view, ex); 200 | } catch (IOException ex) { 201 | log.error("View '" + view + "' is invalid", ex); 202 | throw new ViewException(view, ex.getMessage(), ex); 203 | } 204 | } 205 | 206 | return null; 207 | } 208 | 209 | private void showScene(Stage window, String view, ViewProperties properties) { 210 | var sceneInfo = loadView(view, properties); 211 | 212 | if (sceneInfo != null) { 213 | showScene(window, sceneInfo, properties); 214 | } else { 215 | log.warn("Unable to show view " + view + " in window " + window); 216 | } 217 | } 218 | 219 | private void showScene(Stage window, SceneInfo sceneInfo, ViewProperties properties) { 220 | var scene = sceneInfo.scene(); 221 | var controller = sceneInfo.controller(); 222 | 223 | window.setScene(scene); 224 | viewManager.addWindowView(window, scene); 225 | 226 | if (controller instanceof ScaleAware) { 227 | initWindowScale(sceneInfo); 228 | } 229 | if (controller instanceof SizeAware) { 230 | initWindowSize(scene, (SizeAware) controller); 231 | } 232 | if (controller instanceof StageAware) { 233 | initWindowEvents(scene, (StageAware) controller); 234 | } 235 | 236 | setWindowViewProperties(window, properties); 237 | 238 | if (properties.isDialog()) { 239 | window.initModality(Modality.APPLICATION_MODAL); 240 | window.showAndWait(); 241 | } else { 242 | window.show(); 243 | } 244 | } 245 | 246 | private void setWindowViewProperties(Stage window, ViewProperties properties) { 247 | window.setTitle(properties.getTitle()); 248 | // prevent JavaFX from making unnecessary changes to the window 249 | if (!Objects.equals(window.isMaximized(), properties.isMaximized())) 250 | window.setMaximized(properties.isMaximized()); 251 | if (!Objects.equals(window.isResizable(), properties.isResizable())) 252 | window.setResizable(properties.isResizable()); 253 | 254 | Optional.ofNullable(properties.getIcon()) 255 | .filter(StringUtils::isNotBlank) 256 | .ifPresent(icon -> window.getIcons().add(loadWindowIcon(icon))); 257 | 258 | if (properties.isCenterOnScreen()) { 259 | centerOnScreen(window); 260 | } 261 | } 262 | 263 | /** 264 | * Center the given window on the screen. 265 | * 266 | * @param window Set the window to center. 267 | */ 268 | private void centerOnScreen(Stage window) { 269 | var screenBounds = Screen.getPrimary().getVisualBounds(); 270 | 271 | window.setX((screenBounds.getWidth() - window.getWidth()) / 2); 272 | window.setY((screenBounds.getHeight() - window.getHeight()) / 2); 273 | } 274 | 275 | private Image loadWindowIcon(String iconName) { 276 | try { 277 | var iconResource = new ClassPathResource(ViewLoader.IMAGE_DIRECTORY + File.separator + iconName); 278 | 279 | if (iconResource.exists()) { 280 | return new Image(iconResource.getInputStream()); 281 | } else { 282 | throw new ViewException("Icon '" + iconName + "' not found"); 283 | } 284 | } catch (IOException ex) { 285 | throw new ViewException(ex.getMessage(), ex); 286 | } 287 | } 288 | 289 | private void initWindowScale(SceneInfo sceneInfo) { 290 | var controller = (ScaleAware) sceneInfo.controller(); 291 | 292 | controller.scale(sceneInfo.scene(), sceneInfo.root(), scale); 293 | } 294 | 295 | private void initWindowSize(Scene scene, SizeAware controller) { 296 | var window = (Stage) scene.getWindow(); 297 | controller.setInitialSize(window); 298 | window.widthProperty().addListener((observable, oldValue, newValue) -> { 299 | if (window.isShowing()) { 300 | controller.onSizeChange(newValue, window.getHeight(), window.isMaximized()); 301 | } 302 | }); 303 | window.heightProperty().addListener((observable, oldValue, newValue) -> { 304 | if (window.isShowing()) { 305 | controller.onSizeChange(window.getWidth(), newValue, window.isMaximized()); 306 | } 307 | }); 308 | window.maximizedProperty().addListener(((observable, oldValue, newValue) -> { 309 | if (window.isShowing()) { 310 | controller.onSizeChange(window.getWidth(), window.getHeight(), newValue); 311 | } 312 | })); 313 | } 314 | 315 | private void initWindowEvents(Scene scene, StageAware controller) { 316 | final var window = (Stage) scene.getWindow(); 317 | 318 | window.setOnShown(event -> controller.onShown(window)); 319 | window.setOnCloseRequest(event -> controller.onClosed(window)); 320 | } 321 | 322 | private void onScaleChanged(final float newValue) { 323 | for (var scaleAware : applicationContext.getBeansOfType(ScaleAware.class).values()) { 324 | try { 325 | scaleAware.onScaleChanged(newValue); 326 | } catch (Exception ex) { 327 | log.error("Failed to invoke scale awareness with error " + ex.getMessage(), ex); 328 | } 329 | } 330 | } 331 | 332 | //endregion 333 | 334 | /** 335 | * Contains the general information about a certain scene which might actively be rendered within JavaFX. 336 | * 337 | * @param scene The scene to be rendered. 338 | * @param root The root region of the scene. 339 | * @param controller The controller of the scene. 340 | */ 341 | record SceneInfo(Scene scene, Region root, Object controller) { 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /src/main/java/com/github/spring/boot/javafx/font/controls/Icon.java: -------------------------------------------------------------------------------- 1 | package com.github.spring.boot.javafx.font.controls; 2 | 3 | import javafx.event.EventHandler; 4 | import javafx.geometry.Insets; 5 | import javafx.scene.input.MouseEvent; 6 | import lombok.Builder; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * JavaFX node containing a Fontawesome v4.7 icon. 12 | */ 13 | public class Icon extends AbstractIcon { 14 | //region Unicodes 15 | 16 | public static final String ADDRESS_BOOK_O_UNICODE = "\uf2ba"; 17 | public static final String ADDRESS_BOOK_UNICODE = "\uf2b9"; 18 | public static final String ADDRESS_CARD_O_UNICODE = "\uf2bc"; 19 | public static final String ADDRESS_CARD_UNICODE = "\uf2bb"; 20 | public static final String ADJUST_UNICODE = "\uf042"; 21 | public static final String ADN_UNICODE = "\uf170"; 22 | public static final String ALIGN_CENTER_UNICODE = "\uf037"; 23 | public static final String ALIGN_JUSTIFY_UNICODE = "\uf039"; 24 | public static final String ALIGN_LEFT_UNICODE = "\uf036"; 25 | public static final String ALIGN_RIGHT_UNICODE = "\uf038"; 26 | public static final String AMAZON_UNICODE = "\uf270"; 27 | public static final String AMBULANCE_UNICODE = "\uf0f9"; 28 | public static final String AMERICAN_SIGN_LANGUAGE_INTERPRETING_UNICODE = "\uf2a3"; 29 | public static final String ANCHOR_UNICODE = "\uf13d"; 30 | public static final String ANDROID_UNICODE = "\uf17b"; 31 | public static final String ANGELLIST_UNICODE = "\uf209"; 32 | public static final String ANGLE_DOUBLE_DOWN_UNICODE = "\uf103"; 33 | public static final String ANGLE_DOUBLE_LEFT_UNICODE = "\uf100"; 34 | public static final String ANGLE_DOUBLE_RIGHT_UNICODE = "\uf101"; 35 | public static final String ANGLE_DOUBLE_UP_UNICODE = "\uf102"; 36 | public static final String ANGLE_DOWN_UNICODE = "\uf107"; 37 | public static final String ANGLE_LEFT_UNICODE = "\uf104"; 38 | public static final String ANGLE_RIGHT_UNICODE = "\uf105"; 39 | public static final String ANGLE_UP_UNICODE = "\uf106"; 40 | public static final String APPLE_UNICODE = "\uf179"; 41 | public static final String ARCHIVE_UNICODE = "\uf187"; 42 | public static final String AREA_CHART_UNICODE = "\uf1fe"; 43 | public static final String ARROWS_ALT_UNICODE = "\uf0b2"; 44 | public static final String ARROWS_H_UNICODE = "\uf07e"; 45 | public static final String ARROWS_UNICODE = "\uf047"; 46 | public static final String ARROWS_V_UNICODE = "\uf07d"; 47 | public static final String ARROW_CIRCLE_DOWN_UNICODE = "\uf0ab"; 48 | public static final String ARROW_CIRCLE_LEFT_UNICODE = "\uf0a8"; 49 | public static final String ARROW_CIRCLE_O_DOWN_UNICODE = "\uf01a"; 50 | public static final String ARROW_CIRCLE_O_LEFT_UNICODE = "\uf190"; 51 | public static final String ARROW_CIRCLE_O_RIGHT_UNICODE = "\uf18e"; 52 | public static final String ARROW_CIRCLE_O_UP_UNICODE = "\uf01b"; 53 | public static final String ARROW_CIRCLE_RIGHT_UNICODE = "\uf0a9"; 54 | public static final String ARROW_CIRCLE_UP_UNICODE = "\uf0aa"; 55 | public static final String ARROW_DOWN_UNICODE = "\uf063"; 56 | public static final String ARROW_LEFT_UNICODE = "\uf060"; 57 | public static final String ARROW_RIGHT_UNICODE = "\uf061"; 58 | public static final String ARROW_UP_UNICODE = "\uf062"; 59 | public static final String ASL_INTERPRETING_UNICODE = "\uf2a3"; 60 | public static final String ASSISTIVE_LISTENING_SYSTEMS_UNICODE = "\uf2a2"; 61 | public static final String ASTERISK_UNICODE = "\uf069"; 62 | public static final String AT_UNICODE = "\uf1fa"; 63 | public static final String AUDIO_DESCRIPTION_UNICODE = "\uf29e"; 64 | public static final String AUTOMOBILE_UNICODE = "\uf1b9"; 65 | public static final String BACKWARD_UNICODE = "\uf04a"; 66 | public static final String BALANCE_SCALE_UNICODE = "\uf24e"; 67 | public static final String BANDCAMP_UNICODE = "\uf2d5"; 68 | public static final String BANK_UNICODE = "\uf19c"; 69 | public static final String BAN_UNICODE = "\uf05e"; 70 | public static final String BARCODE_UNICODE = "\uf02a"; 71 | public static final String BARS_UNICODE = "\uf0c9"; 72 | public static final String BAR_CHART_O_UNICODE = "\uf080"; 73 | public static final String BAR_CHART_UNICODE = "\uf080"; 74 | public static final String BATHTUB_UNICODE = "\uf2cd"; 75 | public static final String BATH_UNICODE = "\uf2cd"; 76 | public static final String BATTERY_0_UNICODE = "\uf244"; 77 | public static final String BATTERY_1_UNICODE = "\uf243"; 78 | public static final String BATTERY_2_UNICODE = "\uf242"; 79 | public static final String BATTERY_3_UNICODE = "\uf241"; 80 | public static final String BATTERY_4_UNICODE = "\uf240"; 81 | public static final String BATTERY_EMPTY_UNICODE = "\uf244"; 82 | public static final String BATTERY_FULL_UNICODE = "\uf240"; 83 | public static final String BATTERY_HALF_UNICODE = "\uf242"; 84 | public static final String BATTERY_QUARTER_UNICODE = "\uf243"; 85 | public static final String BATTERY_THREE_QUARTERS_UNICODE = "\uf241"; 86 | public static final String BATTERY_UNICODE = "\uf240"; 87 | public static final String BED_UNICODE = "\uf236"; 88 | public static final String BEER_UNICODE = "\uf0fc"; 89 | public static final String BEHANCE_SQUARE_UNICODE = "\uf1b5"; 90 | public static final String BEHANCE_UNICODE = "\uf1b4"; 91 | public static final String BELL_O_UNICODE = "\uf0a2"; 92 | public static final String BELL_SLASH_O_UNICODE = "\uf1f7"; 93 | public static final String BELL_SLASH_UNICODE = "\uf1f6"; 94 | public static final String BELL_UNICODE = "\uf0f3"; 95 | public static final String BICYCLE_UNICODE = "\uf206"; 96 | public static final String BINOCULARS_UNICODE = "\uf1e5"; 97 | public static final String BIRTHDAY_CAKE_UNICODE = "\uf1fd"; 98 | public static final String BITBUCKET_SQUARE_UNICODE = "\uf172"; 99 | public static final String BITBUCKET_UNICODE = "\uf171"; 100 | public static final String BITCOIN_UNICODE = "\uf15a"; 101 | public static final String BLACK_TIE_UNICODE = "\uf27e"; 102 | public static final String BLIND_UNICODE = "\uf29d"; 103 | public static final String BLUETOOTH_B_UNICODE = "\uf294"; 104 | public static final String BLUETOOTH_UNICODE = "\uf293"; 105 | public static final String BOLD_UNICODE = "\uf032"; 106 | public static final String BOLT_UNICODE = "\uf0e7"; 107 | public static final String BOMB_UNICODE = "\uf1e2"; 108 | public static final String BOOKMARK_O_UNICODE = "\uf097"; 109 | public static final String BOOKMARK_UNICODE = "\uf02e"; 110 | public static final String BOOK_UNICODE = "\uf02d"; 111 | public static final String BRAILLE_UNICODE = "\uf2a1"; 112 | public static final String BRIEFCASE_UNICODE = "\uf0b1"; 113 | public static final String BTC_UNICODE = "\uf15a"; 114 | public static final String BUG_UNICODE = "\uf188"; 115 | public static final String BUILDING_O_UNICODE = "\uf0f7"; 116 | public static final String BUILDING_UNICODE = "\uf1ad"; 117 | public static final String BULLHORN_UNICODE = "\uf0a1"; 118 | public static final String BULLSEYE_UNICODE = "\uf140"; 119 | public static final String BUS_UNICODE = "\uf207"; 120 | public static final String BUYSELLADS_UNICODE = "\uf20d"; 121 | public static final String CAB_UNICODE = "\uf1ba"; 122 | public static final String CALCULATOR_UNICODE = "\uf1ec"; 123 | public static final String CALENDAR_CHECK_O_UNICODE = "\uf274"; 124 | public static final String CALENDAR_MINUS_O_UNICODE = "\uf272"; 125 | public static final String CALENDAR_O_UNICODE = "\uf133"; 126 | public static final String CALENDAR_PLUS_O_UNICODE = "\uf271"; 127 | public static final String CALENDAR_TIMES_O_UNICODE = "\uf273"; 128 | public static final String CALENDAR_UNICODE = "\uf073"; 129 | public static final String CAMERA_RETRO_UNICODE = "\uf083"; 130 | public static final String CAMERA_UNICODE = "\uf030"; 131 | public static final String CARET_DOWN_UNICODE = "\uf0d7"; 132 | public static final String CARET_LEFT_UNICODE = "\uf0d9"; 133 | public static final String CARET_RIGHT_UNICODE = "\uf0da"; 134 | public static final String CARET_SQUARE_O_DOWN_UNICODE = "\uf150"; 135 | public static final String CARET_SQUARE_O_LEFT_UNICODE = "\uf191"; 136 | public static final String CARET_SQUARE_O_RIGHT_UNICODE = "\uf152"; 137 | public static final String CARET_SQUARE_O_UP_UNICODE = "\uf151"; 138 | public static final String CARET_UP_UNICODE = "\uf0d8"; 139 | public static final String CART_ARROW_DOWN_UNICODE = "\uf218"; 140 | public static final String CART_PLUS_UNICODE = "\uf217"; 141 | public static final String CAR_UNICODE = "\uf1b9"; 142 | public static final String CC_AMEX_UNICODE = "\uf1f3"; 143 | public static final String CC_DINERS_CLUB_UNICODE = "\uf24c"; 144 | public static final String CC_DISCOVER_UNICODE = "\uf1f2"; 145 | public static final String CC_JCB_UNICODE = "\uf24b"; 146 | public static final String CC_MASTERCARD_UNICODE = "\uf1f1"; 147 | public static final String CC_PAYPAL_UNICODE = "\uf1f4"; 148 | public static final String CC_STRIPE_UNICODE = "\uf1f5"; 149 | public static final String CC_UNICODE = "\uf20a"; 150 | public static final String CC_VISA_UNICODE = "\uf1f0"; 151 | public static final String CERTIFICATE_UNICODE = "\uf0a3"; 152 | public static final String CHAIN_BROKEN_UNICODE = "\uf127"; 153 | public static final String CHAIN_UNICODE = "\uf0c1"; 154 | public static final String CHECK_CIRCLE_O_UNICODE = "\uf05d"; 155 | public static final String CHECK_CIRCLE_UNICODE = "\uf058"; 156 | public static final String CHECK_SQUARE_O_UNICODE = "\uf046"; 157 | public static final String CHECK_SQUARE_UNICODE = "\uf14a"; 158 | public static final String CHECK_UNICODE = "\uf00c"; 159 | public static final String CHEVRON_CIRCLE_DOWN_UNICODE = "\uf13a"; 160 | public static final String CHEVRON_CIRCLE_LEFT_UNICODE = "\uf137"; 161 | public static final String CHEVRON_CIRCLE_RIGHT_UNICODE = "\uf138"; 162 | public static final String CHEVRON_CIRCLE_UP_UNICODE = "\uf139"; 163 | public static final String CHEVRON_DOWN_UNICODE = "\uf078"; 164 | public static final String CHEVRON_LEFT_UNICODE = "\uf053"; 165 | public static final String CHEVRON_RIGHT_UNICODE = "\uf054"; 166 | public static final String CHEVRON_UP_UNICODE = "\uf077"; 167 | public static final String CHILD_UNICODE = "\uf1ae"; 168 | public static final String CHROME_UNICODE = "\uf268"; 169 | public static final String CIRCLE_O_NOTCH_UNICODE = "\uf1ce"; 170 | public static final String CIRCLE_O_UNICODE = "\uf10c"; 171 | public static final String CIRCLE_THIN_UNICODE = "\uf1db"; 172 | public static final String CIRCLE_UNICODE = "\uf111"; 173 | public static final String CLIPBOARD_UNICODE = "\uf0ea"; 174 | public static final String CLOCK_O_UNICODE = "\uf017"; 175 | public static final String CLONE_UNICODE = "\uf24d"; 176 | public static final String CLOSE_UNICODE = "\uf00d"; 177 | public static final String CLOUD_DOWNLOAD_UNICODE = "\uf0ed"; 178 | public static final String CLOUD_UNICODE = "\uf0c2"; 179 | public static final String CLOUD_UPLOAD_UNICODE = "\uf0ee"; 180 | public static final String CNY_UNICODE = "\uf157"; 181 | public static final String CODEPEN_UNICODE = "\uf1cb"; 182 | public static final String CODE_FORK_UNICODE = "\uf126"; 183 | public static final String CODE_UNICODE = "\uf121"; 184 | public static final String CODIEPIE_UNICODE = "\uf284"; 185 | public static final String COFFEE_UNICODE = "\uf0f4"; 186 | public static final String COGS_UNICODE = "\uf085"; 187 | public static final String COG_UNICODE = "\uf013"; 188 | public static final String COLUMNS_UNICODE = "\uf0db"; 189 | public static final String COMMENTING_O_UNICODE = "\uf27b"; 190 | public static final String COMMENTING_UNICODE = "\uf27a"; 191 | public static final String COMMENTS_O_UNICODE = "\uf0e6"; 192 | public static final String COMMENTS_UNICODE = "\uf086"; 193 | public static final String COMMENT_O_UNICODE = "\uf0e5"; 194 | public static final String COMMENT_UNICODE = "\uf075"; 195 | public static final String COMPASS_UNICODE = "\uf14e"; 196 | public static final String COMPRESS_UNICODE = "\uf066"; 197 | public static final String CONNECTDEVELOP_UNICODE = "\uf20e"; 198 | public static final String CONTAO_UNICODE = "\uf26d"; 199 | public static final String COPYRIGHT_UNICODE = "\uf1f9"; 200 | public static final String COPY_UNICODE = "\uf0c5"; 201 | public static final String CREATIVE_COMMONS_UNICODE = "\uf25e"; 202 | public static final String CREDIT_CARD_ALT_UNICODE = "\uf283"; 203 | public static final String CREDIT_CARD_UNICODE = "\uf09d"; 204 | public static final String CROP_UNICODE = "\uf125"; 205 | public static final String CROSSHAIRS_UNICODE = "\uf05b"; 206 | public static final String CSS3_UNICODE = "\uf13c"; 207 | public static final String CUBES_UNICODE = "\uf1b3"; 208 | public static final String CUBE_UNICODE = "\uf1b2"; 209 | public static final String CUTLERY_UNICODE = "\uf0f5"; 210 | public static final String CUT_UNICODE = "\uf0c4"; 211 | public static final String DASHBOARD_UNICODE = "\uf0e4"; 212 | public static final String DASHCUBE_UNICODE = "\uf210"; 213 | public static final String DATABASE_UNICODE = "\uf1c0"; 214 | public static final String DEAFNESS_UNICODE = "\uf2a4"; 215 | public static final String DEAF_UNICODE = "\uf2a4"; 216 | public static final String DEDENT_UNICODE = "\uf03b"; 217 | public static final String DELICIOUS_UNICODE = "\uf1a5"; 218 | public static final String DESKTOP_UNICODE = "\uf108"; 219 | public static final String DEVIANTART_UNICODE = "\uf1bd"; 220 | public static final String DIAMOND_UNICODE = "\uf219"; 221 | public static final String DIGG_UNICODE = "\uf1a6"; 222 | public static final String DOLLAR_UNICODE = "\uf155"; 223 | public static final String DOT_CIRCLE_O_UNICODE = "\uf192"; 224 | public static final String DOWNLOAD_UNICODE = "\uf019"; 225 | public static final String DRIBBBLE_UNICODE = "\uf17d"; 226 | public static final String DRIVERS_LICENSE_O_UNICODE = "\uf2c3"; 227 | public static final String DRIVERS_LICENSE_UNICODE = "\uf2c2"; 228 | public static final String DROPBOX_UNICODE = "\uf16b"; 229 | public static final String DRUPAL_UNICODE = "\uf1a9"; 230 | public static final String EDGE_UNICODE = "\uf282"; 231 | public static final String EDIT_UNICODE = "\uf044"; 232 | public static final String EERCAST_UNICODE = "\uf2da"; 233 | public static final String EJECT_UNICODE = "\uf052"; 234 | public static final String ELLIPSIS_H_UNICODE = "\uf141"; 235 | public static final String ELLIPSIS_V_UNICODE = "\uf142"; 236 | public static final String EMPIRE_UNICODE = "\uf1d1"; 237 | public static final String ENVELOPE_OPEN_O_UNICODE = "\uf2b7"; 238 | public static final String ENVELOPE_OPEN_UNICODE = "\uf2b6"; 239 | public static final String ENVELOPE_O_UNICODE = "\uf003"; 240 | public static final String ENVELOPE_SQUARE_UNICODE = "\uf199"; 241 | public static final String ENVELOPE_UNICODE = "\uf0e0"; 242 | public static final String ENVIRA_UNICODE = "\uf299"; 243 | public static final String ERASER_UNICODE = "\uf12d"; 244 | public static final String ETSY_UNICODE = "\uf2d7"; 245 | public static final String EURO_UNICODE = "\uf153"; 246 | public static final String EUR_UNICODE = "\uf153"; 247 | public static final String EXCHANGE_UNICODE = "\uf0ec"; 248 | public static final String EXCLAMATION_CIRCLE_UNICODE = "\uf06a"; 249 | public static final String EXCLAMATION_TRIANGLE_UNICODE = "\uf071"; 250 | public static final String EXCLAMATION_UNICODE = "\uf12a"; 251 | public static final String EXPAND_UNICODE = "\uf065"; 252 | public static final String EXPEDITEDSSL_UNICODE = "\uf23e"; 253 | public static final String EXTERNAL_LINK_SQUARE_UNICODE = "\uf14c"; 254 | public static final String EXTERNAL_LINK_UNICODE = "\uf08e"; 255 | public static final String EYEDROPPER_UNICODE = "\uf1fb"; 256 | public static final String EYE_SLASH_UNICODE = "\uf070"; 257 | public static final String EYE_UNICODE = "\uf06e"; 258 | public static final String FACEBOOK_F_UNICODE = "\uf09a"; 259 | public static final String FACEBOOK_OFFICIAL_UNICODE = "\uf230"; 260 | public static final String FACEBOOK_SQUARE_UNICODE = "\uf082"; 261 | public static final String FACEBOOK_UNICODE = "\uf09a"; 262 | public static final String FAST_BACKWARD_UNICODE = "\uf049"; 263 | public static final String FAST_FORWARD_UNICODE = "\uf050"; 264 | public static final String FAX_UNICODE = "\uf1ac"; 265 | public static final String FA_UNICODE = "\uf2b4"; 266 | public static final String FEED_UNICODE = "\uf09e"; 267 | public static final String FEMALE_UNICODE = "\uf182"; 268 | public static final String FIGHTER_JET_UNICODE = "\uf0fb"; 269 | public static final String FILES_O_UNICODE = "\uf0c5"; 270 | public static final String FILE_ARCHIVE_O_UNICODE = "\uf1c6"; 271 | public static final String FILE_AUDIO_O_UNICODE = "\uf1c7"; 272 | public static final String FILE_CODE_O_UNICODE = "\uf1c9"; 273 | public static final String FILE_EXCEL_O_UNICODE = "\uf1c3"; 274 | public static final String FILE_IMAGE_O_UNICODE = "\uf1c5"; 275 | public static final String FILE_MOVIE_O_UNICODE = "\uf1c8"; 276 | public static final String FILE_O_UNICODE = "\uf016"; 277 | public static final String FILE_PDF_O_UNICODE = "\uf1c1"; 278 | public static final String FILE_PHOTO_O_UNICODE = "\uf1c5"; 279 | public static final String FILE_PICTURE_O_UNICODE = "\uf1c5"; 280 | public static final String FILE_POWERPOINT_O_UNICODE = "\uf1c4"; 281 | public static final String FILE_SOUND_O_UNICODE = "\uf1c7"; 282 | public static final String FILE_TEXT_O_UNICODE = "\uf0f6"; 283 | public static final String FILE_TEXT_UNICODE = "\uf15c"; 284 | public static final String FILE_UNICODE = "\uf15b"; 285 | public static final String FILE_VIDEO_O_UNICODE = "\uf1c8"; 286 | public static final String FILE_WORD_O_UNICODE = "\uf1c2"; 287 | public static final String FILE_ZIP_O_UNICODE = "\uf1c6"; 288 | public static final String FILM_UNICODE = "\uf008"; 289 | public static final String FILTER_UNICODE = "\uf0b0"; 290 | public static final String FIREFOX_UNICODE = "\uf269"; 291 | public static final String FIRE_EXTINGUISHER_UNICODE = "\uf134"; 292 | public static final String FIRE_UNICODE = "\uf06d"; 293 | public static final String FIRST_ORDER_UNICODE = "\uf2b0"; 294 | public static final String FLAG_CHECKERED_UNICODE = "\uf11e"; 295 | public static final String FLAG_O_UNICODE = "\uf11d"; 296 | public static final String FLAG_UNICODE = "\uf024"; 297 | public static final String FLASH_UNICODE = "\uf0e7"; 298 | public static final String FLASK_UNICODE = "\uf0c3"; 299 | public static final String FLICKR_UNICODE = "\uf16e"; 300 | public static final String FLOPPY_O_UNICODE = "\uf0c7"; 301 | public static final String FOLDER_OPEN_O_UNICODE = "\uf115"; 302 | public static final String FOLDER_OPEN_UNICODE = "\uf07c"; 303 | public static final String FOLDER_O_UNICODE = "\uf114"; 304 | public static final String FOLDER_UNICODE = "\uf07b"; 305 | public static final String FONTICONS_UNICODE = "\uf280"; 306 | public static final String FONT_AWESOME_UNICODE = "\uf2b4"; 307 | public static final String FONT_UNICODE = "\uf031"; 308 | public static final String FORT_AWESOME_UNICODE = "\uf286"; 309 | public static final String FORUMBEE_UNICODE = "\uf211"; 310 | public static final String FORWARD_UNICODE = "\uf04e"; 311 | public static final String FOURSQUARE_UNICODE = "\uf180"; 312 | public static final String FREE_CODE_CAMP_UNICODE = "\uf2c5"; 313 | public static final String FROWN_O_UNICODE = "\uf119"; 314 | public static final String FUTBOL_O_UNICODE = "\uf1e3"; 315 | public static final String GAMEPAD_UNICODE = "\uf11b"; 316 | public static final String GAVEL_UNICODE = "\uf0e3"; 317 | public static final String GBP_UNICODE = "\uf154"; 318 | public static final String GEARS_UNICODE = "\uf085"; 319 | public static final String GEAR_UNICODE = "\uf013"; 320 | public static final String GENDERLESS_UNICODE = "\uf22d"; 321 | public static final String GET_POCKET_UNICODE = "\uf265"; 322 | public static final String GE_UNICODE = "\uf1d1"; 323 | public static final String GG_CIRCLE_UNICODE = "\uf261"; 324 | public static final String GG_UNICODE = "\uf260"; 325 | public static final String GIFT_UNICODE = "\uf06b"; 326 | public static final String GITHUB_ALT_UNICODE = "\uf113"; 327 | public static final String GITHUB_SQUARE_UNICODE = "\uf092"; 328 | public static final String GITHUB_UNICODE = "\uf09b"; 329 | public static final String GITLAB_UNICODE = "\uf296"; 330 | public static final String GITTIP_UNICODE = "\uf184"; 331 | public static final String GIT_SQUARE_UNICODE = "\uf1d2"; 332 | public static final String GIT_UNICODE = "\uf1d3"; 333 | public static final String GLASS_UNICODE = "\uf000"; 334 | public static final String GLIDE_G_UNICODE = "\uf2a6"; 335 | public static final String GLIDE_UNICODE = "\uf2a5"; 336 | public static final String GLOBE_UNICODE = "\uf0ac"; 337 | public static final String GOOGLE_PLUS_CIRCLE_UNICODE = "\uf2b3"; 338 | public static final String GOOGLE_PLUS_OFFICIAL_UNICODE = "\uf2b3"; 339 | public static final String GOOGLE_PLUS_SQUARE_UNICODE = "\uf0d4"; 340 | public static final String GOOGLE_PLUS_UNICODE = "\uf0d5"; 341 | public static final String GOOGLE_UNICODE = "\uf1a0"; 342 | public static final String GOOGLE_WALLET_UNICODE = "\uf1ee"; 343 | public static final String GRADUATION_CAP_UNICODE = "\uf19d"; 344 | public static final String GRATIPAY_UNICODE = "\uf184"; 345 | public static final String GRAV_UNICODE = "\uf2d6"; 346 | public static final String GROUP_UNICODE = "\uf0c0"; 347 | public static final String HACKER_NEWS_UNICODE = "\uf1d4"; 348 | public static final String HANDSHAKE_O_UNICODE = "\uf2b5"; 349 | public static final String HAND_GRAB_O_UNICODE = "\uf255"; 350 | public static final String HAND_LIZARD_O_UNICODE = "\uf258"; 351 | public static final String HAND_O_DOWN_UNICODE = "\uf0a7"; 352 | public static final String HAND_O_LEFT_UNICODE = "\uf0a5"; 353 | public static final String HAND_O_RIGHT_UNICODE = "\uf0a4"; 354 | public static final String HAND_O_UP_UNICODE = "\uf0a6"; 355 | public static final String HAND_PAPER_O_UNICODE = "\uf256"; 356 | public static final String HAND_PEACE_O_UNICODE = "\uf25b"; 357 | public static final String HAND_POINTER_O_UNICODE = "\uf25a"; 358 | public static final String HAND_ROCK_O_UNICODE = "\uf255"; 359 | public static final String HAND_SCISSORS_O_UNICODE = "\uf257"; 360 | public static final String HAND_SPOCK_O_UNICODE = "\uf259"; 361 | public static final String HAND_STOP_O_UNICODE = "\uf256"; 362 | public static final String HARD_OF_HEARING_UNICODE = "\uf2a4"; 363 | public static final String HASHTAG_UNICODE = "\uf292"; 364 | public static final String HDD_O_UNICODE = "\uf0a0"; 365 | public static final String HEADER_UNICODE = "\uf1dc"; 366 | public static final String HEADPHONES_UNICODE = "\uf025"; 367 | public static final String HEARTBEAT_UNICODE = "\uf21e"; 368 | public static final String HEART_O_UNICODE = "\uf08a"; 369 | public static final String HEART_UNICODE = "\uf004"; 370 | public static final String HISTORY_UNICODE = "\uf1da"; 371 | public static final String HOME_UNICODE = "\uf015"; 372 | public static final String HOSPITAL_O_UNICODE = "\uf0f8"; 373 | public static final String HOTEL_UNICODE = "\uf236"; 374 | public static final String HOURGLASS_1_UNICODE = "\uf251"; 375 | public static final String HOURGLASS_2_UNICODE = "\uf252"; 376 | public static final String HOURGLASS_3_UNICODE = "\uf253"; 377 | public static final String HOURGLASS_END_UNICODE = "\uf253"; 378 | public static final String HOURGLASS_HALF_UNICODE = "\uf252"; 379 | public static final String HOURGLASS_O_UNICODE = "\uf250"; 380 | public static final String HOURGLASS_START_UNICODE = "\uf251"; 381 | public static final String HOURGLASS_UNICODE = "\uf254"; 382 | public static final String HOUZZ_UNICODE = "\uf27c"; 383 | public static final String HTML5_UNICODE = "\uf13b"; 384 | public static final String H_SQUARE_UNICODE = "\uf0fd"; 385 | public static final String ID_BADGE_UNICODE = "\uf2c1"; 386 | public static final String ID_CARD_O_UNICODE = "\uf2c3"; 387 | public static final String ID_CARD_UNICODE = "\uf2c2"; 388 | public static final String ILS_UNICODE = "\uf20b"; 389 | public static final String IMAGE_UNICODE = "\uf03e"; 390 | public static final String IMDB_UNICODE = "\uf2d8"; 391 | public static final String INBOX_UNICODE = "\uf01c"; 392 | public static final String INDENT_UNICODE = "\uf03c"; 393 | public static final String INDUSTRY_UNICODE = "\uf275"; 394 | public static final String INFO_CIRCLE_UNICODE = "\uf05a"; 395 | public static final String INFO_UNICODE = "\uf129"; 396 | public static final String INR_UNICODE = "\uf156"; 397 | public static final String INSTAGRAM_UNICODE = "\uf16d"; 398 | public static final String INSTITUTION_UNICODE = "\uf19c"; 399 | public static final String INTERNET_EXPLORER_UNICODE = "\uf26b"; 400 | public static final String INTERSEX_UNICODE = "\uf224"; 401 | public static final String IOXHOST_UNICODE = "\uf208"; 402 | public static final String ITALIC_UNICODE = "\uf033"; 403 | public static final String I_CURSOR_UNICODE = "\uf246"; 404 | public static final String JOOMLA_UNICODE = "\uf1aa"; 405 | public static final String JPY_UNICODE = "\uf157"; 406 | public static final String JSFIDDLE_UNICODE = "\uf1cc"; 407 | public static final String KEYBOARD_O_UNICODE = "\uf11c"; 408 | public static final String KEY_UNICODE = "\uf084"; 409 | public static final String KRW_UNICODE = "\uf159"; 410 | public static final String LANGUAGE_UNICODE = "\uf1ab"; 411 | public static final String LAPTOP_UNICODE = "\uf109"; 412 | public static final String LASTFM_SQUARE_UNICODE = "\uf203"; 413 | public static final String LASTFM_UNICODE = "\uf202"; 414 | public static final String LEAF_UNICODE = "\uf06c"; 415 | public static final String LEANPUB_UNICODE = "\uf212"; 416 | public static final String LEGAL_UNICODE = "\uf0e3"; 417 | public static final String LEMON_O_UNICODE = "\uf094"; 418 | public static final String LEVEL_DOWN_UNICODE = "\uf149"; 419 | public static final String LEVEL_UP_UNICODE = "\uf148"; 420 | public static final String LIFE_BOUY_UNICODE = "\uf1cd"; 421 | public static final String LIFE_BUOY_UNICODE = "\uf1cd"; 422 | public static final String LIFE_RING_UNICODE = "\uf1cd"; 423 | public static final String LIFE_SAVER_UNICODE = "\uf1cd"; 424 | public static final String LIGHTBULB_O_UNICODE = "\uf0eb"; 425 | public static final String LINE_CHART_UNICODE = "\uf201"; 426 | public static final String LINKEDIN_SQUARE_UNICODE = "\uf08c"; 427 | public static final String LINKEDIN_UNICODE = "\uf0e1"; 428 | public static final String LINK_UNICODE = "\uf0c1"; 429 | public static final String LINODE_UNICODE = "\uf2b8"; 430 | public static final String LINUX_UNICODE = "\uf17c"; 431 | public static final String LIST_ALT_UNICODE = "\uf022"; 432 | public static final String LIST_OL_UNICODE = "\uf0cb"; 433 | public static final String LIST_UL_UNICODE = "\uf0ca"; 434 | public static final String LIST_UNICODE = "\uf03a"; 435 | public static final String LOCATION_ARROW_UNICODE = "\uf124"; 436 | public static final String LOCK_UNICODE = "\uf023"; 437 | public static final String LONG_ARROW_DOWN_UNICODE = "\uf175"; 438 | public static final String LONG_ARROW_LEFT_UNICODE = "\uf177"; 439 | public static final String LONG_ARROW_RIGHT_UNICODE = "\uf178"; 440 | public static final String LONG_ARROW_UP_UNICODE = "\uf176"; 441 | public static final String LOW_VISION_UNICODE = "\uf2a8"; 442 | public static final String MAGIC_UNICODE = "\uf0d0"; 443 | public static final String MAGNET_UNICODE = "\uf076"; 444 | public static final String MAIL_FORWARD_UNICODE = "\uf064"; 445 | public static final String MAIL_REPLY_ALL_UNICODE = "\uf122"; 446 | public static final String MAIL_REPLY_UNICODE = "\uf112"; 447 | public static final String MALE_UNICODE = "\uf183"; 448 | public static final String MAP_MARKER_UNICODE = "\uf041"; 449 | public static final String MAP_O_UNICODE = "\uf278"; 450 | public static final String MAP_PIN_UNICODE = "\uf276"; 451 | public static final String MAP_SIGNS_UNICODE = "\uf277"; 452 | public static final String MAP_UNICODE = "\uf279"; 453 | public static final String MARS_DOUBLE_UNICODE = "\uf227"; 454 | public static final String MARS_STROKE_H_UNICODE = "\uf22b"; 455 | public static final String MARS_STROKE_UNICODE = "\uf229"; 456 | public static final String MARS_STROKE_V_UNICODE = "\uf22a"; 457 | public static final String MARS_UNICODE = "\uf222"; 458 | public static final String MAXCDN_UNICODE = "\uf136"; 459 | public static final String MEANPATH_UNICODE = "\uf20c"; 460 | public static final String MEDIUM_UNICODE = "\uf23a"; 461 | public static final String MEDKIT_UNICODE = "\uf0fa"; 462 | public static final String MEETUP_UNICODE = "\uf2e0"; 463 | public static final String MEH_O_UNICODE = "\uf11a"; 464 | public static final String MERCURY_UNICODE = "\uf223"; 465 | public static final String MICROCHIP_UNICODE = "\uf2db"; 466 | public static final String MICROPHONE_SLASH_UNICODE = "\uf131"; 467 | public static final String MICROPHONE_UNICODE = "\uf130"; 468 | public static final String MINUS_CIRCLE_UNICODE = "\uf056"; 469 | public static final String MINUS_SQUARE_O_UNICODE = "\uf147"; 470 | public static final String MINUS_SQUARE_UNICODE = "\uf146"; 471 | public static final String MINUS_UNICODE = "\uf068"; 472 | public static final String MIXCLOUD_UNICODE = "\uf289"; 473 | public static final String MOBILE_PHONE_UNICODE = "\uf10b"; 474 | public static final String MOBILE_UNICODE = "\uf10b"; 475 | public static final String MODX_UNICODE = "\uf285"; 476 | public static final String MONEY_UNICODE = "\uf0d6"; 477 | public static final String MOON_O_UNICODE = "\uf186"; 478 | public static final String MORTAR_BOARD_UNICODE = "\uf19d"; 479 | public static final String MOTORCYCLE_UNICODE = "\uf21c"; 480 | public static final String MOUSE_POINTER_UNICODE = "\uf245"; 481 | public static final String MUSIC_UNICODE = "\uf001"; 482 | public static final String NAVICON_UNICODE = "\uf0c9"; 483 | public static final String NEUTER_UNICODE = "\uf22c"; 484 | public static final String NEWSPAPER_O_UNICODE = "\uf1ea"; 485 | public static final String OBJECT_GROUP_UNICODE = "\uf247"; 486 | public static final String OBJECT_UNGROUP_UNICODE = "\uf248"; 487 | public static final String ODNOKLASSNIKI_SQUARE_UNICODE = "\uf264"; 488 | public static final String ODNOKLASSNIKI_UNICODE = "\uf263"; 489 | public static final String OPENCART_UNICODE = "\uf23d"; 490 | public static final String OPENID_UNICODE = "\uf19b"; 491 | public static final String OPERA_UNICODE = "\uf26a"; 492 | public static final String OPTIN_MONSTER_UNICODE = "\uf23c"; 493 | public static final String OUTDENT_UNICODE = "\uf03b"; 494 | public static final String PAGELINES_UNICODE = "\uf18c"; 495 | public static final String PAINT_BRUSH_UNICODE = "\uf1fc"; 496 | public static final String PAPERCLIP_UNICODE = "\uf0c6"; 497 | public static final String PAPER_PLANE_O_UNICODE = "\uf1d9"; 498 | public static final String PAPER_PLANE_UNICODE = "\uf1d8"; 499 | public static final String PARAGRAPH_UNICODE = "\uf1dd"; 500 | public static final String PASTE_UNICODE = "\uf0ea"; 501 | public static final String PAUSE_CIRCLE_O_UNICODE = "\uf28c"; 502 | public static final String PAUSE_CIRCLE_UNICODE = "\uf28b"; 503 | public static final String PAUSE_UNICODE = "\uf04c"; 504 | public static final String PAW_UNICODE = "\uf1b0"; 505 | public static final String PAYPAL_UNICODE = "\uf1ed"; 506 | public static final String PENCIL_SQUARE_O_UNICODE = "\uf044"; 507 | public static final String PENCIL_SQUARE_UNICODE = "\uf14b"; 508 | public static final String PENCIL_UNICODE = "\uf040"; 509 | public static final String PERCENT_UNICODE = "\uf295"; 510 | public static final String PHONE_SQUARE_UNICODE = "\uf098"; 511 | public static final String PHONE_UNICODE = "\uf095"; 512 | public static final String PHOTO_UNICODE = "\uf03e"; 513 | public static final String PICTURE_O_UNICODE = "\uf03e"; 514 | public static final String PIED_PIPER_ALT_UNICODE = "\uf1a8"; 515 | public static final String PIED_PIPER_PP_UNICODE = "\uf1a7"; 516 | public static final String PIED_PIPER_UNICODE = "\uf2ae"; 517 | public static final String PIE_CHART_UNICODE = "\uf200"; 518 | public static final String PINTEREST_P_UNICODE = "\uf231"; 519 | public static final String PINTEREST_SQUARE_UNICODE = "\uf0d3"; 520 | public static final String PINTEREST_UNICODE = "\uf0d2"; 521 | public static final String PLANE_UNICODE = "\uf072"; 522 | public static final String PLAY_CIRCLE_O_UNICODE = "\uf01d"; 523 | public static final String PLAY_CIRCLE_UNICODE = "\uf144"; 524 | public static final String PLAY_UNICODE = "\uf04b"; 525 | public static final String PLUG_UNICODE = "\uf1e6"; 526 | public static final String PLUS_CIRCLE_UNICODE = "\uf055"; 527 | public static final String PLUS_SQUARE_O_UNICODE = "\uf196"; 528 | public static final String PLUS_SQUARE_UNICODE = "\uf0fe"; 529 | public static final String PLUS_UNICODE = "\uf067"; 530 | public static final String PODCAST_UNICODE = "\uf2ce"; 531 | public static final String POWER_OFF_UNICODE = "\uf011"; 532 | public static final String PRINT_UNICODE = "\uf02f"; 533 | public static final String PRODUCT_HUNT_UNICODE = "\uf288"; 534 | public static final String PUZZLE_PIECE_UNICODE = "\uf12e"; 535 | public static final String QQ_UNICODE = "\uf1d6"; 536 | public static final String QRCODE_UNICODE = "\uf029"; 537 | public static final String QUESTION_CIRCLE_O_UNICODE = "\uf29c"; 538 | public static final String QUESTION_CIRCLE_UNICODE = "\uf059"; 539 | public static final String QUESTION_UNICODE = "\uf128"; 540 | public static final String QUORA_UNICODE = "\uf2c4"; 541 | public static final String QUOTE_LEFT_UNICODE = "\uf10d"; 542 | public static final String QUOTE_RIGHT_UNICODE = "\uf10e"; 543 | public static final String RANDOM_UNICODE = "\uf074"; 544 | public static final String RAVELRY_UNICODE = "\uf2d9"; 545 | public static final String RA_UNICODE = "\uf1d0"; 546 | public static final String REBEL_UNICODE = "\uf1d0"; 547 | public static final String RECYCLE_UNICODE = "\uf1b8"; 548 | public static final String REDDIT_ALIEN_UNICODE = "\uf281"; 549 | public static final String REDDIT_SQUARE_UNICODE = "\uf1a2"; 550 | public static final String REDDIT_UNICODE = "\uf1a1"; 551 | public static final String REFRESH_UNICODE = "\uf021"; 552 | public static final String REGISTERED_UNICODE = "\uf25d"; 553 | public static final String REMOVE_UNICODE = "\uf00d"; 554 | public static final String RENREN_UNICODE = "\uf18b"; 555 | public static final String REORDER_UNICODE = "\uf0c9"; 556 | public static final String REPEAT_UNICODE = "\uf01e"; 557 | public static final String REPLY_ALL_UNICODE = "\uf122"; 558 | public static final String REPLY_UNICODE = "\uf112"; 559 | public static final String RESISTANCE_UNICODE = "\uf1d0"; 560 | public static final String RETWEET_UNICODE = "\uf079"; 561 | public static final String RMB_UNICODE = "\uf157"; 562 | public static final String ROAD_UNICODE = "\uf018"; 563 | public static final String ROCKET_UNICODE = "\uf135"; 564 | public static final String ROTATE_LEFT_UNICODE = "\uf0e2"; 565 | public static final String ROTATE_RIGHT_UNICODE = "\uf01e"; 566 | public static final String ROUBLE_UNICODE = "\uf158"; 567 | public static final String RSS_SQUARE_UNICODE = "\uf143"; 568 | public static final String RSS_UNICODE = "\uf09e"; 569 | public static final String RUBLE_UNICODE = "\uf158"; 570 | public static final String RUB_UNICODE = "\uf158"; 571 | public static final String RUPEE_UNICODE = "\uf156"; 572 | public static final String S15_UNICODE = "\uf2cd"; 573 | public static final String SAFARI_UNICODE = "\uf267"; 574 | public static final String SAVE_UNICODE = "\uf0c7"; 575 | public static final String SCISSORS_UNICODE = "\uf0c4"; 576 | public static final String SCRIBD_UNICODE = "\uf28a"; 577 | public static final String SEARCH_MINUS_UNICODE = "\uf010"; 578 | public static final String SEARCH_PLUS_UNICODE = "\uf00e"; 579 | public static final String SEARCH_UNICODE = "\uf002"; 580 | public static final String SELLSY_UNICODE = "\uf213"; 581 | public static final String SEND_O_UNICODE = "\uf1d9"; 582 | public static final String SEND_UNICODE = "\uf1d8"; 583 | public static final String SERVER_UNICODE = "\uf233"; 584 | public static final String SHARE_ALT_SQUARE_UNICODE = "\uf1e1"; 585 | public static final String SHARE_ALT_UNICODE = "\uf1e0"; 586 | public static final String SHARE_SQUARE_O_UNICODE = "\uf045"; 587 | public static final String SHARE_SQUARE_UNICODE = "\uf14d"; 588 | public static final String SHARE_UNICODE = "\uf064"; 589 | public static final String SHEKEL_UNICODE = "\uf20b"; 590 | public static final String SHEQEL_UNICODE = "\uf20b"; 591 | public static final String SHIELD_UNICODE = "\uf132"; 592 | public static final String SHIP_UNICODE = "\uf21a"; 593 | public static final String SHIRTSINBULK_UNICODE = "\uf214"; 594 | public static final String SHOPPING_BAG_UNICODE = "\uf290"; 595 | public static final String SHOPPING_BASKET_UNICODE = "\uf291"; 596 | public static final String SHOPPING_CART_UNICODE = "\uf07a"; 597 | public static final String SHOWER_UNICODE = "\uf2cc"; 598 | public static final String SIGNAL_UNICODE = "\uf012"; 599 | public static final String SIGNING_UNICODE = "\uf2a7"; 600 | public static final String SIGN_IN_UNICODE = "\uf090"; 601 | public static final String SIGN_LANGUAGE_UNICODE = "\uf2a7"; 602 | public static final String SIGN_OUT_UNICODE = "\uf08b"; 603 | public static final String SIMPLYBUILT_UNICODE = "\uf215"; 604 | public static final String SITEMAP_UNICODE = "\uf0e8"; 605 | public static final String SKYATLAS_UNICODE = "\uf216"; 606 | public static final String SKYPE_UNICODE = "\uf17e"; 607 | public static final String SLACK_UNICODE = "\uf198"; 608 | public static final String SLIDERS_UNICODE = "\uf1de"; 609 | public static final String SLIDESHARE_UNICODE = "\uf1e7"; 610 | public static final String SMILE_O_UNICODE = "\uf118"; 611 | public static final String SNAPCHAT_GHOST_UNICODE = "\uf2ac"; 612 | public static final String SNAPCHAT_SQUARE_UNICODE = "\uf2ad"; 613 | public static final String SNAPCHAT_UNICODE = "\uf2ab"; 614 | public static final String SNOWFLAKE_O_UNICODE = "\uf2dc"; 615 | public static final String SOCCER_BALL_O_UNICODE = "\uf1e3"; 616 | public static final String SORT_ALPHA_ASC_UNICODE = "\uf15d"; 617 | public static final String SORT_ALPHA_DESC_UNICODE = "\uf15e"; 618 | public static final String SORT_AMOUNT_ASC_UNICODE = "\uf160"; 619 | public static final String SORT_AMOUNT_DESC_UNICODE = "\uf161"; 620 | public static final String SORT_ASC_UNICODE = "\uf0de"; 621 | public static final String SORT_DESC_UNICODE = "\uf0dd"; 622 | public static final String SORT_DOWN_UNICODE = "\uf0dd"; 623 | public static final String SORT_NUMERIC_ASC_UNICODE = "\uf162"; 624 | public static final String SORT_NUMERIC_DESC_UNICODE = "\uf163"; 625 | public static final String SORT_UNICODE = "\uf0dc"; 626 | public static final String SORT_UP_UNICODE = "\uf0de"; 627 | public static final String SOUNDCLOUD_UNICODE = "\uf1be"; 628 | public static final String SPACE_SHUTTLE_UNICODE = "\uf197"; 629 | public static final String SPINNER_UNICODE = "\uf110"; 630 | public static final String SPOON_UNICODE = "\uf1b1"; 631 | public static final String SPOTIFY_UNICODE = "\uf1bc"; 632 | public static final String SQUARE_O_UNICODE = "\uf096"; 633 | public static final String SQUARE_UNICODE = "\uf0c8"; 634 | public static final String STACK_EXCHANGE_UNICODE = "\uf18d"; 635 | public static final String STACK_OVERFLOW_UNICODE = "\uf16c"; 636 | public static final String STAR_HALF_EMPTY_UNICODE = "\uf123"; 637 | public static final String STAR_HALF_FULL_UNICODE = "\uf123"; 638 | public static final String STAR_HALF_O_UNICODE = "\uf123"; 639 | public static final String STAR_HALF_UNICODE = "\uf089"; 640 | public static final String STAR_O_UNICODE = "\uf006"; 641 | public static final String STAR_UNICODE = "\uf005"; 642 | public static final String STEAM_SQUARE_UNICODE = "\uf1b7"; 643 | public static final String STEAM_UNICODE = "\uf1b6"; 644 | public static final String STEP_BACKWARD_UNICODE = "\uf048"; 645 | public static final String STEP_FORWARD_UNICODE = "\uf051"; 646 | public static final String STETHOSCOPE_UNICODE = "\uf0f1"; 647 | public static final String STICKY_NOTE_O_UNICODE = "\uf24a"; 648 | public static final String STICKY_NOTE_UNICODE = "\uf249"; 649 | public static final String STOP_CIRCLE_O_UNICODE = "\uf28e"; 650 | public static final String STOP_CIRCLE_UNICODE = "\uf28d"; 651 | public static final String STOP_UNICODE = "\uf04d"; 652 | public static final String STREET_VIEW_UNICODE = "\uf21d"; 653 | public static final String STRIKETHROUGH_UNICODE = "\uf0cc"; 654 | public static final String STUMBLEUPON_CIRCLE_UNICODE = "\uf1a3"; 655 | public static final String STUMBLEUPON_UNICODE = "\uf1a4"; 656 | public static final String SUBSCRIPT_UNICODE = "\uf12c"; 657 | public static final String SUBWAY_UNICODE = "\uf239"; 658 | public static final String SUITCASE_UNICODE = "\uf0f2"; 659 | public static final String SUN_O_UNICODE = "\uf185"; 660 | public static final String SUPERPOWERS_UNICODE = "\uf2dd"; 661 | public static final String SUPERSCRIPT_UNICODE = "\uf12b"; 662 | public static final String SUPPORT_UNICODE = "\uf1cd"; 663 | public static final String TABLET_UNICODE = "\uf10a"; 664 | public static final String TABLE_UNICODE = "\uf0ce"; 665 | public static final String TACHOMETER_UNICODE = "\uf0e4"; 666 | public static final String TAGS_UNICODE = "\uf02c"; 667 | public static final String TAG_UNICODE = "\uf02b"; 668 | public static final String TASKS_UNICODE = "\uf0ae"; 669 | public static final String TAXI_UNICODE = "\uf1ba"; 670 | public static final String TELEGRAM_UNICODE = "\uf2c6"; 671 | public static final String TELEVISION_UNICODE = "\uf26c"; 672 | public static final String TENCENT_WEIBO_UNICODE = "\uf1d5"; 673 | public static final String TERMINAL_UNICODE = "\uf120"; 674 | public static final String TEXT_HEIGHT_UNICODE = "\uf034"; 675 | public static final String TEXT_WIDTH_UNICODE = "\uf035"; 676 | public static final String THEMEISLE_UNICODE = "\uf2b2"; 677 | public static final String THERMOMETER_0_UNICODE = "\uf2cb"; 678 | public static final String THERMOMETER_1_UNICODE = "\uf2ca"; 679 | public static final String THERMOMETER_2_UNICODE = "\uf2c9"; 680 | public static final String THERMOMETER_3_UNICODE = "\uf2c8"; 681 | public static final String THERMOMETER_4_UNICODE = "\uf2c7"; 682 | public static final String THERMOMETER_EMPTY_UNICODE = "\uf2cb"; 683 | public static final String THERMOMETER_FULL_UNICODE = "\uf2c7"; 684 | public static final String THERMOMETER_HALF_UNICODE = "\uf2c9"; 685 | public static final String THERMOMETER_QUARTER_UNICODE = "\uf2ca"; 686 | public static final String THERMOMETER_THREE_QUARTERS_UNICODE = "\uf2c8"; 687 | public static final String THERMOMETER_UNICODE = "\uf2c7"; 688 | public static final String THUMBS_DOWN_UNICODE = "\uf165"; 689 | public static final String THUMBS_O_DOWN_UNICODE = "\uf088"; 690 | public static final String THUMBS_O_UP_UNICODE = "\uf087"; 691 | public static final String THUMBS_UP_UNICODE = "\uf164"; 692 | public static final String THUMB_TACK_UNICODE = "\uf08d"; 693 | public static final String TH_LARGE_UNICODE = "\uf009"; 694 | public static final String TH_LIST_UNICODE = "\uf00b"; 695 | public static final String TH_UNICODE = "\uf00a"; 696 | public static final String TICKET_UNICODE = "\uf145"; 697 | public static final String TIMES_CIRCLE_O_UNICODE = "\uf05c"; 698 | public static final String TIMES_CIRCLE_UNICODE = "\uf057"; 699 | public static final String TIMES_RECTANGLE_O_UNICODE = "\uf2d4"; 700 | public static final String TIMES_RECTANGLE_UNICODE = "\uf2d3"; 701 | public static final String TIMES_UNICODE = "\uf00d"; 702 | public static final String TINT_UNICODE = "\uf043"; 703 | public static final String TOGGLE_DOWN_UNICODE = "\uf150"; 704 | public static final String TOGGLE_LEFT_UNICODE = "\uf191"; 705 | public static final String TOGGLE_OFF_UNICODE = "\uf204"; 706 | public static final String TOGGLE_ON_UNICODE = "\uf205"; 707 | public static final String TOGGLE_RIGHT_UNICODE = "\uf152"; 708 | public static final String TOGGLE_UP_UNICODE = "\uf151"; 709 | public static final String TRADEMARK_UNICODE = "\uf25c"; 710 | public static final String TRAIN_UNICODE = "\uf238"; 711 | public static final String TRANSGENDER_ALT_UNICODE = "\uf225"; 712 | public static final String TRANSGENDER_UNICODE = "\uf224"; 713 | public static final String TRASH_O_UNICODE = "\uf014"; 714 | public static final String TRASH_UNICODE = "\uf1f8"; 715 | public static final String TREE_UNICODE = "\uf1bb"; 716 | public static final String TRELLO_UNICODE = "\uf181"; 717 | public static final String TRIPADVISOR_UNICODE = "\uf262"; 718 | public static final String TROPHY_UNICODE = "\uf091"; 719 | public static final String TRUCK_UNICODE = "\uf0d1"; 720 | public static final String TRY_UNICODE = "\uf195"; 721 | public static final String TTY_UNICODE = "\uf1e4"; 722 | public static final String TUMBLR_SQUARE_UNICODE = "\uf174"; 723 | public static final String TUMBLR_UNICODE = "\uf173"; 724 | public static final String TURKISH_LIRA_UNICODE = "\uf195"; 725 | public static final String TV_UNICODE = "\uf26c"; 726 | public static final String TWITCH_UNICODE = "\uf1e8"; 727 | public static final String TWITTER_SQUARE_UNICODE = "\uf081"; 728 | public static final String TWITTER_UNICODE = "\uf099"; 729 | public static final String UMBRELLA_UNICODE = "\uf0e9"; 730 | public static final String UNDERLINE_UNICODE = "\uf0cd"; 731 | public static final String UNDO_UNICODE = "\uf0e2"; 732 | public static final String UNIVERSAL_ACCESS_UNICODE = "\uf29a"; 733 | public static final String UNIVERSITY_UNICODE = "\uf19c"; 734 | public static final String UNLINK_UNICODE = "\uf127"; 735 | public static final String UNLOCK_ALT_UNICODE = "\uf13e"; 736 | public static final String UNLOCK_UNICODE = "\uf09c"; 737 | public static final String UNSORTED_UNICODE = "\uf0dc"; 738 | public static final String UPLOAD_UNICODE = "\uf093"; 739 | public static final String USB_UNICODE = "\uf287"; 740 | public static final String USD_UNICODE = "\uf155"; 741 | public static final String USERS_UNICODE = "\uf0c0"; 742 | public static final String USER_CIRCLE_O_UNICODE = "\uf2be"; 743 | public static final String USER_CIRCLE_UNICODE = "\uf2bd"; 744 | public static final String USER_MD_UNICODE = "\uf0f0"; 745 | public static final String USER_O_UNICODE = "\uf2c0"; 746 | public static final String USER_PLUS_UNICODE = "\uf234"; 747 | public static final String USER_SECRET_UNICODE = "\uf21b"; 748 | public static final String USER_TIMES_UNICODE = "\uf235"; 749 | public static final String USER_UNICODE = "\uf007"; 750 | public static final String VCARD_O_UNICODE = "\uf2bc"; 751 | public static final String VCARD_UNICODE = "\uf2bb"; 752 | public static final String VENUS_DOUBLE_UNICODE = "\uf226"; 753 | public static final String VENUS_MARS_UNICODE = "\uf228"; 754 | public static final String VENUS_UNICODE = "\uf221"; 755 | public static final String VIACOIN_UNICODE = "\uf237"; 756 | public static final String VIADEO_SQUARE_UNICODE = "\uf2aa"; 757 | public static final String VIADEO_UNICODE = "\uf2a9"; 758 | public static final String VIDEO_CAMERA_UNICODE = "\uf03d"; 759 | public static final String VIMEO_SQUARE_UNICODE = "\uf194"; 760 | public static final String VIMEO_UNICODE = "\uf27d"; 761 | public static final String VINE_UNICODE = "\uf1ca"; 762 | public static final String VK_UNICODE = "\uf189"; 763 | public static final String VOLUME_CONTROL_PHONE_UNICODE = "\uf2a0"; 764 | public static final String VOLUME_DOWN_UNICODE = "\uf027"; 765 | public static final String VOLUME_OFF_UNICODE = "\uf026"; 766 | public static final String VOLUME_UP_UNICODE = "\uf028"; 767 | public static final String WARNING_UNICODE = "\uf071"; 768 | public static final String WECHAT_UNICODE = "\uf1d7"; 769 | public static final String WEIBO_UNICODE = "\uf18a"; 770 | public static final String WEIXIN_UNICODE = "\uf1d7"; 771 | public static final String WHATSAPP_UNICODE = "\uf232"; 772 | public static final String WHEELCHAIR_ALT_UNICODE = "\uf29b"; 773 | public static final String WHEELCHAIR_UNICODE = "\uf193"; 774 | public static final String WIFI_UNICODE = "\uf1eb"; 775 | public static final String WIKIPEDIA_W_UNICODE = "\uf266"; 776 | public static final String WINDOWS_UNICODE = "\uf17a"; 777 | public static final String WINDOW_CLOSE_O_UNICODE = "\uf2d4"; 778 | public static final String WINDOW_CLOSE_UNICODE = "\uf2d3"; 779 | public static final String WINDOW_MAXIMIZE_UNICODE = "\uf2d0"; 780 | public static final String WINDOW_MINIMIZE_UNICODE = "\uf2d1"; 781 | public static final String WINDOW_RESTORE_UNICODE = "\uf2d2"; 782 | public static final String WON_UNICODE = "\uf159"; 783 | public static final String WORDPRESS_UNICODE = "\uf19a"; 784 | public static final String WPBEGINNER_UNICODE = "\uf297"; 785 | public static final String WPEXPLORER_UNICODE = "\uf2de"; 786 | public static final String WPFORMS_UNICODE = "\uf298"; 787 | public static final String WRENCH_UNICODE = "\uf0ad"; 788 | public static final String XING_SQUARE_UNICODE = "\uf169"; 789 | public static final String XING_UNICODE = "\uf168"; 790 | public static final String YAHOO_UNICODE = "\uf19e"; 791 | public static final String YC_SQUARE_UNICODE = "\uf1d4"; 792 | public static final String YC_UNICODE = "\uf23b"; 793 | public static final String YELP_UNICODE = "\uf1e9"; 794 | public static final String YEN_UNICODE = "\uf157"; 795 | public static final String YOAST_UNICODE = "\uf2b1"; 796 | public static final String YOUTUBE_PLAY_UNICODE = "\uf16a"; 797 | public static final String YOUTUBE_SQUARE_UNICODE = "\uf166"; 798 | public static final String YOUTUBE_UNICODE = "\uf167"; 799 | public static final String Y_COMBINATOR_SQUARE_UNICODE = "\uf1d4"; 800 | public static final String Y_COMBINATOR_UNICODE = "\uf23b"; 801 | public static final String _500PX_UNICODE = "\uf26e"; 802 | 803 | //endregion 804 | 805 | private static final String FILENAME = "fontawesome-webfont.woff"; 806 | 807 | //region Constructors 808 | 809 | /** 810 | * Instantiate a new Font Awesome icon. 811 | */ 812 | public Icon() { 813 | super(FILENAME); 814 | } 815 | 816 | /** 817 | * Instantiate a new Font Awesome icon. 818 | * 819 | * @param unicode The unicode of the Font Awesome icon. 820 | */ 821 | public Icon(String unicode) { 822 | super(FILENAME, unicode); 823 | } 824 | 825 | //endregion 826 | 827 | @Builder 828 | public Icon(String unicode, Insets padding, Boolean visible, EventHandler onMouseClicked, List styleClasses) { 829 | super(FILENAME); 830 | setProperty(unicode, this::setText); 831 | setProperty(padding, this::setPadding); 832 | setProperty(visible, this::setVisible); 833 | setProperty(onMouseClicked, this::setOnMouseClicked); 834 | setProperty(styleClasses, e -> this.getStyleClass().addAll(e)); 835 | } 836 | } 837 | --------------------------------------------------------------------------------