基于 HBox 重新实现 LineComponent (#5416)

This commit is contained in:
Glavo
2026-02-04 20:47:45 +08:00
committed by GitHub
parent 19079ac123
commit c7b9b73292
12 changed files with 436 additions and 436 deletions

View File

@@ -45,9 +45,11 @@ final class ComponentSublistWrapper extends VBox implements NoPaddingComponent {
ComponentSublistWrapper(ComponentSublist sublist) { ComponentSublistWrapper(ComponentSublist sublist) {
boolean noPadding = !sublist.hasComponentPadding(); boolean noPadding = !sublist.hasComponentPadding();
this.getStyleClass().add("options-sublist-wrapper");
Node expandIcon = SVG.KEYBOARD_ARROW_DOWN.createIcon(20); Node expandIcon = SVG.KEYBOARD_ARROW_DOWN.createIcon(20);
expandIcon.getStyleClass().add("expand-icon");
expandIcon.setMouseTransparent(true); expandIcon.setMouseTransparent(true);
HBox.setMargin(expandIcon, new Insets(0, 8, 0, 8));
VBox labelVBox = new VBox(); VBox labelVBox = new VBox();
labelVBox.setMouseTransparent(true); labelVBox.setMouseTransparent(true);
@@ -72,7 +74,7 @@ final class ComponentSublistWrapper extends VBox implements NoPaddingComponent {
} }
HBox header = new HBox(); HBox header = new HBox();
header.setSpacing(16); header.setSpacing(12);
header.getChildren().add(labelVBox); header.getChildren().add(labelVBox);
header.setPadding(new Insets(10, 16, 10, 16)); header.setPadding(new Insets(10, 16, 10, 16));
header.setAlignment(Pos.CENTER_LEFT); header.setAlignment(Pos.CENTER_LEFT);

View File

@@ -20,34 +20,27 @@ package org.jackhuang.hmcl.ui.construct;
import javafx.beans.property.*; import javafx.beans.property.*;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.event.EventHandler; import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.layout.HBox; import javafx.scene.control.OverrunStyle;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.SVG;
/// @author Glavo /// @author Glavo
public final class LineButton extends LineButtonBase { public class LineButton extends LineButtonBase {
private static final String DEFAULT_STYLE_CLASS = "line-button"; private static final String DEFAULT_STYLE_CLASS = "line-button";
private static final int IDX_TRAILING_TEXT = IDX_TRAILING;
private static final int IDX_TRAILING_ICON = IDX_TRAILING + 1;
public static LineButton createNavigationButton() { public static LineButton createNavigationButton() {
var button = new LineButton(); var button = new LineButton();
button.setRightIcon(SVG.ARROW_FORWARD); button.setTrailingIcon(SVG.ARROW_FORWARD);
return button; return button;
} }
public LineButton() { public LineButton() {
getStyleClass().add(DEFAULT_STYLE_CLASS); getStyleClass().add(DEFAULT_STYLE_CLASS);
container.setMouseTransparent(true);
root.setMouseTransparent(true);
FXUtils.onClicked(container, this::fire);
}
public void fire() {
fireEvent(new ActionEvent());
} }
private ObjectProperty<EventHandler<ActionEvent>> onAction; private ObjectProperty<EventHandler<ActionEvent>> onAction;
@@ -55,12 +48,6 @@ public final class LineButton extends LineButtonBase {
public ObjectProperty<EventHandler<ActionEvent>> onActionProperty() { public ObjectProperty<EventHandler<ActionEvent>> onActionProperty() {
if (onAction == null) { if (onAction == null) {
onAction = new ObjectPropertyBase<>() { onAction = new ObjectPropertyBase<>() {
@Override
protected void invalidated() {
setEventHandler(ActionEvent.ACTION, get());
}
@Override @Override
public Object getBean() { public Object getBean() {
return LineButton.this; return LineButton.this;
@@ -70,6 +57,11 @@ public final class LineButton extends LineButtonBase {
public String getName() { public String getName() {
return "onAction"; return "onAction";
} }
@Override
protected void invalidated() {
setEventHandler(ActionEvent.ACTION, get());
}
}; };
} }
return onAction; return onAction;
@@ -83,11 +75,13 @@ public final class LineButton extends LineButtonBase {
onActionProperty().set(value); onActionProperty().set(value);
} }
private StringProperty message; private StringProperty trailingText;
public StringProperty trailingTextProperty() {
if (trailingText == null) {
trailingText = new StringPropertyBase() {
private Label trailingTextLabel;
public StringProperty messageProperty() {
if (message == null) {
message = new StringPropertyBase() {
@Override @Override
public Object getBean() { public Object getBean() {
return LineButton.this; return LineButton.this;
@@ -95,85 +89,78 @@ public final class LineButton extends LineButtonBase {
@Override @Override
public String getName() { public String getName() {
return "message"; return "trailingText";
} }
@Override @Override
protected void invalidated() { protected void invalidated() {
updateRight(); String message = get();
if (message != null && !message.isEmpty()) {
if (trailingTextLabel == null) {
trailingTextLabel = new Label();
trailingTextLabel.getStyleClass().add("trailing-label");
trailingTextLabel.setTextOverrun(OverrunStyle.CENTER_ELLIPSIS);
}
trailingTextLabel.setText(message);
setNode(IDX_TRAILING_TEXT, trailingTextLabel);
} else if (trailingTextLabel != null) {
trailingTextLabel.setText("");
setNode(IDX_TRAILING_TEXT, null);
}
} }
}; };
} }
return message; return trailingText;
} }
public String getMessage() { public String getTrailingText() {
return message == null ? "" : message.get(); return trailingText != null ? trailingText.get() : null;
} }
public void setMessage(String message) { public void setTrailingText(String trailingText) {
messageProperty().set(message); trailingTextProperty().set(trailingText);
} }
private SVG rightIcon; private ObjectProperty<Node> trailingIcon;
private double rightIconSize;
public void setRightIcon(SVG rightIcon) { public ObjectProperty<Node> trailingIconProperty() {
setRightIcon(rightIcon, SVG.DEFAULT_SIZE); if (trailingIcon == null)
trailingIcon = new ObjectPropertyBase<>() {
@Override
public Object getBean() {
return LineButton.this;
}
@Override
public String getName() {
return "trailingIcon";
}
@Override
protected void invalidated() {
setNode(IDX_TRAILING_ICON, get());
}
};
return trailingIcon;
} }
public void setRightIcon(SVG rightIcon, double size) { public Node getTrailingIcon() {
this.rightIcon = rightIcon; return trailingIcon != null ? trailingIcon.get() : null;
this.rightIconSize = size;
updateRight();
} }
//region Right public void setTrailingIcon(Node trailingIcon) {
trailingIconProperty().set(trailingIcon);
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 public void setTrailingIcon(SVG rightIcon) {
setTrailingIcon(rightIcon, 20);
}
public void setTrailingIcon(SVG rightIcon, double size) {
Node rightIconNode = rightIcon.createIcon(size);
rightIconNode.getStyleClass().add("trailing-icon");
setTrailingIcon(rightIconNode);
}
} }

View File

@@ -17,71 +17,25 @@
*/ */
package org.jackhuang.hmcl.ui.construct; package org.jackhuang.hmcl.ui.construct;
import javafx.beans.property.SimpleStringProperty; import javafx.event.ActionEvent;
import javafx.beans.property.StringProperty; import org.jackhuang.hmcl.ui.FXUtils;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
/// @author Glavo /// @author Glavo
public abstract class LineButtonBase extends StackPane implements LineComponent { public abstract class LineButtonBase extends LineComponent {
private static final String DEFAULT_STYLE_CLASS = "line-button-base"; private static final String DEFAULT_STYLE_CLASS = "line-button-base";
protected final BorderPane root; protected final RipplerContainer ripplerContainer;
protected final RipplerContainer container;
private final Label titleLabel;
public LineButtonBase() { public LineButtonBase() {
this.getStyleClass().addAll(LineComponent.DEFAULT_STYLE_CLASS, LineButtonBase.DEFAULT_STYLE_CLASS); this.getStyleClass().addAll(LineButtonBase.DEFAULT_STYLE_CLASS);
this.root = new BorderPane(); this.ripplerContainer = new RipplerContainer(container);
root.setPadding(LineComponent.PADDING); FXUtils.onClicked(this, this::fire);
root.setMinHeight(LineComponent.MIN_HEIGHT);
this.container = new RipplerContainer(root); this.getChildren().setAll(ripplerContainer);
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");
} }
@Override public void fire() {
public BorderPane getRoot() { fireEvent(new ActionEvent());
return root;
} }
private final StringProperty title = new SimpleStringProperty(this, "title");
@Override
public StringProperty titleProperty() {
return title;
}
private StringProperty subtitle;
@Override
public StringProperty subtitleProperty() {
if (subtitle == null) {
subtitle = new LineComponent.SubtitleProperty() {
@Override
public LineButtonBase getBean() {
return LineButtonBase.this;
}
@Override
public Label getTitleLabel() {
return titleLabel;
}
};
}
return subtitle;
}
} }

View File

@@ -17,101 +17,203 @@
*/ */
package org.jackhuang.hmcl.ui.construct; package org.jackhuang.hmcl.ui.construct;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ObjectPropertyBase;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import javafx.beans.property.StringPropertyBase; import javafx.beans.property.StringPropertyBase;
import javafx.css.PseudoClass; import javafx.css.PseudoClass;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.control.Label; import javafx.scene.control.*;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.*;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.SVG;
import java.util.Arrays;
import java.util.Objects;
/// @author Glavo /// @author Glavo
public interface LineComponent extends NoPaddingComponent { public abstract class LineComponent extends StackPane implements NoPaddingComponent {
String DEFAULT_STYLE_CLASS = "line-component"; private static final String DEFAULT_STYLE_CLASS = "line-component";
private static final double MIN_HEIGHT = 48.0;
private static final PseudoClass PSEUDO_LARGER_TITLE = PseudoClass.getPseudoClass("large-title");
PseudoClass PSEUDO_LARGER_TITLE = PseudoClass.getPseudoClass("large-title"); protected static final int IDX_LEADING = 0;
protected static final int IDX_TITLE = 1;
protected static final int IDX_TRAILING = 2;
Insets PADDING = new Insets(8, 8, 8, 16); public static final double SPACING = 12;
Insets ICON_MARGIN = new Insets(0, 16, 0, 0); public static final double DEFAULT_ICON_SIZE = 20;
double MIN_HEIGHT = 48.0;
private Node self() { public static void setMargin(Node child, Insets value) {
return (Node) this; HBox.setMargin(child, value);
} }
BorderPane getRoot(); protected final HBox container;
StringProperty titleProperty(); private final Label titleLabel;
private final VBox titleContainer;
default String getTitle() { public LineComponent() {
return titleProperty().get(); this.getStyleClass().add(DEFAULT_STYLE_CLASS);
this.setMinHeight(MIN_HEIGHT);
this.container = new HBox(SPACING);
container.getStyleClass().add("line-component-container");
container.setAlignment(Pos.CENTER_LEFT);
this.titleLabel = new Label();
titleLabel.getStyleClass().add("title-label");
titleLabel.setMinWidth(Region.USE_PREF_SIZE);
this.titleContainer = new VBox(titleLabel);
titleContainer.getStyleClass().add("title-container");
titleContainer.setMouseTransparent(true);
titleContainer.setAlignment(Pos.CENTER_LEFT);
titleContainer.minWidthProperty().bind(titleLabel.prefWidthProperty());
HBox.setHgrow(titleContainer, Priority.ALWAYS);
this.setNode(IDX_TITLE, titleContainer);
this.getChildren().setAll(container);
} }
default void setTitle(String title) { private Node[] nodes = new Node[2];
titleProperty().set(title);
protected void setNode(int idx, Node node) {
if (nodes.length <= idx)
nodes = Arrays.copyOf(nodes, idx + 1);
if (nodes[idx] != node) {
nodes[idx] = node;
container.getChildren().setAll(Arrays.stream(nodes).filter(Objects::nonNull).toArray(Node[]::new));
}
} }
abstract class SubtitleProperty extends StringPropertyBase { public void setLargeTitle(boolean largeTitle) {
private VBox left; pseudoClassStateChanged(PSEUDO_LARGER_TITLE, largeTitle);
private Label subtitleLabel; }
private final StringProperty title = new StringPropertyBase() {
@Override
public Object getBean() {
return LineComponent.this;
}
@Override @Override
public String getName() { public String getName() {
return "subtitle"; return "title";
} }
@Override
public abstract LineComponent getBean();
public abstract Label getTitleLabel();
@Override @Override
protected void invalidated() { protected void invalidated() {
String subtitle = get(); titleLabel.setText(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());
}
} }
};
public final StringProperty titleProperty() {
return title;
} }
StringProperty subtitleProperty(); public String getTitle() {
return titleProperty().get();
default String getSubtitle() {
return subtitleProperty().get();
} }
default void setSubtitle(String subtitle) { public void setTitle(String title) {
titleProperty().set(title);
}
private StringProperty subtitle;
public final StringProperty subtitleProperty() {
if (subtitle == null) {
subtitle = new StringPropertyBase() {
private Label subtitleLabel;
@Override
public String getName() {
return "subtitle";
}
@Override
public Object getBean() {
return LineComponent.this;
}
@Override
protected void invalidated() {
String subtitle = get();
if (subtitle != null && !subtitle.isEmpty()) {
if (subtitleLabel == null) {
subtitleLabel = new Label();
subtitleLabel.setWrapText(true);
subtitleLabel.setMinHeight(Region.USE_PREF_SIZE);
subtitleLabel.getStyleClass().add("subtitle-label");
}
subtitleLabel.setText(subtitle);
if (titleContainer.getChildren().size() == 1)
titleContainer.getChildren().add(subtitleLabel);
} else if (subtitleLabel != null) {
subtitleLabel.setText(null);
if (titleContainer.getChildren().size() == 2)
titleContainer.getChildren().remove(1);
}
}
};
}
return subtitle;
}
public final String getSubtitle() {
return subtitle != null ? subtitle.get() : null;
}
public final void setSubtitle(String subtitle) {
subtitleProperty().set(subtitle); subtitleProperty().set(subtitle);
} }
default void setLeftIcon(Image icon) { private ObjectProperty<Node> leading;
setLeftIcon(icon, -1.0);
public final ObjectProperty<Node> leadingProperty() {
if (leading == null) {
leading = new ObjectPropertyBase<>() {
@Override
public Object getBean() {
return LineComponent.this;
}
@Override
public String getName() {
return "leading";
}
@Override
protected void invalidated() {
setNode(IDX_LEADING, get());
}
};
}
return leading;
} }
default void setLeftIcon(Image icon, double size) { public final Node getLeading() {
ImageView imageView = new ImageView(icon); return leadingProperty().get();
imageView.getStyleClass().add("left-icon"); }
public final void setLeading(Node node) {
leadingProperty().set(node);
}
public void setLeading(Image icon) {
setLeading(icon, -1);
}
public void setLeading(Image icon, double size) {
var imageView = new ImageView(icon);
if (size > 0) { if (size > 0) {
imageView.setFitWidth(size); imageView.setFitWidth(size);
imageView.setFitHeight(size); imageView.setFitHeight(size);
@@ -119,25 +221,18 @@ public interface LineComponent extends NoPaddingComponent {
imageView.setSmooth(true); imageView.setSmooth(true);
} }
imageView.setMouseTransparent(true); imageView.setMouseTransparent(true);
BorderPane.setAlignment(imageView, Pos.CENTER);
BorderPane.setMargin(imageView, ICON_MARGIN); setNode(IDX_LEADING, imageView);
getRoot().setLeft(imageView);
} }
default void setLeftIcon(SVG svg) { public void setLeading(SVG svg) {
setLeftIcon(svg, SVG.DEFAULT_SIZE); setLeading(svg, DEFAULT_ICON_SIZE);
} }
default void setLeftIcon(SVG svg, double size) { public void setLeading(SVG svg, double size) {
Node node = svg.createIcon(size); Node node = svg.createIcon(size);
node.getStyleClass().add("left-icon");
node.setMouseTransparent(true); node.setMouseTransparent(true);
BorderPane.setAlignment(node, Pos.CENTER); setNode(IDX_LEADING, node);
BorderPane.setMargin(node, ICON_MARGIN);
getRoot().setLeft(node);
} }
default void setLargeTitle(boolean largeTitle) {
self().pseudoClassStateChanged(PSEUDO_LARGER_TITLE, largeTitle);
}
} }

View File

@@ -17,61 +17,47 @@
*/ */
package org.jackhuang.hmcl.ui.construct; package org.jackhuang.hmcl.ui.construct;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.ObjectPropertyBase;
import javafx.geometry.Pos; import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
/// @author Glavo /// @author Glavo
public class LinePane extends BorderPane implements LineComponent { public class LinePane extends LineComponent {
private static final String DEFAULT_STYLE_CLASS = "line-pane"; private static final String DEFAULT_STYLE_CLASS = "line-pane";
private final Label titleLabel;
public LinePane() { public LinePane() {
this.getStyleClass().addAll(LineComponent.DEFAULT_STYLE_CLASS, LinePane.DEFAULT_STYLE_CLASS); this.getStyleClass().add(DEFAULT_STYLE_CLASS);
this.setPadding(LineComponent.PADDING);
this.setMinHeight(LineComponent.MIN_HEIGHT);
this.titleLabel = new Label();
this.setCenter(titleLabel);
BorderPane.setAlignment(titleLabel, Pos.CENTER_LEFT);
titleLabel.textProperty().bind(titleProperty());
titleLabel.getStyleClass().add("title");
} }
@Override private ObjectProperty<Node> right;
public BorderPane getRoot() {
return this;
}
private final StringProperty title = new SimpleStringProperty(this, "title"); public ObjectProperty<Node> rightProperty() {
if (right == null) {
@Override right = new ObjectPropertyBase<>() {
public StringProperty titleProperty() {
return title;
}
private StringProperty subtitle;
@Override
public StringProperty subtitleProperty() {
if (subtitle == null) {
subtitle = new LineComponent.SubtitleProperty() {
@Override @Override
public LinePane getBean() { public Object getBean() {
return LinePane.this; return LinePane.this;
} }
@Override @Override
public Label getTitleLabel() { public String getName() {
return titleLabel; return "right";
}
@Override
protected void invalidated() {
setNode(IDX_TRAILING, get());
} }
}; };
} }
return right;
}
return subtitle; public Node getRight() {
return rightProperty().get();
}
public void setRight(Node right) {
rightProperty().set(right);
} }
} }

View File

@@ -24,9 +24,7 @@ import javafx.beans.property.*;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.css.PseudoClass; import javafx.css.PseudoClass;
import javafx.geometry.Insets;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.input.MouseButton; import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent; import javafx.scene.input.MouseEvent;
@@ -43,7 +41,7 @@ import java.util.function.Function;
import static org.jackhuang.hmcl.ui.FXUtils.determineOptimalPopupPosition; import static org.jackhuang.hmcl.ui.FXUtils.determineOptimalPopupPosition;
/// @author Glavo /// @author Glavo
public final class LineSelectButton<T> extends LineButtonBase { public final class LineSelectButton<T> extends LineButton {
private static final String DEFAULT_STYLE_CLASS = "line-select-button"; private static final String DEFAULT_STYLE_CLASS = "line-select-button";
private static final PseudoClass SELECTED_PSEUDO_CLASS = PseudoClass.getPseudoClass("selected"); private static final PseudoClass SELECTED_PSEUDO_CLASS = PseudoClass.getPseudoClass("selected");
@@ -53,109 +51,96 @@ public final class LineSelectButton<T> extends LineButtonBase {
public LineSelectButton() { public LineSelectButton() {
this.getStyleClass().add(DEFAULT_STYLE_CLASS); this.getStyleClass().add(DEFAULT_STYLE_CLASS);
root.setMouseTransparent(true); InvalidationListener updateTrailingText = observable -> {
T value = getValue();
if (value != null) {
Function<T, String> converter = getConverter();
setTrailingText(converter != null ? converter.apply(value) : value.toString());
} else {
setTrailingText(null);
}
};
converterProperty().addListener(updateTrailingText);
valueProperty().addListener(updateTrailingText);
HBox right = new HBox(); setTrailingIcon(SVG.UNFOLD_MORE);
root.setRight(right);
{
right.setAlignment(Pos.CENTER_RIGHT);
Label valueLabel = new Label(); ripplerContainer.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
valueLabel.getStyleClass().add("subtitle"); if (event.getButton() == MouseButton.SECONDARY) {
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) if (popup != null)
popup.hide(); popup.hide();
event.consume(); event.consume();
} }
}); });
} }
@Override
public void fire() {
super.fire();
if (popup == null) {
PopupMenu popupMenu = new PopupMenu();
this.popup = new JFXPopup(popupMenu);
ripplerContainer.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-label");
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-label");
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) ->
ripplerContainer.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());
}
}
private final ObjectProperty<T> value = new SimpleObjectProperty<>(this, "value"); private final ObjectProperty<T> value = new SimpleObjectProperty<>(this, "value");
public ObjectProperty<T> valueProperty() { public ObjectProperty<T> valueProperty() {
@@ -187,9 +172,8 @@ public final class LineSelectButton<T> extends LineButtonBase {
private ObjectProperty<Function<T, String>> descriptionConverter; private ObjectProperty<Function<T, String>> descriptionConverter;
public ObjectProperty<Function<T, String>> descriptionConverterProperty() { public ObjectProperty<Function<T, String>> descriptionConverterProperty() {
if (descriptionConverter == null) { if (descriptionConverter == null)
descriptionConverter = new SimpleObjectProperty<>(this, "descriptionConverter"); descriptionConverter = new SimpleObjectProperty<>(this, "descriptionConverter");
}
return descriptionConverter; return descriptionConverter;
} }

View File

@@ -19,19 +19,16 @@ package org.jackhuang.hmcl.ui.construct;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import javafx.beans.property.StringPropertyBase; import javafx.beans.property.StringPropertyBase;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.FXUtils;
/// @author Glavo /// @author Glavo
public final class LineTextPane extends LinePane { public final class LineTextPane extends LineComponent {
private static final String DEFAULT_STYLE_CLASS = "line-label-pane"; private static final String DEFAULT_STYLE_CLASS = "line-text-pane";
public LineTextPane() { public LineTextPane() {
this.getStyleClass().add(DEFAULT_STYLE_CLASS); this.getStyleClass().addAll(DEFAULT_STYLE_CLASS);
} }
private StringProperty text; private StringProperty text;
@@ -39,8 +36,6 @@ public final class LineTextPane extends LinePane {
public StringProperty textProperty() { public StringProperty textProperty() {
if (text == null) { if (text == null) {
text = new StringPropertyBase() { text = new StringPropertyBase() {
private static final Insets LABEL_MARGIN = new Insets(0, 8, 0, 16);
private Label rightLabel; private Label rightLabel;
@Override @Override
@@ -56,19 +51,18 @@ public final class LineTextPane extends LinePane {
@Override @Override
protected void invalidated() { protected void invalidated() {
String text = get(); String text = get();
if (text == null || text.isEmpty()) { if (text != null && !text.isEmpty()) {
if (rightLabel != null)
rightLabel.setText(null);
LineTextPane.this.setRight(null);
} else {
if (rightLabel == null) { if (rightLabel == null) {
rightLabel = FXUtils.newSafeTruncatedLabel(); rightLabel = FXUtils.newSafeTruncatedLabel();
FXUtils.copyOnDoubleClick(rightLabel); FXUtils.copyOnDoubleClick(rightLabel);
BorderPane.setMargin(rightLabel, LABEL_MARGIN);
BorderPane.setAlignment(rightLabel, Pos.CENTER_RIGHT);
} }
rightLabel.setText(text); rightLabel.setText(text);
LineTextPane.this.setRight(rightLabel); setNode(IDX_TRAILING, rightLabel);
} else {
if (rightLabel != null)
rightLabel.setText(null);
setNode(IDX_TRAILING, null);
} }
} }
}; };

View File

@@ -20,25 +20,27 @@ package org.jackhuang.hmcl.ui.construct;
import com.jfoenix.controls.JFXToggleButton; import com.jfoenix.controls.JFXToggleButton;
import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Pos;
import javafx.scene.layout.BorderPane;
import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.FXUtils;
public final class LineToggleButton extends LineButtonBase { public final class LineToggleButton extends LineButtonBase {
private static final String DEFAULT_STYLE_CLASS = "line-toggle-button"; private static final String DEFAULT_STYLE_CLASS = "line-toggle-button";
private final JFXToggleButton toggleButton;
public LineToggleButton() { public LineToggleButton() {
this.getStyleClass().add(DEFAULT_STYLE_CLASS); this.getStyleClass().add(DEFAULT_STYLE_CLASS);
JFXToggleButton toggleButton = new JFXToggleButton(); this.toggleButton = new JFXToggleButton();
toggleButton.selectedProperty().bindBidirectional(selectedProperty()); toggleButton.selectedProperty().bindBidirectional(selectedProperty());
toggleButton.setSize(8); toggleButton.setSize(8);
FXUtils.setLimitHeight(toggleButton, 30); FXUtils.setLimitHeight(toggleButton, 30);
setNode(IDX_TRAILING, toggleButton);
}
BorderPane.setAlignment(toggleButton, Pos.CENTER); @Override
root.setRight(toggleButton); public void fire() {
toggleButton.fire();
FXUtils.onClicked(container, toggleButton::fire); super.fire();
} }
private final BooleanProperty selected = new SimpleBooleanProperty(this, "selected"); private final BooleanProperty selected = new SimpleBooleanProperty(this, "selected");

View File

@@ -115,7 +115,6 @@ public final class JavaInstallPage extends WizardSinglePage {
nameField = new JFXTextField(); nameField = new JFXTextField();
nameField.textProperty().bindBidirectional(control.nameProperty); nameField.textProperty().bindBidirectional(control.nameProperty);
FXUtils.setLimitWidth(nameField, 200); FXUtils.setLimitWidth(nameField, 200);
BorderPane.setAlignment(nameField, Pos.CENTER_RIGHT);
namePane.setRight(nameField); namePane.setRight(nameField);
nameField.setValidators( nameField.setValidators(
new RequiredValidator(), new RequiredValidator(),

View File

@@ -151,10 +151,10 @@ public class TerracottaControllerPage extends StackPane {
body.setLineSpacing(4); body.setLineSpacing(4);
var download = createLargeTitleLineButton(); var download = createLargeTitleLineButton();
download.setLeftIcon(FXUtils.newBuiltinImage("/assets/img/terracotta.png")); download.setLeading(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, ICON_SIZE); download.setTrailingIcon(SVG.ARROW_FORWARD, ICON_SIZE);
download.setOnAction(event -> { download.setOnAction(event -> {
TerracottaState.Preparing s = TerracottaManager.download(); TerracottaState.Preparing s = TerracottaManager.download();
if (s != null) { if (s != null) {
@@ -195,10 +195,10 @@ public class TerracottaControllerPage extends StackPane {
flow.setLineSpacing(4); flow.setLineSpacing(4);
var host = createLargeTitleLineButton(); var host = createLargeTitleLineButton();
host.setLeftIcon(SVG.HOST, ICON_SIZE); host.setLeading(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, ICON_SIZE); host.setTrailingIcon(SVG.ARROW_FORWARD, ICON_SIZE);
host.setOnAction(event -> { host.setOnAction(event -> {
if (LauncherHelper.countMangedProcesses() >= 1) { if (LauncherHelper.countMangedProcesses() >= 1) {
TerracottaState.HostScanning s1 = TerracottaManager.setScanning(); TerracottaState.HostScanning s1 = TerracottaManager.setScanning();
@@ -227,10 +227,10 @@ public class TerracottaControllerPage extends StackPane {
}); });
var guest = createLargeTitleLineButton(); var guest = createLargeTitleLineButton();
guest.setLeftIcon(SVG.ADD_CIRCLE, ICON_SIZE); guest.setLeading(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, ICON_SIZE); guest.setTrailingIcon(SVG.ARROW_FORWARD, ICON_SIZE);
guest.setOnAction(event -> { 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);
@@ -251,10 +251,10 @@ public class TerracottaControllerPage extends StackPane {
if (ThreadLocalRandom.current().nextDouble() < 0.02D) { if (ThreadLocalRandom.current().nextDouble() < 0.02D) {
var feedback = createLargeTitleLineButton(); var feedback = createLargeTitleLineButton();
feedback.setLeftIcon(SVG.FEEDBACK, ICON_SIZE); feedback.setLeading(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, ICON_SIZE); feedback.setTrailingIcon(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);
@@ -270,7 +270,7 @@ public class TerracottaControllerPage extends StackPane {
body.setLineSpacing(4); body.setLineSpacing(4);
var room = createLargeTitleLineButton(); var room = createLargeTitleLineButton();
room.setLeftIcon(SVG.ARROW_BACK, ICON_SIZE); room.setLeading(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"));
room.setOnAction(event -> { room.setOnAction(event -> {
@@ -286,7 +286,7 @@ public class TerracottaControllerPage extends StackPane {
progressProperty.set(-1); progressProperty.set(-1);
var room = createLargeTitleLineButton(); var room = createLargeTitleLineButton();
room.setLeftIcon(SVG.ARROW_BACK, ICON_SIZE); room.setLeading(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"));
room.setOnAction(event -> { room.setOnAction(event -> {
@@ -330,13 +330,13 @@ public class TerracottaControllerPage extends StackPane {
FXUtils.onClicked(code, () -> copyCode(cs)); FXUtils.onClicked(code, () -> copyCode(cs));
var copy = createLargeTitleLineButton(); var copy = createLargeTitleLineButton();
copy.setLeftIcon(SVG.CONTENT_COPY, ICON_SIZE); copy.setLeading(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));
var back = createLargeTitleLineButton(); var back = createLargeTitleLineButton();
back.setLeftIcon(SVG.ARROW_BACK, ICON_SIZE); back.setLeading(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"));
back.setOnAction(event -> { back.setOnAction(event -> {
@@ -357,7 +357,7 @@ public class TerracottaControllerPage extends StackPane {
progressProperty.set(-1); progressProperty.set(-1);
var room = createLargeTitleLineButton(); var room = createLargeTitleLineButton();
room.setLeftIcon(SVG.ARROW_BACK, ICON_SIZE); room.setLeading(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"));
room.setOnAction(event -> { room.setOnAction(event -> {
@@ -372,7 +372,7 @@ public class TerracottaControllerPage extends StackPane {
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) {
var info = createLargeTitleLineButton(); var info = createLargeTitleLineButton();
info.setLeftIcon(switch (difficulty) { info.setLeading(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;
@@ -404,7 +404,7 @@ public class TerracottaControllerPage extends StackPane {
tutorial.setSubtitle(i18n("terracotta.status.guest_ok.desc", guestOK.getUrl())); tutorial.setSubtitle(i18n("terracotta.status.guest_ok.desc", guestOK.getUrl()));
var back = createLargeTitleLineButton(); var back = createLargeTitleLineButton();
back.setLeftIcon(SVG.ARROW_BACK, ICON_SIZE); back.setLeading(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"));
back.setOnAction(event -> { back.setOnAction(event -> {
@@ -426,7 +426,7 @@ public class TerracottaControllerPage extends StackPane {
nodesProperty.setAll(); nodesProperty.setAll();
var back = createLargeTitleLineButton(); var back = createLargeTitleLineButton();
back.setLeftIcon(SVG.ARROW_BACK, ICON_SIZE); back.setLeading(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"));
back.setOnAction(event -> { back.setOnAction(event -> {
@@ -438,7 +438,7 @@ public class TerracottaControllerPage extends StackPane {
SpinnerPane exportLog = new SpinnerPane(); SpinnerPane exportLog = new SpinnerPane();
var exportLogInner = createLargeTitleLineButton(); var exportLogInner = createLargeTitleLineButton();
exportLogInner.setLeftIcon(SVG.OUTPUT, ICON_SIZE); exportLogInner.setLeading(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);
@@ -481,7 +481,7 @@ public class TerracottaControllerPage extends StackPane {
if (fatal.isRecoverable()) { if (fatal.isRecoverable()) {
var retry = createLargeTitleLineButton(); var retry = createLargeTitleLineButton();
retry.setLeftIcon(SVG.RESTORE, ICON_SIZE); retry.setLeading(SVG.RESTORE, ICON_SIZE);
retry.setTitle(i18n("terracotta.status.fatal.retry")); retry.setTitle(i18n("terracotta.status.fatal.retry"));
retry.setSubtitle(message); retry.setSubtitle(message);
retry.setOnAction(event -> { retry.setOnAction(event -> {
@@ -552,17 +552,16 @@ public class TerracottaControllerPage extends StackPane {
var header = new LinePane(); var header = new LinePane();
header.setLargeTitle(true); header.setLargeTitle(true);
header.setPadding(Insets.EMPTY);
header.setMinHeight(LinePane.USE_COMPUTED_SIZE); header.setMinHeight(LinePane.USE_COMPUTED_SIZE);
header.setMouseTransparent(true); header.setMouseTransparent(true);
header.setLeftIcon(FXUtils.newBuiltinImage("/assets/img/terracotta.png")); header.setLeading(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) {
LineButton item = new LineButton(); LineButton item = new LineButton();
item.setRightIcon(SVG.OPEN_IN_NEW); item.setTrailingIcon(SVG.OPEN_IN_NEW);
item.setTitle(link.description().getText(I18n.getLocale().getCandidateLocales())); item.setTitle(link.description().getText(I18n.getLocale().getCandidateLocales()));
item.setOnAction(event -> Controllers.dialog( item.setOnAction(event -> Controllers.dialog(
i18n("terracotta.from_local.guide", TerracottaMetadata.PACKAGE_NAME), i18n("terracotta.from_local.guide", TerracottaMetadata.PACKAGE_NAME),

View File

@@ -31,7 +31,6 @@ import javafx.scene.control.ScrollPane;
import javafx.scene.effect.BoxBlur; import javafx.scene.effect.BoxBlur;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
@@ -202,7 +201,6 @@ public final class WorldInfoPage extends SpinnerPane implements WorldManagePage.
HBox right = new HBox(8); HBox right = new HBox(8);
{ {
right.setAlignment(Pos.CENTER_RIGHT); right.setAlignment(Pos.CENTER_RIGHT);
BorderPane.setAlignment(right, Pos.CENTER_RIGHT);
right.getChildren().setAll(visibilityButton, seedLabel); right.getChildren().setAll(visibilityButton, seedLabel);
seedPane.setRight(right); seedPane.setRight(right);
} }
@@ -410,9 +408,9 @@ public final class WorldInfoPage extends SpinnerPane implements WorldManagePage.
} }
} }
private void setRightTextField(BorderPane borderPane, int perfWidth, Tag tag) { private void setRightTextField(LinePane linePane, int perfWidth, Tag tag) {
JFXTextField textField = new JFXTextField(); JFXTextField textField = new JFXTextField();
setRightTextField(borderPane, textField, perfWidth); setRightTextField(linePane, textField, perfWidth);
if (tag instanceof IntTag intTag) { if (tag instanceof IntTag intTag) {
bindTagAndTextField(intTag, textField); bindTagAndTextField(intTag, textField);
} else if (tag instanceof FloatTag floatTag) { } else if (tag instanceof FloatTag floatTag) {
@@ -422,11 +420,10 @@ public final class WorldInfoPage extends SpinnerPane implements WorldManagePage.
} }
} }
private void setRightTextField(BorderPane borderPane, JFXTextField textField, int perfWidth) { private void setRightTextField(LinePane linePane, JFXTextField textField, int perfWidth) {
textField.setDisable(isReadOnly); textField.setDisable(isReadOnly);
textField.setPrefWidth(perfWidth); textField.setPrefWidth(perfWidth);
BorderPane.setAlignment(textField, Pos.CENTER_RIGHT); linePane.setRight(textField);
borderPane.setRight(textField);
} }
private void bindTagAndToggleButton(Tag tag, LineToggleButton toggleButton) { private void bindTagAndToggleButton(Tag tag, LineToggleButton toggleButton) {

View File

@@ -1041,6 +1041,10 @@
-fx-font-size: 12; -fx-font-size: 12;
} }
.options-list-item > .line-component .line-component-container {
-fx-padding: 10 16 10 16;
}
.options-list-item .svg { .options-list-item .svg {
-fx-fill: -monet-on-surface; -fx-fill: -monet-on-surface;
-fx-border-color: -monet-outline-variant; -fx-border-color: -monet-outline-variant;
@@ -1076,6 +1080,10 @@
-fx-border-width: 0; -fx-border-width: 0;
} }
.options-sublist-wrapper .expand-icon .svg {
-fx-fill: -monet-on-surface-variant;
}
/******************************************************************************* /*******************************************************************************
* * * *
* JFX Toggle Button * * JFX Toggle Button *
@@ -1103,15 +1111,16 @@
.jfx-toggle-button Line { .jfx-toggle-button Line {
-fx-stroke: -jfx-untoggle-line-color; -fx-stroke: -jfx-untoggle-line-color;
} }
.jfx-toggle-button:selected Line{
.jfx-toggle-button:selected Line {
-fx-stroke: -jfx-toggle-line-color; -fx-stroke: -jfx-toggle-line-color;
} }
.jfx-toggle-button Circle{ .jfx-toggle-button Circle {
-fx-fill: -jfx-untoggle-color; -fx-fill: -jfx-untoggle-color;
} }
.jfx-toggle-button:selected Circle{ .jfx-toggle-button:selected Circle {
-fx-fill: -jfx-toggle-color; -fx-fill: -jfx-toggle-color;
} }
@@ -1864,47 +1873,39 @@
* * * *
******************************************************************************/ ******************************************************************************/
.line-component .title { .line-component:large-title .title-label {
-fx-text-fill: -monet-on-surface;
}
.line-component:large-title .title {
-fx-font-size: 15px; -fx-font-size: 15px;
} }
.line-component .subtitle {
-fx-text-fill: -monet-on-surface-variant;
}
.line-button .svg {
-fx-opacity: 1;
}
.line-button .svg:disabled { .line-button .svg:disabled {
-fx-opacity: 0.4; -fx-opacity: 0.4;
} }
.line-select-button .svg { .line-button .trailing-label {
-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; -fx-text-fill: -monet-on-surface-variant;
} }
.line-select-button .menu-container:selected .subtitle { .line-button .trailing-icon .svg {
-fx-fill: -monet-on-surface-variant;
-fx-opacity: 1;
}
.line-button .trailing-icon .svg:disabled {
-fx-opacity: 0.4;
}
.line-select-button .menu-container .title-label {
-fx-font-size: 13px;
}
.line-select-button .menu-container:selected .title-label {
-fx-text-fill: -monet-primary; -fx-text-fill: -monet-primary;
}
.line-select-button .menu-container:selected .subtitle-label {
-fx-text-fill: -monet-primary;
}
.line-toggle-button .jfx-toggle-button {
-fx-padding: 0;
} }