Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Key Events

How can I send a custom SWT key event such that it results in the typing of that exact literal character without any conversion?

We are making a custom on-screen graphical keyboard that allows for multiple possible layouts - QWERTY being the main one, but several others planned, including ones that visually simulate the iPhone keyboard layout or others that are non-standard relative to the PC.

I would like to be able to send any arbitrary character of my choosing as an event, such that it will result in the typing of that exact character. However, whenever I send an event with a given character, SWT seems to automatically interpret and convert it based on the system's keyboard settings and status, changing it to be capitalized/lowercase or using the shifted symbols depending on whether or not the shift key (the real one on the actual keyboard, for instance) is pressed. For instance, passing the character 'T' through a key event will result in either 't' or 'T' depending on the shift key. It performs similarly with the '4' character, doing either '4' or '$' based on shift.

This is problematic, as I wish to perform the shifting of characters manually, and indeed may not wish to use the same shift characters as the standard QWERTY keyboard in any given case (perhaps, for instance, I want '$' to be the shifted (or 'function' key) version of 'R' on a smaller, cellphone-like keyboard). How can I use these events such that it does not assume the usage of QWERTY keys?

The documentation for the 'character' field in the SWT events says the following:

the character represented by the key that was typed. This is the final character that results after all modifiers have been applied. For example, when the user types Ctrl+A, the character value is 0x01. It is important that applications do not attempt to modify the character value based on a stateMask (such as SWT.CTRL) or the resulting character will not be correct.

This makes me believe that it should not be converting my character as it is doing, as I am commanding it to use my "final" result.

I have created a simple, stand-alone example to demonstrate my issue (please note that this is not how I am doing the actual Keyboard, and is only meant to highlight the key event issue):

screenshot

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Button;

public class KeyboardIssueExample extends Shell {
    private Text txtTextArea;
    private Composite cmpButtons;
    private Button btnBigA;
    private Button btnBigS;
    private Button btnLittleD;
    private Button btnSix;
    private Button btnSeven;
    private Button btnDollah;
    private Button btnStar;

    private SelectionListener mainListener = new SelectionAdapter() {
        @Override
        public void widgetSelected(SelectionEvent selE) {
            Button b = (Button)selE.widget;
            char c = b.getText().charAt(0);
            sendKey(c);
        }
    };

    /**
     * Launch the application.
     * @param args
     */
    public static void main(String args[]) {
        try {
            Display display = Display.getDefault();
            KeyboardIssueExample shell = new KeyboardIssueExample(display);
            shell.open();
            shell.layout();
            while (!shell.isDisposed()) {
                if (!display.readAndDispatch()) {
                    display.sleep();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Create the shell.
     * @param display
     */
    public KeyboardIssueExample(Display display) {
        super(display, SWT.SHELL_TRIM);
        createContents();
    }

    /**
     * Create contents of the shell.
     */
    protected void createContents() {
        setText("SWT Application");
        setSize(450, 300);
        setLayout(new GridLayout());

        txtTextArea = new Text(this, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.CANCEL | SWT.MULTI);
        txtTextArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));

        cmpButtons = new Composite(this, SWT.NONE);
        cmpButtons.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
        GridLayout gl_cmpButtons = new GridLayout(7, false);
        gl_cmpButtons.marginWidth = 0;
        gl_cmpButtons.marginHeight = 0;
        cmpButtons.setLayout(gl_cmpButtons);

        btnBigA = new Button(cmpButtons, SWT.NONE);
        btnBigA.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
        btnBigA.setText("A");

        btnBigS = new Button(cmpButtons, SWT.NONE);
        btnBigS.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
        btnBigS.setText("S");

        btnLittleD = new Button(cmpButtons, SWT.NONE);
        btnLittleD.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
        btnLittleD.setText("d");

        btnSix = new Button(cmpButtons, SWT.NONE);
        btnSix.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
        btnSix.setText("6");

        btnSeven = new Button(cmpButtons, SWT.NONE);
        btnSeven.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
        btnSeven.setText("7");

        btnDollah = new Button(cmpButtons, SWT.NONE);
        btnDollah.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
        btnDollah.setText("$");

        btnStar = new Button(cmpButtons, SWT.NONE);
        btnStar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
        btnStar.setText("*");

        init();
    }

    private void init() {
        btnBigA.addSelectionListener(mainListener);
        btnBigS.addSelectionListener(mainListener);
        btnLittleD.addSelectionListener(mainListener);
        btnSix.addSelectionListener(mainListener);
        btnSeven.addSelectionListener(mainListener);
        btnDollah.addSelectionListener(mainListener);
        btnStar.addSelectionListener(mainListener);
    }

    @Override
    protected void checkSubclass() {
        // Disable the check that prevents subclassing of SWT components
    }


    private void sendKey(char c) {
        sendKeyEvent(c, SWT.KeyDown);
        sendKeyEvent(c, SWT.KeyUp);
    }

    private void sendKeyEvent(char c, int eventType) {
        txtTextArea.forceFocus();
        Event event = new Event();
        event.type = eventType;
        event.character = c;
        getDisplay().post(event);
    }

}

As you will find, the key events do not respect my own custom usage of capitalization, and instead uses its own. How troublesome.

like image 549
Southpaw Hare Avatar asked Feb 03 '14 15:02

Southpaw Hare


People also ask

What are the key events?

Whenever a user presses any key on the Keyboard, different events are fired. There are three keyboard events, namely keydown , keypress , and keyup .

What is the meaning of key event?

An event which indicates that a keystroke occurred in a component. This low-level event is generated by a component object (such as a text field) when a key is pressed, released, or typed.

What is a key event in coding?

The keyboard events are: keydown : It fires when any key is pressed down. keypress : It fires only when a key that produces a character value is pressed down. For example, if you press the key a , this event will fire as the key a produces a character value of 97 .

What is a key event in a text?

Key events indicate when the user is typing at the keyboard. Specifically, key events are fired by the component with the keyboard focus when the user presses or releases keyboard keys.


1 Answers

Looking at the source of Display.post for the Mac and for Windows there is a lot of code specific to the key up/down events. This includes calls to OS specific APIs to convert the character and key code you specify to the parameters required by the OS specific method used to generate the key stroke event.

For Windows this code calls VkKeyScan which according to the Microsoft documentation does look at the current keyboard state.

Display does have a postEvent(Event) method which adds to the event queue without changing anything, but this is not public so you would have to access it using reflection. Since it is not part of the official API it may change.

Edit: There is actually an Eclipse bug report here for this. It has been open for 10 years so probably isn't going to be fixed!

Update: There is also a Widget.notifyListeners(eventType, Event) method which could be used to send the key events to a specific control. You will have to create the Event and set the required fields.

like image 118
greg-449 Avatar answered Sep 18 '22 02:09

greg-449