package com.cecilia; import com.cecilia.event.BrowseEvent; import com.cecilia.event.DefaultEventBus; import com.cecilia.event.HotkeyEvent; import com.cecilia.event.Listener; import com.goxr3plus.fxborderlessscene.borderless.BorderlessPane; import com.goxr3plus.fxborderlessscene.borderless.BorderlessScene; import com.cecilia.theme.ThemeManager; import com.cecilia.view.window.WindowView; import com.cecilia.view.window.WindowViewModel; import de.saxsys.mvvmfx.FluentViewLoader; import de.saxsys.mvvmfx.ViewTuple; import fr.brouillard.oss.cssfx.CSSFX; import fr.brouillard.oss.cssfx.api.URIToPathConverter; import fr.brouillard.oss.cssfx.impl.log.CSSFXLogger; import javafx.application.Application; import javafx.application.ConditionalFeature; import javafx.application.Platform; import javafx.css.PseudoClass; import javafx.geometry.Insets; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.SceneAntialiasing; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; import javafx.scene.paint.Color; import javafx.stage.Stage; import javafx.stage.StageStyle; import java.io.IOException; import java.io.InputStreamReader; import java.net.URI; import java.nio.file.Paths; import java.util.List; import java.util.Properties; import static java.nio.charset.StandardCharsets.UTF_8; /** * JavaFX App */ public class AppUI extends Application { public static final boolean IS_DEV_MODE = "DEV".equalsIgnoreCase( Resources.getPropertyOrEnv("app.profiles.active", "app.profiles.active") ); public static final List SUPPORTED_HOTKEYS = List.of( new KeyCodeCombination(KeyCode.SLASH), new KeyCodeCombination(KeyCode.T, KeyCombination.CONTROL_DOWN), new KeyCodeCombination(KeyCode.W, KeyCombination.CONTROL_DOWN) ); public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { Thread.currentThread().setUncaughtExceptionHandler(new DefaultExceptionHandler(stage)); loadApplicationProperties(); if (IS_DEV_MODE) { System.out.println("[WARNING] Application is running in development mode."); } var antialiasing = Platform.isSupported(ConditionalFeature.SCENE3D) ? SceneAntialiasing.BALANCED : SceneAntialiasing.DISABLED; ViewTuple viewTuple = FluentViewLoader.fxmlView(WindowView.class).load(); Parent mainView = viewTuple.getView(); BorderlessScene scene = new BorderlessScene(stage, StageStyle.TRANSPARENT, mainView, 950, 650); BorderlessPane rootPane = (BorderlessPane) scene.getRoot(); rootPane.setPadding(new Insets(15, 15, 15, 15)); scene.setOnKeyPressed(this::dispatchHotkeys); scene.removeDefaultCSS(); scene.setMoveControl(mainView); scene.setFill(Color.TRANSPARENT); var tm = ThemeManager.getInstance(); tm.setScene(scene); tm.setTheme(tm.getDefaultTheme()); if (IS_DEV_MODE) { startCssFX(scene); } scene.maximizedProperty().addListener((observableValue, oldVal, newVal) -> { if (newVal) { rootPane.setPadding(new Insets(0, 0, 0, 0)); } else { rootPane.setPadding(new Insets(15, 15, 15, 15)); } }); scene.getStylesheets().addAll(Resources.resolve("assets/styles/index.css")); PseudoClass CUSTOM_THEME = PseudoClass.getPseudoClass("custom-theme"); scene.getRoot().pseudoClassStateChanged(CUSTOM_THEME, true); //Show stage.setScene(scene); stage.setTitle(System.getProperty("app.name")); loadIcons(stage); stage.setResizable(true); stage.setOnCloseRequest(t -> Platform.exit()); // register event listeners DefaultEventBus.getInstance().subscribe(BrowseEvent.class, this::onBrowseEvent); Platform.runLater(() -> { stage.show(); stage.requestFocus(); }); } private void loadApplicationProperties() { Properties properties = new Properties(); try (InputStreamReader in = new InputStreamReader(Resources.getResourceAsStream("/application.properties"), UTF_8)) { properties.load(in); properties.forEach((key, value) -> System.setProperty( String.valueOf(key), String.valueOf(value) )); } catch (IOException e) { throw new RuntimeException(e); } } private void startCssFX(Scene scene) { URIToPathConverter fileUrlConverter = uri -> { try { if (uri != null && uri.startsWith("file:")) { return Paths.get(URI.create(uri)); } } catch (Exception e) { e.printStackTrace(); } return null; }; CSSFX.addConverter(fileUrlConverter).start(); CSSFXLogger.setLoggerFactory(loggerName -> (level, message, args) -> { if (level.ordinal() <= CSSFXLogger.LogLevel.INFO.ordinal()) { System.out.println("[" + level + "] CSSFX: " + String.format(message, args)); } }); CSSFX.start(scene); } private void dispatchHotkeys(KeyEvent event) { for (KeyCodeCombination k : SUPPORTED_HOTKEYS) { if (k.match(event)) { DefaultEventBus.getInstance().publish(new HotkeyEvent(k)); return; } } } private void loadIcons(Stage stage) { int iconSize = 16; while (iconSize <= 1024) { iconSize *= 2; } } @Override public void init() throws Exception { super.init(); } @Override public void stop() throws Exception { super.stop(); } @Listener private void onBrowseEvent(BrowseEvent event) { getHostServices().showDocument(event.getUri().toString()); } }