Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b74b53fbf9 | ||
|
|
22d982a089 | ||
|
|
07369a3aba | ||
|
|
e69464105a | ||
|
|
38d764c7d1 | ||
|
|
69b415e5ef | ||
|
|
64c63b2af0 |
No files matched your search
@@ -23,6 +23,10 @@ import javafx.scene.control.Label;
|
||||
import javafx.scene.effect.DropShadow;
|
||||
import javafx.scene.layout.Background;
|
||||
import javafx.scene.layout.BackgroundFill;
|
||||
import javafx.scene.layout.Border;
|
||||
import javafx.scene.layout.BorderStroke;
|
||||
import javafx.scene.layout.BorderStrokeStyle;
|
||||
import javafx.scene.layout.BorderWidths;
|
||||
import javafx.scene.layout.CornerRadii;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.paint.Color;
|
||||
@@ -30,8 +34,20 @@ import javafx.scene.shape.Shape;
|
||||
import javafx.util.Duration;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
|
||||
/// JFoenix button skin with Anthropic skill tactile press feedback.
|
||||
///
|
||||
/// Upstream skin rewrote Modena's cyan `:pressed` fill to transparent and thereby
|
||||
/// also ate author CSS press backgrounds. This build keeps CSS fills, maps the
|
||||
/// Modena cyan to a skill wash, and paints an unmistakable armed overlay
|
||||
/// (background + border + opacity) on every JFXButton.
|
||||
public class JFXButtonSkin extends ButtonSkin {
|
||||
private static final Color SKILL_PRESS_FILL = Color.web("#D97757", 0.40);
|
||||
private static final Color SKILL_PRESS_BORDER = Color.web("#C96442");
|
||||
private static final Color SKILL_PRESS_DIM = Color.web("#141413", 0.28);
|
||||
|
||||
private final StackPane buttonContainer = new StackPane();
|
||||
/// Full-bleed press veil — opacity toggled from armed/pressed, independent of CSS.
|
||||
private final StackPane pressOverlay = new StackPane();
|
||||
private final JFXRippler buttonRippler = new JFXRippler(new StackPane()) {
|
||||
protected Node getMask() {
|
||||
StackPane mask = new StackPane();
|
||||
@@ -64,6 +80,17 @@ public class JFXButtonSkin extends ButtonSkin {
|
||||
|
||||
public JFXButtonSkin(JFXButton button) {
|
||||
super(button);
|
||||
|
||||
this.pressOverlay.setMouseTransparent(true);
|
||||
this.pressOverlay.setOpacity(0);
|
||||
this.pressOverlay.setBackground(new Background(new BackgroundFill(
|
||||
SKILL_PRESS_FILL, this.defaultRadii, Insets.EMPTY)));
|
||||
this.pressOverlay.setBorder(new Border(new BorderStroke(
|
||||
SKILL_PRESS_BORDER,
|
||||
BorderStrokeStyle.SOLID,
|
||||
this.defaultRadii,
|
||||
new BorderWidths(2.0))));
|
||||
|
||||
this.getSkinnable().armedProperty().addListener((o, oldVal, newVal) -> {
|
||||
if (newVal) {
|
||||
this.releaseManualRippler = this.buttonRippler.createManualRipple();
|
||||
@@ -81,21 +108,23 @@ public class JFXButtonSkin extends ButtonSkin {
|
||||
this.clickedAnimation.play();
|
||||
}
|
||||
}
|
||||
|
||||
this.updatePressOverlay(button);
|
||||
});
|
||||
this.buttonContainer.getChildren().add(this.buttonRippler);
|
||||
this.buttonContainer.getChildren().addAll(this.buttonRippler, this.pressOverlay);
|
||||
button.buttonTypeProperty().addListener((o, oldVal, newVal) -> this.updateButtonType(newVal));
|
||||
button.setOnMousePressed((e) -> {
|
||||
if (this.clickedAnimation != null) {
|
||||
this.clickedAnimation.setRate(1.0F);
|
||||
this.clickedAnimation.play();
|
||||
}
|
||||
this.updatePressOverlay(button);
|
||||
});
|
||||
button.setOnMouseReleased((e) -> {
|
||||
if (this.clickedAnimation != null) {
|
||||
this.clickedAnimation.setRate(-1.0F);
|
||||
this.clickedAnimation.play();
|
||||
}
|
||||
this.updatePressOverlay(button);
|
||||
});
|
||||
|
||||
ReadOnlyBooleanProperty focusVisibleProperty = FXUtils.focusVisibleProperty(button);
|
||||
@@ -111,20 +140,34 @@ public class JFXButtonSkin extends ButtonSkin {
|
||||
this.buttonRippler.hideOverlay();
|
||||
}
|
||||
});
|
||||
button.pressedProperty().addListener((o, oldVal, newVal) -> this.buttonRippler.hideOverlay());
|
||||
button.pressedProperty().addListener((o, oldVal, newVal) -> {
|
||||
this.buttonRippler.hideOverlay();
|
||||
this.updatePressOverlay(button);
|
||||
});
|
||||
button.setPickOnBounds(false);
|
||||
this.buttonContainer.setPickOnBounds(false);
|
||||
this.buttonContainer.shapeProperty().bind(this.getSkinnable().shapeProperty());
|
||||
this.buttonContainer.borderProperty().bind(this.getSkinnable().borderProperty());
|
||||
// Mirror CSS background onto the container. Never call button.setBackground() here —
|
||||
// that used to stomp author :pressed fills (and wiped Modena cyan to transparent).
|
||||
this.buttonContainer.backgroundProperty().bind(Bindings.createObjectBinding(() -> {
|
||||
if (button.getBackground() == null || this.isJavaDefaultBackground(button.getBackground()) || this.isJavaDefaultClickedBackground(button.getBackground())) {
|
||||
button.setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, this.defaultRadii, null)));
|
||||
Background background = button.getBackground();
|
||||
if (background == null || background.getFills().isEmpty() || this.isJavaDefaultBackground(background)) {
|
||||
return new Background(new BackgroundFill(Color.TRANSPARENT, this.defaultRadii, Insets.EMPTY));
|
||||
}
|
||||
if (this.isJavaDefaultClickedBackground(background)) {
|
||||
CornerRadii radii = background.getFills().get(0).getRadii();
|
||||
return new Background(new BackgroundFill(SKILL_PRESS_FILL,
|
||||
radii != null ? radii : this.defaultRadii, Insets.EMPTY));
|
||||
}
|
||||
|
||||
try {
|
||||
return new Background(new BackgroundFill(this.getSkinnable().getBackground() != null ? this.getSkinnable().getBackground().getFills().get(0).getFill() : Color.TRANSPARENT, this.getSkinnable().getBackground() != null ? this.getSkinnable().getBackground().getFills().get(0).getRadii() : this.defaultRadii, Insets.EMPTY));
|
||||
BackgroundFill fill = background.getFills().get(0);
|
||||
return new Background(new BackgroundFill(
|
||||
fill.getFill(),
|
||||
fill.getRadii() != null ? fill.getRadii() : this.defaultRadii,
|
||||
Insets.EMPTY));
|
||||
} catch (Exception var3) {
|
||||
return this.getSkinnable().getBackground();
|
||||
return background;
|
||||
}
|
||||
}, this.getSkinnable().backgroundProperty()));
|
||||
button.ripplerFillProperty().addListener((o, oldVal, newVal) -> this.buttonRippler.setRipplerFill(newVal));
|
||||
@@ -134,6 +177,40 @@ public class JFXButtonSkin extends ButtonSkin {
|
||||
|
||||
this.updateButtonType(button.getButtonType());
|
||||
this.updateChildren();
|
||||
this.updatePressOverlay(button);
|
||||
}
|
||||
|
||||
/// Shows a hard-to-miss press veil: orange wash + warm border; dimming on raised CTAs.
|
||||
private void updatePressOverlay(JFXButton button) {
|
||||
boolean active = !button.isDisabled() && (button.isArmed() || button.isPressed());
|
||||
boolean raised = button.getButtonType() == JFXButton.ButtonType.RAISED
|
||||
|| button.getStyleClass().contains("jfx-button-raised")
|
||||
|| button.getStyleClass().contains("launch-button")
|
||||
|| button.getStyleClass().contains("menu-button");
|
||||
|
||||
CornerRadii radii = this.defaultRadii;
|
||||
Background bg = button.getBackground();
|
||||
if (bg != null && !bg.getFills().isEmpty() && bg.getFills().get(0).getRadii() != null) {
|
||||
radii = bg.getFills().get(0).getRadii();
|
||||
}
|
||||
|
||||
Color fill = raised ? SKILL_PRESS_DIM : SKILL_PRESS_FILL;
|
||||
this.pressOverlay.setBackground(new Background(new BackgroundFill(fill, radii, Insets.EMPTY)));
|
||||
this.pressOverlay.setBorder(new Border(new BorderStroke(
|
||||
SKILL_PRESS_BORDER,
|
||||
BorderStrokeStyle.SOLID,
|
||||
radii,
|
||||
new BorderWidths(active ? 2.0 : 0.0))));
|
||||
this.pressOverlay.setOpacity(active ? 1.0 : 0.0);
|
||||
// Drive opacity via style class so CSS keeps ownership (no setOpacity stomping).
|
||||
final String armedClass = "anthropic-armed";
|
||||
if (active) {
|
||||
if (!button.getStyleClass().contains(armedClass)) {
|
||||
button.getStyleClass().add(armedClass);
|
||||
}
|
||||
} else {
|
||||
button.getStyleClass().remove(armedClass);
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateChildren() {
|
||||
@@ -186,6 +263,7 @@ public class JFXButtonSkin extends ButtonSkin {
|
||||
|
||||
private boolean isJavaDefaultClickedBackground(Background background) {
|
||||
try {
|
||||
// Modena default armed cyan — replace with skill wash instead of wiping to transparent.
|
||||
return "0x039ed3ff".equals(background.getFills().get(0).getFill().toString());
|
||||
} catch (Exception var3) {
|
||||
return false;
|
||||
|
||||
@@ -19,8 +19,8 @@ public final class Metadata {
|
||||
private Metadata() {
|
||||
}
|
||||
|
||||
public static final String NAME = "HMCL";
|
||||
public static final String FULL_NAME = "Hello Minecraft! Launcher";
|
||||
public static final String NAME = "TDLM";
|
||||
public static final String FULL_NAME = "TDLM";
|
||||
public static final String VERSION = System.getProperty("hmcl.version.override", JarUtils.getAttribute("hmcl.version", "@develop@"));
|
||||
|
||||
/// Explicit Application User Model ID used for Windows taskbar grouping and pinning.
|
||||
|
||||
@@ -36,7 +36,22 @@ public final class FontManager {
|
||||
|
||||
public static final double DEFAULT_FONT_SIZE = 12.0f;
|
||||
|
||||
/// Bundled Anthropic UI title face (Source Han Sans SC Bold subset).
|
||||
public static final String TITLE_FONT_FAMILY = "Source Han Sans SC";
|
||||
|
||||
/// Bundled Anthropic UI body face (Source Han Serif SC Regular subset).
|
||||
public static final String BODY_FONT_FAMILY = "Source Han Serif SC";
|
||||
|
||||
private static final String BUNDLED_TITLE_FONT = "/assets/fonts/SourceHanSansSC-Bold.otf";
|
||||
private static final String BUNDLED_BODY_FONT = "/assets/fonts/SourceHanSerifSC-Regular.otf";
|
||||
|
||||
private static volatile boolean bundledFontsLoaded;
|
||||
private static volatile @Nullable String loadedTitleFamily;
|
||||
private static volatile @Nullable String loadedBodyFamily;
|
||||
|
||||
private static final Lazy<Font> DEFAULT_FONT = new Lazy<>(() -> {
|
||||
ensureBundledFontsLoaded();
|
||||
|
||||
Font font;
|
||||
|
||||
// Recommended
|
||||
@@ -70,6 +85,11 @@ public final class FontManager {
|
||||
return font;
|
||||
}
|
||||
|
||||
// Anthropic UI hard-bind: bundled Source Han Serif body face
|
||||
if (loadedBodyFamily != null) {
|
||||
return Font.font(loadedBodyFamily, DEFAULT_FONT_SIZE);
|
||||
}
|
||||
|
||||
// Default
|
||||
|
||||
String fcMatchPattern;
|
||||
@@ -83,8 +103,62 @@ public final class FontManager {
|
||||
private static final ObjectProperty<FontReference> font = new SimpleObjectProperty<>();
|
||||
|
||||
static {
|
||||
ensureBundledFontsLoaded();
|
||||
updateFont();
|
||||
LOG.info("Font: " + (font.get() != null ? font.get().family() : "System"));
|
||||
LOG.info("Font: " + (font.get() != null ? font.get().family() : "System")
|
||||
+ ", title=" + Objects.requireNonNullElse(loadedTitleFamily, "unavailable")
|
||||
+ ", body=" + Objects.requireNonNullElse(loadedBodyFamily, "unavailable"));
|
||||
}
|
||||
|
||||
/// Registers the bundled Source Han subsets so CSS family names resolve.
|
||||
public static void ensureBundledFontsLoaded() {
|
||||
if (bundledFontsLoaded) {
|
||||
return;
|
||||
}
|
||||
synchronized (FontManager.class) {
|
||||
if (bundledFontsLoaded) {
|
||||
return;
|
||||
}
|
||||
loadedTitleFamily = loadBundledFont(BUNDLED_TITLE_FONT, TITLE_FONT_FAMILY);
|
||||
loadedBodyFamily = loadBundledFont(BUNDLED_BODY_FONT, BODY_FONT_FAMILY);
|
||||
bundledFontsLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the resolved title font family after bundled registration, or the hard-bind name.
|
||||
public static String getTitleFontFamily() {
|
||||
ensureBundledFontsLoaded();
|
||||
return Objects.requireNonNullElse(loadedTitleFamily, TITLE_FONT_FAMILY);
|
||||
}
|
||||
|
||||
/// Returns the resolved body font family after bundled registration, or the hard-bind name.
|
||||
public static String getBodyFontFamily() {
|
||||
ensureBundledFontsLoaded();
|
||||
return Objects.requireNonNullElse(loadedBodyFamily, BODY_FONT_FAMILY);
|
||||
}
|
||||
|
||||
private static @Nullable String loadBundledFont(String resourcePath, String expectedFamily) {
|
||||
try (var input = FontManager.class.getResourceAsStream(resourcePath)) {
|
||||
if (input == null) {
|
||||
LOG.warning("Bundled font missing: " + resourcePath);
|
||||
return null;
|
||||
}
|
||||
Font loaded = Font.loadFont(input, DEFAULT_FONT_SIZE);
|
||||
if (loaded == null) {
|
||||
LOG.warning("Failed to decode bundled font: " + resourcePath);
|
||||
return null;
|
||||
}
|
||||
String family = loaded.getFamily();
|
||||
LOG.info("Loaded bundled font " + resourcePath + " as family '" + family + "'");
|
||||
if (!expectedFamily.equalsIgnoreCase(family)) {
|
||||
LOG.warning("Bundled font family mismatch for " + resourcePath
|
||||
+ ": expected '" + expectedFamily + "', got '" + family + "'");
|
||||
}
|
||||
return family;
|
||||
} catch (Exception e) {
|
||||
LOG.warning("Failed to load bundled font " + resourcePath, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateFont() {
|
||||
@@ -96,7 +170,7 @@ public final class FontManager {
|
||||
|
||||
if (fontFamily == null) {
|
||||
Font defaultFont = DEFAULT_FONT.get();
|
||||
font.set(defaultFont != null ? new FontReference(defaultFont) : null);
|
||||
font.set(defaultFont != null ? new FontReference(defaultFont) : new FontReference(BODY_FONT_FAMILY));
|
||||
} else {
|
||||
font.set(new FontReference(fontFamily));
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import javafx.scene.paint.Color;
|
||||
import org.glavo.monetfx.Brightness;
|
||||
import org.glavo.monetfx.ColorRole;
|
||||
import org.glavo.monetfx.ColorScheme;
|
||||
import org.jackhuang.hmcl.theme.ResolvedTheme;
|
||||
import org.jackhuang.hmcl.theme.ThemeColor;
|
||||
import org.jackhuang.hmcl.theme.Themes;
|
||||
|
||||
@@ -26,6 +25,8 @@ 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;
|
||||
/// Loaded after root.css so solid :pressed fills beat Monet / Modena cyan.
|
||||
private static final int PRESS_FEEDBACK_SHEET_INDEX = 4;
|
||||
|
||||
private static final ObservableList<String> stylesheets;
|
||||
|
||||
@@ -34,9 +35,15 @@ public final class StyleSheets {
|
||||
getFontStyleSheet(),
|
||||
getThemeStyleSheet(),
|
||||
getBrightnessStyleSheet(),
|
||||
"/assets/css/root.css"
|
||||
"/assets/css/root.css",
|
||||
"/assets/css/press-feedback.css"
|
||||
};
|
||||
stylesheets = FXCollections.observableList(Arrays.asList(array));
|
||||
// Keep the constant honest for future inserts ahead of press-feedback.
|
||||
if (stylesheets.size() <= PRESS_FEEDBACK_SHEET_INDEX
|
||||
|| !"/assets/css/press-feedback.css".equals(stylesheets.get(PRESS_FEEDBACK_SHEET_INDEX))) {
|
||||
throw new IllegalStateException("press-feedback.css must remain the last stylesheet");
|
||||
}
|
||||
|
||||
FontManager.fontProperty().addListener(o -> stylesheets.set(FONT_STYLE_SHEET_INDEX, getFontStyleSheet()));
|
||||
Themes.colorSchemeProperty().addListener(o -> {
|
||||
@@ -50,51 +57,72 @@ public final class StyleSheets {
|
||||
}
|
||||
|
||||
private static String getFontStyleSheet() {
|
||||
final String defaultCss = "/assets/css/font.css";
|
||||
FontManager.ensureBundledFontsLoaded();
|
||||
|
||||
final FontManager.FontReference font = FontManager.getFont();
|
||||
|
||||
if (font == null || "System".equals(font.family()))
|
||||
return defaultCss;
|
||||
|
||||
String fontFamily = font.family();
|
||||
String style = font.style();
|
||||
String bodyFamily = FontManager.getBodyFontFamily();
|
||||
String titleFamily = FontManager.getTitleFontFamily();
|
||||
String weight = null;
|
||||
String posture = null;
|
||||
|
||||
if (style != null) {
|
||||
style = style.toLowerCase(Locale.ROOT);
|
||||
// Explicit user / env override replaces body face only; titles stay Source Han Sans Bold.
|
||||
if (font != null && !"System".equals(font.family())
|
||||
&& !FontManager.BODY_FONT_FAMILY.equalsIgnoreCase(font.family())
|
||||
&& !FontManager.TITLE_FONT_FAMILY.equalsIgnoreCase(font.family())) {
|
||||
bodyFamily = font.family();
|
||||
String style = font.style();
|
||||
if (style != null) {
|
||||
style = style.toLowerCase(Locale.ROOT);
|
||||
|
||||
if (style.contains("thin"))
|
||||
weight = "100";
|
||||
else if (style.contains("extralight") || style.contains("extra light") || style.contains("ultralight") | style.contains("ultra light"))
|
||||
weight = "200";
|
||||
else if (style.contains("medium"))
|
||||
weight = "500";
|
||||
else if (style.contains("semibold") || style.contains("semi bold") || style.contains("demibold") || style.contains("demi bold"))
|
||||
weight = "600";
|
||||
else if (style.contains("extrabold") || style.contains("extra bold") || style.contains("ultrabold") || style.contains("ultra bold"))
|
||||
weight = "800";
|
||||
else if (style.contains("black") || style.contains("heavy"))
|
||||
weight = "900";
|
||||
else if (style.contains("light"))
|
||||
weight = "lighter";
|
||||
else if (style.contains("bold"))
|
||||
weight = "bold";
|
||||
if (style.contains("thin"))
|
||||
weight = "100";
|
||||
else if (style.contains("extralight") || style.contains("extra light") || style.contains("ultralight") | style.contains("ultra light"))
|
||||
weight = "200";
|
||||
else if (style.contains("medium"))
|
||||
weight = "500";
|
||||
else if (style.contains("semibold") || style.contains("semi bold") || style.contains("demibold") || style.contains("demi bold"))
|
||||
weight = "600";
|
||||
else if (style.contains("extrabold") || style.contains("extra bold") || style.contains("ultrabold") || style.contains("ultra bold"))
|
||||
weight = "800";
|
||||
else if (style.contains("black") || style.contains("heavy"))
|
||||
weight = "900";
|
||||
else if (style.contains("light"))
|
||||
weight = "lighter";
|
||||
else if (style.contains("bold"))
|
||||
weight = "bold";
|
||||
|
||||
posture = style.contains("italic") || style.contains("oblique") ? "italic" : null;
|
||||
posture = style.contains("italic") || style.contains("oblique") ? "italic" : null;
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(".root {");
|
||||
builder.append("-fx-font-family:\"").append(fontFamily).append("\";");
|
||||
|
||||
builder.append("-fx-font-family:\"").append(bodyFamily).append("\", \"Source Han Serif\", serif;");
|
||||
if (weight != null)
|
||||
builder.append("-fx-font-weight:").append(weight).append(";");
|
||||
|
||||
if (posture != null)
|
||||
builder.append("-fx-font-style:").append(posture).append(";");
|
||||
builder.append("}\n");
|
||||
|
||||
builder.append('}');
|
||||
builder.append("""
|
||||
.jfx-decorator-title,
|
||||
.jfx-tool-bar-second .label,
|
||||
.title-label,
|
||||
.class-title,
|
||||
.class-title Text,
|
||||
.jfx-layout-heading,
|
||||
.launch-pane > .launch-button .label,
|
||||
.announcement .title,
|
||||
.method-title,
|
||||
.radio-button-title-label,
|
||||
.two-line-list-item > .title,
|
||||
.advanced-list-item > .rippler-container > .container > .title,
|
||||
.jfx-button-raised,
|
||||
.jfx-button-raised * {
|
||||
""");
|
||||
builder.append("-fx-font-family:\"").append(titleFamily).append("\", \"Source Han Sans\", \"思源黑体\", sans-serif;");
|
||||
builder.append("-fx-font-weight:bold;");
|
||||
builder.append("}\n");
|
||||
|
||||
return toStyleSheetUri(builder.toString());
|
||||
}
|
||||
@@ -121,13 +149,133 @@ public final class StyleSheets {
|
||||
.append(";\n");
|
||||
}
|
||||
|
||||
private static void putHex(StringBuilder builder, String name, String hex) {
|
||||
builder.append(" ").append(name).append(": ").append(hex).append(";\n");
|
||||
}
|
||||
|
||||
/// Maps anthropic-style-cn skill base.css light/dark pairs onto Monet roles so existing CSS flips end-to-end.
|
||||
private static void applySkillTokenOverrides(StringBuilder builder, boolean dark) {
|
||||
// Skill accent (CTA / focus) — never Material blue.
|
||||
final String accent = "#D97757";
|
||||
final String accentWarm = "#C96442";
|
||||
final String error = "#C0453A";
|
||||
final String onAccent = "#FAF9F5";
|
||||
|
||||
if (dark) {
|
||||
putHex(builder, "-monet-surface", "#1A1916");
|
||||
putHex(builder, "-monet-background", "#1A1916");
|
||||
putHex(builder, "-monet-surface-bright", "#2C2B26");
|
||||
putHex(builder, "-monet-surface-dim", "#141413");
|
||||
putHex(builder, "-monet-surface-container-lowest", "#141413");
|
||||
putHex(builder, "-monet-surface-container-low", "#222119");
|
||||
putHex(builder, "-monet-surface-container", "#222119");
|
||||
putHex(builder, "-monet-surface-container-high", "#2C2B26");
|
||||
putHex(builder, "-monet-surface-container-highest", "#2C2B26");
|
||||
putHex(builder, "-monet-surface-variant", "#2C2B26");
|
||||
putHex(builder, "-monet-on-surface", "#EAE7DC");
|
||||
putHex(builder, "-monet-on-background", "#EAE7DC");
|
||||
putHex(builder, "-monet-on-surface-variant", "#9D9A91");
|
||||
putHex(builder, "-monet-outline", "#3A3830");
|
||||
putHex(builder, "-monet-outline-variant", "#2E2D27");
|
||||
putHex(builder, "-monet-inverse-surface", "#F5F3EC");
|
||||
putHex(builder, "-monet-inverse-on-surface", "#141413");
|
||||
putHex(builder, "-monet-secondary-container", "#2C2B26");
|
||||
putHex(builder, "-monet-on-secondary-container", "#EAE7DC");
|
||||
putHex(builder, "-anthropic-shadow", "rgba(0, 0, 0, 0.50)");
|
||||
putHex(builder, "-anthropic-shadow-strong", "rgba(0, 0, 0, 0.65)");
|
||||
putHex(builder, "-anthropic-scrollbar-track", "#222119");
|
||||
putHex(builder, "-anthropic-scrollbar-thumb", "#5C5A54");
|
||||
putHex(builder, "-anthropic-hover-wash", "rgba(234, 231, 220, 0.08)");
|
||||
putHex(builder, "-anthropic-press-wash", "rgba(234, 231, 220, 0.14)");
|
||||
} else {
|
||||
putHex(builder, "-monet-surface", "#ECE9E0");
|
||||
putHex(builder, "-monet-background", "#ECE9E0");
|
||||
putHex(builder, "-monet-surface-bright", "#FDFCF8");
|
||||
putHex(builder, "-monet-surface-dim", "#D8D5CC");
|
||||
putHex(builder, "-monet-surface-container-lowest", "#FDFCF8");
|
||||
putHex(builder, "-monet-surface-container-low", "#F5F3EC");
|
||||
putHex(builder, "-monet-surface-container", "#F5F3EC");
|
||||
putHex(builder, "-monet-surface-container-high", "#FDFCF8");
|
||||
putHex(builder, "-monet-surface-container-highest", "#FDFCF8");
|
||||
putHex(builder, "-monet-surface-variant", "#E8E6DC");
|
||||
putHex(builder, "-monet-on-surface", "#141413");
|
||||
putHex(builder, "-monet-on-background", "#141413");
|
||||
putHex(builder, "-monet-on-surface-variant", "#6B6860");
|
||||
putHex(builder, "-monet-outline", "#D8D5CC");
|
||||
putHex(builder, "-monet-outline-variant", "#E8E6DC");
|
||||
putHex(builder, "-monet-inverse-surface", "#141413");
|
||||
putHex(builder, "-monet-inverse-on-surface", "#FAF9F5");
|
||||
putHex(builder, "-monet-secondary-container", "#E8E6DC");
|
||||
putHex(builder, "-monet-on-secondary-container", "#141413");
|
||||
putHex(builder, "-anthropic-shadow", "rgba(20, 20, 19, 0.10)");
|
||||
putHex(builder, "-anthropic-shadow-strong", "rgba(20, 20, 19, 0.16)");
|
||||
putHex(builder, "-anthropic-scrollbar-track", "#E8E6DC");
|
||||
putHex(builder, "-anthropic-scrollbar-thumb", "#9B9890");
|
||||
putHex(builder, "-anthropic-hover-wash", "rgba(20, 20, 19, 0.06)");
|
||||
putHex(builder, "-anthropic-press-wash", "rgba(20, 20, 19, 0.12)");
|
||||
}
|
||||
|
||||
putHex(builder, "-monet-primary", accent);
|
||||
putHex(builder, "-monet-primary-seed", accent);
|
||||
putHex(builder, "-monet-on-primary", onAccent);
|
||||
putHex(builder, "-monet-primary-container", accentWarm);
|
||||
putHex(builder, "-monet-on-primary-container", onAccent);
|
||||
putHex(builder, "-monet-primary-fixed", dark ? "#4A2E24" : "#F6E0D6");
|
||||
putHex(builder, "-monet-primary-fixed-dim", dark ? "#C96442" : accent);
|
||||
putHex(builder, "-monet-on-primary-fixed", dark ? "#FAF9F5" : "#141413");
|
||||
putHex(builder, "-monet-on-primary-fixed-variant", dark ? "#EAE7DC" : "#6B6860");
|
||||
putHex(builder, "-monet-inverse-primary", accent);
|
||||
putHex(builder, "-monet-surface-tint", accent);
|
||||
|
||||
// Secondary / tertiary: warm orange family only — kill Monet teal/purple companions.
|
||||
putHex(builder, "-monet-secondary", accentWarm);
|
||||
putHex(builder, "-monet-on-secondary", onAccent);
|
||||
putHex(builder, "-monet-secondary-fixed", dark ? "#3A2A22" : "#F0E4DC");
|
||||
putHex(builder, "-monet-secondary-fixed-dim", dark ? accentWarm : accent);
|
||||
putHex(builder, "-monet-on-secondary-fixed", dark ? "#EAE7DC" : "#141413");
|
||||
putHex(builder, "-monet-on-secondary-fixed-variant", dark ? "#9D9A91" : "#6B6860");
|
||||
putHex(builder, "-monet-tertiary", accent);
|
||||
putHex(builder, "-monet-on-tertiary", onAccent);
|
||||
putHex(builder, "-monet-tertiary-container", accentWarm);
|
||||
putHex(builder, "-monet-on-tertiary-container", onAccent);
|
||||
putHex(builder, "-monet-tertiary-fixed", dark ? "#4A2E24" : "#F6E0D6");
|
||||
putHex(builder, "-monet-tertiary-fixed-dim", dark ? accentWarm : accent);
|
||||
putHex(builder, "-monet-on-tertiary-fixed", dark ? "#FAF9F5" : "#141413");
|
||||
putHex(builder, "-monet-on-tertiary-fixed-variant", dark ? "#EAE7DC" : "#6B6860");
|
||||
|
||||
putHex(builder, "-monet-error", error);
|
||||
putHex(builder, "-monet-on-error", onAccent);
|
||||
putHex(builder, "-monet-error-container", dark ? "#4A1A1F" : "#FCEBEB");
|
||||
putHex(builder, "-monet-on-error-container", dark ? "#F9DEDC" : "#410E0B");
|
||||
|
||||
putHex(builder, "-anthropic-accent", accent);
|
||||
putHex(builder, "-anthropic-accent-warm", accentWarm);
|
||||
putHex(builder, "-anthropic-focus-ring", accent);
|
||||
putHex(builder, "-anthropic-focus-glow", "rgba(217, 119, 87, 0.35)");
|
||||
putHex(builder, "-anthropic-primary-hover", dark ? "#E08A6C" : accentWarm);
|
||||
putHex(builder, "-anthropic-primary-press", dark ? "#EFA08A" : "#B4573A");
|
||||
putHex(builder, "-anthropic-primary-container-hover", dark ? "#D97757" : "#B4573A");
|
||||
putHex(builder, "-anthropic-primary-container-press", dark ? "#E08A6C" : "#9A4A32");
|
||||
putHex(builder, "-anthropic-selected-wash", dark ? "rgba(217, 119, 87, 0.22)" : "rgba(217, 119, 87, 0.14)");
|
||||
// Stronger wash for tiny icon buttons where 6–8% alpha is invisible.
|
||||
putHex(builder, "-anthropic-icon-hover-wash", dark ? "rgba(234, 231, 220, 0.14)" : "rgba(20, 20, 19, 0.10)");
|
||||
putHex(builder, "-anthropic-icon-press-wash", dark ? "rgba(217, 119, 87, 0.32)" : "rgba(217, 119, 87, 0.22)");
|
||||
|
||||
// Transparent helpers consumed by root.css
|
||||
putHex(builder, "-monet-primary-transparent-50", accent + "80");
|
||||
putHex(builder, "-monet-primary-transparent-80", accent + "CC");
|
||||
putHex(builder, "-monet-surface-transparent-50", dark ? "#1A191680" : "#ECE9E080");
|
||||
putHex(builder, "-monet-surface-transparent-80", dark ? "#1A1916CC" : "#ECE9E0CC");
|
||||
putHex(builder, "-monet-surface-container-low-transparent-80", dark ? "#222119CC" : "#F5F3ECCC");
|
||||
putHex(builder, "-monet-secondary-container-transparent-50", dark ? "#2C2B2680" : "#E8E6DC80");
|
||||
putHex(builder, "-monet-secondary-container-transparent-80", dark ? "#2C2B26CC" : "#E8E6DCCC");
|
||||
putHex(builder, "-monet-on-surface-variant-transparent-38", dark ? "#9D9A9161" : "#6B686061");
|
||||
putHex(builder, "-monet-inverse-surface-transparent-80", dark ? "#F5F3ECCC" : "#141413CC");
|
||||
}
|
||||
|
||||
private static String getThemeStyleSheet() {
|
||||
final String blueCss = "/assets/css/blue.css";
|
||||
|
||||
if (ResolvedTheme.DEFAULT.equals(Themes.getTheme()))
|
||||
return blueCss;
|
||||
|
||||
ColorScheme scheme = Themes.getColorScheme();
|
||||
boolean dark = scheme.getBrightness() == Brightness.DARK;
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("* {\n");
|
||||
@@ -147,6 +295,9 @@ public final class StyleSheets {
|
||||
addColor(builder, scheme, ColorRole.SECONDARY_CONTAINER, 0.8);
|
||||
addColor(builder, scheme, ColorRole.INVERSE_SURFACE, 0.8);
|
||||
|
||||
// Skill authority: overwrite Monet surfaces/text/borders/accent for light+dark.
|
||||
applySkillTokenOverrides(builder, dark);
|
||||
|
||||
builder.append("}\n");
|
||||
return toStyleSheetUri(builder.toString());
|
||||
}
|
||||
|
||||
@@ -24,10 +24,19 @@ public record ResolvedTheme(ThemeColor primaryColorSeed,
|
||||
|
||||
/// Converts these resolved values to a MonetFX color scheme.
|
||||
///
|
||||
/// Warm secondary / tertiary / neutral seeds keep Monet from inventing teal or purple
|
||||
/// companions next to the orange accent; CSS + [SkillPalette] still pin exact skill hex.
|
||||
///
|
||||
/// @return the generated color scheme
|
||||
public ColorScheme toColorScheme() {
|
||||
boolean darkNeutrals = brightness == Brightness.DARK;
|
||||
return ColorScheme.newBuilder()
|
||||
.setPrimaryColorSeed(primaryColorSeed.color())
|
||||
.setSecondaryColorSeed(SkillPalette.ACCENT_WARM)
|
||||
.setTertiaryColorSeed(SkillPalette.ACCENT)
|
||||
.setNeutralColorSeed(darkNeutrals ? SkillPalette.DARK_SURFACE : SkillPalette.LIGHT_SURFACE)
|
||||
.setNeutralVariantColorSeed(darkNeutrals ? SkillPalette.DARK_OUTLINE : SkillPalette.LIGHT_OUTLINE)
|
||||
.setErrorColorSeed(SkillPalette.ERROR)
|
||||
.setColorStyle(colorStyle)
|
||||
.setBrightness(brightness)
|
||||
.setSpecVersion(ColorSpecVersion.SPEC_2025)
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2025 The HMCL Authors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH AdditionRef-HMCL
|
||||
package org.jackhuang.hmcl.theme;
|
||||
|
||||
import javafx.scene.paint.Color;
|
||||
import org.glavo.monetfx.Brightness;
|
||||
import org.glavo.monetfx.ColorScheme;
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
|
||||
/// Anthropic-style-cn skill light/dark palette used for JavaFX chrome that bypasses CSS.
|
||||
///
|
||||
/// Monet's generated scheme from an orange seed still drifts pink / teal; these exact
|
||||
/// skill tokens keep title bar, frame, and theme-color backgrounds aligned with
|
||||
/// [org.jackhuang.hmcl.setting.StyleSheets] CSS overrides.
|
||||
@NotNullByDefault
|
||||
public final class SkillPalette {
|
||||
public static final String ACCENT_HEX = "#D97757";
|
||||
public static final String ACCENT_WARM_HEX = "#C96442";
|
||||
public static final String ERROR_HEX = "#C0453A";
|
||||
public static final String ON_ACCENT_HEX = "#FAF9F5";
|
||||
|
||||
public static final Color ACCENT = Color.web(ACCENT_HEX);
|
||||
public static final Color ACCENT_WARM = Color.web(ACCENT_WARM_HEX);
|
||||
public static final Color ERROR = Color.web(ERROR_HEX);
|
||||
public static final Color ON_ACCENT = Color.web(ON_ACCENT_HEX);
|
||||
|
||||
public static final Color LIGHT_SURFACE = Color.web("#ECE9E0");
|
||||
public static final Color LIGHT_SURFACE_RAISED = Color.web("#F5F3EC");
|
||||
public static final Color LIGHT_SURFACE_OVERLAY = Color.web("#FDFCF8");
|
||||
public static final Color LIGHT_SURFACE_VARIANT = Color.web("#E8E6DC");
|
||||
public static final Color LIGHT_ON_SURFACE = Color.web("#141413");
|
||||
public static final Color LIGHT_ON_SURFACE_VARIANT = Color.web("#6B6860");
|
||||
public static final Color LIGHT_OUTLINE = Color.web("#D8D5CC");
|
||||
public static final Color LIGHT_OUTLINE_VARIANT = Color.web("#E8E6DC");
|
||||
|
||||
public static final Color DARK_SURFACE = Color.web("#1A1916");
|
||||
public static final Color DARK_SURFACE_RAISED = Color.web("#222119");
|
||||
public static final Color DARK_SURFACE_OVERLAY = Color.web("#2C2B26");
|
||||
public static final Color DARK_SURFACE_VARIANT = Color.web("#2C2B26");
|
||||
public static final Color DARK_ON_SURFACE = Color.web("#EAE7DC");
|
||||
public static final Color DARK_ON_SURFACE_VARIANT = Color.web("#9D9A91");
|
||||
public static final Color DARK_OUTLINE = Color.web("#3A3830");
|
||||
public static final Color DARK_OUTLINE_VARIANT = Color.web("#2E2D27");
|
||||
|
||||
private SkillPalette() {
|
||||
}
|
||||
|
||||
public static boolean isDark(ColorScheme scheme) {
|
||||
return scheme.getBrightness() == Brightness.DARK;
|
||||
}
|
||||
|
||||
public static boolean isDark() {
|
||||
return isDark(Themes.getColorScheme());
|
||||
}
|
||||
|
||||
public static Color surface(boolean dark) {
|
||||
return dark ? DARK_SURFACE : LIGHT_SURFACE;
|
||||
}
|
||||
|
||||
public static Color surfaceContainer(boolean dark) {
|
||||
return dark ? DARK_SURFACE_RAISED : LIGHT_SURFACE_RAISED;
|
||||
}
|
||||
|
||||
public static Color surfaceContainerHigh(boolean dark) {
|
||||
return dark ? DARK_SURFACE_OVERLAY : LIGHT_SURFACE_OVERLAY;
|
||||
}
|
||||
|
||||
public static Color onSurface(boolean dark) {
|
||||
return dark ? DARK_ON_SURFACE : LIGHT_ON_SURFACE;
|
||||
}
|
||||
|
||||
public static Color onSurfaceVariant(boolean dark) {
|
||||
return dark ? DARK_ON_SURFACE_VARIANT : LIGHT_ON_SURFACE_VARIANT;
|
||||
}
|
||||
|
||||
public static Color outline(boolean dark) {
|
||||
return dark ? DARK_OUTLINE : LIGHT_OUTLINE;
|
||||
}
|
||||
|
||||
public static Color surface() {
|
||||
return surface(isDark());
|
||||
}
|
||||
|
||||
public static Color surfaceContainer() {
|
||||
return surfaceContainer(isDark());
|
||||
}
|
||||
|
||||
public static Color onSurface() {
|
||||
return onSurface(isDark());
|
||||
}
|
||||
}
|
||||
@@ -26,10 +26,13 @@ import java.util.Objects;
|
||||
@JsonSerializable
|
||||
public record ThemeColor(@NotNull String name, @NotNull Color color) {
|
||||
|
||||
public static final ThemeColor DEFAULT = new ThemeColor("blue", Color.web("#5C6BC0"));
|
||||
/// Default Anthropic accent seed (skill: `--color-accent-orange`).
|
||||
public static final ThemeColor DEFAULT = new ThemeColor("anthropic_orange", Color.web("#D97757"));
|
||||
|
||||
public static final List<ThemeColor> STANDARD_COLORS = List.of(
|
||||
DEFAULT,
|
||||
new ThemeColor("stone", Color.web("#3D453C")),
|
||||
new ThemeColor("blue", Color.web("#5C6BC0")),
|
||||
new ThemeColor("darker_blue", Color.web("#283593")),
|
||||
new ThemeColor("green", Color.web("#43A047")),
|
||||
new ThemeColor("orange", Color.web("#E67E22")),
|
||||
|
||||
@@ -29,7 +29,6 @@ import javafx.stage.Stage;
|
||||
import javafx.stage.WindowEvent;
|
||||
import org.glavo.monetfx.Brightness;
|
||||
import org.glavo.monetfx.ColorScheme;
|
||||
import org.glavo.monetfx.ColorRole;
|
||||
import org.glavo.monetfx.ColorStyle;
|
||||
import org.glavo.monetfx.Contrast;
|
||||
import org.glavo.monetfx.beans.property.ColorSchemeProperty;
|
||||
@@ -800,7 +799,8 @@ public final class Themes {
|
||||
opacity = background.opacity();
|
||||
}
|
||||
|
||||
Paint themeColorPaint = appearance.toResolvedTheme(context).toColorScheme().getColor(ColorRole.SURFACE_CONTAINER);
|
||||
boolean darkFallback = appearance.toResolvedTheme(context).toColorScheme().getBrightness() == Brightness.DARK;
|
||||
Paint themeColorPaint = SkillPalette.surfaceContainer(darkFallback);
|
||||
BackgroundType fallbackType = Objects.requireNonNullElse(
|
||||
settings().backgroundFallbackTypeProperty().get(),
|
||||
BackgroundType.BUILTIN);
|
||||
@@ -960,7 +960,7 @@ public final class Themes {
|
||||
|
||||
/// Creates a JavaFX background from the current theme color scheme surface container.
|
||||
private static LauncherBackground createThemeColorBackground(double opacity) {
|
||||
return createPaintBackground(getColorScheme().getColor(ColorRole.SURFACE_CONTAINER), opacity);
|
||||
return createPaintBackground(SkillPalette.surfaceContainer(), opacity);
|
||||
}
|
||||
|
||||
/// Creates a JavaFX image background with a separately stored node opacity.
|
||||
@@ -1103,10 +1103,11 @@ public final class Themes {
|
||||
);
|
||||
|
||||
/// The title text fill derived from the current color scheme.
|
||||
///
|
||||
/// Title chrome uses surface tones (Anthropic-like calm bar), so fill stays on-surface
|
||||
/// whether or not the bar is translucent.
|
||||
private static final ObjectBinding<Color> titleFill = Bindings.createObjectBinding(
|
||||
() -> titleBarTransparent.get()
|
||||
? getColorScheme().getOnSurface()
|
||||
: getColorScheme().getOnPrimaryContainer(),
|
||||
() -> SkillPalette.onSurface(),
|
||||
colorSchemeProperty(),
|
||||
titleBarTransparent
|
||||
);
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2020 The HMCL Authors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH AdditionRef-HMCL
|
||||
package org.jackhuang.hmcl.ui;
|
||||
|
||||
/// Golden-ratio layout metrics for Anthropic tool-mode chrome.
|
||||
///
|
||||
/// Primary splits use φ ≈ 1.618 (sidebar:content = 1:φ). Spacing uses the φ ladder
|
||||
/// from an 8px base so hierarchy stays dense — not landing-page whitespace.
|
||||
public final class LayoutMetrics {
|
||||
/// Golden ratio.
|
||||
public static final double PHI = 1.618033988749895;
|
||||
|
||||
/// Sidebar share of a page: 1 / (1 + φ) ≈ 0.382 so sidebar:content = 1:φ.
|
||||
public static final double SIDEBAR_FRACTION = 1.0 / (1.0 + PHI);
|
||||
|
||||
/// Design-minimum page width used when the node has not been laid out yet.
|
||||
public static final double DESIGN_PAGE_WIDTH = 800.0;
|
||||
|
||||
/// Tool-density clamp: keep the rail usable without eating the content column.
|
||||
public static final double SIDEBAR_MIN = 220.0;
|
||||
|
||||
/// φ share of [#DESIGN_PAGE_WIDTH] — exact 1:φ at the launcher minimum width.
|
||||
public static final double SIDEBAR_MAX = Math.rint(DESIGN_PAGE_WIDTH * SIDEBAR_FRACTION); // 306
|
||||
|
||||
/// φ spacing ladder from an 8px base (…, 5, 8, 13, 21, 34).
|
||||
public static final double SPACE_XS = 5.0;
|
||||
public static final double SPACE_SM = 8.0;
|
||||
public static final double SPACE_MD = 13.0;
|
||||
public static final double SPACE_LG = 21.0;
|
||||
public static final double SPACE_XL = 34.0;
|
||||
|
||||
/// Title-bar / touch target height (a11y floor from the skill).
|
||||
public static final double TITLE_BAR = 44.0;
|
||||
|
||||
/// Primary CTA height on the home page (Fibonacci neighbour of title bar / φ body).
|
||||
public static final double LAUNCH_HEIGHT = 55.0;
|
||||
|
||||
/// Primary CTA width (Fibonacci; pairs with the φ sidebar rail).
|
||||
public static final double LAUNCH_WIDTH = 233.0;
|
||||
|
||||
private LayoutMetrics() {
|
||||
}
|
||||
|
||||
/// Resolves the fixed sidebar width for a page of the given width.
|
||||
///
|
||||
/// @param pageWidth the animated page width in pixels; values ≤ 1 fall back to the design minimum
|
||||
/// @return a width clamped to [{@link #SIDEBAR_MIN}, {@link #SIDEBAR_MAX}] on the 1:φ fraction
|
||||
public static double sidebarWidth(double pageWidth) {
|
||||
double width = pageWidth > 1.0 ? pageWidth : DESIGN_PAGE_WIDTH;
|
||||
return Math.rint(Math.max(SIDEBAR_MIN, Math.min(SIDEBAR_MAX, width * SIDEBAR_FRACTION)));
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,7 @@ public enum ContainerAnimations implements TransitionPane.AnimationProducer {
|
||||
/// Animates forward navigation with temporary incoming-page caching.
|
||||
@Override
|
||||
public Timeline animate(Pane container, Node previousNode, Node nextNode) {
|
||||
double offset = container.getWidth() > 0 ? container.getWidth() * 0.2 : 50;
|
||||
double offset = container.getWidth() > 0 ? container.getWidth() * 0.06 : 16;
|
||||
return new Timeline(
|
||||
new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(nextNode.cacheProperty(), isCacheAllowed(nextNode), Interpolator.DISCRETE),
|
||||
@@ -147,12 +147,12 @@ public enum ContainerAnimations implements TransitionPane.AnimationProducer {
|
||||
}
|
||||
},
|
||||
|
||||
/// @see <a href="https://m3.material.io/styles/motion/transitions/transition-patterns">Transitions - Material Design 3</a>
|
||||
/// Soft reverse of [#FORWARD] (reduced travel distance for Anthropic-calm chrome).
|
||||
BACKWARD {
|
||||
/// Animates backward navigation with temporary incoming-page caching.
|
||||
@Override
|
||||
public Timeline animate(Pane container, Node previousNode, Node nextNode) {
|
||||
double offset = container.getWidth() > 0 ? container.getWidth() * 0.2 : 50;
|
||||
double offset = container.getWidth() > 0 ? container.getWidth() * 0.06 : 16;
|
||||
return new Timeline(
|
||||
new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(nextNode.cacheProperty(), isCacheAllowed(nextNode), Interpolator.DISCRETE),
|
||||
@@ -210,8 +210,8 @@ public enum ContainerAnimations implements TransitionPane.AnimationProducer {
|
||||
new KeyValue(left.translateXProperty(), 0, DEFAULT_INTERPOLATOR),
|
||||
new KeyValue(center.translateXProperty(), 0, DEFAULT_INTERPOLATOR)));
|
||||
timeline.getKeyFrames().add(new KeyFrame(DEFAULT_DURATION_HALF,
|
||||
new KeyValue(left.translateXProperty(), -30, DEFAULT_INTERPOLATOR),
|
||||
new KeyValue(center.translateXProperty(), 30, DEFAULT_INTERPOLATOR)));
|
||||
new KeyValue(left.translateXProperty(), -10, DEFAULT_INTERPOLATOR),
|
||||
new KeyValue(center.translateXProperty(), 10, DEFAULT_INTERPOLATOR)));
|
||||
timeline.getKeyFrames().add(new KeyFrame(DEFAULT_DURATION,
|
||||
new KeyValue(left.cacheProperty(), left.isCache(), Interpolator.DISCRETE),
|
||||
new KeyValue(center.cacheProperty(), center.isCache(), Interpolator.DISCRETE)));
|
||||
@@ -233,8 +233,8 @@ public enum ContainerAnimations implements TransitionPane.AnimationProducer {
|
||||
new KeyValue(left.cacheProperty(), isCacheAllowed(left), Interpolator.DISCRETE),
|
||||
new KeyValue(center.cacheProperty(), cacheCenter, Interpolator.DISCRETE)));
|
||||
timeline.getKeyFrames().add(new KeyFrame(DEFAULT_DURATION_HALF,
|
||||
new KeyValue(left.translateXProperty(), -30, DEFAULT_INTERPOLATOR),
|
||||
new KeyValue(center.translateXProperty(), 30, DEFAULT_INTERPOLATOR)));
|
||||
new KeyValue(left.translateXProperty(), -10, DEFAULT_INTERPOLATOR),
|
||||
new KeyValue(center.translateXProperty(), 10, DEFAULT_INTERPOLATOR)));
|
||||
timeline.getKeyFrames().add(new KeyFrame(DEFAULT_DURATION,
|
||||
new KeyValue(left.cacheProperty(), left.isCache(), Interpolator.DISCRETE),
|
||||
new KeyValue(center.cacheProperty(), center.isCache(), Interpolator.DISCRETE),
|
||||
|
||||
@@ -22,6 +22,18 @@ public final class Motion {
|
||||
/// @see <a href="https://flutter.github.io/assets-for-api-docs/assets/animation/curve_linear.mp4">curve_linear.mp4</a>
|
||||
public static final Interpolator LINEAR = Interpolator.LINEAR;
|
||||
|
||||
/// Anthropic skill default ease: `cubic-bezier(0.16, 1, 0.3, 1)` — transform/opacity only.
|
||||
public static final Interpolator ANTHROPIC = new Cubic(0.16, 1.0, 0.3, 1.0);
|
||||
|
||||
/// Skill `--duration-fast` (150ms).
|
||||
public static final Duration FAST = Duration.millis(150);
|
||||
|
||||
/// Skill `--duration-normal` (250ms).
|
||||
public static final Duration NORMAL = Duration.millis(250);
|
||||
|
||||
/// Skill `--duration-slow` (400ms).
|
||||
public static final Duration SLOW = Duration.millis(400);
|
||||
|
||||
/// The emphasizedAccelerate easing curve in the Material specification.
|
||||
///
|
||||
/// See also:
|
||||
|
||||
@@ -22,11 +22,11 @@ import org.jetbrains.annotations.Nullable;
|
||||
@NotNullByDefault
|
||||
public class TransitionPane extends StackPane {
|
||||
|
||||
/// The default easing curve for page transitions.
|
||||
public static final Interpolator DEFAULT_INTERPOLATOR = Motion.EASE;
|
||||
/// Skill ease: cubic-bezier(0.16, 1, 0.3, 1) — no Material overshoot.
|
||||
public static final Interpolator DEFAULT_INTERPOLATOR = Motion.ANTHROPIC;
|
||||
|
||||
/// The default duration for page transitions.
|
||||
public static final Duration DEFAULT_DURATION = Motion.SHORT4;
|
||||
/// Skill `--duration-fast` (150ms).
|
||||
public static final Duration DEFAULT_DURATION = Motion.FAST;
|
||||
|
||||
/// Half the default duration for transitions with two phases.
|
||||
public static final Duration DEFAULT_DURATION_HALF = DEFAULT_DURATION.divide(2);
|
||||
|
||||
@@ -188,8 +188,9 @@ public final class TabController implements PageAware {
|
||||
/// Mirrors the original slide-up and fade-in transition vertically.
|
||||
TAB_DOWN(-1.0);
|
||||
|
||||
private static final Duration DURATION = Motion.MEDIUM4;
|
||||
private static final Duration DURATION = Motion.NORMAL;
|
||||
private static final Duration HALF_DURATION = DURATION.divide(2);
|
||||
private static final Interpolator EASING = Motion.ANTHROPIC;
|
||||
|
||||
private final double direction;
|
||||
|
||||
@@ -200,22 +201,23 @@ public final class TabController implements PageAware {
|
||||
@Override
|
||||
public Animation animate(Pane container, Node previousNode, Node nextNode) {
|
||||
double extent = container.getHeight();
|
||||
double offset = (extent > 0 ? extent * 0.2 : 50) * direction;
|
||||
// Small vertical drift + fade — avoids Material emphasized/overshoot motion.
|
||||
double offset = (extent > 0 ? extent * 0.06 : 16) * direction;
|
||||
var previousTranslation = previousNode.translateYProperty();
|
||||
var nextTranslation = nextNode.translateYProperty();
|
||||
return new Timeline(
|
||||
new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(nextNode.cacheProperty(), isCacheAllowed(nextNode), Interpolator.DISCRETE),
|
||||
new KeyValue(previousTranslation, 0, Motion.EASE_IN_OUT_CUBIC_EMPHASIZED),
|
||||
new KeyValue(previousNode.opacityProperty(), 1, Motion.EASE_IN_OUT_CUBIC_EMPHASIZED),
|
||||
new KeyValue(nextNode.opacityProperty(), 0, Motion.EASE_IN_OUT_CUBIC_EMPHASIZED),
|
||||
new KeyValue(nextTranslation, offset, Motion.EASE_IN_OUT_CUBIC_EMPHASIZED)),
|
||||
new KeyValue(previousTranslation, 0, EASING),
|
||||
new KeyValue(previousNode.opacityProperty(), 1, EASING),
|
||||
new KeyValue(nextNode.opacityProperty(), 0, EASING),
|
||||
new KeyValue(nextTranslation, offset, EASING)),
|
||||
new KeyFrame(HALF_DURATION,
|
||||
new KeyValue(previousNode.opacityProperty(), 0, Motion.EASE_IN_OUT_CUBIC_EMPHASIZED)),
|
||||
new KeyValue(previousNode.opacityProperty(), 0, EASING)),
|
||||
new KeyFrame(DURATION,
|
||||
new KeyValue(nextNode.cacheProperty(), nextNode.isCache(), Interpolator.DISCRETE),
|
||||
new KeyValue(nextNode.opacityProperty(), 1, Motion.EASE_IN_OUT_CUBIC_EMPHASIZED),
|
||||
new KeyValue(nextTranslation, 0, Motion.EASE_IN_OUT_CUBIC_EMPHASIZED)));
|
||||
new KeyValue(nextNode.opacityProperty(), 1, EASING),
|
||||
new KeyValue(nextTranslation, 0, EASING)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -83,8 +83,8 @@ public final class Decorator {
|
||||
/// The minimum width, in pixels, of the visible window content.
|
||||
private static final double MIN_CONTENT_WIDTH = 800.0;
|
||||
|
||||
/// The minimum height, in pixels, including the 450-pixel background and 40-pixel title bar.
|
||||
private static final double MIN_CONTENT_HEIGHT = 450.0 + 40.0;
|
||||
/// The minimum height, in pixels, including the 450-pixel background and 44-pixel title bar.
|
||||
private static final double MIN_CONTENT_HEIGHT = 450.0 + 44.0;
|
||||
|
||||
/// The minimum visible overlap, in pixels, used to accept a persisted window position.
|
||||
private static final double MIN_VISIBLE_WINDOW_AREA = 20.0;
|
||||
@@ -975,13 +975,13 @@ public final class Decorator {
|
||||
|
||||
Timeline timeline = new Timeline(
|
||||
new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(root.opacityProperty(), 0, Motion.EASE),
|
||||
new KeyValue(root.scaleXProperty(), 0.8, Motion.EASE),
|
||||
new KeyValue(root.scaleYProperty(), 0.8, Motion.EASE)),
|
||||
new KeyFrame(Duration.millis(600),
|
||||
new KeyValue(root.opacityProperty(), 1, Motion.EASE),
|
||||
new KeyValue(root.scaleXProperty(), 1, Motion.EASE),
|
||||
new KeyValue(root.scaleYProperty(), 1, Motion.EASE)));
|
||||
new KeyValue(root.opacityProperty(), 0, Motion.ANTHROPIC),
|
||||
new KeyValue(root.scaleXProperty(), 0.985, Motion.ANTHROPIC),
|
||||
new KeyValue(root.scaleYProperty(), 0.985, Motion.ANTHROPIC)),
|
||||
new KeyFrame(Motion.NORMAL,
|
||||
new KeyValue(root.opacityProperty(), 1, Motion.ANTHROPIC),
|
||||
new KeyValue(root.scaleXProperty(), 1, Motion.ANTHROPIC),
|
||||
new KeyValue(root.scaleYProperty(), 1, Motion.ANTHROPIC)));
|
||||
playWindowAnimation(timeline, () -> {
|
||||
resetRootTransform();
|
||||
|
||||
@@ -1003,15 +1003,11 @@ public final class Decorator {
|
||||
if (!nativeDecorationEnabled && AnimationUtils.playWindowAnimation() && OperatingSystem.CURRENT_OS != OperatingSystem.MACOS) {
|
||||
Timeline timeline = new Timeline(
|
||||
new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(root.opacityProperty(), 1, Motion.EASE),
|
||||
new KeyValue(root.translateYProperty(), 0, Motion.EASE),
|
||||
new KeyValue(root.scaleXProperty(), 1, Motion.EASE),
|
||||
new KeyValue(root.scaleYProperty(), 1, Motion.EASE)),
|
||||
new KeyFrame(Motion.SHORT4,
|
||||
new KeyValue(root.opacityProperty(), 0, Motion.EASE),
|
||||
new KeyValue(root.translateYProperty(), 200, Motion.EASE),
|
||||
new KeyValue(root.scaleXProperty(), 0.4, Motion.EASE),
|
||||
new KeyValue(root.scaleYProperty(), 0.4, Motion.EASE)));
|
||||
new KeyValue(root.opacityProperty(), 1, Motion.ANTHROPIC),
|
||||
new KeyValue(root.translateYProperty(), 0, Motion.ANTHROPIC)),
|
||||
new KeyFrame(Motion.FAST,
|
||||
new KeyValue(root.opacityProperty(), 0, Motion.ANTHROPIC),
|
||||
new KeyValue(root.translateYProperty(), 24, Motion.ANTHROPIC)));
|
||||
playWindowAnimation(timeline, () -> {
|
||||
if (getStage() == currentStage) {
|
||||
currentStage.setIconified(true);
|
||||
@@ -1032,13 +1028,9 @@ public final class Decorator {
|
||||
if (!nativeDecorationEnabled && AnimationUtils.playWindowAnimation()) {
|
||||
Timeline timeline = new Timeline(
|
||||
new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(root.opacityProperty(), 1, Motion.EASE),
|
||||
new KeyValue(root.scaleXProperty(), 1, Motion.EASE),
|
||||
new KeyValue(root.scaleYProperty(), 1, Motion.EASE)),
|
||||
new KeyFrame(Duration.millis(200),
|
||||
new KeyValue(root.opacityProperty(), 0, Motion.EASE),
|
||||
new KeyValue(root.scaleXProperty(), 0.8, Motion.EASE),
|
||||
new KeyValue(root.scaleYProperty(), 0.8, Motion.EASE)));
|
||||
new KeyValue(root.opacityProperty(), 1, Motion.ANTHROPIC)),
|
||||
new KeyFrame(Motion.FAST,
|
||||
new KeyValue(root.opacityProperty(), 0, Motion.ANTHROPIC)));
|
||||
playWindowAnimation(timeline, Launcher::stopApplication);
|
||||
} else {
|
||||
stopWindowAnimation();
|
||||
@@ -1172,15 +1164,11 @@ public final class Decorator {
|
||||
|
||||
Timeline timeline = new Timeline(
|
||||
new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(root.opacityProperty(), 0, Motion.EASE),
|
||||
new KeyValue(root.translateYProperty(), 200, Motion.EASE),
|
||||
new KeyValue(root.scaleXProperty(), 0.4, Motion.EASE),
|
||||
new KeyValue(root.scaleYProperty(), 0.4, Motion.EASE)),
|
||||
new KeyFrame(Motion.SHORT4,
|
||||
new KeyValue(root.opacityProperty(), 1, Motion.EASE),
|
||||
new KeyValue(root.translateYProperty(), 0, Motion.EASE),
|
||||
new KeyValue(root.scaleXProperty(), 1, Motion.EASE),
|
||||
new KeyValue(root.scaleYProperty(), 1, Motion.EASE)));
|
||||
new KeyValue(root.opacityProperty(), 0, Motion.ANTHROPIC),
|
||||
new KeyValue(root.translateYProperty(), 24, Motion.ANTHROPIC)),
|
||||
new KeyFrame(Motion.FAST,
|
||||
new KeyValue(root.opacityProperty(), 1, Motion.ANTHROPIC),
|
||||
new KeyValue(root.translateYProperty(), 0, Motion.ANTHROPIC)));
|
||||
playWindowAnimation(timeline, this::resetRootTransform);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,16 +2,19 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH AdditionRef-HMCL
|
||||
package org.jackhuang.hmcl.ui.decorator;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.LayoutMetrics;
|
||||
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/// Lays out a fixed-width sidebar and a growing content area as independent animation targets.
|
||||
/// Lays out a φ-proportioned sidebar and a growing content area as independent animation targets.
|
||||
///
|
||||
/// Sidebar:content ≈ 1:φ (see [LayoutMetrics]), clamped for tool-mode density.
|
||||
@NotNullByDefault
|
||||
public class DecoratorAnimatedPage extends BorderPane {
|
||||
|
||||
@@ -25,7 +28,12 @@ public class DecoratorAnimatedPage extends BorderPane {
|
||||
/// Creates both animation targets without requiring CSS or skin initialization.
|
||||
public DecoratorAnimatedPage() {
|
||||
getStyleClass().add("gray-background");
|
||||
FXUtils.setLimitWidth(left, 200);
|
||||
var sidebarWidth = Bindings.createDoubleBinding(
|
||||
() -> LayoutMetrics.sidebarWidth(getWidth()),
|
||||
widthProperty());
|
||||
left.minWidthProperty().bind(sidebarWidth);
|
||||
left.prefWidthProperty().bind(sidebarWidth);
|
||||
left.maxWidthProperty().bind(sidebarWidth);
|
||||
sidebar.setMinSize(0, 0);
|
||||
left.setCenter(sidebar);
|
||||
setLeft(left);
|
||||
@@ -42,11 +50,11 @@ public class DecoratorAnimatedPage extends BorderPane {
|
||||
return left.getBottom();
|
||||
}
|
||||
|
||||
/// Replaces the bottom content, reserving its preferred height and a 12-pixel bottom gap.
|
||||
/// Replaces the bottom content, reserving its preferred height and a φ-md bottom gap.
|
||||
/// Passing null removes it without replacing the sidebar animation target.
|
||||
public void setSidebarBottom(@Nullable Node node) {
|
||||
if (node != null) {
|
||||
BorderPane.setMargin(node, new Insets(0, 0, 12, 0));
|
||||
BorderPane.setMargin(node, new Insets(0, 0, LayoutMetrics.SPACE_MD, 0));
|
||||
}
|
||||
left.setBottom(node);
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import javafx.util.Duration;
|
||||
import org.glavo.monetfx.ColorRole;
|
||||
import org.glavo.monetfx.ColorScheme;
|
||||
import org.jackhuang.hmcl.theme.SkillPalette;
|
||||
import org.jackhuang.hmcl.theme.Themes;
|
||||
import org.jackhuang.hmcl.ui.*;
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
@@ -81,7 +81,7 @@ final class MainWindowPane extends StackPane {
|
||||
() -> Themes.windowTransparentProperty().get()
|
||||
? null
|
||||
: new Background(new BackgroundFill(
|
||||
Themes.getColorScheme().getColor(ColorRole.SURFACE_CONTAINER),
|
||||
SkillPalette.surfaceContainer(),
|
||||
CornerRadii.EMPTY,
|
||||
Insets.EMPTY)),
|
||||
Themes.windowTransparentProperty(),
|
||||
@@ -173,7 +173,7 @@ final class MainWindowPane extends StackPane {
|
||||
HBox buttons = new HBox();
|
||||
buttons.setAlignment(Pos.TOP_RIGHT);
|
||||
decorator.capableDraggingWindow(buttons);
|
||||
buttons.setMaxSize(Region.USE_PREF_SIZE, 40);
|
||||
buttons.setMaxSize(Region.USE_PREF_SIZE, 44);
|
||||
|
||||
JFXButton minimizeButton = new JFXButton();
|
||||
minimizeButton.setFocusTraversable(false);
|
||||
@@ -194,19 +194,22 @@ final class MainWindowPane extends StackPane {
|
||||
}
|
||||
|
||||
/// Recomputes the title-bar background for the current theme and transparency preference.
|
||||
///
|
||||
/// Uses surface / surface-container tones so the chrome stays calm and brand-forward
|
||||
/// instead of a saturated primary strip.
|
||||
private void updateTitleBarBackground() {
|
||||
@Nullable ColorScheme colorScheme = Themes.getColorScheme();
|
||||
if (colorScheme == null) {
|
||||
titleBar.setBackground(null);
|
||||
} else if (Themes.titleBarTransparentProperty().get()) {
|
||||
Color surface = colorScheme.getSurface();
|
||||
Color surface = SkillPalette.surface(SkillPalette.isDark(colorScheme));
|
||||
titleBar.setBackground(new Background(new BackgroundFill(
|
||||
new Color(surface.getRed(), surface.getGreen(), surface.getBlue(), 0.5),
|
||||
CornerRadii.EMPTY,
|
||||
Insets.EMPTY)));
|
||||
} else {
|
||||
titleBar.setBackground(new Background(new BackgroundFill(
|
||||
colorScheme.getPrimaryContainer(),
|
||||
SkillPalette.surfaceContainer(SkillPalette.isDark(colorScheme)),
|
||||
CornerRadii.EMPTY,
|
||||
Insets.EMPTY)));
|
||||
}
|
||||
@@ -326,7 +329,7 @@ final class MainWindowPane extends StackPane {
|
||||
/// {@inheritDoc}
|
||||
@Override
|
||||
public Timeline animate(Pane container, Node previousNode, Node nextNode) {
|
||||
return createTimeline(previousNode, nextNode, 50, -50);
|
||||
return createTimeline(previousNode, nextNode, 18, -18);
|
||||
}
|
||||
|
||||
/// {@inheritDoc}
|
||||
@@ -350,7 +353,7 @@ final class MainWindowPane extends StackPane {
|
||||
/// {@inheritDoc}
|
||||
@Override
|
||||
public Timeline animate(Pane container, Node previousNode, Node nextNode) {
|
||||
return createTimeline(previousNode, nextNode, -50, 50);
|
||||
return createTimeline(previousNode, nextNode, -18, 18);
|
||||
}
|
||||
|
||||
/// {@inheritDoc}
|
||||
|
||||
@@ -15,6 +15,7 @@ import javafx.scene.layout.*;
|
||||
import org.jackhuang.hmcl.setting.ProxyType;
|
||||
import org.jackhuang.hmcl.task.FetchTask;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.LayoutMetrics;
|
||||
import org.jackhuang.hmcl.ui.WeakListenerHolder;
|
||||
import org.jackhuang.hmcl.ui.construct.*;
|
||||
import org.jackhuang.hmcl.util.Holder;
|
||||
@@ -30,8 +31,8 @@ public class DownloadSettingsPage extends StackPane {
|
||||
private final WeakListenerHolder holder = new WeakListenerHolder();
|
||||
|
||||
public DownloadSettingsPage() {
|
||||
VBox content = new VBox(10);
|
||||
content.setPadding(new Insets(10));
|
||||
VBox content = new VBox(LayoutMetrics.SPACE_MD);
|
||||
content.setPadding(new Insets(LayoutMetrics.SPACE_SM, LayoutMetrics.SPACE_MD, LayoutMetrics.SPACE_MD, LayoutMetrics.SPACE_MD));
|
||||
content.setFillWidth(true);
|
||||
ScrollPane scrollPane = new ScrollPane(content);
|
||||
FXUtils.smoothScrolling(scrollPane);
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.jackhuang.hmcl.setting.GameDirectoryManager;
|
||||
import org.jackhuang.hmcl.theme.Themes;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.LayoutMetrics;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.ui.animation.AnimationUtils;
|
||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||
@@ -79,7 +80,8 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
|
||||
state.setValue(new State(null, titleNode, false, false, true));
|
||||
|
||||
setPadding(new Insets(20));
|
||||
// Tool-density φ ladder (skill): md/lg insets — not landing-page whitespace.
|
||||
setPadding(new Insets(LayoutMetrics.SPACE_MD, LayoutMetrics.SPACE_MD, LayoutMetrics.SPACE_LG, LayoutMetrics.SPACE_MD));
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1017,7 +1017,7 @@ public class PersonalizationPage extends StackPane {
|
||||
}
|
||||
}
|
||||
|
||||
vbox.getChildren().add(new Label("Hello Minecraft! Launcher"));
|
||||
vbox.getChildren().add(new Label("TDLM"));
|
||||
|
||||
fontPane.getContent().add(vbox);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.LayoutMetrics;
|
||||
import org.jackhuang.hmcl.ui.construct.*;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
@@ -47,8 +48,9 @@ public final class SettingsPage extends ScrollPane {
|
||||
public SettingsPage() {
|
||||
this.setFitToWidth(true);
|
||||
|
||||
VBox rootPane = new VBox(10);
|
||||
rootPane.setPadding(new Insets(10));
|
||||
// Title:body φ ladder — tight tool density (sm gap, md/lg insets).
|
||||
VBox rootPane = new VBox(LayoutMetrics.SPACE_SM);
|
||||
rootPane.setPadding(new Insets(LayoutMetrics.SPACE_SM, LayoutMetrics.SPACE_MD, LayoutMetrics.SPACE_MD, LayoutMetrics.SPACE_MD));
|
||||
this.setContent(rootPane);
|
||||
FXUtils.smoothScrolling(this);
|
||||
|
||||
|
||||
@@ -1,35 +1,48 @@
|
||||
/*
|
||||
* Anthropic UI — dark brightness tokens (skill charcoal #1A1916 family).
|
||||
* Loaded after the generated Monet theme sheet; keep AA contrast on surfaces/text.
|
||||
*/
|
||||
* {
|
||||
-anthropic-shadow: rgba(0, 0, 0, 0.50);
|
||||
-anthropic-shadow-strong: rgba(0, 0, 0, 0.65);
|
||||
-anthropic-scrollbar-track: #222119;
|
||||
-anthropic-scrollbar-thumb: #5C5A54;
|
||||
-anthropic-hairline: -monet-outline-variant;
|
||||
-anthropic-focus-ring: #D97757;
|
||||
}
|
||||
|
||||
.hint {
|
||||
-fx-hyperlink-fill: -monet-inverse-primary;
|
||||
-fx-hyperlink-fill: -monet-primary;
|
||||
}
|
||||
|
||||
.tag {
|
||||
-fixed-warning-tag-background: #2c0b0e;
|
||||
-fixed-warning-tag-background: #4A1A1F;
|
||||
}
|
||||
|
||||
.log-window {
|
||||
-fixed-log-background: #282828;
|
||||
-fixed-log-toggle-selected: #DEE2E6;
|
||||
-fixed-log-toggle-unselected: #6C757D;
|
||||
-fixed-log-text-fill: #e3e3e3;
|
||||
-fixed-log-trace-background: #495057;
|
||||
-fixed-log-debug-background: #343A40;
|
||||
-fixed-log-info-background: #212529;
|
||||
-fixed-log-warn-background: #413c26;
|
||||
-fixed-log-warn-text-fill: #fdf3aa;
|
||||
-fixed-log-error-background: #4e3534;
|
||||
-fixed-log-error-text-fill: #f9dedc;
|
||||
-fixed-log-background: -monet-surface;
|
||||
-fixed-log-toggle-selected: -monet-on-surface;
|
||||
-fixed-log-toggle-unselected: -monet-on-surface-variant;
|
||||
-fixed-log-text-fill: -monet-on-surface;
|
||||
-fixed-log-trace-background: -monet-surface-container-high;
|
||||
-fixed-log-debug-background: -monet-surface-container;
|
||||
-fixed-log-info-background: -monet-surface;
|
||||
-fixed-log-warn-background: #3F3A26;
|
||||
-fixed-log-warn-text-fill: #F7E8A0;
|
||||
-fixed-log-error-background: #4E3534;
|
||||
-fixed-log-error-text-fill: #F9DEDC;
|
||||
-fixed-log-fatal-background: #842029;
|
||||
-fixed-log-selected: #3e3e3e;
|
||||
-fixed-log-selected: -monet-surface-container-highest;
|
||||
}
|
||||
|
||||
.addon-channel-icon.alpha .svg {
|
||||
-fx-fill: #ff496e;
|
||||
-fx-fill: #E08A8A;
|
||||
}
|
||||
|
||||
.addon-channel-icon.beta .svg {
|
||||
-fx-fill: #ffa347;
|
||||
-fx-fill: #E08A6C;
|
||||
}
|
||||
|
||||
.addon-channel-icon.release .svg {
|
||||
-fx-fill: #1bd96a;
|
||||
-fx-fill: #A3B58A;
|
||||
}
|
||||
@@ -1,3 +1,16 @@
|
||||
/*
|
||||
* Anthropic UI — light brightness tokens (skill-aligned warm neutrals).
|
||||
* Loaded after the generated Monet theme sheet; keep AA contrast on surfaces/text.
|
||||
*/
|
||||
* {
|
||||
-anthropic-shadow: rgba(20, 20, 19, 0.10);
|
||||
-anthropic-shadow-strong: rgba(20, 20, 19, 0.16);
|
||||
-anthropic-scrollbar-track: #E8E6DC;
|
||||
-anthropic-scrollbar-thumb: #9B9890;
|
||||
-anthropic-hairline: -monet-outline-variant;
|
||||
-anthropic-focus-ring: #D97757;
|
||||
}
|
||||
|
||||
.hint {
|
||||
-fx-hyperlink-fill: -monet-primary;
|
||||
}
|
||||
@@ -7,29 +20,29 @@
|
||||
}
|
||||
|
||||
.log-window {
|
||||
-fixed-log-background: #ffffff;
|
||||
-fixed-log-toggle-selected: #000000;
|
||||
-fixed-log-toggle-unselected: #6C757D;
|
||||
-fixed-log-text-fill: #1f1f1f;
|
||||
-fixed-log-trace-background: #EEE9E0;
|
||||
-fixed-log-debug-background: #EEE9E0;
|
||||
-fixed-log-info-background: #FFFFFF;
|
||||
-fixed-log-warn-background: #fef6d5;
|
||||
-fixed-log-warn-text-fill: #3e2f00;
|
||||
-fixed-log-error-background: #fcebeb;
|
||||
-fixed-log-error-text-fill: #410e0b;
|
||||
-fixed-log-background: -monet-surface;
|
||||
-fixed-log-toggle-selected: -monet-on-surface;
|
||||
-fixed-log-toggle-unselected: -monet-on-surface-variant;
|
||||
-fixed-log-text-fill: -monet-on-surface;
|
||||
-fixed-log-trace-background: -monet-surface-container;
|
||||
-fixed-log-debug-background: -monet-surface-container-low;
|
||||
-fixed-log-info-background: -monet-surface;
|
||||
-fixed-log-warn-background: #F7F0D8;
|
||||
-fixed-log-warn-text-fill: #3E2F00;
|
||||
-fixed-log-error-background: #FCEBEB;
|
||||
-fixed-log-error-text-fill: #410E0B;
|
||||
-fixed-log-fatal-background: #F7A699;
|
||||
-fixed-log-selected: #f2f2f2;
|
||||
-fixed-log-selected: -monet-surface-container-highest;
|
||||
}
|
||||
|
||||
.addon-channel-icon.alpha .svg {
|
||||
-fx-fill: #cb2245;
|
||||
-fx-fill: #C0453A;
|
||||
}
|
||||
|
||||
.addon-channel-icon.beta .svg {
|
||||
-fx-fill: #e08325;
|
||||
-fx-fill: #C96442;
|
||||
}
|
||||
|
||||
.addon-channel-icon.release .svg {
|
||||
-fx-fill: #00af5c;
|
||||
-fx-fill: #5F7A4A;
|
||||
}
|
||||
@@ -1,8 +1,53 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (C) 2025 The HMCL Authors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later WITH AdditionRef-HMCL
|
||||
* Anthropic skill type roles (CN+Latin):
|
||||
* - Sans = UI / nav / buttons / chrome titles (Source Han Sans)
|
||||
* - Serif = body / narrative copy (Source Han Serif)
|
||||
*/
|
||||
.root {
|
||||
/* https://github.com/HMCL-dev/HMCL/pull/3423 */
|
||||
-fx-font-family: -fx-base-font-family;
|
||||
}
|
||||
-fx-font-family: "Source Han Serif SC", "Source Han Serif", "思源宋体", serif;
|
||||
}
|
||||
|
||||
/* UI chrome + interactive — sans */
|
||||
.jfx-decorator-title,
|
||||
.jfx-tool-bar-second .label,
|
||||
.jfx-tool-bar Label,
|
||||
.title-label,
|
||||
.class-title,
|
||||
.class-title Text,
|
||||
.jfx-layout-heading,
|
||||
.launch-pane > .launch-button .label,
|
||||
.announcement .title,
|
||||
.method-title,
|
||||
.radio-button-title-label,
|
||||
.two-line-list-item > .title,
|
||||
.advanced-list-item > .rippler-container > .container > .title,
|
||||
.iconed-item .iconed-item-container,
|
||||
.iconed-menu-item .iconed-item-container,
|
||||
.options-list-item .label,
|
||||
.jfx-button,
|
||||
.jfx-button *,
|
||||
.jfx-button-raised,
|
||||
.jfx-button-raised *,
|
||||
.jfx-button-border,
|
||||
.jfx-button-border *,
|
||||
.tab-label,
|
||||
.dialog-accept,
|
||||
.dialog-cancel,
|
||||
.line-component .title-label,
|
||||
.menu-container .title-label {
|
||||
-fx-font-family: "Source Han Sans SC", "Source Han Sans", "思源黑体", sans-serif;
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
|
||||
/* Body / secondary copy — serif */
|
||||
.subtitle-label,
|
||||
.two-line-list-item > .subtitle,
|
||||
.advanced-list-item > .rippler-container > .container > .subtitle,
|
||||
.announcement text,
|
||||
.method-desc,
|
||||
.options-list-item .label.subtitle,
|
||||
.hint,
|
||||
.html {
|
||||
-fx-font-family: "Source Han Serif SC", "Source Han Serif", "思源宋体", serif;
|
||||
-fx-font-weight: normal;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Anthropic skill — hard press feedback (loaded LAST, beats Monet/root).
|
||||
* Forced :pressed / :armed change background AND border AND opacity.
|
||||
* Solid hex only (no fragile lookups) so Modena cyan cannot win on specificity.
|
||||
*/
|
||||
|
||||
.jfx-button:pressed,
|
||||
.jfx-button:armed,
|
||||
.toggle-icon4:pressed,
|
||||
.toggle-icon4:armed,
|
||||
.toggle-icon-tiny:pressed,
|
||||
.toggle-icon-tiny:armed,
|
||||
.announcement-close-button:pressed,
|
||||
.announcement-close-button:armed,
|
||||
.jfx-decorator-button:pressed,
|
||||
.jfx-decorator-button:armed,
|
||||
.jfx-tool-bar-button:pressed,
|
||||
.jfx-tool-bar-button:armed,
|
||||
.jfx-tool-bar .jfx-decorator-button:pressed,
|
||||
.jfx-tool-bar .jfx-decorator-button:armed {
|
||||
-fx-background-color: rgba(217, 119, 87, 0.42);
|
||||
-fx-border-color: #C96442;
|
||||
-fx-border-width: 2px;
|
||||
-fx-border-radius: 6px;
|
||||
-fx-background-radius: 6px;
|
||||
-fx-opacity: 0.84;
|
||||
-fx-scale-x: 0.94;
|
||||
-fx-scale-y: 0.94;
|
||||
}
|
||||
|
||||
.jfx-button:hover,
|
||||
.toggle-icon4:hover,
|
||||
.toggle-icon-tiny:hover,
|
||||
.announcement-close-button:hover,
|
||||
.jfx-decorator-button:hover,
|
||||
.jfx-tool-bar-button:hover {
|
||||
-fx-background-color: rgba(217, 119, 87, 0.16);
|
||||
-fx-border-color: rgba(201, 100, 66, 0.55);
|
||||
-fx-border-width: 1px;
|
||||
-fx-opacity: 1;
|
||||
}
|
||||
|
||||
.jfx-button-raised:pressed,
|
||||
.jfx-button-raised:armed,
|
||||
.jfx-button-raised-round:pressed,
|
||||
.jfx-button-raised-round:armed,
|
||||
.launch-pane > .jfx-button:pressed,
|
||||
.launch-pane > .jfx-button:armed {
|
||||
-fx-background-color: #9A4A32;
|
||||
-fx-border-color: #F5F3EC;
|
||||
-fx-border-width: 2px;
|
||||
-fx-opacity: 0.90;
|
||||
-fx-scale-x: 0.96;
|
||||
-fx-scale-y: 0.96;
|
||||
}
|
||||
|
||||
.jfx-button-border:pressed,
|
||||
.jfx-button-border:armed {
|
||||
-fx-background-color: rgba(217, 119, 87, 0.28);
|
||||
-fx-border-color: #C96442;
|
||||
-fx-border-width: 2px;
|
||||
-fx-opacity: 0.88;
|
||||
-fx-scale-x: 0.96;
|
||||
-fx-scale-y: 0.96;
|
||||
}
|
||||
|
||||
.toggle-icon4:pressed,
|
||||
.toggle-icon4:armed,
|
||||
.toggle-icon-tiny:pressed,
|
||||
.toggle-icon-tiny:armed,
|
||||
.announcement-close-button:pressed,
|
||||
.announcement-close-button:armed {
|
||||
-fx-background-radius: 50px;
|
||||
-fx-border-radius: 50px;
|
||||
}
|
||||
|
||||
.jfx-button:disabled:pressed,
|
||||
.jfx-button:disabled:armed,
|
||||
.toggle-icon4:disabled:pressed,
|
||||
.toggle-icon4:disabled:armed,
|
||||
.toggle-icon-tiny:disabled:pressed,
|
||||
.toggle-icon-tiny:disabled:armed,
|
||||
.jfx-decorator-button:disabled:pressed,
|
||||
.jfx-decorator-button:disabled:armed,
|
||||
.jfx-tool-bar-button:disabled:pressed,
|
||||
.jfx-tool-bar-button:disabled:armed {
|
||||
-fx-opacity: 0.45;
|
||||
-fx-background-color: transparent;
|
||||
-fx-border-width: 0;
|
||||
-fx-scale-x: 1;
|
||||
-fx-scale-y: 1;
|
||||
}
|
||||
|
||||
/* Skin-driven armed class — mirrors :pressed when Modena/CSS lose the race. */
|
||||
.jfx-button.anthropic-armed,
|
||||
.toggle-icon4.anthropic-armed,
|
||||
.toggle-icon-tiny.anthropic-armed,
|
||||
.jfx-decorator-button.anthropic-armed,
|
||||
.jfx-tool-bar-button.anthropic-armed,
|
||||
.announcement-close-button.anthropic-armed {
|
||||
-fx-background-color: rgba(217, 119, 87, 0.42);
|
||||
-fx-border-color: #C96442;
|
||||
-fx-border-width: 2px;
|
||||
-fx-opacity: 0.84;
|
||||
}
|
||||
|
||||
.jfx-button-raised.anthropic-armed,
|
||||
.launch-pane > .jfx-button.anthropic-armed {
|
||||
-fx-background-color: #9A4A32;
|
||||
-fx-border-color: #F5F3EC;
|
||||
-fx-border-width: 2px;
|
||||
-fx-opacity: 0.90;
|
||||
}
|
||||
@@ -120,7 +120,7 @@
|
||||
.radio-button-title-label {
|
||||
-fx-font-size: 16px;
|
||||
-fx-padding: 14.0 0.0 -20.0 0.0;
|
||||
-fx-text-fill: rgba(0.0, 0.0, 0.0, 0.87);
|
||||
-fx-text-fill: -monet-on-surface;
|
||||
}
|
||||
|
||||
.announcement {
|
||||
@@ -144,7 +144,8 @@
|
||||
}
|
||||
|
||||
.rippler-container {
|
||||
-jfx-feedback-fill: -monet-on-surface;
|
||||
/* Anthropic chrome: no Material ripple / press ink */
|
||||
-jfx-feedback-fill: transparent;
|
||||
}
|
||||
|
||||
.rippler-container:press-feedback-enabled {
|
||||
@@ -185,6 +186,18 @@
|
||||
|
||||
.advanced-list-item {
|
||||
-jfx-selected-background-fill: -monet-secondary-container;
|
||||
-fx-background-radius: 6px;
|
||||
}
|
||||
|
||||
.advanced-list-item:hover > .rippler-container {
|
||||
-fx-background-color: -anthropic-hover-wash;
|
||||
-fx-background-radius: 6px;
|
||||
}
|
||||
|
||||
.advanced-list-item:pressed > .rippler-container,
|
||||
.advanced-list-item:armed > .rippler-container {
|
||||
-fx-background-color: -anthropic-press-wash;
|
||||
-fx-background-radius: 6px;
|
||||
}
|
||||
|
||||
.advanced-list-item:selected > .rippler-container > .container > .title {
|
||||
@@ -229,8 +242,9 @@
|
||||
}
|
||||
|
||||
.class-title {
|
||||
-fx-font-size: 12px;
|
||||
-fx-padding: 8 16 8 16;
|
||||
/* Title block of title:body ≈ 1:φ — md/sm padding under tool density */
|
||||
-fx-font-size: 11px;
|
||||
-fx-padding: 13 13 8 13;
|
||||
}
|
||||
|
||||
.class-title Text {
|
||||
@@ -245,7 +259,7 @@
|
||||
}
|
||||
|
||||
.advanced-list-box-content {
|
||||
-fx-padding: 12 0 0 0;
|
||||
-fx-padding: 13 0 0 0;
|
||||
-fx-spacing: 0;
|
||||
}
|
||||
|
||||
@@ -258,8 +272,9 @@
|
||||
}
|
||||
|
||||
.iconed-item .iconed-item-container {
|
||||
-fx-padding: 10 16 10 16;
|
||||
-fx-spacing: 10;
|
||||
/* Body row under φ title:body — sm/md denser than landing */
|
||||
-fx-padding: 8 13 8 13;
|
||||
-fx-spacing: 13;
|
||||
-fx-font-size: 14;
|
||||
-fx-alignment: CENTER_LEFT;
|
||||
}
|
||||
@@ -274,14 +289,14 @@
|
||||
}
|
||||
|
||||
.iconed-menu-item .iconed-item-container {
|
||||
-fx-padding: 8 16 8 16;
|
||||
-fx-spacing: 10;
|
||||
-fx-padding: 8 13 8 13;
|
||||
-fx-spacing: 13;
|
||||
-fx-font-size: 12;
|
||||
-fx-alignment: CENTER_LEFT;
|
||||
}
|
||||
|
||||
.menu-container {
|
||||
-fx-padding: 8 16 8 16;
|
||||
-fx-padding: 8 13 8 13;
|
||||
}
|
||||
|
||||
.popup-menu .scroll-bar .thumb {
|
||||
@@ -392,7 +407,7 @@
|
||||
}
|
||||
|
||||
.installer-item-wrapper:card {
|
||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 10, 0.12, -1, 2);
|
||||
-fx-effect: dropshadow(gaussian, -anthropic-shadow, 8, 0.08, 0, 1);
|
||||
}
|
||||
|
||||
.installer-item-wrapper .installer-item:list-item {
|
||||
@@ -421,57 +436,82 @@
|
||||
******************************************************************************/
|
||||
|
||||
.launch-pane {
|
||||
-fx-max-height: 57px;
|
||||
-fx-min-height: 57px;
|
||||
-fx-max-width: 230px;
|
||||
-fx-min-width: 230px;
|
||||
/* φ/Fibonacci CTA: 55×233 pairs with title bar 44 and sidebar rail */
|
||||
-fx-max-height: 55px;
|
||||
-fx-min-height: 55px;
|
||||
-fx-max-width: 233px;
|
||||
-fx-min-width: 233px;
|
||||
}
|
||||
|
||||
.launch-pane > .jfx-button {
|
||||
-fx-translate-y: 1px;
|
||||
-fx-max-height: 55px;
|
||||
-fx-min-height: 55px;
|
||||
-fx-background-color: -monet-primary-container;
|
||||
-fx-max-height: 53px;
|
||||
-fx-min-height: 53px;
|
||||
-fx-background-color: -monet-primary;
|
||||
-fx-opacity: 1; /* Override the opacity of disabled button */
|
||||
-fx-cursor: hand;
|
||||
-fx-scale-x: 1;
|
||||
-fx-scale-y: 1;
|
||||
}
|
||||
|
||||
.launch-pane > .jfx-button:hover {
|
||||
-fx-background-color: -anthropic-primary-hover;
|
||||
-fx-scale-x: 1.02;
|
||||
-fx-scale-y: 1.02;
|
||||
}
|
||||
|
||||
.launch-pane > .jfx-button:pressed,
|
||||
.launch-pane > .jfx-button:armed {
|
||||
-fx-background-color: #9A4A32;
|
||||
-fx-border-color: #F5F3EC;
|
||||
-fx-border-width: 2px;
|
||||
-fx-opacity: 0.90;
|
||||
-fx-scale-x: 0.96;
|
||||
-fx-scale-y: 0.96;
|
||||
}
|
||||
|
||||
.launch-pane > .jfx-button:focused {
|
||||
-fx-border-color: -anthropic-focus-ring;
|
||||
-fx-border-width: 1.5px;
|
||||
}
|
||||
|
||||
.launch-pane > .jfx-button.launch-button {
|
||||
-fx-max-width: 200px;
|
||||
-fx-min-width: 200px;
|
||||
-fx-border-width: 0 3px 0 0;
|
||||
-fx-max-width: 201px;
|
||||
-fx-min-width: 201px;
|
||||
-fx-border-width: 0 1px 0 0;
|
||||
-fx-border-color: linear-gradient(
|
||||
to bottom,
|
||||
transparent 0%,
|
||||
transparent 20%,
|
||||
-monet-on-surface-variant 20%,
|
||||
-monet-on-surface-variant 80%,
|
||||
transparent 80%,
|
||||
to bottom,
|
||||
transparent 0%,
|
||||
transparent 22%,
|
||||
-monet-on-primary 22%,
|
||||
-monet-on-primary 78%,
|
||||
transparent 78%,
|
||||
transparent 100%
|
||||
);
|
||||
-fx-background-radius: 4px 0 0 4px;
|
||||
-fx-background-radius: 8px 0 0 8px;
|
||||
}
|
||||
|
||||
.launch-pane > .jfx-button.menu-button {
|
||||
-fx-max-width: 27px;
|
||||
-fx-min-width: 27px;
|
||||
-fx-max-width: 32px;
|
||||
-fx-min-width: 32px;
|
||||
-fx-font-size: 15px;
|
||||
-fx-background-radius: 0 4px 4px 0;
|
||||
-fx-background-radius: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
.launch-pane > .jfx-button > StackPane > .jfx-rippler {
|
||||
-jfx-rippler-fill: -monet-on-primary-container;
|
||||
-jfx-rippler-disabled: true;
|
||||
-jfx-rippler-fill: transparent;
|
||||
-jfx-mask-type: CIRCLE;
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
.launch-pane > .launch-button .label {
|
||||
-fx-text-fill: -monet-on-primary-container;
|
||||
-fx-font-size: 14px;
|
||||
-fx-text-fill: -monet-on-primary;
|
||||
-fx-font-size: 15px;
|
||||
}
|
||||
|
||||
.launch-pane .svg {
|
||||
-fx-fill: -monet-on-primary-container;
|
||||
-fx-fill: -monet-on-primary;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
@@ -498,7 +538,8 @@
|
||||
}
|
||||
|
||||
.jfx-tab-pane .jfx-rippler {
|
||||
-jfx-rippler-fill: white;
|
||||
-jfx-rippler-disabled: true;
|
||||
-jfx-rippler-fill: transparent;
|
||||
}
|
||||
|
||||
.tab-label {
|
||||
@@ -520,8 +561,8 @@
|
||||
******************************************************************************/
|
||||
|
||||
.dialog-trigger {
|
||||
-fx-background-color: WHITE;
|
||||
-jfx-button-type: RAISED;
|
||||
-fx-background-color: -monet-surface-container-highest;
|
||||
-jfx-button-type: FLAT;
|
||||
-fx-font-size: 14.0px;
|
||||
}
|
||||
|
||||
@@ -657,7 +698,7 @@
|
||||
******************************************************************************/
|
||||
|
||||
.icon {
|
||||
-fx-fill: #FE774D;
|
||||
-fx-fill: -monet-primary;
|
||||
-fx-padding: 10.0;
|
||||
-fx-cursor: hand;
|
||||
}
|
||||
@@ -671,17 +712,18 @@
|
||||
.jfx-tool-bar {
|
||||
-fx-font-size: 13.0;
|
||||
-fx-pref-width: 100.0%;
|
||||
-fx-min-height: 40px;
|
||||
-fx-max-height: 40px;
|
||||
-fx-pref-height: 40px;
|
||||
-fx-min-height: 44px;
|
||||
-fx-max-height: 44px;
|
||||
-fx-pref-height: 44px;
|
||||
}
|
||||
|
||||
.jfx-tool-bar .jfx-decorator-title {
|
||||
-fx-font-weight: BOLD;
|
||||
-fx-font-size: 16px;
|
||||
}
|
||||
|
||||
.jfx-tool-bar.background {
|
||||
-fx-background-color: -monet-primary-container;
|
||||
-fx-background-color: -monet-surface-container;
|
||||
}
|
||||
|
||||
/*.jfx-tool-bar HBox {*/
|
||||
@@ -690,21 +732,21 @@
|
||||
/*}*/
|
||||
|
||||
.jfx-tool-bar .jfx-decorator-button {
|
||||
-fx-min-width: 40px;
|
||||
-fx-max-width: 40px;
|
||||
-fx-pref-width: 40px;
|
||||
-fx-min-height: 40px;
|
||||
-fx-max-height: 40px;
|
||||
-fx-pref-height: 40px;
|
||||
-fx-min-width: 44px;
|
||||
-fx-max-width: 44px;
|
||||
-fx-pref-width: 44px;
|
||||
-fx-min-height: 44px;
|
||||
-fx-max-height: 44px;
|
||||
-fx-pref-height: 44px;
|
||||
-fx-cursor: hand;
|
||||
}
|
||||
|
||||
.jfx-tool-bar .jfx-decorator-button .svg {
|
||||
-fx-fill: -monet-on-primary-container;
|
||||
-fx-fill: -monet-on-surface;
|
||||
}
|
||||
|
||||
.jfx-decorator-button .jfx-spinner .arc {
|
||||
-fx-stroke: -monet-on-primary-container;
|
||||
-fx-stroke: -monet-on-surface;
|
||||
}
|
||||
|
||||
.jfx-tool-bar Label {
|
||||
@@ -712,7 +754,7 @@
|
||||
}
|
||||
|
||||
.jfx-tool-bar.gray-background Label {
|
||||
-fx-text-fill: BLACK;
|
||||
-fx-text-fill: -monet-on-surface;
|
||||
}
|
||||
|
||||
.jfx-tool-bar Text {
|
||||
@@ -732,15 +774,15 @@
|
||||
}
|
||||
|
||||
.jfx-tool-bar-second {
|
||||
-fx-pref-height: 42;
|
||||
-fx-padding: 2 2 2 2;
|
||||
-fx-background-color: -monet-primary-container;
|
||||
-fx-pref-height: 48;
|
||||
-fx-padding: 4 8 4 8;
|
||||
-fx-background-color: -monet-surface-container;
|
||||
-fx-alignment: CENTER-LEFT;
|
||||
-fx-spacing: 8;
|
||||
}
|
||||
|
||||
.jfx-tool-bar-second .label {
|
||||
-fx-text-fill: -monet-on-primary-container;
|
||||
-fx-text-fill: -monet-on-surface;
|
||||
-fx-font-size: 16;
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
@@ -758,12 +800,31 @@
|
||||
-fx-min-height: -fx-toggle-icon4-size;
|
||||
-fx-background-radius: 5px;
|
||||
-fx-background-color: transparent;
|
||||
-fx-opacity: 1;
|
||||
-fx-scale-x: 1;
|
||||
-fx-scale-y: 1;
|
||||
-jfx-toggle-color: -monet-on-surface;
|
||||
-jfx-untoggle-color: transparent;
|
||||
}
|
||||
|
||||
.jfx-tool-bar-button:hover {
|
||||
-fx-background-color: -anthropic-icon-hover-wash;
|
||||
-fx-scale-x: 1.04;
|
||||
-fx-scale-y: 1.04;
|
||||
}
|
||||
|
||||
.jfx-tool-bar-button:pressed,
|
||||
.jfx-tool-bar-button:armed {
|
||||
-fx-background-color: rgba(217, 119, 87, 0.42);
|
||||
-fx-border-color: #C96442;
|
||||
-fx-border-width: 2px;
|
||||
-fx-opacity: 0.84;
|
||||
-fx-scale-x: 0.94;
|
||||
-fx-scale-y: 0.94;
|
||||
}
|
||||
|
||||
.jfx-tool-bar-button .icon {
|
||||
-fx-fill: rgb(204.0, 204.0, 51.0);
|
||||
-fx-fill: -monet-on-surface;
|
||||
-fx-padding: 10.0;
|
||||
}
|
||||
|
||||
@@ -774,13 +835,35 @@
|
||||
|
||||
.jfx-decorator-button {
|
||||
-fx-max-width: 40px;
|
||||
-fx-background-radius: 5px;
|
||||
-fx-background-radius: 8px;
|
||||
-fx-max-height: 40px;
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
.jfx-decorator-button:hover {
|
||||
-fx-background-color: -anthropic-hover-wash;
|
||||
-fx-scale-x: 1.04;
|
||||
-fx-scale-y: 1.04;
|
||||
}
|
||||
|
||||
.jfx-decorator-button:pressed,
|
||||
.jfx-decorator-button:armed {
|
||||
-fx-background-color: rgba(217, 119, 87, 0.42);
|
||||
-fx-border-color: #C96442;
|
||||
-fx-border-width: 2px;
|
||||
-fx-opacity: 0.84;
|
||||
-fx-scale-x: 0.94;
|
||||
-fx-scale-y: 0.94;
|
||||
}
|
||||
|
||||
.jfx-decorator-button:focused {
|
||||
-fx-border-color: -anthropic-focus-ring;
|
||||
-fx-border-width: 1.5px;
|
||||
-fx-border-radius: 8px;
|
||||
}
|
||||
|
||||
.jfx-decorator-button .icon {
|
||||
-fx-fill: rgb(204.0, 204.0, 51.0);
|
||||
-fx-fill: -monet-on-surface;
|
||||
-fx-padding: 0.0;
|
||||
}
|
||||
|
||||
@@ -848,6 +931,11 @@
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
.jfx-rippler {
|
||||
-jfx-rippler-disabled: true;
|
||||
-jfx-rippler-fill: transparent;
|
||||
}
|
||||
|
||||
.jfx-rippler:hover {
|
||||
-fx-cursor: hand;
|
||||
}
|
||||
@@ -861,20 +949,80 @@
|
||||
.jfx-button {
|
||||
-jfx-disable-visual-focus: true;
|
||||
-fx-cursor: hand;
|
||||
-fx-background-radius: 6px;
|
||||
-fx-background-color: transparent;
|
||||
-fx-scale-x: 1;
|
||||
-fx-scale-y: 1;
|
||||
}
|
||||
|
||||
.jfx-button .jfx-rippler {
|
||||
-jfx-rippler-fill: -monet-on-surface;
|
||||
-jfx-rippler-disabled: true;
|
||||
-jfx-rippler-fill: transparent;
|
||||
-jfx-mask-type: CIRCLE;
|
||||
-fx-padding: 0.0;
|
||||
}
|
||||
|
||||
/* Calm tactile feedback: tone + border + opacity (no Material ripple). */
|
||||
.jfx-button:hover {
|
||||
-fx-background-color: -anthropic-hover-wash;
|
||||
-fx-border-color: rgba(201, 100, 66, 0.45);
|
||||
-fx-border-width: 1px;
|
||||
-fx-scale-x: 1.02;
|
||||
-fx-scale-y: 1.02;
|
||||
}
|
||||
|
||||
.jfx-button:pressed,
|
||||
.jfx-button:armed {
|
||||
-fx-background-color: rgba(217, 119, 87, 0.42);
|
||||
-fx-border-color: #C96442;
|
||||
-fx-border-width: 2px;
|
||||
-fx-opacity: 0.84;
|
||||
-fx-scale-x: 0.94;
|
||||
-fx-scale-y: 0.94;
|
||||
}
|
||||
|
||||
.jfx-button:focused {
|
||||
-fx-border-color: -anthropic-focus-ring;
|
||||
-fx-border-width: 2px;
|
||||
-fx-border-radius: 6px;
|
||||
-fx-border-insets: 0;
|
||||
-fx-effect: dropshadow(gaussian, -anthropic-focus-glow, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.jfx-button:disabled,
|
||||
.jfx-button:disabled:hover,
|
||||
.jfx-button:disabled:pressed,
|
||||
.jfx-button:disabled:armed {
|
||||
-fx-scale-x: 1;
|
||||
-fx-scale-y: 1;
|
||||
-fx-opacity: 0.45;
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
.jfx-button-raised {
|
||||
-fx-background-color: -monet-primary;
|
||||
-fx-background-radius: 6px;
|
||||
}
|
||||
|
||||
.jfx-button-raised:hover {
|
||||
-fx-background-color: -anthropic-primary-hover;
|
||||
-fx-scale-x: 1.02;
|
||||
-fx-scale-y: 1.02;
|
||||
}
|
||||
|
||||
.jfx-button-raised:pressed,
|
||||
.jfx-button-raised:armed {
|
||||
-fx-background-color: #9A4A32;
|
||||
-fx-border-color: #F5F3EC;
|
||||
-fx-border-width: 2px;
|
||||
-fx-opacity: 0.90;
|
||||
-fx-scale-x: 0.96;
|
||||
-fx-scale-y: 0.96;
|
||||
}
|
||||
|
||||
.jfx-button-raised .jfx-rippler {
|
||||
-jfx-rippler-fill: -monet-on-primary;
|
||||
-jfx-rippler-disabled: true;
|
||||
-jfx-rippler-fill: transparent;
|
||||
-jfx-mask-type: CIRCLE;
|
||||
-fx-padding: 0.0;
|
||||
}
|
||||
@@ -887,11 +1035,33 @@
|
||||
.jfx-button-border {
|
||||
-fx-background-color: transparent;
|
||||
-fx-border-color: -monet-outline;
|
||||
-fx-border-radius: 5px;
|
||||
-fx-border-width: 0.2px;
|
||||
-fx-border-radius: 6px;
|
||||
-fx-border-width: 1px;
|
||||
-fx-padding: 8px;
|
||||
}
|
||||
|
||||
.jfx-button-border:hover {
|
||||
-fx-background-color: -anthropic-hover-wash;
|
||||
-fx-border-color: -monet-primary;
|
||||
-fx-scale-x: 1.02;
|
||||
-fx-scale-y: 1.02;
|
||||
}
|
||||
|
||||
.jfx-button-border:pressed,
|
||||
.jfx-button-border:armed {
|
||||
-fx-background-color: rgba(217, 119, 87, 0.28);
|
||||
-fx-border-color: #C96442;
|
||||
-fx-border-width: 2px;
|
||||
-fx-opacity: 0.88;
|
||||
-fx-scale-x: 0.96;
|
||||
-fx-scale-y: 0.96;
|
||||
}
|
||||
|
||||
.jfx-button-border:focused {
|
||||
-fx-border-color: -anthropic-focus-ring;
|
||||
-fx-border-width: 1.5px;
|
||||
}
|
||||
|
||||
.jfx-button-border, .jfx-button-border * {
|
||||
-fx-text-fill: -monet-primary;
|
||||
}
|
||||
@@ -901,6 +1071,22 @@
|
||||
-fx-background-radius: 50px;
|
||||
}
|
||||
|
||||
.jfx-button-raised-round:hover {
|
||||
-fx-background-color: -anthropic-primary-container-hover;
|
||||
-fx-scale-x: 1.03;
|
||||
-fx-scale-y: 1.03;
|
||||
}
|
||||
|
||||
.jfx-button-raised-round:pressed,
|
||||
.jfx-button-raised-round:armed {
|
||||
-fx-background-color: #9A4A32;
|
||||
-fx-border-color: #F5F3EC;
|
||||
-fx-border-width: 2px;
|
||||
-fx-opacity: 0.90;
|
||||
-fx-scale-x: 0.96;
|
||||
-fx-scale-y: 0.96;
|
||||
}
|
||||
|
||||
.menu-up-down-button .label {
|
||||
-fx-text-fill: -monet-on-surface;
|
||||
}
|
||||
@@ -993,7 +1179,7 @@
|
||||
|
||||
.list-cell:selected, .jfx-list-cell:selected,
|
||||
.list-cell:hover, .jfx-list-cell:hover {
|
||||
-fx-text-fill: black;
|
||||
-fx-text-fill: -monet-on-surface;
|
||||
}
|
||||
|
||||
.jfx-list-cell .jfx-rippler {
|
||||
@@ -1040,31 +1226,31 @@
|
||||
|
||||
.options-list {
|
||||
-fx-background-color: transparent;
|
||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 5, 0.06, -0.5, 1);
|
||||
-fx-effect: dropshadow(gaussian, -anthropic-shadow, 4, 0.04, 0, 1);
|
||||
}
|
||||
|
||||
.depth-0 {
|
||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0), 0, 0, 0, 0);
|
||||
-fx-effect: dropshadow(gaussian, transparent, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.depth-1 {
|
||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 10, 0.12, -1, 2);
|
||||
-fx-effect: dropshadow(gaussian, -anthropic-shadow, 8, 0.08, 0, 1);
|
||||
}
|
||||
|
||||
.depth-2 {
|
||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 15, 0.16, 0, 4);
|
||||
-fx-effect: dropshadow(gaussian, -anthropic-shadow-strong, 12, 0.10, 0, 2);
|
||||
}
|
||||
|
||||
.depth-3 {
|
||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 20, 0.19, 0, 6);
|
||||
-fx-effect: dropshadow(gaussian, -anthropic-shadow-strong, 16, 0.12, 0, 3);
|
||||
}
|
||||
|
||||
.depth-4 {
|
||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 25, 0.25, 0, 8);
|
||||
-fx-effect: dropshadow(gaussian, -anthropic-shadow-strong, 20, 0.14, 0, 4);
|
||||
}
|
||||
|
||||
.depth-5 {
|
||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 30, 0.3, 0, 10);
|
||||
-fx-effect: dropshadow(gaussian, -anthropic-shadow-strong, 24, 0.16, 0, 5);
|
||||
}
|
||||
|
||||
.padding-8 {
|
||||
@@ -1073,30 +1259,30 @@
|
||||
|
||||
.card {
|
||||
-fx-background-color: -monet-surface-container-low-transparent-80;
|
||||
-fx-background-radius: 4;
|
||||
-fx-padding: 8px;
|
||||
-fx-background-radius: 8;
|
||||
-fx-padding: 13px;
|
||||
|
||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 10, 0.12, -1, 2);
|
||||
-fx-effect: dropshadow(gaussian, -anthropic-shadow, 6, 0.06, 0, 1);
|
||||
}
|
||||
|
||||
.card-no-padding {
|
||||
-fx-background-color: -monet-surface-container-low-transparent-80;
|
||||
-fx-background-radius: 4;
|
||||
-fx-background-radius: 8;
|
||||
|
||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 10, 0.12, -1, 2);
|
||||
-fx-effect: dropshadow(gaussian, -anthropic-shadow, 6, 0.06, 0, 1);
|
||||
}
|
||||
|
||||
.card-non-transparent {
|
||||
-fx-background-color: -monet-surface-container-low;
|
||||
-fx-background-radius: 4;
|
||||
-fx-padding: 8px;
|
||||
-fx-background-radius: 8;
|
||||
-fx-padding: 13px;
|
||||
|
||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 5, 0.06, -0.5, 1);
|
||||
-fx-effect: dropshadow(gaussian, -anthropic-shadow, 4, 0.04, 0, 1);
|
||||
}
|
||||
|
||||
.card-list {
|
||||
-fx-padding: 10;
|
||||
-fx-spacing: 10;
|
||||
-fx-padding: 13;
|
||||
-fx-spacing: 13;
|
||||
}
|
||||
|
||||
.md-list-cell {
|
||||
@@ -1132,7 +1318,8 @@
|
||||
-fx-background-color: -monet-surface;
|
||||
-fx-border-color: -monet-outline-variant;
|
||||
-fx-border-width: 1 0 0 0;
|
||||
-fx-padding: 10 16 10 16;
|
||||
/* Body of title:body — sm vertical / md horizontal */
|
||||
-fx-padding: 8 13 8 13;
|
||||
-fx-font-size: 12;
|
||||
}
|
||||
|
||||
@@ -1324,9 +1511,11 @@
|
||||
|
||||
.jfx-decorator-button {
|
||||
-fx-pref-width: 50px;
|
||||
-fx-background-radius: 50px;
|
||||
-fx-background-radius: 8px;
|
||||
-fx-pref-height: 50px;
|
||||
-fx-background-color: transparent;
|
||||
-fx-border-width: 0;
|
||||
-fx-opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1340,17 +1529,38 @@
|
||||
-fx-min-height: -fx-toggle-icon4-size;
|
||||
-fx-background-radius: 50px;
|
||||
-fx-background-color: transparent;
|
||||
-fx-opacity: 1;
|
||||
-fx-scale-x: 1;
|
||||
-fx-scale-y: 1;
|
||||
-jfx-toggle-color: -monet-primary;
|
||||
-jfx-untoggle-color: transparent;
|
||||
}
|
||||
|
||||
.toggle-icon4:hover {
|
||||
-fx-background-color: -anthropic-icon-hover-wash;
|
||||
-fx-scale-x: 1.06;
|
||||
-fx-scale-y: 1.06;
|
||||
}
|
||||
|
||||
.toggle-icon4:pressed,
|
||||
.toggle-icon4:armed {
|
||||
-fx-background-color: rgba(217, 119, 87, 0.42);
|
||||
-fx-border-color: #C96442;
|
||||
-fx-border-width: 2px;
|
||||
-fx-border-radius: 50px;
|
||||
-fx-opacity: 0.84;
|
||||
-fx-scale-x: 0.90;
|
||||
-fx-scale-y: 0.90;
|
||||
}
|
||||
|
||||
.toggle-icon4 .icon {
|
||||
-fx-fill: rgb(204.0, 204.0, 51.0);
|
||||
-fx-fill: -monet-on-surface;
|
||||
-fx-padding: 10.0;
|
||||
}
|
||||
|
||||
.toggle-icon4 .jfx-rippler {
|
||||
-jfx-rippler-fill: -monet-primary;
|
||||
-jfx-rippler-disabled: true;
|
||||
-jfx-rippler-fill: transparent;
|
||||
-jfx-mask-type: CIRCLE;
|
||||
}
|
||||
|
||||
@@ -1365,12 +1575,33 @@
|
||||
-fx-padding: 0;
|
||||
-fx-background-radius: 25px;
|
||||
-fx-background-color: transparent;
|
||||
-fx-opacity: 1;
|
||||
-fx-scale-x: 1;
|
||||
-fx-scale-y: 1;
|
||||
-jfx-toggle-color: -monet-primary;
|
||||
-jfx-untoggle-color: transparent;
|
||||
}
|
||||
|
||||
.toggle-icon-tiny:hover {
|
||||
-fx-background-color: -anthropic-icon-hover-wash;
|
||||
-fx-opacity: 0.92;
|
||||
-fx-scale-x: 1.08;
|
||||
-fx-scale-y: 1.08;
|
||||
}
|
||||
|
||||
.toggle-icon-tiny:pressed,
|
||||
.toggle-icon-tiny:armed {
|
||||
-fx-background-color: rgba(217, 119, 87, 0.42);
|
||||
-fx-border-color: #C96442;
|
||||
-fx-border-width: 2px;
|
||||
-fx-border-radius: 50px;
|
||||
-fx-opacity: 0.80;
|
||||
-fx-scale-x: 0.86;
|
||||
-fx-scale-y: 0.86;
|
||||
}
|
||||
|
||||
.toggle-icon-tiny .icon {
|
||||
-fx-fill: rgb(204.0, 204.0, 51.0);
|
||||
-fx-fill: -monet-on-surface;
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
@@ -1379,7 +1610,8 @@
|
||||
}
|
||||
|
||||
.toggle-icon-tiny .jfx-rippler {
|
||||
-jfx-rippler-fill: -monet-primary;
|
||||
-jfx-rippler-disabled: true;
|
||||
-jfx-rippler-fill: transparent;
|
||||
-jfx-mask-type: CIRCLE;
|
||||
}
|
||||
|
||||
@@ -1393,10 +1625,31 @@
|
||||
-fx-min-height: -fx-announcement-close-button-size;
|
||||
-fx-background-radius: 50px;
|
||||
-fx-background-color: transparent;
|
||||
-fx-opacity: 1;
|
||||
-fx-scale-x: 1;
|
||||
-fx-scale-y: 1;
|
||||
}
|
||||
|
||||
.announcement-close-button:hover {
|
||||
-fx-background-color: -anthropic-icon-hover-wash;
|
||||
-fx-scale-x: 1.06;
|
||||
-fx-scale-y: 1.06;
|
||||
}
|
||||
|
||||
.announcement-close-button:pressed,
|
||||
.announcement-close-button:armed {
|
||||
-fx-background-color: rgba(217, 119, 87, 0.42);
|
||||
-fx-border-color: #C96442;
|
||||
-fx-border-width: 2px;
|
||||
-fx-border-radius: 50px;
|
||||
-fx-opacity: 0.84;
|
||||
-fx-scale-x: 0.90;
|
||||
-fx-scale-y: 0.90;
|
||||
}
|
||||
|
||||
.announcement-close-button .jfx-rippler {
|
||||
-jfx-rippler-fill: -monet-primary;
|
||||
-jfx-rippler-disabled: true;
|
||||
-jfx-rippler-fill: transparent;
|
||||
-jfx-mask-type: CIRCLE;
|
||||
}
|
||||
|
||||
@@ -1598,12 +1851,12 @@
|
||||
}
|
||||
|
||||
.jfx-combo-box-warning {
|
||||
-jfx-focus-color: #D34336;
|
||||
-jfx-unfocus-color: #D34336;
|
||||
-jfx-focus-color: -monet-error;
|
||||
-jfx-unfocus-color: -monet-error;
|
||||
}
|
||||
|
||||
.jfx-combo-box-warning .text {
|
||||
-fx-fill: #D34336;
|
||||
-fx-fill: -monet-error;
|
||||
}
|
||||
|
||||
/*.combo-box-popup .list-view .jfx-list-cell .jfx-rippler {*/
|
||||
@@ -1781,15 +2034,16 @@
|
||||
*******************************************************************************/
|
||||
|
||||
.jfx-decorator {
|
||||
-fx-decorator-color: -monet-primary-container;
|
||||
-fx-decorator-color: -monet-surface-container;
|
||||
}
|
||||
|
||||
.jfx-decorator-drawer {
|
||||
-fx-background-color: rgba(244, 244, 244, 0.5);
|
||||
-fx-background-color: -monet-surface-transparent-80;
|
||||
}
|
||||
|
||||
.jfx-decorator-title {
|
||||
-fx-font-size: 14;
|
||||
-fx-font-size: 16;
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
|
||||
.debug-border {
|
||||
@@ -1798,11 +2052,11 @@
|
||||
}
|
||||
|
||||
.content-background {
|
||||
-fx-background-color: -monet-surface-transparent-50;
|
||||
-fx-background-color: -monet-surface-transparent-80;
|
||||
}
|
||||
|
||||
.gray-background {
|
||||
-fx-background-color: -monet-surface-transparent-50;
|
||||
-fx-background-color: -monet-surface-container-low-transparent-80;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
@@ -1812,12 +2066,12 @@
|
||||
******************************************************************************/
|
||||
|
||||
.scroll-bar:vertical > .track-background, .scroll-bar:horizontal > .track-background {
|
||||
-fx-background-color: #F1F1F1;
|
||||
-fx-background-color: -anthropic-scrollbar-track;
|
||||
-fx-background-insets: 0.0;
|
||||
}
|
||||
|
||||
.scroll-bar:vertical .thumb, .scroll-bar:horizontal .thumb {
|
||||
-fx-background-color: #BCBCBC;
|
||||
-fx-background-color: -anthropic-scrollbar-thumb;
|
||||
-fx-background-insets: 0.0;
|
||||
-fx-background-radius: 1.0;
|
||||
}
|
||||
@@ -1831,7 +2085,7 @@
|
||||
}
|
||||
|
||||
.scroll-bar > .increment-button > .increment-arrow, .scroll-bar > .decrement-button > .decrement-arrow {
|
||||
-fx-background-color: rgb(150.0, 150.0, 150.0);
|
||||
-fx-background-color: -monet-on-surface-variant;
|
||||
}
|
||||
|
||||
.scroll-bar > .increment-button > .increment-arrow {
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
Copyright 2014-2025 Adobe (http://www.adobe.com/), with Reserved Font
|
||||
Name 'Source'. Source is a trademark of Adobe in the United States
|
||||
and/or other countries.
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License,
|
||||
Version 1.1.
|
||||
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font
|
||||
creation efforts of academic and linguistic communities, and to
|
||||
provide a free and open framework in which fonts may be shared and
|
||||
improved in partnership with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply to
|
||||
any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software
|
||||
components as distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to,
|
||||
deleting, or substituting -- in part or in whole -- any of the
|
||||
components of the Original Version, by changing formats or by porting
|
||||
the Font Software to a new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed,
|
||||
modify, redistribute, and sell modified and unmodified copies of the
|
||||
Font Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components, in
|
||||
Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the
|
||||
corresponding Copyright Holder. This restriction only applies to the
|
||||
primary font name as presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created using
|
||||
the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
Binary file not shown.
@@ -936,9 +936,9 @@ launcher.cache_directory.disabled=Disabled
|
||||
launcher.cache_directory.invalid=Failed to create a cache directory, falling back to default.
|
||||
launcher.crash=HMCL has encountered a fatal error! Please copy the following log and ask for help on GitHub or other Minecraft forums.
|
||||
launcher.crash.java_internal_error=HMCL has encountered a fatal error because your Java is corrupted. Please uninstall your Java and download a suitable Java <a href="https://bell-sw.com/pages/downloads/#downloads">here</a>.
|
||||
launcher.title=Hello Minecraft! Launcher v%s
|
||||
launcher.title.modified=Hello Minecraft! Launcher v%s (Modified)
|
||||
launcher.title.unofficial=Hello Minecraft! Launcher v%s (Unofficial Build)
|
||||
launcher.title=TDLM v%s
|
||||
launcher.title.modified=TDLM v%s (Modified)
|
||||
launcher.title.unofficial=TDLM v%s (Unofficial Build)
|
||||
launcher.update_java=Please update your Java version.
|
||||
|
||||
libraries.download=Downloading Libraries
|
||||
|
||||
@@ -756,9 +756,9 @@ launcher.cache_directory.disabled=禁 (恒用戲案夾之徑)
|
||||
launcher.cache_directory.invalid=自訂之快取夾不能立。既復初。
|
||||
launcher.crash=HMCL 有謬而弗能正。宜鈔下文而報謬于右下之鈕。
|
||||
launcher.crash.java_internal_error=HMCL 不能行,以爪哇壞也。宜去是爪哇,而擊<a href="https://bell-sw.com/pages/downloads/#downloads">此</a>以置爪哇之適者。
|
||||
launcher.title=Hello Minecraft! Launcher v%s
|
||||
launcher.title.modified=Hello Minecraft! Launcher %s (已改)
|
||||
launcher.title.unofficial=Hello Minecraft! Launcher %s (非官版)
|
||||
launcher.title=TDLM v%s
|
||||
launcher.title.modified=TDLM %s (已改)
|
||||
launcher.title.unofficial=TDLM %s (非官版)
|
||||
launcher.update_java=宜迭更爪哇。\n君可求助於 https://docs.hmcl.net/help.html。
|
||||
|
||||
libraries.download=引之所依
|
||||
|
||||
@@ -759,9 +759,9 @@ launcher.cache_directory.disabled=禁用 (总是使用游戏文件夹路径)
|
||||
launcher.cache_directory.invalid=无法创建自定义的缓存文件夹。已经恢复到默认设置。
|
||||
launcher.crash=HMCL 遇到了无法处理的错误。请复制下列内容并点击右下角的按钮反馈问题。
|
||||
launcher.crash.java_internal_error=HMCL 由于当前 Java 损坏而无法继续运行。请卸载当前 Java,点击 <a href="https://bell-sw.com/pages/downloads/#downloads">此处</a> 安装合适的 Java 版本。
|
||||
launcher.title=Hello Minecraft! Launcher %s
|
||||
launcher.title.modified=Hello Minecraft! Launcher %s (已修改)
|
||||
launcher.title.unofficial=Hello Minecraft! Launcher %s (非官方构建)
|
||||
launcher.title=TDLM %s
|
||||
launcher.title.modified=TDLM %s (已修改)
|
||||
launcher.title.unofficial=TDLM %s (非官方构建)
|
||||
launcher.update_java=请更新你的 Java。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
|
||||
|
||||
libraries.download=下载依赖库
|
||||
|
||||
@@ -753,9 +753,9 @@ launcher.cache_directory.disabled=停用
|
||||
launcher.cache_directory.invalid=無法建立自訂的快取目錄。已還原至預設設定。
|
||||
launcher.crash=HMCL 遇到了無法處理的錯誤。請複製下列內容並透過 GitHub 或其他 Minecraft 論壇回報問題。
|
||||
launcher.crash.java_internal_error=HMCL 由於目前 Java 損壞而無法繼續執行。請移除目前 Java,點擊 <a href="https://bell-sw.com/pages/downloads/#downloads">此處</a> 安裝合適的 Java 版本。
|
||||
launcher.title=Hello Minecraft! Launcher %s
|
||||
launcher.title.modified=Hello Minecraft! Launcher %s (已修改)
|
||||
launcher.title.unofficial=Hello Minecraft! Launcher %s (非官方建置)
|
||||
launcher.title=TDLM %s
|
||||
launcher.title.modified=TDLM %s (已修改)
|
||||
launcher.title.unofficial=TDLM %s (非官方建置)
|
||||
launcher.update_java=請更新你的 Java
|
||||
|
||||
libraries.download=下載依賴庫
|
||||
|
||||
@@ -15,6 +15,8 @@ public final class ThemeColorTest {
|
||||
@Test
|
||||
public void testOf() {
|
||||
assertEquals(new ThemeColor("#AABBCC", Color.web("#AABBCC")), ThemeColor.of("#AABBCC"));
|
||||
assertEquals(new ThemeColor("anthropic_orange", Color.web("#D97757")), ThemeColor.of("anthropic_orange"));
|
||||
assertEquals(new ThemeColor("stone", Color.web("#3D453C")), ThemeColor.of("stone"));
|
||||
assertEquals(new ThemeColor("blue", Color.web("#5C6BC0")), ThemeColor.of("blue"));
|
||||
assertEquals(new ThemeColor("darker_blue", Color.web("#283593")), ThemeColor.of("darker_blue"));
|
||||
assertEquals(new ThemeColor("green", Color.web("#43A047")), ThemeColor.of("green"));
|
||||
|
||||
Reference in new issue
Block a user