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 }