Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

LayoutManager.java 74KB

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