選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

LayoutDependencyTree.java 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  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.ui.layout;
  17. import java.util.ArrayList;
  18. import java.util.Collection;
  19. import com.google.gwt.core.client.JsArrayString;
  20. import com.vaadin.client.ApplicationConnection;
  21. import com.vaadin.client.ComponentConnector;
  22. import com.vaadin.client.ConnectorMap;
  23. import com.vaadin.client.FastStringMap;
  24. import com.vaadin.client.FastStringSet;
  25. import com.vaadin.client.HasComponentsConnector;
  26. import com.vaadin.client.JsArrayObject;
  27. import com.vaadin.client.Profiler;
  28. import com.vaadin.client.ServerConnector;
  29. import com.vaadin.client.Util;
  30. import com.vaadin.client.VConsole;
  31. import com.vaadin.client.WidgetUtil;
  32. import com.vaadin.client.ui.ManagedLayout;
  33. import com.vaadin.shared.AbstractComponentState;
  34. /**
  35. * Internal class used to keep track of layout dependencies during one layout
  36. * run. This class is not intended to be used directly by applications.
  37. *
  38. * @author Vaadin Ltd
  39. * @since 7.0.0
  40. */
  41. public class LayoutDependencyTree {
  42. private class LayoutDependency {
  43. private final ComponentConnector connector;
  44. private final int direction;
  45. private boolean needsLayout = false;
  46. private boolean needsMeasure = false;
  47. private boolean scrollingParentCached = false;
  48. private ComponentConnector scrollingBoundary = null;
  49. private FastStringSet measureBlockers = FastStringSet.create();
  50. private FastStringSet layoutBlockers = FastStringSet.create();
  51. public LayoutDependency(ComponentConnector connector, int direction) {
  52. this.connector = connector;
  53. this.direction = direction;
  54. }
  55. private void addLayoutBlocker(ComponentConnector blocker) {
  56. String blockerId = blocker.getConnectorId();
  57. if (!layoutBlockers.contains(blockerId)) {
  58. boolean wasEmpty = layoutBlockers.isEmpty();
  59. layoutBlockers.add(blockerId);
  60. if (wasEmpty) {
  61. if (needsLayout) {
  62. getLayoutQueue(direction).remove(
  63. connector.getConnectorId());
  64. } else {
  65. // Propagation already done if needsLayout is set
  66. propagatePotentialLayout();
  67. }
  68. }
  69. }
  70. }
  71. private void removeLayoutBlocker(ComponentConnector blocker) {
  72. String blockerId = blocker.getConnectorId();
  73. if (layoutBlockers.contains(blockerId)) {
  74. layoutBlockers.remove(blockerId);
  75. if (layoutBlockers.isEmpty()) {
  76. if (needsLayout) {
  77. getLayoutQueue(direction).add(
  78. connector.getConnectorId());
  79. } else {
  80. propagateNoUpcomingLayout();
  81. }
  82. }
  83. }
  84. }
  85. private void addMeasureBlocker(ComponentConnector blocker) {
  86. String blockerId = blocker.getConnectorId();
  87. boolean alreadyAdded = measureBlockers.contains(blockerId);
  88. if (alreadyAdded) {
  89. return;
  90. }
  91. boolean wasEmpty = measureBlockers.isEmpty();
  92. measureBlockers.add(blockerId);
  93. if (wasEmpty) {
  94. if (needsMeasure) {
  95. getMeasureQueue(direction).remove(
  96. connector.getConnectorId());
  97. } else {
  98. propagatePotentialResize();
  99. }
  100. }
  101. }
  102. private void removeMeasureBlocker(ComponentConnector blocker) {
  103. String blockerId = blocker.getConnectorId();
  104. boolean alreadyRemoved = !measureBlockers.contains(blockerId);
  105. if (alreadyRemoved) {
  106. return;
  107. }
  108. measureBlockers.remove(blockerId);
  109. if (measureBlockers.isEmpty()) {
  110. if (needsMeasure) {
  111. getMeasureQueue(direction).add(connector.getConnectorId());
  112. } else {
  113. propagateNoUpcomingResize();
  114. }
  115. }
  116. }
  117. public void setNeedsMeasure(boolean needsMeasure) {
  118. if (needsMeasure && !this.needsMeasure) {
  119. // If enabling needsMeasure
  120. this.needsMeasure = needsMeasure;
  121. if (measureBlockers.isEmpty()) {
  122. // Add to queue if there are no blockers
  123. getMeasureQueue(direction).add(connector.getConnectorId());
  124. // Only need to propagate if not already propagated when
  125. // setting blockers
  126. propagatePotentialResize();
  127. }
  128. } else if (!needsMeasure && this.needsMeasure
  129. && measureBlockers.isEmpty()) {
  130. // Only disable if there are no blockers (elements gets measured
  131. // in both directions even if there is a blocker in one
  132. // direction)
  133. this.needsMeasure = needsMeasure;
  134. getMeasureQueue(direction).remove(connector.getConnectorId());
  135. propagateNoUpcomingResize();
  136. }
  137. }
  138. public void setNeedsLayout(boolean needsLayout) {
  139. if (!(connector instanceof ManagedLayout)) {
  140. throw new IllegalStateException(
  141. "Only managed layouts can need layout, layout attempted for "
  142. + Util.getConnectorString(connector));
  143. }
  144. if (needsLayout && !this.needsLayout) {
  145. // If enabling needsLayout
  146. this.needsLayout = needsLayout;
  147. if (layoutBlockers.isEmpty()) {
  148. // Add to queue if there are no blockers
  149. getLayoutQueue(direction).add(connector.getConnectorId());
  150. // Only need to propagate if not already propagated when
  151. // setting blockers
  152. propagatePotentialLayout();
  153. }
  154. } else if (!needsLayout && this.needsLayout
  155. && layoutBlockers.isEmpty()) {
  156. // Only disable if there are no layout blockers
  157. // (SimpleManagedLayout gets layouted in both directions
  158. // even if there is a blocker in one direction)
  159. this.needsLayout = needsLayout;
  160. getLayoutQueue(direction).remove(connector.getConnectorId());
  161. propagateNoUpcomingLayout();
  162. }
  163. }
  164. private void propagatePotentialResize() {
  165. JsArrayString needsSizeForLayout = getNeedsSizeForLayout();
  166. int length = needsSizeForLayout.length();
  167. for (int i = 0; i < length; i++) {
  168. String needsSizeId = needsSizeForLayout.get(i);
  169. LayoutDependency layoutDependency = getDependency(needsSizeId,
  170. direction);
  171. layoutDependency.addLayoutBlocker(connector);
  172. }
  173. }
  174. private JsArrayString getNeedsSizeForLayout() {
  175. // Find all connectors that need the size of this connector for
  176. // layouting
  177. // Parent needs size if it isn't relative?
  178. // Connector itself needs size if it isn't undefined?
  179. // Children doesn't care?
  180. JsArrayString needsSize = JsArrayObject.createArray().cast();
  181. if (!isUndefinedInDirection(connector, direction)) {
  182. needsSize.push(connector.getConnectorId());
  183. }
  184. if (!isRelativeInDirection(connector, direction)) {
  185. ServerConnector parent = connector.getParent();
  186. if (parent instanceof ComponentConnector) {
  187. needsSize.push(parent.getConnectorId());
  188. }
  189. }
  190. return needsSize;
  191. }
  192. private void propagateNoUpcomingResize() {
  193. JsArrayString needsSizeForLayout = getNeedsSizeForLayout();
  194. int length = needsSizeForLayout.length();
  195. for (int i = 0; i < length; i++) {
  196. String mightNeedLayoutId = needsSizeForLayout.get(i);
  197. LayoutDependency layoutDependency = getDependency(
  198. mightNeedLayoutId, direction);
  199. layoutDependency.removeLayoutBlocker(connector);
  200. }
  201. }
  202. private void propagatePotentialLayout() {
  203. JsArrayString resizedByLayout = getResizedByLayout();
  204. int length = resizedByLayout.length();
  205. for (int i = 0; i < length; i++) {
  206. String sizeMightChangeId = resizedByLayout.get(i);
  207. LayoutDependency layoutDependency = getDependency(
  208. sizeMightChangeId, direction);
  209. layoutDependency.addMeasureBlocker(connector);
  210. }
  211. }
  212. private JsArrayString getResizedByLayout() {
  213. // Components that might get resized by a layout of this component
  214. // Parent never resized
  215. // Connector itself resized if undefined
  216. // Children resized if relative
  217. JsArrayString resized = JsArrayObject.createArray().cast();
  218. if (isUndefinedInDirection(connector, direction)) {
  219. resized.push(connector.getConnectorId());
  220. }
  221. if (connector instanceof HasComponentsConnector) {
  222. HasComponentsConnector container = (HasComponentsConnector) connector;
  223. for (ComponentConnector child : container.getChildComponents()) {
  224. if (isRelativeInDirection(child, direction)) {
  225. resized.push(child.getConnectorId());
  226. }
  227. }
  228. }
  229. return resized;
  230. }
  231. private void propagateNoUpcomingLayout() {
  232. JsArrayString resizedByLayout = getResizedByLayout();
  233. int length = resizedByLayout.length();
  234. for (int i = 0; i < length; i++) {
  235. String sizeMightChangeId = resizedByLayout.get(i);
  236. LayoutDependency layoutDependency = getDependency(
  237. sizeMightChangeId, direction);
  238. layoutDependency.removeMeasureBlocker(connector);
  239. }
  240. }
  241. public void markSizeAsChanged() {
  242. Profiler.enter("LayoutDependency.markSizeAsChanged phase 1");
  243. // When the size has changed, all that use that size should be
  244. // layouted
  245. JsArrayString needsSizeForLayout = getNeedsSizeForLayout();
  246. int length = needsSizeForLayout.length();
  247. for (int i = 0; i < length; i++) {
  248. String connectorId = needsSizeForLayout.get(i);
  249. LayoutDependency layoutDependency = getDependency(connectorId,
  250. direction);
  251. if (layoutDependency.connector instanceof ManagedLayout) {
  252. Profiler.enter("LayoutDependency.markSizeAsChanged setNeedsLayout");
  253. layoutDependency.setNeedsLayout(true);
  254. Profiler.leave("LayoutDependency.markSizeAsChanged setNeedsLayout");
  255. } else {
  256. Profiler.enter("LayoutDependency.markSizeAsChanged propagatePostLayoutMeasure");
  257. // Should simulate setNeedsLayout(true) + markAsLayouted ->
  258. // propagate needs measure
  259. layoutDependency.propagatePostLayoutMeasure();
  260. Profiler.leave("LayoutDependency.markSizeAsChanged propagatePostLayoutMeasure");
  261. }
  262. }
  263. Profiler.leave("LayoutDependency.markSizeAsChanged phase 1");
  264. Profiler.enter("LayoutDependency.markSizeAsChanged scrollbars");
  265. // Should also go through the hierarchy to discover appeared or
  266. // disappeared scrollbars
  267. ComponentConnector scrollingBoundary = getScrollingBoundary(connector);
  268. if (scrollingBoundary != null) {
  269. getDependency(scrollingBoundary.getConnectorId(),
  270. getOppositeDirection()).setNeedsMeasure(true);
  271. }
  272. Profiler.leave("LayoutDependency.markSizeAsChanged scrollbars");
  273. }
  274. /**
  275. * Go up the hierarchy to find a component whose size might have changed
  276. * in the other direction because changes to this component causes
  277. * scrollbars to appear or disappear.
  278. *
  279. * @return
  280. */
  281. private LayoutDependency findPotentiallyChangedScrollbar() {
  282. ComponentConnector currentConnector = connector;
  283. while (true) {
  284. ServerConnector parent = currentConnector.getParent();
  285. if (!(parent instanceof ComponentConnector)) {
  286. return null;
  287. }
  288. if (parent instanceof MayScrollChildren) {
  289. return getDependency(currentConnector.getConnectorId(),
  290. getOppositeDirection());
  291. }
  292. currentConnector = (ComponentConnector) parent;
  293. }
  294. }
  295. private int getOppositeDirection() {
  296. return direction == HORIZONTAL ? VERTICAL : HORIZONTAL;
  297. }
  298. public void markAsLayouted() {
  299. if (!layoutBlockers.isEmpty()) {
  300. // Don't do anything if there are layout blockers (SimpleLayout
  301. // gets layouted in both directions even if one direction is
  302. // blocked)
  303. return;
  304. }
  305. setNeedsLayout(false);
  306. propagatePostLayoutMeasure();
  307. }
  308. private void propagatePostLayoutMeasure() {
  309. Profiler.enter("LayoutDependency.propagatePostLayoutMeasure getResizedByLayout");
  310. JsArrayString resizedByLayout = getResizedByLayout();
  311. Profiler.leave("LayoutDependency.propagatePostLayoutMeasure getResizedByLayout");
  312. int length = resizedByLayout.length();
  313. for (int i = 0; i < length; i++) {
  314. Profiler.enter("LayoutDependency.propagatePostLayoutMeasure setNeedsMeasure");
  315. String resizedId = resizedByLayout.get(i);
  316. LayoutDependency layoutDependency = getDependency(resizedId,
  317. direction);
  318. layoutDependency.setNeedsMeasure(true);
  319. Profiler.leave("LayoutDependency.propagatePostLayoutMeasure setNeedsMeasure");
  320. }
  321. // Special case for e.g. wrapping texts
  322. Profiler.enter("LayoutDependency.propagatePostLayoutMeasure horizontal case");
  323. if (direction == HORIZONTAL && !connector.isUndefinedWidth()
  324. && connector.isUndefinedHeight()) {
  325. LayoutDependency dependency = getDependency(
  326. connector.getConnectorId(), VERTICAL);
  327. dependency.setNeedsMeasure(true);
  328. }
  329. Profiler.leave("LayoutDependency.propagatePostLayoutMeasure horizontal case");
  330. }
  331. @Override
  332. public String toString() {
  333. String s = getCompactConnectorString(connector) + "\n";
  334. if (direction == VERTICAL) {
  335. s += "Vertical";
  336. } else {
  337. s += "Horizontal";
  338. }
  339. AbstractComponentState state = connector.getState();
  340. s += " sizing: "
  341. + getSizeDefinition(direction == VERTICAL ? state.height
  342. : state.width) + "\n";
  343. if (needsLayout) {
  344. s += "Needs layout\n";
  345. }
  346. if (getLayoutQueue(direction).contains(connector.getConnectorId())) {
  347. s += "In layout queue\n";
  348. }
  349. s += "Layout blockers: " + blockersToString(layoutBlockers) + "\n";
  350. if (needsMeasure) {
  351. s += "Needs measure\n";
  352. }
  353. if (getMeasureQueue(direction).contains(connector.getConnectorId())) {
  354. s += "In measure queue\n";
  355. }
  356. s += "Measure blockers: " + blockersToString(measureBlockers);
  357. return s;
  358. }
  359. public boolean noMoreChangesExpected() {
  360. return !needsLayout && !needsMeasure && layoutBlockers.isEmpty()
  361. && measureBlockers.isEmpty();
  362. }
  363. }
  364. private static final int HORIZONTAL = 0;
  365. private static final int VERTICAL = 1;
  366. @SuppressWarnings("unchecked")
  367. private final FastStringMap<LayoutDependency>[] dependenciesInDirection = new FastStringMap[] {
  368. FastStringMap.create(), FastStringMap.create() };
  369. private final FastStringSet[] measureQueueInDirection = new FastStringSet[] {
  370. FastStringSet.create(), FastStringSet.create() };
  371. private final FastStringSet[] layoutQueueInDirection = new FastStringSet[] {
  372. FastStringSet.create(), FastStringSet.create() };
  373. private final ApplicationConnection connection;
  374. public LayoutDependencyTree(ApplicationConnection connection) {
  375. this.connection = connection;
  376. }
  377. public void setNeedsMeasure(ComponentConnector connector,
  378. boolean needsMeasure) {
  379. setNeedsHorizontalMeasure(connector, needsMeasure);
  380. setNeedsVerticalMeasure(connector, needsMeasure);
  381. }
  382. public void setNeedsMeasure(String connectorId, boolean needsMeasure) {
  383. ComponentConnector connector = (ComponentConnector) ConnectorMap.get(
  384. connection).getConnector(connectorId);
  385. if (connector == null) {
  386. return;
  387. }
  388. setNeedsMeasure(connector, needsMeasure);
  389. }
  390. public void setNeedsHorizontalMeasure(ComponentConnector connector,
  391. boolean needsMeasure) {
  392. LayoutDependency dependency = getDependency(connector, HORIZONTAL);
  393. dependency.setNeedsMeasure(needsMeasure);
  394. }
  395. public void setNeedsHorizontalMeasure(String connectorId,
  396. boolean needsMeasure) {
  397. // Ensure connector exists
  398. ComponentConnector connector = (ComponentConnector) ConnectorMap.get(
  399. connection).getConnector(connectorId);
  400. if (connector == null) {
  401. return;
  402. }
  403. setNeedsHorizontalMeasure(connector, needsMeasure);
  404. }
  405. public void setNeedsVerticalMeasure(ComponentConnector connector,
  406. boolean needsMeasure) {
  407. LayoutDependency dependency = getDependency(connector, VERTICAL);
  408. dependency.setNeedsMeasure(needsMeasure);
  409. }
  410. public void setNeedsVerticalMeasure(String connectorId, boolean needsMeasure) {
  411. // Ensure connector exists
  412. ComponentConnector connector = (ComponentConnector) ConnectorMap.get(
  413. connection).getConnector(connectorId);
  414. if (connector == null) {
  415. return;
  416. }
  417. setNeedsVerticalMeasure(connector, needsMeasure);
  418. }
  419. private LayoutDependency getDependency(ComponentConnector connector,
  420. int direction) {
  421. return getDependency(connector.getConnectorId(), connector, direction);
  422. }
  423. private LayoutDependency getDependency(String connectorId, int direction) {
  424. return getDependency(connectorId, null, direction);
  425. }
  426. private LayoutDependency getDependency(String connectorId,
  427. ComponentConnector connector, int direction) {
  428. FastStringMap<LayoutDependency> dependencies = dependenciesInDirection[direction];
  429. LayoutDependency dependency = dependencies.get(connectorId);
  430. if (dependency == null) {
  431. if (connector == null) {
  432. connector = (ComponentConnector) ConnectorMap.get(connection)
  433. .getConnector(connectorId);
  434. }
  435. dependency = new LayoutDependency(connector, direction);
  436. dependencies.put(connectorId, dependency);
  437. }
  438. return dependency;
  439. }
  440. private FastStringSet getLayoutQueue(int direction) {
  441. return layoutQueueInDirection[direction];
  442. }
  443. private FastStringSet getMeasureQueue(int direction) {
  444. return measureQueueInDirection[direction];
  445. }
  446. /**
  447. * @param layout
  448. * @param needsLayout
  449. *
  450. * @deprecated As of 7.0.1, use
  451. * {@link #setNeedsHorizontalLayout(String, boolean)} for
  452. * improved performance.
  453. */
  454. @Deprecated
  455. public void setNeedsHorizontalLayout(ManagedLayout layout,
  456. boolean needsLayout) {
  457. setNeedsHorizontalLayout(layout.getConnectorId(), needsLayout);
  458. }
  459. public void setNeedsHorizontalLayout(String connectorId, boolean needsLayout) {
  460. LayoutDependency dependency = getDependency(connectorId, HORIZONTAL);
  461. dependency.setNeedsLayout(needsLayout);
  462. }
  463. /**
  464. * @param layout
  465. * @param needsLayout
  466. *
  467. * @deprecated As of 7.0.1, use
  468. * {@link #setNeedsVerticalLayout(String, boolean)} for improved
  469. * performance.
  470. */
  471. @Deprecated
  472. public void setNeedsVerticalLayout(ManagedLayout layout, boolean needsLayout) {
  473. setNeedsVerticalLayout(layout.getConnectorId(), needsLayout);
  474. }
  475. public void setNeedsVerticalLayout(String connectorId, boolean needsLayout) {
  476. LayoutDependency dependency = getDependency(connectorId, VERTICAL);
  477. dependency.setNeedsLayout(needsLayout);
  478. }
  479. public void markAsHorizontallyLayouted(ManagedLayout layout) {
  480. LayoutDependency dependency = getDependency(layout.getConnectorId(),
  481. HORIZONTAL);
  482. dependency.markAsLayouted();
  483. }
  484. public void markAsVerticallyLayouted(ManagedLayout layout) {
  485. LayoutDependency dependency = getDependency(layout.getConnectorId(),
  486. VERTICAL);
  487. dependency.markAsLayouted();
  488. }
  489. public void markHeightAsChanged(ComponentConnector connector) {
  490. LayoutDependency dependency = getDependency(connector.getConnectorId(),
  491. VERTICAL);
  492. dependency.markSizeAsChanged();
  493. }
  494. public void markWidthAsChanged(ComponentConnector connector) {
  495. LayoutDependency dependency = getDependency(connector.getConnectorId(),
  496. HORIZONTAL);
  497. dependency.markSizeAsChanged();
  498. }
  499. private static boolean isRelativeInDirection(ComponentConnector connector,
  500. int direction) {
  501. if (direction == HORIZONTAL) {
  502. return connector.isRelativeWidth();
  503. } else {
  504. return connector.isRelativeHeight();
  505. }
  506. }
  507. private static boolean isUndefinedInDirection(ComponentConnector connector,
  508. int direction) {
  509. if (direction == VERTICAL) {
  510. return connector.isUndefinedHeight();
  511. } else {
  512. return connector.isUndefinedWidth();
  513. }
  514. }
  515. private static String getCompactConnectorString(ServerConnector connector) {
  516. return WidgetUtil.getSimpleName(connector) + " ("
  517. + connector.getConnectorId() + ")";
  518. }
  519. private static String getSizeDefinition(String size) {
  520. if (size == null || size.length() == 0) {
  521. return "undefined";
  522. } else if (size.endsWith("%")) {
  523. return "relative";
  524. } else {
  525. return "fixed";
  526. }
  527. }
  528. private String blockersToString(FastStringSet blockers) {
  529. StringBuilder b = new StringBuilder("[");
  530. ConnectorMap connectorMap = ConnectorMap.get(connection);
  531. JsArrayString blockersDump = blockers.dump();
  532. for (int i = 0; i < blockersDump.length(); i++) {
  533. ServerConnector blocker = connectorMap.getConnector(blockersDump
  534. .get(i));
  535. if (b.length() != 1) {
  536. b.append(", ");
  537. }
  538. b.append(getCompactConnectorString(blocker));
  539. }
  540. b.append(']');
  541. return b.toString();
  542. }
  543. public boolean hasConnectorsToMeasure() {
  544. return !measureQueueInDirection[HORIZONTAL].isEmpty()
  545. || !measureQueueInDirection[VERTICAL].isEmpty();
  546. }
  547. public boolean hasHorizontalConnectorToLayout() {
  548. return !getLayoutQueue(HORIZONTAL).isEmpty();
  549. }
  550. public boolean hasVerticaConnectorToLayout() {
  551. return !getLayoutQueue(VERTICAL).isEmpty();
  552. }
  553. /**
  554. * @return
  555. * @deprecated As of 7.0.1, use {@link #getHorizontalLayoutTargetsJsArray()}
  556. * for improved performance.
  557. */
  558. @Deprecated
  559. public ManagedLayout[] getHorizontalLayoutTargets() {
  560. return asManagedLayoutArray(getHorizontalLayoutTargetsJsArray());
  561. }
  562. /**
  563. * @return
  564. * @deprecated As of 7.0.1, use {@link #getVerticalLayoutTargetsJsArray()}
  565. * for improved performance.
  566. */
  567. @Deprecated
  568. public ManagedLayout[] getVerticalLayoutTargets() {
  569. return asManagedLayoutArray(getVerticalLayoutTargetsJsArray());
  570. }
  571. private ManagedLayout[] asManagedLayoutArray(JsArrayString connectorIdArray) {
  572. int length = connectorIdArray.length();
  573. ConnectorMap connectorMap = ConnectorMap.get(connection);
  574. ManagedLayout[] result = new ManagedLayout[length];
  575. for (int i = 0; i < length; i++) {
  576. result[i] = (ManagedLayout) connectorMap
  577. .getConnector(connectorIdArray.get(i));
  578. }
  579. return result;
  580. }
  581. public JsArrayString getHorizontalLayoutTargetsJsArray() {
  582. return getLayoutQueue(HORIZONTAL).dump();
  583. }
  584. public JsArrayString getVerticalLayoutTargetsJsArray() {
  585. return getLayoutQueue(VERTICAL).dump();
  586. }
  587. /**
  588. * @return
  589. * @deprecated As of 7.0.1, use {@link #getMeasureTargetsJsArray()} for
  590. * improved performance.
  591. */
  592. @Deprecated
  593. public Collection<ComponentConnector> getMeasureTargets() {
  594. JsArrayString targetIds = getMeasureTargetsJsArray();
  595. int length = targetIds.length();
  596. ArrayList<ComponentConnector> targets = new ArrayList<ComponentConnector>(
  597. length);
  598. ConnectorMap connectorMap = ConnectorMap.get(connection);
  599. for (int i = 0; i < length; i++) {
  600. targets.add((ComponentConnector) connectorMap
  601. .getConnector(targetIds.get(i)));
  602. }
  603. return targets;
  604. }
  605. public JsArrayString getMeasureTargetsJsArray() {
  606. FastStringSet horizontalQueue = getMeasureQueue(HORIZONTAL);
  607. JsArrayString measureTargets = horizontalQueue.dump();
  608. JsArrayString verticalDump = getMeasureQueue(VERTICAL).dump();
  609. int length = verticalDump.length();
  610. for (int i = 0; i < length; i++) {
  611. String connectorId = verticalDump.get(i);
  612. if (!horizontalQueue.contains(connectorId)) {
  613. measureTargets.push(connectorId);
  614. }
  615. }
  616. return measureTargets;
  617. }
  618. public void logDependencyStatus(ComponentConnector connector) {
  619. VConsole.log("====");
  620. String connectorId = connector.getConnectorId();
  621. VConsole.log(getDependency(connectorId, HORIZONTAL).toString());
  622. VConsole.log(getDependency(connectorId, VERTICAL).toString());
  623. }
  624. public boolean noMoreChangesExpected(ComponentConnector connector) {
  625. return getDependency(connector.getConnectorId(), HORIZONTAL)
  626. .noMoreChangesExpected()
  627. && getDependency(connector.getConnectorId(), VERTICAL)
  628. .noMoreChangesExpected();
  629. }
  630. public ComponentConnector getScrollingBoundary(ComponentConnector connector) {
  631. LayoutDependency dependency = getDependency(connector.getConnectorId(),
  632. HORIZONTAL);
  633. if (!dependency.scrollingParentCached) {
  634. ServerConnector parent = dependency.connector.getParent();
  635. if (parent instanceof MayScrollChildren) {
  636. dependency.scrollingBoundary = connector;
  637. } else if (parent instanceof ComponentConnector) {
  638. dependency.scrollingBoundary = getScrollingBoundary((ComponentConnector) parent);
  639. } else {
  640. // No scrolling parent
  641. }
  642. dependency.scrollingParentCached = true;
  643. }
  644. return dependency.scrollingBoundary;
  645. }
  646. }