package org.fxmisc.richtext.mouse; import com.nitorcreations.junit.runners.NestedRunner; import javafx.beans.property.SimpleIntegerProperty; import javafx.geometry.Bounds; import javafx.stage.Stage; import org.fxmisc.richtext.InlineCssTextAreaAppTest; import org.junit.Test; import org.junit.runner.RunWith; import java.util.concurrent.ExecutionException; import static javafx.scene.input.KeyCode.*; import static javafx.scene.input.MouseButton.*; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertTrue; import static org.testfx.util.WaitForAsyncUtils.asyncFx; @RunWith(NestedRunner.class) public class ClickAndDragTests { public class When_Area_Is_Disabled extends InlineCssTextAreaAppTest { @Override public void start(Stage stage) throws Exception { super.start(stage); area.setDisable(true); area.replaceText("When Area Is Disabled Test: Some text goes here"); area.moveTo(0); } @Test public void shift_clicking_area_does_nothing() { moveTo(firstLineOfArea()) .moveBy(20, 0) .press(SHIFT) .press(PRIMARY); assertFalse(area.isFocused()); } @Test public void single_clicking_area_does_nothing() { leftClickOnFirstLine(); assertFalse(area.isFocused()); } @Test public void double_clicking_area_does_nothing() { doubleClickOnFirstLine(); assertFalse(area.isFocused()); } @Test public void triple_clicking_area_does_nothing() { tripleClickOnFirstLine(); assertFalse(area.isFocused()); } @Test public void dragging_the_mouse_does_not_select_text() { moveTo(firstLineOfArea()) .press(PRIMARY) .moveBy(20, 0); assertTrue(area.getSelectedText().isEmpty()); } @Test public void releasing_the_mouse_after_drag_does_nothing() { assertEquals(0, area.getCaretPosition()); SimpleIntegerProperty i = new SimpleIntegerProperty(0); area.setOnNewSelectionDragFinished(e -> i.set(1)); moveTo(firstLineOfArea()) .press(PRIMARY) .dropBy(20, 0); assertEquals(0, area.getCaretPosition()); assertEquals(0, i.get()); } } public class When_Area_Is_Enabled { public class And_Text_Is_Not_Selected extends InlineCssTextAreaAppTest { private String firstWord = "Some"; private String firstParagraph = firstWord + " text goes here"; private String secondWord = "More"; private String secondParagraph = secondWord + " text goes here"; @Override public void start(Stage stage) throws Exception { super.start(stage); area.replaceText(firstParagraph + "\n" + secondParagraph); area.moveTo(0); } @Test public void single_clicking_area_moves_caret_to_that_position() throws InterruptedException, ExecutionException { assertEquals(0, area.getCaretPosition()); Bounds bounds = asyncFx( () -> area.getCharacterBoundsOnScreen(firstWord.length(), firstWord.length() + 1).get()) .get(); moveTo(bounds).clickOn(PRIMARY); assertEquals(firstWord.length(), area.getCaretPosition()); } @Test public void double_clicking_text_in_area_selects_closest_word() { doubleClickOnFirstLine(); assertEquals(firstWord, area.getSelectedText()); } @Test public void triple_clicking_line_in_area_selects_paragraph() throws InterruptedException, ExecutionException { int wordStart = firstParagraph.length() + 1; Bounds bounds = asyncFx( () -> area.getCharacterBoundsOnScreen(wordStart, wordStart + 1).get()) .get(); moveTo(bounds).doubleClickOn(PRIMARY).clickOn(PRIMARY); assertEquals(secondParagraph, area.getSelectedText()); } @Test public void pressing_mouse_over_text_and_dragging_mouse_selects_text() { moveTo(firstLineOfArea()) .press(PRIMARY) .moveBy(20, 0); assertFalse(area.getSelectedText().isEmpty()); } @Test public void pressing_mouse_over_text_and_dragging_and_releasing_mouse_triggers_new_selection_finished() { // Doesn't work on Mac builds; works on Linux & Windows skip_if_on_mac(); SimpleIntegerProperty i = new SimpleIntegerProperty(0); area.setOnNewSelectionDragFinished(e -> i.set(1)); moveTo(firstLineOfArea()) .press(PRIMARY) .moveBy(20, 0) .release(PRIMARY); assertFalse(area.getSelectedText().isEmpty()); assertEquals(1, i.get()); } } public class And_Text_Is_Selected extends InlineCssTextAreaAppTest { private String firstWord = "Some"; private String firstParagraph = firstWord + " text goes here"; private String extraText = "This is extra text"; @Test public void single_clicking_within_selected_text_moves_caret_to_that_position() throws InterruptedException, ExecutionException { // setup interact(() -> { area.replaceText(firstParagraph); area.selectAll(); }); Bounds bounds = asyncFx( () -> area.getCharacterBoundsOnScreen( firstWord.length(), firstWord.length() + 1).get()) .get(); moveTo(bounds).clickOn(PRIMARY); assertEquals(firstWord.length(), area.getCaretPosition()); } @Test public void double_clicking_within_selected_text_selects_closest_word() { // setup interact(() -> { area.replaceText(firstParagraph); area.selectAll(); }); doubleClickOnFirstLine(); assertEquals(firstWord, area.getSelectedText()); } @Test public void triple_clicking_within_selected_text_selects_paragraph() { // setup interact(() -> { area.replaceText(firstParagraph); area.selectAll(); }); tripleClickOnFirstLine(); assertEquals(firstParagraph, area.getSelectedText()); } @Test public void single_clicking_within_selected_text_does_not_trigger_new_selection_finished() throws InterruptedException, ExecutionException { // setup interact(() -> { area.replaceText(firstParagraph); area.selectAll(); }); SimpleIntegerProperty i = new SimpleIntegerProperty(0); area.setOnNewSelectionDragFinished(e -> i.set(1)); Bounds bounds = asyncFx( () -> area.getCharacterBoundsOnScreen( firstWord.length(), firstWord.length() + 1).get()) .get(); moveTo(bounds).clickOn(PRIMARY); assertEquals(0, i.get()); } @Test public void single_clicking_outside_of_selected_text_moves_caret_to_that_position() throws InterruptedException, ExecutionException { // setup interact(() -> { area.replaceText(firstParagraph + "\n" + "this is the selected text"); area.selectRange(1, 0, 2, -1); }); Bounds bounds = asyncFx( () -> area.getCharacterBoundsOnScreen( firstWord.length(), firstWord.length() + 1).get()) .get(); moveTo(bounds).clickOn(PRIMARY); assertEquals(firstWord.length(), area.getCaretPosition()); } @Test public void double_clicking_outside_of_selected_text_selects_closest_word() { // setup interact(() -> { area.replaceText(firstParagraph + "\n" + "this is the selected text"); area.selectRange(1, 0, 2, -1); }); doubleClickOnFirstLine(); assertEquals(firstWord, area.getSelectedText()); } @Test public void triple_clicking_outside_of_selected_text_selects_paragraph() { // setup interact(() -> { area.replaceText(firstParagraph + "\n" + "this is the selected text"); area.selectRange(1, 0, 2, -1); }); tripleClickOnFirstLine(); assertEquals(firstParagraph, area.getSelectedText()); } @Test public void single_clicking_outside_of_selected_text_does_not_trigger_new_selection_finished() throws InterruptedException, ExecutionException { // setup interact(() -> { area.replaceText(firstParagraph + "\n" + "this is the selected text"); area.selectRange(1, 0, 2, -1); }); SimpleIntegerProperty i = new SimpleIntegerProperty(0); area.setOnNewSelectionDragFinished(e -> i.set(1)); Bounds bounds = asyncFx( () -> area.getCharacterBoundsOnScreen(firstWord.length(), firstWord.length() + 1).get()) .get(); moveTo(bounds).clickOn(PRIMARY); assertEquals(0, i.get()); } @Test public void pressing_mouse_on_unselected_text_and_dragging_makes_new_selection() { // setup interact(() -> { area.replaceText(firstParagraph + "\n" + "this is the selected text"); area.selectRange(1, 0, 2, -1); }); String originalSelection = area.getSelectedText(); moveTo(firstLineOfArea()) .press(PRIMARY) .moveBy(20, 0); assertFalse(originalSelection.equals(area.getSelectedText())); } @Test public void pressing_mouse_on_selection_and_dragging_displaces_caret() throws InterruptedException, ExecutionException { // setup interact(() -> { area.replaceText(firstParagraph + "\n" + extraText); area.selectRange(0, firstParagraph.length()); }); String selText = area.getSelectedText(); Bounds firstLetterBounds = asyncFx( () -> area.getCharacterBoundsOnScreen(1, 2).get()) .get(); Bounds firstWordEndBounds = asyncFx( () -> area.getCharacterBoundsOnScreen(firstWord.length(), firstWord.length() + 1).get()) .get(); moveTo(firstLetterBounds) .press(PRIMARY) .moveTo(firstWordEndBounds); assertEquals(firstWord.length(), area.getCaretPosition()); assertEquals(selText, area.getSelectedText()); } @Test public void pressing_mouse_on_selection_and_dragging_and_releasing_moves_selected_text_to_that_position() throws InterruptedException, ExecutionException { // Linux passes; Mac fails at "assertEquals(selText, area.getSelectedText())"; Windows is untested // so only run on Linux // TODO: update test to see if it works on Windows run_only_on_linux(); // setup String twoSpaces = " "; interact(() -> { area.replaceText(firstParagraph + "\n" + twoSpaces + extraText); area.selectRange(0, firstWord.length()); }); String selText = area.getSelectedText(); Bounds letterInFirstWord = asyncFx(() -> area.getCharacterBoundsOnScreen(1, 2).get()) .get(); int insertionPosition = firstParagraph.length() + 2; Bounds insertionBounds = asyncFx(() -> area.getCharacterBoundsOnScreen(insertionPosition, insertionPosition + 1).get()) .get(); moveTo(letterInFirstWord) .press(PRIMARY) .dropTo(insertionBounds); String expectedText = firstParagraph.substring(firstWord.length()) + "\n" + " " + firstWord + " " + extraText; assertEquals(insertionPosition, area.getCaretPosition()); assertEquals(selText, area.getSelectedText()); assertEquals(expectedText, area.getText()); } @Test public void pressing_mouse_on_selection_and_dragging_and_releasing_does_not_trigger_new_selection_finished() throws InterruptedException, ExecutionException { // Linux passes; Mac/Windows uncertain // TODO: update test to see if it works on Mac & Windows run_only_on_linux(); // setup String twoSpaces = " "; interact(() -> { area.replaceText(firstParagraph + "\n" + twoSpaces + extraText); area.selectRange(0, firstWord.length()); }); SimpleIntegerProperty i = new SimpleIntegerProperty(0); area.setOnNewSelectionDragFinished(e -> i.set(1)); Bounds letterInFirstWord = asyncFx( () -> area.getCharacterBoundsOnScreen(1, 2).get()) .get(); final int insertionPosition = firstParagraph.length() + 2; Bounds insertionBounds = asyncFx( () -> area.getCharacterBoundsOnScreen(insertionPosition, insertionPosition + 1).get()) .get(); moveTo(letterInFirstWord) .press(PRIMARY) .dropTo(insertionBounds); assertEquals(0, i.get()); } } } }