1 /* 2 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javafx.scene.control.cell; 27 28 import static javafx.scene.control.cell.CellUtils.createComboBox; 29 import javafx.beans.property.BooleanProperty; 30 import javafx.beans.property.ObjectProperty; 31 import javafx.beans.property.SimpleBooleanProperty; 32 import javafx.beans.property.SimpleObjectProperty; 33 import javafx.collections.FXCollections; 34 import javafx.collections.ObservableList; 35 import javafx.scene.control.*; 36 import javafx.util.Callback; 37 import javafx.util.StringConverter; 38 39 /** 40 * A class containing a {@link ListCell} implementation that draws a 41 * {@link ComboBox} node inside the cell. 42 * 43 * <p>By default, the ComboBoxListCell is rendered as a {@link Label} when not 44 * being edited, and as a ComboBox when in editing mode. The ComboBox will, by 45 * default, stretch to fill the entire list cell. 46 * 47 * <p>To create a ComboBoxListCell, it is necessary to provide zero or more 48 * items that will be shown to the user when the {@link ComboBox} menu is 49 * showing. These items must be of the same type as the ListView items sequence, 50 * such that upon selection, they replace the existing value in the 51 * {@link ListView#itemsProperty() items} list. 52 * 53 * @param <T> The type of the elements contained within the ListView. 54 * @since JavaFX 2.2 55 */ 56 public class ComboBoxListCell<T> extends ListCell<T> { 57 58 /*************************************************************************** 59 * * 60 * Static cell factories * 61 * * 62 **************************************************************************/ 63 64 /** 65 * Creates a ComboBox cell factory for use in {@link ListView} controls. By 66 * default, the ComboBoxCell is rendered as a {@link Label} when not being 67 * edited, and as a ComboBox when in editing mode. The ComboBox will, by 68 * default, stretch to fill the entire list cell. 69 * 70 * @param <T> The type of the elements contained within the ListView. 71 * @param items Zero or more items that will be shown to the user when the 72 * {@link ComboBox} menu is showing. These items must be of the same 73 * type as the ListView items list, such that upon selection, they 74 * replace the existing value in the 75 * {@link ListView#itemsProperty() items} list. 76 * @return A {@link Callback} that will return a ListCell that is able to 77 * work on the type of element contained within the ListView. 78 */ 79 @SafeVarargs 80 public static <T> Callback<ListView<T>, ListCell<T>> forListView(final T... items) { 81 return forListView(FXCollections.observableArrayList(items)); 82 } 83 84 /** 85 * Creates a ComboBox cell factory for use in {@link ListView} controls. By 86 * default, the ComboBoxCell is rendered as a {@link Label} when not being 87 * edited, and as a ComboBox when in editing mode. The ComboBox will, by 88 * default, stretch to fill the entire list cell. 89 * 90 * @param <T> The type of the elements contained within the ListView. 91 * @param converter A {@link StringConverter} to convert the given item (of 92 * type T) to a String for displaying to the user. 93 * @param items Zero or more items that will be shown to the user when the 94 * {@link ComboBox} menu is showing. These items must be of the same 95 * type as the ListView items list, such that 96 * upon selection, they replace the existing value in the 97 * {@link ListView#itemsProperty() items} list. 98 * @return A {@link Callback} that will return a ListCell that is able to 99 * work on the type of element contained within the ListView. 100 */ 101 @SafeVarargs 102 public static <T> Callback<ListView<T>, ListCell<T>> forListView( 103 final StringConverter<T> converter, 104 final T... items) { 105 return forListView(converter, FXCollections.observableArrayList(items)); 106 } 107 108 /** 109 * Creates a ComboBox cell factory for use in {@link ListView} controls. By 110 * default, the ComboBoxCell is rendered as a {@link Label} when not being 111 * edited, and as a ComboBox when in editing mode. The ComboBox will, by 112 * default, stretch to fill the entire list cell. 113 * 114 * @param <T> The type of the elements contained within the ListView. 115 * @param items An {@link ObservableList} containing zero or more items that 116 * will be shown to the user when the {@link ComboBox} menu is showing. 117 * These items must be of the same type as the ListView items sequence, 118 * such that upon selection, they replace the existing value in the 119 * {@link ListView#itemsProperty() items} list. 120 * @return A {@link Callback} that will return a ListCell that is able to 121 * work on the type of element contained within the ListView. 122 */ 123 public static <T> Callback<ListView<T>, ListCell<T>> forListView( 124 final ObservableList<T> items) { 125 return forListView(null, items); 126 } 127 128 /** 129 * Creates a ComboBox cell factory for use in {@link ListView} controls. By 130 * default, the ComboBoxCell is rendered as a {@link Label} when not being 131 * edited, and as a ComboBox when in editing mode. The ComboBox will, by 132 * default, stretch to fill the entire list cell. 133 * 134 * @param <T> The type of the elements contained within the ListView. 135 * @param converter A {@link StringConverter} to convert the given item (of 136 * type T) to a String for displaying to the user. 137 * @param items An {@link ObservableList} containing zero or more items that 138 * will be shown to the user when the {@link ComboBox} menu is showing. 139 * These items must be of the same type as the ListView items sequence, 140 * such that upon selection, they replace the existing value in the 141 * {@link ListView#itemsProperty() items} list. 142 * @return A {@link Callback} that will return a ListCell that is able to 143 * work on the type of element contained within the ListView. 144 */ 145 public static <T> Callback<ListView<T>, ListCell<T>> forListView( 146 final StringConverter<T> converter, 147 final ObservableList<T> items) { 148 return list -> new ComboBoxListCell<T>(converter, items); 149 } 150 151 152 153 /*************************************************************************** 154 * * 155 * Fields * 156 * * 157 **************************************************************************/ 158 159 private final ObservableList<T> items; 160 161 private ComboBox<T> comboBox; 162 163 164 165 /*************************************************************************** 166 * * 167 * Constructors * 168 * * 169 **************************************************************************/ 170 171 /** 172 * Creates a default ComboBoxListCell with an empty items list. 173 */ 174 public ComboBoxListCell() { 175 this(FXCollections.<T>observableArrayList()); 176 } 177 178 /** 179 * Creates a default {@link ComboBoxListCell} instance with the given items 180 * being used to populate the {@link ComboBox} when it is shown. 181 * 182 * @param items The items to show in the ComboBox popup menu when selected 183 * by the user. 184 */ 185 @SafeVarargs 186 public ComboBoxListCell(T... items) { 187 this(FXCollections.observableArrayList(items)); 188 } 189 190 /** 191 * Creates a {@link ComboBoxListCell} instance with the given items 192 * being used to populate the {@link ComboBox} when it is shown, and the 193 * {@link StringConverter} being used to convert the item in to a 194 * user-readable form. 195 * 196 * @param converter A {@link StringConverter} that can convert an item of 197 * type T into a user-readable string so that it may then be shown in 198 * the ComboBox popup menu. 199 * @param items The items to show in the ComboBox popup menu when selected 200 * by the user. 201 */ 202 @SafeVarargs 203 public ComboBoxListCell(StringConverter<T> converter, T... items) { 204 this(converter, FXCollections.observableArrayList(items)); 205 } 206 207 /** 208 * Creates a default {@link ComboBoxListCell} instance with the given items 209 * being used to populate the {@link ComboBox} when it is shown. 210 * 211 * @param items The items to show in the ComboBox popup menu when selected 212 * by the user. 213 */ 214 public ComboBoxListCell(ObservableList<T> items) { 215 this(null, items); 216 } 217 218 /** 219 * Creates a {@link ComboBoxListCell} instance with the given items 220 * being used to populate the {@link ComboBox} when it is shown, and the 221 * {@link StringConverter} being used to convert the item in to a 222 * user-readable form. 223 * 224 * @param converter A {@link StringConverter} that can convert an item of 225 * type T into a user-readable string so that it may then be shown in 226 * the ComboBox popup menu. 227 * @param items The items to show in the ComboBox popup menu when selected 228 * by the user. 229 */ 230 public ComboBoxListCell(StringConverter<T> converter, ObservableList<T> items) { 231 this.getStyleClass().add("combo-box-list-cell"); 232 this.items = items; 233 setConverter(converter != null ? converter : CellUtils.<T>defaultStringConverter()); 234 } 235 236 237 238 /*************************************************************************** 239 * * 240 * Properties * 241 * * 242 **************************************************************************/ 243 244 // --- converter 245 private ObjectProperty<StringConverter<T>> converter = 246 new SimpleObjectProperty<StringConverter<T>>(this, "converter"); 247 248 /** 249 * The {@link StringConverter} property. 250 * @return the {@link StringConverter} property 251 */ 252 public final ObjectProperty<StringConverter<T>> converterProperty() { 253 return converter; 254 } 255 256 /** 257 * Sets the {@link StringConverter} to be used in this cell. 258 * @param value the {@link StringConverter} to be used in this cell 259 */ 260 public final void setConverter(StringConverter<T> value) { 261 converterProperty().set(value); 262 } 263 264 /** 265 * Returns the {@link StringConverter} used in this cell. 266 * @return the {@link StringConverter} used in this cell 267 */ 268 public final StringConverter<T> getConverter() { 269 return converterProperty().get(); 270 } 271 272 273 // --- comboBox editable 274 private BooleanProperty comboBoxEditable = 275 new SimpleBooleanProperty(this, "comboBoxEditable"); 276 277 /** 278 * A property representing whether the ComboBox, when shown to the user, 279 * is editable or not. 280 * @return the property representing whether the ComboBox, when shown to 281 * the user, is editable or not 282 */ 283 public final BooleanProperty comboBoxEditableProperty() { 284 return comboBoxEditable; 285 } 286 287 /** 288 * Configures the ComboBox to be editable (to allow user input outside of the 289 * options provide in the dropdown list). 290 * @param value the editable value for this ComboBox 291 */ 292 public final void setComboBoxEditable(boolean value) { 293 comboBoxEditableProperty().set(value); 294 } 295 296 /** 297 * Returns true if the ComboBox is editable. 298 * @return true if the ComboBox is editable 299 */ 300 public final boolean isComboBoxEditable() { 301 return comboBoxEditableProperty().get(); 302 } 303 304 305 306 /*************************************************************************** 307 * * 308 * Public API * 309 * * 310 **************************************************************************/ 311 312 /** 313 * Returns the items to be displayed in the ChoiceBox when it is showing. 314 * @return the items to be displayed in the ChoiceBox when it is showing 315 */ 316 public ObservableList<T> getItems() { 317 return items; 318 } 319 320 /** {@inheritDoc} */ 321 @Override public void startEdit() { 322 if (! isEditable() || ! getListView().isEditable()) { 323 return; 324 } 325 326 if (comboBox == null) { 327 comboBox = createComboBox(this, items, converterProperty()); 328 comboBox.editableProperty().bind(comboBoxEditableProperty()); 329 } 330 331 comboBox.getSelectionModel().select(getItem()); 332 333 super.startEdit(); 334 335 if (isEditing()) { 336 setText(null); 337 setGraphic(comboBox); 338 } 339 } 340 341 /** {@inheritDoc} */ 342 @Override public void cancelEdit() { 343 super.cancelEdit(); 344 345 setText(getConverter().toString(getItem())); 346 setGraphic(null); 347 } 348 349 /** {@inheritDoc} */ 350 @Override public void updateItem(T item, boolean empty) { 351 super.updateItem(item, empty); 352 CellUtils.updateItem(this, getConverter(), null, null, comboBox); 353 } 354 }