From 0f3bdf50e92d596823c20d912aa87e6fc0c66381 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Mon, 26 May 2025 08:38:27 -0400 Subject: [PATCH] add light/dark mode toggle to footer --- .../resources/i18n/displayStrings.properties | 1 + .../i18n/displayStrings_cs.properties | 1 + .../i18n/displayStrings_de.properties | 1 + .../i18n/displayStrings_es.properties | 1 + .../i18n/displayStrings_fa.properties | 3 +- .../i18n/displayStrings_fr.properties | 1 + .../i18n/displayStrings_it.properties | 1 + .../i18n/displayStrings_ja.properties | 1 + .../i18n/displayStrings_pt-br.properties | 1 + .../i18n/displayStrings_pt.properties | 1 + .../i18n/displayStrings_ru.properties | 3 +- .../i18n/displayStrings_th.properties | 3 +- .../i18n/displayStrings_tr.properties | 1 + .../i18n/displayStrings_vi.properties | 3 +- .../i18n/displayStrings_zh-hans.properties | 1 + .../i18n/displayStrings_zh-hant.properties | 1 + .../src/main/java/haveno/desktop/images.css | 10 +++++- .../java/haveno/desktop/main/MainView.java | 34 +++++++++++++++--- .../src/main/resources/images/dark_mode.png | Bin 0 -> 1760 bytes .../src/main/resources/images/light_mode.png | Bin 0 -> 2013 bytes 20 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 desktop/src/main/resources/images/dark_mode.png create mode 100644 desktop/src/main/resources/images/light_mode.png diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 9ac374865f..a0fd2962b1 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -1334,6 +1334,7 @@ setting.preferences.displayOptions=Display options setting.preferences.showOwnOffers=Show my own offers in offer book setting.preferences.useAnimations=Use animations setting.preferences.useDarkMode=Use dark mode +setting.preferences.useLightMode=Use light mode setting.preferences.sortWithNumOffers=Sort market lists with no. of offers/trades setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index c41cdb2e42..53658b4038 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -1297,6 +1297,7 @@ setting.preferences.displayOptions=Zobrazit možnosti setting.preferences.showOwnOffers=Zobrazit mé vlastní nabídky v seznamu nabídek setting.preferences.useAnimations=Použít animace setting.preferences.useDarkMode=Použít tmavý režim +setting.preferences.useLightMode=Použijte světlý režim setting.preferences.sortWithNumOffers=Seřadit seznamy trhů s počtem nabídek/obchodů setting.preferences.onlyShowPaymentMethodsFromAccount=Skrýt nepodporované způsoby platby setting.preferences.denyApiTaker=Odmítat příjemce, kteří používají API diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 97f21244c7..57fd93df67 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -1033,6 +1033,7 @@ setting.preferences.displayOptions=Darstellungsoptionen setting.preferences.showOwnOffers=Eigenen Angebote im Angebotsbuch zeigen setting.preferences.useAnimations=Animationen abspielen setting.preferences.useDarkMode=Nacht-Modus benutzen +setting.preferences.useLightMode=Leichtmodus verwenden setting.preferences.sortWithNumOffers=Marktlisten nach Anzahl der Angebote/Trades sortieren setting.preferences.onlyShowPaymentMethodsFromAccount=Nicht unterstützte Zahlungsmethoden ausblenden setting.preferences.denyApiTaker=Taker die das API nutzen vermeiden diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index c89963e47a..b5754592dd 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -1034,6 +1034,7 @@ setting.preferences.displayOptions=Mostrar opciones setting.preferences.showOwnOffers=Mostrar mis propias ofertas en el libro de ofertas setting.preferences.useAnimations=Usar animaciones setting.preferences.useDarkMode=Usar modo oscuro +setting.preferences.useLightMode=Usar modo claro setting.preferences.sortWithNumOffers=Ordenar listas de mercado por número de ofertas/intercambios setting.preferences.onlyShowPaymentMethodsFromAccount=Ocultar métodos de pago no soportados setting.preferences.denyApiTaker=Denegar tomadores usando la misma API diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index 9ec78cebe3..44693d7e01 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -1029,7 +1029,8 @@ setting.preferences.addCrypto=افزودن آلتکوین setting.preferences.displayOptions=نمایش گزینه‌ها setting.preferences.showOwnOffers=نمایش پیشنهادهای من در دفتر پیشنهاد setting.preferences.useAnimations=استفاده از انیمیشن‌ها -setting.preferences.useDarkMode=Use dark mode +setting.preferences.useDarkMode=حالت تاریک را استفاده کنید +setting.preferences.useLightMode=حالت روشن را استفاده کنید setting.preferences.sortWithNumOffers=مرتب سازی لیست‌ها با تعداد معاملات/پیشنهادها setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index 1e8a08394b..478ab4c6c3 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -1035,6 +1035,7 @@ setting.preferences.displayOptions=Afficher les options setting.preferences.showOwnOffers=Montrer mes ordres dans le livre des ordres setting.preferences.useAnimations=Utiliser des animations setting.preferences.useDarkMode=Utiliser le mode sombre +setting.preferences.useLightMode=Utiliser le mode clair setting.preferences.sortWithNumOffers=Trier les listes de marché avec le nombre d'ordres/de transactions setting.preferences.onlyShowPaymentMethodsFromAccount=Masquer les méthodes de paiement non supportées setting.preferences.denyApiTaker=Refuser les preneurs utilisant l'API diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index fb651c108e..784b65234f 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -1032,6 +1032,7 @@ setting.preferences.displayOptions=Mostra opzioni setting.preferences.showOwnOffers=Mostra le mie offerte nel libro delle offerte setting.preferences.useAnimations=Usa animazioni setting.preferences.useDarkMode=Usa modalità notte +setting.preferences.useLightMode=Usa la modalità chiara setting.preferences.sortWithNumOffers=Ordina le liste di mercato con n. di offerte/scambi setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index 13c47be899..ccc24ab657 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -1033,6 +1033,7 @@ setting.preferences.displayOptions=表示設定 setting.preferences.showOwnOffers=オファーブックに自分のオファーを表示 setting.preferences.useAnimations=アニメーションを使用 setting.preferences.useDarkMode=ダークモードを利用 +setting.preferences.useLightMode=ライトモードを使用する setting.preferences.sortWithNumOffers=市場リストをオファー/トレードの数で並び替える setting.preferences.onlyShowPaymentMethodsFromAccount=サポートされていない支払い方法を非表示にする setting.preferences.denyApiTaker=APIを使用するテイカーを拒否する diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index bf78a69196..c28a530456 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -1034,6 +1034,7 @@ setting.preferences.displayOptions=Opções de exibição setting.preferences.showOwnOffers=Exibir minhas ofertas no livro de ofertas setting.preferences.useAnimations=Usar animações setting.preferences.useDarkMode=Usar modo escuro +setting.preferences.useLightMode=Usar modo claro setting.preferences.sortWithNumOffers=Ordenar pelo nº de ofertas/negociações setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index fd4bbf5c22..d14b41668c 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -1031,6 +1031,7 @@ setting.preferences.displayOptions=Mostrar opções setting.preferences.showOwnOffers=Mostrar as minhas próprias ofertas no livro de ofertas setting.preferences.useAnimations=Usar animações setting.preferences.useDarkMode=Usar o modo escuro +setting.preferences.useLightMode=Usar modo claro setting.preferences.sortWithNumOffers=Ordenar listas de mercado por nº de ofertas/negociações: setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index 9a4f76f46d..848b765ae2 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -1029,7 +1029,8 @@ setting.preferences.addCrypto=Добавить альткойн setting.preferences.displayOptions=Параметры отображения setting.preferences.showOwnOffers=Показать мои предложения в списке предложений setting.preferences.useAnimations=Использовать анимацию -setting.preferences.useDarkMode=Use dark mode +setting.preferences.useDarkMode=Использовать тёмный режим +setting.preferences.useLightMode=Использовать светлый режим setting.preferences.sortWithNumOffers=Сортировать списки по кол-ву предложений/сделок setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index 8c4d557150..df37fc3f28 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -1029,7 +1029,8 @@ setting.preferences.addCrypto=เพิ่ม crypto setting.preferences.displayOptions=แสดงตัวเลือกเพิ่มเติม setting.preferences.showOwnOffers=แสดงข้อเสนอของฉันเองในสมุดข้อเสนอ setting.preferences.useAnimations=ใช้ภาพเคลื่อนไหว -setting.preferences.useDarkMode=Use dark mode +setting.preferences.useDarkMode=ใช้โหมดมืด +setting.preferences.useLightMode=ใช้โหมดสว่าง setting.preferences.sortWithNumOffers=จัดเรียงรายการโดยเลขของข้อเสนอ / การซื้อขาย setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index 482654f4d7..9b7f12e466 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -1293,6 +1293,7 @@ setting.preferences.displayOptions=Görüntüleme seçenekleri setting.preferences.showOwnOffers=Teklif defterinde kendi tekliflerini göster setting.preferences.useAnimations=Animasyonları kullan setting.preferences.useDarkMode=Karanlık modu kullan +setting.preferences.useLightMode=Aydınlık modu kullan setting.preferences.sortWithNumOffers=Piyasaları teklif sayısına göre sırala setting.preferences.onlyShowPaymentMethodsFromAccount=Olmayan ödeme yöntemlerini gizle setting.preferences.denyApiTaker=API kullanan alıcıları reddet diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index c76bfaf37c..52854a4208 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -1031,7 +1031,8 @@ setting.preferences.addCrypto=Bổ sung crypto setting.preferences.displayOptions=Hiển thị các phương án setting.preferences.showOwnOffers=Hiển thị Báo giá của tôi trong danh mục Báo giá setting.preferences.useAnimations=Sử dụng hoạt ảnh -setting.preferences.useDarkMode=Use dark mode +setting.preferences.useDarkMode=Sử dụng chế độ tối +setting.preferences.useLightMode=Sử dụng chế độ sáng setting.preferences.sortWithNumOffers=Sắp xếp danh sách thị trường với số chào giá/giao dịch setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index fc3e881bda..7e6b9be4d5 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -1033,6 +1033,7 @@ setting.preferences.displayOptions=显示选项 setting.preferences.showOwnOffers=在报价列表中显示我的报价 setting.preferences.useAnimations=使用动画 setting.preferences.useDarkMode=使用夜间模式 +setting.preferences.useLightMode=使用浅色模式 setting.preferences.sortWithNumOffers=使用“报价ID/交易ID”筛选列表 setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index 17b1728157..a1694e3999 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -1033,6 +1033,7 @@ setting.preferences.displayOptions=顯示選項 setting.preferences.showOwnOffers=在報價列表中顯示我的報價 setting.preferences.useAnimations=使用動畫 setting.preferences.useDarkMode=使用夜間模式 +setting.preferences.useLightMode=使用淺色模式 setting.preferences.sortWithNumOffers=使用“報價ID/交易ID”篩選列表 setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API diff --git a/desktop/src/main/java/haveno/desktop/images.css b/desktop/src/main/java/haveno/desktop/images.css index 5d3272d46d..0cb132a340 100644 --- a/desktop/src/main/java/haveno/desktop/images.css +++ b/desktop/src/main/java/haveno/desktop/images.css @@ -363,4 +363,12 @@ #image-xmr-logo { -fx-image: url("../../images/xmr_logo.png"); -} \ No newline at end of file +} + +#image-dark-mode { + -fx-image: url("../../images/dark_mode.png"); +} + +#image-light-mode { + -fx-image: url("../../images/light_mode.png"); +} diff --git a/desktop/src/main/java/haveno/desktop/main/MainView.java b/desktop/src/main/java/haveno/desktop/main/MainView.java index 6efce70112..efb6b2e152 100644 --- a/desktop/src/main/java/haveno/desktop/main/MainView.java +++ b/desktop/src/main/java/haveno/desktop/main/MainView.java @@ -32,6 +32,7 @@ import haveno.core.locale.GlobalSettings; import haveno.core.locale.LanguageUtil; import haveno.core.locale.Res; import haveno.core.provider.price.MarketPrice; +import haveno.core.user.Preferences; import haveno.desktop.Navigation; import haveno.desktop.common.view.CachingViewLoader; import haveno.desktop.common.view.FxmlView; @@ -127,6 +128,7 @@ public class MainView extends InitializableView { private Label xmrSplashInfo; private Popup p2PNetworkWarnMsgPopup, xmrNetworkWarnMsgPopup; private final TorNetworkSettingsWindow torNetworkSettingsWindow; + private final Preferences preferences; private static final int networkIconSize = 20; public static StackPane getRootContainer() { @@ -154,12 +156,14 @@ public class MainView extends InitializableView { CachingViewLoader viewLoader, Navigation navigation, Transitions transitions, - TorNetworkSettingsWindow torNetworkSettingsWindow) { + TorNetworkSettingsWindow torNetworkSettingsWindow, + Preferences preferences) { super(model); this.viewLoader = viewLoader; this.navigation = navigation; MainView.transitions = transitions; this.torNetworkSettingsWindow = torNetworkSettingsWindow; + this.preferences = preferences; } @Override @@ -758,13 +762,33 @@ public class MainView extends InitializableView { setRightAnchor(versionBox, 10d); setBottomAnchor(versionBox, 7d); + // Dark mode toggle + ImageView useDarkModeIcon = new ImageView(); + useDarkModeIcon.setId(preferences.getCssTheme() == 1 ? "image-dark-mode" : "image-light-mode"); + useDarkModeIcon.setFitHeight(networkIconSize); + useDarkModeIcon.setPreserveRatio(true); + useDarkModeIcon.setPickOnBounds(true); + useDarkModeIcon.setCursor(Cursor.HAND); + setRightAnchor(useDarkModeIcon, 8d); + setBottomAnchor(useDarkModeIcon, 6d); + Tooltip modeToolTip = new Tooltip(); + Tooltip.install(useDarkModeIcon, modeToolTip); + useDarkModeIcon.setOnMouseEntered(e -> modeToolTip.setText(Res.get(preferences.getCssTheme() == 1 ? "setting.preferences.useLightMode" : "setting.preferences.useDarkMode"))); + useDarkModeIcon.setOnMouseClicked(e -> { + preferences.setCssTheme(preferences.getCssTheme() != 1); + }); + preferences.getCssThemeProperty().addListener((observable, oldValue, newValue) -> { + useDarkModeIcon.setId(preferences.getCssTheme() == 1 ? "image-dark-mode" : "image-light-mode"); + }); + // P2P Network Label p2PNetworkLabel = new AutoTooltipLabel(); p2PNetworkLabel.setId("footer-pane"); p2PNetworkLabel.textProperty().bind(model.getP2PNetworkInfo()); + double networkIconRightAnchor = 54d; ImageView p2PNetworkIcon = new ImageView(); - setRightAnchor(p2PNetworkIcon, 8d); + setRightAnchor(p2PNetworkIcon, networkIconRightAnchor); setBottomAnchor(p2PNetworkIcon, 6d); p2PNetworkIcon.setPickOnBounds(true); p2PNetworkIcon.setCursor(Cursor.HAND); @@ -790,7 +814,7 @@ public class MainView extends InitializableView { p2PNetworkStatusIcon.setCursor(Cursor.HAND); p2PNetworkStatusIcon.setFitWidth(networkIconSize); p2PNetworkStatusIcon.setFitHeight(networkIconSize); - setRightAnchor(p2PNetworkStatusIcon, 30d); + setRightAnchor(p2PNetworkStatusIcon, networkIconRightAnchor + 22); setBottomAnchor(p2PNetworkStatusIcon, 6d); Tooltip p2pNetworkStatusToolTip = new Tooltip(); Tooltip.install(p2PNetworkStatusIcon, p2pNetworkStatusToolTip); @@ -832,10 +856,10 @@ public class MainView extends InitializableView { VBox vBox = new VBox(); vBox.setAlignment(Pos.CENTER_RIGHT); vBox.getChildren().addAll(p2PNetworkLabel, p2pNetworkProgressBar); - setRightAnchor(vBox, 53d); + setRightAnchor(vBox, networkIconRightAnchor + 45); setBottomAnchor(vBox, 5d); - return new AnchorPane(separator, xmrInfoLabel, versionBox, vBox, p2PNetworkStatusIcon, p2PNetworkIcon) {{ + return new AnchorPane(separator, xmrInfoLabel, versionBox, vBox, p2PNetworkStatusIcon, p2PNetworkIcon, useDarkModeIcon) {{ setId("footer-pane"); setMinHeight(30); setMaxHeight(30); diff --git a/desktop/src/main/resources/images/dark_mode.png b/desktop/src/main/resources/images/dark_mode.png new file mode 100644 index 0000000000000000000000000000000000000000..ceeede409d5cbd6959b21af73430292633a8af00 GIT binary patch literal 1760 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