Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

LayoutDependencyTree.java 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. /*
  2. * Copyright 2000-2016 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 java.util.List;
  20. import java.util.logging.Logger;
  21. import com.google.gwt.core.client.JsArrayString;
  22. import com.vaadin.client.ApplicationConnection;
  23. import com.vaadin.client.ComponentConnector;
  24. import com.vaadin.client.ConnectorMap;
  25. import com.vaadin.client.FastStringMap;
  26. import com.vaadin.client.FastStringSet;
  27. import com.vaadin.client.HasComponentsConnector;
  28. import com.vaadin.client.JsArrayObject;
  29. import com.vaadin.client.Profiler;
  30. import com.vaadin.client.ServerConnector;
  31. import com.vaadin.client.Util;
  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)
  63. .remove(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)
  78. .add(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)
  96. .remove(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
  224. .getChildComponents()) {
  225. if (!Util.shouldSkipMeasurementOfConnector(child, connector)
  226. && isRelativeInDirection(child, direction)) {
  227. resized.push(child.getConnectorId());
  228. }
  229. }
  230. }
  231. return resized;
  232. }
  233. private void propagateNoUpcomingLayout() {
  234. JsArrayString resizedByLayout = getResizedByLayout();
  235. int length = resizedByLayout.length();
  236. for (int i = 0; i < length; i++) {
  237. String sizeMightChangeId = resizedByLayout.get(i);
  238. LayoutDependency layoutDependency = getDependency(
  239. sizeMightChangeId, direction);
  240. layoutDependency.removeMeasureBlocker(connector);
  241. }
  242. }
  243. public void markSizeAsChanged() {
  244. Profiler.enter("LayoutDependency.markSizeAsChanged phase 1");
  245. // When the size has changed, all that use that size should be
  246. // layouted
  247. JsArrayString needsSizeForLayout = getNeedsSizeForLayout();
  248. int length = needsSizeForLayout.length();
  249. for (int i = 0; i < length; i++) {
  250. String connectorId = needsSizeForLayout.get(i);
  251. LayoutDependency layoutDependency = getDependency(connectorId,
  252. direction);
  253. if (layoutDependency.connector instanceof ManagedLayout) {
  254. Profiler.enter(
  255. "LayoutDependency.markSizeAsChanged setNeedsLayout");
  256. layoutDependency.setNeedsLayout(true);
  257. Profiler.leave(
  258. "LayoutDependency.markSizeAsChanged setNeedsLayout");
  259. } else {
  260. Profiler.enter(
  261. "LayoutDependency.markSizeAsChanged propagatePostLayoutMeasure");
  262. // Should simulate setNeedsLayout(true) + markAsLayouted ->
  263. // propagate needs measure
  264. layoutDependency.propagatePostLayoutMeasure();
  265. Profiler.leave(
  266. "LayoutDependency.markSizeAsChanged propagatePostLayoutMeasure");
  267. }
  268. }
  269. Profiler.leave("LayoutDependency.markSizeAsChanged phase 1");
  270. Profiler.enter("LayoutDependency.markSizeAsChanged scrollbars");
  271. // Should also go through the hierarchy to discover appeared or
  272. // disappeared scrollbars
  273. ComponentConnector scrollingBoundary = getScrollingBoundary(
  274. connector);
  275. if (scrollingBoundary != null) {
  276. getDependency(scrollingBoundary.getConnectorId(),
  277. getOppositeDirection()).setNeedsMeasure(true);
  278. }
  279. Profiler.leave("LayoutDependency.markSizeAsChanged scrollbars");
  280. }
  281. private int getOppositeDirection() {
  282. return direction == HORIZONTAL ? VERTICAL : HORIZONTAL;
  283. }
  284. public void markAsLayouted() {
  285. if (!layoutBlockers.isEmpty()) {
  286. // Don't do anything if there are layout blockers (SimpleLayout
  287. // gets layouted in both directions even if one direction is
  288. // blocked)
  289. return;
  290. }
  291. setNeedsLayout(false);
  292. propagatePostLayoutMeasure();
  293. }
  294. private void propagatePostLayoutMeasure() {
  295. Profiler.enter(
  296. "LayoutDependency.propagatePostLayoutMeasure getResizedByLayout");
  297. JsArrayString resizedByLayout = getResizedByLayout();
  298. Profiler.leave(
  299. "LayoutDependency.propagatePostLayoutMeasure getResizedByLayout");
  300. int length = resizedByLayout.length();
  301. for (int i = 0; i < length; i++) {
  302. Profiler.enter(
  303. "LayoutDependency.propagatePostLayoutMeasure setNeedsMeasure");
  304. String resizedId = resizedByLayout.get(i);
  305. LayoutDependency layoutDependency = getDependency(resizedId,
  306. direction);
  307. layoutDependency.setNeedsMeasure(true);
  308. Profiler.leave(
  309. "LayoutDependency.propagatePostLayoutMeasure setNeedsMeasure");
  310. }
  311. // Special case for e.g. wrapping texts
  312. Profiler.enter(
  313. "LayoutDependency.propagatePostLayoutMeasure horizontal case");
  314. if (direction == HORIZONTAL && !connector.isUndefinedWidth()
  315. && connector.isUndefinedHeight()) {
  316. LayoutDependency dependency = getDependency(
  317. connector.getConnectorId(), VERTICAL);
  318. dependency.setNeedsMeasure(true);
  319. }
  320. Profiler.leave(
  321. "LayoutDependency.propagatePostLayoutMeasure horizontal case");
  322. }
  323. @Override
  324. public String toString() {
  325. String s = getCompactConnectorString(connector) + "\n";
  326. if (direction == VERTICAL) {
  327. s += "Vertical";
  328. } else {
  329. s += "Horizontal";
  330. }
  331. AbstractComponentState state = connector.getState();
  332. s += " sizing: "
  333. + getSizeDefinition(
  334. direction == VERTICAL ? state.height : state.width)
  335. + "\n";
  336. if (needsLayout) {
  337. s += "Needs layout\n";
  338. }
  339. if (getLayoutQueue(direction)
  340. .contains(connector.getConnectorId())) {
  341. s += "In layout queue\n";
  342. }
  343. s += "Layout blockers: " + blockersToString(layoutBlockers) + "\n";
  344. if (needsMeasure) {
  345. s += "Needs measure\n";
  346. }
  347. if (getMeasureQueue(direction)
  348. .contains(connector.getConnectorId())) {
  349. s += "In measure queue\n";
  350. }
  351. s += "Measure blockers: " + blockersToString(measureBlockers);
  352. return s;
  353. }
  354. public boolean noMoreChangesExpected() {
  355. return !needsLayout && !needsMeasure && layoutBlockers.isEmpty()
  356. && measureBlockers.isEmpty();
  357. }
  358. }
  359. private static final int HORIZONTAL = 0;
  360. private static final int VERTICAL = 1;
  361. @SuppressWarnings("unchecked")
  362. private final FastStringMap<LayoutDependency>[] dependenciesInDirection = new FastStringMap[] {
  363. FastStringMap.create(), FastStringMap.create() };
  364. private final FastStringSet[] measureQueueInDirection = {
  365. FastStringSet.create(), FastStringSet.create() };
  366. private final FastStringSet[] layoutQueueInDirection = {
  367. FastStringSet.create(), FastStringSet.create() };
  368. private final ApplicationConnection connection;
  369. public LayoutDependencyTree(ApplicationConnection connection) {
  370. this.connection = connection;
  371. }
  372. public void setNeedsMeasure(ComponentConnector connector,
  373. boolean needsMeasure) {
  374. setNeedsHorizontalMeasure(connector, needsMeasure);
  375. setNeedsVerticalMeasure(connector, needsMeasure);
  376. }
  377. /**
  378. * @param connectorId
  379. * @param needsMeasure
  380. *
  381. * @deprecated As of 7.4.2, use
  382. * {@link #setNeedsMeasure(ComponentConnector, boolean)} for
  383. * improved performance.
  384. */
  385. @Deprecated
  386. public void setNeedsMeasure(String connectorId, boolean needsMeasure) {
  387. ComponentConnector connector = (ComponentConnector) ConnectorMap
  388. .get(connection).getConnector(connectorId);
  389. if (connector == null) {
  390. return;
  391. }
  392. setNeedsMeasure(connector, needsMeasure);
  393. }
  394. public void setNeedsHorizontalMeasure(ComponentConnector connector,
  395. boolean needsMeasure) {
  396. LayoutDependency dependency = getDependency(connector, HORIZONTAL);
  397. dependency.setNeedsMeasure(needsMeasure);
  398. }
  399. public void setNeedsHorizontalMeasure(String connectorId,
  400. boolean needsMeasure) {
  401. // Ensure connector exists
  402. ComponentConnector connector = (ComponentConnector) ConnectorMap
  403. .get(connection).getConnector(connectorId);
  404. if (connector == null) {
  405. return;
  406. }
  407. setNeedsHorizontalMeasure(connector, needsMeasure);
  408. }
  409. public void setNeedsVerticalMeasure(ComponentConnector connector,
  410. boolean needsMeasure) {
  411. LayoutDependency dependency = getDependency(connector, VERTICAL);
  412. dependency.setNeedsMeasure(needsMeasure);
  413. }
  414. public void setNeedsVerticalMeasure(String connectorId,
  415. boolean needsMeasure) {
  416. // Ensure connector exists
  417. ComponentConnector connector = (ComponentConnector) ConnectorMap
  418. .get(connection).getConnector(connectorId);
  419. if (connector == null) {
  420. return;
  421. }
  422. setNeedsVerticalMeasure(connector, needsMeasure);
  423. }
  424. private LayoutDependency getDependency(ComponentConnector connector,
  425. int direction) {
  426. return getDependency(connector.getConnectorId(), connector, direction);
  427. }
  428. private LayoutDependency getDependency(String connectorId, int direction) {
  429. return getDependency(connectorId, null, direction);
  430. }
  431. private LayoutDependency getDependency(String connectorId,
  432. ComponentConnector connector, int direction) {
  433. FastStringMap<LayoutDependency> dependencies = dependenciesInDirection[direction];
  434. LayoutDependency dependency = dependencies.get(connectorId);
  435. if (dependency == null) {
  436. if (connector == null) {
  437. connector = (ComponentConnector) ConnectorMap.get(connection)
  438. .getConnector(connectorId);
  439. if (connector == null) {
  440. getLogger().warning("No connector found for id "
  441. + connectorId + " while creating LayoutDependency");
  442. return null;
  443. }
  444. }
  445. dependency = new LayoutDependency(connector, direction);
  446. dependencies.put(connectorId, dependency);
  447. }
  448. return dependency;
  449. }
  450. private FastStringSet getLayoutQueue(int direction) {
  451. return layoutQueueInDirection[direction];
  452. }
  453. private FastStringSet getMeasureQueue(int direction) {
  454. return measureQueueInDirection[direction];
  455. }
  456. /**
  457. * @param layout
  458. * @param needsLayout
  459. *
  460. * @deprecated As of 7.0.1, use
  461. * {@link #setNeedsHorizontalLayout(String, boolean)} for
  462. * improved performance.
  463. */
  464. @Deprecated
  465. public void setNeedsHorizontalLayout(ManagedLayout layout,
  466. boolean needsLayout) {
  467. setNeedsHorizontalLayout(layout.getConnectorId(), needsLayout);
  468. }
  469. public void setNeedsHorizontalLayout(String connectorId,
  470. boolean needsLayout) {
  471. LayoutDependency dependency = getDependency(connectorId, HORIZONTAL);
  472. if (dependency != null) {
  473. dependency.setNeedsLayout(needsLayout);
  474. } else {
  475. getLogger()
  476. .warning("No dependency found in setNeedsHorizontalLayout");
  477. }
  478. }
  479. /**
  480. * @param layout
  481. * @param needsLayout
  482. *
  483. * @deprecated As of 7.0.1, use
  484. * {@link #setNeedsVerticalLayout(String, boolean)} for improved
  485. * performance.
  486. */
  487. @Deprecated
  488. public void setNeedsVerticalLayout(ManagedLayout layout,
  489. boolean needsLayout) {
  490. setNeedsVerticalLayout(layout.getConnectorId(), needsLayout);
  491. }
  492. public void setNeedsVerticalLayout(String connectorId,
  493. boolean needsLayout) {
  494. LayoutDependency dependency = getDependency(connectorId, VERTICAL);
  495. if (dependency != null) {
  496. dependency.setNeedsLayout(needsLayout);
  497. } else {
  498. getLogger()
  499. .warning("No dependency found in setNeedsVerticalLayout");
  500. }
  501. }
  502. public void markAsHorizontallyLayouted(ManagedLayout layout) {
  503. LayoutDependency dependency = getDependency(layout.getConnectorId(),
  504. HORIZONTAL);
  505. dependency.markAsLayouted();
  506. }
  507. public void markAsVerticallyLayouted(ManagedLayout layout) {
  508. LayoutDependency dependency = getDependency(layout.getConnectorId(),
  509. VERTICAL);
  510. dependency.markAsLayouted();
  511. }
  512. public void markHeightAsChanged(ComponentConnector connector) {
  513. LayoutDependency dependency = getDependency(connector.getConnectorId(),
  514. VERTICAL);
  515. dependency.markSizeAsChanged();
  516. }
  517. public void markWidthAsChanged(ComponentConnector connector) {
  518. LayoutDependency dependency = getDependency(connector.getConnectorId(),
  519. HORIZONTAL);
  520. dependency.markSizeAsChanged();
  521. }
  522. private static boolean isRelativeInDirection(ComponentConnector connector,
  523. int direction) {
  524. if (direction == HORIZONTAL) {
  525. return connector.isRelativeWidth();
  526. } else {
  527. return connector.isRelativeHeight();
  528. }
  529. }
  530. private static boolean isUndefinedInDirection(ComponentConnector connector,
  531. int direction) {
  532. if (direction == VERTICAL) {
  533. return connector.isUndefinedHeight();
  534. } else {
  535. return connector.isUndefinedWidth();
  536. }
  537. }
  538. private static String getCompactConnectorString(ServerConnector connector) {
  539. return connector.getClass().getSimpleName() + " ("
  540. + connector.getConnectorId() + ")";
  541. }
  542. private static String getSizeDefinition(String size) {
  543. if (size == null || size.isEmpty()) {
  544. return "undefined";
  545. } else if (size.endsWith("%")) {
  546. return "relative";
  547. } else {
  548. return "fixed";
  549. }
  550. }
  551. private String blockersToString(FastStringSet blockers) {
  552. StringBuilder b = new StringBuilder("[");
  553. ConnectorMap connectorMap = ConnectorMap.get(connection);
  554. JsArrayString blockersDump = blockers.dump();
  555. for (int i = 0; i < blockersDump.length(); i++) {
  556. ServerConnector blocker = connectorMap
  557. .getConnector(blockersDump.get(i));
  558. if (b.length() != 1) {
  559. b.append(", ");
  560. }
  561. b.append(getCompactConnectorString(blocker));
  562. }
  563. b.append(']');
  564. return b.toString();
  565. }
  566. public boolean hasConnectorsToMeasure() {
  567. return !measureQueueInDirection[HORIZONTAL].isEmpty()
  568. || !measureQueueInDirection[VERTICAL].isEmpty();
  569. }
  570. public boolean hasHorizontalConnectorToLayout() {
  571. return !getLayoutQueue(HORIZONTAL).isEmpty();
  572. }
  573. public boolean hasVerticaConnectorToLayout() {
  574. return !getLayoutQueue(VERTICAL).isEmpty();
  575. }
  576. /**
  577. * @return
  578. * @deprecated As of 7.0.1, use {@link #getHorizontalLayoutTargetsJsArray()}
  579. * for improved performance.
  580. */
  581. @Deprecated
  582. public ManagedLayout[] getHorizontalLayoutTargets() {
  583. return asManagedLayoutArray(getHorizontalLayoutTargetsJsArray());
  584. }
  585. /**
  586. * @return
  587. * @deprecated As of 7.0.1, use {@link #getVerticalLayoutTargetsJsArray()}
  588. * for improved performance.
  589. */
  590. @Deprecated
  591. public ManagedLayout[] getVerticalLayoutTargets() {
  592. return asManagedLayoutArray(getVerticalLayoutTargetsJsArray());
  593. }
  594. private ManagedLayout[] asManagedLayoutArray(
  595. JsArrayString connectorIdArray) {
  596. int length = connectorIdArray.length();
  597. ConnectorMap connectorMap = ConnectorMap.get(connection);
  598. ManagedLayout[] result = new ManagedLayout[length];
  599. for (int i = 0; i < length; i++) {
  600. result[i] = (ManagedLayout) connectorMap
  601. .getConnector(connectorIdArray.get(i));
  602. }
  603. return result;
  604. }
  605. public JsArrayString getHorizontalLayoutTargetsJsArray() {
  606. return getLayoutQueue(HORIZONTAL).dump();
  607. }
  608. public JsArrayString getVerticalLayoutTargetsJsArray() {
  609. return getLayoutQueue(VERTICAL).dump();
  610. }
  611. /**
  612. * @return
  613. * @deprecated As of 7.0.1, use {@link #getMeasureTargetsJsArray()} for
  614. * improved performance.
  615. */
  616. @Deprecated
  617. public Collection<ComponentConnector> getMeasureTargets() {
  618. JsArrayString targetIds = getMeasureTargetsJsArray();
  619. int length = targetIds.length();
  620. List<ComponentConnector> targets = new ArrayList<>(length);
  621. ConnectorMap connectorMap = ConnectorMap.get(connection);
  622. for (int i = 0; i < length; i++) {
  623. targets.add((ComponentConnector) connectorMap
  624. .getConnector(targetIds.get(i)));
  625. }
  626. return targets;
  627. }
  628. public JsArrayString getMeasureTargetsJsArray() {
  629. FastStringSet allMeasuredTargets = FastStringSet.create();
  630. allMeasuredTargets.addAll(getMeasureQueue(HORIZONTAL));
  631. allMeasuredTargets.addAll(getMeasureQueue(VERTICAL));
  632. return allMeasuredTargets.dump();
  633. }
  634. public void logDependencyStatus(ComponentConnector connector) {
  635. getLogger().info("====");
  636. String connectorId = connector.getConnectorId();
  637. getLogger().info(getDependency(connectorId, HORIZONTAL).toString());
  638. getLogger().info(getDependency(connectorId, VERTICAL).toString());
  639. }
  640. public boolean noMoreChangesExpected(ComponentConnector connector) {
  641. return getDependency(connector.getConnectorId(), HORIZONTAL)
  642. .noMoreChangesExpected()
  643. && getDependency(connector.getConnectorId(), VERTICAL)
  644. .noMoreChangesExpected();
  645. }
  646. public ComponentConnector getScrollingBoundary(
  647. ComponentConnector connector) {
  648. LayoutDependency dependency = getDependency(connector.getConnectorId(),
  649. HORIZONTAL);
  650. if (!dependency.scrollingParentCached) {
  651. ServerConnector parent = dependency.connector.getParent();
  652. if (parent instanceof MayScrollChildren) {
  653. dependency.scrollingBoundary = connector;
  654. } else if (parent instanceof ComponentConnector) {
  655. dependency.scrollingBoundary = getScrollingBoundary(
  656. (ComponentConnector) parent);
  657. } else {
  658. // No scrolling parent
  659. }
  660. dependency.scrollingParentCached = true;
  661. }
  662. return dependency.scrollingBoundary;
  663. }
  664. private static Logger getLogger() {
  665. return Logger.getLogger(LayoutDependencyTree.class.getName());
  666. }
  667. }