diff options
-rw-r--r-- | WebContent/VAADIN/themes/base/tree/img/connector-item.png | bin | 0 -> 157 bytes | |||
-rw-r--r-- | WebContent/VAADIN/themes/base/tree/img/connector.png | bin | 0 -> 140 bytes | |||
-rw-r--r-- | WebContent/VAADIN/themes/base/tree/tree.css | 20 | ||||
-rw-r--r-- | WebContent/VAADIN/themes/liferay/tree/tree.css | 4 | ||||
-rw-r--r-- | WebContent/VAADIN/themes/reindeer/tree/tree.css | 4 | ||||
-rw-r--r-- | WebContent/VAADIN/themes/runo/tree/tree.css | 4 | ||||
-rw-r--r-- | src/com/vaadin/terminal/gwt/client/ui/VTree.java | 63 | ||||
-rw-r--r-- | src/com/vaadin/ui/themes/BaseTheme.java | 8 | ||||
-rw-r--r-- | tests/src/com/vaadin/tests/components/tree/TreeWithConnectors.java | 215 |
9 files changed, 283 insertions, 35 deletions
diff --git a/WebContent/VAADIN/themes/base/tree/img/connector-item.png b/WebContent/VAADIN/themes/base/tree/img/connector-item.png Binary files differnew file mode 100644 index 0000000000..a64c209cb1 --- /dev/null +++ b/WebContent/VAADIN/themes/base/tree/img/connector-item.png diff --git a/WebContent/VAADIN/themes/base/tree/img/connector.png b/WebContent/VAADIN/themes/base/tree/img/connector.png Binary files differnew file mode 100644 index 0000000000..c56b8f4244 --- /dev/null +++ b/WebContent/VAADIN/themes/base/tree/img/connector.png diff --git a/WebContent/VAADIN/themes/base/tree/tree.css b/WebContent/VAADIN/themes/base/tree/tree.css index 9e056dc4d6..37af1d11a8 100644 --- a/WebContent/VAADIN/themes/base/tree/tree.css +++ b/WebContent/VAADIN/themes/base/tree/tree.css @@ -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 { diff --git a/WebContent/VAADIN/themes/liferay/tree/tree.css b/WebContent/VAADIN/themes/liferay/tree/tree.css index 23fe10f52e..3c3c6abdfe 100644 --- a/WebContent/VAADIN/themes/liferay/tree/tree.css +++ b/WebContent/VAADIN/themes/liferay/tree/tree.css @@ -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; } diff --git a/WebContent/VAADIN/themes/reindeer/tree/tree.css b/WebContent/VAADIN/themes/reindeer/tree/tree.css index 3f77bd991d..4f31d43960 100644 --- a/WebContent/VAADIN/themes/reindeer/tree/tree.css +++ b/WebContent/VAADIN/themes/reindeer/tree/tree.css @@ -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 { diff --git a/WebContent/VAADIN/themes/runo/tree/tree.css b/WebContent/VAADIN/themes/runo/tree/tree.css index bec3b318dd..c2458bc0b8 100644 --- a/WebContent/VAADIN/themes/runo/tree/tree.css +++ b/WebContent/VAADIN/themes/runo/tree/tree.css @@ -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 { diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTree.java b/src/com/vaadin/terminal/gwt/client/ui/VTree.java index 11db29cbf6..ef08e81e10 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VTree.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VTree.java @@ -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)); diff --git a/src/com/vaadin/ui/themes/BaseTheme.java b/src/com/vaadin/ui/themes/BaseTheme.java index c8ba08d30d..babdb1f1d5 100644 --- a/src/com/vaadin/ui/themes/BaseTheme.java +++ b/src/com/vaadin/ui/themes/BaseTheme.java @@ -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 index 0000000000..996b5aafc9 --- /dev/null +++ b/tests/src/com/vaadin/tests/components/tree/TreeWithConnectors.java @@ -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 |