1717 */
1818package org .jackhuang .hmcl .ui .construct ;
1919
20- import javafx .beans .DefaultProperty ;
20+ import javafx .beans .InvalidationListener ;
2121import javafx .beans .binding .Bindings ;
22- import javafx .beans .binding .ObjectBinding ;
23- import javafx .beans .property .IntegerProperty ;
24- import javafx .beans .property .SimpleIntegerProperty ;
25- import javafx .beans .property .SimpleStringProperty ;
26- import javafx .beans .property .StringProperty ;
2722import javafx .collections .FXCollections ;
2823import javafx .collections .ObservableList ;
2924import javafx .css .PseudoClass ;
3328import javafx .scene .Node ;
3429import javafx .scene .control .Control ;
3530import javafx .scene .control .Label ;
36- import javafx .scene .layout .HBox ;
37- import javafx .scene .layout .Priority ;
38- import javafx .scene .layout .VBox ;
31+ import javafx .scene .layout .*;
3932import org .jackhuang .hmcl .util .javafx .MappedObservableList ;
4033
41- import java .util .List ;
42- import java .util .function .Supplier ;
43-
44- @ DefaultProperty ("content" )
45- public class ComponentList extends Control {
46- private final StringProperty title = new SimpleStringProperty (this , "title" , "Group" );
47- private final StringProperty subtitle = new SimpleStringProperty (this , "subtitle" , "" );
48- private final IntegerProperty depth = new SimpleIntegerProperty (this , "depth" , 0 );
49- private boolean hasSubtitle = false ;
50- public final ObservableList <Node > content = FXCollections .observableArrayList ();
51- private Supplier <List <? extends Node >> lazyInitializer ;
34+ public class ComponentList extends Control implements NoPaddingComponent {
5235
5336 public ComponentList () {
5437 getStyleClass ().add ("options-list" );
5538 }
5639
57- public ComponentList (Supplier <List <? extends Node >> lazyInitializer ) {
58- this ();
59- this .lazyInitializer = lazyInitializer ;
60- }
61-
62- public String getTitle () {
63- return title .get ();
64- }
65-
66- public StringProperty titleProperty () {
67- return title ;
68- }
69-
70- public void setTitle (String title ) {
71- this .title .set (title );
72- }
73-
74- public String getSubtitle () {
75- return subtitle .get ();
76- }
77-
78- public StringProperty subtitleProperty () {
79- return subtitle ;
80- }
81-
82- public void setSubtitle (String subtitle ) {
83- this .subtitle .set (subtitle );
84- }
85-
86- public int getDepth () {
87- return depth .get ();
88- }
89-
90- public IntegerProperty depthProperty () {
91- return depth ;
92- }
93-
94- public void setDepth (int depth ) {
95- this .depth .set (depth );
96- }
97-
98- public boolean isHasSubtitle () {
99- return hasSubtitle ;
100- }
101-
102- public void setHasSubtitle (boolean hasSubtitle ) {
103- this .hasSubtitle = hasSubtitle ;
104- }
40+ private final ObservableList <Node > content = FXCollections .observableArrayList ();
10541
10642 public ObservableList <Node > getContent () {
10743 return content ;
10844 }
10945
110- void doLazyInit () {
111- if (lazyInitializer != null ) {
112- this .getContent ().setAll (lazyInitializer .get ());
113- setNeedsLayout (true );
114- lazyInitializer = null ;
115- }
116- }
117-
11846 @ Override
11947 public Orientation getContentBias () {
12048 return Orientation .HORIZONTAL ;
@@ -130,49 +58,72 @@ private static final class Skin extends ControlSkinBase<ComponentList> {
13058 private static final PseudoClass PSEUDO_CLASS_LAST = PseudoClass .getPseudoClass ("last" );
13159
13260 private final ObservableList <Node > list ;
133- private final ObjectBinding <Node > firstItem ;
134- private final ObjectBinding <Node > lastItem ;
13561
13662 Skin (ComponentList control ) {
13763 super (control );
13864
13965 list = MappedObservableList .create (control .getContent (), node -> {
140- ComponentListCell cell = new ComponentListCell (node );
141- cell .getStyleClass ().add ("options-list-item" );
142- if (node .getProperties ().containsKey ("ComponentList.vgrow" )) {
143- VBox .setVgrow (cell , (Priority ) node .getProperties ().get ("ComponentList.vgrow" ));
66+ Pane wrapper ;
67+ if (node instanceof ComponentSublist sublist ) {
68+ sublist .getStyleClass ().remove ("options-list" );
69+ sublist .getStyleClass ().add ("options-sublist" );
70+ wrapper = new ComponentSublistWrapper (sublist );
71+ } else {
72+ wrapper = new StackPane (node );
73+ }
74+
75+ wrapper .getStyleClass ().add ("options-list-item" );
76+
77+ if (node .getProperties ().get ("ComponentList.vgrow" ) instanceof Priority priority ) {
78+ VBox .setVgrow (wrapper , priority );
14479 }
145- if (node instanceof LineButtonBase || node instanceof LinePane || node .getProperties ().containsKey ("ComponentList.noPadding" )) {
146- cell .getStyleClass ().add ("no-padding" );
80+
81+ if (node instanceof NoPaddingComponent || node .getProperties ().containsKey ("ComponentList.noPadding" )) {
82+ wrapper .getStyleClass ().add ("no-padding" );
14783 }
148- return cell ;
84+ return wrapper ;
14985 });
15086
151- firstItem = Bindings .valueAt (list , 0 );
152- firstItem .addListener ((observable , oldValue , newValue ) -> {
153- if (newValue != null )
154- newValue .pseudoClassStateChanged (PSEUDO_CLASS_FIRST , true );
155- if (oldValue != null )
156- oldValue .pseudoClassStateChanged (PSEUDO_CLASS_FIRST , false );
157- });
158- if (!list .isEmpty ())
159- list .get (0 ).pseudoClassStateChanged (PSEUDO_CLASS_FIRST , true );
160-
161- lastItem = Bindings .valueAt (list , Bindings .subtract (Bindings .size (list ), 1 ));
162- lastItem .addListener ((observable , oldValue , newValue ) -> {
163- if (newValue != null )
164- newValue .pseudoClassStateChanged (PSEUDO_CLASS_LAST , true );
165- if (oldValue != null )
166- oldValue .pseudoClassStateChanged (PSEUDO_CLASS_LAST , false );
167- });
168- if (!list .isEmpty ())
169- list .get (list .size () - 1 ).pseudoClassStateChanged (PSEUDO_CLASS_LAST , true );
87+ updateStyle ();
88+ list .addListener ((InvalidationListener ) o -> updateStyle ());
17089
17190 VBox vbox = new VBox ();
17291 vbox .setFillWidth (true );
17392 Bindings .bindContent (vbox .getChildren (), list );
17493 node = vbox ;
17594 }
95+
96+ private Node prevFirstItem ;
97+ private Node prevLastItem ;
98+
99+ private void updateStyle () {
100+ Node firstItem ;
101+ Node lastItem ;
102+
103+ if (list .isEmpty ()) {
104+ firstItem = null ;
105+ lastItem = null ;
106+ } else {
107+ firstItem = list .get (0 );
108+ lastItem = list .get (list .size () - 1 );
109+ }
110+
111+ if (firstItem != prevFirstItem ) {
112+ if (prevFirstItem != null )
113+ prevFirstItem .pseudoClassStateChanged (PSEUDO_CLASS_FIRST , false );
114+ if (firstItem != null )
115+ firstItem .pseudoClassStateChanged (PSEUDO_CLASS_FIRST , true );
116+ prevFirstItem = firstItem ;
117+ }
118+
119+ if (lastItem != prevLastItem ) {
120+ if (prevLastItem != null )
121+ prevLastItem .pseudoClassStateChanged (PSEUDO_CLASS_LAST , false );
122+ if (lastItem != null )
123+ lastItem .pseudoClassStateChanged (PSEUDO_CLASS_LAST , true );
124+ prevLastItem = lastItem ;
125+ }
126+ }
176127 }
177128
178129 public static Node createComponentListTitle (String title ) {
@@ -189,4 +140,8 @@ public static Node createComponentListTitle(String title) {
189140 public static void setVgrow (Node node , Priority priority ) {
190141 node .getProperties ().put ("ComponentList.vgrow" , priority );
191142 }
143+
144+ public static void setNoPadding (Node node ) {
145+ node .getProperties ().put ("ComponentList.noPadding" , true );
146+ }
192147}
0 commit comments