So I have two ways of exiting my applications, with the buttons I created and with the keybindings I created.
The problems I am having are with minimize. For the button, I click it on initial launch and it doesnt work. The second time I click it, it will then work. Here is the code for the button.
#FXML
public void minimizeClick() {
minimizeButton.setOnAction(e ->
( (Stage) ( (Button) e.getSource() ).getScene().getWindow() ).setIconified(true)
);
}
Now for the keybindings, it will work the first time when I press CMD+M on Mac OS. When I bring up the application again in the same session, it will stutter. So I then have to use the key-bind combination twice for it to execute the action. Here is the code:
scene.setOnKeyPressed(e -> {
if(e.getCode() == KeyCode.COMMAND) {
detW = true;
detM = true;
}
if(detW && e.getCode() == KeyCode.W) {
System.exit(0);
detW = false;
}
else if(detM && e.getCode() == KeyCode.M) {
primaryStage.setIconified(true);
detM = false;
}
});
The 3rd condition handles the minimizing. The method handles exiting program as well but obviously I can only exit the program once, so far at least. In the future I will have termination be mapped to CMD+Q instead of CMD+W.
Why is this not working?
You didn't post your FXML file, but I'm going to assume you have something like
<Button text="..." fx:id="minimizeButton" onAction="#minimizeClick" />
in it.
Buttons have a property called onAction, of type EventHandler<ActionEvent>. If the button is clicked (or otherwise fired, e.g. via the keyboard), then if onAction is not null, it is executed.
The onAction="#minimizeClick" sets the onAction property to an EventHandler which invokes the minimizeClick() method defined in the controller.
So you can think of all this as "when the button is clicked, minimizeClick() is invoked in the controller".
Your minimizeClick() method is this:
#FXML
public void minimizeClick() {
minimizeButton.setOnAction(e ->
( (Stage) ( (Button) e.getSource() ).getScene().getWindow() ).setIconified(true)
);
}
What this method does, is set the button's onAction property to a new handler; i.e. it replaces the current onAction handler with a new handler (which minimizes the window).
So the first time the button is clicked, minimizeClick() is invoked. That replaces the current onAction handler (invoke minimizeClick()) with a new handler.
The second (and any subsequent) time the button is clicked, the newly-installed onAction handler is invoked, which minimizes the window.
Consequently the behavior you actually see is that nothing happens on the first button click, but on the second button click the window is minimized.
If you were not using FXML (Java only), then your code would be equivalent to
minimizeButton.setOnAction(e1 -> {
minimizeButton.setOnAction(e2 -> {
((Stage)((Button)e2.getSource()).getScene().getWindow()).setIconified(true);
});
});
Obviously, you just want a simple handler that minimizes the button, i.e. you want the minimizeClick() method to minimize the window. So you need
#FXML
public void minimizeClick() {
( (Stage) ( (Button) e.getSource() ).getScene().getWindow() ).setIconified(true);
}
I don't understand what you mean by "when I bring up the application again it will stutter", but it sounds like that is unrelated to the code you posted. For your key event handlers, you should use KeyEvent.isShortcutDown() etc to see if the cmd key is pressed when the key of interest is pressed, instead of trying to keep track of it yourself. I.e. try
scene.setOnKeyPressed(e -> {
if(e.getCode() == KeyCode.W && e.isShortcutDown()) {
System.exit(0);
// Aside: you should really use Platform.exit() instead of System.exit(0)
// as it will gracefully shutdown the FX toolkit and ensure your
// Application's stop() method is called, etc.
} else if(e.getCode() == KeyCode.M && e.isShortcutDown()) {
primaryStage.setIconified(true);
}
});
Note that the default (native OS) behavior of cmd-M on a Mac is to minimize the window, so this may be confounding the behavior you observe. (I.e. I think on a Mac this behavior may occur without any of these key handlers.)
Related
If you have ever used Window.alert("msg"); API in GWT to show popup, I am not sure but the call to this API pauses the code execution until a user action is taken (cliking the ok button), Simillar to that i have created a custom popup, when it is shown i don't want the code to execute further till any user input in received on the popup, How can i pause the code execution further?
Assume :-
//Some Code
MY Popup (Here i want to wait till a user action is received.)
//Some code
I read somewhere to use Synchronized key word but that didn't work either,Do you have answer to this. How GWT compiler sees "Synchronized" keyword does it ignores the keyword?
Create something like a ConfirmCallBack that you fire when the "OK" button (or whatever) is clicked in the popuppanel.
//method in your own popup class
public static void confirm(String message, ConfirmCallBack confirmCallBack)
{
Button confirmButton = new Button(confirmButtonText, event ->
{
confirmCallBack.callback(true);
//hide popup
});
}
Than also have the ConfirmCallBack interface like
public interface ConfirmCallBack
{
void callback(boolean result);
}
Then call your own popup like
MyPopup.confirm("Hello world", result ->
{
if (result)
{
//my code to be executed after clicking the ok button
}
}
public JoinChatClient(String serverAddress, String chatName)
{
chatWindow.getContentPane().add(sendButton, "South");
chatWindow.getContentPane().add(splitPane, "Center");
chatWindow.setSize(800,500);
sendButton.addActionListener(this);
chatWindow.setTitle("Chat Room");
chatWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
splitPane.setDividerLocation(350);
sendButton.setBackground(Color.gray);
sendButton.setForeground(Color.red);
outChatTextArea.setEditable(false);
inChatTextArea.setFont (new Font("default",Font.ITALIC,20));
outChatTextArea.setFont(new Font("default",Font.BOLD,20));
inChatTextArea.setLineWrap(true);
outChatTextArea.setLineWrap(true);
inChatTextArea.setWrapStyleWord(true);
outChatTextArea.setWrapStyleWord(true);
inChatTextArea.setText("Enter text to be sent here.");
outChatTextArea.setText("You can move the separator bar!");
inChatTextArea.addFocusListener(new FocusListener() {
public void focusGained(FocusEvent e) {
if(inChatTextArea.getText().equals("Enter text to be sent here."))
{
inChatTextArea.setText("");
inChatTextArea.setFont(new Font("default",Font.BOLD,20));
}
}
public void focusLost(FocusEvent e) {
if(inChatTextArea.getText().isEmpty())
{
inChatTextArea.setFont (new Font("default",Font.ITALIC,20));
inChatTextArea.setText("Enter text to be sent here.");
}
}
});
chatWindow.getRootPane().setDefaultButton(sendButton);
chatWindow.setVisible(true);
}
I've looked over all the threads I could find concerning this, and I cannot figure out why hitting ENTER doesn't activate the actionPerformed method attached to sendButton. Is it because the text field has a FocusListener?
Things I've tried:
changing the statement to target the specific text field (inChatTextArea)
moved the setVisible statement to the end
targeted different parts of the GUI when hitting enter
Bear in mind I've only included the code that builds the GUI in an attempt to waste less of your time.
What I want: Ideally, I want to keep my FocusListener (or something like it) so that I can display the "text field hint." I would like to be able to hit ENTER to send the user's text while the inChatTextArea field is focused.
If a component on the JFrame has focus, and can accept an enter key press, such as one of the JTextAreas, then the enter presses will go to that component and not to the default button. For the default button to work, then the JFrame or the button or some other component that does not accept enter key presses, needs to have focus. I'm guessing that one of your JTextAreas has stolen the focus, and that this is messing you up.
This question is old, but I found it when having the same issue. So I hope others might find it useful.
I figured out that getRootPane() will return null if the component is trying to access the root pane too early, e.g. under construction of the component.
Hence, I propose to use SwingUtilities.invoke(Runnable) to postpone setting the default button on the root pane, and also to request the focus to the button.
So this method could be a helper method on a class to extend from:
protected void setDefaultButton(JButton button) {
// Uses invoke later, as getRootPane() might return null if the method is called under construction
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JRootPane rootPane = getRootPane();
if (rootPane != null) {
rootPane.setDefaultButton(button);
}
button.requestFocus(); // set the focus on the button
}
});
}
I am at an SWT application where one can rearrange controls within a shell (or any Composite for that matter) via drag&drop. That's basically no problem, DragSources and DropTargets are all in place and listeners attached accordingly. I even implemented a custom Transfer type for the sake of exercise. Pretty straightforward.
But now the requirement is that a drag should only be initiated, if the ALT key is pressed while the drag gesture is performed, otherwise nothing should be done. (The ALT key is an example, could be CTRL as well.)
So far, I see or have thought about the following approaches. All of them either don't work or are ugly.
\1. Intercept and cancel the DragDetect event
The idea is to cancel the event if the ALT key is not pressed with event.doit = false.
lblPos.addListener(SWT.DragDetect, new Listener() {
public #Override void handleEvent(Event event) {
if ((event.stateMask & SWT.ALT) == 0)
event.doit = false; // XXX: doit will not be evaluated
}
});
However, that doesn't work. The doit flag is apparently not evaluated.
\2. Intercept and cancel the DND.DragStart event.
class RowDragListener implements DragSourceListener {
public #Override void dragStart(DragSourceEvent event) {
if (/* ALT key not pressed */)
event.doit = false;
}
...
}
This has the opposite problem of appraoch 1. While the doit flag is properly evaluated and thus suitable to cancel the drag, there is no stateMask in the event that can be inspected for modifier keys. So the question arises, how can I query the keyboard directly (without installing KeyUp/Down event handlers)? What is the current up/down state of the ALT key?
\3. Combine 1 and 2
Inspect the stateMask in the DragDetect event, store the result somewhere, then react accordingly in the DND.DragStart event. This shouldn't be too hard, but I think it's ugly and should not be done this way. Instead of DragDetect, KeyUp/Down events could be captured and the last known state of the ALT key be stored.
\4. Override Control.dragDetect(Event) or Control.dragDetect(MouseEvent)
These methods ultimately create DragDetect events if they see the conditions for it fulfilled.
Check the event's stateMask and invoke the overridden method from the super class only if the desired modifier key is signalled. Problem here is, from the documentation it is not clear if this is the only code path that is treaded upon a drag gesture. In fact, these two methods are independent from each other (they don't invoke each other), so it's not even clear which one to override. These methods already are two separate ways to initiate a drag gesture. Who knows how many more ways are there? Overriding them all would be error prone, if possible at all, and certainly not clean.
So my questions are:
1. How would you do that? Any other ideas?
2. If approach 2 seems the most reasonable, how is the keyboard queried without resorting to event handlers?
(Sorry for the formatting of this post, i seem to be unable to grasp the syntax. Or maybe it's not my fault, who knows.)
UPDATE: There's one thing to note, which i noticed during the implementation. On Windows, ALT-Drag&Drop has the specific meaning of a link operation (as opposed to move or copy; cmp. DND.DROP_* constants). That's why, if you choose to use the ALT key in a similar fashion, be advised to include the following line at every reasonable occasion in the DropTargetListener.
if (event.detail == DND.DROP_LINK) event.detail = DND.DROP_MOVE;
I have this in the dragEnter, dragOver and dragOperationChanged listener methods and this works quite fine.
You can listen to SWT.DragDetect event, check the state mask and create the drag source only if conditions are met. Then pass the event to the newly created drag source by calling notifyListeneres(). After drag finishes the drag source has to be disposed.
Here is a snippet where drag is initiated only if alt is pressed, and uses text as transfer:
public static void main(String[] args) {
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setLayout(new GridLayout());
shell.addListener(SWT.DragDetect, new Listener() {
#Override
public void handleEvent(Event event) {
if ((event.stateMask & SWT.ALT) != 0) {
final DragSource dragSource = new DragSource(shell, DND.DROP_MOVE);
dragSource.addDragListener(new DragSourceAdapter(){
#Override
public void dragFinished(DragSourceEvent event) {
dragSource.dispose();
}
#Override
public void dragSetData(DragSourceEvent event) {
event.data = "text";
}
});
dragSource.setTransfer(new Transfer[]{TextTransfer.getInstance()});
dragSource.notifyListeners(SWT.DragDetect, event);
}
}
});
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
I have this code:
DOM.setEventListener(row.getElement(), new ClickListener(){
#Override
public void onClick(Widget sender) {
// TODO Auto-generated method stub
}});
I think the code is fine and ClickListener extends EventListener, but it gives error saying: The method setEventListener(Element, EventListener) in the type DOM is not applicable for the arguments (Element, new ClickListener(){})
The real answer is that you probably don't. While this is available to attach listeners to events, you may only attach a single listener per element - that listen then gets all dom events that have been configured (see DOM.sinkEvents) - and you are responsible for making sure to detach all listeners before the page unloads, else some browsers will leak memory.
Instead, strongly consider using a Widget (and subclasses) to manage events. RootPanel, the base widget that others should be added to, will manage detaching all other widgets from the page to prevent memory leaks.
Additionally, you are able to listen to the events that happen within there based on the kind of event you are after. For example, even on a widget like a Label that doesnt' normally fire mouseover events, you can still attach handlers and get notification:
Label label = new Label();
label.addDomHandler(new MouseOverHandler() {
#Override
public void onMouseOver(MouseOverEvent event) {
// do something
}
}, MouseOverEvent.getType());
RootPanel.get().add(label);
In most cases, you'll be using existing support methods, like Button to get a click event - there are convinience methods already there for you, thanks to interfaces like HasClickHandlers:
Button button = new Button();
button.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
// do something
}
});
panel.add(button);
More on GWT, Widget, and Memory leaks:
https://developers.google.com/web-toolkit/articles/dom_events_memory_leaks_and_you
http://code.google.com/p/google-web-toolkit/wiki/UnderstandingMemoryLeaks
I'm writing an applet and want to figure out how to make a button and a key event cover the same bit of code. For this question, I'll call this button fireButton. The code for the action event would of course look like this:
public void actionPerformed(ActionEvent e) {
if (e.getSource() == fireButton) {
//all the code that pressing button executes
}
}
Now, I want pressing the 'enter' key to perform the same code that the action event handles, but I do not want to rewrite all the code again in a keyPressed method.
To be specific, I'm writing a battleship program, and the 'Fire' button takes input from two textFields, handles exceptions, and passes the input as parameters to a method that fires at a particular square in the grid. Ideally, pressing the enter key would function the same way as if I had pressed the fire button. Is there a way to make a certain method call an actionPerformed method? If not, what would be an elegant solution to the problem?
Create an Action
Add the Action to the JButton
Use Key Bindings to bind the Enter key to the Action
Read the Swing tutorial. There are sections on:
How to Use Actions
How to Use Key Bindings
If you are just talking about invoking the "Fire" button with the enter key then check out Enter Key and Button for a couple of approaches.
I suggest you put all the code in a separate method that receives all the relevant data from the event (if any) as parameters:
public void actionPerformed(ActionEvent e) {
if (e.getSource() == fireButton) {
Object relevantData0 = new Object(); // e.getSomething();
Object relevantData1 = new Object(); // e.getSomethingElse();
handleFireAction(relevantData1, relevantData2);
}
}
public void actionPerformed(KeyEvent e) {
if (e.getSource() == fireButton) {
Object relevantData0 = new Object(); // e.getSomething();
Object relevantData1 = new Object(); // e.getSomethingElse();
handleFireAction(relevantData1, relevantData2);
}
}
private void handleFireAction(Object relevantData0, Object relevantData1) { // Object relevantDat2, and so on
//all the code that pressing button executes
}
If you don't need any data from the event its even easier ;)
This way you just write your code once for both events. It's a general OO aproach.
Hope this helps.
Borrowing from MVC I would recommend you have a controller class which handles these sorts of requests. Then all you have to do is delegate to the controller in each event handler.
Like so:
public class BattleShipController {
public void handleFireAction() {
// ...
}
}
//-- in your UI class(es)
private BattleShipController _controller = new BattleShipController();
//-- in event calls:
_controller.handleFireAction();
If you post relevant code I can make further suggestions.