]> source.dussan.org Git - vaadin-framework.git/commitdiff
Fixes #6745 "Connectors for Tree"
authorJouni Koivuviita <jouni.koivuviita@itmill.com>
Thu, 19 May 2011 08:31:58 +0000 (08:31 +0000)
committerJouni Koivuviita <jouni.koivuviita@itmill.com>
Thu, 19 May 2011 08:31:58 +0000 (08:31 +0000)
svn changeset:18907/svn branch:6.6

WebContent/VAADIN/themes/base/tree/img/connector-item.png [new file with mode: 0644]
WebContent/VAADIN/themes/base/tree/img/connector.png [new file with mode: 0644]
WebContent/VAADIN/themes/base/tree/tree.css
WebContent/VAADIN/themes/liferay/tree/tree.css
WebContent/VAADIN/themes/reindeer/tree/tree.css
WebContent/VAADIN/themes/runo/tree/tree.css
src/com/vaadin/terminal/gwt/client/ui/VTree.java
src/com/vaadin/ui/themes/BaseTheme.java
tests/src/com/vaadin/tests/components/tree/TreeWithConnectors.java [new file with mode: 0644]

diff --git a/WebContent/VAADIN/themes/base/tree/img/connector-item.png b/WebContent/VAADIN/themes/base/tree/img/connector-item.png
new file mode 100644 (file)
index 0000000..a64c209
Binary files /dev/null and b/WebContent/VAADIN/themes/base/tree/img/connector-item.png differ
diff --git a/WebContent/VAADIN/themes/base/tree/img/connector.png b/WebContent/VAADIN/themes/base/tree/img/connector.png
new file mode 100644 (file)
index 0000000..c56b8f4
Binary files /dev/null and b/WebContent/VAADIN/themes/base/tree/img/connector.png differ
index 9e056dc4d62e9f2483d5f0ba83ced180ddc0589b..37af1d11a8af044a9c8c28987e6c61a3844ae472 100644 (file)
@@ -4,17 +4,29 @@
        padding: 1px 0;
        outline: none;
 }
-.v-tree-node {
+.v-tree .v-tree-node {
        background: transparent url(../common/img/sprites.png) no-repeat 5px -37px;
        padding: 1px 0;
 }
 .v-tree-node-caption:focus {
        outline: none;
 }
-.v-tree-node-expanded {
-       background-position: -5px -10px;
+.v-tree div.v-tree-node-leaf {
+       background: transparent;
+}
+.v-tree-connectors div.v-tree-node-leaf {
+       background: transparent url(img/connector-item.png) no-repeat 8px 0;
+}
+.v-tree-connectors div.v-tree-node-leaf-last {
+       background: transparent url(img/connector-item.png) no-repeat 8px bottom;
+}
+.v-tree .v-tree-node-expanded {
+       background: transparent url(../common/img/sprites.png) no-repeat -5px -10px;
+}
+.v-tree-connectors .v-tree-node-children {
+       background: transparent url(img/connector.png) repeat-y 8px bottom;
 }
-div.v-tree-node-leaf {
+.v-tree-connectors .v-tree-node-children-last {
        background: transparent;
 }
 .v-tree-node-caption {
index 23fe10f52e40d7d3a0d6083de3e0c2128ce6b860..3c3c6abdfe511e5570d317131f16a192ea2413d8 100644 (file)
@@ -1,4 +1,4 @@
-.v-tree-node {
+.v-tree .v-tree-node {
        background: transparent url(arrows_sprites.png) no-repeat -42px 1px;
        -background: transparent url(arrows_sprites-ie6.png) no-repeat -42px 1px;
 }
@@ -13,7 +13,7 @@
        background-position: -14px -30px;
 }
 
-.v-tree-node-expanded {
+.v-tree .v-tree-node-expanded {
        background-position: -28px -14px;
 }
 
index 3f77bd991d658cf76fb68f830f6f6a0a3a581b66..4f31d439603ad541895ae5ba239f74ad155ee554 100644 (file)
@@ -1,4 +1,4 @@
-.v-tree-node {
+.v-tree .v-tree-node {
        background: transparent url(img/arrows.png) no-repeat 6px -10px;
 }
 .v-ie6 .v-tree-node {
@@ -8,7 +8,7 @@
 .v-ie6 div.v-tree-node-leaf {
        background: transparent;
 }
-.v-tree-node-expanded {
+.v-tree .v-tree-node-expanded {
        background-position: -7px 5px;
 }
 .v-tree-node-caption {
index bec3b318dd6269ddc79b3150e931fd2c3f1b4c61..c2458bc0b8bfaf0c4980a32077f9ae00202cd1cd 100644 (file)
@@ -1,7 +1,7 @@
-.v-tree-node {
+.v-tree .v-tree-node {
        background: transparent url(img/collapsed.png) no-repeat 2px 1px;
 }
-.v-tree-node-expanded {
+.v-tree .v-tree-node-expanded {
        background: transparent url(img/expanded.png) no-repeat 2px 1px;
 }
 .v-tree-node-caption {
index 11db29cbf6b32d47e286120402a399f32f645d48..ef08e81e10ee29ac20179787cd7cedf793326919 100644 (file)
@@ -263,6 +263,7 @@ public class VTree extends FocusElementPanel implements Paintable,
         }
 
         body.clear();
+        TreeNode childTree = null;
         for (final Iterator<?> i = uidl.getChildIterator(); i.hasNext();) {
             final UIDL childUidl = (UIDL) i.next();
             if ("actions".equals(childUidl.getTag())) {
@@ -272,7 +273,7 @@ public class VTree extends FocusElementPanel implements Paintable,
                 updateDropHandler(childUidl);
                 continue;
             }
-            final TreeNode childTree = new TreeNode();
+            childTree = new TreeNode();
             if (childTree.ie6compatnode != null) {
                 body.add(childTree);
             }
@@ -280,6 +281,12 @@ public class VTree extends FocusElementPanel implements Paintable,
             if (childTree.ie6compatnode == null) {
                 body.add(childTree);
             }
+            childTree.addStyleDependentName("root");
+            childTree.childNodeContainer.addStyleDependentName("root");
+        }
+        if (childTree != null) {
+            childTree.addStyleDependentName("last");
+            childTree.childNodeContainer.addStyleDependentName("last");
         }
         final String selectMode = uidl.getStringAttribute("selectmode");
         selectable = !"none".equals(selectMode);
@@ -410,7 +417,8 @@ public class VTree extends FocusElementPanel implements Paintable,
                                     VerticalDropLocation curDetail = (VerticalDropLocation) event
                                             .getDropDetails().get("detail");
                                     if (curDetail == detail
-                                            && newKey.equals(currentMouseOverKey)) {
+                                            && newKey
+                                                    .equals(currentMouseOverKey)) {
                                         keyToNode.get(newKey).emphasis(detail);
                                     }
                                     /*
@@ -503,8 +511,8 @@ public class VTree extends FocusElementPanel implements Paintable,
      * Sends the selection to the server
      */
     private void sendSelectionToServer() {
-        client.updateVariable(paintableId, "selected",
-                selectedIds.toArray(new String[selectedIds.size()]), immediate);
+        client.updateVariable(paintableId, "selected", selectedIds
+                .toArray(new String[selectedIds.size()]), immediate);
         selectionHasChanged = false;
     }
 
@@ -752,9 +760,8 @@ public class VTree extends FocusElementPanel implements Paintable,
                     if (selectable) {
                         // caption click = selection change && possible click
                         // event
-                        if (handleClickSelection(
-                                event.getCtrlKey() || event.getMetaKey(),
-                                event.getShiftKey())) {
+                        if (handleClickSelection(event.getCtrlKey()
+                                || event.getMetaKey(), event.getShiftKey())) {
                             event.preventDefault();
                         }
                     } else {
@@ -775,10 +782,10 @@ public class VTree extends FocusElementPanel implements Paintable,
                                 && (type == Event.ONTOUCHSTART || event
                                         .getButton() == NativeEvent.BUTTON_LEFT)) {
                             mouseDownEvent = event; // save event for possible
-                                                    // dd operation
+                            // dd operation
                             if (type == Event.ONMOUSEDOWN) {
                                 event.preventDefault(); // prevent text
-                                                        // selection
+                                // selection
                             } else {
                                 /*
                                  * FIXME We prevent touch start event to be used
@@ -850,9 +857,11 @@ public class VTree extends FocusElementPanel implements Paintable,
                             || !selectable
                             || (!isNullSelectionAllowed && isSelected() && selectedIds
                                     .size() == 1);
-                    client.updateVariable(paintableId, "clickedKey", key, false);
-                    client.updateVariable(paintableId, "clickEvent",
-                            details.toString(), imm);
+                    client
+                            .updateVariable(paintableId, "clickedKey", key,
+                                    false);
+                    client.updateVariable(paintableId, "clickEvent", details
+                            .toString(), imm);
                 }
             };
             if (treeHasFocus) {
@@ -876,6 +885,7 @@ public class VTree extends FocusElementPanel implements Paintable,
         }
 
         protected void constructDom() {
+            addStyleName(CLASSNAME);
             // workaround for a very weird IE6 issue #1245
             if (BrowserInfo.get().isIE6()) {
                 ie6compatnode = DOM.createDiv();
@@ -928,7 +938,6 @@ public class VTree extends FocusElementPanel implements Paintable,
             } else {
                 addStyleName(CLASSNAME + "-leaf");
             }
-            addStyleName(CLASSNAME);
             if (uidl.hasAttribute("style")) {
                 addStyleName(CLASSNAME + "-" + uidl.getStringAttribute("style"));
                 Widget.setStyleName(nodeCaptionDiv, CLASSNAME + "-caption-"
@@ -952,14 +961,14 @@ public class VTree extends FocusElementPanel implements Paintable,
                 if (icon == null) {
                     onloadHandled = false;
                     icon = new Icon(client);
-                    DOM.insertBefore(DOM.getFirstChild(nodeCaptionDiv),
-                            icon.getElement(), nodeCaptionSpan);
+                    DOM.insertBefore(DOM.getFirstChild(nodeCaptionDiv), icon
+                            .getElement(), nodeCaptionSpan);
                 }
                 icon.setUri(uidl.getStringAttribute("icon"));
             } else {
                 if (icon != null) {
-                    DOM.removeChild(DOM.getFirstChild(nodeCaptionDiv),
-                            icon.getElement());
+                    DOM.removeChild(DOM.getFirstChild(nodeCaptionDiv), icon
+                            .getElement());
                     icon = null;
                 }
             }
@@ -1031,6 +1040,11 @@ public class VTree extends FocusElementPanel implements Paintable,
                 if (ie6compatnode == null) {
                     childNodeContainer.add(childTree);
                 }
+                if (!i.hasNext()) {
+                    childTree.addStyleDependentName(childTree.isLeaf()
+                            ? "leaf-last" : "last");
+                    childTree.childNodeContainer.addStyleDependentName("last");
+                }
             }
             childrenLoaded = true;
         }
@@ -1692,9 +1706,8 @@ public class VTree extends FocusElementPanel implements Paintable,
             // keypress event
             keyCode = CHARCODE_SPACE;
         }
-        if (handleKeyNavigation(keyCode,
-                event.isControlKeyDown() || event.isMetaKeyDown(),
-                event.isShiftKeyDown())) {
+        if (handleKeyNavigation(keyCode, event.isControlKeyDown()
+                || event.isMetaKeyDown(), event.isShiftKeyDown())) {
             event.preventDefault();
             event.stopPropagation();
         }
@@ -1708,9 +1721,9 @@ public class VTree extends FocusElementPanel implements Paintable,
      * .event.dom.client.KeyDownEvent)
      */
     public void onKeyDown(KeyDownEvent event) {
-        if (handleKeyNavigation(event.getNativeEvent().getKeyCode(),
-                event.isControlKeyDown() || event.isMetaKeyDown(),
-                event.isShiftKeyDown())) {
+        if (handleKeyNavigation(event.getNativeEvent().getKeyCode(), event
+                .isControlKeyDown()
+                || event.isMetaKeyDown(), event.isShiftKeyDown())) {
             event.preventDefault();
             event.stopPropagation();
         }
@@ -2168,8 +2181,8 @@ public class VTree extends FocusElementPanel implements Paintable,
 
         ArrayList<Integer> positions = new ArrayList<Integer>();
         while (treeNode.getParentNode() != null) {
-            positions.add(0,
-                    treeNode.getParentNode().getChildren().indexOf(treeNode));
+            positions.add(0, treeNode.getParentNode().getChildren().indexOf(
+                    treeNode));
             treeNode = treeNode.getParentNode();
         }
         positions.add(0, getRootNodes().indexOf(treeNode));
index c8ba08d30dd4b16f216426b0f52caf1cec83d164..babdb1f1d5a76dd186456a28b6fbce3b452d8efc 100644 (file)
@@ -44,4 +44,12 @@ public class BaseTheme {
     @Deprecated
     public static final String PANEL_LIGHT = "light";
 
+    /**
+     * Adds connector lines between the tree nodes to better visualize the
+     * hierarchy.
+     * 
+     * @since 6.6.1
+     */
+    public static final String TREE_CONNECTORS = "connectors";
+
 }
\ No newline at end of file
diff --git a/tests/src/com/vaadin/tests/components/tree/TreeWithConnectors.java b/tests/src/com/vaadin/tests/components/tree/TreeWithConnectors.java
new file mode 100644 (file)
index 0000000..996b5aa
--- /dev/null
@@ -0,0 +1,215 @@
+package com.vaadin.tests.components.tree;
+
+import java.util.Collection;
+import java.util.Date;
+
+import com.vaadin.data.Item;
+import com.vaadin.data.util.HierarchicalContainer;
+import com.vaadin.event.DataBoundTransferable;
+import com.vaadin.event.Transferable;
+import com.vaadin.event.dd.DragAndDropEvent;
+import com.vaadin.event.dd.DropHandler;
+import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
+import com.vaadin.terminal.ThemeResource;
+import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation;
+import com.vaadin.tests.components.TestBase;
+import com.vaadin.ui.Panel;
+import com.vaadin.ui.Tree;
+import com.vaadin.ui.AbstractSelect.AcceptItem;
+import com.vaadin.ui.Tree.TreeDragMode;
+import com.vaadin.ui.Tree.TreeTargetDetails;
+import com.vaadin.ui.themes.Reindeer;
+
+public class TreeWithConnectors extends TestBase {
+
+    @Override
+    protected void setup() {
+        ThemeResource notCachedFileIcon = new ThemeResource(
+                "../runo/icons/16/document.png?" + new Date().getTime());
+        ThemeResource notCachedFolderIconHuge = new ThemeResource(
+                "../runo/icons/64/folder.png?" + new Date().getTime());
+        ThemeResource notCachedFolderIconLarge = new ThemeResource(
+                "../runo/icons/32/folder.png?" + new Date().getTime());
+        ThemeResource notCachedFolderIconLargeOther = new ThemeResource(
+                "../runo/icons/32/ok.png?" + new Date().getTime());
+
+        Tree t = new Tree();
+        t.setImmediate(true);
+        t.addStyleName(Reindeer.TREE_CONNECTORS);
+
+        for (int i = 1; i <= 5; i++) {
+            String item = "Root " + i;
+            t.addItem(item);
+            if (i == 1) {
+                t.setItemIcon(item, notCachedFileIcon);
+                addChildren(t, item, true);
+            } else if (i == 2) {
+                t.setItemIcon(item, notCachedFolderIconHuge);
+                addChildren(t, item, false);
+            } else if (i == 3) {
+                t.setItemIcon(item, notCachedFolderIconLarge);
+                addChildren(t, item, true);
+            } else if (i == 4) {
+                t.setItemIcon(item, notCachedFolderIconLargeOther);
+                addChildren(t, item, false);
+            } else if (i == 5) {
+                addChildren(t, item, true);
+            }
+        }
+
+        Panel p = new Panel();
+        p.addComponent(t);
+        p.setSizeFull();
+        getLayout().setSizeFull();
+
+        addComponent(p);
+
+        addDnD(t);
+    }
+
+    private void addDnD(final Tree t) {
+        t.setDragMode(TreeDragMode.NODE);
+        DropHandler itemSorter = new DropHandler() {
+
+            @SuppressWarnings("unused")
+            private void populateSubTree(HierarchicalContainer idx,
+                    HierarchicalContainer subtree, Object itemId) {
+                Collection<?> children = subtree.getChildren(itemId);
+                if (children != null) {
+
+                    for (Object childId : children) {
+                        Item addItem = idx.addItem(childId);
+                        if (addItem != null) {
+                            // did not exist, populate properties
+                            Item item = subtree.getItem(itemId);
+                            Collection<?> itemPropertyIds = item
+                                    .getItemPropertyIds();
+                            for (Object propId : itemPropertyIds) {
+                                addItem.getItemProperty(propId)
+                                        .setValue(
+                                                item.getItemProperty(propId)
+                                                        .getValue());
+                            }
+                        }
+                        idx.setParent(childId, itemId);
+                        populateSubTree(idx, subtree, childId);
+                    }
+                }
+
+            }
+
+            @SuppressWarnings("unused")
+            private HierarchicalContainer getSubTree(HierarchicalContainer idx,
+                    Object itemId) {
+                HierarchicalContainer hierarchicalContainer = new HierarchicalContainer();
+                Collection<?> containerPropertyIds = idx
+                        .getContainerPropertyIds();
+                for (Object object : containerPropertyIds) {
+                    hierarchicalContainer.addContainerProperty(object, idx
+                            .getType(object), null);
+                }
+                hierarchicalContainer.addItem(itemId);
+                copyChildren(idx, hierarchicalContainer, itemId);
+                return hierarchicalContainer;
+            }
+
+            private void copyChildren(HierarchicalContainer source,
+                    HierarchicalContainer target, Object itemId) {
+                Collection<?> children = source.getChildren(itemId);
+                if (children != null) {
+                    for (Object childId : children) {
+                        Item item = source.getItem(childId);
+                        Item addedItem = target.addItem(childId);
+                        target.setParent(childId, itemId);
+                        Collection<?> itemPropertyIds = item
+                                .getItemPropertyIds();
+                        for (Object propertyId : itemPropertyIds) {
+                            addedItem.getItemProperty(propertyId)
+                                    .setValue(
+                                            item.getItemProperty(propertyId)
+                                                    .getValue());
+                        }
+                        copyChildren(source, target, childId);
+                    }
+                }
+
+            }
+
+            public void drop(DragAndDropEvent event) {
+                TreeTargetDetails details = (TreeTargetDetails) event
+                        .getTargetDetails();
+                // TODO set properties, so same sorter could be used in Table
+                Transferable transferable = event.getTransferable();
+                if (transferable instanceof DataBoundTransferable) {
+                    DataBoundTransferable transferrable2 = (DataBoundTransferable) transferable;
+
+                    Object itemId = transferrable2.getItemId();
+
+                    Object itemIdOver = details.getItemIdOver();
+
+                    // TODO could use the "folder" node id to make the drop
+                    // logic simpler
+                    Object itemIdInto = details.getItemIdInto();
+                    VerticalDropLocation dropLocation = details
+                            .getDropLocation();
+
+                    Object itemIdAfter = details.getItemIdAfter();
+
+                    if (itemIdOver.equals(itemIdInto)) { // directly on a node
+                        t.setParent(itemId, itemIdOver);
+                        return;
+                    }
+
+                    ((HierarchicalContainer) t.getContainerDataSource())
+                            .setParent(itemId, itemIdInto);
+
+                    if (dropLocation == null) {
+                        System.err.println("No detail of drop place available");
+                    }
+                    ((HierarchicalContainer) t.getContainerDataSource())
+                            .moveAfterSibling(itemId, itemIdAfter);
+                }
+
+                return;
+            }
+
+            public AcceptCriterion getAcceptCriterion() {
+                // TODO should actually check that source is same as target
+                return AcceptItem.ALL;
+            }
+
+        };
+
+        t.setDropHandler(itemSorter);
+    }
+
+    protected void addChildren(Tree t, String parent, boolean recurse) {
+        for (int i = 1; i <= Math.max(3, 3 + Math.random() * 2); i++) {
+            String item = parent + ", child " + i;
+            t.addItem(item);
+            t.setChildrenAllowed(parent, true);
+            t.setParent(item, parent);
+            if (recurse) {
+                if (i % 2 == 0) {
+                    addChildren(t, item, false);
+                    t.expandItem(parent);
+                } else {
+                    t.setChildrenAllowed(item, false);
+                }
+            } else {
+                t.setChildrenAllowed(item, false);
+            }
+        }
+    }
+
+    @Override
+    protected String getDescription() {
+        return "A tree using the 'connectors' stylename should have Windows-like dotted connector lines joining the different hierarchy levels.";
+    }
+
+    @Override
+    protected Integer getTicketNumber() {
+        return 6745;
+    }
+
+}
\ No newline at end of file