Skip to content

Commit ccdf08a

Browse files
committed
refactor(startup): replace custom JavaFX splash with JVM -splash
Use Java native splash for consistent behavior across OS and Flatpak.\n\n- remove custom StartupSplashController/Widget module\n- wire -splash for run, jar/shadowJar, jpackage, and Flatpak launcher\n- close native splash after primary stage show\n- add startup splash PNG and keep SVG source asset
1 parent 8ca0d57 commit ccdf08a

9 files changed

Lines changed: 515 additions & 406 deletions

File tree

build.gradle.kts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ val jpackageExecutable = packagingJavaHome.map { javaHome ->
174174
file("$javaHome/bin/$executable").absolutePath
175175
}
176176

177+
val startupSplashImage = file("src/main/resources/images/startup-splash-primer-dark.png")
178+
val startupSplashEnabled = startupSplashImage.exists()
179+
val startupSplashJvmOption = "-splash:${startupSplashImage.absolutePath}"
180+
val startupSplashPackagedJvmOption = "-splash:\$APPDIR/${startupSplashImage.name}"
181+
177182
/*
178183
* Per-platform icon resolution for jpackage.
179184
* Keep jpackage icons under packaging/.
@@ -248,7 +253,12 @@ javafx {
248253

249254
application {
250255
mainClass.set(Meta.mainClass)
251-
applicationDefaultJvmArgs = listOf(Meta.nativeAccessOption)
256+
applicationDefaultJvmArgs = buildList {
257+
add(Meta.nativeAccessOption)
258+
if (startupSplashEnabled) {
259+
add(startupSplashJvmOption)
260+
}
261+
}
252262
}
253263

254264
/*
@@ -332,6 +342,9 @@ tasks.named<Jar>("jar") {
332342
manifest {
333343
attributes["Main-Class"] = Meta.mainClass
334344
attributes["Implementation-Version"] = project.version.toString()
345+
if (startupSplashEnabled) {
346+
attributes["SplashScreen-Image"] = "images/${startupSplashImage.name}"
347+
}
335348
}
336349
}
337350

@@ -348,6 +361,9 @@ tasks.named<ShadowJar>("shadowJar") {
348361
manifest {
349362
attributes["Main-Class"] = Meta.mainClass
350363
attributes["Implementation-Version"] = project.version.toString()
364+
if (startupSplashEnabled) {
365+
attributes["SplashScreen-Image"] = "images/${startupSplashImage.name}"
366+
}
351367
}
352368
}
353369

@@ -408,6 +424,11 @@ tasks.register<Exec>("jpackageCurrentPlatform") {
408424
"--java-options", Meta.nativeAccessOption
409425
)
410426

427+
if (startupSplashEnabled) {
428+
args("--app-content", startupSplashImage.absolutePath)
429+
args("--java-options", startupSplashPackagedJvmOption)
430+
}
431+
411432
if (jpackageIcon.exists()) {
412433
args("--icon", jpackageIcon.absolutePath)
413434
} else {
@@ -464,6 +485,11 @@ if (hostOs == "windows") {
464485
"--java-options", Meta.nativeAccessOption
465486
)
466487

488+
if (startupSplashEnabled) {
489+
args("--app-content", startupSplashImage.absolutePath)
490+
args("--java-options", startupSplashPackagedJvmOption)
491+
}
492+
467493
if (jpackageIcon.exists()) {
468494
args("--icon", jpackageIcon.absolutePath)
469495
} else {

packaging/flatpak/fr.inria.corese.CoreseGui.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ modules:
3131
build-commands:
3232
- install -Dm644 corese-gui-standalone.jar /app/bin/corese-gui-standalone.jar
3333
- install -Dm755 packaging/flatpak/scripts/run.sh /app/bin/corese-gui
34+
- install -Dm644 src/main/resources/images/startup-splash-primer-dark.png /app/share/corese-gui/startup-splash-primer-dark.png
3435
- install -Dm644 packaging/flatpak/appdata/${FLATPAK_ID}.appdata.xml /app/share/metainfo/${FLATPAK_ID}.appdata.xml
3536
- install -Dm644 packaging/flatpak/appdata/${FLATPAK_ID}.desktop /app/share/applications/${FLATPAK_ID}.desktop
3637
- install -Dm644 packaging/assets/logo/${FLATPAK_ID}.svg /app/share/icons/hicolor/scalable/apps/${FLATPAK_ID}.svg

packaging/flatpak/scripts/run.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ export GDK_BACKEND=x11
55

66
exec /app/jre/bin/java \
77
--enable-native-access=ALL-UNNAMED \
8+
-splash:/app/share/corese-gui/startup-splash-primer-dark.png \
89
-jar /app/bin/corese-gui-standalone.jar \
910
"$@"

src/main/java/fr/inria/corese/gui/App.java

Lines changed: 15 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,17 @@
88
import fr.inria.corese.gui.feature.main.MainView;
99
import fr.inria.corese.gui.feature.main.ViewManager;
1010
import fr.inria.corese.gui.feature.main.navigation.NavigationBarController;
11-
import fr.inria.corese.gui.feature.startup.splash.StartupSplashController;
1211
import fr.inria.corese.gui.utils.AppExecutors;
1312
import fr.inria.corese.gui.utils.fx.SvgImageLoader;
13+
import java.awt.SplashScreen;
1414
import java.util.Objects;
15-
import javafx.animation.PauseTransition;
1615
import javafx.application.Application;
1716
import javafx.application.Platform;
1817
import javafx.geometry.Rectangle2D;
1918
import javafx.scene.Scene;
2019
import javafx.scene.image.Image;
2120
import javafx.stage.Screen;
2221
import javafx.stage.Stage;
23-
import javafx.util.Duration;
2422
import org.slf4j.Logger;
2523
import org.slf4j.LoggerFactory;
2624

@@ -42,17 +40,7 @@ public final class App extends Application {
4240
public void start(Stage primaryStage) {
4341
Platform.setImplicitExit(true);
4442
ThemeManager themeManager = ThemeManager.getInstance();
45-
46-
StartupSplashController splashController = createStartupSplashController(themeManager);
47-
if (splashController != null) {
48-
splashController.show();
49-
}
50-
51-
long startupStartNanos = System.nanoTime();
52-
PauseTransition initializeDelay = new PauseTransition(Duration.millis(StartupSplashController.RENDER_GUARD_MS));
53-
initializeDelay.setOnFinished(
54-
event -> initializeAndShowMainStage(primaryStage, splashController, startupStartNanos, themeManager));
55-
initializeDelay.play();
43+
initializeAndShowMainStage(primaryStage, themeManager);
5644
}
5745

5846
@Override
@@ -69,8 +57,7 @@ public static void main(String[] args) {
6957
System.exit(0);
7058
}
7159

72-
private static void initializeAndShowMainStage(Stage primaryStage, StartupSplashController splashController,
73-
long startupStartNanos, ThemeManager themeManager) {
60+
private static void initializeAndShowMainStage(Stage primaryStage, ThemeManager themeManager) {
7461
try {
7562
MainView mainView = new MainView();
7663
NavigationBarController navigationController = new NavigationBarController();
@@ -91,44 +78,22 @@ private static void initializeAndShowMainStage(Stage primaryStage, StartupSplash
9178
applyInitialWindowSize(primaryStage);
9279
primaryStage.setOnCloseRequest(event -> Platform.exit());
9380

94-
schedulePrimaryStageShow(primaryStage, splashController, startupStartNanos);
81+
primaryStage.show();
82+
closeNativeSplashIfPresent();
9583
} catch (RuntimeException e) {
96-
closeSplashStage(splashController);
84+
closeNativeSplashIfPresent();
9785
throw e;
9886
}
9987
}
10088

101-
private static void schedulePrimaryStageShow(Stage primaryStage, StartupSplashController splashController,
102-
long startupStartNanos) {
103-
double elapsedMs = (System.nanoTime() - startupStartNanos) / 1_000_000.0;
104-
double remainingMs = StartupSplashController.MIN_DISPLAY_MS - elapsedMs;
105-
if (splashController == null || remainingMs <= 0) {
106-
showPrimaryStageAndCloseSplash(primaryStage, splashController);
107-
return;
108-
}
109-
PauseTransition delay = new PauseTransition(Duration.millis(remainingMs));
110-
delay.setOnFinished(event -> showPrimaryStageAndCloseSplash(primaryStage, splashController));
111-
delay.play();
112-
}
113-
114-
private static void showPrimaryStageAndCloseSplash(Stage primaryStage, StartupSplashController splashController) {
115-
primaryStage.show();
116-
closeSplashStage(splashController);
117-
}
118-
119-
private static void closeSplashStage(StartupSplashController splashController) {
120-
if (splashController == null) {
121-
return;
122-
}
123-
splashController.close();
124-
}
125-
126-
private static StartupSplashController createStartupSplashController(ThemeManager themeManager) {
89+
private static void closeNativeSplashIfPresent() {
12790
try {
128-
return new StartupSplashController(themeManager);
129-
} catch (RuntimeException e) {
130-
LOGGER.warn("Unable to create startup splash", e);
131-
return null;
91+
SplashScreen splash = SplashScreen.getSplashScreen();
92+
if (splash != null) {
93+
splash.close();
94+
}
95+
} catch (UnsupportedOperationException _) {
96+
// No native splash active.
13297
}
13398
}
13499

@@ -140,8 +105,8 @@ private static void applyApplicationIcon(Stage stage) {
140105
return;
141106
}
142107

143-
Image pngIcon = new Image(
144-
Objects.requireNonNull(App.class.getResourceAsStream(APP_LOGO_PNG_RESOURCE), "Application icon not found"));
108+
Image pngIcon = new Image(Objects.requireNonNull(App.class.getResourceAsStream(APP_LOGO_PNG_RESOURCE),
109+
"Application icon not found"));
145110
stage.getIcons().add(pngIcon);
146111
} catch (Exception e) {
147112
LOGGER.warn("Failed to load application icon", e);

src/main/java/fr/inria/corese/gui/feature/startup/splash/StartupSplashController.java

Lines changed: 0 additions & 102 deletions
This file was deleted.

0 commit comments

Comments
 (0)