diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/MainWindowPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/MainWindowPane.java index 0152a00b84..fb36167694 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/MainWindowPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/MainWindowPane.java @@ -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( diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java index dc2cef9374..56a71b760b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java @@ -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()) { diff --git a/HMCL/src/main/resources/assets/css/root.css b/HMCL/src/main/resources/assets/css/root.css index 39e81db0f9..c090d3c119 100644 --- a/HMCL/src/main/resources/assets/css/root.css +++ b/HMCL/src/main/resources/assets/css/root.css @@ -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; +}