1 /*
2 * *************************************************************************************************************************************************************
3 *
4 * SteelBlue: DCI User Interfaces
5 * http://tidalwave.it/projects/steelblue
6 *
7 * Copyright (C) 2015 - 2024 by Tidalwave s.a.s. (http://tidalwave.it)
8 *
9 * *************************************************************************************************************************************************************
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
17 * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
18 *
19 * *************************************************************************************************************************************************************
20 *
21 * git clone https://bitbucket.org/tidalwave/steelblue-src
22 * git clone https://github.com/tidalwave-it/steelblue-src
23 *
24 * *************************************************************************************************************************************************************
25 */
26 package it.tidalwave.ui.javafx;
27
28 import javax.annotation.Nonnull;
29 import java.util.concurrent.Executor;
30 import java.util.concurrent.Executors;
31 import java.io.IOException;
32 import javafx.scene.Parent;
33 import javafx.scene.Scene;
34 import javafx.scene.input.KeyCombination;
35 import javafx.stage.Screen;
36 import javafx.stage.Stage;
37 import javafx.stage.StageStyle;
38 import javafx.application.Application;
39 import javafx.application.Platform;
40 import jfxtras.styles.jmetro.JMetro;
41 import jfxtras.styles.jmetro.Style;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44 import it.tidalwave.util.PreferencesHandler;
45 import lombok.Getter;
46 import lombok.Setter;
47 import static it.tidalwave.util.PreferencesHandler.KEY_FULL_SCREEN;
48 import static it.tidalwave.util.PreferencesHandler.KEY_INITIAL_SIZE;
49
50 /***************************************************************************************************************************************************************
51 *
52 * @author Fabrizio Giudici
53 *
54 **************************************************************************************************************************************************************/
55 public abstract class JavaFXApplicationWithSplash extends Application
56 {
57 private static final String DEFAULT_APPLICATION_FXML = "Application.fxml";
58
59 private static final String DEFAULT_SPLASH_FXML = "Splash.fxml";
60
61 // Don't use Slf4j and its static logger - give Main a chance to initialize things
62 private final Logger log = LoggerFactory.getLogger(JavaFXApplicationWithSplash.class);
63
64 private Splash splash;
65
66 @Getter @Setter
67 private boolean maximized;
68
69 @Getter @Setter
70 private boolean fullScreen;
71
72 @Getter @Setter
73 private boolean fullScreenLocked;
74
75 @Getter @Setter
76 protected String applicationFxml = DEFAULT_APPLICATION_FXML;
77
78 @Getter @Setter
79 protected String splashFxml = DEFAULT_SPLASH_FXML;
80
81 /***********************************************************************************************************************************************************
82 * {@inheritDoc}
83 **********************************************************************************************************************************************************/
84 @Override
85 public void init()
86 {
87 log.info("init()");
88 splash = new Splash(this, splashFxml, this::createScene);
89 splash.init();
90 }
91
92 /***********************************************************************************************************************************************************
93 * {@inheritDoc}
94 **********************************************************************************************************************************************************/
95 @Override
96 public void start (@Nonnull final Stage stage)
97 {
98 log.info("start({})", stage);
99 final var splashStage = new Stage(StageStyle.TRANSPARENT);
100 stage.setMaximized(maximized);
101 // splashStage.setMaximized(maximized); FIXME: doesn't work
102 configureFullScreen(stage);
103 // configureFullScreen(splashStage); FIXME: deadlocks JDK 1.8.0_40
104
105 if (!maximized && !fullScreen)
106 {
107 splashStage.centerOnScreen();
108 }
109
110 splash.show(splashStage);
111
112 getExecutor().execute(() -> // FIXME: use JavaFX Worker?
113 {
114 initializeInBackground();
115 Platform.runLater(() ->
116 {
117 try
118 {
119 final var applicationNad = createParent();
120 final var scene = createScene((Parent)applicationNad.getNode());
121 stage.setOnCloseRequest(event -> onClosing());
122 stage.setScene(scene);
123 onStageCreated(stage, applicationNad);
124 final var preferencesHandler = PreferencesHandler.getInstance();
125 stage.setFullScreen(preferencesHandler.getProperty(KEY_FULL_SCREEN).orElse(false));
126 final double scale = preferencesHandler.getProperty(KEY_INITIAL_SIZE).orElse(0.65);
127 final var screenSize = Screen.getPrimary().getBounds();
128 stage.setWidth(scale * screenSize.getWidth());
129 stage.setHeight(scale * screenSize.getHeight());
130 stage.show();
131 splashStage.toFront();
132 splash.dismiss();
133 }
134 catch (IOException e)
135 {
136 log.error("", e);
137 }
138 });
139 });
140 }
141
142 /***********************************************************************************************************************************************************
143 *
144 **********************************************************************************************************************************************************/
145 protected void onStageCreated (@Nonnull final Stage stage, @Nonnull final NodeAndDelegate<?> applicationNad)
146 {
147 }
148
149 /***********************************************************************************************************************************************************
150 *
151 **********************************************************************************************************************************************************/
152 @Nonnull
153 protected abstract NodeAndDelegate<?> createParent()
154 throws IOException;
155
156 /***********************************************************************************************************************************************************
157 *
158 **********************************************************************************************************************************************************/
159 protected abstract void initializeInBackground();
160
161 /***********************************************************************************************************************************************************
162 * Invoked when the main {@link Stage} is being closed.
163 **********************************************************************************************************************************************************/
164 protected void onClosing()
165 {
166 }
167
168 /***********************************************************************************************************************************************************
169 *
170 **********************************************************************************************************************************************************/
171 @Nonnull
172 protected Executor getExecutor()
173 {
174 return Executors.newSingleThreadExecutor();
175 }
176
177 /***********************************************************************************************************************************************************
178 *
179 **********************************************************************************************************************************************************/
180 protected Scene createScene (@Nonnull final Parent parent)
181 {
182 final var scene = new Scene(parent);
183 final var jMetro = new JMetro(Style.DARK);
184 jMetro.setScene(scene);
185 return scene;
186 }
187
188 /***********************************************************************************************************************************************************
189 *
190 **********************************************************************************************************************************************************/
191 private void configureFullScreen (@Nonnull final Stage stage)
192 {
193 stage.setFullScreen(fullScreen);
194
195 if (fullScreen && fullScreenLocked)
196 {
197 stage.setFullScreenExitHint("");
198 stage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
199 }
200 }
201 }