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.

LayoutDependencyTree.java 29KB

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