Suppose I want to have my program to react same way, say, navigate to next record, in response to different events, including pressing a key, clicking GUI button, selecting menu item and so on.
This was done with "actions" in Swing.
Can I materialize this concept in some program object in JavaFX?
Or I should make a porridge of interacting objects?
Action is still there in JavaFX. Example belows how to create an action, bind it to a keyboard shortcut and share between two different elements.
Button go = new Button("Go");
EventHandler<ActionEvent> goAction = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
browser.load(location.getText(), new Runnable() {
#Override
public void run() {
System.out.println("---------------");
System.out.println(browser.getHTML());
}
});
}
};
...
MenuItem menuItem = new MenuItem("Go!");
menuItem.setAccelerator(new KeyCodeCombination(KeyCode.G, KeyCombination.CONTROL_DOWN));
go.setOnAction(goAction);
menuItem.setOnAction(goAction);
JavaFX provides many events. You also do this with setOn() method:
button.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
// code here
}
});
Related
Scenario. I have a graph on which i can perform panning using right click. This works perfectly. Then I added menus on right click perfectly.
Problem. Now right click menus are shown even on mouse release after drag operation completions.
Is there a way to differentiate mouse release and mouse release after mouse drag in Java Swing or JavaFX?
Mouse events are generated independently of one another.
I assume you panning code works with a combination of mousePressed/mouseMoved.
So you need to add some logic to indicate that you are in "panning mode". So if you have a mousePressed followed by a mouseMoved you set a Boolean variable to indicate "panning" mode.
Then in the mouseReleased code you need to check the variable. If "panning mode" then set "panning mode" off and return. Otherwise you are in "popup mode" so you can display the popup.
Since event.isDragDetect() is always true, so I was not able to differentiate events. I created a java class to store a boolean value. This was needed to modify final object state in inner class and was not possible using Boolean wrapper class. Later I am modifying final objects states based on mouse clicked and mouse dragged. I am also checking if mouse release is after drag or without drag as below:-
private void addRightClickMenu() {
final SubMarineBooleanUtilityClass showMenu = new SubMarineBooleanUtilityClass(true);
final MenuItem graphButton1 = new MenuItem("Save Graph as..");
graphButton1.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
saveGraphAs();
}
});
final MenuItem graphButton2 = new MenuItem("Reset Graph");
graphButton2.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
resetGraph(controlGraph1.getLineChart(), controlGraph2.getLineChart());
}
});
final ContextMenu menu = new ContextMenu(graphButton1, graphButton2);
//Mouse Drag operation cycle=Mouse click+Mouse dragged+Mouse release
getAnchorPaneGraphView().setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (MouseButton.SECONDARY.equals(event.getButton())) {
showMenu.setValueBoolean(true);
}
}
});
getAnchorPaneGraphView().setOnMouseReleased(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (MouseButton.SECONDARY.equals(event.getButton()) && showMenu.isValueBoolean()) {
menu.show(getAnchorPaneGraphView(), event.getScreenX(), event.getScreenY());
}
}
});
getAnchorPaneGraphView().setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (MouseButton.SECONDARY.equals(event.getButton())) {
showMenu.setValueBoolean(false);
}
}
});
}
public class SubMarineBooleanUtilityClass {
boolean valueBoolean=false;
/**
* #return boolean
*/
public boolean isValueBoolean() {
return valueBoolean;
}
/**
* Constructor passing a boolean values
* #param value
*/
public SubMarineBooleanUtilityClass(boolean value){
this.valueBoolean=value;
}
/**set boolean value
* #param value
*/
public void setValueBoolean(boolean valueBoolean) {
this.valueBoolean = valueBoolean;
}
}
I have created an SWT text editor and also have implemented the cut, copy and paste features but now I need to implement CTRL + BACKSPACE, to delete the preceding word, and CTRL + DEL, to delete the proceeding word.
The code which copied text
private class Copy implements SelectionListener{
public void widgetSelected(SelectionEvent event) {
Display.getDefault().asyncExec(new Runnable() {
#Override
public void run() {
copySelectedMessages();
}
private void copySelectedMessages(){
//StringBuffer stringCopied =new StringBuffer();
String textData = editor.getSelectionText();
//TextTransfer textTransfer = TextTransfer.getInstance();
System.out.println("you hv selected"+textData);
//Clipboard clipboard = new Clipboard(Display.getDefault());
TextTransfer transfer = TextTransfer.getInstance();
clipboard.setContents(new Object[] { textData }, new TextTransfer[] { transfer });
}
});
}
}
The code for the editor
editor = new StyledText( this, SWT.MULTI | SWT.V_SCROLL );
editor.setLayoutData( new GridData(GridData.FILL_BOTH) );
editor.setFont( new Font(Display.getDefault(),"Cambria", 10, SWT.NORMAL) );
The listener
proceeding.addSelectionListener(new proceed());
private class proceed implements SelectionListener{
public void widgetSelected(SelectionEvent event) {
Display.getDefault().asyncExec(new Runnable() {
#Override
public void run() {
// Code to check for CTRL + backspace and CTRL + delete
}
});
}
So now how can CTRL+BACKSPACE and CTRL+ DELETEfunctionality can be implemented in SWT.
addSelectionListener() is the wrong method to use here; use addKeyListener() and then a KeyAdapter to process the events.
java2s has an example: http://www.java2s.com/Code/JavaAPI/org.eclipse.swt.custom/StyledTextaddKeyListenerKeyListenerkey.htm
or here for a more complete one: http://www.java2s.com/Tutorial/Java/0280__SWT/Asimpleeditor.htm
Your code to implement "copy to clipboard" is not necessary since StyledText already implements this. Just call the copy() method. The second example shows how to install a global listener for Ctrl+C via a menu item.
As for how to delete the next or previous word, call st.invokeAction() with ST.DELETE_WORD_NEXT or ST.DELETE_WORD_PREVIOUS (org.eclipse.swt.custom.ST).
By default, those actions are bound to Ctrl+BackSpace and Ctrl+Delete. But again, those keys only trigger the action when the widget has the focus. If you want to enable them no matter which widget in the window has the focus, then create a menu item.
I have a context menu which activated on right click of a node in my JavaFX application. Originally I just had one menu item, but I decided to add another. When I added another, the second menu item worked but the first menu item stopped working. Here was my code:
final ContextMenu contextMenu = new ContextMenu();
MenuItem delete = new MenuItem("Delete");
MenuItem hyperlink = new MenuItem("Hyperlink...");
contextMenu.getItems().addAll(delete, hyperlink);
//handles deletion
webView.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e){
if (e.isSecondaryButtonDown()){
contextMenu.show(workspace, e.getScreenX(), e.getScreenY());
delete.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
//do stuff
}
});
}
}
});
//handles hyperlink
webView.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e){
if (e.isSecondaryButtonDown()){
contextMenu.show(workspace, e.getScreenX(), e.getScreenY());
hyperlink.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
//do stuff
}
});
}
}
});
}
I assumed this was because the listeners were overriding each other, so I changed it to add event handlers. Here's how I changed the code:
final ContextMenu contextMenu = new ContextMenu();
MenuItem delete = new MenuItem("Delete");
MenuItem hyperlink = new MenuItem("Hyperlink...");
contextMenu.getItems().addAll(delete, hyperlink);
//handles deletion
webView.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e){
if (e.isSecondaryButtonDown()){
contextMenu.show(workspace, e.getScreenX(), e.getScreenY());
delete.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
//do stuff
}
});
}
}
});
//handles hyperlink
webView.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e){
if (e.isSecondaryButtonDown()){
contextMenu.show(workspace, e.getScreenX(), e.getScreenY());
hyperlink.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
//do stuff
}
});
}
}
});
Now, NOTHING is happening when I right-click the node. Can someone explain why, and what my solution might be?
There is a number of things:
Yes, in your first version, the handlers were overriding each other.
Direct translation to addEventHandler style would use event type MouseEvent.MOUSE_PRESSED, not MOUSE_CLICKED, since MOUSE_CLICKED occurs only after the mouse button has been released, thus the isSecondaryButtonDown() test always returns false—that's why nothing is happening.
Instead of detecting mouse presses, you can listen to CONTEXT_MENU_REQUESTED events:
webView.setOnContextMenuRequested(new EventHandler<ContextMenuEvent>() {
#Override
public void handle(ContextMenuEvent e) {
contextMenu.show(workspace, e.getScreenX(), e.getScreenY());
}
}
You should set the menu item's action right after its creation, not on each right click:
MenuItem delete = new MenuItem("Delete");
delete.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
//do stuff
}
});
The only thing you need to do on right click is show the context menu, as shown above.
Why are you duplicating code. Just follow your first approach and do both things inside the same EventHandler.
One more note: Define your ActionEvents for the MenuItems outside of the webView handlers. What you are doing now is every time you click a menuItem you are resetting the ActionEvents. From your code, I can only see that you need to detect the MouseEvent just to show the Context Menu. Which can be done in one webView.setOnMousePressed(...).
I have many ItemWidget which extends a Composite. If a click event is received on one of the items a change event should be fired and other item widgets should receive this event.
It tried the following:
public class ItemWidget extends Composite implements HasChangeHandlers {
FocusPanel focusPanel = new FocusPanel();
public ItemWidget() {
Label label = new Label("click me");
focusPanel.add(label);
initWidget(focusPanel);
focusPanel.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
// inform other items
fireChange();
}
});
addChangeHandler(new ChangeHandler() {
#Override
public void onChange(ChangeEvent event) {
GWT.log("ChangeEvent received");
}
});
}
private void fireChange() {
GWT.log("fire event");
NativeEvent nativeEvent = Document.get().createChangeEvent();
ChangeEvent.fireNativeEvent(nativeEvent, this);
}
#Override
public HandlerRegistration addChangeHandler(ChangeHandler handler) {
return addDomHandler(handler, ChangeEvent.getType());
}
}
Using the above code only the item which is clicked receives the ChangeEvent.
How can I receive the ChangeEvent on all the other item widgets too?
Typically, when an event fires, a presenter/Activity makes the necessary changes to the other widgets.
If you want multiple copies of you widget to listen to the same event, you may be better off using the EventBus, especially if you use this pattern more than once:
How to use the GWT EventBus
Then each of your ItemWidget can fire a custom event that all copies of this widget listen to.
By looking at the API quickly I think your problem comes from your implementation of
public void fireEvent(GwtEvent<?> event)
You did not override it in your composite view. You did not create any mechanism to send the event to the other widgets.
Have a deeper look at the method fireNativeEvent
http://www.gwtproject.org/javadoc/latest/com/google/gwt/event/dom/client/DomEvent.html
When you use ChangeEvent.fireNativeEvent the second param hasHandlers should be a kind of event bus.
To do what you want to do I think you need all the items to be on this eventBus.
When I create my TableViewer in my dialog class. I am adding a ListChangeListener. This listener listens to a ObservableList in my Data Model Class.
This is my createTableViewer method in the dialog class.
private void createTableViewer(Composite parent) {
viewer = new AplotDataTableViewer(parent, SWT.BORDER|SWT.V_SCROLL|SWT.FULL_SELECTION);
IObservableList iob = AplotDataModel.getInstance().getObservableList();
viewer.setInput(iob);
iob.addListChangeListener(new IListChangeListener() {
#Override
public void handleListChange(ListChangeEvent event) {
updateTableViewer();
}
});
}
So what is happening. When a user Closes the dialog using the Window Close Button (Red X).
That is disposing all the widgets and closing the window. When the Dialog is opened back.
The ListChangeListener is looking to the updateTableViewer, but the widgets in the update is already disposed.
Right now there are 2 ways to close the dialog.
1. Red X - maybe doing a Window.close()
2. My close button on the form.
#Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.OK_ID, "Close Aplot",
true);
}
#Override
protected void okPressed() {
getShell().setVisible(false);
}
Which is using okPressed and hiding the shell.
What I would like is to have both methods of closing the dialog the same.
Is it possible to add a listener to the Shell and in the handleEvent method. Have a call to the okPressed method?
getShell().addListener(SWT.Close, new Listener() {
#Override
public void handleEvent(Event e) {
okPressed();
}
});
Instead of SWT.Close should I be using Window.Close?
Should I be using Close_ID instead of ok_ID
#Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.CLOSE_ID, "Close Aplot",
true);
}
#Override
protected void closePressed() {
getShell().setVisible(false);
}
Is there a way to get my active ListChangeListener and remove it?
protected void closePressed() {
AplotDataModel.getInstance().getObservableList().removeListChangeListener(this);
}
I am not sure how to get active listeners?
I want to Close the Dialog either using the Windows Close Button (Red X) or the Close Button on the form. If possible I wish both actions would use the same code to remove the active Listener from my IObservableList and close the dialog.
Have you tried adding a DisposeListener to the window? The dispose listener can then unregister any event listeners you set on its controls. This will happen regardless of how the window is closed, either from the red X or by calling shell.close() in the okPressed() method.
For example:
private void createTableViewer(Composite parent) {
viewer = new AplotDataTableViewer(parent, SWT.BORDER|SWT.V_SCROLL|SWT.FULL_SELECTION);
final IObservableList iob = AplotDataModel.getInstance().getObservableList();
viewer.setInput(iob);
final IListChangeListener listener = new IListChangeListener() {
#Override
public void handleListChange(ListChangeEvent event) {
updateTableViewer();
}
};
iob.addListChangeListener(listener);
getShell().addDisposeListener(
new DisposeListener() {
#Override public void widgetDisposed(DisposeEvent e) {
iob.removeListChangeListener(listener);
}
});
}