summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpatrik <patrik@vaadin.com>2015-08-06 14:08:38 +0300
committerVaadin Code Review <review@vaadin.com>2015-08-28 07:22:24 +0000
commit1d36db6d112c818a9c75e7cd10eb6b3519119406 (patch)
tree107d6b67a75216dc5adca0833d90049751807fd3
parentc6622ac5cbf4ddbcec35e02f92f74cf46d147e71 (diff)
downloadvaadin-framework-1d36db6d112c818a9c75e7cd10eb6b3519119406.tar.gz
vaadin-framework-1d36db6d112c818a9c75e7cd10eb6b3519119406.zip
Add better keyboard Close Shortcut API for Window (#17383)
Change-Id: I29c7d288fe35f6801cf3576ba06751adce821340
-rw-r--r--WebContent/release-notes.html5
-rw-r--r--client/src/com/vaadin/client/ui/VWindow.java13
-rw-r--r--server/src/com/vaadin/event/ShortcutAction.java30
-rw-r--r--server/src/com/vaadin/ui/Window.java238
-rw-r--r--server/tests/src/com/vaadin/tests/design/DesignFormatterTest.java4
-rw-r--r--server/tests/src/com/vaadin/tests/server/component/button/ButtonDeclarativeTest.java2
-rw-r--r--server/tests/src/com/vaadin/tests/server/component/window/WindowDeclarativeTest.java39
-rw-r--r--uitest/src/com/vaadin/tests/components/window/WindowCloseShortcuts.java199
8 files changed, 467 insertions, 63 deletions
diff --git a/WebContent/release-notes.html b/WebContent/release-notes.html
index b7cdba7887..61511b3002 100644
--- a/WebContent/release-notes.html
+++ b/WebContent/release-notes.html
@@ -109,6 +109,11 @@
<h3 id="incompatible">Incompatible or Behavior-altering Changes in @version-minor@</h3>
<ul>
+ <li>Window close shortcuts have been changed, and ESCAPE is no longer a hard-coded default, but rather a soft one,
+ and can be removed. If the <pre>close-shortcut</pre> attribute of the <pre>v-window</pre> element is present,
+ it must list all close shortcuts, including ESCAPE, separated by whitespace. Existing, unchanged code should
+ behave as before. See ticket <a href="https://dev.vaadin.com/ticket/17383">#17383</a> for more information
+ on the reasoning behind the change.</li>
<li>The push path has been changed from /PUSH/ to /PUSH to be compatible with JSR 356.</li>
<li>Widgetset files and other pre-compressed resources are sent as gzip to compatible browsers.
This may interfere with custom response compression solutions that do not respect the Content-Encoding response header.</li>
diff --git a/client/src/com/vaadin/client/ui/VWindow.java b/client/src/com/vaadin/client/ui/VWindow.java
index e34e12a20b..2d2d6ecee1 100644
--- a/client/src/com/vaadin/client/ui/VWindow.java
+++ b/client/src/com/vaadin/client/ui/VWindow.java
@@ -44,8 +44,6 @@ import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
-import com.google.gwt.event.dom.client.KeyUpEvent;
-import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.ScrollEvent;
import com.google.gwt.event.dom.client.ScrollHandler;
import com.google.gwt.event.shared.HandlerRegistration;
@@ -79,8 +77,7 @@ import com.vaadin.shared.ui.window.WindowRole;
* @author Vaadin Ltd
*/
public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
- ScrollHandler, KeyDownHandler, KeyUpHandler, FocusHandler, BlurHandler,
- Focusable {
+ ScrollHandler, KeyDownHandler, FocusHandler, BlurHandler, Focusable {
private static ArrayList<VWindow> windowOrder = new ArrayList<VWindow>();
@@ -221,7 +218,6 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
constructDOM();
contentPanel.addScrollHandler(this);
contentPanel.addKeyDownHandler(this);
- contentPanel.addKeyUpHandler(this);
contentPanel.addFocusHandler(this);
contentPanel.addBlurHandler(this);
}
@@ -1344,13 +1340,6 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
}
@Override
- public void onKeyUp(KeyUpEvent event) {
- if (isClosable() && event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
- onCloseClick();
- }
- }
-
- @Override
public void onBlur(BlurEvent event) {
if (client.hasEventListeners(this, EventId.BLUR)) {
client.updateVariable(id, EventId.BLUR, "", true);
diff --git a/server/src/com/vaadin/event/ShortcutAction.java b/server/src/com/vaadin/event/ShortcutAction.java
index 09accae1c7..dd511c23c0 100644
--- a/server/src/com/vaadin/event/ShortcutAction.java
+++ b/server/src/com/vaadin/event/ShortcutAction.java
@@ -17,6 +17,7 @@
package com.vaadin.event;
import java.io.Serializable;
+import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -55,7 +56,7 @@ public class ShortcutAction extends Action {
private final int keyCode;
- private final int[] modifiers;
+ private int[] modifiers;
/**
* Creates a shortcut that reacts to the given {@link KeyCode} and
@@ -73,7 +74,7 @@ public class ShortcutAction extends Action {
public ShortcutAction(String caption, int kc, int... m) {
super(caption);
keyCode = kc;
- modifiers = m;
+ setModifiers(m);
}
/**
@@ -94,7 +95,7 @@ public class ShortcutAction extends Action {
public ShortcutAction(String caption, Resource icon, int kc, int... m) {
super(caption, icon);
keyCode = kc;
- modifiers = m;
+ setModifiers(m);
}
/**
@@ -190,7 +191,7 @@ public class ShortcutAction extends Action {
// Given modifiers override this indicated in the caption
if (modifierKeys != null) {
- modifiers = modifierKeys;
+ setModifiers(modifierKeys);
} else {
// Read modifiers from caption
int[] mod = new int[match.length() - 1];
@@ -208,13 +209,30 @@ public class ShortcutAction extends Action {
break;
}
}
- modifiers = mod;
+ setModifiers(mod);
}
} else {
keyCode = -1;
- modifiers = modifierKeys;
+ setModifiers(modifierKeys);
}
+
+ }
+
+ /**
+ * When setting modifiers, make sure that modifiers is a valid array AND
+ * that it's sorted.
+ *
+ * @param modifiers
+ * the modifier keys for this shortcut
+ */
+ private void setModifiers(int... modifiers) {
+ if (modifiers == null) {
+ this.modifiers = new int[0];
+ } else {
+ this.modifiers = modifiers;
+ }
+ Arrays.sort(this.modifiers);
}
/**
diff --git a/server/src/com/vaadin/ui/Window.java b/server/src/com/vaadin/ui/Window.java
index 61ba5826b8..fd5565f900 100644
--- a/server/src/com/vaadin/ui/Window.java
+++ b/server/src/com/vaadin/ui/Window.java
@@ -21,6 +21,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -74,7 +75,7 @@ import com.vaadin.util.ReflectTools;
* @author Vaadin Ltd.
* @since 3.0
*/
-@SuppressWarnings("serial")
+@SuppressWarnings({ "serial", "deprecation" })
public class Window extends Panel implements FocusNotifier, BlurNotifier,
LegacyComponent {
@@ -102,6 +103,11 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
};
/**
+ * Holds registered CloseShortcut instances for query and later removal
+ */
+ private List<CloseShortcut> closeShortcuts = new ArrayList<CloseShortcut>(4);
+
+ /**
* Creates a new, empty window
*/
public Window() {
@@ -130,6 +136,7 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
super(caption, content);
registerRpc(rpc);
setSizeUndefined();
+ setCloseShortcut(KeyCode.ESCAPE);
}
/* ********************************************************************* */
@@ -828,14 +835,22 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
}
}
- /*
- * Actions
- */
- protected CloseShortcut closeShortcut;
-
/**
- * Makes is possible to close the window by pressing the given
- * {@link KeyCode} and (optional) {@link ModifierKey}s.<br/>
+ * This is the old way of adding a keyboard shortcut to close a
+ * {@link Window} - to preserve compatibility with existing code under the
+ * new functionality, this method now first removes all registered close
+ * shortcuts, then adds the default ESCAPE shortcut key, and then attempts
+ * to add the shortcut provided as parameters to this method. This method,
+ * and its companion {@link #removeCloseShortcut()}, are now considered
+ * deprecated, as their main function is to preserve exact backwards
+ * compatibility with old code. For all new code, use the new keyboard
+ * shortcuts API: {@link #addCloseShortcut(int,int...)},
+ * {@link #removeCloseShortcut(int,int...)},
+ * {@link #removeAllCloseShortcuts()}, {@link #hasCloseShortcut(int,int...)}
+ * and {@link #getCloseShortcuts()}.
+ * <p>
+ * Original description: Makes it possible to close the window by pressing
+ * the given {@link KeyCode} and (optional) {@link ModifierKey}s.<br/>
* Note that this shortcut only reacts while the window has focus, closing
* itself - if you want to close a window from a UI, use
* {@link UI#addAction(com.vaadin.event.Action)} of the UI instead.
@@ -843,29 +858,137 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
* @param keyCode
* the keycode for invoking the shortcut
* @param modifiers
- * the (optional) modifiers for invoking the shortcut, null for
- * none
+ * the (optional) modifiers for invoking the shortcut. Can be set
+ * to null to be explicit about not having modifiers.
+ *
+ * @deprecated Use {@link #addCloseShortcut(int, int...)} instead.
*/
+ @Deprecated
public void setCloseShortcut(int keyCode, int... modifiers) {
- if (closeShortcut != null) {
- removeAction(closeShortcut);
- }
- closeShortcut = new CloseShortcut(this, keyCode, modifiers);
- addAction(closeShortcut);
+ removeCloseShortcut();
+ addCloseShortcut(keyCode, modifiers);
}
/**
- * Removes the keyboard shortcut previously set with
- * {@link #setCloseShortcut(int, int...)}.
+ * Removes all keyboard shortcuts previously set with
+ * {@link #setCloseShortcut(int, int...)} and
+ * {@link #addCloseShortcut(int, int...)}, then adds the default
+ * {@link KeyCode#ESCAPE} shortcut.
+ * <p>
+ * This is the old way of removing the (single) keyboard close shortcut, and
+ * is retained only for exact backwards compatibility. For all new code, use
+ * the new keyboard shortcuts API: {@link #addCloseShortcut(int,int...)},
+ * {@link #removeCloseShortcut(int,int...)},
+ * {@link #removeAllCloseShortcuts()}, {@link #hasCloseShortcut(int,int...)}
+ * and {@link #getCloseShortcuts()}.
+ *
+ * @deprecated Use {@link #removeCloseShortcut(int, int...)} instead.
*/
+ @Deprecated
public void removeCloseShortcut() {
- if (closeShortcut != null) {
- removeAction(closeShortcut);
- closeShortcut = null;
+ for (int i = 0; i < closeShortcuts.size(); ++i) {
+ CloseShortcut sc = closeShortcuts.get(i);
+ removeAction(sc);
+ }
+ closeShortcuts.clear();
+ addCloseShortcut(KeyCode.ESCAPE);
+ }
+
+ /**
+ * Adds a close shortcut - pressing this key while holding down all (if any)
+ * modifiers specified while this Window is in focus will close the Window.
+ *
+ * @since
+ * @param keyCode
+ * the keycode for invoking the shortcut
+ * @param modifiers
+ * the (optional) modifiers for invoking the shortcut. Can be set
+ * to null to be explicit about not having modifiers.
+ */
+ public void addCloseShortcut(int keyCode, int... modifiers) {
+
+ // Ignore attempts to re-add existing shortcuts
+ if (hasCloseShortcut(keyCode, modifiers)) {
+ return;
+ }
+
+ // Actually add the shortcut
+ CloseShortcut shortcut = new CloseShortcut(this, keyCode, modifiers);
+ addAction(shortcut);
+ closeShortcuts.add(shortcut);
+ }
+
+ /**
+ * Removes a close shortcut previously added with
+ * {@link #addCloseShortcut(int, int...)}.
+ *
+ * @since
+ * @param keyCode
+ * the keycode for invoking the shortcut
+ * @param modifiers
+ * the (optional) modifiers for invoking the shortcut. Can be set
+ * to null to be explicit about not having modifiers.
+ */
+ public void removeCloseShortcut(int keyCode, int... modifiers) {
+ for (CloseShortcut shortcut : closeShortcuts) {
+ if (shortcut.equals(keyCode, modifiers)) {
+ removeAction(shortcut);
+ closeShortcuts.remove(shortcut);
+ return;
+ }
}
}
/**
+ * Removes all close shortcuts. This includes the default ESCAPE shortcut.
+ * It is up to the user to add back any and all keyboard close shortcuts
+ * they may require. For more fine-grained control over shortcuts, use
+ * {@link #removeCloseShortcut(int, int...)}.
+ *
+ * @since
+ */
+ public void removeAllCloseShortcuts() {
+ for (CloseShortcut shortcut : closeShortcuts) {
+ removeAction(shortcut);
+ }
+ closeShortcuts.clear();
+ }
+
+ /**
+ * Checks if a close window shortcut key has already been registered.
+ *
+ * @since
+ * @param keyCode
+ * the keycode for invoking the shortcut
+ * @param modifiers
+ * the (optional) modifiers for invoking the shortcut. Can be set
+ * to null to be explicit about not having modifiers.
+ * @return true, if an exactly matching shortcut has been registered.
+ */
+ public boolean hasCloseShortcut(int keyCode, int... modifiers) {
+ for (CloseShortcut shortcut : closeShortcuts) {
+ if (shortcut.equals(keyCode, modifiers)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns an unmodifiable collection of {@link CloseShortcut} objects
+ * currently registered with this {@link Window}. This method is provided
+ * mainly so that users can implement their own serialization routines. To
+ * check if a certain combination of keys has been registered as a close
+ * shortcut, use the {@link #hasCloseShortcut(int, int...)} method instead.
+ *
+ * @since
+ * @return an unmodifiable Collection of CloseShortcut objects.
+ */
+ public Collection<CloseShortcut> getCloseShortcuts() {
+ return Collections.unmodifiableCollection(closeShortcuts);
+ }
+
+ /**
* A {@link ShortcutListener} specifically made to define a keyboard
* shortcut that closes the window.
*
@@ -930,6 +1053,25 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
public void handleAction(Object sender, Object target) {
window.close();
}
+
+ public boolean equals(int keyCode, int... modifiers) {
+ if (keyCode != getKeyCode()) {
+ return false;
+ }
+
+ if (getModifiers() != null) {
+ int[] mods = null;
+ if (modifiers != null) {
+ // Modifiers provided by the parent ShortcutAction class
+ // are guaranteed to be sorted. We still need to sort
+ // the modifiers passed in as argument.
+ mods = Arrays.copyOf(modifiers, modifiers.length);
+ Arrays.sort(mods);
+ }
+ return Arrays.equals(mods, getModifiers());
+ }
+ return true;
+ }
}
/*
@@ -1244,11 +1386,26 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
setPositionX(Integer.parseInt(position[0]));
setPositionY(Integer.parseInt(position[1]));
}
+
+ // Parse shortcuts if defined, otherwise rely on default behavior
if (design.hasAttr("close-shortcut")) {
- ShortcutAction shortcut = DesignAttributeHandler
- .readAttribute("close-shortcut", design.attributes(),
- ShortcutAction.class);
- setCloseShortcut(shortcut.getKeyCode(), shortcut.getModifiers());
+
+ // Parse shortcuts
+ String[] shortcutStrings = DesignAttributeHandler.readAttribute(
+ "close-shortcut", design.attributes(), String.class).split(
+ "\\s+");
+
+ removeAllCloseShortcuts();
+
+ for (String part : shortcutStrings) {
+ if (!part.isEmpty()) {
+ ShortcutAction shortcut = DesignAttributeHandler
+ .getFormatter().parse(part.trim(),
+ ShortcutAction.class);
+ addCloseShortcut(shortcut.getKeyCode(),
+ shortcut.getModifiers());
+ }
+ }
}
}
@@ -1302,19 +1459,24 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
DesignAttributeHandler.writeAttribute("position", design.attributes(),
getPosition(), def.getPosition(), String.class);
- CloseShortcut shortcut = getCloseShortcut();
- if (shortcut != null) {
- // TODO What if several close shortcuts??
-
- CloseShortcut defShortcut = def.getCloseShortcut();
- if (defShortcut == null
- || shortcut.getKeyCode() != defShortcut.getKeyCode()
- || !Arrays.equals(shortcut.getModifiers(),
- defShortcut.getModifiers())) {
- DesignAttributeHandler.writeAttribute("close-shortcut",
- design.attributes(), shortcut, null,
- CloseShortcut.class);
+ // Process keyboard shortcuts
+ if (closeShortcuts.size() == 1 && hasCloseShortcut(KeyCode.ESCAPE)) {
+ // By default, we won't write anything if we're relying on default
+ // shortcut behavior
+ } else {
+ // Dump all close shortcuts to a string...
+ String attrString = "";
+
+ // TODO: add canonical support for array data in XML attributes
+ for (CloseShortcut shortcut : closeShortcuts) {
+ String shortcutString = DesignAttributeHandler.getFormatter()
+ .format(shortcut, CloseShortcut.class);
+ attrString += shortcutString + " ";
}
+
+ // Write everything except the last "," to the design
+ DesignAttributeHandler.writeAttribute("close-shortcut",
+ design.attributes(), attrString.trim(), null, String.class);
}
for (Component c : getAssistiveDescription()) {
@@ -1328,10 +1490,6 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
return getPositionX() + "," + getPositionY();
}
- private CloseShortcut getCloseShortcut() {
- return closeShortcut;
- }
-
@Override
protected Collection<String> getCustomAttributes() {
Collection<String> result = super.getCustomAttributes();
diff --git a/server/tests/src/com/vaadin/tests/design/DesignFormatterTest.java b/server/tests/src/com/vaadin/tests/design/DesignFormatterTest.java
index acee3e2ca8..6510d8ad40 100644
--- a/server/tests/src/com/vaadin/tests/design/DesignFormatterTest.java
+++ b/server/tests/src/com/vaadin/tests/design/DesignFormatterTest.java
@@ -203,7 +203,7 @@ public class DesignFormatterTest {
ShortcutAction action = new ShortcutAction("&^d");
String formatted = formatter.format(action);
// note the space here - it separates key combination from caption
- assertEquals("alt-ctrl-d d", formatted);
+ assertEquals("ctrl-alt-d d", formatted);
ShortcutAction result = formatter
.parse(formatted, ShortcutAction.class);
@@ -215,7 +215,7 @@ public class DesignFormatterTest {
ShortcutAction action = new ShortcutAction(null, KeyCode.D, new int[] {
ModifierKey.ALT, ModifierKey.CTRL });
String formatted = formatter.format(action);
- assertEquals("alt-ctrl-d", formatted);
+ assertEquals("ctrl-alt-d", formatted);
ShortcutAction result = formatter
.parse(formatted, ShortcutAction.class);
diff --git a/server/tests/src/com/vaadin/tests/server/component/button/ButtonDeclarativeTest.java b/server/tests/src/com/vaadin/tests/server/component/button/ButtonDeclarativeTest.java
index 51abf6be96..b1c10789b4 100644
--- a/server/tests/src/com/vaadin/tests/server/component/button/ButtonDeclarativeTest.java
+++ b/server/tests/src/com/vaadin/tests/server/component/button/ButtonDeclarativeTest.java
@@ -98,7 +98,7 @@ public class ButtonDeclarativeTest extends DeclarativeTestBase<Button> {
@Test
public void testAttributes() {
String design = "<v-button tabindex=3 plain-text='' icon-alt=OK "
- + "click-shortcut=ctrl-shift-o></v-button>";
+ + "click-shortcut=shift-ctrl-o></v-button>";
Button b = new Button("");
b.setTabIndex(3);
b.setIconAlternateText("OK");
diff --git a/server/tests/src/com/vaadin/tests/server/component/window/WindowDeclarativeTest.java b/server/tests/src/com/vaadin/tests/server/component/window/WindowDeclarativeTest.java
index 1d233af494..607fb471b9 100644
--- a/server/tests/src/com/vaadin/tests/server/component/window/WindowDeclarativeTest.java
+++ b/server/tests/src/com/vaadin/tests/server/component/window/WindowDeclarativeTest.java
@@ -71,8 +71,9 @@ public class WindowDeclarativeTest extends DeclarativeTestBase<Window> {
expected.setClosable(!expected.isClosable());
expected.setDraggable(!expected.isDraggable());
- expected.setCloseShortcut(KeyCode.ESCAPE, ModifierKey.CTRL,
- ModifierKey.ALT);
+ expected.removeAllCloseShortcuts();
+ expected.addCloseShortcut(KeyCode.ESCAPE, ModifierKey.ALT,
+ ModifierKey.CTRL);
expected.setAssistivePrefix("Hello");
expected.setAssistivePostfix("World");
@@ -86,6 +87,40 @@ public class WindowDeclarativeTest extends DeclarativeTestBase<Window> {
}
@Test
+ public void testMultiCloseShortcuts() {
+
+ Window expected = new Window();
+
+ // Add two shortcuts - should now contain three (default escape + two
+ // added)
+ expected.addCloseShortcut(KeyCode.SPACEBAR);
+ expected.addCloseShortcut(KeyCode.ARROW_LEFT, ModifierKey.ALT,
+ ModifierKey.CTRL);
+
+ // Try to add the same shortcut again, should be no-op
+ expected.addCloseShortcut(KeyCode.ARROW_LEFT, ModifierKey.CTRL,
+ ModifierKey.ALT);
+
+ // Add a third shortcut, should total four (default escape + three
+ // added)
+ expected.addCloseShortcut(KeyCode.ARROW_RIGHT, ModifierKey.CTRL);
+
+ // Test validity
+ String design = "<v-window close-shortcut='escape spacebar ctrl-alt-left ctrl-right' />";
+ testRead(design, expected);
+ testWrite(design, expected);
+
+ // Try removing the spacebar shortcut
+ expected.removeCloseShortcut(KeyCode.SPACEBAR);
+
+ // Test again
+ design = "<v-window close-shortcut='escape ctrl-alt-left ctrl-right' />";
+ testRead(design, expected);
+ testWrite(design, expected);
+
+ }
+
+ @Test
public void testInvalidPosition() {
assertInvalidPosition("");
assertInvalidPosition("1");
diff --git a/uitest/src/com/vaadin/tests/components/window/WindowCloseShortcuts.java b/uitest/src/com/vaadin/tests/components/window/WindowCloseShortcuts.java
new file mode 100644
index 0000000000..d9c22a26ee
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/window/WindowCloseShortcuts.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.components.window;
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Attribute;
+import org.jsoup.nodes.Element;
+import org.jsoup.nodes.Node;
+import org.jsoup.nodes.TextNode;
+
+import com.vaadin.annotations.Theme;
+import com.vaadin.event.ShortcutAction.KeyCode;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Button.ClickListener;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.Panel;
+import com.vaadin.ui.RichTextArea;
+import com.vaadin.ui.UI;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.Window;
+import com.vaadin.ui.declarative.Design;
+import com.vaadin.ui.declarative.DesignContext;
+
+@Theme("valo")
+@SuppressWarnings("serial")
+public class WindowCloseShortcuts extends AbstractTestUI {
+
+ private Window window;
+ private Label designLabel;
+ private VerticalLayout buttonLayout;
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ getLayout().setSpacing(true);
+
+ window = new Window("Test window");
+ window.setVisible(true);
+ window.setModal(true);
+ window.setContent(new RichTextArea());
+
+ Panel buttonPanel = new Panel();
+ buttonLayout = new VerticalLayout();
+ buttonLayout.setSizeFull();
+ buttonPanel.setCaption("Demo controls");
+
+ addButton(new Button("Open window", new ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ UI.getCurrent().addWindow(window);
+ window.center();
+ updateDesign();
+ }
+ }));
+
+ addButton(new Button("Add ENTER close shortcut", new ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ window.addCloseShortcut(KeyCode.ENTER);
+ updateDesign();
+ }
+ }));
+
+ addButton(new Button("Add TAB close shortcut", new ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ window.addCloseShortcut(KeyCode.TAB);
+ updateDesign();
+ }
+ }));
+
+ addButton(new Button("Remove ESC close shortcut", new ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ window.removeCloseShortcut(KeyCode.ESCAPE);
+ updateDesign();
+ }
+ }));
+
+ addButton(new Button("Clear all close shortcuts", new ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ window.removeAllCloseShortcuts();
+ updateDesign();
+ }
+ }));
+
+ addButton(new Button("Reset to default state", new ClickListener() {
+ @Override
+ @SuppressWarnings("deprecation")
+ public void buttonClick(ClickEvent event) {
+ window.removeCloseShortcut();
+ updateDesign();
+ }
+ }));
+
+ buttonPanel.setContent(buttonLayout);
+ buttonPanel.setSizeUndefined();
+ addComponent(buttonPanel);
+ buttonPanel.setWidth("400px");
+
+ Panel designPanel = new Panel();
+ designPanel.setCaption("Window design");
+ designLabel = new Label("");
+ VerticalLayout designLayout = new VerticalLayout();
+ designLayout.addComponent(designLabel);
+ designPanel.setContent(designLayout);
+ addComponent(designPanel);
+
+ updateDesign();
+ }
+
+ private void addButton(Button b) {
+ b.setWidth("100%");
+ buttonLayout.addComponent(b);
+ }
+
+ //
+ // The following code is adapted from DeclarativeTestBaseBase.java
+ // (that's not a typo)
+ //
+
+ private void updateDesign() {
+ String design = "";
+ try {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ DesignContext dc = new DesignContext();
+ dc.setRootComponent(window);
+ Design.write(dc, outputStream);
+ design = outputStream.toString("UTF-8");
+ } catch (Exception e) {
+ return;
+ }
+ Element producedElem = Jsoup.parse(design).body().child(0);
+ design = elementToHtml(producedElem);
+ designLabel.setCaption(design);
+ }
+
+ //
+ // The following code is copied directly from DeclarativeTestBaseBase.java
+ // (that's not a typo, either)
+ //
+
+ private String elementToHtml(Element producedElem) {
+ StringBuilder stringBuilder = new StringBuilder();
+ elementToHtml(producedElem, stringBuilder);
+ return stringBuilder.toString();
+ }
+
+ private String elementToHtml(Element producedElem, StringBuilder sb) {
+ ArrayList<String> names = new ArrayList<String>();
+ for (Attribute a : producedElem.attributes().asList()) {
+ names.add(a.getKey());
+ }
+ Collections.sort(names);
+
+ sb.append("<" + producedElem.tagName() + "");
+ for (String attrName : names) {
+ sb.append(" ").append(attrName).append("=").append("\'")
+ .append(producedElem.attr(attrName)).append("\'");
+ }
+ sb.append(">");
+ for (Node child : producedElem.childNodes()) {
+ if (child instanceof Element) {
+ elementToHtml((Element) child, sb);
+ } else if (child instanceof TextNode) {
+ String text = ((TextNode) child).text();
+ sb.append(text.trim());
+ }
+ }
+ sb.append("</").append(producedElem.tagName()).append(">");
+ return sb.toString();
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 17383;
+ }
+
+}