You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

LayoutManager.java 75KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872
  1. /*
  2. * Copyright 2000-2021 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client;
  17. import java.util.Collection;
  18. import java.util.HashMap;
  19. import java.util.HashSet;
  20. import java.util.Map;
  21. import java.util.Set;
  22. import java.util.logging.Level;
  23. import java.util.logging.Logger;
  24. import com.google.gwt.core.client.Duration;
  25. import com.google.gwt.core.client.JsArrayString;
  26. import com.google.gwt.dom.client.Element;
  27. import com.google.gwt.dom.client.Style;
  28. import com.google.gwt.dom.client.Style.Overflow;
  29. import com.google.gwt.user.client.Timer;
  30. import com.vaadin.client.MeasuredSize.MeasureResult;
  31. import com.vaadin.client.ui.ManagedLayout;
  32. import com.vaadin.client.ui.PostLayoutListener;
  33. import com.vaadin.client.ui.SimpleManagedLayout;
  34. import com.vaadin.client.ui.VNotification;
  35. import com.vaadin.client.ui.layout.ElementResizeEvent;
  36. import com.vaadin.client.ui.layout.ElementResizeListener;
  37. import com.vaadin.client.ui.layout.LayoutDependencyTree;
  38. public class LayoutManager {
  39. private static final String STATE_CHANGE_MESSAGE = "Cannot run layout while processing state change from the server.";
  40. private static final String LOOP_ABORT_MESSAGE = "Aborting layout after 100 passes. This would probably be an infinite loop.";
  41. private static final boolean DEBUG_LOGGING = false;
  42. private ApplicationConnection connection;
  43. private final Set<Element> measuredNonConnectorElements = new HashSet<>();
  44. private final MeasuredSize nullSize = new MeasuredSize();
  45. private LayoutDependencyTree currentDependencyTree;
  46. private FastStringSet needsHorizontalLayout = FastStringSet.create();
  47. private FastStringSet needsVerticalLayout = FastStringSet.create();
  48. private FastStringSet needsMeasure = FastStringSet.create();
  49. private FastStringSet pendingOverflowFixes = FastStringSet.create();
  50. private final Map<Element, Collection<ElementResizeListener>> elementResizeListeners = new HashMap<>();
  51. private final Set<Element> listenersToFire = new HashSet<>();
  52. private boolean layoutPending = false;
  53. private Timer layoutTimer = new Timer() {
  54. @Override
  55. public void run() {
  56. layoutNow();
  57. }
  58. };
  59. private boolean everythingNeedsMeasure = false;
  60. private boolean thoroughSizeCheck = true;
  61. /**
  62. * Sets the application connection this instance is connected to. Called
  63. * internally by the framework.
  64. *
  65. * @param connection
  66. * the application connection this instance is connected to
  67. */
  68. public void setConnection(ApplicationConnection connection) {
  69. if (this.connection != null) {
  70. throw new RuntimeException(
  71. "LayoutManager connection can never be changed");
  72. }
  73. this.connection = connection;
  74. }
  75. /**
  76. * Set whether the measuring should use a thorough size check that evaluates
  77. * the presence of the element and uses calculated size, or default to a
  78. * slightly faster check that can result in incorrect size information if
  79. * the check is triggered while a transform animation is ongoing. This can
  80. * happen e.g. when a PopupView is opened.
  81. * <p>
  82. * By default, the thorough size check is enabled.
  83. *
  84. * @param thoroughSizeCheck
  85. * {@code true} if thorough size check enabled, {@code false} if
  86. * not
  87. * @since
  88. */
  89. public void setThoroughSizeChck(boolean thoroughSizeCheck) {
  90. this.thoroughSizeCheck = thoroughSizeCheck;
  91. }
  92. /**
  93. * Returns the application connection for this layout manager.
  94. *
  95. * @return connection
  96. */
  97. protected ApplicationConnection getConnection() {
  98. return connection;
  99. }
  100. /**
  101. * Gets the layout manager associated with the given
  102. * {@link ApplicationConnection}.
  103. *
  104. * @param connection
  105. * the application connection to get a layout manager for
  106. * @return the layout manager associated with the provided application
  107. * connection
  108. */
  109. public static LayoutManager get(ApplicationConnection connection) {
  110. return connection.getLayoutManager();
  111. }
  112. /**
  113. * Registers that a ManagedLayout is depending on the size of an Element.
  114. * This causes this layout manager to measure the element in the beginning
  115. * of every layout phase and call the appropriate layout method of the
  116. * managed layout if the size of the element has changed.
  117. *
  118. * @param owner
  119. * the ManagedLayout that depends on an element
  120. * @param element
  121. * the Element that should be measured
  122. */
  123. public void registerDependency(ManagedLayout owner, Element element) {
  124. MeasuredSize measuredSize = ensureMeasured(element);
  125. setNeedsLayout(owner);
  126. measuredSize.addDependent(owner.getConnectorId());
  127. }
  128. private MeasuredSize ensureMeasured(Element element) {
  129. MeasuredSize measuredSize = getMeasuredSize(element, null);
  130. if (measuredSize == null) {
  131. measuredSize = new MeasuredSize();
  132. if (ConnectorMap.get(connection).getConnector(element) == null) {
  133. measuredNonConnectorElements.add(element);
  134. }
  135. setMeasuredSize(element, measuredSize);
  136. }
  137. return measuredSize;
  138. }
  139. private boolean needsMeasure(Element e) {
  140. ComponentConnector connector = connection.getConnectorMap()
  141. .getConnector(e);
  142. if (connector != null && needsMeasureForManagedLayout(connector)) {
  143. return true;
  144. } else if (elementResizeListeners.containsKey(e)) {
  145. return true;
  146. }
  147. return getMeasuredSize(e, nullSize).hasDependents();
  148. }
  149. private boolean needsMeasureForManagedLayout(ComponentConnector connector) {
  150. if (connector instanceof ManagedLayout) {
  151. return true;
  152. }
  153. return connector.getParent() instanceof ManagedLayout;
  154. }
  155. /**
  156. * Assigns a measured size to an element. Method defined as protected for
  157. * legacy reasons.
  158. *
  159. * @param element
  160. * the dom element to attach the measured size to
  161. * @param measuredSize
  162. * the measured size to attach to the element. If
  163. * <code>null</code>, any previous measured size is removed.
  164. */
  165. protected native void setMeasuredSize(Element element,
  166. MeasuredSize measuredSize)
  167. /*-{
  168. if (measuredSize) {
  169. element.vMeasuredSize = measuredSize;
  170. } else {
  171. delete element.vMeasuredSize;
  172. }
  173. }-*/;
  174. /**
  175. * Gets the measured size for an element. Method defined as protected for
  176. * legacy reasons.
  177. *
  178. * @param element
  179. * The element to get measured size for
  180. * @param defaultSize
  181. * The size to return if no measured size could be found
  182. * @return The measured size for the element or {@literal defaultSize}
  183. */
  184. protected native MeasuredSize getMeasuredSize(Element element,
  185. MeasuredSize defaultSize)
  186. /*-{
  187. return element.vMeasuredSize || defaultSize;
  188. }-*/;
  189. private final MeasuredSize getMeasuredSize(Element element) {
  190. MeasuredSize measuredSize = getMeasuredSize(element, null);
  191. if (measuredSize == null) {
  192. measuredSize = new MeasuredSize();
  193. setMeasuredSize(element, measuredSize);
  194. }
  195. return measuredSize;
  196. }
  197. /**
  198. * Registers that a ManagedLayout is no longer depending on the size of an
  199. * Element.
  200. *
  201. * @see #registerDependency(ManagedLayout, Element)
  202. *
  203. * @param owner
  204. * the ManagedLayout no longer depends on an element
  205. * @param element
  206. * the Element that that no longer needs to be measured
  207. */
  208. public void unregisterDependency(ManagedLayout owner, Element element) {
  209. MeasuredSize measuredSize = getMeasuredSize(element, null);
  210. if (measuredSize == null) {
  211. return;
  212. }
  213. measuredSize.removeDependent(owner.getConnectorId());
  214. stopMeasuringIfUnecessary(element);
  215. }
  216. public boolean isLayoutRunning() {
  217. return currentDependencyTree != null;
  218. }
  219. private void countLayout(FastStringMap<Integer> layoutCounts,
  220. ManagedLayout layout) {
  221. Integer count = layoutCounts.get(layout.getConnectorId());
  222. if (count == null) {
  223. count = Integer.valueOf(0);
  224. } else {
  225. count = Integer.valueOf(count.intValue() + 1);
  226. }
  227. layoutCounts.put(layout.getConnectorId(), count);
  228. if (count.intValue() > 2) {
  229. getLogger().severe(Util.getConnectorString(layout)
  230. + " has been layouted " + count.intValue() + " times");
  231. }
  232. }
  233. public void layoutLater() {
  234. if (!layoutPending) {
  235. layoutPending = true;
  236. layoutTimer.schedule(100);
  237. }
  238. }
  239. public void layoutNow() {
  240. if (isLayoutRunning()) {
  241. throw new IllegalStateException(
  242. "Can't start a new layout phase before the previous layout phase ends.");
  243. }
  244. if (connection.getMessageHandler().isUpdatingState()) {
  245. // If assertions are enabled, throw an exception
  246. assert false : STATE_CHANGE_MESSAGE;
  247. // Else just log a warning and postpone the layout
  248. getLogger().warning(STATE_CHANGE_MESSAGE);
  249. // Framework will call layoutNow when the state update is completed
  250. return;
  251. }
  252. layoutPending = false;
  253. layoutTimer.cancel();
  254. try {
  255. currentDependencyTree = new LayoutDependencyTree(connection);
  256. doLayout();
  257. } finally {
  258. currentDependencyTree = null;
  259. }
  260. }
  261. /**
  262. * Called once per iteration in the layout loop before size calculations so
  263. * different browsers quirks can be handled. Mainly this exists for legacy
  264. * reasons.
  265. */
  266. protected void performBrowserLayoutHacks() {
  267. // Permutations implement this
  268. }
  269. private void doLayout() {
  270. getLogger().info("Starting layout phase");
  271. Profiler.enter("LayoutManager phase init");
  272. FastStringMap<Integer> layoutCounts = FastStringMap.create();
  273. int passes = 0;
  274. Duration totalDuration = new Duration();
  275. ConnectorMap connectorMap = ConnectorMap.get(connection);
  276. JsArrayString dump = needsHorizontalLayout.dump();
  277. int dumpLength = dump.length();
  278. for (int i = 0; i < dumpLength; i++) {
  279. String layoutId = dump.get(i);
  280. currentDependencyTree.setNeedsHorizontalLayout(layoutId, true);
  281. }
  282. dump = needsVerticalLayout.dump();
  283. dumpLength = dump.length();
  284. for (int i = 0; i < dumpLength; i++) {
  285. String layoutId = dump.get(i);
  286. currentDependencyTree.setNeedsVerticalLayout(layoutId, true);
  287. }
  288. needsHorizontalLayout = FastStringSet.create();
  289. needsVerticalLayout = FastStringSet.create();
  290. dump = needsMeasure.dump();
  291. dumpLength = dump.length();
  292. for (int i = 0; i < dumpLength; i++) {
  293. ServerConnector connector = connectorMap.getConnector(dump.get(i));
  294. if (connector != null) {
  295. currentDependencyTree
  296. .setNeedsMeasure((ComponentConnector) connector, true);
  297. }
  298. }
  299. needsMeasure = FastStringSet.create();
  300. measureNonConnectors();
  301. Profiler.leave("LayoutManager phase init");
  302. while (true) {
  303. Profiler.enter("Layout pass");
  304. passes++;
  305. performBrowserLayoutHacks();
  306. Profiler.enter("Layout measure connectors");
  307. int measuredConnectorCount = measureConnectors(
  308. currentDependencyTree, everythingNeedsMeasure);
  309. Profiler.leave("Layout measure connectors");
  310. everythingNeedsMeasure = false;
  311. if (measuredConnectorCount == 0) {
  312. getLogger().info("No more changes in pass " + passes);
  313. Profiler.leave("Layout pass");
  314. break;
  315. }
  316. int firedListeners = 0;
  317. if (!listenersToFire.isEmpty()) {
  318. HashSet<Element> listenersCopy = new HashSet<Element>(
  319. listenersToFire);
  320. listenersToFire.clear();
  321. firedListeners = listenersToFire.size();
  322. Profiler.enter("Layout fire resize events");
  323. for (Element element : listenersCopy) {
  324. Collection<ElementResizeListener> listeners = elementResizeListeners
  325. .get(element);
  326. if (listeners != null) {
  327. Profiler.enter(
  328. "Layout fire resize events - listeners not null");
  329. Profiler.enter(
  330. "ElementResizeListener.onElementResize copy list");
  331. ElementResizeListener[] array = listeners.toArray(
  332. new ElementResizeListener[listeners.size()]);
  333. Profiler.leave(
  334. "ElementResizeListener.onElementResize copy list");
  335. ElementResizeEvent event = new ElementResizeEvent(this,
  336. element);
  337. for (ElementResizeListener listener : array) {
  338. try {
  339. String key = null;
  340. if (Profiler.isEnabled()) {
  341. Profiler.enter(
  342. "ElementResizeListener.onElementResize construct profiler key");
  343. key = "ElementResizeListener.onElementResize for "
  344. + listener.getClass()
  345. .getSimpleName();
  346. Profiler.leave(
  347. "ElementResizeListener.onElementResize construct profiler key");
  348. Profiler.enter(key);
  349. }
  350. listener.onElementResize(event);
  351. if (Profiler.isEnabled()) {
  352. Profiler.leave(key);
  353. }
  354. } catch (RuntimeException e) {
  355. getLogger().log(Level.SEVERE,
  356. "Error in resize listener", e);
  357. }
  358. }
  359. Profiler.leave(
  360. "Layout fire resize events - listeners not null");
  361. }
  362. }
  363. Profiler.leave("Layout fire resize events");
  364. }
  365. Profiler.enter("LayoutManager handle ManagedLayout");
  366. FastStringSet updatedSet = FastStringSet.create();
  367. int layoutCount = 0;
  368. while (currentDependencyTree.hasHorizontalConnectorToLayout()
  369. || currentDependencyTree.hasVerticaConnectorToLayout()) {
  370. JsArrayString layoutTargets = currentDependencyTree
  371. .getHorizontalLayoutTargetsJsArray();
  372. int length = layoutTargets.length();
  373. for (int i = 0; i < length; i++) {
  374. ManagedLayout layout = (ManagedLayout) connectorMap
  375. .getConnector(layoutTargets.get(i));
  376. if (layout instanceof DirectionalManagedLayout) {
  377. currentDependencyTree
  378. .markAsHorizontallyLayouted(layout);
  379. DirectionalManagedLayout cl = (DirectionalManagedLayout) layout;
  380. try {
  381. String key = null;
  382. if (Profiler.isEnabled()) {
  383. key = "layoutHorizontally() for "
  384. + cl.getClass().getSimpleName();
  385. Profiler.enter(key);
  386. }
  387. cl.layoutHorizontally();
  388. layoutCount++;
  389. if (Profiler.isEnabled()) {
  390. Profiler.leave(key);
  391. }
  392. } catch (RuntimeException e) {
  393. getLogger().log(Level.SEVERE,
  394. "Error in ManagedLayout handling", e);
  395. }
  396. countLayout(layoutCounts, cl);
  397. } else {
  398. currentDependencyTree
  399. .markAsHorizontallyLayouted(layout);
  400. currentDependencyTree.markAsVerticallyLayouted(layout);
  401. SimpleManagedLayout rr = (SimpleManagedLayout) layout;
  402. try {
  403. String key = null;
  404. if (Profiler.isEnabled()) {
  405. key = "layout() for "
  406. + rr.getClass().getSimpleName();
  407. Profiler.enter(key);
  408. }
  409. rr.layout();
  410. layoutCount++;
  411. if (Profiler.isEnabled()) {
  412. Profiler.leave(key);
  413. }
  414. } catch (RuntimeException e) {
  415. getLogger().log(Level.SEVERE,
  416. "Error in SimpleManagedLayout (horizontal) handling",
  417. e);
  418. }
  419. countLayout(layoutCounts, rr);
  420. }
  421. if (DEBUG_LOGGING) {
  422. updatedSet.add(layout.getConnectorId());
  423. }
  424. }
  425. layoutTargets = currentDependencyTree
  426. .getVerticalLayoutTargetsJsArray();
  427. length = layoutTargets.length();
  428. for (int i = 0; i < length; i++) {
  429. ManagedLayout layout = (ManagedLayout) connectorMap
  430. .getConnector(layoutTargets.get(i));
  431. if (layout instanceof DirectionalManagedLayout) {
  432. currentDependencyTree.markAsVerticallyLayouted(layout);
  433. DirectionalManagedLayout cl = (DirectionalManagedLayout) layout;
  434. try {
  435. String key = null;
  436. if (Profiler.isEnabled()) {
  437. key = "layoutVertically() for "
  438. + cl.getClass().getSimpleName();
  439. Profiler.enter(key);
  440. }
  441. cl.layoutVertically();
  442. layoutCount++;
  443. if (Profiler.isEnabled()) {
  444. Profiler.leave(key);
  445. }
  446. } catch (RuntimeException e) {
  447. getLogger().log(Level.SEVERE,
  448. "Error in DirectionalManagedLayout handling",
  449. e);
  450. }
  451. countLayout(layoutCounts, cl);
  452. } else {
  453. currentDependencyTree
  454. .markAsHorizontallyLayouted(layout);
  455. currentDependencyTree.markAsVerticallyLayouted(layout);
  456. SimpleManagedLayout rr = (SimpleManagedLayout) layout;
  457. try {
  458. String key = null;
  459. if (Profiler.isEnabled()) {
  460. key = "layout() for "
  461. + rr.getClass().getSimpleName();
  462. Profiler.enter(key);
  463. }
  464. rr.layout();
  465. layoutCount++;
  466. if (Profiler.isEnabled()) {
  467. Profiler.leave(key);
  468. }
  469. } catch (RuntimeException e) {
  470. getLogger().log(Level.SEVERE,
  471. "Error in SimpleManagedLayout (vertical) handling",
  472. e);
  473. }
  474. countLayout(layoutCounts, rr);
  475. }
  476. if (DEBUG_LOGGING) {
  477. updatedSet.add(layout.getConnectorId());
  478. }
  479. }
  480. }
  481. Profiler.leave("LayoutManager handle ManagedLayout");
  482. if (DEBUG_LOGGING) {
  483. JsArrayString changedCids = updatedSet.dump();
  484. StringBuilder b = new StringBuilder(" ");
  485. b.append(changedCids.length());
  486. b.append(" requestLayout invocations ");
  487. if (changedCids.length() < 30) {
  488. for (int i = 0; i < changedCids.length(); i++) {
  489. if (i != 0) {
  490. b.append(", ");
  491. } else {
  492. b.append(": ");
  493. }
  494. String connectorString = changedCids.get(i);
  495. if (changedCids.length() < 10) {
  496. ServerConnector connector = ConnectorMap
  497. .get(connection)
  498. .getConnector(connectorString);
  499. connectorString = Util
  500. .getConnectorString(connector);
  501. }
  502. b.append(connectorString);
  503. }
  504. }
  505. getLogger().info(b.toString());
  506. }
  507. Profiler.leave("Layout pass");
  508. getLogger().info("Pass " + passes + " measured "
  509. + measuredConnectorCount + " elements, fired "
  510. + firedListeners + " listeners and did " + layoutCount
  511. + " layouts.");
  512. if (passes > 100) {
  513. getLogger().severe(LOOP_ABORT_MESSAGE);
  514. if (ApplicationConfiguration.isDebugMode()) {
  515. VNotification
  516. .createNotification(VNotification.DELAY_FOREVER,
  517. connection.getUIConnector().getWidget())
  518. .show(LOOP_ABORT_MESSAGE, VNotification.CENTERED,
  519. "error");
  520. }
  521. break;
  522. }
  523. }
  524. Profiler.enter("layout PostLayoutListener");
  525. JsArrayObject<ComponentConnector> componentConnectors = connectorMap
  526. .getComponentConnectorsAsJsArray();
  527. int size = componentConnectors.size();
  528. for (int i = 0; i < size; i++) {
  529. ComponentConnector connector = componentConnectors.get(i);
  530. if (connector instanceof PostLayoutListener) {
  531. String key = null;
  532. if (Profiler.isEnabled()) {
  533. key = "layout PostLayoutListener for "
  534. + connector.getClass().getSimpleName();
  535. Profiler.enter(key);
  536. }
  537. ((PostLayoutListener) connector).postLayout();
  538. if (Profiler.isEnabled()) {
  539. Profiler.leave(key);
  540. }
  541. }
  542. }
  543. Profiler.leave("layout PostLayoutListener");
  544. // Ensure temporary variables are cleaned
  545. if (!pendingOverflowFixes.isEmpty()) {
  546. getLogger().warning(
  547. "pendingOverflowFixes is not empty at the end of doLayout: "
  548. + pendingOverflowFixes.dump());
  549. pendingOverflowFixes = FastStringSet.create();
  550. }
  551. getLogger().info("Total layout phase time: "
  552. + totalDuration.elapsedMillis() + "ms");
  553. }
  554. private void logConnectorStatus(int connectorId) {
  555. currentDependencyTree.logDependencyStatus(
  556. (ComponentConnector) ConnectorMap.get(connection)
  557. .getConnector(Integer.toString(connectorId)));
  558. }
  559. private int measureConnectors(LayoutDependencyTree layoutDependencyTree,
  560. boolean measureAll) {
  561. Profiler.enter("Layout overflow fix handling");
  562. JsArrayString pendingOverflowConnectorsIds = pendingOverflowFixes
  563. .dump();
  564. int pendingOverflowCount = pendingOverflowConnectorsIds.length();
  565. ConnectorMap connectorMap = ConnectorMap.get(connection);
  566. if (pendingOverflowCount > 0) {
  567. Map<Element, String> originalOverflows = new HashMap<>();
  568. FastStringSet delayedOverflowFixes = FastStringSet.create();
  569. // First set overflow to hidden (and save previous value so it can
  570. // be restored later)
  571. for (int i = 0; i < pendingOverflowCount; i++) {
  572. String connectorId = pendingOverflowConnectorsIds.get(i);
  573. ComponentConnector componentConnector = (ComponentConnector) connectorMap
  574. .getConnector(connectorId);
  575. if (delayOverflowFix(componentConnector)) {
  576. delayedOverflowFixes.add(connectorId);
  577. continue;
  578. }
  579. if (DEBUG_LOGGING) {
  580. getLogger().info("Doing overflow fix for "
  581. + Util.getConnectorString(componentConnector)
  582. + " in " + Util.getConnectorString(
  583. componentConnector.getParent()));
  584. }
  585. Profiler.enter("Overflow fix apply");
  586. Element parentElement = componentConnector.getWidget()
  587. .getElement().getParentElement();
  588. Style style = parentElement.getStyle();
  589. String originalOverflow = style.getOverflow();
  590. if (originalOverflow != null
  591. && !originalOverflows.containsKey(parentElement)) {
  592. // Store original value for restore, but only the first time
  593. // the value is changed
  594. originalOverflows.put(parentElement, originalOverflow);
  595. }
  596. style.setOverflow(Overflow.HIDDEN);
  597. Profiler.leave("Overflow fix apply");
  598. }
  599. pendingOverflowFixes.removeAll(delayedOverflowFixes);
  600. JsArrayString remainingOverflowFixIds = pendingOverflowFixes.dump();
  601. int remainingCount = remainingOverflowFixIds.length();
  602. Profiler.enter("Overflow fix reflow");
  603. // Then ensure all scrolling elements are reflowed by measuring
  604. for (int i = 0; i < remainingCount; i++) {
  605. ComponentConnector componentConnector = (ComponentConnector) connectorMap
  606. .getConnector(remainingOverflowFixIds.get(i));
  607. componentConnector.getWidget().getElement().getParentElement()
  608. .getOffsetHeight();
  609. }
  610. Profiler.leave("Overflow fix reflow");
  611. Profiler.enter("Overflow fix restore");
  612. // Finally restore old overflow value and update bookkeeping
  613. for (int i = 0; i < remainingCount; i++) {
  614. String connectorId = remainingOverflowFixIds.get(i);
  615. ComponentConnector componentConnector = (ComponentConnector) connectorMap
  616. .getConnector(connectorId);
  617. Element parentElement = componentConnector.getWidget()
  618. .getElement().getParentElement();
  619. parentElement.getStyle().setProperty("overflow",
  620. originalOverflows.get(parentElement));
  621. layoutDependencyTree.setNeedsMeasure(componentConnector, true);
  622. }
  623. Profiler.leave("Overflow fix restore");
  624. if (!pendingOverflowFixes.isEmpty()) {
  625. getLogger().info(
  626. "Did overflow fix for " + remainingCount + " elements");
  627. }
  628. pendingOverflowFixes = delayedOverflowFixes;
  629. }
  630. Profiler.leave("Layout overflow fix handling");
  631. int measureCount = 0;
  632. if (measureAll) {
  633. Profiler.enter("Layout measureAll");
  634. JsArrayObject<ComponentConnector> allConnectors = connectorMap
  635. .getComponentConnectorsAsJsArray();
  636. int size = allConnectors.size();
  637. // Find connectors that should actually be measured
  638. JsArrayObject<ComponentConnector> connectors = JsArrayObject
  639. .createArray().cast();
  640. for (int i = 0; i < size; i++) {
  641. ComponentConnector candidate = allConnectors.get(i);
  642. if (!Util.shouldSkipMeasurementOfConnector(candidate)
  643. && needsMeasure(candidate.getWidget().getElement())) {
  644. connectors.add(candidate);
  645. }
  646. }
  647. int connectorCount = connectors.size();
  648. for (int i = 0; i < connectorCount; i++) {
  649. measureConnector(connectors.get(i));
  650. }
  651. for (int i = 0; i < connectorCount; i++) {
  652. layoutDependencyTree.setNeedsMeasure(connectors.get(i), false);
  653. }
  654. measureCount += connectorCount;
  655. Profiler.leave("Layout measureAll");
  656. }
  657. Profiler.enter("Layout measure from tree");
  658. while (layoutDependencyTree.hasConnectorsToMeasure()) {
  659. JsArrayString measureTargets = layoutDependencyTree
  660. .getMeasureTargetsJsArray();
  661. int length = measureTargets.length();
  662. for (int i = 0; i < length; i++) {
  663. ComponentConnector connector = (ComponentConnector) connectorMap
  664. .getConnector(measureTargets.get(i));
  665. measureConnector(connector);
  666. measureCount++;
  667. }
  668. for (int i = 0; i < length; i++) {
  669. ComponentConnector connector = (ComponentConnector) connectorMap
  670. .getConnector(measureTargets.get(i));
  671. layoutDependencyTree.setNeedsMeasure(connector, false);
  672. }
  673. }
  674. Profiler.leave("Layout measure from tree");
  675. return measureCount;
  676. }
  677. /*
  678. * Delay the overflow fix if the involved connectors might still change
  679. */
  680. private boolean delayOverflowFix(ComponentConnector componentConnector) {
  681. if (!currentDependencyTree.noMoreChangesExpected(componentConnector)) {
  682. return true;
  683. }
  684. ServerConnector parent = componentConnector.getParent();
  685. if (parent instanceof ComponentConnector && !currentDependencyTree
  686. .noMoreChangesExpected((ComponentConnector) parent)) {
  687. return true;
  688. }
  689. return false;
  690. }
  691. private void measureConnector(ComponentConnector connector) {
  692. Profiler.enter("LayoutManager.measureConnector");
  693. Element element = connector.getWidget().getElement();
  694. MeasuredSize measuredSize = getMeasuredSize(element);
  695. MeasureResult measureResult = measuredAndUpdate(element, measuredSize);
  696. if (measureResult.isChanged()) {
  697. onConnectorChange(connector, measureResult.isWidthChanged(),
  698. measureResult.isHeightChanged());
  699. }
  700. Profiler.leave("LayoutManager.measureConnector");
  701. }
  702. private void onConnectorChange(ComponentConnector connector,
  703. boolean widthChanged, boolean heightChanged) {
  704. Profiler.enter("LayoutManager.onConnectorChange");
  705. Profiler.enter("LayoutManager.onConnectorChange setNeedsOverflowFix");
  706. setNeedsOverflowFix(connector);
  707. Profiler.leave("LayoutManager.onConnectorChange setNeedsOverflowFix");
  708. Profiler.enter("LayoutManager.onConnectorChange heightChanged");
  709. if (heightChanged) {
  710. currentDependencyTree.markHeightAsChanged(connector);
  711. }
  712. Profiler.leave("LayoutManager.onConnectorChange heightChanged");
  713. Profiler.enter("LayoutManager.onConnectorChange widthChanged");
  714. if (widthChanged) {
  715. currentDependencyTree.markWidthAsChanged(connector);
  716. }
  717. Profiler.leave("LayoutManager.onConnectorChange widthChanged");
  718. Profiler.leave("LayoutManager.onConnectorChange");
  719. }
  720. private void setNeedsOverflowFix(ComponentConnector connector) {
  721. // IE9 doesn't need the original fix, but for some reason it needs this
  722. if (BrowserInfo.get().requiresOverflowAutoFix()) {
  723. ComponentConnector scrollingBoundary = currentDependencyTree
  724. .getScrollingBoundary(connector);
  725. if (scrollingBoundary != null) {
  726. pendingOverflowFixes.add(scrollingBoundary.getConnectorId());
  727. }
  728. }
  729. }
  730. private void measureNonConnectors() {
  731. Profiler.enter("LayoutManager.measureNonConenctors");
  732. for (Element element : measuredNonConnectorElements) {
  733. measuredAndUpdate(element, getMeasuredSize(element, null));
  734. }
  735. Profiler.leave("LayoutManager.measureNonConenctors");
  736. getLogger().info("Measured " + measuredNonConnectorElements.size()
  737. + " non connector elements");
  738. }
  739. private MeasureResult measuredAndUpdate(Element element,
  740. MeasuredSize measuredSize) {
  741. MeasureResult measureResult = measuredSize.measure(element,
  742. thoroughSizeCheck);
  743. if (measureResult.isChanged()) {
  744. notifyListenersAndDepdendents(element,
  745. measureResult.isWidthChanged(),
  746. measureResult.isHeightChanged());
  747. }
  748. return measureResult;
  749. }
  750. private void notifyListenersAndDepdendents(Element element,
  751. boolean widthChanged, boolean heightChanged) {
  752. assert widthChanged || heightChanged;
  753. Profiler.enter("LayoutManager.notifyListenersAndDepdendents");
  754. MeasuredSize measuredSize = getMeasuredSize(element, nullSize);
  755. JsArrayString dependents = measuredSize.getDependents();
  756. for (int i = 0; i < dependents.length(); i++) {
  757. String pid = dependents.get(i);
  758. if (pid != null) {
  759. if (heightChanged) {
  760. currentDependencyTree.setNeedsVerticalLayout(pid, true);
  761. }
  762. if (widthChanged) {
  763. currentDependencyTree.setNeedsHorizontalLayout(pid, true);
  764. }
  765. }
  766. }
  767. if (elementResizeListeners.containsKey(element)) {
  768. listenersToFire.add(element);
  769. }
  770. Profiler.leave("LayoutManager.notifyListenersAndDepdendents");
  771. }
  772. private static boolean isManagedLayout(ComponentConnector connector) {
  773. return connector instanceof ManagedLayout;
  774. }
  775. public void forceLayout() {
  776. ConnectorMap connectorMap = connection.getConnectorMap();
  777. JsArrayObject<ComponentConnector> componentConnectors = connectorMap
  778. .getComponentConnectorsAsJsArray();
  779. int size = componentConnectors.size();
  780. for (int i = 0; i < size; i++) {
  781. ComponentConnector connector = componentConnectors.get(i);
  782. if (connector instanceof ManagedLayout) {
  783. setNeedsLayout((ManagedLayout) connector);
  784. }
  785. }
  786. setEverythingNeedsMeasure();
  787. layoutNow();
  788. }
  789. /**
  790. * Marks that a ManagedLayout should be layouted in the next layout phase
  791. * even if none of the elements managed by the layout have been resized.
  792. * <p>
  793. * This method should not be invoked during a layout phase since it only
  794. * controls what will happen in the beginning of the next phase. If you want
  795. * to explicitly cause some layout to be considered in an ongoing layout
  796. * phase, you should use {@link #setNeedsMeasure(ComponentConnector)}
  797. * instead.
  798. *
  799. * @param layout
  800. * the managed layout that should be layouted
  801. */
  802. public final void setNeedsLayout(ManagedLayout layout) {
  803. setNeedsHorizontalLayout(layout);
  804. setNeedsVerticalLayout(layout);
  805. }
  806. /**
  807. * Marks that a ManagedLayout should be layouted horizontally in the next
  808. * layout phase even if none of the elements managed by the layout have been
  809. * resized horizontally.
  810. * <p>
  811. * For SimpleManagedLayout which is always layouted in both directions, this
  812. * has the same effect as {@link #setNeedsLayout(ManagedLayout)}.
  813. * <p>
  814. * This method should not be invoked during a layout phase since it only
  815. * controls what will happen in the beginning of the next phase. If you want
  816. * to explicitly cause some layout to be considered in an ongoing layout
  817. * phase, you should use {@link #setNeedsMeasure(ComponentConnector)}
  818. * instead.
  819. *
  820. * @param layout
  821. * the managed layout that should be layouted
  822. */
  823. public final void setNeedsHorizontalLayout(ManagedLayout layout) {
  824. if (isLayoutRunning()) {
  825. getLogger().warning(
  826. "setNeedsHorizontalLayout should not be run while a layout phase is in progress.");
  827. }
  828. needsHorizontalLayout.add(layout.getConnectorId());
  829. }
  830. /**
  831. * Marks that a ManagedLayout should be layouted vertically in the next
  832. * layout phase even if none of the elements managed by the layout have been
  833. * resized vertically.
  834. * <p>
  835. * For SimpleManagedLayout which is always layouted in both directions, this
  836. * has the same effect as {@link #setNeedsLayout(ManagedLayout)}.
  837. * <p>
  838. * This method should not be invoked during a layout phase since it only
  839. * controls what will happen in the beginning of the next phase. If you want
  840. * to explicitly cause some layout to be considered in an ongoing layout
  841. * phase, you should use {@link #setNeedsMeasure(ComponentConnector)}
  842. * instead.
  843. *
  844. * @param layout
  845. * the managed layout that should be layouted
  846. */
  847. public final void setNeedsVerticalLayout(ManagedLayout layout) {
  848. if (isLayoutRunning()) {
  849. getLogger().warning(
  850. "setNeedsVerticalLayout should not be run while a layout phase is in progress.");
  851. }
  852. needsVerticalLayout.add(layout.getConnectorId());
  853. }
  854. /**
  855. * Gets the outer height (including margins, paddings and borders) of the
  856. * given element, provided that it has been measured. These elements are
  857. * guaranteed to be measured:
  858. * <ul>
  859. * <li>ManagedLayouts and their child Connectors
  860. * <li>Elements for which there is at least one ElementResizeListener
  861. * <li>Elements for which at least one ManagedLayout has registered a
  862. * dependency
  863. * </ul>
  864. *
  865. * -1 is returned if the element has not been measured. If 0 is returned, it
  866. * might indicate that the element is not attached to the DOM.
  867. * <p>
  868. * The value returned by this method is always rounded up. To get the exact
  869. * outer width, use {@link #getOuterHeightDouble(Element)}
  870. *
  871. * @param element
  872. * the element to get the measured size for
  873. * @return the measured outer height (including margins, paddings and
  874. * borders) of the element in pixels.
  875. */
  876. public final int getOuterHeight(Element element) {
  877. assert needsMeasure(
  878. element) : "Getting measurement for element that is not measured";
  879. return (int) Math
  880. .ceil(getMeasuredSize(element, nullSize).getOuterHeight());
  881. }
  882. /**
  883. * Gets the outer height (including margins, paddings and borders) of the
  884. * given element, provided that it has been measured. These elements are
  885. * guaranteed to be measured:
  886. * <ul>
  887. * <li>ManagedLayouts and their child Connectors
  888. * <li>Elements for which there is at least one ElementResizeListener
  889. * <li>Elements for which at least one ManagedLayout has registered a
  890. * dependency
  891. * </ul>
  892. *
  893. * -1 is returned if the element has not been measured. If 0 is returned, it
  894. * might indicate that the element is not attached to the DOM.
  895. *
  896. * @since 7.5.1
  897. * @param element
  898. * the element to get the measured size for
  899. * @return the measured outer height (including margins, paddings and
  900. * borders) of the element in pixels.
  901. */
  902. public final double getOuterHeightDouble(Element element) {
  903. assert needsMeasure(
  904. element) : "Getting measurement for element that is not measured";
  905. return getMeasuredSize(element, nullSize).getOuterHeight();
  906. }
  907. /**
  908. * Gets the outer width (including margins, paddings and borders) of the
  909. * given element, provided that it has been measured. These elements are
  910. * guaranteed to be measured:
  911. * <ul>
  912. * <li>ManagedLayouts and their child Connectors
  913. * <li>Elements for which there is at least one ElementResizeListener
  914. * <li>Elements for which at least one ManagedLayout has registered a
  915. * dependency
  916. * </ul>
  917. *
  918. * -1 is returned if the element has not been measured. If 0 is returned, it
  919. * might indicate that the element is not attached to the DOM.
  920. * <p>
  921. * The value returned by this method is always rounded up. To get the exact
  922. * outer width, use {@link #getOuterWidthDouble(Element)}
  923. *
  924. * @since 7.5.1
  925. * @param element
  926. * the element to get the measured size for
  927. * @return the measured outer width (including margins, paddings and
  928. * borders) of the element in pixels.
  929. */
  930. public final int getOuterWidth(Element element) {
  931. assert needsMeasure(
  932. element) : "Getting measurement for element that is not measured";
  933. return (int) Math
  934. .ceil(getMeasuredSize(element, nullSize).getOuterWidth());
  935. }
  936. /**
  937. * Gets the outer width (including margins, paddings and borders) of the
  938. * given element, provided that it has been measured. These elements are
  939. * guaranteed to be measured:
  940. * <ul>
  941. * <li>ManagedLayouts and their child Connectors
  942. * <li>Elements for which there is at least one ElementResizeListener
  943. * <li>Elements for which at least one ManagedLayout has registered a
  944. * dependency
  945. * </ul>
  946. *
  947. * -1 is returned if the element has not been measured. If 0 is returned, it
  948. * might indicate that the element is not attached to the DOM.
  949. *
  950. * @param element
  951. * the element to get the measured size for
  952. * @return the measured outer width (including margins, paddings and
  953. * borders) of the element in pixels.
  954. */
  955. public final double getOuterWidthDouble(Element element) {
  956. assert needsMeasure(
  957. element) : "Getting measurement for element that is not measured";
  958. return getMeasuredSize(element, nullSize).getOuterWidth();
  959. }
  960. /**
  961. * Gets the inner height (excluding margins, paddings and borders) of the
  962. * given element, provided that it has been measured. These elements are
  963. * guaranteed to be measured:
  964. * <ul>
  965. * <li>ManagedLayouts and their child Connectors
  966. * <li>Elements for which there is at least one ElementResizeListener
  967. * <li>Elements for which at least one ManagedLayout has registered a
  968. * dependency
  969. * </ul>
  970. *
  971. * -1 is returned if the element has not been measured. If 0 is returned, it
  972. * might indicate that the element is not attached to the DOM.
  973. * <p>
  974. * The value returned by this method is always rounded up. To get the exact
  975. * outer width, use {@link #getInnerHeightDouble(Element)}
  976. *
  977. * @param element
  978. * the element to get the measured size for
  979. * @return the measured inner height (excluding margins, paddings and
  980. * borders) of the element in pixels.
  981. */
  982. public final int getInnerHeight(Element element) {
  983. assert needsMeasure(
  984. element) : "Getting measurement for element that is not measured";
  985. return (int) Math
  986. .ceil(getMeasuredSize(element, nullSize).getInnerHeight());
  987. }
  988. /**
  989. * Gets the inner height (excluding margins, paddings and borders) of the
  990. * given element, provided that it has been measured. These elements are
  991. * guaranteed to be measured:
  992. * <ul>
  993. * <li>ManagedLayouts and their child Connectors
  994. * <li>Elements for which there is at least one ElementResizeListener
  995. * <li>Elements for which at least one ManagedLayout has registered a
  996. * dependency
  997. * </ul>
  998. *
  999. * -1 is returned if the element has not been measured. If 0 is returned, it
  1000. * might indicate that the element is not attached to the DOM.
  1001. *
  1002. * @since 7.5.1
  1003. * @param element
  1004. * the element to get the measured size for
  1005. * @return the measured inner height (excluding margins, paddings and
  1006. * borders) of the element in pixels.
  1007. */
  1008. public final double getInnerHeightDouble(Element element) {
  1009. assert needsMeasure(
  1010. element) : "Getting measurement for element that is not measured";
  1011. return getMeasuredSize(element, nullSize).getInnerHeight();
  1012. }
  1013. /**
  1014. * Gets the inner width (excluding margins, paddings and borders) of the
  1015. * given element, provided that it has been measured. These elements are
  1016. * guaranteed to be measured:
  1017. * <ul>
  1018. * <li>ManagedLayouts and their child Connectors
  1019. * <li>Elements for which there is at least one ElementResizeListener
  1020. * <li>Elements for which at least one ManagedLayout has registered a
  1021. * dependency
  1022. * </ul>
  1023. *
  1024. * -1 is returned if the element has not been measured. If 0 is returned, it
  1025. * might indicate that the element is not attached to the DOM.
  1026. * <p>
  1027. * The value returned by this method is always rounded up. To get the exact
  1028. * outer width, use {@link #getOuterHeightDouble(Element)}
  1029. *
  1030. * @param element
  1031. * the element to get the measured size for
  1032. * @return the measured inner width (excluding margins, paddings and
  1033. * borders) of the element in pixels.
  1034. */
  1035. public final int getInnerWidth(Element element) {
  1036. assert needsMeasure(
  1037. element) : "Getting measurement for element that is not measured";
  1038. return (int) Math
  1039. .ceil(getMeasuredSize(element, nullSize).getInnerWidth());
  1040. }
  1041. /**
  1042. * Gets the inner width (excluding margins, paddings and borders) of the
  1043. * given element, provided that it has been measured. These elements are
  1044. * guaranteed to be measured:
  1045. * <ul>
  1046. * <li>ManagedLayouts and their child Connectors
  1047. * <li>Elements for which there is at least one ElementResizeListener
  1048. * <li>Elements for which at least one ManagedLayout has registered a
  1049. * dependency
  1050. * </ul>
  1051. *
  1052. * -1 is returned if the element has not been measured. If 0 is returned, it
  1053. * might indicate that the element is not attached to the DOM.
  1054. *
  1055. * @since 7.5.1
  1056. * @param element
  1057. * the element to get the measured size for
  1058. * @return the measured inner width (excluding margins, paddings and
  1059. * borders) of the element in pixels.
  1060. */
  1061. public final double getInnerWidthDouble(Element element) {
  1062. assert needsMeasure(
  1063. element) : "Getting measurement for element that is not measured";
  1064. return getMeasuredSize(element, nullSize).getInnerWidth();
  1065. }
  1066. /**
  1067. * Gets the border height (top border + bottom border) of the given element,
  1068. * provided that it has been measured. These elements are guaranteed to be
  1069. * measured:
  1070. * <ul>
  1071. * <li>ManagedLayouts and their child Connectors
  1072. * <li>Elements for which there is at least one ElementResizeListener
  1073. * <li>Elements for which at least one ManagedLayout has registered a
  1074. * dependency
  1075. * </ul>
  1076. *
  1077. * A negative number is returned if the element has not been measured. If 0
  1078. * is returned, it might indicate that the element is not attached to the
  1079. * DOM.
  1080. *
  1081. * @param element
  1082. * the element to get the measured size for
  1083. * @return the measured border height (top border + bottom border) of the
  1084. * element in pixels.
  1085. */
  1086. public final int getBorderHeight(Element element) {
  1087. assert needsMeasure(
  1088. element) : "Getting measurement for element that is not measured";
  1089. return getMeasuredSize(element, nullSize).getBorderHeight();
  1090. }
  1091. /**
  1092. * Gets the padding height (top padding + bottom padding) of the given
  1093. * element, provided that it has been measured. These elements are
  1094. * guaranteed to be measured:
  1095. * <ul>
  1096. * <li>ManagedLayouts and their child Connectors
  1097. * <li>Elements for which there is at least one ElementResizeListener
  1098. * <li>Elements for which at least one ManagedLayout has registered a
  1099. * dependency
  1100. * </ul>
  1101. *
  1102. * A negative number is returned if the element has not been measured. If 0
  1103. * is returned, it might indicate that the element is not attached to the
  1104. * DOM.
  1105. *
  1106. * @param element
  1107. * the element to get the measured size for
  1108. * @return the measured padding height (top padding + bottom padding) of the
  1109. * element in pixels.
  1110. */
  1111. public int getPaddingHeight(Element element) {
  1112. assert needsMeasure(
  1113. element) : "Getting measurement for element that is not measured";
  1114. return getMeasuredSize(element, nullSize).getPaddingHeight();
  1115. }
  1116. /**
  1117. * Gets the border width (left border + right border) of the given element,
  1118. * provided that it has been measured. These elements are guaranteed to be
  1119. * measured:
  1120. * <ul>
  1121. * <li>ManagedLayouts and their child Connectors
  1122. * <li>Elements for which there is at least one ElementResizeListener
  1123. * <li>Elements for which at least one ManagedLayout has registered a
  1124. * dependency
  1125. * </ul>
  1126. *
  1127. * A negative number is returned if the element has not been measured. If 0
  1128. * is returned, it might indicate that the element is not attached to the
  1129. * DOM.
  1130. *
  1131. * @param element
  1132. * the element to get the measured size for
  1133. * @return the measured border width (left border + right border) of the
  1134. * element in pixels.
  1135. */
  1136. public int getBorderWidth(Element element) {
  1137. assert needsMeasure(
  1138. element) : "Getting measurement for element that is not measured";
  1139. return getMeasuredSize(element, nullSize).getBorderWidth();
  1140. }
  1141. /**
  1142. * Gets the top border of the given element, provided that it has been
  1143. * measured. These elements are guaranteed to be measured:
  1144. * <ul>
  1145. * <li>ManagedLayouts and their child Connectors
  1146. * <li>Elements for which there is at least one ElementResizeListener
  1147. * <li>Elements for which at least one ManagedLayout has registered a
  1148. * dependency
  1149. * </ul>
  1150. *
  1151. * A negative number is returned if the element has not been measured. If 0
  1152. * is returned, it might indicate that the element is not attached to the
  1153. * DOM.
  1154. *
  1155. * @param element
  1156. * the element to get the measured size for
  1157. * @return the measured top border of the element in pixels.
  1158. */
  1159. public int getBorderTop(Element element) {
  1160. assert needsMeasure(
  1161. element) : "Getting measurement for element that is not measured";
  1162. return getMeasuredSize(element, nullSize).getBorderTop();
  1163. }
  1164. /**
  1165. * Gets the left border of the given element, provided that it has been
  1166. * measured. These elements are guaranteed to be measured:
  1167. * <ul>
  1168. * <li>ManagedLayouts and their child Connectors
  1169. * <li>Elements for which there is at least one ElementResizeListener
  1170. * <li>Elements for which at least one ManagedLayout has registered a
  1171. * dependency
  1172. * </ul>
  1173. *
  1174. * A negative number is returned if the element has not been measured. If 0
  1175. * is returned, it might indicate that the element is not attached to the
  1176. * DOM.
  1177. *
  1178. * @param element
  1179. * the element to get the measured size for
  1180. * @return the measured left border of the element in pixels.
  1181. */
  1182. public int getBorderLeft(Element element) {
  1183. assert needsMeasure(
  1184. element) : "Getting measurement for element that is not measured";
  1185. return getMeasuredSize(element, nullSize).getBorderLeft();
  1186. }
  1187. /**
  1188. * Gets the bottom border of the given element, provided that it has been
  1189. * measured. These elements are guaranteed to be measured:
  1190. * <ul>
  1191. * <li>ManagedLayouts and their child Connectors
  1192. * <li>Elements for which there is at least one ElementResizeListener
  1193. * <li>Elements for which at least one ManagedLayout has registered a
  1194. * dependency
  1195. * </ul>
  1196. *
  1197. * A negative number is returned if the element has not been measured. If 0
  1198. * is returned, it might indicate that the element is not attached to the
  1199. * DOM.
  1200. *
  1201. * @param element
  1202. * the element to get the measured size for
  1203. * @return the measured bottom border of the element in pixels.
  1204. */
  1205. public int getBorderBottom(Element element) {
  1206. assert needsMeasure(
  1207. element) : "Getting measurement for element that is not measured";
  1208. return getMeasuredSize(element, nullSize).getBorderBottom();
  1209. }
  1210. /**
  1211. * Gets the right border of the given element, provided that it has been
  1212. * measured. These elements are guaranteed to be measured:
  1213. * <ul>
  1214. * <li>ManagedLayouts and their child Connectors
  1215. * <li>Elements for which there is at least one ElementResizeListener
  1216. * <li>Elements for which at least one ManagedLayout has registered a
  1217. * dependency
  1218. * </ul>
  1219. *
  1220. * A negative number is returned if the element has not been measured. If 0
  1221. * is returned, it might indicate that the element is not attached to the
  1222. * DOM.
  1223. *
  1224. * @param element
  1225. * the element to get the measured size for
  1226. * @return the measured right border of the element in pixels.
  1227. */
  1228. public int getBorderRight(Element element) {
  1229. assert needsMeasure(
  1230. element) : "Getting measurement for element that is not measured";
  1231. return getMeasuredSize(element, nullSize).getBorderRight();
  1232. }
  1233. /**
  1234. * Gets the padding width (left padding + right padding) of the given
  1235. * element, provided that it has been measured. These elements are
  1236. * guaranteed to be measured:
  1237. * <ul>
  1238. * <li>ManagedLayouts and their child Connectors
  1239. * <li>Elements for which there is at least one ElementResizeListener
  1240. * <li>Elements for which at least one ManagedLayout has registered a
  1241. * dependency
  1242. * </ul>
  1243. *
  1244. * A negative number is returned if the element has not been measured. If 0
  1245. * is returned, it might indicate that the element is not attached to the
  1246. * DOM.
  1247. *
  1248. * @param element
  1249. * the element to get the measured size for
  1250. * @return the measured padding width (left padding + right padding) of the
  1251. * element in pixels.
  1252. */
  1253. public int getPaddingWidth(Element element) {
  1254. assert needsMeasure(
  1255. element) : "Getting measurement for element that is not measured";
  1256. return getMeasuredSize(element, nullSize).getPaddingWidth();
  1257. }
  1258. /**
  1259. * Gets the top padding of the given element, provided that it has been
  1260. * measured. These elements are guaranteed to be measured:
  1261. * <ul>
  1262. * <li>ManagedLayouts and their child Connectors
  1263. * <li>Elements for which there is at least one ElementResizeListener
  1264. * <li>Elements for which at least one ManagedLayout has registered a
  1265. * dependency
  1266. * </ul>
  1267. *
  1268. * A negative number is returned if the element has not been measured. If 0
  1269. * is returned, it might indicate that the element is not attached to the
  1270. * DOM.
  1271. *
  1272. * @param element
  1273. * the element to get the measured size for
  1274. * @return the measured top padding of the element in pixels.
  1275. */
  1276. public int getPaddingTop(Element element) {
  1277. assert needsMeasure(
  1278. element) : "Getting measurement for element that is not measured";
  1279. return getMeasuredSize(element, nullSize).getPaddingTop();
  1280. }
  1281. /**
  1282. * Gets the left padding of the given element, provided that it has been
  1283. * measured. These elements are guaranteed to be measured:
  1284. * <ul>
  1285. * <li>ManagedLayouts and their child Connectors
  1286. * <li>Elements for which there is at least one ElementResizeListener
  1287. * <li>Elements for which at least one ManagedLayout has registered a
  1288. * dependency
  1289. * </ul>
  1290. *
  1291. * A negative number is returned if the element has not been measured. If 0
  1292. * is returned, it might indicate that the element is not attached to the
  1293. * DOM.
  1294. *
  1295. * @param element
  1296. * the element to get the measured size for
  1297. * @return the measured left padding of the element in pixels.
  1298. */
  1299. public int getPaddingLeft(Element element) {
  1300. assert needsMeasure(
  1301. element) : "Getting measurement for element that is not measured";
  1302. return getMeasuredSize(element, nullSize).getPaddingLeft();
  1303. }
  1304. /**
  1305. * Gets the bottom padding of the given element, provided that it has been
  1306. * measured. These elements are guaranteed to be measured:
  1307. * <ul>
  1308. * <li>ManagedLayouts and their child Connectors
  1309. * <li>Elements for which there is at least one ElementResizeListener
  1310. * <li>Elements for which at least one ManagedLayout has registered a
  1311. * dependency
  1312. * </ul>
  1313. *
  1314. * A negative number is returned if the element has not been measured. If 0
  1315. * is returned, it might indicate that the element is not attached to the
  1316. * DOM.
  1317. *
  1318. * @param element
  1319. * the element to get the measured size for
  1320. * @return the measured bottom padding of the element in pixels.
  1321. */
  1322. public int getPaddingBottom(Element element) {
  1323. assert needsMeasure(
  1324. element) : "Getting measurement for element that is not measured";
  1325. return getMeasuredSize(element, nullSize).getPaddingBottom();
  1326. }
  1327. /**
  1328. * Gets the right padding of the given element, provided that it has been
  1329. * measured. These elements are guaranteed to be measured:
  1330. * <ul>
  1331. * <li>ManagedLayouts and their child Connectors
  1332. * <li>Elements for which there is at least one ElementResizeListener
  1333. * <li>Elements for which at least one ManagedLayout has registered a
  1334. * dependency
  1335. * </ul>
  1336. *
  1337. * A negative number is returned if the element has not been measured. If 0
  1338. * is returned, it might indicate that the element is not attached to the
  1339. * DOM.
  1340. *
  1341. * @param element
  1342. * the element to get the measured size for
  1343. * @return the measured right padding of the element in pixels.
  1344. */
  1345. public int getPaddingRight(Element element) {
  1346. assert needsMeasure(
  1347. element) : "Getting measurement for element that is not measured";
  1348. return getMeasuredSize(element, nullSize).getPaddingRight();
  1349. }
  1350. /**
  1351. * Gets the top margin of the given element, provided that it has been
  1352. * measured. These elements are guaranteed to be measured:
  1353. * <ul>
  1354. * <li>ManagedLayouts and their child Connectors
  1355. * <li>Elements for which there is at least one ElementResizeListener
  1356. * <li>Elements for which at least one ManagedLayout has registered a
  1357. * dependency
  1358. * </ul>
  1359. *
  1360. * A negative number is returned if the element has not been measured. If 0
  1361. * is returned, it might indicate that the element is not attached to the
  1362. * DOM.
  1363. *
  1364. * @param element
  1365. * the element to get the measured size for
  1366. * @return the measured top margin of the element in pixels.
  1367. */
  1368. public int getMarginTop(Element element) {
  1369. assert needsMeasure(
  1370. element) : "Getting measurement for element that is not measured";
  1371. return getMeasuredSize(element, nullSize).getMarginTop();
  1372. }
  1373. /**
  1374. * Gets the right margin of the given element, provided that it has been
  1375. * measured. These elements are guaranteed to be measured:
  1376. * <ul>
  1377. * <li>ManagedLayouts and their child Connectors
  1378. * <li>Elements for which there is at least one ElementResizeListener
  1379. * <li>Elements for which at least one ManagedLayout has registered a
  1380. * dependency
  1381. * </ul>
  1382. *
  1383. * A negative number is returned if the element has not been measured. If 0
  1384. * is returned, it might indicate that the element is not attached to the
  1385. * DOM.
  1386. *
  1387. * @param element
  1388. * the element to get the measured size for
  1389. * @return the measured right margin of the element in pixels.
  1390. */
  1391. public int getMarginRight(Element element) {
  1392. assert needsMeasure(
  1393. element) : "Getting measurement for element that is not measured";
  1394. return getMeasuredSize(element, nullSize).getMarginRight();
  1395. }
  1396. /**
  1397. * Gets the bottom margin of the given element, provided that it has been
  1398. * measured. These elements are guaranteed to be measured:
  1399. * <ul>
  1400. * <li>ManagedLayouts and their child Connectors
  1401. * <li>Elements for which there is at least one ElementResizeListener
  1402. * <li>Elements for which at least one ManagedLayout has registered a
  1403. * dependency
  1404. * </ul>
  1405. *
  1406. * A negative number is returned if the element has not been measured. If 0
  1407. * is returned, it might indicate that the element is not attached to the
  1408. * DOM.
  1409. *
  1410. * @param element
  1411. * the element to get the measured size for
  1412. * @return the measured bottom margin of the element in pixels.
  1413. */
  1414. public int getMarginBottom(Element element) {
  1415. assert needsMeasure(
  1416. element) : "Getting measurement for element that is not measured";
  1417. return getMeasuredSize(element, nullSize).getMarginBottom();
  1418. }
  1419. /**
  1420. * Gets the left margin of the given element, provided that it has been
  1421. * measured. These elements are guaranteed to be measured:
  1422. * <ul>
  1423. * <li>ManagedLayouts and their child Connectors
  1424. * <li>Elements for which there is at least one ElementResizeListener
  1425. * <li>Elements for which at least one ManagedLayout has registered a
  1426. * dependency
  1427. * </ul>
  1428. *
  1429. * A negative number is returned if the element has not been measured. If 0
  1430. * is returned, it might indicate that the element is not attached to the
  1431. * DOM.
  1432. *
  1433. * @param element
  1434. * the element to get the measured size for
  1435. * @return the measured left margin of the element in pixels.
  1436. */
  1437. public int getMarginLeft(Element element) {
  1438. assert needsMeasure(
  1439. element) : "Getting measurement for element that is not measured";
  1440. return getMeasuredSize(element, nullSize).getMarginLeft();
  1441. }
  1442. /**
  1443. * Gets the combined top & bottom margin of the given element, provided that
  1444. * they have been measured. These elements are guaranteed to be measured:
  1445. * <ul>
  1446. * <li>ManagedLayouts and their child Connectors
  1447. * <li>Elements for which there is at least one ElementResizeListener
  1448. * <li>Elements for which at least one ManagedLayout has registered a
  1449. * dependency
  1450. * </ul>
  1451. *
  1452. * A negative number is returned if the element has not been measured. If 0
  1453. * is returned, it might indicate that the element is not attached to the
  1454. * DOM.
  1455. *
  1456. * @param element
  1457. * the element to get the measured margin for
  1458. * @return the measured top+bottom margin of the element in pixels.
  1459. */
  1460. public int getMarginHeight(Element element) {
  1461. return getMarginTop(element) + getMarginBottom(element);
  1462. }
  1463. /**
  1464. * Gets the combined left & right margin of the given element, provided that
  1465. * they have been measured. These elements are guaranteed to be measured:
  1466. * <ul>
  1467. * <li>ManagedLayouts and their child Connectors
  1468. * <li>Elements for which there is at least one ElementResizeListener
  1469. * <li>Elements for which at least one ManagedLayout has registered a
  1470. * dependency
  1471. * </ul>
  1472. *
  1473. * A negative number is returned if the element has not been measured. If 0
  1474. * is returned, it might indicate that the element is not attached to the
  1475. * DOM.
  1476. *
  1477. * @param element
  1478. * the element to get the measured margin for
  1479. * @return the measured left+right margin of the element in pixels.
  1480. */
  1481. public int getMarginWidth(Element element) {
  1482. return getMarginLeft(element) + getMarginRight(element);
  1483. }
  1484. /**
  1485. * Registers the outer height (including margins, borders and paddings) of a
  1486. * component. This can be used as an optimization by ManagedLayouts; by
  1487. * informing the LayoutManager about what size a component will have, the
  1488. * layout propagation can continue directly without first measuring the
  1489. * potentially resized elements.
  1490. *
  1491. * @param component
  1492. * the component for which the size is reported
  1493. * @param outerHeight
  1494. * the new outer height (including margins, borders and paddings)
  1495. * of the component in pixels
  1496. */
  1497. public void reportOuterHeight(ComponentConnector component,
  1498. int outerHeight) {
  1499. Element element = component.getWidget().getElement();
  1500. MeasuredSize measuredSize = getMeasuredSize(element);
  1501. if (isLayoutRunning()) {
  1502. boolean heightChanged = measuredSize.setOuterHeight(outerHeight);
  1503. if (heightChanged) {
  1504. onConnectorChange(component, false, true);
  1505. notifyListenersAndDepdendents(element, false, true);
  1506. }
  1507. currentDependencyTree.setNeedsVerticalMeasure(component, false);
  1508. } else if (measuredSize.getOuterHeight() != outerHeight) {
  1509. setNeedsMeasure(component);
  1510. }
  1511. }
  1512. /**
  1513. * Registers the height reserved for a relatively sized component. This can
  1514. * be used as an optimization by ManagedLayouts; by informing the
  1515. * LayoutManager about what size a component will have, the layout
  1516. * propagation can continue directly without first measuring the potentially
  1517. * resized elements.
  1518. *
  1519. * @param component
  1520. * the relatively sized component for which the size is reported
  1521. * @param assignedHeight
  1522. * the inner height of the relatively sized component's parent
  1523. * element in pixels
  1524. */
  1525. public void reportHeightAssignedToRelative(ComponentConnector component,
  1526. int assignedHeight) {
  1527. assert component.isRelativeHeight();
  1528. float percentSize = parsePercent(
  1529. component.getState().height == null ? ""
  1530. : component.getState().height);
  1531. int effectiveHeight = Math.round(assignedHeight * (percentSize / 100));
  1532. reportOuterHeight(component, effectiveHeight);
  1533. }
  1534. /**
  1535. * Registers the width reserved for a relatively sized component. This can
  1536. * be used as an optimization by ManagedLayouts; by informing the
  1537. * LayoutManager about what size a component will have, the layout
  1538. * propagation can continue directly without first measuring the potentially
  1539. * resized elements.
  1540. *
  1541. * @param component
  1542. * the relatively sized component for which the size is reported
  1543. * @param assignedWidth
  1544. * the inner width of the relatively sized component's parent
  1545. * element in pixels
  1546. */
  1547. public void reportWidthAssignedToRelative(ComponentConnector component,
  1548. int assignedWidth) {
  1549. assert component.isRelativeWidth();
  1550. float percentSize = parsePercent(component.getState().width == null ? ""
  1551. : component.getState().width);
  1552. int effectiveWidth = Math.round(assignedWidth * (percentSize / 100));
  1553. reportOuterWidth(component, effectiveWidth);
  1554. }
  1555. private static float parsePercent(String size) {
  1556. return Float.parseFloat(size.substring(0, size.length() - 1));
  1557. }
  1558. /**
  1559. * Registers the outer width (including margins, borders and paddings) of a
  1560. * component. This can be used as an optimization by ManagedLayouts; by
  1561. * informing the LayoutManager about what size a component will have, the
  1562. * layout propagation can continue directly without first measuring the
  1563. * potentially resized elements.
  1564. *
  1565. * @param component
  1566. * the component for which the size is reported
  1567. * @param outerWidth
  1568. * the new outer width (including margins, borders and paddings)
  1569. * of the component in pixels
  1570. */
  1571. public void reportOuterWidth(ComponentConnector component, int outerWidth) {
  1572. Element element = component.getWidget().getElement();
  1573. MeasuredSize measuredSize = getMeasuredSize(element);
  1574. if (isLayoutRunning()) {
  1575. boolean widthChanged = measuredSize.setOuterWidth(outerWidth);
  1576. if (widthChanged) {
  1577. onConnectorChange(component, true, false);
  1578. notifyListenersAndDepdendents(element, true, false);
  1579. }
  1580. currentDependencyTree.setNeedsHorizontalMeasure(component, false);
  1581. } else if (measuredSize.getOuterWidth() != outerWidth) {
  1582. setNeedsMeasure(component);
  1583. }
  1584. }
  1585. /**
  1586. * Adds a listener that will be notified whenever the size of a specific
  1587. * element changes. Adding a listener to an element also ensures that all
  1588. * sizes for that element will be available starting from the next layout
  1589. * phase.
  1590. *
  1591. * @param element
  1592. * the element that should be checked for size changes
  1593. * @param listener
  1594. * an ElementResizeListener that will be informed whenever the
  1595. * size of the target element has changed
  1596. */
  1597. public void addElementResizeListener(Element element,
  1598. ElementResizeListener listener) {
  1599. Collection<ElementResizeListener> listeners = elementResizeListeners
  1600. .get(element);
  1601. if (listeners == null) {
  1602. listeners = new HashSet<>();
  1603. elementResizeListeners.put(element, listeners);
  1604. ensureMeasured(element);
  1605. }
  1606. listeners.add(listener);
  1607. }
  1608. /**
  1609. * Removes an element resize listener from the provided element. This might
  1610. * cause this LayoutManager to stop tracking the size of the element if no
  1611. * other sources are interested in the size.
  1612. *
  1613. * @param element
  1614. * the element to which the element resize listener was
  1615. * previously added
  1616. * @param listener
  1617. * the ElementResizeListener that should no longer get informed
  1618. * about size changes to the target element.
  1619. */
  1620. public void removeElementResizeListener(Element element,
  1621. ElementResizeListener listener) {
  1622. Collection<ElementResizeListener> listeners = elementResizeListeners
  1623. .get(element);
  1624. if (listeners != null) {
  1625. listeners.remove(listener);
  1626. if (listeners.isEmpty()) {
  1627. elementResizeListeners.remove(element);
  1628. stopMeasuringIfUnecessary(element);
  1629. }
  1630. }
  1631. }
  1632. private void stopMeasuringIfUnecessary(Element element) {
  1633. if (!needsMeasure(element)) {
  1634. measuredNonConnectorElements.remove(element);
  1635. setMeasuredSize(element, null);
  1636. }
  1637. }
  1638. /**
  1639. * Informs this LayoutManager that the size of a component might have
  1640. * changed. This method should be used whenever the size of an individual
  1641. * component might have changed from outside of Vaadin's normal update
  1642. * phase, e.g. when an icon has been loaded or when the user resizes some
  1643. * part of the UI using the mouse.
  1644. * <p>
  1645. * To set an entire component hierarchy to be measured, use
  1646. * {@link #setNeedsMeasureRecursively(ComponentConnector)} instead.
  1647. * <p>
  1648. * If there is no upcoming layout phase, a new layout phase is scheduled.
  1649. *
  1650. * @param component
  1651. * the component whose size might have changed.
  1652. */
  1653. public void setNeedsMeasure(ComponentConnector component) {
  1654. if (isLayoutRunning()) {
  1655. currentDependencyTree.setNeedsMeasure(component, true);
  1656. } else {
  1657. needsMeasure.add(component.getConnectorId());
  1658. layoutLater();
  1659. }
  1660. }
  1661. /**
  1662. * Informs this LayoutManager that some sizes in a component hierarchy might
  1663. * have changed. This method should be used whenever the size of any child
  1664. * component might have changed from outside of Vaadin's normal update
  1665. * phase, e.g. when a CSS class name related to sizing has been changed.
  1666. * <p>
  1667. * To set a single component to be measured, use
  1668. * {@link #setNeedsMeasure(ComponentConnector)} instead.
  1669. * <p>
  1670. * If there is no upcoming layout phase, a new layout phase is scheduled.
  1671. *
  1672. * @since 7.2
  1673. * @param component
  1674. * the component at the root of the component hierarchy to
  1675. * measure
  1676. */
  1677. public void setNeedsMeasureRecursively(ComponentConnector component) {
  1678. setNeedsMeasure(component);
  1679. if (component instanceof HasComponentsConnector) {
  1680. HasComponentsConnector hasComponents = (HasComponentsConnector) component;
  1681. for (ComponentConnector child : hasComponents
  1682. .getChildComponents()) {
  1683. setNeedsMeasureRecursively(child);
  1684. }
  1685. }
  1686. }
  1687. public void setEverythingNeedsMeasure() {
  1688. everythingNeedsMeasure = true;
  1689. }
  1690. private static Logger getLogger() {
  1691. return Logger.getLogger(LayoutManager.class.getName());
  1692. }
  1693. /**
  1694. * Checks if there is something waiting for a layout to take place.
  1695. *
  1696. * @since 7.5.6
  1697. * @return true if there are connectors waiting for measurement or layout,
  1698. * false otherwise
  1699. */
  1700. public boolean isLayoutNeeded() {
  1701. if (!needsHorizontalLayout.isEmpty()
  1702. || !needsVerticalLayout.isEmpty()) {
  1703. return true;
  1704. }
  1705. if (!needsMeasure.isEmpty()) {
  1706. return true;
  1707. }
  1708. if (everythingNeedsMeasure) {
  1709. return true;
  1710. }
  1711. return false;
  1712. }
  1713. }