I'm trying to make a application that rely on SWT (not the Eclipse Workbench) use Swing component, however I noticed that just adding an SWT_AWT frame prevent the use of the shortcut command+q, and I'm not sure why or how to work around this.
I'm developing on an Intel macOs (but depending on your platform so adapt you dependencies)
org.eclipse.platform:org.eclipse.swt.cocoa.macosx.x86_64:3.120.0
Here's the reproducer:
import org.eclipse.swt.SWT;
import org.eclipse.swt.awt.SWT_AWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
public class SWTKeyLogger implements Listener{
public static void main(String[] args) {
Display.setAppName("SWT AWT Bridge reproducer");
var display = new Display();
var shell = new Shell(display, SWT.CLOSE | SWT.MIN | SWT.RESIZE | SWT.TITLE | SWT.BORDER);
shell.setSize(400, 200);
shell.setText("Press Key on the blank window");
Listener listener = e -> System.out.println("[SWT] " + e.type == SWT.KeyDown ? "Key Down" : "Key Up");
var swingComposite = new Composite(shell, SWT.EMBEDDED);
swingComposite.setLayout(new GridLayout(1, true));
swingComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
swingComposite.addListener(SWT.KeyDown, listener);
swingComposite.addListener(SWT.KeyUp, listener);
var frame = SWT_AWT.new_Frame(swingComposite);
frame.addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
System.out.println("[AWT] Key released");
}
});
}
}
When runing the above code typing command+q doesn't exit the application, instead it hangs, and it may have to be forcefully terminated.
The listener installed on the AWT frame however report the keys, but not the SWT liteners.
And removing the frame make things work again, i.e. typing command+q terminate the application. And the SWT listeners on the composite report the keys.
- var frame = SWT_AWT.new_Frame(swingComposite);
- frame.addKeyListener(new KeyAdapter() {
- #Override
- public void keyReleased(KeyEvent e) {
- System.out.println("[AWT] Key released");
- }
- });
Thanks for any guidance.
I have a JavaFX Button that triggers when the user presses enter. This causes a FileChooser to open up. Some people (like myself) may hit enter inside the FileChooser to save the file. However, this causes the save button to trigger itself again and open the FileChooser again to save a new file. Clicking the button (in the FileChooser) with the mouse does not have this issue.
I thought consuming the event from the button would do something about this issue, but it only consumes the button on the GUI's event, rather than the FileChooser button. I've tried looking for ways to modify the FileChooser's EventHandler to consume an enter keypress, but with no success.
I've also tried taking the focus off the button and moving it to the parent (a Pane) so it can't be clicked again. However, there are buttons that benefit being clicked multiple times without having to regain focus on them again.
A example of my code looks like this (obviously this would be part of a bigger class that extends Application):
EventHandler<KeyEvent> enter = event -> {
if (event.getCode() == KeyCode.ENTER && event.getSource() instanceof Button) {
Button src = (Button) event.getSource();
src.fire();
}
event.consume();
};
Button b1 = new Button("Save");
b1.setOnKeyReleased(enter);
/* Called by .fire method */
b1.setOnAction(event -> {
/* Create the save dialog box */
FileChooser saveDialog = new FileChooser();
saveDialog.setTitle("Save");
/* Get file */
File f = saveDialog.showSaveDialog(stage);
/*
* ... do stuff with file ...
*/
});
Note: This example isn't my exact code. Instead the key released event is a variable used for multiple buttons, rather than just the save button (i.e. b2.setOnKeyReleased(enter);
b2.setOnAction(event -> {/* Do something */});).
How could I go about preventing the button from triggering when the user presses enter in the FileChooser? I don't want the user to be stuck in a loop if they don't have a mouse. I'm aware that pressing Alt+S also saves it, but I can't expect all users to be aware of that.
EDIT: As requested in a comment which appears to be deleted now, here's a runnable version of the code:
import java.io.File;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
public class ButtonTest extends Application {
#Override
public void start(Stage stage) throws Exception {
/* EventHandler to be used with multiple buttons */
EventHandler<KeyEvent> enter = event -> {
if (event.getCode() == KeyCode.ENTER && event.getSource() instanceof Button) {
Button src = (Button) event.getSource();
src.fire();
}
event.consume();
};
/* Create a new button */
Button b1 = new Button("Save");
Button b2 = new Button("Print");
/* Add event handlers */
b1.setOnKeyReleased(enter);
b2.setOnKeyReleased(enter);
/* Called by .fire method of save button */
b1.setOnAction(event -> {
/* Create the save dialog box */
FileChooser saveDialog = new FileChooser();
saveDialog.setTitle("Save");
/* Get file */
File f = saveDialog.showSaveDialog(stage);
/* ... do stuff with file ... */
});
/* Called by .fire method of print button */
b2.setOnAction(event -> System.out.println("Pressed"));
Scene scene = new Scene(new HBox(b1, b2));
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
The problem is firing the Button from the onKeyReleased handler. By the time you release the ENTER key the FileChooser has been hidden and the Stage has regained focus, meaning the key-release event is given to your Stage/Button. Obviously this will cause a cycle.
One possible solution is to fire the Button from inside a onKeyPressed handler. This will give slightly different behavior relative to other applications, however, which your users might not expect/appreciate.
Another possible solution is to track if the FileChooser had been open before firing the Button, like what Matt does in his answer.
What you seem to be trying to do is allow users to use the ENTER key to fire the Button; this should be default behavior on platforms like Windows.
Not for me. Space is the only key that triggers a button. I think the reason for that is because enter is used to trigger the default button which is set using btn.setDefaultButton(true);
For me, pressing ENTER while the Button has focus fires the action event when using JavaFX 11.0.2 but not JavaFX 8u202, both on Windows 10. It appears the behavior of Button changed since JavaFX 8. Below is the different implementations of com.sun.javafx.scene.control.behavior.ButtonBehavior showing the registered key bindings.
JavaFX 8u202
protected static final List<KeyBinding> BUTTON_BINDINGS = new ArrayList<KeyBinding>();
static {
BUTTON_BINDINGS.add(new KeyBinding(SPACE, KEY_PRESSED, PRESS_ACTION));
BUTTON_BINDINGS.add(new KeyBinding(SPACE, KEY_RELEASED, RELEASE_ACTION));
}
JavaFX 11.0.2
public ButtonBehavior(C control) {
super(control);
/* SOME CODE OMITTED FOR BREVITY */
// then button-specific mappings for key and mouse input
addDefaultMapping(buttonInputMap,
new KeyMapping(SPACE, KeyEvent.KEY_PRESSED, this::keyPressed),
new KeyMapping(SPACE, KeyEvent.KEY_RELEASED, this::keyReleased),
new MouseMapping(MouseEvent.MOUSE_PRESSED, this::mousePressed),
new MouseMapping(MouseEvent.MOUSE_RELEASED, this::mouseReleased),
new MouseMapping(MouseEvent.MOUSE_ENTERED, this::mouseEntered),
new MouseMapping(MouseEvent.MOUSE_EXITED, this::mouseExited),
// on non-Mac OS platforms, we support pressing the ENTER key to activate the button
new KeyMapping(new KeyBinding(ENTER, KeyEvent.KEY_PRESSED), this::keyPressed, event -> PlatformUtil.isMac()),
new KeyMapping(new KeyBinding(ENTER, KeyEvent.KEY_RELEASED), this::keyReleased, event -> PlatformUtil.isMac())
);
/* SOME CODE OMITTED FOR BREVITY */
}
As you can see, both register SPACE to fire the Button when it has focus. However, the JavaFX 11.0.2 implementation also registers ENTER for the sameābut only for non-Mac OS platforms. I couldn't find any documentation about this change in behavior.
If you want the same behavior in JavaFX 8, and you don't mind hacking into the internals of JavaFX, then you can use reflection to alter the behavior of all button-like controls in your application. Here's a utility method example:
import com.sun.javafx.PlatformUtil;
import com.sun.javafx.scene.control.behavior.ButtonBehavior;
import com.sun.javafx.scene.control.behavior.KeyBinding;
import java.lang.reflect.Field;
import java.util.List;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
public final class ButtonUtils {
public static void installEnterFiresButtonFix() throws ReflectiveOperationException {
if (PlatformUtil.isMac()) {
return;
}
Field bindingsField = ButtonBehavior.class.getDeclaredField("BUTTON_BINDINGS");
Field pressedActionField = ButtonBehavior.class.getDeclaredField("PRESS_ACTION");
Field releasedActionField = ButtonBehavior.class.getDeclaredField("RELEASE_ACTION");
bindingsField.setAccessible(true);
pressedActionField.setAccessible(true);
releasedActionField.setAccessible(true);
#SuppressWarnings("unchecked")
List<KeyBinding> bindings = (List<KeyBinding>) bindingsField.get(null);
String pressedAction = (String) pressedActionField.get(null);
String releasedAction = (String) releasedActionField.get(null);
bindings.add(new KeyBinding(KeyCode.ENTER, KeyEvent.KEY_PRESSED, pressedAction));
bindings.add(new KeyBinding(KeyCode.ENTER, KeyEvent.KEY_RELEASED, releasedAction));
}
private ButtonUtils() {}
}
You would call this utility method early in the startup of your application, before any Buttons are created. Here's an example using it:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
ButtonUtils.installEnterFiresButtonFix();
} catch (ReflectiveOperationException ex) {
ex.printStackTrace();
}
Button button = new Button("Save");
button.setOnAction(event -> {
event.consume();
System.out.println(new FileChooser().showSaveDialog(primaryStage));
});
Scene scene = new Scene(new StackPane(button), 300, 150);
primaryStage.setScene(scene);
primaryStage.setTitle("Workshop");
primaryStage.show();
}
}
Reminder: This fix is implementation dependent.
I added a boolean for the fileChooser being open and it seems to be working for me but I had to split the events up otherwise it will only fire the print button every other
public class Main extends Application {
private boolean fileChooserOpen = false;
#Override
public void start(Stage stage) throws Exception{
/* EventHandler to be used with multiple buttons */
EventHandler<KeyEvent> enterWithFileChooser = event -> {
if (!fileChooserOpen && event.getCode() == KeyCode.ENTER && event.getSource() instanceof Button) {
Button src = (Button) event.getSource();
src.fire();
fileChooserOpen = true;
}else {
fileChooserOpen = false;
}
event.consume();
};
EventHandler<KeyEvent> enter = event -> {
if (event.getCode() == KeyCode.ENTER && event.getSource() instanceof Button) {
Button src = (Button) event.getSource();
src.fire();
}
event.consume();
};
/* Create a new button */
Button b1 = new Button("Save");
Button b2 = new Button("Print");
/* Add event handlers */
b1.setOnKeyReleased(enterWithFileChooser);
b2.setOnKeyReleased(enter);
/* Called by .fire method of save button */
b1.setOnAction(event -> {
/* Create the save dialog box */
FileChooser saveDialog = new FileChooser();
saveDialog.setTitle("Save");
/* Get file */
File f = saveDialog.showSaveDialog(stage);
/* ... do stuff with file ... */
});
/* Called by .fire method of print button */
b2.setOnAction(event -> System.out.println("Pressed"));
Scene scene = new Scene(new HBox(b1, b2));
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) { launch(args); }
}
I am using HostListener to get window close event and try to prevent the event.
I imported HostListener First and Used this code. But, I couldn't prevent the window close event.
import { HostListener } from '#angular/core';
#HostListener('window:beforeunload', ['$event'])
onWindowClose(event: any): void {
this.doSomething();
}
You can prevent window close event for separate component, using HostListener.
First Import HostListener in your component
import { HostListener } from '#angular/core';
And, Use this code to prevent window close.
#HostListener('window:beforeunload', ['$event'])
onWindowClose(event: any): void {
// Do something
event.preventDefault();
event.returnValue = false;
}
Now, Your event was prevented.
I would like to capture all events within a GWT frame. I've found several ways to do this, but they only return mousemove and mouseout events. I also need keypresses, input, etc. The goal is to capture the events and send them to another client by using websockets, and then replicate them on the other side (co-browsing).
I am using a page on the same domain within the frame.
public class ESinkFrame extends Frame implements EventListener {
public ESinkFrame(String src){
super(src);
DOM.sinkEvents(getElement(), Event.KEYEVENTS);
DOM.sinkEvents(getElement(), Event.MOUSEEVENTS);
}
public void onBrowserEvent(Event event) {
System.out.println( "sunk event: " + DOM.eventGetTypeString(event) );
}
}
And when I use it, I also try to attach a different way of grabbing the events.
ESinkFrame frame = new ESinkFrame("http://127.0.0.1:8888/other.html");
RootPanel.get().add(frame);
FrameElement frameElt = frame.getElement().cast();
Document frameDoc = frameElt.getContentDocument();
BodyElement body = frameDoc.getBody();
Element el = body.cast();
DOM.setEventListener(el, new EventListener()
{
public void onBrowserEvent(Event event)
{
Window.alert("test");
}
});
DOM.sinkEvents(el, Event.KEYEVENTS);
Event.addNativePreviewHandler(new NativePreviewHandler(){
public void onPreviewNativeEvent(NativePreviewEvent event) {
String eventName = event.getNativeEvent().getType();
if (event.isFirstHandler() /* && (event.getTypeInt() & Event.MOUSEEVENTS) == 0*/)
System.out.println("PreviewHandler: " + eventName);
}
});
I have an windows forms application, with a form that holds 2 tabcontrols and a grid. I'd like to catch the pressing of esc key on any on this controls.
The question is : is it a simpler way to do that without subscribing to the keypress event on each control ?
Thanks!
You can Simply Do following.
Implement an IMessageFilter and Handle Key Down event.
Here is the complete Code to hook Escape Key Down.
public class MyKeboardHook:IMessageFilter
{
public const int WM_KEYDOWN = 0x0100;
public const int VK_ESCAPE = 0x1B;
public event EventHandler EscapeKeyDown;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_KEYDOWN && m.WParam == new IntPtr(VK_ESCAPE))
{
OnEscapeKeyPressed();
}
return false; //Do not Process anything
}
protected virtual void OnEscapeKeyDown()
{
if(this.EscapeKeyDown!=null)
{
EscapeKeyDown(this, EventArgs.Empty);
}
}
}
Now you need to register this. The best place would be in Main
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MyKeboardHook myKeboardHook = new MyKeboardHook();
myKeboardHook.EscapeKeyDown += (e, x) =>
{
MessageBox.Show("Escape Key Pressed");
};
Application.AddMessageFilter(myKeboardHook);
Application.Run(new Form1());
}
}
Subscribe to the event on the form itself.
If the control doesn't handle the event it should bubble up to the form where it will be handled.