I am using the android's custom keyboard to build a keypad. The OnKeyboardActionListener in android returns the "keyCode" of the key being pressed. But I want in particular, the onscreen x and y coordinates of that key ( with or without using the keyCode ).
P.S : Even coordinates of the center of the key would help. I have found a method "squaredDistanceFrom" which returns the squared distance between the center of the key and point where the screen is touched. Is there a function which returns the center's coordinates?
I know this question is quite old now, but I have been working on a project over the past two weeks with similar requirements and I struggled finding a solution. So here is what I propose:
Build a working keyboard
First, let start from a functional keyboard. Some very useful resources to get started:
The official android API guide and the sample from the sdk. The later can be compiled and used without modification.
A first tutorial that helps building a simple keyboard, and another one with lots of explanations.
Get the coordinates of the key pressed
I haven't found a direct way of retrieving the coordinates from the key pressed in the API.
However, we can determine and retrieve the actual instance the key pressed. First we need to create an onTouchListener on the KeyboardView. This will allow us to retrieve a MotionEvent and consequently the coordinates of the touch event within the view.
Then, there is a method in the Keyboard class which is called getKeys returning a list of Keys. We can then use the isInside method from the Key class to verify that the coordinates retrieved previously are inside of the key or not, and thereby retrieving the instance of the key pressed.
We need to retrieve the list of keys from the current keyboard every time is is displayed. This way, even if the user swaps from one layout to another (by using symbols or numbers for instance), we get the correct set of keys. We can do so by overriding the onStartInputView method from our class.
Sample
Retrieve the keys:
public void retrieveKeys() {
keyList = kv.getKeyboard().getKeys();
}
Override the onStartInputView which is called when the keyboard is displayed:
#Override
public void onStartInputView(EditorInfo info, boolean restarting) {
super.onStartInputView(info, restarting);
retrieveKeys();
}
Create an onTouchListener for the KeyboardView:
#Override
public View onCreateInputView() {
kv = (KeyboardView)getLayoutInflater().inflate(R.layout.keyboard, null);
kv.setOnKeyboardActionListener(this);
keyboard = new Keyboard(this, R.xml.qwerty);
kv.setKeyboard(keyboard);
retrieveKeys();
// Set the onTouchListener to be able to retrieve a MotionEvent
kv.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// For each key in the key list
for (Keyboard.Key k : keyList) {
// If the coordinates from the Motion event are inside of the key
if (k.isInside((int) event.getX(), (int) event.getY())) {
// k is the key pressed
Log.d("Debugging",
"Key pressed: X=" + k.x + " - Y=" + k.y);
int centreX, centreY;
centreX = (k.width/2) + k.x;
centreY = (k.width/2) + k.x;
// These values are relative to the Keyboard View
Log.d("Debugging",
"Centre of the key pressed: X="+centreX+" - Y="+centreY);
}
}
// Return false to avoid consuming the touch event
return false;
}
});
return kv;
}
Entire example (without the package and imports):
public class MyTestKeyboard extends InputMethodService
implements KeyboardView.OnKeyboardActionListener {
private KeyboardView kv;
private Keyboard keyboard;
private boolean caps = false;
private List<Keyboard.Key> keyList;
public void retrieveKeys() {
keyList = kv.getKeyboard().getKeys();
}
#Override
public void onStartInputView(EditorInfo info, boolean restarting) {
super.onStartInputView(info, restarting);
retrieveKeys();
}
#Override
public View onCreateInputView() {
kv = (KeyboardView)getLayoutInflater().inflate(R.layout.keyboard, null);
kv.setOnKeyboardActionListener(this);
keyboard = new Keyboard(this, R.xml.qwerty);
kv.setKeyboard(keyboard);
retrieveKeys();
// Set the onTouchListener to be able to retrieve a MotionEvent
kv.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// For each key in the key list
for (Keyboard.Key k : keyList) {
// If the coordinates from the Motion event are inside of the key
if (k.isInside((int) event.getX(), (int) event.getY())) {
// k is the key pressed
Log.d("Debugging",
"Key pressed: X=" + k.x + " - Y=" + k.y);
int centreX, centreY;
centreX = (k.width/2) + k.x;
centreY = (k.width/2) + k.x;
// These values are relative to the Keyboard View
Log.d("Debugging",
"Centre of the key pressed: X="+centreX+" - Y="+centreY);
}
}
// Return false to avoid consuming the touch event
return false;
}
});
return kv;
}
#Override
public void onKey(int primaryCode, int[] keyCodes) {
InputConnection ic = getCurrentInputConnection();
switch(primaryCode){
case Keyboard.KEYCODE_DELETE :
ic.deleteSurroundingText(1, 0);
break;
case Keyboard.KEYCODE_SHIFT:
caps = !caps;
keyboard.setShifted(caps);
kv.invalidateAllKeys();
break;
case Keyboard.KEYCODE_DONE:
ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
break;
default:
char code = (char)primaryCode;
if(Character.isLetter(code) && caps){
code = Character.toUpperCase(code);
}
ic.commitText(String.valueOf(code),1);
}
}
#Override
public void onPress(int primaryCode) {}
#Override
public void onRelease(int primaryCode) {}
#Override
public void onText(CharSequence text) {}
#Override
public void swipeLeft() {}
#Override
public void swipeRight() {}
#Override
public void swipeDown() {}
#Override
public void swipeUp() {}
}
Note: Tested on a Motorola Moto G XT1039 Android 5.1 API 22
Related
I want to create a Cell factory that returns a TableCell that behaves exactly like TextFieldTableCell, with the following difference: When it loses focus, it commits the changes.
My code is very simple:
public final class TextFieldCellFactory<S, T> implements Callback<TableColumn<S, T>, TableCell<S, T>> {
#Override
public TableCell<S, T> call(TableColumn<S, T> p) {
class EditingCell extends TextFieldTableCell {
public EditingCell() {
super();
setConverter(new DefaultStringConverter());
focusedProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
System.out.println("changed!");
System.out.println("getText() = " + getText());
System.out.println("textProperty() = " + textProperty().get());
System.out.println("getItem = " + getItem());
}
});
}
#Override
public void startEdit() {
super.startEdit();
}
#Override
public void cancelEdit() {
super.cancelEdit();
}
}
return new EditingCell();
}
}
As you see I add a change listener in the focusedProperty. The problem is that the change method is not called (nothing is printed).
How can I get the desired behaviour? Thank you.
Basically, you have to register the listener with the textField's (not the cell's) focusedProperty. As the textfield is a private field of super, it's not directly accessible - you have to look it up once after it was added to the cell. That's when an edit was started for the first time:
private TextField myTextField;
#Override
public void startEdit() {
super.startEdit();
if (isEditing() && myTextField == null) {
// most simple case, assuming that there is no graphic other than the field
// TBD: implement the general case: walk the tree and find the field
myTextField = (TextField) getGraphic();
myTextField.focusedProperty().addListener((e, old, nvalue) -> {
if (!nvalue) {
T edited = getConverter().fromString(myTextField.getText());
commitEdit(edited);
}
});
}
}
Some notes:
this is a workaround around an open issue (vote for it!)
since jdk8, it's not entirely functional: won't commit if you click somewhere else inside the table
a recent answer uses a binding approach which might or not be fully functional (didn't test)
I have a SuggestBox. I want 2 things:
-1st, when user Type in a word if the SuggestOracle suggest any word & then if i select that word by hitting the Enter key on the selected word or clicking on that word it will call a methodX
-2st, suppose I typed a word into a suggest & SuggestOracle suggests NO word, then I want that when I hit Enter key it will fire methodX
This below code met the 1st requirement.
getView().getSuggestBox().addSelectionHandler(new SelectionHandler(){
#Override
public void onSelection(SelectionEvent event) {
methodX();
}
});
This below code met the 2nd requirement.
getView().getSuggestBox().addKeyDownHandler(new KeyDownHandler(){
#Override
public void onKeyDown(KeyDownEvent event) {
if(event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
methodX();
}
}
});
However, if i use both of them, then I got this problem.
FOr example, When I type "car" the suggest oracle show "car insurance" "car buy" & then when I use arrow down key to select "car buy" & hit the Enter key, then the system call methodX("car"); not "methodX("car buy")"
WHat is the problem? I think they got conflict or something
How to fix it?
I think you simply need to add a ValueChangeHandler to the ValueBox
getView().getSuggestBox()getValueBox().addValueChangehandler( ... );
As far as I remember, this event is triggered, if you hit enter in the Box.
There is no need to register two handlers. This can be achieved by single handler as shown below that will be fired event value is selected via mouse click or by pressing enter key.
suggestionBox.addSelectionHandler(new SelectionHandler<SuggestOracle.Suggestion>() {
#Override
public void onSelection(SelectionEvent<Suggestion> event) {
System.out.println("selection changed :" + event.getSelectedItem().getDisplayString());
methodX();
}
});
If nothing works then try with below handler.
suggestionBox.getValueBox().addValueChangeHandler(new ValueChangeHandler<String>() {
#Override
public void onValueChange(ValueChangeEvent<String> event) {
System.out.println("value changed :" + event.getValue());
}
});
I think you should try to use a custom SelectionHandler class, and play with
getDisplayString and getReplacementString
here is a quick copy/paste of my code:
public class SearchStuff Composite implements SelectionHandler<ItemSuggestion> {
#Override
public void onSelection(SelectionEvent<ItemSuggestion> event) {
System.out.println(" getDisplayString( ) = " + event.getSelectedItem().getDisplayString()) ;
System.out.println("getReplacementString( ) = " + event.getSelectedItem().getReplacementString());
}
public class ItemSuggestion implements IsSerializable, com.google.gwt.user.client.ui.SuggestOracle.Suggestion{
#Override
public String getReplacementString() {
....
}
}
I am working on a Big Project and i have a lot of GWT code written. Now i am working on making the project fully compatible with Tablets like iPad and Android Tablets.
As a part of this, i have noticed that touch devices takes 300ms delay to handle click events. In this project, writing touch events again is a very tedious task. I have done a lot of researches in this and found the Google Fast Buttons API used in Google Voice Application. I tried that and its working good but requires a lot of coding and JSNI.
My question is, Is there anything else available in your knowledge to easily overcome this delay?
Here is a pure java implementation of the fast button.It doesn't include a single line of JNSI
package com.apollo.tabletization.shared.util;
import java.util.Date;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Touch;
import com.google.gwt.event.dom.client.HasAllTouchHandlers;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;
/** Implementation of Google FastButton {#link http://code.google.com/mobile/articles/fast_buttons.html} */
public class FastButton extends Composite {
private boolean touchHandled = false;
private boolean clickHandled = false;
private boolean touchMoved = false;
private int startY;
private int startX;
private int timeStart;
public FastButton(Widget child) {
// TODO - messages
assert (child instanceof HasAllTouchHandlers) : "";
assert (child instanceof HasClickHandlers) : "";
initWidget(child);
sinkEvents(Event.TOUCHEVENTS | Event.ONCLICK);
}
#Override
public Widget getWidget() {
return super.getWidget();
}
#Override
public void onBrowserEvent(Event event) {
timeStart = getUnixTimeStamp();
switch (DOM.eventGetType(event)) {
case Event.ONTOUCHSTART:
{
onTouchStart(event);
break;
}
case Event.ONTOUCHEND:
{
onTouchEnd(event);
break;
}
case Event.ONTOUCHMOVE:
{
onTouchMove(event);
break;
}
case Event.ONCLICK:
{
onClick(event);
return;
}
}
super.onBrowserEvent(event);
}
private void onClick(Event event) {
event.stopPropagation();
int timeEnd = getUnixTimeStamp();
if(touchHandled) {
//Window.alert("click via touch: "+ this.toString() + "..." +timeStart+"---"+timeEnd);
touchHandled = false;
clickHandled = true;
super.onBrowserEvent(event);
}
else {
if(clickHandled) {
event.preventDefault();
}
else {
clickHandled = false;
//Window.alert("click nativo: "+ this.toString()+ "..." +(timeStart-timeEnd)+"==="+timeStart+"---"+timeEnd);
super.onBrowserEvent(event);
}
}
}
private void onTouchEnd(Event event) {
if (!touchMoved) {
touchHandled = true;
fireClick();
}
}
private void onTouchMove(Event event) {
if (!touchMoved) {
Touch touch = event.getTouches().get(0);
int deltaX = Math.abs(startX - touch.getClientX());
int deltaY = Math.abs(startY - touch.getClientY());
if (deltaX > 5 || deltaY > 5) {
touchMoved = true;
}
}
}
private void onTouchStart(Event event) {
Touch touch = event.getTouches().get(0);
this.startX = touch.getClientX();
this.startY = touch.getClientY();
touchMoved = false;
}
private void fireClick() {
NativeEvent evt = Document.get().createClickEvent(1, 0, 0, 0, 0, false,
false, false, false);
getElement().dispatchEvent(evt);
}
private int getUnixTimeStamp() {
Date date = new Date();
int iTimeStamp = (int) (date.getTime() * .001);
return iTimeStamp;
}
}
I have tried to use the above answers and comments to take a stab at this implementation.
I have also posteds a sample GWT project which can be used for easy comparison:
http://gwt-fast-touch-press.appspot.com/
https://github.com/ashtonthomas/gwt-fast-touch-press
Please note that you will only be able to see the time saved if you are on a mobile device (or devices that handles the touch events and doesn't just fall back to onClick).
I have added 3 fast buttons and 3 normal buttons. You can easily see an improvement when on older mobile devices and sometimes less so on newer (Samsung Galaxy Nexus only showed delays of around 100ms while the 1st gen iPad was over 400ms almost every time). The biggest improvement is when you try to rapidly and consecutively click the boxes (not really buttons here but can be adapted)
package io.ashton.fastpress.client.fast;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Touch;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;
/**
*
* GWT Implementation influenced by Google's FastPressElement:
* https://developers.google.com/mobile/articles/fast_buttons
*
* Using Code examples and comments from:
* http://stackoverflow.com/questions/9596807/converting-gwt-click-events-to-touch-events
*
* The FastPressElement is used to avoid the 300ms delay on mobile devices (Only do this if you want
* to ignore the possibility of a double tap - The browser waits to see if we actually want to
* double top)
*
* The "press" event will occur significantly fast (around 300ms faster). However the biggest
* improvement is from enabling fast consecutive touches.
*
* If you try to rapidly touch one or more FastPressElements, you will notice a MUCH great
* improvement.
*
* NOTE: Different browsers will handle quick swipe or long hold/drag touches differently.
* This is an edge case if the user is long pressing or pressing while dragging the finger
* slightly (but staying on the element) - The browser may or may not fire the event. However,
* the browser will always fire the regular tap/press very quickly.
*
* TODO We should be able to embed fastElements and have the child fastElements NOT bubble the event
* So we can embed the elements if needed (???)
*
* #author ashton
*
*/
public abstract class FastPressElement extends Composite implements HasPressHandlers {
private boolean touchHandled = false;
private boolean clickHandled = false;
private boolean touchMoved = false;
private boolean isEnabled = true;
private int touchId;
private int flashDelay = 75; // Default time delay in ms to flash style change
public FastPressElement() {
// Sink Click and Touch Events
// I am not going to sink Mouse events since
// I don't think we will gain anything
sinkEvents(Event.ONCLICK | Event.TOUCHEVENTS); // Event.TOUCHEVENTS adds all (Start, End,
// Cancel, Change)
}
public FastPressElement(int msDelay) {
this();
if (msDelay >= 0) {
flashDelay = msDelay;
}
}
public void setEnabled(boolean enabled) {
if (enabled) {
onEnablePressStyle();
} else {
onDisablePressStyle();
}
this.isEnabled = enabled;
}
/**
* Use this method in the same way you would use addClickHandler or addDomHandler
*
*/
#Override
public HandlerRegistration addPressHandler(PressHandler handler) {
// Use Widget's addHandler to ensureHandlers and add the type/return handler
// We don't use addDom/BitlessHandlers since we aren't sinkEvents
// We also aren't even dealing with a DomEvent
return addHandler(handler, PressEvent.getType());
}
/**
*
* #param event
*/
private void firePressEvent(Event event) {
// This better verify a ClickEvent or TouchEndEvent
// TODO might want to verify
// (hitting issue with web.bindery vs g.gwt.user package diff)
PressEvent pressEvent = new PressEvent(event);
fireEvent(pressEvent);
}
/**
* Implement the handler for pressing but NOT releasing the button. Normally you just want to show
* some CSS style change to alert the user the element is active but not yet pressed
*
* ONLY FOR STYLE CHANGE - Will briefly be called onClick
*
* TIP: Don't make a dramatic style change. Take note that if a user is just trying to scroll, and
* start on the element and then scrolls off, we may not want to distract them too much. If a user
* does scroll off the element,
*
*/
public abstract void onHoldPressDownStyle();
/**
* Implement the handler for release of press. This should just be some CSS or Style change.
*
* ONLY FOR STYLE CHANGE - Will briefly be called onClick
*
* TIP: This should just go back to the normal style.
*/
public abstract void onHoldPressOffStyle();
/**
* Change styling to disabled
*/
public abstract void onDisablePressStyle();
/**
* Change styling to enabled
*
* TIP:
*/
public abstract void onEnablePressStyle();
#Override
public Widget getWidget() {
return super.getWidget();
}
#Override
public void onBrowserEvent(Event event) {
switch (DOM.eventGetType(event)) {
case Event.ONTOUCHSTART: {
if (isEnabled) {
onTouchStart(event);
}
break;
}
case Event.ONTOUCHEND: {
if (isEnabled) {
onTouchEnd(event);
}
break;
}
case Event.ONTOUCHMOVE: {
if (isEnabled) {
onTouchMove(event);
}
break;
}
case Event.ONCLICK: {
if (isEnabled) {
onClick(event);
}
return;
}
default: {
// Let parent handle event if not one of the above (?)
super.onBrowserEvent(event);
}
}
}
private void onClick(Event event) {
event.stopPropagation();
if (touchHandled) {
// if the touch is already handled, we are on a device
// that supports touch (so you aren't in the desktop browser)
touchHandled = false;// reset for next press
clickHandled = true;//
super.onBrowserEvent(event);
} else {
if (clickHandled) {
// Not sure how this situation would occur
// onClick being called twice..
event.preventDefault();
} else {
// Press not handled yet
// We still want to briefly fire the style change
// To give good user feedback
// Show HoldPress when possible
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
#Override
public void execute() {
// Show hold press
onHoldPressDownStyle();
// Now schedule a delay (which will allow the actual
// onTouchClickFire to executed
Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
#Override
public boolean execute() {
// Clear the style change
onHoldPressOffStyle();
return false;
}
}, flashDelay);
}
});
clickHandled = false;
firePressEvent(event);
}
}
}
private void onTouchStart(Event event) {
onHoldPressDownStyle(); // Show style change
// Stop the event from bubbling up
event.stopPropagation();
// Only handle if we have exactly one touch
if (event.getTargetTouches().length() == 1) {
Touch start = event.getTargetTouches().get(0);
touchId = start.getIdentifier();
touchMoved = false;
}
}
/**
* Check to see if the touch has moved off of the element.
*
* NOTE that in iOS the elasticScroll may make the touch/move cancel more difficult.
*
* #param event
*/
private void onTouchMove(Event event) {
if (!touchMoved) {
Touch move = null;
for (int i = 0; i < event.getChangedTouches().length(); i++) {
if (event.getChangedTouches().get(i).getIdentifier() == touchId) {
move = event.getChangedTouches().get(i);
}
}
// Check to see if we moved off of the original element
// Use Page coordinates since we compare with widget's absolute coordinates
int yCord = move.getPageY();
int xCord = move.getPageX();
boolean yTop = getWidget().getAbsoluteTop() > yCord; // is y above element
boolean yBottom = (getWidget().getAbsoluteTop() + getWidget().getOffsetHeight()) < yCord; // y
// below
boolean xLeft = getWidget().getAbsoluteLeft() > xCord; // is x to the left of element
boolean xRight = (getWidget().getAbsoluteLeft() + getWidget().getOffsetWidth()) < xCord; // x
// to
// the
// right
if (yTop || yBottom || xLeft || xRight) {
touchMoved = true;
onHoldPressOffStyle();// Go back to normal style
}
}
}
private void onTouchEnd(Event event) {
if (!touchMoved) {
touchHandled = true;
firePressEvent(event);
event.preventDefault();
onHoldPressOffStyle();// Change back the style
}
}
}
I think the code from the prior answer as written has a few problems, in particular when their are multiple touches.
(NOTE: I'm looking at code I wrote using the Elemental library as reference, so some of the calls might be different in the user library).
a) The code is not filtering touches aimed at the button; it calls TouchEvent.getTouches(). You want to call TouchEvent.getTargetTouches() on touchstart and touchmove to get the the touches just for your button. You want to call TouchEvent.getChangedTouches() on touchend to get the end touch.
b) The code does not take into account multitouch. On touchstart, you can check that a single touch is available and bail out if there is more than one. Also, on touchstart, stash away the id of touch, then use this in touchmove and touchend to find your touch id in the array that is returned (in case the user has touched another finger later on). You can also simplify and check for multiple touches on touchmove and touchend and bail again there.
c) I believe you need to call stopPropagation on touchstart, since you are handling the event. I don't see where they call event.stopPropagation on the touchstart event You can see that this happens in the click handlers, but not the touchstart. This prevents the touch from being turned into a click automatically by the browser, which would cause multiple clicks.
There is also a simpler way. If you don't care about dragging starting on a button, then you can simply call your click logic in the touchstart event (and make sure you check for single touch, and call event.stopPropagation ) and ignore touchmove and touchend. All the touchmove and touchend stuff is to handle the case of allowing dragging to start on the button.
Also Try FastClick
FastClick is a simple, easy-to-use library for eliminating the 300ms delay between a physical tap and the firing of a click event on mobile browsers. The aim is to make your application feel less laggy and more responsive while avoiding any interference with your current logic.
FastClick is developed by FT Labs, part of the Financial Times.
The library has been deployed as part of the FT Web App and is tried and tested on the following mobile browsers:
Mobile Safari on iOS 3 and upwards
Chrome on iOS 5 and upwards
Chrome on Android (ICS)
Opera Mobile 11.5 and upwards
Android Browser since Android 2
PlayBook OS 1 and upwards
FastClick doesn't attach any listeners on desktop browsers as it is not needed. Those that have been tested are:
Safari
Chrome
Internet Explorer
Firefox
Opera
I'm trying to capture right-clicks on a widget, to popup my own context menu instead of the browser's. There are a couple references on this, but the most popular one here is a little dated, although some of the comments contain more recent code snippets.
I've pieced together bits and I've got it working in Chrome and FF but not IE. In IE it doesn't display the default browser context menu, but it doesn't display my menu. I'm just getting into GWT so I'm assuming I'm not doing something right with the right kinds of handlers or events. I'm also using the gwt-graphics module, that's where the Rectangle class that I'm extending comes from, in case that's relevant.
Here's my code:
public class RectangleRightClickable extends Rectangle {
public RectangleRightClickable(int x, int y, int width, int height) {
super(x, y, width, height);
sinkEvents(Event.ONCONTEXTMENU);
}
public void onBrowserEvent(Event event) {
GWT.log("onBrowserEvent");
event.stopPropagation();
event.preventDefault();
GWT.log("event type : " + DOM.eventGetType(event));
switch(DOM.eventGetType(event)) {
case Event.ONCONTEXTMENU:
if (DOM.eventGetButton(event) == Event.BUTTON_RIGHT) {
GWT.log("Event.BUTTON_RIGHT", null);
showMenu();
}
break;
default:
GWT.log(event.toString());
break;
}
}
protected void showMenu() {
final RectangleRightClickable parent = this;
final PopupMenu popMenu = new PopupMenu();
popMenu.addMenuItem(new Label("Add thing"));
popMenu.setPopupPositionAndShow(new PopupPanel.PositionCallback() {
public void setPosition(int offsetWidth, int offsetHeight) {
int left = parent.getX() + parent.getWidth();
int top = parent.getY() + parent.getWidth();
popMenu.setPopupPosition(left, top);
}
});
}
}
Got this response on the GWT google groups list, which worked:
addDomHandler(new ContextMenuHandler()
{
#Override
public void onContextMenu(ContextMenuEvent event)
{
showMenu();
event.preventDefault();
}
}, ContextMenuEvent.getType());
I am trying to get a proper method for days to select multiple cells in a flextable's column.
So far i only managed to do it with clicks which works well, but a drag selection would be much better. I have been reading docs and searching, but all the stuff i found was based on deprecated code. I use GWT 2.0 .
I know i need some event handler which would run when drag selection mouse gesture occurs, and that handler needs to know the cell's index where the selection start and of course the cell's index where the selection ends.
Any advice || code would be much appreciated.
This needs to be improved but it should give you the basic idea. First you need to create a CustomTable that listens to MouseEvents. You can do this by extending composite to wrap a focuspanel and a flextable as such :
public class CustomTable extends Composite implements MouseDownHandler, MouseMoveHandler, MouseUpHandler{
List<CellWidget> widgets = new ArrayList<CellWidget>();
FlexTable table = new FlexTable();
FocusPanel focusPanel = new FocusPanel();
boolean selecting= false;
Point selectStart,selectEnd;
public CustomTable(){
focusPanel.setWidget(table);
focusPanel.addMouseDownHandler(this);
focusPanel.addMouseMoveHandler(this);
focusPanel.addMouseUpHandler(this);
initWidget(focusPanel);
}
public void setWidget(int row, int column, CellWidget widget){
widgets.add(widget);
table.setWidget(row, column, widget);
}
#Override
public void onMouseUp(MouseUpEvent event) {
event.preventDefault();
if (selecting){
selecting=false;
DOM.releaseCapture(this.getElement());
selectEnd = new Point(event.getClientX(),event.getClientY());
for (CellWidget widget : widgets){
if (widget.isIn(selectStart,selectEnd))
widget.say();
}
selectStart = selectEnd = null;
}
}
#Override
public void onMouseMove(MouseMoveEvent event) {
event.preventDefault();
if (selecting){
//do some fancy layout
}
}
#Override
public void onMouseDown(MouseDownEvent event) {
event.preventDefault();
selecting = true;
DOM.setCapture(this.getElement());
selectStart = new Point(event.getClientX(),event.getClientY());
}
}
Next you define a CellWidget which basically encapsulates what you would like to add to your cells. When added to DOM, CellWidget calculates and stores its position later to determine if it is in the selected area :
public class CellWidget extends Composite{
Widget content;
Point topLeft,topRight,bottomLeft,bottomRight;
public CellWidget(Widget w){
this.content = w;
initWidget(w);
}
#Override
protected void onLoad() {
topLeft = new Point(getAbsoluteLeft(),getAbsoluteTop());
topRight = new Point(getAbsoluteLeft()+getOffsetWidth(),getAbsoluteTop());
bottomLeft = new Point(getAbsoluteLeft(),getAbsoluteTop()+getOffsetHeight());
bottomRight = new Point(getAbsoluteLeft()+getOffsetWidth(),getAbsoluteTop()+getOffsetHeight());
}
public void say(){
Window.alert(content + " is selected!");
}
public boolean isIn(Point start, Point end){
if (topLeft.isBetween(start, end) || topRight.isBetween(start, end)
|| bottomLeft.isBetween(start, end) || bottomRight.isBetween(start, end))
return true;
else
return false;
}
}
A simple point implementation to make things easier :
public class Point {
int x,y;
public Point(int x,int y){
this.x=x;
this.y=y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
#Override
public String toString() {
return x+","+y;
}
public boolean isBetween(Point p1,Point p2){
if (p1.getX() < x && p2.getX() > x && p1.getY() < y && p2.getY() > y)
return true;
return false;
}
}
Finally at your EntryPoint module you wrap things up by :
public void onModuleLoad() {
RootPanel rootPanel = RootPanel.get();
CustomTable table = new CustomTable();
table.setWidget(0, 0, new CellWidget(new Label("hello 0,0")));
table.setWidget(0, 1, new CellWidget(new Label("hello 0,1")));
table.setWidget(1, 0, new CellWidget(new Label("hello 1,0")));
table.setWidget(1, 1, new CellWidget(new Label("hello 1,1")));
rootPanel.add(table);
}
I know that the actual logic to determine if the widgets fall within the selected area is incomplete and needs to be improved but i think this solution is clear enough to give the basic idea. Cheers