diff --git a/pom.xml b/pom.xml index 81585b0..0c9f072 100644 --- a/pom.xml +++ b/pom.xml @@ -133,12 +133,12 @@ com.imyeyu.fx timi-fx - 0.0.5 + 0.0.6 com.imyeyu.fx.icon timi-fx-icon - 0.0.2 + 0.0.3 com.imyeyu.lang diff --git a/src/main/java/com/imyeyu/fx/ui/ScreenIdentify.java b/src/main/java/com/imyeyu/fx/ui/ScreenIdentify.java index 3298d33..1cf672a 100644 --- a/src/main/java/com/imyeyu/fx/ui/ScreenIdentify.java +++ b/src/main/java/com/imyeyu/fx/ui/ScreenIdentify.java @@ -1,6 +1,6 @@ package com.imyeyu.fx.ui; -import com.imyeyu.fx.utils.ScreenFX; +import com.imyeyu.fx.utils.screen.Screens; import javafx.beans.binding.Bindings; import javafx.beans.property.BooleanProperty; import javafx.beans.property.ReadOnlyBooleanProperty; @@ -66,7 +66,7 @@ public class ScreenIdentify extends Stage implements TimiFXUI { public void showIdentify() { show(); - List screens = ScreenFX.SCREENS; + List screens = Screens.all(); for (int i = 0; i < screens.size(); i++) { showingIdentifyList.add(new Identify(screens.get(i), i)); } @@ -111,7 +111,7 @@ public class ScreenIdentify extends Stage implements TimiFXUI { getContent().setAll(text); getScene().setFill(BLACK); - getScene().getStylesheets().addAll(CSS_STYLE, CSS_FONT); + TimiFXUI.applyStyle(getScene()); sizeToScene(); } } diff --git a/src/main/java/com/imyeyu/fx/ui/TimiFXUI.java b/src/main/java/com/imyeyu/fx/ui/TimiFXUI.java index 6d073e1..5ae8306 100644 --- a/src/main/java/com/imyeyu/fx/ui/TimiFXUI.java +++ b/src/main/java/com/imyeyu/fx/ui/TimiFXUI.java @@ -1,18 +1,25 @@ package com.imyeyu.fx.ui; -import com.imyeyu.fx.utils.BgFill; -import com.imyeyu.fx.utils.BorderStroke; +import com.imyeyu.fx.utils.BorderBuilder; +import com.imyeyu.fx.utils.bg.BackgroundFillBuilder; +import com.imyeyu.fx.utils.layout.Layout; import com.imyeyu.lang.multi.ResourcesMultilingual; +import com.sun.javafx.css.StyleManager; import javafx.geometry.Insets; +import javafx.geometry.VPos; import javafx.scene.Node; +import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.SeparatorMenuItem; import javafx.scene.control.TableColumn; import javafx.scene.effect.DropShadow; import javafx.scene.layout.Background; import javafx.scene.layout.Border; +import javafx.scene.layout.GridPane; import javafx.scene.paint.Color; +import java.util.Objects; + /** * @author 夜雨 * @version 2024-04-13 14:18 @@ -22,14 +29,55 @@ public interface TimiFXUI { /** 静态资源路径 */ String RESOURCE = "/timifx/"; + String RESOURCE_STYLE = RESOURCE + "/style"; + /** 样式文件 */ - String CSS_STYLE = RESOURCE + "style/style.css"; + String CSS_STYLE = resource("style/style.css"); + + /** 控件默认样式文件 */ + String CSS_USER_AGENT = resource("style/user-agent.css"); /** 全局字体替换 */ - String CSS_FONT = RESOURCE + "style/font.css"; + String CSS_FONT = resource("style/font.css"); ResourcesMultilingual MULTILINGUAL = new ResourcesMultilingual(); + /** + * 获取静态资源路径 + * + * @param path 相对静态资源目录的路径 + * + * @return 静态资源绝对路径 + */ + private static String resource(String path) { + return Objects.requireNonNull(TimiFXUI.class.getResource(RESOURCE + path)).toExternalForm(); + } + + /** + * 应用全局样式 + * + * @param scene 场景 + * @param stylesheets 追加的样式表 + */ + static void applyStyle(Scene scene, String... stylesheets) { + if (!Boolean.TRUE.equals(scene.getProperties().get(CSS_USER_AGENT))) { + StyleManager.getInstance().addUserAgentStylesheet(scene, CSS_USER_AGENT); + scene.getProperties().put(CSS_USER_AGENT, true); + } + if (!scene.getStylesheets().contains(CSS_STYLE)) { + scene.getStylesheets().add(CSS_STYLE); + } + if (!scene.getStylesheets().contains(CSS_FONT)) { + scene.getStylesheets().add(CSS_FONT); + } + for (String stylesheet : stylesheets) { + if (!scene.getStylesheets().contains(stylesheet)) { + scene.getStylesheets().add(stylesheet); + } + } + } + + /** * * @@ -122,58 +170,58 @@ public interface TimiFXUI { interface Stroke { /** 透明边框 */ - Border TP = new BorderStroke(Colorful.TRANSPARENT).build(); + Border TP = BorderBuilder.solid(Colorful.TRANSPARENT).build(); /** 默认边框 */ - Border DEFAULT = new BorderStroke(Colorful.BORDER).build(); + Border DEFAULT = BorderBuilder.solid(Colorful.BORDER).build(); /** 禁用边框 */ - Border DISABLE = new BorderStroke("#E1E1E1").build(); + Border DISABLE = BorderBuilder.solid("#E1E1E1").build(); /** 聚焦边框 */ - Border FOCUSED = new BorderStroke(Colorful.BORDER).build(); + Border FOCUSED = BorderBuilder.solid(Colorful.BORDER).build(); /** 上边框 */ - Border TOP = new BorderStroke(Colorful.BORDER).top().build(); + Border TOP = BorderBuilder.solid(Colorful.BORDER).top().build(); /** 左边框 */ - Border LEFT = new BorderStroke(Colorful.BORDER).left().build(); + Border LEFT = BorderBuilder.solid(Colorful.BORDER).left().build(); /** 右边框 */ - Border RIGHT = new BorderStroke(Colorful.BORDER).right().build(); + Border RIGHT = BorderBuilder.solid(Colorful.BORDER).right().build(); /** 下边框 */ - Border BOTTOM = new BorderStroke(Colorful.BORDER).bottom().build(); + Border BOTTOM = BorderBuilder.solid(Colorful.BORDER).bottom().build(); /** 除了上边框 */ - Border EX_TOP = new BorderStroke(Colorful.BORDER).exTop().build(); + Border EX_TOP = BorderBuilder.solid(Colorful.BORDER).exceptTop().build(); /** 除了左边框 */ - Border EX_LEFT = new BorderStroke(Colorful.BORDER).exLeft().build(); + Border EX_LEFT = BorderBuilder.solid(Colorful.BORDER).exceptLeft().build(); /** 除了右边框 */ - Border EX_RIGHT = new BorderStroke(Colorful.BORDER).exRight().build(); + Border EX_RIGHT = BorderBuilder.solid(Colorful.BORDER).exceptRight().build(); /** 除了下边框 */ - Border EX_BOTTOM = new BorderStroke(Colorful.BORDER).exBottom().build(); + Border EX_BOTTOM = BorderBuilder.solid(Colorful.BORDER).exceptBottom().build(); /** 上右边框 */ - Border TR = new BorderStroke(Colorful.BORDER).width(1, 1, 0, 0).build(); + Border TR = BorderBuilder.solid(Colorful.BORDER).width(1, 1, 0, 0).build(); /** 右下边框 */ - Border RB = new BorderStroke(Colorful.BORDER).width(0, 1, 1, 0).build(); + Border RB = BorderBuilder.solid(Colorful.BORDER).width(0, 1, 1, 0).build(); /** 左下边框 */ - Border BL = new BorderStroke(Colorful.BORDER).width(0, 0, 1, 1).build(); + Border BL = BorderBuilder.solid(Colorful.BORDER).width(0, 0, 1, 1).build(); /** 左上边框 */ - Border LT = new BorderStroke(Colorful.BORDER).width(1, 0, 0, 1).build(); + Border LT = BorderBuilder.solid(Colorful.BORDER).width(1, 0, 0, 1).build(); /** 水平边框,边的方向而非位置 */ - Border H = new BorderStroke(Colorful.BORDER).width(1, 0).build(); + Border H = BorderBuilder.solid(Colorful.BORDER).width(1, 0).build(); /** 垂直边框,边的方向而非位置 */ - Border V = new BorderStroke(Colorful.BORDER).width(0, 1).build(); + Border V = BorderBuilder.solid(Colorful.BORDER).width(0, 1).build(); } /** @@ -279,37 +327,37 @@ public interface TimiFXUI { interface BG { /** FX 默认背景(#F4F4F4) */ - Background DEFAULT = new BgFill(Colorful.GRAY_WHITE).build(); + Background DEFAULT = BackgroundFillBuilder.solid(Colorful.GRAY_WHITE).build(); /** 灰色背景 */ - Background GRAY = new BgFill(Colorful.GRAY).build(); + Background GRAY = BackgroundFillBuilder.solid(Colorful.GRAY).build(); /** 亮灰背景 */ - Background LIGHT_GRAY = new BgFill(Colorful.LIGHT_GRAY).build(); + Background LIGHT_GRAY = BackgroundFillBuilder.solid(Colorful.LIGHT_GRAY).build(); /** 纯黑背景 */ - Background BLACK = new BgFill(Colorful.BLACK).build(); + Background BLACK = BackgroundFillBuilder.solid(Colorful.BLACK).build(); /** 纯白背景 */ - Background WHITE = new BgFill(Colorful.WHITE).build(); + Background WHITE = BackgroundFillBuilder.solid(Colorful.WHITE).build(); /** 透明背景 */ - Background TRANSPARENT = new BgFill(Colorful.TRANSPARENT).build(); + Background TRANSPARENT = BackgroundFillBuilder.solid(Colorful.TRANSPARENT).build(); /** 淡蓝背景 */ - Background LIGHT_BLUE = new BgFill(Colorful.LIGHT_BLUE).build(); + Background LIGHT_BLUE = BackgroundFillBuilder.solid(Colorful.LIGHT_BLUE).build(); /** 聚焦色背景 */ - Background FOCUSED = new BgFill(Colorful.FOCUSED_DEFAULT).build(); + Background FOCUSED = BackgroundFillBuilder.solid(Colorful.FOCUSED_DEFAULT).build(); /** 指向背景,通常用于提示组件尺寸响应拖动 */ - Background HOVER = new BgFill("#0007").build(); + Background HOVER = BackgroundFillBuilder.solid("#0007").build(); /** 渐变的标题背景 */ - Background TITLE = new BgFill("#DDD", "#F4F4F400").toRight().build(); + Background TITLE = BackgroundFillBuilder.linear("#DDD", "#F4F4F400").toRight().build(); /** 填充的标题背景 */ - Background TITLE_FILL = new BgFill("#DDD").build(); + Background TITLE_FILL = BackgroundFillBuilder.solid("#DDD").build(); } /** @@ -378,6 +426,12 @@ public interface TimiFXUI { return label; } + static Label gridLabel(String text) { + Label label = Layout.margin.top(GridPane::setMargin, TimiFXUI.label(text), 4); + GridPane.setValignment(label, VPos.TOP); + return label; + } + /** * 构建通用标题标签 * 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 21d80c6..58e3d83 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/DateTimePicker.java +++ b/src/main/java/com/imyeyu/fx/ui/components/DateTimePicker.java @@ -2,7 +2,9 @@ package com.imyeyu.fx.ui.components; import com.imyeyu.fx.TimiFX; import com.imyeyu.fx.icon.TimiFXIcon; +import com.imyeyu.fx.icon.TimiIcon; import com.imyeyu.fx.ui.TimiFXUI; +import com.imyeyu.fx.utils.Nodes; import com.imyeyu.java.bean.Callback; import com.imyeyu.utils.Time; import com.sun.javafx.scene.control.DatePickerContent; @@ -62,16 +64,16 @@ public class DateTimePicker extends Region implements TimiFXUI, TimiFXUI.Colorfu minute = new ListView<>(); second = new ListView<>(); - hour.getStyleClass().add(CSS.BORDER_RB); - minute.getStyleClass().add(CSS.BORDER_RB); - second.getStyleClass().add(CSS.BORDER_B); + hour.setBorder(Stroke.RB); + minute.setBorder(Stroke.RB); + second.setBorder(Stroke.BOTTOM); 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.setBorder(Stroke.LEFT); now.getStyleClass().add(STYLE_CLASS_NOW); timePane = new BorderPane(hmsPane); @@ -111,8 +113,8 @@ public class DateTimePicker extends Region implements TimiFXUI, TimiFXUI.Colorfu // 失焦关闭而非选择后关闭 } }; - IconButton clear = new IconButton(TimiFXIcon.fromName("FAIL", GRAY)); - clear.getStyleClass().add(CSS.BORDER_ALL); + IconButton clear = new IconButton(TimiFXIcon.get(TimiIcon.FAIL, GRAY)); + clear.setBorder(Stroke.DEFAULT); clear.getStyleClass().add(STYLE_CLASS_CLEAR); clear.translateXProperty().bind(widthProperty().subtract(40)); clear.setTranslateY(15); @@ -186,9 +188,9 @@ public class DateTimePicker extends Region implements TimiFXUI, TimiFXUI.Colorfu minute.addEventFilter(ScrollEvent.SCROLL, middleScroll); second.addEventFilter(ScrollEvent.SCROLL, middleScroll); - TimiFX.hoverFocus(hour); - TimiFX.hoverFocus(minute); - TimiFX.hoverFocus(second); + Nodes.requestFocusOnHover(hour); + Nodes.requestFocusOnHover(minute); + Nodes.requestFocusOnHover(second); // 此刻 now.setOnAction(e -> setValue(Time.now())); diff --git a/src/main/java/com/imyeyu/fx/ui/components/FileTreeView.java b/src/main/java/com/imyeyu/fx/ui/components/FileTreeView.java index 7831412..b272928 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/FileTreeView.java +++ b/src/main/java/com/imyeyu/fx/ui/components/FileTreeView.java @@ -1,13 +1,14 @@ package com.imyeyu.fx.ui.components; -import com.imyeyu.fx.ObservableUtils; import com.imyeyu.fx.icon.TimiFXIcon; +import com.imyeyu.fx.icon.TimiIcon; import com.imyeyu.fx.task.RunAsync; import com.imyeyu.fx.ui.TimiFXUI; import com.imyeyu.fx.ui.components.alert.AlertButton; import com.imyeyu.fx.ui.components.alert.AlertConfirm; import com.imyeyu.fx.ui.components.alert.AlertTextField; import com.imyeyu.fx.ui.components.alert.AlertTips; +import com.imyeyu.fx.utils.Observables; import com.imyeyu.io.IO; import com.imyeyu.java.TimiJava; import com.imyeyu.java.bean.CallbackArgReturn; @@ -64,8 +65,8 @@ public class FileTreeView extends TreeView implements TimiFXUI.Colorful { setCellFactory(cell -> new TreeCell<>() { final Label loading = new Label(TimiFXUI.MULTILINGUAL.text("loading")); - final Text iconFile = TimiFXIcon.fromName("FILE"); - final Text iconDirectory = TimiFXIcon.fromName("FOLDER"); + final Text iconFile = TimiFXIcon.get(TimiIcon.FILE); + final Text iconDirectory = TimiFXIcon.get(TimiIcon.FOLDER); { iconFile.fillProperty().bind(Bindings.when(FileTreeView.this.focusedProperty().and(selectedProperty())).then(GRAY_WHITE).otherwise(GRAY)); @@ -94,10 +95,10 @@ public class FileTreeView extends TreeView implements TimiFXUI.Colorful { }); // 右键菜单 - MenuItem menuMkdir = new MenuItem(TimiFXUI.MULTILINGUAL.text("file.mkdir"), TimiFXIcon.fromName("FOLDER_ADD")); + MenuItem menuMkdir = new MenuItem(TimiFXUI.MULTILINGUAL.text("file.mkdir"), TimiFXIcon.get(TimiIcon.FOLDER_ADD)); MenuItem menuRename = new MenuItem(TimiFXUI.MULTILINGUAL.text("rename")); - MenuItem menuRefresh = new MenuItem(TimiFXUI.MULTILINGUAL.text("refresh"), TimiFXIcon.fromName("REFRESH")); - MenuItem menuDestroy = new MenuItem(TimiFXUI.MULTILINGUAL.text("delete"), TimiFXIcon.fromName("FAIL", RED)); + MenuItem menuRefresh = new MenuItem(TimiFXUI.MULTILINGUAL.text("refresh"), TimiFXIcon.get(TimiIcon.REFRESH)); + MenuItem menuDestroy = new MenuItem(TimiFXUI.MULTILINGUAL.text("delete"), TimiFXIcon.get(TimiIcon.FAIL, RED)); setContextMenu(new ContextMenu(menuMkdir, menuRename, menuRefresh, TimiFXUI.sep(), menuDestroy)); @@ -108,7 +109,7 @@ public class FileTreeView extends TreeView implements TimiFXUI.Colorful { menuMkdir.setOnAction(e -> mkdir(getSelectionModel().getSelectedItem())); // 重命名 - menuRename.disableProperty().bind(ObservableUtils.onlyOnceInList(getSelectionModel().getSelectedItems()).not()); + menuRename.disableProperty().bind(Observables.onlyOnceInList(getSelectionModel().getSelectedItems()).not()); menuRename.setOnAction(e -> rename(getSelectionModel().getSelectedItem())); // 刷新 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 2780f31..b6318cf 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/IconButton.java +++ b/src/main/java/com/imyeyu/fx/ui/components/IconButton.java @@ -1,8 +1,8 @@ package com.imyeyu.fx.ui.components; -import com.imyeyu.fx.TimiFX; import com.imyeyu.fx.ui.TimiFXUI; import com.imyeyu.java.TimiJava; +import com.imyeyu.fx.utils.Nodes; import javafx.beans.binding.Bindings; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; @@ -13,6 +13,7 @@ import javafx.scene.control.Button; import javafx.scene.control.Skin; import javafx.scene.image.Image; import javafx.scene.image.ImageView; +import javafx.scene.layout.Border; import javafx.scene.paint.Paint; /** @@ -118,7 +119,7 @@ public class IconButton extends Button implements TimiFXUI, TimiFXUI.Colorful { if (node != null) { setGraphic(node); } - TimiFX.hoverOpacity(this); + Nodes.bindHoverOpacity(this); // 自适应尺寸、单独图标、图标文本混合时设置不同的内边距 paddingProperty().bind(Bindings.createObjectBinding(() -> { @@ -140,7 +141,22 @@ public class IconButton extends Button implements TimiFXUI, TimiFXUI.Colorful { * @return 本实例 */ public IconButton withBackground() { - return withBackground(null); + return withBackground((Border) null); + } + + /** + * 添加按钮背景 + * + * @param border 边框 + * @return 本实例 + */ + public IconButton withBackground(Border border) { + getStyleClass().add(CSS.BG_BUTTON); + if (border != null) { + setBorder(border); + } + opacityProperty().unbind(); + return this; } /** @@ -150,11 +166,13 @@ public class IconButton extends Button implements TimiFXUI, TimiFXUI.Colorful { * @return 本实例 */ public IconButton withBackground(String borderClass) { - getStyleClass().add(CSS.BG_BUTTON); + return withBackground().appendBorderStyle(borderClass); + } + + private IconButton appendBorderStyle(String borderClass) { if (TimiJava.isNotEmpty(borderClass)) { getStyleClass().add(borderClass); } - opacityProperty().unbind(); return this; } diff --git a/src/main/java/com/imyeyu/fx/ui/components/IconPicker.java b/src/main/java/com/imyeyu/fx/ui/components/IconPicker.java index 2db9f2d..f8b8ed6 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/IconPicker.java +++ b/src/main/java/com/imyeyu/fx/ui/components/IconPicker.java @@ -1,9 +1,10 @@ package com.imyeyu.fx.ui.components; import com.imyeyu.fx.icon.TimiFXIcon; +import com.imyeyu.fx.icon.TimiIcon; import com.imyeyu.fx.ui.TimiFXUI; -import com.imyeyu.fx.utils.BgFill; -import com.imyeyu.fx.utils.SmoothScroll; +import com.imyeyu.fx.utils.bg.BackgroundFillBuilder; +import com.imyeyu.fx.utils.scroll.SmoothScroll; import com.imyeyu.java.TimiJava; import com.imyeyu.java.ref.Ref; import com.imyeyu.utils.Collect; @@ -29,8 +30,12 @@ import javafx.stage.Stage; import javafx.stage.StageStyle; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; /** * {@link TimiFXIcon} 的图标选择器(标签搜索需要联网) @@ -58,7 +63,7 @@ public class IconPicker extends TextField implements TimiFXUI { if (TimiJava.isEmpty(value)) { root.setLeft(null); } else { - Text icon = TimiFXIcon.fromName(value); + Text icon = TimiFXIcon.get(TimiIcon.valueOf(value)); BorderPane.setMargin(icon, new Insets(0, 2, 0, 0)); BorderPane.setAlignment(icon, Pos.CENTER); root.setLeft(icon); @@ -81,7 +86,7 @@ public class IconPicker extends TextField implements TimiFXUI { if (newToggle == null) { clear(); } else if (newToggle instanceof ToggleIcon icon) { - setText(icon.name.toUpperCase()); + setText(icon.value.toString()); } }); } @@ -109,7 +114,7 @@ public class IconPicker extends TextField implements TimiFXUI { * @return 图标图像 */ public Image getValue() { - return TimiFXIcon.imageFromName(getText()); + return TimiFXIcon.getImage(TimiIcon.valueOf(getText())); } /** @@ -122,7 +127,7 @@ public class IconPicker extends TextField implements TimiFXUI { final ToggleGroup group; final List icons; - final Map nameMapping = TimiFXIcon.getNameMapping(); + final Map unicodeMap = Arrays.stream(TimiIcon.values()).collect(Collectors.toMap(Function.identity(), TimiIcon::getUnicode)); public IconPickerPopup() { // 图标 @@ -131,13 +136,12 @@ public class IconPicker extends TextField implements TimiFXUI { icons = new ArrayList<>(); group = new ToggleGroup(); - Map items = Collect.sortMapByStringKeyASC(nameMapping); - for (Map.Entry item : items.entrySet()) { + Map items = Collect.sortMap(unicodeMap, Comparator.comparing(Enum::name)); + for (Map.Entry item : items.entrySet()) { icons.add(new ToggleIcon(group, item.getKey())); } tile.getChildren().addAll(icons); - - focusedProperty().addListener((obs, o, isFocused) -> { + focusedProperty().addListener((_, _, isFocused) -> { if (!isFocused) { hide(); } @@ -156,12 +160,12 @@ public class IconPicker extends TextField implements TimiFXUI { setPadding(new Insets(6, 8, 6, 8)); setFitToWidth(true); - SmoothScroll.scrollPane(this); + SmoothScroll.pane.install(this); }}); }}); }}); scene.setFill(null); - scene.getStylesheets().addAll(CSS_STYLE, CSS_FONT); + TimiFXUI.applyStyle(scene); setScene(scene); setWidth(360); setHeight(240); @@ -177,16 +181,16 @@ public class IconPicker extends TextField implements TimiFXUI { */ private static class ToggleIcon extends ToggleButton { - private static final Background SELECTED = new BgFill("#99D1FF").build(); + private static final Background SELECTED = BackgroundFillBuilder.solid("#99D1FF").build(); /** 图标名称 */ - final String name; + final TimiIcon value; - public ToggleIcon(ToggleGroup ownerGroup, String name) { + public ToggleIcon(ToggleGroup ownerGroup, TimiIcon value) { super(""); - this.name = name; + this.value = value; - setGraphic(TimiFXIcon.fromName(name)); + setGraphic(TimiFXIcon.get(value, 16)); getStyleClass().clear(); managedProperty().bind(visibleProperty()); borderProperty().bind(Bindings.when(hoverProperty()).then(Stroke.FOCUSED).otherwise(Stroke.TP)); 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 f14417e..dbc3f38 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/Pagination.java +++ b/src/main/java/com/imyeyu/fx/ui/components/Pagination.java @@ -1,6 +1,7 @@ package com.imyeyu.fx.ui.components; import com.imyeyu.fx.icon.TimiFXIcon; +import com.imyeyu.fx.icon.TimiIcon; import com.imyeyu.fx.ui.TimiFXUI; import javafx.beans.binding.Bindings; import javafx.beans.property.IntegerProperty; @@ -15,6 +16,7 @@ import javafx.scene.control.Label; import javafx.scene.control.ToggleButton; import javafx.scene.control.ToggleGroup; import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Border; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; @@ -79,8 +81,7 @@ public class Pagination extends HBox implements TimiFXUI { PageButton tb; // 上一页 - prev = new IconButton(TimiFXIcon.fromName("ARROW_1_W")).withBackground(); - prev.getStyleClass().add(CSS.BORDER_N); + prev = new IconButton(TimiFXIcon.get(TimiIcon.ARROW_1_W)).withBackground(Border.EMPTY); prev.setMaxHeight(Double.MAX_VALUE); prev.disableProperty().bind(ip.isEqualTo(0)); getChildren().add(prev); @@ -96,7 +97,7 @@ public class Pagination extends HBox implements TimiFXUI { // 2. 总页数大于等于 8,激活下标小于 4(1 - 4 页) tb.visibleProperty().bind(cp.greaterThan(i).and(cp.lessThan(8)).or(cp.greaterThan(7).and(ip.lessThan(4)))); } else { - tb.getStyleClass().add(CSS.BORDER_LR); + tb.setBorder(Stroke.V); } pageButtons.add(tb); getChildren().add(tb); @@ -149,9 +150,8 @@ public class Pagination extends HBox implements TimiFXUI { } // 下一页 - next = new IconButton(TimiFXIcon.fromName("ARROW_1_E")).withBackground(); + next = new IconButton(TimiFXIcon.get(TimiIcon.ARROW_1_E)).withBackground(Border.EMPTY); next.setMaxHeight(Double.MAX_VALUE); - next.getStyleClass().add(CSS.BORDER_N); next.disableProperty().bind(ip.isEqualTo(cp.subtract(1)).or(cp.isEqualTo(0))); getStyleClass().add(STYLE_CLASS); @@ -324,7 +324,8 @@ public class Pagination extends HBox implements TimiFXUI { public PageButton() { indexProperty = new SimpleIntegerProperty(); - getStyleClass().addAll(CSS.BORDER_R, CSS.BG_BUTTON); + getStyleClass().add(CSS.BG_BUTTON); + setBorder(Stroke.RIGHT); // 页码即显示内容 textProperty().bind(indexProperty.add(1).asString()); managedProperty().bind(visibleProperty()); diff --git a/src/main/java/com/imyeyu/fx/ui/components/RadioButtonGroup.java b/src/main/java/com/imyeyu/fx/ui/components/RadioButtonGroup.java index 7e9a0fa..45e2bb4 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/RadioButtonGroup.java +++ b/src/main/java/com/imyeyu/fx/ui/components/RadioButtonGroup.java @@ -1,6 +1,6 @@ package com.imyeyu.fx.ui.components; -import com.imyeyu.fx.TimiFX; +import com.imyeyu.fx.utils.Nodes; import javafx.beans.property.BooleanProperty; import javafx.beans.property.DoubleProperty; import javafx.beans.property.IntegerProperty; @@ -14,12 +14,16 @@ import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; +import javafx.event.EventHandler; import javafx.geometry.Pos; import javafx.scene.control.Control; import javafx.scene.control.Skin; import javafx.scene.control.ToggleButton; import javafx.scene.control.ToggleGroup; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseEvent; +import lombok.Getter; /** * 单选按钮组组件 @@ -75,6 +79,7 @@ public class RadioButtonGroup extends Control { public static final String STYLE_CLASS_SINGLE = "single"; /** 按钮列表 */ + @Getter private final ObservableList buttons; /** 按钮组 */ @@ -99,8 +104,15 @@ public class RadioButtonGroup extends Control { private final BooleanProperty allowClearSelection; /** 禁止重复点击清空 */ - private final javafx.event.EventHandler keepSelectionEvent = event -> { - if (!isAllowClearSelection() && event.getSource() instanceof ToggleButton button && button.isSelected()) { + private final EventHandler keepSelectionEvent = event -> { + if (shouldKeepSelection(event.getSource())) { + event.consume(); + } + }; + + /** 禁止通过键盘重复触发清空 */ + private final EventHandler keepSelectionKeyEvent = event -> { + if ((event.getCode() == KeyCode.SPACE || event.getCode() == KeyCode.ENTER) && shouldKeepSelection(event.getSource())) { event.consume(); } }; @@ -130,7 +142,11 @@ public class RadioButtonGroup extends Control { * 初始化选中同步 */ private void initSelectionSync() { - toggleGroup.selectedToggleProperty().addListener((obs, o, toggle) -> { + toggleGroup.selectedToggleProperty().addListener((obs, oldToggle, toggle) -> { + if (!isAllowClearSelection() && toggle == null && oldToggle instanceof ToggleButton button && buttons.contains(button)) { + toggleGroup.selectToggle(button); + return; + } if (toggle instanceof ToggleButton button) { selectedButton.set(button); selectedIndex.set(buttons.indexOf(button)); @@ -178,7 +194,7 @@ public class RadioButtonGroup extends Control { * 初始化模式样式 */ private void initModeStyleClass() { - TimiFX.toggleStyleClass4Binding(this, mode.isEqualTo(Mode.SLIDER), STYLE_CLASS_SLIDER_MODE, STYLE_CLASS_BUTTON_MODE); + Nodes.bindStyleClass(this, mode.isEqualTo(Mode.SLIDER), STYLE_CLASS_SLIDER_MODE, STYLE_CLASS_BUTTON_MODE); } /** @@ -192,6 +208,8 @@ public class RadioButtonGroup extends Control { } button.setToggleGroup(toggleGroup); button.addEventFilter(MouseEvent.MOUSE_PRESSED, keepSelectionEvent); + button.addEventFilter(KeyEvent.KEY_PRESSED, keepSelectionKeyEvent); + button.addEventFilter(KeyEvent.KEY_RELEASED, keepSelectionKeyEvent); } /** @@ -201,11 +219,23 @@ public class RadioButtonGroup extends Control { */ private void detachButton(ToggleButton button) { button.removeEventFilter(MouseEvent.MOUSE_PRESSED, keepSelectionEvent); + button.removeEventFilter(KeyEvent.KEY_PRESSED, keepSelectionKeyEvent); + button.removeEventFilter(KeyEvent.KEY_RELEASED, keepSelectionKeyEvent); if (button.getToggleGroup() == toggleGroup) { button.setToggleGroup(null); } } + /** + * 判断是否需要保持当前选中 + * + * @param source 事件源 + * @return true 为拦截当前重复触发 + */ + private boolean shouldKeepSelection(Object source) { + return !isAllowClearSelection() && source instanceof ToggleButton button && button.isSelected(); + } + /** * 同步当前选中状态 */ @@ -235,15 +265,6 @@ public class RadioButtonGroup extends Control { return new RadioButtonGroupSkin(this); } - /** - * 获取按钮列表 - * - * @return 按钮列表 - */ - public ObservableList getButtons() { - return buttons; - } - /** * 添加按钮 * diff --git a/src/main/java/com/imyeyu/fx/ui/components/RadioButtonGroupSkin.java b/src/main/java/com/imyeyu/fx/ui/components/RadioButtonGroupSkin.java index 69d460b..131e814 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/RadioButtonGroupSkin.java +++ b/src/main/java/com/imyeyu/fx/ui/components/RadioButtonGroupSkin.java @@ -1,11 +1,9 @@ package com.imyeyu.fx.ui.components; import com.imyeyu.fx.bean.Interpolates; - import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; -import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; @@ -14,7 +12,6 @@ import javafx.geometry.Bounds; import javafx.scene.control.SkinBase; import javafx.scene.control.ToggleButton; import javafx.scene.layout.HBox; -import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; import javafx.util.Duration; @@ -27,11 +24,8 @@ import javafx.util.Duration; */ public class RadioButtonGroupSkin extends SkinBase { - /** 高度补偿 */ - private static final double HEIGHT_COMPENSATE = 2; - /** 动画时长 */ - private static final Duration SLIDER_DURATION = Duration.millis(180); + private static final Duration SLIDER_DURATION = Duration.millis(320); /** 按钮容器 */ private final HBox box; @@ -159,25 +153,6 @@ public class RadioButtonGroupSkin extends SkinBase { } } - /** - * 刷新按钮尺寸策略 - */ - private void refreshButtonSizing() { - boolean sliderMode = getSkinnable().getMode() == RadioButtonGroup.Mode.SLIDER; - for (ToggleButton button : getSkinnable().getButtons()) { - button.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); - button.setPrefHeight(Region.USE_COMPUTED_SIZE); - if (sliderMode) { - HBox.setHgrow(button, Priority.NEVER); - button.setMaxWidth(Region.USE_PREF_SIZE); - } - else { - HBox.setHgrow(button, Priority.ALWAYS); - button.setMaxWidth(Double.MAX_VALUE); - } - } - } - /** * 应用滑块位置和尺寸 */ 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 8cbb41b..9311e34 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/TabPane.java +++ b/src/main/java/com/imyeyu/fx/ui/components/TabPane.java @@ -1,6 +1,7 @@ package com.imyeyu.fx.ui.components; import com.imyeyu.fx.icon.TimiFXIcon; +import com.imyeyu.fx.icon.TimiIcon; import com.imyeyu.fx.ui.TimiFXUI; import com.imyeyu.java.bean.CallbackArg; import com.imyeyu.java.ref.Ref; @@ -48,7 +49,7 @@ public class TabPane extends javafx.scene.control.TabPane implements TimiFXUI, T closeBtn.setPrefWidth(18); closeBtn.getStyleClass().clear(); - IconButton icon = new IconButton(TimiFXIcon.fromName("FAIL", GRAY)); + IconButton icon = new IconButton(TimiFXIcon.get(TimiIcon.FAIL, GRAY)); icon.setAlignment(Pos.CENTER_LEFT); icon.setMouseTransparent(true); icon.prefWidthProperty().bind(closeBtn.widthProperty()); 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 78e33f1..4cb0865 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/TextAreaEditor.java +++ b/src/main/java/com/imyeyu/fx/ui/components/TextAreaEditor.java @@ -1,11 +1,12 @@ package com.imyeyu.fx.ui.components; import com.imyeyu.fx.icon.TimiFXIcon; +import com.imyeyu.fx.icon.TimiIcon; import com.imyeyu.fx.ui.TimiFXUI; import com.imyeyu.fx.ui.components.popup.PopupTipsService; -import com.imyeyu.fx.utils.Anchor; -import com.imyeyu.fx.utils.Column; -import com.imyeyu.fx.utils.SmoothScroll; +import com.imyeyu.fx.utils.grid.Column; +import com.imyeyu.fx.utils.layout.Layout; +import com.imyeyu.fx.utils.scroll.SmoothScroll; import com.imyeyu.java.TimiJava; import com.imyeyu.java.bean.Callback; import com.imyeyu.java.ref.Ref; @@ -38,6 +39,7 @@ import javafx.scene.input.ContextMenuEvent; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Border; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.BorderPane; import javafx.scene.layout.GridPane; @@ -116,31 +118,31 @@ public class TextAreaEditor extends TextArea implements TimiFXUI { showLineNumber = new SimpleBooleanProperty(true); // 撤销 - undo = new IconButton(TimiFXIcon.fromName("ARROW_0_W")).withBackground(CSS.BORDER_R); + undo = new IconButton(TimiFXIcon.get(TimiIcon.ARROW_0_W)).withBackground(Stroke.RIGHT); undo.disableProperty().bind(undoableProperty().not()); PopupTipsService.installText(undo, TimiFXUI.MULTILINGUAL.text("undo")); // 重做 - redo = new IconButton(TimiFXIcon.fromName("ARROW_0_E")).withBackground(CSS.BORDER_R); + redo = new IconButton(TimiFXIcon.get(TimiIcon.ARROW_0_E)).withBackground(Stroke.RIGHT); redo.disableProperty().bind(redoableProperty().not()); PopupTipsService.installText(redo, TimiFXUI.MULTILINGUAL.text("redo")); // 复制 - copy = new IconButton(TimiFXIcon.fromName("COPY")).withBackground(CSS.BORDER_R); + copy = new IconButton(TimiFXIcon.get(TimiIcon.COPY)).withBackground(Stroke.RIGHT); PopupTipsService.installText(copy, TimiFXUI.MULTILINGUAL.text("copy")); // 剪切 - cut = new IconButton(TimiFXIcon.fromName("CUT")).withBackground(CSS.BORDER_R); + cut = new IconButton(TimiFXIcon.get(TimiIcon.CUT)).withBackground(Stroke.RIGHT); cut.disableProperty().bind(editableProperty().not()); PopupTipsService.installText(cut, TimiFXUI.MULTILINGUAL.text("cut")); // 粘贴 - paste = new IconButton(TimiFXIcon.fromName("PASTE")).withBackground(CSS.BORDER_R); + paste = new IconButton(TimiFXIcon.get(TimiIcon.PASTE)).withBackground(Stroke.RIGHT); paste.disableProperty().bind(editableProperty().not()); PopupTipsService.installText(paste, TimiFXUI.MULTILINGUAL.text("paste")); // 换行 - wrap = new ToggleIcon(TimiFXIcon.fromName("WRAP")); + wrap = new ToggleIcon(TimiFXIcon.get(TimiIcon.WRAP)); wrap.setBorder(Stroke.RIGHT); PopupTipsService.installText(wrap, TimiFXUI.MULTILINGUAL.text("wrap")); @@ -154,12 +156,13 @@ public class TextAreaEditor extends TextArea implements TimiFXUI { // 顶部背景(阻止触发文本域选择) Button headerBackground = new Button(" "); - headerBackground.getStyleClass().addAll(CSS.BORDER_N, CSS.BG_TP); + headerBackground.getStyleClass().add(CSS.BG_TP); + headerBackground.setBorder(Border.EMPTY); headerBackground.setBackground(BG.DEFAULT); AnchorPane headerPane = new AnchorPane(); - Anchor.def(header); - Anchor.def(headerBackground); + Layout.anchor.fill(header); + Layout.anchor.fill(headerBackground); headerPane.getChildren().setAll(headerBackground, header); headerPane.setBorder(Stroke.BOTTOM); @@ -321,7 +324,7 @@ public class TextAreaEditor extends TextArea implements TimiFXUI { }); // 平滑滚动 - SmoothScroll.scrollPaneV(scrollPane); + SmoothScroll.pane.install(scrollPane); root.setLeft(lineNumber); root.setCenter(scrollPane); @@ -412,7 +415,7 @@ public class TextAreaEditor extends TextArea implements TimiFXUI { selects = FXCollections.observableHashMap(); // 查找 - Text icon = TimiFXIcon.fromName("MAGNIFIER"); + Text icon = TimiFXIcon.get(TimiIcon.MAGNIFIER); keyword = new TextField() { @Override @@ -426,7 +429,7 @@ public class TextAreaEditor extends TextArea implements TimiFXUI { textGroup.setTranslateY(-.5); // 匹配大小写 - toggleCase = new ToggleIcon(TimiFXIcon.fromName("FONTSIZE")); + toggleCase = new ToggleIcon(TimiFXIcon.get(TimiIcon.FONT_SIZE)); toggleCase.setBorder(Stroke.LEFT); toggleCase.setCursor(Cursor.DEFAULT); toggleCase.setFocusTraversable(false); @@ -446,29 +449,31 @@ public class TextAreaEditor extends TextArea implements TimiFXUI { } }; keyword.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); - keyword.getStyleClass().addAll("find-field", CSS.BORDER_R, CSS.PADDING_N); + keyword.getStyleClass().addAll("find-field", CSS.PADDING_N); + keyword.setBorder(Stroke.RIGHT); // 查找结果 result = TimiFXUI.label(" "); result.setPadding(new Insets(0, 4, 0, 4)); - IconButton prev = new IconButton(TimiFXIcon.fromName("ARROW_0_N")).withBackground(); - prev.getStyleClass().add(CSS.BORDER_R); + IconButton prev = new IconButton(TimiFXIcon.get(TimiIcon.ARROW_0_N)).withBackground(); + prev.setBorder(Stroke.RIGHT); prev.setFocusTraversable(false); - IconButton next = new IconButton(TimiFXIcon.fromName("ARROW_0_S")).withBackground(); - next.getStyleClass().add(CSS.BORDER_R); + IconButton next = new IconButton(TimiFXIcon.get(TimiIcon.ARROW_0_S)).withBackground(); + next.setBorder(Stroke.RIGHT); next.setFocusTraversable(false); // 关闭 - IconButton close = new IconButton(TimiFXIcon.fromName("FAIL")).withBackground(); - close.getStyleClass().add(CSS.BORDER_L); + IconButton close = new IconButton(TimiFXIcon.get(TimiIcon.FAIL)).withBackground(); + close.setBorder(Stroke.LEFT); close.setFocusTraversable(false); // 替换值 TextField replaceValue = new TextField(); - replaceValue.getStyleClass().addAll("replace-field", CSS.BORDER_TR); + replaceValue.getStyleClass().add("replace-field"); + replaceValue.setBorder(Stroke.TR); replaceValue.disableProperty().bind(editableProperty().not()); Button replace = new Button(TimiFXUI.MULTILINGUAL.text("replace")); - replace.getStyleClass().add(CSS.BORDER_R); + replace.setBorder(Stroke.RIGHT); replace.setFocusTraversable(false); replace.disableProperty().bind(editableProperty().not().or(replaceValue.textProperty().isEmpty().or(Bindings.isEmpty(selects)))); replace.addEventFilter(MouseEvent.MOUSE_DRAGGED, e -> { @@ -477,12 +482,12 @@ public class TextAreaEditor extends TextArea implements TimiFXUI { } }); Button replaceAll = new Button(TimiFXUI.MULTILINGUAL.text("replace_all")); - replaceAll.getStyleClass().add(CSS.BORDER_R); + replaceAll.setBorder(Stroke.RIGHT); replaceAll.setFocusTraversable(false); replaceAll.disableProperty().bind(replace.disabledProperty()); getStyleClass().add("find-pane"); - getColumnConstraints().addAll(Column.build(), Column.build().width(260), Column.VALUE_FILL); + getColumnConstraints().addAll(Column.builder().build(), Column.builder().width(260).build(), Column.valueFill()); setBorder(Stroke.BOTTOM); setBackground(BG.DEFAULT); @@ -645,7 +650,7 @@ public class TextAreaEditor extends TextArea implements TimiFXUI { // 不可单向绑定,TextArea 内部需要调度 setVvalue,也不可双向绑定,会造成滚动错位 scrollPane.setVvalue(newV.doubleValue()); }); - getStyleClass().add(CSS.BORDER_L); + setBorder(Stroke.LEFT); setEditable(false); setNodeOrientation(NodeOrientation.RIGHT_TO_LEFT); addEventFilter(MouseEvent.ANY, Event::consume); @@ -688,7 +693,7 @@ public class TextAreaEditor extends TextArea implements TimiFXUI { scrollPane.vvalueProperty().addListener((obs, o, n) -> scrollPane.setVvalue(ownerScrollPane.getVvalue())); scrollPane.getContent().setCursor(Cursor.DEFAULT); - SmoothScroll.scrollPaneV(scrollPane); + SmoothScroll.pane.install(scrollPane); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } diff --git a/src/main/java/com/imyeyu/fx/ui/components/TextAreaEditorField.java b/src/main/java/com/imyeyu/fx/ui/components/TextAreaEditorField.java index f0c8930..e5e3247 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/TextAreaEditorField.java +++ b/src/main/java/com/imyeyu/fx/ui/components/TextAreaEditorField.java @@ -2,7 +2,9 @@ package com.imyeyu.fx.ui.components; import com.imyeyu.fx.TimiFX; import com.imyeyu.fx.icon.TimiFXIcon; +import com.imyeyu.fx.icon.TimiIcon; import com.imyeyu.fx.ui.TimiFXUI; +import com.imyeyu.fx.utils.Windows; import com.imyeyu.java.bean.CallbackArg; import com.imyeyu.java.ref.Ref; import javafx.beans.property.SimpleStringProperty; @@ -71,8 +73,7 @@ public class TextAreaEditorField extends TextField implements TimiFXUI { textGroup.setTranslateY(-.5); // 插入编辑器图标 - IconButton editor = new IconButton(TimiFXIcon.fromName("WRITING")).withBackground(); - editor.getStyleClass().add(CSS.BORDER_L); + IconButton editor = new IconButton(TimiFXIcon.get(TimiIcon.WRITING)).withBackground(Stroke.LEFT); editor.setCursor(Cursor.DEFAULT); BorderPane root = new BorderPane(); @@ -88,7 +89,7 @@ public class TextAreaEditorField extends TextField implements TimiFXUI { if (onShowEditorEvent != null) { onShowEditorEvent.handler(editorStage); } - TimiFX.showCenter(getScene().getWindow(), editorStage); + Windows.showCentered(getScene().getWindow(), editorStage); }); } catch (IllegalAccessException e) { @@ -138,12 +139,12 @@ public class TextAreaEditorField extends TextField implements TimiFXUI { EditorStage() { editor = new TextAreaEditor(); - editor.getStyleClass().add(CSS.BORDER_T); + editor.setBorder(Stroke.TOP); Scene scene = new Scene(editor); - scene.getStylesheets().addAll(CSS_STYLE, CSS_FONT); + TimiFXUI.applyStyle(scene); setScene(scene); - getIcons().add(TimiFXIcon.iconFromName("WRITING")); + getIcons().add(TimiFXIcon.rerender(TimiIcon.WRITING)); setWidth(850); setHeight(620); } 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 a718394..e1aace1 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/TimePicker.java +++ b/src/main/java/com/imyeyu/fx/ui/components/TimePicker.java @@ -2,7 +2,9 @@ package com.imyeyu.fx.ui.components; import com.imyeyu.fx.TimiFX; import com.imyeyu.fx.icon.TimiFXIcon; +import com.imyeyu.fx.icon.TimiIcon; import com.imyeyu.fx.ui.TimiFXUI; +import com.imyeyu.fx.utils.Nodes; import com.imyeyu.utils.Time; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleIntegerProperty; @@ -51,21 +53,20 @@ public class TimePicker extends HBox implements TimiFXUI { textField.setAlignment(Pos.CENTER); textField.setPrefWidth(70); textField.setOnContextMenuRequested(Event::consume); - IconButton button = new IconButton(TimiFXIcon.fromName("CLOCK")).withBackground(); - button.getStyleClass().add(CSS.BORDER_TRB); + IconButton button = new IconButton(TimiFXIcon.get(TimiIcon.CLOCK)).withBackground(Stroke.EX_LEFT); // 时间选择 hour = new ListView<>(); minute = new ListView<>(); second = new ListView<>(); - hour.getStyleClass().add(CSS.BORDER_RB); - minute.getStyleClass().add(CSS.BORDER_RB); - second.getStyleClass().add(CSS.BORDER_B); + hour.setBorder(Stroke.RB); + minute.setBorder(Stroke.RB); + second.setBorder(Stroke.BOTTOM); // 此刻 Button now = new Button(TimiFXUI.MULTILINGUAL.text("now")); - now.getStyleClass().add(CSS.BORDER_L); + now.setBorder(Stroke.LEFT); now.getStyleClass().add(STYLE_CLASS_NOW); HBox timeBox = new HBox(hour, minute, second); @@ -134,9 +135,9 @@ public class TimePicker extends HBox implements TimiFXUI { minute.addEventFilter(ScrollEvent.SCROLL, middleScroll); second.addEventFilter(ScrollEvent.SCROLL, middleScroll); - TimiFX.hoverFocus(hour); - TimiFX.hoverFocus(minute); - TimiFX.hoverFocus(second); + Nodes.requestFocusOnHover(hour); + Nodes.requestFocusOnHover(minute); + Nodes.requestFocusOnHover(second); // 值监听 value.addListener((obs, o, n) -> { 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 d5094ef..f6932df 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/ToggleIcon.java +++ b/src/main/java/com/imyeyu/fx/ui/components/ToggleIcon.java @@ -1,8 +1,8 @@ package com.imyeyu.fx.ui.components; -import com.imyeyu.fx.TimiFX; import com.imyeyu.fx.ui.TimiFXUI; -import com.imyeyu.fx.utils.BgFill; +import com.imyeyu.fx.utils.Nodes; +import com.imyeyu.fx.utils.bg.BackgroundFillBuilder; import com.imyeyu.java.TimiJava; import javafx.beans.binding.Bindings; import javafx.beans.property.BooleanProperty; @@ -25,7 +25,7 @@ import javafx.scene.layout.Background; public class ToggleIcon extends ToggleButton implements TimiFXUI { private static final String STYLE_CLASS = "toggle-icon"; - private static final Background BG_SELECTED = new BgFill("#99D1FF").build(); + private static final Background BG_SELECTED = BackgroundFillBuilder.solid("#99D1FF").build(); /** 选中图标 */ protected final Node selected; @@ -118,7 +118,7 @@ public class ToggleIcon extends ToggleButton implements TimiFXUI { graphicProperty().bind(Bindings.when(selectedProperty()).then(selected).otherwise(otherwise)); } selected.getStyleClass().add("icon"); - TimiFX.hoverOpacity(this); + Nodes.bindHoverOpacity(this); getStyleClass().setAll(CSS.MINECRAFT, STYLE_CLASS); setAlignment(Pos.CENTER); 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 a2b56fa..0f944ec 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, RESOURCE + "style/components/tray-menu.css"); + TimiFXUI.applyStyle(scene, TrayFX.class.getResource(RESOURCE + "style/components/tray-menu.css").toExternalForm()); scene.setFill(null); stage = new Stage(); stage.setWidth(1); diff --git a/src/main/java/com/imyeyu/fx/ui/components/TreeView.java b/src/main/java/com/imyeyu/fx/ui/components/TreeView.java index dbb04c8..b21aac4 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/TreeView.java +++ b/src/main/java/com/imyeyu/fx/ui/components/TreeView.java @@ -1,6 +1,6 @@ package com.imyeyu.fx.ui.components; -import com.imyeyu.fx.utils.SmoothScroll; +import com.imyeyu.fx.utils.scroll.SmoothScroll; import javafx.collections.ObservableList; import javafx.scene.control.TreeItem; @@ -20,7 +20,7 @@ public class TreeView extends javafx.scene.control.TreeView { setRoot(dummyRoot); setShowRoot(false); // 平滑滚动 - SmoothScroll.virtual(this); + SmoothScroll.virtual.install(this); } /** diff --git a/src/main/java/com/imyeyu/fx/ui/components/alert/AbstractAlert.java b/src/main/java/com/imyeyu/fx/ui/components/alert/AbstractAlert.java index c28ad1b..84b6838 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/alert/AbstractAlert.java +++ b/src/main/java/com/imyeyu/fx/ui/components/alert/AbstractAlert.java @@ -1,8 +1,8 @@ package com.imyeyu.fx.ui.components.alert; -import com.imyeyu.fx.TimiFX; import com.imyeyu.fx.ui.TimiFXUI; -import com.imyeyu.fx.utils.ScreenFX; +import com.imyeyu.fx.utils.Windows; +import com.imyeyu.fx.utils.screen.Screens; import com.imyeyu.java.bean.CallbackArg; import com.imyeyu.java.bean.CallbackArgReturn; import javafx.beans.property.ObjectProperty; @@ -102,7 +102,7 @@ public abstract class AbstractAlert extends Stage implements TimiFXUI, TimiFXUI. }}); Scene scene = new Scene(root); - scene.getStylesheets().addAll(CSS_STYLE, CSS_FONT); + TimiFXUI.applyStyle(scene); initModality(Modality.WINDOW_MODAL); setScene(scene); @@ -184,7 +184,7 @@ public abstract class AbstractAlert extends Stage implements TimiFXUI, TimiFXUI. initOwner(owner); } setOnShown(e -> { - TimiFX.relativeCenter(owner, this); + Windows.centerOnOwner(owner, this); callShownListeners(e); }); show(); @@ -200,7 +200,7 @@ public abstract class AbstractAlert extends Stage implements TimiFXUI, TimiFXUI. initOwner(owner); } setOnShown(e -> { - TimiFX.relativeCenter(owner, this); + Windows.centerOnOwner(owner, this); callShownListeners(e); }); showAndWait(); @@ -208,7 +208,7 @@ public abstract class AbstractAlert extends Stage implements TimiFXUI, TimiFXUI. /** 相对于主屏幕中间显示 */ public void showRelativeCenter4PrimaryScreen() { - showRelativeCenter4Screen(ScreenFX.primary); + showRelativeCenter4Screen(Screens.primary()); } /** @@ -219,7 +219,7 @@ public abstract class AbstractAlert extends Stage implements TimiFXUI, TimiFXUI. public void showRelativeCenter4Screen(Screen screen) { initModality(Modality.APPLICATION_MODAL); setOnShown(e -> { - TimiFX.relativeCenter4Screen(screen, this); + Windows.centerOnScreen(this, screen); callShownListeners(e); }); show(); @@ -227,7 +227,7 @@ public abstract class AbstractAlert extends Stage implements TimiFXUI, TimiFXUI. /** 相对于主屏幕中间显示并等待 */ public void showAwaitRelativeCenter4PrimaryScreen() { - showAwaitRelativeCenter4Screen(ScreenFX.primary); + showAwaitRelativeCenter4Screen(Screens.primary()); } /** @@ -238,7 +238,7 @@ public abstract class AbstractAlert extends Stage implements TimiFXUI, TimiFXUI. public void showAwaitRelativeCenter4Screen(Screen screen) { initModality(Modality.APPLICATION_MODAL); setOnShown(e -> { - TimiFX.relativeCenter4Screen(screen, this); + Windows.centerOnScreen(this, screen); callShownListeners(e); }); showAndWait(); diff --git a/src/main/java/com/imyeyu/fx/ui/components/alert/AbstractAlertFile.java b/src/main/java/com/imyeyu/fx/ui/components/alert/AbstractAlertFile.java index f2b6356..bf57e2a 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/alert/AbstractAlertFile.java +++ b/src/main/java/com/imyeyu/fx/ui/components/alert/AbstractAlertFile.java @@ -1,6 +1,7 @@ package com.imyeyu.fx.ui.components.alert; import com.imyeyu.fx.icon.TimiFXIcon; +import com.imyeyu.fx.icon.TimiIcon; import com.imyeyu.fx.ui.TimiFXUI; import com.imyeyu.fx.ui.components.FileTreeView; import com.imyeyu.fx.ui.components.IconButton; @@ -18,6 +19,7 @@ import javafx.scene.control.TextField; import javafx.scene.control.TreeItem; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Border; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; @@ -66,27 +68,23 @@ public abstract class AbstractAlertFile extends AbstractAlert implements TimiFXU VBox header = new VBox(); // 主页 - IconButton home = new IconButton(TimiFXIcon.fromName("HOME")).withBackground(); - home.getStyleClass().add(CSS.BORDER_R); + IconButton home = new IconButton(TimiFXIcon.get(TimiIcon.HOME)).withBackground(Stroke.RIGHT); PopupTipsService.installText(home, TimiFXUI.MULTILINGUAL.text("home")); // 刷新 - IconButton refresh = new IconButton(TimiFXIcon.fromName("REFRESH")).withBackground(); - refresh.getStyleClass().add(CSS.BORDER_R); + IconButton refresh = new IconButton(TimiFXIcon.get(TimiIcon.REFRESH)).withBackground(Stroke.RIGHT); PopupTipsService.installText(refresh, TimiFXUI.MULTILINGUAL.text("refresh")); // 创建文件夹 - IconButton mkdir = new IconButton(TimiFXIcon.fromName("FOLDER_ADD")).withBackground(); - mkdir.getStyleClass().add(CSS.BORDER_R); + IconButton mkdir = new IconButton(TimiFXIcon.get(TimiIcon.FOLDER_ADD)).withBackground(Stroke.RIGHT); PopupTipsService.installText(mkdir, TimiFXUI.MULTILINGUAL.text("file.mkdir")); // 删除 - IconButton destroy = new IconButton(TimiFXIcon.fromName("FAIL", Colorful.RED)).withBackground(); - destroy.getStyleClass().add(CSS.BORDER_R); + IconButton destroy = new IconButton(TimiFXIcon.get(TimiIcon.FAIL, Colorful.RED)).withBackground(Stroke.RIGHT); PopupTipsService.installText(destroy, TimiFXUI.MULTILINGUAL.text("delete")); // 切换隐藏 - toggleHide = new ToggleIcon(TimiFXIcon.fromName("HIDE")); + toggleHide = new ToggleIcon(TimiFXIcon.get(TimiIcon.HIDE)); PopupTipsService.installText(toggleHide, TimiFXUI.MULTILINGUAL.text("file.show_hide")); BorderPane ctrl = new BorderPane(); @@ -99,9 +97,8 @@ public abstract class AbstractAlertFile extends AbstractAlert implements TimiFXU // 绝对路径 absolutePath = new TextField(); - absolutePath.getStyleClass().add(CSS.BORDER_N); - IconButton absolutePathGo = new IconButton(TimiFXIcon.fromName("ARROW_2_E")).withBackground(); - absolutePathGo.getStyleClass().add(CSS.BORDER_L); + absolutePath.setBorder(Border.EMPTY); + IconButton absolutePathGo = new IconButton(TimiFXIcon.get(TimiIcon.ARROW_2_E)).withBackground(Stroke.LEFT); { if (mode == SelectionMode.SINGLE) { PopupTipsLabel tips = PopupTipsService.installBindingText(absolutePath, absolutePath.textProperty()); @@ -128,10 +125,10 @@ public abstract class AbstractAlertFile extends AbstractAlert implements TimiFXU { confirm = AlertButton.confirm(); - confirm.getStyleClass().add(CSS.BORDER_L); + confirm.setBorder(Stroke.LEFT); cancel = AlertButton.cancel(); - cancel.getStyleClass().add(CSS.BORDER_L); + cancel.setBorder(Stroke.LEFT); setRightButtons(confirm, cancel); btnPane.setPadding(Insets.EMPTY); diff --git a/src/main/java/com/imyeyu/fx/ui/components/alert/AlertFileBlendSelector.java b/src/main/java/com/imyeyu/fx/ui/components/alert/AlertFileBlendSelector.java index 2b06eb2..1caf82e 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/alert/AlertFileBlendSelector.java +++ b/src/main/java/com/imyeyu/fx/ui/components/alert/AlertFileBlendSelector.java @@ -1,6 +1,7 @@ package com.imyeyu.fx.ui.components.alert; import com.imyeyu.fx.icon.TimiFXIcon; +import com.imyeyu.fx.icon.TimiIcon; import com.imyeyu.fx.ui.TimiFXUI; import javafx.beans.binding.Bindings; import javafx.scene.control.SelectionMode; @@ -24,7 +25,7 @@ public class AlertFileBlendSelector extends AbstractAlertFile { // 确认 confirm.disableProperty().bind(Bindings.isEmpty(tree.getSelectionModel().getSelectedItems())); - getIcons().setAll(TimiFXIcon.iconFromName("FOLDER")); + getIcons().setAll(TimiFXIcon.rerender(TimiIcon.FOLDER)); setTitle(TimiFXUI.MULTILINGUAL.text("file.tips.select_directory")); } } diff --git a/src/main/java/com/imyeyu/fx/ui/components/alert/AlertFilePathSelector.java b/src/main/java/com/imyeyu/fx/ui/components/alert/AlertFilePathSelector.java index 050e56d..13e4afe 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/alert/AlertFilePathSelector.java +++ b/src/main/java/com/imyeyu/fx/ui/components/alert/AlertFilePathSelector.java @@ -1,6 +1,7 @@ package com.imyeyu.fx.ui.components.alert; import com.imyeyu.fx.icon.TimiFXIcon; +import com.imyeyu.fx.icon.TimiIcon; import com.imyeyu.fx.ui.TimiFXUI; import javafx.beans.binding.Bindings; import javafx.scene.control.SelectionMode; @@ -30,7 +31,7 @@ public class AlertFilePathSelector extends AbstractAlertFile { return item == null || item.getValue().isFile(); }, tree.getSelectionModel().selectedItemProperty())); - getIcons().setAll(TimiFXIcon.iconFromName("FOLDER")); + getIcons().setAll(TimiFXIcon.rerender(TimiIcon.FOLDER)); setTitle(TimiFXUI.MULTILINGUAL.text("file.tips.select_directory")); addItemFilter(File::isDirectory); diff --git a/src/main/java/com/imyeyu/fx/ui/components/alert/AlertFileSelector.java b/src/main/java/com/imyeyu/fx/ui/components/alert/AlertFileSelector.java index 77a346d..abc6e52 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/alert/AlertFileSelector.java +++ b/src/main/java/com/imyeyu/fx/ui/components/alert/AlertFileSelector.java @@ -1,6 +1,7 @@ package com.imyeyu.fx.ui.components.alert; import com.imyeyu.fx.icon.TimiFXIcon; +import com.imyeyu.fx.icon.TimiIcon; import com.imyeyu.fx.ui.TimiFXUI; import com.imyeyu.io.IO; import com.imyeyu.java.TimiJava; @@ -44,7 +45,7 @@ public class AlertFileSelector extends AbstractAlertFile { return item == null || item.getValue().isDirectory(); }, tree.getSelectionModel().selectedItemProperty())); - getIcons().setAll(TimiFXIcon.iconFromName("FILE")); + getIcons().setAll(TimiFXIcon.rerender(TimiIcon.FILE)); setTitle(TimiFXUI.MULTILINGUAL.text("file.select")); addItemFilter(file -> { diff --git a/src/main/java/com/imyeyu/fx/ui/components/alert/AlertTextArea.java b/src/main/java/com/imyeyu/fx/ui/components/alert/AlertTextArea.java index 0932630..ca912b9 100644 --- a/src/main/java/com/imyeyu/fx/ui/components/alert/AlertTextArea.java +++ b/src/main/java/com/imyeyu/fx/ui/components/alert/AlertTextArea.java @@ -2,7 +2,8 @@ package com.imyeyu.fx.ui.components.alert; import com.imyeyu.fx.TimiFX; import com.imyeyu.fx.ui.TimiFXUI; -import com.imyeyu.fx.utils.SmoothScroll; +import com.imyeyu.fx.utils.scroll.SmoothScroll; +import com.imyeyu.fx.utils.Windows; import com.imyeyu.java.bean.CallbackArg; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; @@ -85,7 +86,7 @@ public class AlertTextArea extends AbstractAlertInput