From ebcf64b94911597a123a599cd758e36b4abfe8bf Mon Sep 17 00:00:00 2001 From: Miki Date: Tue, 10 Feb 2015 15:04:27 +0200 Subject: [PATCH] Fix declarative support for MenuBar (#16328) Change-Id: Icd70a02aa8ffef9d1aca4b833ac23aeff5813771 --- server/src/com/vaadin/ui/MenuBar.java | 153 +++++++++++++++++- .../declarative/DesignAttributeHandler.java | 5 +- .../menubar/MenuBarDeclarativeTest.java | 139 ++++++++++++++++ 3 files changed, 293 insertions(+), 4 deletions(-) create mode 100644 server/tests/src/com/vaadin/tests/components/menubar/MenuBarDeclarativeTest.java diff --git a/server/src/com/vaadin/ui/MenuBar.java b/server/src/com/vaadin/ui/MenuBar.java index 6b6555c0a2..747ce42727 100644 --- a/server/src/com/vaadin/ui/MenuBar.java +++ b/server/src/com/vaadin/ui/MenuBar.java @@ -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; /** *

@@ -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 subMenus = new ArrayList(); + 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(); + } + + 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 getCustomAttributes() { + Collection result = super.getCustomAttributes(); + result.add(DESIGN_ATTR_PLAIN_TEXT); + result.add("html-content-allowed"); + return result; + } }// class MenuBar diff --git a/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java b/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java index 215afd5041..2b446bda0e 100644 --- a/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java +++ b/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java @@ -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 index 0000000000..e6dee44812 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/components/menubar/MenuBarDeclarativeTest.java @@ -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 { + + @Test + // #16328 + public void testReadWrite() throws IOException { + String design = "" + + "

Save" + + "Open" + + "Close" + + "Help" + + "About" + + "SubItem" + + "WTF?!" + ""; + 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 = " " + + "File" + + "Save" + + "Open" + + "" + + "Exit" + + "Not for everybody" + + "" + + "Other" + + "Sub" + + "Option 1 - no html" + + "Option 2" + + "Option 3" // + + "" // + + "" // + + "foo" + + ""; + // 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 html", 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 = "" + + "File" + + "Save" + + "Open" + + "" + + "Exit" // + + ""; + MenuBar menuBar = new MenuBar(); + menuBar.setHtmlContentAllowed(true); + 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); + testRead(design, menuBar); + testWrite(design, menuBar); + } +} \ No newline at end of file -- 2.39.5