2025-04-21 14:46:26 +08:00

183 lines
6.1 KiB
Java

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<KeyCodeCombination> 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<WindowView, WindowViewModel> 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());
}
}