123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872 |
- /*
- * Copyright 2000-2021 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;
-
- import java.util.Collection;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Map;
- import java.util.Set;
- import java.util.logging.Level;
- import java.util.logging.Logger;
-
- import com.google.gwt.core.client.Duration;
- import com.google.gwt.core.client.JsArrayString;
- import com.google.gwt.dom.client.Element;
- import com.google.gwt.dom.client.Style;
- import com.google.gwt.dom.client.Style.Overflow;
- import com.google.gwt.user.client.Timer;
- import com.vaadin.client.MeasuredSize.MeasureResult;
- import com.vaadin.client.ui.ManagedLayout;
- import com.vaadin.client.ui.PostLayoutListener;
- import com.vaadin.client.ui.SimpleManagedLayout;
- import com.vaadin.client.ui.VNotification;
- import com.vaadin.client.ui.layout.ElementResizeEvent;
- import com.vaadin.client.ui.layout.ElementResizeListener;
- import com.vaadin.client.ui.layout.LayoutDependencyTree;
-
- public class LayoutManager {
- private static final String STATE_CHANGE_MESSAGE = "Cannot run layout while processing state change from the server.";
-
- private static final String LOOP_ABORT_MESSAGE = "Aborting layout after 100 passes. This would probably be an infinite loop.";
-
- private static final boolean DEBUG_LOGGING = false;
-
- private ApplicationConnection connection;
- private final Set<Element> measuredNonConnectorElements = new HashSet<>();
- private final MeasuredSize nullSize = new MeasuredSize();
-
- private LayoutDependencyTree currentDependencyTree;
-
- private FastStringSet needsHorizontalLayout = FastStringSet.create();
- private FastStringSet needsVerticalLayout = FastStringSet.create();
-
- private FastStringSet needsMeasure = FastStringSet.create();
-
- private FastStringSet pendingOverflowFixes = FastStringSet.create();
-
- private final Map<Element, Collection<ElementResizeListener>> elementResizeListeners = new HashMap<>();
- private final Set<Element> listenersToFire = new HashSet<>();
-
- private boolean layoutPending = false;
- private Timer layoutTimer = new Timer() {
- @Override
- public void run() {
- layoutNow();
- }
- };
- private boolean everythingNeedsMeasure = false;
- private boolean thoroughSizeCheck = true;
-
- /**
- * Sets the application connection this instance is connected to. Called
- * internally by the framework.
- *
- * @param connection
- * the application connection this instance is connected to
- */
- public void setConnection(ApplicationConnection connection) {
- if (this.connection != null) {
- throw new RuntimeException(
- "LayoutManager connection can never be changed");
- }
- this.connection = connection;
- }
-
- /**
- * Set whether the measuring should use a thorough size check that evaluates
- * the presence of the element and uses calculated size, or default to a
- * slightly faster check that can result in incorrect size information if
- * the check is triggered while a transform animation is ongoing. This can
- * happen e.g. when a PopupView is opened.
- * <p>
- * By default, the thorough size check is enabled.
- *
- * @param thoroughSizeCheck
- * {@code true} if thorough size check enabled, {@code false} if
- * not
- * @since 8.13
- */
- public void setThoroughSizeChck(boolean thoroughSizeCheck) {
- this.thoroughSizeCheck = thoroughSizeCheck;
- }
-
- /**
- * Returns the application connection for this layout manager.
- *
- * @return connection
- */
- protected ApplicationConnection getConnection() {
- return connection;
- }
-
- /**
- * Gets the layout manager associated with the given
- * {@link ApplicationConnection}.
- *
- * @param connection
- * the application connection to get a layout manager for
- * @return the layout manager associated with the provided application
- * connection
- */
- public static LayoutManager get(ApplicationConnection connection) {
- return connection.getLayoutManager();
- }
-
- /**
- * Registers that a ManagedLayout is depending on the size of an Element.
- * This causes this layout manager to measure the element in the beginning
- * of every layout phase and call the appropriate layout method of the
- * managed layout if the size of the element has changed.
- *
- * @param owner
- * the ManagedLayout that depends on an element
- * @param element
- * the Element that should be measured
- */
- public void registerDependency(ManagedLayout owner, Element element) {
- MeasuredSize measuredSize = ensureMeasured(element);
- setNeedsLayout(owner);
- measuredSize.addDependent(owner.getConnectorId());
- }
-
- private MeasuredSize ensureMeasured(Element element) {
- MeasuredSize measuredSize = getMeasuredSize(element, null);
- if (measuredSize == null) {
- measuredSize = new MeasuredSize();
-
- if (ConnectorMap.get(connection).getConnector(element) == null) {
- measuredNonConnectorElements.add(element);
- }
- setMeasuredSize(element, measuredSize);
- }
- return measuredSize;
- }
-
- private boolean needsMeasure(Element e) {
- ComponentConnector connector = connection.getConnectorMap()
- .getConnector(e);
- if (connector != null && needsMeasureForManagedLayout(connector)) {
- return true;
- } else if (elementResizeListeners.containsKey(e)) {
- return true;
- }
- return getMeasuredSize(e, nullSize).hasDependents();
- }
-
- private boolean needsMeasureForManagedLayout(ComponentConnector connector) {
- if (connector instanceof ManagedLayout) {
- return true;
- }
- return connector.getParent() instanceof ManagedLayout;
- }
-
- /**
- * Assigns a measured size to an element. Method defined as protected for
- * legacy reasons.
- *
- * @param element
- * the dom element to attach the measured size to
- * @param measuredSize
- * the measured size to attach to the element. If
- * <code>null</code>, any previous measured size is removed.
- */
- protected native void setMeasuredSize(Element element,
- MeasuredSize measuredSize)
- /*-{
- if (measuredSize) {
- element.vMeasuredSize = measuredSize;
- } else {
- delete element.vMeasuredSize;
- }
- }-*/;
-
- /**
- * Gets the measured size for an element. Method defined as protected for
- * legacy reasons.
- *
- * @param element
- * The element to get measured size for
- * @param defaultSize
- * The size to return if no measured size could be found
- * @return The measured size for the element or {@literal defaultSize}
- */
- protected native MeasuredSize getMeasuredSize(Element element,
- MeasuredSize defaultSize)
- /*-{
- return element.vMeasuredSize || defaultSize;
- }-*/;
-
- private final MeasuredSize getMeasuredSize(Element element) {
- MeasuredSize measuredSize = getMeasuredSize(element, null);
- if (measuredSize == null) {
- measuredSize = new MeasuredSize();
- setMeasuredSize(element, measuredSize);
- }
- return measuredSize;
- }
-
- /**
- * Registers that a ManagedLayout is no longer depending on the size of an
- * Element.
- *
- * @see #registerDependency(ManagedLayout, Element)
- *
- * @param owner
- * the ManagedLayout no longer depends on an element
- * @param element
- * the Element that that no longer needs to be measured
- */
- public void unregisterDependency(ManagedLayout owner, Element element) {
- MeasuredSize measuredSize = getMeasuredSize(element, null);
- if (measuredSize == null) {
- return;
- }
- measuredSize.removeDependent(owner.getConnectorId());
- stopMeasuringIfUnecessary(element);
- }
-
- public boolean isLayoutRunning() {
- return currentDependencyTree != null;
- }
-
- private void countLayout(FastStringMap<Integer> layoutCounts,
- ManagedLayout layout) {
- Integer count = layoutCounts.get(layout.getConnectorId());
- if (count == null) {
- count = Integer.valueOf(0);
- } else {
- count = Integer.valueOf(count.intValue() + 1);
- }
- layoutCounts.put(layout.getConnectorId(), count);
- if (count.intValue() > 2) {
- getLogger().severe(Util.getConnectorString(layout)
- + " has been layouted " + count.intValue() + " times");
- }
- }
-
- public void layoutLater() {
- if (!layoutPending) {
- layoutPending = true;
- layoutTimer.schedule(100);
- }
- }
-
- public void layoutNow() {
- if (isLayoutRunning()) {
- throw new IllegalStateException(
- "Can't start a new layout phase before the previous layout phase ends.");
- }
-
- if (connection.getMessageHandler().isUpdatingState()) {
- // If assertions are enabled, throw an exception
- assert false : STATE_CHANGE_MESSAGE;
-
- // Else just log a warning and postpone the layout
- getLogger().warning(STATE_CHANGE_MESSAGE);
-
- // Framework will call layoutNow when the state update is completed
- return;
- }
-
- layoutPending = false;
- layoutTimer.cancel();
- try {
- currentDependencyTree = new LayoutDependencyTree(connection);
- doLayout();
- } finally {
- currentDependencyTree = null;
- }
- }
-
- /**
- * Called once per iteration in the layout loop before size calculations so
- * different browsers quirks can be handled. Mainly this exists for legacy
- * reasons.
- */
- protected void performBrowserLayoutHacks() {
- // Permutations implement this
- }
-
- private void doLayout() {
- getLogger().info("Starting layout phase");
- Profiler.enter("LayoutManager phase init");
-
- FastStringMap<Integer> layoutCounts = FastStringMap.create();
-
- int passes = 0;
- Duration totalDuration = new Duration();
-
- ConnectorMap connectorMap = ConnectorMap.get(connection);
-
- JsArrayString dump = needsHorizontalLayout.dump();
- int dumpLength = dump.length();
- for (int i = 0; i < dumpLength; i++) {
- String layoutId = dump.get(i);
- currentDependencyTree.setNeedsHorizontalLayout(layoutId, true);
- }
-
- dump = needsVerticalLayout.dump();
- dumpLength = dump.length();
- for (int i = 0; i < dumpLength; i++) {
- String layoutId = dump.get(i);
- currentDependencyTree.setNeedsVerticalLayout(layoutId, true);
- }
- needsHorizontalLayout = FastStringSet.create();
- needsVerticalLayout = FastStringSet.create();
-
- dump = needsMeasure.dump();
- dumpLength = dump.length();
- for (int i = 0; i < dumpLength; i++) {
- ServerConnector connector = connectorMap.getConnector(dump.get(i));
- if (connector != null) {
- currentDependencyTree
- .setNeedsMeasure((ComponentConnector) connector, true);
- }
- }
- needsMeasure = FastStringSet.create();
-
- measureNonConnectors();
-
- Profiler.leave("LayoutManager phase init");
-
- while (true) {
- Profiler.enter("Layout pass");
- passes++;
-
- performBrowserLayoutHacks();
-
- Profiler.enter("Layout measure connectors");
- int measuredConnectorCount = measureConnectors(
- currentDependencyTree, everythingNeedsMeasure);
- Profiler.leave("Layout measure connectors");
-
- everythingNeedsMeasure = false;
- if (measuredConnectorCount == 0) {
- getLogger().info("No more changes in pass " + passes);
- Profiler.leave("Layout pass");
- break;
- }
-
- int firedListeners = 0;
- if (!listenersToFire.isEmpty()) {
- HashSet<Element> listenersCopy = new HashSet<Element>(
- listenersToFire);
- listenersToFire.clear();
- firedListeners = listenersToFire.size();
- Profiler.enter("Layout fire resize events");
- for (Element element : listenersCopy) {
- Collection<ElementResizeListener> listeners = elementResizeListeners
- .get(element);
- if (listeners != null) {
- Profiler.enter(
- "Layout fire resize events - listeners not null");
- Profiler.enter(
- "ElementResizeListener.onElementResize copy list");
- ElementResizeListener[] array = listeners.toArray(
- new ElementResizeListener[listeners.size()]);
- Profiler.leave(
- "ElementResizeListener.onElementResize copy list");
- ElementResizeEvent event = new ElementResizeEvent(this,
- element);
- for (ElementResizeListener listener : array) {
- try {
- String key = null;
- if (Profiler.isEnabled()) {
- Profiler.enter(
- "ElementResizeListener.onElementResize construct profiler key");
- key = "ElementResizeListener.onElementResize for "
- + listener.getClass()
- .getSimpleName();
- Profiler.leave(
- "ElementResizeListener.onElementResize construct profiler key");
- Profiler.enter(key);
- }
-
- listener.onElementResize(event);
- if (Profiler.isEnabled()) {
- Profiler.leave(key);
- }
- } catch (RuntimeException e) {
- getLogger().log(Level.SEVERE,
- "Error in resize listener", e);
- }
- }
- Profiler.leave(
- "Layout fire resize events - listeners not null");
- }
- }
- Profiler.leave("Layout fire resize events");
- }
-
- Profiler.enter("LayoutManager handle ManagedLayout");
-
- FastStringSet updatedSet = FastStringSet.create();
-
- int layoutCount = 0;
- while (currentDependencyTree.hasHorizontalConnectorToLayout()
- || currentDependencyTree.hasVerticaConnectorToLayout()) {
-
- JsArrayString layoutTargets = currentDependencyTree
- .getHorizontalLayoutTargetsJsArray();
- int length = layoutTargets.length();
- for (int i = 0; i < length; i++) {
- ManagedLayout layout = (ManagedLayout) connectorMap
- .getConnector(layoutTargets.get(i));
- if (layout instanceof DirectionalManagedLayout) {
- currentDependencyTree
- .markAsHorizontallyLayouted(layout);
- DirectionalManagedLayout cl = (DirectionalManagedLayout) layout;
- try {
- String key = null;
- if (Profiler.isEnabled()) {
- key = "layoutHorizontally() for "
- + cl.getClass().getSimpleName();
- Profiler.enter(key);
- }
-
- cl.layoutHorizontally();
- layoutCount++;
-
- if (Profiler.isEnabled()) {
- Profiler.leave(key);
- }
- } catch (RuntimeException e) {
- getLogger().log(Level.SEVERE,
- "Error in ManagedLayout handling", e);
- }
- countLayout(layoutCounts, cl);
- } else {
- currentDependencyTree
- .markAsHorizontallyLayouted(layout);
- currentDependencyTree.markAsVerticallyLayouted(layout);
- SimpleManagedLayout rr = (SimpleManagedLayout) layout;
- try {
- String key = null;
- if (Profiler.isEnabled()) {
- key = "layout() for "
- + rr.getClass().getSimpleName();
- Profiler.enter(key);
- }
-
- rr.layout();
- layoutCount++;
-
- if (Profiler.isEnabled()) {
- Profiler.leave(key);
- }
- } catch (RuntimeException e) {
- getLogger().log(Level.SEVERE,
- "Error in SimpleManagedLayout (horizontal) handling",
- e);
-
- }
- countLayout(layoutCounts, rr);
- }
- if (DEBUG_LOGGING) {
- updatedSet.add(layout.getConnectorId());
- }
- }
-
- layoutTargets = currentDependencyTree
- .getVerticalLayoutTargetsJsArray();
- length = layoutTargets.length();
- for (int i = 0; i < length; i++) {
- ManagedLayout layout = (ManagedLayout) connectorMap
- .getConnector(layoutTargets.get(i));
- if (layout instanceof DirectionalManagedLayout) {
- currentDependencyTree.markAsVerticallyLayouted(layout);
- DirectionalManagedLayout cl = (DirectionalManagedLayout) layout;
- try {
- String key = null;
- if (Profiler.isEnabled()) {
- key = "layoutVertically() for "
- + cl.getClass().getSimpleName();
- Profiler.enter(key);
- }
-
- cl.layoutVertically();
- layoutCount++;
-
- if (Profiler.isEnabled()) {
- Profiler.leave(key);
- }
- } catch (RuntimeException e) {
- getLogger().log(Level.SEVERE,
- "Error in DirectionalManagedLayout handling",
- e);
- }
- countLayout(layoutCounts, cl);
- } else {
- currentDependencyTree
- .markAsHorizontallyLayouted(layout);
- currentDependencyTree.markAsVerticallyLayouted(layout);
- SimpleManagedLayout rr = (SimpleManagedLayout) layout;
- try {
- String key = null;
- if (Profiler.isEnabled()) {
- key = "layout() for "
- + rr.getClass().getSimpleName();
- Profiler.enter(key);
- }
-
- rr.layout();
- layoutCount++;
-
- if (Profiler.isEnabled()) {
- Profiler.leave(key);
- }
- } catch (RuntimeException e) {
- getLogger().log(Level.SEVERE,
- "Error in SimpleManagedLayout (vertical) handling",
- e);
- }
- countLayout(layoutCounts, rr);
- }
- if (DEBUG_LOGGING) {
- updatedSet.add(layout.getConnectorId());
- }
- }
- }
-
- Profiler.leave("LayoutManager handle ManagedLayout");
-
- if (DEBUG_LOGGING) {
- JsArrayString changedCids = updatedSet.dump();
-
- StringBuilder b = new StringBuilder(" ");
- b.append(changedCids.length());
- b.append(" requestLayout invocations ");
- if (changedCids.length() < 30) {
- for (int i = 0; i < changedCids.length(); i++) {
- if (i != 0) {
- b.append(", ");
- } else {
- b.append(": ");
- }
- String connectorString = changedCids.get(i);
- if (changedCids.length() < 10) {
- ServerConnector connector = ConnectorMap
- .get(connection)
- .getConnector(connectorString);
- connectorString = Util
- .getConnectorString(connector);
- }
- b.append(connectorString);
- }
- }
- getLogger().info(b.toString());
- }
-
- Profiler.leave("Layout pass");
-
- getLogger().info("Pass " + passes + " measured "
- + measuredConnectorCount + " elements, fired "
- + firedListeners + " listeners and did " + layoutCount
- + " layouts.");
-
- if (passes > 100) {
- getLogger().severe(LOOP_ABORT_MESSAGE);
- if (ApplicationConfiguration.isDebugMode()) {
- VNotification
- .createNotification(VNotification.DELAY_FOREVER,
- connection.getUIConnector().getWidget())
- .show(LOOP_ABORT_MESSAGE, VNotification.CENTERED,
- "error");
- }
- break;
- }
- }
-
- Profiler.enter("layout PostLayoutListener");
- JsArrayObject<ComponentConnector> componentConnectors = connectorMap
- .getComponentConnectorsAsJsArray();
- int size = componentConnectors.size();
- for (int i = 0; i < size; i++) {
- ComponentConnector connector = componentConnectors.get(i);
- if (connector instanceof PostLayoutListener) {
- String key = null;
- if (Profiler.isEnabled()) {
- key = "layout PostLayoutListener for "
- + connector.getClass().getSimpleName();
- Profiler.enter(key);
- }
-
- ((PostLayoutListener) connector).postLayout();
-
- if (Profiler.isEnabled()) {
- Profiler.leave(key);
- }
- }
- }
- Profiler.leave("layout PostLayoutListener");
-
- // Ensure temporary variables are cleaned
- if (!pendingOverflowFixes.isEmpty()) {
- getLogger().warning(
- "pendingOverflowFixes is not empty at the end of doLayout: "
- + pendingOverflowFixes.dump());
- pendingOverflowFixes = FastStringSet.create();
- }
-
- getLogger().info("Total layout phase time: "
- + totalDuration.elapsedMillis() + "ms");
- }
-
- private void logConnectorStatus(int connectorId) {
- currentDependencyTree.logDependencyStatus(
- (ComponentConnector) ConnectorMap.get(connection)
- .getConnector(Integer.toString(connectorId)));
- }
-
- private int measureConnectors(LayoutDependencyTree layoutDependencyTree,
- boolean measureAll) {
- Profiler.enter("Layout overflow fix handling");
- JsArrayString pendingOverflowConnectorsIds = pendingOverflowFixes
- .dump();
- int pendingOverflowCount = pendingOverflowConnectorsIds.length();
- ConnectorMap connectorMap = ConnectorMap.get(connection);
- if (pendingOverflowCount > 0) {
- Map<Element, String> originalOverflows = new HashMap<>();
-
- FastStringSet delayedOverflowFixes = FastStringSet.create();
-
- // First set overflow to hidden (and save previous value so it can
- // be restored later)
- for (int i = 0; i < pendingOverflowCount; i++) {
- String connectorId = pendingOverflowConnectorsIds.get(i);
- ComponentConnector componentConnector = (ComponentConnector) connectorMap
- .getConnector(connectorId);
-
- if (delayOverflowFix(componentConnector)) {
- delayedOverflowFixes.add(connectorId);
- continue;
- }
-
- if (DEBUG_LOGGING) {
- getLogger().info("Doing overflow fix for "
- + Util.getConnectorString(componentConnector)
- + " in " + Util.getConnectorString(
- componentConnector.getParent()));
- }
- Profiler.enter("Overflow fix apply");
-
- Element parentElement = componentConnector.getWidget()
- .getElement().getParentElement();
- Style style = parentElement.getStyle();
- String originalOverflow = style.getOverflow();
-
- if (originalOverflow != null
- && !originalOverflows.containsKey(parentElement)) {
- // Store original value for restore, but only the first time
- // the value is changed
- originalOverflows.put(parentElement, originalOverflow);
- }
-
- style.setOverflow(Overflow.HIDDEN);
- Profiler.leave("Overflow fix apply");
- }
-
- pendingOverflowFixes.removeAll(delayedOverflowFixes);
-
- JsArrayString remainingOverflowFixIds = pendingOverflowFixes.dump();
- int remainingCount = remainingOverflowFixIds.length();
-
- Profiler.enter("Overflow fix reflow");
- // Then ensure all scrolling elements are reflowed by measuring
- for (int i = 0; i < remainingCount; i++) {
- ComponentConnector componentConnector = (ComponentConnector) connectorMap
- .getConnector(remainingOverflowFixIds.get(i));
- componentConnector.getWidget().getElement().getParentElement()
- .getOffsetHeight();
- }
- Profiler.leave("Overflow fix reflow");
-
- Profiler.enter("Overflow fix restore");
- // Finally restore old overflow value and update bookkeeping
- for (int i = 0; i < remainingCount; i++) {
- String connectorId = remainingOverflowFixIds.get(i);
- ComponentConnector componentConnector = (ComponentConnector) connectorMap
- .getConnector(connectorId);
- Element parentElement = componentConnector.getWidget()
- .getElement().getParentElement();
- parentElement.getStyle().setProperty("overflow",
- originalOverflows.get(parentElement));
-
- layoutDependencyTree.setNeedsMeasure(componentConnector, true);
- }
- Profiler.leave("Overflow fix restore");
-
- if (!pendingOverflowFixes.isEmpty()) {
- getLogger().info(
- "Did overflow fix for " + remainingCount + " elements");
- }
- pendingOverflowFixes = delayedOverflowFixes;
- }
- Profiler.leave("Layout overflow fix handling");
-
- int measureCount = 0;
- if (measureAll) {
- Profiler.enter("Layout measureAll");
- JsArrayObject<ComponentConnector> allConnectors = connectorMap
- .getComponentConnectorsAsJsArray();
- int size = allConnectors.size();
-
- // Find connectors that should actually be measured
- JsArrayObject<ComponentConnector> connectors = JsArrayObject
- .createArray().cast();
- for (int i = 0; i < size; i++) {
- ComponentConnector candidate = allConnectors.get(i);
- if (!Util.shouldSkipMeasurementOfConnector(candidate)
- && needsMeasure(candidate.getWidget().getElement())) {
- connectors.add(candidate);
- }
- }
-
- int connectorCount = connectors.size();
- for (int i = 0; i < connectorCount; i++) {
- measureConnector(connectors.get(i));
- }
- for (int i = 0; i < connectorCount; i++) {
- layoutDependencyTree.setNeedsMeasure(connectors.get(i), false);
- }
- measureCount += connectorCount;
-
- Profiler.leave("Layout measureAll");
- }
-
- Profiler.enter("Layout measure from tree");
- while (layoutDependencyTree.hasConnectorsToMeasure()) {
- JsArrayString measureTargets = layoutDependencyTree
- .getMeasureTargetsJsArray();
- int length = measureTargets.length();
- for (int i = 0; i < length; i++) {
- ComponentConnector connector = (ComponentConnector) connectorMap
- .getConnector(measureTargets.get(i));
- measureConnector(connector);
- measureCount++;
- }
- for (int i = 0; i < length; i++) {
- ComponentConnector connector = (ComponentConnector) connectorMap
- .getConnector(measureTargets.get(i));
- layoutDependencyTree.setNeedsMeasure(connector, false);
- }
- }
- Profiler.leave("Layout measure from tree");
-
- return measureCount;
- }
-
- /*
- * Delay the overflow fix if the involved connectors might still change
- */
- private boolean delayOverflowFix(ComponentConnector componentConnector) {
- if (!currentDependencyTree.noMoreChangesExpected(componentConnector)) {
- return true;
- }
- ServerConnector parent = componentConnector.getParent();
- if (parent instanceof ComponentConnector && !currentDependencyTree
- .noMoreChangesExpected((ComponentConnector) parent)) {
- return true;
- }
-
- return false;
- }
-
- private void measureConnector(ComponentConnector connector) {
- Profiler.enter("LayoutManager.measureConnector");
- Element element = connector.getWidget().getElement();
- MeasuredSize measuredSize = getMeasuredSize(element);
- MeasureResult measureResult = measuredAndUpdate(element, measuredSize);
-
- if (measureResult.isChanged()) {
- onConnectorChange(connector, measureResult.isWidthChanged(),
- measureResult.isHeightChanged());
- }
- Profiler.leave("LayoutManager.measureConnector");
- }
-
- private void onConnectorChange(ComponentConnector connector,
- boolean widthChanged, boolean heightChanged) {
- Profiler.enter("LayoutManager.onConnectorChange");
- Profiler.enter("LayoutManager.onConnectorChange setNeedsOverflowFix");
- setNeedsOverflowFix(connector);
- Profiler.leave("LayoutManager.onConnectorChange setNeedsOverflowFix");
- Profiler.enter("LayoutManager.onConnectorChange heightChanged");
- if (heightChanged) {
- currentDependencyTree.markHeightAsChanged(connector);
- }
- Profiler.leave("LayoutManager.onConnectorChange heightChanged");
- Profiler.enter("LayoutManager.onConnectorChange widthChanged");
- if (widthChanged) {
- currentDependencyTree.markWidthAsChanged(connector);
- }
- Profiler.leave("LayoutManager.onConnectorChange widthChanged");
- Profiler.leave("LayoutManager.onConnectorChange");
- }
-
- private void setNeedsOverflowFix(ComponentConnector connector) {
- // IE9 doesn't need the original fix, but for some reason it needs this
- if (BrowserInfo.get().requiresOverflowAutoFix()) {
- ComponentConnector scrollingBoundary = currentDependencyTree
- .getScrollingBoundary(connector);
- if (scrollingBoundary != null) {
- pendingOverflowFixes.add(scrollingBoundary.getConnectorId());
- }
- }
- }
-
- private void measureNonConnectors() {
- Profiler.enter("LayoutManager.measureNonConenctors");
- for (Element element : measuredNonConnectorElements) {
- measuredAndUpdate(element, getMeasuredSize(element, null));
- }
- Profiler.leave("LayoutManager.measureNonConenctors");
- getLogger().info("Measured " + measuredNonConnectorElements.size()
- + " non connector elements");
- }
-
- private MeasureResult measuredAndUpdate(Element element,
- MeasuredSize measuredSize) {
- MeasureResult measureResult = measuredSize.measure(element,
- thoroughSizeCheck);
- if (measureResult.isChanged()) {
- notifyListenersAndDepdendents(element,
- measureResult.isWidthChanged(),
- measureResult.isHeightChanged());
- }
- return measureResult;
- }
-
- private void notifyListenersAndDepdendents(Element element,
- boolean widthChanged, boolean heightChanged) {
- assert widthChanged || heightChanged;
-
- Profiler.enter("LayoutManager.notifyListenersAndDepdendents");
-
- MeasuredSize measuredSize = getMeasuredSize(element, nullSize);
- JsArrayString dependents = measuredSize.getDependents();
- for (int i = 0; i < dependents.length(); i++) {
- String pid = dependents.get(i);
- if (pid != null) {
- if (heightChanged) {
- currentDependencyTree.setNeedsVerticalLayout(pid, true);
- }
- if (widthChanged) {
- currentDependencyTree.setNeedsHorizontalLayout(pid, true);
- }
- }
- }
- if (elementResizeListeners.containsKey(element)) {
- listenersToFire.add(element);
- }
- Profiler.leave("LayoutManager.notifyListenersAndDepdendents");
- }
-
- private static boolean isManagedLayout(ComponentConnector connector) {
- return connector instanceof ManagedLayout;
- }
-
- public void forceLayout() {
- ConnectorMap connectorMap = connection.getConnectorMap();
- JsArrayObject<ComponentConnector> componentConnectors = connectorMap
- .getComponentConnectorsAsJsArray();
- int size = componentConnectors.size();
- for (int i = 0; i < size; i++) {
- ComponentConnector connector = componentConnectors.get(i);
- if (connector instanceof ManagedLayout) {
- setNeedsLayout((ManagedLayout) connector);
- }
- }
- setEverythingNeedsMeasure();
- layoutNow();
- }
-
- /**
- * Marks that a ManagedLayout should be layouted in the next layout phase
- * even if none of the elements managed by the layout have been resized.
- * <p>
- * This method should not be invoked during a layout phase since it only
- * controls what will happen in the beginning of the next phase. If you want
- * to explicitly cause some layout to be considered in an ongoing layout
- * phase, you should use {@link #setNeedsMeasure(ComponentConnector)}
- * instead.
- *
- * @param layout
- * the managed layout that should be layouted
- */
- public final void setNeedsLayout(ManagedLayout layout) {
- setNeedsHorizontalLayout(layout);
- setNeedsVerticalLayout(layout);
- }
-
- /**
- * Marks that a ManagedLayout should be layouted horizontally in the next
- * layout phase even if none of the elements managed by the layout have been
- * resized horizontally.
- * <p>
- * For SimpleManagedLayout which is always layouted in both directions, this
- * has the same effect as {@link #setNeedsLayout(ManagedLayout)}.
- * <p>
- * This method should not be invoked during a layout phase since it only
- * controls what will happen in the beginning of the next phase. If you want
- * to explicitly cause some layout to be considered in an ongoing layout
- * phase, you should use {@link #setNeedsMeasure(ComponentConnector)}
- * instead.
- *
- * @param layout
- * the managed layout that should be layouted
- */
- public final void setNeedsHorizontalLayout(ManagedLayout layout) {
- if (isLayoutRunning()) {
- getLogger().warning(
- "setNeedsHorizontalLayout should not be run while a layout phase is in progress.");
- }
- needsHorizontalLayout.add(layout.getConnectorId());
- }
-
- /**
- * Marks that a ManagedLayout should be layouted vertically in the next
- * layout phase even if none of the elements managed by the layout have been
- * resized vertically.
- * <p>
- * For SimpleManagedLayout which is always layouted in both directions, this
- * has the same effect as {@link #setNeedsLayout(ManagedLayout)}.
- * <p>
- * This method should not be invoked during a layout phase since it only
- * controls what will happen in the beginning of the next phase. If you want
- * to explicitly cause some layout to be considered in an ongoing layout
- * phase, you should use {@link #setNeedsMeasure(ComponentConnector)}
- * instead.
- *
- * @param layout
- * the managed layout that should be layouted
- */
- public final void setNeedsVerticalLayout(ManagedLayout layout) {
- if (isLayoutRunning()) {
- getLogger().warning(
- "setNeedsVerticalLayout should not be run while a layout phase is in progress.");
- }
- needsVerticalLayout.add(layout.getConnectorId());
- }
-
- /**
- * Gets the outer height (including margins, paddings and borders) of the
- * given element, provided that it has been measured. These elements are
- * guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * -1 is returned if the element has not been measured. If 0 is returned, it
- * might indicate that the element is not attached to the DOM.
- * <p>
- * The value returned by this method is always rounded up. To get the exact
- * outer width, use {@link #getOuterHeightDouble(Element)}
- *
- * @param element
- * the element to get the measured size for
- * @return the measured outer height (including margins, paddings and
- * borders) of the element in pixels.
- */
- public final int getOuterHeight(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return (int) Math
- .ceil(getMeasuredSize(element, nullSize).getOuterHeight());
- }
-
- /**
- * Gets the outer height (including margins, paddings and borders) of the
- * given element, provided that it has been measured. These elements are
- * guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * -1 is returned if the element has not been measured. If 0 is returned, it
- * might indicate that the element is not attached to the DOM.
- *
- * @since 7.5.1
- * @param element
- * the element to get the measured size for
- * @return the measured outer height (including margins, paddings and
- * borders) of the element in pixels.
- */
- public final double getOuterHeightDouble(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getOuterHeight();
- }
-
- /**
- * Gets the outer width (including margins, paddings and borders) of the
- * given element, provided that it has been measured. These elements are
- * guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * -1 is returned if the element has not been measured. If 0 is returned, it
- * might indicate that the element is not attached to the DOM.
- * <p>
- * The value returned by this method is always rounded up. To get the exact
- * outer width, use {@link #getOuterWidthDouble(Element)}
- *
- * @since 7.5.1
- * @param element
- * the element to get the measured size for
- * @return the measured outer width (including margins, paddings and
- * borders) of the element in pixels.
- */
- public final int getOuterWidth(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return (int) Math
- .ceil(getMeasuredSize(element, nullSize).getOuterWidth());
- }
-
- /**
- * Gets the outer width (including margins, paddings and borders) of the
- * given element, provided that it has been measured. These elements are
- * guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * -1 is returned if the element has not been measured. If 0 is returned, it
- * might indicate that the element is not attached to the DOM.
- *
- * @param element
- * the element to get the measured size for
- * @return the measured outer width (including margins, paddings and
- * borders) of the element in pixels.
- */
- public final double getOuterWidthDouble(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getOuterWidth();
- }
-
- /**
- * Gets the inner height (excluding margins, paddings and borders) of the
- * given element, provided that it has been measured. These elements are
- * guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * -1 is returned if the element has not been measured. If 0 is returned, it
- * might indicate that the element is not attached to the DOM.
- * <p>
- * The value returned by this method is always rounded up. To get the exact
- * outer width, use {@link #getInnerHeightDouble(Element)}
- *
- * @param element
- * the element to get the measured size for
- * @return the measured inner height (excluding margins, paddings and
- * borders) of the element in pixels.
- */
- public final int getInnerHeight(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return (int) Math
- .ceil(getMeasuredSize(element, nullSize).getInnerHeight());
- }
-
- /**
- * Gets the inner height (excluding margins, paddings and borders) of the
- * given element, provided that it has been measured. These elements are
- * guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * -1 is returned if the element has not been measured. If 0 is returned, it
- * might indicate that the element is not attached to the DOM.
- *
- * @since 7.5.1
- * @param element
- * the element to get the measured size for
- * @return the measured inner height (excluding margins, paddings and
- * borders) of the element in pixels.
- */
- public final double getInnerHeightDouble(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getInnerHeight();
- }
-
- /**
- * Gets the inner width (excluding margins, paddings and borders) of the
- * given element, provided that it has been measured. These elements are
- * guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * -1 is returned if the element has not been measured. If 0 is returned, it
- * might indicate that the element is not attached to the DOM.
- * <p>
- * The value returned by this method is always rounded up. To get the exact
- * outer width, use {@link #getOuterHeightDouble(Element)}
- *
- * @param element
- * the element to get the measured size for
- * @return the measured inner width (excluding margins, paddings and
- * borders) of the element in pixels.
- */
- public final int getInnerWidth(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return (int) Math
- .ceil(getMeasuredSize(element, nullSize).getInnerWidth());
- }
-
- /**
- * Gets the inner width (excluding margins, paddings and borders) of the
- * given element, provided that it has been measured. These elements are
- * guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * -1 is returned if the element has not been measured. If 0 is returned, it
- * might indicate that the element is not attached to the DOM.
- *
- * @since 7.5.1
- * @param element
- * the element to get the measured size for
- * @return the measured inner width (excluding margins, paddings and
- * borders) of the element in pixels.
- */
- public final double getInnerWidthDouble(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getInnerWidth();
- }
-
- /**
- * Gets the border height (top border + bottom border) of the given element,
- * provided that it has been measured. These elements are guaranteed to be
- * measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured size for
- * @return the measured border height (top border + bottom border) of the
- * element in pixels.
- */
- public final int getBorderHeight(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getBorderHeight();
- }
-
- /**
- * Gets the padding height (top padding + bottom padding) of the given
- * element, provided that it has been measured. These elements are
- * guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured size for
- * @return the measured padding height (top padding + bottom padding) of the
- * element in pixels.
- */
- public int getPaddingHeight(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getPaddingHeight();
- }
-
- /**
- * Gets the border width (left border + right border) of the given element,
- * provided that it has been measured. These elements are guaranteed to be
- * measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured size for
- * @return the measured border width (left border + right border) of the
- * element in pixels.
- */
- public int getBorderWidth(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getBorderWidth();
- }
-
- /**
- * Gets the top border of the given element, provided that it has been
- * measured. These elements are guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured size for
- * @return the measured top border of the element in pixels.
- */
- public int getBorderTop(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getBorderTop();
- }
-
- /**
- * Gets the left border of the given element, provided that it has been
- * measured. These elements are guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured size for
- * @return the measured left border of the element in pixels.
- */
- public int getBorderLeft(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getBorderLeft();
- }
-
- /**
- * Gets the bottom border of the given element, provided that it has been
- * measured. These elements are guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured size for
- * @return the measured bottom border of the element in pixels.
- */
- public int getBorderBottom(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getBorderBottom();
- }
-
- /**
- * Gets the right border of the given element, provided that it has been
- * measured. These elements are guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured size for
- * @return the measured right border of the element in pixels.
- */
- public int getBorderRight(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getBorderRight();
- }
-
- /**
- * Gets the padding width (left padding + right padding) of the given
- * element, provided that it has been measured. These elements are
- * guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured size for
- * @return the measured padding width (left padding + right padding) of the
- * element in pixels.
- */
- public int getPaddingWidth(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getPaddingWidth();
- }
-
- /**
- * Gets the top padding of the given element, provided that it has been
- * measured. These elements are guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured size for
- * @return the measured top padding of the element in pixels.
- */
- public int getPaddingTop(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getPaddingTop();
- }
-
- /**
- * Gets the left padding of the given element, provided that it has been
- * measured. These elements are guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured size for
- * @return the measured left padding of the element in pixels.
- */
- public int getPaddingLeft(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getPaddingLeft();
- }
-
- /**
- * Gets the bottom padding of the given element, provided that it has been
- * measured. These elements are guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured size for
- * @return the measured bottom padding of the element in pixels.
- */
- public int getPaddingBottom(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getPaddingBottom();
- }
-
- /**
- * Gets the right padding of the given element, provided that it has been
- * measured. These elements are guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured size for
- * @return the measured right padding of the element in pixels.
- */
- public int getPaddingRight(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getPaddingRight();
- }
-
- /**
- * Gets the top margin of the given element, provided that it has been
- * measured. These elements are guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured size for
- * @return the measured top margin of the element in pixels.
- */
- public int getMarginTop(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getMarginTop();
- }
-
- /**
- * Gets the right margin of the given element, provided that it has been
- * measured. These elements are guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured size for
- * @return the measured right margin of the element in pixels.
- */
- public int getMarginRight(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getMarginRight();
- }
-
- /**
- * Gets the bottom margin of the given element, provided that it has been
- * measured. These elements are guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured size for
- * @return the measured bottom margin of the element in pixels.
- */
- public int getMarginBottom(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getMarginBottom();
- }
-
- /**
- * Gets the left margin of the given element, provided that it has been
- * measured. These elements are guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured size for
- * @return the measured left margin of the element in pixels.
- */
- public int getMarginLeft(Element element) {
- assert needsMeasure(
- element) : "Getting measurement for element that is not measured";
- return getMeasuredSize(element, nullSize).getMarginLeft();
- }
-
- /**
- * Gets the combined top & bottom margin of the given element, provided that
- * they have been measured. These elements are guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured margin for
- * @return the measured top+bottom margin of the element in pixels.
- */
- public int getMarginHeight(Element element) {
- return getMarginTop(element) + getMarginBottom(element);
- }
-
- /**
- * Gets the combined left & right margin of the given element, provided that
- * they have been measured. These elements are guaranteed to be measured:
- * <ul>
- * <li>ManagedLayouts and their child Connectors
- * <li>Elements for which there is at least one ElementResizeListener
- * <li>Elements for which at least one ManagedLayout has registered a
- * dependency
- * </ul>
- *
- * A negative number is returned if the element has not been measured. If 0
- * is returned, it might indicate that the element is not attached to the
- * DOM.
- *
- * @param element
- * the element to get the measured margin for
- * @return the measured left+right margin of the element in pixels.
- */
- public int getMarginWidth(Element element) {
- return getMarginLeft(element) + getMarginRight(element);
- }
-
- /**
- * Registers the outer height (including margins, borders and paddings) of a
- * component. This can be used as an optimization by ManagedLayouts; by
- * informing the LayoutManager about what size a component will have, the
- * layout propagation can continue directly without first measuring the
- * potentially resized elements.
- *
- * @param component
- * the component for which the size is reported
- * @param outerHeight
- * the new outer height (including margins, borders and paddings)
- * of the component in pixels
- */
- public void reportOuterHeight(ComponentConnector component,
- int outerHeight) {
- Element element = component.getWidget().getElement();
- MeasuredSize measuredSize = getMeasuredSize(element);
- if (isLayoutRunning()) {
- boolean heightChanged = measuredSize.setOuterHeight(outerHeight);
-
- if (heightChanged) {
- onConnectorChange(component, false, true);
- notifyListenersAndDepdendents(element, false, true);
- }
- currentDependencyTree.setNeedsVerticalMeasure(component, false);
- } else if (measuredSize.getOuterHeight() != outerHeight) {
- setNeedsMeasure(component);
- }
- }
-
- /**
- * Registers the height reserved for a relatively sized component. This can
- * be used as an optimization by ManagedLayouts; by informing the
- * LayoutManager about what size a component will have, the layout
- * propagation can continue directly without first measuring the potentially
- * resized elements.
- *
- * @param component
- * the relatively sized component for which the size is reported
- * @param assignedHeight
- * the inner height of the relatively sized component's parent
- * element in pixels
- */
- public void reportHeightAssignedToRelative(ComponentConnector component,
- int assignedHeight) {
- assert component.isRelativeHeight();
-
- float percentSize = parsePercent(
- component.getState().height == null ? ""
- : component.getState().height);
- int effectiveHeight = Math.round(assignedHeight * (percentSize / 100));
-
- reportOuterHeight(component, effectiveHeight);
- }
-
- /**
- * Registers the width reserved for a relatively sized component. This can
- * be used as an optimization by ManagedLayouts; by informing the
- * LayoutManager about what size a component will have, the layout
- * propagation can continue directly without first measuring the potentially
- * resized elements.
- *
- * @param component
- * the relatively sized component for which the size is reported
- * @param assignedWidth
- * the inner width of the relatively sized component's parent
- * element in pixels
- */
- public void reportWidthAssignedToRelative(ComponentConnector component,
- int assignedWidth) {
- assert component.isRelativeWidth();
-
- float percentSize = parsePercent(component.getState().width == null ? ""
- : component.getState().width);
- int effectiveWidth = Math.round(assignedWidth * (percentSize / 100));
-
- reportOuterWidth(component, effectiveWidth);
- }
-
- private static float parsePercent(String size) {
- return Float.parseFloat(size.substring(0, size.length() - 1));
- }
-
- /**
- * Registers the outer width (including margins, borders and paddings) of a
- * component. This can be used as an optimization by ManagedLayouts; by
- * informing the LayoutManager about what size a component will have, the
- * layout propagation can continue directly without first measuring the
- * potentially resized elements.
- *
- * @param component
- * the component for which the size is reported
- * @param outerWidth
- * the new outer width (including margins, borders and paddings)
- * of the component in pixels
- */
- public void reportOuterWidth(ComponentConnector component, int outerWidth) {
- Element element = component.getWidget().getElement();
- MeasuredSize measuredSize = getMeasuredSize(element);
- if (isLayoutRunning()) {
- boolean widthChanged = measuredSize.setOuterWidth(outerWidth);
-
- if (widthChanged) {
- onConnectorChange(component, true, false);
- notifyListenersAndDepdendents(element, true, false);
- }
- currentDependencyTree.setNeedsHorizontalMeasure(component, false);
- } else if (measuredSize.getOuterWidth() != outerWidth) {
- setNeedsMeasure(component);
- }
- }
-
- /**
- * Adds a listener that will be notified whenever the size of a specific
- * element changes. Adding a listener to an element also ensures that all
- * sizes for that element will be available starting from the next layout
- * phase.
- *
- * @param element
- * the element that should be checked for size changes
- * @param listener
- * an ElementResizeListener that will be informed whenever the
- * size of the target element has changed
- */
- public void addElementResizeListener(Element element,
- ElementResizeListener listener) {
- Collection<ElementResizeListener> listeners = elementResizeListeners
- .get(element);
- if (listeners == null) {
- listeners = new HashSet<>();
- elementResizeListeners.put(element, listeners);
- ensureMeasured(element);
- }
- listeners.add(listener);
- }
-
- /**
- * Removes an element resize listener from the provided element. This might
- * cause this LayoutManager to stop tracking the size of the element if no
- * other sources are interested in the size.
- *
- * @param element
- * the element to which the element resize listener was
- * previously added
- * @param listener
- * the ElementResizeListener that should no longer get informed
- * about size changes to the target element.
- */
- public void removeElementResizeListener(Element element,
- ElementResizeListener listener) {
- Collection<ElementResizeListener> listeners = elementResizeListeners
- .get(element);
- if (listeners != null) {
- listeners.remove(listener);
- if (listeners.isEmpty()) {
- elementResizeListeners.remove(element);
- stopMeasuringIfUnecessary(element);
- }
- }
- }
-
- private void stopMeasuringIfUnecessary(Element element) {
- if (!needsMeasure(element)) {
- measuredNonConnectorElements.remove(element);
- setMeasuredSize(element, null);
- }
- }
-
- /**
- * Informs this LayoutManager that the size of a component might have
- * changed. This method should be used whenever the size of an individual
- * component might have changed from outside of Vaadin's normal update
- * phase, e.g. when an icon has been loaded or when the user resizes some
- * part of the UI using the mouse.
- * <p>
- * To set an entire component hierarchy to be measured, use
- * {@link #setNeedsMeasureRecursively(ComponentConnector)} instead.
- * <p>
- * If there is no upcoming layout phase, a new layout phase is scheduled.
- *
- * @param component
- * the component whose size might have changed.
- */
- public void setNeedsMeasure(ComponentConnector component) {
- if (isLayoutRunning()) {
- currentDependencyTree.setNeedsMeasure(component, true);
- } else {
- needsMeasure.add(component.getConnectorId());
- layoutLater();
- }
- }
-
- /**
- * Informs this LayoutManager that some sizes in a component hierarchy might
- * have changed. This method should be used whenever the size of any child
- * component might have changed from outside of Vaadin's normal update
- * phase, e.g. when a CSS class name related to sizing has been changed.
- * <p>
- * To set a single component to be measured, use
- * {@link #setNeedsMeasure(ComponentConnector)} instead.
- * <p>
- * If there is no upcoming layout phase, a new layout phase is scheduled.
- *
- * @since 7.2
- * @param component
- * the component at the root of the component hierarchy to
- * measure
- */
- public void setNeedsMeasureRecursively(ComponentConnector component) {
- setNeedsMeasure(component);
-
- if (component instanceof HasComponentsConnector) {
- HasComponentsConnector hasComponents = (HasComponentsConnector) component;
- for (ComponentConnector child : hasComponents
- .getChildComponents()) {
- setNeedsMeasureRecursively(child);
- }
- }
- }
-
- public void setEverythingNeedsMeasure() {
- everythingNeedsMeasure = true;
- }
-
- private static Logger getLogger() {
- return Logger.getLogger(LayoutManager.class.getName());
- }
-
- /**
- * Checks if there is something waiting for a layout to take place.
- *
- * @since 7.5.6
- * @return true if there are connectors waiting for measurement or layout,
- * false otherwise
- */
- public boolean isLayoutNeeded() {
- if (!needsHorizontalLayout.isEmpty()
- || !needsVerticalLayout.isEmpty()) {
- return true;
- }
- if (!needsMeasure.isEmpty()) {
- return true;
- }
-
- if (everythingNeedsMeasure) {
- return true;
- }
-
- return false;
- }
- }
|