diff --git a/pom.xml b/pom.xml
index b5394f4..d1a2753 100644
--- a/pom.xml
+++ b/pom.xml
@@ -27,6 +27,7 @@
17.0.15
2.0.1
+ 12.4.0
C:\Program Files\Java\jdk-17
@@ -171,6 +172,17 @@
${javafx.version}
+
+ org.kordamp.ikonli
+ ikonli-javafx
+ ${ikonli.version}
+
+
+ org.kordamp.ikonli
+ ikonli-feather-pack
+ ${ikonli.version}
+
+
io.github.mkpaz
diff --git a/src/main/java/com/example/workutils/system/ui/icon/AppIcon.java b/src/main/java/com/example/workutils/system/ui/icon/AppIcon.java
new file mode 100644
index 0000000..f1f467d
--- /dev/null
+++ b/src/main/java/com/example/workutils/system/ui/icon/AppIcon.java
@@ -0,0 +1,31 @@
+package com.example.workutils.system.ui.icon;
+
+import org.kordamp.ikonli.Ikon;
+import org.kordamp.ikonli.feather.Feather;
+
+public enum AppIcon {
+ MENU(Feather.MENU),
+ SETTINGS(Feather.SETTINGS),
+ CHEVRON_LEFT(Feather.CHEVRON_LEFT),
+ CHEVRON_RIGHT(Feather.CHEVRON_RIGHT),
+ GIT_SYNC(Feather.REFRESH_CW),
+ GIT_REVIEW(Feather.GIT_BRANCH),
+ DIFF(Feather.COLUMNS),
+ SQL_EXPORT(Feather.DATABASE),
+ MYBATIS_GENERATOR(Feather.CODE),
+ SCANNER(Feather.SEARCH),
+ API_DOC(Feather.FILE_TEXT),
+ EXPORT(Feather.DOWNLOAD),
+ CANCEL(Feather.X),
+ APP(Feather.TOOL);
+
+ private final Feather glyph;
+
+ AppIcon(Feather glyph) {
+ this.glyph = glyph;
+ }
+
+ public Ikon glyph() {
+ return glyph;
+ }
+}
diff --git a/src/main/java/com/example/workutils/system/ui/icon/IconFactory.java b/src/main/java/com/example/workutils/system/ui/icon/IconFactory.java
new file mode 100644
index 0000000..de06cf0
--- /dev/null
+++ b/src/main/java/com/example/workutils/system/ui/icon/IconFactory.java
@@ -0,0 +1,25 @@
+package com.example.workutils.system.ui.icon;
+
+import org.kordamp.ikonli.javafx.FontIcon;
+
+import java.util.Objects;
+
+public final class IconFactory {
+ private static final int BUTTON_ICON_SIZE = 16;
+ private static final String ICON_STYLE_CLASS = "app-icon";
+
+ private IconFactory() {
+ }
+
+ public static FontIcon create(AppIcon icon, int size) {
+ FontIcon node = FontIcon.of(Objects.requireNonNull(icon, "icon must not be null").glyph(), size);
+ node.getStyleClass().add(ICON_STYLE_CLASS);
+ return node;
+ }
+
+ public static FontIcon createButtonIcon(AppIcon icon, String accessibleText) {
+ FontIcon node = create(icon, BUTTON_ICON_SIZE);
+ node.setAccessibleText(Objects.requireNonNull(accessibleText, "accessibleText must not be null"));
+ return node;
+ }
+}
diff --git a/src/test/java/com/example/workutils/system/ui/icon/IconFactoryTest.java b/src/test/java/com/example/workutils/system/ui/icon/IconFactoryTest.java
new file mode 100644
index 0000000..3cc234f
--- /dev/null
+++ b/src/test/java/com/example/workutils/system/ui/icon/IconFactoryTest.java
@@ -0,0 +1,31 @@
+package com.example.workutils.system.ui.icon;
+
+import org.junit.jupiter.api.Test;
+import org.kordamp.ikonli.feather.Feather;
+import org.kordamp.ikonli.javafx.FontIcon;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class IconFactoryTest {
+
+ @Test
+ void mapsEverySemanticIconToFeatherAndCreatesAccessibleNodes() {
+ for (AppIcon icon : AppIcon.values()) {
+ assertInstanceOf(Feather.class, icon.glyph());
+
+ FontIcon node = IconFactory.createButtonIcon(icon, "系统设置");
+ assertEquals(16, node.getIconSize());
+ assertEquals("系统设置", node.getAccessibleText());
+ }
+ }
+
+ @Test
+ void createsSemanticIconWithRequestedSizeAndSharedStyle() {
+ FontIcon node = IconFactory.create(AppIcon.MENU, 20);
+
+ assertEquals(20, node.getIconSize());
+ assertTrue(node.getStyleClass().contains("app-icon"));
+ }
+}