View Javadoc
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   }