修复部分控件在深色模式下颜色异常的问题 (#4899)

This commit is contained in:
Glavo
2025-12-02 20:29:46 +08:00
committed by GitHub
parent 75292d2a52
commit a1e75e48da
15 changed files with 171 additions and 86 deletions

View File

@@ -28,6 +28,8 @@ import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.css.*;
import javafx.css.converter.BooleanConverter;
import javafx.geometry.Insets;
@@ -60,6 +62,9 @@ public final class JFXColorPickerSkin extends JFXGenericPickerSkin<Color> {
"colorLabelVisible",
true);
private final ObjectProperty<Color> textFill = new SimpleObjectProperty<>(Color.BLACK);
private final ObjectProperty<Background> colorBoxBackground = new SimpleObjectProperty<>(null);
public JFXColorPickerSkin(final ColorPicker colorPicker) {
super(colorPicker);
@@ -67,14 +72,16 @@ public final class JFXColorPickerSkin extends JFXGenericPickerSkin<Color> {
displayNode = new Label("");
displayNode.getStyleClass().add("color-label");
displayNode.setMouseTransparent(true);
displayNode.textFillProperty().bind(textFill);
// label graphic
colorBox = new JFXClippedPane(displayNode);
colorBox.getStyleClass().add("color-box");
colorBox.setManaged(false);
colorBox.backgroundProperty().bind(colorBoxBackground);
initColor();
final JFXRippler rippler = new JFXRippler(colorBox, JFXRippler.RipplerMask.FIT);
rippler.ripplerFillProperty().bind(displayNode.textFillProperty());
rippler.ripplerFillProperty().bind(textFill);
getChildren().setAll(rippler);
JFXDepthManager.setDepth(getSkinnable(), 1);
getSkinnable().setPickOnBounds(false);
@@ -168,13 +175,13 @@ public final class JFXColorPickerSkin extends JFXGenericPickerSkin<Color> {
200,
Interpolator.EASE_BOTH)));
animateColor.setOnFinished((finish) -> {
JFXNodeUtils.updateBackground(colorBox.getBackground(), colorBox, colorCircle.getFill());
colorBoxBackground.set(new Background(new BackgroundFill(colorCircle.getFill(), new CornerRadii(3), Insets.EMPTY)));
colorBox.getChildren().remove(colorCircle);
});
animateColor.play();
}
// update label color
displayNode.setTextFill(circleColor.grayscale().getRed() < 0.5 ? Color.valueOf(
textFill.set(circleColor.grayscale().getRed() < 0.5 ? Color.valueOf(
"rgba(255, 255, 255, 0.87)") : Color.valueOf("rgba(0, 0, 0, 0.87)"));
if (colorLabelVisible.get()) {
displayNode.setText(JFXNodeUtils.colorToHex(circleColor));
@@ -188,9 +195,9 @@ public final class JFXColorPickerSkin extends JFXGenericPickerSkin<Color> {
Color color = colorPicker.getValue();
Color circleColor = color == null ? Color.WHITE : color;
// update picker box color
colorBox.setBackground(new Background(new BackgroundFill(circleColor, new CornerRadii(3), Insets.EMPTY)));
colorBoxBackground.set(new Background(new BackgroundFill(circleColor, new CornerRadii(3), Insets.EMPTY)));
// update label color
displayNode.setTextFill(circleColor.grayscale().getRed() < 0.5 ? Color.valueOf(
textFill.set(circleColor.grayscale().getRed() < 0.5 ? Color.valueOf(
"rgba(255, 255, 255, 0.87)") : Color.valueOf("rgba(0, 0, 0, 0.87)"));
if (colorLabelVisible.get()) {
displayNode.setText(JFXNodeUtils.colorToHex(circleColor));

View File

@@ -32,9 +32,7 @@ import javafx.beans.property.SimpleObjectProperty;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.*;
@@ -156,15 +154,15 @@ public class JFXCustomColorPickerDialog extends StackPane {
Color fontColor = ((Color) newVal.getFills().get(0).getFill()).grayscale()
.getRed() > 0.5 ? Color.valueOf(
"rgba(40, 40, 40, 0.87)") : Color.valueOf("rgba(255, 255, 255, 0.87)");
for (Node tabNode : tabs.lookupAll(".tab")) {
for (Node node : tabNode.lookupAll(".tab-label")) {
((Label) node).setTextFill(fontColor);
}
for (Node node : tabNode.lookupAll(".jfx-rippler")) {
((JFXRippler) node).setRipplerFill(fontColor);
}
}
((Pane) tabs.lookup(".tab-selected-line")).setBackground(new Background(new BackgroundFill(fontColor, CornerRadii.EMPTY, Insets.EMPTY)));
// for (Node tabNode : tabs.lookupAll(".tab")) {
// for (Node node : tabNode.lookupAll(".tab-label")) {
// ((Label) node).setTextFill(fontColor);
// }
// for (Node node : tabNode.lookupAll(".jfx-rippler")) {
// ((JFXRippler) node).setRipplerFill(fontColor);
// }
// }
// ((Pane) tabs.lookup(".tab-selected-line")).setBackground(new Background(new BackgroundFill(fontColor, CornerRadii.EMPTY, Insets.EMPTY)));
pickerDecorator.lookupAll(".jfx-decorator-button").forEach(button -> {
((JFXButton) button).setRipplerFill(fontColor);
((SVGGlyph) ((JFXButton) button).getGraphic()).setFill(fontColor);

View File

@@ -47,6 +47,7 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG;
public final class StyleSheets {
private static final int FONT_STYLE_SHEET_INDEX = 0;
private static final int THEME_STYLE_SHEET_INDEX = 1;
private static final int BRIGHTNESS_SHEET_INDEX = 2;
private static final ObservableList<String> stylesheets;
@@ -54,12 +55,16 @@ public final class StyleSheets {
String[] array = new String[]{
getFontStyleSheet(),
getThemeStyleSheet(),
getBrightnessStyleSheet(),
"/assets/css/root.css"
};
stylesheets = FXCollections.observableList(Arrays.asList(array));
FontManager.fontProperty().addListener(o -> stylesheets.set(FONT_STYLE_SHEET_INDEX, getFontStyleSheet()));
Themes.colorSchemeProperty().addListener(o -> stylesheets.set(THEME_STYLE_SHEET_INDEX, getThemeStyleSheet()));
Themes.colorSchemeProperty().addListener(o -> {
stylesheets.set(THEME_STYLE_SHEET_INDEX, getThemeStyleSheet());
stylesheets.set(BRIGHTNESS_SHEET_INDEX, getBrightnessStyleSheet());
});
}
private static String toStyleSheetUri(String styleSheet, String fallback) {
@@ -131,6 +136,12 @@ public final class StyleSheets {
return toStyleSheetUri(builder.toString(), defaultCss);
}
private static String getBrightnessStyleSheet() {
return Themes.getColorScheme().getBrightness() == Brightness.LIGHT
? "/assets/css/brightness-light.css"
: "/assets/css/brightness-dark.css";
}
private static void addColor(StringBuilder builder, String name, Color color) {
builder.append(" ").append(name)
.append(": ").append(ThemeColor.getColorDisplayName(color)).append(";\n");
@@ -171,13 +182,6 @@ public final class StyleSheets {
addColor(builder, scheme, ColorRole.SURFACE_CONTAINER_LOW, 0.8);
addColor(builder, scheme, ColorRole.SECONDARY_CONTAINER, 0.8);
// If we use -monet-error-container as the tag color, there may be a conflict between the tag and background colors on the mod management page
if (scheme.getBrightness() == Brightness.LIGHT) {
addColor(builder, "-warning-tag-background", Color.web("#f1aeb5"));
} else {
addColor(builder, "-warning-tag-background", Color.web("#2c0b0e"));
}
builder.append("}\n");
return toStyleSheetUri(builder.toString(), blueCss);
}

View File

@@ -113,8 +113,16 @@ public final class Themes {
return colorScheme.get();
}
private static final ObjectBinding<Color> titleFill = Bindings.createObjectBinding(
() -> config().isTitleTransparent()
? getColorScheme().getOnSurface()
: getColorScheme().getOnPrimaryContainer(),
colorSchemeProperty(),
config().titleTransparentProperty()
);
public static ObservableValue<Color> titleFillProperty() {
return colorSchemeProperty().getOnPrimaryContainer();
return titleFill;
}
public static BooleanBinding darkModeProperty() {

View File

@@ -1468,7 +1468,6 @@ public final class FXUtils {
hyperlinkAction.accept(link);
});
text.setCursor(Cursor.HAND);
text.setFill(Color.web("#0070E0"));
text.setUnderline(true);
texts.add(text);
} else if ("b".equals(element.getTagName())) {

View File

@@ -264,7 +264,6 @@ public final class LogWindow extends Stage {
VBox vbox = new VBox(3);
vbox.setPadding(new Insets(3, 0, 3, 0));
vbox.setStyle("-fx-background-color: white");
getChildren().setAll(vbox);
{
@@ -286,8 +285,7 @@ public final class LogWindow extends Stage {
HBox hBox = new HBox(3);
for (int i = 0; i < LEVELS.length; i++) {
ToggleButton button = new ToggleButton();
button.setStyle("-fx-background-color: " + FXUtils.toWeb(LEVELS[i].getColor()) + ";");
button.getStyleClass().add("log-toggle");
button.getStyleClass().addAll("log-toggle", LEVELS[i].name().toLowerCase(Locale.ROOT));
button.textProperty().bind(control.buttonText[i]);
button.setSelected(true);
control.showLevel[i].bind(button.selectedProperty());

View File

@@ -24,7 +24,7 @@ import org.jackhuang.hmcl.ui.SVG;
public final class IconedMenuItem extends IconedItem {
public IconedMenuItem(SVG icon, String text, Runnable action, JFXPopup popup) {
super(icon != null ? icon.createIcon(14) : null, text);
super(icon != null ? FXUtils.limitingSize(icon.createIcon(14), 14, 14) : null, text);
getStyleClass().setAll("iconed-menu-item");

View File

@@ -304,6 +304,7 @@ public class DecoratorSkin extends SkinBase<Decorator> {
BorderPane center = new BorderPane();
if (title != null) {
Label titleLabel = new Label();
titleLabel.textFillProperty().bind(Themes.titleFillProperty());
BorderPane.setAlignment(titleLabel, Pos.CENTER_LEFT);
titleLabel.getStyleClass().add("jfx-decorator-title");
if (titleNode == null) {

View File

@@ -52,6 +52,7 @@ import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Profiles;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.theme.Themes;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
@@ -120,6 +121,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
titleLabel.setRotate(180);
}
titleLabel.getStyleClass().add("jfx-decorator-title");
titleLabel.textFillProperty().bind(Themes.titleFillProperty());
titleNode.getChildren().setAll(titleIcon, titleLabel);
state.setValue(new State(null, titleNode, false, false, true));

View File

@@ -40,7 +40,6 @@ import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import org.jackhuang.hmcl.game.LauncherHelper;
@@ -168,6 +167,7 @@ public class TerracottaControllerPage extends StackPane {
progressProperty.set(0);
TextFlow body = FXUtils.segmentToTextFlow(i18n("terracotta.confirm.desc"), Controllers::onHyperlinkAction);
body.getStyleClass().add("terracotta-license");
body.setLineSpacing(4);
LineButton download = LineButton.of();
@@ -211,6 +211,7 @@ public class TerracottaControllerPage extends StackPane {
progressProperty.set(1);
TextFlow flow = FXUtils.segmentToTextFlow(i18n("terracotta.confirm.desc"), Controllers::onHyperlinkAction);
flow.getStyleClass().add("terracotta-license");
flow.setLineSpacing(4);
LineButton host = LineButton.of();
@@ -626,9 +627,9 @@ public class TerracottaControllerPage extends StackPane {
}
HBox secondLine = new HBox();
secondLine.getStyleClass().add("second-line");
{
Text text = new Text(button.subTitle.get());
text.setFill(new Color(0, 0, 0, 0.5));
TextFlow lblSubtitle = new TextFlow(text);
lblSubtitle.getStyleClass().add("subtitle");

View File

@@ -56,5 +56,4 @@
-monet-on-surface-variant-transparent-38: #45465161;
-monet-surface-container-low-transparent-80: #F5F2FACC;
-monet-secondary-container-transparent-80: #D0D5FDCC;
-warning-tag-background: #F1AEB5;
}

View File

@@ -0,0 +1,18 @@
.hint {
-fx-hyperlink-fill: -monet-inverse-primary;
}
.two-line-list-item {
-fixed-warning-tag-background: #2c0b0e;
}
.log-window {
-fixed-log-text-fill: #FFFFFF;
-fixed-log-trace: #495057;
-fixed-log-debug: #343A40;
-fixed-log-info: #212529;
-fixed-log-warn: #331904;
-fixed-log-error: #58151C;
-fixed-log-fatal: #842029;
-fixed-log-selected: #6C757D;
}

View File

@@ -0,0 +1,18 @@
.hint {
-fx-hyperlink-fill: -monet-primary;
}
.two-line-list-item {
-fixed-warning-tag-background: #F1AEB5;
}
.log-window {
-fixed-log-text-fill: #000000;
-fixed-log-trace: #EEE9E0;
-fixed-log-debug: #EEE9E0;
-fixed-log-info: #FFFFFF;
-fixed-log-warn: #FFEECC;
-fixed-log-error: #FFCCBB;
-fixed-log-fatal: #F7A699;
-fixed-log-selected: #C4C4C4;
}

View File

@@ -48,6 +48,11 @@
-fx-padding: 6;
}
.hint .hyperlink {
-fx-fill: -monet-primary;
-fx-text-fill: -monet-primary;
}
.hint.info {
-fx-background-color: -monet-primary-fixed-dim;
-fx-border-color: -monet-outline;
@@ -76,12 +81,17 @@
}
.hint.warning {
-fx-background-color: -monet-tertiary-container;
-fx-background-color: -monet-tertiary-fixed-dim;
-fx-border-color: -monet-outline;
}
.hint.warning Text, .hint.warning .svg {
-fx-fill: -monet-on-tertiary-container;
-fx-fill: -monet-on-tertiary-fixed;
}
.hint.warning .hyperlink {
-fx-fill: -fx-hyperlink-fill;
-fx-text-fill: -fx-hyperlink-fill;
}
.skin-pane .jfx-text-field {
@@ -145,7 +155,7 @@
}
.announcement .hyperlink {
-fx-fill: #0070E0;
-fx-fill: -monet-primary;
-fx-font-size: 13px;
}
@@ -289,6 +299,10 @@
-fx-font-size: 12px;
}
.two-line-list-item > .second-line > .subtitle Text {
-fx-fill: -monet-on-surface-variant;
}
.two-line-list-item > .first-line > .tag {
-fx-text-fill: -monet-on-secondary-container;
-fx-background-color: -monet-secondary-container;
@@ -299,7 +313,7 @@
.two-line-list-item > .first-line > .tag-warning {
-fx-text-fill: -monet-on-error-container;
-fx-background-color: -warning-tag-background;
-fx-background-color: -fixed-warning-tag-background;
-fx-padding: 2;
-fx-font-weight: normal;
-fx-font-size: 12px;
@@ -320,6 +334,10 @@
-fx-font-size: 15px;
}
.terracotta-license Text {
-fx-fill: -monet-on-surface;
}
.game-crash-window {
-fx-background-color: -monet-surface-container;
}
@@ -329,7 +347,7 @@
}
.game-crash-window .crash-reason-text-flow .hyperlink {
-fx-fill: #0070E0;
-fx-fill: -monet-primary;
}
.wrap-text > HBox > .subtitle {
@@ -826,6 +844,7 @@
.jfx-text-field, .jfx-password-field, .jfx-text-area {
-fx-background-color: -monet-surface-container-highest;
-fx-highlight-fill: -monet-primary;
-fx-text-fill: -monet-on-surface;
-fx-font-weight: BOLD;
-fx-prompt-text-fill: -monet-on-surface-variant;
@@ -1208,46 +1227,67 @@
-fx-text-fill: gray;
}
.log-window {
-fx-background-color: -monet-surface-container;
}
.log-window .scroll-bar .thumb {
-fx-fill: rgba(0, 0, 0, 0.5);
-fx-fill: -monet-surface-tint;
}
.log-window .jfx-button {
-fx-text-fill: -monet-on-surface;
}
.log-window-list-cell {
-fx-text-fill: black;
-fx-text-fill: -fixed-log-text-fill;
-fx-border-width: 0 0 1 0;
-fx-border-color: #dddddd;
-fx-border-color: -monet-outline-variant;
}
.log-window-list-cell:empty {
-fx-border-width: 0;
}
.log-window-list-cell:fatal {
-fx-background-color: #F7A699;
.log-window-list-cell:fatal,
.log-toggle.fatal {
-fx-text-fill: -fixed-log-text-fill;
-fx-background-color: -fixed-log-fatal;
}
.log-window-list-cell:error {
-fx-background-color: #FFCCBB;
.log-window-list-cell:error,
.log-toggle.error {
-fx-text-fill: -fixed-log-text-fill;
-fx-background-color: -fixed-log-error;
}
.log-window-list-cell:warn {
-fx-background-color: #FFEECC;
.log-window-list-cell:warn,
.log-toggle.warn {
-fx-text-fill: -fixed-log-text-fill;
-fx-background-color: -fixed-log-warn;
}
.log-window-list-cell:info {
-fx-background-color: #FFFFFF;
.log-window-list-cell:info,
.log-toggle.info {
-fx-text-fill: -fixed-log-text-fill;
-fx-background-color: -fixed-log-info;
}
.log-window-list-cell:debug {
-fx-background-color: #EEE9E0;
.log-window-list-cell:debug,
.log-toggle.debug {
-fx-text-fill: -fixed-log-text-fill;
-fx-background-color: -fixed-log-debug;
}
.log-window-list-cell:trace {
-fx-background-color: #EEE9E0;
.log-window-list-cell:trace,
.log-toggle.trace {
-fx-text-fill: -fixed-log-text-fill;
-fx-background-color: -fixed-log-trace;
}
.log-window-list-cell:selected {
-fx-background-color: #C4C4C4;
-fx-text-fill: -fixed-log-text-fill;
-fx-background-color: -fixed-log-selected;
}
/*******************************************************************************
@@ -1360,6 +1400,10 @@
-fx-fill: -monet-on-surface;
}
.jfx-combo-box .text-field {
-fx-text-fill: -monet-on-surface;
}
.combo-box-popup .list-view {
-fx-background-color: -monet-surface-container;
}
@@ -1405,17 +1449,6 @@
-fx-min-height: 25px;
}
.jfx-color-picker > .jfx-rippler > .color-box {
-fx-background-color: -monet-primary-seed;
-fx-background-radius: 3px;
-fx-background-insets: 0px;
}
.jfx-color-picker > .jfx-rippler > .color-box:disabled {
-fx-background-color: transparent;
}
.color-palette-region {
}
@@ -1439,6 +1472,18 @@
-fx-max-width: 300;
}
.custom-color-dialog .tab .tab-label {
-fx-text-fill: -monet-on-surface;
}
.custom-color-dialog .tab .jfx-rippler {
-jfx-rippler-fill: -monet-on-surface;
}
.custom-color-dialog .tab-selected-line {
-fx-background-color: -monet-on-surface;
}
/*******************************************************************************
* *
* JFX Decorator *
@@ -1454,14 +1499,9 @@
}
.jfx-decorator-title {
-fx-text-fill: -monet-on-primary-container;
-fx-font-size: 14;
}
.jfx-decorator-title:transparent {
-fx-text-fill: black;
}
.window {
-fx-background-color: transparent;
-fx-padding: 8;

View File

@@ -17,8 +17,6 @@
*/
package org.jackhuang.hmcl.util;
import javafx.scene.paint.Color;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -27,30 +25,24 @@ import java.util.regex.Pattern;
* @author huangyuhui
*/
public enum Log4jLevel {
FATAL(1, Color.web("#F7A699")),
ERROR(2, Color.web("#FFCCBB")),
WARN(3, Color.web("#FFEECC")),
INFO(4, Color.web("#FBFBFB")),
DEBUG(5, Color.web("#EEE9E0")),
TRACE(6, Color.BLUE),
ALL(2147483647, Color.BLACK);
FATAL(1),
ERROR(2),
WARN(3),
INFO(4),
DEBUG(5),
TRACE(6),
ALL(2147483647);
private final int level;
private final Color color;
Log4jLevel(int level, Color color) {
Log4jLevel(int level) {
this.level = level;
this.color = color;
}
public int getLevel() {
return level;
}
public Color getColor() {
return color;
}
public boolean lessOrEqual(Log4jLevel level) {
return this.level <= level.level;
}