]> source.dussan.org Git - vaadin-framework.git/commitdiff
Fix Grid details height calculation issue (#10343)
authorAdam Wagner <wbadam@users.noreply.github.com>
Tue, 6 Feb 2018 09:10:33 +0000 (11:10 +0200)
committerIlia Motornyi <elmot@vaadin.com>
Tue, 6 Feb 2018 09:10:33 +0000 (11:10 +0200)
client/src/main/java/com/vaadin/client/WidgetUtil.java
client/src/main/java/com/vaadin/client/connectors/grid/DetailsManagerConnector.java
client/src/main/java/com/vaadin/client/widget/escalator/events/SpacerVisibilityChangedEvent.java [new file with mode: 0644]
client/src/main/java/com/vaadin/client/widget/escalator/events/SpacerVisibilityChangedHandler.java [new file with mode: 0644]
client/src/main/java/com/vaadin/client/widgets/Escalator.java
client/src/main/java/com/vaadin/client/widgets/Grid.java
uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicDetailsTest.java

index cf4addc37edb1f70c8474e6222031a8041912458..e91ab9172c96239c5a8f426e07f54fd5a8700130 100644 (file)
@@ -1866,6 +1866,26 @@ public class WidgetUtil {
         return typeof obj === 'string' || obj instanceof String;
     }-*/;
 
+    /**
+     * Returns whether the given element is displayed.
+     * <p>
+     * This method returns false if either the given element or any of its
+     * ancestors has the style {@code display: none} applied.
+     *
+     * @param element
+     *         the element to test for visibility
+     * @return {@code true} if the element is displayed, {@code false} otherwise
+     * @since
+     */
+    public static native boolean isDisplayed(Element element)
+    /*-{
+        // This measurement is borrowed from JQuery and measures the visible
+        // size of the element. The measurement should return false when either
+        // the element or any of its ancestors has "display: none" style.
+        return !!(element.offsetWidth || element.offsetHeight
+            || element.getClientRects().length);
+    }-*/;
+
     /**
      * Utility methods for displaying error message on components.
      *
index 177056c0e321f360f7ca677a3e6c0a08d8756885..b9792ad79d377435fe61d79eda892a19662c6b77 100644 (file)
@@ -21,6 +21,7 @@ import java.util.Map;
 import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
 import com.google.gwt.dom.client.Element;
+import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.gwt.user.client.ui.Widget;
 import com.vaadin.client.ComponentConnector;
 import com.vaadin.client.ConnectorMap;
@@ -56,6 +57,11 @@ public class DetailsManagerConnector extends AbstractExtensionConnector {
     /* Registration for data change handler. */
     private Registration dataChangeRegistration;
 
+    /**
+     * Handle for the spacer visibility change handler.
+     */
+    private HandlerRegistration spacerVisibilityChangeRegistration;
+
     private final Map<Element, ScheduledCommand> elementToResizeCommand = new HashMap<Element, Scheduler.ScheduledCommand>();
     private final ElementResizeListener detailsRowResizeListener = event -> {
         if (elementToResizeCommand.containsKey(event.getElement())) {
@@ -140,9 +146,12 @@ public class DetailsManagerConnector extends AbstractExtensionConnector {
                 if (spacerCellBorderHeights != null
                         && !getLayoutManager().isLayoutRunning()
                         && getDetailsComponentConnectorId(rowIndex) != null) {
-                    double height = getLayoutManager().getOuterHeightDouble(
-                            element) + spacerCellBorderHeights;
-                    getWidget().setDetailsHeight(rowIndex, height);
+                    // Measure and set details height if element is visible
+                    if (WidgetUtil.isDisplayed(element)) {
+                        double height = getLayoutManager().getOuterHeightDouble(
+                                element) + spacerCellBorderHeights;
+                        getWidget().setDetailsHeight(rowIndex, height);
+                    }
                 }
             };
         }
@@ -180,6 +189,19 @@ public class DetailsManagerConnector extends AbstractExtensionConnector {
         getWidget().setDetailsGenerator(new CustomDetailsGenerator());
         dataChangeRegistration = getWidget().getDataSource()
                 .addDataChangeHandler(new DetailsChangeHandler());
+
+        // When details element is shown, remeasure it in the layout phase
+        spacerVisibilityChangeRegistration = getParent().getWidget()
+                .addSpacerVisibilityChangedHandler(event -> {
+                    if (event.isSpacerVisible()) {
+                        String id = indexToDetailConnectorId
+                                .get(event.getRowIndex());
+                        ComponentConnector connector = (ComponentConnector) ConnectorMap
+                                .get(getConnection()).getConnector(id);
+                        getLayoutManager()
+                                .setNeedsMeasureRecursively(connector);
+                    }
+                });
     }
 
     private void detachIfNeeded(int rowIndex, String id) {
@@ -215,6 +237,8 @@ public class DetailsManagerConnector extends AbstractExtensionConnector {
         dataChangeRegistration.remove();
         dataChangeRegistration = null;
 
+        spacerVisibilityChangeRegistration.removeHandler();
+
         indexToDetailConnectorId.clear();
     }
 
diff --git a/client/src/main/java/com/vaadin/client/widget/escalator/events/SpacerVisibilityChangedEvent.java b/client/src/main/java/com/vaadin/client/widget/escalator/events/SpacerVisibilityChangedEvent.java
new file mode 100644 (file)
index 0000000..116d9a3
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2000-2016 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.client.widget.escalator.events;
+
+import com.google.gwt.event.shared.GwtEvent;
+
+/**
+ * Event fired when a spacer element is hidden or shown in Escalator.
+ *
+ * @author Vaadin Ltd
+ * @since
+ */
+public class SpacerVisibilityChangedEvent extends GwtEvent<SpacerVisibilityChangedHandler> {
+
+    /**
+     * Handler type.
+     */
+    public static final Type<SpacerVisibilityChangedHandler> TYPE = new Type<>();
+
+    public static final Type<SpacerVisibilityChangedHandler> getType() {
+        return TYPE;
+    }
+
+    private final int rowIndex;
+    private final boolean visible;
+
+    /**
+     * Creates a spacer visibility changed event.
+     *
+     * @param rowIndex
+     *         index of row to which the spacer belongs
+     * @param visible
+     *         {@code true} if the spacer element is shown, {@code false} if the
+     *         spacer element is hidden
+     */
+    public SpacerVisibilityChangedEvent(int rowIndex, boolean visible) {
+        this.rowIndex = rowIndex;
+        this.visible = visible;
+    }
+
+    /**
+     * Gets the row index to which the spacer element belongs.
+     *
+     * @return the row index to which the spacer element belongs
+     */
+    public int getRowIndex() {
+        return rowIndex;
+    }
+
+    /**
+     * Gets whether the spacer element is displayed.
+     *
+     * @return {@code true} if the spacer element is shown, {@code false} if the
+     * spacer element is hidden
+     */
+    public boolean isSpacerVisible() {
+        return visible;
+    }
+
+    @Override
+    public Type<SpacerVisibilityChangedHandler> getAssociatedType() {
+        return TYPE;
+    }
+
+    @Override
+    protected void dispatch(SpacerVisibilityChangedHandler handler) {
+        handler.onSpacerVisibilityChanged(this);
+    }
+
+}
diff --git a/client/src/main/java/com/vaadin/client/widget/escalator/events/SpacerVisibilityChangedHandler.java b/client/src/main/java/com/vaadin/client/widget/escalator/events/SpacerVisibilityChangedHandler.java
new file mode 100644 (file)
index 0000000..fe7b02a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2000-2016 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.client.widget.escalator.events;
+
+import com.google.gwt.event.shared.EventHandler;
+
+/**
+ * Event handler for a spacer visibility changed event.
+ *
+ * @author Vaadin Ltd
+ * @since
+ */
+public interface SpacerVisibilityChangedHandler extends EventHandler {
+
+    /**
+     * Called when a spacer visibility changed event is fired, when a spacer's
+     * visibility changes.
+     *
+     * @param event
+     *            the spacer visibility changed event
+     */
+    public void onSpacerVisibilityChanged(SpacerVisibilityChangedEvent event);
+}
index b3008d28dc1bb0ba7a9d95b8d0e18f9c041d4c00..da488e11536f7caefac503f987798c86212817b2 100644 (file)
@@ -64,6 +64,7 @@ import com.google.gwt.user.client.ui.Widget;
 import com.vaadin.client.BrowserInfo;
 import com.vaadin.client.ComputedStyle;
 import com.vaadin.client.DeferredWorker;
+import com.vaadin.client.LayoutManager;
 import com.vaadin.client.Profiler;
 import com.vaadin.client.WidgetUtil;
 import com.vaadin.client.ui.SubPartAware;
@@ -87,6 +88,7 @@ import com.vaadin.client.widget.escalator.ScrollbarBundle.VerticalScrollbarBundl
 import com.vaadin.client.widget.escalator.Spacer;
 import com.vaadin.client.widget.escalator.SpacerUpdater;
 import com.vaadin.client.widget.escalator.events.RowHeightChangedEvent;
+import com.vaadin.client.widget.escalator.events.SpacerVisibilityChangedEvent;
 import com.vaadin.client.widget.grid.events.ScrollEvent;
 import com.vaadin.client.widget.grid.events.ScrollHandler;
 import com.vaadin.client.widgets.Escalator.JsniUtil.TouchHandlerBundle;
@@ -4962,11 +4964,15 @@ public class Escalator extends Widget
             public void show() {
                 getRootElement().getStyle().clearDisplay();
                 getDecoElement().getStyle().clearDisplay();
+                Escalator.this.fireEvent(
+                        new SpacerVisibilityChangedEvent(getRow(), true));
             }
 
             public void hide() {
                 getRootElement().getStyle().setDisplay(Display.NONE);
                 getDecoElement().getStyle().setDisplay(Display.NONE);
+                Escalator.this.fireEvent(
+                        new SpacerVisibilityChangedEvent(getRow(), false));
             }
 
             /**
index a790ccb31456e84ebe7fe62dea89d50cea3de645..982e6d2ecbb0a8ce73cb151bfc33c35889f83d8f 100755 (executable)
@@ -103,6 +103,8 @@ import com.vaadin.client.widget.escalator.Spacer;
 import com.vaadin.client.widget.escalator.SpacerUpdater;
 import com.vaadin.client.widget.escalator.events.RowHeightChangedEvent;
 import com.vaadin.client.widget.escalator.events.RowHeightChangedHandler;
+import com.vaadin.client.widget.escalator.events.SpacerVisibilityChangedEvent;
+import com.vaadin.client.widget.escalator.events.SpacerVisibilityChangedHandler;
 import com.vaadin.client.widget.grid.AutoScroller;
 import com.vaadin.client.widget.grid.AutoScroller.AutoScrollerCallback;
 import com.vaadin.client.widget.grid.AutoScroller.ScrollAxis;
@@ -8558,6 +8560,19 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>,
         return escalator.addHandler(handler, RowHeightChangedEvent.TYPE);
     }
 
+    /**
+     * Adds a spacer visibility changed handler to the underlying escalator.
+     *
+     * @param handler
+     *         the handler to be called when a spacer's visibility changes
+     * @return the registration object with which the handler can be removed
+     * @since
+     */
+    public HandlerRegistration addSpacerVisibilityChangedHandler(
+            SpacerVisibilityChangedHandler handler) {
+        return escalator.addHandler(handler, SpacerVisibilityChangedEvent.TYPE);
+    }
+
     /**
      * Adds a low-level DOM event handler to this Grid. The handler is inserted
      * into the given position in the list of handlers. The handlers are invoked
index 3cc1906dcc6b29c77554144e40864324e67e0577..68542268d54d9175f98479bc7f6235587458c5ce 100644 (file)
@@ -319,4 +319,21 @@ public class GridBasicDetailsTest extends GridBasicsTest {
                 getGridElement().getDetails(0).getText().contains("One"));
     }
 
+    @Test
+    public void detailsSizeCorrectAfterScrolling() {
+        selectMenuPath(DETAILS_GENERATOR_PERSISTING);
+        selectMenuPath(OPEN_FIRST_ITEM_DETAILS);
+
+        // Scroll to request next range
+        getGridElement().scrollToRow(21);
+        getGridElement().scrollToRow(0);
+        assertGreater("Details row should have correct height",
+                getGridElement().getDetails(0).getSize().getHeight(), 30);
+
+        // Scroll outside of cached rows
+        getGridElement().scrollToRow(101);
+        getGridElement().scrollToRow(0);
+        assertGreater("Details row should have correct height",
+                getGridElement().getDetails(0).getSize().getHeight(), 30);
+    }
 }