From d67dd700e93642aa55ae3734bef61d3723119e20 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 17 Dec 2022 18:34:10 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=BB=E9=A2=98=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E9=97=AE=E9=A2=98=20(#1915)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update blue.css * fix * fix * Cleanup Code * Use blue.bss * fix read theme --- .../org/jackhuang/hmcl/setting/Config.java | 5 +- .../org/jackhuang/hmcl/setting/Theme.java | 107 +++++++++++------- .../org/jackhuang/hmcl/ui/Controllers.java | 3 +- .../java/org/jackhuang/hmcl/ui/FXUtils.java | 2 + .../jackhuang/hmcl/ui/GameCrashWindow.java | 3 +- .../java/org/jackhuang/hmcl/ui/LogWindow.java | 8 +- .../java/org/jackhuang/hmcl/ui/WebStage.java | 3 +- .../hmcl/ui/construct/FontComboBox.java | 3 +- .../hmcl/ui/main/PersonalizationPage.java | 11 +- HMCL/src/main/resources/assets/css/blue.css | 6 +- .../java/org/jackhuang/hmcl/util/Lang.java | 12 ++ 11 files changed, 104 insertions(+), 59 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java index 310b3867c..87789663d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java @@ -40,7 +40,6 @@ import org.jackhuang.hmcl.util.i18n.Locales; import org.jackhuang.hmcl.util.i18n.Locales.SupportedLocale; import org.jackhuang.hmcl.util.javafx.ObservableHelper; import org.jackhuang.hmcl.util.javafx.PropertyUtils; -import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jetbrains.annotations.Nullable; import java.io.File; @@ -120,7 +119,7 @@ public final class Config implements Cloneable, Observable { private DoubleProperty height = new SimpleDoubleProperty(); @SerializedName("theme") - private ObjectProperty theme = new SimpleObjectProperty<>(Theme.BLUE); + private ObjectProperty theme = new SimpleObjectProperty<>(); @SerializedName("localization") private ObjectProperty localization = new SimpleObjectProperty<>(Locales.DEFAULT); @@ -147,7 +146,7 @@ public final class Config implements Cloneable, Observable { private ObservableList> accountStorages = FXCollections.observableArrayList(); @SerializedName("fontFamily") - private StringProperty fontFamily = new SimpleStringProperty(OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS ? "Consolas" : "Monospace"); + private StringProperty fontFamily = new SimpleStringProperty(); @SerializedName("fontSize") private DoubleProperty fontSize = new SimpleDoubleProperty(12); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Theme.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Theme.java index 9c0cef24a..857d0fe05 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Theme.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Theme.java @@ -24,13 +24,12 @@ import javafx.beans.binding.Bindings; import javafx.beans.binding.ObjectBinding; import javafx.scene.paint.Color; import org.jackhuang.hmcl.util.Logging; -import org.jackhuang.hmcl.util.ResourceNotFoundError; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.IOUtils; -import org.jackhuang.hmcl.util.javafx.BindingMapping; import java.io.File; import java.io.IOException; +import java.util.Locale; import java.util.Objects; import java.util.Optional; import java.util.logging.Level; @@ -50,6 +49,11 @@ public class Theme { Color.web("#B71C1C") // red }; + public static Theme getTheme() { + Theme theme = config().getTheme(); + return theme == null ? BLUE : theme; + } + private final Color paint; private final String color; private final String name; @@ -73,7 +77,7 @@ public class Theme { } public boolean isLight() { - return Color.web(color).grayscale().getRed() >= 0.5; + return paint.grayscale().getRed() >= 0.5; } public Color getForegroundColor() { @@ -81,33 +85,31 @@ public class Theme { } public String[] getStylesheets(String overrideFontFamily) { - Color textFill = getForegroundColor(); + String css = "/assets/css/blue.css"; String fontFamily = System.getProperty("hmcl.font.override", overrideFontFamily); - String css; - try { - File temp = File.createTempFile("hmcl", ".css"); - FileUtils.writeText(temp, IOUtils.readFullyAsString(ResourceNotFoundError.getResourceAsStream("/assets/css/custom.css")) - .replace("%base-color%", color) - .replace("%base-red%", Integer.toString((int)Math.ceil(paint.getRed() * 256))) - .replace("%base-green%", Integer.toString((int)Math.ceil(paint.getGreen() * 256))) - .replace("%base-blue%", Integer.toString((int)Math.ceil(paint.getBlue() * 256))) - .replace("%base-rippler-color%", String.format("rgba(%d, %d, %d, 0.3)", (int) Math.ceil(paint.getRed() * 256), (int) Math.ceil(paint.getGreen() * 256), (int) Math.ceil(paint.getBlue() * 256))) - .replace("%disabled-font-color%", String.format("rgba(%d, %d, %d, 0.7)", (int) Math.ceil(textFill.getRed() * 256), (int) Math.ceil(textFill.getGreen() * 256), (int) Math.ceil(textFill.getBlue() * 256))) - .replace("%font-color%", getColorDisplayName(getForegroundColor())) - .replace("%font%", Optional.ofNullable(fontFamily).map(f -> "-fx-font-family: \"" + f + "\";").orElse(""))); - temp.deleteOnExit(); - css = temp.toURI().toString(); - } catch (IOException | NullPointerException e) { - Logging.LOG.log(Level.SEVERE, "Unable to create theme stylesheet. Fallback to blue theme.", e); - css = "/assets/css/blue.css"; + if (fontFamily != null || !this.color.equalsIgnoreCase(BLUE.color)) { + Color textFill = getForegroundColor(); + try { + File temp = File.createTempFile("hmcl", ".css"); + FileUtils.writeText(temp, IOUtils.readFullyAsString(Theme.class.getResourceAsStream("/assets/css/custom.css")) + .replace("%base-color%", color) + .replace("%base-red%", Integer.toString((int) Math.ceil(paint.getRed() * 256))) + .replace("%base-green%", Integer.toString((int) Math.ceil(paint.getGreen() * 256))) + .replace("%base-blue%", Integer.toString((int) Math.ceil(paint.getBlue() * 256))) + .replace("%base-rippler-color%", String.format("rgba(%d, %d, %d, 0.3)", (int) Math.ceil(paint.getRed() * 256), (int) Math.ceil(paint.getGreen() * 256), (int) Math.ceil(paint.getBlue() * 256))) + .replace("%disabled-font-color%", String.format("rgba(%d, %d, %d, 0.7)", (int) Math.ceil(textFill.getRed() * 256), (int) Math.ceil(textFill.getGreen() * 256), (int) Math.ceil(textFill.getBlue() * 256))) + .replace("%font-color%", getColorDisplayName(getForegroundColor())) + .replace("%font%", Optional.ofNullable(fontFamily).map(f -> "-fx-font-family: \"" + f + "\";").orElse(""))); + temp.deleteOnExit(); + css = temp.toURI().toString(); + } catch (IOException | NullPointerException e) { + Logging.LOG.log(Level.SEVERE, "Unable to create theme stylesheet. Fallback to blue theme.", e); + } } - return new String[]{ - css, - "/assets/css/root.css" - }; + return new String[]{css, "/assets/css/root.css"}; } public static Theme custom(String color) { @@ -119,25 +121,35 @@ public class Theme { public static Optional getTheme(String name) { if (name == null) return Optional.empty(); - else if (name.equalsIgnoreCase("blue")) - return Optional.of(custom("#5C6BC0")); - else if (name.equalsIgnoreCase("darker_blue")) - return Optional.of(custom("#283593")); - else if (name.equalsIgnoreCase("green")) - return Optional.of(custom("#43A047")); - else if (name.equalsIgnoreCase("orange")) - return Optional.of(custom("#E67E22")); - else if (name.equalsIgnoreCase("purple")) - return Optional.of(custom("#9C27B0")); - else if (name.equalsIgnoreCase("red")) - return Optional.of(custom("#F44336")); - - if (name.startsWith("#")) + else if (name.startsWith("#")) try { Color.web(name); return Optional.of(custom(name)); } catch (IllegalArgumentException ignore) { } + else { + String color = null; + switch (name.toLowerCase(Locale.ROOT)) { + case "blue": + return Optional.of(BLUE); + case "darker_blue": + color = "#283593"; + break; + case "green": + color = "#43A047"; + break; + case "orange": + color = "#E67E22"; + break; + case "purple": + color = "#9C27B0"; + break; + case "red": + color = "#F44336"; + } + if (color != null) + return Optional.of(new Theme(name, color)); + } return Optional.empty(); } @@ -146,17 +158,26 @@ public class Theme { return c != null ? String.format("#%02x%02x%02x", Math.round(c.getRed() * 255.0D), Math.round(c.getGreen() * 255.0D), Math.round(c.getBlue() * 255.0D)).toUpperCase() : null; } + private static final ObjectBinding BLACK_FILL = Bindings.createObjectBinding(() -> BLACK); + private static final ObjectBinding WHITE_FILL = Bindings.createObjectBinding(() -> Color.WHITE); + private static ObjectBinding FOREGROUND_FILL; + public static ObjectBinding foregroundFillBinding() { - return BindingMapping.of(config().themeProperty()) - .map(Theme::getForegroundColor); + if (FOREGROUND_FILL == null) + FOREGROUND_FILL = Bindings.createObjectBinding( + () -> Theme.getTheme().getForegroundColor(), + config().themeProperty() + ); + + return FOREGROUND_FILL; } public static ObjectBinding blackFillBinding() { - return Bindings.createObjectBinding(() -> BLACK); + return BLACK_FILL; } public static ObjectBinding whiteFillBinding() { - return Bindings.createObjectBinding(() -> Color.WHITE); + return WHITE_FILL; } public static class TypeAdapter extends com.google.gson.TypeAdapter { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java index 6b13766ff..7ac8ae7f4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -35,6 +35,7 @@ import org.jackhuang.hmcl.game.ModpackHelper; import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.setting.EnumCommonDirectory; import org.jackhuang.hmcl.setting.Profiles; +import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.ui.account.AccountListPage; @@ -181,7 +182,7 @@ public final class Controllers { stage.setMinWidth(800 + 2 + 16); // bg width + border width*2 + shadow width*2 decorator.getDecorator().prefWidthProperty().bind(scene.widthProperty()); decorator.getDecorator().prefHeightProperty().bind(scene.heightProperty()); - scene.getStylesheets().setAll(config().getTheme().getStylesheets(config().getLauncherFontFamily())); + scene.getStylesheets().setAll(Theme.getTheme().getStylesheets(config().getLauncherFontFamily())); stage.getIcons().add(newImage("/assets/img/icon.png")); stage.setTitle(Metadata.FULL_TITLE); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java index cf725ef77..426b2eba1 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java @@ -94,6 +94,8 @@ public final class FXUtils { private FXUtils() { } + public static String DEFAULT_MONOSPACE_FONT = OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS ? "Consolas" : "Monospace"; + public static void runInFX(Runnable runnable) { if (Platform.isFxApplicationThread()) { runnable.run(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java index 1d82f24d5..8c80600ba 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java @@ -38,6 +38,7 @@ import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.*; import org.jackhuang.hmcl.launch.ProcessListener; +import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.util.Lang; @@ -107,7 +108,7 @@ public class GameCrashWindow extends Stage { this.feedbackTextFlow.getChildren().addAll(FXUtils.parseSegment(i18n("game.crash.feedback"), Controllers::onHyperlinkAction)); setScene(new Scene(view, 800, 480)); - getScene().getStylesheets().addAll(config().getTheme().getStylesheets(config().getLauncherFontFamily())); + getScene().getStylesheets().addAll(Theme.getTheme().getStylesheets(config().getLauncherFontFamily())); setTitle(i18n("game.crash.title")); getIcons().add(newImage("/assets/img/icon.png")); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LogWindow.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LogWindow.java index 3fbd60489..52c78ed4f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LogWindow.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LogWindow.java @@ -36,6 +36,8 @@ import javafx.scene.layout.*; import javafx.stage.Stage; import org.apache.commons.lang3.mutable.MutableObject; import org.jackhuang.hmcl.game.LauncherHelper; +import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.Log4jLevel; import org.jackhuang.hmcl.util.platform.OperatingSystem; @@ -87,7 +89,7 @@ public final class LogWindow extends Stage { public LogWindow() { setScene(new Scene(impl, 800, 480)); - getScene().getStylesheets().addAll(config().getTheme().getStylesheets(config().getLauncherFontFamily())); + getScene().getStylesheets().addAll(Theme.getTheme().getStylesheets(config().getLauncherFontFamily())); setTitle(i18n("logwindow.title")); getIcons().add(newImage("/assets/img/icon.png")); @@ -286,7 +288,9 @@ public final class LogWindow extends Stage { if (!listView.getItems().isEmpty() && control.autoScroll.get()) listView.scrollTo(listView.getItems().size() - 1); }); - listView.setStyle("-fx-font-family: " + config().getFontFamily() + "; -fx-font-size: " + config().getFontSize() + "px;"); + + listView.setStyle("-fx-font-family: " + Lang.requireNonNullElse(config().getFontFamily(), FXUtils.DEFAULT_MONOSPACE_FONT) + + "; -fx-font-size: " + config().getFontSize() + "px;"); MutableObject lastCell = new MutableObject<>(); listView.setCellFactory(x -> new ListCell() { { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/WebStage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/WebStage.java index 0e7207626..056266385 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/WebStage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/WebStage.java @@ -26,6 +26,7 @@ import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; import javafx.stage.Stage; import org.jackhuang.hmcl.Metadata; +import org.jackhuang.hmcl.setting.Theme; import static org.jackhuang.hmcl.setting.ConfigHolder.config; import static org.jackhuang.hmcl.ui.FXUtils.newImage; @@ -42,7 +43,7 @@ public class WebStage extends Stage { public WebStage(int width, int height) { setScene(new Scene(pane, width, height)); - getScene().getStylesheets().addAll(config().getTheme().getStylesheets(config().getLauncherFontFamily())); + getScene().getStylesheets().addAll(Theme.getTheme().getStylesheets(config().getLauncherFontFamily())); getIcons().add(newImage("/assets/img/icon.png")); webView.getEngine().setUserDataDirectory(Metadata.HMCL_DIRECTORY.toFile()); webView.setContextMenuEnabled(false); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FontComboBox.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FontComboBox.java index 567f1733a..58b0923be 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FontComboBox.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FontComboBox.java @@ -26,7 +26,6 @@ import org.jackhuang.hmcl.util.javafx.BindingMapping; import com.jfoenix.controls.JFXComboBox; import com.jfoenix.controls.JFXListCell; -import javafx.beans.NamedArg; import javafx.beans.binding.Bindings; import javafx.scene.text.Font; @@ -34,7 +33,7 @@ public class FontComboBox extends JFXComboBox { private boolean loaded = false; - public FontComboBox(@NamedArg(value = "fontSize", defaultValue = "12.0") double fontSize) { + public FontComboBox() { styleProperty().bind(Bindings.concat("-fx-font-family: \"", valueProperty(), "\"")); setCellFactory(listView -> new JFXListCell() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/PersonalizationPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/PersonalizationPage.java index 5d8b2ee54..78ea3a618 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/PersonalizationPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/PersonalizationPage.java @@ -41,6 +41,7 @@ import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.construct.*; +import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.javafx.SafeStringConverter; import java.util.Arrays; @@ -72,7 +73,7 @@ public class PersonalizationPage extends StackPane { themeColorPickerContainer.setMinHeight(30); themePane.setRight(themeColorPickerContainer); - ColorPicker picker = new ColorPicker(Color.web(config().getTheme().getColor())); + ColorPicker picker = new ColorPicker(Color.web(Theme.getTheme().getColor())); picker.getCustomColors().setAll(Theme.SUGGESTED_COLORS); picker.setOnAction(e -> { Theme theme = Theme.custom(Theme.getColorDisplayName(picker.getValue())); @@ -141,7 +142,7 @@ public class PersonalizationPage extends StackPane { HBox hBox = new HBox(); hBox.setSpacing(3); - FontComboBox cboLogFont = new FontComboBox(12); + FontComboBox cboLogFont = new FontComboBox(); cboLogFont.valueProperty().bindBidirectional(config().fontFamilyProperty()); JFXTextField txtLogFontSize = new JFXTextField(); @@ -159,7 +160,7 @@ public class PersonalizationPage extends StackPane { Label lblLogFontDisplay = new Label("[23:33:33] [Client Thread/INFO] [WaterPower]: Loaded mod WaterPower."); lblLogFontDisplay.fontProperty().bind(Bindings.createObjectBinding( - () -> Font.font(config().getFontFamily(), config().getFontSize()), + () -> Font.font(Lang.requireNonNullElse(config().getFontFamily(), FXUtils.DEFAULT_MONOSPACE_FONT), config().getFontSize()), config().fontFamilyProperty(), config().fontSizeProperty())); fontPane.getChildren().add(lblLogFontDisplay); @@ -191,7 +192,7 @@ public class PersonalizationPage extends StackPane { HBox hBox = new HBox(); hBox.setSpacing(8); - FontComboBox cboFont = new FontComboBox(12); + FontComboBox cboFont = new FontComboBox(); cboFont.valueProperty().bindBidirectional(config().launcherFontFamilyProperty()); JFXButton clearButton = new JFXButton(); @@ -210,7 +211,7 @@ public class PersonalizationPage extends StackPane { () -> Font.font(config().getLauncherFontFamily(), 12), config().launcherFontFamilyProperty())); config().launcherFontFamilyProperty().addListener((a, b, newValue) -> { - Controllers.getScene().getStylesheets().setAll(config().getTheme().getStylesheets(newValue)); + Controllers.getScene().getStylesheets().setAll(Theme.getTheme().getStylesheets(newValue)); }); vbox.getChildren().add(lblFontDisplay); diff --git a/HMCL/src/main/resources/assets/css/blue.css b/HMCL/src/main/resources/assets/css/blue.css index 4427c574b..27ecafabf 100644 --- a/HMCL/src/main/resources/assets/css/blue.css +++ b/HMCL/src/main/resources/assets/css/blue.css @@ -16,9 +16,13 @@ * along with this program. If not, see . */ .root { - -fx-base-color: #5c6bc0; + -fx-base-color: #5C6BC0; -fx-base-darker-color: derive(-fx-base-color, -10%); -fx-base-check-color: derive(-fx-base-color, 30%); + -fx-rippler-color: rgba(92, 107, 192, 0.3); -fx-base-rippler-color: rgba(92, 107, 192, 0.3); + -fx-base-disabled-text-fill: rgba(256, 256, 256, 0.7); -fx-base-text-fill: white; + + -theme-thumb: rgba(92, 107, 192, 0.3); } \ No newline at end of file diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java index 183d3f5c0..b0be0988c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java @@ -34,6 +34,18 @@ public final class Lang { private Lang() { } + public static T requireNonNullElse(T value, T defaultValue) { + return value != null ? value : defaultValue; + } + + public static T requireNonNullElseGet(T value, Supplier defaultValue) { + return value != null ? value : defaultValue.get(); + } + + public static U requireNonNullElseGet(T value, Function mapper, Supplier defaultValue) { + return value != null ? mapper.apply(value) : defaultValue.get(); + } + /** * Construct a mutable map by given key-value pairs. * @param pairs entries in the new map