1 /* 2 * Copyright (c) 2011, 2015, 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; 27 28 import static com.sun.javafx.scene.control.infrastructure.ControlTestUtils.assertStyleClassContains; 29 import static org.junit.Assert.assertArrayEquals; 30 import static org.junit.Assert.assertEquals; 31 import static org.junit.Assert.assertFalse; 32 import static org.junit.Assert.assertNotNull; 33 import static org.junit.Assert.assertNull; 34 import static org.junit.Assert.assertSame; 35 import static org.junit.Assert.assertTrue; 36 import static org.junit.Assert.fail; 37 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.Collections; 41 import java.util.List; 42 43 import com.sun.javafx.scene.control.behavior.ListCellBehavior; 44 import com.sun.javafx.scene.control.infrastructure.KeyEventFirer; 45 import com.sun.javafx.scene.control.infrastructure.KeyModifier; 46 import javafx.application.Platform; 47 import javafx.beans.binding.Bindings; 48 import javafx.beans.property.ObjectProperty; 49 import javafx.beans.property.ReadOnlyBooleanWrapper; 50 import javafx.beans.property.SimpleObjectProperty; 51 import javafx.collections.FXCollections; 52 import javafx.collections.ListChangeListener; 53 import javafx.collections.ObservableList; 54 import javafx.scene.control.cell.CheckBoxListCell; 55 import javafx.scene.control.cell.ComboBoxListCell; 56 import javafx.scene.control.cell.TextFieldListCell; 57 import com.sun.javafx.scene.control.VirtualScrollBar; 58 import javafx.scene.image.ImageView; 59 import javafx.scene.input.KeyCode; 60 import javafx.scene.layout.VBox; 61 import javafx.scene.paint.Color; 62 import javafx.scene.shape.Rectangle; 63 import javafx.util.Callback; 64 65 import org.junit.Before; 66 import org.junit.Test; 67 68 import com.sun.javafx.scene.control.infrastructure.VirtualFlowTestUtils; 69 import com.sun.javafx.scene.control.infrastructure.StageLoader; 70 import com.sun.javafx.scene.control.test.Person; 71 import com.sun.javafx.scene.control.test.RT_22463_Person; 72 import com.sun.javafx.tk.Toolkit; 73 74 public class ListViewTest { 75 private ListView<String> listView; 76 private MultipleSelectionModel<String> sm; 77 private FocusModel<String> fm; 78 79 @Before public void setup() { 80 listView = new ListView<>(); 81 sm = listView.getSelectionModel(); 82 fm = listView.getFocusModel(); 83 } 84 85 86 /********************************************************************* 87 * Tests for the constructors * 88 ********************************************************************/ 89 90 @Test public void noArgConstructorSetsTheStyleClass() { 91 assertStyleClassContains(listView, "list-view"); 92 } 93 94 @Test public void noArgConstructorSetsNonNullSelectionModel() { 95 assertNotNull(sm); 96 } 97 98 @Test public void noArgConstructorSetsNonNullItems() { 99 assertNotNull(listView.getItems()); 100 } 101 102 @Test public void noArgConstructor_selectedItemIsNull() { 103 assertNull(sm.getSelectedItem()); 104 } 105 106 @Test public void noArgConstructor_selectedIndexIsNegativeOne() { 107 assertEquals(-1, sm.getSelectedIndex()); 108 } 109 110 @Test public void singleArgConstructorSetsTheStyleClass() { 111 final ListView<String> b2 = new ListView<>(FXCollections.observableArrayList("Hi")); 112 assertStyleClassContains(b2, "list-view"); 113 } 114 115 @Test public void singleArgConstructorSetsNonNullSelectionModel() { 116 final ListView<String> b2 = new ListView<>(FXCollections.<String>observableArrayList("Hi")); 117 assertNotNull(b2.getSelectionModel()); 118 } 119 120 @Test public void singleArgConstructorAllowsNullItems() { 121 final ListView<String> b2 = new ListView<String>(null); 122 assertNull(b2.getItems()); 123 } 124 125 @Test public void singleArgConstructorTakesItems() { 126 ObservableList<String> items = FXCollections.observableArrayList("Hi"); 127 final ListView<String> b2 = new ListView<>(items); 128 assertSame(items, b2.getItems()); 129 } 130 131 @Test public void singleArgConstructor_selectedItemIsNull() { 132 final ListView<String> b2 = new ListView<>(FXCollections.observableArrayList("Hi")); 133 assertNull(b2.getSelectionModel().getSelectedItem()); 134 } 135 136 @Test public void singleArgConstructor_selectedIndexIsNegativeOne() { 137 final ListView<String> b2 = new ListView<>(FXCollections.observableArrayList("Hi")); 138 assertEquals(-1, b2.getSelectionModel().getSelectedIndex()); 139 } 140 141 /********************************************************************* 142 * Tests for selection model * 143 ********************************************************************/ 144 145 @Test public void selectionModelCanBeNull() { 146 listView.setSelectionModel(null); 147 assertNull(listView.getSelectionModel()); 148 } 149 150 @Test public void selectionModelCanBeBound() { 151 MultipleSelectionModel<String> sm = new ListView.ListViewBitSetSelectionModel<String>(listView); 152 ObjectProperty<MultipleSelectionModel<String>> other = new SimpleObjectProperty<MultipleSelectionModel<String>>(sm); 153 listView.selectionModelProperty().bind(other); 154 assertSame(sm, sm); 155 } 156 157 @Test public void selectionModelCanBeChanged() { 158 MultipleSelectionModel<String> sm = new ListView.ListViewBitSetSelectionModel<String>(listView); 159 listView.setSelectionModel(sm); 160 assertSame(sm, sm); 161 } 162 163 @Test public void canSetSelectedItemToAnItemEvenWhenThereAreNoItems() { 164 final String randomString = new String("I AM A CRAZY RANDOM STRING"); 165 sm.select(randomString); 166 assertEquals(-1, sm.getSelectedIndex()); 167 assertSame(randomString, sm.getSelectedItem()); 168 } 169 170 @Test public void canSetSelectedItemToAnItemNotInTheDataModel() { 171 listView.getItems().addAll("Apple", "Orange", "Banana"); 172 final String randomString = new String("I AM A CRAZY RANDOM STRING"); 173 sm.select(randomString); 174 assertEquals(-1, sm.getSelectedIndex()); 175 assertSame(randomString, sm.getSelectedItem()); 176 } 177 178 @Test public void settingTheSelectedItemToAnItemInItemsResultsInTheCorrectSelectedIndex() { 179 listView.getItems().addAll("Apple", "Orange", "Banana"); 180 sm.select("Orange"); 181 assertEquals(1, sm.getSelectedIndex()); 182 assertSame("Orange", sm.getSelectedItem()); 183 } 184 185 @Test public void settingTheSelectedItemToANonexistantItemAndThenSettingItemsWhichContainsItResultsInCorrectSelectedIndex() { 186 sm.select("Orange"); 187 listView.getItems().addAll("Apple", "Orange", "Banana"); 188 assertEquals(1, sm.getSelectedIndex()); 189 assertSame("Orange", sm.getSelectedItem()); 190 } 191 192 @Test public void ensureSelectionClearsWhenAllItemsAreRemoved_selectIndex0() { 193 listView.getItems().addAll("Apple", "Orange", "Banana"); 194 sm.select(0); 195 listView.getItems().clear(); 196 assertEquals(-1, sm.getSelectedIndex()); 197 assertEquals(null, sm.getSelectedItem()); 198 } 199 200 @Test public void ensureSelectionClearsWhenAllItemsAreRemoved_selectIndex2() { 201 listView.getItems().addAll("Apple", "Orange", "Banana"); 202 sm.select(2); 203 listView.getItems().clear(); 204 assertEquals(-1, sm.getSelectedIndex()); 205 assertEquals(null, sm.getSelectedItem()); 206 } 207 208 @Test public void ensureSelectedItemRemainsAccurateWhenItemsAreCleared() { 209 listView.getItems().addAll("Apple", "Orange", "Banana"); 210 sm.select(2); 211 listView.getItems().clear(); 212 assertNull(sm.getSelectedItem()); 213 assertEquals(-1, sm.getSelectedIndex()); 214 215 listView.getItems().addAll("Kiwifruit", "Mandarin", "Pineapple"); 216 sm.select(2); 217 assertEquals("Pineapple", sm.getSelectedItem()); 218 } 219 220 @Test public void ensureSelectionShiftsDownWhenOneNewItemIsAdded() { 221 listView.getItems().addAll("Apple", "Orange", "Banana"); 222 sm.select(1); 223 assertEquals(1, sm.getSelectedIndex()); 224 assertEquals("Orange", sm.getSelectedItem()); 225 226 listView.getItems().add(0, "Kiwifruit"); 227 assertEquals(2, sm.getSelectedIndex()); 228 assertEquals("Orange", sm.getSelectedItem()); 229 } 230 231 @Test public void ensureSelectionShiftsDownWhenMultipleNewItemAreAdded() { 232 listView.getItems().addAll("Apple", "Orange", "Banana"); 233 sm.select(1); 234 assertEquals(1, sm.getSelectedIndex()); 235 assertEquals("Orange", sm.getSelectedItem()); 236 237 listView.getItems().addAll(0, Arrays.asList("Kiwifruit", "Pineapple", "Mandarin")); 238 assertEquals("Orange", sm.getSelectedItem()); 239 assertEquals(4, sm.getSelectedIndex()); 240 } 241 242 @Test public void ensureSelectionShiftsUpWhenOneItemIsRemoved() { 243 listView.getItems().addAll("Apple", "Orange", "Banana"); 244 sm.select(1); 245 assertEquals(1, sm.getSelectedIndex()); 246 assertEquals("Orange", sm.getSelectedItem()); 247 248 listView.getItems().remove("Apple"); 249 assertEquals(0, sm.getSelectedIndex()); 250 assertEquals("Orange", sm.getSelectedItem()); 251 } 252 253 @Test public void ensureSelectionShiftsUpWheMultipleItemsAreRemoved() { 254 listView.getItems().addAll("Apple", "Orange", "Banana"); 255 sm.select(2); 256 assertEquals(2, sm.getSelectedIndex()); 257 assertEquals("Banana", sm.getSelectedItem()); 258 259 listView.getItems().removeAll(Arrays.asList("Apple", "Orange")); 260 assertEquals(0, sm.getSelectedIndex()); 261 assertEquals("Banana", sm.getSelectedItem()); 262 } 263 264 @Test public void ensureSelectionIsCorrectWhenItemsChange() { 265 listView.setItems(FXCollections.observableArrayList("Item 1")); 266 sm.select(0); 267 assertEquals("Item 1", sm.getSelectedItem()); 268 269 listView.setItems(FXCollections.observableArrayList("Item 2")); 270 assertEquals(-1, sm.getSelectedIndex()); 271 assertNull(sm.getSelectedItem()); 272 assertEquals(0, fm.getFocusedIndex()); 273 assertEquals("Item 2", fm.getFocusedItem()); 274 } 275 276 @Test public void test_rt15793() { 277 // ListView selectedIndex is 0 although the items list is empty 278 final ListView lv = new ListView(); 279 final ObservableList list = FXCollections.observableArrayList(); 280 lv.setItems(list); 281 list.add("toto"); 282 lv.getSelectionModel().select(0); 283 assertEquals(0, lv.getSelectionModel().getSelectedIndex()); 284 list.remove(0); 285 assertEquals(-1, lv.getSelectionModel().getSelectedIndex()); 286 } 287 288 @Test public void test_rt17522_focusShouldMoveWhenItemAddedAtFocusIndex() { 289 final ListView lv = new ListView(); 290 FocusModel fm = lv.getFocusModel(); 291 lv.getItems().add("row1"); 292 fm.focus(0); 293 assertTrue(fm.isFocused(0)); 294 295 lv.getItems().add(0, "row0"); 296 assertTrue(fm.isFocused(1)); 297 } 298 299 @Test public void test_rt17522_focusShouldMoveWhenItemAddedBeforeFocusIndex() { 300 final ListView lv = new ListView(); 301 FocusModel fm = lv.getFocusModel(); 302 lv.getItems().addAll("row1", "row2"); 303 fm.focus(1); 304 assertTrue(fm.isFocused(1)); 305 assertEquals("row2", fm.getFocusedItem()); 306 307 lv.getItems().add(1, "row0"); 308 assertTrue(fm.isFocused(2)); 309 assertEquals("row2", fm.getFocusedItem()); 310 assertFalse(fm.isFocused(1)); 311 } 312 313 @Test public void test_rt17522_focusShouldNotMoveWhenItemAddedAfterFocusIndex() { 314 final ListView lv = new ListView(); 315 FocusModel fm = lv.getFocusModel(); 316 lv.getItems().addAll("row1"); 317 fm.focus(0); 318 assertTrue(fm.isFocused(0)); 319 assertEquals("row1", fm.getFocusedItem()); 320 321 lv.getItems().add(1, "row2"); 322 assertTrue(fm.isFocused(0)); 323 assertEquals("row1", fm.getFocusedItem()); 324 assertFalse(fm.isFocused(1)); 325 } 326 327 @Test public void test_rt17522_focusShouldBeResetWhenFocusedItemIsRemoved() { 328 final ListView lv = new ListView(); 329 FocusModel fm = lv.getFocusModel(); 330 lv.getItems().add("row1"); 331 fm.focus(0); 332 assertTrue(fm.isFocused(0)); 333 334 lv.getItems().remove("row1"); 335 assertTrue(fm.getFocusedIndex() == -1); 336 assertNull(fm.getFocusedItem()); 337 } 338 339 @Test public void test_rt17522_focusShouldMoveWhenItemRemovedBeforeFocusIndex() { 340 final ListView lv = new ListView(); 341 FocusModel fm = lv.getFocusModel(); 342 lv.getItems().addAll("row1", "row2"); 343 fm.focus(1); 344 assertTrue(fm.isFocused(1)); 345 assertEquals("row2", fm.getFocusedItem()); 346 347 lv.getItems().remove("row1"); 348 assertTrue(fm.isFocused(0)); 349 assertEquals("row2", fm.getFocusedItem()); 350 } 351 352 @Test public void test_rt17522_focusShouldNotMoveWhenItemRemovedAfterFocusIndex() { 353 final ListView lv = new ListView(); 354 FocusModel fm = lv.getFocusModel(); 355 lv.getItems().addAll("row1", "row2"); 356 fm.focus(0); 357 assertTrue(fm.isFocused(0)); 358 assertEquals("row1", fm.getFocusedItem()); 359 360 lv.getItems().remove("row2"); 361 assertTrue(fm.isFocused(0)); 362 assertEquals("row1", fm.getFocusedItem()); 363 } 364 365 @Test public void test_rt18385() { 366 listView.getItems().addAll("row1", "row2", "row3"); 367 sm.select(1); 368 listView.getItems().add("Another Row"); 369 assertEquals(1, sm.getSelectedIndices().size()); 370 assertEquals(1, sm.getSelectedItems().size()); 371 } 372 373 @Test public void test_rt18339_onlyEditWhenListViewIsEditable_editableIsFalse() { 374 listView.setEditable(false); 375 listView.edit(1); 376 assertEquals(-1, listView.getEditingIndex()); 377 } 378 379 @Test public void test_rt18339_onlyEditWhenListViewIsEditable_editableIsTrue() { 380 listView.setEditable(true); 381 listView.edit(1); 382 assertEquals(1, listView.getEditingIndex()); 383 } 384 385 @Test public void test_rt14451() { 386 listView.getItems().addAll("Apple", "Orange", "Banana"); 387 sm.setSelectionMode(SelectionMode.MULTIPLE); 388 sm.selectRange(0, 2); // select from 0 (inclusive) to 2 (exclusive) 389 assertEquals(2, sm.getSelectedIndices().size()); 390 } 391 392 private int rt_18969_hitCount = 0; 393 @Test public void test_rt18969() { 394 rt_18969_hitCount = 0; 395 ObservableList<String> emptyModel = FXCollections.observableArrayList(); 396 listView.setItems(emptyModel); 397 assertTrue(listView.getItems().isEmpty()); 398 399 sm.selectedItemProperty().addListener((observable, oldValue, newValue) -> { 400 rt_18969_hitCount++; 401 }); 402 403 ObservableList<String> mod = FXCollections.observableArrayList(); 404 mod.add(System.currentTimeMillis()+""); 405 listView.getItems().setAll(mod); 406 407 sm.select(0); 408 assertTrue(sm.isSelected(0)); 409 assertEquals(1, rt_18969_hitCount); 410 411 // sleep for 100ms so that the currentTimeMillis is guaranteed to be 412 // a different value than the first one 413 try { 414 Thread.sleep(100); 415 } catch (InterruptedException ex) { 416 ex.printStackTrace(); 417 } 418 419 // the list is totally changing (it is being cleared), so we should 420 // be nulling out the selection model state 421 mod = FXCollections.observableArrayList(); 422 mod.add(System.currentTimeMillis()+""); 423 listView.getItems().setAll(mod); 424 425 // it should be two, as there is no null event in between (although there 426 // used to be, so the test used to be for three hits) 427 assertEquals(2, rt_18969_hitCount); 428 } 429 430 @Test public void test_rt21586() { 431 listView.getItems().setAll("Apple", "Orange", "Banana"); 432 listView.getSelectionModel().select(1); 433 assertEquals(1, listView.getSelectionModel().getSelectedIndex()); 434 assertEquals("Orange", listView.getSelectionModel().getSelectedItem()); 435 436 listView.getItems().setAll("Kiwifruit", "Pineapple", "Grape"); 437 assertEquals(-1, listView.getSelectionModel().getSelectedIndex()); 438 assertNull(listView.getSelectionModel().getSelectedItem()); 439 } 440 441 @Test public void test_rt27820_1() { 442 listView.getItems().setAll("Apple", "Orange"); 443 listView.getSelectionModel().select(0); 444 assertEquals(1, listView.getSelectionModel().getSelectedItems().size()); 445 assertEquals("Apple", listView.getSelectionModel().getSelectedItem()); 446 447 listView.getItems().clear(); 448 assertEquals(0, listView.getSelectionModel().getSelectedItems().size()); 449 assertNull(listView.getSelectionModel().getSelectedItem()); 450 } 451 452 @Test public void test_rt27820_2() { 453 listView.getItems().setAll("Apple", "Orange"); 454 listView.getSelectionModel().select(1); 455 assertEquals(1, listView.getSelectionModel().getSelectedItems().size()); 456 assertEquals("Orange", listView.getSelectionModel().getSelectedItem()); 457 458 listView.getItems().clear(); 459 assertEquals(0, listView.getSelectionModel().getSelectedItems().size()); 460 assertNull(listView.getSelectionModel().getSelectedItem()); 461 } 462 463 @Test public void test_rt28534() { 464 ListView<Person> list = new ListView<Person>(); 465 list.setItems(FXCollections.observableArrayList( 466 new Person("Jacob", "Smith", "jacob.smith@example.com"), 467 new Person("Isabella", "Johnson", "isabella.johnson@example.com"), 468 new Person("Ethan", "Williams", "ethan.williams@example.com"), 469 new Person("Emma", "Jones", "emma.jones@example.com"), 470 new Person("Michael", "Brown", "michael.brown@example.com"))); 471 472 VirtualFlowTestUtils.assertRowsNotEmpty(list, 0, 5); // rows 0 - 5 should be filled 473 VirtualFlowTestUtils.assertRowsEmpty(list, 5, -1); // rows 5+ should be empty 474 475 // now we replace the data and expect the cells that have no data 476 // to be empty 477 list.setItems(FXCollections.observableArrayList( 478 new Person("*_*Emma", "Jones", "emma.jones@example.com"), 479 new Person("_Michael", "Brown", "michael.brown@example.com"))); 480 481 VirtualFlowTestUtils.assertRowsNotEmpty(list, 0, 2); // rows 0 - 2 should be filled 482 VirtualFlowTestUtils.assertRowsEmpty(list, 2, -1); // rows 2+ should be empty 483 } 484 485 @Test public void test_rt22463() { 486 final ListView<RT_22463_Person> list = new ListView<RT_22463_Person>(); 487 488 // before the change things display fine 489 RT_22463_Person p1 = new RT_22463_Person(); 490 p1.setId(1l); 491 p1.setName("name1"); 492 RT_22463_Person p2 = new RT_22463_Person(); 493 p2.setId(2l); 494 p2.setName("name2"); 495 list.setItems(FXCollections.observableArrayList(p1, p2)); 496 VirtualFlowTestUtils.assertCellTextEquals(list, 0, "name1"); 497 VirtualFlowTestUtils.assertCellTextEquals(list, 1, "name2"); 498 499 // now we change the persons but they are still equal as the ID's don't 500 // change - but the items list is cleared so the cells should update 501 RT_22463_Person new_p1 = new RT_22463_Person(); 502 new_p1.setId(1l); 503 new_p1.setName("updated name1"); 504 RT_22463_Person new_p2 = new RT_22463_Person(); 505 new_p2.setId(2l); 506 new_p2.setName("updated name2"); 507 list.getItems().clear(); 508 list.setItems(FXCollections.observableArrayList(new_p1, new_p2)); 509 VirtualFlowTestUtils.assertCellTextEquals(list, 0, "updated name1"); 510 VirtualFlowTestUtils.assertCellTextEquals(list, 1, "updated name2"); 511 } 512 513 @Test public void test_rt28637() { 514 ObservableList<String> items = FXCollections.observableArrayList("String1", "String2", "String3", "String4"); 515 516 final ListView<String> listView = new ListView<String>(); 517 listView.setItems(items); 518 519 listView.getSelectionModel().select(0); 520 assertEquals("String1", listView.getSelectionModel().getSelectedItem()); 521 assertEquals("String1", listView.getSelectionModel().getSelectedItems().get(0)); 522 assertEquals(0, listView.getSelectionModel().getSelectedIndex()); 523 524 items.remove(listView.getSelectionModel().getSelectedItem()); 525 assertEquals("String2", listView.getSelectionModel().getSelectedItem()); 526 assertEquals("String2", listView.getSelectionModel().getSelectedItems().get(0)); 527 assertEquals(0, listView.getSelectionModel().getSelectedIndex()); 528 } 529 530 @Test public void test_rt28819_1() { 531 ObservableList<String> emptyModel = FXCollections.observableArrayList(); 532 533 final ListView<String> listView = new ListView<String>(); 534 listView.setItems(emptyModel); 535 VirtualFlowTestUtils.assertRowsEmpty(listView, 0, 5); 536 537 ObservableList<String> mod = FXCollections.observableArrayList(); 538 String value = System.currentTimeMillis()+""; 539 mod.add(value); 540 listView.setItems(mod); 541 VirtualFlowTestUtils.assertCellCount(listView, 1); 542 VirtualFlowTestUtils.assertCellTextEquals(listView, 0, value); 543 } 544 545 @Test public void test_rt28819_2() { 546 ObservableList<String> emptyModel = FXCollections.observableArrayList(); 547 548 final ListView<String> listView = new ListView<String>(); 549 listView.setItems(emptyModel); 550 VirtualFlowTestUtils.assertRowsEmpty(listView, 0, 5); 551 552 ObservableList<String> mod1 = FXCollections.observableArrayList(); 553 String value1 = System.currentTimeMillis()+""; 554 mod1.add(value1); 555 listView.getItems().setAll(mod1); 556 VirtualFlowTestUtils.assertCellCount(listView, 1); 557 VirtualFlowTestUtils.assertCellTextEquals(listView, 0, value1); 558 } 559 560 @Test public void test_rt29390() { 561 ObservableList<String> items = FXCollections.observableArrayList( 562 "String1", "String2", "String3", "String4", 563 "String1", "String2", "String3", "String4", 564 "String1", "String2", "String3", "String4", 565 "String1", "String2", "String3", "String4" 566 ); 567 568 final ListView<String> listView = new ListView<String>(items); 569 listView.setMaxHeight(50); 570 listView.setPrefHeight(50); 571 572 // we want the vertical scrollbar 573 VirtualScrollBar scrollBar = VirtualFlowTestUtils.getVirtualFlowVerticalScrollbar(listView); 574 575 assertNotNull(scrollBar); 576 assertTrue(scrollBar.isVisible()); 577 assertTrue(scrollBar.getVisibleAmount() > 0.0); 578 assertTrue(scrollBar.getVisibleAmount() < 1.0); 579 580 // this next test is likely to be brittle, but we'll see...If it is the 581 // cause of failure then it can be commented out 582 assertEquals(0.125, scrollBar.getVisibleAmount(), 0.0); 583 } 584 585 @Test public void test_rt30400() { 586 // create a listview that'll render cells using the check box cell factory 587 ObservableList<String> items = FXCollections.observableArrayList("String1"); 588 final ListView<String> listView = new ListView<String>(items); 589 listView.setMinHeight(100); 590 listView.setPrefHeight(100); 591 listView.setCellFactory(CheckBoxListCell.forListView(param -> new ReadOnlyBooleanWrapper(true))); 592 593 // because only the first row has data, all other rows should be 594 // empty (and not contain check boxes - we just check the first four here) 595 VirtualFlowTestUtils.assertRowsNotEmpty(listView, 0, 1); 596 VirtualFlowTestUtils.assertCellNotEmpty(VirtualFlowTestUtils.getCell(listView, 0)); 597 VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(listView, 1)); 598 VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(listView, 2)); 599 VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(listView, 3)); 600 } 601 602 @Test public void test_rt29420() { 603 final ListView<String> listView = new ListView<String>(); 604 605 VBox vbox = new VBox(listView); 606 StageLoader sl = new StageLoader(vbox); 607 608 // the initial width of a ListView should be the golden rectangle where 609 // the height is hardcoded to be 400 610 final double initialWidth = listView.prefWidth(-1); 611 assertEquals(400 * 0.618033987, initialWidth, 0.00); 612 613 // add in some items, and re-measure - seeing as the items are narrow, 614 // the width shouldn't change 615 listView.getItems().addAll("one", "two", "three", "four", "five", "six"); 616 Toolkit.getToolkit().firePulse(); 617 final double withContentWidth = listView.prefWidth(-1); 618 assertEquals(initialWidth, withContentWidth, 0.00); 619 620 // remove the items - and the width should remain the same 621 listView.getItems().clear(); 622 Toolkit.getToolkit().firePulse(); 623 final double afterEmptiedWidth = listView.prefWidth(-1); 624 assertEquals(initialWidth, afterEmptiedWidth, 0.00); 625 626 sl.dispose(); 627 } 628 629 @Test public void test_rt31165() { 630 final ObservableList names = FXCollections.observableArrayList("Adam", "Alex", "Alfred", "Albert"); 631 final ObservableList data = FXCollections.observableArrayList(); 632 for (int i = 0; i < 18; i++) { 633 data.add(""+i); 634 } 635 636 final ListView listView = new ListView(data); 637 listView.setPrefSize(200, 250); 638 listView.setEditable(true); 639 listView.setCellFactory(ComboBoxListCell.forListView(names)); 640 641 IndexedCell cell = VirtualFlowTestUtils.getCell(listView, 1); 642 assertEquals("1", cell.getText()); 643 assertFalse(cell.isEditing()); 644 645 listView.edit(1); 646 647 assertEquals(1, listView.getEditingIndex()); 648 assertTrue(cell.isEditing()); 649 650 VirtualFlowTestUtils.getVirtualFlow(listView).requestLayout(); 651 Toolkit.getToolkit().firePulse(); 652 653 assertEquals(1, listView.getEditingIndex()); 654 assertTrue(cell.isEditing()); 655 } 656 657 @Test public void test_rt31471() { 658 final ObservableList names = FXCollections.observableArrayList("Adam", "Alex", "Alfred", "Albert"); 659 final ListView listView = new ListView(names); 660 661 IndexedCell cell = VirtualFlowTestUtils.getCell(listView, 0); 662 assertEquals("Adam", cell.getItem()); 663 664 listView.setFixedCellSize(50); 665 666 VirtualFlowTestUtils.getVirtualFlow(listView).requestLayout(); 667 Toolkit.getToolkit().firePulse(); 668 669 assertEquals("Adam", cell.getItem()); 670 assertEquals(50, cell.getHeight(), 0.00); 671 } 672 673 private int rt_31200_count = 0; 674 @Test public void test_rt_31200() { 675 final ListView listView = new ListView(); 676 listView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() { 677 @Override 678 public ListCell<String> call(ListView<String> param) { 679 return new ListCell<String>() { 680 ImageView view = new ImageView(); 681 { setGraphic(view); }; 682 683 @Override 684 protected void updateItem(String item, boolean empty) { 685 if (getItem() == null ? item == null : getItem().equals(item)) { 686 rt_31200_count++; 687 } 688 super.updateItem(item, empty); 689 if (item == null || empty) { 690 view.setImage(null); 691 setText(null); 692 } else { 693 setText(item); 694 } 695 } 696 }; 697 } 698 }); 699 listView.getItems().setAll("one", "two", "three", "four", "five"); 700 701 StageLoader sl = new StageLoader(listView); 702 703 assertEquals(24, rt_31200_count); 704 705 // resize the stage 706 sl.getStage().setHeight(250); 707 Toolkit.getToolkit().firePulse(); 708 sl.getStage().setHeight(50); 709 Toolkit.getToolkit().firePulse(); 710 assertEquals(24, rt_31200_count); 711 712 sl.dispose(); 713 } 714 715 @Test public void test_rt_30484() { 716 final ListView listView = new ListView(); 717 listView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() { 718 @Override public ListCell<String> call(ListView<String> param) { 719 return new ListCell<String>() { 720 Rectangle graphic = new Rectangle(10, 10, Color.RED); 721 { setGraphic(graphic); }; 722 723 @Override protected void updateItem(String item, boolean empty) { 724 super.updateItem(item, empty); 725 if (item == null || empty) { 726 graphic.setVisible(false); 727 setText(null); 728 } else { 729 graphic.setVisible(true); 730 setText(item); 731 } 732 } 733 }; 734 } 735 }); 736 737 // First two rows have content, so the graphic should show. 738 // All other rows have no content, so graphic should not show. 739 listView.getItems().setAll("one", "two"); 740 741 VirtualFlowTestUtils.assertGraphicIsVisible(listView, 0); 742 VirtualFlowTestUtils.assertGraphicIsVisible(listView, 1); 743 VirtualFlowTestUtils.assertGraphicIsNotVisible(listView, 2); 744 VirtualFlowTestUtils.assertGraphicIsNotVisible(listView, 3); 745 VirtualFlowTestUtils.assertGraphicIsNotVisible(listView, 4); 746 VirtualFlowTestUtils.assertGraphicIsNotVisible(listView, 5); 747 } 748 749 private int rt_29650_start_count = 0; 750 private int rt_29650_commit_count = 0; 751 private int rt_29650_cancel_count = 0; 752 @Test public void test_rt_29650() { 753 listView.setOnEditStart(t -> { 754 rt_29650_start_count++; 755 }); 756 listView.setOnEditCommit(t -> { 757 rt_29650_commit_count++; 758 }); 759 listView.setOnEditCancel(t -> { 760 rt_29650_cancel_count++; 761 }); 762 763 listView.getItems().setAll("one", "two", "three", "four", "five"); 764 listView.setEditable(true); 765 listView.setCellFactory(TextFieldListCell.forListView()); 766 767 StageLoader sl = new StageLoader(listView); 768 769 listView.edit(0); 770 771 Toolkit.getToolkit().firePulse(); 772 773 ListCell rootCell = (ListCell) VirtualFlowTestUtils.getCell(listView, 0); 774 TextField textField = (TextField) rootCell.getGraphic(); 775 textField.setText("Testing!"); 776 KeyEventFirer keyboard = new KeyEventFirer(textField); 777 keyboard.doKeyPress(KeyCode.ENTER); 778 779 // TODO should the following assert be enabled? 780 // assertEquals("Testing!", listView.getItems().get(0)); 781 assertEquals(1, rt_29650_start_count); 782 assertEquals(1, rt_29650_commit_count); 783 assertEquals(0, rt_29650_cancel_count); 784 785 sl.dispose(); 786 } 787 788 @Test public void test_rt35039() { 789 final List<String> data = new ArrayList<>(); 790 data.add("aabbaa"); 791 data.add("bbc"); 792 793 final ListView<String> listView = new ListView<>(); 794 listView.setItems(FXCollections.observableArrayList(data)); 795 796 StageLoader sl = new StageLoader(listView); 797 798 // selection starts off on row -1 799 assertNull(listView.getSelectionModel().getSelectedItem()); 800 801 // select "bbc" and ensure everything is set to that 802 listView.getSelectionModel().select(1); 803 assertEquals("bbc", listView.getSelectionModel().getSelectedItem()); 804 805 // change the items list - but retain the same content. We expect 806 // that "bbc" remains selected as it is still in the list 807 listView.setItems(FXCollections.observableArrayList(data)); 808 assertEquals("bbc", listView.getSelectionModel().getSelectedItem()); 809 810 sl.dispose(); 811 } 812 813 @Test public void test_rt35857() { 814 ObservableList<String> fxList = FXCollections.observableArrayList("A", "B", "C"); 815 final ListView<String> listView = new ListView<String>(fxList); 816 817 listView.getSelectionModel().select(0); 818 819 ObservableList<String> selectedItems = listView.getSelectionModel().getSelectedItems(); 820 assertEquals(1, selectedItems.size()); 821 assertEquals("A", selectedItems.get(0)); 822 823 listView.getItems().removeAll(selectedItems); 824 assertEquals(2, fxList.size()); 825 assertEquals("B", fxList.get(0)); 826 assertEquals("C", fxList.get(1)); 827 } 828 829 private int rt_35889_cancel_count = 0; 830 @Test public void test_rt35889() { 831 final ListView<String> textFieldListView = new ListView<String>(); 832 textFieldListView.setItems(FXCollections.observableArrayList("A", "B", "C")); 833 textFieldListView.setEditable(true); 834 textFieldListView.setCellFactory(TextFieldListCell.forListView()); 835 textFieldListView.setOnEditCancel(t -> { 836 rt_35889_cancel_count++; 837 System.out.println("On Edit Cancel: " + t); 838 }); 839 840 ListCell cell0 = (ListCell) VirtualFlowTestUtils.getCell(textFieldListView, 0); 841 assertNull(cell0.getGraphic()); 842 assertEquals("A", cell0.getText()); 843 844 textFieldListView.edit(0); 845 TextField textField = (TextField) cell0.getGraphic(); 846 assertNotNull(textField); 847 848 assertEquals(0, rt_35889_cancel_count); 849 850 textField.setText("Z"); 851 KeyEventFirer keyboard = new KeyEventFirer(textField); 852 keyboard.doKeyPress(KeyCode.ENTER); 853 854 assertEquals(0, rt_35889_cancel_count); 855 } 856 857 @Test public void test_rt25679() { 858 Button focusBtn = new Button("Focus here"); 859 860 final ListView<String> listView = new ListView<String>(); 861 SelectionModel sm = listView.getSelectionModel(); 862 listView.setItems(FXCollections.observableArrayList("A", "B", "C")); 863 864 VBox vbox = new VBox(focusBtn, listView); 865 866 StageLoader sl = new StageLoader(vbox); 867 sl.getStage().requestFocus(); 868 focusBtn.requestFocus(); 869 Toolkit.getToolkit().firePulse(); 870 871 // test initial state 872 assertEquals(sl.getStage().getScene().getFocusOwner(), focusBtn); 873 assertTrue(focusBtn.isFocused()); 874 assertEquals(-1, sm.getSelectedIndex()); 875 assertNull(sm.getSelectedItem()); 876 877 // move focus to the listview 878 listView.requestFocus(); 879 880 // ensure that there is a selection (where previously there was not one) 881 assertEquals(sl.getStage().getScene().getFocusOwner(), listView); 882 assertTrue(listView.isFocused()); 883 assertEquals(-1, sm.getSelectedIndex()); 884 assertNull(sm.getSelectedItem()); 885 886 sl.dispose(); 887 } 888 889 private int rt_37061_index_counter = 0; 890 private int rt_37061_item_counter = 0; 891 @Test public void test_rt_37061() { 892 ListView<Integer> tv = new ListView<>(); 893 tv.getItems().add(1); 894 tv.getSelectionModel().select(0); 895 896 // note we add the listeners after the selection is made, so the counters 897 // at this point are still both at zero. 898 tv.getSelectionModel().selectedIndexProperty().addListener((observable, oldValue, newValue) -> { 899 rt_37061_index_counter++; 900 }); 901 902 tv.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { 903 rt_37061_item_counter++; 904 }); 905 906 // add a new item. This does not impact the selected index or selected item 907 // so the counters should remain at zero. 908 tv.getItems().add(2); 909 assertEquals(0, rt_37061_index_counter); 910 assertEquals(0, rt_37061_item_counter); 911 } 912 913 private int rt_37538_count = 0; 914 @Test public void test_rt_37538_noCNextCall() { 915 test_rt_37538(false, false); 916 } 917 918 @Test public void test_rt_37538_callCNextOnce() { 919 test_rt_37538(true, false); 920 } 921 922 @Test public void test_rt_37538_callCNextInLoop() { 923 test_rt_37538(false, true); 924 } 925 926 private void test_rt_37538(boolean callCNextOnce, boolean callCNextInLoop) { 927 ListView<Integer> list = new ListView<>(); 928 for ( int i = 1; i <= 50; i++ ) { 929 list.getItems().add(i); 930 } 931 932 list.getSelectionModel().getSelectedItems().addListener((ListChangeListener.Change<? extends Integer> c) -> { 933 if (callCNextOnce) { 934 c.next(); 935 } else if (callCNextInLoop) { 936 while (c.next()) { 937 // no-op 938 } 939 } 940 941 if (rt_37538_count >= 1) { 942 Thread.dumpStack(); 943 fail("This method should only be called once"); 944 } 945 946 rt_37538_count++; 947 }); 948 949 StageLoader sl = new StageLoader(list); 950 assertEquals(0, rt_37538_count); 951 list.getSelectionModel().select(0); 952 assertEquals(1, rt_37538_count); 953 sl.dispose(); 954 } 955 956 @Test 957 public void test_rt_35395_fixedCellSize() { 958 test_rt_35395(true); 959 } 960 961 @Test 962 public void test_rt_35395_notFixedCellSize() { 963 test_rt_35395(false); 964 } 965 966 private int rt_35395_counter; 967 968 private void test_rt_35395(boolean useFixedCellSize) { 969 rt_35395_counter = 0; 970 971 ObservableList<String> items = FXCollections.observableArrayList(); 972 for (int i = 0; i < 20; ++i) { 973 items.addAll("red", "green", "blue", "purple"); 974 } 975 976 ListView<String> listView = new ListView<>(items); 977 if (useFixedCellSize) { 978 listView.setFixedCellSize(24); 979 } 980 listView.setCellFactory(lv -> new ListCell<String>() { 981 @Override 982 protected void updateItem(String color, boolean empty) { 983 rt_35395_counter += 1; 984 super.updateItem(color, empty); 985 setText(null); 986 if (empty) { 987 setGraphic(null); 988 } else { 989 Rectangle rect = new Rectangle(16, 16); 990 rect.setStyle("-fx-fill: " + color); 991 setGraphic(rect); 992 } 993 } 994 }); 995 996 StageLoader sl = new StageLoader(listView); 997 998 Platform.runLater(() -> { 999 rt_35395_counter = 0; 1000 items.set(10, "yellow"); 1001 Platform.runLater(() -> { 1002 Toolkit.getToolkit().firePulse(); 1003 assertEquals(1, rt_35395_counter); 1004 rt_35395_counter = 0; 1005 items.set(30, "yellow"); 1006 Platform.runLater(() -> { 1007 Toolkit.getToolkit().firePulse(); 1008 assertEquals(0, rt_35395_counter); 1009 rt_35395_counter = 0; 1010 items.remove(12); 1011 Platform.runLater(() -> { 1012 Toolkit.getToolkit().firePulse(); 1013 assertEquals(useFixedCellSize ? 39 : 45, rt_35395_counter); 1014 rt_35395_counter = 0; 1015 items.add(12, "yellow"); 1016 Platform.runLater(() -> { 1017 Toolkit.getToolkit().firePulse(); 1018 assertEquals(useFixedCellSize ? 39 : 45, rt_35395_counter); 1019 rt_35395_counter = 0; 1020 listView.scrollTo(5); 1021 Platform.runLater(() -> { 1022 Toolkit.getToolkit().firePulse(); 1023 assertEquals(5, rt_35395_counter); 1024 rt_35395_counter = 0; 1025 listView.scrollTo(55); 1026 Platform.runLater(() -> { 1027 Toolkit.getToolkit().firePulse(); 1028 assertEquals(useFixedCellSize ? 17 : 53, rt_35395_counter); 1029 sl.dispose(); 1030 }); 1031 }); 1032 }); 1033 }); 1034 }); 1035 }); 1036 }); 1037 } 1038 1039 @Test public void test_rt_37632() { 1040 final ObservableList<String> listOne = FXCollections.observableArrayList("A", "B", "C"); 1041 final ObservableList<String> listTwo = FXCollections.observableArrayList("C"); 1042 1043 final ListView<String> listView = new ListView<>(); 1044 MultipleSelectionModel<String> sm = listView.getSelectionModel(); 1045 listView.setItems(listOne); 1046 listView.getSelectionModel().selectFirst(); 1047 1048 assertEquals(0, sm.getSelectedIndex()); 1049 assertEquals("A", sm.getSelectedItem()); 1050 assertEquals(1, sm.getSelectedIndices().size()); 1051 assertEquals(0, (int) sm.getSelectedIndices().get(0)); 1052 assertEquals(1, sm.getSelectedItems().size()); 1053 assertEquals("A", sm.getSelectedItems().get(0)); 1054 1055 listView.setItems(listTwo); 1056 1057 assertEquals(-1, sm.getSelectedIndex()); 1058 assertNull(sm.getSelectedItem()); 1059 assertEquals(0, sm.getSelectedIndices().size()); 1060 assertEquals(0, sm.getSelectedItems().size()); 1061 } 1062 1063 private int rt_37853_cancelCount; 1064 private int rt_37853_commitCount; 1065 @Test public void test_rt_37853() { 1066 listView.setCellFactory(TextFieldListCell.forListView()); 1067 listView.setEditable(true); 1068 1069 for (int i = 0; i < 10; i++) { 1070 listView.getItems().add("" + i); 1071 } 1072 1073 StageLoader sl = new StageLoader(listView); 1074 1075 listView.setOnEditCancel(editEvent -> rt_37853_cancelCount++); 1076 listView.setOnEditCommit(editEvent -> rt_37853_commitCount++); 1077 1078 assertEquals(0, rt_37853_cancelCount); 1079 assertEquals(0, rt_37853_commitCount); 1080 1081 listView.edit(1); 1082 assertNotNull(listView.getEditingIndex()); 1083 1084 listView.getItems().clear(); 1085 assertEquals(1, rt_37853_cancelCount); 1086 assertEquals(0, rt_37853_commitCount); 1087 1088 sl.dispose(); 1089 } 1090 1091 @Test public void test_rt_38787_remove_b() { 1092 // selection moves to "a" 1093 test_rt_38787("a", 0, "b"); 1094 } 1095 1096 @Test public void test_rt_38787_remove_b_c() { 1097 // selection moves to "a" 1098 test_rt_38787("a", 0, "b", "c"); 1099 } 1100 1101 @Test public void test_rt_38787_remove_c_d() { 1102 // selection moves to "b" 1103 test_rt_38787("b", 1, "c", "d"); 1104 } 1105 1106 @Test public void test_rt_38787_remove_a() { 1107 // selection moves to "b", now in index 0 1108 test_rt_38787("b", 0, "a"); 1109 } 1110 1111 @Test public void test_rt_38787_remove_z() { 1112 // selection shouldn't move as 'z' doesn't exist 1113 test_rt_38787("b", 1, "z"); 1114 } 1115 1116 private void test_rt_38787(String expectedItem, int expectedIndex, String... itemsToRemove) { 1117 ListView<String> stringListView = new ListView<>(); 1118 stringListView.getItems().addAll("a","b","c","d"); 1119 1120 MultipleSelectionModel<String> sm = stringListView.getSelectionModel(); 1121 sm.select("b"); 1122 1123 // test pre-conditions 1124 assertEquals(1, sm.getSelectedIndex()); 1125 assertEquals(1, (int)sm.getSelectedIndices().get(0)); 1126 assertEquals("b", sm.getSelectedItem()); 1127 assertEquals("b", sm.getSelectedItems().get(0)); 1128 assertFalse(sm.isSelected(0)); 1129 assertTrue(sm.isSelected(1)); 1130 assertFalse(sm.isSelected(2)); 1131 1132 // removing items 1133 stringListView.getItems().removeAll(itemsToRemove); 1134 1135 // testing against expectations 1136 assertEquals(expectedIndex, sm.getSelectedIndex()); 1137 assertEquals(expectedIndex, (int)sm.getSelectedIndices().get(0)); 1138 assertEquals(expectedItem, sm.getSelectedItem()); 1139 assertEquals(expectedItem, sm.getSelectedItems().get(0)); 1140 } 1141 1142 private int rt_38341_indices_count = 0; 1143 private int rt_38341_items_count = 0; 1144 @Test public void test_rt_38341() { 1145 ListView<String> stringListView = new ListView<>(); 1146 stringListView.getItems().addAll("a","b","c","d"); 1147 1148 MultipleSelectionModel<String> sm = stringListView.getSelectionModel(); 1149 sm.getSelectedIndices().addListener((ListChangeListener<Integer>) c -> rt_38341_indices_count++); 1150 sm.getSelectedItems().addListener((ListChangeListener<String>) c -> rt_38341_items_count++); 1151 1152 assertEquals(0, rt_38341_indices_count); 1153 assertEquals(0, rt_38341_items_count); 1154 1155 // expand the first child of root, and select it (note: root isn't visible) 1156 sm.select(1); 1157 assertEquals(1, sm.getSelectedIndex()); 1158 assertEquals(1, sm.getSelectedIndices().size()); 1159 assertEquals(1, (int)sm.getSelectedIndices().get(0)); 1160 assertEquals(1, sm.getSelectedItems().size()); 1161 assertEquals("b", sm.getSelectedItem()); 1162 assertEquals("b", sm.getSelectedItems().get(0)); 1163 1164 assertEquals(1, rt_38341_indices_count); 1165 assertEquals(1, rt_38341_items_count); 1166 1167 // now delete it 1168 stringListView.getItems().remove(1); 1169 1170 // selection should move to the childs parent in index 0 1171 assertEquals(0, sm.getSelectedIndex()); 1172 assertEquals(1, sm.getSelectedIndices().size()); 1173 assertEquals(0, (int)sm.getSelectedIndices().get(0)); 1174 assertEquals(1, sm.getSelectedItems().size()); 1175 assertEquals("a", sm.getSelectedItem()); 1176 assertEquals("a", sm.getSelectedItems().get(0)); 1177 1178 // we also expect there to be an event in the selection model for 1179 // selected indices and selected items 1180 assertEquals(sm.getSelectedIndices() +"", 2, rt_38341_indices_count); 1181 assertEquals(2, rt_38341_items_count); 1182 } 1183 1184 @Test public void test_rt_39132() { 1185 ObservableList items = FXCollections.observableArrayList("one", "two", "three"); 1186 ListView listView = new ListView<>(); 1187 listView.setItems(items); 1188 1189 MultipleSelectionModel sm = listView.getSelectionModel(); 1190 sm.select(0); 1191 1192 assertEquals(0, sm.getSelectedIndex()); 1193 assertEquals("one", sm.getSelectedItem()); 1194 1195 items.add(0, "new item"); 1196 assertEquals(1, sm.getSelectedIndex()); 1197 assertEquals("one", sm.getSelectedItem()); 1198 } 1199 1200 private int rt_38943_index_count = 0; 1201 private int rt_38943_item_count = 0; 1202 @Test public void test_rt_38943() { 1203 ListView<String> listView = new ListView<>(FXCollections.observableArrayList("one", "two", "three")); 1204 1205 MultipleSelectionModel sm = listView.getSelectionModel(); 1206 1207 sm.selectedIndexProperty().addListener((observable, oldValue, newValue) -> rt_38943_index_count++); 1208 sm.selectedItemProperty().addListener((observable, oldValue, newValue) -> rt_38943_item_count++); 1209 1210 assertEquals(-1, sm.getSelectedIndex()); 1211 assertNull(sm.getSelectedItem()); 1212 assertEquals(0, rt_38943_index_count); 1213 assertEquals(0, rt_38943_item_count); 1214 1215 sm.select(0); 1216 assertEquals(0, sm.getSelectedIndex()); 1217 assertEquals("one", sm.getSelectedItem()); 1218 assertEquals(1, rt_38943_index_count); 1219 assertEquals(1, rt_38943_item_count); 1220 1221 sm.clearSelection(0); 1222 assertEquals(-1, sm.getSelectedIndex()); 1223 assertNull(sm.getSelectedItem()); 1224 assertEquals(2, rt_38943_index_count); 1225 assertEquals(2, rt_38943_item_count); 1226 } 1227 1228 @Test public void test_rt_38884() { 1229 ListView<String> listView = new ListView<>(); 1230 ObservableList<String> items = listView.getItems(); 1231 1232 listView.getSelectionModel().getSelectedItems().addListener((ListChangeListener.Change<? extends String> c) -> { 1233 while (c.next()) { 1234 if (c.wasRemoved()) { 1235 assertTrue(c.getRemovedSize() > 0); 1236 1237 List<? extends String> removed = c.getRemoved(); 1238 String removedItem = null; 1239 try { 1240 removedItem = removed.get(0); 1241 } catch (Exception e) { 1242 fail(); 1243 } 1244 1245 assertEquals("foo", removedItem); 1246 } 1247 } 1248 }); 1249 1250 items.add("foo"); 1251 listView.getSelectionModel().select(0); 1252 items.clear(); 1253 } 1254 1255 private int rt_37360_add_count = 0; 1256 private int rt_37360_remove_count = 0; 1257 @Test public void test_rt_37360() { 1258 ListView<String> stringListView = new ListView<>(); 1259 stringListView.getItems().addAll("a", "b"); 1260 1261 MultipleSelectionModel<String> sm = stringListView.getSelectionModel(); 1262 sm.setSelectionMode(SelectionMode.MULTIPLE); 1263 sm.getSelectedItems().addListener((ListChangeListener<String>) c -> { 1264 while (c.next()) { 1265 if (c.wasAdded()) { 1266 rt_37360_add_count += c.getAddedSize(); 1267 } 1268 if (c.wasRemoved()) { 1269 rt_37360_remove_count += c.getRemovedSize(); 1270 } 1271 } 1272 }); 1273 1274 assertEquals(0, sm.getSelectedItems().size()); 1275 assertEquals(0, rt_37360_add_count); 1276 assertEquals(0, rt_37360_remove_count); 1277 1278 sm.select(0); 1279 assertEquals(1, sm.getSelectedItems().size()); 1280 assertEquals(1, rt_37360_add_count); 1281 assertEquals(0, rt_37360_remove_count); 1282 1283 sm.select(1); 1284 assertEquals(2, sm.getSelectedItems().size()); 1285 assertEquals(2, rt_37360_add_count); 1286 assertEquals(0, rt_37360_remove_count); 1287 1288 sm.clearAndSelect(1); 1289 assertEquals(1, sm.getSelectedItems().size()); 1290 assertEquals(2, rt_37360_add_count); 1291 assertEquals(1, rt_37360_remove_count); 1292 } 1293 1294 @Test public void test_rt_38491() { 1295 ListView<String> stringListView = new ListView<>(); 1296 stringListView.getItems().addAll("a", "b"); 1297 1298 MultipleSelectionModel<String> sm = stringListView.getSelectionModel(); 1299 sm.setSelectionMode(SelectionMode.MULTIPLE); 1300 1301 FocusModel<String> fm = stringListView.getFocusModel(); 1302 1303 // click on row 0 1304 VirtualFlowTestUtils.clickOnRow(stringListView, 0); 1305 assertTrue(sm.isSelected(0)); 1306 assertEquals("a", sm.getSelectedItem()); 1307 assertTrue(fm.isFocused(0)); 1308 assertEquals("a", fm.getFocusedItem()); 1309 assertEquals(0, fm.getFocusedIndex()); 1310 1311 int anchor = ListCellBehavior.getAnchor(stringListView, null); 1312 assertTrue(ListCellBehavior.hasNonDefaultAnchor(stringListView)); 1313 assertEquals(0, anchor); 1314 1315 // now add a new item at row 0. This has the effect of pushing down 1316 // the selected item into row 1. 1317 stringListView.getItems().add(0, "z"); 1318 1319 // The first bug was that selection and focus were not moving down to 1320 // be on row 1, so we test that now 1321 assertFalse(sm.isSelected(0)); 1322 assertFalse(fm.isFocused(0)); 1323 assertTrue(sm.isSelected(1)); 1324 assertEquals("a", sm.getSelectedItem()); 1325 assertTrue(fm.isFocused(1)); 1326 assertEquals("a", fm.getFocusedItem()); 1327 assertEquals(1, fm.getFocusedIndex()); 1328 1329 // The second bug was that the anchor was not being pushed down as well 1330 // (when it should). 1331 anchor = ListCellBehavior.getAnchor(stringListView, null); 1332 assertTrue(ListCellBehavior.hasNonDefaultAnchor(stringListView)); 1333 assertEquals(1, anchor); 1334 } 1335 1336 private final ObservableList<String> rt_39256_list = FXCollections.observableArrayList(); 1337 @Test public void test_rt_39256() { 1338 ListView<String> stringListView = new ListView<>(); 1339 stringListView.getItems().addAll("a","b", "c", "d"); 1340 1341 MultipleSelectionModel<String> sm = stringListView.getSelectionModel(); 1342 sm.setSelectionMode(SelectionMode.MULTIPLE); 1343 1344 // rt_39256_list.addListener((ListChangeListener<String>) change -> { 1345 // while (change.next()) { 1346 // System.err.println("number of selected persons (in bound list): " + change.getList().size()); 1347 // } 1348 // }); 1349 1350 Bindings.bindContent(rt_39256_list, sm.getSelectedItems()); 1351 1352 assertEquals(0, sm.getSelectedItems().size()); 1353 assertEquals(0, rt_39256_list.size()); 1354 1355 sm.selectAll(); 1356 assertEquals(4, sm.getSelectedItems().size()); 1357 assertEquals(4, rt_39256_list.size()); 1358 1359 sm.selectAll(); 1360 assertEquals(4, sm.getSelectedItems().size()); 1361 assertEquals(4, rt_39256_list.size()); 1362 1363 sm.selectAll(); 1364 assertEquals(4, sm.getSelectedItems().size()); 1365 assertEquals(4, rt_39256_list.size()); 1366 } 1367 1368 private final ObservableList<String> rt_39482_list = FXCollections.observableArrayList(); 1369 @Test public void test_rt_39482() { 1370 ListView<String> stringListView = new ListView<>(); 1371 stringListView.getItems().addAll("a", "b", "c", "d"); 1372 1373 MultipleSelectionModel<String> sm = stringListView.getSelectionModel(); 1374 sm.setSelectionMode(SelectionMode.MULTIPLE); 1375 1376 sm.getSelectedItems().addListener((ListChangeListener<String>) change -> { 1377 while (change.next()) { 1378 System.out.println("sm.getSelectedItems(): " + change.getList()); 1379 } 1380 }); 1381 1382 rt_39482_list.addListener((ListChangeListener<String>) change -> { 1383 while (change.next()) { 1384 System.out.println("rt_39482_list: " + change.getList()); 1385 } 1386 }); 1387 1388 Bindings.bindContent(rt_39482_list, sm.getSelectedItems()); 1389 1390 assertEquals(0, sm.getSelectedItems().size()); 1391 assertEquals(0, rt_39482_list.size()); 1392 1393 test_rt_39482_selectRow("a", sm, 0); 1394 test_rt_39482_selectRow("b", sm, 1); 1395 test_rt_39482_selectRow("c", sm, 2); 1396 test_rt_39482_selectRow("d", sm, 3); 1397 } 1398 1399 private void test_rt_39482_selectRow(String expectedString, 1400 MultipleSelectionModel<String> sm, 1401 int rowToSelect) { 1402 System.out.println("\nSelect row " + rowToSelect); 1403 sm.selectAll(); 1404 assertEquals(4, sm.getSelectedIndices().size()); 1405 assertEquals(4, sm.getSelectedItems().size()); 1406 assertEquals(4, rt_39482_list.size()); 1407 1408 sm.clearAndSelect(rowToSelect); 1409 assertEquals(1, sm.getSelectedIndices().size()); 1410 assertEquals(1, sm.getSelectedItems().size()); 1411 assertEquals(expectedString, sm.getSelectedItem()); 1412 assertEquals(expectedString, rt_39482_list.get(0)); 1413 assertEquals(1, rt_39482_list.size()); 1414 } 1415 1416 @Test public void test_rt_39559_useSM_selectAll() { 1417 test_rt_39559(true); 1418 } 1419 1420 @Test public void test_rt_39559_useKeyboard_selectAll() { 1421 test_rt_39559(false); 1422 } 1423 1424 private void test_rt_39559(boolean useSMSelectAll) { 1425 ListView<String> stringListView = new ListView<>(); 1426 stringListView.getItems().addAll("a", "b", "c", "d"); 1427 1428 MultipleSelectionModel<String> sm = stringListView.getSelectionModel(); 1429 sm.setSelectionMode(SelectionMode.MULTIPLE); 1430 1431 StageLoader sl = new StageLoader(stringListView); 1432 KeyEventFirer keyboard = new KeyEventFirer(stringListView); 1433 1434 assertEquals(0, sm.getSelectedItems().size()); 1435 1436 sm.clearAndSelect(0); 1437 1438 if (useSMSelectAll) { 1439 sm.selectAll(); 1440 } else { 1441 keyboard.doKeyPress(KeyCode.A, KeyModifier.getShortcutKey()); 1442 } 1443 1444 assertEquals(4, sm.getSelectedItems().size()); 1445 assertEquals(0, (int) ListCellBehavior.getAnchor(stringListView, -1)); 1446 1447 keyboard.doKeyPress(KeyCode.DOWN, KeyModifier.SHIFT); 1448 1449 assertEquals(0, (int) ListCellBehavior.getAnchor(stringListView, -1)); 1450 assertEquals(2, sm.getSelectedItems().size()); 1451 assertEquals("a", sm.getSelectedItems().get(0)); 1452 assertEquals("b", sm.getSelectedItems().get(1)); 1453 1454 sl.dispose(); 1455 } 1456 1457 @Test public void test_rt_16068_firstElement_selectAndRemoveSameRow() { 1458 // select and then remove the 'a' item, selection and focus should both 1459 // stay at the first row, now 'b' 1460 test_rt_16068(0, 0, 0); 1461 } 1462 1463 @Test public void test_rt_16068_firstElement_selectRowAndRemoveLaterSibling() { 1464 // select row 'a', and remove row 'c', selection and focus should not change 1465 test_rt_16068(0, 2, 0); 1466 } 1467 1468 @Test public void test_rt_16068_middleElement_selectAndRemoveSameRow() { 1469 // select and then remove the 'b' item, selection and focus should both 1470 // move up one row to the 'a' item 1471 test_rt_16068(1, 1, 0); 1472 } 1473 1474 @Test public void test_rt_16068_middleElement_selectRowAndRemoveLaterSibling() { 1475 // select row 'b', and remove row 'c', selection and focus should not change 1476 test_rt_16068(1, 2, 1); 1477 } 1478 1479 @Test public void test_rt_16068_middleElement_selectRowAndRemoveEarlierSibling() { 1480 // select row 'b', and remove row 'a', selection and focus should move up 1481 // one row, remaining on 'b' 1482 test_rt_16068(1, 0, 0); 1483 } 1484 1485 @Test public void test_rt_16068_lastElement_selectAndRemoveSameRow() { 1486 // select and then remove the 'd' item, selection and focus should both 1487 // move up one row to the 'c' item 1488 test_rt_16068(3, 3, 2); 1489 } 1490 1491 @Test public void test_rt_16068_lastElement_selectRowAndRemoveEarlierSibling() { 1492 // select row 'd', and remove row 'a', selection and focus should move up 1493 // one row, remaining on 'd' 1494 test_rt_16068(3, 0, 2); 1495 } 1496 1497 private void test_rt_16068(int indexToSelect, int indexToRemove, int expectedIndex) { 1498 ListView<String> stringListView = new ListView<>(); 1499 stringListView.getItems().addAll("a", "b", "c", "d"); 1500 1501 MultipleSelectionModel<?> sm = stringListView.getSelectionModel(); 1502 FocusModel<?> fm = stringListView.getFocusModel(); 1503 1504 sm.select(indexToSelect); 1505 assertEquals(indexToSelect, sm.getSelectedIndex()); 1506 assertEquals(stringListView.getItems().get(indexToSelect), sm.getSelectedItem()); 1507 assertEquals(indexToSelect, fm.getFocusedIndex()); 1508 assertEquals(stringListView.getItems().get(indexToSelect), fm.getFocusedItem()); 1509 1510 stringListView.getItems().remove(indexToRemove); 1511 assertEquals(expectedIndex, sm.getSelectedIndex()); 1512 assertEquals(stringListView.getItems().get(expectedIndex), sm.getSelectedItem()); 1513 assertEquals(expectedIndex, fm.getFocusedIndex()); 1514 assertEquals(stringListView.getItems().get(expectedIndex), fm.getFocusedItem()); 1515 } 1516 1517 @Test public void test_rt_22599() { 1518 ObservableList<RT22599_DataType> initialData = FXCollections.observableArrayList( 1519 new RT22599_DataType(1, "row1"), 1520 new RT22599_DataType(2, "row2"), 1521 new RT22599_DataType(3, "row3") 1522 ); 1523 1524 ListView<RT22599_DataType> listView = new ListView<>(); 1525 listView.setItems(initialData); 1526 1527 StageLoader sl = new StageLoader(listView); 1528 1529 // testing initial state 1530 assertNotNull(listView.getSkin()); 1531 assertEquals("row1", VirtualFlowTestUtils.getCell(listView, 0).getText()); 1532 assertEquals("row2", VirtualFlowTestUtils.getCell(listView, 1).getText()); 1533 assertEquals("row3", VirtualFlowTestUtils.getCell(listView, 2).getText()); 1534 1535 // change row 0 (where "row1" currently resides), keeping same id. 1536 // Because 'set' is called, the control should update to the new content 1537 // without any user interaction 1538 RT22599_DataType data; 1539 initialData.set(0, data = new RT22599_DataType(0, "row1a")); 1540 Toolkit.getToolkit().firePulse(); 1541 assertEquals("row1a", VirtualFlowTestUtils.getCell(listView, 0).getText()); 1542 1543 // change the row 0 (where we currently have "row1a") value directly. 1544 // Because there is no associated property, this won't be observed, so 1545 // the control should still show "row1a" rather than "row1b" 1546 data.text = "row1b"; 1547 Toolkit.getToolkit().firePulse(); 1548 assertEquals("row1a", VirtualFlowTestUtils.getCell(listView, 0).getText()); 1549 1550 // call refresh() to force a refresh of all visible cells 1551 listView.refresh(); 1552 Toolkit.getToolkit().firePulse(); 1553 assertEquals("row1b", VirtualFlowTestUtils.getCell(listView, 0).getText()); 1554 1555 sl.dispose(); 1556 } 1557 1558 private static class RT22599_DataType { 1559 public int id = 0; 1560 public String text = ""; 1561 1562 public RT22599_DataType(int id, String text) { 1563 this.id = id; 1564 this.text = text; 1565 } 1566 1567 @Override public String toString() { 1568 return text; 1569 } 1570 1571 @Override public boolean equals(Object obj) { 1572 if (obj == null) return false; 1573 return id == ((RT22599_DataType)obj).id; 1574 } 1575 } 1576 1577 private int rt_39966_count = 0; 1578 @Test public void test_rt_39966() { 1579 ObservableList<String> list = FXCollections.observableArrayList("Hello World"); 1580 ListView<String> listView = new ListView<>(list); 1581 1582 StageLoader sl = new StageLoader(listView); 1583 1584 // initially there is no selection 1585 assertTrue(listView.getSelectionModel().isEmpty()); 1586 1587 listView.getSelectionModel().selectedItemProperty().addListener((value, s1, s2) -> { 1588 if (rt_39966_count == 0) { 1589 rt_39966_count++; 1590 assertFalse(listView.getSelectionModel().isEmpty()); 1591 } else { 1592 assertTrue(listView.getSelectionModel().isEmpty()); 1593 } 1594 }); 1595 1596 // our assertion two lines down always succeeds. What fails is our 1597 // assertion above within the listener. 1598 listView.getSelectionModel().select(0); 1599 assertFalse(listView.getSelectionModel().isEmpty()); 1600 1601 list.remove(0); 1602 assertTrue(listView.getSelectionModel().isEmpty()); 1603 1604 sl.dispose(); 1605 } 1606 1607 /** 1608 * Bullet 1: selected index must be updated 1609 * Corner case: last selected. Fails for core 1610 */ 1611 @Test public void test_rt_40012_selectedAtLastOnDisjointRemoveItemsAbove() { 1612 ObservableList<String> items = FXCollections.observableArrayList("0", "1", "2", "3", "4", "5"); 1613 ListView<String> listView = new ListView<>(items); 1614 SelectionModel sm = listView.getSelectionModel(); 1615 1616 int last = items.size() - 1; 1617 1618 // selecting item "5" 1619 sm.select(last); 1620 1621 // disjoint remove of 2 elements above the last selected 1622 // Removing "1" and "3" 1623 items.removeAll(items.get(1), items.get(3)); 1624 1625 // selection should move up two places such that it remains on item "5", 1626 // but in index (last - 2). 1627 int expected = last - 2; 1628 assertEquals("5", sm.getSelectedItem()); 1629 assertEquals("selected index after disjoint removes above", expected, sm.getSelectedIndex()); 1630 } 1631 1632 /** 1633 * Variant of 1: if selectedIndex is not updated, 1634 * the old index is no longer valid 1635 * for accessing the items. 1636 */ 1637 @Test public void test_rt_40012_accessSelectedAtLastOnDisjointRemoveItemsAbove() { 1638 ObservableList<String> items = FXCollections.observableArrayList("0", "1", "2", "3", "4", "5"); 1639 ListView<String> listView = new ListView<>(items); 1640 SelectionModel sm = listView.getSelectionModel(); 1641 1642 int last = items.size() - 1; 1643 1644 // selecting item "5" 1645 sm.select(last); 1646 1647 // disjoint remove of 2 elements above the last selected 1648 items.removeAll(items.get(1), items.get(3)); 1649 int selected = sm.getSelectedIndex(); 1650 if (selected > -1) { 1651 items.get(selected); 1652 } 1653 } 1654 1655 /** 1656 * Bullet 2: selectedIndex notification count 1657 * 1658 * Note that we don't use the corner case of having the last index selected 1659 * (which fails already on updating the index) 1660 */ 1661 private int rt_40012_count = 0; 1662 @Test public void test_rt_40012_selectedIndexNotificationOnDisjointRemovesAbove() { 1663 ObservableList<String> items = FXCollections.observableArrayList("0", "1", "2", "3", "4", "5"); 1664 ListView<String> listView = new ListView<>(items); 1665 SelectionModel sm = listView.getSelectionModel(); 1666 1667 int last = items.size() - 2; 1668 sm.select(last); 1669 assertEquals(last, sm.getSelectedIndex()); 1670 1671 rt_40012_count = 0; 1672 sm.selectedIndexProperty().addListener(o -> rt_40012_count++); 1673 1674 // disjoint remove of 2 elements above the last selected 1675 items.removeAll(items.get(1), items.get(3)); 1676 assertEquals("sanity: selectedIndex must be shifted by -2", last - 2, sm.getSelectedIndex()); 1677 assertEquals("must fire single event on removes above", 1, rt_40012_count); 1678 } 1679 1680 /** 1681 * Bullet 3: unchanged selectedItem must not fire change 1682 */ 1683 @Test 1684 public void test_rt_40012_selectedItemNotificationOnDisjointRemovesAbove() { 1685 ObservableList<String> items = FXCollections.observableArrayList("0", "1", "2", "3", "4", "5"); 1686 ListView<String> listView = new ListView<>(items); 1687 SelectionModel sm = listView.getSelectionModel(); 1688 1689 int last = items.size() - 2; 1690 Object lastItem = items.get(last); 1691 sm.select(last); 1692 assertEquals(lastItem, sm.getSelectedItem()); 1693 1694 rt_40012_count = 0; 1695 sm.selectedItemProperty().addListener(o -> rt_40012_count++); 1696 1697 // disjoint remove of 2 elements above the last selected 1698 items.removeAll(items.get(1), items.get(3)); 1699 assertEquals("sanity: selectedItem unchanged", lastItem, sm.getSelectedItem()); 1700 assertEquals("must not fire on unchanged selected item", 0, rt_40012_count); 1701 } 1702 1703 @Test public void test_rt_40185() { 1704 final ListView<String> lv = new ListView<>(); 1705 final ArrayList<Integer> expected = new ArrayList<>(); 1706 Collections.addAll(expected, 1, 2); 1707 1708 lv.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); 1709 lv.getSelectionModel().getSelectedIndices().addListener((ListChangeListener<Integer>) change -> { 1710 while (change.next()) { 1711 if (change.wasRemoved()) { 1712 assertEquals(expected, change.getRemoved()); 1713 } 1714 } 1715 }); 1716 1717 lv.getItems().addAll("-0-","-1-","-2-"); 1718 lv.getSelectionModel().selectIndices(1, 2); 1719 lv.getSelectionModel().clearSelection(); 1720 } 1721 1722 /** 1723 * ClearAndSelect fires invalid change event if selectedIndex is unchanged. 1724 */ 1725 private int rt_40212_count = 0; 1726 @Test public void test_rt_40212() { 1727 final ListView<Integer> lv = new ListView<>(); 1728 for (int i = 0; i < 10; i++) { 1729 lv.getItems().add(i); 1730 } 1731 1732 MultipleSelectionModel<Integer> sm = lv.getSelectionModel(); 1733 sm.setSelectionMode(SelectionMode.MULTIPLE); 1734 1735 sm.selectRange(3, 5); 1736 int selected = sm.getSelectedIndex(); 1737 1738 sm.getSelectedIndices().addListener((ListChangeListener<Integer>) change -> { 1739 assertEquals("sanity: selectedIndex unchanged", selected, sm.getSelectedIndex()); 1740 while(change.next()) { 1741 assertEquals("single event on clearAndSelect already selected", 1, ++rt_40212_count); 1742 1743 boolean type = change.wasAdded() || change.wasRemoved() || change.wasPermutated() || change.wasUpdated(); 1744 assertTrue("at least one of the change types must be true", type); 1745 } 1746 }); 1747 1748 sm.clearAndSelect(selected); 1749 } 1750 1751 @Test public void test_rt_40280() { 1752 final ListView<String> view = new ListView<>(); 1753 StageLoader sl = new StageLoader(view); 1754 view.getFocusModel().getFocusedIndex(); 1755 sl.dispose(); 1756 } 1757 1758 /** 1759 * Test list change of selectedIndices on setIndices. Fails for core .. 1760 */ 1761 @Test public void test_rt_40263() { 1762 final ListView<Integer> lv = new ListView<>(); 1763 for (int i = 0; i < 10; i++) { 1764 lv.getItems().add(i); 1765 } 1766 1767 MultipleSelectionModel<Integer> sm = lv.getSelectionModel(); 1768 sm.setSelectionMode(SelectionMode.MULTIPLE); 1769 1770 int[] indices = new int[]{2, 5, 7}; 1771 ListChangeListener<Integer> l = c -> { 1772 // firstly, we expect only one change 1773 int subChanges = 0; 1774 while(c.next()) { 1775 subChanges++; 1776 } 1777 assertEquals(1, subChanges); 1778 1779 // secondly, we expect the added size to be three, as that is the 1780 // number of items selected 1781 c.reset(); 1782 c.next(); 1783 System.out.println("Added items: " + c.getAddedSubList()); 1784 assertEquals(indices.length, c.getAddedSize()); 1785 assertArrayEquals(indices, c.getAddedSubList().stream().mapToInt(i -> i).toArray()); 1786 }; 1787 sm.getSelectedIndices().addListener(l); 1788 sm.selectIndices(indices[0], indices); 1789 } 1790 }