mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2026-10-06 03:28:25 +00:00
add a system theme option that follows the os light or dark setting, and make it the default for new installs
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package com.sparrowwallet.sparrow;
|
||||
|
||||
import com.sparrowwallet.sparrow.control.DialogImage;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
@@ -11,10 +12,26 @@ public class AboutController {
|
||||
@FXML
|
||||
private Label title;
|
||||
|
||||
@FXML
|
||||
private DialogImage dialogImage;
|
||||
|
||||
public void initializeView() {
|
||||
title.setText(SparrowWallet.APP_NAME + " " + SparrowWallet.APP_VERSION + SparrowWallet.APP_VERSION_SUFFIX);
|
||||
}
|
||||
|
||||
public void refreshTheme() {
|
||||
String darkCss = AppServices.class.getResource("darktheme.css").toExternalForm();
|
||||
if(AppServices.isDarkTheme()) {
|
||||
if(!stage.getScene().getStylesheets().contains(darkCss)) {
|
||||
stage.getScene().getStylesheets().add(darkCss);
|
||||
}
|
||||
} else {
|
||||
stage.getScene().getStylesheets().remove(darkCss);
|
||||
}
|
||||
|
||||
dialogImage.refresh();
|
||||
}
|
||||
|
||||
public void setStage(Stage stage) {
|
||||
this.stage = stage;
|
||||
}
|
||||
|
||||
@@ -388,8 +388,8 @@ public class AppController implements Initializable {
|
||||
|
||||
Theme configTheme = Config.get().getTheme();
|
||||
if(configTheme == null) {
|
||||
configTheme = Theme.LIGHT;
|
||||
Config.get().setTheme(Theme.LIGHT);
|
||||
configTheme = Theme.SYSTEM;
|
||||
Config.get().setTheme(Theme.SYSTEM);
|
||||
}
|
||||
final Theme selectedTheme = configTheme;
|
||||
Optional<Toggle> selectedThemeToggle = theme.getToggles().stream().filter(toggle -> selectedTheme.equals(toggle.getUserData())).findFirst();
|
||||
@@ -578,6 +578,8 @@ public class AppController implements Initializable {
|
||||
controller.initializeView();
|
||||
setStageIcon(stage);
|
||||
stage.setOnShowing(event -> {
|
||||
//The macOS application menu reuses a single About stage, so the theme may have changed since it was created
|
||||
controller.refreshTheme();
|
||||
AppServices.moveToActiveWindowScreen(stage, 600, 460);
|
||||
});
|
||||
|
||||
@@ -1374,6 +1376,7 @@ public class AppController implements Initializable {
|
||||
File walletFile = Storage.getExistingWallet(wallet.getName());
|
||||
if(walletFile != null) {
|
||||
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||
alert.initOwner(rootStack.getScene().getWindow());
|
||||
AppServices.setStageIcon(alert.getDialogPane().getScene().getWindow());
|
||||
alert.setTitle("Existing wallet found");
|
||||
alert.setHeaderText("Replace existing wallet?");
|
||||
@@ -2643,7 +2646,7 @@ public class AppController implements Initializable {
|
||||
Config.get().setTheme(selectedTheme);
|
||||
}
|
||||
|
||||
EventManager.get().post(new ThemeChangedEvent(selectedTheme));
|
||||
EventManager.get().post(new ThemeChangedEvent(AppServices.getActiveTheme()));
|
||||
}
|
||||
|
||||
private void serverToggleStartAnimation() {
|
||||
@@ -2747,13 +2750,24 @@ public class AppController implements Initializable {
|
||||
|
||||
@Subscribe
|
||||
public void themeChanged(ThemeChangedEvent event) {
|
||||
//Owned dialogs follow the main window stylesheets, but these non-modal dialogs have no owner
|
||||
List<Scene> scenes = new ArrayList<>(List.of(tabs.getScene()));
|
||||
if(sendToManyDialog != null) {
|
||||
scenes.add(sendToManyDialog.getDialogPane().getScene());
|
||||
}
|
||||
if(searchWalletDialog != null) {
|
||||
scenes.add(searchWalletDialog.getDialogPane().getScene());
|
||||
}
|
||||
|
||||
String darkCss = getClass().getResource("darktheme.css").toExternalForm();
|
||||
if(event.getTheme() == Theme.DARK) {
|
||||
if(!tabs.getScene().getStylesheets().contains(darkCss)) {
|
||||
tabs.getScene().getStylesheets().add(darkCss);
|
||||
for(Scene scene : scenes) {
|
||||
if(event.getTheme() == Theme.DARK) {
|
||||
if(!scene.getStylesheets().contains(darkCss)) {
|
||||
scene.getStylesheets().add(darkCss);
|
||||
}
|
||||
} else {
|
||||
scene.getStylesheets().remove(darkCss);
|
||||
}
|
||||
} else {
|
||||
tabs.getScene().getStylesheets().remove(darkCss);
|
||||
}
|
||||
|
||||
for(Tab tab : tabs.getTabs()) {
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.sparrowwallet.sparrow.net.*;
|
||||
import io.reactivex.rxjavafx.schedulers.JavaFxScheduler;
|
||||
import io.reactivex.subjects.PublishSubject;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.ColorScheme;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
@@ -132,6 +133,8 @@ public class AppServices {
|
||||
|
||||
private static volatile ChainTip announcedTip;
|
||||
|
||||
private static volatile boolean systemDarkTheme;
|
||||
|
||||
private static final Map<Integer, BlockSummary> blockSummaries = new ConcurrentHashMap<>();
|
||||
|
||||
private static Map<Integer, Double> targetBlockFeeRates;
|
||||
@@ -956,13 +959,42 @@ public class AppServices {
|
||||
return getInteractionServices().showAlert(title, content, alertType, graphic, buttons);
|
||||
}
|
||||
|
||||
public static void monitorSystemTheme() {
|
||||
try {
|
||||
Platform.Preferences preferences = Platform.getPreferences();
|
||||
systemDarkTheme = preferences.getColorScheme() == ColorScheme.DARK;
|
||||
preferences.colorSchemeProperty().addListener((observable, oldValue, colorScheme) -> {
|
||||
systemDarkTheme = colorScheme == ColorScheme.DARK;
|
||||
if(Config.get().getTheme() == null || Config.get().getTheme() == Theme.SYSTEM) {
|
||||
EventManager.get().post(new ThemeChangedEvent(getActiveTheme()));
|
||||
}
|
||||
});
|
||||
} catch(Exception e) {
|
||||
log.warn("Could not read the system color scheme", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Theme getActiveTheme() {
|
||||
Theme theme = Config.get().getTheme();
|
||||
if(theme == null || theme == Theme.SYSTEM) {
|
||||
return systemDarkTheme ? Theme.DARK : Theme.LIGHT;
|
||||
}
|
||||
|
||||
return theme;
|
||||
}
|
||||
|
||||
public static boolean isDarkTheme() {
|
||||
return getActiveTheme() == Theme.DARK;
|
||||
}
|
||||
|
||||
public static void setStageIcon(Window window) {
|
||||
Stage stage = (Stage)window;
|
||||
stage.getIcons().add(getWindowIcon());
|
||||
|
||||
if(stage.getScene() != null) {
|
||||
if(Config.get().getTheme() == Theme.DARK) {
|
||||
stage.getScene().getStylesheets().add(AppServices.class.getResource("darktheme.css").toExternalForm());
|
||||
String darkCss = AppServices.class.getResource("darktheme.css").toExternalForm();
|
||||
if(isDarkTheme() && !stage.getScene().getStylesheets().contains(darkCss)) {
|
||||
stage.getScene().getStylesheets().add(darkCss);
|
||||
}
|
||||
if(Config.get().isChunkAddresses()) {
|
||||
stage.getScene().getRoot().getStyleClass().add("chunk-addresses");
|
||||
|
||||
@@ -46,6 +46,7 @@ public class SparrowDesktop extends Application {
|
||||
URL.setURLStreamHandlerFactory(protocol -> WalletIcon.PROTOCOL.equals(protocol) ? new WalletIcon.WalletIconStreamHandler() : null);
|
||||
|
||||
AppServices.initialize(this);
|
||||
AppServices.monitorSystemTheme();
|
||||
|
||||
boolean createNewWallet = false;
|
||||
Mode mode = Config.get().getMode();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
package com.sparrowwallet.sparrow;
|
||||
|
||||
public enum Theme {
|
||||
LIGHT, DARK
|
||||
LIGHT, DARK, SYSTEM
|
||||
}
|
||||
|
||||
@@ -993,6 +993,7 @@ public class DevicePane extends TitledDescriptionPane {
|
||||
List<Wallet> wallets = new ArrayList<>();
|
||||
|
||||
RangeInputDialog rangeInputDialog = new RangeInputDialog(StandardAccount.ACCOUNT_0.getAccountNumber(), StandardAccount.ACCOUNT_30.getAccountNumber(), StandardAccount.ACCOUNT_10.getAccountNumber());
|
||||
rangeInputDialog.initOwner(this.getScene().getWindow());
|
||||
rangeInputDialog.setTitle("Choose number of accounts");
|
||||
rangeInputDialog.setHeaderText("Enter the number of additional accounts to scan for existing funds.\n\nThis may take a few minutes depending on how many accounts are selected.");
|
||||
Optional<Integer> optRange = rangeInputDialog.showAndWait();
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.sparrowwallet.sparrow.control;
|
||||
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import com.sparrowwallet.sparrow.Theme;
|
||||
import com.sparrowwallet.sparrow.io.Config;
|
||||
import javafx.beans.NamedArg;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
@@ -43,7 +41,7 @@ public class DialogImage extends StackPane {
|
||||
|
||||
protected void refresh(Type type) {
|
||||
SVGImage svgImage;
|
||||
if(Config.get().getTheme() == Theme.DARK) {
|
||||
if(AppServices.isDarkTheme()) {
|
||||
svgImage = loadSVGImage("/image/dialog/" + type.name().toLowerCase(Locale.ROOT) + "-invert.svg");
|
||||
} else {
|
||||
svgImage = loadSVGImage("/image/dialog/" + type.name().toLowerCase(Locale.ROOT) + ".svg");
|
||||
|
||||
@@ -2,9 +2,7 @@ package com.sparrowwallet.sparrow.control;
|
||||
|
||||
import com.sparrowwallet.drongo.OsType;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import com.sparrowwallet.sparrow.Theme;
|
||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||
import com.sparrowwallet.sparrow.io.Config;
|
||||
import com.sparrowwallet.sparrow.net.MempoolRateSize;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.NamedArg;
|
||||
@@ -63,7 +61,7 @@ public class MempoolSizeFeeRatesChart extends StackedAreaChart<String, Number> {
|
||||
}
|
||||
|
||||
scenePane.getStylesheets().add(AppServices.class.getResource("general.css").toExternalForm());
|
||||
if(Config.get().getTheme() == Theme.DARK) {
|
||||
if(AppServices.isDarkTheme()) {
|
||||
scenePane.getStylesheets().add(AppServices.class.getResource("darktheme.css").toExternalForm());
|
||||
}
|
||||
scenePane.getStylesheets().add(AppServices.class.getResource("wallet/wallet.css").toExternalForm());
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package com.sparrowwallet.sparrow.control;
|
||||
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import com.sparrowwallet.sparrow.Theme;
|
||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||
import com.sparrowwallet.sparrow.io.Config;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.Node;
|
||||
import org.controlsfx.glyphfont.Glyph;
|
||||
@@ -31,7 +29,7 @@ public enum QREncoding {
|
||||
|
||||
public Node getSVGImage() {
|
||||
try {
|
||||
URL url = AppServices.class.getResource("/image/qrencoding/" + getName().toLowerCase(Locale.ROOT) + "-icon" + (Config.get().getTheme() == Theme.DARK ? "-invert" : "") + ".svg");
|
||||
URL url = AppServices.class.getResource("/image/qrencoding/" + getName().toLowerCase(Locale.ROOT) + "-icon" + (AppServices.isDarkTheme() ? "-invert" : "") + ".svg");
|
||||
if(url != null) {
|
||||
return SVGLoader.load(url);
|
||||
} else {
|
||||
|
||||
@@ -98,7 +98,7 @@ public class TransactionDiagram extends GridPane {
|
||||
}
|
||||
|
||||
scenePane.getStylesheets().add(AppServices.class.getResource("general.css").toExternalForm());
|
||||
if(Config.get().getTheme() == Theme.DARK) {
|
||||
if(AppServices.isDarkTheme()) {
|
||||
scenePane.getStylesheets().add(AppServices.class.getResource("darktheme.css").toExternalForm());
|
||||
}
|
||||
scenePane.getStylesheets().add(AppServices.class.getResource("wallet/wallet.css").toExternalForm());
|
||||
@@ -998,7 +998,7 @@ public class TransactionDiagram extends GridPane {
|
||||
transactionDiagram.setFinal(true);
|
||||
transactionDiagram.setExpanded(isExpanded());
|
||||
transactionDiagram.setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, null, null)));
|
||||
transactionDiagram.setStyle("-fx-text-background-color: " + (Config.get().getTheme() == Theme.DARK ? "#ffffff" : "#000000"));
|
||||
transactionDiagram.setStyle("-fx-text-background-color: " + (AppServices.isDarkTheme() ? "#ffffff" : "#000000"));
|
||||
updateDerivedDiagram(transactionDiagram);
|
||||
Scene scene = new Scene(transactionDiagram);
|
||||
scene.setFill(Color.TRANSPARENT);
|
||||
|
||||
@@ -2,9 +2,7 @@ package com.sparrowwallet.sparrow.control;
|
||||
|
||||
import com.sparrowwallet.drongo.wallet.*;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import com.sparrowwallet.sparrow.Theme;
|
||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||
import com.sparrowwallet.sparrow.io.Config;
|
||||
import com.sparrowwallet.sparrow.io.ImageUtils;
|
||||
import com.sparrowwallet.sparrow.io.Storage;
|
||||
import javafx.application.Platform;
|
||||
@@ -12,7 +10,6 @@ import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.paint.ImagePattern;
|
||||
import javafx.scene.shape.Circle;
|
||||
@@ -73,7 +70,7 @@ public class WalletIcon extends StackPane {
|
||||
WalletModel walletModel = keystore.getWalletModel();
|
||||
|
||||
SVGImage svgImage;
|
||||
if(Config.get().getTheme() == Theme.DARK) {
|
||||
if(AppServices.isDarkTheme()) {
|
||||
svgImage = loadSVGImage("/image/walletmodel/" + walletModel.getType() + "-icon-invert.svg");
|
||||
} else {
|
||||
svgImage = loadSVGImage("/image/walletmodel/" + walletModel.getType() + "-icon.svg");
|
||||
|
||||
@@ -2,12 +2,9 @@ package com.sparrowwallet.sparrow.control;
|
||||
|
||||
import com.sparrowwallet.drongo.wallet.WalletModel;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import com.sparrowwallet.sparrow.Theme;
|
||||
import com.sparrowwallet.sparrow.io.Config;
|
||||
import javafx.beans.NamedArg;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.girod.javafx.svgimage.SVGImage;
|
||||
import org.girod.javafx.svgimage.SVGLoader;
|
||||
@@ -51,7 +48,7 @@ public class WalletModelImage extends StackPane {
|
||||
|
||||
protected void refresh(WalletModel walletModel) {
|
||||
SVGImage svgImage;
|
||||
if(Config.get().getTheme() == Theme.DARK) {
|
||||
if(AppServices.isDarkTheme()) {
|
||||
svgImage = loadSVGImage("/image/walletmodel/" + walletModel.getType() + "-invert.svg");
|
||||
} else {
|
||||
svgImage = loadSVGImage("/image/walletmodel/" + walletModel.getType() + ".svg");
|
||||
|
||||
@@ -903,7 +903,7 @@ public class PaymentController extends WalletFormController implements Initializ
|
||||
public static Node getBitcoinCharacter() {
|
||||
try {
|
||||
URL url;
|
||||
if(Config.get().getTheme() == Theme.DARK) {
|
||||
if(AppServices.isDarkTheme()) {
|
||||
url = AppServices.class.getResource("/image/bitcoin-character-invert.svg");
|
||||
} else {
|
||||
url = AppServices.class.getResource("/image/bitcoin-character.svg");
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<Label fx:id="title" text="Sparrow" styleClass="title-label" />
|
||||
</HBox>
|
||||
<Region HBox.hgrow="ALWAYS"/>
|
||||
<DialogImage type="SPARROW" AnchorPane.rightAnchor="0"/>
|
||||
<DialogImage fx:id="dialogImage" type="SPARROW" AnchorPane.rightAnchor="0"/>
|
||||
</HBox>
|
||||
<VBox spacing="10" styleClass="content-area">
|
||||
<Label text="Sparrow is a Bitcoin wallet with the goal of providing greater transparency and usability on the path to full financial self-sovereignty. It attempts to provide all of the detail about your wallet setup, transactions and UTXOs so that you can transact with a full understanding of your money." wrapText="true" />
|
||||
|
||||
@@ -107,6 +107,11 @@
|
||||
</Menu>
|
||||
<Menu mnemonicParsing="false" text="Theme">
|
||||
<items>
|
||||
<RadioMenuItem mnemonicParsing="false" text="System" toggleGroup="$theme" onAction="#setTheme">
|
||||
<userData>
|
||||
<Theme fx:constant="SYSTEM" />
|
||||
</userData>
|
||||
</RadioMenuItem>
|
||||
<RadioMenuItem mnemonicParsing="false" text="Light" toggleGroup="$theme" onAction="#setTheme">
|
||||
<userData>
|
||||
<Theme fx:constant="LIGHT" />
|
||||
|
||||
Reference in New Issue
Block a user