Merge pull request #27 from informatik-ag-ngl/f/status_tray
Restore Status Tray Functionality
This commit is contained in:
commit
9a947739a6
@ -75,7 +75,7 @@ public class Settings {
|
|||||||
|
|
||||||
private void supplementDefaults() {
|
private void supplementDefaults() {
|
||||||
items.putIfAbsent("enterToSend", new SettingsItem<>(true, "Enter to send", "Sends a message by pressing the enter key."));
|
items.putIfAbsent("enterToSend", new SettingsItem<>(true, "Enter to send", "Sends a message by pressing the enter key."));
|
||||||
items.putIfAbsent("onCloseMode", new SettingsItem<>(true, "Hide on close", "Hides the chat window when it is closed."));
|
items.putIfAbsent("hideOnClose", new SettingsItem<>(true, "Hide on close", "Hides the chat window when it is closed."));
|
||||||
items.putIfAbsent("currentTheme", new SettingsItem<>("dark", "Current Theme Name", "The name of the currently selected theme."));
|
items.putIfAbsent("currentTheme", new SettingsItem<>("dark", "Current Theme Name", "The name of the currently selected theme."));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,15 +124,15 @@ public class Settings {
|
|||||||
* @return the current on close mode.
|
* @return the current on close mode.
|
||||||
* @since Envoy Client v0.3-alpha
|
* @since Envoy Client v0.3-alpha
|
||||||
*/
|
*/
|
||||||
public Boolean getCurrentOnCloseMode() { return (Boolean) items.get("onCloseMode").get(); }
|
public Boolean isHideOnClose() { return (Boolean) items.get("hideOnClose").get(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the current on close mode.
|
* Sets the current on close mode.
|
||||||
*
|
*
|
||||||
* @param currentOnCloseMode the on close mode that should be set.
|
* @param hideOnClose whether the application should be minimized on close
|
||||||
* @since Envoy Client v0.3-alpha
|
* @since Envoy Client v0.3-alpha
|
||||||
*/
|
*/
|
||||||
public void setCurrentOnCloseMode(boolean currentOnCloseMode) { ((SettingsItem<Boolean>) items.get("onCloseMode")).set(currentOnCloseMode); }
|
public void setHideOnClose(boolean hideOnClose) { ((SettingsItem<Boolean>) items.get("hideOnClose")).set(hideOnClose); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the items
|
* @return the items
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
package envoy.client.ui;
|
package envoy.client.ui;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
|
|
||||||
import envoy.client.data.Settings;
|
import envoy.client.data.Settings;
|
||||||
@ -145,6 +149,23 @@ public class IconUtil {
|
|||||||
return icons;
|
return icons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a buffered image from the resource folder which is compatible with AWT.
|
||||||
|
*
|
||||||
|
* @param path the path to the icon inside the resource folder
|
||||||
|
* @return the loaded image
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public static BufferedImage loadAWTCompatible(String path) {
|
||||||
|
BufferedImage image = null;
|
||||||
|
try {
|
||||||
|
image = ImageIO.read(IconUtil.class.getResource(path));
|
||||||
|
} catch (IOException e) {
|
||||||
|
EnvoyLog.getLogger(IconUtil.class).log(Level.WARNING, String.format("Could not load image at path %s: ", path), e);
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method should be called if the display of an image depends upon the
|
* This method should be called if the display of an image depends upon the
|
||||||
* currently active theme.<br>
|
* currently active theme.<br>
|
||||||
@ -154,7 +175,7 @@ public class IconUtil {
|
|||||||
* @return the theme specific folder
|
* @return the theme specific folder
|
||||||
* @since Envoy Client v0.1-beta
|
* @since Envoy Client v0.1-beta
|
||||||
*/
|
*/
|
||||||
public static String themeSpecificSubFolder() {
|
private static String themeSpecificSubFolder() {
|
||||||
return Settings.getInstance().isUsingDefaultTheme() ? Settings.getInstance().getCurrentTheme() + "/" : "";
|
return Settings.getInstance().isUsingDefaultTheme() ? Settings.getInstance().getCurrentTheme() + "/" : "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,13 @@ package envoy.client.ui;
|
|||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.TrayIcon.MessageType;
|
import java.awt.TrayIcon.MessageType;
|
||||||
import java.awt.event.WindowAdapter;
|
|
||||||
import java.awt.event.WindowEvent;
|
import javafx.application.Platform;
|
||||||
import java.util.logging.Level;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
import envoy.client.event.MessageCreationEvent;
|
import envoy.client.event.MessageCreationEvent;
|
||||||
import envoy.data.Message;
|
import envoy.data.Message;
|
||||||
import envoy.event.EventBus;
|
import envoy.event.EventBus;
|
||||||
import envoy.exception.EnvoyException;
|
|
||||||
import envoy.util.EnvoyLog;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Project: <strong>envoy-client</strong><br>
|
* Project: <strong>envoy-client</strong><br>
|
||||||
@ -35,66 +33,65 @@ public class StatusTrayIcon {
|
|||||||
*/
|
*/
|
||||||
private boolean displayMessages = false;
|
private boolean displayMessages = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@code true} if the status tray icon is supported on this platform
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public static boolean isSupported() { return SystemTray.isSupported(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link StatusTrayIcon} with the Envoy logo, a tool tip and a pop-up
|
* Creates a {@link StatusTrayIcon} with the Envoy logo, a tool tip and a pop-up
|
||||||
* menu.
|
* menu.
|
||||||
*
|
*
|
||||||
* @param focusTarget the {@link Window} which focus determines if message
|
* @param stage the stage whose focus determines if message
|
||||||
* notifications are displayed
|
* notifications are displayed
|
||||||
* @throws EnvoyException if the currently used OS does not support the System
|
* @since Envoy Client v0.2-beta
|
||||||
* Tray API
|
|
||||||
* @since Envoy Client v0.2-alpha
|
|
||||||
*/
|
*/
|
||||||
public StatusTrayIcon(Window focusTarget) throws EnvoyException {
|
public StatusTrayIcon(Stage stage) {
|
||||||
if (!SystemTray.isSupported()) throw new EnvoyException("The Envoy tray icon is not supported.");
|
trayIcon = new TrayIcon(IconUtil.loadAWTCompatible("/icons/envoy_logo.png"), "Envoy");
|
||||||
|
|
||||||
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
|
||||||
final Image img = Toolkit.getDefaultToolkit().createImage(loader.getResource("envoy_logo.png"));
|
|
||||||
trayIcon = new TrayIcon(img, "Envoy Client");
|
|
||||||
trayIcon.setImageAutoSize(true);
|
trayIcon.setImageAutoSize(true);
|
||||||
trayIcon.setToolTip("You are notified if you have unread messages.");
|
trayIcon.setToolTip("You are notified if you have unread messages.");
|
||||||
|
|
||||||
final PopupMenu popup = new PopupMenu();
|
final PopupMenu popup = new PopupMenu();
|
||||||
|
|
||||||
final MenuItem exitMenuItem = new MenuItem("Exit");
|
final MenuItem exitMenuItem = new MenuItem("Exit");
|
||||||
exitMenuItem.addActionListener(evt -> System.exit(0));
|
exitMenuItem.addActionListener(evt -> { Platform.exit(); System.exit(0); });
|
||||||
popup.add(exitMenuItem);
|
popup.add(exitMenuItem);
|
||||||
|
|
||||||
trayIcon.setPopupMenu(popup);
|
trayIcon.setPopupMenu(popup);
|
||||||
|
|
||||||
// Only display messages if the chat window is not focused
|
// Only display messages if the stage is not focused
|
||||||
focusTarget.addWindowFocusListener(new WindowAdapter() {
|
stage.focusedProperty().addListener((ov, onHidden, onShown) -> displayMessages = !ov.getValue());
|
||||||
|
|
||||||
@Override
|
|
||||||
public void windowGainedFocus(WindowEvent e) { displayMessages = false; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void windowLostFocus(WindowEvent e) { displayMessages = true; }
|
|
||||||
});
|
|
||||||
|
|
||||||
// Show the window if the user clicks on the icon
|
// Show the window if the user clicks on the icon
|
||||||
trayIcon.addActionListener(evt -> { focusTarget.setVisible(true); focusTarget.requestFocus(); });
|
trayIcon.addActionListener(evt -> Platform.runLater(() -> { stage.setIconified(false); stage.toFront(); stage.requestFocus(); }));
|
||||||
|
|
||||||
// Start processing message events
|
// Start processing message events
|
||||||
// TODO: Handle other message types
|
EventBus.getInstance().register(MessageCreationEvent.class, evt -> {
|
||||||
EventBus.getInstance()
|
if (displayMessages) trayIcon
|
||||||
.register(MessageCreationEvent.class,
|
.displayMessage(
|
||||||
evt -> { if (displayMessages) trayIcon.displayMessage("New message received", evt.get().getText(), MessageType.INFO); });
|
evt.get().hasAttachment() ? "New " + evt.get().getAttachment().getType().toString().toLowerCase() + " message received"
|
||||||
|
: "New message received",
|
||||||
|
evt.get().getText(),
|
||||||
|
MessageType.INFO);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes this {@link StatusTrayIcon} appear in the system tray.
|
* Makes the icon appear in the system tray.
|
||||||
*
|
*
|
||||||
* @throws EnvoyException if the status icon could not be attaches to the system
|
|
||||||
* tray for system-internal reasons
|
|
||||||
* @since Envoy Client v0.2-alpha
|
* @since Envoy Client v0.2-alpha
|
||||||
*/
|
*/
|
||||||
public void show() throws EnvoyException {
|
public void show() {
|
||||||
try {
|
try {
|
||||||
SystemTray.getSystemTray().add(trayIcon);
|
SystemTray.getSystemTray().add(trayIcon);
|
||||||
} catch (final AWTException e) {
|
} catch (AWTException e) {}
|
||||||
EnvoyLog.getLogger(StatusTrayIcon.class).log(Level.INFO, "Could not display StatusTrayIcon: ", e);
|
|
||||||
throw new EnvoyException("Could not attach Envoy tray icon to system tray.", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the icon from the system tray.
|
||||||
|
*
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public void hide() { SystemTray.getSystemTray().remove(trayIcon); }
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,7 @@ import javafx.scene.control.Alert.AlertType;
|
|||||||
import envoy.client.data.*;
|
import envoy.client.data.*;
|
||||||
import envoy.client.net.Client;
|
import envoy.client.net.Client;
|
||||||
import envoy.client.net.WriteProxy;
|
import envoy.client.net.WriteProxy;
|
||||||
import envoy.client.ui.ClearableTextField;
|
import envoy.client.ui.*;
|
||||||
import envoy.client.ui.SceneContext;
|
|
||||||
import envoy.client.ui.Startup;
|
|
||||||
import envoy.data.LoginCredentials;
|
import envoy.data.LoginCredentials;
|
||||||
import envoy.data.User;
|
import envoy.data.User;
|
||||||
import envoy.data.User.UserStatus;
|
import envoy.data.User.UserStatus;
|
||||||
@ -64,6 +62,7 @@ public final class LoginScene {
|
|||||||
private static final Logger logger = EnvoyLog.getLogger(LoginScene.class);
|
private static final Logger logger = EnvoyLog.getLogger(LoginScene.class);
|
||||||
private static final EventBus eventBus = EventBus.getInstance();
|
private static final EventBus eventBus = EventBus.getInstance();
|
||||||
private static final ClientConfig config = ClientConfig.getInstance();
|
private static final ClientConfig config = ClientConfig.getInstance();
|
||||||
|
private static final Settings settings = Settings.getInstance();
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void initialize() {
|
private void initialize() {
|
||||||
@ -210,5 +209,23 @@ public final class LoginScene {
|
|||||||
sceneContext.getStage().setMinWidth(350);
|
sceneContext.getStage().setMinWidth(350);
|
||||||
sceneContext.load(SceneContext.SceneInfo.CHAT_SCENE);
|
sceneContext.load(SceneContext.SceneInfo.CHAT_SCENE);
|
||||||
sceneContext.<ChatScene>getController().initializeData(sceneContext, localDB, client, writeProxy);
|
sceneContext.<ChatScene>getController().initializeData(sceneContext, localDB, client, writeProxy);
|
||||||
|
|
||||||
|
if (StatusTrayIcon.isSupported()) {
|
||||||
|
|
||||||
|
// Configure hide on close
|
||||||
|
sceneContext.getStage().setOnCloseRequest(e -> {
|
||||||
|
if (settings.isHideOnClose()) {
|
||||||
|
sceneContext.getStage().setIconified(true);
|
||||||
|
e.consume();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize status tray icon
|
||||||
|
final var trayIcon = new StatusTrayIcon(sceneContext.getStage());
|
||||||
|
settings.getItems().get("hideOnClose").setChangeHandler(c -> {
|
||||||
|
if (((Boolean) c)) trayIcon.show();
|
||||||
|
else trayIcon.hide();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ public class GeneralSettingsPane extends SettingsPane {
|
|||||||
final var vbox = new VBox();
|
final var vbox = new VBox();
|
||||||
|
|
||||||
// TODO: Support other value types
|
// TODO: Support other value types
|
||||||
List.of("onCloseMode", "enterToSend")
|
List.of("hideOnClose", "enterToSend")
|
||||||
.stream()
|
.stream()
|
||||||
.map(settings.getItems()::get)
|
.map(settings.getItems()::get)
|
||||||
.map(i -> new SettingsCheckbox((SettingsItem<Boolean>) i))
|
.map(i -> new SettingsCheckbox((SettingsItem<Boolean>) i))
|
||||||
|
Reference in New Issue
Block a user