Set new button dragdetected problema - java

I use JavaFX, project has 2 Anchorpane (pane and paneDrop) , one of them has a button, when I drag this button create new button , when dragdropped setOnDragDetected method to new button. Problem is, when I try drag one of new butoons only last creating button move.
#FXML
private Button source;
#FXML
private AnchorPane pane;
#FXML
private AnchorPane paneDrop;
private Button b;
int i = 1;
int moveI = 0;
#FXML
private void dragDetected(MouseEvent event) {
System.out.println("onDragDetected");
b = new Button(i + "");
i++;
pane.getChildren().add(b);
b.setLayoutX(source.getLayoutX());
b.setLayoutY(source.getLayoutX());
/* drag was detected, start drag-and-drop gesture*/
paneDrop.setOnDragOver(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent k) {
paneDrop.setOnDragDropped(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent k1) {
System.out.println("ondragExited");
pane.getChildren().remove(b);
paneDrop.getChildren().add(b);
b.setLayoutX(k1.getSceneX() - paneDrop.getLayoutX());
b.setLayoutY(k1.getSceneY() - paneDrop.getLayoutY());
b.setOnDragDetected(new EventHandler<MouseEvent>() {
public void handle(MouseEvent t) {
/* drag was detected, start a drag-and-drop gesture*/
/* allow any transfer mode */
paneDrop.setOnDragOver(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent t) {
System.out.println("ondragOver");
paneDrop.setOnDragDropped(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent t1) {
System.out.println("ondragOver");
if (t1.getGestureSource() != pane
&& t1.getDragboard().hasString()) {
t1.acceptTransferModes(TransferMode.COPY_OR_MOVE);
}
t1.consume();
}
});
b.setLayoutX(t.getSceneX());
b.setLayoutY(t.getSceneY() - 224);
if (t.getGestureSource() != pane
&& t.getDragboard().hasString()) {
t.acceptTransferModes(TransferMode.COPY_OR_MOVE);
}
t.consume();
}
});
Dragboard db = b.startDragAndDrop(TransferMode.MOVE);
System.out.println("ttttttttttttt");
/* Put a string on a dragboard */
ClipboardContent content = new ClipboardContent();
content.putString(b.getText());
db.setContent(content);
t.consume();
}
});
k1.consume();
}
});
System.out.println("ondragOver");
b.setLayoutX(k.getSceneX() - pane.getLayoutX());
b.setLayoutY(k.getSceneY() - pane.getLayoutY());
if (k.getGestureSource() != pane
&& k.getDragboard().hasString()) {
k.acceptTransferModes(TransferMode.COPY_OR_MOVE);
}
k.consume();
}
});
/* allow any transfer mode */
Dragboard db = source.startDragAndDrop(TransferMode.MOVE);
/* put a string on dragboard */
ClipboardContent content = new ClipboardContent();
//System.out.println(source.getText());
content.putString(source.getText());
db.setContent(content);
event.consume();
}

Related

Javafx drag-drop does not work in the first time

I just made a simple Application which includes FlowPane and some VBoxs which has a Button.
The Main class is like this.
public class Main extends Application {
#Override
public void start(Stage stage) {
Gallery a = new Gallery();
a.setMaxWidth(200);
a.setPrefWidth(200);
Scene scene = new Scene(a);
stage.setTitle("Welcome to JavaFX!");
stage.setScene(scene);
stage.sizeToScene();
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
and this Gallery.class is the main back ground class which extends FlowPane.
public class Gallery extends FlowPane{
public Gallery() {
super();
PlotterPanel p1 = new PlotterPanel(this, "B1" );
getChildren().add(p1);
PlotterPanel p2 = new PlotterPanel(this, "B2");
getChildren().add(p2);
PlotterPanel p3 = new PlotterPanel(this, "B3" );
getChildren().add(p3);
PlotterPanel p4 = new PlotterPanel(this, "B4" );
getChildren().add(p4);
PlotterPanel p5 = new PlotterPanel(this, "B5" );
getChildren().add(p5);
PlotterPanel p6 = new PlotterPanel(this, "B6" );
getChildren().add(p6);
}
}
And PlotterPanel is the VBox which has Button and can be drag-drop in the Gallery.
public class PlotterPanel extends VBox{
private static final String TAB_DRAG_KEY = "titledpane";
private ObjectProperty<VBox> draggingTab;
private Gallery mgallery;
private PlotterPanel self;
public PlotterPanel(Gallery gallery, String name) {
super();
mgallery = gallery;
setPrefWidth(100);
setPrefHeight(100);
self = this;
Button btn = new Button(name);
btn.setEffect(new DropShadow());
getChildren().add(btn);
draggingTab = new SimpleObjectProperty<VBox>();
setOnDragOver(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent event) {
final Dragboard dragboard = event.getDragboard();
if (dragboard.hasString()
&& TAB_DRAG_KEY.equals(dragboard.getString())
&& draggingTab.get() != null) {
event.acceptTransferModes(TransferMode.MOVE);
event.consume();
}
}
});
setOnDragDropped(new EventHandler<DragEvent>() {
public void handle(final DragEvent event) {
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasString()) {
Pane parent = mgallery;
Object source = event.getGestureSource();
int sourceIndex = parent.getChildren().indexOf(source);
System.out.println(sourceIndex);
int targetIndex = parent.getChildren().indexOf(self);
System.out.println(targetIndex);
List<Node> nodes = new ArrayList<Node>(parent.getChildren());
if (sourceIndex < targetIndex) {
Collections.rotate(
nodes.subList(sourceIndex, targetIndex + 1), -1);
} else {
Collections.rotate(
nodes.subList(targetIndex, sourceIndex + 1), 1);
}
parent.getChildren().clear();
parent.getChildren().addAll(nodes);
success = true;
}
event.setDropCompleted(success);
event.consume();
}
});
setOnDragDetected(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
Dragboard dragboard = self.startDragAndDrop(TransferMode.MOVE);
ClipboardContent clipboardContent = new ClipboardContent();
clipboardContent.putString(TAB_DRAG_KEY);
dragboard.setContent(clipboardContent);
draggingTab.set(self);
event.consume();
}
});
}
}
The problem is that when i drag the PlotterPanel in the Gallery, it can`t be dragged at the first time. And i works after second try.
It shows the dragging box when i start dragging, but when i try to drop on the other Node the Mouse point shows the x sign.
but when the target is already tried Node to be dragged, i can drop on that Node.
How can i make the Drag-drop works well in JavaFX?
The problem is in your condition to accept the drag:
if (dragboard.hasString()
&& TAB_DRAG_KEY.equals(dragboard.getString())
&& draggingTab.get() != null) {
event.acceptTransferModes(TransferMode.MOVE);
event.consume();
}
You are checking the draggingTab of the target, not the source. If the target hasn't been dragged itself, its draggingTab property will still be null. If you have dragged the target already, then it won't be null and it will accept the drag.
Did you mean to make draggingTab static?

How to drag and drop button onto GridPane?

I'm working on a school project creating a simple battleship game and i want to use the Drag And Drop function to drag buttons from HBox on the bottom of the screen to the GridPane that the player uses to place ships on. But i can't get it to work properly. Here's a picture of my board per now.
I have tried to use the button.setOnMouseDragged(e -> {CODE HERE}); but it doesnt work.
Here is the code i used for my window
public class GridOrganizer {
private BorderPane borderPane;
public GridOrganizer() {
borderPane = new BorderPane();
borderPane.setStyle("-fx-background-color: grey;");
borderPane.setPrefHeight(600);
borderPane.setPrefWidth(600);
createGrid();
}
public void createGrid() {
//Creates the grids where the game is played and buttons/ships to place on grid
GridPane playerGrid = new GridPane();
GridPane enemyGrid = new GridPane();
Insets padding = new Insets(10);
//Create playergrid
for (int i = 0; i < 10; i++) {
playerGrid.getColumnConstraints().add(new ColumnConstraints(50)); //50 wide
playerGrid.getRowConstraints().add(new RowConstraints(50));
}
//Create enemygrid
for (int i = 0; i < 10; i++) {
enemyGrid.getColumnConstraints().add(new ColumnConstraints(50)); //50 wide
enemyGrid.getRowConstraints().add(new RowConstraints(50));
}
//looping through row and columns and adds buttons
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
Button button = new Button();
button.setPrefHeight(50);
button.setPrefWidth(50);
GridPane.setConstraints(button, j, i); //(button, column, row)
playerGrid.getChildren().add(button); //add button on each index
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
//HIT or MISS
System.out.println("Row: " + GridPane.getRowIndex(button) + ", Column: " + GridPane.getColumnIndex(button));
button.setStyle("-fx-background-color: grey;");
}
});
}
}
//..same with enemy grid
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
Button button = new Button();
button.setPrefHeight(50);
button.setPrefWidth(50);
GridPane.setConstraints(button, j, i); //(button, column, row)
enemyGrid.getChildren().add(button); //add button on each index
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
System.out.println("Row: " + GridPane.getRowIndex(button) + ", Column: " + GridPane.getColumnIndex(button));
button.setStyle("-fx-background-color: grey;");
}
});
}
}
//Make buttons for the ships
Button rowboat = new Button("Rowboat");
Button sailboat = new Button("Sailboat");
Button submarine = new Button("Submarine");
Button destroyer = new Button("Destroyer");
Button battleship = new Button("Battleship");
//Size the ship buttons to match game description
battleship.setPrefHeight(50);
battleship.setPrefWidth(250); //size 5
destroyer.setPrefHeight(50);
destroyer.setPrefWidth(200); //size 4
submarine.setPrefHeight(50);
submarine.setPrefWidth(150); //size 3
sailboat.setPrefHeight(50);
sailboat.setPrefWidth(150); //size 3
rowboat.setPrefHeight(50);
rowboat.setPrefWidth(100); //size 2
//Drags button
rowboat.setOnMouseDragged(e -> {
//CODE HERE
});
//Drops button on grid
//CODE HERE
sailboat.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
//PLACE SHIP
}
});
submarine.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
//PLACE SHIP
}
});
destroyer.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
//PLACE SHIP
}
});
battleship.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
//PLACE SHIP
}
});
HBox ships = new HBox(); //Horizontal box
ships.getChildren().addAll(rowboat, sailboat, submarine, destroyer, battleship); //Add buttons to horizontal box
//Add grids and ship buttons to pane with padding
borderPane.setLeft(enemyGrid);
BorderPane.setMargin(enemyGrid, padding);
borderPane.setRight(playerGrid);
BorderPane.setMargin(playerGrid, padding);
borderPane.setBottom(ships);
BorderPane.setMargin(ships, padding);
}
public Pane getGrid() {
return borderPane;
}
}
The plan here is to start a drag event with the data needed to create your button when the drag event finished. In your code, it looks like you have added the Buttons into the Grid already. That means you need to only transfer a String to change the Button's text. In my code, I am using a StackPane when creating the Grid. I then create and add the Buttons later. You approach may be better. I have looked that far. I have added an MCVE (Altered code from here):
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
* Demonstrates a drag-and-drop feature.
*/
public class HelloDragAndDrop extends Application
{
#Override
public void start(Stage stage)
{
//Source Buttons.
final Button boat1 = new Button("boat1");
final Button boat2 = new Button("boat2");
final Button boat3 = new Button("boat3");
final Button boat4 = new Button("boat4");
//Adding OnDragDetected to source Buttons.
setOnDragDetected(boat1);
setOnDragDetected(boat2);
setOnDragDetected(boat3);
setOnDragDetected(boat4);
//Adding onDragDone to source Buttons.
setOnDragDone(boat1);
setOnDragDone(boat2);
setOnDragDone(boat3);
setOnDragDone(boat4);
//Creating GridPane
GridPane gridPane = new GridPane();
gridPane.setVgap(5);
gridPane.setHgap(5);
gridPane.setPadding(new Insets(5, 5, 5, 5));
gridPane.setStyle("-fx-background-color: black;");
//Adding StackPane to every Cell in the GridPane and Adding the Target Events to each StackPane.
for (int i = 0; i < 6; i++) {
StackPane stackPane = new StackPane();
stackPane.setPrefSize(150, 50);
stackPane.setStyle("-fx-background-color: yellow;");
setOnDragOver(stackPane);
setOnDragEntered(stackPane);
setOnDragExited(stackPane);
setOnDragDropped(stackPane);
gridPane.add(stackPane, i / 3, i % 3);
}
HBox root = new HBox(new VBox(boat1, boat2, boat3, boat4), gridPane);
stage.setTitle("Hello Drag And Drop");
Scene scene = new Scene(root, 400, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
//source events handlers
public void setOnDragDetected(Button source)
{
source.setOnDragDetected((MouseEvent event) -> {
/* drag was detected, start drag-and-drop gesture*/
System.out.println("onDragDetected");
/* allow any transfer mode */
Dragboard db = source.startDragAndDrop(TransferMode.ANY);
/* put a string on dragboard */
ClipboardContent content = new ClipboardContent();
content.putString(source.getText());
db.setContent(content);
event.consume();
});
}
public void setOnDragDone(Button source)
{
source.setOnDragDone((DragEvent event) -> {
/* the drag-and-drop gesture ended */
System.out.println("onDragDone");
/* if the data was successfully moved, clear it */
// if (event.getTransferMode() == TransferMode.MOVE) {
// source.setText("");
// }
event.consume();
});
}
//target event handlers
public void setOnDragOver(StackPane target)
{
target.setOnDragOver((DragEvent event) -> {
/* data is dragged over the target */
System.out.println("onDragOver");
/* accept it only if it is not dragged from the same node
* and if it has a string data */
if (event.getGestureSource() != target
&& event.getDragboard().hasString()) {
/* allow for both copying and moving, whatever user chooses */
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
}
event.consume();
});
}
public void setOnDragEntered(StackPane target)
{
target.setOnDragEntered((DragEvent event) -> {
/* the drag-and-drop gesture entered the target */
System.out.println("onDragEntered");
/* show to the user that it is an actual gesture target */
if (event.getGestureSource() != target
&& event.getDragboard().hasString()) {
target.setStyle("-fx-background-color: green;");
}
event.consume();
});
}
public void setOnDragExited(StackPane target)
{
target.setOnDragExited((DragEvent event) -> {
/* mouse moved away, remove the graphical cues */
target.setStyle("-fx-background-color: transparent;");
event.consume();
});
}
public void setOnDragDropped(StackPane target)
{
target.setOnDragDropped((DragEvent event) -> {
/* data dropped */
System.out.println("onDragDropped");
/* if there is a string data on dragboard, read it and use it */
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasString()) {
//target.setText(db.getString());
Button tempBoat = new Button(db.getString());
tempBoat.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
target.getChildren().clear();
target.getChildren().add(tempBoat);
success = true;
}
/* let the source know whether the string was successfully
* transferred and used */
event.setDropCompleted(success);
event.consume();
});
}
}
In this code, the Buttons that represent the Boats all have onDragDetected and onDragDone event handlers attached. They are considered Source/start of a Drag event. For every cell in the GridPane, a StackPane is added. These StackPanes are considered the Target/areas to drop drag events. Each StackPane has onDragOver, onDragEntered, onDragDropped, and onDragExited attached. Once a drag event is complete, the StackPane/Target that receives the event gets a new Button as a child with the same name as the Source Button.

JavaFX 8: Unstable interactive TabPane when adding new Tab

I am making a highly interactive TabPane for viewing contact lists in JavaFX 8. For this I have made my own subclass of Tab, EditableTab, which has functionality for changing the name of the tab by double clicking on the name in the overview. When the user clicks the + sign to create a new contact list, I want the program to create a new tab, select it, then focus the name and select all the text - it is natural to name the contact list at once (similar to when you create a new file in windows).
My problem: This seems to be very unstable. Most of the times, it seems some kind of animation/transition problem arises, and the tab name ends up empty. Here is a screenshot of what usually, but not always, happens when the + button is clicked:
And here is what I want:
Here is the code for my EditableTab:
public class EditableTab extends Tab {
private Label lbl;
private TextField txtField;
public EditableTab(String text, Node content) {
super();
setContent(content);
lbl = new Label(text);
txtField = new TextField(text);
txtField.setStyle("-fx-background-color: transparent");
setGraphic(lbl);
setupInteractivity();
}
public TextField getTextField() {
return txtField;
}
private void setupInteractivity() {
lbl.setOnMouseClicked((mouseEvent) -> {
if (mouseEvent.getClickCount() == 2) {
showTextField();
}
});
txtField.setOnAction(event -> setGraphic(lbl));
txtField.focusedProperty().addListener(
(observable, oldValue, newValue) -> {
if (! newValue) {
lbl.setText(txtField.getText());
setGraphic(lbl);
}
});
}
public void showTextField() {
txtField.setPrefWidth(lbl.getWidth());
txtField.setText(lbl.getText());
setGraphic(txtField);
txtField.selectAll();
txtField.requestFocus();
}
}
And here is the code where the functionality is implemented:
private void addNewContactlist() {
Contactlist newList = new Contactlist();
newList.setName("New contact list");
contactlistApp.getContactlistData().add(newList);
ListView<Person> lv = new ListView<Person>(newList.getContacts());
setupListView(lv);
int position = tabPane.getTabs().size() - 1;
EditableTab tab = createEditableTab("New contact list", lv);
tabPane.getTabs().add(position, tab);
tabPane.getSelectionModel().select(tab);
tab.showTextField();
}
I suspect that the problem comes from some animation/transition timings, but that is really just a guess. I tried wrapping the showTextField() call in a Platform.runLater() with no luck.
Here is a small test app to replicate the issue:
public class TestApp extends Application {
TabPane tabPane = new TabPane();
#Override
public void start(Stage primaryStage) throws Exception {
Tab addNewContactlistTab = new Tab();
addNewContactlistTab.setClosable(false);
Label lbl = new Label("\u2795");
lbl.setOnMouseClicked(mouseEvent -> {
if (tabPane.getTabs().size() == 1) {
addNewTab();
}
});
addNewContactlistTab.setGraphic(lbl);
tabPane.getTabs().add(addNewContactlistTab);
addNewContactlistTab.selectedProperty().addListener(
(observable, oldValue, newValue) -> {
if (newValue && tabPane.getTabs().size() != 1) {
addNewTab();
}
});
Scene scene = new Scene(tabPane);
primaryStage.setScene(scene);
primaryStage.setWidth(600);
primaryStage.setHeight(400);
primaryStage.show();
}
private void addNewTab() {
int insertionIndex = tabPane.getTabs().size() - 1;
ListView<String> lv = new ListView<String>();
EditableTab tab = new EditableTab("Unnamed", lv);
tabPane.getTabs().add(insertionIndex, tab);
tabPane.getSelectionModel().select(tab);
tab.showTextField();
}
public static void main(String[] args) {
launch();
}
}
Here is my code for the RenamableTab class:
import javafx.application.Platform;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TextField;
public class RenamableTab extends Tab {
private final Label label;
private final TextField textField;
public RenamableTab() {
this("New Tab", null);
}
public RenamableTab(String text) {
this(text, null);
}
public RenamableTab(String text, Node content) {
super();
label = new Label(text);
textField = new TextField(text);
setContent(content);
textField.setStyle("-fx-background-color: transparent");
setGraphic(label);
label.setOnMouseClicked((mouseEvent) -> {
if (mouseEvent.getClickCount() == 2) {
rename();
}
});
textField.setOnAction(event -> setGraphic(label));
textField.focusedProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue) {
label.setText(textField.getText());
setGraphic(label);
}
});
}
public TextField getTextField() {
return textField;
}
public void rename() {
//textField.setPrefWidth(label.getWidth());
//textField.setText(label.getText());
setGraphic(textField);
new Thread() {
#Override
public void run() {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
Platform.runLater(new Runnable() {
#Override
public void run() {
textField.selectAll();
textField.requestFocus();
}
});
}
}.start();
}
}
And here is my code for the FancyTabPane:
import javafx.event.EventHandler;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.input.MouseEvent;
public class FancyTabPane extends TabPane {
public FancyTabPane() {
Tab newTabTab = new Tab();
newTabTab.setClosable(false);
Label addLabel = new Label("\u2795");
addLabel.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent paramT) {
System.out.println("mouse click");
addTab();
}
});
/*
* getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>() {
* #Override
* public void changed(ObservableValue<? extends Tab> paramObservableValue, Tab paramT1, Tab
* paramT2) {
* System.out.println("model");
* if (paramT1 == newTabTab) {
* System.out.println("tab");
* addTab();
* }
* }
* });
*/
newTabTab.setGraphic(addLabel);
getTabs().add(newTabTab);
}
public void addTab() {
RenamableTab newTab = new RenamableTab();
getTabs().add(getTabs().size() - 1, newTab);
getSelectionModel().select(newTab);
newTab.rename();
}
}
I am having issued with the new tab button when another tab is selected, not sure how you overcame that.

How to add custom SWT context menu for Text box control

I would like to modify the right click context menu for some some SWT Text boxes.
I would like to still have some of the default options like Copy, Cut, Paste, but would also like to have a custom action 'Generate Random' to fill the text box with a UUID.
How can I add such a menu to the control?
here's what I came up with to add some of the standard functions (cut, copy, paste, select all) as well as a custom action (generate UUID)
public static void addContextMenuWithUUID(final Text control)
{
Menu menu = new Menu(control);
MenuItem item = new MenuItem(menu, SWT.PUSH);
item.setText("Cut");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.cut();
}
});
item = new MenuItem(menu, SWT.PUSH);
item.setText("Copy");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.copy();
}
});
item = new MenuItem(menu, SWT.PUSH);
item.setText("Paste");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.paste();
}
});
item = new MenuItem(menu, SWT.PUSH);
item.setText("Select All");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.selectAll();
}
});
item = new MenuItem(menu, SWT.PUSH);
item.setText("Generate UUID");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.setText(UUID.randomUUID().toString());
}
});
control.setMenu(menu);
}
When I had to do something similar a while ago, this is what I did,
I adopted the TextActionHandler class provided by eclipse and modified the code to suit my needs.
public final class TextActionHandler {
enum TextAction {
CUT (WorkbenchMessages.Workbench_cut, IWorkbenchCommandConstants.EDIT_CUT),
COPY (WorkbenchMessages.Workbench_copy, IWorkbenchCommandConstants.EDIT_COPY),
PASTE (WorkbenchMessages.Workbench_paste, IWorkbenchCommandConstants.EDIT_PASTE),
DELETE (WorkbenchMessages.Workbench_delete, null),
SELECT_ALL(WorkbenchMessages.Workbench_selectAll, WorkbenchCommandConstants.EDIT_SELECT_ALL);
private String text;
private String commandId;
private TextAction(String text, String commandId ) {
this.text = text;
this.commandId = commandId;
}
public String getCommandId() {
return commandId;
}
public String getText() {
return text;
}
}
public TextActionHandler(Text text) {
addText(text);
}
public TextActionHandler() {
super();
}
public void addText(Text textControl) {
if (textControl == null) {
return;
}
textControl.addDisposeListener(new DisposeListener() {
#Override
public void widgetDisposed(DisposeEvent e) {
removeText(activeTextControl);
}
});
textControl.addListener(SWT.Activate, textControlListener);
textControl.addListener(SWT.Deactivate, textControlListener);
textControl.addKeyListener(keyAdapter);
textControl.addMouseListener(mouseAdapter);
activeTextControl = textControl;
updateActionsEnableState();
}
public void hookContextMenu() {
final MenuManager menuMgr = new MenuManager("#PMPopupMenu");
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager manager) {
addContextMenuOptions(menuMgr);
}
});
Menu menu = menuMgr.createContextMenu(activeTextControl);
activeTextControl.setMenu(menu);
}
private void addContextMenuOptions(MenuManager manager) {
manager.removeAll();
manager.add(textCutAction);
manager.add(textCopyAction);
manager.add(textPasteAction);
manager.add(textDeleteAction);
manager.add(new Separator());
manager.add(textSelectAllAction);
// add your own action handlers here
}
...
// example.
private final class CutActionHandler extends Action {
private CutActionHandler() {
setProperties(this, TextAction.CUT);
setEnabled(false);
}
#Override
public void runWithEvent(Event event) {
if (activeTextControl != null && !activeTextControl.isDisposed()) {
activeTextControl.cut();
updateActionsEnableState();
}
}
#Override
public boolean isEnabled() {
return activeTextControl != null && !activeTextControl.isDisposed()
&& activeTextControl.getEditable()
&& activeTextControl.getSelectionCount() > 0;
}
public void updateEnabledState() {
setEnabled(isEnabled());
}
}
private void setProperties(Action action, TextAction actionEnum){
action.setText(actionEnum.getText());
action.setActionDefinitionId(actionEnum.getCommandId());
action.setImageDescriptor(getImageDescriptor(actionEnum));
action.setDisabledImageDescriptor(getDisabledImageDescriptor(actionEnum));
}
}
Likewise, you can have your own ActionHandlers added. e.g, RandomGeneratorHandler.
To hook this to your textboxes, do
Text text = new Text(parent, SWT.NONE);
...
TextActionHandler handler = new TextActionHandler();
handler.addText(text);
handler.hookContextMenu();
Note - I have not provided the complete class here, for other actions like copy, paste, delete and select all etc, you will have to do something similar as Cut. I have used the same code defined in the TextActionHandler class.

JavaFX ListView & ContextMenu - getSelectedItem() returns null

I want to use a context menu item on the lines of a listView. In the event handler of the listView's MOUSE_CLICKED event, the getSelectionModel().getSelectedItem() returns the selected item, thats ok. But, when I handle the contextMenuItem's onAction event, it returns null. However, graphically the item is selected.
Is there a way to "keep" the selection after the first event handling?
Here is the relevant part of the code:
ListView<Text> nameList = new ListView<>();
final ContextMenu cCm = new ContextMenu();
MenuItem cItem = new MenuItem("someText");
cCm.getItems().add(cItem);
...
nameList.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
if (e.getButton() == MouseButton.SECONDARY) {
//its OK here:
System.out.println(nameList.getSelectionModel().getSelectedItem().getText());
cCm.show(nameList, e.getScreenX(), e.getScreenY());
}
}
});
cItem.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
final Stage dialog = new Stage();
dialog.initModality(Modality.WINDOW_MODAL);
//nullPointerException on the following:
Text t = new Text(nameList.getSelectionModel().getSelectedItem().getText());
//showing dialog, etc.
I pretty much created an exact replica of what you did, and my implementation worked:
private void initRandomCardListView() {
populateRandomList();
final ContextMenu randomListContextMenu = new ContextMenu();
MenuItem replaceCardMenuItem = new MenuItem("Replace");
replaceCardMenuItem.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
replaceRandomCard();
}
});
randomListContextMenu.getItems().add(replaceCardMenuItem);
randomCardList.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getButton().equals(MouseButton.SECONDARY)) {
randomListContextMenu.show(randomCardList, event.getScreenX(), event.getScreenY());
}
}
});
}
private void replaceRandomCard() {
System.out.println("jobs done");
System.out.println("card selected: " + randomCardList.selectionModelProperty().get().getSelectedItem().toString());
System.out.println("card index: " + randomCardList.getSelectionModel().getSelectedIndex());
System.out.println("card index: " + randomCardList.getSelectionModel().getSelectedItem().toString());
}
I don't have any null pointer exceptions. Overall your implementation looks good. There is most likely something that is wrong with the items in your listview.

Categories