使用 LineSelectButton 代替 JFXComboxBox (#5337)

This commit is contained in:
Glavo
2026-01-29 20:28:45 +08:00
committed by GitHub
parent 90a795b2c8
commit e3231859ea
25 changed files with 797 additions and 394 deletions

View File

@@ -137,7 +137,7 @@ public class JFXPopup extends PopupControl {
boolean isRTL = node.getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT; 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.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) parent.getY() + origin.getY() + scene.getY() + (vAlign == PopupVPosition.BOTTOM ? ((Region) node).getHeight() : 0)
); );

View File

@@ -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"), 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"), 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"), 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"), 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"), 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"), 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"),

View File

@@ -142,7 +142,7 @@ public class ComponentList extends Control {
if (node.getProperties().containsKey("ComponentList.vgrow")) { if (node.getProperties().containsKey("ComponentList.vgrow")) {
VBox.setVgrow(cell, (Priority) node.getProperties().get("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"); cell.getStyleClass().add("no-padding");
} }
return cell; return cell;

View File

@@ -0,0 +1,123 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2026 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
*/
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);
}
}

View File

@@ -0,0 +1,112 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2026 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
*/
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<EventHandler<ActionEvent>> 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<EventHandler<ActionEvent>> onActionProperty() {
return onAction;
}
public EventHandler<ActionEvent> getOnAction() {
return onActionProperty().get();
}
public void setOnAction(EventHandler<ActionEvent> 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);
}
}

View File

@@ -0,0 +1,231 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2026 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
*/
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<T> 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<T, String> 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<T, String> 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<T, String> 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<T> value = new SimpleObjectProperty<>(this, "value");
public ObjectProperty<T> valueProperty() {
return value;
}
public T getValue() {
return valueProperty().get();
}
public void setValue(T value) {
valueProperty().set(value);
}
private final ObjectProperty<Function<T, String>> converter = new SimpleObjectProperty<>(this, "converter");
public ObjectProperty<Function<T, String>> converterProperty() {
return converter;
}
public Function<T, String> getConverter() {
return converterProperty().get();
}
public void setConverter(Function<T, String> value) {
converterProperty().set(value);
}
private ObjectProperty<Function<T, String>> descriptionConverter;
public ObjectProperty<Function<T, String>> descriptionConverterProperty() {
if (descriptionConverter == null) {
descriptionConverter = new SimpleObjectProperty<>(this, "descriptionConverter");
}
return descriptionConverter;
}
public Function<T, String> getDescriptionConverter() {
return descriptionConverterProperty().get();
}
public void setDescriptionConverter(Function<T, String> value) {
descriptionConverterProperty().set(value);
}
private final ListProperty<T> items = new SimpleListProperty<>(this, "items", FXCollections.emptyObservableList());
public ListProperty<T> itemsProperty() {
return items;
}
public void setItems(ObservableList<T> value) {
itemsProperty().set(value);
}
public void setItems(Collection<T> value) {
if (value instanceof ObservableList<T> observableList) {
this.setItems(observableList);
} else {
this.setItems(FXCollections.observableArrayList(value));
}
}
@SafeVarargs
public final void setItems(T... values) {
this.setItems(FXCollections.observableArrayList(values));
}
public ObservableList<T> getItems() {
return items.get();
}
}

View File

@@ -0,0 +1,57 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
*/
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);
}
}

View File

@@ -1,113 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
*/
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);
}
}

View File

@@ -158,6 +158,10 @@ public class RipplerContainer extends StackPane {
updateChildren(); updateChildren();
} }
public JFXRippler getRippler() {
return buttonRippler;
}
public Node getContainer() { public Node getContainer() {
return container.get(); return container.get();
} }

View File

@@ -31,15 +31,15 @@ import org.jackhuang.hmcl.task.FetchTask;
import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.WeakListenerHolder; import org.jackhuang.hmcl.ui.WeakListenerHolder;
import org.jackhuang.hmcl.ui.construct.*; import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.util.i18n.I18n;
import org.jackhuang.hmcl.util.javafx.SafeStringConverter; import org.jackhuang.hmcl.util.javafx.SafeStringConverter;
import java.net.Proxy; import java.net.Proxy;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import static org.jackhuang.hmcl.setting.ConfigHolder.config; 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.i18n.I18n.i18n;
import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.selectedItemPropertyFor;
public class DownloadSettingsPage extends StackPane { public class DownloadSettingsPage extends StackPane {
@@ -55,51 +55,37 @@ public class DownloadSettingsPage extends StackPane {
getChildren().setAll(scrollPane); getChildren().setAll(scrollPane);
{ {
VBox downloadSource = new VBox(8); var downloadSource = new ComponentList();
downloadSource.getStyleClass().add("card-non-transparent"); downloadSource.getStyleClass().add("card-non-transparent");
{ {
VBox chooseWrapper = new VBox(); var autoChooseDownloadSource = new LineToggleButton();
chooseWrapper.setPadding(new Insets(8, 0, 8, 0)); autoChooseDownloadSource.setTitle(i18n("settings.launcher.download_source.auto"));
JFXCheckBox chkAutoChooseDownloadSource = new JFXCheckBox(i18n("settings.launcher.download_source.auto")); autoChooseDownloadSource.selectedProperty().bindBidirectional(config().autoChooseDownloadTypeProperty());
chkAutoChooseDownloadSource.selectedProperty().bindBidirectional(config().autoChooseDownloadTypeProperty());
chooseWrapper.getChildren().setAll(chkAutoChooseDownloadSource);
BorderPane versionListSourcePane = new BorderPane(); Function<String, String> converter = key -> i18n("download.provider." + key);
versionListSourcePane.setPadding(new Insets(0, 0, 8, 30)); Function<String, String> descriptionConverter = key -> {
versionListSourcePane.disableProperty().bind(chkAutoChooseDownloadSource.selectedProperty().not()); String bundleKey = "download.provider." + key + ".desc";
{ return I18n.hasKey(bundleKey) ? i18n(bundleKey) : null;
Label label = new Label(i18n("settings.launcher.version_list_source")); };
BorderPane.setAlignment(label, Pos.CENTER_LEFT);
versionListSourcePane.setLeft(label);
JFXComboBox<String> cboVersionListSource = new JFXComboBox<>(); var versionListSourcePane = new LineSelectButton<String>();
cboVersionListSource.setConverter(stringConverter(key -> i18n("download.provider." + key))); versionListSourcePane.disableProperty().bind(autoChooseDownloadSource.selectedProperty().not());
versionListSourcePane.setRight(cboVersionListSource); versionListSourcePane.setTitle(i18n("settings.launcher.version_list_source"));
FXUtils.setLimitWidth(cboVersionListSource, 400); versionListSourcePane.setConverter(converter);
versionListSourcePane.setDescriptionConverter(descriptionConverter);
versionListSourcePane.setItems(DownloadProviders.AUTO_PROVIDERS.keySet());
versionListSourcePane.valueProperty().bindBidirectional(config().versionListSourceProperty());
cboVersionListSource.getItems().setAll(DownloadProviders.AUTO_PROVIDERS.keySet()); var downloadSourcePane = new LineSelectButton<String>();
selectedItemPropertyFor(cboVersionListSource).bindBidirectional(config().versionListSourceProperty()); 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(); downloadSource.getContent().setAll(autoChooseDownloadSource, versionListSourcePane, downloadSourcePane);
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<String> 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);
} }
content.getChildren().addAll(ComponentList.createComponentListTitle(i18n("settings.launcher.download_source")), downloadSource); content.getChildren().addAll(ComponentList.createComponentListTitle(i18n("settings.launcher.download_source")), downloadSource);

View File

@@ -25,7 +25,6 @@ import javafx.beans.binding.StringBinding;
import javafx.beans.binding.When; import javafx.beans.binding.When;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.control.ColorPicker; import javafx.scene.control.ColorPicker;
@@ -78,19 +77,13 @@ public class PersonalizationPage extends StackPane {
ComponentList themeList = new ComponentList(); ComponentList themeList = new ComponentList();
{ {
BorderPane brightnessPane = new BorderPane(); var brightnessPane = new LineSelectButton<String>();
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); themeList.getContent().add(brightnessPane);
Label left = new Label(i18n("settings.launcher.brightness"));
BorderPane.setAlignment(left, Pos.CENTER_LEFT);
brightnessPane.setLeft(left);
JFXComboBox<String> 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)); Platform.runLater(() -> JFXDepthManager.setDepth(picker, 0));
} }
{ {
OptionToggleButton titleTransparentButton = new OptionToggleButton(); LineToggleButton titleTransparentButton = new LineToggleButton();
themeList.getContent().add(titleTransparentButton); themeList.getContent().add(titleTransparentButton);
titleTransparentButton.selectedProperty().bindBidirectional(config().titleTransparentProperty()); titleTransparentButton.selectedProperty().bindBidirectional(config().titleTransparentProperty());
titleTransparentButton.setTitle(i18n("settings.launcher.title_transparent")); titleTransparentButton.setTitle(i18n("settings.launcher.title_transparent"));
} }
{ {
OptionToggleButton animationButton = new OptionToggleButton(); LineToggleButton animationButton = new LineToggleButton();
themeList.getContent().add(animationButton); themeList.getContent().add(animationButton);
animationButton.selectedProperty().bindBidirectional(config().animationDisabledProperty()); animationButton.selectedProperty().bindBidirectional(config().animationDisabledProperty());
animationButton.setTitle(i18n("settings.launcher.turn_off_animations")); animationButton.setTitle(i18n("settings.launcher.turn_off_animations"));
@@ -304,46 +297,33 @@ public class PersonalizationPage extends StackPane {
} }
{ {
BorderPane fontAntiAliasingPane = new BorderPane(); var fontAntiAliasingPane = new LineSelectButton<Optional<FontSmoothingType>>();
{ fontAntiAliasingPane.setTitle(i18n("settings.launcher.font.anti_aliasing"));
VBox left = new VBox(); fontAntiAliasingPane.setSubtitle(i18n("settings.take_effect_after_restart"));
Label title = new Label(i18n("settings.launcher.font.anti_aliasing")); fontAntiAliasingPane.setConverter(value ->
title.getStyleClass().add("title"); value.isPresent()
Label subtitle = new Label(i18n("settings.take_effect_after_restart")); ? i18n("settings.launcher.font.anti_aliasing." + value.get().name().toLowerCase(Locale.ROOT))
subtitle.getStyleClass().add("subtitle"); : i18n("settings.launcher.font.anti_aliasing.auto")
left.getChildren().setAll(title, subtitle); );
fontAntiAliasingPane.setLeft(left); fontAntiAliasingPane.setItems(
}
{
@SuppressWarnings("unchecked")
JFXComboBox<Optional<FontSmoothingType>> cboAntiAliasing = new JFXComboBox<>(FXCollections.observableArrayList(
Optional.empty(), Optional.empty(),
Optional.of(FontSmoothingType.LCD), Optional.of(FontSmoothingType.LCD),
Optional.of(FontSmoothingType.GRAY) Optional.of(FontSmoothingType.GRAY)
)); );
String fontAntiAliasing = globalConfig().getFontAntiAliasing(); String fontAntiAliasing = globalConfig().getFontAntiAliasing();
if ("lcd".equalsIgnoreCase(fontAntiAliasing)) { if ("lcd".equalsIgnoreCase(fontAntiAliasing)) {
cboAntiAliasing.setValue(Optional.of(FontSmoothingType.LCD)); fontAntiAliasingPane.setValue(Optional.of(FontSmoothingType.LCD));
} else if ("gray".equalsIgnoreCase(fontAntiAliasing)) { } else if ("gray".equalsIgnoreCase(fontAntiAliasing)) {
cboAntiAliasing.setValue(Optional.of(FontSmoothingType.GRAY)); fontAntiAliasingPane.setValue(Optional.of(FontSmoothingType.GRAY));
} else { } else {
cboAntiAliasing.setValue(Optional.empty()); fontAntiAliasingPane.setValue(Optional.empty());
} }
cboAntiAliasing.setConverter(FXUtils.stringConverter(value -> {
if (value.isPresent()) { FXUtils.onChange(fontAntiAliasingPane.valueProperty(), value ->
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)) globalConfig().setFontAntiAliasing(value.map(it -> it.name().toLowerCase(Locale.ROOT))
.orElse(null))); .orElse(null)));
fontAntiAliasingPane.setRight(cboAntiAliasing);
}
fontPane.getContent().add(fontAntiAliasingPane); fontPane.getContent().add(fontAntiAliasingPane);
} }

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.ui.main; package org.jackhuang.hmcl.ui.main;
import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXComboBox;
import com.jfoenix.controls.JFXRadioButton; import com.jfoenix.controls.JFXRadioButton;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.InvalidationListener; 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.Controllers;
import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.construct.ComponentList; import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.ui.construct.ComponentSublist;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType; 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.RemoteVersion;
import org.jackhuang.hmcl.upgrade.UpdateChannel; import org.jackhuang.hmcl.upgrade.UpdateChannel;
import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.upgrade.UpdateChecker;
@@ -69,7 +65,6 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import static org.jackhuang.hmcl.setting.ConfigHolder.config; 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.Lang.thread;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.selectedItemPropertyFor; 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.setTitle(i18n("update.preview"));
previewPane.setSubtitle(i18n("update.preview.subtitle")); previewPane.setSubtitle(i18n("update.preview.subtitle"));
previewPane.selectedProperty().bindBidirectional(config().acceptPreviewUpdateProperty()); previewPane.selectedProperty().bindBidirectional(config().acceptPreviewUpdateProperty());
@@ -250,37 +245,27 @@ public final class SettingsPage extends ScrollPane {
} }
{ {
BorderPane languagePane = new BorderPane(); var chooseLanguagePane = new LineSelectButton<SupportedLocale>();
chooseLanguagePane.setTitle(i18n("settings.launcher.language"));
VBox left = new VBox(); chooseLanguagePane.setSubtitle(i18n("settings.take_effect_after_restart"));
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);
SupportedLocale currentLocale = I18n.getLocale(); SupportedLocale currentLocale = I18n.getLocale();
JFXComboBox<SupportedLocale> cboLanguage = new JFXComboBox<>(); chooseLanguagePane.setConverter(locale -> {
cboLanguage.setConverter(stringConverter(locale -> {
if (locale.isDefault()) if (locale.isDefault())
return locale.getDisplayName(currentLocale); return locale.getDisplayName(currentLocale);
else if (locale.isSameLanguage(currentLocale)) else if (locale.isSameLanguage(currentLocale))
return locale.getDisplayName(locale); return locale.getDisplayName(locale);
else else
return locale.getDisplayName(currentLocale) + " - " + locale.getDisplayName(locale); return locale.getDisplayName(currentLocale) + " - " + locale.getDisplayName(locale);
})); });
cboLanguage.getItems().setAll(SupportedLocale.getSupportedLocales()); chooseLanguagePane.setItems(SupportedLocale.getSupportedLocales());
selectedItemPropertyFor(cboLanguage).bindBidirectional(config().localizationProperty()); chooseLanguagePane.valueProperty().bindBidirectional(config().localizationProperty());
FXUtils.setLimitWidth(cboLanguage, 300); settingsPane.getContent().add(chooseLanguagePane);
languagePane.setRight(cboLanguage);
settingsPane.getContent().add(languagePane);
} }
{ {
OptionToggleButton disableAutoGameOptionsPane = new OptionToggleButton(); LineToggleButton disableAutoGameOptionsPane = new LineToggleButton();
disableAutoGameOptionsPane.setTitle(i18n("settings.launcher.disable_auto_game_options")); disableAutoGameOptionsPane.setTitle(i18n("settings.launcher.disable_auto_game_options"));
disableAutoGameOptionsPane.selectedProperty().bindBidirectional(config().disableAutoGameOptionsProperty()); disableAutoGameOptionsPane.selectedProperty().bindBidirectional(config().disableAutoGameOptionsProperty());

View File

@@ -37,7 +37,7 @@ import org.jackhuang.hmcl.setting.Profiles;
import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.construct.ComponentList; import org.jackhuang.hmcl.ui.construct.ComponentList;
import org.jackhuang.hmcl.ui.construct.FileItem; 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.construct.PageCloseEvent;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.StringUtils;
@@ -55,7 +55,7 @@ public final class ProfilePage extends BorderPane implements DecoratorPage {
private final Profile profile; private final Profile profile;
private final JFXTextField txtProfileName; private final JFXTextField txtProfileName;
private final FileItem gameDir; private final FileItem gameDir;
private final OptionToggleButton toggleUseRelativePath; private final LineToggleButton toggleUseRelativePath;
/** /**
* @param profile null if creating a new profile. * @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.setTitle(i18n("profile.instance_directory.choose"));
gameDir.pathProperty().bindBidirectional(location); gameDir.pathProperty().bindBidirectional(location);
toggleUseRelativePath = new OptionToggleButton(); toggleUseRelativePath = new LineToggleButton();
toggleUseRelativePath.setTitle(i18n("profile.use_relative_path")); toggleUseRelativePath.setTitle(i18n("profile.use_relative_path"));
gameDir.convertToRelativePathProperty().bind(toggleUseRelativePath.selectedProperty()); gameDir.convertToRelativePathProperty().bind(toggleUseRelativePath.selectedProperty());

View File

@@ -1,12 +1,10 @@
package org.jackhuang.hmcl.ui.versions; package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.controls.JFXComboBox;
import com.jfoenix.controls.JFXTextField; import com.jfoenix.controls.JFXTextField;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Pos;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane; import javafx.scene.control.ScrollPane;
import javafx.scene.layout.*; import javafx.scene.layout.*;
@@ -26,7 +24,6 @@ import java.nio.file.FileSystems;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
import static org.jackhuang.hmcl.ui.FXUtils.stringConverter;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class AdvancedVersionSettingPage extends StackPane implements DecoratorPage { 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 txtWrapper;
private final JFXTextField txtPreLaunchCommand; private final JFXTextField txtPreLaunchCommand;
private final JFXTextField txtPostExitCommand; private final JFXTextField txtPostExitCommand;
private final OptionToggleButton noJVMArgsPane; private final LineToggleButton noJVMArgsPane;
private final OptionToggleButton noOptimizingJVMArgsPane; private final LineToggleButton noOptimizingJVMArgsPane;
private final OptionToggleButton noGameCheckPane; private final LineToggleButton noGameCheckPane;
private final OptionToggleButton noJVMCheckPane; private final LineToggleButton noJVMCheckPane;
private final OptionToggleButton noNativesPatchPane; private final LineToggleButton noNativesPatchPane;
private final OptionToggleButton useNativeGLFWPane; private final LineToggleButton useNativeGLFWPane;
private final OptionToggleButton useNativeOpenALPane; private final LineToggleButton useNativeOpenALPane;
private final ComponentSublist nativesDirSublist; private final ComponentSublist nativesDirSublist;
private final MultiFileItem<NativesDirectoryType> nativesDirItem; private final MultiFileItem<NativesDirectoryType> nativesDirItem;
private final MultiFileItem.FileOption<NativesDirectoryType> nativesDirCustomOption; private final MultiFileItem.FileOption<NativesDirectoryType> nativesDirCustomOption;
private final JFXComboBox<Renderer> cboRenderer; private final LineSelectButton<Renderer> rendererPane;
public AdvancedVersionSettingPage(Profile profile, @Nullable String versionId, VersionSetting versionSetting) { public AdvancedVersionSettingPage(Profile profile, @Nullable String versionId, VersionSetting versionSetting) {
this.profile = profile; this.profile = profile;
@@ -170,40 +167,31 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor
nativesDirHint.setText(i18n("settings.advanced.natives_directory.hint")); nativesDirHint.setText(i18n("settings.advanced.natives_directory.hint"));
nativesDirItem.getChildren().add(nativesDirHint); nativesDirItem.getChildren().add(nativesDirHint);
BorderPane rendererPane = new BorderPane(); rendererPane = new LineSelectButton<>();
{ rendererPane.setTitle(i18n("settings.advanced.renderer"));
Label label = new Label(i18n("settings.advanced.renderer")); rendererPane.setConverter(e -> i18n("settings.advanced.renderer." + e.name().toLowerCase(Locale.ROOT)));
rendererPane.setLeft(label); rendererPane.setItems(Renderer.values());
BorderPane.setAlignment(label, Pos.CENTER_LEFT);
cboRenderer = new JFXComboBox<>(); noJVMArgsPane = new LineToggleButton();
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.setTitle(i18n("settings.advanced.no_jvm_args")); noJVMArgsPane.setTitle(i18n("settings.advanced.no_jvm_args"));
noOptimizingJVMArgsPane = new OptionToggleButton(); noOptimizingJVMArgsPane = new LineToggleButton();
noOptimizingJVMArgsPane.setTitle(i18n("settings.advanced.no_optimizing_jvm_args")); noOptimizingJVMArgsPane.setTitle(i18n("settings.advanced.no_optimizing_jvm_args"));
noOptimizingJVMArgsPane.disableProperty().bind(noJVMArgsPane.selectedProperty()); noOptimizingJVMArgsPane.disableProperty().bind(noJVMArgsPane.selectedProperty());
noGameCheckPane = new OptionToggleButton(); noGameCheckPane = new LineToggleButton();
noGameCheckPane.setTitle(i18n("settings.advanced.dont_check_game_completeness")); noGameCheckPane.setTitle(i18n("settings.advanced.dont_check_game_completeness"));
noJVMCheckPane = new OptionToggleButton(); noJVMCheckPane = new LineToggleButton();
noJVMCheckPane.setTitle(i18n("settings.advanced.dont_check_jvm_validity")); noJVMCheckPane.setTitle(i18n("settings.advanced.dont_check_jvm_validity"));
noNativesPatchPane = new OptionToggleButton(); noNativesPatchPane = new LineToggleButton();
noNativesPatchPane.setTitle(i18n("settings.advanced.dont_patch_natives")); noNativesPatchPane.setTitle(i18n("settings.advanced.dont_patch_natives"));
useNativeGLFWPane = new OptionToggleButton(); useNativeGLFWPane = new LineToggleButton();
useNativeGLFWPane.setTitle(i18n("settings.advanced.use_native_glfw")); useNativeGLFWPane.setTitle(i18n("settings.advanced.use_native_glfw"));
useNativeOpenALPane = new OptionToggleButton(); useNativeOpenALPane = new LineToggleButton();
useNativeOpenALPane.setTitle(i18n("settings.advanced.use_native_openal")); useNativeOpenALPane.setTitle(i18n("settings.advanced.use_native_openal"));
workaroundPane.getContent().setAll( workaroundPane.getContent().setAll(
@@ -239,7 +227,7 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor
FXUtils.bindString(txtWrapper, versionSetting.wrapperProperty()); FXUtils.bindString(txtWrapper, versionSetting.wrapperProperty());
FXUtils.bindString(txtPreLaunchCommand, versionSetting.preLaunchCommandProperty()); FXUtils.bindString(txtPreLaunchCommand, versionSetting.preLaunchCommandProperty());
FXUtils.bindString(txtPostExitCommand, versionSetting.postExitCommandProperty()); FXUtils.bindString(txtPostExitCommand, versionSetting.postExitCommandProperty());
FXUtils.bindEnum(cboRenderer, versionSetting.rendererProperty()); rendererPane.valueProperty().bindBidirectional(versionSetting.rendererProperty());
noGameCheckPane.selectedProperty().bindBidirectional(versionSetting.notCheckGameProperty()); noGameCheckPane.selectedProperty().bindBidirectional(versionSetting.notCheckGameProperty());
noJVMCheckPane.selectedProperty().bindBidirectional(versionSetting.notCheckJVMProperty()); noJVMCheckPane.selectedProperty().bindBidirectional(versionSetting.notCheckJVMProperty());
noJVMArgsPane.selectedProperty().bindBidirectional(versionSetting.noJVMArgsProperty()); noJVMArgsPane.selectedProperty().bindBidirectional(versionSetting.noJVMArgsProperty());
@@ -281,7 +269,7 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor
FXUtils.unbind(txtWrapper, versionSetting.wrapperProperty()); FXUtils.unbind(txtWrapper, versionSetting.wrapperProperty());
FXUtils.unbind(txtPreLaunchCommand, versionSetting.preLaunchCommandProperty()); FXUtils.unbind(txtPreLaunchCommand, versionSetting.preLaunchCommandProperty());
FXUtils.unbind(txtPostExitCommand, versionSetting.postExitCommandProperty()); FXUtils.unbind(txtPostExitCommand, versionSetting.postExitCommandProperty());
FXUtils.unbindEnum(cboRenderer, versionSetting.rendererProperty()); rendererPane.valueProperty().unbindBidirectional(versionSetting.rendererProperty());
noGameCheckPane.selectedProperty().unbindBidirectional(versionSetting.notCheckGameProperty()); noGameCheckPane.selectedProperty().unbindBidirectional(versionSetting.notCheckGameProperty());
noJVMCheckPane.selectedProperty().unbindBidirectional(versionSetting.notCheckJVMProperty()); noJVMCheckPane.selectedProperty().unbindBidirectional(versionSetting.notCheckJVMProperty());
noJVMArgsPane.selectedProperty().unbindBidirectional(versionSetting.noJVMArgsProperty()); noJVMArgsPane.selectedProperty().unbindBidirectional(versionSetting.noJVMArgsProperty());

View File

@@ -43,6 +43,7 @@ import org.jackhuang.hmcl.ui.WeakListenerHolder;
import org.jackhuang.hmcl.ui.construct.*; import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.util.*; 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.BindingMapping;
import org.jackhuang.hmcl.util.javafx.PropertyUtils; import org.jackhuang.hmcl.util.javafx.PropertyUtils;
import org.jackhuang.hmcl.util.javafx.SafeStringConverter; import org.jackhuang.hmcl.util.javafx.SafeStringConverter;
@@ -57,7 +58,6 @@ import java.lang.ref.WeakReference;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean; 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.GIGABYTES;
import static org.jackhuang.hmcl.util.DataSizeUnit.MEGABYTES; import static org.jackhuang.hmcl.util.DataSizeUnit.MEGABYTES;
import static org.jackhuang.hmcl.util.Pair.pair; import static org.jackhuang.hmcl.util.Pair.pair;
@@ -78,7 +78,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
private final JFXComboBox<String> cboWindowsSize; private final JFXComboBox<String> cboWindowsSize;
private final JFXTextField txtServerIP; private final JFXTextField txtServerIP;
private final ComponentList componentList; private final ComponentList componentList;
private final JFXComboBox<LauncherVisibility> cboLauncherVisibility; private final LineSelectButton<LauncherVisibility> launcherVisibilityPane;
private final JFXCheckBox chkAutoAllocate; private final JFXCheckBox chkAutoAllocate;
private final JFXCheckBox chkFullscreen; private final JFXCheckBox chkFullscreen;
private final ComponentSublist javaSublist; private final ComponentSublist javaSublist;
@@ -90,9 +90,9 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
private final ComponentSublist gameDirSublist; private final ComponentSublist gameDirSublist;
private final MultiFileItem<GameDirectoryType> gameDirItem; private final MultiFileItem<GameDirectoryType> gameDirItem;
private final MultiFileItem.FileOption<GameDirectoryType> gameDirCustomOption; private final MultiFileItem.FileOption<GameDirectoryType> gameDirCustomOption;
private final JFXComboBox<ProcessPriority> cboProcessPriority; private final LineSelectButton<ProcessPriority> processPriorityPane;
private final OptionToggleButton showLogsPane; private final LineToggleButton showLogsPane;
private final OptionToggleButton enableDebugLogOutputPane; private final LineToggleButton enableDebugLogOutputPane;
private final ImagePickerItem iconPickerItem; private final ImagePickerItem iconPickerItem;
private final ChangeListener<Collection<JavaRuntime>> javaListChangeListener; private final ChangeListener<Collection<JavaRuntime>> javaListChangeListener;
@@ -351,17 +351,10 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
maxMemoryPane.getChildren().setAll(title, chkAutoAllocate, lowerBoundPane, progressBarPane, digitalPane); maxMemoryPane.getChildren().setAll(title, chkAutoAllocate, lowerBoundPane, progressBarPane, digitalPane);
} }
BorderPane launcherVisibilityPane = new BorderPane(); launcherVisibilityPane = new LineSelectButton<>();
{ launcherVisibilityPane.setTitle(i18n("settings.advanced.launcher_visible"));
Label label = new Label(i18n("settings.advanced.launcher_visible")); launcherVisibilityPane.setItems(LauncherVisibility.values());
launcherVisibilityPane.setLeft(label); launcherVisibilityPane.setConverter(e -> i18n("settings.advanced.launcher_visibility." + e.name().toLowerCase(Locale.ROOT)));
BorderPane.setAlignment(label, Pos.CENTER_LEFT);
cboLauncherVisibility = new JFXComboBox<>();
launcherVisibilityPane.setRight(cboLauncherVisibility);
BorderPane.setAlignment(cboLauncherVisibility, Pos.CENTER_RIGHT);
FXUtils.setLimitWidth(cboLauncherVisibility, 300);
}
BorderPane dimensionPane = new BorderPane(); 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")); showLogsPane.setTitle(i18n("settings.show_log"));
enableDebugLogOutputPane = new OptionToggleButton(); enableDebugLogOutputPane = new LineToggleButton();
enableDebugLogOutputPane.setTitle(i18n("settings.enable_debug_log_output")); enableDebugLogOutputPane.setTitle(i18n("settings.enable_debug_log_output"));
processPriorityPane = new LineSelectButton<>();
BorderPane processPriorityPane = new BorderPane(); processPriorityPane.setTitle(i18n("settings.advanced.process_priority"));
{ processPriorityPane.setConverter(e -> i18n("settings.advanced.process_priority." + e.name().toLowerCase(Locale.ROOT)));
Label label = new Label(i18n("settings.advanced.process_priority")); processPriorityPane.setDescriptionConverter(e -> {
processPriorityPane.setLeft(label); String bundleKey = "settings.advanced.process_priority." + e.name().toLowerCase(Locale.ROOT) + ".desc";
BorderPane.setAlignment(label, Pos.CENTER_LEFT); return I18n.hasKey(bundleKey) ? i18n(bundleKey) : null;
});
cboProcessPriority = new JFXComboBox<>(); processPriorityPane.setItems(ProcessPriority.values());
processPriorityPane.setRight(cboProcessPriority);
BorderPane.setAlignment(cboProcessPriority, Pos.CENTER_RIGHT);
FXUtils.setLimitWidth(cboProcessPriority, 300);
}
GridPane serverPane = new GridPane(); GridPane serverPane = new GridPane();
{ {
@@ -436,14 +425,9 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
serverPane.addRow(0, new Label(i18n("settings.advanced.server_ip")), txtServerIP); serverPane.addRow(0, new Label(i18n("settings.advanced.server_ip")), txtServerIP);
} }
BorderPane showAdvancedSettingPane = new BorderPane(); LineNavigationButton showAdvancedSettingPane = new LineNavigationButton();
{ showAdvancedSettingPane.setTitle(i18n("settings.advanced"));
Label label = new Label(i18n("settings.advanced")); showAdvancedSettingPane.setOnAction(event -> {
showAdvancedSettingPane.setLeft(label);
BorderPane.setAlignment(label, Pos.CENTER_LEFT);
JFXButton button = FXUtils.newBorderButton(i18n("settings.advanced.modify"));
button.setOnAction(e -> {
if (lastVersionSetting != null) { if (lastVersionSetting != null) {
if (advancedVersionSettingPage == null) if (advancedVersionSettingPage == null)
advancedVersionSettingPage = new AdvancedVersionSettingPage(profile, versionId, lastVersionSetting); advancedVersionSettingPage = new AdvancedVersionSettingPage(profile, versionId, lastVersionSetting);
@@ -451,8 +435,6 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
Controllers.navigateForward(advancedVersionSettingPage); Controllers.navigateForward(advancedVersionSettingPage);
} }
}); });
showAdvancedSettingPane.setRight(button);
}
componentList.getContent().addAll( componentList.getContent().addAll(
javaSublist, javaSublist,
@@ -487,12 +469,6 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onDecoratorPageNavigating); 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()); componentList.disableProperty().bind(enableSpecificSettings.not());
} }
@@ -537,8 +513,8 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
chkFullscreen.selectedProperty().unbindBidirectional(lastVersionSetting.fullscreenProperty()); chkFullscreen.selectedProperty().unbindBidirectional(lastVersionSetting.fullscreenProperty());
showLogsPane.selectedProperty().unbindBidirectional(lastVersionSetting.showLogsProperty()); showLogsPane.selectedProperty().unbindBidirectional(lastVersionSetting.showLogsProperty());
enableDebugLogOutputPane.selectedProperty().unbindBidirectional(lastVersionSetting.enableDebugLogOutputProperty()); enableDebugLogOutputPane.selectedProperty().unbindBidirectional(lastVersionSetting.enableDebugLogOutputProperty());
FXUtils.unbindEnum(cboLauncherVisibility, lastVersionSetting.launcherVisibilityProperty()); launcherVisibilityPane.valueProperty().unbindBidirectional(lastVersionSetting.launcherVisibilityProperty());
FXUtils.unbindEnum(cboProcessPriority, lastVersionSetting.processPriorityProperty()); processPriorityPane.valueProperty().unbindBidirectional(lastVersionSetting.processPriorityProperty());
lastVersionSetting.usesGlobalProperty().removeListener(usesGlobalListener); lastVersionSetting.usesGlobalProperty().removeListener(usesGlobalListener);
lastVersionSetting.javaVersionTypeProperty().removeListener(javaListener); lastVersionSetting.javaVersionTypeProperty().removeListener(javaListener);
@@ -572,8 +548,8 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
chkFullscreen.selectedProperty().bindBidirectional(versionSetting.fullscreenProperty()); chkFullscreen.selectedProperty().bindBidirectional(versionSetting.fullscreenProperty());
showLogsPane.selectedProperty().bindBidirectional(versionSetting.showLogsProperty()); showLogsPane.selectedProperty().bindBidirectional(versionSetting.showLogsProperty());
enableDebugLogOutputPane.selectedProperty().bindBidirectional(versionSetting.enableDebugLogOutputProperty()); enableDebugLogOutputPane.selectedProperty().bindBidirectional(versionSetting.enableDebugLogOutputProperty());
FXUtils.bindEnum(cboLauncherVisibility, versionSetting.launcherVisibilityProperty()); launcherVisibilityPane.valueProperty().bindBidirectional(versionSetting.launcherVisibilityProperty());
FXUtils.bindEnum(cboProcessPriority, versionSetting.processPriorityProperty()); processPriorityPane.valueProperty().bindBidirectional(versionSetting.processPriorityProperty());
if (versionId != null) if (versionId != null)
enableSpecificSettings.set(!versionSetting.isUsesGlobal()); enableSpecificSettings.set(!versionSetting.isUsesGlobal());

View File

@@ -19,7 +19,6 @@ package org.jackhuang.hmcl.ui.versions;
import com.github.steveice10.opennbt.tag.builtin.*; import com.github.steveice10.opennbt.tag.builtin.*;
import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXComboBox;
import com.jfoenix.controls.JFXTextField; import com.jfoenix.controls.JFXTextField;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections; 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.setTitle(i18n("world.info.allow_cheats"));
allowCheatsButton.setDisable(worldManagePage.isReadOnly()); allowCheatsButton.setDisable(worldManagePage.isReadOnly());
@@ -252,7 +251,7 @@ public final class WorldInfoPage extends SpinnerPane {
checkTagAndSetListener(tag, allowCheatsButton); checkTagAndSetListener(tag, allowCheatsButton);
} }
OptionToggleButton generateFeaturesButton = new OptionToggleButton(); LineToggleButton generateFeaturesButton = new LineToggleButton();
{ {
generateFeaturesButton.setTitle(i18n("world.info.generate_features")); generateFeaturesButton.setTitle(i18n("world.info.generate_features"));
generateFeaturesButton.setDisable(worldManagePage.isReadOnly()); generateFeaturesButton.setDisable(worldManagePage.isReadOnly());
@@ -261,35 +260,32 @@ public final class WorldInfoPage extends SpinnerPane {
checkTagAndSetListener(tag, generateFeaturesButton); checkTagAndSetListener(tag, generateFeaturesButton);
} }
BorderPane difficultyPane = new BorderPane(); LineSelectButton<Difficulty> difficultyButton = new LineSelectButton<>();
{ {
setLeftLabel(difficultyPane, "world.info.difficulty"); difficultyButton.setTitle(i18n("world.info.difficulty"));
difficultyButton.setDisable(worldManagePage.isReadOnly());
JFXComboBox<Difficulty> difficultyBox = new JFXComboBox<>(Difficulty.items); difficultyButton.setItems(Difficulty.items);
difficultyBox.setDisable(worldManagePage.isReadOnly());
BorderPane.setAlignment(difficultyBox, Pos.CENTER_RIGHT);
difficultyPane.setRight(difficultyBox);
Tag tag = dataTag.get("Difficulty"); Tag tag = dataTag.get("Difficulty");
if (tag instanceof ByteTag byteTag) { if (tag instanceof ByteTag byteTag) {
Difficulty difficulty = Difficulty.of(byteTag.getValue()); Difficulty difficulty = Difficulty.of(byteTag.getValue());
if (difficulty != null) { if (difficulty != null) {
difficultyBox.setValue(difficulty); difficultyButton.setValue(difficulty);
difficultyBox.valueProperty().addListener((o, oldValue, newValue) -> { difficultyButton.valueProperty().addListener((o, oldValue, newValue) -> {
if (newValue != null) { if (newValue != null) {
byteTag.setValue((byte) newValue.ordinal()); byteTag.setValue((byte) newValue.ordinal());
saveLevelDat(); saveLevelDat();
} }
}); });
} else { } else {
difficultyBox.setDisable(true); difficultyButton.setDisable(true);
} }
} else { } else {
difficultyBox.setDisable(true); difficultyButton.setDisable(true);
} }
} }
OptionToggleButton difficultyLockPane = new OptionToggleButton(); LineToggleButton difficultyLockPane = new LineToggleButton();
{ {
difficultyLockPane.setTitle(i18n("world.info.difficulty_lock")); difficultyLockPane.setTitle(i18n("world.info.difficulty_lock"));
difficultyLockPane.setDisable(worldManagePage.isReadOnly()); difficultyLockPane.setDisable(worldManagePage.isReadOnly());
@@ -300,7 +296,7 @@ public final class WorldInfoPage extends SpinnerPane {
basicInfo.getContent().setAll( basicInfo.getContent().setAll(
worldNamePane, gameVersionPane, iconPane, seedPane, lastPlayedPane, timePane, worldNamePane, gameVersionPane, iconPane, seedPane, lastPlayedPane, timePane,
allowCheatsButton, generateFeaturesButton, difficultyPane, difficultyLockPane); allowCheatsButton, generateFeaturesButton, difficultyButton, difficultyLockPane);
rootPane.getChildren().addAll(ComponentList.createComponentListTitle(i18n("world.info.basic")), basicInfo); 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<GameType> playerGameTypeButton = new LineSelectButton<>();
{ {
setLeftLabel(playerGameTypePane, "world.info.player.game_type"); playerGameTypeButton.setTitle(i18n("world.info.player.game_type"));
playerGameTypeButton.setDisable(worldManagePage.isReadOnly());
JFXComboBox<GameType> gameTypeBox = new JFXComboBox<>(GameType.items); playerGameTypeButton.setItems(GameType.items);
gameTypeBox.setDisable(worldManagePage.isReadOnly());
BorderPane.setAlignment(gameTypeBox, Pos.CENTER_RIGHT);
playerGameTypePane.setRight(gameTypeBox);
Tag tag = player.get("playerGameType"); Tag tag = player.get("playerGameType");
Tag hardcoreTag = dataTag.get("hardcore"); Tag hardcoreTag = dataTag.get("hardcore");
@@ -389,8 +382,8 @@ public final class WorldInfoPage extends SpinnerPane {
if (tag instanceof IntTag intTag) { if (tag instanceof IntTag intTag) {
GameType gameType = GameType.of(intTag.getValue(), isHardcore); GameType gameType = GameType.of(intTag.getValue(), isHardcore);
if (gameType != null) { if (gameType != null) {
gameTypeBox.setValue(gameType); playerGameTypeButton.setValue(gameType);
gameTypeBox.valueProperty().addListener((o, oldValue, newValue) -> { playerGameTypeButton.valueProperty().addListener((o, oldValue, newValue) -> {
if (newValue != null) { if (newValue != null) {
if (newValue == GameType.HARDCORE) { if (newValue == GameType.HARDCORE) {
intTag.setValue(0); // survival (hardcore worlds are survival+hardcore flag) intTag.setValue(0); // survival (hardcore worlds are survival+hardcore flag)
@@ -407,10 +400,10 @@ public final class WorldInfoPage extends SpinnerPane {
} }
}); });
} else { } else {
gameTypeBox.setDisable(true); playerGameTypeButton.setDisable(true);
} }
} else { } else {
gameTypeBox.setDisable(true); playerGameTypeButton.setDisable(true);
} }
} }
@@ -458,7 +451,7 @@ public final class WorldInfoPage extends SpinnerPane {
playerInfo.getContent().setAll( playerInfo.getContent().setAll(
locationPane, lastDeathLocationPane, spawnPane, locationPane, lastDeathLocationPane, spawnPane,
playerGameTypePane, healthPane, foodLevelPane, xpLevelPane playerGameTypeButton, healthPane, foodLevelPane, xpLevelPane
); );
rootPane.getChildren().addAll(ComponentList.createComponentListTitle(i18n("world.info.player")), playerInfo); rootPane.getChildren().addAll(ComponentList.createComponentListTitle(i18n("world.info.player")), playerInfo);
@@ -489,7 +482,7 @@ public final class WorldInfoPage extends SpinnerPane {
borderPane.setRight(label); borderPane.setRight(label);
} }
private void checkTagAndSetListener(Tag tag, OptionToggleButton toggleButton) { private void checkTagAndSetListener(Tag tag, LineToggleButton toggleButton) {
if (tag instanceof ByteTag byteTag) { if (tag instanceof ByteTag byteTag) {
byte value = byteTag.getValue(); byte value = byteTag.getValue();
if (value == 0 || value == 1) { if (value == 0 || value == 1) {

View File

@@ -1857,3 +1857,35 @@
.tooltip .text { .tooltip .text {
-fx-fill: -monet-inverse-on-surface; -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;
}

View File

@@ -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.no_code=Failed to download
download.failed.refresh=Failed to fetch version list. Please click here to retry. download.failed.refresh=Failed to fetch version list. Please click here to retry.
download.game=New Game download.game=New Game
download.provider.bmclapi=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/) download.provider.bmclapi=BMCLAPI
download.provider.mojang=Official (OptiFine is provided by 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=From Official Sources
download.provider.official.desc=Latest, but may load slowly
download.provider.balanced=From Fastest Available 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=From Mirror
download.provider.mirror.desc=Fast, but may not be the latest
download.java=Downloading Java download.java=Downloading Java
download.java.override=This Java version already exists. Do you want to uninstall and reinstall it? download.java.override=This Java version already exists. Do you want to uninstall and reinstall it?
download.java.process=Java Download Process 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.precall_command.prompt=Commands to execute before the game launches
settings.advanced.process_priority=Process Priority settings.advanced.process_priority=Process Priority
settings.advanced.process_priority.low=Low 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=Below Normal
settings.advanced.process_priority.below_normal.desc=
settings.advanced.process_priority.normal=Normal 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=Above Normal
settings.advanced.process_priority.above_normal.desc=
settings.advanced.process_priority.high=High 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=Post-exit Command
settings.advanced.post_exit_command.prompt=Commands to execute after the game exits settings.advanced.post_exit_command.prompt=Commands to execute after the game exits
settings.advanced.renderer=Renderer settings.advanced.renderer=Renderer

View File

@@ -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.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.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.game=Nuevo juego
download.provider.bmclapi=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/) download.provider.bmclapi=BMCLAPI
download.provider.mojang=Oficial (OptiFine es proporcionado por 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.official=De fuentes oficiales
download.provider.balanced=De la fuente más rápida disponible download.provider.balanced=De la fuente más rápida disponible
download.provider.mirror=Desde espejo download.provider.mirror=Desde espejo

View File

@@ -295,8 +295,10 @@ download.failed=%1$s のダウンロードに失敗しました、応答コー
download.failed.empty=候補者なし。戻るにはここをクリックしてください。 download.failed.empty=候補者なし。戻るにはここをクリックしてください。
download.failed.refresh=バージョンリストをダウンロードできません。ここをクリックして再試行してください。 download.failed.refresh=バージョンリストをダウンロードできません。ここをクリックして再試行してください。
download.game=ゲームのダウンロード download.game=ゲームのダウンロード
download.provider.bmclapi=BMCLAPI(ミラーソース) download.provider.bmclapi=BMCLAPI
download.provider.mojang=MojangOptiFineはBMCLAPIにより提供されます 推奨 download.provider.bmclapi.desc=ミラーソース
download.provider.mojang=Mojang
download.provider.mojang.desc=OptiFineはBMCLAPIにより提供されます
download.provider.official=公式ソースからロード download.provider.official=公式ソースからロード
download.provider.balanced=mcbbsのソースからロード download.provider.balanced=mcbbsのソースからロード
download.provider.mirror=ミラーソースからロード download.provider.mirror=ミラーソースからロード

View File

@@ -334,11 +334,16 @@ download.failed.empty=[無可裝之版,點此返]\n(君可求助於右上之
download.failed.no_code=引之未成 download.failed.no_code=引之未成
download.failed.refresh=[載版列未成,點此重試]\n(君可求助於右上之鈕) download.failed.refresh=[載版列未成,點此重試]\n(君可求助於右上之鈕)
download.game=新戲 download.game=新戲
download.provider.bmclapi=BMCLAPI (bangbang93https://bmclapi2.bangbang93.com) download.provider.bmclapi=BMCLAPI
download.provider.mojang=官版 (Optifine 猶取諸 BMCLAPI 也) download.provider.bmclapi.desc=bangbang93https://bmclapi2.bangbang93.com
download.provider.official=官版 (至新,容有艱) download.provider.mojang=官版
download.provider.balanced=先其快者 (均,容有舊) download.provider.mojang.desc=Optifine 猶取諸 BMCLAPI 也
download.provider.mirror=先別源 (至快,容有舊) 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=引爪哇
download.java.override=爪哇是版既現,除而復裝諸? download.java.override=爪哇是版既現,除而復裝諸?
download.java.process=引爪哇 download.java.process=引爪哇
@@ -1066,11 +1071,16 @@ settings.advanced.no_jvm_args=無添爪哇虛機之本通弦
settings.advanced.precall_command=令於戲前 settings.advanced.precall_command=令於戲前
settings.advanced.precall_command.prompt=將前戲啟而行 settings.advanced.precall_command.prompt=將前戲啟而行
settings.advanced.process_priority=進程之次 settings.advanced.process_priority=進程之次
settings.advanced.process_priority.low= (節戲所佔,容有滯焉) settings.advanced.process_priority.low=
settings.advanced.process_priority.below_normal=略低 (節戲所佔,容有滯焉) settings.advanced.process_priority.low.desc=節戲所佔,容有滯焉
settings.advanced.process_priority.normal=中 (權衡) settings.advanced.process_priority.below_normal=略低
settings.advanced.process_priority.above_normal=略高 (以先礦藝之作動而後餘者也) settings.advanced.process_priority.below_normal.desc=節戲所佔,容有滯焉
settings.advanced.process_priority.high=高 (以先礦藝之作動而後餘者也) 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=令於戲訖
settings.advanced.post_exit_command.prompt=將後戲訖而行 settings.advanced.post_exit_command.prompt=將後戲訖而行
settings.advanced.renderer=繪器 settings.advanced.renderer=繪器

View File

@@ -335,8 +335,10 @@ download.failed.empty=Версий нет. Нажмите здесь, чтобы
download.failed.no_code=Не удалось скачать download.failed.no_code=Не удалось скачать
download.failed.refresh=Не удалось получить список версий. Нажмите здесь, чтобы повторить попытку. download.failed.refresh=Не удалось получить список версий. Нажмите здесь, чтобы повторить попытку.
download.game=Новая игра download.game=Новая игра
download.provider.bmclapi=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/) download.provider.bmclapi=BMCLAPI
download.provider.mojang=Официальный (OptiFine предоставляется 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=Из официальных источников
download.provider.balanced=Из самых быстрых доступных download.provider.balanced=Из самых быстрых доступных
download.provider.mirror=Из зеркала download.provider.mirror=Из зеркала

View File

@@ -334,8 +334,10 @@ download.failed.empty=Немає доступних версій. Натисні
download.failed.no_code=Не вдалося завантажити download.failed.no_code=Не вдалося завантажити
download.failed.refresh=Не вдалося отримати список версій. Натисніть тут, щоб повторити спробу. download.failed.refresh=Не вдалося отримати список версій. Натисніть тут, щоб повторити спробу.
download.game=Нова гра download.game=Нова гра
download.provider.bmclapi=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/) download.provider.bmclapi=BMCLAPI
download.provider.mojang=Офіційне (OptiFine надається 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=З офіційних джерел
download.provider.balanced=З найшвидшого доступного download.provider.balanced=З найшвидшого доступного
download.provider.mirror=З дзеркала download.provider.mirror=З дзеркала

View File

@@ -351,11 +351,16 @@ download.failed.empty=沒有可供安裝的版本,按一下此處返回。
download.failed.no_code=下載失敗 download.failed.no_code=下載失敗
download.failed.refresh=載入版本清單失敗,按一下此處重試。 download.failed.refresh=載入版本清單失敗,按一下此處重試。
download.game=新遊戲 download.game=新遊戲
download.provider.bmclapi=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/) download.provider.bmclapi=BMCLAPI
download.provider.mojang=官方伺服器 (OptiFine 由 BMCLAPI 提供) download.provider.bmclapi.desc=bangbang93, https://bmclapi2.bangbang93.com/
download.provider.official=盡量使用官方源 (最新,但可能載入慢) download.provider.mojang=官方伺服器
download.provider.balanced=選取載入速度快的下載源 (平衡,但可能不是最新) download.provider.mojang.desc=OptiFine 由 BMCLAPI 提供
download.provider.mirror=盡量使用鏡像源 (載入快,但可能不是最新) 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=下載 Java
download.java.override=此 Java 版本已經存在,是否移除並重新安裝? download.java.override=此 Java 版本已經存在,是否移除並重新安裝?
download.java.process=下載 Java download.java.process=下載 Java
@@ -1123,11 +1128,16 @@ settings.advanced.no_optimizing_jvm_args=不自動新增 Java 虛擬機最佳化
settings.advanced.precall_command=遊戲啟動前執行指令 settings.advanced.precall_command=遊戲啟動前執行指令
settings.advanced.precall_command.prompt=將在遊戲啟動前呼叫使用 settings.advanced.precall_command.prompt=將在遊戲啟動前呼叫使用
settings.advanced.process_priority=處理程式優先度 settings.advanced.process_priority=處理程式優先度
settings.advanced.process_priority.low= (節省遊戲占用資源,可能會造成遊戲卡頓) settings.advanced.process_priority.low=
settings.advanced.process_priority.below_normal=較低 (節省遊戲占用資源,可能會造成遊戲卡頓) settings.advanced.process_priority.low.desc=節省遊戲占用資源,可能會造成遊戲卡頓
settings.advanced.process_priority.normal=中 (平衡) settings.advanced.process_priority.below_normal=較低
settings.advanced.process_priority.above_normal=較高 (優先保證遊戲執行,但可能會導致其他程式卡頓) settings.advanced.process_priority.below_normal.desc=節省遊戲占用資源,可能會造成遊戲卡頓
settings.advanced.process_priority.high=高 (優先保證遊戲執行,但可能會導致其他程式卡頓) 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=遊戲結束後執行指令
settings.advanced.post_exit_command.prompt=將在遊戲結束後呼叫使用 settings.advanced.post_exit_command.prompt=將在遊戲結束後呼叫使用
settings.advanced.renderer=繪製器 settings.advanced.renderer=繪製器

View File

@@ -353,11 +353,16 @@ download.failed.empty=[没有可供安装的版本,点击此处返回]\n(你
download.failed.no_code=下载失败 download.failed.no_code=下载失败
download.failed.refresh=[加载版本列表失败,点击此处重试]\n(你可以点击右上角帮助按钮进行求助) download.failed.refresh=[加载版本列表失败,点击此处重试]\n(你可以点击右上角帮助按钮进行求助)
download.game=新游戏 download.game=新游戏
download.provider.bmclapi=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com) download.provider.bmclapi=BMCLAPI
download.provider.mojang=官方 (OptiFine 自动安装使用 BMCLAPI 下载源) download.provider.bmclapi.desc=bangbang93, https://bmclapi2.bangbang93.com
download.provider.official=尽量使用官方源 (最新,但可能加载慢) download.provider.mojang=官方
download.provider.balanced=选择加载速度快的下载源 (平衡,但可能不是最新) download.provider.mojang.desc=OptiFine 自动安装使用 BMCLAPI 下载源
download.provider.mirror=尽量使用镜像源 (加载快,但可能不是最新) 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=下载 Java
download.java.override=此 Java 版本已经存在,是否卸载并重新安装? download.java.override=此 Java 版本已经存在,是否卸载并重新安装?
download.java.process=下载 Java download.java.process=下载 Java
@@ -1127,11 +1132,16 @@ settings.advanced.no_optimizing_jvm_args=不自动添加 Java 虚拟机优化参
settings.advanced.precall_command=游戏启动前执行命令 settings.advanced.precall_command=游戏启动前执行命令
settings.advanced.precall_command.prompt=将在游戏启动前调用 settings.advanced.precall_command.prompt=将在游戏启动前调用
settings.advanced.process_priority=进程优先级 settings.advanced.process_priority=进程优先级
settings.advanced.process_priority.low= (节省游戏占用资源,可能会造成游戏卡顿) settings.advanced.process_priority.low=
settings.advanced.process_priority.below_normal=较低 (节省游戏占用资源,可能会造成游戏卡顿) settings.advanced.process_priority.low.desc=节省游戏占用资源,可能会造成游戏卡顿
settings.advanced.process_priority.normal=中 (平衡) settings.advanced.process_priority.below_normal=较低
settings.advanced.process_priority.above_normal=较高 (优先保证游戏运行,但可能会导致其他程序卡顿) settings.advanced.process_priority.below_normal.desc=节省游戏占用资源,可能会造成游戏卡顿
settings.advanced.process_priority.high=高 (优先保证游戏运行,但可能会导致其他程序卡顿) 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=游戏结束后执行命令
settings.advanced.post_exit_command.prompt=将在游戏结束后调用 settings.advanced.post_exit_command.prompt=将在游戏结束后调用
settings.advanced.renderer=渲染器 settings.advanced.renderer=渲染器