gwt: How to use setEventListener? - java

I have this code:
DOM.setEventListener(row.getElement(), new ClickListener(){
#Override
public void onClick(Widget sender) {
// TODO Auto-generated method stub
}});
I think the code is fine and ClickListener extends EventListener, but it gives error saying: The method setEventListener(Element, EventListener) in the type DOM is not applicable for the arguments (Element, new ClickListener(){})

The real answer is that you probably don't. While this is available to attach listeners to events, you may only attach a single listener per element - that listen then gets all dom events that have been configured (see DOM.sinkEvents) - and you are responsible for making sure to detach all listeners before the page unloads, else some browsers will leak memory.
Instead, strongly consider using a Widget (and subclasses) to manage events. RootPanel, the base widget that others should be added to, will manage detaching all other widgets from the page to prevent memory leaks.
Additionally, you are able to listen to the events that happen within there based on the kind of event you are after. For example, even on a widget like a Label that doesnt' normally fire mouseover events, you can still attach handlers and get notification:
Label label = new Label();
label.addDomHandler(new MouseOverHandler() {
#Override
public void onMouseOver(MouseOverEvent event) {
// do something
}
}, MouseOverEvent.getType());
RootPanel.get().add(label);
In most cases, you'll be using existing support methods, like Button to get a click event - there are convinience methods already there for you, thanks to interfaces like HasClickHandlers:
Button button = new Button();
button.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
// do something
}
});
panel.add(button);
More on GWT, Widget, and Memory leaks:
https://developers.google.com/web-toolkit/articles/dom_events_memory_leaks_and_you
http://code.google.com/p/google-web-toolkit/wiki/UnderstandingMemoryLeaks

Related

GWT weird onblur handler behaviour

Hi i am using focus and blur handlers for a flow panel to add outlines. However, i am getting some weird behavior given the code :
panel.addDomHandler(new FocusHandler() {
#Override
public void onFocus(FocusEvent focusEvent) {
panel.addStyleName("FOOSTYLE");
}
}, FocusEvent.getType());
panel.addDomHandler(new BlurHandler() {
FlowPanel _panel = focusPanel;
boolean init = false;
#Override
public void onBlur(BlurEvent blurEvent) {
console.log("Do Blur");
panel.removeStyleName("FOOSTYLE");
}
}, BlurEvent.getType());
The removeStyleName() in the blur handler seems to be called for no reason, also note the log, it's not even executed when the random blur handler gets called, if ever it was.
any suggestions?
To handle focus and blur events you should use FocusPanel. It implements HasFocusHandlers and HasBlurHandlers so you can call addFocusHandler() and addBlurHandler() instead of low-level addDomHandler() method.
If you need to use FlowPanel to lay out your widgets you can add it to the FocusPanel.

GXT - Can't reach onscroll event handler on text area

I'm trying to have an handler on Text Area(gxt) to get to know when user reach the top of text area.
TextArea logTextArea = new TextArea();
logTextArea.setReadOnly(true);
logTextArea.addDomHandler(new ScrollHandler() {
#Override
public void onScroll(ScrollEvent event) {
InputElement textAreaElement = logTextArea.getCell().getInputElement(logTextArea.getElement());
int scrollTop = textAreaElement.getScrollTop();
}
}, ScrollEvent.getType());
VerticalLayoutContainer dataContainer = new VerticalLayoutContainer();
HorizontalLayoutContainer secondRow = new HorizontalLayoutContainer();
secondRow.add(logTextArea, new HorizontalLayoutData(1, 1, new Margins(5, 10, 5, 10)));
dataContainer.add(secondRow, new VerticalLayoutData(1, 0.5));
add(dataContainer);//this class extends ContentPanel
This handler is never called on scroll, but I also tried with a lot of other events, like mouseover, mousewhell, mouseclick ... and all of these events worked. Can somebody help with any idea?
AFAIK, scroll event will not work in cell based widgets out of the box if the event's target and the widget are not the same elements.
And GXT's TextArea is a widget that has such DOM structure.
That's all because scroll event is a "non bubbling" event.
AFAIK, GWT widgets, that uses cells, have a special handling for non bubbling events to be dispatched through GWT events system.
And the list of types of supporting non bubbling events are too short and limited to focus, blur, load and error events.
See CellBasedWidgetImplStandard class for details.
First solution, that I may suggest, is to explicitly assign onscroll handler for textarea.
For example:
Event.sinkEvents(textAreaElement, Event.getEventsSunk(textAreaElement) | Event.ONSCROLL);
here textAreaElement - is the textarea DOM element.
But this should be done every time when the cell rerenders its content (e.g. when setValue method called for TextArea widget).
Other solution is a bit hacky and uses private api implementation, but could be applied only once to cell based widget. You can do the same things as done in CellBasedWidgetImplStandard.sinkEvent(Widget, String) e.g. :
WidgetHelper.sinkEvent(logTextArea.getElement(), BrowserEvents.SCROLL);
where WidgetHelper may looks like this:
public class WidgetHelper {
public static void sincEvent(Element element, String typeName){
element.setAttribute("__gwtCellBasedWidgetImplDispatching" + typeName, "true");
sinkEvent0(element, typeName);
}
private static native void sinkEvent0(Element element, String typeName) /*-{
element.addEventListener(typeName,
#com.google.gwt.user.cellview.client.CellBasedWidgetImplStandard::dispatchNonBubblingEvent, true);
}-*/;
}
Probably, this is a subject to create an issue in GWT project.

SWT: Influence the detection of a Drag&Drop gesture (or: how to query the keyboard)

I am at an SWT application where one can rearrange controls within a shell (or any Composite for that matter) via drag&drop. That's basically no problem, DragSources and DropTargets are all in place and listeners attached accordingly. I even implemented a custom Transfer type for the sake of exercise. Pretty straightforward.
But now the requirement is that a drag should only be initiated, if the ALT key is pressed while the drag gesture is performed, otherwise nothing should be done. (The ALT key is an example, could be CTRL as well.)
So far, I see or have thought about the following approaches. All of them either don't work or are ugly.
\1. Intercept and cancel the DragDetect event
The idea is to cancel the event if the ALT key is not pressed with event.doit = false.
lblPos.addListener(SWT.DragDetect, new Listener() {
public #Override void handleEvent(Event event) {
if ((event.stateMask & SWT.ALT) == 0)
event.doit = false; // XXX: doit will not be evaluated
}
});
However, that doesn't work. The doit flag is apparently not evaluated.
\2. Intercept and cancel the DND.DragStart event.
class RowDragListener implements DragSourceListener {
public #Override void dragStart(DragSourceEvent event) {
if (/* ALT key not pressed */)
event.doit = false;
}
...
}
This has the opposite problem of appraoch 1. While the doit flag is properly evaluated and thus suitable to cancel the drag, there is no stateMask in the event that can be inspected for modifier keys. So the question arises, how can I query the keyboard directly (without installing KeyUp/Down event handlers)? What is the current up/down state of the ALT key?
\3. Combine 1 and 2
Inspect the stateMask in the DragDetect event, store the result somewhere, then react accordingly in the DND.DragStart event. This shouldn't be too hard, but I think it's ugly and should not be done this way. Instead of DragDetect, KeyUp/Down events could be captured and the last known state of the ALT key be stored.
\4. Override Control.dragDetect(Event) or Control.dragDetect(MouseEvent)
These methods ultimately create DragDetect events if they see the conditions for it fulfilled.
Check the event's stateMask and invoke the overridden method from the super class only if the desired modifier key is signalled. Problem here is, from the documentation it is not clear if this is the only code path that is treaded upon a drag gesture. In fact, these two methods are independent from each other (they don't invoke each other), so it's not even clear which one to override. These methods already are two separate ways to initiate a drag gesture. Who knows how many more ways are there? Overriding them all would be error prone, if possible at all, and certainly not clean.
So my questions are:
1. How would you do that? Any other ideas?
2. If approach 2 seems the most reasonable, how is the keyboard queried without resorting to event handlers?
(Sorry for the formatting of this post, i seem to be unable to grasp the syntax. Or maybe it's not my fault, who knows.)
UPDATE: There's one thing to note, which i noticed during the implementation. On Windows, ALT-Drag&Drop has the specific meaning of a link operation (as opposed to move or copy; cmp. DND.DROP_* constants). That's why, if you choose to use the ALT key in a similar fashion, be advised to include the following line at every reasonable occasion in the DropTargetListener.
if (event.detail == DND.DROP_LINK) event.detail = DND.DROP_MOVE;
I have this in the dragEnter, dragOver and dragOperationChanged listener methods and this works quite fine.
You can listen to SWT.DragDetect event, check the state mask and create the drag source only if conditions are met. Then pass the event to the newly created drag source by calling notifyListeneres(). After drag finishes the drag source has to be disposed.
Here is a snippet where drag is initiated only if alt is pressed, and uses text as transfer:
public static void main(String[] args) {
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setLayout(new GridLayout());
shell.addListener(SWT.DragDetect, new Listener() {
#Override
public void handleEvent(Event event) {
if ((event.stateMask & SWT.ALT) != 0) {
final DragSource dragSource = new DragSource(shell, DND.DROP_MOVE);
dragSource.addDragListener(new DragSourceAdapter(){
#Override
public void dragFinished(DragSourceEvent event) {
dragSource.dispose();
}
#Override
public void dragSetData(DragSourceEvent event) {
event.data = "text";
}
});
dragSource.setTransfer(new Transfer[]{TextTransfer.getInstance()});
dragSource.notifyListeners(SWT.DragDetect, event);
}
}
});
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}

Fire selection event from code

I am writing an RCP application in eclipse that contains a combobox, and upon selecting any of its items, a selection event is being fired and some random code comes in action. The listener looks something like this:
randomComboBox.addSelectionListener(new SelectionListener(){
#Override
public void widgetSelected(SelectionEvent e) {
// random code
}
#Override
public void widgetDefaultSelected(SelectionEvent e) {
// TODO Auto-generated method stub
}
});
My question is: is it possible to fire the event from the code? For example if I add:
randomComboBox.select(0);
no event is being fired. In this case, do I have to write my own listener?
The select method of the combo box sends an event of the type SWT.Modify when it changes the selection, so you could use a ModifyListener instead of a SelectionListener.
Actually, the ModifyListener listens to changes in the text field of the combo box, this means it reacts to the text change that is caused by the selection. This also means that it will be fired if that text is changed by other paths (e.g. user entries in the combo text field).
Keeping that behaviour in mind, a ModifyListener might be an option.
Do not use ModifyListener if your ComboBox is "Read Only"
Combo comboBoxColor = new Combo(composite, SWT.BORDER | SWT.READ_ONLY);
You can fire any event explicitly (Programmatically)
For e.g.
control.notifyListeners(eventType , event);
In your case:
comboBoxColor.notifyListeners(SWT.Selection, new Event())
SWT.Selection -> Event type, you can get all event constants from SWT class.
new Event() -> Event object

How to Subscribe to GUI Events in Other JFrames

What is the best practice for subscribing to events from another JFrame? For example, I have a "settings" form, and when the user presses okay on the settings form, I want the main form to know about this so it can retrieve the settings.
Thanks.
Here is my ideal interface:
public void showSettingsButton_Click() {
frmSettings sForm = new sForm(this._currentSettings);
//sForm.btnOkay.Click = okayButtonClicked; // What to do here?
sForm.setVisible(true);
}
public void okayButtonClicked(frmSettings sForm) {
this._currentSettings = sForm.getSettings();
}
Someone publishes an Event, that something has changed, here the settings. A subscriber that registered for this specifig event, gets notified about it and can do his work, here get the settings. This is called publisher/subscriber.
For this you can use Eventbus or implementing something smaller on your own.
One approach is to have only a single JFrame. All the other 'free floating top level containers' could be modal dialogs. Access the the main GUI will be blocked until the current dialog is dismissed, and the code in the main frame can check the settings of the dialog after it is dismissed.
For anyone interested, here is what I ended up going with. I'm not sure if it's the best way, but it is working for my purposes.
// Method called when the "Show Settings" button is pressed from the main JFrame
private void showSettingsButton_Click() {
// Create new settings form and populate with my settings
frmSettings sForm = new frmSettings(this.mySettings);
// Get the "Save" button and register for its click event...
JButton btnSave = sForm.getSaveButton();
btnSave.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent evt) {
SaveSettings(sForm);
}
});
// Show the settings form
sForm.setVisible(true);
}
// Method called whenever the save button is clicked on the settings form
private void SaveSettings(frmSettings sForm) {
// Get the new settings and assign them to the local member
Settings newSettings = sForm.getSettings();
this.mySettings = newSettings;
}
And if, like me, you are coming from a .NET perspective, here is the C# version:
private void showSettingsButton_Click(object sender, EventArgs e)
{
frmSettings sForm = new frmSettings(this.mySettings);
sForm.btnSave += new EventHandler(SaveSettings);
sForm.Show();
}
private void SaveSettings(object sender, EventArgs e)
{
frmSettings sForm = (frmSettings)sender; // This isn't the exact cast you need..
Settings newSettings = sForm.Settings;
this.mySettings = newSettings;
}

Categories

Resources