How can I run a piece of code (or more exactly: close the stage) when the JavaFX stage lost it's focus?
For example in Dropbox or Chrome: if you click the tray icon, a small window opens. If you click anywhere on the screen now, the window closes. Exactly this is the behaviour I want to create in my JavaFX application.
I searched a long time already for a solution, but couldn't find one...
So, I'm looking for something something like this:
stage.addEventHandler(EventType.FOCUS_LOST, new EventHandler() { /*...*/ } );
Thank you for helping me out!
Add a listener to stage.focusedProperty().
primaryStage.focusedProperty().addListener(new ChangeListener<Boolean>()
{
#Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean onHidden, Boolean onShown)
{
<Your code here>
}
});
Related
I'm creating a simple Calculator program using Java, Swing.
The keybindings work fine. Well... almost. I ran the software, pressed the number buttons and everything went well, as it should've. Then, I pressed some buttons using my mouse and still, everything is just fine up to this point.
The problem comes when, after pressing the buttons with my mouse the keybindings stop working.
Here's the code for pressing the number 0 (the code for the rest of the buttons are implemented in the same way).
actions[0] = new press0Action();
frame.getRootPane().getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD0, 0), "0");
frame.getRootPane().getRootPane().getActionMap().put("0", actions[0]);
private class press0Action extends AbstractAction {
#Override
public void actionPerformed(ActionEvent e) {
buttons[0].doClick();
}
}
private void buttonPressed0() {
buttons[0].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
//Code goes here for pressin the button...
});
}
So why can't I use the keyboard after clicking on the buttons in the
GUI manually with a mouse?
How can I fix this problem?
Thank you for answering in advance! Feel free to add any suggestions for improvement.
P.S.: I've got a feeling it's something to do with the fact that I bound the keys to frame.getRootPane()
You registered your inputs for the root pane, and with no explicit condition. Instead try registering them for the entire window:
actions[0] = new press0Action();
frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD0, 0), "0");
frame.getRootPane().getRootPane().getActionMap().put("0", actions[0]);
Note the alternate getInputMap with argument.
I am showing a Keyboard if the user clicks into a my extended TextField with the code shown below. When scrolling with a mouse you don't loose the focus to the TextField, but when scrolling by touch the focus is lost - and keyboard dispears of course. Is there a way to get the same behavior on touchScroll as on mouseScroll? I don't want the keyboard to disapear if the user is scrolling with touch!
focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(final ObservableValue<? extends Boolean> observable, final Boolean oldValue,
final Boolean newValue) {
KeyboardUtils.INSTANCE.setVisible(newValue);
}
});
This is pretty basic sir, when you are scrolling with a Touchscroll you definitely Touch a scrollable Pane area, and that Pane requestFocus()by the touch, so your TextField will loose its focus.
so to solve it you send focus back to your TextField if you detect a touch either by using the Scrolling listener of that Pane or Node or go for setOnTouchStationary() or setOnTouchReleased(), to help tweak the visibility of your keyboard instead of lying on focus of your TextField.
EDIT
Try this
Node lastFocusedNode =null; // lastly known node to have focus
//now every node or child in your ScrollPane or Scrollable parent
//that you care about will have a focusable listener-including
// your textfield
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(
ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
if(!newValue){//if they loose focus
lastFocusedNode = textField;
//if they loose focus attach them to lastFocusedNode
}
}
});
//the above saves you iterations
then when your ScrollPane/scrollable Node Receives focus you set them to the lastFocusedNode since its just going to allow scrolling.
sp.focusedProperty().addListener(new InvalidationListener() {
#Override
public void invalidated(Observable observable) {
if (lastFocusedNode != null) {
lastFocusedNode.requestFocus();
}
}
});
the above assumes your ScrollPane will just not do anything consuming aside from scrolling..
if you ScrollPane/scrollable parent is not going with that assumption then
you go with this approach-detect when the user scrolls after touching your content area of your Scrollable Node-this works only if user attempts to scroll after touching.
//approach loaded
final InvalidationListener lis = new InvalidationListener() {
#Override
public void invalidated(Observable observable) {
//here it is changing
if(sp.isFocused())
lastFocusedNode.requestFocus();//take the focus away
}
};
using the above invalidation listener you set it on the hvalueProperty() and vvalueProperty() or your ScrollPane - which Scrollable parent are you using?
sp.hvalueProperty().addListener(lis);
sp.vvalueProperty().addListener(lis);
then you are done. any of the above solution will cause No problemo
EDIT 2
from what i know TouchEvent is for Touch enabled computers, so maybe go with MouseEvent and you can detect Pane.setOnMousePressed(); etc etcc
Hope it helps
I'm trying to make sort of toggle buttons with LibGDX, so I searched how I could do them, and I found the ToggleButton class, but I suppose it's old since I don't have it in the last build...
So I'm trying to do them this way:
final TextButton button = new TextButton(weapon.getName(), skin2, "buy");
button.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
if(button.isChecked()){
button.setChecked(false);
System.out.println("unchecked");
} else {
button.setChecked(true);
System.out.println("checked");
}
}
});
Actually, it keeps telling me unchecked, as if my button was always unchecked, so the setChecked method doesn't seems to word ...
I tried the toggle method, and it doesn't help at all, and I didn't found any other solution ...
So i was wondering if you had any idea of how I should do this !
Thanks for your help ! :)
The button is toggled automatically when clicked, you don't have to add another listener to do it manually.
So the reason it only prints "unchecked" is because the Button checks itself when clicked, and then your listener is called, which just unchecks it immediately.
Having a
public void buttonClick(ClickEvent event) {
MyPopup popup = new MyPopup();
getWindow().addWindow(popup);
log.warn("Added POPUP");
//lot of method calling here then
getWindow().removeWindow(popup);
log.warn("Removed Popup");
}
I would expect to show a popup window and after some milisecundom (after the expensive method calls) it should hide itself. The log says :
2014-02-19 15:26:51 WARN xyzClass:82 - Added POPUP
2014-02-19 15:26:51 WARN xyzClass:135 - Removed Popup
But the truth is that there is no popup showing here.
If i only show it, and not remove it later (the popup will show)
public void buttonClick(ClickEvent event) {
MyPopup popup = new MyPopup();
getWindow().addWindow(popup);
log.warn("Added POPUP");
//lot of method calling here then
log.warn("Removed Popup");
}
My main reason for this i want to achieve a glasspanel/loading screen functionality # Vaadin, and not had found better solution yet. Any solution/description why the popup not shown up i would appreciate
Just do not have time to render it. You add it and immediately remove.
Try this approach, for example:
private MyPopup popup;
public void buttonClick(ClickEvent event) {
Thread workThread = new Thread() {
#Override
public void run() {
// some initialization here
getWindow().removeWindow(popup);
}
};
workThread.start();
popup = new MyPopup();
getWindow().addWindow(popup);
}
Depending on Vaadin version you can make use of ICEPush plugin (Vaadin 6) or built-in feature called Server Push (Vaadin 7).
public void buttonClick(ClickEvent event) {
MyPopup popup = new MyPopup();
getWindow().addWindow(popup);
log.warn("Added POPUP");
// start background thread with ICEPush or ServerPush
}
// Background thread in a separate class
// update UI accordingly when thread finished the job
getWindow().removeWindow(popup);
log.warn("Removed Popup");
Thanks to it you can move your time-consuming operations to another class thus decouple your business logic from the presentation layer. You can find examples of usage in the links above.
I have an SWT WizardDialog with a number of pages. When this dialog first opens I have to do a check for some conditions and if those conditions are met I need to show a popup over the freshly opened dialog.
So I have this code to listen for SWT.Show event. The event listener responds to SWT.Show to conduct its tests and show a message box:
final WizardDialog dialog = new WizardDialog(shell, wizard);
dialog.setTitle("New Wizard");
dialog.create();
dialog.getShell().addListener(SWT.Show, new Listener()
{
private boolean firstShowing = true;
#Override
public void handleEvent(Event event)
{
if (firstShowing && someConditionExists())
{
MessageBox messageBox = new MessageBox(dialog.getShell(), SWT.OK
| SWT.ICON_WARNING);
messageBox.setMessage("Test");
messageBox.open();
firstShowing = false;
}
}
});
dialog.open();
Except it's called too soon! The dialog is not visible when the handler is called. My message box appears before the dialog is visible and the dialog only appears when I dismiss the message box.
So clearly the SWT.Show is unreliable, at least on Windows where I'm running it. I've also tried putting this code into a ShellListener on the activation but that happens even before the SWT.Show example above.
So how do I reliably show a message box when the dialog is made visible?
Plan B is a dirty timer based hack where a timer is set to fire 200 ms into the future and hope that it triggers when the dialog is visible but obviously this could introduce it's own issues.
I'm using in similar situation (need that appStarted() is called after application window is visible) something like below.
public class App extends ApplicationWindow {
#Override
protected Control createContents(Composite parent) {
// ...
getShell().addShellListener(new ShellAdapter() {
#Override
public void shellActivated(ShellEvent shellevent) {
if (!started) {
Shell s = (Shell) shellevent.getSource();
s.setVisible(true);
appStarted();
started = true;
}
}
});
}
}
Maybe You can use the same like below:
final WizardDialog dialog = new WizardDialog(shell, wizard);
dialog.setTitle("New Wizard");
dialog.create();
dialog.getShell().addShellListener(new ShellAdapter() {
#Override
public void shellActivated(ShellEvent shellevent) {
if (firstShowing && someConditionExists()) {
Shell s = (Shell) shellevent.getSource();
s.setVisible(true);
MessageBox messageBox = new MessageBox(dialog.getShell(), SWT.OK | SWT.ICON_WARNING);
messageBox.setMessage("Test");
messageBox.open();
firstShowing = false;
}
}
});
dialog.open();
Instead of hooking the SWT.Show event, you may get more luck with hooking a PaintListener on to your dialog's Composite. (You'll probably want to unhook it during the first execution.)
What about overriding dialog.open() methodon your WizardDialog class? The first line of the overridden method would call super.open(), which would make it visible. Just put your custom code after that, in the .open() method.
The issue with the approach you're taking above appears to be that it responds to a Show event, which is simply notification that Show has been requested, not that the dialog is visible. The Show event could very well be designed to allow you to know when something is about to be shown, and take some action before that happens, as you've experienced.
I know that this is an old thread. But in case someone finds it useful, I found that overriding Dialog.create() rather than Dialog.open() worked for me.
it's called too soon!
I also run recently in the same trouble. The code was executed too early - my upload action (which I wanted to start automatically under some conditions) was started before the page was displayed.
This happens because the page can only be shown after the code in the SWT.SHOW listener or in the inherited setVisible() method is completed.
#Override
public void setVisible(boolean visible) {
if (visible) {
org.eclipse.ui.progress.UIJob("Auto start the upload") {
#Override
public IStatus runInUIThread(IProgressMonitor monitor) {
if (isAutoStartQcUploadSelected)
startUpload();
return Status.OK_STATUS;
}
};
uiJob.schedule();
}
super.setVisible(visible);
}
org.eclipse.ui.progress.UIJob as described FAQ_Can_I_make_a_job_run_in_the_UI_thread has solved the issue.
P.S.: Yes, I know that's an old question :-)
But it is the first one propesed by google and the hint with the UI Job was missing.
The code of marioosh can be further improved, by storing the ShellAdapter in a variable.
Remove the ShellAdapter when the listener is triggered for the first time.
The variable started is no longer needed.
The statement s.setVisible(true); is not necessary, because this event is just triggered when the shell gets visible.
public class App extends ApplicationWindow {
#Override
protected Control createContents(Composite parent) {
// ...
ShellAdapter shellActivatedAdapter = new ShellAdapter() {
#Override
public void shellActivated(ShellEvent shellevent) {
shellevent.getSource().removeShellListener(shellActivatedAdapter);
appStarted();
}
};
getShell().addShellListener(shellActivatedAdapter);
}
}