diff options
author | patrik <patrik@vaadin.com> | 2015-08-06 14:08:38 +0300 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2015-08-28 07:22:24 +0000 |
commit | 1d36db6d112c818a9c75e7cd10eb6b3519119406 (patch) | |
tree | 107d6b67a75216dc5adca0833d90049751807fd3 | |
parent | c6622ac5cbf4ddbcec35e02f92f74cf46d147e71 (diff) | |
download | vaadin-framework-1d36db6d112c818a9c75e7cd10eb6b3519119406.tar.gz vaadin-framework-1d36db6d112c818a9c75e7cd10eb6b3519119406.zip |
Add better keyboard Close Shortcut API for Window (#17383)
Change-Id: I29c7d288fe35f6801cf3576ba06751adce821340
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; + } + +} |