diff --git a/src/main/java/com/imyeyu/fx/ui/TimiFXUI.java b/src/main/java/com/imyeyu/fx/ui/TimiFXUI.java index 56d7803..6d073e1 100644 --- a/src/main/java/com/imyeyu/fx/ui/TimiFXUI.java +++ b/src/main/java/com/imyeyu/fx/ui/TimiFXUI.java @@ -23,10 +23,10 @@ public interface TimiFXUI { String RESOURCE = "/timifx/"; /** 样式文件 */ - String CSS_STYLE = RESOURCE + "style.css"; + String CSS_STYLE = RESOURCE + "style/style.css"; /** 全局字体替换 */ - String CSS_FONT = RESOURCE + "font.css"; + String CSS_FONT = RESOURCE + "style/font.css"; ResourcesMultilingual MULTILINGUAL = new ResourcesMultilingual(); diff --git a/src/main/java/com/imyeyu/fx/ui/components/DateTimePicker.java b/src/main/java/com/imyeyu/fx/ui/components/DateTimePicker.java index 79299ec..21d80c6 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/DateTimePicker.java +++ b/src/main/java/com/imyeyu/fx/ui/components/DateTimePicker.java @@ -36,6 +36,10 @@ import java.time.LocalTime; public class DateTimePicker extends Region implements TimiFXUI, TimiFXUI.Colorful { private static final String STYLE_CLASS = "date-time-picker"; + private static final String STYLE_CLASS_TIME_PANE = "date-time-picker-time-pane"; + private static final String STYLE_CLASS_TIME_BOX = "date-time-picker-time-box"; + private static final String STYLE_CLASS_NOW = "date-time-picker-now"; + private static final String STYLE_CLASS_CLEAR = "date-time-picker-clear"; /** 日期选择器 */ @Getter @@ -49,6 +53,8 @@ public class DateTimePicker extends Region implements TimiFXUI, TimiFXUI.Colorfu /** 默认构造器 */ public DateTimePicker() { + getStylesheets().add(DateTimePicker.class.getResource(RESOURCE + "style/components/date-time-picker.css").toExternalForm()); + value = new SimpleLongProperty(-1); // 时间选择 @@ -61,17 +67,17 @@ public class DateTimePicker extends Region implements TimiFXUI, TimiFXUI.Colorfu second.getStyleClass().add(CSS.BORDER_B); GridPane hmsPane = new GridPane(); + hmsPane.getStyleClass().add(STYLE_CLASS_TIME_BOX); hmsPane.addRow(0, hour, minute, second); Button now = new Button(TimiFXUI.MULTILINGUAL.text("now_tick")); now.getStyleClass().add(CSS.BORDER_L); + now.getStyleClass().add(STYLE_CLASS_NOW); timePane = new BorderPane(hmsPane); - timePane.setBorder(Stroke.EX_LEFT); - timePane.setEffect(Shadow.IMAGE); + timePane.getStylesheets().add(DateTimePicker.class.getResource(RESOURCE + "style/components/date-time-picker.css").toExternalForm()); + timePane.getStyleClass().add(STYLE_CLASS_TIME_PANE); timePane.setPrefWidth(140); - timePane.setBackground(BG.DEFAULT); - timePane.setTranslateY(-1); timePane.setBottom(now); BorderPane.setAlignment(now, Pos.CENTER_RIGHT); @@ -107,6 +113,7 @@ public class DateTimePicker extends Region implements TimiFXUI, TimiFXUI.Colorfu }; IconButton clear = new IconButton(TimiFXIcon.fromName("FAIL", GRAY)); clear.getStyleClass().add(CSS.BORDER_ALL); + clear.getStyleClass().add(STYLE_CLASS_CLEAR); clear.translateXProperty().bind(widthProperty().subtract(40)); clear.setTranslateY(15); clear.visibleProperty().bind(valueProperty().isNotNull()); diff --git a/src/main/java/com/imyeyu/fx/ui/components/Hyperlink.java b/src/main/java/com/imyeyu/fx/ui/components/Hyperlink.java index d8a35fc..4a9277c 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/Hyperlink.java +++ b/src/main/java/com/imyeyu/fx/ui/components/Hyperlink.java @@ -29,6 +29,7 @@ import java.util.regex.Pattern; */ public class Hyperlink extends Label implements TimiFXUI, TimiFXUI.Colorful { + private static final String STYLE_CLASS = "x-hyperlink"; private static final Pattern URL_PATTERN = Pattern.compile("https?://(www\\.)?[-a-zA-Z\\d@:%._+~#=]{1,256}\\.[a-zA-Z\\d()]{1,6}\\b([-a-zA-Z\\d()@:%_+.~#?&/=]*)"); /** 链接监听 */ @@ -70,10 +71,9 @@ public class Hyperlink extends Label implements TimiFXUI, TimiFXUI.Colorful { this.url = new SimpleStringProperty(url); + getStyleClass().add(STYLE_CLASS); setCursor(Cursor.HAND); setGraphic(icon); - setTextFill(FOCUSED_DEFAULT); - underlineProperty().bind(hoverProperty()); addEventFilter(MouseEvent.MOUSE_CLICKED, e -> { if (e.getButton() == MouseButton.PRIMARY) { @@ -130,4 +130,9 @@ public class Hyperlink extends Label implements TimiFXUI, TimiFXUI.Colorful { public StringProperty urlProperty() { return url; } -} \ No newline at end of file + + @Override + public String getUserAgentStylesheet() { + return Hyperlink.class.getResource(RESOURCE + "style/components/hyperlink.css").toExternalForm(); + } +} diff --git a/src/main/java/com/imyeyu/fx/ui/components/IconButton.java b/src/main/java/com/imyeyu/fx/ui/components/IconButton.java index 3453d80..2780f31 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/IconButton.java +++ b/src/main/java/com/imyeyu/fx/ui/components/IconButton.java @@ -205,4 +205,9 @@ public class IconButton extends Button implements TimiFXUI, TimiFXUI.Colorful { iconTextPadding = new Insets(tv, h, tv, h); return defaultSkin; } + + @Override + public String getUserAgentStylesheet() { + return IconButton.class.getResource(RESOURCE + "style/components/icon-button.css").toExternalForm(); + } } diff --git a/src/main/java/com/imyeyu/fx/ui/components/LabelProgressBar.java b/src/main/java/com/imyeyu/fx/ui/components/LabelProgressBar.java index 5ae2937..c2486d2 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/LabelProgressBar.java +++ b/src/main/java/com/imyeyu/fx/ui/components/LabelProgressBar.java @@ -1,5 +1,6 @@ package com.imyeyu.fx.ui.components; +import com.imyeyu.fx.ui.TimiFXUI; import com.imyeyu.java.bean.CallbackArgReturn; import javafx.beans.property.DoubleProperty; import javafx.beans.property.StringProperty; @@ -16,7 +17,7 @@ import lombok.Setter; * @since 2022-08-09 10:48 */ @Getter -public class LabelProgressBar extends StackPane { +public class LabelProgressBar extends StackPane implements TimiFXUI { private static final String STYLE_CLASS = "label-progress-bar"; @@ -32,6 +33,8 @@ public class LabelProgressBar extends StackPane { /** 默认构造 */ public LabelProgressBar() { + getStylesheets().add(LabelProgressBar.class.getResource(RESOURCE + "style/components/label-progress-bar.css").toExternalForm()); + label = new Label(); bar = new ProgressBar(); bar.prefWidthProperty().bind(widthProperty()); diff --git a/src/main/java/com/imyeyu/fx/ui/components/Pagination.java b/src/main/java/com/imyeyu/fx/ui/components/Pagination.java index 7eb8f93..f14417e 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/Pagination.java +++ b/src/main/java/com/imyeyu/fx/ui/components/Pagination.java @@ -59,6 +59,8 @@ public class Pagination extends HBox implements TimiFXUI { /** 默认构造 */ public Pagination() { + getStylesheets().add(Pagination.class.getResource(RESOURCE + "style/components/pagination.css").toExternalForm()); + // 基本参数 lp = new SimpleLongProperty(); ip = new SimpleIntegerProperty(); diff --git a/src/main/java/com/imyeyu/fx/ui/components/ProgressSlider.java b/src/main/java/com/imyeyu/fx/ui/components/ProgressSlider.java index 21000b4..db262ad 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/ProgressSlider.java +++ b/src/main/java/com/imyeyu/fx/ui/components/ProgressSlider.java @@ -52,4 +52,9 @@ public class ProgressSlider extends Slider implements TimiFXUI { } return skin; } + + @Override + public String getUserAgentStylesheet() { + return ProgressSlider.class.getResource(RESOURCE + "style/components/progress-slider.css").toExternalForm(); + } } diff --git a/src/main/java/com/imyeyu/fx/ui/components/SegmentedButtonGroup.java b/src/main/java/com/imyeyu/fx/ui/components/SegmentedButtonGroup.java new file mode 100644 index 0000000..1d9481e --- /dev/null +++ b/src/main/java/com/imyeyu/fx/ui/components/SegmentedButtonGroup.java @@ -0,0 +1,365 @@ +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 buttons; + + /** 按钮组 */ + private final ToggleGroup toggleGroup; + + /** 当前选中按钮 */ + private final ReadOnlyObjectWrapper selectedButton; + + /** 当前选中下标 */ + private final IntegerProperty selectedIndex; + + /** 按钮间距 */ + private final DoubleProperty spacing; + + /** 布局对齐方式 */ + private final ObjectProperty 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) 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 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 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 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; + } +} diff --git a/src/main/java/com/imyeyu/fx/ui/components/SegmentedButtonGroupSkin.java b/src/main/java/com/imyeyu/fx/ui/components/SegmentedButtonGroupSkin.java new file mode 100644 index 0000000..7c8330a --- /dev/null +++ b/src/main/java/com/imyeyu/fx/ui/components/SegmentedButtonGroupSkin.java @@ -0,0 +1,86 @@ +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 { + + /** 保持当前选中状态 */ + static final javafx.event.EventHandler 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) 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); + } + } +} diff --git a/src/main/java/com/imyeyu/fx/ui/components/SelectableLabel.java b/src/main/java/com/imyeyu/fx/ui/components/SelectableLabel.java index 3108c86..b94659d 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/SelectableLabel.java +++ b/src/main/java/com/imyeyu/fx/ui/components/SelectableLabel.java @@ -102,6 +102,11 @@ public class SelectableLabel extends TextArea implements TimiFXUI, TimiFXUI.Colo return defaultSkin; } + @Override + public String getUserAgentStylesheet() { + return SelectableLabel.class.getResource(RESOURCE + "style/components/selectable-label.css").toExternalForm(); + } + /** * 绑定文本样式 * diff --git a/src/main/java/com/imyeyu/fx/ui/components/TabPane.java b/src/main/java/com/imyeyu/fx/ui/components/TabPane.java index 64931e4..8cbb41b 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/TabPane.java +++ b/src/main/java/com/imyeyu/fx/ui/components/TabPane.java @@ -26,6 +26,11 @@ import java.util.List; @Getter public class TabPane extends javafx.scene.control.TabPane implements TimiFXUI, TimiFXUI.Colorful { + @Override + public String getUserAgentStylesheet() { + return TabPane.class.getResource(RESOURCE + "style/components/tab-pane.css").toExternalForm(); + } + @Override protected Skin createDefaultSkin() { Skin skin = super.createDefaultSkin(); diff --git a/src/main/java/com/imyeyu/fx/ui/components/TextAreaEditor.java b/src/main/java/com/imyeyu/fx/ui/components/TextAreaEditor.java index fe69024..78e33f1 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/TextAreaEditor.java +++ b/src/main/java/com/imyeyu/fx/ui/components/TextAreaEditor.java @@ -51,6 +51,7 @@ import lombok.Getter; import java.util.HashMap; import java.util.Map; +import java.util.Objects; /** * 复杂文本域编辑器,显示按钮操作文本域,文本域显示行号,可自定义操作功能 @@ -258,6 +259,11 @@ public class TextAreaEditor extends TextArea implements TimiFXUI { }); } + @Override + public String getUserAgentStylesheet() { + return Objects.requireNonNull(TextAreaEditor.class.getResource(RESOURCE + "style/components/text-area-editor.css")).toExternalForm(); + } + @Override protected Skin createDefaultSkin() { TextAreaEditSkin skin = new TextAreaEditSkin(this); diff --git a/src/main/java/com/imyeyu/fx/ui/components/TimePicker.java b/src/main/java/com/imyeyu/fx/ui/components/TimePicker.java index b924d05..a718394 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/TimePicker.java +++ b/src/main/java/com/imyeyu/fx/ui/components/TimePicker.java @@ -29,6 +29,9 @@ import java.time.LocalDateTime; public class TimePicker extends HBox implements TimiFXUI { private static final String STYLE_CLASS = "time-picker"; + private static final String STYLE_CLASS_POPUP = "time-picker-popup"; + private static final String STYLE_CLASS_TIME_BOX = "time-picker-time-box"; + private static final String STYLE_CLASS_NOW = "time-picker-now"; private final Popup popup; private final TextField textField; @@ -39,6 +42,8 @@ public class TimePicker extends HBox implements TimiFXUI { /** 默认构造器 */ public TimePicker() { + getStylesheets().add(TimePicker.class.getResource(RESOURCE + "style/components/time-picker.css").toExternalForm()); + value = new SimpleIntegerProperty(-1); textField = new TextField(); @@ -61,14 +66,16 @@ public class TimePicker extends HBox implements TimiFXUI { // 此刻 Button now = new Button(TimiFXUI.MULTILINGUAL.text("now")); now.getStyleClass().add(CSS.BORDER_L); + now.getStyleClass().add(STYLE_CLASS_NOW); + HBox timeBox = new HBox(hour, minute, second); + timeBox.getStyleClass().add(STYLE_CLASS_TIME_BOX); BorderPane root = new BorderPane(); + root.getStylesheets().add(TimePicker.class.getResource(RESOURCE + "style/components/time-picker.css").toExternalForm()); + root.getStyleClass().add(STYLE_CLASS_POPUP); BorderPane.setAlignment(now, Pos.CENTER_RIGHT); - root.setEffect(Shadow.POPUP); - root.setBorder(Stroke.DEFAULT); root.setPrefSize(140, 211); - root.setBackground(BG.DEFAULT); - root.setCenter(new HBox(hour, minute, second)); + root.setCenter(timeBox); root.setBottom(now); popup = new Popup(); diff --git a/src/main/java/com/imyeyu/fx/ui/components/ToggleIcon.java b/src/main/java/com/imyeyu/fx/ui/components/ToggleIcon.java index c597ca7..d5094ef 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/ToggleIcon.java +++ b/src/main/java/com/imyeyu/fx/ui/components/ToggleIcon.java @@ -139,6 +139,11 @@ public class ToggleIcon extends ToggleButton implements TimiFXUI { }, textProperty(), autoSize, skinProperty())); } + @Override + public String getUserAgentStylesheet() { + return ToggleIcon.class.getResource(RESOURCE + "style/components/toggle-icon.css").toExternalForm(); + } + /** * 添加按钮背景 * diff --git a/src/main/java/com/imyeyu/fx/ui/components/TrayFX.java b/src/main/java/com/imyeyu/fx/ui/components/TrayFX.java index ffc15b6..a2b56fa 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/TrayFX.java +++ b/src/main/java/com/imyeyu/fx/ui/components/TrayFX.java @@ -106,7 +106,7 @@ public final class TrayFX implements TimiFXUI { root = new StackPane(); Scene scene = new Scene(root); - scene.getStylesheets().addAll(CSS_STYLE, CSS_FONT); + scene.getStylesheets().addAll(CSS_STYLE, CSS_FONT, RESOURCE + "style/components/tray-menu.css"); scene.setFill(null); stage = new Stage(); stage.setWidth(1); diff --git a/src/main/java/com/imyeyu/fx/ui/components/navigation/Navigation.java b/src/main/java/com/imyeyu/fx/ui/components/navigation/Navigation.java index 6a5d662..6b12fe8 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/navigation/Navigation.java +++ b/src/main/java/com/imyeyu/fx/ui/components/navigation/Navigation.java @@ -79,12 +79,15 @@ public class Navigation extends ScrollPane implements TimiFXUI { initItemsListener(); } + @Override + public String getUserAgentStylesheet() { + return Navigation.class.getResource(RESOURCE + "style/components/navigation.css").toExternalForm(); + } + /** * 初始化视图 */ private void initView() { - contentBox.setBorder(Stroke.BOTTOM); - getStyleClass().addAll("navigation", "sp-border"); setMaxWidth(Double.MAX_VALUE); setVbarPolicy(ScrollBarPolicy.NEVER); diff --git a/src/main/java/com/imyeyu/fx/ui/components/navigation/NavigationGroup.java b/src/main/java/com/imyeyu/fx/ui/components/navigation/NavigationGroup.java index ad2d1d1..7057f51 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/navigation/NavigationGroup.java +++ b/src/main/java/com/imyeyu/fx/ui/components/navigation/NavigationGroup.java @@ -54,6 +54,7 @@ public class NavigationGroup extends TitledPane { structureChangedAction = () -> {}; rawTitle = ""; + getStylesheets().add(NavigationGroup.class.getResource("/timifx/style/components/navigation.css").toExternalForm()); contentBox.setPadding(Insets.EMPTY); setContent(contentBox); getStyleClass().add("group-pane"); diff --git a/src/main/resources/timifx/style/base.css b/src/main/resources/timifx/style/base.css new file mode 100644 index 0000000..678bb74 --- /dev/null +++ b/src/main/resources/timifx/style/base.css @@ -0,0 +1,68 @@ +/****************************************************************************** + * * + * Pixel Size to Font Size * + * * + * 1px: 0.083333em * + * 2px: 0.166667em * + * 3px: 0.25em * + * 4px: 0.333333em * + * 5px: 0.416667em * + * 6px: 0.5em * + * 7px: 0.583333em * + * 8px: 0.666667em * + * 9px: 0.75em * + * 10px: 0.833333em * + * 11px: 0.916667em * + * 12px: 1.0em * + * * + ******************************************************************************/ + +/* + - TimiFX 的样式表 - + + 1. 主体颜色为灰色,辅助色为靛青 #177CB0 + 2. style.css 仅保留导入入口 + 3. 基础控件、布局、工具类、组件样式分文件维护 + +*/ +* { + -fx-tab-size: 4; + -fx-font-smoothing-type: gray; +} +.root { + -timi-fx-color: #177CB0; + -timi-fx-icon-color: #333; + -timi-fx-border-color: #B5B5B5; + -timi-fx-popup-shadow: dropshadow(three-pass-box, rgba(0, 0, 0, .3), 5, 0, 0, 1); + -timi-fx-opacity-hover: .7; + -timi-fx-selected-color: #7CC4FF; + -timi-fx-opacity-disabled: .4; + + -fx-accent: -timi-fx-color; + -fx-focus-color: transparent; + -fx-focused-base: transparent; + -fx-text-box-border: -timi-fx-border-color; + -fx-faint-focus-color: transparent; + -fx-shadow-highlight-color: transparent; + -fx-cell-focus-inner-border: transparent; + -fx-control-inner-background: #FFF; +} +/* --------------------------- 聚焦边距 --------------------------- */ +.text-area:focused, +.text-field:focused { + -fx-background-insets: 1, 2; +} +.button:focused { + -fx-background-insets: 0; +} +.list-view:focused { + -fx-background-insets: 0, 0, 2; +} +.table-view:focused { + -fx-background-insets: 0, 0, 1; +} +/* --------------------------- 只读 --------------------------- */ +.text-area:readonly, +.text-field:readonly { + -fx-text-fill: #666; +} diff --git a/src/main/resources/timifx/style/components/date-time-picker.css b/src/main/resources/timifx/style/components/date-time-picker.css new file mode 100644 index 0000000..a8f6c67 --- /dev/null +++ b/src/main/resources/timifx/style/components/date-time-picker.css @@ -0,0 +1,19 @@ +.date-time-picker { + -fx-pref-width: 200; +} +.date-time-picker-time-pane { + -fx-border-width: 0 0 0 1; + -fx-border-color: -timi-fx-border-color; + -fx-background-color: #F4F4F4; + -fx-translate-y: -1; + -fx-effect: dropshadow(three-pass-box, rgba(0, 0, 0, .25), 4, 0, 0, 1); +} +.date-time-picker-time-box { + -fx-hgap: 0; +} +.date-time-picker-now { + -fx-alignment: center-right; +} +.date-time-picker-clear { + -fx-background-color: #F4F4F4; +} diff --git a/src/main/resources/timifx/style/components/hyperlink.css b/src/main/resources/timifx/style/components/hyperlink.css new file mode 100644 index 0000000..43b4e78 --- /dev/null +++ b/src/main/resources/timifx/style/components/hyperlink.css @@ -0,0 +1,6 @@ +.x-hyperlink { + -fx-text-fill: #177CB0; +} +.x-hyperlink:hover { + -fx-underline: true; +} diff --git a/src/main/resources/timifx/style/components/icon-button.css b/src/main/resources/timifx/style/components/icon-button.css new file mode 100644 index 0000000..3515714 --- /dev/null +++ b/src/main/resources/timifx/style/components/icon-button.css @@ -0,0 +1,4 @@ +.icon-button { + -fx-border-width: 1; + -fx-border-color: transparent; +} diff --git a/src/main/resources/timifx/style/components/label-progress-bar.css b/src/main/resources/timifx/style/components/label-progress-bar.css new file mode 100644 index 0000000..7d432ba --- /dev/null +++ b/src/main/resources/timifx/style/components/label-progress-bar.css @@ -0,0 +1,6 @@ +.label-progress-bar .bar { + -fx-background-color: #7ACAF4; +} +.label-progress-bar:indeterminate .bar { + -fx-background-color: linear-gradient(to left, transparent, #7ACAF4); +} diff --git a/src/main/resources/timifx/style/components/navigation.css b/src/main/resources/timifx/style/components/navigation.css new file mode 100644 index 0000000..23ab7c7 --- /dev/null +++ b/src/main/resources/timifx/style/components/navigation.css @@ -0,0 +1,33 @@ +.navigation { + -fx-border-width: 1; + -fx-border-color: -timi-fx-border-color; +} +.navigation .navigation-button { + -fx-padding: .25em .833333em; /* 3 10 */ + -fx-border-width: 0; + -fx-border-color: -timi-fx-border-color; +} +.navigation .navigation-button.after-group { + -fx-border-width: 1 0 0 0; +} +.navigation .navigation-button:hover { + -fx-background-color: #CCC; +} +.navigation .navigation-button:selected { + -fx-text-fill: #FFF; + -fx-background-color: -timi-fx-color; +} +.navigation .navigation-button:disabled { + -fx-opacity: -timi-fx-opacity-disabled; +} + +.group-pane { + -fx-animated: false; + -fx-border-width: 1 0 0 0; +} +.group-pane.bottom-line { + -fx-border-width: 1 0 1 0; +} +.group-pane .content { + -fx-padding: 0; +} diff --git a/src/main/resources/timifx/style/components/pagination.css b/src/main/resources/timifx/style/components/pagination.css new file mode 100644 index 0000000..da0c70e --- /dev/null +++ b/src/main/resources/timifx/style/components/pagination.css @@ -0,0 +1,6 @@ +.x-pagination .button, +.x-pagination .toggle-button { + -fx-padding: 0; + -fx-pref-width: 28; + -fx-pref-height: 26; +} diff --git a/src/main/resources/timifx/style/components/progress-slider.css b/src/main/resources/timifx/style/components/progress-slider.css new file mode 100644 index 0000000..26870ba --- /dev/null +++ b/src/main/resources/timifx/style/components/progress-slider.css @@ -0,0 +1,7 @@ +.progress-slider .track { + -fx-padding: 0; +} +.progress-slider .progress-bar .bar { + -fx-pref-height: 6; + -fx-background-insets: 0; +} diff --git a/src/main/resources/timifx/style/components/segmented-button-group.css b/src/main/resources/timifx/style/components/segmented-button-group.css new file mode 100644 index 0000000..062adf6 --- /dev/null +++ b/src/main/resources/timifx/style/components/segmented-button-group.css @@ -0,0 +1,38 @@ +.segmented-button-group { + -fx-skin: "com.imyeyu.fx.ui.components.SegmentedButtonGroupSkin"; +} +.segmented-button-group .segmented-button-group-box { + -fx-spacing: 0; + -fx-alignment: center-left; +} +.segmented-button-group .segmented-button-group-button { + -fx-background-radius: 0; + -fx-border-radius: 0; + -fx-border-width: 1 1 1 0; + -fx-padding: .333333em 1em; /* 4 12 */ + -fx-background-color: #FFF; +} +.segmented-button-group .segmented-button-group-button.first, +.segmented-button-group .segmented-button-group-button.single { + -fx-border-width: 1; +} +.segmented-button-group .segmented-button-group-button.single { + -fx-background-radius: .083333em; + -fx-border-radius: .083333em; +} +.segmented-button-group .segmented-button-group-button.first { + -fx-background-radius: .083333em 0 0 .083333em; + -fx-border-radius: .083333em 0 0 .083333em; +} +.segmented-button-group .segmented-button-group-button.last { + -fx-background-radius: 0 .083333em .083333em 0; + -fx-border-radius: 0 .083333em .083333em 0; +} +.segmented-button-group .segmented-button-group-button:hover { + -fx-background-color: #F4F9FC; +} +.segmented-button-group .segmented-button-group-button:selected { + -fx-background-color: -timi-fx-color; + -fx-border-color: -timi-fx-color; + -fx-text-fill: #FFF; +} diff --git a/src/main/resources/timifx/style/components/selectable-label.css b/src/main/resources/timifx/style/components/selectable-label.css new file mode 100644 index 0000000..a58e48a --- /dev/null +++ b/src/main/resources/timifx/style/components/selectable-label.css @@ -0,0 +1,7 @@ +.selectable-label * { + -fx-background-color: transparent; +} +.selectable-label .scroll-pane { + -fx-hbar-policy: never; + -fx-vbar-policy: never; +} diff --git a/src/main/resources/timifx/style/components/tab-pane.css b/src/main/resources/timifx/style/components/tab-pane.css new file mode 100644 index 0000000..c0c1b22 --- /dev/null +++ b/src/main/resources/timifx/style/components/tab-pane.css @@ -0,0 +1,35 @@ +.tab-pane { + -fx-border-color: -timi-fx-border-color; +} +.tab-pane > .tab-header-area > .headers-region > .tab { + -fx-padding: 0 0 0 .5em; + -fx-background-insets: 0 0 1 0, 0 1 1 0, 1 2 1 1; + -fx-background-radius: 0, 0, 0; +} +.tab-pane > .tab-header-area > .headers-region > .tab:selected { + -fx-background-insets: 0 1 0 0, 0 1 0 0; +} +.tab-pane > .tab-header-area > .headers-region > .tab > .tab-container > .tab-label { + -fx-padding: 0 .5em 0 0; +} +.tab-pane > .tab-header-area > .control-buttons-tab { + -fx-padding: 0; +} +.tab-pane > .tab-header-area > .control-buttons-tab > .container { + -fx-padding: 0; +} +.tab-pane > .tab-header-area > .control-buttons-tab > .container > .tab-down-button { + -fx-padding: 0 .5em; + -fx-background-insets: 0; + -fx-background-radius: 0; +} +.tab-pane > .tab-header-area > .control-buttons-tab > .container > .tab-down-button > .arrow { + -fx-shape: "M301.5,393.5v1h1v1h1v1h1v1h1v1h1v-1h1v-1h1v-1h1v-1h1v-1Z"; + -fx-min-width: 9; + -fx-min-height: 5; + -fx-pref-width: 9; + -fx-pref-height: 5; +} +.tab-pane:top > .tab-header-area { + -fx-padding: 0; +} diff --git a/src/main/resources/timifx/style/components/text-area-editor.css b/src/main/resources/timifx/style/components/text-area-editor.css new file mode 100644 index 0000000..f699771 --- /dev/null +++ b/src/main/resources/timifx/style/components/text-area-editor.css @@ -0,0 +1,7 @@ +.text-area-editor .find-field { + -fx-background-color: #FFF; + -fx-background-insets: 0; +} +.text-area-editor .replace-field { + -fx-padding: .25em .333333em .25em 1.25em; /* 3 4 3 15 */ +} diff --git a/src/main/resources/timifx/style/components/time-picker.css b/src/main/resources/timifx/style/components/time-picker.css new file mode 100644 index 0000000..b1e7aa5 --- /dev/null +++ b/src/main/resources/timifx/style/components/time-picker.css @@ -0,0 +1,15 @@ +.time-picker .text-field:readonly { + -fx-text-fill: #666; +} +.time-picker-popup { + -fx-effect: dropshadow(three-pass-box, rgba(0, 0, 0, .3), 5, 0, 0, 1); + -fx-border-width: 1; + -fx-border-color: -timi-fx-border-color; + -fx-background-color: #F4F4F4; +} +.time-picker-time-box { + -fx-spacing: 0; +} +.time-picker-now { + -fx-alignment: center-right; +} diff --git a/src/main/resources/timifx/style/components/toggle-icon.css b/src/main/resources/timifx/style/components/toggle-icon.css new file mode 100644 index 0000000..b8ffb7d --- /dev/null +++ b/src/main/resources/timifx/style/components/toggle-icon.css @@ -0,0 +1,6 @@ +.toggle-icon:disabled { + -fx-opacity: -timi-fx-opacity-disabled; +} +.toggle-icon:disabled .icon { + -fx-opacity: -timi-fx-opacity-disabled; +} diff --git a/src/main/resources/timifx/style/components/tray-menu.css b/src/main/resources/timifx/style/components/tray-menu.css new file mode 100644 index 0000000..53311ab --- /dev/null +++ b/src/main/resources/timifx/style/components/tray-menu.css @@ -0,0 +1,3 @@ +.tray-menu { + -fx-border-width: 0; +} diff --git a/src/main/resources/timifx/style.css b/src/main/resources/timifx/style/controls.css similarity index 51% rename from src/main/resources/timifx/style.css rename to src/main/resources/timifx/style/controls.css index efbcc73..583b118 100644 --- a/src/main/resources/timifx/style.css +++ b/src/main/resources/timifx/style/controls.css @@ -1,81 +1,4 @@ -/****************************************************************************** - * * - * Pixel Size to Font Size * - * * - * 1px: 0.083333em * - * 2px: 0.166667em * - * 3px: 0.25em * - * 4px: 0.333333em * - * 5px: 0.416667em * - * 6px: 0.5em * - * 7px: 0.583333em * - * 8px: 0.666667em * - * 9px: 0.75em * - * 10px: 0.833333em * - * 11px: 0.916667em * - * 12px: 1.0em * - * * - ******************************************************************************/ - -/* - - TimiFX 的样式表 - - - 1. 主体颜色为灰色,辅助色为靛青 #177CB0 - 2. 基本组件在前,复杂组件在后,公共类最后 - -*/ -* { - -fx-tab-size: 4; - -fx-font-smoothing-type: gray; -} -.root { - -timi-fx-color: #177CB0; - -timi-fx-icon-color: #333; - -timi-fx-border-color: #B5B5B5; - -timi-fx-popup-shadow: dropshadow(three-pass-box, rgba(0, 0, 0, .3), 5, 0, 0, 1); - -timi-fx-opacity-hover: .7; - -timi-fx-selected-color: #7CC4FF; - -timi-fx-opacity-disabled: .4; - - -fx-accent: -timi-fx-color; - -fx-focus-color: transparent; - -fx-focused-base: transparent; - -fx-text-box-border: -timi-fx-border-color; - -fx-faint-focus-color: transparent; - -fx-shadow-highlight-color: transparent; - -fx-cell-focus-inner-border: transparent; - -fx-control-inner-background: #FFF; -} -/* --------------------------- 聚焦边距 --------------------------- */ -.text-area:focused, -.text-field:focused { - -fx-background-insets: 1, 2; -} -.button:focused { - -fx-background-insets: 0; -} -.list-view:focused { - -fx-background-insets: 0, 0, 2; -} -.table-view:focused { - -fx-background-insets: 0, 0, 1; -} -/* --------------------------- 只读 --------------------------- */ -.text-area:readonly, -.text-field:readonly, -.time-picker:readonly { - -fx-text-fill: #666; -} /* --------------------------- 控件 --------------------------- */ -/* 可选标签 */ -.selectable-label * { - -fx-background-color: transparent; -} -.selectable-label .scroll-pane { - -fx-hbar-policy: never; - -fx-vbar-policy: never; -} -/* 按钮 */ .button { -fx-padding: .25em .833333em; /* 3 10 */ -fx-border-width: 1; @@ -95,19 +18,6 @@ .toggle-button:selected { -fx-background-color: -timi-fx-selected-color; } -/* 图标按钮 */ -.icon-button { - -fx-border-width: 1; - -fx-border-color: transparent; -} -/* 切换图标按钮 */ -.toggle-icon:disabled { - -fx-opacity: -timi-fx-opacity-disabled; -} -.toggle-icon:disabled .icon { - -fx-opacity: -timi-fx-opacity-disabled; -} -/* 文本框 */ .text-field { -fx-padding: .25em .333333em; /* 3 4 */ -fx-border-width: 1; @@ -116,7 +26,6 @@ -fx-background-insets: 0; -fx-background-radius: 0; } -/* 文本域 */ .text-area { -fx-border-width: 1; -fx-border-color: -timi-fx-border-color; @@ -133,15 +42,6 @@ .text-area .scroll-pane { -fx-padding: 0; } -/* 文本域编辑器 */ -.text-area-editor .find-field { - -fx-background-color: #FFF; - -fx-background-insets: 0; -} -.text-area-editor .replace-field { - -fx-padding: .25em .333333em .25em 1.25em; /* 3 4 3 15 */ -} -/* 滑动选择 */ .slider > .track { -fx-padding: .25em; -fx-background-radius: 0; @@ -156,9 +56,8 @@ -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color; -fx-background-insets: 0 0 -1 0, 0, 1, 2; } -/* 树形结构 */ .tree-view { - -fx-padding: 0; + -fx-padding: 0; } .tree-view .tree-disclosure-node { -fx-padding: .166667em .583333em 0 .333333em; @@ -171,7 +70,6 @@ -fx-pref-width: 5; -fx-pref-height: 9; } -/* 列表 */ .list-view { -fx-padding: 0; -fx-border-width: 1; @@ -183,7 +81,6 @@ -fx-background-color: -fx-control-inner-background; -fx-background-insets: 0; } -/* 表格 */ .table-view { -fx-padding: 0; -fx-border-width: 1; @@ -205,9 +102,7 @@ .table-view .text-field:focused { -fx-background-insets: 1; } -/* 下拉选择,菜单按钮,颜色选择,日期选择 */ .combo-box, -.menu-button, .date-picker, .color-picker { -fx-border-width: 1; @@ -227,12 +122,11 @@ -fx-padding: .25em; } .menu-button.icon > .label { - -fx-padding: .166667em .083333em .25em .333333em; /* 2 1 3 4 */ + -fx-padding: .166667em .083333em .25em .333333em; /* 2 1 3 4 */ } .menu-button.empty > .label { - -fx-padding: 0; + -fx-padding: 0; } -/* 下拉箭头 */ .combo-box-base .text-field { -fx-border-width: 0; } @@ -277,7 +171,6 @@ -fx-border-color: -timi-fx-border-color; -fx-background-color: -fx-background; } -/* 单选,多选 */ .check-box, .radio-button, .check-box:hover, @@ -302,7 +195,6 @@ -fx-pref-width: 10; -fx-pref-height: 8; } -/* 弹出菜单 */ .context-menu { -fx-color: -fx-base; -fx-effect: -timi-fx-popup-shadow; @@ -313,33 +205,27 @@ -fx-background-insets: 0; } .context-menu .menu-item:focused { - -fx-background-color: -timi-fx-selected-color; + -fx-background-color: -timi-fx-selected-color; } .context-menu .menu-item:focused .label { - -fx-text-fill: #333; + -fx-text-fill: #333; } .context-menu .separator { - -fx-padding: 0; + -fx-padding: 0; } -/* 托盘菜单 */ -.tray-menu { - -fx-border-width: 0; -} -/* 菜单 */ .menu > .right-container > .arrow { - -fx-shape: "M303.5,391.5h1v1h1v1h1v1h1v1h1v1h-1v1h-1v1h-1v1h-1v1h-1Z"; + -fx-shape: "M303.5,391.5h1v1h1v1h1v1h1v1h1v1h-1v1h-1v1h-1v1h-1v1h-1Z"; -fx-min-width: 5; -fx-min-height: 9; -fx-pref-width: 5; -fx-pref-height: 9; } .radio-menu-item:checked > .left-container > .radio { - -fx-shape: "M3,7v2h1v1h1v1h1v1h1v-1h1v-1h1V9h1V8h1V7h1V6h1V4h-2v1h-1v1H9v1H8v1H7v1H6V8H5V7H3z"; + -fx-shape: "M3,7v2h1v1h1v1h1v1h1v-1h1v-1h1V9h1V8h1V7h1V6h1V4h-2v1h-1v1H9v1H8v1H7v1H6V8H5V7H3z"; } .check-menu-item:checked > .left-container > .check { - -fx-shape: "M3,7v2h1v1h1v1h1v1h1v-1h1v-1h1V9h1V8h1V7h1V6h1V4h-2v1h-1v1H9v1H8v1H7v1H6V8H5V7H3z"; + -fx-shape: "M3,7v2h1v1h1v1h1v1h1v-1h1v-1h1V9h1V8h1V7h1V6h1V4h-2v1h-1v1H9v1H8v1H7v1H6V8H5V7H3z"; } -/* 日期选择 */ .date-picker:focused { -fx-background-color: -fx-control-inner-background; } @@ -386,7 +272,6 @@ .date-picker-popup > * > .spinner > .button > .right-arrow { -fx-shape: "M306,394h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-2v1h1v1h1v1h1v1h1v1h1v1h1v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1h2v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1Z"; } -/* 颜色选择器 */ .color-palette { -fx-translate-y: -1; -fx-background-radius: 0; @@ -411,7 +296,6 @@ -fx-background-insets: 12px 0 0 0, 13px 1px 1px 1px; -fx-background-radius: 0; } -/* 滚动条 */ .scroll-bar { -fx-padding: 0; } @@ -442,7 +326,6 @@ -fx-background-insets: 0; -fx-background-radius: 0; } -/* 列表滚动条 */ .tree-view .scroll-bar:vertical, .list-view .scroll-bar:vertical, .table-view .scroll-bar:vertical { @@ -450,7 +333,6 @@ -fx-border-color: -timi-fx-border-color; -fx-background-color: -fx-background; } - .tree-view .scroll-bar:horizontal, .list-view .scroll-bar:horizontal, .table-view .scroll-bar:horizontal { @@ -458,7 +340,6 @@ -fx-border-color: -timi-fx-border-color; -fx-background-color: -fx-background; } -/* 进度条 */ .progress-bar { -fx-padding: 1; -fx-border-width: 1; @@ -480,29 +361,6 @@ .progress-bar:indeterminate .bar { -fx-background-color: linear-gradient(to left, transparent, -timi-fx-color); } -/* 标签进度 */ -.label-progress-bar .bar { - -fx-background-color: #7ACAF4; -} -.label-progress-bar:indeterminate .bar { - -fx-background-color: linear-gradient(to left, transparent, #7ACAF4); -} -/* 滑动选择 - 进度 */ -.progress-slider .track { - -fx-padding: 0; -} -.progress-slider .progress-bar .bar { - -fx-pref-height: 6; - -fx-background-insets: 0; -} -/* 分页 */ -.x-pagination .button, -.x-pagination .toggle-button { - -fx-padding: 0; - -fx-pref-width: 28; - -fx-pref-height: 26; -} -/* 标题组件 */ .titled-pane { -fx-animate: false; -fx-border-width: 1; @@ -540,7 +398,6 @@ -fx-border-width: 0 0 1 0; -fx-background-insets: 0, 0; } -/* 图表 */ .chart { -fx-padding: 0 0 6 0; } @@ -548,287 +405,22 @@ -fx-padding: 0 24 4 6; } .chart-legend { - -fx-padding: 4 8; - -fx-background-radius: 0, 0; + -fx-padding: 4 8; + -fx-background-radius: 0, 0; } .area-legend-symbol { -fx-padding: 6; -fx-background-radius: 0; -fx-background-insets: 0, 6; } -/* 导航 */ -.navigation { - -fx-border-width: 1; - -fx-border-color: -timi-fx-border-color; -} -.navigation .group-pane { - -fx-animated: false; - -fx-border-width: 1 0 0 0; -} -.navigation .group-pane.bottom-line { - -fx-border-width: 1 0 1 0; -} -.navigation .group-pane .content { - -fx-padding: 0; -} -.navigation .navigation-button { - -fx-padding: .25em .833333em; /* 3 10 */ - -fx-border-width: 0; - -fx-border-color: -timi-fx-border-color; -} -.navigation .navigation-button.after-group { /* 紧挨着上一个导航组时,添加上边框 */ - -fx-border-width: 1 0 0 0; -} -.navigation .navigation-button:hover { - -fx-background-color: #CCC; -} -.navigation .navigation-button:selected { - -fx-text-fill: #FFF; - -fx-background-color: -timi-fx-color; -} -.navigation .navigation-button:disabled { - -fx-opacity: -timi-fx-opacity-disabled; -} -/* 可编辑表格 */ -.editable-table .table-row-cell:odd { - -fx-background-color: -fx-table-cell-border-color, -fx-control-inner-background; -} -.editable-table .table-row-cell:filled:selected, -.editable-table .table-row-cell:filled:selected:focused { - -fx-padding: 0; - -fx-text-fill: -fx-selection-bar-text; - -fx-background: -timi-fx-selected-color; - -fx-border-width: 0; - -fx-background-color: -timi-fx-selected-color; - -fx-background-insets: 0; - -fx-table-cell-border-color: -timi-fx-selected-color; -} -.editable-table .table-cell { - -fx-padding: 0; - -fx-cell-size: 0; - -fx-border-width: .083333em .083333em .083333em 0; /* 1 1 1 0 */ -} -.editable-table .table-row-cell .text-field { - -fx-background-color: -timi-fx-color, #FFF; - -fx-background-insets: 0, 0; -} -.editable-table .table-row-cell .text-field:focused { - -fx-background-insets: 0, 1; -} -/* 选项卡 */ -.tab-pane { - -fx-border-color: -timi-fx-border-color; -} -.tab-pane > .tab-header-area > .headers-region > .tab { - -fx-padding: 0 0 0 .5em; - -fx-background-insets: 0 0 1 0, 0 1 1 0, 1 2 1 1; - -fx-background-radius: 0, 0, 0; -} -.tab-pane > .tab-header-area > .headers-region > .tab:selected { - -fx-background-insets: 0 1 0 0, 0 1 0 0; -} -.tab-pane > .tab-header-area > .headers-region > .tab > .tab-container > .tab-label { - -fx-padding: 0 .5em 0 0; -} -.tab-pane > .tab-header-area > .control-buttons-tab { - -fx-padding: 0; -} -.tab-pane > .tab-header-area > .control-buttons-tab > .container { - -fx-padding: 0; -} -.tab-pane > .tab-header-area > .control-buttons-tab > .container > .tab-down-button { - -fx-padding: 0 .5em; - -fx-background-insets: 0; - -fx-background-radius: 0; -} -.tab-pane > .tab-header-area > .control-buttons-tab > .container > .tab-down-button > .arrow { - -fx-shape: "M301.5,393.5v1h1v1h1v1h1v1h1v1h1v-1h1v-1h1v-1h1v-1h1v-1Z"; - -fx-min-width: 9; - -fx-min-height: 5; - -fx-pref-width: 9; - -fx-pref-height: 5; -} -.tab-pane:top > .tab-header-area { - -fx-padding: 0; -} -/* 分割线 */ .separator > .line { - -fx-border-style: null; - -fx-border-width: 0; + -fx-border-style: null; + -fx-border-width: 0; } .separator:horizontal > .line { - -fx-padding: 0; - -fx-min-height: 1; - -fx-max-height: 1; - -fx-pref-height: 1; - -fx-background-color: -timi-fx-border-color; -} -/* --------------------------- 布局 --------------------------- */ -/* 滚动 */ -.scroll-pane { -fx-padding: 0; - -fx-background-insets: 0; -} -.scroll-pane .corner { - -fx-background-color: transparent; -} -/* 分割 */ -.split-pane { - -fx-padding: 0; - -fx-background-color: transparent; - -fx-background-insets: 0; -} -/* --------------------------- 可选类 --------------------------- */ -/* 其他 */ -.bg-tp, -.bg-tp .viewport { - -fx-background-color: transparent; - -fx-background-insets: 0; -} -.bg-white { - -fx-background-color: #FFF; -} -.bg-black { - -fx-background-color: #000; -} -.bg-default { - -fx-background-color: #F4F4F4; -} -.bg-button-static { - -fx-border-color: -timi-fx-border-color; - -fx-background-color: -fx-body-color; -} -.bg-button-static:disabled { - -fx-opacity: -timi-fx-opacity-disabled; -} -.bg-button { - -fx-border-color: -timi-fx-border-color; - -fx-background-color: -fx-body-color; -} -.bg-button:hover { - -fx-color: -fx-hover-base; -} -.bg-button:armed { - -fx-color: -fx-pressed-base; -} -.bg-button:disabled { - -fx-opacity: -timi-fx-opacity-disabled; -} -.hover-opacity:hover { - -fx-opacity: -timi-fx-opacity-hover; -} -/* 滚动面板的滚动条边框 */ -.sp-border .scroll-bar:vertical { - -fx-border-width: 0 0 0 1; - -fx-border-color: -timi-fx-border-color; -} -.sp-border .scroll-bar:horizontal { - -fx-border-width: 1 0 0 0; - -fx-border-color: -timi-fx-border-color; -} -.sp-border .corner { - -fx-border-width: 1 0 0 1; - -fx-border-color: -timi-fx-border-color; -} -/* 内边距 */ -.padding-n { - -fx-padding: 0; -} -/* 字体颜色 */ -.white { - -fx-text-fill: #FFF; -} -.red { - -fx-text-fill: #F30; -} -.brown { - -fx-text-fill: #A67D7B; -} -.black { - -fx-text-fill: #000; -} -.orange { - -fx-text-fill: #F60; -} -.yellow { - -fx-text-fill: #FF0; -} -.green { - -fx-text-fill: #393; -} -.dark-green { - -fx-text-fill: #373; -} -.gray { - -fx-text-fill: #666; -} -.blue { - -fx-text-fill: #008DCB; -} -.light-blue { - -fx-text-fill: #DDEAF0; -} -.gray-white { - -fx-text-fill: #F4F4F4; -} -.light-gray { - -fx-text-fill: #B5B5B5; -} -.dark-gray { - -fx-text-fill: #333; -} -.pink { - -fx-text-fill: #FF7A9B; -} -.transparent { - -fx-text-fill: #FFFFFF00; -} -/* 边框控制 */ -.border-all { - -fx-border-width: 1; -} -.border-n { - -fx-border-width: 0; -} -.border-t { - -fx-border-width: 1 0 0 0; -} -.border-r { - -fx-border-width: 0 1 0 0; -} -.border-b { - -fx-border-width: 0 0 1 0; -} -.border-l { - -fx-border-width: 0 0 0 1; -} -.border-tr { - -fx-border-width: 1 1 0 0; -} -.border-rb { - -fx-border-width: 0 1 1 0; -} -.border-bl { - -fx-border-width: 0 0 1 1; -} -.border-lt { - -fx-border-width: 1 0 0 1; -} -.border-trb { - -fx-border-width: 1 1 1 0; -} -.border-rbl { - -fx-border-width: 0 1 1 1; -} -.border-blt { - -fx-border-width: 1 0 1 1; -} -.border-ltr { - -fx-border-width: 1 1 0 1; -} -.border-tb { - -fx-border-width: 1 0 1 0; -} -.border-lr { - -fx-border-width: 0 1 0 1; + -fx-min-height: 1; + -fx-max-height: 1; + -fx-pref-height: 1; + -fx-background-color: -timi-fx-border-color; } diff --git a/src/main/resources/timifx/font.css b/src/main/resources/timifx/style/font.css similarity index 89% rename from src/main/resources/timifx/font.css rename to src/main/resources/timifx/style/font.css index 5b9fe89..d17127f 100644 --- a/src/main/resources/timifx/font.css +++ b/src/main/resources/timifx/style/font.css @@ -1,5 +1,5 @@ @font-face { - src: url('./MinecraftAE.ttf'); + src: url('../MinecraftAE.ttf'); } /* Minecraft AE 字体在 16 32 64 128 像素时表现为最清晰 */ @@ -19,4 +19,4 @@ .minecraft-ae { -fx-font-size: 16; -fx-font-family: 'Minecraft AE'; -} \ No newline at end of file +} diff --git a/src/main/resources/timifx/style/layout.css b/src/main/resources/timifx/style/layout.css new file mode 100644 index 0000000..c0b0a5d --- /dev/null +++ b/src/main/resources/timifx/style/layout.css @@ -0,0 +1,13 @@ +/* --------------------------- 布局 --------------------------- */ +.scroll-pane { + -fx-padding: 0; + -fx-background-insets: 0; +} +.scroll-pane .corner { + -fx-background-color: transparent; +} +.split-pane { + -fx-padding: 0; + -fx-background-color: transparent; + -fx-background-insets: 0; +} diff --git a/src/main/resources/timifx/style/style.css b/src/main/resources/timifx/style/style.css new file mode 100644 index 0000000..600fb31 --- /dev/null +++ b/src/main/resources/timifx/style/style.css @@ -0,0 +1,4 @@ +@import "base.css"; +@import "controls.css"; +@import "layout.css"; +@import "utility.css"; diff --git a/src/main/resources/timifx/style/utility.css b/src/main/resources/timifx/style/utility.css new file mode 100644 index 0000000..80a89d2 --- /dev/null +++ b/src/main/resources/timifx/style/utility.css @@ -0,0 +1,174 @@ +/* --------------------------- 可选类 --------------------------- */ +.bg-tp, +.bg-tp .viewport { + -fx-background-color: transparent; + -fx-background-insets: 0; +} +.bg-white { + -fx-background-color: #FFF; +} +.bg-black { + -fx-background-color: #000; +} +.bg-default { + -fx-background-color: #F4F4F4; +} +.bg-button-static { + -fx-border-color: -timi-fx-border-color; + -fx-background-color: -fx-body-color; +} +.bg-button-static:disabled { + -fx-opacity: -timi-fx-opacity-disabled; +} +.bg-button { + -fx-border-color: -timi-fx-border-color; + -fx-background-color: -fx-body-color; +} +.bg-button:hover { + -fx-color: -fx-hover-base; +} +.bg-button:armed { + -fx-color: -fx-pressed-base; +} +.bg-button:disabled { + -fx-opacity: -timi-fx-opacity-disabled; +} +.hover-opacity:hover { + -fx-opacity: -timi-fx-opacity-hover; +} +.sp-border .scroll-bar:vertical { + -fx-border-width: 0 0 0 1; + -fx-border-color: -timi-fx-border-color; +} +.sp-border .scroll-bar:horizontal { + -fx-border-width: 1 0 0 0; + -fx-border-color: -timi-fx-border-color; +} +.sp-border .corner { + -fx-border-width: 1 0 0 1; + -fx-border-color: -timi-fx-border-color; +} +.editable-table .table-row-cell:odd { + -fx-background-color: -fx-table-cell-border-color, -fx-control-inner-background; +} +.editable-table .table-row-cell:filled:selected, +.editable-table .table-row-cell:filled:selected:focused { + -fx-padding: 0; + -fx-text-fill: -fx-selection-bar-text; + -fx-background: -timi-fx-selected-color; + -fx-border-width: 0; + -fx-background-color: -timi-fx-selected-color; + -fx-background-insets: 0; + -fx-table-cell-border-color: -timi-fx-selected-color; +} +.editable-table .table-cell { + -fx-padding: 0; + -fx-cell-size: 0; + -fx-border-width: .083333em .083333em .083333em 0; /* 1 1 1 0 */ +} +.editable-table .table-row-cell .text-field { + -fx-background-color: -timi-fx-color, #FFF; + -fx-background-insets: 0, 0; +} +.editable-table .table-row-cell .text-field:focused { + -fx-background-insets: 0, 1; +} +.padding-n { + -fx-padding: 0; +} +.white { + -fx-text-fill: #FFF; +} +.red { + -fx-text-fill: #F30; +} +.brown { + -fx-text-fill: #A67D7B; +} +.black { + -fx-text-fill: #000; +} +.orange { + -fx-text-fill: #F60; +} +.yellow { + -fx-text-fill: #FF0; +} +.green { + -fx-text-fill: #393; +} +.dark-green { + -fx-text-fill: #373; +} +.gray { + -fx-text-fill: #666; +} +.blue { + -fx-text-fill: #008DCB; +} +.light-blue { + -fx-text-fill: #DDEAF0; +} +.gray-white { + -fx-text-fill: #F4F4F4; +} +.light-gray { + -fx-text-fill: #B5B5B5; +} +.dark-gray { + -fx-text-fill: #333; +} +.pink { + -fx-text-fill: #FF7A9B; +} +.transparent { + -fx-text-fill: #FFFFFF00; +} +.border-all { + -fx-border-width: 1; +} +.border-n { + -fx-border-width: 0; +} +.border-t { + -fx-border-width: 1 0 0 0; +} +.border-r { + -fx-border-width: 0 1 0 0; +} +.border-b { + -fx-border-width: 0 0 1 0; +} +.border-l { + -fx-border-width: 0 0 0 1; +} +.border-tr { + -fx-border-width: 1 1 0 0; +} +.border-rb { + -fx-border-width: 0 1 1 0; +} +.border-bl { + -fx-border-width: 0 0 1 1; +} +.border-lt { + -fx-border-width: 1 0 0 1; +} +.border-trb { + -fx-border-width: 1 1 1 0; +} +.border-rbl { + -fx-border-width: 0 1 1 1; +} +.border-blt { + -fx-border-width: 1 0 1 1; +} +.border-ltr { + -fx-border-width: 1 1 0 1; +} +.border-tb { + -fx-border-width: 1 0 1 0; +} +.border-lr { + -fx-border-width: 0 1 0 1; +} diff --git a/src/test/java/com/imyeyu/fx/ui/examples/component/RootLayout.java b/src/test/java/com/imyeyu/fx/ui/examples/component/RootLayout.java index f49f3e7..23667b4 100644 --- a/src/test/java/com/imyeyu/fx/ui/examples/component/RootLayout.java +++ b/src/test/java/com/imyeyu/fx/ui/examples/component/RootLayout.java @@ -5,6 +5,10 @@ import com.imyeyu.fx.ui.examples.component.sidebar.Sidebar; import com.imyeyu.fx.ui.examples.service.PageService; import com.imyeyu.inject.annotation.Component; import com.imyeyu.inject.annotation.PostConstruct; +import javafx.application.Platform; +import javafx.scene.control.Menu; +import javafx.scene.control.MenuBar; +import javafx.scene.control.MenuItem; import javafx.scene.layout.BorderPane; /** diff --git a/src/test/java/com/imyeyu/fx/ui/examples/component/sidebar/Sidebar.java b/src/test/java/com/imyeyu/fx/ui/examples/component/sidebar/Sidebar.java index 51be609..d26540b 100644 --- a/src/test/java/com/imyeyu/fx/ui/examples/component/sidebar/Sidebar.java +++ b/src/test/java/com/imyeyu/fx/ui/examples/component/sidebar/Sidebar.java @@ -41,7 +41,7 @@ public class Sidebar extends Navigation { add(new NavigationGroup(TimiFXUI.MULTILINGUAL.text("animation")).add(interpolator, smoothScroll)); // 组件 - SidebarItem[] components = {SidebarItem.DATE_TIME_PICKER, SidebarItem.EDITABLE_TABLE_CELL, SidebarItem.FILE_TREE_VIEW, SidebarItem.ICON_BUTTON, SidebarItem.ICON_PICKER, SidebarItem.LABEL_PROGRESS_BAR, SidebarItem.NAVIGATION, SidebarItem.NUMBER_FIELD, SidebarItem.PROGRESS_SLIDER, SidebarItem.SELECTABLE_LABEL, SidebarItem.TEXT_AREA_EDITOR, SidebarItem.TITLE_LABEL, SidebarItem.TOGGLE_ICON, SidebarItem.X_PAGINATION, SidebarItem.X_TAB_PANE, SidebarItem.X_TREE_VIEW}; + SidebarItem[] components = {SidebarItem.BUTTON, SidebarItem.DATE_TIME_PICKER, SidebarItem.EDITABLE_TABLE_CELL, SidebarItem.FILE_TREE_VIEW, SidebarItem.ICON_BUTTON, SidebarItem.ICON_PICKER, SidebarItem.LABEL_PROGRESS_BAR, SidebarItem.NAVIGATION, SidebarItem.NUMBER_FIELD, SidebarItem.PROGRESS_SLIDER, SidebarItem.SELECTABLE_LABEL, SidebarItem.TEXT_AREA_EDITOR, SidebarItem.TITLE_LABEL, SidebarItem.TOGGLE_ICON, SidebarItem.X_PAGINATION, SidebarItem.X_TAB_PANE, SidebarItem.X_TREE_VIEW}; Item[] componentItems = new Item[components.length]; for (int i = 0; i < componentItems.length; i++) { componentItems[i] = new Item(components[i]); diff --git a/src/test/java/com/imyeyu/fx/ui/examples/component/sidebar/SidebarItem.java b/src/test/java/com/imyeyu/fx/ui/examples/component/sidebar/SidebarItem.java index 810a658..949d52b 100644 --- a/src/test/java/com/imyeyu/fx/ui/examples/component/sidebar/SidebarItem.java +++ b/src/test/java/com/imyeyu/fx/ui/examples/component/sidebar/SidebarItem.java @@ -6,6 +6,7 @@ import com.imyeyu.fx.ui.examples.component.AbstractPane; import com.imyeyu.fx.ui.examples.view.pages.AlertDemo; import com.imyeyu.fx.ui.examples.view.pages.AnimationRendererDemo; import com.imyeyu.fx.ui.examples.view.pages.BindingsConfigDemo; +import com.imyeyu.fx.ui.examples.view.pages.ButtonDemo; import com.imyeyu.fx.ui.examples.view.pages.ExtendDemo; import com.imyeyu.fx.ui.examples.view.pages.PopupTipsDemo; import com.imyeyu.fx.ui.examples.view.pages.RunAsyncDemo; @@ -81,6 +82,9 @@ public enum SidebarItem { // ---------- 组件 ---------- + /** 按钮组 */ + BUTTON(ButtonDemo.class, "切换按钮组"), + /** 详细时间选择器 */ DATE_TIME_PICKER(DateTimePickerDemo.class, TimiFXUI.MULTILINGUAL.text("fx.example.date_time_picker")), diff --git a/src/test/java/com/imyeyu/fx/ui/examples/view/pages/ButtonDemo.java b/src/test/java/com/imyeyu/fx/ui/examples/view/pages/ButtonDemo.java new file mode 100644 index 0000000..b34434a --- /dev/null +++ b/src/test/java/com/imyeyu/fx/ui/examples/view/pages/ButtonDemo.java @@ -0,0 +1,93 @@ +package com.imyeyu.fx.ui.examples.view.pages; + +import com.imyeyu.fx.ui.TimiFXUI; +import com.imyeyu.fx.ui.components.SegmentedButtonGroup; +import com.imyeyu.fx.ui.examples.component.AbstractDemoPane; +import com.imyeyu.fx.ui.examples.component.sidebar.SidebarItem; +import com.imyeyu.inject.annotation.Component; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.control.ToggleButton; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.VBox; + +/** + * 按钮组件示例 + * + * @author 夜雨 + * @since 2026-06-25 + */ +@Component +public class ButtonDemo extends AbstractDemoPane { + + public ButtonDemo() { + title.setText(SidebarItem.BUTTON.getText()); + document.setText("暂无"); + document.setUrl(""); + source.setText("src/main/java/com/imyeyu/fx/ui/components/SegmentedButtonGroup.java"); + source.setUrl(""); + tips.setText("演示 SegmentedButtonGroup 的标准控件写法,按钮列表、选中状态和允许取消选择都是组件对外暴露的标准属性"); + + SegmentedButtonGroup defaultGroup = new SegmentedButtonGroup(); + defaultGroup.addButton("全部"); + defaultGroup.addButton("启用"); + defaultGroup.addButton("停用"); + defaultGroup.setSelectedIndex(0); + + SegmentedButtonGroup spacingGroup = new SegmentedButtonGroup(); + spacingGroup.setSpacing(6); + spacingGroup.addButton("左对齐"); + spacingGroup.addButton("居中"); + spacingGroup.addButton("右对齐"); + spacingGroup.setSelectedIndex(1); + + SegmentedButtonGroup clearableGroup = new SegmentedButtonGroup(); + clearableGroup.setAllowClearSelection(true); + clearableGroup.addButton("日"); + clearableGroup.addButton("周"); + clearableGroup.addButton("月"); + clearableGroup.addButton("年"); + clearableGroup.setSelectedIndex(2); + + Label selectedButtonText = TimiFXUI.label(); + Label selectedIndexText = TimiFXUI.label(); + selectedButtonText.setText("当前选中: " + clearableGroup.getSelectedButton().getText()); + selectedIndexText.setText("当前下标: " + clearableGroup.getSelectedIndex()); + clearableGroup.selectedButtonProperty().addListener((obs, o, button) -> { + if (button == null) { + selectedButtonText.setText("当前选中: 无"); + return; + } + selectedButtonText.setText("当前选中: " + button.getText()); + }); + clearableGroup.selectedIndexProperty().addListener((obs, o, index) -> selectedIndexText.setText("当前下标: " + index.intValue())); + + SegmentedButtonGroup customButtonGroup = new SegmentedButtonGroup(); + ToggleButton primary = new ToggleButton("概览"); + ToggleButton analysis = new ToggleButton("分析"); + ToggleButton history = new ToggleButton("历史"); + customButtonGroup.getButtons().addAll(primary, analysis, history); + customButtonGroup.setAlignment(Pos.CENTER_LEFT); + customButtonGroup.setSelectedButton(analysis); + + setCenter(new VBox() {{ + setSpacing(12); + setPadding(new Insets(20)); + getChildren().addAll( + new GridPane() {{ + setHgap(12); + setVgap(10); + + int row = 0; + addRow(row++, TimiFXUI.label("默认单选"), defaultGroup); + addRow(row++, TimiFXUI.label("带间距"), spacingGroup); + addRow(row++, TimiFXUI.label("允许取消"), clearableGroup); + addRow(row, TimiFXUI.label("外部按钮"), customButtonGroup); + }}, + selectedButtonText, + selectedIndexText + ); + }}); + } +} diff --git a/src/test/java/com/imyeyu/fx/ui/examples/view/pages/Style.java b/src/test/java/com/imyeyu/fx/ui/examples/view/pages/Style.java index 8188609..684a80d 100644 --- a/src/test/java/com/imyeyu/fx/ui/examples/view/pages/Style.java +++ b/src/test/java/com/imyeyu/fx/ui/examples/view/pages/Style.java @@ -75,7 +75,7 @@ public class Style extends AbstractPane { // 样式文件 TitleLabel styleFile = new TitleLabel(TimiFXUI.MULTILINGUAL.text("fx.example.style.file")); Label labelFile = TimiFXUI.label(TimiFXUI.MULTILINGUAL.text("source")); - Hyperlink file = new Hyperlink("https://git.imyeyu.net/Timi/timi-fx/src/master/src/main/resources/timifx/style.css"); + Hyperlink file = new Hyperlink("https://git.imyeyu.net/Timi/timi-fx/src/master/src/main/resources/timifx/style/style.css"); setCenter(new VBox() {{ setSpacing(16);