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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488
  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 com.google.gwt.core.client.Duration;
  23. import com.google.gwt.core.client.JsArrayString;
  24. import com.google.gwt.dom.client.Element;
  25. import com.google.gwt.dom.client.Style;
  26. import com.google.gwt.dom.client.Style.Overflow;
  27. import com.google.gwt.user.client.Timer;
  28. import com.vaadin.client.MeasuredSize.MeasureResult;
  29. import com.vaadin.client.ui.ManagedLayout;
  30. import com.vaadin.client.ui.PostLayoutListener;
  31. import com.vaadin.client.ui.SimpleManagedLayout;
  32. import com.vaadin.client.ui.VNotification;
  33. import com.vaadin.client.ui.layout.ElementResizeEvent;
  34. import com.vaadin.client.ui.layout.ElementResizeListener;
  35. import com.vaadin.client.ui.layout.LayoutDependencyTree;
  36. public class LayoutManager {
  37. private static final String LOOP_ABORT_MESSAGE = "Aborting layout after 100 passes. This would probably be an infinite loop.";
  38. private static final boolean debugLogging = false;
  39. private ApplicationConnection connection;
  40. private final Set<Element> measuredNonConnectorElements = new HashSet<Element>();
  41. private final MeasuredSize nullSize = new MeasuredSize();
  42. private LayoutDependencyTree currentDependencyTree;
  43. private FastStringSet needsHorizontalLayout = FastStringSet.create();
  44. private FastStringSet needsVerticalLayout = FastStringSet.create();
  45. private FastStringSet needsMeasure = FastStringSet.create();
  46. private FastStringSet pendingOverflowFixes = FastStringSet.create();
  47. private final Map<Element, Collection<ElementResizeListener>> elementResizeListeners = new HashMap<Element, Collection<ElementResizeListener>>();
  48. private final Set<Element> listenersToFire = new HashSet<Element>();
  49. private boolean layoutPending = false;
  50. private Timer layoutTimer = new Timer() {
  51. @Override
  52. public void run() {
  53. layoutNow();
  54. }
  55. };
  56. private boolean everythingNeedsMeasure = false;
  57. public void setConnection(ApplicationConnection connection) {
  58. if (this.connection != null) {
  59. throw new RuntimeException(
  60. "LayoutManager connection can never be changed");
  61. }
  62. this.connection = connection;
  63. }
  64. /**
  65. * Returns the application connection for this layout manager.
  66. *
  67. * @return connection
  68. */
  69. protected ApplicationConnection getConnection() {
  70. return connection;
  71. }
  72. /**
  73. * Gets the layout manager associated with the given
  74. * {@link ApplicationConnection}.
  75. *
  76. * @param connection
  77. * the application connection to get a layout manager for
  78. * @return the layout manager associated with the provided application
  79. * connection
  80. */
  81. public static LayoutManager get(ApplicationConnection connection) {
  82. return connection.getLayoutManager();
  83. }
  84. /**
  85. * Registers that a ManagedLayout is depending on the size of an Element.
  86. * This causes this layout manager to measure the element in the beginning
  87. * of every layout phase and call the appropriate layout method of the
  88. * managed layout if the size of the element has changed.
  89. *
  90. * @param owner
  91. * the ManagedLayout that depends on an element
  92. * @param element
  93. * the Element that should be measured
  94. */
  95. public void registerDependency(ManagedLayout owner, Element element) {
  96. MeasuredSize measuredSize = ensureMeasured(element);
  97. setNeedsLayout(owner);
  98. measuredSize.addDependent(owner.getConnectorId());
  99. }
  100. private MeasuredSize ensureMeasured(Element element) {
  101. MeasuredSize measuredSize = getMeasuredSize(element, null);
  102. if (measuredSize == null) {
  103. measuredSize = new MeasuredSize();
  104. if (ConnectorMap.get(connection).getConnector(element) == null) {
  105. measuredNonConnectorElements.add(element);
  106. }
  107. setMeasuredSize(element, measuredSize);
  108. }
  109. return measuredSize;
  110. }
  111. private boolean needsMeasure(Element e) {
  112. ComponentConnector connector = connection.getConnectorMap()
  113. .getConnector(e);
  114. if (connector != null && needsMeasureForManagedLayout(connector)) {
  115. return true;
  116. } else if (elementResizeListeners.containsKey(e)) {
  117. return true;
  118. } else if (getMeasuredSize(e, nullSize).hasDependents()) {
  119. return true;
  120. } else {
  121. return false;
  122. }
  123. }
  124. private boolean needsMeasureForManagedLayout(ComponentConnector connector) {
  125. if (connector instanceof ManagedLayout) {
  126. return true;
  127. } else if (connector.getParent() instanceof ManagedLayout) {
  128. return true;
  129. } else {
  130. return false;
  131. }
  132. }
  133. /**
  134. * Assigns a measured size to an element. Method defined as protected to
  135. * allow separate implementation for IE8.
  136. *
  137. * @param element
  138. * the dom element to attach the measured size to
  139. * @param measuredSize
  140. * the measured size to attach to the element. If
  141. * <code>null</code>, any previous measured size is removed.
  142. */
  143. protected native void setMeasuredSize(Element element,
  144. MeasuredSize measuredSize)
  145. /*-{
  146. if (measuredSize) {
  147. element.vMeasuredSize = measuredSize;
  148. } else {
  149. delete element.vMeasuredSize;
  150. }
  151. }-*/;
  152. /**
  153. * Gets the measured size for an element. Method defined as protected to
  154. * allow separate implementation for IE8.
  155. *
  156. * @param element
  157. * The element to get measured size for
  158. * @param defaultSize
  159. * The size to return if no measured size could be found
  160. * @return The measured size for the element or {@literal defaultSize}
  161. */
  162. protected native MeasuredSize getMeasuredSize(Element element,
  163. MeasuredSize defaultSize)
  164. /*-{
  165. return element.vMeasuredSize || defaultSize;
  166. }-*/;
  167. private final MeasuredSize getMeasuredSize(ComponentConnector connector) {
  168. Element element = connector.getWidget().getElement();
  169. MeasuredSize measuredSize = getMeasuredSize(element, null);
  170. if (measuredSize == null) {
  171. measuredSize = new MeasuredSize();
  172. setMeasuredSize(element, measuredSize);
  173. }
  174. return measuredSize;
  175. }
  176. /**
  177. * Registers that a ManagedLayout is no longer depending on the size of an
  178. * Element.
  179. *
  180. * @see #registerDependency(ManagedLayout, Element)
  181. *
  182. * @param owner
  183. * the ManagedLayout no longer depends on an element
  184. * @param element
  185. * the Element that that no longer needs to be measured
  186. */
  187. public void unregisterDependency(ManagedLayout owner, Element element) {
  188. MeasuredSize measuredSize = getMeasuredSize(element, null);
  189. if (measuredSize == null) {
  190. return;
  191. }
  192. measuredSize.removeDependent(owner.getConnectorId());
  193. stopMeasuringIfUnecessary(element);
  194. }
  195. public boolean isLayoutRunning() {
  196. return currentDependencyTree != null;
  197. }
  198. private void countLayout(FastStringMap<Integer> layoutCounts,
  199. ManagedLayout layout) {
  200. Integer count = layoutCounts.get(layout.getConnectorId());
  201. if (count == null) {
  202. count = Integer.valueOf(0);
  203. } else {
  204. count = Integer.valueOf(count.intValue() + 1);
  205. }
  206. layoutCounts.put(layout.getConnectorId(), count);
  207. if (count.intValue() > 2) {
  208. VConsole.error(Util.getConnectorString(layout)
  209. + " has been layouted " + count.intValue() + " times");
  210. }
  211. }
  212. public void layoutLater() {
  213. if (!layoutPending) {
  214. layoutPending = true;
  215. layoutTimer.schedule(100);
  216. }
  217. }
  218. public void layoutNow() {
  219. if (isLayoutRunning()) {
  220. throw new IllegalStateException(
  221. "Can't start a new layout phase before the previous layout phase ends.");
  222. }
  223. layoutPending = false;
  224. layoutTimer.cancel();
  225. try {
  226. currentDependencyTree = new LayoutDependencyTree(connection);
  227. doLayout();
  228. } finally {
  229. currentDependencyTree = null;
  230. }
  231. }
  232. /**
  233. * Called once per iteration in the layout loop before size calculations so
  234. * different browsers quirks can be handled. Mainly this is currently for
  235. * the IE8 permutation.
  236. */
  237. protected void performBrowserLayoutHacks() {
  238. // Permutations implement this
  239. }
  240. private void doLayout() {
  241. VConsole.log("Starting layout phase");
  242. Profiler.enter("LayoutManager phase init");
  243. FastStringMap<Integer> layoutCounts = FastStringMap.create();
  244. int passes = 0;
  245. Duration totalDuration = new Duration();
  246. ConnectorMap connectorMap = ConnectorMap.get(connection);
  247. JsArrayString dump = needsHorizontalLayout.dump();
  248. int dumpLength = dump.length();
  249. for (int i = 0; i < dumpLength; i++) {
  250. String layoutId = dump.get(i);
  251. currentDependencyTree.setNeedsHorizontalLayout(layoutId, true);
  252. }
  253. dump = needsVerticalLayout.dump();
  254. dumpLength = dump.length();
  255. for (int i = 0; i < dumpLength; i++) {
  256. String layoutId = dump.get(i);
  257. currentDependencyTree.setNeedsVerticalLayout(layoutId, true);
  258. }
  259. needsHorizontalLayout = FastStringSet.create();
  260. needsVerticalLayout = FastStringSet.create();
  261. dump = needsMeasure.dump();
  262. dumpLength = dump.length();
  263. for (int i = 0; i < dumpLength; i++) {
  264. String layoutId = dump.get(i);
  265. currentDependencyTree.setNeedsMeasure(layoutId, true);
  266. }
  267. needsMeasure = FastStringSet.create();
  268. measureNonConnectors();
  269. Profiler.leave("LayoutManager phase init");
  270. while (true) {
  271. Profiler.enter("Layout pass");
  272. passes++;
  273. performBrowserLayoutHacks();
  274. Profiler.enter("Layout measure connectors");
  275. int measuredConnectorCount = measureConnectors(
  276. currentDependencyTree, everythingNeedsMeasure);
  277. Profiler.leave("Layout measure connectors");
  278. everythingNeedsMeasure = false;
  279. if (measuredConnectorCount == 0) {
  280. VConsole.log("No more changes in pass " + passes);
  281. Profiler.leave("Layout pass");
  282. break;
  283. }
  284. int firedListeners = 0;
  285. if (!listenersToFire.isEmpty()) {
  286. firedListeners = listenersToFire.size();
  287. Profiler.enter("Layout fire resize events");
  288. for (Element element : listenersToFire) {
  289. Collection<ElementResizeListener> listeners = elementResizeListeners
  290. .get(element);
  291. if (listeners != null) {
  292. Profiler.enter("Layout fire resize events - listeners not null");
  293. Profiler.enter("ElementResizeListener.onElementResize copy list");
  294. ElementResizeListener[] array = listeners
  295. .toArray(new ElementResizeListener[listeners
  296. .size()]);
  297. Profiler.leave("ElementResizeListener.onElementResize copy list");
  298. ElementResizeEvent event = new ElementResizeEvent(this,
  299. element);
  300. for (ElementResizeListener listener : array) {
  301. try {
  302. String key = null;
  303. if (Profiler.isEnabled()) {
  304. Profiler.enter("ElementResizeListener.onElementResize construct profiler key");
  305. key = "ElementResizeListener.onElementResize for "
  306. + Util.getSimpleName(listener);
  307. Profiler.leave("ElementResizeListener.onElementResize construct profiler key");
  308. Profiler.enter(key);
  309. }
  310. listener.onElementResize(event);
  311. if (Profiler.isEnabled()) {
  312. Profiler.leave(key);
  313. }
  314. } catch (RuntimeException e) {
  315. VConsole.error(e);
  316. }
  317. }
  318. Profiler.leave("Layout fire resize events - listeners not null");
  319. }
  320. }
  321. listenersToFire.clear();
  322. Profiler.leave("Layout fire resize events");
  323. }
  324. Profiler.enter("LayoutManager handle ManagedLayout");
  325. FastStringSet updatedSet = FastStringSet.create();
  326. int layoutCount = 0;
  327. while (currentDependencyTree.hasHorizontalConnectorToLayout()
  328. || currentDependencyTree.hasVerticaConnectorToLayout()) {
  329. JsArrayString layoutTargets = currentDependencyTree
  330. .getHorizontalLayoutTargetsJsArray();
  331. int length = layoutTargets.length();
  332. for (int i = 0; i < length; i++) {
  333. ManagedLayout layout = (ManagedLayout) connectorMap
  334. .getConnector(layoutTargets.get(i));
  335. if (layout instanceof DirectionalManagedLayout) {
  336. currentDependencyTree
  337. .markAsHorizontallyLayouted(layout);
  338. DirectionalManagedLayout cl = (DirectionalManagedLayout) layout;
  339. try {
  340. String key = null;
  341. if (Profiler.isEnabled()) {
  342. key = "layoutHorizontally() for "
  343. + Util.getSimpleName(cl);
  344. Profiler.enter(key);
  345. }
  346. cl.layoutHorizontally();
  347. layoutCount++;
  348. if (Profiler.isEnabled()) {
  349. Profiler.leave(key);
  350. }
  351. } catch (RuntimeException e) {
  352. VConsole.error(e);
  353. }
  354. countLayout(layoutCounts, cl);
  355. } else {
  356. currentDependencyTree
  357. .markAsHorizontallyLayouted(layout);
  358. currentDependencyTree.markAsVerticallyLayouted(layout);
  359. SimpleManagedLayout rr = (SimpleManagedLayout) layout;
  360. try {
  361. String key = null;
  362. if (Profiler.isEnabled()) {
  363. key = "layout() for " + Util.getSimpleName(rr);
  364. Profiler.enter(key);
  365. }
  366. rr.layout();
  367. layoutCount++;
  368. if (Profiler.isEnabled()) {
  369. Profiler.leave(key);
  370. }
  371. } catch (RuntimeException e) {
  372. VConsole.error(e);
  373. }
  374. countLayout(layoutCounts, rr);
  375. }
  376. if (debugLogging) {
  377. updatedSet.add(layout.getConnectorId());
  378. }
  379. }
  380. layoutTargets = currentDependencyTree
  381. .getVerticalLayoutTargetsJsArray();
  382. length = layoutTargets.length();
  383. for (int i = 0; i < length; i++) {
  384. ManagedLayout layout = (ManagedLayout) connectorMap
  385. .getConnector(layoutTargets.get(i));
  386. if (layout instanceof DirectionalManagedLayout) {
  387. currentDependencyTree.markAsVerticallyLayouted(layout);
  388. DirectionalManagedLayout cl = (DirectionalManagedLayout) layout;
  389. try {
  390. String key = null;
  391. if (Profiler.isEnabled()) {
  392. key = "layoutHorizontally() for "
  393. + Util.getSimpleName(cl);
  394. Profiler.enter(key);
  395. }
  396. cl.layoutVertically();
  397. layoutCount++;
  398. if (Profiler.isEnabled()) {
  399. Profiler.leave(key);
  400. }
  401. } catch (RuntimeException e) {
  402. VConsole.error(e);
  403. }
  404. countLayout(layoutCounts, cl);
  405. } else {
  406. currentDependencyTree
  407. .markAsHorizontallyLayouted(layout);
  408. currentDependencyTree.markAsVerticallyLayouted(layout);
  409. SimpleManagedLayout rr = (SimpleManagedLayout) layout;
  410. try {
  411. String key = null;
  412. if (Profiler.isEnabled()) {
  413. key = "layout() for " + Util.getSimpleName(rr);
  414. Profiler.enter(key);
  415. }
  416. rr.layout();
  417. layoutCount++;
  418. if (Profiler.isEnabled()) {
  419. Profiler.leave(key);
  420. }
  421. } catch (RuntimeException e) {
  422. VConsole.error(e);
  423. }
  424. countLayout(layoutCounts, rr);
  425. }
  426. if (debugLogging) {
  427. updatedSet.add(layout.getConnectorId());
  428. }
  429. }
  430. }
  431. Profiler.leave("LayoutManager handle ManagedLayout");
  432. if (debugLogging) {
  433. JsArrayString changedCids = updatedSet.dump();
  434. StringBuilder b = new StringBuilder(" ");
  435. b.append(changedCids.length());
  436. b.append(" requestLayout invocations ");
  437. if (changedCids.length() < 30) {
  438. for (int i = 0; i < changedCids.length(); i++) {
  439. if (i != 0) {
  440. b.append(", ");
  441. } else {
  442. b.append(": ");
  443. }
  444. String connectorString = changedCids.get(i);
  445. if (changedCids.length() < 10) {
  446. ServerConnector connector = ConnectorMap.get(
  447. connection).getConnector(connectorString);
  448. connectorString = Util
  449. .getConnectorString(connector);
  450. }
  451. b.append(connectorString);
  452. }
  453. }
  454. VConsole.log(b.toString());
  455. }
  456. Profiler.leave("Layout pass");
  457. VConsole.log("Pass " + passes + " measured "
  458. + measuredConnectorCount + " elements, fired "
  459. + firedListeners + " listeners and did " + layoutCount
  460. + " layouts.");
  461. if (passes > 100) {
  462. VConsole.log(LOOP_ABORT_MESSAGE);
  463. if (ApplicationConfiguration.isDebugMode()) {
  464. VNotification.createNotification(
  465. VNotification.DELAY_FOREVER,
  466. connection.getUIConnector().getWidget())
  467. .show(LOOP_ABORT_MESSAGE, VNotification.CENTERED,
  468. "error");
  469. }
  470. break;
  471. }
  472. }
  473. Profiler.enter("layout PostLayoutListener");
  474. JsArrayObject<ComponentConnector> componentConnectors = connectorMap
  475. .getComponentConnectorsAsJsArray();
  476. int size = componentConnectors.size();
  477. for (int i = 0; i < size; i++) {
  478. ComponentConnector connector = componentConnectors.get(i);
  479. if (connector instanceof PostLayoutListener) {
  480. String key = null;
  481. if (Profiler.isEnabled()) {
  482. key = "layout PostLayoutListener for "
  483. + Util.getSimpleName(connector);
  484. Profiler.enter(key);
  485. }
  486. ((PostLayoutListener) connector).postLayout();
  487. if (Profiler.isEnabled()) {
  488. Profiler.leave(key);
  489. }
  490. }
  491. }
  492. Profiler.leave("layout PostLayoutListener");
  493. cleanMeasuredSizes();
  494. VConsole.log("Total layout phase time: "
  495. + totalDuration.elapsedMillis() + "ms");
  496. }
  497. private void logConnectorStatus(int connectorId) {
  498. currentDependencyTree
  499. .logDependencyStatus((ComponentConnector) ConnectorMap.get(
  500. connection).getConnector(Integer.toString(connectorId)));
  501. }
  502. private int measureConnectors(LayoutDependencyTree layoutDependencyTree,
  503. boolean measureAll) {
  504. Profiler.enter("Layout overflow fix handling");
  505. JsArrayString pendingOverflowConnectorsIds = pendingOverflowFixes
  506. .dump();
  507. int pendingOverflowCount = pendingOverflowConnectorsIds.length();
  508. ConnectorMap connectorMap = ConnectorMap.get(connection);
  509. if (pendingOverflowCount > 0) {
  510. HashMap<Element, String> originalOverflows = new HashMap<Element, String>();
  511. FastStringSet delayedOverflowFixes = FastStringSet.create();
  512. // First set overflow to hidden (and save previous value so it can
  513. // be restored later)
  514. for (int i = 0; i < pendingOverflowCount; i++) {
  515. String connectorId = pendingOverflowConnectorsIds.get(i);
  516. ComponentConnector componentConnector = (ComponentConnector) connectorMap
  517. .getConnector(connectorId);
  518. // Delay the overflow fix if the involved connectors might still
  519. // change
  520. boolean connectorChangesExpected = !currentDependencyTree
  521. .noMoreChangesExpected(componentConnector);
  522. boolean parentChangesExcpected = componentConnector.getParent() instanceof ComponentConnector
  523. && !currentDependencyTree
  524. .noMoreChangesExpected((ComponentConnector) componentConnector
  525. .getParent());
  526. if (connectorChangesExpected || parentChangesExcpected) {
  527. delayedOverflowFixes.add(connectorId);
  528. continue;
  529. }
  530. if (debugLogging) {
  531. VConsole.log("Doing overflow fix for "
  532. + Util.getConnectorString(componentConnector)
  533. + " in "
  534. + Util.getConnectorString(componentConnector
  535. .getParent()));
  536. }
  537. Profiler.enter("Overflow fix apply");
  538. Element parentElement = componentConnector.getWidget()
  539. .getElement().getParentElement();
  540. Style style = parentElement.getStyle();
  541. String originalOverflow = style.getOverflow();
  542. if (originalOverflow != null
  543. && !originalOverflows.containsKey(parentElement)) {
  544. // Store original value for restore, but only the first time
  545. // the value is changed
  546. originalOverflows.put(parentElement, originalOverflow);
  547. }
  548. style.setOverflow(Overflow.HIDDEN);
  549. Profiler.leave("Overflow fix apply");
  550. }
  551. pendingOverflowFixes.removeAll(delayedOverflowFixes);
  552. JsArrayString remainingOverflowFixIds = pendingOverflowFixes.dump();
  553. int remainingCount = remainingOverflowFixIds.length();
  554. Profiler.enter("Overflow fix reflow");
  555. // Then ensure all scrolling elements are reflowed by measuring
  556. for (int i = 0; i < remainingCount; i++) {
  557. ComponentConnector componentConnector = (ComponentConnector) connectorMap
  558. .getConnector(remainingOverflowFixIds.get(i));
  559. componentConnector.getWidget().getElement().getParentElement()
  560. .getOffsetHeight();
  561. }
  562. Profiler.leave("Overflow fix reflow");
  563. Profiler.enter("Overflow fix restore");
  564. // Finally restore old overflow value and update bookkeeping
  565. for (int i = 0; i < remainingCount; i++) {
  566. String connectorId = remainingOverflowFixIds.get(i);
  567. ComponentConnector componentConnector = (ComponentConnector) connectorMap
  568. .getConnector(connectorId);
  569. Element parentElement = componentConnector.getWidget()
  570. .getElement().getParentElement();
  571. parentElement.getStyle().setProperty("overflow",
  572. originalOverflows.get(parentElement));
  573. layoutDependencyTree.setNeedsMeasure(connectorId, true);
  574. }
  575. Profiler.leave("Overflow fix restore");
  576. if (!pendingOverflowFixes.isEmpty()) {
  577. VConsole.log("Did overflow fix for " + remainingCount
  578. + " elements");
  579. }
  580. pendingOverflowFixes = delayedOverflowFixes;
  581. }
  582. Profiler.leave("Layout overflow fix handling");
  583. int measureCount = 0;
  584. if (measureAll) {
  585. Profiler.enter("Layout measureAll");
  586. JsArrayObject<ComponentConnector> allConnectors = connectorMap
  587. .getComponentConnectorsAsJsArray();
  588. int size = allConnectors.size();
  589. // Find connectors that should actually be measured
  590. JsArrayObject<ComponentConnector> connectors = JsArrayObject
  591. .createArray().cast();
  592. for (int i = 0; i < size; i++) {
  593. ComponentConnector candidate = allConnectors.get(i);
  594. if (needsMeasure(candidate.getWidget().getElement())) {
  595. connectors.add(candidate);
  596. }
  597. }
  598. int connectorCount = connectors.size();
  599. for (int i = 0; i < connectorCount; i++) {
  600. measureConnector(connectors.get(i));
  601. }
  602. for (int i = 0; i < connectorCount; i++) {
  603. layoutDependencyTree.setNeedsMeasure(connectors.get(i)
  604. .getConnectorId(), false);
  605. }
  606. measureCount += connectorCount;
  607. Profiler.leave("Layout measureAll");
  608. }
  609. Profiler.enter("Layout measure from tree");
  610. while (layoutDependencyTree.hasConnectorsToMeasure()) {
  611. JsArrayString measureTargets = layoutDependencyTree
  612. .getMeasureTargetsJsArray();
  613. int length = measureTargets.length();
  614. for (int i = 0; i < length; i++) {
  615. ComponentConnector connector = (ComponentConnector) connectorMap
  616. .getConnector(measureTargets.get(i));
  617. measureConnector(connector);
  618. measureCount++;
  619. }
  620. for (int i = 0; i < length; i++) {
  621. String connectorId = measureTargets.get(i);
  622. layoutDependencyTree.setNeedsMeasure(connectorId, false);
  623. }
  624. }
  625. Profiler.leave("Layout measure from tree");
  626. return measureCount;
  627. }
  628. private void measureConnector(ComponentConnector connector) {
  629. Profiler.enter("LayoutManager.measureConnector");
  630. Element element = connector.getWidget().getElement();
  631. MeasuredSize measuredSize = getMeasuredSize(connector);
  632. MeasureResult measureResult = measuredAndUpdate(element, measuredSize);
  633. if (measureResult.isChanged()) {
  634. onConnectorChange(connector, measureResult.isWidthChanged(),
  635. measureResult.isHeightChanged());
  636. }
  637. Profiler.leave("LayoutManager.measureConnector");
  638. }
  639. private void onConnectorChange(ComponentConnector connector,
  640. boolean widthChanged, boolean heightChanged) {
  641. Profiler.enter("LayoutManager.onConnectorChange");
  642. Profiler.enter("LayoutManager.onConnectorChange setNeedsOverflowFix");
  643. setNeedsOverflowFix(connector);
  644. Profiler.leave("LayoutManager.onConnectorChange setNeedsOverflowFix");
  645. Profiler.enter("LayoutManager.onConnectorChange heightChanged");
  646. if (heightChanged) {
  647. currentDependencyTree.markHeightAsChanged(connector);
  648. }
  649. Profiler.leave("LayoutManager.onConnectorChange heightChanged");
  650. Profiler.enter("LayoutManager.onConnectorChange widthChanged");
  651. if (widthChanged) {
  652. currentDependencyTree.markWidthAsChanged(connector);
  653. }
  654. Profiler.leave("LayoutManager.onConnectorChange widthChanged");
  655. Profiler.leave("LayoutManager.onConnectorChange");
  656. }
  657. private void setNeedsOverflowFix(ComponentConnector connector) {
  658. // IE9 doesn't need the original fix, but for some reason it needs this
  659. if (BrowserInfo.get().requiresOverflowAutoFix()
  660. || BrowserInfo.get().isIE9()) {
  661. ComponentConnector scrollingBoundary = currentDependencyTree
  662. .getScrollingBoundary(connector);
  663. if (scrollingBoundary != null) {
  664. pendingOverflowFixes.add(scrollingBoundary.getConnectorId());
  665. }
  666. }
  667. }
  668. private void measureNonConnectors() {
  669. Profiler.enter("LayoutManager.measureNonConenctors");
  670. for (Element element : measuredNonConnectorElements) {
  671. measuredAndUpdate(element, getMeasuredSize(element, null));
  672. }
  673. Profiler.leave("LayoutManager.measureNonConenctors");
  674. VConsole.log("Measured " + measuredNonConnectorElements.size()
  675. + " non connector elements");
  676. }
  677. private MeasureResult measuredAndUpdate(Element element,
  678. MeasuredSize measuredSize) {
  679. MeasureResult measureResult = measuredSize.measure(element);
  680. if (measureResult.isChanged()) {
  681. notifyListenersAndDepdendents(element,
  682. measureResult.isWidthChanged(),
  683. measureResult.isHeightChanged());
  684. }
  685. return measureResult;
  686. }
  687. private void notifyListenersAndDepdendents(Element element,
  688. boolean widthChanged, boolean heightChanged) {
  689. assert widthChanged || heightChanged;
  690. Profiler.enter("LayoutManager.notifyListenersAndDepdendents");
  691. MeasuredSize measuredSize = getMeasuredSize(element, nullSize);
  692. JsArrayString dependents = measuredSize.getDependents();
  693. for (int i = 0; i < dependents.length(); i++) {
  694. String pid = dependents.get(i);
  695. if (pid != null) {
  696. if (heightChanged) {
  697. currentDependencyTree.setNeedsVerticalLayout(pid, true);
  698. }
  699. if (widthChanged) {
  700. currentDependencyTree.setNeedsHorizontalLayout(pid, true);
  701. }
  702. }
  703. }
  704. if (elementResizeListeners.containsKey(element)) {
  705. listenersToFire.add(element);
  706. }
  707. Profiler.leave("LayoutManager.notifyListenersAndDepdendents");
  708. }
  709. private static boolean isManagedLayout(ComponentConnector connector) {
  710. return connector instanceof ManagedLayout;
  711. }
  712. public void forceLayout() {
  713. ConnectorMap connectorMap = connection.getConnectorMap();
  714. JsArrayObject<ComponentConnector> componentConnectors = connectorMap
  715. .getComponentConnectorsAsJsArray();
  716. int size = componentConnectors.size();
  717. for (int i = 0; i < size; i++) {
  718. ComponentConnector connector = componentConnectors.get(i);
  719. if (connector instanceof ManagedLayout) {
  720. setNeedsLayout((ManagedLayout) connector);
  721. }
  722. }
  723. setEverythingNeedsMeasure();
  724. layoutNow();
  725. }
  726. /**
  727. * Marks that a ManagedLayout should be layouted in the next layout phase
  728. * even if none of the elements managed by the layout have been resized.
  729. *
  730. * @param layout
  731. * the managed layout that should be layouted
  732. */
  733. public final void setNeedsLayout(ManagedLayout layout) {
  734. setNeedsHorizontalLayout(layout);
  735. setNeedsVerticalLayout(layout);
  736. }
  737. /**
  738. * Marks that a ManagedLayout should be layouted horizontally in the next
  739. * layout phase even if none of the elements managed by the layout have been
  740. * resized horizontally.
  741. *
  742. * For SimpleManagedLayout which is always layouted in both directions, this
  743. * has the same effect as {@link #setNeedsLayout(ManagedLayout)}.
  744. *
  745. * @param layout
  746. * the managed layout that should be layouted
  747. */
  748. public final void setNeedsHorizontalLayout(ManagedLayout layout) {
  749. needsHorizontalLayout.add(layout.getConnectorId());
  750. }
  751. /**
  752. * Marks that a ManagedLayout should be layouted vertically in the next
  753. * layout phase even if none of the elements managed by the layout have been
  754. * resized vertically.
  755. *
  756. * For SimpleManagedLayout which is always layouted in both directions, this
  757. * has the same effect as {@link #setNeedsLayout(ManagedLayout)}.
  758. *
  759. * @param layout
  760. * the managed layout that should be layouted
  761. */
  762. public final void setNeedsVerticalLayout(ManagedLayout layout) {
  763. needsVerticalLayout.add(layout.getConnectorId());
  764. }
  765. /**
  766. * Gets the outer height (including margins, paddings and borders) of the
  767. * given element, provided that it has been measured. These elements are
  768. * guaranteed to be measured:
  769. * <ul>
  770. * <li>ManagedLayotus and their child Connectors
  771. * <li>Elements for which there is at least one ElementResizeListener
  772. * <li>Elements for which at least one ManagedLayout has registered a
  773. * dependency
  774. * </ul>
  775. *
  776. * -1 is returned if the element has not been measured. If 0 is returned, it
  777. * might indicate that the element is not attached to the DOM.
  778. *
  779. * @param element
  780. * the element to get the measured size for
  781. * @return the measured outer height (including margins, paddings and
  782. * borders) of the element in pixels.
  783. */
  784. public final int getOuterHeight(Element element) {
  785. assert needsMeasure(element) : "Getting measurement for element that is not measured";
  786. return getMeasuredSize(element, nullSize).getOuterHeight();
  787. }
  788. /**
  789. * Gets the outer width (including margins, paddings and borders) of the
  790. * given element, provided that it has been measured. These elements are
  791. * guaranteed to be measured:
  792. * <ul>
  793. * <li>ManagedLayotus and their child Connectors
  794. * <li>Elements for which there is at least one ElementResizeListener
  795. * <li>Elements for which at least one ManagedLayout has registered a
  796. * dependency
  797. * </ul>
  798. *
  799. * -1 is returned if the element has not been measured. If 0 is returned, it
  800. * might indicate that the element is not attached to the DOM.
  801. *
  802. * @param element
  803. * the element to get the measured size for
  804. * @return the measured outer width (including margins, paddings and
  805. * borders) of the element in pixels.
  806. */
  807. public final int getOuterWidth(Element element) {
  808. assert needsMeasure(element) : "Getting measurement for element that is not measured";
  809. return getMeasuredSize(element, nullSize).getOuterWidth();
  810. }
  811. /**
  812. * Gets the inner height (excluding margins, paddings and borders) of the
  813. * given element, provided that it has been measured. These elements are
  814. * guaranteed to be measured:
  815. * <ul>
  816. * <li>ManagedLayotus and their child Connectors
  817. * <li>Elements for which there is at least one ElementResizeListener
  818. * <li>Elements for which at least one ManagedLayout has registered a
  819. * dependency
  820. * </ul>
  821. *
  822. * -1 is returned if the element has not been measured. If 0 is returned, it
  823. * might indicate that the element is not attached to the DOM.
  824. *
  825. * @param element
  826. * the element to get the measured size for
  827. * @return the measured inner height (excluding margins, paddings and
  828. * borders) of the element in pixels.
  829. */
  830. public final int getInnerHeight(Element element) {
  831. assert needsMeasure(element) : "Getting measurement for element that is not measured";
  832. return getMeasuredSize(element, nullSize).getInnerHeight();
  833. }
  834. /**
  835. * Gets the inner width (excluding 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>ManagedLayotus 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. *
  848. * @param element
  849. * the element to get the measured size for
  850. * @return the measured inner width (excluding margins, paddings and
  851. * borders) of the element in pixels.
  852. */
  853. public final int getInnerWidth(Element element) {
  854. assert needsMeasure(element) : "Getting measurement for element that is not measured";
  855. return getMeasuredSize(element, nullSize).getInnerWidth();
  856. }
  857. /**
  858. * Gets the border height (top border + bottom border) of the given element,
  859. * provided that it has been measured. These elements are guaranteed to be
  860. * measured:
  861. * <ul>
  862. * <li>ManagedLayotus and their child Connectors
  863. * <li>Elements for which there is at least one ElementResizeListener
  864. * <li>Elements for which at least one ManagedLayout has registered a
  865. * dependency
  866. * </ul>
  867. *
  868. * A negative number is returned if the element has not been measured. If 0
  869. * is returned, it might indicate that the element is not attached to the
  870. * DOM.
  871. *
  872. * @param element
  873. * the element to get the measured size for
  874. * @return the measured border height (top border + bottom border) of the
  875. * element in pixels.
  876. */
  877. public final int getBorderHeight(Element element) {
  878. assert needsMeasure(element) : "Getting measurement for element that is not measured";
  879. return getMeasuredSize(element, nullSize).getBorderHeight();
  880. }
  881. /**
  882. * Gets the padding height (top padding + bottom padding) of the given
  883. * element, provided that it has been measured. These elements are
  884. * guaranteed to be measured:
  885. * <ul>
  886. * <li>ManagedLayotus and their child Connectors
  887. * <li>Elements for which there is at least one ElementResizeListener
  888. * <li>Elements for which at least one ManagedLayout has registered a
  889. * dependency
  890. * </ul>
  891. *
  892. * A negative number is returned if the element has not been measured. If 0
  893. * is returned, it might indicate that the element is not attached to the
  894. * DOM.
  895. *
  896. * @param element
  897. * the element to get the measured size for
  898. * @return the measured padding height (top padding + bottom padding) of the
  899. * element in pixels.
  900. */
  901. public int getPaddingHeight(Element element) {
  902. assert needsMeasure(element) : "Getting measurement for element that is not measured";
  903. return getMeasuredSize(element, nullSize).getPaddingHeight();
  904. }
  905. /**
  906. * Gets the border width (left border + right border) of the given element,
  907. * provided that it has been measured. These elements are guaranteed to be
  908. * measured:
  909. * <ul>
  910. * <li>ManagedLayotus and their child Connectors
  911. * <li>Elements for which there is at least one ElementResizeListener
  912. * <li>Elements for which at least one ManagedLayout has registered a
  913. * dependency
  914. * </ul>
  915. *
  916. * A negative number is returned if the element has not been measured. If 0
  917. * is returned, it might indicate that the element is not attached to the
  918. * DOM.
  919. *
  920. * @param element
  921. * the element to get the measured size for
  922. * @return the measured border width (left border + right border) of the
  923. * element in pixels.
  924. */
  925. public int getBorderWidth(Element element) {
  926. assert needsMeasure(element) : "Getting measurement for element that is not measured";
  927. return getMeasuredSize(element, nullSize).getBorderWidth();
  928. }
  929. /**
  930. * Gets the padding width (left padding + right padding) of the given
  931. * element, provided that it has been measured. These elements are
  932. * guaranteed to be measured:
  933. * <ul>
  934. * <li>ManagedLayotus and their child Connectors
  935. * <li>Elements for which there is at least one ElementResizeListener
  936. * <li>Elements for which at least one ManagedLayout has registered a
  937. * dependency
  938. * </ul>
  939. *
  940. * A negative number is returned if the element has not been measured. If 0
  941. * is returned, it might indicate that the element is not attached to the
  942. * DOM.
  943. *
  944. * @param element
  945. * the element to get the measured size for
  946. * @return the measured padding width (left padding + right padding) of the
  947. * element in pixels.
  948. */
  949. public int getPaddingWidth(Element element) {
  950. assert needsMeasure(element) : "Getting measurement for element that is not measured";
  951. return getMeasuredSize(element, nullSize).getPaddingWidth();
  952. }
  953. /**
  954. * Gets the top padding of the given element, provided that it has been
  955. * measured. These elements are guaranteed to be measured:
  956. * <ul>
  957. * <li>ManagedLayotus and their child Connectors
  958. * <li>Elements for which there is at least one ElementResizeListener
  959. * <li>Elements for which at least one ManagedLayout has registered a
  960. * dependency
  961. * </ul>
  962. *
  963. * A negative number is returned if the element has not been measured. If 0
  964. * is returned, it might indicate that the element is not attached to the
  965. * DOM.
  966. *
  967. * @param element
  968. * the element to get the measured size for
  969. * @return the measured top padding of the element in pixels.
  970. */
  971. public int getPaddingTop(Element element) {
  972. assert needsMeasure(element) : "Getting measurement for element that is not measured";
  973. return getMeasuredSize(element, nullSize).getPaddingTop();
  974. }
  975. /**
  976. * Gets the left padding of the given element, provided that it has been
  977. * measured. These elements are guaranteed to be measured:
  978. * <ul>
  979. * <li>ManagedLayotus and their child Connectors
  980. * <li>Elements for which there is at least one ElementResizeListener
  981. * <li>Elements for which at least one ManagedLayout has registered a
  982. * dependency
  983. * </ul>
  984. *
  985. * A negative number is returned if the element has not been measured. If 0
  986. * is returned, it might indicate that the element is not attached to the
  987. * DOM.
  988. *
  989. * @param element
  990. * the element to get the measured size for
  991. * @return the measured left padding of the element in pixels.
  992. */
  993. public int getPaddingLeft(Element element) {
  994. assert needsMeasure(element) : "Getting measurement for element that is not measured";
  995. return getMeasuredSize(element, nullSize).getPaddingLeft();
  996. }
  997. /**
  998. * Gets the bottom padding of the given element, provided that it has been
  999. * measured. These elements are guaranteed to be measured:
  1000. * <ul>
  1001. * <li>ManagedLayotus and their child Connectors
  1002. * <li>Elements for which there is at least one ElementResizeListener
  1003. * <li>Elements for which at least one ManagedLayout has registered a
  1004. * dependency
  1005. * </ul>
  1006. *
  1007. * A negative number is returned if the element has not been measured. If 0
  1008. * is returned, it might indicate that the element is not attached to the
  1009. * DOM.
  1010. *
  1011. * @param element
  1012. * the element to get the measured size for
  1013. * @return the measured bottom padding of the element in pixels.
  1014. */
  1015. public int getPaddingBottom(Element element) {
  1016. assert needsMeasure(element) : "Getting measurement for element that is not measured";
  1017. return getMeasuredSize(element, nullSize).getPaddingBottom();
  1018. }
  1019. /**
  1020. * Gets the right padding of the given element, provided that it has been
  1021. * measured. These elements are guaranteed to be measured:
  1022. * <ul>
  1023. * <li>ManagedLayotus and their child Connectors
  1024. * <li>Elements for which there is at least one ElementResizeListener
  1025. * <li>Elements for which at least one ManagedLayout has registered a
  1026. * dependency
  1027. * </ul>
  1028. *
  1029. * A negative number is returned if the element has not been measured. If 0
  1030. * is returned, it might indicate that the element is not attached to the
  1031. * DOM.
  1032. *
  1033. * @param element
  1034. * the element to get the measured size for
  1035. * @return the measured right padding of the element in pixels.
  1036. */
  1037. public int getPaddingRight(Element element) {
  1038. assert needsMeasure(element) : "Getting measurement for element that is not measured";
  1039. return getMeasuredSize(element, nullSize).getPaddingRight();
  1040. }
  1041. /**
  1042. * Gets the top margin of the given element, provided that it has been
  1043. * measured. These elements are guaranteed to be measured:
  1044. * <ul>
  1045. * <li>ManagedLayotus and their child Connectors
  1046. * <li>Elements for which there is at least one ElementResizeListener
  1047. * <li>Elements for which at least one ManagedLayout has registered a
  1048. * dependency
  1049. * </ul>
  1050. *
  1051. * A negative number is returned if the element has not been measured. If 0
  1052. * is returned, it might indicate that the element is not attached to the
  1053. * DOM.
  1054. *
  1055. * @param element
  1056. * the element to get the measured size for
  1057. * @return the measured top margin of the element in pixels.
  1058. */
  1059. public int getMarginTop(Element element) {
  1060. assert needsMeasure(element) : "Getting measurement for element that is not measured";
  1061. return getMeasuredSize(element, nullSize).getMarginTop();
  1062. }
  1063. /**
  1064. * Gets the right margin of the given element, provided that it has been
  1065. * measured. These elements are guaranteed to be measured:
  1066. * <ul>
  1067. * <li>ManagedLayotus and their child Connectors
  1068. * <li>Elements for which there is at least one ElementResizeListener
  1069. * <li>Elements for which at least one ManagedLayout has registered a
  1070. * dependency
  1071. * </ul>
  1072. *
  1073. * A negative number is returned if the element has not been measured. If 0
  1074. * is returned, it might indicate that the element is not attached to the
  1075. * DOM.
  1076. *
  1077. * @param element
  1078. * the element to get the measured size for
  1079. * @return the measured right margin of the element in pixels.
  1080. */
  1081. public int getMarginRight(Element element) {
  1082. assert needsMeasure(element) : "Getting measurement for element that is not measured";
  1083. return getMeasuredSize(element, nullSize).getMarginRight();
  1084. }
  1085. /**
  1086. * Gets the bottom margin of the given element, provided that it has been
  1087. * measured. These elements are guaranteed to be measured:
  1088. * <ul>
  1089. * <li>ManagedLayotus and their child Connectors
  1090. * <li>Elements for which there is at least one ElementResizeListener
  1091. * <li>Elements for which at least one ManagedLayout has registered a
  1092. * dependency
  1093. * </ul>
  1094. *
  1095. * A negative number is returned if the element has not been measured. If 0
  1096. * is returned, it might indicate that the element is not attached to the
  1097. * DOM.
  1098. *
  1099. * @param element
  1100. * the element to get the measured size for
  1101. * @return the measured bottom margin of the element in pixels.
  1102. */
  1103. public int getMarginBottom(Element element) {
  1104. assert needsMeasure(element) : "Getting measurement for element that is not measured";
  1105. return getMeasuredSize(element, nullSize).getMarginBottom();
  1106. }
  1107. /**
  1108. * Gets the left margin of the given element, provided that it has been
  1109. * measured. These elements are guaranteed to be measured:
  1110. * <ul>
  1111. * <li>ManagedLayotus and their child Connectors
  1112. * <li>Elements for which there is at least one ElementResizeListener
  1113. * <li>Elements for which at least one ManagedLayout has registered a
  1114. * dependency
  1115. * </ul>
  1116. *
  1117. * A negative number is returned if the element has not been measured. If 0
  1118. * is returned, it might indicate that the element is not attached to the
  1119. * DOM.
  1120. *
  1121. * @param element
  1122. * the element to get the measured size for
  1123. * @return the measured left margin of the element in pixels.
  1124. */
  1125. public int getMarginLeft(Element element) {
  1126. assert needsMeasure(element) : "Getting measurement for element that is not measured";
  1127. return getMeasuredSize(element, nullSize).getMarginLeft();
  1128. }
  1129. /**
  1130. * Gets the combined top & bottom margin of the given element, provided that
  1131. * they have been measured. These elements are guaranteed to be measured:
  1132. * <ul>
  1133. * <li>ManagedLayotus and their child Connectors
  1134. * <li>Elements for which there is at least one ElementResizeListener
  1135. * <li>Elements for which at least one ManagedLayout has registered a
  1136. * dependency
  1137. * </ul>
  1138. *
  1139. * A negative number is returned if the element has not been measured. If 0
  1140. * is returned, it might indicate that the element is not attached to the
  1141. * DOM.
  1142. *
  1143. * @param element
  1144. * the element to get the measured margin for
  1145. * @return the measured top+bottom margin of the element in pixels.
  1146. */
  1147. public int getMarginHeight(Element element) {
  1148. return getMarginTop(element) + getMarginBottom(element);
  1149. }
  1150. /**
  1151. * Gets the combined left & right margin of the given element, provided that
  1152. * they have been measured. These elements are guaranteed to be measured:
  1153. * <ul>
  1154. * <li>ManagedLayotus and their child Connectors
  1155. * <li>Elements for which there is at least one ElementResizeListener
  1156. * <li>Elements for which at least one ManagedLayout has registered a
  1157. * dependency
  1158. * </ul>
  1159. *
  1160. * A negative number is returned if the element has not been measured. If 0
  1161. * is returned, it might indicate that the element is not attached to the
  1162. * DOM.
  1163. *
  1164. * @param element
  1165. * the element to get the measured margin for
  1166. * @return the measured left+right margin of the element in pixels.
  1167. */
  1168. public int getMarginWidth(Element element) {
  1169. return getMarginLeft(element) + getMarginRight(element);
  1170. }
  1171. /**
  1172. * Registers the outer height (including margins, borders and paddings) of a
  1173. * component. This can be used as an optimization by ManagedLayouts; by
  1174. * informing the LayoutManager about what size a component will have, the
  1175. * layout propagation can continue directly without first measuring the
  1176. * potentially resized elements.
  1177. *
  1178. * @param component
  1179. * the component for which the size is reported
  1180. * @param outerHeight
  1181. * the new outer height (including margins, borders and paddings)
  1182. * of the component in pixels
  1183. */
  1184. public void reportOuterHeight(ComponentConnector component, int outerHeight) {
  1185. MeasuredSize measuredSize = getMeasuredSize(component);
  1186. if (isLayoutRunning()) {
  1187. boolean heightChanged = measuredSize.setOuterHeight(outerHeight);
  1188. if (heightChanged) {
  1189. onConnectorChange(component, false, true);
  1190. notifyListenersAndDepdendents(component.getWidget()
  1191. .getElement(), false, true);
  1192. }
  1193. currentDependencyTree.setNeedsVerticalMeasure(component, false);
  1194. } else if (measuredSize.getOuterHeight() != outerHeight) {
  1195. setNeedsMeasure(component);
  1196. }
  1197. }
  1198. /**
  1199. * Registers the height reserved for a relatively sized component. This can
  1200. * be used as an optimization by ManagedLayouts; by informing the
  1201. * LayoutManager about what size a component will have, the layout
  1202. * propagation can continue directly without first measuring the potentially
  1203. * resized elements.
  1204. *
  1205. * @param component
  1206. * the relatively sized component for which the size is reported
  1207. * @param assignedHeight
  1208. * the inner height of the relatively sized component's parent
  1209. * element in pixels
  1210. */
  1211. public void reportHeightAssignedToRelative(ComponentConnector component,
  1212. int assignedHeight) {
  1213. assert component.isRelativeHeight();
  1214. float percentSize = parsePercent(component.getState().height == null ? ""
  1215. : component.getState().height);
  1216. int effectiveHeight = Math.round(assignedHeight * (percentSize / 100));
  1217. reportOuterHeight(component, effectiveHeight);
  1218. }
  1219. /**
  1220. * Registers the width reserved for a relatively sized component. This can
  1221. * be used as an optimization by ManagedLayouts; by informing the
  1222. * LayoutManager about what size a component will have, the layout
  1223. * propagation can continue directly without first measuring the potentially
  1224. * resized elements.
  1225. *
  1226. * @param component
  1227. * the relatively sized component for which the size is reported
  1228. * @param assignedWidth
  1229. * the inner width of the relatively sized component's parent
  1230. * element in pixels
  1231. */
  1232. public void reportWidthAssignedToRelative(ComponentConnector component,
  1233. int assignedWidth) {
  1234. assert component.isRelativeWidth();
  1235. float percentSize = parsePercent(component.getState().width == null ? ""
  1236. : component.getState().width);
  1237. int effectiveWidth = Math.round(assignedWidth * (percentSize / 100));
  1238. reportOuterWidth(component, effectiveWidth);
  1239. }
  1240. private static float parsePercent(String size) {
  1241. return Float.parseFloat(size.substring(0, size.length() - 1));
  1242. }
  1243. /**
  1244. * Registers the outer width (including margins, borders and paddings) of a
  1245. * component. This can be used as an optimization by ManagedLayouts; by
  1246. * informing the LayoutManager about what size a component will have, the
  1247. * layout propagation can continue directly without first measuring the
  1248. * potentially resized elements.
  1249. *
  1250. * @param component
  1251. * the component for which the size is reported
  1252. * @param outerWidth
  1253. * the new outer width (including margins, borders and paddings)
  1254. * of the component in pixels
  1255. */
  1256. public void reportOuterWidth(ComponentConnector component, int outerWidth) {
  1257. MeasuredSize measuredSize = getMeasuredSize(component);
  1258. if (isLayoutRunning()) {
  1259. boolean widthChanged = measuredSize.setOuterWidth(outerWidth);
  1260. if (widthChanged) {
  1261. onConnectorChange(component, true, false);
  1262. notifyListenersAndDepdendents(component.getWidget()
  1263. .getElement(), true, false);
  1264. }
  1265. currentDependencyTree.setNeedsHorizontalMeasure(component, false);
  1266. } else if (measuredSize.getOuterWidth() != outerWidth) {
  1267. setNeedsMeasure(component);
  1268. }
  1269. }
  1270. /**
  1271. * Adds a listener that will be notified whenever the size of a specific
  1272. * element changes. Adding a listener to an element also ensures that all
  1273. * sizes for that element will be available starting from the next layout
  1274. * phase.
  1275. *
  1276. * @param element
  1277. * the element that should be checked for size changes
  1278. * @param listener
  1279. * an ElementResizeListener that will be informed whenever the
  1280. * size of the target element has changed
  1281. */
  1282. public void addElementResizeListener(Element element,
  1283. ElementResizeListener listener) {
  1284. Collection<ElementResizeListener> listeners = elementResizeListeners
  1285. .get(element);
  1286. if (listeners == null) {
  1287. listeners = new HashSet<ElementResizeListener>();
  1288. elementResizeListeners.put(element, listeners);
  1289. ensureMeasured(element);
  1290. }
  1291. listeners.add(listener);
  1292. }
  1293. /**
  1294. * Removes an element resize listener from the provided element. This might
  1295. * cause this LayoutManager to stop tracking the size of the element if no
  1296. * other sources are interested in the size.
  1297. *
  1298. * @param element
  1299. * the element to which the element resize listener was
  1300. * previously added
  1301. * @param listener
  1302. * the ElementResizeListener that should no longer get informed
  1303. * about size changes to the target element.
  1304. */
  1305. public void removeElementResizeListener(Element element,
  1306. ElementResizeListener listener) {
  1307. Collection<ElementResizeListener> listeners = elementResizeListeners
  1308. .get(element);
  1309. if (listeners != null) {
  1310. listeners.remove(listener);
  1311. if (listeners.isEmpty()) {
  1312. elementResizeListeners.remove(element);
  1313. stopMeasuringIfUnecessary(element);
  1314. }
  1315. }
  1316. }
  1317. private void stopMeasuringIfUnecessary(Element element) {
  1318. if (!needsMeasure(element)) {
  1319. measuredNonConnectorElements.remove(element);
  1320. setMeasuredSize(element, null);
  1321. }
  1322. }
  1323. /**
  1324. * Informs this LayoutManager that the size of a component might have
  1325. * changed. If there is no upcoming layout phase, a new layout phase is
  1326. * scheduled. This method should be used whenever a size might have changed
  1327. * from outside of Vaadin's normal update phase, e.g. when an icon has been
  1328. * loaded or when the user resizes some part of the UI using the mouse.
  1329. *
  1330. * @param component
  1331. * the component whose size might have changed.
  1332. */
  1333. public void setNeedsMeasure(ComponentConnector component) {
  1334. if (isLayoutRunning()) {
  1335. currentDependencyTree.setNeedsMeasure(component, true);
  1336. } else {
  1337. needsMeasure.add(component.getConnectorId());
  1338. layoutLater();
  1339. }
  1340. }
  1341. public void setEverythingNeedsMeasure() {
  1342. everythingNeedsMeasure = true;
  1343. }
  1344. /**
  1345. * Clean measured sizes which are no longer needed. Only for IE8.
  1346. */
  1347. protected void cleanMeasuredSizes() {
  1348. }
  1349. }