3636import java .io .InterruptedIOException ;
3737import java .util .ArrayList ;
3838import java .util .Collections ;
39+ import java .util .EventListener ;
3940import java .util .List ;
4041import java .util .Objects ;
4142import java .util .Optional ;
4243import java .util .function .BiPredicate ;
4344import java .util .function .Function ;
4445import java .util .function .Predicate ;
46+ import java .util .function .Supplier ;
4547
4648public abstract class AzureComboBox <T > extends ComboBox <T > implements AzureFormInputComponent <T > {
4749 public static final String EMPTY_ITEM = StringUtils .EMPTY ;
@@ -54,8 +56,13 @@ public abstract class AzureComboBox<T> extends ComboBox<T> implements AzureFormI
5456 private boolean required ;
5557 private Object value ;
5658 private boolean valueNotSet = true ;
57- private boolean valueFixed ;
5859 private String label ;
60+ @ Nullable
61+ @ Setter
62+ protected Boolean valueFixed ;
63+ @ Getter
64+ @ Setter
65+ private Supplier <? extends List <? extends T >> itemsLoader ;
5966
6067 public AzureComboBox () {
6168 this (true );
@@ -70,6 +77,15 @@ public AzureComboBox(boolean refresh) {
7077 }
7178 }
7279
80+ public AzureComboBox (@ Nonnull Supplier <? extends List <? extends T >> itemsLoader ) {
81+ this (itemsLoader , true );
82+ }
83+
84+ public AzureComboBox (@ Nonnull Supplier <? extends List <? extends T >> itemsLoader , boolean refresh ) {
85+ this (refresh );
86+ this .itemsLoader = itemsLoader ;
87+ }
88+
7389 @ Override
7490 public JComponent getInputComponent () {
7591 return this ;
@@ -80,7 +96,7 @@ protected void init() {
8096 this .inputEditor = new AzureComboBoxEditor ();
8197 this .setEditable (true );
8298 this .setEditor (this .inputEditor );
83- this .setRenderer (new SimpleListCellRenderer <T >() {
99+ this .setRenderer (new SimpleListCellRenderer <>() {
84100 @ Override
85101 public void customize (@ Nonnull final JList <? extends T > l , final T t , final int i , final boolean b ,
86102 final boolean b1 ) {
@@ -91,10 +107,20 @@ public void customize(@Nonnull final JList<? extends T> l, final T t, final int
91107 if (isFilterable ()) {
92108 this .addPopupMenuListener (new AzureComboBoxPopupMenuListener ());
93109 }
110+ final TailingDebouncer valueDebouncer = new TailingDebouncer (() -> {
111+ @ SuppressWarnings ("unchecked" )
112+ final ValueListener <T >[] listeners = this .listenerList .getListeners (ValueListener .class );
113+ for (final ValueListener <T > listener : listeners ) {
114+ listener .onValueChanged (this .getValue ());
115+ }
116+ }, DEBOUNCE_DELAY );
94117 this .addItemListener ((e ) -> {
95118 if (e .getStateChange () == ItemEvent .SELECTED ) {
96119 this .refreshValue ();
97120 }
121+ if (e .getStateChange () == ItemEvent .SELECTED || e .getStateChange () == ItemEvent .DESELECTED ) {
122+ valueDebouncer .debounce ();
123+ }
98124 });
99125 }
100126
@@ -105,24 +131,28 @@ public T getValue() {
105131
106132 @ Override
107133 public void setValue (final T val ) {
108- this .setValue (val , false );
134+ this .setValue (val , null );
109135 }
110136
111- public void setValue (final T val , final boolean fixed ) {
112- this .valueFixed = fixed ;
113- this .setEditable (!this .valueFixed );
137+ public void setValue (final T val , final Boolean fixed ) {
138+ Optional .ofNullable (fixed ).ifPresent (f -> {
139+ this .valueFixed = fixed ;
140+ this .setEditable (!f );
141+ });
114142 this .valueNotSet = false ;
115143 this .value = val ;
116144 this .refreshValue ();
117145 }
118146
119147 public void setValue (final ItemReference <T > val ) {
120- this .setValue (val , false );
148+ this .setValue (val , null );
121149 }
122150
123- public void setValue (final ItemReference <T > val , final boolean fixed ) {
124- this .valueFixed = fixed ;
125- this .setEditable (!this .valueFixed );
151+ public void setValue (final ItemReference <T > val , final Boolean fixed ) {
152+ Optional .ofNullable (fixed ).ifPresent (f -> {
153+ this .valueFixed = fixed ;
154+ this .setEditable (!f );
155+ });
126156 this .valueNotSet = false ;
127157 this .value = val ;
128158 this .refreshValue ();
@@ -166,7 +196,7 @@ public void refreshItems() {
166196 private void doRefreshItems () {
167197 try {
168198 this .setLoading (true );
169- final List <? extends T > items = this .loadItems ();
199+ final List <? extends T > items = this .loadItemsInner ();
170200 this .setLoading (false );
171201 AzureTaskManager .getInstance ().runLater (() -> this .setItems (items ), AzureTask .Modality .ANY );
172202 } catch (final Exception e ) {
@@ -203,7 +233,7 @@ protected void setLoading(final boolean loading) {
203233 this .setEnabled (false );
204234 this .setEditor (this .loadingSpinner );
205235 } else {
206- this .setEnabled (!valueFixed );
236+ this .setEnabled (!Objects . equals ( valueFixed , true ) );
207237 this .setEditor (this .inputEditor );
208238 }
209239 }, AzureTask .Modality .ANY );
@@ -227,11 +257,21 @@ protected ExtendableTextComponent.Extension getExtension() {
227257 }
228258
229259 protected Observable <? extends List <? extends T >> loadItemsAsync () {
230- return Observable .fromCallable (this ::loadItems ).subscribeOn (Schedulers .io ());
260+ return Observable .fromCallable (this ::loadItemsInner ).subscribeOn (Schedulers .io ());
261+ }
262+
263+ protected final List <? extends T > loadItemsInner () throws Exception {
264+ if (Objects .nonNull (this .itemsLoader )) {
265+ return this .itemsLoader .get ();
266+ } else {
267+ return this .loadItems ();
268+ }
231269 }
232270
233271 @ Nonnull
234- protected abstract List <? extends T > loadItems () throws Exception ;
272+ protected List <? extends T > loadItems () throws Exception {
273+ return Collections .emptyList ();
274+ }
235275
236276 @ Nullable
237277 protected T getDefaultValue () {
@@ -394,4 +434,17 @@ public boolean is(Object obj) {
394434 return this .predicate .test ((T ) obj );
395435 }
396436 }
437+
438+ public void addValueListener (ValueListener <T > listener ) {
439+ this .listenerList .add (ValueListener .class , listener );
440+ }
441+
442+ public void removeValueListener (ValueListener <T > listener ) {
443+ this .listenerList .remove (ValueListener .class , listener );
444+ }
445+
446+ @ FunctionalInterface
447+ public interface ValueListener <T > extends EventListener {
448+ void onValueChanged (@ Nullable T value );
449+ }
397450}
0 commit comments