I need to add a key listener to my TitelAreaDialog is there any solution to do this ?
You can add a Listener to the Display by using:
Listener listener = new Listener() {
public void handleEvent(Event event) {
System.out.println(event.character);
}
}
getShell().getDisplay().addFilter(SWT.KeyDown, listener);
This will output all pressed keys without consuming the events, i.e. the underlying widgets will still register the events.
Remember to remove it again in the close() method of the Dialog:
#Override
public boolean close()
{
getShell().getDisplay().removeFilter(SWT.KeyDown, listener);
super.close();
}
Related
I want to create a JDialog where the text in the textfields is selected but only if the focus is gained from keyboard (TAB, CTRL+TAB). I have found several topics on this matter but had problems with implementing it.
Here is one which I was trying.
And my code:
public class Dialogg extends JDialog implements FocusListener, MouseListener {
private boolean focusFromMouse = false;
public Dialogg() {
JTextField tf1 = new JTextField("text1");
JTextField tf2 = new JTextField("text2");
tf1.addMouseListener(this);
tf2.addMouseListener(this);
tf1.addFocusListener(this);
tf2.addFocusListener(this);
}
#Override
public void focusGained(FocusEvent e) {
if (!focusFromMouse) {
JTextField tf = (JTextField) e.getComponent();
tf.selectAll();
focusFromMouse = true;
}
}
#Override
public void focusLost(FocusEvent e) {
focusFromMouse = false;
}
#Override
public void mouseClicked(MouseEvent e) {
focusFromMouse = true;
}
}
It does not work as intended, it does not matter what is focus source the text always highlights. When I run the code and follow it step by step it turns out that focusGained code happens before mouseClicked code so the flag is not reset when it should. Any hints?
EDIT:
As suggested by M. Prokhorov I have deleted less relevant (for the question) lines from the code.Thank you.
EDIT 2:
I am trying to wrap focus listener as suggested by camickr. It looks like this now:
tf1.addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent evt){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
if (!focusFromMouse){
tf1.selectAll();
focusFromMouse=true;
}
}
});
}
public void focusLost(FocusEvent evt){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
focusFromMouse=false;
}
});
}
});
public void mouseClicked(MouseEvent e) {
focusFromMouse=true;
I am printing line after each event to see the action order and still mouseClicked happens last. What am I doing wrong?
EDIT 3:
OK, I have found a solution which fulfils requirements of my simple Dialog.
I could not find a way of doing this with use of invokeLater or EventQueue. Vladislav's method works but as I understand it restricts the user to only use the keyboard.
I have used the initial approach but I have added an auxiliary variable and few conditions which allow to pass the flag "unharmed" trough Events that should not change the flag at given moment. It may not be subtle or universal but works for my app. Here is the code:
public void focusGained(FocusEvent e) {
if(!focusFromMouse){
if (higlight){
JTextField tf = (JTextField) e.getComponent();
tf.selectAll();
focusFromMouse=false;
}
}
}
public void focusLost(FocusEvent e) {
if (focusFromMouse){
higlight=false;
focusFromMouse=false;
}else{
higlight=true;
}
}
public void mousePressed(MouseEvent e) {
focusFromMouse=true;
}
At the first, by default, focus on JTextField is requested by mouse-press event, not by mouse-click.
So, this method:
public void mouseClicked(MouseEvent e) {
focusFromMouse = true;
}
is useless because the mouse-click event is triggered after the mouse-press event.
One way to solve your problem is to remove all native MouseListeners from JTextField:
...
for( MouseListener ml : tf1.getMouseListeners() ){
tf1.removeMouseListener(ml);
}
for( MouseMotionListener mml : tf1.getMouseMotionListeners() ){
tf1.removeMouseMotionListener(mml);
}
...
Another way is to handle all mouse events and consume those of them, which are triggered by JTextField:
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
if( event.getSource() == tf1 ){
((MouseEvent)event).consume();
}
}
}, AWTEvent.MOUSE_EVENT_MASK);
When I run the code and follow it step by step it turns out that focusGained code happens before mouseClicked
Wrap the code in the FocusListener in a SwingUtilities.invokeLater(). The will place the code on the end of the Event Dispatch Thread (EDT), so the code will run after the variable in the MouseListener has been set.
See Concurrency in Swing for more information about the EDT.
Edit:
Just noticed the other answer. You might be able to do something simpler. Istead of listener for mouseClicked, listen for mousePressed. A mouseClicked event is only generated AFTER the mouseReleased event, so by that time the FocusListener logic has already been executed, even when added to the end of the EDT.
Edit 2:
If the above doesn't work then you might be able to use the EventQueue.peek() method to see if a MouseEvent is on the queue. This might even be easier than worrying about using the invokeLater.
In my project I need to fire an event after a node was clicked in my CellTree. I solved this with the following code.
model.setSelectionHandler(new SelectionChangeEvent.Handler()
{
#Override
public void onSelectionChange(SelectionChangeEvent event)
{
//My logic is here
}
});
The problem is that this only works if the node is not selected already. Clicking the node again will not fire the event. Is there a click handler or another event which is fired after a node was clicked?
Please try with SelectionHandler api .
This can be achieved by creating your own TreeItem that implements ClickHandler
public class CustomTreeItem extends TreeItem implements ClickHandler
{
//classes logic here
#Override
public void onClick(ClickEvent event)
{
// TODO Auto-generated method stub
}
}
Sometimes I use something like this:
model.setSelectionHandler(new SelectionChangeEvent.Handler()
{
#Override
public void onSelectionChange(SelectionChangeEvent event)
{
SomeType selected = model.getSelectedObject();
if (selected != null)
{
// Logic here...
model.clear();
}
}
});
But this solution obviously removes visual feedback what was selected.
You could add a DOM handler to your CellTree using [Widget.addDomHandler](http://www.gwtproject.org/javadoc/latest/com/google/gwt/user/client/ui/Widget.html#addDomHandler(H, com.google.gwt.event.dom.client.DomEvent.Type)):
cellTree.addDomHandler(new ClickHandler()
{
#Override
public void onClick(ClickEvent event)
{
// TODO: check if a node is selected and it was clicked here
}
}, ClickEvent.getType());
Additionally you might need to prevent calling the handler twice if you use the selection handler as well and click a node.
Just a side note: Unfortunately I did not see an easy way to determine if the user actually clicked on a tree item like a bounds check for the click coordinates. So this might get a bit harder to achieve.
I have many ItemWidget which extends a Composite. If a click event is received on one of the items a change event should be fired and other item widgets should receive this event.
It tried the following:
public class ItemWidget extends Composite implements HasChangeHandlers {
FocusPanel focusPanel = new FocusPanel();
public ItemWidget() {
Label label = new Label("click me");
focusPanel.add(label);
initWidget(focusPanel);
focusPanel.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
// inform other items
fireChange();
}
});
addChangeHandler(new ChangeHandler() {
#Override
public void onChange(ChangeEvent event) {
GWT.log("ChangeEvent received");
}
});
}
private void fireChange() {
GWT.log("fire event");
NativeEvent nativeEvent = Document.get().createChangeEvent();
ChangeEvent.fireNativeEvent(nativeEvent, this);
}
#Override
public HandlerRegistration addChangeHandler(ChangeHandler handler) {
return addDomHandler(handler, ChangeEvent.getType());
}
}
Using the above code only the item which is clicked receives the ChangeEvent.
How can I receive the ChangeEvent on all the other item widgets too?
Typically, when an event fires, a presenter/Activity makes the necessary changes to the other widgets.
If you want multiple copies of you widget to listen to the same event, you may be better off using the EventBus, especially if you use this pattern more than once:
How to use the GWT EventBus
Then each of your ItemWidget can fire a custom event that all copies of this widget listen to.
By looking at the API quickly I think your problem comes from your implementation of
public void fireEvent(GwtEvent<?> event)
You did not override it in your composite view. You did not create any mechanism to send the event to the other widgets.
Have a deeper look at the method fireNativeEvent
http://www.gwtproject.org/javadoc/latest/com/google/gwt/event/dom/client/DomEvent.html
When you use ChangeEvent.fireNativeEvent the second param hasHandlers should be a kind of event bus.
To do what you want to do I think you need all the items to be on this eventBus.
When I create my TableViewer in my dialog class. I am adding a ListChangeListener. This listener listens to a ObservableList in my Data Model Class.
This is my createTableViewer method in the dialog class.
private void createTableViewer(Composite parent) {
viewer = new AplotDataTableViewer(parent, SWT.BORDER|SWT.V_SCROLL|SWT.FULL_SELECTION);
IObservableList iob = AplotDataModel.getInstance().getObservableList();
viewer.setInput(iob);
iob.addListChangeListener(new IListChangeListener() {
#Override
public void handleListChange(ListChangeEvent event) {
updateTableViewer();
}
});
}
So what is happening. When a user Closes the dialog using the Window Close Button (Red X).
That is disposing all the widgets and closing the window. When the Dialog is opened back.
The ListChangeListener is looking to the updateTableViewer, but the widgets in the update is already disposed.
Right now there are 2 ways to close the dialog.
1. Red X - maybe doing a Window.close()
2. My close button on the form.
#Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.OK_ID, "Close Aplot",
true);
}
#Override
protected void okPressed() {
getShell().setVisible(false);
}
Which is using okPressed and hiding the shell.
What I would like is to have both methods of closing the dialog the same.
Is it possible to add a listener to the Shell and in the handleEvent method. Have a call to the okPressed method?
getShell().addListener(SWT.Close, new Listener() {
#Override
public void handleEvent(Event e) {
okPressed();
}
});
Instead of SWT.Close should I be using Window.Close?
Should I be using Close_ID instead of ok_ID
#Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.CLOSE_ID, "Close Aplot",
true);
}
#Override
protected void closePressed() {
getShell().setVisible(false);
}
Is there a way to get my active ListChangeListener and remove it?
protected void closePressed() {
AplotDataModel.getInstance().getObservableList().removeListChangeListener(this);
}
I am not sure how to get active listeners?
I want to Close the Dialog either using the Windows Close Button (Red X) or the Close Button on the form. If possible I wish both actions would use the same code to remove the active Listener from my IObservableList and close the dialog.
Have you tried adding a DisposeListener to the window? The dispose listener can then unregister any event listeners you set on its controls. This will happen regardless of how the window is closed, either from the red X or by calling shell.close() in the okPressed() method.
For example:
private void createTableViewer(Composite parent) {
viewer = new AplotDataTableViewer(parent, SWT.BORDER|SWT.V_SCROLL|SWT.FULL_SELECTION);
final IObservableList iob = AplotDataModel.getInstance().getObservableList();
viewer.setInput(iob);
final IListChangeListener listener = new IListChangeListener() {
#Override
public void handleListChange(ListChangeEvent event) {
updateTableViewer();
}
};
iob.addListChangeListener(listener);
getShell().addDisposeListener(
new DisposeListener() {
#Override public void widgetDisposed(DisposeEvent e) {
iob.removeListChangeListener(listener);
}
});
}
I'm making a level editor for my game. I have a property panel where I can modify the selected object its properties. I also have a Save button to write the level xml.
A field-edit is submitted(*) when the editor component lost the focus or Enter is pressed. This is working great, but the only problem is that when I have this sequence of actions:
Edit a field
Press the save button
Because, what happens is this:
I edit the field
I press the save button
The level is saved
The field lost the focus
The edit is submitted
As you can see, this is the wrong order. Of course I want the field to lose its focus, which causes the submit and then save the level.
Is there a trick, hack or workaround to make the field first lose the focus and then perform the action listener of the save button?
Thanks in advance.
(* submit = the edit to the field is also made in the object property)
EDIT: For the field I'm using a FocusAdapter with focusLost:
FocusAdapter focusAdapter = new FocusAdapter()
{
#Override
public void focusLost(FocusEvent e)
{
compProperties.setProperty(i, getColor());
record(); // For undo-redo mechanism
}
};
And for the button a simple ActionListener with actionPerformed`.
btnSave.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
// Save the level
}
});
Hmm ... can't reproduce: in the snippet below the lost is always notified before the actionPerfomed, independent on whether I click the button or use the mnemonic:
final JTextField field = new JTextField("some text to change");
FocusAdapter focus = new FocusAdapter() {
#Override
public void focusLost(FocusEvent e) {
LOG.info("lost: " + field.getText());
}
};
field.addFocusListener(focus);
Action save = new AbstractAction("save") {
#Override
public void actionPerformed(ActionEvent e) {
LOG.info("save: " + field.getText());
}
};
save.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_S);
JButton button = new JButton(save);
JComponent box = Box.createHorizontalBox();
box.add(field);
box.add(button);
On the other hand, focus is a tricky property to rely on, the ordering might be system-dependent (mine is win vista). Check how the snippet behave on yours.
If you see the same sequence as I do, the problem is somewhere else
if you get the save before the lost, try to wrap the the save action into invokeLater (which puts it at the end of the EventQueue, so it's executed after all pending events)
Action save = new AbstractAction("save") {
#Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
LOG.info("save: " + field.getText());
}
});
}
};
Normally, wrapping your save code into an SwingUtilities.invokeLater() should do the trick. As you already mentioned, this doesn't work? Try this:
private boolean editFocus = false;
FocusAdapter focusAdapter = new FocusAdapter()
{
#Override
public void focusGained(FocusEvent e){
editFocus = true;
}
#Override
public void focusLost(FocusEvent e){
compProperties.setProperty(i, getColor());
record(); // For undo-redo mechanism
editFocus = false;
if (saveRequested){
save();
}
}
};
and for your button:
private boolean saveRequested = false;
btnSave.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
if (editFocus){
saveRequested = true;
return;
} else {
save();
}
}
});
and then your save method:
private void save(){
// do your saving work
saveRequested = false;
}
This only works when your focusLost gets called after your button's action. If suddenly the order is correct, this code will get save() called twice.
But again, wrapping your save() code in your original approach should work, because the save code will execute after processing all events. That is after processing your button click and your focusLost events. Because your focusLost code executes immediately (it's not wrapped in an invokeLater()), the focusLost code should be executed always before your save code. This does not mean that the event order will be correct! But the code associated to the events will executed in the right order.