From e66c76b49e772a8c1f15d39015b2d82f33a198ea Mon Sep 17 00:00:00 2001 From: KSSJW <165921128+KSSJW@users.noreply.github.com> Date: Sun, 19 Jul 2026 14:48:32 +0800 Subject: [PATCH 1/5] Feat: Implement dynamic folder icon and use to game directory list --- .../main/java/org/jackhuang/hmcl/ui/SVG.java | 1 + .../ui/directory/GameDirectoryListItemSkin.java | 17 ++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java index fb2c3b99858..2a33131ab8b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java @@ -66,6 +66,7 @@ public enum SVG { FILE_OPEN("M6 22q-0.825 0-1.4125-0.5875T4 20v-16q0-0.825 0.5875-1.4125T6 2h8l6 6v6h-2v-5H13v-5H6v16h9v2H6Zm15.95 0.375L19 19.425v2.225h-2v-5.65h5.65v2h-2.25l2.95 2.95-1.4 1.425Zm-15.95-2.375v-16 16Z"), FOLDER("M4 20Q3.175 20 2.5875 19.4125T2 18V6Q2 5.175 2.5875 4.5875T4 4H10L12 6H20Q20.825 6 21.4125 6.5875T22 8V18Q22 18.825 21.4125 19.4125T20 20H4ZM4 18H20V8H11.175L9.175 6H4V18ZM4 18V6 18Z"), FOLDER_COPY("M3 21Q2.175 21 1.5875 20.4125T1 19V6H3V19H20V21H3ZM7 17Q6.175 17 5.5875 16.4125T5 15V4Q5 3.175 5.5875 2.5875T7 2H12L14 4H21Q21.825 4 22.4125 4.5875T23 6V15Q23 15.825 22.4125 16.4125T21 17H7ZM7 15H21V6H13.175L11.175 4H7V15ZM7 15V4 15Z"), + FOLDER_FILL("M4 20Q3.175 20 2.5875 19.4125T2 18V6Q2 5.175 2.5875 4.5875T4 4H10L12 6H20Q20.825 6 21.4125 6.5875T22 8V18Q22 18.825 21.4125 19.4125T20 20H4Z"), FOLDER_OPEN("M4 20Q3.175 20 2.5875 19.4125T2 18V6Q2 5.175 2.5875 4.5875T4 4H10L12 6H20Q20.825 6 21.4125 6.5875T22 8H11.175L9.175 6H4V18L6.4 10H23.5L20.925 18.575Q20.725 19.225 20.1875 19.6125T19 20H4ZM6.1 18H19L20.8 12H7.9L6.1 18ZM6.1 18 7.9 12 6.1 18ZM4 8V6 8Z"), FORMAT_LIST_BULLETED("M9 19V17H21V19H9ZM9 13V11H21V13H9ZM9 7V5H21V7H9ZM5 20Q4.175 20 3.5875 19.4125T3 18Q3 17.175 3.5875 16.5875T5 16Q5.825 16 6.4125 16.5875T7 18Q7 18.825 6.4125 19.4125T5 20ZM5 14Q4.175 14 3.5875 13.4125T3 12Q3 11.175 3.5875 10.5875T5 10Q5.825 10 6.4125 10.5875T7 12Q7 12.825 6.4125 13.4125T5 14ZM5 8Q4.175 8 3.5875 7.4125T3 6Q3 5.175 3.5875 4.5875T5 4Q5.825 4 6.4125 4.5875T7 6Q7 6.825 6.4125 7.4125T5 8Z"), FORT("M1 21V17l2-2V9L1 7V3H3V5H5V3H7V5H9V3h2V7L9 9v1h6V9L13 7V3h2V5h2V3h2V5h2V3h2V7L21 9v6l2 2v4H14V18q0-.825-.5875-1.4125T12 16q-.825 0-1.4125.5875T10 18v3H1Zm2-2H8V18q0-1.65 1.175-2.825T12 14q1.65 0 2.825 1.175T16 18v1h5V17.825l-2-2V8.175L20.175 7h-4.35L17 8.175V12H7V8.175L8.175 7H3.825L5 8.175v7.65l-2 2V19Zm9-6Z"), diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java index ce637ac4bbd..5b6dd80243b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java @@ -21,13 +21,14 @@ import javafx.css.PseudoClass; import javafx.geometry.Insets; import javafx.geometry.Pos; -import javafx.scene.Node; import javafx.scene.control.SkinBase; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import org.jackhuang.hmcl.setting.GameDirectoryManager; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.ui.SVGContainer; +import org.jackhuang.hmcl.ui.animation.Motion; import org.jackhuang.hmcl.ui.construct.RipplerContainer; import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jetbrains.annotations.NotNullByDefault; @@ -48,18 +49,20 @@ public GameDirectoryListItemSkin(GameDirectoryListItem skinnable) { root.setPickOnBounds(false); RipplerContainer container = new RipplerContainer(root); + SVGContainer left = SVG.FOLDER.createIcon(20); + left.setMouseTransparent(true); + BorderPane.setMargin(left, new Insets(0, 6, 0, 6)); + BorderPane.setAlignment(left, Pos.CENTER_LEFT); + root.setLeft(left); + FXUtils.onChangeAndOperate(skinnable.selectedProperty(), active -> { skinnable.pseudoClassStateChanged(SELECTED, active); + + left.setIcon(active ? SVG.FOLDER_FILL : SVG.FOLDER, Motion.SHORT4); }); FXUtils.onClicked(getSkinnable(), () -> GameDirectoryManager.setSelectedGameDirectory(skinnable.getGameDirectory())); - Node left = SVG.FOLDER.createIcon(20); - left.setMouseTransparent(true); - BorderPane.setMargin(left, new Insets(0, 6, 0, 6)); - root.setLeft(left); - BorderPane.setAlignment(left, Pos.CENTER_LEFT); - TwoLineListItem item = new TwoLineListItem(); item.setPickOnBounds(false); BorderPane.setAlignment(item, Pos.CENTER); From b2f1b7778325103f42fa406eea4a02079cd18007 Mon Sep 17 00:00:00 2001 From: KSSJW <165921128+KSSJW@users.noreply.github.com> Date: Sun, 19 Jul 2026 14:58:00 +0800 Subject: [PATCH 2/5] Fix: Optimizing state change --- .../hmcl/ui/directory/GameDirectoryListItemSkin.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java index 5b6dd80243b..374e3bb9843 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java @@ -57,8 +57,11 @@ public GameDirectoryListItemSkin(GameDirectoryListItem skinnable) { FXUtils.onChangeAndOperate(skinnable.selectedProperty(), active -> { skinnable.pseudoClassStateChanged(SELECTED, active); - - left.setIcon(active ? SVG.FOLDER_FILL : SVG.FOLDER, Motion.SHORT4); + + SVG targetIcon = active ? SVG.FOLDER_FILL : SVG.FOLDER; + if (left.getIcon() != targetIcon) { + left.setIcon(targetIcon, Motion.SHORT4); + } }); FXUtils.onClicked(getSkinnable(), () -> GameDirectoryManager.setSelectedGameDirectory(skinnable.getGameDirectory())); From e798cebb462ac26b74b84cfb3d6be6bdb6762885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BF=AB=E4=B9=90=E5=87=A4=E7=88=AA=E7=8E=8B?= <165921128+KSSJW@users.noreply.github.com> Date: Sun, 19 Jul 2026 15:03:49 +0800 Subject: [PATCH 3/5] Fix: Initial selection state Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java index 374e3bb9843..d4fed54b22f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java @@ -49,7 +49,7 @@ public GameDirectoryListItemSkin(GameDirectoryListItem skinnable) { root.setPickOnBounds(false); RipplerContainer container = new RipplerContainer(root); - SVGContainer left = SVG.FOLDER.createIcon(20); + SVGContainer left = (skinnable.selectedProperty().get() ? SVG.FOLDER_FILL : SVG.FOLDER).createIcon(20); left.setMouseTransparent(true); BorderPane.setMargin(left, new Insets(0, 6, 0, 6)); BorderPane.setAlignment(left, Pos.CENTER_LEFT); From c92adbf7f70048709eab088124c0b0c3826c3027 Mon Sep 17 00:00:00 2001 From: KSSJW <165921128+KSSJW@users.noreply.github.com> Date: Sun, 19 Jul 2026 15:07:51 +0800 Subject: [PATCH 4/5] Refactor: skinnable.isSelected() --- .../jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java index d4fed54b22f..2b1feba0de0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/directory/GameDirectoryListItemSkin.java @@ -49,7 +49,7 @@ public GameDirectoryListItemSkin(GameDirectoryListItem skinnable) { root.setPickOnBounds(false); RipplerContainer container = new RipplerContainer(root); - SVGContainer left = (skinnable.selectedProperty().get() ? SVG.FOLDER_FILL : SVG.FOLDER).createIcon(20); + SVGContainer left = (skinnable.isSelected() ? SVG.FOLDER_FILL : SVG.FOLDER).createIcon(20); left.setMouseTransparent(true); BorderPane.setMargin(left, new Insets(0, 6, 0, 6)); BorderPane.setAlignment(left, Pos.CENTER_LEFT); From 54b4c9350d2439fd7475018f3e7c48aae986fb25 Mon Sep 17 00:00:00 2001 From: KSSJW <165921128+KSSJW@users.noreply.github.com> Date: Mon, 20 Jul 2026 11:38:41 +0800 Subject: [PATCH 5/5] Fix: Precise SVG Scaling --- HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java index 2a33131ab8b..9991ac13b85 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java @@ -66,7 +66,7 @@ public enum SVG { FILE_OPEN("M6 22q-0.825 0-1.4125-0.5875T4 20v-16q0-0.825 0.5875-1.4125T6 2h8l6 6v6h-2v-5H13v-5H6v16h9v2H6Zm15.95 0.375L19 19.425v2.225h-2v-5.65h5.65v2h-2.25l2.95 2.95-1.4 1.425Zm-15.95-2.375v-16 16Z"), FOLDER("M4 20Q3.175 20 2.5875 19.4125T2 18V6Q2 5.175 2.5875 4.5875T4 4H10L12 6H20Q20.825 6 21.4125 6.5875T22 8V18Q22 18.825 21.4125 19.4125T20 20H4ZM4 18H20V8H11.175L9.175 6H4V18ZM4 18V6 18Z"), FOLDER_COPY("M3 21Q2.175 21 1.5875 20.4125T1 19V6H3V19H20V21H3ZM7 17Q6.175 17 5.5875 16.4125T5 15V4Q5 3.175 5.5875 2.5875T7 2H12L14 4H21Q21.825 4 22.4125 4.5875T23 6V15Q23 15.825 22.4125 16.4125T21 17H7ZM7 15H21V6H13.175L11.175 4H7V15ZM7 15V4 15Z"), - FOLDER_FILL("M4 20Q3.175 20 2.5875 19.4125T2 18V6Q2 5.175 2.5875 4.5875T4 4H10L12 6H20Q20.825 6 21.4125 6.5875T22 8V18Q22 18.825 21.4125 19.4125T20 20H4Z"), + FOLDER_FILL("M4 20q-.825 0-1.4125-.5875T2 18v-12q0-.825.5875-1.4125T4 4h6l2 2h8q.825 0 1.4125.5875T22 8v10q0 .825-.5875 1.4125T20 20H4Z"), FOLDER_OPEN("M4 20Q3.175 20 2.5875 19.4125T2 18V6Q2 5.175 2.5875 4.5875T4 4H10L12 6H20Q20.825 6 21.4125 6.5875T22 8H11.175L9.175 6H4V18L6.4 10H23.5L20.925 18.575Q20.725 19.225 20.1875 19.6125T19 20H4ZM6.1 18H19L20.8 12H7.9L6.1 18ZM6.1 18 7.9 12 6.1 18ZM4 8V6 8Z"), FORMAT_LIST_BULLETED("M9 19V17H21V19H9ZM9 13V11H21V13H9ZM9 7V5H21V7H9ZM5 20Q4.175 20 3.5875 19.4125T3 18Q3 17.175 3.5875 16.5875T5 16Q5.825 16 6.4125 16.5875T7 18Q7 18.825 6.4125 19.4125T5 20ZM5 14Q4.175 14 3.5875 13.4125T3 12Q3 11.175 3.5875 10.5875T5 10Q5.825 10 6.4125 10.5875T7 12Q7 12.825 6.4125 13.4125T5 14ZM5 8Q4.175 8 3.5875 7.4125T3 6Q3 5.175 3.5875 4.5875T5 4Q5.825 4 6.4125 4.5875T7 6Q7 6.825 6.4125 7.4125T5 8Z"), FORT("M1 21V17l2-2V9L1 7V3H3V5H5V3H7V5H9V3h2V7L9 9v1h6V9L13 7V3h2V5h2V3h2V5h2V3h2V7L21 9v6l2 2v4H14V18q0-.825-.5875-1.4125T12 16q-.825 0-1.4125.5875T10 18v3H1Zm2-2H8V18q0-1.65 1.175-2.825T12 14q1.65 0 2.825 1.175T16 18v1h5V17.825l-2-2V8.175L20.175 7h-4.35L17 8.175V12H7V8.175L8.175 7H3.825L5 8.175v7.65l-2 2V19Zm9-6Z"),