feat: platform-style window buttons, 21px dialog radius, thin round progress bar, title mark

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
layue13andClaude Sonnet 5.5 committed 2026-11-08 14:33:44 +08:00
1 parent 0269e54b95
commit 07ff244cd2
3 files changed
+138 -5

No files matched your search

@@ -27,6 +27,7 @@ import javafx.util.Duration;
import org.glavo.monetfx.ColorRole;
import org.glavo.monetfx.ColorScheme;
import org.jackhuang.hmcl.theme.Themes;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import org.jackhuang.hmcl.theme.WarmPalette;
import org.jackhuang.hmcl.ui.*;
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
@@ -102,7 +103,11 @@ final class MainWindowPane extends StackPane {
titleBar = new BorderPane();
titleBar.setPickOnBounds(false);
titleBar.getStyleClass().add("jfx-tool-bar");
titleBar.setRight(windowButtons);
if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) {
titleBar.setLeft(windowButtons);
} else {
titleBar.setRight(windowButtons);
}
navBarPane = new TransitionPane();
titleBar.setCenter(navBarPane);
@@ -163,21 +168,39 @@ final class MainWindowPane extends StackPane {
/// @return a size-limited top-right button row
private HBox createWindowButtons() {
HBox buttons = new HBox();
buttons.setAlignment(Pos.TOP_RIGHT);
decorator.capableDraggingWindow(buttons);
if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) {
// macOS style: round close/minimize buttons on the left. Zoom is shown but disabled.
buttons.setAlignment(Pos.CENTER_LEFT);
buttons.setSpacing(8);
buttons.setPadding(new Insets(0, 0, 0, 20));
buttons.setMaxSize(Region.USE_PREF_SIZE, 40);
JFXButton closeButton = macButton("mac-close", decorator::closeWindow);
JFXButton minimizeButton = macButton("mac-minimize", decorator::minimizeWindow);
JFXButton zoomButton = macButton("mac-zoom", () -> {
});
zoomButton.setDisable(true);
buttons.getChildren().setAll(closeButton, minimizeButton, zoomButton);
return buttons;
}
// Windows and Linux style: flat rectangular buttons on the right.
buttons.setAlignment(Pos.TOP_RIGHT);
buttons.setMaxSize(Region.USE_PREF_SIZE, 40);
JFXButton minimizeButton = new JFXButton();
minimizeButton.setFocusTraversable(false);
minimizeButton.setGraphic(SVG.MINIMIZE_CENTER.createIcon(Themes.titleFillProperty()));
minimizeButton.getStyleClass().add("jfx-decorator-button");
minimizeButton.getStyleClass().add("window-button");
minimizeButton.setOnAction(event -> decorator.minimizeWindow());
decorator.forbidDraggingWindow(minimizeButton);
JFXButton closeButton = new JFXButton();
closeButton.setFocusTraversable(false);
closeButton.setGraphic(SVG.CLOSE.createIcon(Themes.titleFillProperty()));
closeButton.getStyleClass().add("jfx-decorator-button");
closeButton.getStyleClass().addAll("window-button", "window-close-button");
closeButton.setOnAction(event -> decorator.closeWindow());
decorator.forbidDraggingWindow(closeButton);
@@ -185,6 +208,19 @@ final class MainWindowPane extends StackPane {
return buttons;
}
/// Creates one round macOS-style window button.
///
/// @param styleClass the class that selects the button color
/// @param action the action to run when the button is pressed
private JFXButton macButton(String styleClass, Runnable action) {
JFXButton button = new JFXButton();
button.setFocusTraversable(false);
button.getStyleClass().addAll("mac-window-button", styleClass);
button.setOnAction(event -> action.run());
decorator.forbidDraggingWindow(button);
return button;
}
/// Recomputes the title-bar background for the current theme and transparency preference.
private void updateTitleBarBackground() {
titleBar.setBackground(new Background(new BackgroundFill(
@@ -71,7 +71,8 @@ public final class MainPage extends StackPane implements DecoratorPage {
titleNode.setPadding(new Insets(0, 0, 0, 2));
titleNode.setAlignment(Pos.CENTER_LEFT);
ImageView titleIcon = new ImageView(FXUtils.newBuiltinImage("/assets/img/icon-title.png"));
Region titleIcon = new Region();
titleIcon.getStyleClass().add("title-mark");
Label titleLabel = new Label();
titleLabel.textProperty().bind(Controllers.launcherTitleProperty());
if (I18n.isUpsideDown()) {
@@ -2253,3 +2253,99 @@
-fx-border-color: transparent -monet-outline-variant transparent transparent;
-fx-border-width: 0 1px 0 0;
}
/* ---- Window buttons ---- */
.window-button {
-fx-min-width: 46px;
-fx-pref-width: 46px;
-fx-max-width: 46px;
-fx-min-height: 40px;
-fx-pref-height: 40px;
-fx-max-height: 40px;
-fx-background-color: transparent;
-fx-background-radius: 0;
-fx-cursor: default;
}
.window-button:hover {
-fx-background-color: -monet-surface-container;
}
.window-close-button:hover {
-fx-background-color: #C42B1C;
}
.window-close-button:hover .svg {
-fx-fill: white;
}
.mac-window-button {
-fx-min-width: 12px;
-fx-pref-width: 12px;
-fx-max-width: 12px;
-fx-min-height: 12px;
-fx-pref-height: 12px;
-fx-max-height: 12px;
-fx-padding: 0;
-fx-background-radius: 6px;
-fx-border-radius: 6px;
-fx-border-width: 0.5px;
-fx-cursor: default;
}
.mac-close { -fx-background-color: #FF5F57; -fx-border-color: #E0443E; }
.mac-minimize { -fx-background-color: #FEBC2E; -fx-border-color: #DEA123; }
.mac-zoom { -fx-background-color: #28C840; -fx-border-color: #1AAB29; }
.mac-zoom:disabled { -fx-opacity: 1; -fx-background-color: #D5CEC1; -fx-border-color: #C2BAAB; }
.mac-close:hover, .mac-minimize:hover { -fx-effect: innershadow(gaussian, rgba(0, 0, 0, 0.25), 3, 0.2, 0, 0); }
/* ---- Dialogs: one corner radius family (8 / 13 / 21) ---- */
.jfx-dialog-layout, .task-executor-dialog-layout {
-fx-background-color: -monet-surface-bright;
-fx-background-radius: 21px;
-fx-border-color: -monet-outline-variant;
-fx-border-radius: 21px;
-fx-border-width: 1px;
-fx-effect: dropshadow(gaussian, rgba(60, 40, 20, 0.18), 34, 0.1, 0, 8);
}
.dialog-accept, .dialog-cancel, .dialog-error {
-fx-background-radius: 8px;
}
/* ---- Progress bar: thin, round-ended ---- */
.jfx-progress-bar > .track {
-fx-background-color: -monet-surface-container-highest;
-fx-pref-height: 6px;
-fx-min-height: 6px;
-fx-max-height: 6px;
}
.jfx-progress-bar > .bar {
-fx-background-color: -monet-primary;
-fx-padding: 0;
-fx-pref-height: 6px;
-fx-min-height: 6px;
-fx-max-height: 6px;
}
.jfx-progress-bar > .track,
.jfx-progress-bar > .bar {
-fx-background-radius: 999px;
}
.card, .card-no-padding, .card-non-transparent {
-fx-background-radius: 13px;
}
/* The small accent mark that stands in for a logo in the title bar. */
.title-mark {
-fx-background-color: -monet-primary;
-fx-background-radius: 5px;
-fx-min-width: 18px;
-fx-pref-width: 18px;
-fx-max-width: 18px;
-fx-min-height: 18px;
-fx-pref-height: 18px;
-fx-max-height: 18px;
}