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.createChoiceBox; 29 import javafx.beans.property.ObjectProperty; 30 import javafx.beans.property.SimpleObjectProperty; 31 import javafx.collections.FXCollections; 32 import javafx.collections.ObservableList; 33 import javafx.scene.control.ChoiceBox; 34 import javafx.scene.control.Label; 35 import javafx.scene.control.ListCell; 36 import javafx.scene.control.ListView; 37 import javafx.util.Callback; 38 import javafx.util.StringConverter; 39 40 /** 41 * A class containing a {@link ListCell} implementation that draws a 42 * {@link ChoiceBox} node inside the cell. 43 * 44 * <p>By default, the ChoiceBoxListCell is rendered as a {@link Label} when not 45 * being edited, and as a ChoiceBox when in editing mode. The ChoiceBox will, by 46 * default, stretch to fill the entire list cell. 47 * 48 * <p>To create a ChoiceBoxListCell, it is necessary to provide zero or more 49 * items that will be shown to the user when the {@link ChoiceBox} menu is 50 * showing. These items must be of the same type as the ListView items sequence, 51 * such that upon selection, they replace the existing value in the 52 * {@link ListView#itemsProperty() items} list. 53 * 54 * @param <T> The type of the elements contained within the ListView. 55 * @since JavaFX 2.2 56 */ 57 public class ChoiceBoxListCell<T> extends ListCell<T> { 58 59 /*************************************************************************** 60 * * 61 * Static cell factories * 62 * * 63 **************************************************************************/ 64 65 /** 66 * Creates a ChoiceBox cell factory for use in {@link ListView} controls. 67 * By default, the ChoiceBoxCell is rendered as a {@link Label} when not 68 * being edited, and as a ChoiceBox when in editing mode. The ChoiceBox will, 69 * by default, stretch to fill the entire list cell. 70 * 71 * @param <T> The type of the elements contained within the ListView. 72 * @param items Zero or more items that will be shown to the user when the 73 * {@link ChoiceBox} menu is showing. These items must be of the same 74 * type as the ListView items list, such that upon selection, they 75 * replace the existing value in the 76 * {@link ListView#itemsProperty() items} list. 77 * @return A {@link Callback} that will return a ListCell that is able to 78 * work on the type of element contained within the ListView. 79 */ 80 @SafeVarargs 81 public static <T> Callback<ListView<T>, ListCell<T>> forListView(final T... items) { 82 return forListView(FXCollections.observableArrayList(items)); 83 } 84 85 /** 86 * Creates a ChoiceBox cell factory for use in {@link ListView} controls. By 87 * default, the ChoiceBoxCell is rendered as a {@link Label} when not being 88 * edited, and as a ChoiceBox when in editing mode. The ChoiceBox will, by 89 * default, stretch to fill the entire list cell. 90 * 91 * @param <T> The type of the elements contained within the ListView. 92 * @param converter A {@link StringConverter} to convert the given item (of type T) 93 * to a String for displaying to the user. 94 * @param items Zero or more items that will be shown to the user when the 95 * {@link ChoiceBox} menu is showing. These items must be of the same 96 * type as the ListView items list, such that upon selection, they 97 * replace the existing value in the 98 * {@link ListView#itemsProperty() items} list. 99 * @return A {@link Callback} that will return a ListCell that is able to 100 * work on the type of element contained within the ListView. 101 */ 102 @SafeVarargs 103 public static <T> Callback<ListView<T>, ListCell<T>> forListView( 104 final StringConverter<T> converter, 105 final T... items) { 106 return forListView(converter, FXCollections.observableArrayList(items)); 107 } 108 109 /** 110 * Creates a ChoiceBox cell factory for use in {@link ListView} controls. 111 * By default, the ChoiceBoxCell is rendered as a {@link Label} when not 112 * being edited, and as a ChoiceBox when in editing mode. The ChoiceBox 113 * will, by default, stretch to fill the entire list cell. 114 * 115 * @param <T> The type of the elements contained within the ListView. 116 * @param items An {@link ObservableList} containing zero or more items that 117 * will be shown to the user when the {@link ChoiceBox} menu is showing. 118 * These items must be of the same type as the ListView items sequence, 119 * such that upon selection, they replace the existing value in the 120 * {@link ListView#itemsProperty() items} list. 121 * @return A {@link Callback} that will return a ListCell that is able to 122 * work on the type of element contained within the ListView. 123 */ 124 public static <T> Callback<ListView<T>, ListCell<T>> forListView( 125 final ObservableList<T> items) { 126 return forListView(null, items); 127 } 128 129 /** 130 * Creates a ChoiceBox cell factory for use in {@link ListView} controls. By 131 * default, the ChoiceBoxCell is rendered as a {@link Label} when not being 132 * edited, and as a ChoiceBox when in editing mode. The ChoiceBox will, by 133 * default, stretch to fill the entire list cell. 134 * 135 * @param <T> The type of the elements contained within the ListView. 136 * @param converter A {@link StringConverter} to convert the given item (of type T) 137 * to a String for displaying to the user. 138 * @param items An {@link ObservableList} containing zero or more items that 139 * will be shown to the user when the {@link ChoiceBox} menu is showing. 140 * These items must be of the same type as the ListView items sequence, 141 * such that upon selection, they replace the existing value in the 142 * {@link ListView#itemsProperty() items} list. 143 * @return A {@link Callback} that will return a ListCell that is able to 144 * work on the type of element contained within the ListView. 145 */ 146 public static <T> Callback<ListView<T>, ListCell<T>> forListView( 147 final StringConverter<T> converter, 148 final ObservableList<T> items) { 149 return list -> new ChoiceBoxListCell<T>(converter, items); 150 } 151 152 153 154 /*************************************************************************** 155 * * 156 * Fields * 157 * * 158 **************************************************************************/ 159 160 private final ObservableList<T> items; 161 162 private ChoiceBox<T> choiceBox; 163 164 165 166 /*************************************************************************** 167 * * 168 * Constructors * 169 * * 170 **************************************************************************/ 171 172 /** 173 * Creates a default ChoiceBoxListCell with an empty items list. 174 */ 175 public ChoiceBoxListCell() { 176 this(FXCollections.<T>observableArrayList()); 177 } 178 179 /** 180 * Creates a default {@link ChoiceBoxListCell} instance with the given items 181 * being used to populate the {@link ChoiceBox} when it is shown. 182 * 183 * @param items The items to show in the ChoiceBox popup menu when selected 184 * by the user. 185 */ 186 @SafeVarargs 187 public ChoiceBoxListCell(T... items) { 188 this(FXCollections.observableArrayList(items)); 189 } 190 191 /** 192 * Creates a {@link ChoiceBoxListCell} instance with the given items 193 * being used to populate the {@link ChoiceBox} when it is shown, and the 194 * {@link StringConverter} being used to convert the item in to a 195 * user-readable form. 196 * 197 * @param converter A {@link StringConverter} that can convert an item of type T 198 * into a user-readable string so that it may then be shown in the 199 * ChoiceBox popup menu. 200 * @param items The items to show in the ChoiceBox popup menu when selected 201 * by the user. 202 */ 203 @SafeVarargs 204 public ChoiceBoxListCell(StringConverter<T> converter, T... items) { 205 this(converter, FXCollections.observableArrayList(items)); 206 } 207 208 /** 209 * Creates a default {@link ChoiceBoxListCell} instance with the given items 210 * being used to populate the {@link ChoiceBox} when it is shown. 211 * 212 * @param items The items to show in the ChoiceBox popup menu when selected 213 * by the user. 214 */ 215 public ChoiceBoxListCell(ObservableList<T> items) { 216 this(null, items); 217 } 218 219 /** 220 * Creates a {@link ChoiceBoxListCell} instance with the given items 221 * being used to populate the {@link ChoiceBox} when it is shown, and the 222 * {@link StringConverter} being used to convert the item in to a 223 * user-readable form. 224 * 225 * @param converter A {@link StringConverter} that can convert an item of type T 226 * into a user-readable string so that it may then be shown in the 227 * ChoiceBox popup menu. 228 * @param items The items to show in the ChoiceBox popup menu when selected 229 * by the user. 230 */ 231 public ChoiceBoxListCell(StringConverter<T> converter, ObservableList<T> items) { 232 this.getStyleClass().add("choice-box-list-cell"); 233 this.items = items; 234 setConverter(converter != null ? converter : CellUtils.<T>defaultStringConverter()); 235 } 236 237 238 239 /*************************************************************************** 240 * * 241 * Properties * 242 * * 243 **************************************************************************/ 244 245 // --- converter 246 private ObjectProperty<StringConverter<T>> converter = 247 new SimpleObjectProperty<StringConverter<T>>(this, "converter"); 248 249 /** 250 * 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 */ 259 public final void setConverter(StringConverter<T> value) { 260 converterProperty().set(value); 261 } 262 263 /** 264 * Returns the {@link StringConverter} used in this cell. 265 */ 266 public final StringConverter<T> getConverter() { 267 return converterProperty().get(); 268 } 269 270 271 272 /*************************************************************************** 273 * * 274 * Public API * 275 * * 276 **************************************************************************/ 277 278 /** 279 * Returns the items to be displayed in the ChoiceBox when it is showing. 280 */ 281 public ObservableList<T> getItems() { 282 return items; 283 } 284 285 /** {@inheritDoc} */ 286 @Override public void startEdit() { 287 if (! isEditable() || ! getListView().isEditable()) { 288 return; 289 } 290 291 if (choiceBox == null) { 292 choiceBox = createChoiceBox(this, items, converterProperty()); 293 } 294 295 choiceBox.getSelectionModel().select(getItem()); 296 297 super.startEdit(); 298 299 if (isEditing()) { 300 setText(null); 301 setGraphic(choiceBox); 302 } 303 } 304 305 /** {@inheritDoc} */ 306 @Override public void cancelEdit() { 307 super.cancelEdit(); 308 309 setText(getConverter().toString(getItem())); 310 setGraphic(null); 311 } 312 313 /** {@inheritDoc} */ 314 @Override public void updateItem(T item, boolean empty) { 315 super.updateItem(item, empty); 316 CellUtils.updateItem(this, getConverter(), null, null, choiceBox); 317 } 318 }