From 6dc19ebdc558167f4cf27cff2d87b3cf16ee1c4f Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Tue, 22 Oct 2019 09:24:56 +0300 Subject: [PATCH] Add themes Update UI --- pom.xml | 2 +- .../java/logiledus/About/AboutWindow.java | 6 +- src/main/java/logiledus/AppPreferences.java | 10 +- src/main/java/logiledus/MainFx.java | 12 +- src/main/java/logiledus/Mediator.java | 8 + .../Settings/SettingsController.java | 12 +- .../logiledus/Settings/SettingsWindow.java | 5 +- src/main/resources/EffectsPane.fxml | 9 +- src/main/resources/Main.fxml | 116 +++---- src/main/resources/SettingsLayout.fxml | 3 +- src/main/resources/dark.css | 299 ++++++++++++++++++ src/main/resources/light.css | 31 +- src/main/resources/locale.properties | 1 + src/main/resources/locale_rus.properties | 1 + src/main/resources/pttrn_drk.png | Bin 0 -> 8552 bytes 15 files changed, 422 insertions(+), 93 deletions(-) create mode 100644 src/main/resources/dark.css create mode 100644 src/main/resources/pttrn_drk.png diff --git a/pom.xml b/pom.xml index b31178b..22f7453 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ loper LogiLedus - 1.0-SNAPSHOT + 1.1-SNAPSHOT diff --git a/src/main/java/logiledus/About/AboutWindow.java b/src/main/java/logiledus/About/AboutWindow.java index 9ba36ca..fb07ccc 100644 --- a/src/main/java/logiledus/About/AboutWindow.java +++ b/src/main/java/logiledus/About/AboutWindow.java @@ -5,6 +5,7 @@ import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.stage.Stage; +import logiledus.Mediator; import java.io.IOException; import java.util.Locale; @@ -35,8 +36,9 @@ public class AboutWindow { new Image(getClass().getResourceAsStream("/ico/appIcon_64.png")), new Image(getClass().getResourceAsStream("/ico/appIcon_128.png")) ); - stageAbout.setScene(new Scene(parentAbout, 500, 500)); - + Scene scene = new Scene(parentAbout, 500, 500); + scene.getStylesheets().add(Mediator.getInstance().getPreferences().getTheme()); + stageAbout.setScene(scene); stageAbout.show(); } catch (IOException ignored){} diff --git a/src/main/java/logiledus/AppPreferences.java b/src/main/java/logiledus/AppPreferences.java index fdc8ca4..bb8b58b 100644 --- a/src/main/java/logiledus/AppPreferences.java +++ b/src/main/java/logiledus/AppPreferences.java @@ -14,12 +14,6 @@ public class AppPreferences { public void setUseTray(boolean value){ preferences.putBoolean("USE_TRAY", value); } public boolean getUseTray(){ return preferences.getBoolean("USE_TRAY", true); } -/* - public void setPath(String path){ - preferences.put("PATH", path); - } - public String getPath(){ - return preferences.get("PATH", "/"); - } -*/ + public void setTheme(String value){ preferences.put("THEME", value); } + public String getTheme(){ return preferences.get("THEME", "/light.css"); } } diff --git a/src/main/java/logiledus/MainFx.java b/src/main/java/logiledus/MainFx.java index ccb295b..4beaec9 100644 --- a/src/main/java/logiledus/MainFx.java +++ b/src/main/java/logiledus/MainFx.java @@ -16,7 +16,7 @@ import java.util.Locale; import java.util.ResourceBundle; public class MainFx extends Application { - public static final String appVersion = "v1.0"; + public static final String appVersion = "v1.1"; private static boolean traySupport = true; @@ -38,9 +38,6 @@ public class MainFx extends Application { SwingUtilities.invokeLater(this::addAppToTray); } //-------------------------------------------------------- - Mediator.getInstance().setHostServices(getHostServices()); - Mediator.getInstance().setPreferences(appPreferences); - FXMLLoader loader = new FXMLLoader(getClass().getResource("/Main.fxml")); Locale locale = new Locale(Locale.getDefault().getISO3Language()); @@ -78,7 +75,12 @@ public class MainFx extends Application { primaryStage.setMinWidth(1215); primaryStage.setMinHeight(550); Scene mainScene = new Scene(root, 1215, 525); - mainScene.getStylesheets().add("/light.css"); + mainScene.getStylesheets().add(appPreferences.getTheme()); + + Mediator.getInstance().setHostServices(getHostServices()); + Mediator.getInstance().setPreferences(appPreferences); + Mediator.getInstance().setScene(mainScene); + primaryStage.setScene(mainScene); primaryStage.show(); diff --git a/src/main/java/logiledus/Mediator.java b/src/main/java/logiledus/Mediator.java index b1f20a8..2c471d7 100644 --- a/src/main/java/logiledus/Mediator.java +++ b/src/main/java/logiledus/Mediator.java @@ -1,16 +1,19 @@ package logiledus; import javafx.application.HostServices; +import javafx.scene.Scene; public class Mediator{ private HostServices hostServices; private AppPreferences preferences; + private Scene scene; public static Mediator getInstance(){ return MediatorHolder.INSTANCE; } public void setHostServices(HostServices hostServices){ this.hostServices = hostServices; } public void setPreferences(AppPreferences preferences){ this.preferences = preferences; } + public void setScene(Scene scene){ this.scene = scene; } private static class MediatorHolder{ private static final Mediator INSTANCE = new Mediator(); @@ -18,4 +21,9 @@ public class Mediator{ public HostServices getHostServices() { return hostServices; } public AppPreferences getPreferences() { return preferences; } + + public void setTheme(String themeString){ + scene.getStylesheets().remove(0); + scene.getStylesheets().add(themeString); + } } \ No newline at end of file diff --git a/src/main/java/logiledus/Settings/SettingsController.java b/src/main/java/logiledus/Settings/SettingsController.java index 5d24f2d..3992ea2 100644 --- a/src/main/java/logiledus/Settings/SettingsController.java +++ b/src/main/java/logiledus/Settings/SettingsController.java @@ -16,16 +16,26 @@ public class SettingsController implements Initializable { private Button cancelBtn, okBtn; @FXML - private CheckBox trayCB; + private CheckBox trayCB, drkThemeCB; @Override public void initialize(URL url, ResourceBundle resourceBundle) { trayCB.setSelected(Mediator.getInstance().getPreferences().getUseTray()); + if (Mediator.getInstance().getPreferences().getTheme().equals("/dark.css")) + drkThemeCB.setSelected(true); cancelBtn.setOnAction(actionEvent -> ((Stage) cancelBtn.getScene().getWindow()).close()); okBtn.setOnAction(actionEvent -> { Mediator.getInstance().getPreferences().setUseTray(trayCB.isSelected()); + if (drkThemeCB.isSelected()) { + Mediator.getInstance().getPreferences().setTheme("/dark.css"); + Mediator.getInstance().setTheme("/dark.css"); + } + else { + Mediator.getInstance().getPreferences().setTheme("/light.css"); + Mediator.getInstance().setTheme("/light.css"); + } ((Stage) cancelBtn.getScene().getWindow()).close(); }); diff --git a/src/main/java/logiledus/Settings/SettingsWindow.java b/src/main/java/logiledus/Settings/SettingsWindow.java index ed02c3f..7afade4 100644 --- a/src/main/java/logiledus/Settings/SettingsWindow.java +++ b/src/main/java/logiledus/Settings/SettingsWindow.java @@ -5,6 +5,7 @@ import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.stage.Stage; +import logiledus.Mediator; import java.io.IOException; import java.util.Locale; @@ -35,7 +36,9 @@ public class SettingsWindow { new Image(getClass().getResourceAsStream("/ico/appIcon_64.png")), new Image(getClass().getResourceAsStream("/ico/appIcon_128.png")) ); - stageAbout.setScene(new Scene(parentAbout, 500, 500)); + Scene scene = new Scene(parentAbout, 500, 500); + scene.getStylesheets().add(Mediator.getInstance().getPreferences().getTheme()); + stageAbout.setScene(scene); stageAbout.show(); diff --git a/src/main/resources/EffectsPane.fxml b/src/main/resources/EffectsPane.fxml index ce26635..f515bd0 100644 --- a/src/main/resources/EffectsPane.fxml +++ b/src/main/resources/EffectsPane.fxml @@ -1,16 +1,9 @@ - - - - - - - + - diff --git a/src/main/resources/Main.fxml b/src/main/resources/Main.fxml index 36e15ba..f00341a 100644 --- a/src/main/resources/Main.fxml +++ b/src/main/resources/Main.fxml @@ -9,6 +9,7 @@ + @@ -33,7 +34,7 @@ - + @@ -41,7 +42,7 @@ - + @@ -51,7 +52,7 @@ - + @@ -71,57 +72,64 @@ - - - - - - - - - + + + + + + + + + + diff --git a/src/main/resources/SettingsLayout.fxml b/src/main/resources/SettingsLayout.fxml index 0bca1f6..36b5d57 100644 --- a/src/main/resources/SettingsLayout.fxml +++ b/src/main/resources/SettingsLayout.fxml @@ -17,9 +17,10 @@
- + +
diff --git a/src/main/resources/dark.css b/src/main/resources/dark.css new file mode 100644 index 0000000..0a866e2 --- /dev/null +++ b/src/main/resources/dark.css @@ -0,0 +1,299 @@ +.root{ + -fx-background: #2b2b2b; +} + +.tool-bar{ + -fx-background-color: #2b2b2b; +} +/* -======================== Buttons =====================- */ +.button SVGPath{ + -fx-fill: #ebebeb; +} +.button, .toggle-button, .menu-button { + -fx-background-color: #3c3f41; + -fx-background-insets: 0 0 0 0, 0, 1, 2; + -fx-background-radius: 2; + -fx-border-color: #47ca60; + -fx-border-radius: 2; + -fx-border-width: 2; + -fx-text-fill: #ebebeb; + //-fx-effect: dropshadow(three-pass-box, #dadada, 2, 0, 0, 0); +} +.menu-button:hover, .button:hover, .choice-box:hover, .menu-button:focused:hover, .button:focused:hover, .choice-box:focused:hover, .toggle-button:hover, .toggle-button:focused:hover{ + -fx-background-color: #3c3f41; + -fx-background-insets: 0 0 0 0, 0, 1, 2; + -fx-background-radius: 2; + -fx-border-color: #4a81dd; + -fx-border-radius: 2; + -fx-border-width: 2; + -fx-text-fill: #ebebeb; + //-fx-effect: dropshadow(three-pass-box, #4a81dd, 2, 0, 0, 0); +} +.menu-button:focused, .button:focused, .choice-box:focused, .toggle-button:focused{ + -fx-background-color: #4b4e51; + -fx-background-insets: 0 0 0 0, 0, 1, 2; + -fx-background-radius: 2; + -fx-border-color: #4c7352; + -fx-border-radius: 2; + -fx-border-width: 2; + -fx-text-fill: #ebebeb; + //-fx-effect: dropshadow(three-pass-box, #dadada, 2, 0, 0, 0); +} + +.menu-button:pressed, .button:pressed, .menu-button:pressed:hover, .button:pressed:hover, .toggle-button:pressed, .toggle-button:pressed:hover{ + -fx-background-color: #3c3f41; + -fx-background-insets: 0 0 0 0, 0, 1, 2; + -fx-background-radius: 2; + -fx-border-color: #e82382; + -fx-border-radius: 2; + -fx-border-width: 2; + -fx-text-fill: #ebebeb; + //-fx-effect: dropshadow(three-pass-box, #e82382, 2, 0, 0, 0); +} +/* -======================== Buttons keys =====================- */ +.toggle-button:selected, .toggle-button:selected:hover{ + -fx-background-color: #214131; + -fx-background-insets: 0 0 0 0, 0, 1, 2; + -fx-background-radius: 8; + -fx-border-color: #ccfed1; + -fx-border-radius: 8; + -fx-border-width: 1; + -fx-text-fill: #ebebeb; + //-fx-effect: dropshadow(three-pass-box, #dadada, 2, 0, 0, 0); +} + +.button_keycap, .toggle-button_keycap { + -fx-background-color: #3c3f41; + -fx-background-radius: 8; + -fx-background-insets: 0 0 0 0, 0, 1, 2; + -fx-border-color: #cfcfcf; + -fx-border-radius: 8; + -fx-border-width: 1; + -fx-text-fill: #ebebeb; + -fx-effect: dropshadow(three-pass-box, #8f8f8f, 1, 0, 0, 0); +} +.button_keycap:hover, .button_keycap:focused:hover, .toggle-button_keycap:hover, .toggle-button_keycap:focused:hover{ + -fx-background-color: #3c3f41; + -fx-background-insets: 0 0 0 0, 0, 1, 2; + -fx-border-color: #4a81dd; + -fx-border-radius: 8; + -fx-background-radius: 8; + -fx-border-width: 1; + -fx-text-fill: #ebebeb; + -fx-effect: dropshadow(three-pass-box, #4a81dd, 2, 0, 0, 0); +} +.button_keycap:focused, .toggle-button_keycap:focused{ + -fx-background-color: #dadada; + -fx-background-insets: 0 0 0 0, 0, 1, 2; + -fx-border-color: #8f8f8f; + -fx-border-radius: 8; + -fx-background-radius: 8; + -fx-border-width: 1; + -fx-text-fill: #ebebeb; + -fx-effect: dropshadow(three-pass-box, #8f8f8f, 1, 0, 0, 0); +} + +.button_keycap:pressed, .button_keycap:pressed:hover, .toggle-button_keycap:pressed, .toggle-button_keycap:pressed:hover{ + -fx-background-color: #3c3f41; + -fx-background-insets: 0 0 0 0, 0, 1, 2; + -fx-border-color: #e82382; + -fx-border-radius: 8; + -fx-background-radius: 8; + -fx-border-width: 1; + -fx-text-fill: #ebebeb; + -fx-effect: dropshadow(three-pass-box, #e82382, 2, 0, 0, 0); +} + +.toggle-button_keycap:selected, .toggle-button_keycap:selected:hover{ + -fx-background-color: #214131; + -fx-background-insets: 0 0 0 0, 0, 1, 2; + -fx-border-color: #ccfed1; + -fx-border-radius: 8; + -fx-background-radius: 8; + -fx-border-width: 1; + -fx-text-fill: #ebebeb; + -fx-effect: none; +} + +.always_off_button:disabled, .always_off_button:disabled:selected { + -fx-opacity: 1.0; + -fx-background-color: #412441; + -fx-background-insets: 0 0 0 0, 0, 1, 2; + -fx-border-color: #f1cbf1; + -fx-border-radius: 8; + -fx-background-radius: 8; + -fx-border-width: 1; + -fx-text-fill: #ebebeb; + -fx-effect: dropshadow(three-pass-box, #f1cbf1, 1, 0, 0, 0); +} +/* -======================== TextArea =====================- */ +.text-area{ + -fx-background-color: transparent; + -fx-control-inner-background: #3c3f41; + -fx-border-color: #06b9bb; + -fx-border-radius: 3; + -fx-border-width: 1; + -fx-text-fill: #ebebeb; +} + +.progress-bar { + -fx-background-color: transparent; + -fx-box-border: transparent; +} +.progress-bar > .track { + -fx-background-color: transparent; + -fx-box-border: transparent; +} +.progress-bar > .bar { + -fx-background-color: linear-gradient(to right, #00bce4, #ff5f53); + -fx-background-radius: 2; + -fx-background-insets: 1 1 2 1; + -fx-padding: 0.23em; +} + +.dialog-pane { + -fx-background-color: #3c3f41; +} +.dialog-pane > .button-bar > .container{ + -fx-background-color: #2b2b2b; +} + +.dialog-pane > .label{ + -fx-padding: 10 5 10 5; +} + +/* -======================== ToolBar =========================- */ +/* +.tool-bar{ + -fx-background-color: transparent; +} +*/ +/* -======================== Choice box =========================- */ +.choice-box { + -fx-background-color: #3c3f41; + -fx-border-color: #3c3f41; + -fx-border-radius: 3; + -fx-border-width: 2; + -fx-mark-color: #eea11e; + -fx-effect: dropshadow(three-pass-box, #b4b4b4, 2, 0, 0, 0); +} +.choice-box > .label { + -fx-text-fill: #ebebeb; +} + +.choice-box:pressed, .choice-box:pressed:hover{ + -fx-background-color: #3c3f41; + -fx-border-color: #eea11e; + -fx-border-radius: 3; + -fx-border-width: 2; + -fx-text-fill: #ebebeb; + -fx-effect: dropshadow(three-pass-box, #eea11e, 2, 0, 0, 0); +} + +// Background color of the whole context menu +.choice-box .context-menu { + -fx-background-color: #3c3f41; +} + +// Focused item background color in the list +.choice-box .context-menu .menu-item:focused { + -fx-background-color: #eea11e; + +} +.choice-box .context-menu .menu-item:focused .label { + -fx-text-fill: #ebebeb; +} + +/* -======================== TAB PANE =========================- */ +.tab-pane .tab SVGPath{ + -fx-fill: #ebebeb; +} +.tab-pane .tab:selected SVGPath, .tab-pane .tab:selected:hover SVGPath{ + -fx-fill: #ebebeb; +} +.tab-pane .tab:hover SVGPath{ + -fx-fill: #d0d0d0; +} +.tab-pane .tab{ + -fx-background-color: #3c3f41; /* #2b2b2b; */ + -fx-focus-color: transparent; + -fx-faint-focus-color: transparent; + -fx-border-radius: 0 0 0 0; + -fx-border-width: 0 0 3 0; + -fx-border-color: #2b2b2b; +} + +.tab-pane .tab:selected{ + -fx-background-color: #3c3f41;/*#2b2b2b; */ + -fx-focus-color: transparent; + -fx-faint-focus-color: transparent; + -fx-border-radius: 0 0 0 0; + -fx-border-width: 0 0 3 0; + -fx-border-color: #40b9bb; /* #289de8; */ +} + +.tab-pane > .tab-header-area { + -fx-background-insets: 0.0; + -fx-padding: 5 5 5 5; +} + +.tab-pane > .tab-header-area > .tab-header-background +{ + -fx-background-color: #2b2b2b; + +} +.tab-pane > .tab-header-area > .headers-region > .tab { + -fx-padding: 10; +} +/* -========================== Context menu =====================- */ +.menu-button .label { + -fx-text-fill: #ebebeb; +} + +.menu-button .arrow { + -fx-background-color: #ebebeb; +} + +.context-menu { + -fx-background-color: #3c3f41; + -fx-cursor: hand; +} +.context-menu .menu-item .label { + -fx-text-fill: #ebebeb; +} +.context-menu .menu-item:focused .label { + -fx-text-fill: white; +} +/* -========================== Text Field =====================- */ +.text-field { + -fx-border-color: #289de8; + -fx-border-width: 0 0 1 0; + -fx-background-color: transparent; + -fx-text-fill: #ebebeb; +} +.text-field:focused { + -fx-border-color: #e82382; + -fx-border-width: 0 0 1 0; + -fx-background-color: transparent; + -fx-text-fill: #e82382; +} +/* -========================== footer pane =====================- */ +.footer{ + -fx-background-color: #3c3f41; + -fx-border-color: #ebebeb; + -fx-border-width: 1 0 0 0; +} +/* -========================== header pane =====================- */ +.header { + -fx-background-image: url("pttrn_drk.png"); + -fx-background-position: center; + -fx-background-repeat: repeat; + -fx-border-color: #b5b5b5; + -fx-border-width: 0 0 1 0; +} +.header-box { + -fx-background-color: #3c3f41; + -fx-background-insets: 0 0 0 0, 0, 1, 2; + -fx-background-radius: 2; + -fx-padding: 3.5; +} \ No newline at end of file diff --git a/src/main/resources/light.css b/src/main/resources/light.css index 032ad67..005a434 100644 --- a/src/main/resources/light.css +++ b/src/main/resources/light.css @@ -46,18 +46,18 @@ -fx-text-fill: #2c2c2c; //-fx-effect: dropshadow(three-pass-box, #e82382, 2, 0, 0, 0); } - +/* -======================== Buttons keys =====================- */ .toggle-button:selected, .toggle-button:selected:hover{ -fx-background-color: #e1feeb; -fx-background-insets: 0 0 0 0, 0, 1, 2; - -fx-background-radius: 2; + -fx-background-radius: 8; -fx-border-color: #ccfed1; - -fx-border-radius: 2; - -fx-border-width: 2; + -fx-border-radius: 8; + -fx-border-width: 1; -fx-text-fill: #2c2c2c; - //-fx-effect: dropshadow(three-pass-box, #dadada, 2, 0, 0, 0); + -fx-effect: dropshadow(three-pass-box, #dadada, 2, 0, 0, 0); } -/* -======================== Buttons keys =====================- */ + .button_keycap, .toggle-button_keycap { -fx-background-color: #fefefe; -fx-background-radius: 8; @@ -108,7 +108,7 @@ -fx-background-radius: 8; -fx-border-width: 1; -fx-text-fill: #2c2c2c; - -fx-effect: none; + -fx-effect: dropshadow(three-pass-box, #e82382, 2, 0, 0, 0); } .always_off_button:disabled, .always_off_button:disabled:selected { @@ -128,7 +128,7 @@ -fx-control-inner-background: #fefefe; -fx-border-color: #06b9bb; -fx-border-radius: 3; - -fx-border-width: 2; + -fx-border-width: 1; -fx-text-fill: #2c2c2c; } @@ -215,16 +215,16 @@ -fx-focus-color: transparent; -fx-faint-focus-color: transparent; -fx-border-radius: 0 0 0 0; - -fx-border-width: 3 0 0 0; + -fx-border-width: 0 0 3 0; -fx-border-color: #fefefe; } .tab-pane .tab:selected{ - -fx-background-color: #ebebeb; + -fx-background-color: #fefefe; -fx-focus-color: transparent; -fx-faint-focus-color: transparent; -fx-border-radius: 0 0 0 0; - -fx-border-width: 3 0 0 0; + -fx-border-width: 0 0 3 0; -fx-border-color: #40b9bb; /* #289de8; */ } @@ -235,7 +235,7 @@ .tab-pane > .tab-header-area > .tab-header-background { - -fx-background-color: #fefefe; + -fx-background-color: #ebebeb; } .tab-pane > .tab-header-area > .headers-region > .tab { @@ -278,4 +278,11 @@ -fx-background-repeat: repeat; -fx-border-color: #b5b5b5; -fx-border-width: 0 0 1 0; +} + +.header-box { + -fx-background-color: #ebebeb; + -fx-background-insets: 0 0 0 0, 0, 1, 2; + -fx-background-radius: 2; + -fx-padding: 3.5; } \ No newline at end of file diff --git a/src/main/resources/locale.properties b/src/main/resources/locale.properties index 419f3d5..277b487 100644 --- a/src/main/resources/locale.properties +++ b/src/main/resources/locale.properties @@ -705,3 +705,4 @@ setting_tray_support=Minimize to tray setting_save_and_close=Save and close setting_cancel=Cancel open=Open +setting_use_dark_theme=Use dark theme diff --git a/src/main/resources/locale_rus.properties b/src/main/resources/locale_rus.properties index 5a0e9c1..5268bef 100644 --- a/src/main/resources/locale_rus.properties +++ b/src/main/resources/locale_rus.properties @@ -31,3 +31,4 @@ setting_tray_support=\u0421\u0432\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u044 setting_cancel=\u041E\u0442\u043C\u0435\u043D\u0438\u0442\u044C setting_save_and_close=\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u0438 \u0437\u0430\u043A\u0440\u044B\u0442\u044C open=\u041E\u0442\u043A\u0440\u044B\u0442\u044C +setting_use_dark_theme=\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u0442\u0451\u043C\u043D\u0443\u044E \u0442\u0435\u043C\u0443 diff --git a/src/main/resources/pttrn_drk.png b/src/main/resources/pttrn_drk.png new file mode 100644 index 0000000000000000000000000000000000000000..4e6fee5a55123969e45db71f575a8408b52e579f GIT binary patch literal 8552 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 i6je{BZE1