Merge pull request #78 from informatik-ag-ngl/f/improved_settings
Improved Settings Architecture
This commit is contained in:
commit
1a3fbddfe2
@ -1,6 +1,7 @@
|
||||
package envoy.client;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
@ -11,6 +12,7 @@ import javax.xml.datatype.DatatypeFactory;
|
||||
import envoy.client.event.EventBus;
|
||||
import envoy.client.event.MessageCreationEvent;
|
||||
import envoy.client.util.EnvoyLog;
|
||||
import envoy.client.util.SerializationUtils;
|
||||
import envoy.exception.EnvoyException;
|
||||
import envoy.schema.*;
|
||||
import envoy.schema.Message.Metadata.MessageState;
|
||||
@ -38,7 +40,7 @@ public class LocalDB {
|
||||
private Sync sync = objectFactory.createSync();
|
||||
private Sync readMessages = objectFactory.createSync();
|
||||
|
||||
private static final Logger logger = EnvoyLog.getLogger(LocalDB.class.getSimpleName());
|
||||
private static final Logger logger = EnvoyLog.getLogger(LocalDB.class.getSimpleName());
|
||||
|
||||
/**
|
||||
* Constructs an empty local database. To serialize any chats to the file
|
||||
@ -83,10 +85,10 @@ public class LocalDB {
|
||||
*/
|
||||
public void save() throws IOException {
|
||||
// Save users
|
||||
write(usersFile, users);
|
||||
SerializationUtils.write(usersFile, users);
|
||||
|
||||
// Save chats
|
||||
write(localDBFile, chats);
|
||||
SerializationUtils.write(localDBFile, chats);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,8 +97,7 @@ public class LocalDB {
|
||||
* @throws EnvoyException if the loading process failed
|
||||
* @since Envoy v0.2-alpha
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void loadUsers() throws EnvoyException { users = read(usersFile, HashMap.class); }
|
||||
public void loadUsers() throws EnvoyException { users = SerializationUtils.read(usersFile, HashMap.class); }
|
||||
|
||||
/**
|
||||
* Loads all chats saved by Envoy for the client user.
|
||||
@ -104,31 +105,7 @@ public class LocalDB {
|
||||
* @throws EnvoyException if the loading process failed
|
||||
* @since Envoy v0.1-alpha
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void loadChats() throws EnvoyException { chats = read(localDBFile, ArrayList.class); }
|
||||
|
||||
private <T> T read(File file, Class<T> serializedClass) throws EnvoyException {
|
||||
if (file == null) throw new NullPointerException("File is null");
|
||||
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(file))) {
|
||||
return serializedClass.cast(in.readObject());
|
||||
} catch (ClassNotFoundException | IOException e) {
|
||||
throw new EnvoyException("Could not load serialized object", e);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> void write(File file, T obj) throws IOException {
|
||||
if (file == null) throw new NullPointerException("File is null");
|
||||
if (obj == null) throw new NullPointerException("Object to serialize is null");
|
||||
if (!file.exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
}
|
||||
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file))) {
|
||||
out.writeObject(obj);
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
public void loadChats() throws EnvoyException { chats = SerializationUtils.read(localDBFile, ArrayList.class); }
|
||||
|
||||
/**
|
||||
* Creates a {@link Message} object serializable to XML.
|
||||
@ -238,8 +215,7 @@ public class LocalDB {
|
||||
// Updating UserStatus of all users in LocalDB
|
||||
for (User user : returnSync.getUsers())
|
||||
for (Chat chat : getChats())
|
||||
if (user.getID() == chat.getRecipient().getID())
|
||||
chat.getRecipient().setStatus(user.getStatus());
|
||||
if (user.getID() == chat.getRecipient().getID()) chat.getRecipient().setStatus(user.getStatus());
|
||||
|
||||
sync.getMessages().clear();
|
||||
sync.getUsers().clear();
|
||||
@ -301,7 +277,8 @@ public class LocalDB {
|
||||
public void clearUnreadMessagesSync() { unreadMessagesSync.getMessages().clear(); }
|
||||
|
||||
/**
|
||||
* @return a {@code Map<String, User>} of all users stored locally with their user names as keys
|
||||
* @return a {@code Map<String, User>} of all users stored locally with their
|
||||
* user names as keys
|
||||
* @since Envoy v0.2-alpha
|
||||
*/
|
||||
public Map<String, User> getUsers() { return users; }
|
||||
|
@ -7,6 +7,8 @@ import java.util.prefs.Preferences;
|
||||
|
||||
import envoy.client.ui.Color;
|
||||
import envoy.client.ui.Theme;
|
||||
import envoy.client.util.SerializationUtils;
|
||||
import envoy.exception.EnvoyException;
|
||||
|
||||
/**
|
||||
* Manages all application settings, which are different objects that can be
|
||||
@ -25,20 +27,18 @@ import envoy.client.ui.Theme;
|
||||
public class Settings {
|
||||
|
||||
// Actual settings accessible by the rest of the application
|
||||
private boolean enterToSend = true;
|
||||
private Map<String, Theme> themes;
|
||||
private String currentTheme;
|
||||
private boolean currentOnCloseMode;
|
||||
private Map<String, SettingsItem<?>> items;
|
||||
private Map<String, Theme> themes;
|
||||
|
||||
/**
|
||||
* Required to save the settings.
|
||||
* Settings are stored in this file.
|
||||
*/
|
||||
private Preferences prefs = Preferences.userNodeForPackage(Settings.class);
|
||||
private static final File settingsFile = new File(Config.getInstance().getHomeDirectory(), "settings.ser");
|
||||
|
||||
/**
|
||||
* User-defined themes are stored inside this file.
|
||||
*/
|
||||
private File themeFile = new File(Config.getInstance().getHomeDirectory(), "themes.ser");
|
||||
private static final File themeFile = new File(Config.getInstance().getHomeDirectory(), "themes.ser");
|
||||
|
||||
/**
|
||||
* Singleton instance of this class.
|
||||
@ -51,30 +51,21 @@ public class Settings {
|
||||
*
|
||||
* @since Envoy v0.2-alpha
|
||||
*/
|
||||
private Settings() { load(); }
|
||||
|
||||
/**
|
||||
* This method is used to ensure that there is only one instance of Settings.
|
||||
*
|
||||
* @return the instance of Settings
|
||||
* @since Envoy v0.2-alpha
|
||||
*/
|
||||
public static Settings getInstance() { return settings; }
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void load() {
|
||||
setEnterToSend(prefs.getBoolean("enterToSend", true));
|
||||
setCurrentTheme(prefs.get("theme", "dark"));
|
||||
setCurrentOnCloseMode(prefs.getBoolean("onCloseMode", true));
|
||||
private Settings() {
|
||||
// Load settings from settings file
|
||||
try {
|
||||
items = SerializationUtils.read(settingsFile, HashMap.class);
|
||||
} catch (EnvoyException e) {
|
||||
items = new HashMap<>();
|
||||
}
|
||||
supplementDefaults();
|
||||
|
||||
// Load themes from theme file
|
||||
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(themeFile))) {
|
||||
Object obj = in.readObject();
|
||||
if (obj instanceof HashMap) themes = (Map<String, Theme>) obj;
|
||||
} catch (IOException | ClassNotFoundException e) {
|
||||
themes = new HashMap<>();
|
||||
currentTheme = "dark";
|
||||
e.printStackTrace();
|
||||
try {
|
||||
themes = SerializationUtils.read(themeFile, HashMap.class);
|
||||
} catch (EnvoyException e1) {
|
||||
themes = new HashMap<>();
|
||||
setCurrentTheme("dark");
|
||||
}
|
||||
|
||||
// Load standard themes not defined in the themes file
|
||||
@ -86,6 +77,14 @@ public class Settings {
|
||||
Color.black, Color.black));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to ensure that there is only one instance of Settings.
|
||||
*
|
||||
* @return the instance of Settings
|
||||
* @since Envoy v0.2-alpha
|
||||
*/
|
||||
public static Settings getInstance() { return settings; }
|
||||
|
||||
/**
|
||||
* Updates the preferences when the save button is clicked.
|
||||
*
|
||||
@ -94,15 +93,17 @@ public class Settings {
|
||||
* @since Envoy v0.2-alpha
|
||||
*/
|
||||
public void save() throws IOException {
|
||||
prefs.put("theme", currentTheme);
|
||||
prefs.putBoolean("enterToSend", isEnterToSend());
|
||||
prefs.putBoolean("onCloseMode", currentOnCloseMode);
|
||||
// Save settings to settings file
|
||||
SerializationUtils.write(settingsFile, items);
|
||||
|
||||
// Save themes to theme file
|
||||
themeFile.createNewFile();
|
||||
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(themeFile))) {
|
||||
out.writeObject(themes);
|
||||
}
|
||||
SerializationUtils.write(themeFile, themes);
|
||||
}
|
||||
|
||||
private void supplementDefaults() {
|
||||
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("currentTheme", new SettingsItem<>("dark", null));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,7 +118,7 @@ public class Settings {
|
||||
* @return the name of the currently active {@link Theme}
|
||||
* @since Envoy v0.2-alpha
|
||||
*/
|
||||
public String getCurrentTheme() { return currentTheme; }
|
||||
public String getCurrentTheme() { return (String) items.get("currentTheme").get(); }
|
||||
|
||||
/**
|
||||
* Sets the name of the current {@link Theme}.
|
||||
@ -125,7 +126,8 @@ public class Settings {
|
||||
* @param themeName the name to set
|
||||
* @since Envoy v0.2-alpha
|
||||
*/
|
||||
public void setCurrentTheme(String themeName) { currentTheme = themeName; }
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setCurrentTheme(String themeName) { ((SettingsItem<String>) items.get("currentTheme")).set(themeName); }
|
||||
|
||||
/**
|
||||
* @return {@code true}, if pressing the {@code Enter} key suffices to send a
|
||||
@ -133,7 +135,7 @@ public class Settings {
|
||||
* {@code Control} key.
|
||||
* @since Envoy v0.2-alpha
|
||||
*/
|
||||
public boolean isEnterToSend() { return enterToSend; }
|
||||
public boolean isEnterToSend() { return (boolean) items.get("enterToSend").get(); }
|
||||
|
||||
/**
|
||||
* Changes the keystrokes performed by the user to send a message.
|
||||
@ -143,7 +145,33 @@ public class Settings {
|
||||
* conjunction with the {@code Control} key.
|
||||
* @since Envoy v0.2-alpha
|
||||
*/
|
||||
public void setEnterToSend(boolean enterToSend) { this.enterToSend = enterToSend; }
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setEnterToSend(boolean enterToSend) { ((SettingsItem<Boolean>) items.get("enterToSend")).set(enterToSend); }
|
||||
|
||||
/**
|
||||
* @return the current on close mode.
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public boolean getCurrentOnCloseMode() { return (boolean) items.get("onCloseMode").get(); }
|
||||
|
||||
/**
|
||||
* Sets the current on close mode.
|
||||
*
|
||||
* @param currentOnCloseMode the on close mode that should be set.
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setCurrentOnCloseMode(boolean currentOnCloseMode) { ((SettingsItem<Boolean>) items.get("onCloseMode")).set(currentOnCloseMode); }
|
||||
|
||||
/**
|
||||
* @return the items
|
||||
*/
|
||||
public Map<String, SettingsItem<?>> getItems() { return items; }
|
||||
|
||||
/**
|
||||
* @param items the items to set
|
||||
*/
|
||||
public void setItems(Map<String, SettingsItem<?>> items) { this.items = items; }
|
||||
|
||||
/**
|
||||
* @return a {@code Map<String, Theme>} of all themes with their names as keys
|
||||
@ -158,18 +186,4 @@ public class Settings {
|
||||
* @since Envoy v0.2-alpha
|
||||
*/
|
||||
public void setThemes(Map<String, Theme> themes) { this.themes = themes; }
|
||||
|
||||
/**
|
||||
* @return the current on close mode.
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public boolean getCurrentOnCloseMode() { return currentOnCloseMode; }
|
||||
|
||||
/**
|
||||
* Sets the current on close mode.
|
||||
*
|
||||
* @param currentOnCloseMode the on close mode that should be set.
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public void setCurrentOnCloseMode(boolean currentOnCloseMode) { this.currentOnCloseMode = currentOnCloseMode; }
|
||||
}
|
148
src/main/java/envoy/client/SettingsItem.java
Normal file
148
src/main/java/envoy/client/SettingsItem.java
Normal file
@ -0,0 +1,148 @@
|
||||
package envoy.client;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import envoy.client.ui.PrimaryToggleSwitch;
|
||||
|
||||
/**
|
||||
* Encapsulates a persistent value that is directly or indirectly mutable by the
|
||||
* user.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-clientChess</strong><br>
|
||||
* File: <strong>SettingsItem.java</strong><br>
|
||||
* Created: <strong>23.12.2019</strong><br>
|
||||
*
|
||||
* @param <T> the type of this {@link SettingsItem}'s value
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public class SettingsItem<T> implements Serializable {
|
||||
|
||||
private T value;
|
||||
private Class<? extends JComponent> componentClass;
|
||||
private String userFriendlyName, description;
|
||||
|
||||
transient private Consumer<T> changeHandler;
|
||||
|
||||
private static final Map<Class<?>, Class<? extends JComponent>> componentClasses = new HashMap<>();
|
||||
|
||||
private static final long serialVersionUID = 2146837835556852218L;
|
||||
|
||||
static {
|
||||
componentClasses.put(Boolean.class, PrimaryToggleSwitch.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a {@link SettingsItem}. The default value's class will be mapped
|
||||
* to a {@link JComponent} that can be used to display this {@link SettingsItem}
|
||||
* to the user.
|
||||
*
|
||||
* @param value the default value
|
||||
* @param userFriendlyName the user friendly name (short)
|
||||
* @param description the description (long)
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public SettingsItem(T value, String userFriendlyName, String description) {
|
||||
this(value, componentClasses.get(value.getClass()));
|
||||
this.userFriendlyName = userFriendlyName;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a {@link SettingsItem}. The default value's class will be mapped
|
||||
* to a specific {@link JComponent}. The mapping can also be disables if this
|
||||
* parameter is {@code null}. In that case a {@link NullPointerException} will
|
||||
* be thrown if the method {@link SettingsItem#getComponent()} is called.
|
||||
*
|
||||
* @param value the default value
|
||||
* @param componentClass the class of the {@link JComponent} to represent this {@link SettingsItem} with
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public SettingsItem(T value, Class<? extends JComponent> componentClass) {
|
||||
this.value = value;
|
||||
this.componentClass = componentClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an instance of the {@link JComponent} that represents this {@link SettingsItem}
|
||||
* @throws ReflectiveOperationException if the component initialization failed
|
||||
* @throws SecurityException if the component initialization failed
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public JComponent getComponent() throws ReflectiveOperationException, SecurityException {
|
||||
if (componentClass == null) throw new NullPointerException("Component class is null");
|
||||
return componentClass.getConstructor(SettingsItem.class).newInstance(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the value
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public T get() { return value; }
|
||||
|
||||
/**
|
||||
* Changes the value of this {@link SettingsItem}. If a {@code ChangeHandler} if
|
||||
* defined, it will be invoked with this value.
|
||||
*
|
||||
* @param value the value to set
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public void set(T value) {
|
||||
if (changeHandler != null && value != this.value) changeHandler.accept(value);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the componentClass
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public Class<? extends JComponent> getComponentClass() { return componentClass; }
|
||||
|
||||
/**
|
||||
* @param componentClass the componentClass to set
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public void setComponentClass(Class<? extends JComponent> componentClass) { this.componentClass = componentClass; }
|
||||
|
||||
/**
|
||||
* @return the userFriendlyName
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public String getUserFriendlyName() { return userFriendlyName; }
|
||||
|
||||
/**
|
||||
* @param userFriendlyName the userFriendlyName to set
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public void setUserFriendlyName(String userFriendlyName) { this.userFriendlyName = userFriendlyName; }
|
||||
|
||||
/**
|
||||
* @return the description
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public String getDescription() { return description; }
|
||||
|
||||
/**
|
||||
* @param description the description to set
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
|
||||
/**
|
||||
* Sets a {@code ChangeHandler} for this {@link SettingsItem}. It will be
|
||||
* invoked with the current value once during the registration and every time
|
||||
* when the value changes.
|
||||
*
|
||||
* @param changeHandler the changeHandler to set
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public void setChangeHandler(Consumer<T> changeHandler) {
|
||||
this.changeHandler = changeHandler;
|
||||
changeHandler.accept(value);
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package envoy.client.event;
|
||||
|
||||
/**
|
||||
* Encapsulates a change to the {@code enterToSend} setting.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>EnterToSendEvent.java</strong><br>
|
||||
* Created: <strong>22 Dec 2019</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public class EnterToSendEvent implements Event<Boolean> {
|
||||
|
||||
private boolean mode;
|
||||
|
||||
/**
|
||||
* Initializes an {@link EnterToSendEvent}.
|
||||
*
|
||||
* @param mode the state of the {@code enterToSend} setting
|
||||
* @since Envoy 0.3-alpha
|
||||
*/
|
||||
public EnterToSendEvent(boolean mode) { this.mode = mode; }
|
||||
|
||||
@Override
|
||||
public Boolean get() { return mode; }
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package envoy.client.event;
|
||||
|
||||
/**
|
||||
* Encapsulates a change to the {@code currentOnCloseMode} setting.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>OnCloseChangeEvent.java</strong><br>
|
||||
* Created: <strong>22 Dec 2019</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public class OnCloseChangeEvent implements Event<Boolean> {
|
||||
|
||||
private boolean closeMode;
|
||||
|
||||
/**
|
||||
* Initializes an {@link OnCloseChangeEvent}.
|
||||
*
|
||||
* @param closeMode the state of the {@code currentOnCloseMode} setting
|
||||
* @since Envoy 0.3-alpha
|
||||
*/
|
||||
public OnCloseChangeEvent(boolean closeMode) { this.closeMode = closeMode; }
|
||||
|
||||
@Override
|
||||
public Boolean get() { return closeMode; }
|
||||
}
|
@ -1,114 +1,57 @@
|
||||
package envoy.client.ui;
|
||||
|
||||
import java.awt.*;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.logging.Logger;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import envoy.client.Settings;
|
||||
import envoy.client.event.Event;
|
||||
import envoy.client.event.EventBus;
|
||||
import envoy.client.util.EnvoyLog;
|
||||
import envoy.client.SettingsItem;
|
||||
|
||||
/**
|
||||
* This Component can be used to toggle between two options. e.g. on and
|
||||
* off.<br>
|
||||
* This component can be used to toggle between two options. This will change
|
||||
* the state of a {@code boolean} {@link SettingsItem}.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>PrimaryToggleSwitch.java</strong><br>
|
||||
* Created: <strong>21 Dec 2019</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public class PrimaryToggleSwitch extends JPanel {
|
||||
public class PrimaryToggleSwitch extends JButton {
|
||||
|
||||
private final JButton b = new JButton("");
|
||||
private boolean state;
|
||||
|
||||
private boolean currentState;
|
||||
|
||||
private static final Logger logger = EnvoyLog.getLogger(PrimaryToggleSwitch.class.getSimpleName());
|
||||
private static final long serialVersionUID = -721155303106833184L;
|
||||
private static final long serialVersionUID = -721155303106833184L;
|
||||
|
||||
/**
|
||||
* This is the constructor for the PrimaryToggleSwitch.
|
||||
* Initializes a {@link PrimaryToggleSwitch}.
|
||||
*
|
||||
* @param initialState The state the toggleSwitch is standardly set to. </br>
|
||||
* true: off </br>
|
||||
* false: on
|
||||
* @param eventClass the class of the event dispatched by this toggleSwitch
|
||||
* @param settingsItem the {@link SettingsItem} that is controlled by this
|
||||
* {@link PrimaryToggleSwitch}
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public PrimaryToggleSwitch(boolean initialState, Class<? extends Event<Boolean>> eventClass) {
|
||||
setEnabled(true);
|
||||
setVisible(true);
|
||||
|
||||
public PrimaryToggleSwitch(SettingsItem<Boolean> settingsItem) {
|
||||
setPreferredSize(new Dimension(50, 25));
|
||||
setMinimumSize(new Dimension(50, 25));
|
||||
setMaximumSize(new Dimension(50, 25));
|
||||
|
||||
b.setPreferredSize(new Dimension(25, 25));
|
||||
b.setMinimumSize(new Dimension(25, 25));
|
||||
b.setMaximumSize(new Dimension(25, 25));
|
||||
setBorderPainted(false);
|
||||
setFocusPainted(false);
|
||||
setContentAreaFilled(false);
|
||||
|
||||
b.setBackground(Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getInteractableBackgroundColor());
|
||||
|
||||
GridBagLayout gbl_toggleSwitch = new GridBagLayout();
|
||||
gbl_toggleSwitch.columnWidths = new int[] { 1, 1 };
|
||||
gbl_toggleSwitch.rowHeights = new int[] { 1 };
|
||||
gbl_toggleSwitch.columnWeights = new double[] { 1.0, 1.0 };
|
||||
gbl_toggleSwitch.rowWeights = new double[] { 1.0 };
|
||||
|
||||
setLayout(gbl_toggleSwitch);
|
||||
|
||||
setState(initialState);
|
||||
|
||||
b.addActionListener((evt) -> {
|
||||
try {
|
||||
// Dispatch event
|
||||
Constructor<? extends Event<Boolean>> constructor = eventClass.getConstructor(boolean.class);
|
||||
EventBus.getInstance().dispatch(constructor.newInstance(currentState));
|
||||
|
||||
setState(!currentState);
|
||||
revalidate();
|
||||
repaint();
|
||||
} catch (ReflectiveOperationException | SecurityException e) {
|
||||
logger.warning("An error occured while changing the setting: " + e);
|
||||
}
|
||||
});
|
||||
|
||||
repaint();
|
||||
state = settingsItem.get();
|
||||
addActionListener((evt) -> { state = !state; settingsItem.set(state); revalidate(); repaint(); });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent(Graphics g) {
|
||||
g.setColor(Color.LIGHT_GRAY);
|
||||
g.fillRect(0, 0, 50, 25);
|
||||
g.setColor(Color.GREEN);
|
||||
g.fillRect(0, 0, 25, 25);
|
||||
g.setColor(state ? Color.GREEN : Color.LIGHT_GRAY);
|
||||
g.fillRoundRect(0, 0, getWidth(), getHeight(), 25, 25);
|
||||
|
||||
g.setColor(Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getInteractableBackgroundColor());
|
||||
g.fillRoundRect(state ? 25 : 0, 0, 25, 25, 25, 25);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets the state of this {@link PrimaryToggleSwitch}.
|
||||
*
|
||||
* @param state {@code true} to enable the switch, {@code false} to disable it
|
||||
* @since Envoy 0.3-alpha
|
||||
*/
|
||||
public void setState(boolean state) {
|
||||
GridBagConstraints gbc_toggleButton = new GridBagConstraints();
|
||||
|
||||
if (state) {
|
||||
gbc_toggleButton.anchor = GridBagConstraints.WEST;
|
||||
gbc_toggleButton.gridx = 0;
|
||||
} else {
|
||||
gbc_toggleButton.anchor = GridBagConstraints.EAST;
|
||||
gbc_toggleButton.gridx = 1;
|
||||
}
|
||||
gbc_toggleButton.gridy = 0;
|
||||
add(b, gbc_toggleButton);
|
||||
|
||||
currentState = state;
|
||||
}
|
||||
}
|
||||
}
|
@ -143,8 +143,7 @@ public class Startup {
|
||||
new StatusTrayIcon(chatWindow).show();
|
||||
|
||||
// If the tray icon is supported and corresponding settings is set, hide the chat window on close
|
||||
if (Settings.getInstance().getCurrentOnCloseMode())
|
||||
chatWindow.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
|
||||
Settings.getInstance().getItems().get("onCloseMode").setChangeHandler((onCloseMode) -> chatWindow.setDefaultCloseOperation((boolean) onCloseMode ? JFrame.HIDE_ON_CLOSE : JFrame.EXIT_ON_CLOSE));
|
||||
} catch (EnvoyException e) {
|
||||
logger.warning("The StatusTrayIcon is not supported on this platform!");
|
||||
}
|
||||
|
@ -1,169 +0,0 @@
|
||||
package envoy.client.ui.settings;
|
||||
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JTextPane;
|
||||
|
||||
import envoy.client.Settings;
|
||||
import envoy.client.event.*;
|
||||
import envoy.client.ui.PrimaryToggleSwitch;
|
||||
import envoy.client.ui.Theme;
|
||||
import envoy.client.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* Displays GUI components that allow general settings regarding the client.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>General.java</strong><br>
|
||||
* Created: <strong>21 Dec 2019</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public class General extends SettingsPanel {
|
||||
|
||||
private Theme theme;
|
||||
private boolean onCloseState;
|
||||
private boolean enterToSend;
|
||||
|
||||
private PrimaryToggleSwitch toggleSwitch;
|
||||
private JTextPane onCloseModeTextPane = new JTextPane();
|
||||
private JTextPane onCloseModeStatePane = new JTextPane();
|
||||
|
||||
private PrimaryToggleSwitch toggleSwitchEnterToSend;
|
||||
private JTextPane enterToSendTextPane = new JTextPane();
|
||||
private JTextPane enterToSendStatePane = new JTextPane();
|
||||
|
||||
private static final Logger logger = EnvoyLog.getLogger(General.class.getSimpleName());
|
||||
private static final long serialVersionUID = -7470848775130754239L;
|
||||
|
||||
/**
|
||||
* This is the constructor for the General class. Here the user can set general
|
||||
* settings for the client.
|
||||
*
|
||||
* @since Envoy 0.3-alpha
|
||||
*/
|
||||
public General() {
|
||||
theme = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme());
|
||||
|
||||
setBackground(theme.getCellColor());
|
||||
|
||||
GridBagLayout gbl_general = new GridBagLayout();
|
||||
gbl_general.columnWidths = new int[] { 1, 1 };
|
||||
gbl_general.rowHeights = new int[] { 1, 1, 1, 1, 1 };
|
||||
gbl_general.columnWeights = new double[] { 1.0, 0.1 };
|
||||
gbl_general.rowWeights = new double[] { 0.02, 0.0005, 0.02, 0.0005, 1.0 };
|
||||
|
||||
setLayout(gbl_general);
|
||||
|
||||
createSettingElement(0,
|
||||
OnCloseChangeEvent.class,
|
||||
Settings.getInstance().getCurrentOnCloseMode(),
|
||||
toggleSwitch,
|
||||
onCloseModeStatePane,
|
||||
onCloseModeTextPane,
|
||||
"Client runs in the background, when window is closed");
|
||||
EventBus.getInstance().register(OnCloseChangeEvent.class, (evt) -> changeOnClose(((OnCloseChangeEvent) evt).get()));
|
||||
|
||||
createSettingElement(2,
|
||||
EnterToSendEvent.class,
|
||||
Settings.getInstance().isEnterToSend(),
|
||||
toggleSwitchEnterToSend,
|
||||
enterToSendStatePane,
|
||||
enterToSendTextPane,
|
||||
"Press Enter to send messages");
|
||||
EventBus.getInstance().register(EnterToSendEvent.class, (evt) -> changeEnterToSend(((EnterToSendEvent) evt).get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method changes the on close mode of the client.
|
||||
*
|
||||
* @param state This is the integer that defines weather the toggleSwitch is on
|
||||
* or off.
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public void changeOnClose(boolean state) {
|
||||
this.onCloseState = state;
|
||||
|
||||
onCloseModeStatePane.setText(state ? "ON" : "OFF");
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method changes the enter to send a message setting.
|
||||
*
|
||||
* @param state This is the integer that defines weather the toggleSwitch is on
|
||||
* or off.
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public void changeEnterToSend(boolean state) {
|
||||
this.enterToSend = state;
|
||||
|
||||
enterToSendStatePane.setText(state ? "ON" : "OFF");
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void createSettingElement(int gridy, Class<? extends Event<Boolean>> eventClass, boolean state, PrimaryToggleSwitch toggleSwitch,
|
||||
JTextPane stateText, JTextPane descriptionText, String text) {
|
||||
toggleSwitch = new PrimaryToggleSwitch(state, eventClass);
|
||||
|
||||
GridBagConstraints gbc_toggleSwitch = new GridBagConstraints();
|
||||
gbc_toggleSwitch.gridx = 1;
|
||||
gbc_toggleSwitch.gridy = gridy;
|
||||
|
||||
add(toggleSwitch, gbc_toggleSwitch);
|
||||
|
||||
stateText.setText(state ? "ON" : "OFF");
|
||||
stateText.setBackground(theme.getCellColor());
|
||||
stateText.setForeground(theme.getUserNameColor());
|
||||
stateText.setEditable(false);
|
||||
|
||||
GridBagConstraints gbc_stateText = new GridBagConstraints();
|
||||
gbc_stateText.anchor = GridBagConstraints.NORTH;
|
||||
gbc_stateText.gridx = 1;
|
||||
gbc_stateText.gridy = gridy + 1;
|
||||
|
||||
add(stateText, gbc_stateText);
|
||||
|
||||
descriptionText.setText(text);
|
||||
descriptionText.setBackground(theme.getBackgroundColor().invert());
|
||||
descriptionText.setForeground(theme.getUserNameColor());
|
||||
descriptionText.setEditable(false);
|
||||
|
||||
GridBagConstraints gbc_descriptionText = new GridBagConstraints();
|
||||
gbc_descriptionText.fill = GridBagConstraints.BOTH;
|
||||
gbc_descriptionText.gridx = 0;
|
||||
gbc_descriptionText.gridy = gridy;
|
||||
gbc_descriptionText.gridheight = 2;
|
||||
gbc_descriptionText.insets = new Insets(5, 5, 5, 5);
|
||||
|
||||
add(descriptionText, gbc_descriptionText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionListener getOkButtonAction() {
|
||||
return (evt) -> {
|
||||
if (onCloseState != Settings.getInstance().getCurrentOnCloseMode()) try {
|
||||
Settings.getInstance().setCurrentOnCloseMode(onCloseState);
|
||||
JOptionPane.showMessageDialog(null, "The changes will take effect the next time the program is started.");
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Close mode could not be changed! ", e);
|
||||
}
|
||||
|
||||
if (enterToSend != Settings.getInstance().isEnterToSend()) try {
|
||||
Settings.getInstance().setEnterToSend(enterToSend);
|
||||
JOptionPane.showMessageDialog(null, "The changes will take effect the next time the program is started.");
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Enter to send mode could not be changed! ", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package envoy.client.ui.settings;
|
||||
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JTextPane;
|
||||
|
||||
import envoy.client.Settings;
|
||||
import envoy.client.SettingsItem;
|
||||
import envoy.client.ui.Theme;
|
||||
import envoy.client.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* Displays GUI components that allow general settings regarding the client.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>GeneralSettingsPanel.java</strong><br>
|
||||
* Created: <strong>21 Dec 2019</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public class GeneralSettingsPanel extends SettingsPanel {
|
||||
|
||||
private Theme theme;
|
||||
|
||||
private static final String[] items = { "onCloseMode", "enterToSend" };
|
||||
private static final Logger logger = EnvoyLog.getLogger(GeneralSettingsPanel.class.getSimpleName());
|
||||
private static final long serialVersionUID = -7470848775130754239L;
|
||||
|
||||
/**
|
||||
* This is the constructor for the General class. Here the user can set general
|
||||
* settings for the client.
|
||||
*
|
||||
* @since Envoy 0.3-alpha
|
||||
*/
|
||||
public GeneralSettingsPanel() {
|
||||
theme = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme());
|
||||
|
||||
setBackground(theme.getCellColor());
|
||||
|
||||
GridBagLayout gbl_general = new GridBagLayout();
|
||||
gbl_general.columnWidths = new int[] { 1, 1 };
|
||||
gbl_general.rowHeights = new int[] { 1, 1, 1 };
|
||||
gbl_general.columnWeights = new double[] { 1.0, 0.1 };
|
||||
gbl_general.rowWeights = new double[] { 0.02, 0.02, 1.0 };
|
||||
|
||||
setLayout(gbl_general);
|
||||
|
||||
for (int i = 0; i < items.length; i++)
|
||||
try {
|
||||
createSettingElement(i, Settings.getInstance().getItems().get(items[i]));
|
||||
} catch (SecurityException | ReflectiveOperationException e) {
|
||||
logger.log(Level.WARNING, "Could not create settings item", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void createSettingElement(int gridy, SettingsItem<?> settingsItem) throws SecurityException, ReflectiveOperationException {
|
||||
JTextPane descriptionText = new JTextPane();
|
||||
|
||||
JComponent settingComponent = settingsItem.getComponent();
|
||||
|
||||
GridBagConstraints gbc_toggleSwitch = new GridBagConstraints();
|
||||
gbc_toggleSwitch.gridx = 1;
|
||||
gbc_toggleSwitch.gridy = gridy;
|
||||
|
||||
add(settingComponent, gbc_toggleSwitch);
|
||||
|
||||
descriptionText.setText(settingsItem.getDescription());
|
||||
descriptionText.setBackground(theme.getBackgroundColor());
|
||||
descriptionText.setForeground(theme.getBackgroundColor().invert());
|
||||
descriptionText.setEditable(false);
|
||||
|
||||
GridBagConstraints gbc_descriptionText = new GridBagConstraints();
|
||||
gbc_descriptionText.fill = GridBagConstraints.BOTH;
|
||||
gbc_descriptionText.gridx = 0;
|
||||
gbc_descriptionText.gridy = gridy;
|
||||
gbc_descriptionText.insets = new Insets(5, 5, 5, 5);
|
||||
|
||||
add(descriptionText, gbc_descriptionText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionListener getOkButtonAction() { return (evt) -> {}; }
|
||||
}
|
@ -57,7 +57,7 @@ public class SettingsScreen extends JDialog {
|
||||
public SettingsScreen() {
|
||||
// Initialize settings pages
|
||||
Map<String, Class<? extends SettingsPanel>> panels = new HashMap<>();
|
||||
panels.put("General", General.class);
|
||||
panels.put("General", GeneralSettingsPanel.class);
|
||||
panels.put("Color Themes", ThemeCustomizationPanel.class);
|
||||
|
||||
setBounds(10, 10, 450, 650);
|
||||
|
@ -194,7 +194,8 @@ public class ThemeCustomizationPanel extends SettingsPanel {
|
||||
|
||||
button.addActionListener((evt) -> {
|
||||
try {
|
||||
Color newColor = (Color) JColorChooser.showDialog(null, "Choose a color", color);
|
||||
java.awt.Color c = JColorChooser.showDialog(null, "Choose a color", color);
|
||||
Color newColor = new Color(c.getRGB());
|
||||
if (newColor.getRGB() != color.getRGB()) {
|
||||
logger.log(Level.FINEST, "New Color: " + String.valueOf(color.getRGB()));
|
||||
// TODO: When Theme changed in same settings screen, color variable doesn't
|
||||
|
57
src/main/java/envoy/client/util/SerializationUtils.java
Normal file
57
src/main/java/envoy/client/util/SerializationUtils.java
Normal file
@ -0,0 +1,57 @@
|
||||
package envoy.client.util;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import envoy.exception.EnvoyException;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-clientChess</strong><br>
|
||||
* File: <strong>SerializationUtils.javaEvent.java</strong><br>
|
||||
* Created: <strong>23.12.2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public class SerializationUtils {
|
||||
|
||||
private SerializationUtils() {}
|
||||
|
||||
/**
|
||||
* Deserializes an arbitrary {@link Serializable} object from a file.
|
||||
*
|
||||
* @param <T> the type of the object to deserialize
|
||||
* @param file the file deserialize from
|
||||
* @param serializedClass the class of the object to deserialize
|
||||
* @return the deserialized object
|
||||
* @throws EnvoyException if an error occurred during deserialization
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public static <T extends Serializable> T read(File file, Class<T> serializedClass) throws EnvoyException {
|
||||
if (file == null) throw new NullPointerException("File is null");
|
||||
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(file))) {
|
||||
return serializedClass.cast(in.readObject());
|
||||
} catch (ClassNotFoundException | IOException e) {
|
||||
throw new EnvoyException("Could not load serialized object", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes an arbitrary object to a file.
|
||||
*
|
||||
* @param file the file to serialize to
|
||||
* @param obj the object to serialize
|
||||
* @throws IOException if an error occurred during serialization
|
||||
* @since Envoy v0.3-alpha
|
||||
*/
|
||||
public static void write(File file, Object obj) throws IOException {
|
||||
if (file == null) throw new NullPointerException("File is null");
|
||||
if (obj == null) throw new NullPointerException("Object to serialize is null");
|
||||
if (!file.exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
}
|
||||
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file))) {
|
||||
out.writeObject(obj);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user