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

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