fix css var

This commit is contained in:
Timi
2026-06-26 18:00:15 +08:00
parent 0457ffc432
commit 0f8b23be91
22 changed files with 84 additions and 532 deletions
@@ -1,365 +0,0 @@
package com.imyeyu.fx.ui.components;
import com.imyeyu.fx.ui.TimiFXUI;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.control.Control;
import javafx.scene.control.Skin;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
/**
* 分段按钮组组件,使用单选模式组织多个切换按钮
*
* @author 夜雨
* @since 2026-06-25
*/
public class SegmentedButtonGroup extends Control {
/** 根样式 */
public static final String STYLE_CLASS = "segmented-button-group";
/** 按钮容器样式 */
public static final String STYLE_CLASS_BOX = "segmented-button-group-box";
/** 按钮样式 */
public static final String STYLE_CLASS_BUTTON = "segmented-button-group-button";
/** 首按钮样式 */
public static final String STYLE_CLASS_FIRST = "first";
/** 中间按钮样式 */
public static final String STYLE_CLASS_MIDDLE = "middle";
/** 末按钮样式 */
public static final String STYLE_CLASS_LAST = "last";
/** 单按钮样式 */
public static final String STYLE_CLASS_SINGLE = "single";
/** 按钮列表 */
private final ObservableList<ToggleButton> buttons;
/** 按钮组 */
private final ToggleGroup toggleGroup;
/** 当前选中按钮 */
private final ReadOnlyObjectWrapper<ToggleButton> selectedButton;
/** 当前选中下标 */
private final IntegerProperty selectedIndex;
/** 按钮间距 */
private final DoubleProperty spacing;
/** 布局对齐方式 */
private final ObjectProperty<Pos> alignment;
/** true 为允许清空选中 */
private final BooleanProperty allowClearSelection;
/**
* 默认构造器
*/
public SegmentedButtonGroup() {
buttons = FXCollections.observableArrayList();
toggleGroup = new ToggleGroup();
selectedButton = new ReadOnlyObjectWrapper<>();
selectedIndex = new SimpleIntegerProperty(-1);
spacing = new SimpleDoubleProperty();
alignment = new SimpleObjectProperty<>(Pos.CENTER_LEFT);
allowClearSelection = new SimpleBooleanProperty();
getStyleClass().add(STYLE_CLASS);
setFocusTraversable(false);
initSelectionSync();
initButtonsListener();
}
/**
* 初始化选中同步
*/
private void initSelectionSync() {
toggleGroup.selectedToggleProperty().addListener((obs, o, toggle) -> {
if (toggle instanceof ToggleButton button) {
selectedButton.set(button);
selectedIndex.set(buttons.indexOf(button));
return;
}
selectedButton.set(null);
selectedIndex.set(-1);
});
selectedIndex.addListener((obs, o, newIndex) -> {
int index = newIndex == null ? -1 : newIndex.intValue();
if (index < 0) {
if (isAllowClearSelection()) {
toggleGroup.selectToggle(null);
}
return;
}
if (index < buttons.size()) {
toggleGroup.selectToggle(buttons.get(index));
}
});
}
/**
* 初始化按钮监听
*/
private void initButtonsListener() {
buttons.addListener((ListChangeListener<ToggleButton>) change -> {
while (change.next()) {
if (change.wasRemoved()) {
for (ToggleButton button : change.getRemoved()) {
detachButton(button);
}
}
if (change.wasAdded()) {
for (ToggleButton button : change.getAddedSubList()) {
attachButton(button);
}
}
}
syncSelectionState();
});
}
/**
* 挂载按钮到当前组件
*
* @param button 按钮
*/
private void attachButton(ToggleButton button) {
if (button == null) {
throw new IllegalArgumentException("button must not be null");
}
button.setToggleGroup(toggleGroup);
button.addEventFilter(javafx.scene.input.MouseEvent.MOUSE_PRESSED, SegmentedButtonGroupSkin.EVENT_KEEP_SELECTED);
}
/**
* 卸载按钮
*
* @param button 按钮
*/
private void detachButton(ToggleButton button) {
button.removeEventFilter(javafx.scene.input.MouseEvent.MOUSE_PRESSED, SegmentedButtonGroupSkin.EVENT_KEEP_SELECTED);
if (button.getToggleGroup() == toggleGroup) {
button.setToggleGroup(null);
}
}
/**
* 同步当前选中状态
*/
private void syncSelectionState() {
ToggleButton currentSelectedButton = getSelectedButton();
if (currentSelectedButton != null && !buttons.contains(currentSelectedButton)) {
if (isAllowClearSelection()) {
toggleGroup.selectToggle(null);
} else if (!buttons.isEmpty()) {
toggleGroup.selectToggle(buttons.getFirst());
}
return;
}
int index = selectedIndex.get();
if (index < 0 || buttons.size() <= index) {
if (!buttons.isEmpty() && !isAllowClearSelection() && toggleGroup.getSelectedToggle() == null) {
toggleGroup.selectToggle(buttons.getFirst());
}
return;
}
toggleGroup.selectToggle(buttons.get(index));
}
@Override
protected Skin<?> createDefaultSkin() {
return new SegmentedButtonGroupSkin(this);
}
@Override
public String getUserAgentStylesheet() {
return SegmentedButtonGroup.class.getResource(TimiFXUI.RESOURCE + "style/components/segmented-button-group.css").toExternalForm();
}
/**
* 获取按钮列表
*
* @return 按钮列表
*/
public ObservableList<ToggleButton> getButtons() {
return buttons;
}
/**
* 添加按钮
*
* @param buttons 按钮列表
*/
public void addButton(ToggleButton... buttons) {
getButtons().addAll(buttons);
}
/**
* 添加文本按钮
*
* @param text 按钮文本
* @return 新增按钮
*/
public ToggleButton addButton(String text) {
ToggleButton button = new ToggleButton(text);
getButtons().add(button);
return button;
}
/**
* 设置当前选中按钮
*
* @param button 当前选中按钮
*/
public void setSelectedButton(ToggleButton button) {
if (button == null) {
if (isAllowClearSelection()) {
toggleGroup.selectToggle(null);
}
return;
}
if (!buttons.contains(button)) {
throw new IllegalArgumentException("button must belong to current group");
}
toggleGroup.selectToggle(button);
}
/**
* 获取当前选中按钮
*
* @return 当前选中按钮
*/
public ToggleButton getSelectedButton() {
return selectedButton.get();
}
/**
* 获取当前选中按钮属性
*
* @return 当前选中按钮属性
*/
public ReadOnlyObjectProperty<ToggleButton> selectedButtonProperty() {
return selectedButton.getReadOnlyProperty();
}
/**
* 设置当前选中下标
*
* @param selectedIndex 当前选中下标
*/
public void setSelectedIndex(int selectedIndex) {
this.selectedIndex.set(selectedIndex);
}
/**
* 获取当前选中下标
*
* @return 当前选中下标
*/
public int getSelectedIndex() {
return selectedIndex.get();
}
/**
* 获取当前选中下标属性
*
* @return 当前选中下标属性
*/
public IntegerProperty selectedIndexProperty() {
return selectedIndex;
}
/**
* 设置按钮间距
*
* @param spacing 按钮间距
*/
public void setSpacing(double spacing) {
this.spacing.set(Math.max(0, spacing));
}
/**
* 获取按钮间距
*
* @return 按钮间距
*/
public double getSpacing() {
return spacing.get();
}
/**
* 获取按钮间距属性
*
* @return 按钮间距属性
*/
public DoubleProperty spacingProperty() {
return spacing;
}
/**
* 设置布局对齐方式
*
* @param alignment 布局对齐方式
*/
public void setAlignment(Pos alignment) {
this.alignment.set(alignment);
}
/**
* 获取布局对齐方式
*
* @return 布局对齐方式
*/
public Pos getAlignment() {
return alignment.get();
}
/**
* 获取布局对齐方式属性
*
* @return 布局对齐方式属性
*/
public ObjectProperty<Pos> alignmentProperty() {
return alignment;
}
/**
* 设置是否允许清空选中
*
* @param allowClearSelection true 为允许清空选中
*/
public void setAllowClearSelection(boolean allowClearSelection) {
this.allowClearSelection.set(allowClearSelection);
if (!allowClearSelection && toggleGroup.getSelectedToggle() == null && !buttons.isEmpty()) {
toggleGroup.selectToggle(buttons.getFirst());
}
}
/**
* 获取是否允许清空选中
*
* @return true 为允许清空选中
*/
public boolean isAllowClearSelection() {
return allowClearSelection.get();
}
/**
* 获取是否允许清空选中属性
*
* @return 是否允许清空选中属性
*/
public BooleanProperty allowClearSelectionProperty() {
return allowClearSelection;
}
}
@@ -1,86 +0,0 @@
package com.imyeyu.fx.ui.components;
import javafx.collections.ListChangeListener;
import javafx.scene.control.SkinBase;
import javafx.scene.control.ToggleButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
/**
* 分段按钮组皮肤
*
* @author 夜雨
* @since 2026-06-25
*/
public class SegmentedButtonGroupSkin extends SkinBase<SegmentedButtonGroup> {
/** 保持当前选中状态 */
static final javafx.event.EventHandler<MouseEvent> EVENT_KEEP_SELECTED = event -> {
if (event.getSource() instanceof ToggleButton button && button.isSelected()) {
event.consume();
}
};
/** 按钮容器 */
private final HBox box;
/**
* 构造按钮组皮肤
*
* @param control 按钮组控件
*/
public SegmentedButtonGroupSkin(SegmentedButtonGroup control) {
super(control);
box = new HBox();
box.getStyleClass().add(SegmentedButtonGroup.STYLE_CLASS_BOX);
box.spacingProperty().bind(control.spacingProperty());
box.alignmentProperty().bind(control.alignmentProperty());
getChildren().add(box);
rebuildButtons();
control.getButtons().addListener((ListChangeListener<ToggleButton>) change -> rebuildButtons());
}
/**
* 重建按钮结构
*/
private void rebuildButtons() {
box.getChildren().setAll(getSkinnable().getButtons());
syncButtonStyleClass();
}
/**
* 同步按钮样式
*/
private void syncButtonStyleClass() {
int size = getSkinnable().getButtons().size();
for (int i = 0; i < size; i++) {
ToggleButton button = getSkinnable().getButtons().get(i);
button.setMaxWidth(Double.MAX_VALUE);
button.getStyleClass().removeAll(
SegmentedButtonGroup.STYLE_CLASS_BUTTON,
SegmentedButtonGroup.STYLE_CLASS_FIRST,
SegmentedButtonGroup.STYLE_CLASS_MIDDLE,
SegmentedButtonGroup.STYLE_CLASS_LAST,
SegmentedButtonGroup.STYLE_CLASS_SINGLE
);
button.getStyleClass().add(SegmentedButtonGroup.STYLE_CLASS_BUTTON);
if (size == 1) {
button.getStyleClass().add(SegmentedButtonGroup.STYLE_CLASS_SINGLE);
continue;
}
if (i == 0) {
button.getStyleClass().add(SegmentedButtonGroup.STYLE_CLASS_FIRST);
continue;
}
if (i == size - 1) {
button.getStyleClass().add(SegmentedButtonGroup.STYLE_CLASS_LAST);
continue;
}
button.getStyleClass().add(SegmentedButtonGroup.STYLE_CLASS_MIDDLE);
}
}
}