Delayed size changes caused by added or removed scrollbars should be taken into account.tags/8.12.0.alpha1
@@ -377,6 +377,15 @@ public class GridConnector extends AbstractListingConnector | |||
getLayoutManager().layoutNow(); | |||
}); | |||
// Handling Escalator size changes | |||
grid.getEscalator().addEscalatorSizeChangeHandler(event -> { | |||
getLayoutManager().setNeedsMeasure(GridConnector.this); | |||
if (!getConnection().getMessageHandler().isUpdatingState() | |||
&& !getLayoutManager().isLayoutRunning()) { | |||
getLayoutManager().layoutNow(); | |||
} | |||
}); | |||
/* Item click events */ | |||
grid.addBodyClickHandler(itemClickHandler); | |||
grid.addBodyDoubleClickHandler(itemClickHandler); |
@@ -0,0 +1,88 @@ | |||
/* | |||
* Copyright 2000-2018 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.grid.events; | |||
import com.google.gwt.event.shared.EventHandler; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
/** | |||
* FOR INTERNAL USE ONLY, MAY GET REMOVED OR MODIFIED AT ANY TIME! | |||
* <p> | |||
* Event handler that gets notified when the size of the Escalator changes. | |||
* | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface EscalatorSizeChangeHandler extends EventHandler { | |||
/** | |||
* FOR INTERNAL USE ONLY, MAY GET REMOVED OR MODIFIED AT ANY TIME! | |||
* <p> | |||
* Called when the size of the Escalator changes. | |||
* | |||
* @param event | |||
* the row visibility change event describing the change | |||
*/ | |||
void onEscalatorSizeChange(EscalatorSizeChangeEvent event); | |||
/** | |||
* FOR INTERNAL USE ONLY, MAY GET REMOVED OR MODIFIED AT ANY TIME! | |||
* <p> | |||
* Event fired when the Escalator size changes. | |||
* | |||
* @author Vaadin Ltd | |||
*/ | |||
public class EscalatorSizeChangeEvent | |||
extends GwtEvent<EscalatorSizeChangeHandler> { | |||
/** | |||
* FOR INTERNAL USE ONLY, MAY GET REMOVED OR MODIFIED AT ANY TIME! | |||
* <p> | |||
* The type of this event. | |||
*/ | |||
public static final Type<EscalatorSizeChangeHandler> TYPE = new Type<>(); | |||
/** | |||
* FOR INTERNAL USE ONLY, MAY GET REMOVED OR MODIFIED AT ANY TIME! | |||
* <p> | |||
* Creates a new Escalator size change event. | |||
* | |||
*/ | |||
public EscalatorSizeChangeEvent() { | |||
// NOP | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see com.google.gwt.event.shared.GwtEvent#getAssociatedType() | |||
*/ | |||
@Override | |||
public Type<EscalatorSizeChangeHandler> getAssociatedType() { | |||
return TYPE; | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see | |||
* com.google.gwt.event.shared.GwtEvent#dispatch(com.google.gwt.event. | |||
* shared .EventHandler) | |||
*/ | |||
@Override | |||
protected void dispatch(EscalatorSizeChangeHandler handler) { | |||
handler.onEscalatorSizeChange(this); | |||
} | |||
} | |||
} |
@@ -94,6 +94,8 @@ import com.vaadin.client.widget.escalator.SpacerUpdater; | |||
import com.vaadin.client.widget.escalator.events.RowHeightChangedEvent; | |||
import com.vaadin.client.widget.escalator.events.SpacerIndexChangedEvent; | |||
import com.vaadin.client.widget.escalator.events.SpacerVisibilityChangedEvent; | |||
import com.vaadin.client.widget.grid.events.EscalatorSizeChangeHandler; | |||
import com.vaadin.client.widget.grid.events.EscalatorSizeChangeHandler.EscalatorSizeChangeEvent; | |||
import com.vaadin.client.widget.grid.events.ScrollEvent; | |||
import com.vaadin.client.widget.grid.events.ScrollHandler; | |||
import com.vaadin.client.widgets.Escalator.JsniUtil.TouchHandlerBundle; | |||
@@ -7513,10 +7515,17 @@ public class Escalator extends Widget | |||
@Override | |||
public void setWidth(final String width) { | |||
String oldWidth = getElement().getStyle().getProperty("width"); | |||
if (width != null && !width.isEmpty()) { | |||
super.setWidth(width); | |||
if (!width.equals(oldWidth)) { | |||
fireEscalatorSizeChangeEvent(); | |||
} | |||
} else { | |||
super.setWidth(DEFAULT_WIDTH); | |||
if (!DEFAULT_WIDTH.equals(oldWidth)) { | |||
fireEscalatorSizeChangeEvent(); | |||
} | |||
} | |||
recalculateElementSizes(); | |||
@@ -7558,7 +7567,11 @@ public class Escalator extends Widget | |||
final int escalatorRowsBefore = body.visualRowOrder.size(); | |||
if (height != null && !height.isEmpty()) { | |||
String oldHeight = getElement().getStyle().getProperty("height"); | |||
super.setHeight(height); | |||
if (!height.equals(oldHeight)) { | |||
fireEscalatorSizeChangeEvent(); | |||
} | |||
} else { | |||
if (getHeightMode() == HeightMode.UNDEFINED) { | |||
int newHeightByRows = body.getRowCount(); | |||
@@ -7568,7 +7581,12 @@ public class Escalator extends Widget | |||
} | |||
return; | |||
} else { | |||
String oldHeight = getElement().getStyle() | |||
.getProperty("height"); | |||
super.setHeight(DEFAULT_HEIGHT); | |||
if (!DEFAULT_HEIGHT.equals(oldHeight)) { | |||
fireEscalatorSizeChangeEvent(); | |||
} | |||
} | |||
} | |||
@@ -7855,6 +7873,25 @@ public class Escalator extends Widget | |||
return array; | |||
} | |||
/** | |||
* FOR INTERNAL USE ONLY, MAY GET REMOVED OR MODIFIED AT ANY TIME! | |||
* <p> | |||
* Adds an event handler that gets notified when the Escalator size changes. | |||
* | |||
* @param escalatorSizeChangeHandler | |||
* the event handler | |||
* @return a handler registration for the added handler | |||
*/ | |||
public HandlerRegistration addEscalatorSizeChangeHandler( | |||
EscalatorSizeChangeHandler escalatorSizeChangeHandler) { | |||
return addHandler(escalatorSizeChangeHandler, | |||
EscalatorSizeChangeEvent.TYPE); | |||
} | |||
private void fireEscalatorSizeChangeEvent() { | |||
fireEvent(new EscalatorSizeChangeEvent()); | |||
} | |||
/** | |||
* Adds an event handler that gets notified when the range of visible rows | |||
* changes e.g. because of scrolling, row resizing or spacers |
@@ -0,0 +1,109 @@ | |||
package com.vaadin.tests.components.grid; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import com.vaadin.annotations.Widgetset; | |||
import com.vaadin.data.provider.DataProvider; | |||
import com.vaadin.data.provider.ListDataProvider; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.shared.ui.grid.HeightMode; | |||
import com.vaadin.tests.components.AbstractReindeerTestUI; | |||
import com.vaadin.ui.Alignment; | |||
import com.vaadin.ui.Button; | |||
import com.vaadin.ui.Grid; | |||
import com.vaadin.ui.GridLayout; | |||
import com.vaadin.ui.TabSheet; | |||
import com.vaadin.ui.VerticalLayout; | |||
@Widgetset("com.vaadin.DefaultWidgetSet") | |||
public class GridSizeChange extends AbstractReindeerTestUI { | |||
private Grid<Integer> grid; | |||
private List<Integer> data; | |||
private ListDataProvider<Integer> dataProvider; | |||
private int counter = 0; | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
grid = new Grid<>(); | |||
data = new ArrayList<>(); | |||
for (int i = 0; i < 10; ++i) { | |||
data.add(i); | |||
++counter; | |||
} | |||
dataProvider = DataProvider.ofCollection(data); | |||
grid.setDataProvider(dataProvider); | |||
// create column and fill rows | |||
grid.addColumn(item -> "row_" + item).setCaption("Item"); | |||
// set height mode and height | |||
grid.setHeightMode(HeightMode.ROW); | |||
grid.setHeightByRows(10); | |||
grid.setWidth(90, Unit.PIXELS); | |||
// create a tabsheet with one tab and place grid inside | |||
VerticalLayout tab = new VerticalLayout(); | |||
tab.setSpacing(false); | |||
tab.setMargin(false); | |||
TabSheet tabSheet = new TabSheet(); | |||
tabSheet.setWidthUndefined(); | |||
tabSheet.addTab(tab, "Tab"); | |||
tab.addComponent(grid); | |||
GridLayout layout = new GridLayout(3, 2); | |||
layout.setDefaultComponentAlignment(Alignment.TOP_CENTER); | |||
layout.addComponent(new Button("Reduce height", e -> { | |||
double newHeight = grid.getHeightByRows() - 1; | |||
grid.setHeightByRows(newHeight); | |||
})); | |||
layout.addComponent(new Button("Remove row", e -> { | |||
removeRow(); | |||
dataProvider.refreshAll(); | |||
})); | |||
layout.addComponent(new Button("Reduce width", e -> { | |||
grid.setWidth(grid.getWidth() - 30, Unit.PIXELS); | |||
})); | |||
layout.addComponent(new Button("Increase height", e -> { | |||
double newHeight = grid.getHeightByRows() + 1; | |||
grid.setHeightByRows(newHeight); | |||
})); | |||
layout.addComponent(new Button("Add row", e -> { | |||
addRow(); | |||
dataProvider.refreshAll(); | |||
})); | |||
layout.addComponent(new Button("Increase width", e -> { | |||
grid.setWidth(grid.getWidth() + 30, Unit.PIXELS); | |||
})); | |||
addComponent(tabSheet); | |||
addComponent(layout); | |||
getLayout().setSpacing(true); | |||
} | |||
private void removeRow() { | |||
data.remove(0); | |||
dataProvider.refreshAll(); | |||
} | |||
private void addRow() { | |||
++counter; | |||
data.add(counter); | |||
dataProvider.refreshAll(); | |||
} | |||
@Override | |||
protected String getTestDescription() { | |||
return "Changing Grid size should resize the TabSheet accordingly " | |||
+ "even if scrollbar(s) appear or disappear."; | |||
} | |||
} |
@@ -0,0 +1,95 @@ | |||
package com.vaadin.tests.components.grid; | |||
import static org.hamcrest.MatcherAssert.assertThat; | |||
import static org.hamcrest.number.IsCloseTo.closeTo; | |||
import static org.junit.Assert.assertEquals; | |||
import org.junit.Test; | |||
import org.openqa.selenium.WebElement; | |||
import com.vaadin.testbench.By; | |||
import com.vaadin.testbench.elements.ButtonElement; | |||
import com.vaadin.testbench.elements.GridElement; | |||
import com.vaadin.testbench.elements.TabSheetElement; | |||
import com.vaadin.tests.tb3.MultiBrowserTest; | |||
public class GridSizeChangeTest extends MultiBrowserTest { | |||
private TabSheetElement tabSheet; | |||
private GridElement grid; | |||
private WebElement vScrollbar; | |||
private WebElement hScrollbar; | |||
@Test | |||
public void scrollbarsTakenIntoAccountInSizeChanges() { | |||
openTestURL(); | |||
tabSheet = $(TabSheetElement.class).first(); | |||
grid = $(GridElement.class).first(); | |||
vScrollbar = grid.findElement(By.className("v-grid-scroller-vertical")); | |||
hScrollbar = grid | |||
.findElement(By.className("v-grid-scroller-horizontal")); | |||
// ensure no initial scrollbars | |||
ensureVerticalScrollbar(false); | |||
ensureHorizontalScrollbar(false); | |||
assertGridWithinTabSheet(); | |||
$(ButtonElement.class).caption("Reduce height").first().click(); | |||
// more rows than height -> scrollbar | |||
assertGridWithinTabSheet(); | |||
ensureVerticalScrollbar(true); | |||
$(ButtonElement.class).caption("Remove row").first().click(); | |||
// height matches rows -> no scrollbar | |||
assertGridWithinTabSheet(); | |||
ensureVerticalScrollbar(false); | |||
$(ButtonElement.class).caption("Reduce width").first().click(); | |||
// column too wide -> scrollbar | |||
assertGridWithinTabSheet(); | |||
ensureHorizontalScrollbar(true); | |||
$(ButtonElement.class).caption("Increase width").first().click(); | |||
// column fits -> no scrollbar | |||
assertGridWithinTabSheet(); | |||
ensureHorizontalScrollbar(false); | |||
$(ButtonElement.class).caption("Add row").first().click(); | |||
// more rows than height -> scrollbar | |||
assertGridWithinTabSheet(); | |||
ensureVerticalScrollbar(true); | |||
$(ButtonElement.class).caption("Increase height").first().click(); | |||
// height matches rows -> no scrollbar | |||
assertGridWithinTabSheet(); | |||
ensureVerticalScrollbar(false); | |||
} | |||
private void ensureVerticalScrollbar(boolean displayed) { | |||
assertEquals(displayed ? "block" : "none", | |||
vScrollbar.getCssValue("display")); | |||
} | |||
private void ensureHorizontalScrollbar(boolean displayed) { | |||
assertEquals(displayed ? "block" : "none", | |||
hScrollbar.getCssValue("display")); | |||
} | |||
private void assertGridWithinTabSheet() throws AssertionError { | |||
// allow two pixel leeway | |||
assertThat( | |||
"Grid and TabSheet should always have the same bottom position, " | |||
+ "not be offset by a scrollbar's thickness", | |||
(double) grid.getLocation().getY() + grid.getSize().getHeight(), | |||
closeTo(tabSheet.getLocation().getY() | |||
+ tabSheet.getSize().getHeight(), 2)); | |||
} | |||
} |