summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--WebContent/VAADIN/themes/base/tree/img/connector-item.pngbin0 -> 157 bytes
-rw-r--r--WebContent/VAADIN/themes/base/tree/img/connector.pngbin0 -> 140 bytes
-rw-r--r--WebContent/VAADIN/themes/base/tree/tree.css20
-rw-r--r--WebContent/VAADIN/themes/liferay/tree/tree.css4
-rw-r--r--WebContent/VAADIN/themes/reindeer/tree/tree.css4
-rw-r--r--WebContent/VAADIN/themes/runo/tree/tree.css4
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VTree.java63
-rw-r--r--src/com/vaadin/ui/themes/BaseTheme.java8
-rw-r--r--tests/src/com/vaadin/tests/components/tree/TreeWithConnectors.java215
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
new file mode 100644
index 0000000000..a64c209cb1
--- /dev/null
+++ b/WebContent/VAADIN/themes/base/tree/img/connector-item.png
Binary files 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
index 0000000000..c56b8f4244
--- /dev/null
+++ b/WebContent/VAADIN/themes/base/tree/img/connector.png
Binary files differ
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