svn changeset:14289/svn branch:6.4tags/6.7.0.beta1
@@ -12,12 +12,17 @@ import com.google.gwt.user.client.DOM; | |||
import com.google.gwt.user.client.DeferredCommand; | |||
import com.google.gwt.user.client.Element; | |||
import com.google.gwt.user.client.Event; | |||
import com.google.gwt.user.client.ui.HasWidgets; | |||
import com.google.gwt.user.client.ui.KeyboardListener; | |||
import com.google.gwt.user.client.ui.KeyboardListenerCollection; | |||
import com.google.gwt.user.client.ui.Widget; | |||
import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.BrowserInfo; | |||
import com.vaadin.terminal.gwt.client.Container; | |||
import com.vaadin.terminal.gwt.client.Paintable; | |||
import com.vaadin.terminal.gwt.client.UIDL; | |||
import com.vaadin.terminal.gwt.client.Util; | |||
import com.vaadin.terminal.gwt.client.ui.richtextarea.VRichTextArea; | |||
/** | |||
* A helper class to implement keyboard shorcut handling. Keeps a list of owners | |||
@@ -27,7 +32,45 @@ import com.vaadin.terminal.gwt.client.UIDL; | |||
* @author IT Mill ltd | |||
*/ | |||
public class ShortcutActionHandler { | |||
private final ArrayList actions = new ArrayList(); | |||
/** | |||
* An interface implemented by those users (most often {@link Container}s, | |||
* but HasWidgets at least) of this helper class that want to support | |||
* special components like {@link VRichTextArea} that don't properly | |||
* propagate key down events. Those components can build support for | |||
* shortcut actions by traversing the closest | |||
* {@link ShortcutActionHandlerOwner} from the component hierarchy an | |||
* passing keydown events to {@link ShortcutActionHandler}. | |||
*/ | |||
public interface ShortcutActionHandlerOwner extends HasWidgets { | |||
/** | |||
* Returns the ShortCutActionHandler currently used or null if there is | |||
* currently no shortcutactionhandler | |||
*/ | |||
ShortcutActionHandler getShortcutActionHandler(); | |||
} | |||
/** | |||
* A focusable {@link Paintable} implementing this interface will be | |||
* notified before shortcut actions are handled if it will be the target of | |||
* the action (most commonly means it is the focused component during the | |||
* keyboard combination is triggered by the user). | |||
*/ | |||
public interface BeforeShortcutActionListener extends Paintable { | |||
/** | |||
* This method is called by ShortcutActionHandler before firing the | |||
* shortcut if the Paintable is currently focused (aka the target of the | |||
* shortcut action). Eg. a field can update its possibly changed value | |||
* to the server before shortcut action is fired. | |||
* | |||
* @param e | |||
* the event that triggered the shortcut action | |||
*/ | |||
public void onBeforeShortcutAction(Event e); | |||
} | |||
private final ArrayList<ShortcutAction> actions = new ArrayList<ShortcutAction>(); | |||
private ApplicationConnection client; | |||
private String paintableId; | |||
@@ -68,38 +111,66 @@ public class ShortcutActionHandler { | |||
} | |||
} | |||
public void handleKeyboardEvent(final Event event) { | |||
public void handleKeyboardEvent(final Event event, Paintable target) { | |||
final int modifiers = KeyboardListenerCollection | |||
.getKeyboardModifiers(event); | |||
final char keyCode = (char) DOM.eventGetKeyCode(event); | |||
final ShortcutKeyCombination kc = new ShortcutKeyCombination(keyCode, | |||
modifiers); | |||
final Iterator it = actions.iterator(); | |||
final Iterator<ShortcutAction> it = actions.iterator(); | |||
while (it.hasNext()) { | |||
final ShortcutAction a = (ShortcutAction) it.next(); | |||
final ShortcutAction a = it.next(); | |||
if (a.getShortcutCombination().equals(kc)) { | |||
final Element et = DOM.eventGetTarget(event); | |||
final Paintable target = client.getPaintable(et); | |||
DOM.eventPreventDefault(event); | |||
shakeTarget(et); | |||
DeferredCommand.addCommand(new Command() { | |||
public void execute() { | |||
shakeTarget(et); | |||
} | |||
}); | |||
DeferredCommand.addCommand(new Command() { | |||
public void execute() { | |||
if (target != null) { | |||
client.updateVariable(paintableId, "actiontarget", | |||
target, false); | |||
} | |||
client.updateVariable(paintableId, "action", | |||
a.getKey(), true); | |||
} | |||
}); | |||
fireAction(event, a, target); | |||
break; | |||
} | |||
} | |||
} | |||
public void handleKeyboardEvent(final Event event) { | |||
handleKeyboardEvent(event, null); | |||
} | |||
private void fireAction(final Event event, final ShortcutAction a, | |||
Paintable target) { | |||
final Element et = DOM.eventGetTarget(event); | |||
if (target == null) { | |||
Widget w = Util.findWidget(et, null); | |||
while (w != null && !(w instanceof Paintable)) { | |||
w = w.getParent(); | |||
} | |||
target = (Paintable) w; | |||
} | |||
final Paintable finalTarget = target; | |||
event.preventDefault(); | |||
/* | |||
* The target component might have unpublished changes, try to | |||
* synchronize them before firing shortcut action. | |||
*/ | |||
if (finalTarget instanceof BeforeShortcutActionListener) { | |||
((BeforeShortcutActionListener) finalTarget) | |||
.onBeforeShortcutAction(event); | |||
} else { | |||
shakeTarget(et); | |||
DeferredCommand.addCommand(new Command() { | |||
public void execute() { | |||
shakeTarget(et); | |||
} | |||
}); | |||
} | |||
DeferredCommand.addCommand(new Command() { | |||
public void execute() { | |||
if (finalTarget != null) { | |||
client.updateVariable(paintableId, "actiontarget", | |||
finalTarget, false); | |||
} | |||
client.updateVariable(paintableId, "action", a.getKey(), true); | |||
} | |||
}); | |||
} | |||
/** | |||
@@ -108,9 +179,10 @@ public class ShortcutActionHandler { | |||
* sent to server. This is done by removing focus and then returning it | |||
* immediately back to target element. | |||
* <p> | |||
* This is practically a hack and should be replaced with an interface via | |||
* widgets could be notified when they should fire value change. Big task | |||
* for TextFields, DateFields and various selects. | |||
* This is practically a hack and should be replaced with an interface | |||
* {@link BeforeShortcutActionListener} via widgets could be notified when | |||
* they should fire value change. Big task for TextFields, DateFields and | |||
* various selects. | |||
* | |||
* <p> | |||
* TODO separate opera impl with generator |
@@ -24,8 +24,10 @@ import com.vaadin.terminal.gwt.client.RenderInformation; | |||
import com.vaadin.terminal.gwt.client.RenderSpace; | |||
import com.vaadin.terminal.gwt.client.UIDL; | |||
import com.vaadin.terminal.gwt.client.Util; | |||
import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner; | |||
public class VPanel extends SimplePanel implements Container { | |||
public class VPanel extends SimplePanel implements Container, | |||
ShortcutActionHandlerOwner { | |||
public static final String CLICK_EVENT_IDENTIFIER = "click"; | |||
public static final String CLASSNAME = "v-panel"; | |||
@@ -548,4 +550,8 @@ public class VPanel extends SimplePanel implements Container { | |||
detectContainerBorders(); | |||
} | |||
public ShortcutActionHandler getShortcutActionHandler() { | |||
return shortcutHandler; | |||
} | |||
} |
@@ -23,6 +23,7 @@ import com.vaadin.terminal.gwt.client.Paintable; | |||
import com.vaadin.terminal.gwt.client.UIDL; | |||
import com.vaadin.terminal.gwt.client.Util; | |||
import com.vaadin.terminal.gwt.client.VTooltip; | |||
import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.BeforeShortcutActionListener; | |||
/** | |||
* This class represents a basic text input field with one row. | |||
@@ -31,7 +32,7 @@ import com.vaadin.terminal.gwt.client.VTooltip; | |||
* | |||
*/ | |||
public class VTextField extends TextBoxBase implements Paintable, Field, | |||
ChangeHandler, FocusHandler, BlurHandler { | |||
ChangeHandler, FocusHandler, BlurHandler, BeforeShortcutActionListener { | |||
/** | |||
* The input node CSS classname. | |||
@@ -381,4 +382,8 @@ public class VTextField extends TextBoxBase implements Paintable, Field, | |||
} | |||
} | |||
public void onBeforeShortcutAction(Event e) { | |||
valueChange(false); | |||
} | |||
} |
@@ -35,12 +35,13 @@ import com.vaadin.terminal.gwt.client.Paintable; | |||
import com.vaadin.terminal.gwt.client.RenderSpace; | |||
import com.vaadin.terminal.gwt.client.UIDL; | |||
import com.vaadin.terminal.gwt.client.Util; | |||
import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner; | |||
/** | |||
* | |||
*/ | |||
public class VView extends SimplePanel implements Container, ResizeHandler, | |||
Window.ClosingHandler { | |||
Window.ClosingHandler, ShortcutActionHandlerOwner { | |||
private static final String CLASSNAME = "v-view"; | |||
@@ -693,4 +694,8 @@ public class VView extends SimplePanel implements Container, ResizeHandler, | |||
parentFrame = getParentFrame(); | |||
} | |||
public ShortcutActionHandler getShortcutActionHandler() { | |||
return actionHandler; | |||
} | |||
} |
@@ -32,6 +32,7 @@ import com.vaadin.terminal.gwt.client.RenderSpace; | |||
import com.vaadin.terminal.gwt.client.UIDL; | |||
import com.vaadin.terminal.gwt.client.Util; | |||
import com.vaadin.terminal.gwt.client.VDebugConsole; | |||
import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner; | |||
/** | |||
* "Sub window" component. | |||
@@ -40,7 +41,8 @@ import com.vaadin.terminal.gwt.client.VDebugConsole; | |||
* | |||
* @author IT Mill Ltd | |||
*/ | |||
public class VWindow extends VOverlay implements Container, ScrollListener { | |||
public class VWindow extends VOverlay implements Container, ScrollListener, | |||
ShortcutActionHandlerOwner { | |||
/** | |||
* Minimum allowed height of a window. This refers to the content area, not | |||
@@ -1201,4 +1203,8 @@ public class VWindow extends VOverlay implements Container, ScrollListener { | |||
// NOP, window has own caption, layout captio not rendered | |||
} | |||
public ShortcutActionHandler getShortcutActionHandler() { | |||
return shortcutHandler; | |||
} | |||
} |
@@ -8,6 +8,8 @@ import com.google.gwt.event.dom.client.BlurEvent; | |||
import com.google.gwt.event.dom.client.BlurHandler; | |||
import com.google.gwt.event.dom.client.ChangeEvent; | |||
import com.google.gwt.event.dom.client.ChangeHandler; | |||
import com.google.gwt.event.dom.client.KeyDownEvent; | |||
import com.google.gwt.event.dom.client.KeyDownHandler; | |||
import com.google.gwt.event.dom.client.KeyPressEvent; | |||
import com.google.gwt.event.dom.client.KeyPressHandler; | |||
import com.google.gwt.event.shared.HandlerRegistration; | |||
@@ -15,15 +17,20 @@ import com.google.gwt.user.client.Command; | |||
import com.google.gwt.user.client.DOM; | |||
import com.google.gwt.user.client.DeferredCommand; | |||
import com.google.gwt.user.client.Element; | |||
import com.google.gwt.user.client.Event; | |||
import com.google.gwt.user.client.ui.Composite; | |||
import com.google.gwt.user.client.ui.FlowPanel; | |||
import com.google.gwt.user.client.ui.HTML; | |||
import com.google.gwt.user.client.ui.RichTextArea; | |||
import com.google.gwt.user.client.ui.Widget; | |||
import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.Paintable; | |||
import com.vaadin.terminal.gwt.client.UIDL; | |||
import com.vaadin.terminal.gwt.client.Util; | |||
import com.vaadin.terminal.gwt.client.ui.Field; | |||
import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler; | |||
import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.BeforeShortcutActionListener; | |||
import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner; | |||
/** | |||
* This class implements a basic client side rich text editor component. | |||
@@ -32,7 +39,8 @@ import com.vaadin.terminal.gwt.client.ui.Field; | |||
* | |||
*/ | |||
public class VRichTextArea extends Composite implements Paintable, Field, | |||
ChangeHandler, BlurHandler, KeyPressHandler { | |||
ChangeHandler, BlurHandler, KeyPressHandler, KeyDownHandler, | |||
BeforeShortcutActionListener { | |||
/** | |||
* The input node CSS classname. | |||
@@ -64,11 +72,16 @@ public class VRichTextArea extends Composite implements Paintable, Field, | |||
private HandlerRegistration keyPressHandler; | |||
private ShortcutActionHandlerOwner hasShortcutActionHandler; | |||
private String currentValue = ""; | |||
public VRichTextArea() { | |||
fp.add(formatter); | |||
rta.setWidth("100%"); | |||
rta.addBlurHandler(this); | |||
rta.addKeyDownHandler(this); | |||
fp.add(rta); | |||
@@ -97,7 +110,8 @@ public class VRichTextArea extends Composite implements Paintable, Field, | |||
id = uidl.getId(); | |||
if (uidl.hasVariable("text")) { | |||
rta.setHTML(uidl.getStringVariable("text")); | |||
currentValue = uidl.getStringVariable("text"); | |||
rta.setHTML(currentValue); | |||
} | |||
setEnabled(!uidl.getBooleanAttribute("disabled")); | |||
@@ -130,9 +144,12 @@ public class VRichTextArea extends Composite implements Paintable, Field, | |||
* Method is public to let popupview force synchronization on close. | |||
*/ | |||
public void synchronizeContentToServer() { | |||
final String html = rta.getHTML(); | |||
if (client != null && id != null) { | |||
client.updateVariable(id, "text", html, immediate); | |||
final String html = rta.getHTML(); | |||
if (!html.equals(currentValue)) { | |||
client.updateVariable(id, "text", html, immediate); | |||
currentValue = html; | |||
} | |||
} | |||
} | |||
@@ -251,4 +268,34 @@ public class VRichTextArea extends Composite implements Paintable, Field, | |||
} | |||
} | |||
public void onKeyDown(KeyDownEvent event) { | |||
// delegate to closest shortcut action handler | |||
// throw event from the iframe forward to the shortcuthandler | |||
ShortcutActionHandler shortcutHandler = getShortcutHandlerOwner() | |||
.getShortcutActionHandler(); | |||
if (shortcutHandler != null) { | |||
shortcutHandler | |||
.handleKeyboardEvent(com.google.gwt.user.client.Event | |||
.as(event.getNativeEvent()), this); | |||
} | |||
} | |||
private ShortcutActionHandlerOwner getShortcutHandlerOwner() { | |||
if (hasShortcutActionHandler == null) { | |||
Widget parent = getParent(); | |||
while (parent != null) { | |||
if (parent instanceof ShortcutActionHandlerOwner) { | |||
break; | |||
} | |||
parent = parent.getParent(); | |||
} | |||
hasShortcutActionHandler = (ShortcutActionHandlerOwner) parent; | |||
} | |||
return hasShortcutActionHandler; | |||
} | |||
public void onBeforeShortcutAction(Event e) { | |||
synchronizeContentToServer(); | |||
} | |||
} |