diff options
7 files changed, 149 insertions, 21 deletions
diff --git a/client/src/main/java/com/vaadin/client/connectors/grid/TreeRendererConnector.java b/client/src/main/java/com/vaadin/client/connectors/grid/TreeRendererConnector.java index df0da24e99..fe27b76bdc 100644 --- a/client/src/main/java/com/vaadin/client/connectors/grid/TreeRendererConnector.java +++ b/client/src/main/java/com/vaadin/client/connectors/grid/TreeRendererConnector.java @@ -17,6 +17,7 @@ package com.vaadin.client.connectors.grid; import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.user.client.Element; +import com.vaadin.client.annotations.OnStateChange; import com.vaadin.client.renderers.HtmlRenderer; import com.vaadin.client.renderers.Renderer; import com.vaadin.client.widget.grid.RendererCellReference; @@ -27,7 +28,7 @@ import com.vaadin.ui.Tree.TreeRenderer; import elemental.json.JsonObject; /** - * Connector for TreeRenderer + * Connector for TreeRenderer. * * @author Vaadin Ltd * @since 8.1 @@ -42,9 +43,8 @@ public class TreeRendererConnector @Override public void render(RendererCellReference cell, String htmlString) { - String content = "<span class=\"v-captiontext\">" + - SafeHtmlUtils.htmlEscape(htmlString) - + "</span>"; + String content = "<span class=\"v-captiontext\">" + + getContentString(htmlString) + "</span>"; JsonObject row = getParent().getParent().getDataSource() .getRow(cell.getRowIndex()); @@ -56,9 +56,30 @@ public class TreeRendererConnector } super.render(cell, content); } + + private String getContentString(String htmlString) { + switch (getState().mode) { + case HTML: + return htmlString; + case PREFORMATTED: + return "<pre>" + SafeHtmlUtils.htmlEscape(htmlString) + + "</pre>"; + default: + return SafeHtmlUtils.htmlEscape(htmlString); + } + } }; } + @OnStateChange("mode") + void updateContentMode() { + // Redraw content + getParent().getParent().getWidget().requestRefreshBody(); + + // Some pre-formatted content might change size of content. + getParent().getParent().getWidget().recalculateColumnWidths(); + } + @Override public ColumnConnector getParent() { return (ColumnConnector) super.getParent(); diff --git a/client/src/main/java/com/vaadin/client/widgets/Grid.java b/client/src/main/java/com/vaadin/client/widgets/Grid.java index aa0261d6d9..45937e0c07 100755 --- a/client/src/main/java/com/vaadin/client/widgets/Grid.java +++ b/client/src/main/java/com/vaadin/client/widgets/Grid.java @@ -6381,8 +6381,10 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, /** * Request delayed refresh of all body rows. + * + * @since 8.1 */ - private void requestRefreshBody() { + public void requestRefreshBody() { if (!refreshBodyRequested) { refreshBodyRequested = true; Scheduler.get().scheduleFinally(() -> { diff --git a/server/src/main/java/com/vaadin/ui/Tree.java b/server/src/main/java/com/vaadin/ui/Tree.java index aaff872c95..35bc680213 100644 --- a/server/src/main/java/com/vaadin/ui/Tree.java +++ b/server/src/main/java/com/vaadin/ui/Tree.java @@ -218,13 +218,14 @@ public class Tree<T> extends Composite private TreeGrid<T> treeGrid = new TreeGrid<>(); private ItemCaptionGenerator<T> captionGenerator = String::valueOf; private IconGenerator<T> iconProvider = t -> null; + private final TreeRenderer renderer; /** * Constructs a new Tree Component. */ public Tree() { setCompositionRoot(treeGrid); - TreeRenderer renderer = new TreeRenderer(); + renderer = new TreeRenderer(); treeGrid.getDataCommunicator().addDataGenerator(renderer); treeGrid.addColumn(i -> captionGenerator.apply(i), renderer) .setId("column"); @@ -762,4 +763,25 @@ public class Tree<T> extends Composite public void setComponentError(ErrorMessage componentError) { treeGrid.setComponentError(componentError); } + + /** + * Sets the height of a row. If -1 (default), the row height is calculated + * based on the theme for an empty row before the Tree is displayed. + * + * @param rowHeight + * The height of a row in pixels or -1 for automatic calculation + */ + public void setRowHeight(double rowHeight) { + treeGrid.setRowHeight(rowHeight); + } + + /** + * Sets the content mode of the item caption. + * + * @param contentMode + * the content mode + */ + public void setContentMode(ContentMode contentMode) { + renderer.getState().mode = contentMode; + } } diff --git a/shared/src/main/java/com/vaadin/shared/ui/tree/TreeRendererState.java b/shared/src/main/java/com/vaadin/shared/ui/tree/TreeRendererState.java index 794f37895f..824d30ecc5 100644 --- a/shared/src/main/java/com/vaadin/shared/ui/tree/TreeRendererState.java +++ b/shared/src/main/java/com/vaadin/shared/ui/tree/TreeRendererState.java @@ -15,6 +15,7 @@ */ package com.vaadin.shared.ui.tree; +import com.vaadin.shared.ui.ContentMode; import com.vaadin.shared.ui.grid.renderers.AbstractRendererState; /** @@ -25,4 +26,5 @@ import com.vaadin.shared.ui.grid.renderers.AbstractRendererState; */ public class TreeRendererState extends AbstractRendererState { + public ContentMode mode = ContentMode.TEXT; } diff --git a/themes/src/main/themes/VAADIN/themes/valo/components/_treegrid.scss b/themes/src/main/themes/VAADIN/themes/valo/components/_treegrid.scss index 0fd812bdf8..ca2078e410 100644 --- a/themes/src/main/themes/VAADIN/themes/valo/components/_treegrid.scss +++ b/themes/src/main/themes/VAADIN/themes/valo/components/_treegrid.scss @@ -27,6 +27,9 @@ $v-treegrid-class-depth: depth !default; .#{$primary-stylename}-expander { + display: inline-block; + vertical-align: top; + &::before { display: inline-block; width: $v-treegrid-expander-width; @@ -62,6 +65,7 @@ $v-treegrid-class-depth: depth !default; // Expander and cell content in same line .#{$primary-stylename}-cell-content { display: inline-block; + vertical-align: middle; } .#{$primary-stylename}-row-focused { diff --git a/uitest/src/main/java/com/vaadin/tests/components/tree/TreeBasicFeatures.java b/uitest/src/main/java/com/vaadin/tests/components/tree/TreeBasicFeatures.java index 9f69f3d760..edcb76e1ca 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/tree/TreeBasicFeatures.java +++ b/uitest/src/main/java/com/vaadin/tests/components/tree/TreeBasicFeatures.java @@ -1,7 +1,11 @@ package com.vaadin.tests.components.tree; import java.util.Arrays; +import java.util.Enumeration; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; import com.vaadin.annotations.Theme; import com.vaadin.annotations.Widgetset; @@ -12,21 +16,24 @@ import com.vaadin.server.ClassResource; import com.vaadin.server.ThemeResource; import com.vaadin.server.VaadinRequest; import com.vaadin.shared.Registration; +import com.vaadin.shared.ui.ContentMode; import com.vaadin.tests.components.AbstractTestUIWithLog; import com.vaadin.tests.data.bean.HierarchicalTestBean; import com.vaadin.ui.Component; +import com.vaadin.ui.Grid.SelectionMode; import com.vaadin.ui.IconGenerator; import com.vaadin.ui.MenuBar; import com.vaadin.ui.MenuBar.Command; import com.vaadin.ui.MenuBar.MenuItem; import com.vaadin.ui.Tree; import com.vaadin.ui.VerticalLayout; -import com.vaadin.ui.Grid.SelectionMode; @Theme("tests-valo-disabled-animations") @Widgetset("com.vaadin.DefaultWidgetSet") public class TreeBasicFeatures extends AbstractTestUIWithLog { + public static final double[] ROW_HEIGHTS = new double[] { 35.5d, 72.78d }; + private Tree<HierarchicalTestBean> tree; private TreeDataProvider<HierarchicalTestBean> inMemoryDataProvider; private IconGenerator<HierarchicalTestBean> iconGenerator = i -> { @@ -69,7 +76,9 @@ public class TreeBasicFeatures extends AbstractTestUIWithLog { MenuItem componentMenu = menu.addItem("Component", null); createIconMenu(componentMenu.addItem("Icons", null)); createCaptionMenu(componentMenu.addItem("Captions", null)); + createContentModeMenu(componentMenu.addItem("ContentMode", null)); createSelectionModeMenu(componentMenu.addItem("Selection Mode", null)); + createRowHeightMenu(componentMenu.addItem("Row Height", null)); componentMenu.addItem("Item Click Listener", new Command() { private Registration registration; @@ -110,6 +119,12 @@ public class TreeBasicFeatures extends AbstractTestUIWithLog { return menu; } + private void createRowHeightMenu(MenuItem rowHeightMenu) { + Stream.concat(Stream.of(-1d), Arrays.stream(ROW_HEIGHTS).boxed()) + .forEach(height -> rowHeightMenu.addItem(String.valueOf(height), + item -> tree.setRowHeight(height))); + } + private void createSelectionModeMenu(MenuItem modeMenu) { for (SelectionMode mode : SelectionMode.values()) { modeMenu.addItem(mode.name(), item -> tree.setSelectionMode(mode)); @@ -117,13 +132,26 @@ public class TreeBasicFeatures extends AbstractTestUIWithLog { } private void createCaptionMenu(MenuItem captionMenu) { - captionMenu.addItem("String.valueOf", - menu -> tree.setItemCaptionGenerator(String::valueOf)); - captionMenu - .addItem("Custom caption", - menu -> tree.setItemCaptionGenerator(i -> "Id: " - + i.getId() + ", Depth: " + i.getDepth() - + ", Index: " + i.getIndex())); + captionMenu.addItem("String.valueOf", menu -> { + tree.setItemCaptionGenerator(String::valueOf); + tree.setContentMode(ContentMode.TEXT); + }); + captionMenu.addItem("Custom caption", menu -> { + tree.setItemCaptionGenerator(i -> "Id: " + i.getId() + "\nDepth: " + + i.getDepth() + ", Index: " + i.getIndex()); + tree.setContentMode(ContentMode.PREFORMATTED); + }); + captionMenu.addItem("HTML caption", menu -> { + tree.setItemCaptionGenerator( + i -> "Id: " + i.getId() + "<br/>Depth: " + i.getDepth() + + "<br/>Index: " + i.getIndex()); + tree.setContentMode(ContentMode.HTML); + }); + } + + private void createContentModeMenu(MenuItem contentModeMenu) { + Arrays.stream(ContentMode.values()).forEach(mode -> contentModeMenu + .addItem(mode.toString(), item -> tree.setContentMode(mode))); } private void createIconMenu(MenuItem iconMenu) { diff --git a/uitest/src/test/java/com/vaadin/tests/components/tree/TreeBasicFeaturesTest.java b/uitest/src/test/java/com/vaadin/tests/components/tree/TreeBasicFeaturesTest.java index d65523d6ec..4137e11098 100644 --- a/uitest/src/test/java/com/vaadin/tests/components/tree/TreeBasicFeaturesTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/tree/TreeBasicFeaturesTest.java @@ -1,6 +1,7 @@ package com.vaadin.tests.components.tree; import java.io.IOException; +import java.util.Arrays; import java.util.function.Predicate; import org.junit.Assert; @@ -92,7 +93,8 @@ public class TreeBasicFeaturesTest extends MultiBrowserTest { for (int j = 0; j < 3; ++j) { item = tree.getItem(n++); - Assert.assertEquals((shouldHaveIcon ? "\ue92d" : "") + "1 | " + j, + Assert.assertEquals( + (shouldHaveIcon ? "\ue92d" : "") + "1 | " + j, item.getText()); Assert.assertEquals("Unexpected icon state", shouldHaveIcon, @@ -111,27 +113,62 @@ public class TreeBasicFeaturesTest extends MultiBrowserTest { @Test public void tree_custom_caption() { + // Set row height big enough to show whole content. + selectMenuPath("Component", "Row Height", + String.valueOf(TreeBasicFeatures.ROW_HEIGHTS[1])); + selectMenuPath("Component", "Captions", "Custom caption"); TreeElement tree = $(TreeElement.class).first(); - Assert.assertEquals("Id: /0/0, Depth: 0, Index: 0", + Assert.assertEquals("Id: /0/0\nDepth: 0, Index: 0", tree.getItem(0).getText()); - Assert.assertEquals("Id: /0/1, Depth: 0, Index: 1", + Assert.assertEquals("Id: /0/1\nDepth: 0, Index: 1", tree.getItem(1).getText()); tree.expand(0); - Assert.assertEquals("Id: /0/0/1/0, Depth: 1, Index: 0", + Assert.assertEquals("Id: /0/0/1/0\nDepth: 1, Index: 0", tree.getItem(1).getText()); - Assert.assertEquals("Id: /0/0/1/1, Depth: 1, Index: 1", + Assert.assertEquals("Id: /0/0/1/1\nDepth: 1, Index: 1", tree.getItem(2).getText()); tree.expand(1); - Assert.assertEquals("Id: /0/0/1/0/2/0, Depth: 2, Index: 0", + Assert.assertEquals("Id: /0/0/1/0/2/0\nDepth: 2, Index: 0", tree.getItem(2).getText()); - Assert.assertEquals("Id: /0/0/1/0/2/1, Depth: 2, Index: 1", + Assert.assertEquals("Id: /0/0/1/0/2/1\nDepth: 2, Index: 1", tree.getItem(3).getText()); assertNoErrorNotifications(); } @Test + public void tree_html_caption_and_expander_position() { + // Set row height big enough to show whole content. + selectMenuPath("Component", "Row Height", + String.valueOf(TreeBasicFeatures.ROW_HEIGHTS[1])); + + selectMenuPath("Component", "Captions", "HTML caption"); + TreeElement tree = $(TreeElement.class).first(); + Assert.assertEquals("Id: /0/0\nDepth: 0\nIndex: 0", + tree.getItem(0).getText()); + + Assert.assertEquals("Expander element not aligned to top", + tree.getExpandElement(0).getLocation().getY(), + tree.getItem(0).getLocation().getY()); + + assertNoErrorNotifications(); + } + + @Test + public void tree_html_caption_text_mode() { + // Set row height big enough to show whole content. + selectMenuPath("Component", "Captions", "HTML caption"); + selectMenuPath("Component", "ContentMode", "TEXT"); + + TreeElement tree = $(TreeElement.class).first(); + Assert.assertEquals("Id: /0/0<br/>Depth: 0<br/>Index: 0", + tree.getItem(0).getText()); + + assertNoErrorNotifications(); + } + + @Test public void tree_item_click() { selectMenuPath("Component", "Item Click Listener"); $(TreeElement.class).first().getItem(1).click(); @@ -203,4 +240,16 @@ public class TreeBasicFeaturesTest extends MultiBrowserTest { Assert.assertFalse("First row was not deselected", wrap.getRow(0).isSelected()); } + + @Test + public void tree_row_heigth() { + TreeElement tree = $(TreeElement.class).first(); + TreeGridElement wrap = tree.wrap(TreeGridElement.class); + Arrays.stream(TreeBasicFeatures.ROW_HEIGHTS).boxed() + .map(String::valueOf).forEach(height -> { + selectMenuPath("Component", "Row Height", height); + Assert.assertTrue(wrap.getCell(0, 0).getAttribute("style") + .contains("height: " + height + "px;")); + }); + } } |