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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client;
  5. import java.util.HashSet;
  6. import java.util.Set;
  7. import com.google.gwt.core.client.Duration;
  8. import com.google.gwt.core.client.JsArrayString;
  9. import com.google.gwt.dom.client.Element;
  10. import com.vaadin.terminal.gwt.client.ui.ManagedLayout;
  11. import com.vaadin.terminal.gwt.client.ui.PostLayoutListener;
  12. import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout;
  13. public class LayoutManager {
  14. private final ApplicationConnection connection;
  15. private final Set<Element> nonPaintableElements = new HashSet<Element>();
  16. private final MeasuredSize nullSize = new MeasuredSize();
  17. private boolean layoutRunning = false;
  18. public LayoutManager(ApplicationConnection connection) {
  19. this.connection = connection;
  20. }
  21. public static LayoutManager get(ApplicationConnection connection) {
  22. return connection.getLayoutManager();
  23. }
  24. public void registerDependency(ManagedLayout owner, Element element) {
  25. MeasuredSize measuredSize = ensureMeasured(element);
  26. MeasuredSize ownerSize = getMeasuredSize(owner);
  27. if (measuredSize.isHeightNeedsUpdate()) {
  28. ownerSize.setHeightNeedsUpdate();
  29. }
  30. if (measuredSize.isWidthNeedsUpdate()) {
  31. ownerSize.setWidthNeedsUpdate();
  32. }
  33. measuredSize.addDependent(owner.getId());
  34. }
  35. private MeasuredSize ensureMeasured(Element element) {
  36. MeasuredSize measuredSize = getMeasuredSize(element, null);
  37. if (measuredSize == null) {
  38. measuredSize = new MeasuredSize();
  39. if (ConnectorMap.get(connection).getConnector(element) == null) {
  40. nonPaintableElements.add(element);
  41. }
  42. setMeasuredSize(element, measuredSize);
  43. }
  44. return measuredSize;
  45. }
  46. private boolean needsMeasure(Element e) {
  47. if (connection.getConnectorMap().getConnectorId(e) != null) {
  48. return true;
  49. } else if (getMeasuredSize(e, nullSize).hasDependents()) {
  50. return true;
  51. } else {
  52. return false;
  53. }
  54. }
  55. private static native void setMeasuredSize(Element element,
  56. MeasuredSize measuredSize)
  57. /*-{
  58. if (measuredSize) {
  59. element.vMeasuredSize = measuredSize;
  60. } else {
  61. delete element.vMeasuredSize;
  62. }
  63. }-*/;
  64. private static native final MeasuredSize getMeasuredSize(Element element,
  65. MeasuredSize defaultSize)
  66. /*-{
  67. return element.vMeasuredSize || defaultSize;
  68. }-*/;
  69. private static final MeasuredSize getMeasuredSize(
  70. ComponentConnector paintable) {
  71. Element element = paintable.getWidget().getElement();
  72. MeasuredSize measuredSize = getMeasuredSize(element, null);
  73. if (measuredSize == null) {
  74. measuredSize = new MeasuredSize();
  75. setMeasuredSize(element, measuredSize);
  76. }
  77. return measuredSize;
  78. }
  79. public void unregisterDependency(ManagedLayout owner, Element element) {
  80. MeasuredSize measuredSize = getMeasuredSize(element, null);
  81. if (measuredSize == null) {
  82. return;
  83. }
  84. measuredSize.removeDependent(owner.getId());
  85. if (!needsMeasure(element)) {
  86. nonPaintableElements.remove(element);
  87. setMeasuredSize(element, null);
  88. }
  89. }
  90. public boolean isLayoutRunning() {
  91. return layoutRunning;
  92. }
  93. public void doLayout() {
  94. if (layoutRunning) {
  95. throw new IllegalStateException(
  96. "Can't start a new layout phase before the previous layout phase ends.");
  97. }
  98. VConsole.log("Starting layout phase");
  99. layoutRunning = true;
  100. ConnectorMap paintableMap = connection.getConnectorMap();
  101. ComponentConnector[] paintableWidgets = paintableMap
  102. .getRegisteredComponentConnectors();
  103. int passes = 0;
  104. Duration totalDuration = new Duration();
  105. while (true) {
  106. Duration passDuration = new Duration();
  107. passes++;
  108. measureElements(paintableWidgets);
  109. FastStringSet needsHeightUpdate = FastStringSet.create();
  110. FastStringSet needsWidthUpdate = FastStringSet.create();
  111. for (ComponentConnector paintable : paintableWidgets) {
  112. MeasuredSize measuredSize = getMeasuredSize(paintable);
  113. boolean managed = isManagedLayout(paintable);
  114. ComponentContainerConnector parent = paintable.getParent();
  115. boolean managedParent = parent != null
  116. && isManagedLayout(parent);
  117. if (measuredSize.isHeightNeedsUpdate()) {
  118. if (managed) {
  119. needsHeightUpdate.add(paintable.getId());
  120. }
  121. if (!paintable.isRelativeHeight() && managedParent) {
  122. needsHeightUpdate.add(parent.getId());
  123. }
  124. }
  125. if (measuredSize.isWidthNeedsUpdate()) {
  126. if (managed) {
  127. needsWidthUpdate.add(paintable.getId());
  128. }
  129. if (!paintable.isRelativeWidth() && managedParent) {
  130. needsWidthUpdate.add(parent.getId());
  131. }
  132. }
  133. measuredSize.clearDirtyState();
  134. }
  135. int measureTime = passDuration.elapsedMillis();
  136. VConsole.log("Measure in " + measureTime + " ms");
  137. FastStringSet updatedSet = FastStringSet.create();
  138. JsArrayString needsWidthUpdateArray = needsWidthUpdate.dump();
  139. for (int i = 0; i < needsWidthUpdateArray.length(); i++) {
  140. String pid = needsWidthUpdateArray.get(i);
  141. ServerConnector paintable = paintableMap.getConnector(pid);
  142. if (paintable instanceof DirectionalManagedLayout) {
  143. DirectionalManagedLayout cl = (DirectionalManagedLayout) paintable;
  144. cl.layoutHorizontally();
  145. } else if (paintable instanceof SimpleManagedLayout) {
  146. SimpleManagedLayout rr = (SimpleManagedLayout) paintable;
  147. rr.layout();
  148. needsHeightUpdate.remove(pid);
  149. }
  150. updatedSet.add(pid);
  151. }
  152. JsArrayString needsHeightUpdateArray = needsHeightUpdate.dump();
  153. for (int i = 0; i < needsHeightUpdateArray.length(); i++) {
  154. String pid = needsHeightUpdateArray.get(i);
  155. ComponentConnector paintable = (ComponentConnector) paintableMap
  156. .getConnector(pid);
  157. if (paintable instanceof DirectionalManagedLayout) {
  158. DirectionalManagedLayout cl = (DirectionalManagedLayout) paintable;
  159. cl.layoutVertically();
  160. } else if (paintable instanceof SimpleManagedLayout) {
  161. SimpleManagedLayout rr = (SimpleManagedLayout) paintable;
  162. rr.layout();
  163. }
  164. updatedSet.add(pid);
  165. }
  166. JsArrayString changed = updatedSet.dump();
  167. VConsole.log(changed.length() + " requestLayout invocations in "
  168. + (passDuration.elapsedMillis() - measureTime) + "ms");
  169. StringBuilder b = new StringBuilder();
  170. b.append(changed.length());
  171. b.append(" changed widgets in pass ");
  172. b.append(passes);
  173. b.append(" in ");
  174. b.append(passDuration.elapsedMillis());
  175. b.append(" ms: ");
  176. if (changed.length() < 10) {
  177. for (int i = 0; i < changed.length(); i++) {
  178. if (i != 0) {
  179. b.append(", ");
  180. }
  181. b.append(changed.get(i));
  182. }
  183. }
  184. VConsole.log(b.toString());
  185. if (changed.length() == 0) {
  186. VConsole.log("No more changes in pass " + passes);
  187. break;
  188. }
  189. if (passes > 100) {
  190. VConsole.log("Aborting layout");
  191. break;
  192. }
  193. }
  194. VConsole.log("Layout phase done");
  195. VConsole.log("Calling post layout listeners");
  196. for (ComponentConnector vPaintableWidget : paintableWidgets) {
  197. if (vPaintableWidget instanceof PostLayoutListener) {
  198. ((PostLayoutListener) vPaintableWidget).postLayout();
  199. }
  200. }
  201. layoutRunning = false;
  202. VConsole.log("Total layout phase time: "
  203. + totalDuration.elapsedMillis() + "ms");
  204. }
  205. private void measureElements(ComponentConnector[] paintableWidgets) {
  206. for (ComponentConnector paintableWidget : paintableWidgets) {
  207. Element element = paintableWidget.getWidget().getElement();
  208. MeasuredSize measuredSize = getMeasuredSize(paintableWidget);
  209. measuredAndUpdate(element, measuredSize);
  210. }
  211. for (Element element : nonPaintableElements) {
  212. MeasuredSize measuredSize = getMeasuredSize(element, null);
  213. measuredAndUpdate(element, measuredSize);
  214. measuredSize.clearDirtyState();
  215. }
  216. }
  217. private void measuredAndUpdate(Element element, MeasuredSize measuredSize) {
  218. if (measuredSize.measure(element)) {
  219. JsArrayString dependents = measuredSize.getDependents();
  220. for (int i = 0; i < dependents.length(); i++) {
  221. String pid = dependents.get(i);
  222. ComponentConnector dependent = (ComponentConnector) connection
  223. .getConnectorMap().getConnector(pid);
  224. if (dependent != null) {
  225. MeasuredSize dependentSize = getMeasuredSize(dependent);
  226. if (measuredSize.isHeightNeedsUpdate()) {
  227. dependentSize.setHeightNeedsUpdate();
  228. }
  229. if (measuredSize.isWidthNeedsUpdate()) {
  230. dependentSize.setWidthNeedsUpdate();
  231. }
  232. }
  233. }
  234. }
  235. }
  236. private static boolean isManagedLayout(ComponentConnector paintable) {
  237. return paintable instanceof ManagedLayout;
  238. }
  239. public void foceLayout() {
  240. ConnectorMap paintableMap = connection.getConnectorMap();
  241. ComponentConnector[] paintableWidgets = paintableMap
  242. .getRegisteredComponentConnectors();
  243. for (ComponentConnector vPaintableWidget : paintableWidgets) {
  244. MeasuredSize measuredSize = getMeasuredSize(vPaintableWidget);
  245. measuredSize.setHeightNeedsUpdate();
  246. measuredSize.setWidthNeedsUpdate();
  247. }
  248. doLayout();
  249. }
  250. public final void setNeedsUpdate(ManagedLayout layout) {
  251. setWidthNeedsUpdate(layout);
  252. setHeightNeedsUpdate(layout);
  253. }
  254. public final void setWidthNeedsUpdate(ManagedLayout layout) {
  255. getMeasuredSize(layout).setWidthNeedsUpdate();
  256. }
  257. public final void setHeightNeedsUpdate(ManagedLayout layout) {
  258. getMeasuredSize(layout).setHeightNeedsUpdate();
  259. }
  260. public boolean isMeasured(Element element) {
  261. return getMeasuredSize(element, nullSize) != nullSize;
  262. }
  263. public final int getOuterHeight(Element element) {
  264. return getMeasuredSize(element, nullSize).getOuterHeight();
  265. }
  266. public final int getOuterWidth(Element element) {
  267. return getMeasuredSize(element, nullSize).getOuterWidth();
  268. }
  269. public final int getInnerHeight(Element element) {
  270. return getMeasuredSize(element, nullSize).getInnerHeight();
  271. }
  272. public final int getInnerWidth(Element element) {
  273. return getMeasuredSize(element, nullSize).getInnerWidth();
  274. }
  275. public final int getBorderHeight(Element element) {
  276. return getMeasuredSize(element, nullSize).getBorderHeight();
  277. }
  278. public int getPaddingHeight(Element element) {
  279. return getMeasuredSize(element, nullSize).getPaddingHeight();
  280. }
  281. public int getBorderWidth(Element element) {
  282. return getMeasuredSize(element, nullSize).getBorderWidth();
  283. }
  284. public int getPaddingWidth(Element element) {
  285. return getMeasuredSize(element, nullSize).getPaddingWidth();
  286. }
  287. public int getPaddingTop(Element element) {
  288. return getMeasuredSize(element, nullSize).getPaddingTop();
  289. }
  290. public int getPaddingLeft(Element element) {
  291. return getMeasuredSize(element, nullSize).getPaddingLeft();
  292. }
  293. public int getPaddingBottom(Element element) {
  294. return getMeasuredSize(element, nullSize).getPaddingBottom();
  295. }
  296. public int getPaddingRight(Element element) {
  297. return getMeasuredSize(element, null).getPaddingRight();
  298. }
  299. }