refactor Navigation
This commit is contained in:
@@ -1,236 +0,0 @@
|
|||||||
package com.imyeyu.fx.ui.components;
|
|
||||||
|
|
||||||
import com.imyeyu.fx.TimiFX;
|
|
||||||
import com.imyeyu.fx.ui.TimiFXUI;
|
|
||||||
import com.imyeyu.fx.utils.SmoothScroll;
|
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.ListChangeListener;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.geometry.Insets;
|
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.control.ScrollPane;
|
|
||||||
import javafx.scene.control.TitledPane;
|
|
||||||
import javafx.scene.control.ToggleButton;
|
|
||||||
import javafx.scene.control.ToggleGroup;
|
|
||||||
import javafx.scene.input.MouseEvent;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 纵向导航组件,可实现二级导航,折叠导航
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2022-02-17 00:11
|
|
||||||
*/
|
|
||||||
public class Navigation extends ScrollPane implements TimiFXUI {
|
|
||||||
|
|
||||||
/** 导航数据列表 */
|
|
||||||
@Getter
|
|
||||||
protected final ObservableList<ToggleButton> items;
|
|
||||||
|
|
||||||
/** 已选中监听 */
|
|
||||||
protected final ObjectProperty<ToggleButton> selectedItem;
|
|
||||||
|
|
||||||
/** 默认构造器 */
|
|
||||||
public Navigation() {
|
|
||||||
items = FXCollections.observableArrayList();
|
|
||||||
selectedItem = new SimpleObjectProperty<>();
|
|
||||||
|
|
||||||
VBox root = new VBox();
|
|
||||||
root.setBorder(Stroke.BOTTOM);
|
|
||||||
|
|
||||||
getStyleClass().addAll("navigation", "sp-border");
|
|
||||||
setMaxWidth(Double.MAX_VALUE);
|
|
||||||
setVbarPolicy(ScrollBarPolicy.NEVER);
|
|
||||||
setFitToWidth(true);
|
|
||||||
setContent(root);
|
|
||||||
|
|
||||||
SmoothScroll.scrollPaneV(this);
|
|
||||||
|
|
||||||
// ---------- 事件 ----------
|
|
||||||
|
|
||||||
ToggleGroup group = new ToggleGroup();
|
|
||||||
|
|
||||||
// 主动选择(代码触发)
|
|
||||||
selectedItem.addListener((obs, o, newSelectedItem) -> group.selectToggle(newSelectedItem));
|
|
||||||
|
|
||||||
// 被动选择(操作触发)
|
|
||||||
group.selectedToggleProperty().addListener((obs, o, toggle) -> {
|
|
||||||
if (toggle instanceof ToggleButton btn) {
|
|
||||||
selectedItem.set(btn);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ObservableList<Node> children = root.getChildren();
|
|
||||||
|
|
||||||
// 响应 TimiFXUI
|
|
||||||
items.addListener((ListChangeListener<ToggleButton>) c -> {
|
|
||||||
while (c.next()) {
|
|
||||||
if (c.wasAdded()) {
|
|
||||||
// 添加
|
|
||||||
List<? extends ToggleButton> list = c.getAddedSubList();
|
|
||||||
for (ToggleButton toggleButton : list) {
|
|
||||||
toggleButton.setMaxWidth(Double.MAX_VALUE);
|
|
||||||
toggleButton.getStyleClass().setAll(CSS.MINECRAFT, "navigation-button");
|
|
||||||
toggleButton.addEventFilter(MouseEvent.MOUSE_PRESSED, TimiFX.EVENT_CONSUME_TG_BTN);
|
|
||||||
|
|
||||||
if (toggleButton.getProperties().get("OWNER") instanceof TitledPane pane) {
|
|
||||||
// 存在所属组
|
|
||||||
if (!children.contains(pane)) {
|
|
||||||
// 未添加组
|
|
||||||
children.add(pane);
|
|
||||||
}
|
|
||||||
if (pane.getContent() instanceof VBox box) {
|
|
||||||
box.getChildren().add(toggleButton);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 单独项
|
|
||||||
if (!children.isEmpty()) {
|
|
||||||
if (children.getLast() instanceof TitledPane) {
|
|
||||||
// 上一项是组导航,添加上边框
|
|
||||||
toggleButton.getStyleClass().add("after-group");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
children.add(toggleButton);
|
|
||||||
}
|
|
||||||
// 归组
|
|
||||||
group.getToggles().add(toggleButton);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (c.wasRemoved()) {
|
|
||||||
// 移除
|
|
||||||
List<? extends ToggleButton> list = c.getRemoved();
|
|
||||||
for (ToggleButton toggleButton : list) {
|
|
||||||
if (toggleButton.getProperties().get("OWNER") instanceof TitledPane pane) {
|
|
||||||
// 存在所属组
|
|
||||||
if (pane.getContent() instanceof VBox box) {
|
|
||||||
box.getChildren().remove(toggleButton);
|
|
||||||
if (box.getChildren().isEmpty()) {
|
|
||||||
// 该组已没有列表项
|
|
||||||
children.remove(box);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 单独项
|
|
||||||
children.remove(toggleButton);
|
|
||||||
}
|
|
||||||
// 从组移除
|
|
||||||
group.getToggles().remove(toggleButton);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加导航按钮
|
|
||||||
*
|
|
||||||
* @param buttons 导航按钮
|
|
||||||
*/
|
|
||||||
public void add(ToggleButton... buttons) {
|
|
||||||
getItems().addAll(buttons);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加默认没有展开的导航组
|
|
||||||
*
|
|
||||||
* @param title 标题
|
|
||||||
* @param buttons 导航项
|
|
||||||
* @return 构造的折叠面板
|
|
||||||
*/
|
|
||||||
public TitledPane addGroup(String title, ToggleButton... buttons) {
|
|
||||||
return addGroup(title, false, buttons);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加默认展开的导航组
|
|
||||||
*
|
|
||||||
* @param title 标题
|
|
||||||
* @param buttons 导航项
|
|
||||||
* @return 构造的折叠面板
|
|
||||||
*/
|
|
||||||
public TitledPane addExpandedGroup(String title, ToggleButton... buttons) {
|
|
||||||
return addGroup(title, true, buttons);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加导航组
|
|
||||||
*
|
|
||||||
* @param title 标题
|
|
||||||
* @param isExpanded true 为默认展开
|
|
||||||
* @param buttons 导航项
|
|
||||||
* @return 构造的折叠面板
|
|
||||||
*/
|
|
||||||
public TitledPane addGroup(String title, boolean isExpanded, ToggleButton... buttons) {
|
|
||||||
TitledPane pane = new TitledPane();
|
|
||||||
pane.setText(title);
|
|
||||||
return addGroup(pane, isExpanded, buttons);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加导航组
|
|
||||||
*
|
|
||||||
* @param pane 所属组
|
|
||||||
* @param isExpanded true 为默认展开
|
|
||||||
* @param buttons 导航项
|
|
||||||
* @return 原折叠面板
|
|
||||||
*/
|
|
||||||
public TitledPane addGroup(TitledPane pane, boolean isExpanded, ToggleButton... buttons) {
|
|
||||||
VBox content = new VBox();
|
|
||||||
content.setPadding(Insets.EMPTY);
|
|
||||||
|
|
||||||
pane.setContent(content);
|
|
||||||
pane.setExpanded(isExpanded);
|
|
||||||
pane.getStyleClass().add("group-pane");
|
|
||||||
|
|
||||||
for (ToggleButton button : buttons) {
|
|
||||||
button.getProperties().put("OWNER", pane);
|
|
||||||
items.add(button);
|
|
||||||
}
|
|
||||||
return pane;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取该按钮所属组
|
|
||||||
*
|
|
||||||
* @param btn 按钮
|
|
||||||
* @return 所属组,null 时为不属于任何组
|
|
||||||
*/
|
|
||||||
public TitledPane getGroup(ToggleButton btn) {
|
|
||||||
if (btn.getProperties().get("OWNER") instanceof TitledPane pane) {
|
|
||||||
return pane;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置当前激活导航项
|
|
||||||
*
|
|
||||||
* @param button 导航项
|
|
||||||
*/
|
|
||||||
public void setSelectedItem(ToggleButton button) {
|
|
||||||
selectedItem.set(button);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前激活的导航项
|
|
||||||
*
|
|
||||||
* @return 当前激活的导航项
|
|
||||||
*/
|
|
||||||
public ToggleButton getSelectedItem() {
|
|
||||||
return selectedItem.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取激活导航项监听
|
|
||||||
*
|
|
||||||
* @return 激活导航项监听
|
|
||||||
*/
|
|
||||||
public ObjectProperty<ToggleButton> selectedItem() {
|
|
||||||
return selectedItem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,570 @@
|
|||||||
|
package com.imyeyu.fx.ui.components.navigation;
|
||||||
|
|
||||||
|
import com.imyeyu.fx.TimiFX;
|
||||||
|
import com.imyeyu.fx.ui.TimiFXUI;
|
||||||
|
import com.imyeyu.fx.utils.SmoothScroll;
|
||||||
|
import javafx.beans.property.DoubleProperty;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleDoubleProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ListChangeListener;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.control.TitledPane;
|
||||||
|
import javafx.scene.control.ToggleButton;
|
||||||
|
import javafx.scene.control.ToggleGroup;
|
||||||
|
import javafx.scene.input.MouseEvent;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 纵向导航组件,可实现多级折叠导航
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2026-06-16
|
||||||
|
*/
|
||||||
|
public class Navigation extends ScrollPane implements TimiFXUI {
|
||||||
|
|
||||||
|
/** 默认缩进像素 */
|
||||||
|
static final double DEFAULT_INDENT_SIZE = 16;
|
||||||
|
|
||||||
|
/** 导航项原始标题属性 */
|
||||||
|
private static final String RAW_ITEM_TEXT = "RAW_ITEM_TEXT";
|
||||||
|
|
||||||
|
/** 导航项原始图标属性 */
|
||||||
|
private static final String RAW_ITEM_GRAPHIC = "RAW_ITEM_GRAPHIC";
|
||||||
|
|
||||||
|
/** 导航项图标同步锁属性 */
|
||||||
|
private static final String GRAPHIC_SYNCING = "GRAPHIC_SYNCING";
|
||||||
|
|
||||||
|
/** 导航项是否已初始化 */
|
||||||
|
private static final String INITIALIZED = "INITIALIZED";
|
||||||
|
|
||||||
|
/** 导航数据列表 */
|
||||||
|
@Getter
|
||||||
|
protected final ObservableList<Node> items;
|
||||||
|
|
||||||
|
/** 已选中监听 */
|
||||||
|
protected final ObjectProperty<ToggleButton> selectedItem;
|
||||||
|
|
||||||
|
/** 缩进像素 */
|
||||||
|
private final DoubleProperty indentSize;
|
||||||
|
|
||||||
|
/** 根容器 */
|
||||||
|
private final VBox contentBox;
|
||||||
|
|
||||||
|
/** 选择组 */
|
||||||
|
private final ToggleGroup toggleGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认构造器
|
||||||
|
*/
|
||||||
|
public Navigation() {
|
||||||
|
items = FXCollections.observableArrayList();
|
||||||
|
selectedItem = new SimpleObjectProperty<>();
|
||||||
|
indentSize = new SimpleDoubleProperty(DEFAULT_INDENT_SIZE);
|
||||||
|
contentBox = new VBox();
|
||||||
|
toggleGroup = new ToggleGroup();
|
||||||
|
|
||||||
|
initView();
|
||||||
|
initSelection();
|
||||||
|
initPropertyListener();
|
||||||
|
initItemsListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化视图
|
||||||
|
*/
|
||||||
|
private void initView() {
|
||||||
|
contentBox.setBorder(Stroke.BOTTOM);
|
||||||
|
|
||||||
|
getStyleClass().addAll("navigation", "sp-border");
|
||||||
|
setMaxWidth(Double.MAX_VALUE);
|
||||||
|
setVbarPolicy(ScrollBarPolicy.NEVER);
|
||||||
|
setFitToWidth(true);
|
||||||
|
setContent(contentBox);
|
||||||
|
|
||||||
|
SmoothScroll.scrollPaneV(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化选中同步
|
||||||
|
*/
|
||||||
|
private void initSelection() {
|
||||||
|
selectedItem.addListener((obs, o, newSelectedItem) -> {
|
||||||
|
if (newSelectedItem == null) {
|
||||||
|
toggleGroup.selectToggle(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (toggleGroup.getToggles().contains(newSelectedItem)) {
|
||||||
|
toggleGroup.selectToggle(newSelectedItem);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
toggleGroup.selectedToggleProperty().addListener((obs, o, toggle) -> {
|
||||||
|
if (toggle instanceof ToggleButton button) {
|
||||||
|
selectedItem.set(button);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedItem.set(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化属性监听
|
||||||
|
*/
|
||||||
|
private void initPropertyListener() {
|
||||||
|
indentSize.addListener((obs, o, n) -> rebuild());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化导航数据监听
|
||||||
|
*/
|
||||||
|
private void initItemsListener() {
|
||||||
|
items.addListener(this::handleItemsChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理导航数据变更
|
||||||
|
*
|
||||||
|
* @param change 变更内容
|
||||||
|
*/
|
||||||
|
private void handleItemsChanged(ListChangeListener.Change<? extends Node> change) {
|
||||||
|
while (change.next()) {
|
||||||
|
if (change.wasAdded()) {
|
||||||
|
for (Node node : change.getAddedSubList()) {
|
||||||
|
bindGroup(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (change.wasRemoved()) {
|
||||||
|
for (Node node : change.getRemoved()) {
|
||||||
|
unbindGroup(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rebuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定导航组刷新回调
|
||||||
|
*
|
||||||
|
* @param node 子节点
|
||||||
|
*/
|
||||||
|
private void bindGroup(Node node) {
|
||||||
|
if (node instanceof NavigationGroup group) {
|
||||||
|
group.setStructureChangedAction(this::rebuild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解绑导航组刷新回调
|
||||||
|
*
|
||||||
|
* @param node 子节点
|
||||||
|
*/
|
||||||
|
private void unbindGroup(Node node) {
|
||||||
|
if (node instanceof NavigationGroup group) {
|
||||||
|
group.setStructureChangedAction(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重建导航结构
|
||||||
|
*/
|
||||||
|
private void rebuild() {
|
||||||
|
contentBox.getChildren().clear();
|
||||||
|
toggleGroup.getToggles().clear();
|
||||||
|
|
||||||
|
for (Node node : items) {
|
||||||
|
Node attachedNode = buildNode(node, 0);
|
||||||
|
if (attachedNode != null) {
|
||||||
|
contentBox.getChildren().add(attachedNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshStandaloneItemStyle();
|
||||||
|
if (selectedItem.get() != null && !toggleGroup.getToggles().contains(selectedItem.get())) {
|
||||||
|
selectedItem.set(null);
|
||||||
|
}
|
||||||
|
if (selectedItem.get() != null) {
|
||||||
|
toggleGroup.selectToggle(selectedItem.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建节点
|
||||||
|
*
|
||||||
|
* @param node 节点
|
||||||
|
* @param level 层级
|
||||||
|
* @return 可挂载节点
|
||||||
|
*/
|
||||||
|
private Node buildNode(Node node, int level) {
|
||||||
|
if (node instanceof NavigationGroup group) {
|
||||||
|
return buildGroup(group, level);
|
||||||
|
}
|
||||||
|
if (node instanceof ToggleButton button) {
|
||||||
|
return buildItem(button, level);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建导航组
|
||||||
|
*
|
||||||
|
* @param group 导航组
|
||||||
|
* @param level 层级
|
||||||
|
* @return 导航组节点
|
||||||
|
*/
|
||||||
|
private NavigationGroup buildGroup(NavigationGroup group, int level) {
|
||||||
|
int currentLevel = level + group.getLevelOffset();
|
||||||
|
group.applyLevel(currentLevel, getIndentSize());
|
||||||
|
if (!group.getStyleClass().contains("group-pane")) {
|
||||||
|
group.getStyleClass().add("group-pane");
|
||||||
|
}
|
||||||
|
|
||||||
|
VBox groupContent = group.getContentBox();
|
||||||
|
groupContent.getChildren().clear();
|
||||||
|
for (Node child : group.getItems()) {
|
||||||
|
Node childNode = buildNode(child, currentLevel + 1);
|
||||||
|
if (childNode != null) {
|
||||||
|
groupContent.getChildren().add(childNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建导航项
|
||||||
|
*
|
||||||
|
* @param button 导航项
|
||||||
|
* @param level 层级
|
||||||
|
* @return 导航项节点
|
||||||
|
*/
|
||||||
|
private ToggleButton buildItem(ToggleButton button, int level) {
|
||||||
|
configureButton(button, level);
|
||||||
|
toggleGroup.getToggles().add(button);
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新独立导航项样式
|
||||||
|
*/
|
||||||
|
private void refreshStandaloneItemStyle() {
|
||||||
|
List<Node> children = contentBox.getChildren();
|
||||||
|
for (int i = 0; i < children.size(); i++) {
|
||||||
|
Node node = children.get(i);
|
||||||
|
if (node instanceof ToggleButton button) {
|
||||||
|
button.getStyleClass().remove("after-group");
|
||||||
|
if (0 < i && children.get(i - 1) instanceof TitledPane) {
|
||||||
|
button.getStyleClass().add("after-group");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置导航项
|
||||||
|
*
|
||||||
|
* @param button 导航项
|
||||||
|
* @param level 层级
|
||||||
|
*/
|
||||||
|
private void configureButton(ToggleButton button, int level) {
|
||||||
|
cacheItemState(button);
|
||||||
|
button.setText(resolveItemText(button));
|
||||||
|
button.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
button.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
applyButtonIndent(button, Math.max(0, level) * getIndentSize());
|
||||||
|
button.getStyleClass().setAll(CSS.MINECRAFT, "navigation-button");
|
||||||
|
|
||||||
|
if (!Boolean.TRUE.equals(button.getProperties().get(INITIALIZED))) {
|
||||||
|
button.addEventFilter(MouseEvent.MOUSE_PRESSED, TimiFX.EVENT_CONSUME_TG_BTN);
|
||||||
|
button.graphicProperty().addListener((obs, o, n) -> {
|
||||||
|
if (!Boolean.TRUE.equals(button.getProperties().get(GRAPHIC_SYNCING))) {
|
||||||
|
button.getProperties().put(RAW_ITEM_GRAPHIC, n);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
button.getProperties().put(INITIALIZED, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存导航项原始状态
|
||||||
|
*
|
||||||
|
* @param button 导航项
|
||||||
|
*/
|
||||||
|
private void cacheItemState(ToggleButton button) {
|
||||||
|
if (!(button.getProperties().get(RAW_ITEM_TEXT) instanceof String)) {
|
||||||
|
button.getProperties().put(RAW_ITEM_TEXT, button.getText());
|
||||||
|
}
|
||||||
|
if (!button.getProperties().containsKey(RAW_ITEM_GRAPHIC)) {
|
||||||
|
button.getProperties().put(RAW_ITEM_GRAPHIC, button.getGraphic());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取导航项原始标题
|
||||||
|
*
|
||||||
|
* @param button 导航项
|
||||||
|
* @return 原始标题
|
||||||
|
*/
|
||||||
|
private String resolveItemText(ToggleButton button) {
|
||||||
|
cacheItemState(button);
|
||||||
|
return (String) button.getProperties().get(RAW_ITEM_TEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用导航项缩进
|
||||||
|
*
|
||||||
|
* @param button 导航项
|
||||||
|
* @param indentWidth 缩进宽度
|
||||||
|
*/
|
||||||
|
private void applyButtonIndent(ToggleButton button, double indentWidth) {
|
||||||
|
Node rawGraphic = (Node) button.getProperties().get(RAW_ITEM_GRAPHIC);
|
||||||
|
if (0 == indentWidth) {
|
||||||
|
button.getProperties().put(GRAPHIC_SYNCING, true);
|
||||||
|
button.setGraphic(rawGraphic);
|
||||||
|
button.getProperties().put(GRAPHIC_SYNCING, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Region spacer = new Region();
|
||||||
|
spacer.setMinWidth(indentWidth);
|
||||||
|
spacer.setPrefWidth(indentWidth);
|
||||||
|
spacer.setMaxWidth(indentWidth);
|
||||||
|
|
||||||
|
HBox graphicBox = new HBox();
|
||||||
|
graphicBox.getChildren().add(spacer);
|
||||||
|
if (rawGraphic != null) {
|
||||||
|
graphicBox.getChildren().add(rawGraphic);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.getProperties().put(GRAPHIC_SYNCING, true);
|
||||||
|
button.setGraphic(graphicBox);
|
||||||
|
button.getProperties().put(GRAPHIC_SYNCING, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加导航按钮
|
||||||
|
*
|
||||||
|
* @param buttons 导航按钮
|
||||||
|
*/
|
||||||
|
public void add(ToggleButton... buttons) {
|
||||||
|
getItems().addAll(buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加导航组
|
||||||
|
*
|
||||||
|
* @param groups 导航组
|
||||||
|
*/
|
||||||
|
public void add(NavigationGroup... groups) {
|
||||||
|
getItems().addAll(groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加导航节点
|
||||||
|
*
|
||||||
|
* @param nodes 导航节点
|
||||||
|
*/
|
||||||
|
public void add(Node... nodes) {
|
||||||
|
getItems().addAll(nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加默认没有展开的导航组
|
||||||
|
*
|
||||||
|
* @param title 标题
|
||||||
|
* @param buttons 导航项
|
||||||
|
* @return 构造的导航组
|
||||||
|
*/
|
||||||
|
public NavigationGroup addGroup(String title, ToggleButton... buttons) {
|
||||||
|
return addGroup(title, 0, false, buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加默认展开的导航组
|
||||||
|
*
|
||||||
|
* @param title 标题
|
||||||
|
* @param buttons 导航项
|
||||||
|
* @return 构造的导航组
|
||||||
|
*/
|
||||||
|
public NavigationGroup addExpandedGroup(String title, ToggleButton... buttons) {
|
||||||
|
return addGroup(title, 0, true, buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加默认没有展开的导航组
|
||||||
|
*
|
||||||
|
* @param title 标题
|
||||||
|
* @param level 缩进偏移量
|
||||||
|
* @param buttons 导航项
|
||||||
|
* @return 构造的导航组
|
||||||
|
*/
|
||||||
|
public NavigationGroup addGroup(String title, int level, ToggleButton... buttons) {
|
||||||
|
return addGroup(title, level, false, buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加默认展开的导航组
|
||||||
|
*
|
||||||
|
* @param title 标题
|
||||||
|
* @param level 缩进偏移量
|
||||||
|
* @param buttons 导航项
|
||||||
|
* @return 构造的导航组
|
||||||
|
*/
|
||||||
|
public NavigationGroup addExpandedGroup(String title, int level, ToggleButton... buttons) {
|
||||||
|
return addGroup(title, level, true, buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加导航组
|
||||||
|
*
|
||||||
|
* @param title 标题
|
||||||
|
* @param level 缩进偏移量
|
||||||
|
* @param isExpanded true 为默认展开
|
||||||
|
* @param buttons 导航项
|
||||||
|
* @return 构造的导航组
|
||||||
|
*/
|
||||||
|
public NavigationGroup addGroup(String title, int level, boolean isExpanded, ToggleButton... buttons) {
|
||||||
|
NavigationGroup group = new NavigationGroup(title).levelOffset(level).expanded(isExpanded);
|
||||||
|
group.add(buttons);
|
||||||
|
add(group);
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加导航组
|
||||||
|
*
|
||||||
|
* @param group 导航组
|
||||||
|
* @return 原导航组
|
||||||
|
*/
|
||||||
|
public NavigationGroup addGroup(NavigationGroup group) {
|
||||||
|
add(group);
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加导航组
|
||||||
|
*
|
||||||
|
* @param group 导航组
|
||||||
|
* @param isExpanded true 为默认展开
|
||||||
|
* @param buttons 导航项
|
||||||
|
* @return 原导航组
|
||||||
|
*/
|
||||||
|
public NavigationGroup addGroup(NavigationGroup group, boolean isExpanded, ToggleButton... buttons) {
|
||||||
|
return addGroup(group, group.getLevelOffset(), isExpanded, buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加导航组
|
||||||
|
*
|
||||||
|
* @param group 导航组
|
||||||
|
* @param level 缩进偏移量
|
||||||
|
* @param isExpanded true 为默认展开
|
||||||
|
* @param buttons 导航项
|
||||||
|
* @return 原导航组
|
||||||
|
*/
|
||||||
|
public NavigationGroup addGroup(NavigationGroup group, int level, boolean isExpanded, ToggleButton... buttons) {
|
||||||
|
group.levelOffset(level).expanded(isExpanded).add(buttons);
|
||||||
|
add(group);
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该按钮所属组
|
||||||
|
*
|
||||||
|
* @param button 按钮
|
||||||
|
* @return 所属组,null 时为不属于任何组
|
||||||
|
*/
|
||||||
|
public NavigationGroup getGroup(ToggleButton button) {
|
||||||
|
for (Node node : items) {
|
||||||
|
NavigationGroup group = findGroup(node, button);
|
||||||
|
if (group != null) {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置当前激活导航项
|
||||||
|
*
|
||||||
|
* @param button 导航项
|
||||||
|
*/
|
||||||
|
public void setSelectedItem(ToggleButton button) {
|
||||||
|
selectedItem.set(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前激活的导航项
|
||||||
|
*
|
||||||
|
* @return 当前激活的导航项
|
||||||
|
*/
|
||||||
|
public ToggleButton getSelectedItem() {
|
||||||
|
return selectedItem.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取激活导航项监听
|
||||||
|
*
|
||||||
|
* @return 激活导航项监听
|
||||||
|
*/
|
||||||
|
public ObjectProperty<ToggleButton> selectedItem() {
|
||||||
|
return selectedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置缩进像素
|
||||||
|
*
|
||||||
|
* @param indentSize 缩进像素
|
||||||
|
*/
|
||||||
|
public void setIndentSize(double indentSize) {
|
||||||
|
this.indentSize.set(Math.max(0, indentSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取缩进像素
|
||||||
|
*
|
||||||
|
* @return 缩进像素
|
||||||
|
*/
|
||||||
|
public double getIndentSize() {
|
||||||
|
return indentSize.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取缩进像素属性
|
||||||
|
*
|
||||||
|
* @return 缩进像素属性
|
||||||
|
*/
|
||||||
|
public DoubleProperty indentSize() {
|
||||||
|
return indentSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找按钮所属导航组
|
||||||
|
*
|
||||||
|
* @param node 当前节点
|
||||||
|
* @param button 目标按钮
|
||||||
|
* @return 所属导航组
|
||||||
|
*/
|
||||||
|
private NavigationGroup findGroup(Node node, ToggleButton button) {
|
||||||
|
if (!(node instanceof NavigationGroup group)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (Node child : group.getItems()) {
|
||||||
|
if (child == button) {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
NavigationGroup nestedGroup = findGroup(child, button);
|
||||||
|
if (nestedGroup != null) {
|
||||||
|
return nestedGroup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,252 @@
|
|||||||
|
package com.imyeyu.fx.ui.components.navigation;
|
||||||
|
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ListChangeListener;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.TitledPane;
|
||||||
|
import javafx.scene.control.ToggleButton;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导航组
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2026-06-16
|
||||||
|
*/
|
||||||
|
public class NavigationGroup extends TitledPane {
|
||||||
|
|
||||||
|
/** 子节点列表 */
|
||||||
|
@Getter
|
||||||
|
private final ObservableList<Node> items;
|
||||||
|
|
||||||
|
/** 内容容器 */
|
||||||
|
private final VBox contentBox;
|
||||||
|
|
||||||
|
/** 结构变更回调 */
|
||||||
|
private Runnable structureChangedAction;
|
||||||
|
|
||||||
|
/** 原始标题 */
|
||||||
|
private String rawTitle;
|
||||||
|
|
||||||
|
/** 原始图标 */
|
||||||
|
private Node rawGraphic;
|
||||||
|
|
||||||
|
/** 标题同步锁 */
|
||||||
|
private boolean textSyncing;
|
||||||
|
|
||||||
|
/** 图标同步锁 */
|
||||||
|
private boolean graphicSyncing;
|
||||||
|
|
||||||
|
/** 标题缩进偏移量 */
|
||||||
|
private int levelOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认构造器
|
||||||
|
*/
|
||||||
|
public NavigationGroup() {
|
||||||
|
items = FXCollections.observableArrayList();
|
||||||
|
contentBox = new VBox();
|
||||||
|
structureChangedAction = () -> {};
|
||||||
|
rawTitle = "";
|
||||||
|
|
||||||
|
contentBox.setPadding(Insets.EMPTY);
|
||||||
|
setContent(contentBox);
|
||||||
|
getStyleClass().add("group-pane");
|
||||||
|
items.addListener(this::handleItemsChanged);
|
||||||
|
initRawStateListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过标题创建导航组
|
||||||
|
*
|
||||||
|
* @param title 标题
|
||||||
|
*/
|
||||||
|
public NavigationGroup(String title) {
|
||||||
|
this();
|
||||||
|
setText(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加导航项
|
||||||
|
*
|
||||||
|
* @param buttons 导航项
|
||||||
|
* @return 当前导航组
|
||||||
|
*/
|
||||||
|
public NavigationGroup add(ToggleButton... buttons) {
|
||||||
|
items.addAll(buttons);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加导航组
|
||||||
|
*
|
||||||
|
* @param groups 导航组
|
||||||
|
* @return 当前导航组
|
||||||
|
*/
|
||||||
|
public NavigationGroup add(NavigationGroup... groups) {
|
||||||
|
items.addAll(groups);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加子节点
|
||||||
|
*
|
||||||
|
* @param nodes 子节点
|
||||||
|
* @return 当前导航组
|
||||||
|
*/
|
||||||
|
public NavigationGroup add(Node... nodes) {
|
||||||
|
items.addAll(nodes);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否默认展开
|
||||||
|
*
|
||||||
|
* @param expanded true 为默认展开
|
||||||
|
* @return 当前导航组
|
||||||
|
*/
|
||||||
|
public NavigationGroup expanded(boolean expanded) {
|
||||||
|
setExpanded(expanded);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置标题缩进偏移量
|
||||||
|
*
|
||||||
|
* @param levelOffset 偏移量
|
||||||
|
* @return 当前导航组
|
||||||
|
*/
|
||||||
|
public NavigationGroup levelOffset(int levelOffset) {
|
||||||
|
this.levelOffset = Math.max(0, levelOffset);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取内容容器
|
||||||
|
*
|
||||||
|
* @return 内容容器
|
||||||
|
*/
|
||||||
|
VBox getContentBox() {
|
||||||
|
return contentBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取标题缩进偏移量
|
||||||
|
*
|
||||||
|
* @return 标题缩进偏移量
|
||||||
|
*/
|
||||||
|
int getLevelOffset() {
|
||||||
|
return levelOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置结构变更回调
|
||||||
|
*
|
||||||
|
* @param structureChangedAction 回调
|
||||||
|
*/
|
||||||
|
void setStructureChangedAction(Runnable structureChangedAction) {
|
||||||
|
this.structureChangedAction = structureChangedAction == null ? () -> {} : structureChangedAction;
|
||||||
|
for (Node node : items) {
|
||||||
|
if (node instanceof NavigationGroup group) {
|
||||||
|
group.setStructureChangedAction(this::notifyStructureChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用显示层级
|
||||||
|
*
|
||||||
|
* @param level 层级
|
||||||
|
* @param indentSize 缩进像素
|
||||||
|
*/
|
||||||
|
void applyLevel(int level, double indentSize) {
|
||||||
|
textSyncing = true;
|
||||||
|
super.setText(rawTitle);
|
||||||
|
textSyncing = false;
|
||||||
|
applyGraphicIndent(Math.max(0, level) * indentSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理子节点变更
|
||||||
|
*
|
||||||
|
* @param change 变更内容
|
||||||
|
*/
|
||||||
|
private void handleItemsChanged(ListChangeListener.Change<? extends Node> change) {
|
||||||
|
while (change.next()) {
|
||||||
|
if (change.wasAdded()) {
|
||||||
|
for (Node node : change.getAddedSubList()) {
|
||||||
|
if (node instanceof NavigationGroup group) {
|
||||||
|
group.setStructureChangedAction(this::notifyStructureChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (change.wasRemoved()) {
|
||||||
|
for (Node node : change.getRemoved()) {
|
||||||
|
if (node instanceof NavigationGroup group) {
|
||||||
|
group.setStructureChangedAction(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifyStructureChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 触发结构变更通知
|
||||||
|
*/
|
||||||
|
private void notifyStructureChanged() {
|
||||||
|
structureChangedAction.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化原始状态监听
|
||||||
|
*/
|
||||||
|
private void initRawStateListener() {
|
||||||
|
rawTitle = getText() == null ? "" : getText();
|
||||||
|
rawGraphic = getGraphic();
|
||||||
|
|
||||||
|
textProperty().addListener((obs, o, n) -> {
|
||||||
|
if (!textSyncing) {
|
||||||
|
rawTitle = n == null ? "" : n;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
graphicProperty().addListener((obs, o, n) -> {
|
||||||
|
if (!graphicSyncing) {
|
||||||
|
rawGraphic = n;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用标题缩进
|
||||||
|
*
|
||||||
|
* @param indentWidth 缩进宽度
|
||||||
|
*/
|
||||||
|
private void applyGraphicIndent(double indentWidth) {
|
||||||
|
if (0 == indentWidth) {
|
||||||
|
graphicSyncing = true;
|
||||||
|
super.setGraphic(rawGraphic);
|
||||||
|
graphicSyncing = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Region spacer = new Region();
|
||||||
|
spacer.setMinWidth(indentWidth);
|
||||||
|
spacer.setPrefWidth(indentWidth);
|
||||||
|
spacer.setMaxWidth(indentWidth);
|
||||||
|
|
||||||
|
HBox graphicBox = new HBox();
|
||||||
|
graphicBox.getChildren().add(spacer);
|
||||||
|
if (rawGraphic != null) {
|
||||||
|
graphicBox.getChildren().add(rawGraphic);
|
||||||
|
}
|
||||||
|
graphicSyncing = true;
|
||||||
|
super.setGraphic(graphicBox);
|
||||||
|
graphicSyncing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.imyeyu.fx.ui.components.navigation;
|
||||||
|
|
||||||
|
import javafx.scene.control.ToggleButton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导航项
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2026-06-16
|
||||||
|
*/
|
||||||
|
public class NavigationItem extends ToggleButton {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认构造器
|
||||||
|
*/
|
||||||
|
public NavigationItem() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过标题创建导航项
|
||||||
|
*
|
||||||
|
* @param text 标题
|
||||||
|
*/
|
||||||
|
public NavigationItem(String text) {
|
||||||
|
super(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/** 导航组件 */
|
||||||
|
package com.imyeyu.fx.ui.components.navigation;
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.imyeyu.fx.ui.examples.view.pages.component;
|
package com.imyeyu.fx.ui.examples.view.pages.component;
|
||||||
|
|
||||||
import com.imyeyu.fx.ui.TimiFXUI;
|
import com.imyeyu.fx.ui.TimiFXUI;
|
||||||
import com.imyeyu.fx.ui.components.Navigation;
|
import com.imyeyu.fx.ui.components.navigation.Navigation;
|
||||||
|
import com.imyeyu.fx.ui.components.navigation.NavigationGroup;
|
||||||
|
import com.imyeyu.fx.ui.components.navigation.NavigationItem;
|
||||||
import com.imyeyu.fx.ui.examples.component.AbstractDemoPane;
|
import com.imyeyu.fx.ui.examples.component.AbstractDemoPane;
|
||||||
import com.imyeyu.fx.ui.examples.component.sidebar.SidebarItem;
|
import com.imyeyu.fx.ui.examples.component.sidebar.SidebarItem;
|
||||||
import com.imyeyu.fx.utils.Column;
|
import com.imyeyu.fx.utils.Column;
|
||||||
@@ -10,7 +12,6 @@ import com.imyeyu.inject.annotation.Component;
|
|||||||
import javafx.geometry.Insets;
|
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.control.ToggleButton;
|
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,40 +33,55 @@ public class NavigationDemo extends AbstractDemoPane {
|
|||||||
Label labelCommonly = TimiFXUI.label(TimiFXUI.MULTILINGUAL.text("commonly"));
|
Label labelCommonly = TimiFXUI.label(TimiFXUI.MULTILINGUAL.text("commonly"));
|
||||||
labelCommonly.setAlignment(Pos.CENTER);
|
labelCommonly.setAlignment(Pos.CENTER);
|
||||||
Navigation commonly = new Navigation();
|
Navigation commonly = new Navigation();
|
||||||
commonly.add(new ToggleButton("item 0"));
|
commonly.setPrefWidth(240);
|
||||||
commonly.add(new ToggleButton("item 1"));
|
commonly.add(new NavigationItem("item 0"));
|
||||||
commonly.add(new ToggleButton("item 2"));
|
commonly.add(new NavigationItem("item 1"));
|
||||||
|
commonly.add(new NavigationItem("item 2"));
|
||||||
|
|
||||||
// 二级
|
// 二级
|
||||||
Label labelSecond = TimiFXUI.label(TimiFXUI.MULTILINGUAL.text("fx.example.navigation.second"));
|
Label labelSecond = TimiFXUI.label(TimiFXUI.MULTILINGUAL.text("fx.example.navigation.second"));
|
||||||
labelSecond.setAlignment(Pos.CENTER);
|
labelSecond.setAlignment(Pos.CENTER);
|
||||||
Navigation second = new Navigation();
|
Navigation second = new Navigation();
|
||||||
second.add(new ToggleButton("item 0"));
|
second.add(new NavigationItem("item 0"));
|
||||||
second.add(new ToggleButton("item 1"));
|
second.add(new NavigationItem("item 1"));
|
||||||
second.add(new ToggleButton("item 2"));
|
second.add(new NavigationItem("item 2"));
|
||||||
second.addGroup(TimiFXUI.MULTILINGUAL.text("fx.example.navigation.second"), new ToggleButton("item 0"), new ToggleButton("item 1"));
|
second.add(new NavigationGroup(TimiFXUI.MULTILINGUAL.text("fx.example.navigation.second"))
|
||||||
|
.add(new NavigationItem("item 0"), new NavigationItem("item 1")));
|
||||||
|
|
||||||
// 默认展开
|
// 默认展开
|
||||||
Label labelExpanded = TimiFXUI.label(TimiFXUI.MULTILINGUAL.text("fx.example.navigation.expanded"));
|
Label labelExpanded = TimiFXUI.label(TimiFXUI.MULTILINGUAL.text("fx.example.navigation.expanded"));
|
||||||
labelExpanded.setAlignment(Pos.CENTER);
|
labelExpanded.setAlignment(Pos.CENTER);
|
||||||
Navigation expanded = new Navigation();
|
Navigation expanded = new Navigation();
|
||||||
expanded.add(new ToggleButton("item 0"));
|
expanded.add(new NavigationItem("item 0"));
|
||||||
expanded.add(new ToggleButton("item 1"));
|
expanded.add(new NavigationItem("item 1"));
|
||||||
expanded.add(new ToggleButton("item 2"));
|
expanded.add(new NavigationItem("item 2"));
|
||||||
expanded.addExpandedGroup(TimiFXUI.MULTILINGUAL.text("fx.example.navigation.second"), new ToggleButton("item 0"), new ToggleButton("item 1"));
|
expanded.add(new NavigationGroup(TimiFXUI.MULTILINGUAL.text("fx.example.navigation.second"))
|
||||||
|
.expanded(true)
|
||||||
|
.add(new NavigationItem("item 0"), new NavigationItem("item 1")));
|
||||||
|
|
||||||
// 多个二级
|
// 三级
|
||||||
Label labelMulti = TimiFXUI.label(TimiFXUI.MULTILINGUAL.text("fx.example.navigation.multi"));
|
Label labelMulti = TimiFXUI.label(TimiFXUI.MULTILINGUAL.text("fx.example.navigation.multi"));
|
||||||
labelMulti.setAlignment(Pos.CENTER);
|
labelMulti.setAlignment(Pos.CENTER);
|
||||||
Navigation multi = new Navigation();
|
Navigation multi = new Navigation();
|
||||||
multi.add(new ToggleButton("item 0"));
|
multi.add(new NavigationItem("item 0"));
|
||||||
multi.add(new ToggleButton("item 1"));
|
multi.add(new NavigationItem("item 1"));
|
||||||
multi.add(new ToggleButton("item 2"));
|
multi.add(new NavigationItem("item 2"));
|
||||||
multi.addExpandedGroup(TimiFXUI.MULTILINGUAL.text("fx.example.navigation.second"), new ToggleButton("item 0"), new ToggleButton("item 1"));
|
multi.add(
|
||||||
multi.addExpandedGroup(TimiFXUI.MULTILINGUAL.text("fx.example.navigation.second"), new ToggleButton("item 0"), new ToggleButton("item 1"));
|
new NavigationGroup(TimiFXUI.MULTILINGUAL.text("fx.example.navigation.second"))
|
||||||
|
.expanded(true)
|
||||||
|
.add(
|
||||||
|
new NavigationItem("item 0"),
|
||||||
|
new NavigationGroup(TimiFXUI.MULTILINGUAL.text("fx.example.navigation.multi"))
|
||||||
|
.expanded(true)
|
||||||
|
.add(new NavigationItem("item 0"), new NavigationItem("item 1"))
|
||||||
|
),
|
||||||
|
new NavigationGroup(TimiFXUI.MULTILINGUAL.text("fx.example.navigation.second"))
|
||||||
|
.expanded(true)
|
||||||
|
.add(new NavigationItem("item 0"), new NavigationItem("item 1"))
|
||||||
|
);
|
||||||
|
|
||||||
setCenter(new GridPane() {{
|
setCenter(new GridPane() {{
|
||||||
Column col = Column.build().width(120);
|
Column col = Column.build().width(220);
|
||||||
getColumnConstraints().addAll(col, col, col, col);
|
getColumnConstraints().addAll(col, col, col, col);
|
||||||
getRowConstraints().addAll(Row.build(), Row.build().alwaysPriority());
|
getRowConstraints().addAll(Row.build(), Row.build().alwaysPriority());
|
||||||
setVgap(5);
|
setVgap(5);
|
||||||
|
|||||||
Reference in New Issue
Block a user