166 * rectangle = new Rectangle(10, 10);
167 * }
168 *
169 * @Override protected void updateItem(Color item, boolean empty) {
170 * super.updateItem(item, empty);
171 *
172 * if (item == null || empty) {
173 * setGraphic(null);
174 * } else {
175 * rectangle.setFill(item);
176 * setGraphic(rectangle);
177 * }
178 * }
179 * };
180 * }
181 *});</code></pre>
182 *
183 * <p>Admittedly the above approach is far more verbose, but it offers the
184 * required functionality without encountering the scenegraph constraints.
185 *
186 * @see ComboBoxBase
187 * @see Cell
188 * @see ListCell
189 * @see StringConverter
190 * @since JavaFX 2.1
191 */
192 public class ComboBox<T> extends ComboBoxBase<T> {
193
194 /***************************************************************************
195 * *
196 * Static properties and methods *
197 * *
198 **************************************************************************/
199
200 private static <T> StringConverter<T> defaultStringConverter() {
201 return new StringConverter<T>() {
202 @Override public String toString(T t) {
203 return t == null ? null : t.toString();
204 }
205
212
213
214 /***************************************************************************
215 * *
216 * Constructors *
217 * *
218 **************************************************************************/
219
220 /**
221 * Creates a default ComboBox instance with an empty
222 * {@link #itemsProperty() items} list and default
223 * {@link #selectionModelProperty() selection model}.
224 */
225 public ComboBox() {
226 this(FXCollections.<T>observableArrayList());
227 }
228
229 /**
230 * Creates a default ComboBox instance with the provided items list and
231 * a default {@link #selectionModelProperty() selection model}.
232 */
233 public ComboBox(ObservableList<T> items) {
234 getStyleClass().add(DEFAULT_STYLE_CLASS);
235 setAccessibleRole(AccessibleRole.COMBO_BOX);
236 setItems(items);
237 setSelectionModel(new ComboBoxSelectionModel<T>(this));
238
239 // listen to the value property input by the user, and if the value is
240 // set to something that exists in the items list, we should update the
241 // selection model to indicate that this is the selected item
242 valueProperty().addListener((ov, t, t1) -> {
243 if (getItems() == null) return;
244
245 SelectionModel<T> sm = getSelectionModel();
246 int index = getItems().indexOf(t1);
247
248 if (index == -1) {
249 Runnable r = () -> {
250 sm.setSelectedIndex(-1);
251 sm.setSelectedItem(t1);
297 * *
298 * Properties *
299 * *
300 **************************************************************************/
301
302 // --- items
303 /**
304 * The list of items to show within the ComboBox popup.
305 */
306 private ObjectProperty<ObservableList<T>> items = new SimpleObjectProperty<ObservableList<T>>(this, "items");
307 public final void setItems(ObservableList<T> value) { itemsProperty().set(value); }
308 public final ObservableList<T> getItems() {return items.get(); }
309 public ObjectProperty<ObservableList<T>> itemsProperty() { return items; }
310
311
312 // --- string converter
313 /**
314 * Converts the user-typed input (when the ComboBox is
315 * {@link #editableProperty() editable}) to an object of type T, such that
316 * the input may be retrieved via the {@link #valueProperty() value} property.
317 */
318 public ObjectProperty<StringConverter<T>> converterProperty() { return converter; }
319 private ObjectProperty<StringConverter<T>> converter =
320 new SimpleObjectProperty<StringConverter<T>>(this, "converter", ComboBox.<T>defaultStringConverter());
321 public final void setConverter(StringConverter<T> value) { converterProperty().set(value); }
322 public final StringConverter<T> getConverter() {return converterProperty().get(); }
323
324
325 // --- cell factory
326 /**
327 * Providing a custom cell factory allows for complete customization of the
328 * rendering of items in the ComboBox. Refer to the {@link Cell} javadoc
329 * for more information on cell factories.
330 */
331 private ObjectProperty<Callback<ListView<T>, ListCell<T>>> cellFactory =
332 new SimpleObjectProperty<Callback<ListView<T>, ListCell<T>>>(this, "cellFactory");
333 public final void setCellFactory(Callback<ListView<T>, ListCell<T>> value) { cellFactoryProperty().set(value); }
334 public final Callback<ListView<T>, ListCell<T>> getCellFactory() {return cellFactoryProperty().get(); }
335 public ObjectProperty<Callback<ListView<T>, ListCell<T>>> cellFactoryProperty() { return cellFactory; }
336
337
338 // --- button cell
339 /**
340 * The button cell is used to render what is shown in the ComboBox 'button'
341 * area. If a cell is set here, it does not change the rendering of the
342 * ComboBox popup list - that rendering is controlled via the
343 * {@link #cellFactoryProperty() cell factory} API.
344 * @since JavaFX 2.2
345 */
346 public ObjectProperty<ListCell<T>> buttonCellProperty() { return buttonCell; }
347 private ObjectProperty<ListCell<T>> buttonCell =
348 new SimpleObjectProperty<ListCell<T>>(this, "buttonCell");
349 public final void setButtonCell(ListCell<T> value) { buttonCellProperty().set(value); }
350 public final ListCell<T> getButtonCell() {return buttonCellProperty().get(); }
351
352
353 // --- Selection Model
354 /**
355 * The selection model for the ComboBox. A ComboBox only supports
356 * single selection.
357 */
358 private ObjectProperty<SingleSelectionModel<T>> selectionModel = new SimpleObjectProperty<SingleSelectionModel<T>>(this, "selectionModel") {
359 private SingleSelectionModel<T> oldSM = null;
360 @Override protected void invalidated() {
361 if (oldSM != null) {
362 oldSM.selectedItemProperty().removeListener(selectedItemListener);
363 }
396 private ReadOnlyObjectWrapper<TextField> editor;
397 public final TextField getEditor() {
398 return editorProperty().get();
399 }
400 public final ReadOnlyObjectProperty<TextField> editorProperty() {
401 if (editor == null) {
402 editor = new ReadOnlyObjectWrapper<>(this, "editor");
403 textField = new FakeFocusTextField();
404 editor.set(textField);
405 }
406 return editor.getReadOnlyProperty();
407 }
408
409
410 // --- Placeholder Node
411 private ObjectProperty<Node> placeholder;
412 /**
413 * This Node is shown to the user when the ComboBox has no content to show.
414 * The placeholder node is shown in the ComboBox popup area
415 * when the items list is null or empty.
416 * @since JavaFX 8.0
417 */
418 public final ObjectProperty<Node> placeholderProperty() {
419 if (placeholder == null) {
420 placeholder = new SimpleObjectProperty<Node>(this, "placeholder");
421 }
422 return placeholder;
423 }
424 public final void setPlaceholder(Node value) {
425 placeholderProperty().set(value);
426 }
427 public final Node getPlaceholder() {
428 return placeholder == null ? null : placeholder.get();
429 }
430
431
432
433 /***************************************************************************
434 * *
435 * Methods *
|
166 * rectangle = new Rectangle(10, 10);
167 * }
168 *
169 * @Override protected void updateItem(Color item, boolean empty) {
170 * super.updateItem(item, empty);
171 *
172 * if (item == null || empty) {
173 * setGraphic(null);
174 * } else {
175 * rectangle.setFill(item);
176 * setGraphic(rectangle);
177 * }
178 * }
179 * };
180 * }
181 *});</code></pre>
182 *
183 * <p>Admittedly the above approach is far more verbose, but it offers the
184 * required functionality without encountering the scenegraph constraints.
185 *
186 * @param <T> The type of the value that has been selected or otherwise entered
187 * in to this ComboBox
188 * @see ComboBoxBase
189 * @see Cell
190 * @see ListCell
191 * @see StringConverter
192 * @since JavaFX 2.1
193 */
194 public class ComboBox<T> extends ComboBoxBase<T> {
195
196 /***************************************************************************
197 * *
198 * Static properties and methods *
199 * *
200 **************************************************************************/
201
202 private static <T> StringConverter<T> defaultStringConverter() {
203 return new StringConverter<T>() {
204 @Override public String toString(T t) {
205 return t == null ? null : t.toString();
206 }
207
214
215
216 /***************************************************************************
217 * *
218 * Constructors *
219 * *
220 **************************************************************************/
221
222 /**
223 * Creates a default ComboBox instance with an empty
224 * {@link #itemsProperty() items} list and default
225 * {@link #selectionModelProperty() selection model}.
226 */
227 public ComboBox() {
228 this(FXCollections.<T>observableArrayList());
229 }
230
231 /**
232 * Creates a default ComboBox instance with the provided items list and
233 * a default {@link #selectionModelProperty() selection model}.
234 * @param items the list of items
235 */
236 public ComboBox(ObservableList<T> items) {
237 getStyleClass().add(DEFAULT_STYLE_CLASS);
238 setAccessibleRole(AccessibleRole.COMBO_BOX);
239 setItems(items);
240 setSelectionModel(new ComboBoxSelectionModel<T>(this));
241
242 // listen to the value property input by the user, and if the value is
243 // set to something that exists in the items list, we should update the
244 // selection model to indicate that this is the selected item
245 valueProperty().addListener((ov, t, t1) -> {
246 if (getItems() == null) return;
247
248 SelectionModel<T> sm = getSelectionModel();
249 int index = getItems().indexOf(t1);
250
251 if (index == -1) {
252 Runnable r = () -> {
253 sm.setSelectedIndex(-1);
254 sm.setSelectedItem(t1);
300 * *
301 * Properties *
302 * *
303 **************************************************************************/
304
305 // --- items
306 /**
307 * The list of items to show within the ComboBox popup.
308 */
309 private ObjectProperty<ObservableList<T>> items = new SimpleObjectProperty<ObservableList<T>>(this, "items");
310 public final void setItems(ObservableList<T> value) { itemsProperty().set(value); }
311 public final ObservableList<T> getItems() {return items.get(); }
312 public ObjectProperty<ObservableList<T>> itemsProperty() { return items; }
313
314
315 // --- string converter
316 /**
317 * Converts the user-typed input (when the ComboBox is
318 * {@link #editableProperty() editable}) to an object of type T, such that
319 * the input may be retrieved via the {@link #valueProperty() value} property.
320 * @return the converter property
321 */
322 public ObjectProperty<StringConverter<T>> converterProperty() { return converter; }
323 private ObjectProperty<StringConverter<T>> converter =
324 new SimpleObjectProperty<StringConverter<T>>(this, "converter", ComboBox.<T>defaultStringConverter());
325 public final void setConverter(StringConverter<T> value) { converterProperty().set(value); }
326 public final StringConverter<T> getConverter() {return converterProperty().get(); }
327
328
329 // --- cell factory
330 /**
331 * Providing a custom cell factory allows for complete customization of the
332 * rendering of items in the ComboBox. Refer to the {@link Cell} javadoc
333 * for more information on cell factories.
334 */
335 private ObjectProperty<Callback<ListView<T>, ListCell<T>>> cellFactory =
336 new SimpleObjectProperty<Callback<ListView<T>, ListCell<T>>>(this, "cellFactory");
337 public final void setCellFactory(Callback<ListView<T>, ListCell<T>> value) { cellFactoryProperty().set(value); }
338 public final Callback<ListView<T>, ListCell<T>> getCellFactory() {return cellFactoryProperty().get(); }
339 public ObjectProperty<Callback<ListView<T>, ListCell<T>>> cellFactoryProperty() { return cellFactory; }
340
341
342 // --- button cell
343 /**
344 * The button cell is used to render what is shown in the ComboBox 'button'
345 * area. If a cell is set here, it does not change the rendering of the
346 * ComboBox popup list - that rendering is controlled via the
347 * {@link #cellFactoryProperty() cell factory} API.
348 * @return the button cell property
349 * @since JavaFX 2.2
350 */
351 public ObjectProperty<ListCell<T>> buttonCellProperty() { return buttonCell; }
352 private ObjectProperty<ListCell<T>> buttonCell =
353 new SimpleObjectProperty<ListCell<T>>(this, "buttonCell");
354 public final void setButtonCell(ListCell<T> value) { buttonCellProperty().set(value); }
355 public final ListCell<T> getButtonCell() {return buttonCellProperty().get(); }
356
357
358 // --- Selection Model
359 /**
360 * The selection model for the ComboBox. A ComboBox only supports
361 * single selection.
362 */
363 private ObjectProperty<SingleSelectionModel<T>> selectionModel = new SimpleObjectProperty<SingleSelectionModel<T>>(this, "selectionModel") {
364 private SingleSelectionModel<T> oldSM = null;
365 @Override protected void invalidated() {
366 if (oldSM != null) {
367 oldSM.selectedItemProperty().removeListener(selectedItemListener);
368 }
401 private ReadOnlyObjectWrapper<TextField> editor;
402 public final TextField getEditor() {
403 return editorProperty().get();
404 }
405 public final ReadOnlyObjectProperty<TextField> editorProperty() {
406 if (editor == null) {
407 editor = new ReadOnlyObjectWrapper<>(this, "editor");
408 textField = new FakeFocusTextField();
409 editor.set(textField);
410 }
411 return editor.getReadOnlyProperty();
412 }
413
414
415 // --- Placeholder Node
416 private ObjectProperty<Node> placeholder;
417 /**
418 * This Node is shown to the user when the ComboBox has no content to show.
419 * The placeholder node is shown in the ComboBox popup area
420 * when the items list is null or empty.
421 * @return the placeholder property
422 * @since JavaFX 8.0
423 */
424 public final ObjectProperty<Node> placeholderProperty() {
425 if (placeholder == null) {
426 placeholder = new SimpleObjectProperty<Node>(this, "placeholder");
427 }
428 return placeholder;
429 }
430 public final void setPlaceholder(Node value) {
431 placeholderProperty().set(value);
432 }
433 public final Node getPlaceholder() {
434 return placeholder == null ? null : placeholder.get();
435 }
436
437
438
439 /***************************************************************************
440 * *
441 * Methods *
|