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 74KB

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