From 6fe8bfa9b78797b483ac811840630b5dcc6785e9 Mon Sep 17 00:00:00 2001 From: Matti Tahvonen Date: Wed, 29 Aug 2007 12:37:25 +0000 Subject: [PATCH] added initial support for keyboard shortcuts. Now only Panel supports them on server side, IView (main windows) on client side. svn changeset:2150/svn branch:trunk --- .../itmill/toolkit/demo/KeyboardShortcut.java | 78 +++++++------- .../toolkit/terminal/gwt/client/UIDL.java | 8 ++ .../gwt/client/ui/IShortcutAction.java | 27 +++++ .../toolkit/terminal/gwt/client/ui/IView.java | 62 +++++++++++ .../gwt/client/ui/ShortcutKeyCombination.java | 49 +++++++++ src/com/itmill/toolkit/ui/Button.java | 100 +----------------- src/com/itmill/toolkit/ui/Panel.java | 98 ++++++++++++++++- 7 files changed, 280 insertions(+), 142 deletions(-) create mode 100644 src/com/itmill/toolkit/terminal/gwt/client/ui/IShortcutAction.java create mode 100644 src/com/itmill/toolkit/terminal/gwt/client/ui/ShortcutKeyCombination.java diff --git a/src/com/itmill/toolkit/demo/KeyboardShortcut.java b/src/com/itmill/toolkit/demo/KeyboardShortcut.java index 4bb1e6cff8..76a3ba3321 100644 --- a/src/com/itmill/toolkit/demo/KeyboardShortcut.java +++ b/src/com/itmill/toolkit/demo/KeyboardShortcut.java @@ -12,7 +12,7 @@ import com.itmill.toolkit.ui.*; * */ public class KeyboardShortcut extends com.itmill.toolkit.Application implements - Handler { +Handler { private Window main; private Button a; @@ -25,6 +25,23 @@ public class KeyboardShortcut extends com.itmill.toolkit.Application implements private AbstractField f; + Action[] actions = new Action[] { + new ShortcutAction("Button a action", + ShortcutAction.KeyCode.A, new int[] { + ShortcutAction.ModifierKey.CTRL, + ShortcutAction.ModifierKey.SHIFT }), + new ShortcutAction("Button z action", + ShortcutAction.KeyCode.Z, new int[] { + ShortcutAction.ModifierKey.CTRL, + ShortcutAction.ModifierKey.SHIFT }), + new ShortcutAction("Button x action", + ShortcutAction.KeyCode.X, new int[] { + ShortcutAction.ModifierKey.CTRL, + ShortcutAction.ModifierKey.SHIFT }), + new ShortcutAction("Restart ", + ShortcutAction.KeyCode.ESCAPE, null) + }; + public void init() { main = new Window("Keyboard shortcuts demo"); @@ -32,32 +49,29 @@ public class KeyboardShortcut extends com.itmill.toolkit.Application implements setTheme("corporate"); main - .addComponent(new Label( - "

Test application for shortcut actions

" - + "

Notes:
" - + "This feature is under development and it's API may still change.
" - + "If events do not work, set focus to Textfield first.
" - + "Browsers may have reserved the keyboard combinations used in " - + "this demo for other purposes.

", - Label.CONTENT_RAW)); + .addComponent(new Label( + "

Test application for shortcut actions

" + + "

Notes:
" + + "This feature is under development and it's API may still change.
" + + "If events do not work, set focus to Textfield first.
" + + "Browsers may have reserved the keyboard combinations used in " + + "this demo for other purposes.

", + Label.CONTENT_XHTML)); main - .addComponent(new Label( - "ESC restarts program, ctrl-shift-a clicks A button, " - + "ctrl-shift-z clicks Z button, ctrl-shift-x clicks X button")); + .addComponent(new Label( + "ESC restarts program, ctrl-shift-a clicks A button, " + + "ctrl-shift-z clicks Z button, ctrl-shift-x clicks X button")); // Restart button close = new Button("restart", this, "close"); - close.addActionHandler(this); + main.addComponent(close); a = new Button("Button A", this, "buttonAHandler"); - a.addActionHandler(this); z = new Button("Button Z", this, "buttonZHandler"); - z.addActionHandler(this); x = new Button("Button X", this, "buttonXHandler"); - x.addActionHandler(this); f = new TextField("Textfield"); @@ -66,43 +80,23 @@ public class KeyboardShortcut extends com.itmill.toolkit.Application implements main.addComponent(x); main.addComponent(f); + main.addActionHandler(this); + f.focus(); } public Action[] getActions(Object target, Object sender) { - Action[] actions = new Action[1]; - if (sender == a) { - actions[0] = (Action) new ShortcutAction("Button a action", - ShortcutAction.KeyCode.A, new int[] { - ShortcutAction.ModifierKey.CTRL, - ShortcutAction.ModifierKey.SHIFT }); - } else if (sender == z) { - actions[0] = (Action) (new ShortcutAction("Button z action", - ShortcutAction.KeyCode.Z, new int[] { - ShortcutAction.ModifierKey.CTRL, - ShortcutAction.ModifierKey.SHIFT })); - - } else if (sender == x) { - actions[0] = (Action) new ShortcutAction("Button x action", - ShortcutAction.KeyCode.X, new int[] { - ShortcutAction.ModifierKey.CTRL, - ShortcutAction.ModifierKey.SHIFT }); - } else { - // restart button - actions[0] = new ShortcutAction("Restart ", - ShortcutAction.KeyCode.ESCAPE, null); - } return actions; } public void handleAction(Action action, Object sender, Object target) { - if (target == a) + if (action == actions[0]) this.buttonAHandler(); - if (target == z) + if (action == actions[1]) this.buttonZHandler(); - if (target == x) + if (action == actions[2]) this.buttonXHandler(); - if (target == close) + if (action == actions[3]) this.close(); } diff --git a/src/com/itmill/toolkit/terminal/gwt/client/UIDL.java b/src/com/itmill/toolkit/terminal/gwt/client/UIDL.java index 4cffdbcd35..18ef9306a9 100644 --- a/src/com/itmill/toolkit/terminal/gwt/client/UIDL.java +++ b/src/com/itmill/toolkit/terminal/gwt/client/UIDL.java @@ -93,6 +93,14 @@ public class UIDL { return s; } + public int[] getIntArrayAttribute(String name) { + JSONArray a = (JSONArray) ((JSONObject) json.get(1)).get(name); + int[] s = new int[a.size()]; + for (int i = 0; i < a.size(); i++) + s[i] = Integer.parseInt(((JSONString) a.get(i)).stringValue()); + return s; + } + public HashSet getStringArrayAttributeAsSet(String string) { JSONArray a = getArrayVariable(string); HashSet s = new HashSet(); diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/IShortcutAction.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/IShortcutAction.java new file mode 100644 index 0000000000..73a1e7c94c --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/IShortcutAction.java @@ -0,0 +1,27 @@ +package com.itmill.toolkit.terminal.gwt.client.ui; + +public class IShortcutAction { + + private ShortcutKeyCombination sc; + private String caption; + private String key; + + public IShortcutAction(String key, ShortcutKeyCombination sc, String caption) { + this.sc = sc; + this.key = key; + this.caption = caption; + } + + public ShortcutKeyCombination getShortcutCombination() { + return sc; + } + + public String getCaption() { + return caption; + } + + public String getKey() { + return key; + } + +} diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/IView.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/IView.java index 099ed630c5..445eb4b967 100644 --- a/src/com/itmill/toolkit/terminal/gwt/client/ui/IView.java +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/IView.java @@ -1,8 +1,11 @@ package com.itmill.toolkit.terminal.gwt.client.ui; +import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; +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.RootPanel; import com.google.gwt.user.client.ui.SimplePanel; @@ -19,13 +22,25 @@ public class IView extends SimplePanel implements Paintable { private HashSet subWindows = new HashSet(); + private ArrayList actions = new ArrayList(); + private ApplicationConnection client; + + private String id; + + public IView() { + super(); + sinkEvents(Event.KEYEVENTS); + } public String getTheme() { return theme; } public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + this.client = client; + + this.id = uidl.getId(); // Start drawing from scratch clear(); @@ -76,6 +91,8 @@ public class IView extends SimplePanel implements Paintable { subWindows.add(w); } ((Paintable)w).updateFromUIDL(childUidl, client); + } else if ("actions".equals(childUidl.getTag())) { + updateActionMap(childUidl); } } @@ -88,4 +105,49 @@ public class IView extends SimplePanel implements Paintable { } } + private void updateActionMap(UIDL c) { + actions.clear(); + Iterator it = c.getChildIterator(); + while(it.hasNext()) { + UIDL action = (UIDL) it.next(); + + int[] modifiers = null; + if(action.hasAttribute("mk")) + modifiers = action.getIntArrayAttribute("mk"); + + ShortcutKeyCombination kc = new ShortcutKeyCombination( + action.getIntAttribute("kc"), + modifiers); + String key = action.getStringAttribute("key"); + String caption = action.getStringAttribute("caption"); + actions.add(new IShortcutAction(key,kc, caption)); + } + } + + public void onBrowserEvent(Event event) { + if(DOM.eventGetType(event) == Event.ONKEYDOWN) { + handleKeyEvent(event); + } + super.onBrowserEvent(event); + } + + private void handleKeyEvent(Event event) { + client.console.log("keyEvent"); + + ShortcutKeyCombination kc = new ShortcutKeyCombination(); + kc.altKey = DOM.eventGetAltKey(event); + kc.ctrlKey = DOM.eventGetCtrlKey(event); + kc.shiftKey = DOM.eventGetShiftKey(event); + kc.keyCode = DOM.eventGetKeyCode(event); + Iterator it = actions.iterator(); + while(it.hasNext()) { + IShortcutAction a = (IShortcutAction) it.next(); + if(a.getShortcutCombination().equals(kc)) { + client.updateVariable(id, "action", a.getKey(), true); + } + } + } + } + + diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/ShortcutKeyCombination.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/ShortcutKeyCombination.java new file mode 100644 index 0000000000..2732859f62 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/ShortcutKeyCombination.java @@ -0,0 +1,49 @@ +package com.itmill.toolkit.terminal.gwt.client.ui; + +public class ShortcutKeyCombination { + + public static final int SHIFT = 16; + public static final int CTRL = 17; + public static final int ALT = 18; + + + + int keyCode = 0; + boolean altKey = false; + boolean ctrlKey = false; + boolean shiftKey = false; + boolean metaKey = false; + + public ShortcutKeyCombination() { + } + + ShortcutKeyCombination(int kc, int[] modifiers) { + keyCode = kc; + if(modifiers != null) { + for (int i = 0; i < modifiers.length; i++) { + switch (modifiers[i]) { + case ALT: + altKey = true; + break; + case CTRL: + ctrlKey = true; + break; + case SHIFT: + shiftKey = true; + break; + default: + break; + } + } + } + } + + public boolean equals(ShortcutKeyCombination other) { + if( this.keyCode == other.keyCode && + this.altKey == other.altKey && + this.ctrlKey == other.ctrlKey && + this.shiftKey == other.shiftKey) + return true; + return false; + } +} \ No newline at end of file diff --git a/src/com/itmill/toolkit/ui/Button.java b/src/com/itmill/toolkit/ui/Button.java index b15d3b2da3..8968c229f2 100644 --- a/src/com/itmill/toolkit/ui/Button.java +++ b/src/com/itmill/toolkit/ui/Button.java @@ -52,17 +52,12 @@ import com.itmill.toolkit.terminal.PaintTarget; * @VERSION@ * @since 3.0 */ -public class Button extends AbstractField implements Action.Container { +public class Button extends AbstractField { /* Private members ************************************************* */ boolean switchMode = false; - /** List of action handlers */ - private LinkedList actionHandlers = null; - - /** Action mapper */ - private KeyMapper actionMapper = null; /** * Creates a new push button. The value of the push button is allways false @@ -176,44 +171,6 @@ public class Button extends AbstractField implements Action.Container { } target.addVariable(this, "state", state); - // Actions - if (actionHandlers != null) { - Set actionSet = new LinkedHashSet(); - for (Iterator ahi = actionHandlers.iterator(); ahi.hasNext();) { - Action[] aa = ((Action.Handler) ahi.next()).getActions(this, - this); - if (aa != null) - for (int ai = 0; ai < aa.length; ai++) { - actionSet.add(aa[ai]); - } - } - - target.startTag("actions"); - target.addVariable(this, "action", ""); - for (Iterator i = actionSet.iterator(); i.hasNext();) { - try { - ShortcutAction a = (ShortcutAction) i.next(); - target.startTag("action"); - if (a.getCaption() != null) - target.addAttribute("caption", a.getCaption()); - if (a.getIcon() != null) - target.addAttribute("icon", a.getIcon()); - target.addAttribute("key", actionMapper.key(a)); - target.addAttribute("keycode", a.getKeyCode()); - if (a.getModifiers() != null) { - int[] modifiers = a.getModifiers(); - target.addAttribute("modifiers", modifiers.length); - for (int j = 0; j < modifiers.length; j++) { - target.addAttribute("modifier" + j, modifiers[j]); - } - } - target.endTag("action"); - } catch (Exception e) { - // ignore non-shorcut actions for button - } - } - target.endTag("actions"); - } } /** @@ -249,21 +206,6 @@ public class Button extends AbstractField implements Action.Container { setValue(new Boolean(false)); } } - // Actions - // TODO this is pretty much copy-pasted from tree, may be simplified - if (variables.containsKey("action")) { - - StringTokenizer st = new StringTokenizer((String) variables - .get("action"), ","); - if (st.countTokens() == 2) { - Action action = (Action) actionMapper.get(st.nextToken()); - if (action != null && actionHandlers != null) - for (Iterator i = actionHandlers.iterator(); i.hasNext();) - ((Action.Handler) i.next()).handleAction(action, this, - this); - } - } - } /** @@ -403,44 +345,4 @@ public class Button extends AbstractField implements Action.Container { fireEvent(new Button.ClickEvent(this)); } - /** - * Adds an action handler. - * - * @see com.itmill.toolkit.event.Action.Container#addActionHandler(Action.Handler) - */ - public void addActionHandler(Action.Handler actionHandler) { - - if (actionHandler != null) { - - if (actionHandlers == null) { - actionHandlers = new LinkedList(); - actionMapper = new KeyMapper(); - } - - if (!actionHandlers.contains(actionHandler)) { - actionHandlers.add(actionHandler); - requestRepaint(); - } - } - } - - /** - * Removes an action handler. - * - * @see com.itmill.toolkit.event.Action.Container#removeActionHandler(Action.Handler) - */ - public void removeActionHandler(Action.Handler actionHandler) { - - if (actionHandlers != null && actionHandlers.contains(actionHandler)) { - - actionHandlers.remove(actionHandler); - - if (actionHandlers.isEmpty()) { - actionHandlers = null; - actionMapper = null; - } - - requestRepaint(); - } - } } diff --git a/src/com/itmill/toolkit/ui/Panel.java b/src/com/itmill/toolkit/ui/Panel.java index 6d0321f10b..f55a7e3896 100644 --- a/src/com/itmill/toolkit/ui/Panel.java +++ b/src/com/itmill/toolkit/ui/Panel.java @@ -29,8 +29,16 @@ package com.itmill.toolkit.ui; import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import com.itmill.toolkit.event.Action; +import com.itmill.toolkit.event.ShortcutAction; +import com.itmill.toolkit.event.Action.Handler; +import com.itmill.toolkit.terminal.KeyMapper; import com.itmill.toolkit.terminal.PaintException; import com.itmill.toolkit.terminal.PaintTarget; import com.itmill.toolkit.terminal.Scrollable; @@ -46,7 +54,7 @@ import com.itmill.toolkit.terminal.Sizeable; */ public class Panel extends AbstractComponentContainer implements Sizeable, Scrollable, ComponentContainer.ComponentAttachListener, - ComponentContainer.ComponentDetachListener { + ComponentContainer.ComponentDetachListener, Action.Container { /** * Layout of the panel. @@ -87,6 +95,12 @@ public class Panel extends AbstractComponentContainer implements Sizeable, * Scrolling mode. */ private boolean scrollable = false; + + /** List of action handlers */ + private LinkedList actionHandlers = null; + + /** Action mapper */ + private KeyMapper actionMapper = null; /** * Creates a new empty panel. Ordered layout is used. @@ -193,6 +207,41 @@ public class Panel extends AbstractComponentContainer implements Sizeable, target.addVariable(this, "scrollleft", getScrollOffsetX()); target.addVariable(this, "scrolldown", getScrollOffsetY()); } + + + if (actionHandlers != null && !actionHandlers.isEmpty()) { + target.addVariable(this, "action", ""); + target.startTag("actions"); + + for (Iterator ahi = actionHandlers.iterator(); ahi.hasNext();) { + Action[] aa = ((Action.Handler) ahi.next()).getActions(null, this); + if (aa != null) { + for (int ai = 0; ai < aa.length; ai++) { + Action a = aa[ai]; + target.startTag("action"); + String akey = actionMapper.key(aa[ai]); + target.addAttribute("key", akey); + if (a.getCaption() != null) + target.addAttribute("caption", a.getCaption()); + if (a.getIcon() != null) + target.addAttribute("icon", a.getIcon()); + if (a instanceof ShortcutAction) { + ShortcutAction sa = (ShortcutAction) a; + target.addAttribute("kc", sa.getKeyCode()); + int[] modifiers = sa.getModifiers(); + if(modifiers != null) { + String[] smodifiers = new String[modifiers.length]; + for (int i = 0; i < modifiers.length; i++) + smodifiers[i] = String.valueOf(modifiers[i]); + target.addAttribute("mk", smodifiers); + } + } + target.endTag("action"); + } + } + } + target.endTag("actions"); + } } /** @@ -312,6 +361,17 @@ public class Panel extends AbstractComponentContainer implements Sizeable, setScrollOffsetX(newScrollX.intValue()); if (newScrollY != null && newScrollY.intValue() != getScrollOffsetY()) setScrollOffsetY(newScrollY.intValue()); + + // Actions + if (variables.containsKey("action")) { + String key = (String) variables.get("action"); + Action action = (Action) actionMapper.get(key); + if (action != null && actionHandlers != null) + for (Iterator i = actionHandlers.iterator(); i.hasNext();) + ((Action.Handler) i.next()).handleAction(action, this, + this); + } + } /** @@ -454,4 +514,40 @@ public class Panel extends AbstractComponentContainer implements Sizeable, layout.removeAllComponents(); } + public void addActionHandler(Handler actionHandler) { + if (actionHandler != null) { + + if (actionHandlers == null) { + actionHandlers = new LinkedList(); + actionMapper = new KeyMapper(); + } + + if (!actionHandlers.contains(actionHandler)) { + actionHandlers.add(actionHandler); + requestRepaint(); + } + } + + } + + /** + * Removes an action handler. + * + * @see com.itmill.toolkit.event.Action.Container#removeActionHandler(Action.Handler) + */ + public void removeActionHandler(Action.Handler actionHandler) { + + if (actionHandlers != null && actionHandlers.contains(actionHandler)) { + + actionHandlers.remove(actionHandler); + + if (actionHandlers.isEmpty()) { + actionHandlers = null; + actionMapper = null; + } + + requestRepaint(); + } + } + } -- 2.39.5