创建通用的 LineButton 控件并简化 TerracottaControllerPage (#5395)
This commit is contained in:
@@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
* 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.*;
|
||||||
|
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 LineButton extends LineButtonBase {
|
||||||
|
private static final String DEFAULT_STYLE_CLASS = "line-button";
|
||||||
|
|
||||||
|
public static LineButton createNavigationButton() {
|
||||||
|
var button = new LineButton();
|
||||||
|
button.setRightIcon(SVG.ARROW_FORWARD);
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LineButton() {
|
||||||
|
getStyleClass().add(DEFAULT_STYLE_CLASS);
|
||||||
|
|
||||||
|
root.setMouseTransparent(true);
|
||||||
|
|
||||||
|
FXUtils.onClicked(container, this::fire);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fire() {
|
||||||
|
fireEvent(new ActionEvent());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObjectProperty<EventHandler<ActionEvent>> onAction;
|
||||||
|
|
||||||
|
public ObjectProperty<EventHandler<ActionEvent>> onActionProperty() {
|
||||||
|
if (onAction == null) {
|
||||||
|
onAction = new ObjectPropertyBase<>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void invalidated() {
|
||||||
|
setEventHandler(ActionEvent.ACTION, get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getBean() {
|
||||||
|
return LineButton.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "onAction";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return onAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EventHandler<ActionEvent> getOnAction() {
|
||||||
|
return onActionProperty().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnAction(EventHandler<ActionEvent> value) {
|
||||||
|
onActionProperty().set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private StringProperty message;
|
||||||
|
|
||||||
|
public StringProperty messageProperty() {
|
||||||
|
if (message == null) {
|
||||||
|
message = new StringPropertyBase() {
|
||||||
|
@Override
|
||||||
|
public Object getBean() {
|
||||||
|
return LineButton.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "message";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void invalidated() {
|
||||||
|
updateRight();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message == null ? "" : message.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
messageProperty().set(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SVG rightIcon;
|
||||||
|
private double rightIconSize;
|
||||||
|
|
||||||
|
public void setRightIcon(SVG rightIcon) {
|
||||||
|
setRightIcon(rightIcon, SVG.DEFAULT_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRightIcon(SVG rightIcon, double size) {
|
||||||
|
this.rightIcon = rightIcon;
|
||||||
|
this.rightIconSize = size;
|
||||||
|
updateRight();
|
||||||
|
}
|
||||||
|
|
||||||
|
//region Right
|
||||||
|
|
||||||
|
private Label messageLabel;
|
||||||
|
private Node rightIconNode;
|
||||||
|
private SVG currentRightIcon;
|
||||||
|
private double currentRightIconSize;
|
||||||
|
|
||||||
|
private void updateRight() {
|
||||||
|
HBox right;
|
||||||
|
if (root.getRight() instanceof HBox box) {
|
||||||
|
right = box;
|
||||||
|
} else {
|
||||||
|
right = new HBox();
|
||||||
|
right.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
root.setRight(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
right.getChildren().clear();
|
||||||
|
|
||||||
|
String message = getMessage();
|
||||||
|
if (message != null && !message.isEmpty()) {
|
||||||
|
if (messageLabel == null) {
|
||||||
|
messageLabel = new Label();
|
||||||
|
messageLabel.getStyleClass().add("subtitle");
|
||||||
|
}
|
||||||
|
messageLabel.setText(message);
|
||||||
|
right.getChildren().add(messageLabel);
|
||||||
|
} else if (messageLabel != null) {
|
||||||
|
messageLabel.setText("");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightIcon != currentRightIcon || rightIconSize != currentRightIconSize) {
|
||||||
|
if (rightIcon != null) {
|
||||||
|
rightIconNode = rightIcon.createIcon(rightIconSize);
|
||||||
|
HBox.setMargin(rightIconNode, new Insets(0, 8, 0, 8));
|
||||||
|
} else {
|
||||||
|
rightIconNode = null;
|
||||||
|
}
|
||||||
|
currentRightIcon = rightIcon;
|
||||||
|
currentRightIconSize = rightIconSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightIconNode != null)
|
||||||
|
right.getChildren().add(rightIconNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
}
|
||||||
@@ -19,19 +19,15 @@ package org.jackhuang.hmcl.ui.construct;
|
|||||||
|
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
import javafx.beans.property.StringPropertyBase;
|
|
||||||
import javafx.geometry.Insets;
|
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.Region;
|
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
|
|
||||||
/// @author Glavo
|
/// @author Glavo
|
||||||
public abstract class LineButtonBase extends StackPane implements NoPaddingComponent {
|
public abstract class LineButtonBase extends StackPane implements LineComponent {
|
||||||
|
|
||||||
private static final Insets PADDING = new Insets(8, 8, 8, 16);
|
private static final String DEFAULT_STYLE_CLASS = "line-button-base";
|
||||||
|
|
||||||
protected final BorderPane root;
|
protected final BorderPane root;
|
||||||
protected final RipplerContainer container;
|
protected final RipplerContainer container;
|
||||||
@@ -39,9 +35,11 @@ public abstract class LineButtonBase extends StackPane implements NoPaddingCompo
|
|||||||
private final Label titleLabel;
|
private final Label titleLabel;
|
||||||
|
|
||||||
public LineButtonBase() {
|
public LineButtonBase() {
|
||||||
|
this.getStyleClass().addAll(LineComponent.DEFAULT_STYLE_CLASS, LineButtonBase.DEFAULT_STYLE_CLASS);
|
||||||
|
|
||||||
this.root = new BorderPane();
|
this.root = new BorderPane();
|
||||||
root.setPadding(PADDING);
|
root.setPadding(LineComponent.PADDING);
|
||||||
root.setMinHeight(48);
|
root.setMinHeight(LineComponent.MIN_HEIGHT);
|
||||||
|
|
||||||
this.container = new RipplerContainer(root);
|
this.container = new RipplerContainer(root);
|
||||||
this.getChildren().setAll(container);
|
this.getChildren().setAll(container);
|
||||||
@@ -53,59 +51,32 @@ public abstract class LineButtonBase extends StackPane implements NoPaddingCompo
|
|||||||
titleLabel.getStyleClass().add("title");
|
titleLabel.getStyleClass().add("title");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BorderPane getRoot() {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
private final StringProperty title = new SimpleStringProperty(this, "title");
|
private final StringProperty title = new SimpleStringProperty(this, "title");
|
||||||
|
|
||||||
|
@Override
|
||||||
public StringProperty titleProperty() {
|
public StringProperty titleProperty() {
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTitle() {
|
|
||||||
return titleProperty().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTitle(String title) {
|
|
||||||
this.titleProperty().set(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
private StringProperty subtitle;
|
private StringProperty subtitle;
|
||||||
|
|
||||||
|
@Override
|
||||||
public StringProperty subtitleProperty() {
|
public StringProperty subtitleProperty() {
|
||||||
if (subtitle == null) {
|
if (subtitle == null) {
|
||||||
subtitle = new StringPropertyBase() {
|
subtitle = new LineComponent.SubtitleProperty() {
|
||||||
private VBox left;
|
|
||||||
private Label subtitleLabel;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public LineButtonBase getBean() {
|
||||||
return "subtitle";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getBean() {
|
|
||||||
return LineButtonBase.this;
|
return LineButtonBase.this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void invalidated() {
|
public Label getTitleLabel() {
|
||||||
String subtitle = get();
|
return titleLabel;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -113,11 +84,4 @@ public abstract class LineButtonBase extends StackPane implements NoPaddingCompo
|
|||||||
return subtitle;
|
return subtitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSubtitle() {
|
|
||||||
return subtitleProperty().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSubtitle(String subtitle) {
|
|
||||||
subtitleProperty().set(subtitle);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* 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.StringProperty;
|
||||||
|
import javafx.beans.property.StringPropertyBase;
|
||||||
|
import javafx.css.PseudoClass;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import org.jackhuang.hmcl.ui.SVG;
|
||||||
|
|
||||||
|
/// @author Glavo
|
||||||
|
public interface LineComponent extends NoPaddingComponent {
|
||||||
|
String DEFAULT_STYLE_CLASS = "line-component";
|
||||||
|
|
||||||
|
PseudoClass PSEUDO_LARGER_TITLE = PseudoClass.getPseudoClass("large-title");
|
||||||
|
|
||||||
|
Insets PADDING = new Insets(8, 8, 8, 16);
|
||||||
|
Insets ICON_MARGIN = new Insets(0, 16, 0, 0);
|
||||||
|
double MIN_HEIGHT = 48.0;
|
||||||
|
|
||||||
|
private Node self() {
|
||||||
|
return (Node) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BorderPane getRoot();
|
||||||
|
|
||||||
|
StringProperty titleProperty();
|
||||||
|
|
||||||
|
default String getTitle() {
|
||||||
|
return titleProperty().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
default void setTitle(String title) {
|
||||||
|
titleProperty().set(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class SubtitleProperty extends StringPropertyBase {
|
||||||
|
private VBox left;
|
||||||
|
private Label subtitleLabel;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "subtitle";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract LineComponent getBean();
|
||||||
|
|
||||||
|
public abstract Label getTitleLabel();
|
||||||
|
|
||||||
|
@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(getTitleLabel(), subtitleLabel);
|
||||||
|
getBean().getRoot().setCenter(left);
|
||||||
|
} else if (left != null) {
|
||||||
|
subtitleLabel.setText(null);
|
||||||
|
getBean().getRoot().setCenter(getTitleLabel());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StringProperty subtitleProperty();
|
||||||
|
|
||||||
|
default String getSubtitle() {
|
||||||
|
return subtitleProperty().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
default void setSubtitle(String subtitle) {
|
||||||
|
subtitleProperty().set(subtitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void setLeftIcon(Image icon) {
|
||||||
|
setLeftIcon(icon, -1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void setLeftIcon(Image icon, double size) {
|
||||||
|
ImageView imageView = new ImageView(icon);
|
||||||
|
imageView.getStyleClass().add("left-icon");
|
||||||
|
if (size > 0) {
|
||||||
|
imageView.setFitWidth(size);
|
||||||
|
imageView.setFitHeight(size);
|
||||||
|
imageView.setPreserveRatio(true);
|
||||||
|
imageView.setSmooth(true);
|
||||||
|
}
|
||||||
|
imageView.setMouseTransparent(true);
|
||||||
|
BorderPane.setAlignment(imageView, Pos.CENTER);
|
||||||
|
BorderPane.setMargin(imageView, ICON_MARGIN);
|
||||||
|
getRoot().setLeft(imageView);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void setLeftIcon(SVG svg) {
|
||||||
|
setLeftIcon(svg, SVG.DEFAULT_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void setLeftIcon(SVG svg, double size) {
|
||||||
|
Node node = svg.createIcon(size);
|
||||||
|
node.getStyleClass().add("left-icon");
|
||||||
|
node.setMouseTransparent(true);
|
||||||
|
BorderPane.setAlignment(node, Pos.CENTER);
|
||||||
|
BorderPane.setMargin(node, ICON_MARGIN);
|
||||||
|
getRoot().setLeft(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void setLargeTitle(boolean largeTitle) {
|
||||||
|
self().pseudoClassStateChanged(PSEUDO_LARGER_TITLE, largeTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -19,24 +19,21 @@ package org.jackhuang.hmcl.ui.construct;
|
|||||||
|
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
import javafx.beans.property.StringPropertyBase;
|
|
||||||
import javafx.geometry.Insets;
|
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.Region;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
|
|
||||||
/// @author Glavo
|
/// @author Glavo
|
||||||
public class LinePane extends BorderPane implements NoPaddingComponent {
|
public class LinePane extends BorderPane implements LineComponent {
|
||||||
|
private static final String DEFAULT_STYLE_CLASS = "line-pane";
|
||||||
private static final Insets PADDING = new Insets(8, 8, 8, 16);
|
|
||||||
|
|
||||||
private final Label titleLabel;
|
private final Label titleLabel;
|
||||||
|
|
||||||
public LinePane() {
|
public LinePane() {
|
||||||
this.setPadding(PADDING);
|
this.getStyleClass().addAll(LineComponent.DEFAULT_STYLE_CLASS, LinePane.DEFAULT_STYLE_CLASS);
|
||||||
this.setMinHeight(48);
|
|
||||||
|
this.setPadding(LineComponent.PADDING);
|
||||||
|
this.setMinHeight(LineComponent.MIN_HEIGHT);
|
||||||
|
|
||||||
this.titleLabel = new Label();
|
this.titleLabel = new Label();
|
||||||
this.setCenter(titleLabel);
|
this.setCenter(titleLabel);
|
||||||
@@ -45,71 +42,36 @@ public class LinePane extends BorderPane implements NoPaddingComponent {
|
|||||||
titleLabel.getStyleClass().add("title");
|
titleLabel.getStyleClass().add("title");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BorderPane getRoot() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private final StringProperty title = new SimpleStringProperty(this, "title");
|
private final StringProperty title = new SimpleStringProperty(this, "title");
|
||||||
|
|
||||||
|
@Override
|
||||||
public StringProperty titleProperty() {
|
public StringProperty titleProperty() {
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTitle() {
|
|
||||||
return titleProperty().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTitle(String title) {
|
|
||||||
this.titleProperty().set(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
private StringProperty subtitle;
|
private StringProperty subtitle;
|
||||||
|
|
||||||
|
@Override
|
||||||
public StringProperty subtitleProperty() {
|
public StringProperty subtitleProperty() {
|
||||||
if (subtitle == null) {
|
if (subtitle == null) {
|
||||||
subtitle = new StringPropertyBase() {
|
subtitle = new LineComponent.SubtitleProperty() {
|
||||||
private VBox left;
|
|
||||||
private Label subtitleLabel;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public LinePane getBean() {
|
||||||
return "subtitle";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getBean() {
|
|
||||||
return LinePane.this;
|
return LinePane.this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void invalidated() {
|
public Label getTitleLabel() {
|
||||||
String subtitle = get();
|
return titleLabel;
|
||||||
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);
|
|
||||||
LinePane.this.setCenter(left);
|
|
||||||
} else if (left != null) {
|
|
||||||
subtitleLabel.setText(null);
|
|
||||||
LinePane.this.setCenter(titleLabel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return subtitle;
|
return subtitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSubtitle() {
|
|
||||||
return subtitleProperty().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSubtitle(String subtitle) {
|
|
||||||
subtitleProperty().set(subtitle);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ import javafx.beans.property.DoubleProperty;
|
|||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.SimpleDoubleProperty;
|
import javafx.beans.property.SimpleDoubleProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
|
||||||
import javafx.beans.property.StringProperty;
|
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.beans.value.WeakChangeListener;
|
import javafx.beans.value.WeakChangeListener;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
@@ -34,13 +32,8 @@ import javafx.scene.Cursor;
|
|||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.ScrollPane;
|
import javafx.scene.control.ScrollPane;
|
||||||
import javafx.scene.image.Image;
|
|
||||||
import javafx.scene.image.ImageView;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.Priority;
|
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import javafx.scene.text.TextFlow;
|
import javafx.scene.text.TextFlow;
|
||||||
import org.jackhuang.hmcl.game.LauncherHelper;
|
import org.jackhuang.hmcl.game.LauncherHelper;
|
||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
@@ -57,13 +50,7 @@ import org.jackhuang.hmcl.ui.SVG;
|
|||||||
import org.jackhuang.hmcl.ui.WeakListenerHolder;
|
import org.jackhuang.hmcl.ui.WeakListenerHolder;
|
||||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||||
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
||||||
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.HintPane;
|
|
||||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
|
||||||
import org.jackhuang.hmcl.ui.construct.RipplerContainer;
|
|
||||||
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
|
||||||
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
|
||||||
import org.jackhuang.hmcl.ui.versions.Versions;
|
import org.jackhuang.hmcl.ui.versions.Versions;
|
||||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||||
import org.jackhuang.hmcl.util.i18n.LocaleUtils;
|
import org.jackhuang.hmcl.util.i18n.LocaleUtils;
|
||||||
@@ -163,12 +150,12 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
body.getStyleClass().add("terracotta-hint");
|
body.getStyleClass().add("terracotta-hint");
|
||||||
body.setLineSpacing(4);
|
body.setLineSpacing(4);
|
||||||
|
|
||||||
LineButton download = LineButton.of();
|
var download = createLargeTitleLineButton();
|
||||||
download.setLeftImage(FXUtils.newBuiltinImage("/assets/img/terracotta.png"));
|
download.setLeftIcon(FXUtils.newBuiltinImage("/assets/img/terracotta.png"));
|
||||||
download.setTitle(i18n(String.format("terracotta.status.uninitialized.%s.title", fork)));
|
download.setTitle(i18n(String.format("terracotta.status.uninitialized.%s.title", fork)));
|
||||||
download.setSubtitle(i18n("terracotta.status.uninitialized.desc"));
|
download.setSubtitle(i18n("terracotta.status.uninitialized.desc"));
|
||||||
download.setRightIcon(SVG.ARROW_FORWARD);
|
download.setRightIcon(SVG.ARROW_FORWARD, ICON_SIZE);
|
||||||
FXUtils.onClicked(download, () -> {
|
download.setOnAction(event -> {
|
||||||
TerracottaState.Preparing s = TerracottaManager.download();
|
TerracottaState.Preparing s = TerracottaManager.download();
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
UI_STATE.set(s);
|
UI_STATE.set(s);
|
||||||
@@ -207,12 +194,12 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
flow.getStyleClass().add("terracotta-hint");
|
flow.getStyleClass().add("terracotta-hint");
|
||||||
flow.setLineSpacing(4);
|
flow.setLineSpacing(4);
|
||||||
|
|
||||||
LineButton host = LineButton.of();
|
var host = createLargeTitleLineButton();
|
||||||
host.setLeftIcon(SVG.HOST);
|
host.setLeftIcon(SVG.HOST, ICON_SIZE);
|
||||||
host.setTitle(i18n("terracotta.status.waiting.host.title"));
|
host.setTitle(i18n("terracotta.status.waiting.host.title"));
|
||||||
host.setSubtitle(i18n("terracotta.status.waiting.host.desc"));
|
host.setSubtitle(i18n("terracotta.status.waiting.host.desc"));
|
||||||
host.setRightIcon(SVG.ARROW_FORWARD);
|
host.setRightIcon(SVG.ARROW_FORWARD, ICON_SIZE);
|
||||||
FXUtils.onClicked(host, () -> {
|
host.setOnAction(event -> {
|
||||||
if (LauncherHelper.countMangedProcesses() >= 1) {
|
if (LauncherHelper.countMangedProcesses() >= 1) {
|
||||||
TerracottaState.HostScanning s1 = TerracottaManager.setScanning();
|
TerracottaState.HostScanning s1 = TerracottaManager.setScanning();
|
||||||
if (s1 != null) {
|
if (s1 != null) {
|
||||||
@@ -239,12 +226,12 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
LineButton guest = LineButton.of();
|
var guest = createLargeTitleLineButton();
|
||||||
guest.setLeftIcon(SVG.ADD_CIRCLE);
|
guest.setLeftIcon(SVG.ADD_CIRCLE, ICON_SIZE);
|
||||||
guest.setTitle(i18n("terracotta.status.waiting.guest.title"));
|
guest.setTitle(i18n("terracotta.status.waiting.guest.title"));
|
||||||
guest.setSubtitle(i18n("terracotta.status.waiting.guest.desc"));
|
guest.setSubtitle(i18n("terracotta.status.waiting.guest.desc"));
|
||||||
guest.setRightIcon(SVG.ARROW_FORWARD);
|
guest.setRightIcon(SVG.ARROW_FORWARD, ICON_SIZE);
|
||||||
FXUtils.onClicked(guest, () -> {
|
guest.setOnAction(event -> {
|
||||||
Controllers.prompt(i18n("terracotta.status.waiting.guest.prompt.title"), (code, handler) -> {
|
Controllers.prompt(i18n("terracotta.status.waiting.guest.prompt.title"), (code, handler) -> {
|
||||||
Task<TerracottaState.GuestConnecting> task = TerracottaManager.setGuesting(code);
|
Task<TerracottaState.GuestConnecting> task = TerracottaManager.setGuesting(code);
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
@@ -263,11 +250,11 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (ThreadLocalRandom.current().nextDouble() < 0.02D) {
|
if (ThreadLocalRandom.current().nextDouble() < 0.02D) {
|
||||||
LineButton feedback = LineButton.of();
|
var feedback = createLargeTitleLineButton();
|
||||||
feedback.setLeftIcon(SVG.FEEDBACK);
|
feedback.setLeftIcon(SVG.FEEDBACK, ICON_SIZE);
|
||||||
feedback.setTitle(i18n("terracotta.feedback.title"));
|
feedback.setTitle(i18n("terracotta.feedback.title"));
|
||||||
feedback.setSubtitle(i18n("terracotta.feedback.desc"));
|
feedback.setSubtitle(i18n("terracotta.feedback.desc"));
|
||||||
feedback.setRightIcon(SVG.OPEN_IN_NEW);
|
feedback.setRightIcon(SVG.OPEN_IN_NEW, ICON_SIZE);
|
||||||
FXUtils.onClicked(feedback, () -> FXUtils.openLink(TerracottaMetadata.FEEDBACK_LINK));
|
FXUtils.onClicked(feedback, () -> FXUtils.openLink(TerracottaMetadata.FEEDBACK_LINK));
|
||||||
|
|
||||||
nodesProperty.setAll(flow, host, guest, feedback);
|
nodesProperty.setAll(flow, host, guest, feedback);
|
||||||
@@ -282,11 +269,11 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
body.getStyleClass().add("terracotta-hint");
|
body.getStyleClass().add("terracotta-hint");
|
||||||
body.setLineSpacing(4);
|
body.setLineSpacing(4);
|
||||||
|
|
||||||
LineButton room = LineButton.of();
|
var room = createLargeTitleLineButton();
|
||||||
room.setLeftIcon(SVG.ARROW_BACK);
|
room.setLeftIcon(SVG.ARROW_BACK, ICON_SIZE);
|
||||||
room.setTitle(i18n("terracotta.back"));
|
room.setTitle(i18n("terracotta.back"));
|
||||||
room.setSubtitle(i18n("terracotta.status.scanning.back"));
|
room.setSubtitle(i18n("terracotta.status.scanning.back"));
|
||||||
FXUtils.onClicked(room, () -> {
|
room.setOnAction(event -> {
|
||||||
TerracottaState.Waiting s = TerracottaManager.setWaiting();
|
TerracottaState.Waiting s = TerracottaManager.setWaiting();
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
UI_STATE.set(s);
|
UI_STATE.set(s);
|
||||||
@@ -298,11 +285,11 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
statusProperty.set(i18n("terracotta.status.host_starting"));
|
statusProperty.set(i18n("terracotta.status.host_starting"));
|
||||||
progressProperty.set(-1);
|
progressProperty.set(-1);
|
||||||
|
|
||||||
LineButton room = LineButton.of();
|
var room = createLargeTitleLineButton();
|
||||||
room.setLeftIcon(SVG.ARROW_BACK);
|
room.setLeftIcon(SVG.ARROW_BACK, ICON_SIZE);
|
||||||
room.setTitle(i18n("terracotta.back"));
|
room.setTitle(i18n("terracotta.back"));
|
||||||
room.setSubtitle(i18n("terracotta.status.host_starting.back"));
|
room.setSubtitle(i18n("terracotta.status.host_starting.back"));
|
||||||
FXUtils.onClicked(room, () -> {
|
room.setOnAction(event -> {
|
||||||
TerracottaState.Waiting s = TerracottaManager.setWaiting();
|
TerracottaState.Waiting s = TerracottaManager.setWaiting();
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
UI_STATE.set(s);
|
UI_STATE.set(s);
|
||||||
@@ -342,17 +329,17 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
code.setCursor(Cursor.HAND);
|
code.setCursor(Cursor.HAND);
|
||||||
FXUtils.onClicked(code, () -> copyCode(cs));
|
FXUtils.onClicked(code, () -> copyCode(cs));
|
||||||
|
|
||||||
LineButton copy = LineButton.of();
|
var copy = createLargeTitleLineButton();
|
||||||
copy.setLeftIcon(SVG.CONTENT_COPY);
|
copy.setLeftIcon(SVG.CONTENT_COPY, ICON_SIZE);
|
||||||
copy.setTitle(i18n("terracotta.status.host_ok.code.copy"));
|
copy.setTitle(i18n("terracotta.status.host_ok.code.copy"));
|
||||||
copy.setSubtitle(i18n("terracotta.status.host_ok.code.desc"));
|
copy.setSubtitle(i18n("terracotta.status.host_ok.code.desc"));
|
||||||
FXUtils.onClicked(copy, () -> copyCode(cs));
|
FXUtils.onClicked(copy, () -> copyCode(cs));
|
||||||
|
|
||||||
LineButton back = LineButton.of();
|
var back = createLargeTitleLineButton();
|
||||||
back.setLeftIcon(SVG.ARROW_BACK);
|
back.setLeftIcon(SVG.ARROW_BACK, ICON_SIZE);
|
||||||
back.setTitle(i18n("terracotta.back"));
|
back.setTitle(i18n("terracotta.back"));
|
||||||
back.setSubtitle(i18n("terracotta.status.host_ok.back"));
|
back.setSubtitle(i18n("terracotta.status.host_ok.back"));
|
||||||
FXUtils.onClicked(back, () -> {
|
back.setOnAction(event -> {
|
||||||
TerracottaState.Waiting s = TerracottaManager.setWaiting();
|
TerracottaState.Waiting s = TerracottaManager.setWaiting();
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
UI_STATE.set(s);
|
UI_STATE.set(s);
|
||||||
@@ -369,11 +356,11 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
statusProperty.set(i18n("terracotta.status.guest_starting"));
|
statusProperty.set(i18n("terracotta.status.guest_starting"));
|
||||||
progressProperty.set(-1);
|
progressProperty.set(-1);
|
||||||
|
|
||||||
LineButton room = LineButton.of();
|
var room = createLargeTitleLineButton();
|
||||||
room.setLeftIcon(SVG.ARROW_BACK);
|
room.setLeftIcon(SVG.ARROW_BACK, ICON_SIZE);
|
||||||
room.setTitle(i18n("terracotta.back"));
|
room.setTitle(i18n("terracotta.back"));
|
||||||
room.setSubtitle(i18n("terracotta.status.guest_starting.back"));
|
room.setSubtitle(i18n("terracotta.status.guest_starting.back"));
|
||||||
FXUtils.onClicked(room, () -> {
|
room.setOnAction(event -> {
|
||||||
TerracottaState.Waiting s = TerracottaManager.setWaiting();
|
TerracottaState.Waiting s = TerracottaManager.setWaiting();
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
UI_STATE.set(s);
|
UI_STATE.set(s);
|
||||||
@@ -384,12 +371,12 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
if (state instanceof TerracottaState.GuestStarting) {
|
if (state instanceof TerracottaState.GuestStarting) {
|
||||||
TerracottaState.GuestStarting.Difficulty difficulty = ((TerracottaState.GuestStarting) state).getDifficulty();
|
TerracottaState.GuestStarting.Difficulty difficulty = ((TerracottaState.GuestStarting) state).getDifficulty();
|
||||||
if (difficulty != null && difficulty != TerracottaState.GuestStarting.Difficulty.UNKNOWN) {
|
if (difficulty != null && difficulty != TerracottaState.GuestStarting.Difficulty.UNKNOWN) {
|
||||||
LineButton info = LineButton.of();
|
var info = createLargeTitleLineButton();
|
||||||
info.setLeftIcon(switch (difficulty) {
|
info.setLeftIcon(switch (difficulty) {
|
||||||
case UNKNOWN -> throw new AssertionError();
|
case UNKNOWN -> throw new AssertionError();
|
||||||
case EASIEST, SIMPLE -> SVG.INFO;
|
case EASIEST, SIMPLE -> SVG.INFO;
|
||||||
case MEDIUM, TOUGH -> SVG.WARNING;
|
case MEDIUM, TOUGH -> SVG.WARNING;
|
||||||
});
|
}, ICON_SIZE);
|
||||||
|
|
||||||
String difficultyID = difficulty.name().toLowerCase(Locale.ROOT);
|
String difficultyID = difficulty.name().toLowerCase(Locale.ROOT);
|
||||||
info.setTitle(i18n(String.format("terracotta.difficulty.%s", difficultyID)));
|
info.setTitle(i18n(String.format("terracotta.difficulty.%s", difficultyID)));
|
||||||
@@ -412,15 +399,15 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
statusProperty.set(i18n("terracotta.status.guest_ok"));
|
statusProperty.set(i18n("terracotta.status.guest_ok"));
|
||||||
progressProperty.set(1);
|
progressProperty.set(1);
|
||||||
|
|
||||||
LineButton tutorial = LineButton.of();
|
var tutorial = createLargeTitleLineButton();
|
||||||
tutorial.setTitle(i18n("terracotta.status.guest_ok.title"));
|
tutorial.setTitle(i18n("terracotta.status.guest_ok.title"));
|
||||||
tutorial.setSubtitle(i18n("terracotta.status.guest_ok.desc", guestOK.getUrl()));
|
tutorial.setSubtitle(i18n("terracotta.status.guest_ok.desc", guestOK.getUrl()));
|
||||||
|
|
||||||
LineButton back = LineButton.of();
|
var back = createLargeTitleLineButton();
|
||||||
back.setLeftIcon(SVG.ARROW_BACK);
|
back.setLeftIcon(SVG.ARROW_BACK, ICON_SIZE);
|
||||||
back.setTitle(i18n("terracotta.back"));
|
back.setTitle(i18n("terracotta.back"));
|
||||||
back.setSubtitle(i18n("terracotta.status.guest_ok.back"));
|
back.setSubtitle(i18n("terracotta.status.guest_ok.back"));
|
||||||
FXUtils.onClicked(back, () -> {
|
back.setOnAction(event -> {
|
||||||
TerracottaState.Waiting s = TerracottaManager.setWaiting();
|
TerracottaState.Waiting s = TerracottaManager.setWaiting();
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
UI_STATE.set(s);
|
UI_STATE.set(s);
|
||||||
@@ -438,11 +425,11 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
progressProperty.set(1);
|
progressProperty.set(1);
|
||||||
nodesProperty.setAll();
|
nodesProperty.setAll();
|
||||||
|
|
||||||
LineButton back = LineButton.of();
|
var back = createLargeTitleLineButton();
|
||||||
back.setLeftIcon(SVG.ARROW_BACK);
|
back.setLeftIcon(SVG.ARROW_BACK, ICON_SIZE);
|
||||||
back.setTitle(i18n("terracotta.back"));
|
back.setTitle(i18n("terracotta.back"));
|
||||||
back.setSubtitle(i18n("terracotta.status.exception.back"));
|
back.setSubtitle(i18n("terracotta.status.exception.back"));
|
||||||
FXUtils.onClicked(back, () -> {
|
back.setOnAction(event -> {
|
||||||
TerracottaState.Waiting s = TerracottaManager.setWaiting();
|
TerracottaState.Waiting s = TerracottaManager.setWaiting();
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
UI_STATE.set(s);
|
UI_STATE.set(s);
|
||||||
@@ -450,8 +437,8 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
});
|
});
|
||||||
|
|
||||||
SpinnerPane exportLog = new SpinnerPane();
|
SpinnerPane exportLog = new SpinnerPane();
|
||||||
LineButton exportLogInner = LineButton.of();
|
var exportLogInner = createLargeTitleLineButton();
|
||||||
exportLogInner.setLeftIcon(SVG.OUTPUT);
|
exportLogInner.setLeftIcon(SVG.OUTPUT, ICON_SIZE);
|
||||||
exportLogInner.setTitle(i18n("terracotta.export_log"));
|
exportLogInner.setTitle(i18n("terracotta.export_log"));
|
||||||
exportLogInner.setSubtitle(i18n("terracotta.export_log.desc"));
|
exportLogInner.setSubtitle(i18n("terracotta.export_log.desc"));
|
||||||
exportLog.setContent(exportLogInner);
|
exportLog.setContent(exportLogInner);
|
||||||
@@ -459,7 +446,7 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
// FIXME: SpinnerPane loses its content width in loading state.
|
// FIXME: SpinnerPane loses its content width in loading state.
|
||||||
exportLog.minHeightProperty().bind(back.heightProperty());
|
exportLog.minHeightProperty().bind(back.heightProperty());
|
||||||
|
|
||||||
FXUtils.onClicked(exportLogInner, () -> {
|
exportLogInner.setOnAction(event -> {
|
||||||
exportLog.setLoading(true);
|
exportLog.setLoading(true);
|
||||||
|
|
||||||
TerracottaManager.exportLogs().thenAcceptAsync(Schedulers.io(), data -> {
|
TerracottaManager.exportLogs().thenAcceptAsync(Schedulers.io(), data -> {
|
||||||
@@ -493,11 +480,11 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
progressProperty.set(1);
|
progressProperty.set(1);
|
||||||
|
|
||||||
if (fatal.isRecoverable()) {
|
if (fatal.isRecoverable()) {
|
||||||
LineButton retry = LineButton.of();
|
var retry = createLargeTitleLineButton();
|
||||||
retry.setLeftIcon(SVG.RESTORE);
|
retry.setLeftIcon(SVG.RESTORE, ICON_SIZE);
|
||||||
retry.setTitle(i18n("terracotta.status.fatal.retry"));
|
retry.setTitle(i18n("terracotta.status.fatal.retry"));
|
||||||
retry.setSubtitle(message);
|
retry.setSubtitle(message);
|
||||||
FXUtils.onClicked(retry, () -> {
|
retry.setOnAction(event -> {
|
||||||
TerracottaState s = TerracottaManager.recover();
|
TerracottaState s = TerracottaManager.recover();
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
UI_STATE.set(s);
|
UI_STATE.set(s);
|
||||||
@@ -534,7 +521,8 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
children.add(statusPane);
|
children.add(statusPane);
|
||||||
children.addAll(nodesProperty);
|
children.addAll(nodesProperty);
|
||||||
}
|
}
|
||||||
|
// Prevent the shadow of components from being clipped
|
||||||
|
StackPane.setMargin(components, new Insets(0, 0, 5, 0));
|
||||||
transition.setContent(components, ContainerAnimations.SLIDE_UP_FADE_IN);
|
transition.setContent(components, ContainerAnimations.SLIDE_UP_FADE_IN);
|
||||||
};
|
};
|
||||||
listener.changed(UI_STATE, null, UI_STATE.get());
|
listener.changed(UI_STATE, null, UI_STATE.get());
|
||||||
@@ -561,34 +549,27 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
|
|
||||||
private ComponentSublist getThirdPartyDownloadNodes() {
|
private ComponentSublist getThirdPartyDownloadNodes() {
|
||||||
ComponentSublist locals = new ComponentSublist();
|
ComponentSublist locals = new ComponentSublist();
|
||||||
locals.setComponentPadding(false);
|
|
||||||
|
|
||||||
LineButton header = LineButton.of(false);
|
var header = new LinePane();
|
||||||
header.setLeftImage(FXUtils.newBuiltinImage("/assets/img/terracotta.png"));
|
header.setLargeTitle(true);
|
||||||
|
header.setPadding(Insets.EMPTY);
|
||||||
|
header.setMinHeight(LinePane.USE_COMPUTED_SIZE);
|
||||||
|
header.setMouseTransparent(true);
|
||||||
|
header.setLeftIcon(FXUtils.newBuiltinImage("/assets/img/terracotta.png"));
|
||||||
header.setTitle(i18n("terracotta.from_local.title"));
|
header.setTitle(i18n("terracotta.from_local.title"));
|
||||||
header.setSubtitle(i18n("terracotta.from_local.desc"));
|
header.setSubtitle(i18n("terracotta.from_local.desc"));
|
||||||
locals.setHeaderLeft(header);
|
locals.setHeaderLeft(header);
|
||||||
|
|
||||||
for (TerracottaMetadata.Link link : TerracottaMetadata.PACKAGE_LINKS) {
|
for (TerracottaMetadata.Link link : TerracottaMetadata.PACKAGE_LINKS) {
|
||||||
HBox node = new HBox();
|
LineButton item = new LineButton();
|
||||||
node.setAlignment(Pos.CENTER_LEFT);
|
item.setRightIcon(SVG.OPEN_IN_NEW);
|
||||||
node.setPadding(new Insets(10, 16, 10, 16));
|
item.setTitle(link.description().getText(I18n.getLocale().getCandidateLocales()));
|
||||||
|
item.setOnAction(event -> Controllers.dialog(
|
||||||
Label description = new Label(link.description().getText(I18n.getLocale().getCandidateLocales()));
|
|
||||||
HBox placeholder = new HBox();
|
|
||||||
HBox.setHgrow(placeholder, Priority.ALWAYS);
|
|
||||||
Node icon = SVG.OPEN_IN_NEW.createIcon(16);
|
|
||||||
node.getChildren().setAll(description, placeholder, icon);
|
|
||||||
|
|
||||||
String url = link.link();
|
|
||||||
RipplerContainer container = new RipplerContainer(node);
|
|
||||||
container.setOnMouseClicked(ev -> Controllers.dialog(
|
|
||||||
i18n("terracotta.from_local.guide", TerracottaMetadata.PACKAGE_NAME),
|
i18n("terracotta.from_local.guide", TerracottaMetadata.PACKAGE_NAME),
|
||||||
i18n("message.info"), MessageDialogPane.MessageType.INFO,
|
i18n("message.info"), MessageDialogPane.MessageType.INFO,
|
||||||
() -> FXUtils.openLink(url)
|
() -> FXUtils.openLink(link.link())
|
||||||
));
|
));
|
||||||
ComponentList.setNoPadding(container);
|
locals.getContent().add(item);
|
||||||
locals.getContent().add(container);
|
|
||||||
}
|
}
|
||||||
return locals;
|
return locals;
|
||||||
}
|
}
|
||||||
@@ -597,103 +578,12 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
FXUtils.copyText(code, i18n("terracotta.status.host_ok.code.copy.toast"));
|
FXUtils.copyText(code, i18n("terracotta.status.host_ok.code.copy.toast"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class LineButton extends RipplerContainer {
|
private static final double ICON_SIZE = 28;
|
||||||
private final WeakListenerHolder holder = new WeakListenerHolder();
|
|
||||||
|
|
||||||
private final ObjectProperty<Node> left = new SimpleObjectProperty<>(this, "left");
|
private static LineButton createLargeTitleLineButton() {
|
||||||
private final ObjectProperty<Node> right = new SimpleObjectProperty<>(this, "right");
|
var lineButton = new LineButton();
|
||||||
private final StringProperty title = new SimpleStringProperty(this, "title", "");
|
lineButton.setLargeTitle(true);
|
||||||
private final StringProperty subTitle = new SimpleStringProperty(this, "subTitle", "");
|
return lineButton;
|
||||||
|
|
||||||
public static LineButton of() {
|
|
||||||
return of(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static LineButton of(boolean padding) {
|
|
||||||
HBox container = new HBox();
|
|
||||||
if (padding) {
|
|
||||||
container.setPadding(new Insets(10, 16, 10, 16));
|
|
||||||
}
|
|
||||||
container.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
container.setCursor(Cursor.HAND);
|
|
||||||
container.setSpacing(16);
|
|
||||||
|
|
||||||
LineButton button = new LineButton(container);
|
|
||||||
VBox spacing = new VBox();
|
|
||||||
HBox.setHgrow(spacing, Priority.ALWAYS);
|
|
||||||
button.holder.add(FXUtils.observeWeak(() -> {
|
|
||||||
List<Node> nodes = new ArrayList<>(4);
|
|
||||||
Node left = button.left.get();
|
|
||||||
if (left != null) {
|
|
||||||
nodes.add(left);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// FIXME: It's sucked to have the following TwoLineListItem-liked logic whose subtitle is a TextFlow.
|
|
||||||
VBox middle = new VBox();
|
|
||||||
middle.getStyleClass().add("two-line-list-item");
|
|
||||||
middle.setMouseTransparent(true);
|
|
||||||
{
|
|
||||||
HBox firstLine = new HBox();
|
|
||||||
firstLine.getStyleClass().add("first-line");
|
|
||||||
{
|
|
||||||
Label lblTitle = new Label(button.title.get());
|
|
||||||
lblTitle.getStyleClass().add("title");
|
|
||||||
firstLine.getChildren().setAll(lblTitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
HBox secondLine = new HBox();
|
|
||||||
secondLine.getStyleClass().add("second-line");
|
|
||||||
{
|
|
||||||
Text text = new Text(button.subTitle.get());
|
|
||||||
|
|
||||||
TextFlow lblSubtitle = new TextFlow(text);
|
|
||||||
lblSubtitle.getStyleClass().add("subtitle");
|
|
||||||
secondLine.getChildren().setAll(lblSubtitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
middle.getChildren().setAll(firstLine, secondLine);
|
|
||||||
}
|
|
||||||
nodes.add(middle);
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes.add(spacing);
|
|
||||||
|
|
||||||
Node right = button.right.get();
|
|
||||||
if (right != null) {
|
|
||||||
nodes.add(right);
|
|
||||||
}
|
|
||||||
|
|
||||||
container.getChildren().setAll(nodes);
|
|
||||||
}, button.title, button.subTitle, button.left, button.right));
|
|
||||||
ComponentList.setNoPadding(button);
|
|
||||||
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
private LineButton(Node container) {
|
|
||||||
super(container);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTitle(String title) {
|
|
||||||
this.title.set(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSubtitle(String subtitle) {
|
|
||||||
this.subTitle.set(subtitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLeftImage(Image left) {
|
|
||||||
this.left.set(new ImageView(left));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLeftIcon(SVG left) {
|
|
||||||
this.left.set(left.createIcon(28));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRightIcon(SVG right) {
|
|
||||||
this.right.set(right.createIcon(28));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class PlayerProfileUI extends VBox {
|
private static final class PlayerProfileUI extends VBox {
|
||||||
|
|||||||
@@ -424,7 +424,7 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
LineNavigationButton showAdvancedSettingPane = new LineNavigationButton();
|
var showAdvancedSettingPane = LineButton.createNavigationButton();
|
||||||
showAdvancedSettingPane.setTitle(i18n("settings.advanced"));
|
showAdvancedSettingPane.setTitle(i18n("settings.advanced"));
|
||||||
showAdvancedSettingPane.setOnAction(event -> {
|
showAdvancedSettingPane.setOnAction(event -> {
|
||||||
if (lastVersionSetting != null) {
|
if (lastVersionSetting != null) {
|
||||||
|
|||||||
@@ -1860,10 +1860,29 @@
|
|||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* *
|
* *
|
||||||
* Line Button *
|
* Line Component *
|
||||||
* *
|
* *
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
|
.line-component .title {
|
||||||
|
-fx-text-fill: -monet-on-surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-component:large-title .title {
|
||||||
|
-fx-font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-component .subtitle {
|
||||||
|
-fx-text-fill: -monet-on-surface-variant;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-button .svg {
|
||||||
|
-fx-opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-button .svg:disabled {
|
||||||
|
-fx-opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
.line-select-button .svg {
|
.line-select-button .svg {
|
||||||
-fx-opacity: 1;
|
-fx-opacity: 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user