Skip to content

Commit

Permalink
Merge pull request #439 from JordanMartinez/addContextMenu
Browse files Browse the repository at this point in the history
Add API for setting/getting context menu; implement default behavior
  • Loading branch information
JordanMartinez authored Jan 28, 2017
2 parents 5109237 + f28fc3d commit efea005
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 1 deletion.
1 change: 1 addition & 0 deletions richtextfx/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dependencies {
testCompile ("org.testfx:testfx-junit:4.0.5-alpha") {
exclude(group: "junit", module: "junit")
}
testCompile group: 'com.nitorcreations', name: 'junit-runners', version: '1.2'
}

javadoc {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.IndexRange;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
Expand Down Expand Up @@ -275,6 +276,29 @@ public static enum CaretVisibility {
public IntFunction<? extends Node> getParagraphGraphicFactory() { return paragraphGraphicFactory.get(); }
public ObjectProperty<IntFunction<? extends Node>> paragraphGraphicFactoryProperty() { return paragraphGraphicFactory; }

/** The {@link ContextMenu} for the area, which is by default null. */
private ObjectProperty<ContextMenu> contextMenu = new SimpleObjectProperty<>(null);
public final ContextMenu getContextMenu() { return contextMenu.get(); }
public final void setContextMenu(ContextMenu menu) { contextMenu.setValue(menu); }
public final ObjectProperty<ContextMenu> contextMenuObjectProperty() { return contextMenu; }
protected final boolean isContextMenuPresent() { return contextMenu.get() != null; }

/**
* The horizontal amount in pixels by which to offset the {@link #contextMenu} when it is shown, which has
* a default value of 2.
*/
private double contextMenuXOffset = 2;
public final double getContextMenuXOffset() { return contextMenuXOffset; }
public final void setContextMenuXOffset(double offset) { contextMenuXOffset = offset; }

/**
* The vertical amount in pixels by which to offset the {@link #contextMenu} when it is shown, which has
* a default value of 2.
*/
private double contextMenuYOffset = 2;
public final double getContextMenuYOffset() { return contextMenuYOffset; }
public final void setContextMenuYOffset(double offset) { contextMenuYOffset = offset; }

/**
* Indicates whether the initial style should also be used for plain text
* inserted into this text area. When {@code false}, the style immediately
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
import javafx.event.Event;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.IndexRange;
import javafx.scene.input.ContextMenuEvent;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
Expand Down Expand Up @@ -183,7 +185,13 @@ class StyledTextAreaBehavior {
consume(eventType(MouseEvent.MOUSE_RELEASED), StyledTextAreaBehavior::mouseReleased)
);

EVENT_TEMPLATE = sequence(mouseEventTemplate, keyPressedTemplate, keyTypedTemplate);
InputMapTemplate<StyledTextAreaBehavior, ? super ContextMenuEvent> contextMenuEventTemplate = consumeWhen(
EventPattern.eventType(ContextMenuEvent.CONTEXT_MENU_REQUESTED),
b -> !b.view.isDisabled() && b.view.isContextMenuPresent(),
StyledTextAreaBehavior::showContextMenu
);

EVENT_TEMPLATE = sequence(mouseEventTemplate, keyPressedTemplate, keyTypedTemplate, contextMenuEventTemplate);
}

/**
Expand Down Expand Up @@ -385,12 +393,24 @@ private void skipToNextWord(SelectionPolicy selectionPolicy) {
* Mouse handling implementation *
* ********************************************************************** */

private void showContextMenu(ContextMenuEvent e) {
ContextMenu menu = view.getContextMenu();
double xOffset = view.getContextMenuXOffset();
double yOffset = view.getContextMenuYOffset();

menu.show(view, e.getScreenX() + xOffset, e.getScreenY() + yOffset);
}

private void mousePressed(MouseEvent e) {
// don't respond if disabled
if(view.isDisabled()) {
return;
}

if (view.isContextMenuPresent() && view.getContextMenu().isShowing()) {
view.getContextMenu().hide();
}

if(e.getButton() == MouseButton.PRIMARY) {
// ensure focus
view.requestFocus();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package org.fxmisc.richtext.model;

import com.nitorcreations.junit.runners.NestedRunner;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.input.KeyCode;
import javafx.scene.input.MouseButton;
import javafx.stage.Stage;
import org.fxmisc.flowless.VirtualizedScrollPane;
import org.fxmisc.richtext.InlineCssTextArea;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.testfx.framework.junit.ApplicationTest;

@RunWith(NestedRunner.class)
public class StyledTextAreaBehaviorTest {

static {
String osName = System.getProperty("os.name").toLowerCase();

WINDOWS_OS = osName.contains("win");
}

private static final boolean WINDOWS_OS;

public class ContextMenuTests extends ApplicationTest {

public InlineCssTextArea area = new InlineCssTextArea();

@Override
public void start(Stage stage) throws Exception {
area.setContextMenu(new ContextMenu(new MenuItem("A Menu Item")));
// offset needs to be 5 to prevent test failures
area.setContextMenuXOffset(5);
area.setContextMenuYOffset(5);

VirtualizedScrollPane<InlineCssTextArea> pane = new VirtualizedScrollPane<>(area);
stage.setScene(new Scene(pane, 500, 500));
stage.show();
}

@Test
public void clickingSecondaryShowsContextMenu() {
// when
rightClickOn(area);

// then
area.getContextMenu().isShowing();
}

@Test
public void pressingSecondaryShowsContextMenu() {
// when
moveTo(area);
press(MouseButton.SECONDARY);

// then
area.getContextMenu().isShowing();
}

@Test
public void pressingPrimaryMouseButtonHidesContextMenu() {
// given menu is showing
rightClickOn(area);

press(MouseButton.PRIMARY);
assert !area.getContextMenu().isShowing();
}

@Test
public void pressingMiddleMouseButtonHidesContextMenu() {
// given menu is showing
rightClickOn(area);

press(MouseButton.MIDDLE);
assert !area.getContextMenu().isShowing();
}

@Test
public void requestingContextMenuViaKeyboardWorksOnWindows() {
if (WINDOWS_OS) {
clickOn(area);
press(KeyCode.CONTEXT_MENU);

assert area.getContextMenu().isShowing();
}
}

}

}

0 comments on commit efea005

Please sign in to comment.