How do I prevent a multi-line text field from "stealing" tab-key presses?
I mean: I'd like to use TAB to cycle between the elements of a window, but when I enter the multiline text, TAB becomes a "normal" key, and simply inserts tabulators into the text I'm typing.
How do I handle this? Should I write some custom listener, or can I change the behaviour of the component by using an SWT constant?
SWT defines a mechanism for implementing this kind of behaviour using the TraverseListener class. The following snippet (taken from here) shows how:
Text text1 = new Text(shell, SWT.MULTI | SWT.WRAP);
text1.setBounds(10,10,150,50);
text1.setText("Tab will traverse out from here.");
text1.addTraverseListener(new TraverseListener() {
public void keyTraversed(TraverseEvent e) {
if (e.detail == SWT.TRAVERSE_TAB_NEXT || e.detail == SWT.TRAVERSE_TAB_PREVIOUS) {
e.doit = true;
}
}
});
Add a keystroke listener to the textfield or textarea and implement a special handler for TAB. I've seen this 'tab catching' on Swing applications too and it's usually annoying.
You could offer another handler for Shift-TAB which then would insert a \t, if you still want to allow tabs in the textfield.
Related
In SWT, we can set the tab oder by using the code below:
composite.setTabList(new Control[]{button1, button3});
Is there any way to change the key traversal from Tab to Enter? My application connect to a bar code reader and it just support Enter key after read the barcode
There is a TraverseListener in SWT that can be used to change the effect of traversal keys.
For example, a traverse listener can be used to focus the next field on Enter like this:
text.addTraverseListener(new TraverseListener() {
#Override
public void keyTraversed(TraverseEvent event) {
if (event.detail == SWT.TRAVERSE_RETURN) {
event.doit = false;
// focus next control
}
}
});
Setting the doit flag of the event to false consumes the event and prevents it from causing the default action - if any. In a multi-line text field, the Enter key may begin a new line unless the event was consumed by a listener.
I have grid layout witch some fields added like that:
private Component userDetailsTab(final User user) {
final GridLayout details = new GridLayout(2, 1);
details.setMargin(true);
details.setSpacing(true);
details.addComponent(createDetailLabel(Messages.User_Name));
final Component username = createDetailValue(user.getName());
details.addComponent(username);
...
I have also Layout click listener which replace labels on text field, it looks like that:
final TextField tf = new TextField();
details.addListener(new LayoutClickListener() {
private static final long serialVersionUID = -7374243623325736476L;
#Override
public void layoutClick(LayoutClickEvent event) {
Component com = event.getChildComponent();
if (event.getChildComponent() instanceof Label) {
Label label = (Label)event.getChildComponent();
details.replaceComponent(com, tf);
tf.setValue(label.getValue());
}
}
});
In future I want to enable click on label, edit it and write changes to database after clicking somewhere else (on different label for example).
Now when I click on 1st label and then on 2nd label, effect is: 1st has value of 2nd and 2nd is text field witch value of 2nd. Why it's going that way? What should i do to after clicking on 1st and then 2nd get 1st label witch value of 1st?
You don't need to swap between Labels and TextFields, you can just use a TextField and style it look like a Label when it's not focused.
When I tried to create click-to-edit labels, it created a ton of extra work for me. I'd discourage it (and do as Patton suggests in the comments).
However, if you're going to insist on trying to create in-place editing, you will want to do the following:
Create a new class that extends a layout (e.g. HorizontalLayout), which can swap out a label for a text field
use LayoutClickListener to removeComponent(myLabel) and addComponent(myTextField)
use BlurListener to swap back to the label
use ValueChangeListener on the text field to copy its value to the label
This is a still a bad idea because:
Users cannot see affordances as easily (they can't tell what's editable)
Users cannot use the keyboard to tab to the field they want to edit
It adds unncessary complexity (maintenance time, etc).
I would recommend, if you want in-place editing, just show the text field, and save the new value with the BlurListener.
I am looking for a way to put example text into a swing JTextField and have it grayed out. The example text should then disappear as soon as any thing is entered into that text field. Some what similar to what stackoverflow does when a user is posting a question with the title field.
I would like it if it was already a extended implementation of JTextField so that I can just drop it in as a simple replacement. Anything from swingx would work. I guess if there is not an easy way to do this my option will probably be to override the paint method of JTextField do something that way maybe.
Thanks
The Text Prompt class provides the required functionality without using a custom JTextField.
It allows you to specify a prompt that is displayed when the text field is empty. As soon as you type text the prompt is removed.
The prompt is actually a JLabel so you can customize the font, style, colour, transparency etc..:
JTextField tf7 = new JTextField(10);
TextPrompt tp7 = new TextPrompt("First Name", tf7);
tp7.setForeground( Color.RED );
Some examples of customizing the look of the prompt:
If you can use external librairies, the Swing components from Jide software have what you are looking for; it's called LabeledTextField (javadoc) and it's part of the JIDE Common Layer (Open Source Project) - which is free. It's doing what mklhmnn suggested.
How about initialize the text field with default text and give it a focus listener such that when focus is gained, if the text .equals the default text, call selectAll() on the JTextField.
Rather than overriding, put a value in the field and add a KeyListener that would remove the value when a key stroke is registered. Maybe also have it change the foreground.
You could wrap this up into your own custom JTextField class that would take the default text in a constructor.
private JLabel l;
JPromptTextField(String prompt) {
l = new JLabel(prompt, SwingConstants.CENTER);
l.setForeground(Color.GRAY);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (this.getText().length() == 0) {
// Reshape the label if needed, then paint
final Rectangle mine = this.getBounds();
final Rectangle its = l.getBounds();
boolean resized = (mine.width != its.width) || (mine.height != its.height);
boolean moved = (mine.x != its.x) || (mine.y != its.y);
if (resized || moved)
l.setBounds(mine);
l.paint(g);
}
}
You can't do that with a plain text field, but you can put a disabled JLabel on top of the JTextField and hide it if the text field gets the focus.
Do it like this:
Define the string with the initial text you like and set up your TextField:
String initialText = "Enter your initial text here";
jTextField1.setText(initialText);
Add a Focus Listener to your TextField, which selects the entire contents of the TextField if it still has the initial value. Anything you may type in will replace the entire contents, since it is selected.
jTextField1.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusGained(java.awt.event.FocusEvent evt) {
if (jTextField1.getText().equals(initialText)) {
jTextField1.selectAll();
}
}
});
I have a JTextPane where I want to restrict the user to enter a message with only 200 characters. So, I have a KeyListener which listens for a Keyevent and checks for a KeyEvent. If the message is more than 200 characters, a JOptionPane.showMessageDialog is shown to display a warning to the user. This bit works fine.
The problem is that once the warning is displayed and the users clicks on 'OK' he can only use the Backspace key in the JTextPane. I want the user to be able to use the delete key, the arrow keys, the shift and control keys to be able to select the text to be deleted.
Can anybody suggest a way of achieving this??
// Add Key Listener to Send Field
chatEditorKeyListener = new KeyAdapter()
{
public void keyPressed(KeyEvent e)
{
checkKeystroke(e);
}
};
private void checkKeystroke(KeyEvent e)
{
//Check if enter or back space is entered
if( e.getKeyCode() != KeyEvent.VK_BACK_SPACE && e.getKeyCode() != KeyEvent.VK_ENTER )
{
// user is typing, so test the size as we go and report when we hit boundary
String text = messageBox.getText();
if(text.length() > maxMessageSize)
{
showAlertBox();
}
}
else if ( e.getKeyCode() == KeyEvent.VK_ENTER)
{
//User sending the message
e.consume();
String text = messageBox.getText();
if(text.length() > maxMessageSize)
{
showAlertBox();
}
Drag-and-drop. Copy-and-paste. Accessibility input methods. There are many reasons why this approach is not appropriate.
Instead restrict the contents through the Document. Set a DocumentFilter through AbstractDocument.setDocumentFilter so that you do not need to subclass or implement the document.
A pop up is not great for user experience. Be more subtle. Not allowing any more character will do (please don't beep!). Possibly add a countdown as twitter and stackoverflow do.
Read the section from the Swing tutorial on "Text Component Features" which contains a section on "Implementing a Document Filter" that does exactly what you want.
Test if the current size+1 hits the boundary, consume the event and show the message box.
It's important to comsume the event to never actually oversize your text box!
I am making a component in SWT that contains a text field and a list. Whenever text is entered it filters the list. So far everything is working great and I am just trying to add some nice usability features.
What I want to do is listen for any key events in the List field, if it is Enter that is pressed I perform the 'ok' action (already done) but otherwise I want focus to change to the text field and have the key event triggered there. Basically, if the focus is on the List field and the user types something I want it to be automatically typed into the text field.
Responding to the keyPressed or keyReleased event is fine for setting the focus to the text field, but I need to then repeat the keyEvent somehow so that whatever was typed is actually entered. Any ideas?
So this is what I did:
itemList.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent e) {
if (e.keyCode == '\r' || e.keyCode == SWT.KEYPAD_CR) {
okButtonAction();
} else if (e.keyCode == SWT.ARROW_UP || e.keyCode == SWT.ARROW_DOWN || e.keyCode == SWT.ARROW_LEFT || e.keyCode == SWT.ARROW_RIGHT) {
super.keyReleased(e);
} else if (e.character > 0) {
filterInput.setFocus();
Event event = new Event();
event.type = SWT.KeyDown;
event.keyCode = e.keyCode;
event.character = e.character;
Display.getCurrent().post(event);
try {
Thread.sleep(10);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
event.type = SWT.KeyUp;
Display.getCurrent().post(event);
}
}
});
I read that the Display.post method was there for doing automatic GUI testing, but it works for my purpose here, so I will use it unless anyone can give me a good reason why not??
My first idea was that there may be a way to re-fire the key event (or fire a copy of it) to the text field. But that may not yield the desired result, because there are some key events you probably don't want transferred from the list to the text, e.g. pressing the Up and Down arrow keys for navigation within the list.
So you should decide which key events trigger the focus transfer. From your question, I understand that you are implementing a text filter, so you should restrict the transfer to text characters. Once you know the character that was entered, you may append it to the filter text manually (or insert at the cursor position of the text field).