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

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