diff --git a/HMCL/src/main/java/com/jfoenix/controls/JFXPopup.java b/HMCL/src/main/java/com/jfoenix/controls/JFXPopup.java index b3e40b6f2..89e9900d2 100644 --- a/HMCL/src/main/java/com/jfoenix/controls/JFXPopup.java +++ b/HMCL/src/main/java/com/jfoenix/controls/JFXPopup.java @@ -137,7 +137,7 @@ public class JFXPopup extends PopupControl { boolean isRTL = node.getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT; - this.show(parent, + this.show(node, parent.getX() + scene.getX() + origin.getX() + (hAlign == PopupHPosition.RIGHT ? ((Region) node).getWidth() : 0), parent.getY() + origin.getY() + scene.getY() + (vAlign == PopupVPosition.BOTTOM ? ((Region) node).getHeight() : 0) ); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java index 47cac09fc..8c200f8bc 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java @@ -123,6 +123,7 @@ public enum SVG { TEXTURE("M4.4-3Q3.925-3.1 3.5125-3.5125T3-4.4L19.6-21Q20.125-20.875 20.5-20.4875T21.025-19.6L4.4-3ZM3-9.3V-12.1L11.9-21H14.7L3-9.3ZM3-17V-19Q3-19.825 3.5875-20.4125T5-21H7L3-17ZM17-3 21-7V-5Q21-4.175 20.4125-3.5875T19-3H17ZM9.3-3 21-14.7V-11.9L12.1-3H9.3Z"), TRIP("M4 21Q3.175 21 2.5875 20.4125T2 19V8Q2 7.175 2.5875 6.5875T4 6H8V4Q8 3.175 8.5875 2.5875T10 2H14Q14.825 2 15.4125 2.5875T16 4V6H20Q20.825 6 21.4125 6.5875T22 8V19Q22 19.825 21.4125 20.4125T20 21H4ZM10 6H14V4H10V6ZM6 8H4V19H6V8ZM16 19V8H8V19H16ZM18 8V19H20V8H18ZM12 13.5Z"), TUNE("M11 21V15H13V17H21V19H13V21H11ZM3 19V17H9V19H3ZM7 15V13H3V11H7V9H9V15H7ZM11 13V11H21V13H11ZM15 9V3H17V5H21V7H17V9H15ZM3 7V5H13V7H3Z"), + UNFOLD_MORE("M12 21 7.5 16.5l1.45-1.45L12 18.1l3.05-3.05 1.45 1.45L12 21ZM8.95 9.05 7.5 7.6 12 3.1l4.5 4.5-1.45 1.45L12 6 8.95 9.05Z"), UPDATE("M12 21Q10.125 21 8.4875 20.2875T5.6375 18.3625Q4.425 17.15 3.7125 15.5125T3 12Q3 10.125 3.7125 8.4875T5.6375 5.6375Q6.85 4.425 8.4875 3.7125T12 3Q14.05 3 15.8875 3.875T19 6.35V4H21V10H15V8H17.75Q16.725 6.6 15.225 5.8T12 5Q9.075 5 7.0375 7.0375T5 12Q5 14.925 7.0375 16.9625T12 19Q14.625 19 16.5875 17.3T18.9 13H20.95Q20.575 16.425 18.0125 18.7125T12 21ZM14.8 16.2 11 12.4V7H13V11.6L16.2 14.8 14.8 16.2Z"), UNDO("M8 19q-.425 0-.712-.288T7 18t.288-.712T8 17h6.1q1.575 0 2.738-1T18 13.5T16.838 11T14.1 10H7.8l1.9 1.9q.275.275.275.7t-.275.7t-.7.275t-.7-.275L4.7 9.7q-.15-.15-.213-.325T4.426 9t.063-.375T4.7 8.3l3.6-3.6q.275-.275.7-.275t.7.275t.275.7t-.275.7L7.8 8h6.3q2.425 0 4.163 1.575T20 13.5t-1.737 3.925T14.1 19z"), VISIBILITY("M12 16q1.875 0 3.1875-1.3125T16.5 11.5 15.1875 8.3125 12 7 8.8125 8.3125 7.5 11.5t1.3125 3.1875T12 16Zm0-1.8q-1.125 0-1.9125-.7875T9.3 11.5t.7875-1.9125T12 8.8q1.125 0 1.9125.7875T14.7 11.5q0 1.125-.7875 1.9125T12 14.2ZM12 19q-3.65 0-6.65-2.0375T1 11.5Q2.35 8.075 5.35 6.0375T12 4q3.65 0 6.65 2.0375T23 11.5q-1.35 3.425-4.35 5.4625T12 19Zm0-7.5ZM12 17q2.825 0 5.1875-1.4875T20.8 11.5q-1.25-2.525-3.6125-4.0125T12 6 6.8125 7.4875 3.2 11.5q1.25 2.525 3.6125 4.0125T12 17Z"), diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java index 03dd49b87..634d37e05 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java @@ -142,7 +142,7 @@ public class ComponentList extends Control { if (node.getProperties().containsKey("ComponentList.vgrow")) { VBox.setVgrow(cell, (Priority) node.getProperties().get("ComponentList.vgrow")); } - if (node.getProperties().containsKey("ComponentList.noPadding")) { + if (node instanceof LineButtonBase || node.getProperties().containsKey("ComponentList.noPadding")) { cell.getStyleClass().add("no-padding"); } return cell; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/LineButtonBase.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/LineButtonBase.java new file mode 100644 index 000000000..4cf4f1307 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/LineButtonBase.java @@ -0,0 +1,123 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.ui.construct; + +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.beans.property.StringPropertyBase; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; + +/// @author Glavo +public abstract class LineButtonBase extends StackPane { + + private static final Insets PADDING = new Insets(8, 8, 8, 16); + + protected final BorderPane root; + protected final RipplerContainer container; + + private final Label titleLabel; + + public LineButtonBase() { + this.root = new BorderPane(); + root.setPadding(PADDING); + root.setMinHeight(48); + + this.container = new RipplerContainer(root); + this.getChildren().setAll(container); + + this.titleLabel = new Label(); + root.setCenter(titleLabel); + BorderPane.setAlignment(titleLabel, Pos.CENTER_LEFT); + titleLabel.textProperty().bind(titleProperty()); + titleLabel.getStyleClass().add("title"); + } + + private final StringProperty title = new SimpleStringProperty(this, "title"); + + public StringProperty titleProperty() { + return title; + } + + public String getTitle() { + return titleProperty().get(); + } + + public void setTitle(String title) { + this.titleProperty().set(title); + } + + private StringProperty subtitle; + + public StringProperty subtitleProperty() { + if (subtitle == null) { + subtitle = new StringPropertyBase() { + private VBox left; + private Label subtitleLabel; + + @Override + public String getName() { + return "subtitle"; + } + + @Override + public Object getBean() { + return LineButtonBase.this; + } + + @Override + protected void invalidated() { + String subtitle = get(); + if (subtitle != null && !subtitle.isEmpty()) { + if (left == null) { + left = new VBox(); + left.setMouseTransparent(true); + left.setAlignment(Pos.CENTER_LEFT); + + subtitleLabel = new Label(); + subtitleLabel.setWrapText(true); + subtitleLabel.setMinHeight(Region.USE_PREF_SIZE); + subtitleLabel.getStyleClass().add("subtitle"); + } + subtitleLabel.setText(subtitle); + left.getChildren().setAll(titleLabel, subtitleLabel); + root.setCenter(left); + } else if (left != null) { + subtitleLabel.setText(null); + root.setCenter(titleLabel); + } + } + }; + } + + return subtitle; + } + + public String getSubtitle() { + return subtitleProperty().get(); + } + + public void setSubtitle(String subtitle) { + subtitleProperty().set(subtitle); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/LineNavigationButton.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/LineNavigationButton.java new file mode 100644 index 000000000..104b083e8 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/LineNavigationButton.java @@ -0,0 +1,112 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.ui.construct; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.ObjectPropertyBase; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.SVG; + +/// @author Glavo +public final class LineNavigationButton extends LineButtonBase { + private static final String DEFAULT_STYLE_CLASS = "line-navigation-button"; + + public LineNavigationButton() { + getStyleClass().add(DEFAULT_STYLE_CLASS); + + root.setMouseTransparent(true); + + HBox right = new HBox(); + root.setRight(right); + { + right.setAlignment(Pos.CENTER_RIGHT); + + Label valueLabel = new Label(); + valueLabel.getStyleClass().add("subtitle"); + valueLabel.textProperty().bind(messageProperty()); + + Node arrowIcon = SVG.ARROW_FORWARD.createIcon(24); + HBox.setMargin(arrowIcon, new Insets(0, 8, 0, 8)); + + disabledProperty().addListener((observable, oldValue, newValue) -> + arrowIcon.setOpacity(newValue ? 0.4 : 1.0)); + + right.getChildren().setAll(valueLabel, arrowIcon); + } + + FXUtils.onClicked(container, this::fire); + } + + public void fire() { + fireEvent(new ActionEvent()); + } + + private final ObjectProperty> onAction = new ObjectPropertyBase<>() { + + @Override + protected void invalidated() { + setEventHandler(ActionEvent.ACTION, get()); + } + + @Override + public Object getBean() { + return LineNavigationButton.this; + } + + @Override + public String getName() { + return "onAction"; + } + }; + + public ObjectProperty> onActionProperty() { + return onAction; + } + + public EventHandler getOnAction() { + return onActionProperty().get(); + } + + public void setOnAction(EventHandler value) { + onActionProperty().set(value); + } + + private final StringProperty message = new SimpleStringProperty(this, "message", ""); + + public StringProperty messageProperty() { + return message; + } + + public String getMessage() { + return messageProperty().get(); + } + + public void setMessage(String message) { + messageProperty().set(message); + } + +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/LineSelectButton.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/LineSelectButton.java new file mode 100644 index 000000000..3cbf6d1a1 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/LineSelectButton.java @@ -0,0 +1,231 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.ui.construct; + +import com.jfoenix.controls.JFXPopup; +import javafx.beans.InvalidationListener; +import javafx.beans.binding.Bindings; +import javafx.beans.property.*; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.css.PseudoClass; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import javafx.scene.input.ScrollEvent; +import javafx.scene.layout.*; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.util.javafx.MappedObservableList; + +import java.util.Collection; +import java.util.Objects; +import java.util.function.Function; + +import static org.jackhuang.hmcl.ui.FXUtils.determineOptimalPopupPosition; + +/// @author Glavo +public final class LineSelectButton extends LineButtonBase { + + private static final String DEFAULT_STYLE_CLASS = "line-select-button"; + private static final PseudoClass SELECTED_PSEUDO_CLASS = PseudoClass.getPseudoClass("selected"); + + private JFXPopup popup; + + public LineSelectButton() { + this.getStyleClass().add(DEFAULT_STYLE_CLASS); + + root.setMouseTransparent(true); + + HBox right = new HBox(); + root.setRight(right); + { + right.setAlignment(Pos.CENTER_RIGHT); + + Label valueLabel = new Label(); + valueLabel.getStyleClass().add("subtitle"); + + InvalidationListener updateValue = observable -> { + T value = getValue(); + if (value == null) + valueLabel.setText(""); + else { + Function converter = getConverter(); + valueLabel.setText(converter != null ? converter.apply(value) : value.toString()); + } + }; + converterProperty().addListener(updateValue); + valueProperty().addListener(updateValue); + + Node arrowIcon = SVG.UNFOLD_MORE.createIcon(24); + HBox.setMargin(arrowIcon, new Insets(0, 8, 0, 8)); + + right.getChildren().setAll(valueLabel, arrowIcon); + } + + container.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> { + if (event.getButton() == MouseButton.PRIMARY) { + if (popup == null) { + PopupMenu popupMenu = new PopupMenu(); + this.popup = new JFXPopup(popupMenu); + + container.addEventFilter(ScrollEvent.ANY, ignored -> popup.hide()); + + Bindings.bindContent(popupMenu.getContent(), MappedObservableList.create(itemsProperty(), item -> { + VBox vbox = new VBox(); + + var itemTitleLabel = new Label(); + itemTitleLabel.getStyleClass().add("title"); + itemTitleLabel.textProperty().bind(Bindings.createStringBinding(() -> { + if (item == null) + return ""; + + Function converter = getConverter(); + return converter != null ? converter.apply(item) : Objects.toString(item, ""); + }, converterProperty())); + + var itemSubtitleLabel = new Label(); + itemSubtitleLabel.getStyleClass().add("subtitle"); + itemSubtitleLabel.textProperty().bind(Bindings.createStringBinding(() -> { + Function descriptionConverter = getDescriptionConverter(); + return descriptionConverter != null ? descriptionConverter.apply(item) : ""; + }, descriptionConverterProperty())); + + FXUtils.onChangeAndOperate(itemSubtitleLabel.textProperty(), text -> { + if (text == null || text.isEmpty()) { + vbox.getChildren().setAll(itemTitleLabel); + } else { + vbox.getChildren().setAll(itemTitleLabel, itemSubtitleLabel); + } + }); + + var wrapper = new StackPane(vbox); + wrapper.setAlignment(Pos.CENTER_LEFT); + wrapper.getStyleClass().add("menu-container"); + wrapper.setMouseTransparent(true); + RipplerContainer ripplerContainer = new RipplerContainer(wrapper); + FXUtils.onClicked(ripplerContainer, () -> { + setValue(item); + popup.hide(); + }); + + FXUtils.onChangeAndOperate(valueProperty(), + value -> wrapper.pseudoClassStateChanged(SELECTED_PSEUDO_CLASS, Objects.equals(value, item))); + + return ripplerContainer; + })); + + popup.showingProperty().addListener((observable, oldValue, newValue) -> + container.getRippler().setRipplerDisabled(newValue)); + } + + if (popup.isShowing()) { + popup.hide(); + } else { + JFXPopup.PopupVPosition vPosition = determineOptimalPopupPosition(this, popup); + popup.show(this, vPosition, JFXPopup.PopupHPosition.RIGHT, + 0, + vPosition == JFXPopup.PopupVPosition.TOP ? this.getHeight() : -this.getHeight()); + } + + event.consume(); + } else if (event.getButton() == MouseButton.SECONDARY) { + if (popup != null) + popup.hide(); + + event.consume(); + } + }); + } + + private final ObjectProperty value = new SimpleObjectProperty<>(this, "value"); + + public ObjectProperty valueProperty() { + return value; + } + + public T getValue() { + return valueProperty().get(); + } + + public void setValue(T value) { + valueProperty().set(value); + } + + private final ObjectProperty> converter = new SimpleObjectProperty<>(this, "converter"); + + public ObjectProperty> converterProperty() { + return converter; + } + + public Function getConverter() { + return converterProperty().get(); + } + + public void setConverter(Function value) { + converterProperty().set(value); + } + + private ObjectProperty> descriptionConverter; + + public ObjectProperty> descriptionConverterProperty() { + if (descriptionConverter == null) { + descriptionConverter = new SimpleObjectProperty<>(this, "descriptionConverter"); + } + return descriptionConverter; + } + + public Function getDescriptionConverter() { + return descriptionConverterProperty().get(); + } + + public void setDescriptionConverter(Function value) { + descriptionConverterProperty().set(value); + } + + private final ListProperty items = new SimpleListProperty<>(this, "items", FXCollections.emptyObservableList()); + + public ListProperty itemsProperty() { + return items; + } + + public void setItems(ObservableList value) { + itemsProperty().set(value); + } + + public void setItems(Collection value) { + if (value instanceof ObservableList observableList) { + this.setItems(observableList); + } else { + this.setItems(FXCollections.observableArrayList(value)); + } + } + + @SafeVarargs + public final void setItems(T... values) { + this.setItems(FXCollections.observableArrayList(values)); + } + + public ObservableList getItems() { + return items.get(); + } + +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/LineToggleButton.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/LineToggleButton.java new file mode 100644 index 000000000..51bbc0ce1 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/LineToggleButton.java @@ -0,0 +1,57 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2021 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.ui.construct; + +import com.jfoenix.controls.JFXToggleButton; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.geometry.Pos; +import javafx.scene.layout.BorderPane; +import org.jackhuang.hmcl.ui.FXUtils; + +public final class LineToggleButton extends LineButtonBase { + private static final String DEFAULT_STYLE_CLASS = "line-toggle-button"; + + public LineToggleButton() { + this.getStyleClass().add(DEFAULT_STYLE_CLASS); + + JFXToggleButton toggleButton = new JFXToggleButton(); + toggleButton.selectedProperty().bindBidirectional(selectedProperty()); + toggleButton.setSize(8); + FXUtils.setLimitHeight(toggleButton, 30); + + BorderPane.setAlignment(toggleButton, Pos.CENTER); + root.setRight(toggleButton); + + FXUtils.onClicked(container, toggleButton::fire); + } + + private final BooleanProperty selected = new SimpleBooleanProperty(this, "selected"); + + public BooleanProperty selectedProperty() { + return selected; + } + + public boolean isSelected() { + return selectedProperty().get(); + } + + public void setSelected(boolean selected) { + selectedProperty().set(selected); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/OptionToggleButton.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/OptionToggleButton.java deleted file mode 100644 index af4684534..000000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/OptionToggleButton.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2021 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.ui.construct; - -import com.jfoenix.controls.JFXToggleButton; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; -import javafx.geometry.Insets; -import javafx.geometry.Pos; -import javafx.scene.control.Label; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.StackPane; -import javafx.scene.layout.VBox; -import org.jackhuang.hmcl.ui.FXUtils; -import org.jackhuang.hmcl.util.StringUtils; - -public class OptionToggleButton extends StackPane { - private final StringProperty title = new SimpleStringProperty(); - private final StringProperty subtitle = new SimpleStringProperty(); - private final BooleanProperty selected = new SimpleBooleanProperty(); - - public OptionToggleButton() { - getProperties().put("ComponentList.noPadding", true); - - BorderPane pane = new BorderPane(); - pane.setPadding(new Insets(8, 8, 8, 16)); - RipplerContainer container = new RipplerContainer(pane); - getChildren().setAll(container); - - VBox left = new VBox(); - left.setMouseTransparent(true); - Label titleLabel = new Label(); - titleLabel.textProperty().bind(title); - Label subtitleLabel = new Label(); - subtitleLabel.setWrapText(true); - subtitleLabel.setMouseTransparent(true); - subtitleLabel.getStyleClass().add("subtitle"); - subtitleLabel.textProperty().bind(subtitle); - pane.setCenter(left); - left.setAlignment(Pos.CENTER_LEFT); - - JFXToggleButton toggleButton = new JFXToggleButton(); - StackPane right = new StackPane(toggleButton); - right.setAlignment(Pos.CENTER); - pane.setRight(right); - toggleButton.selectedProperty().bindBidirectional(selected); - toggleButton.setSize(8); - FXUtils.setLimitHeight(toggleButton, 30); - - FXUtils.onClicked(container, () -> toggleButton.setSelected(!toggleButton.isSelected())); - - FXUtils.onChangeAndOperate(subtitleProperty(), subtitle -> { - if (StringUtils.isNotBlank(subtitle)) { - left.getChildren().setAll(titleLabel, subtitleLabel); - } else { - left.getChildren().setAll(titleLabel); - } - }); - } - - public String getTitle() { - return title.get(); - } - - public StringProperty titleProperty() { - return title; - } - - public void setTitle(String title) { - this.title.set(title); - } - - public String getSubtitle() { - return subtitle.get(); - } - - public StringProperty subtitleProperty() { - return subtitle; - } - - public void setSubtitle(String subtitle) { - this.subtitle.set(subtitle); - } - - public boolean isSelected() { - return selected.get(); - } - - public BooleanProperty selectedProperty() { - return selected; - } - - public void setSelected(boolean selected) { - this.selected.set(selected); - } -} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/RipplerContainer.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/RipplerContainer.java index 90016dc59..27b3bdf2e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/RipplerContainer.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/RipplerContainer.java @@ -158,6 +158,10 @@ public class RipplerContainer extends StackPane { updateChildren(); } + public JFXRippler getRippler() { + return buttonRippler; + } + public Node getContainer() { return container.get(); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/DownloadSettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/DownloadSettingsPage.java index 0e7d535d8..d1a154032 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/DownloadSettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/DownloadSettingsPage.java @@ -31,15 +31,15 @@ import org.jackhuang.hmcl.task.FetchTask; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.WeakListenerHolder; import org.jackhuang.hmcl.ui.construct.*; +import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.javafx.SafeStringConverter; import java.net.Proxy; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; import static org.jackhuang.hmcl.setting.ConfigHolder.config; -import static org.jackhuang.hmcl.ui.FXUtils.stringConverter; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.selectedItemPropertyFor; public class DownloadSettingsPage extends StackPane { @@ -55,51 +55,37 @@ public class DownloadSettingsPage extends StackPane { getChildren().setAll(scrollPane); { - VBox downloadSource = new VBox(8); + var downloadSource = new ComponentList(); downloadSource.getStyleClass().add("card-non-transparent"); { - VBox chooseWrapper = new VBox(); - chooseWrapper.setPadding(new Insets(8, 0, 8, 0)); - JFXCheckBox chkAutoChooseDownloadSource = new JFXCheckBox(i18n("settings.launcher.download_source.auto")); - chkAutoChooseDownloadSource.selectedProperty().bindBidirectional(config().autoChooseDownloadTypeProperty()); - chooseWrapper.getChildren().setAll(chkAutoChooseDownloadSource); + var autoChooseDownloadSource = new LineToggleButton(); + autoChooseDownloadSource.setTitle(i18n("settings.launcher.download_source.auto")); + autoChooseDownloadSource.selectedProperty().bindBidirectional(config().autoChooseDownloadTypeProperty()); - BorderPane versionListSourcePane = new BorderPane(); - versionListSourcePane.setPadding(new Insets(0, 0, 8, 30)); - versionListSourcePane.disableProperty().bind(chkAutoChooseDownloadSource.selectedProperty().not()); - { - Label label = new Label(i18n("settings.launcher.version_list_source")); - BorderPane.setAlignment(label, Pos.CENTER_LEFT); - versionListSourcePane.setLeft(label); + Function converter = key -> i18n("download.provider." + key); + Function descriptionConverter = key -> { + String bundleKey = "download.provider." + key + ".desc"; + return I18n.hasKey(bundleKey) ? i18n(bundleKey) : null; + }; - JFXComboBox cboVersionListSource = new JFXComboBox<>(); - cboVersionListSource.setConverter(stringConverter(key -> i18n("download.provider." + key))); - versionListSourcePane.setRight(cboVersionListSource); - FXUtils.setLimitWidth(cboVersionListSource, 400); + var versionListSourcePane = new LineSelectButton(); + versionListSourcePane.disableProperty().bind(autoChooseDownloadSource.selectedProperty().not()); + versionListSourcePane.setTitle(i18n("settings.launcher.version_list_source")); + versionListSourcePane.setConverter(converter); + versionListSourcePane.setDescriptionConverter(descriptionConverter); + versionListSourcePane.setItems(DownloadProviders.AUTO_PROVIDERS.keySet()); + versionListSourcePane.valueProperty().bindBidirectional(config().versionListSourceProperty()); - cboVersionListSource.getItems().setAll(DownloadProviders.AUTO_PROVIDERS.keySet()); - selectedItemPropertyFor(cboVersionListSource).bindBidirectional(config().versionListSourceProperty()); - } + var downloadSourcePane = new LineSelectButton(); + downloadSourcePane.disableProperty().bind(autoChooseDownloadSource.selectedProperty()); + downloadSourcePane.setTitle(i18n("settings.launcher.download_source")); + downloadSourcePane.setConverter(converter); + downloadSourcePane.setDescriptionConverter(descriptionConverter); + downloadSourcePane.setItems(DownloadProviders.DIRECT_PROVIDERS.keySet()); + downloadSourcePane.valueProperty().bindBidirectional(config().downloadTypeProperty()); - BorderPane downloadSourcePane = new BorderPane(); - downloadSourcePane.setPadding(new Insets(0, 0, 8, 30)); - downloadSourcePane.disableProperty().bind(chkAutoChooseDownloadSource.selectedProperty()); - { - Label label = new Label(i18n("settings.launcher.download_source")); - BorderPane.setAlignment(label, Pos.CENTER_LEFT); - downloadSourcePane.setLeft(label); - - JFXComboBox cboDownloadSource = new JFXComboBox<>(); - cboDownloadSource.setConverter(stringConverter(key -> i18n("download.provider." + key))); - downloadSourcePane.setRight(cboDownloadSource); - FXUtils.setLimitWidth(cboDownloadSource, 420); - - cboDownloadSource.getItems().setAll(DownloadProviders.DIRECT_PROVIDERS.keySet()); - selectedItemPropertyFor(cboDownloadSource).bindBidirectional(config().downloadTypeProperty()); - } - - downloadSource.getChildren().setAll(chooseWrapper, versionListSourcePane, downloadSourcePane); + downloadSource.getContent().setAll(autoChooseDownloadSource, versionListSourcePane, downloadSourcePane); } content.getChildren().addAll(ComponentList.createComponentListTitle(i18n("settings.launcher.download_source")), downloadSource); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/PersonalizationPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/PersonalizationPage.java index d0f1e3bc8..b782451fc 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/PersonalizationPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/PersonalizationPage.java @@ -25,7 +25,6 @@ import javafx.beans.binding.StringBinding; import javafx.beans.binding.When; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; -import javafx.collections.FXCollections; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.ColorPicker; @@ -78,19 +77,13 @@ public class PersonalizationPage extends StackPane { ComponentList themeList = new ComponentList(); { - BorderPane brightnessPane = new BorderPane(); + var brightnessPane = new LineSelectButton(); + brightnessPane.setTitle(i18n("settings.launcher.brightness")); + brightnessPane.setConverter(name -> i18n("settings.launcher.brightness." + name)); + brightnessPane.setItems("auto", "light", "dark"); + brightnessPane.valueProperty().bindBidirectional(config().themeBrightnessProperty()); + themeList.getContent().add(brightnessPane); - - Label left = new Label(i18n("settings.launcher.brightness")); - BorderPane.setAlignment(left, Pos.CENTER_LEFT); - - brightnessPane.setLeft(left); - - JFXComboBox cboBrightness = new JFXComboBox<>(); - cboBrightness.getItems().setAll("auto", "light", "dark"); - cboBrightness.setConverter(FXUtils.stringConverter(name -> i18n("settings.launcher.brightness." + name))); - cboBrightness.valueProperty().bindBidirectional(config().themeBrightnessProperty()); - brightnessPane.setRight(cboBrightness); } { @@ -112,13 +105,13 @@ public class PersonalizationPage extends StackPane { Platform.runLater(() -> JFXDepthManager.setDepth(picker, 0)); } { - OptionToggleButton titleTransparentButton = new OptionToggleButton(); + LineToggleButton titleTransparentButton = new LineToggleButton(); themeList.getContent().add(titleTransparentButton); titleTransparentButton.selectedProperty().bindBidirectional(config().titleTransparentProperty()); titleTransparentButton.setTitle(i18n("settings.launcher.title_transparent")); } { - OptionToggleButton animationButton = new OptionToggleButton(); + LineToggleButton animationButton = new LineToggleButton(); themeList.getContent().add(animationButton); animationButton.selectedProperty().bindBidirectional(config().animationDisabledProperty()); animationButton.setTitle(i18n("settings.launcher.turn_off_animations")); @@ -304,45 +297,32 @@ public class PersonalizationPage extends StackPane { } { - BorderPane fontAntiAliasingPane = new BorderPane(); - { - VBox left = new VBox(); - Label title = new Label(i18n("settings.launcher.font.anti_aliasing")); - title.getStyleClass().add("title"); - Label subtitle = new Label(i18n("settings.take_effect_after_restart")); - subtitle.getStyleClass().add("subtitle"); - left.getChildren().setAll(title, subtitle); - fontAntiAliasingPane.setLeft(left); + var fontAntiAliasingPane = new LineSelectButton>(); + fontAntiAliasingPane.setTitle(i18n("settings.launcher.font.anti_aliasing")); + fontAntiAliasingPane.setSubtitle(i18n("settings.take_effect_after_restart")); + fontAntiAliasingPane.setConverter(value -> + value.isPresent() + ? i18n("settings.launcher.font.anti_aliasing." + value.get().name().toLowerCase(Locale.ROOT)) + : i18n("settings.launcher.font.anti_aliasing.auto") + ); + fontAntiAliasingPane.setItems( + Optional.empty(), + Optional.of(FontSmoothingType.LCD), + Optional.of(FontSmoothingType.GRAY) + ); + + String fontAntiAliasing = globalConfig().getFontAntiAliasing(); + if ("lcd".equalsIgnoreCase(fontAntiAliasing)) { + fontAntiAliasingPane.setValue(Optional.of(FontSmoothingType.LCD)); + } else if ("gray".equalsIgnoreCase(fontAntiAliasing)) { + fontAntiAliasingPane.setValue(Optional.of(FontSmoothingType.GRAY)); + } else { + fontAntiAliasingPane.setValue(Optional.empty()); } - { - @SuppressWarnings("unchecked") - JFXComboBox> cboAntiAliasing = new JFXComboBox<>(FXCollections.observableArrayList( - Optional.empty(), - Optional.of(FontSmoothingType.LCD), - Optional.of(FontSmoothingType.GRAY) - )); - String fontAntiAliasing = globalConfig().getFontAntiAliasing(); - if ("lcd".equalsIgnoreCase(fontAntiAliasing)) { - cboAntiAliasing.setValue(Optional.of(FontSmoothingType.LCD)); - } else if ("gray".equalsIgnoreCase(fontAntiAliasing)) { - cboAntiAliasing.setValue(Optional.of(FontSmoothingType.GRAY)); - } else { - cboAntiAliasing.setValue(Optional.empty()); - } - cboAntiAliasing.setConverter(FXUtils.stringConverter(value -> { - if (value.isPresent()) { - return i18n("settings.launcher.font.anti_aliasing." + value.get().name().toLowerCase(Locale.ROOT)); - } else { - return i18n("settings.launcher.font.anti_aliasing.auto"); - } - })); - FXUtils.onChange(cboAntiAliasing.valueProperty(), value -> - globalConfig().setFontAntiAliasing(value.map(it -> it.name().toLowerCase(Locale.ROOT)) - .orElse(null))); - - fontAntiAliasingPane.setRight(cboAntiAliasing); - } + FXUtils.onChange(fontAntiAliasingPane.valueProperty(), value -> + globalConfig().setFontAntiAliasing(value.map(it -> it.name().toLowerCase(Locale.ROOT)) + .orElse(null))); fontPane.getContent().add(fontAntiAliasingPane); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java index 1ad646e66..b01031fd3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java @@ -18,7 +18,6 @@ package org.jackhuang.hmcl.ui.main; import com.jfoenix.controls.JFXButton; -import com.jfoenix.controls.JFXComboBox; import com.jfoenix.controls.JFXRadioButton; import javafx.application.Platform; import javafx.beans.InvalidationListener; @@ -40,11 +39,8 @@ import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; -import org.jackhuang.hmcl.ui.construct.ComponentList; -import org.jackhuang.hmcl.ui.construct.ComponentSublist; +import org.jackhuang.hmcl.ui.construct.*; import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType; -import org.jackhuang.hmcl.ui.construct.MultiFileItem; -import org.jackhuang.hmcl.ui.construct.OptionToggleButton; import org.jackhuang.hmcl.upgrade.RemoteVersion; import org.jackhuang.hmcl.upgrade.UpdateChannel; import org.jackhuang.hmcl.upgrade.UpdateChecker; @@ -69,7 +65,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import static org.jackhuang.hmcl.setting.ConfigHolder.config; -import static org.jackhuang.hmcl.ui.FXUtils.stringConverter; import static org.jackhuang.hmcl.util.Lang.thread; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.selectedItemPropertyFor; @@ -206,7 +201,7 @@ public final class SettingsPage extends ScrollPane { } { - OptionToggleButton previewPane = new OptionToggleButton(); + LineToggleButton previewPane = new LineToggleButton(); previewPane.setTitle(i18n("update.preview")); previewPane.setSubtitle(i18n("update.preview.subtitle")); previewPane.selectedProperty().bindBidirectional(config().acceptPreviewUpdateProperty()); @@ -250,37 +245,27 @@ public final class SettingsPage extends ScrollPane { } { - BorderPane languagePane = new BorderPane(); - - VBox left = new VBox(); - Label title = new Label(i18n("settings.launcher.language")); - title.getStyleClass().add("title"); - Label subtitle = new Label(i18n("settings.take_effect_after_restart")); - subtitle.getStyleClass().add("subtitle"); - left.getChildren().setAll(title, subtitle); - languagePane.setLeft(left); + var chooseLanguagePane = new LineSelectButton(); + chooseLanguagePane.setTitle(i18n("settings.launcher.language")); + chooseLanguagePane.setSubtitle(i18n("settings.take_effect_after_restart")); SupportedLocale currentLocale = I18n.getLocale(); - JFXComboBox cboLanguage = new JFXComboBox<>(); - cboLanguage.setConverter(stringConverter(locale -> { + chooseLanguagePane.setConverter(locale -> { if (locale.isDefault()) return locale.getDisplayName(currentLocale); else if (locale.isSameLanguage(currentLocale)) return locale.getDisplayName(locale); else return locale.getDisplayName(currentLocale) + " - " + locale.getDisplayName(locale); - })); - cboLanguage.getItems().setAll(SupportedLocale.getSupportedLocales()); - selectedItemPropertyFor(cboLanguage).bindBidirectional(config().localizationProperty()); + }); + chooseLanguagePane.setItems(SupportedLocale.getSupportedLocales()); + chooseLanguagePane.valueProperty().bindBidirectional(config().localizationProperty()); - FXUtils.setLimitWidth(cboLanguage, 300); - languagePane.setRight(cboLanguage); - - settingsPane.getContent().add(languagePane); + settingsPane.getContent().add(chooseLanguagePane); } { - OptionToggleButton disableAutoGameOptionsPane = new OptionToggleButton(); + LineToggleButton disableAutoGameOptionsPane = new LineToggleButton(); disableAutoGameOptionsPane.setTitle(i18n("settings.launcher.disable_auto_game_options")); disableAutoGameOptionsPane.selectedProperty().bindBidirectional(config().disableAutoGameOptionsProperty()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfilePage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfilePage.java index 899fe7b70..2070664e0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfilePage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/profile/ProfilePage.java @@ -37,7 +37,7 @@ import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.construct.ComponentList; import org.jackhuang.hmcl.ui.construct.FileItem; -import org.jackhuang.hmcl.ui.construct.OptionToggleButton; +import org.jackhuang.hmcl.ui.construct.LineToggleButton; import org.jackhuang.hmcl.ui.construct.PageCloseEvent; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.util.StringUtils; @@ -55,7 +55,7 @@ public final class ProfilePage extends BorderPane implements DecoratorPage { private final Profile profile; private final JFXTextField txtProfileName; private final FileItem gameDir; - private final OptionToggleButton toggleUseRelativePath; + private final LineToggleButton toggleUseRelativePath; /** * @param profile null if creating a new profile. @@ -113,7 +113,7 @@ public final class ProfilePage extends BorderPane implements DecoratorPage { gameDir.setTitle(i18n("profile.instance_directory.choose")); gameDir.pathProperty().bindBidirectional(location); - toggleUseRelativePath = new OptionToggleButton(); + toggleUseRelativePath = new LineToggleButton(); toggleUseRelativePath.setTitle(i18n("profile.use_relative_path")); gameDir.convertToRelativePathProperty().bind(toggleUseRelativePath.selectedProperty()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AdvancedVersionSettingPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AdvancedVersionSettingPage.java index 478c05558..4322cc7e7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AdvancedVersionSettingPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AdvancedVersionSettingPage.java @@ -1,12 +1,10 @@ package org.jackhuang.hmcl.ui.versions; -import com.jfoenix.controls.JFXComboBox; import com.jfoenix.controls.JFXTextField; import javafx.beans.binding.Bindings; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.SimpleObjectProperty; -import javafx.geometry.Pos; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.layout.*; @@ -26,7 +24,6 @@ import java.nio.file.FileSystems; import java.util.Arrays; import java.util.Locale; -import static org.jackhuang.hmcl.ui.FXUtils.stringConverter; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public final class AdvancedVersionSettingPage extends StackPane implements DecoratorPage { @@ -44,17 +41,17 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor private final JFXTextField txtWrapper; private final JFXTextField txtPreLaunchCommand; private final JFXTextField txtPostExitCommand; - private final OptionToggleButton noJVMArgsPane; - private final OptionToggleButton noOptimizingJVMArgsPane; - private final OptionToggleButton noGameCheckPane; - private final OptionToggleButton noJVMCheckPane; - private final OptionToggleButton noNativesPatchPane; - private final OptionToggleButton useNativeGLFWPane; - private final OptionToggleButton useNativeOpenALPane; + private final LineToggleButton noJVMArgsPane; + private final LineToggleButton noOptimizingJVMArgsPane; + private final LineToggleButton noGameCheckPane; + private final LineToggleButton noJVMCheckPane; + private final LineToggleButton noNativesPatchPane; + private final LineToggleButton useNativeGLFWPane; + private final LineToggleButton useNativeOpenALPane; private final ComponentSublist nativesDirSublist; private final MultiFileItem nativesDirItem; private final MultiFileItem.FileOption nativesDirCustomOption; - private final JFXComboBox cboRenderer; + private final LineSelectButton rendererPane; public AdvancedVersionSettingPage(Profile profile, @Nullable String versionId, VersionSetting versionSetting) { this.profile = profile; @@ -170,40 +167,31 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor nativesDirHint.setText(i18n("settings.advanced.natives_directory.hint")); nativesDirItem.getChildren().add(nativesDirHint); - BorderPane rendererPane = new BorderPane(); - { - Label label = new Label(i18n("settings.advanced.renderer")); - rendererPane.setLeft(label); - BorderPane.setAlignment(label, Pos.CENTER_LEFT); + rendererPane = new LineSelectButton<>(); + rendererPane.setTitle(i18n("settings.advanced.renderer")); + rendererPane.setConverter(e -> i18n("settings.advanced.renderer." + e.name().toLowerCase(Locale.ROOT))); + rendererPane.setItems(Renderer.values()); - cboRenderer = new JFXComboBox<>(); - cboRenderer.getItems().setAll(Renderer.values()); - cboRenderer.setConverter(stringConverter(e -> i18n("settings.advanced.renderer." + e.name().toLowerCase(Locale.ROOT)))); - rendererPane.setRight(cboRenderer); - BorderPane.setAlignment(cboRenderer, Pos.CENTER_RIGHT); - FXUtils.setLimitWidth(cboRenderer, 300); - } - - noJVMArgsPane = new OptionToggleButton(); + noJVMArgsPane = new LineToggleButton(); noJVMArgsPane.setTitle(i18n("settings.advanced.no_jvm_args")); - noOptimizingJVMArgsPane = new OptionToggleButton(); + noOptimizingJVMArgsPane = new LineToggleButton(); noOptimizingJVMArgsPane.setTitle(i18n("settings.advanced.no_optimizing_jvm_args")); noOptimizingJVMArgsPane.disableProperty().bind(noJVMArgsPane.selectedProperty()); - noGameCheckPane = new OptionToggleButton(); + noGameCheckPane = new LineToggleButton(); noGameCheckPane.setTitle(i18n("settings.advanced.dont_check_game_completeness")); - noJVMCheckPane = new OptionToggleButton(); + noJVMCheckPane = new LineToggleButton(); noJVMCheckPane.setTitle(i18n("settings.advanced.dont_check_jvm_validity")); - noNativesPatchPane = new OptionToggleButton(); + noNativesPatchPane = new LineToggleButton(); noNativesPatchPane.setTitle(i18n("settings.advanced.dont_patch_natives")); - useNativeGLFWPane = new OptionToggleButton(); + useNativeGLFWPane = new LineToggleButton(); useNativeGLFWPane.setTitle(i18n("settings.advanced.use_native_glfw")); - useNativeOpenALPane = new OptionToggleButton(); + useNativeOpenALPane = new LineToggleButton(); useNativeOpenALPane.setTitle(i18n("settings.advanced.use_native_openal")); workaroundPane.getContent().setAll( @@ -239,7 +227,7 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor FXUtils.bindString(txtWrapper, versionSetting.wrapperProperty()); FXUtils.bindString(txtPreLaunchCommand, versionSetting.preLaunchCommandProperty()); FXUtils.bindString(txtPostExitCommand, versionSetting.postExitCommandProperty()); - FXUtils.bindEnum(cboRenderer, versionSetting.rendererProperty()); + rendererPane.valueProperty().bindBidirectional(versionSetting.rendererProperty()); noGameCheckPane.selectedProperty().bindBidirectional(versionSetting.notCheckGameProperty()); noJVMCheckPane.selectedProperty().bindBidirectional(versionSetting.notCheckJVMProperty()); noJVMArgsPane.selectedProperty().bindBidirectional(versionSetting.noJVMArgsProperty()); @@ -281,7 +269,7 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor FXUtils.unbind(txtWrapper, versionSetting.wrapperProperty()); FXUtils.unbind(txtPreLaunchCommand, versionSetting.preLaunchCommandProperty()); FXUtils.unbind(txtPostExitCommand, versionSetting.postExitCommandProperty()); - FXUtils.unbindEnum(cboRenderer, versionSetting.rendererProperty()); + rendererPane.valueProperty().unbindBidirectional(versionSetting.rendererProperty()); noGameCheckPane.selectedProperty().unbindBidirectional(versionSetting.notCheckGameProperty()); noJVMCheckPane.selectedProperty().unbindBidirectional(versionSetting.notCheckJVMProperty()); noJVMArgsPane.selectedProperty().unbindBidirectional(versionSetting.noJVMArgsProperty()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionSettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionSettingsPage.java index f706c2cfc..99439b021 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionSettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionSettingsPage.java @@ -43,6 +43,7 @@ import org.jackhuang.hmcl.ui.WeakListenerHolder; import org.jackhuang.hmcl.ui.construct.*; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.util.*; +import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.javafx.BindingMapping; import org.jackhuang.hmcl.util.javafx.PropertyUtils; import org.jackhuang.hmcl.util.javafx.SafeStringConverter; @@ -57,7 +58,6 @@ import java.lang.ref.WeakReference; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; -import static org.jackhuang.hmcl.ui.FXUtils.stringConverter; import static org.jackhuang.hmcl.util.DataSizeUnit.GIGABYTES; import static org.jackhuang.hmcl.util.DataSizeUnit.MEGABYTES; import static org.jackhuang.hmcl.util.Pair.pair; @@ -78,7 +78,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag private final JFXComboBox cboWindowsSize; private final JFXTextField txtServerIP; private final ComponentList componentList; - private final JFXComboBox cboLauncherVisibility; + private final LineSelectButton launcherVisibilityPane; private final JFXCheckBox chkAutoAllocate; private final JFXCheckBox chkFullscreen; private final ComponentSublist javaSublist; @@ -90,9 +90,9 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag private final ComponentSublist gameDirSublist; private final MultiFileItem gameDirItem; private final MultiFileItem.FileOption gameDirCustomOption; - private final JFXComboBox cboProcessPriority; - private final OptionToggleButton showLogsPane; - private final OptionToggleButton enableDebugLogOutputPane; + private final LineSelectButton processPriorityPane; + private final LineToggleButton showLogsPane; + private final LineToggleButton enableDebugLogOutputPane; private final ImagePickerItem iconPickerItem; private final ChangeListener> javaListChangeListener; @@ -351,17 +351,10 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag maxMemoryPane.getChildren().setAll(title, chkAutoAllocate, lowerBoundPane, progressBarPane, digitalPane); } - BorderPane launcherVisibilityPane = new BorderPane(); - { - Label label = new Label(i18n("settings.advanced.launcher_visible")); - launcherVisibilityPane.setLeft(label); - BorderPane.setAlignment(label, Pos.CENTER_LEFT); - - cboLauncherVisibility = new JFXComboBox<>(); - launcherVisibilityPane.setRight(cboLauncherVisibility); - BorderPane.setAlignment(cboLauncherVisibility, Pos.CENTER_RIGHT); - FXUtils.setLimitWidth(cboLauncherVisibility, 300); - } + launcherVisibilityPane = new LineSelectButton<>(); + launcherVisibilityPane.setTitle(i18n("settings.advanced.launcher_visible")); + launcherVisibilityPane.setItems(LauncherVisibility.values()); + launcherVisibilityPane.setConverter(e -> i18n("settings.advanced.launcher_visibility." + e.name().toLowerCase(Locale.ROOT))); BorderPane dimensionPane = new BorderPane(); { @@ -391,23 +384,19 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag } } - showLogsPane = new OptionToggleButton(); + showLogsPane = new LineToggleButton(); showLogsPane.setTitle(i18n("settings.show_log")); - enableDebugLogOutputPane = new OptionToggleButton(); + enableDebugLogOutputPane = new LineToggleButton(); enableDebugLogOutputPane.setTitle(i18n("settings.enable_debug_log_output")); - - BorderPane processPriorityPane = new BorderPane(); - { - Label label = new Label(i18n("settings.advanced.process_priority")); - processPriorityPane.setLeft(label); - BorderPane.setAlignment(label, Pos.CENTER_LEFT); - - cboProcessPriority = new JFXComboBox<>(); - processPriorityPane.setRight(cboProcessPriority); - BorderPane.setAlignment(cboProcessPriority, Pos.CENTER_RIGHT); - FXUtils.setLimitWidth(cboProcessPriority, 300); - } + processPriorityPane = new LineSelectButton<>(); + processPriorityPane.setTitle(i18n("settings.advanced.process_priority")); + processPriorityPane.setConverter(e -> i18n("settings.advanced.process_priority." + e.name().toLowerCase(Locale.ROOT))); + processPriorityPane.setDescriptionConverter(e -> { + String bundleKey = "settings.advanced.process_priority." + e.name().toLowerCase(Locale.ROOT) + ".desc"; + return I18n.hasKey(bundleKey) ? i18n(bundleKey) : null; + }); + processPriorityPane.setItems(ProcessPriority.values()); GridPane serverPane = new GridPane(); { @@ -436,23 +425,16 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag serverPane.addRow(0, new Label(i18n("settings.advanced.server_ip")), txtServerIP); } - BorderPane showAdvancedSettingPane = new BorderPane(); - { - Label label = new Label(i18n("settings.advanced")); - showAdvancedSettingPane.setLeft(label); - BorderPane.setAlignment(label, Pos.CENTER_LEFT); + LineNavigationButton showAdvancedSettingPane = new LineNavigationButton(); + showAdvancedSettingPane.setTitle(i18n("settings.advanced")); + showAdvancedSettingPane.setOnAction(event -> { + if (lastVersionSetting != null) { + if (advancedVersionSettingPage == null) + advancedVersionSettingPage = new AdvancedVersionSettingPage(profile, versionId, lastVersionSetting); - JFXButton button = FXUtils.newBorderButton(i18n("settings.advanced.modify")); - button.setOnAction(e -> { - if (lastVersionSetting != null) { - if (advancedVersionSettingPage == null) - advancedVersionSettingPage = new AdvancedVersionSettingPage(profile, versionId, lastVersionSetting); - - Controllers.navigateForward(advancedVersionSettingPage); - } - }); - showAdvancedSettingPane.setRight(button); - } + Controllers.navigateForward(advancedVersionSettingPage); + } + }); componentList.getContent().addAll( javaSublist, @@ -487,12 +469,6 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onDecoratorPageNavigating); - cboLauncherVisibility.getItems().setAll(LauncherVisibility.values()); - cboLauncherVisibility.setConverter(stringConverter(e -> i18n("settings.advanced.launcher_visibility." + e.name().toLowerCase(Locale.ROOT)))); - - cboProcessPriority.getItems().setAll(ProcessPriority.values()); - cboProcessPriority.setConverter(stringConverter(e -> i18n("settings.advanced.process_priority." + e.name().toLowerCase(Locale.ROOT)))); - componentList.disableProperty().bind(enableSpecificSettings.not()); } @@ -537,8 +513,8 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag chkFullscreen.selectedProperty().unbindBidirectional(lastVersionSetting.fullscreenProperty()); showLogsPane.selectedProperty().unbindBidirectional(lastVersionSetting.showLogsProperty()); enableDebugLogOutputPane.selectedProperty().unbindBidirectional(lastVersionSetting.enableDebugLogOutputProperty()); - FXUtils.unbindEnum(cboLauncherVisibility, lastVersionSetting.launcherVisibilityProperty()); - FXUtils.unbindEnum(cboProcessPriority, lastVersionSetting.processPriorityProperty()); + launcherVisibilityPane.valueProperty().unbindBidirectional(lastVersionSetting.launcherVisibilityProperty()); + processPriorityPane.valueProperty().unbindBidirectional(lastVersionSetting.processPriorityProperty()); lastVersionSetting.usesGlobalProperty().removeListener(usesGlobalListener); lastVersionSetting.javaVersionTypeProperty().removeListener(javaListener); @@ -572,8 +548,8 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag chkFullscreen.selectedProperty().bindBidirectional(versionSetting.fullscreenProperty()); showLogsPane.selectedProperty().bindBidirectional(versionSetting.showLogsProperty()); enableDebugLogOutputPane.selectedProperty().bindBidirectional(versionSetting.enableDebugLogOutputProperty()); - FXUtils.bindEnum(cboLauncherVisibility, versionSetting.launcherVisibilityProperty()); - FXUtils.bindEnum(cboProcessPriority, versionSetting.processPriorityProperty()); + launcherVisibilityPane.valueProperty().bindBidirectional(versionSetting.launcherVisibilityProperty()); + processPriorityPane.valueProperty().bindBidirectional(versionSetting.processPriorityProperty()); if (versionId != null) enableSpecificSettings.set(!versionSetting.isUsesGlobal()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldInfoPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldInfoPage.java index 30c06ee7f..965450da4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldInfoPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldInfoPage.java @@ -19,7 +19,6 @@ package org.jackhuang.hmcl.ui.versions; import com.github.steveice10.opennbt.tag.builtin.*; import com.jfoenix.controls.JFXButton; -import com.jfoenix.controls.JFXComboBox; import com.jfoenix.controls.JFXTextField; import javafx.beans.property.SimpleBooleanProperty; import javafx.collections.FXCollections; @@ -243,7 +242,7 @@ public final class WorldInfoPage extends SpinnerPane { }); } - OptionToggleButton allowCheatsButton = new OptionToggleButton(); + LineToggleButton allowCheatsButton = new LineToggleButton(); { allowCheatsButton.setTitle(i18n("world.info.allow_cheats")); allowCheatsButton.setDisable(worldManagePage.isReadOnly()); @@ -252,7 +251,7 @@ public final class WorldInfoPage extends SpinnerPane { checkTagAndSetListener(tag, allowCheatsButton); } - OptionToggleButton generateFeaturesButton = new OptionToggleButton(); + LineToggleButton generateFeaturesButton = new LineToggleButton(); { generateFeaturesButton.setTitle(i18n("world.info.generate_features")); generateFeaturesButton.setDisable(worldManagePage.isReadOnly()); @@ -261,35 +260,32 @@ public final class WorldInfoPage extends SpinnerPane { checkTagAndSetListener(tag, generateFeaturesButton); } - BorderPane difficultyPane = new BorderPane(); + LineSelectButton difficultyButton = new LineSelectButton<>(); { - setLeftLabel(difficultyPane, "world.info.difficulty"); - - JFXComboBox difficultyBox = new JFXComboBox<>(Difficulty.items); - difficultyBox.setDisable(worldManagePage.isReadOnly()); - BorderPane.setAlignment(difficultyBox, Pos.CENTER_RIGHT); - difficultyPane.setRight(difficultyBox); + difficultyButton.setTitle(i18n("world.info.difficulty")); + difficultyButton.setDisable(worldManagePage.isReadOnly()); + difficultyButton.setItems(Difficulty.items); Tag tag = dataTag.get("Difficulty"); if (tag instanceof ByteTag byteTag) { Difficulty difficulty = Difficulty.of(byteTag.getValue()); if (difficulty != null) { - difficultyBox.setValue(difficulty); - difficultyBox.valueProperty().addListener((o, oldValue, newValue) -> { + difficultyButton.setValue(difficulty); + difficultyButton.valueProperty().addListener((o, oldValue, newValue) -> { if (newValue != null) { byteTag.setValue((byte) newValue.ordinal()); saveLevelDat(); } }); } else { - difficultyBox.setDisable(true); + difficultyButton.setDisable(true); } } else { - difficultyBox.setDisable(true); + difficultyButton.setDisable(true); } } - OptionToggleButton difficultyLockPane = new OptionToggleButton(); + LineToggleButton difficultyLockPane = new LineToggleButton(); { difficultyLockPane.setTitle(i18n("world.info.difficulty_lock")); difficultyLockPane.setDisable(worldManagePage.isReadOnly()); @@ -300,7 +296,7 @@ public final class WorldInfoPage extends SpinnerPane { basicInfo.getContent().setAll( worldNamePane, gameVersionPane, iconPane, seedPane, lastPlayedPane, timePane, - allowCheatsButton, generateFeaturesButton, difficultyPane, difficultyLockPane); + allowCheatsButton, generateFeaturesButton, difficultyButton, difficultyLockPane); rootPane.getChildren().addAll(ComponentList.createComponentListTitle(i18n("world.info.basic")), basicInfo); } @@ -373,14 +369,11 @@ public final class WorldInfoPage extends SpinnerPane { }); } - BorderPane playerGameTypePane = new BorderPane(); + LineSelectButton playerGameTypeButton = new LineSelectButton<>(); { - setLeftLabel(playerGameTypePane, "world.info.player.game_type"); - - JFXComboBox gameTypeBox = new JFXComboBox<>(GameType.items); - gameTypeBox.setDisable(worldManagePage.isReadOnly()); - BorderPane.setAlignment(gameTypeBox, Pos.CENTER_RIGHT); - playerGameTypePane.setRight(gameTypeBox); + playerGameTypeButton.setTitle(i18n("world.info.player.game_type")); + playerGameTypeButton.setDisable(worldManagePage.isReadOnly()); + playerGameTypeButton.setItems(GameType.items); Tag tag = player.get("playerGameType"); Tag hardcoreTag = dataTag.get("hardcore"); @@ -389,8 +382,8 @@ public final class WorldInfoPage extends SpinnerPane { if (tag instanceof IntTag intTag) { GameType gameType = GameType.of(intTag.getValue(), isHardcore); if (gameType != null) { - gameTypeBox.setValue(gameType); - gameTypeBox.valueProperty().addListener((o, oldValue, newValue) -> { + playerGameTypeButton.setValue(gameType); + playerGameTypeButton.valueProperty().addListener((o, oldValue, newValue) -> { if (newValue != null) { if (newValue == GameType.HARDCORE) { intTag.setValue(0); // survival (hardcore worlds are survival+hardcore flag) @@ -407,10 +400,10 @@ public final class WorldInfoPage extends SpinnerPane { } }); } else { - gameTypeBox.setDisable(true); + playerGameTypeButton.setDisable(true); } } else { - gameTypeBox.setDisable(true); + playerGameTypeButton.setDisable(true); } } @@ -458,7 +451,7 @@ public final class WorldInfoPage extends SpinnerPane { playerInfo.getContent().setAll( locationPane, lastDeathLocationPane, spawnPane, - playerGameTypePane, healthPane, foodLevelPane, xpLevelPane + playerGameTypeButton, healthPane, foodLevelPane, xpLevelPane ); rootPane.getChildren().addAll(ComponentList.createComponentListTitle(i18n("world.info.player")), playerInfo); @@ -489,7 +482,7 @@ public final class WorldInfoPage extends SpinnerPane { borderPane.setRight(label); } - private void checkTagAndSetListener(Tag tag, OptionToggleButton toggleButton) { + private void checkTagAndSetListener(Tag tag, LineToggleButton toggleButton) { if (tag instanceof ByteTag byteTag) { byte value = byteTag.getValue(); if (value == 0 || value == 1) { diff --git a/HMCL/src/main/resources/assets/css/root.css b/HMCL/src/main/resources/assets/css/root.css index eb10eae33..f5bbc4bea 100644 --- a/HMCL/src/main/resources/assets/css/root.css +++ b/HMCL/src/main/resources/assets/css/root.css @@ -1857,3 +1857,35 @@ .tooltip .text { -fx-fill: -monet-inverse-on-surface; } + +/******************************************************************************* + * * + * Line Button * + * * + ******************************************************************************/ + + +.line-select-button .svg { + -fx-opacity: 1; +} + +.line-select-button .svg:disabled { + -fx-opacity: 0.4; +} + +.line-select-button .menu-container .title { + -fx-font-size: 13px; + -fx-text-fill: -monet-on-surface; +} + +.line-select-button .menu-container:selected .title { + -fx-text-fill: -monet-primary; +} + +.line-select-button .menu-container .subtitle { + -fx-text-fill: -monet-on-surface-variant; +} + +.line-select-button .menu-container:selected .subtitle { + -fx-text-fill: -monet-primary; +} \ No newline at end of file diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index ebb0b29f0..db76bf372 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -357,11 +357,16 @@ download.failed.empty=No versions are available. Please click here to go back. download.failed.no_code=Failed to download download.failed.refresh=Failed to fetch version list. Please click here to retry. download.game=New Game -download.provider.bmclapi=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/) -download.provider.mojang=Official (OptiFine is provided by BMCLAPI) +download.provider.bmclapi=BMCLAPI +download.provider.bmclapi.desc=bangbang93, https://bmclapi2.bangbang93.com/ +download.provider.mojang=Official +download.provider.mojang.desc=OptiFine is provided by BMCLAPI download.provider.official=From Official Sources +download.provider.official.desc=Latest, but may load slowly download.provider.balanced=From Fastest Available +download.provider.balanced.desc=Balanced, but may not be the latest download.provider.mirror=From Mirror +download.provider.mirror.desc=Fast, but may not be the latest download.java=Downloading Java download.java.override=This Java version already exists. Do you want to uninstall and reinstall it? download.java.process=Java Download Process @@ -1343,10 +1348,15 @@ settings.advanced.precall_command=Pre-launch Command settings.advanced.precall_command.prompt=Commands to execute before the game launches settings.advanced.process_priority=Process Priority settings.advanced.process_priority.low=Low +settings.advanced.process_priority.low.desc= settings.advanced.process_priority.below_normal=Below Normal +settings.advanced.process_priority.below_normal.desc= settings.advanced.process_priority.normal=Normal +settings.advanced.process_priority.normal.desc= settings.advanced.process_priority.above_normal=Above Normal +settings.advanced.process_priority.above_normal.desc= settings.advanced.process_priority.high=High +settings.advanced.process_priority.high.desc= settings.advanced.post_exit_command=Post-exit Command settings.advanced.post_exit_command.prompt=Commands to execute after the game exits settings.advanced.renderer=Renderer diff --git a/HMCL/src/main/resources/assets/lang/I18N_es.properties b/HMCL/src/main/resources/assets/lang/I18N_es.properties index 9b1ddca4c..40a629367 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_es.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_es.properties @@ -338,8 +338,10 @@ download.failed.empty=No hay versiones disponibles, por favor haga clic aquí pa download.failed.no_code=No se ha podido descargar download.failed.refresh=No se ha podido obtener la lista de versiones. Por favor, haga clic aquí para volver a intentarlo. download.game=Nuevo juego -download.provider.bmclapi=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/) -download.provider.mojang=Oficial (OptiFine es proporcionado por BMCLAPI) +download.provider.bmclapi=BMCLAPI +download.provider.bmclapi.desc=bangbang93, https://bmclapi2.bangbang93.com/ +download.provider.mojang=Oficial +download.provider.mojang.desc=OptiFine es proporcionado por BMCLAPI download.provider.official=De fuentes oficiales download.provider.balanced=De la fuente más rápida disponible download.provider.mirror=Desde espejo diff --git a/HMCL/src/main/resources/assets/lang/I18N_ja.properties b/HMCL/src/main/resources/assets/lang/I18N_ja.properties index 187db5037..d1622445e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ja.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ja.properties @@ -295,8 +295,10 @@ download.failed=%1$s のダウンロードに失敗しました、応答コー download.failed.empty=候補者なし。戻るにはここをクリックしてください。 download.failed.refresh=バージョンリストをダウンロードできません。ここをクリックして再試行してください。 download.game=ゲームのダウンロード -download.provider.bmclapi=BMCLAPI(ミラーソース) -download.provider.mojang=Mojang(OptiFineはBMCLAPIにより提供されます) 推奨 +download.provider.bmclapi=BMCLAPI +download.provider.bmclapi.desc=ミラーソース +download.provider.mojang=Mojang +download.provider.mojang.desc=OptiFineはBMCLAPIにより提供されます download.provider.official=公式ソースからロード download.provider.balanced=mcbbsのソースからロード download.provider.mirror=ミラーソースからロード diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index 0185a5a45..d3eaf70f1 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -334,11 +334,16 @@ download.failed.empty=[無可裝之版,點此返]\n(君可求助於右上之 download.failed.no_code=引之未成 download.failed.refresh=[載版列未成,點此重試]\n(君可求助於右上之鈕) download.game=新戲 -download.provider.bmclapi=BMCLAPI (bangbang93,https://bmclapi2.bangbang93.com) -download.provider.mojang=官版 (Optifine 猶取諸 BMCLAPI 也) -download.provider.official=先官版 (至新,容有艱) -download.provider.balanced=先其快者 (均,容有舊) -download.provider.mirror=先別源 (至快,容有舊) +download.provider.bmclapi=BMCLAPI +download.provider.bmclapi.desc=bangbang93,https://bmclapi2.bangbang93.com +download.provider.mojang=官版 +download.provider.mojang.desc=Optifine 猶取諸 BMCLAPI 也 +download.provider.official=先官版 +download.provider.official.desc=至新,容有艱 +download.provider.balanced=先其快者 +download.provider.balanced.desc=均,容有舊 +download.provider.mirror=先別源 +download.provider.mirror.desc=至快,容有舊 download.java=引爪哇 download.java.override=爪哇是版既現,除而復裝諸? download.java.process=引爪哇 @@ -1066,11 +1071,16 @@ settings.advanced.no_jvm_args=無添爪哇虛機之本通弦 settings.advanced.precall_command=令於戲前 settings.advanced.precall_command.prompt=將前戲啟而行 settings.advanced.process_priority=進程之次 -settings.advanced.process_priority.low=低 (節戲所佔,容有滯焉) -settings.advanced.process_priority.below_normal=略低 (節戲所佔,容有滯焉) -settings.advanced.process_priority.normal=中 (權衡) -settings.advanced.process_priority.above_normal=略高 (以先礦藝之作動而後餘者也) -settings.advanced.process_priority.high=高 (以先礦藝之作動而後餘者也) +settings.advanced.process_priority.low=低 +settings.advanced.process_priority.low.desc=節戲所佔,容有滯焉 +settings.advanced.process_priority.below_normal=略低 +settings.advanced.process_priority.below_normal.desc=節戲所佔,容有滯焉 +settings.advanced.process_priority.normal=中 +settings.advanced.process_priority.normal.desc=權衡 +settings.advanced.process_priority.above_normal=略高 +settings.advanced.process_priority.above_normal.desc=以先礦藝之作動而後餘者也 +settings.advanced.process_priority.high=高 +settings.advanced.process_priority.high.desc=以先礦藝之作動而後餘者也 settings.advanced.post_exit_command=令於戲訖 settings.advanced.post_exit_command.prompt=將後戲訖而行 settings.advanced.renderer=繪器 diff --git a/HMCL/src/main/resources/assets/lang/I18N_ru.properties b/HMCL/src/main/resources/assets/lang/I18N_ru.properties index 60098a6ee..762c97dda 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ru.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ru.properties @@ -335,8 +335,10 @@ download.failed.empty=Версий нет. Нажмите здесь, чтобы download.failed.no_code=Не удалось скачать download.failed.refresh=Не удалось получить список версий. Нажмите здесь, чтобы повторить попытку. download.game=Новая игра -download.provider.bmclapi=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/) -download.provider.mojang=Официальный (OptiFine предоставляется BMCLAPI) +download.provider.bmclapi=BMCLAPI +download.provider.bmclapi.desc=bangbang93, https://bmclapi2.bangbang93.com/ +download.provider.mojang=Официальный +download.provider.mojang.desc=OptiFine предоставляется BMCLAPI download.provider.official=Из официальных источников download.provider.balanced=Из самых быстрых доступных download.provider.mirror=Из зеркала diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index 978448e0c..d36b7193d 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -334,8 +334,10 @@ download.failed.empty=Немає доступних версій. Натисні download.failed.no_code=Не вдалося завантажити download.failed.refresh=Не вдалося отримати список версій. Натисніть тут, щоб повторити спробу. download.game=Нова гра -download.provider.bmclapi=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/) -download.provider.mojang=Офіційне (OptiFine надається BMCLAPI) +download.provider.bmclapi=BMCLAPI +download.provider.bmclapi.desc=bangbang93, https://bmclapi2.bangbang93.com/ +download.provider.mojang=Офіційне +download.provider.mojang.desc=OptiFine надається BMCLAPI download.provider.official=З офіційних джерел download.provider.balanced=З найшвидшого доступного download.provider.mirror=З дзеркала diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 8a4ddaa8d..8f9f3f2d2 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -351,11 +351,16 @@ download.failed.empty=沒有可供安裝的版本,按一下此處返回。 download.failed.no_code=下載失敗 download.failed.refresh=載入版本清單失敗,按一下此處重試。 download.game=新遊戲 -download.provider.bmclapi=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/) -download.provider.mojang=官方伺服器 (OptiFine 由 BMCLAPI 提供) -download.provider.official=盡量使用官方源 (最新,但可能載入慢) -download.provider.balanced=選取載入速度快的下載源 (平衡,但可能不是最新) -download.provider.mirror=盡量使用鏡像源 (載入快,但可能不是最新) +download.provider.bmclapi=BMCLAPI +download.provider.bmclapi.desc=bangbang93, https://bmclapi2.bangbang93.com/ +download.provider.mojang=官方伺服器 +download.provider.mojang.desc=OptiFine 由 BMCLAPI 提供 +download.provider.official=盡量使用官方源 +download.provider.official.desc=最新,但可能載入慢 +download.provider.balanced=選取載入速度快的下載源 +download.provider.balanced.desc=平衡,但可能不是最新 +download.provider.mirror=盡量使用鏡像源 +download.provider.mirror.desc=載入快,但可能不是最新 download.java=下載 Java download.java.override=此 Java 版本已經存在,是否移除並重新安裝? download.java.process=下載 Java @@ -1123,11 +1128,16 @@ settings.advanced.no_optimizing_jvm_args=不自動新增 Java 虛擬機最佳化 settings.advanced.precall_command=遊戲啟動前執行指令 settings.advanced.precall_command.prompt=將在遊戲啟動前呼叫使用 settings.advanced.process_priority=處理程式優先度 -settings.advanced.process_priority.low=低 (節省遊戲占用資源,可能會造成遊戲卡頓) -settings.advanced.process_priority.below_normal=較低 (節省遊戲占用資源,可能會造成遊戲卡頓) -settings.advanced.process_priority.normal=中 (平衡) -settings.advanced.process_priority.above_normal=較高 (優先保證遊戲執行,但可能會導致其他程式卡頓) -settings.advanced.process_priority.high=高 (優先保證遊戲執行,但可能會導致其他程式卡頓) +settings.advanced.process_priority.low=低 +settings.advanced.process_priority.low.desc=節省遊戲占用資源,可能會造成遊戲卡頓 +settings.advanced.process_priority.below_normal=較低 +settings.advanced.process_priority.below_normal.desc=節省遊戲占用資源,可能會造成遊戲卡頓 +settings.advanced.process_priority.normal=中 +settings.advanced.process_priority.normal.desc=平衡 +settings.advanced.process_priority.above_normal=較高 +settings.advanced.process_priority.above_normal.desc=優先保證遊戲執行,但可能會導致其他程式卡頓 +settings.advanced.process_priority.high=高 +settings.advanced.process_priority.high.desc=優先保證遊戲執行,但可能會導致其他程式卡頓 settings.advanced.post_exit_command=遊戲結束後執行指令 settings.advanced.post_exit_command.prompt=將在遊戲結束後呼叫使用 settings.advanced.renderer=繪製器 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index f089ab84a..6383c018c 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -353,11 +353,16 @@ download.failed.empty=[没有可供安装的版本,点击此处返回]\n(你 download.failed.no_code=下载失败 download.failed.refresh=[加载版本列表失败,点击此处重试]\n(你可以点击右上角帮助按钮进行求助) download.game=新游戏 -download.provider.bmclapi=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com) -download.provider.mojang=官方 (OptiFine 自动安装使用 BMCLAPI 下载源) -download.provider.official=尽量使用官方源 (最新,但可能加载慢) -download.provider.balanced=选择加载速度快的下载源 (平衡,但可能不是最新) -download.provider.mirror=尽量使用镜像源 (加载快,但可能不是最新) +download.provider.bmclapi=BMCLAPI +download.provider.bmclapi.desc=bangbang93, https://bmclapi2.bangbang93.com +download.provider.mojang=官方 +download.provider.mojang.desc=OptiFine 自动安装使用 BMCLAPI 下载源 +download.provider.official=尽量使用官方源 +download.provider.official.desc=最新,但可能加载慢 +download.provider.balanced=选择加载速度快的下载源 +download.provider.balanced.desc=平衡,但可能不是最新 +download.provider.mirror=尽量使用镜像源 +download.provider.mirror.desc=加载快,但可能不是最新 download.java=下载 Java download.java.override=此 Java 版本已经存在,是否卸载并重新安装? download.java.process=下载 Java @@ -1127,11 +1132,16 @@ settings.advanced.no_optimizing_jvm_args=不自动添加 Java 虚拟机优化参 settings.advanced.precall_command=游戏启动前执行命令 settings.advanced.precall_command.prompt=将在游戏启动前调用 settings.advanced.process_priority=进程优先级 -settings.advanced.process_priority.low=低 (节省游戏占用资源,可能会造成游戏卡顿) -settings.advanced.process_priority.below_normal=较低 (节省游戏占用资源,可能会造成游戏卡顿) -settings.advanced.process_priority.normal=中 (平衡) -settings.advanced.process_priority.above_normal=较高 (优先保证游戏运行,但可能会导致其他程序卡顿) -settings.advanced.process_priority.high=高 (优先保证游戏运行,但可能会导致其他程序卡顿) +settings.advanced.process_priority.low=低 +settings.advanced.process_priority.low.desc=节省游戏占用资源,可能会造成游戏卡顿 +settings.advanced.process_priority.below_normal=较低 +settings.advanced.process_priority.below_normal.desc=节省游戏占用资源,可能会造成游戏卡顿 +settings.advanced.process_priority.normal=中 +settings.advanced.process_priority.normal.desc=平衡 +settings.advanced.process_priority.above_normal=较高 +settings.advanced.process_priority.above_normal.desc=优先保证游戏运行,但可能会导致其他程序卡顿 +settings.advanced.process_priority.high=高 +settings.advanced.process_priority.high.desc=优先保证游戏运行,但可能会导致其他程序卡顿 settings.advanced.post_exit_command=游戏结束后执行命令 settings.advanced.post_exit_command.prompt=将在游戏结束后调用 settings.advanced.renderer=渲染器