]> source.dussan.org Git - vaadin-framework.git/commitdiff
Fix declarative support for MenuBar (#16328)
authorMiki <miki@vaadin.com>
Tue, 10 Feb 2015 13:04:27 +0000 (15:04 +0200)
committerVaadin Code Review <review@vaadin.com>
Thu, 26 Mar 2015 13:33:30 +0000 (13:33 +0000)
Change-Id: Icd70a02aa8ffef9d1aca4b833ac23aeff5813771

server/src/com/vaadin/ui/MenuBar.java
server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java
server/tests/src/com/vaadin/tests/components/menubar/MenuBarDeclarativeTest.java [new file with mode: 0644]

index 6b6555c0a2a762e98653763cb08d2578e2b1fc51..747ce427279eb7952d94f267a0b26fc165a4866c 100644 (file)
@@ -17,17 +17,25 @@ package com.vaadin.ui;
 
 import java.io.Serializable;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Stack;
 
+import org.jsoup.nodes.Attributes;
+import org.jsoup.nodes.Element;
+import org.jsoup.nodes.Node;
+import org.jsoup.parser.Tag;
+
 import com.vaadin.server.PaintException;
 import com.vaadin.server.PaintTarget;
 import com.vaadin.server.Resource;
 import com.vaadin.shared.ui.menubar.MenuBarConstants;
 import com.vaadin.shared.ui.menubar.MenuBarState;
 import com.vaadin.ui.Component.Focusable;
+import com.vaadin.ui.declarative.DesignAttributeHandler;
+import com.vaadin.ui.declarative.DesignContext;
 
 /**
  * <p>
@@ -932,7 +940,150 @@ public class MenuBar extends AbstractComponent implements LegacyComponent,
             this.checked = checked;
             markAsDirty();
         }
-
     }// class MenuItem
 
+    @Override
+    public void writeDesign(Element design, DesignContext designContext) {
+        super.writeDesign(design, designContext);
+        for (MenuItem item : getItems()) {
+            design.appendChild(createMenuElement(item));
+        }
+
+        // in many cases there seems to be an empty more menu item
+        if (getMoreMenuItem() != null && !getMoreMenuItem().getText().isEmpty()) {
+            Element moreMenu = createMenuElement(getMoreMenuItem());
+            moreMenu.attr("more", "");
+            design.appendChild(moreMenu);
+        }
+
+        if (!htmlContentAllowed) {
+            design.attr(DESIGN_ATTR_PLAIN_TEXT, "");
+        }
+    }
+
+    protected Element createMenuElement(MenuItem item) {
+        Element menuElement = new Element(Tag.valueOf("menu"), "");
+        // Defaults
+        MenuItem def = new MenuItem("", null, null);
+
+        Attributes attr = menuElement.attributes();
+        DesignAttributeHandler.writeAttribute("icon", attr, item.getIcon(),
+                def.getIcon(), Resource.class);
+        DesignAttributeHandler.writeAttribute("disabled", attr,
+                !item.isEnabled(), !def.isEnabled(), boolean.class);
+        DesignAttributeHandler.writeAttribute("visible", attr,
+                item.isVisible(), def.isVisible(), boolean.class);
+        DesignAttributeHandler.writeAttribute("separator", attr,
+                item.isSeparator(), def.isSeparator(), boolean.class);
+        DesignAttributeHandler.writeAttribute("checkable", attr,
+                item.isCheckable(), def.isCheckable(), boolean.class);
+        DesignAttributeHandler.writeAttribute("checked", attr,
+                item.isChecked(), def.isChecked(), boolean.class);
+        DesignAttributeHandler.writeAttribute("description", attr,
+                item.getDescription(), def.getDescription(), String.class);
+        DesignAttributeHandler.writeAttribute("style-name", attr,
+                item.getStyleName(), def.getStyleName(), String.class);
+
+        menuElement.append(item.getText());
+
+        if (item.hasChildren()) {
+            for (MenuItem subMenu : item.getChildren()) {
+                menuElement.appendChild(createMenuElement(subMenu));
+            }
+        }
+
+        return menuElement;
+    }
+
+    protected MenuItem readMenuElement(Element menuElement) {
+        Resource icon = null;
+        if (menuElement.hasAttr("icon")) {
+            icon = DesignAttributeHandler.getFormatter().parse(
+                    menuElement.attr("icon"), Resource.class);
+        }
+
+        String caption = "";
+        List<Element> subMenus = new ArrayList<Element>();
+        for (Node node : menuElement.childNodes()) {
+            if (node instanceof Element
+                    && ((Element) node).tagName().equals("menu")) {
+                subMenus.add((Element) node);
+            }
+            caption += node.toString();
+        }
+        MenuItem menu = new MenuItem(caption.trim(), icon, null);
+
+        Attributes attr = menuElement.attributes();
+        if (menuElement.hasAttr("icon")) {
+            menu.setIcon(DesignAttributeHandler.readAttribute("icon", attr,
+                    Resource.class));
+        }
+        if (menuElement.hasAttr("disabled")) {
+            menu.setEnabled(!DesignAttributeHandler.readAttribute("disabled",
+                    attr, boolean.class));
+        }
+        if (menuElement.hasAttr("visible")) {
+            menu.setVisible(DesignAttributeHandler.readAttribute("visible",
+                    attr, boolean.class));
+        }
+        if (menuElement.hasAttr("separator")) {
+            menu.setSeparator(DesignAttributeHandler.readAttribute("separator",
+                    attr, boolean.class));
+        }
+        if (menuElement.hasAttr("checkable")) {
+            menu.setCheckable(DesignAttributeHandler.readAttribute("checkable",
+                    attr, boolean.class));
+        }
+        if (menuElement.hasAttr("checked")) {
+            menu.setChecked(DesignAttributeHandler.readAttribute("checked",
+                    attr, boolean.class));
+        }
+        if (menuElement.hasAttr("description")) {
+            menu.setDescription(DesignAttributeHandler.readAttribute(
+                    "description", attr, String.class));
+        }
+        if (menuElement.hasAttr("style-name")) {
+            menu.setStyleName(DesignAttributeHandler.readAttribute(
+                    "style-name", attr, String.class));
+        }
+
+        if (!subMenus.isEmpty()) {
+            menu.itsChildren = new ArrayList<MenuItem>();
+        }
+
+        for (Element subMenu : subMenus) {
+            MenuItem newItem = readMenuElement(subMenu);
+
+            newItem.setParent(menu);
+            menu.itsChildren.add(newItem);
+        }
+
+        return menu;
+    }
+
+    @Override
+    public void readDesign(Element design, DesignContext designContext) {
+        super.readDesign(design, designContext);
+
+        for (Element itemElement : design.children()) {
+            if (itemElement.tagName().equals("menu")) {
+                MenuItem menuItem = readMenuElement(itemElement);
+                if (itemElement.hasAttr("more")) {
+                    setMoreMenuItem(menuItem);
+                } else {
+                    menuItems.add(menuItem);
+                }
+            }
+        }
+
+        setHtmlContentAllowed(!design.hasAttr(DESIGN_ATTR_PLAIN_TEXT));
+    }
+
+    @Override
+    protected Collection<String> getCustomAttributes() {
+        Collection<String> result = super.getCustomAttributes();
+        result.add(DESIGN_ATTR_PLAIN_TEXT);
+        result.add("html-content-allowed");
+        return result;
+    }
 }// class MenuBar
index 215afd50412e5029c82f074aab211de4e331b7ae..2b446bda0e269ddadd68aa6a246edab2301132d8 100644 (file)
@@ -38,7 +38,6 @@ import org.jsoup.nodes.Node;
 
 import com.vaadin.data.util.converter.Converter;
 import com.vaadin.shared.util.SharedUtil;
-import com.vaadin.ui.Component;
 
 /**
  * Default attribute handler implementation used when parsing designs to
@@ -192,8 +191,8 @@ public class DesignAttributeHandler implements Serializable {
      * @param defaultInstance
      *            the default instance for comparing default values
      */
-    public static void writeAttribute(Component component, String attribute,
-            Attributes attr, Component defaultInstance) {
+    public static void writeAttribute(Object component, String attribute,
+            Attributes attr, Object defaultInstance) {
         Method getter = findGetterForAttribute(component.getClass(), attribute);
         if (getter == null) {
             getLogger().warning(
diff --git a/server/tests/src/com/vaadin/tests/components/menubar/MenuBarDeclarativeTest.java b/server/tests/src/com/vaadin/tests/components/menubar/MenuBarDeclarativeTest.java
new file mode 100644 (file)
index 0000000..e6dee44
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * 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.menubar;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import com.vaadin.server.ExternalResource;
+import com.vaadin.server.ThemeResource;
+import com.vaadin.tests.design.DeclarativeTestBase;
+import com.vaadin.ui.MenuBar;
+import com.vaadin.ui.MenuBar.MenuItem;
+
+/**
+ * Tests declarative support for menu bars.
+ * 
+ * @since 7.4
+ * @author Vaadin Ltd
+ */
+public class MenuBarDeclarativeTest extends DeclarativeTestBase<MenuBar> {
+
+    @Test
+    // #16328
+    public void testReadWrite() throws IOException {
+        String design = "<v-menu-bar auto-open='true' tabindex=5>"
+                + "<menu checkable='true'>Save</menu>"
+                + "<menu description='Open a file'>Open</menu>"
+                + "<menu disabled='true'>Close</menu>"
+                + "<menu icon='http://foo.bar/ico.png'>Help</menu>"
+                + "<menu visible='false'>About</menu>"
+                + "<menu>Sub<menu>Item</menu></menu>"
+                + "<menu more>WTF?!</menu>" + "</v-menu-bar>";
+        MenuBar bar = new MenuBar();
+        bar.setAutoOpen(true);
+        bar.setHtmlContentAllowed(true);
+        bar.setTabIndex(5);
+
+        bar.addItem("Save", null).setCheckable(true);
+        bar.addItem("Open", null).setDescription("Open a file");
+        bar.addItem("Close", null).setEnabled(false);
+        bar.addItem("Help", null).setIcon(
+                new ExternalResource("http://foo.bar/ico.png"));
+        bar.addItem("About", null).setVisible(false);
+
+        bar.addItem("Sub", null).addItem("Item", null);
+
+        bar.setMoreMenuItem(bar.new MenuItem("WTF?!", null, null));
+
+        testWrite(design, bar);
+        testRead(design, bar);
+    }
+
+    @Test
+    // #16328
+    public void testTicketSpec1() throws IOException {
+        String design = "<v-menu-bar auto-open='true' plain-text tabindex=5> "
+                + "<menu>File"
+                + "<menu>Save</menu>"
+                + "<menu icon=\"theme://../runo/icons/16/folder.png\">Open</menu>"
+                + "<menu separator='true' />"
+                + "<menu disabled='true'>Exit</menu>"
+                + "<menu visible='false'>Not for everybody</menu>"
+                + "</menu>"
+                + "<menu description=\"This contains many items in sub menus\">Other"
+                + "<menu style-name=\"fancy\">Sub"
+                + "<menu checkable='true' checked='true'>Option 1 - no <b>html</b></menu>"
+                + "<menu checkable='true'>Option 2</menu>"
+                + "<menu checkable='true'>Option 3</menu>" //
+                + "</menu>" //
+                + "</menu>" //
+                + "<menu more icon=\"theme://icon.png\">foo</menu>"
+                + "</v-menu-bar>";
+        // for one reason or another, no component has a correct .equals
+        // implementation, which makes tests a bit annoying
+        MenuBar menuBar = new MenuBar();
+        menuBar.setHtmlContentAllowed(false);
+        menuBar.setTabIndex(5);
+        menuBar.setAutoOpen(true);
+        // File menu
+        MenuItem fileMenu = menuBar.addItem("File", null);
+        fileMenu.addItem("Save", null);
+        fileMenu.addItem("Open", new ThemeResource(
+                "../runo/icons/16/folder.png"), null);
+        fileMenu.addSeparator();
+        fileMenu.addItem("Exit", null).setEnabled(false);
+        fileMenu.addItem("Not for everybody", null).setVisible(false);
+        MenuItem otherMenu = menuBar.addItem("Other", null);
+        otherMenu.setDescription("This contains many items in sub menus");
+        MenuItem subMenu = otherMenu.addItem("Sub", null);
+        subMenu.setStyleName("fancy");
+        MenuItem option1 = subMenu.addItem("Option 1 - no <b>html</b>", null);
+        option1.setCheckable(true);
+        option1.setChecked(true);
+        subMenu.addItem("Option 2", null).setCheckable(true);
+        subMenu.addItem("Option 3", null).setCheckable(true);
+        menuBar.setMoreMenuItem(null);
+        MenuItem moreMenu = menuBar.getMoreMenuItem();
+        moreMenu.setIcon(new ThemeResource("icon.png"));
+        moreMenu.setText("foo");
+        testRead(design, menuBar);
+        testWrite(design, menuBar);
+    }
+
+    @Test
+    // #16328
+    public void testTicketSpec2() throws IOException {
+        String design = "<v-menu-bar>"
+                + "<menu><b>File</b>"
+                + "<menu><font style=\"color: red\">Save</font></menu>"
+                + "<menu icon=\"theme://../runo/icons/16/folder.png\">Open</menu>"
+                + "<menu separator='true' />"
+                + "<menu disabled='true'>Exit</menu>" //
+                + "</menu></v-menu-bar>";
+        MenuBar menuBar = new MenuBar();
+        menuBar.setHtmlContentAllowed(true);
+        MenuItem fileMenu = menuBar.addItem("<b>File</b>", null);
+        fileMenu.addItem("<font style='color: red'>Save</font>", null);
+        fileMenu.addItem("Open", new ThemeResource(
+                "../runo/icons/16/folder.png"), null);
+        fileMenu.addSeparator();
+        fileMenu.addItem("Exit", null).setEnabled(false);
+        testRead(design, menuBar);
+        testWrite(design, menuBar);
+    }
+}
\ No newline at end of file