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):
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.
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.
Related
I am using Java's SWT toolkit to create a GUI with text field inputs. These input fields require numerical input and have units assigned to them. I'm trying to create a fancy way to integrate units within the field as a fixed suffix to the text, such that the user can only edit the numerical part. I'd also like for the suffix to be greyed out so the user knows it is disabled - something like the following:
While searching, I saw some solutions with a mask formatter from Swing that might do the trick, but I'm sort of hoping there might be something default with SWT. Any suggestions on how to make this work?
The field is part of a a matrix, so I can't simply add the units to a header label. I suppose I could create another column after the text field that could provide units as a label, but I'm going for something more intuitive and aesthetic.
Any suggestions?
One option would be to group Text and Label widgets in the same composite, and set the text on the Label to the desired suffix:
The area to the left of the suffix is the single-line text field, which can be edited, and the suffix is a disabled Label.
public class TextWithSuffixExample {
public class TextWithSuffix {
public TextWithSuffix(final Composite parent) {
// The border gives the appearance of a single component
final Composite baseComposite = new Composite(parent, SWT.BORDER);
baseComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
final GridLayout baseCompositeGridLayout = new GridLayout(2, false);
baseCompositeGridLayout.marginHeight = 0;
baseCompositeGridLayout.marginWidth = 0;
baseComposite.setLayout(baseCompositeGridLayout);
// You can set the background color and force it on
// the children (the Text and Label objects) to add
// to the illusion of a single component
baseComposite.setBackground(new Color(parent.getDisplay(), new RGB(255, 255, 255)));
baseComposite.setBackgroundMode(SWT.INHERIT_FORCE);
final Text text = new Text(baseComposite, SWT.SINGLE | SWT.RIGHT);
text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final Label label = new Label(baseComposite, SWT.NONE);
label.setEnabled(false);
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, true));
label.setText("kg/m^3");
}
}
final Display display;
final Shell shell;
public TextWithSuffixExample() {
display = new Display();
shell = new Shell(display);
shell.setLayout(new GridLayout());
shell.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
new TextWithSuffix(shell);
}
public void run() {
shell.setSize(200, 100);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
public static void main(final String[] args) {
new TextWithSuffixExample().run();
}
}
I have a ScrolledComposite widget that I'm initializing inside a selection listener (in turn attached to another composite). This ScrolledComposite contains in turn another Composite, which in turn holds a Button and a Label. While the internal composite appears, none of its children widgets do. I've used ScrolledComposite plenty of times before, and everything looks right to my eye. Can any of you see anything wrong? Note, the ScrolledComposite is a class variable. Also note that this problem is occurring regardless of if I ever dispose of the composite and its contents in the else condition.
final Button showConsole = new Button(topLeft, SWT.CHECK);
showConsole.setText("Show Debug Console");
showConsole.setFont(new Font(domains.getDisplay(), "Segoe UI", 9, SWT.ITALIC));
showConsole.setSelection(false);
showConsole.addSelectionListener(new SelectionListener() {
#Override
public void widgetSelected(SelectionEvent e) {
//The total widget group is only supposed to appear when the button is selected
if (showConsole.getSelection()) {
scrolledConsoleComp = new ScrolledComposite(leftComposite,
SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
Composite consoleComposite = new ScrolledComposite(scrolledConsoleComp, SWT.NONE | SWT.BORDER);
consoleComposite.setLayout(new GridLayout());
consoleComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
consoleComposite.setVisible(true);
scrolledConsoleComp.setContent(consoleComposite);
scrolledConsoleComp.setExpandHorizontal(true);
scrolledConsoleComp.setExpandVertical(true);
scrolledConsoleComp.setLayout(new GridLayout());
scrolledConsoleComp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
Button clear = new Button(consoleComposite, SWT.PUSH);
clear.setText("Clear Console");
final Label consoleText = new Label(consoleComposite, SWT.WRAP);
consoleText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
consoleText.setText("Messages: \n" + consoleData);
clear.addSelectionListener(new SelectionListener() {
#Override
public void widgetSelected(SelectionEvent e) {
consoleData = "";
consoleText.setText("Messages: \n" + consoleData);
}
#Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
scrolledConsoleComp.setMinSize(leftComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
leftComposite.layout(true);
} else {
scrolledConsoleComp.setVisible(false);
scrolledConsoleComp.dispose();
leftComposite.layout(true);
}
}
I appreciate any insight. Let me know if anything in this question is unclear. Thank you!
You're creating your Button and your Label inside a ScrolledComposite. They won't be shown because setContent() is not called.
Usually a ScrolledComposite contains a Composite that holds all the other Widgets.
Your consoleComposite should become a Composite.
No need for a call to setVisible()
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
How to combine button and text field using SWT and also give a customized icon for the button.
Many have suggested to use Combo but it gives drop down option, but I don't want it to be a drop down icon. Can any one help here and suggest an implementation strategy?
If I understand your question correctly, then the best approach would be to use a Composite to group Text and Label objects.
The Text object is obviously your text field, and with the Label you can set an image to give a nice border-less button.
Depending on your needs, this can be easily extended to allow for a multi-line text area, but I'll stick with a single line for the example
public class TextWithButtonExample {
/**
* Simple class to accomplish what you want by wrapping
* the Text and Label objects with a Composite.
*/
public class TextWithButton {
public TextWithButton(final Composite parent) {
// The border gives the appearance of a single component
final Composite baseComposite = new Composite(parent, SWT.BORDER);
baseComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
final GridLayout baseCompositeGridLayout = new GridLayout(2, false);
baseCompositeGridLayout.marginHeight = 0;
baseCompositeGridLayout.marginWidth = 0;
baseComposite.setLayout(baseCompositeGridLayout);
// You can set the background color and force it on
// the children (the Text and Label objects) to add
// to the illusion of a single component
baseComposite.setBackground(new Color(parent.getDisplay(), new RGB(255, 255, 255)));
baseComposite.setBackgroundMode(SWT.INHERIT_FORCE);
final Text text = new Text(baseComposite, SWT.SINGLE);
text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
// Get whatever image you want from wherever
// (in this case the web so it can be run easily!)
Image buttonImage;
try {
buttonImage = ImageDescriptor.createFromURL(new URL("http://eclipse-icons.i24.cc/ovr16/clear.gif")).createImage();
} catch (final MalformedURLException e) {
buttonImage = null;
}
final Label button = new Label(baseComposite, SWT.NONE);
button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true));
button.setImage(buttonImage);
button.addMouseListener(new MouseAdapter() {
#Override
public void mouseUp(final MouseEvent e) {
// Do whatever you want when the 'button' is clicked
text.setText("");
}
});
}
}
final Display display;
final Shell shell;
public TextWithButtonExample() {
display = new Display();
shell = new Shell(display);
shell.setLayout(new GridLayout());
shell.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
new TextWithButton(shell);
}
public void run() {
shell.setSize(200, 100);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
public static void main(final String[] args) {
new TextWithButtonExample().run();
}
}
I'm having a few troubles with the dialogs in the eclipse rcp. I wish to have a dialog which shows me a MasterDetailBlock to manage an arbitrary amount of entities shown in a table on the master part with their corresponding DetailPages shown in the detail part. As of now, this is done using a View, but a non-modal dialog seems more fitting for this.
At first, I tried the naive way of just taking the code from the view und put it into the dialog, with a few modifications due to the difference between view and dialog creation. However, most controls were missing. A search on Google, the eclipse forums and here on Stackoverflow did not bring a solution for this. After checking these sites for the solution, I tried to understand what's happening by stepping through the code with the debugger, but that didn't help me either.
The following Code should show a dialog containing section in which a button should be displayed. However, it doesn't:
protected Control createDialogArea(Composite parent) {
parent.setLayout(new GridLayout(1, true));
Section section = toolkit.createSection(parent, ExpandableComposite.EXPANDED | ExpandableComposite.TITLE_BAR);
section.setLayout(new GridLayout());
section.setText("Section");
section.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
Button test = toolkit.createButton(section, "test", SWT.PUSH);
test.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
test.setVisible(true);
section.computeSize(SWT.DEFAULT, SWT.DEFAULT);
return parent;
}
The result of this is:
However, as a MasterDetailBlock needs a form, I'll provide this Code as well:
protected Control createDialogArea(Composite parent) {
parent.setLayout(new GridLayout(1, true));
form = new ScrolledForm(parent);
form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
form.getBody().setLayout(new FillLayout());
Composite formComposite = toolkit.createComposite(form.getBody());
formComposite.setLayout(new GridLayout(1,true));
Section section = toolkit.createSection(formComposite, ExpandableComposite.EXPANDED | ExpandableComposite.TITLE_BAR);
section.setLayout(new GridLayout());
section.setText("Section");
section.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
Button test = toolkit.createButton(section, "test", SWT.PUSH);
test.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
test.setVisible(true);
section.computeSize(SWT.DEFAULT, SWT.DEFAULT);
return parent;
}
Just a slight modification by adding a form to the dialog and everything goes on the form. However, the result looks like this:
I'm afraid I'm missing something obvious here. As I said, searching didn't bring any enlightment and stepping through the code didn't help either. My last resort "trying stuff to see what happens and try to understand that" didn't help much, as the results didn't change from the ones already posted.
So, do I miss something? (Which I think it is)
If you can provide me a link to show me what's wrong (or anything from your experience as well), I would appriciate that.
Thank you for your help.
There is a
section.setClient(test);
missing. Moreover, you should have this when using a Form and section is expandible:
protected final IExpansionListener expansionListener= new ExpansionAdapter()
{
/**
* {#inheritDoc}
*/
#Override
public void expansionStateChanged(ExpansionEvent e)
{
ScrolledForm form = ...; //your scrolled form goes here
form.layout(new Control[] {(ExpandableComposite) e.getSource()}, SWT.ALL | SWT.CHANGED);
form.reflow(true);
}
};
...
section.addExpansionListener(expansionListener);
Instead of the button test you should add a Composite which contains the button. Each section can have only one client.
I'm working on an application using SWT in Java, and I've encountered some wierd behavior from multiple StyledText widgets. It's fairly consistent: if there are more than one StyledText widget displayed in the window/view/editor at once, you can select whatever you want from each one individually, at the same time. In the screenshot, there are 4 separate widgets with 4 separate selections.
My expectation is that if I start selecting from one widget, whatever other widget that might already have a selection should then lose it, similarly to the behavior you would expect from a web browser; only one selection at a time.
I'd like to fix this problem, but I hope to avoid needing to make some manager that listens to every widget and turns off selections when a new one is made. Is there a better way to do this? (Also why is this happening in the first place?)
Why is it happening?
To best of my knowledge the behavior of a widget depends on its implementation i.e. whether it is a
native widget (like org.eclipse.swt.widgets.Text) or
custom widget (like org.eclipse.swt.custom.StyledText)
The difference lies in the handling of the mouse down or mouse up events.
For example,
In case of org.eclipse.swt.widgets.Text the left mouse down eventually translates to OS.SendMessage (hwnd, OS.WM_LBUTTONUP, wParam, lParam);
Whereas
org.eclipse.swt.custom.StyledText uses a mouse event handler and extra processing in the handleMouseDown(Event event) method. Most of the functionality or UI is done using custom draw/redraw/validate/invalidate/update methods.
To put it in a very crude win32 sdk way:
There are some controls provided by windows/win32 GDI
And, some are custom user drawn controls
See the below SWT code which uses text, styledtext, browser etc for testing. Also Note the browser control is not exactly a win32 control it is a wrapper control around Internet Explorer activex or mozilla's gecko engine, therefore its behavior is same as that of styled text.
Any possible solution ?
Well I can only think of borrowing SWT's styledtext code and then making a version which suits me. Or
As you have already mentioned, use some listener to reset all the other unfocused widgets (which even in my opinion is not a very clean solution).
Test Code & Output
Code:
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
public class StyledTextTest {
private static Display display;
public static void main(String[] args)
{
display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new GridLayout(2,true));
shell.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
createStyledText(shell);
createStyledText(shell);
createText(shell);
createText(shell);
createCombo(shell);
createCombo(shell);
createCustomCombo(shell);
createCustomCombo(shell);
createBrowser(shell);
createBrowser(shell);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
private static void createCustomCombo(Composite parent)
{
new Label(parent, SWT.NONE).setText("Custom Combo");
CCombo c = new CCombo(parent, SWT.DROP_DOWN);
c.setItems(new String[] {"test best", "best rest", "rest test"});
c.select(0);
c.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
}
private static void createCombo(Composite parent)
{
new Label(parent, SWT.NONE).setText("Combo");
Combo c = new Combo(parent, SWT.DROP_DOWN);
c.setItems(new String[] {"test best", "best rest", "rest test"});
c.select(0);
c.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
}
static void createBrowser(Composite parent)
{
new Label(parent, SWT.NONE).setText("Browser");
Browser browser = new Browser(parent, SWT.NONE);
browser.setText("<div>This is a test !!</div>");
browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
}
static void createText(Composite parent) {
new Label(parent, SWT.NONE).setText("Text");
final Text text = new Text(parent, SWT.BORDER);
text.setText("0123456789 ABCDEFGHIJKLM NOPQRSTUVWXYZ");
}
static void createStyledText(Composite parent)
{
new Label(parent, SWT.NONE).setText("Styled Text");
StyledText text = new StyledText (parent, SWT.BORDER|SWT.SINGLE);
text.setText("0123456789 ABCDEFGHIJKLM NOPQRSTUVWXYZ");
// make 0123456789 appear bold
StyleRange style1 = new StyleRange();
style1.start = 0;
style1.length = 10;
style1.fontStyle = SWT.BOLD;
text.setStyleRange(style1);
// make ABCDEFGHIJKLM have a red font
StyleRange style2 = new StyleRange();
style2.start = 11;
style2.length = 13;
style2.foreground = display.getSystemColor(SWT.COLOR_RED);
text.setStyleRange(style2);
// make NOPQRSTUVWXYZ have a blue background
StyleRange style3 = new StyleRange();
style3.start = 25;
style3.length = 13;
style3.fontStyle = SWT.BOLD | SWT.ITALIC;
text.setStyleRange(style3);
}
}