Added system tray icon support

This commit is contained in:
dorving 2023-05-03 21:09:57 +02:00
parent 6a9b1201ac
commit f6626d1537
3 changed files with 118 additions and 5 deletions

View File

@ -5,6 +5,7 @@ import gearth.misc.Cacher;
import gearth.misc.UpdateChecker; import gearth.misc.UpdateChecker;
import gearth.misc.listenerpattern.ObservableObject; import gearth.misc.listenerpattern.ObservableObject;
import gearth.ui.GEarthController; import gearth.ui.GEarthController;
import gearth.ui.GEarthTrayIcon;
import gearth.ui.themes.Theme; import gearth.ui.themes.Theme;
import gearth.ui.themes.ThemeFactory; import gearth.ui.themes.ThemeFactory;
import gearth.ui.titlebar.TitleBarConfig; import gearth.ui.titlebar.TitleBarConfig;
@ -41,7 +42,6 @@ public class GEarth extends Application {
public void start(Stage primaryStage) throws Exception{ public void start(Stage primaryStage) throws Exception{
main = this; main = this;
stage = primaryStage; stage = primaryStage;
FXMLLoader loader = new FXMLLoader(getClass().getResource("/gearth/ui/G-Earth.fxml")); FXMLLoader loader = new FXMLLoader(getClass().getResource("/gearth/ui/G-Earth.fxml"));
Parent root; Parent root;
try { try {
@ -120,7 +120,8 @@ public class GEarth extends Application {
stage.getScene().getStylesheets().add(GEarth.class.getResource(String.format("/gearth/ui/themes/%s/styling.css", theme.internalName())).toExternalForm()); stage.getScene().getStylesheets().add(GEarth.class.getResource(String.format("/gearth/ui/themes/%s/styling.css", theme.internalName())).toExternalForm());
stage.getIcons().clear(); stage.getIcons().clear();
stage.getIcons().add(new Image(GEarth.class.getResourceAsStream(String.format("/gearth/ui/themes/%s/logoSmall.png", theme.overridesLogo() ? theme.internalName() : defaultTheme.internalName())))); final Image image = new Image(GEarth.class.getResourceAsStream(String.format("/gearth/ui/themes/%s/logoSmall.png", theme.overridesLogo() ? theme.internalName() : defaultTheme.internalName())));
stage.getIcons().add(image);
stage.setTitle((theme.overridesTitle() ? theme.title() : defaultTheme.title()) + " " + GEarth.version); stage.setTitle((theme.overridesTitle() ? theme.title() : defaultTheme.title()) + " " + GEarth.version);
controller.infoController.img_logo.setImage(new Image(GEarth.class.getResourceAsStream( controller.infoController.img_logo.setImage(new Image(GEarth.class.getResourceAsStream(
@ -131,7 +132,11 @@ public class GEarth extends Application {
))); )));
controller.infoController.version.setText(stage.getTitle()); controller.infoController.version.setText(stage.getTitle());
// }); // });
GEarthTrayIcon.updateOrCreate(image);
}
public GEarthController getController() {
return controller;
} }
public static String[] args; public static String[] args;

View File

@ -0,0 +1,105 @@
package gearth.ui;
import gearth.GEarth;
import gearth.services.extension_handler.extensions.GEarthExtension;
import gearth.services.extension_handler.extensions.extensionproducers.ExtensionProducerFactory;
import gearth.services.extension_handler.extensions.implementations.network.NetworkExtensionServer;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.Image;
import javafx.stage.Stage;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
/**
* Adds a {@link TrayIcon} to the {@link SystemTray} for this G-Earth instance.
*
* @author Dorving
*/
public final class GEarthTrayIcon {
private static final String TO_FRONT_LABEL = "To front";
private static final String TO_BACK_LABEL = "To back";
private static PopupMenu menu;
public static void updateOrCreate(Image image) {
if (!SystemTray.isSupported())
return;
final NetworkExtensionServer server = ExtensionProducerFactory.getExtensionServer();
final BufferedImage awtImage = SwingFXUtils.fromFXImage(image, null);
final String appTitle = "G-Earth " + GEarth.version + " (" + server.getPort() + ")";
final Optional<TrayIcon> trayIcon = Stream.of(SystemTray.getSystemTray().getTrayIcons())
.filter(other -> Objects.equals(other.getToolTip(), appTitle))
.findFirst();
if (trayIcon.isPresent()) {
EventQueue.invokeLater(() -> trayIcon.get().setImage(awtImage));
} else {
menu = new PopupMenu();
menu.add(createToFrontOrBackMenuItem());
menu.addSeparator();
menu.addSeparator();
menu.add(createInstallMenuItem());
try {
SystemTray.getSystemTray().add(new TrayIcon(awtImage, appTitle, menu));
} catch (AWTException e) {
e.printStackTrace();
menu = null;
}
}
}
private static MenuItem createToFrontOrBackMenuItem() {
final MenuItem showMenuItem = new MenuItem(TO_FRONT_LABEL);
showMenuItem.addActionListener(e -> {
if (Objects.equals(showMenuItem.getLabel(), TO_FRONT_LABEL)) {
showMenuItem.setLabel(TO_BACK_LABEL);
Platform.runLater(() -> GEarth.main.getController().getStage().toFront());
} else {
showMenuItem.setLabel(TO_FRONT_LABEL);
Platform.runLater(() -> GEarth.main.getController().getStage().toBack());
}
});
return showMenuItem;
}
private static MenuItem createInstallMenuItem() {
final MenuItem showMenuItem = new MenuItem("Install Extension...");
showMenuItem.addActionListener(e ->
Optional.ofNullable(GEarth.main.getController())
.map(c -> c.extensionsController)
.ifPresent(c -> Platform.runLater(() -> {
final Stage stage = c.parentController.getStage();
final boolean isOnTop = stage.isAlwaysOnTop();
stage.setAlwaysOnTop(true); // bit of a hack to force stage to front
c.installBtnClicked(null);
stage.setAlwaysOnTop(isOnTop);
})));
return showMenuItem;
}
/**
* Adds the argued extension as a menu item to {@link #menu}.
*
* @param extension the {@link GEarthExtension} to add to the {@link #menu}.
*/
public static void addExtension(GEarthExtension extension) {
if (menu == null)
return;
final MenuItem menuItem = new MenuItem("Show "+extension.getTitle());
EventQueue.invokeLater(() -> menu.insert(menuItem, 2));
menuItem
.addActionListener(e -> Platform.runLater(() -> extension.getClickedObservable().fireEvent()));
extension.getDeletedObservable()
.addListener(() -> EventQueue.invokeLater(() -> menu.remove(menuItem)));
}
}

View File

@ -8,6 +8,7 @@ import gearth.services.extension_handler.extensions.implementations.network.exec
import gearth.services.extension_handler.extensions.implementations.network.executer.ExtensionRunner; import gearth.services.extension_handler.extensions.implementations.network.executer.ExtensionRunner;
import gearth.services.extension_handler.extensions.implementations.network.executer.ExtensionRunnerFactory; import gearth.services.extension_handler.extensions.implementations.network.executer.ExtensionRunnerFactory;
import gearth.services.g_python.GPythonShell; import gearth.services.g_python.GPythonShell;
import gearth.ui.GEarthTrayIcon;
import gearth.ui.SubForm; import gearth.ui.SubForm;
import gearth.ui.subforms.extensions.logger.ExtensionLogger; import gearth.ui.subforms.extensions.logger.ExtensionLogger;
import gearth.ui.translations.LanguageBundle; import gearth.ui.translations.LanguageBundle;
@ -18,6 +19,7 @@ import javafx.scene.control.*;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File; import java.io.File;
@ -54,9 +56,10 @@ public class ExtensionsController extends SubForm {
protected void onParentSet() { protected void onParentSet() {
ExtensionItemContainerProducer producer = new ExtensionItemContainerProducer(extensioncontainer, scroller); ExtensionItemContainerProducer producer = new ExtensionItemContainerProducer(extensioncontainer, scroller);
extensionHandler = new ExtensionHandler(getHConnection()); extensionHandler = new ExtensionHandler(getHConnection());
extensionHandler.getObservable().addListener((e -> { extensionHandler.getObservable().addListener((e -> Platform.runLater(() -> {
Platform.runLater(() -> producer.extensionConnected(e)); producer.extensionConnected(e);
})); GEarthTrayIcon.addExtension(e);
})));
//noinspection OptionalGetWithoutIsPresent //noinspection OptionalGetWithoutIsPresent
networkExtensionsProducer networkExtensionsProducer