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.

VOrderedLayout.java 32KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918
  1. package com.vaadin.terminal.gwt.client.ui;
  2. import java.util.ArrayList;
  3. import java.util.Iterator;
  4. import java.util.Set;
  5. import com.google.gwt.core.client.JsArrayString;
  6. import com.google.gwt.user.client.ui.Widget;
  7. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  8. import com.vaadin.terminal.gwt.client.BrowserInfo;
  9. import com.vaadin.terminal.gwt.client.Paintable;
  10. import com.vaadin.terminal.gwt.client.RenderSpace;
  11. import com.vaadin.terminal.gwt.client.UIDL;
  12. import com.vaadin.terminal.gwt.client.Util;
  13. import com.vaadin.terminal.gwt.client.ValueMap;
  14. import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize;
  15. import com.vaadin.terminal.gwt.client.RenderInformation.Size;
  16. import com.vaadin.terminal.gwt.client.ui.layout.CellBasedLayout;
  17. import com.vaadin.terminal.gwt.client.ui.layout.ChildComponentContainer;
  18. public class VOrderedLayout extends CellBasedLayout {
  19. public static final String CLASSNAME = "v-orderedlayout";
  20. private int orientation;
  21. // Can be removed once OrderedLayout is removed
  22. private boolean allowOrientationUpdate = false;
  23. /**
  24. * Size of the layout excluding any margins.
  25. */
  26. private Size activeLayoutSize = new Size(0, 0);
  27. private boolean isRendering = false;
  28. private String width = "";
  29. private boolean sizeHasChangedDuringRendering = false;
  30. private ValueMap expandRatios;
  31. private double expandRatioSum;
  32. private double defaultExpandRatio;
  33. private ValueMap alignments;
  34. public VOrderedLayout() {
  35. this(CLASSNAME, ORIENTATION_VERTICAL);
  36. allowOrientationUpdate = true;
  37. }
  38. protected VOrderedLayout(String className, int orientation) {
  39. setStyleName(className);
  40. this.orientation = orientation;
  41. STYLENAME_SPACING = className + "-spacing";
  42. STYLENAME_MARGIN_TOP = className + "-margin-top";
  43. STYLENAME_MARGIN_RIGHT = className + "-margin-right";
  44. STYLENAME_MARGIN_BOTTOM = className + "-margin-bottom";
  45. STYLENAME_MARGIN_LEFT = className + "-margin-left";
  46. }
  47. @Override
  48. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  49. isRendering = true;
  50. super.updateFromUIDL(uidl, client);
  51. // Only non-cached, visible UIDL:s can introduce changes
  52. if (uidl.getBooleanAttribute("cached")
  53. || uidl.getBooleanAttribute("invisible")) {
  54. isRendering = false;
  55. return;
  56. }
  57. if (allowOrientationUpdate) {
  58. handleOrientationUpdate(uidl);
  59. }
  60. // IStopWatch w = new IStopWatch("OrderedLayout.updateFromUIDL");
  61. ArrayList<Widget> uidlWidgets = new ArrayList<Widget>(uidl
  62. .getChildCount());
  63. ArrayList<ChildComponentContainer> relativeSizeComponents = new ArrayList<ChildComponentContainer>();
  64. ArrayList<UIDL> relativeSizeComponentUIDL = new ArrayList<UIDL>();
  65. int pos = 0;
  66. for (final Iterator<Object> it = uidl.getChildIterator(); it.hasNext();) {
  67. final UIDL childUIDL = (UIDL) it.next();
  68. final Paintable child = client.getPaintable(childUIDL);
  69. Widget widget = (Widget) child;
  70. // Create container for component
  71. ChildComponentContainer childComponentContainer = getComponentContainer(widget);
  72. if (childComponentContainer == null) {
  73. // This is a new component
  74. childComponentContainer = createChildContainer(widget);
  75. }
  76. addOrMoveChild(childComponentContainer, pos++);
  77. /*
  78. * Components which are to be expanded in the same orientation as
  79. * the layout are rendered later when it is clear how much space
  80. * they can use
  81. */
  82. if (!Util.isCached(childUIDL)) {
  83. FloatSize relativeSize = Util.parseRelativeSize(childUIDL);
  84. childComponentContainer.setRelativeSize(relativeSize);
  85. }
  86. if (childComponentContainer.isComponentRelativeSized(orientation)) {
  87. relativeSizeComponents.add(childComponentContainer);
  88. relativeSizeComponentUIDL.add(childUIDL);
  89. } else {
  90. if (isDynamicWidth()) {
  91. childComponentContainer.renderChild(childUIDL, client, 0);
  92. } else {
  93. childComponentContainer.renderChild(childUIDL, client,
  94. activeLayoutSize.getWidth());
  95. }
  96. if (sizeHasChangedDuringRendering && Util.isCached(childUIDL)) {
  97. // notify cached relative sized component about size
  98. // chance
  99. client.handleComponentRelativeSize(childComponentContainer
  100. .getWidget());
  101. }
  102. }
  103. uidlWidgets.add(widget);
  104. }
  105. // w.mark("Rendering of "
  106. // + (uidlWidgets.size() - relativeSizeComponents.size())
  107. // + " absolute size components done");
  108. /*
  109. * Remove any children after pos. These are the ones that previously
  110. * were in the layout but have now been removed
  111. */
  112. removeChildrenAfter(pos);
  113. // w.mark("Old children removed");
  114. /* Fetch alignments and expand ratio from UIDL */
  115. updateAlignmentsAndExpandRatios(uidl, uidlWidgets);
  116. // w.mark("Alignments and expand ratios updated");
  117. /* Fetch widget sizes from rendered components */
  118. updateWidgetSizes();
  119. // w.mark("Widget sizes updated");
  120. recalculateLayout();
  121. // w.mark("Layout size calculated (" + activeLayoutSize +
  122. // ") offsetSize: "
  123. // + getOffsetWidth() + "," + getOffsetHeight());
  124. /* Render relative size components */
  125. for (int i = 0; i < relativeSizeComponents.size(); i++) {
  126. ChildComponentContainer childComponentContainer = relativeSizeComponents
  127. .get(i);
  128. UIDL childUIDL = relativeSizeComponentUIDL.get(i);
  129. if (isDynamicWidth()) {
  130. childComponentContainer.renderChild(childUIDL, client, 0);
  131. } else {
  132. childComponentContainer.renderChild(childUIDL, client,
  133. activeLayoutSize.getWidth());
  134. }
  135. if (Util.isCached(childUIDL)) {
  136. /*
  137. * We must update the size of the relative sized component if
  138. * the expand ratio or something else in the layout changes
  139. * which affects the size of a relative sized component
  140. */
  141. client.handleComponentRelativeSize(childComponentContainer
  142. .getWidget());
  143. }
  144. // childComponentContainer.updateWidgetSize();
  145. }
  146. // w.mark("Rendering of " + (relativeSizeComponents.size())
  147. // + " relative size components done");
  148. /* Fetch widget sizes for relative size components */
  149. for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
  150. .values()) {
  151. /* Update widget size from DOM */
  152. childComponentContainer.updateWidgetSize();
  153. }
  154. // w.mark("Widget sizes updated");
  155. /*
  156. * Components with relative size in main direction may affect the layout
  157. * size in the other direction
  158. */
  159. if ((isHorizontal() && isDynamicHeight())
  160. || (isVertical() && isDynamicWidth())) {
  161. layoutSizeMightHaveChanged();
  162. }
  163. // w.mark("Layout dimensions updated");
  164. /* Update component spacing */
  165. updateContainerMargins();
  166. /*
  167. * Update component sizes for components with relative size in non-main
  168. * direction
  169. */
  170. if (updateRelativeSizesInNonMainDirection()) {
  171. // Sizes updated - might affect the other dimension so we need to
  172. // recheck the widget sizes and recalculate layout dimensions
  173. updateWidgetSizes();
  174. layoutSizeMightHaveChanged();
  175. }
  176. calculateAlignments();
  177. // w.mark("recalculateComponentSizesAndAlignments done");
  178. setRootSize();
  179. if (BrowserInfo.get().isIE()) {
  180. /*
  181. * This should fix the issue with padding not always taken into
  182. * account for the containers leading to no spacing between
  183. * elements.
  184. */
  185. root.getStyle().setProperty("zoom", "1");
  186. }
  187. // w.mark("runDescendentsLayout done");
  188. isRendering = false;
  189. sizeHasChangedDuringRendering = false;
  190. }
  191. private void layoutSizeMightHaveChanged() {
  192. Size oldSize = new Size(activeLayoutSize.getWidth(), activeLayoutSize
  193. .getHeight());
  194. calculateLayoutDimensions();
  195. /*
  196. * If layout dimension changes we must also update container sizes
  197. */
  198. if (!oldSize.equals(activeLayoutSize)) {
  199. calculateContainerSize();
  200. }
  201. }
  202. private void updateWidgetSizes() {
  203. for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
  204. .values()) {
  205. /*
  206. * Update widget size from DOM
  207. */
  208. childComponentContainer.updateWidgetSize();
  209. }
  210. }
  211. private void recalculateLayout() {
  212. /* Calculate space for relative size components */
  213. int spaceForExpansion = calculateLayoutDimensions();
  214. if (!widgetToComponentContainer.isEmpty()) {
  215. /* Divide expansion space between component containers */
  216. expandComponentContainers(spaceForExpansion);
  217. /* Update container sizes */
  218. calculateContainerSize();
  219. }
  220. }
  221. private void expandComponentContainers(int spaceForExpansion) {
  222. int remaining = spaceForExpansion;
  223. for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
  224. .values()) {
  225. remaining -= childComponentContainer.expand(orientation,
  226. spaceForExpansion);
  227. }
  228. if (remaining > 0) {
  229. // Some left-over pixels due to rounding errors
  230. // Add one pixel to each container until there are no pixels left
  231. // FIXME extra pixels should be divided among expanded widgets if
  232. // such a widgets exists
  233. Iterator<Widget> widgetIterator = iterator();
  234. while (widgetIterator.hasNext() && remaining-- > 0) {
  235. ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator
  236. .next();
  237. childComponentContainer.expandExtra(orientation, 1);
  238. }
  239. }
  240. }
  241. private void handleOrientationUpdate(UIDL uidl) {
  242. int newOrientation = ORIENTATION_VERTICAL;
  243. if ("horizontal".equals(uidl.getStringAttribute("orientation"))) {
  244. newOrientation = ORIENTATION_HORIZONTAL;
  245. }
  246. if (orientation != newOrientation) {
  247. orientation = newOrientation;
  248. for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
  249. .values()) {
  250. childComponentContainer.setOrientation(orientation);
  251. }
  252. }
  253. }
  254. /**
  255. * Updated components with relative height in horizontal layouts and
  256. * components with relative width in vertical layouts. This is only needed
  257. * if the height (horizontal layout) or width (vertical layout) has not been
  258. * specified.
  259. */
  260. private boolean updateRelativeSizesInNonMainDirection() {
  261. int updateDirection = 1 - orientation;
  262. if ((updateDirection == ORIENTATION_HORIZONTAL && !isDynamicWidth())
  263. || (updateDirection == ORIENTATION_VERTICAL && !isDynamicHeight())) {
  264. return false;
  265. }
  266. boolean updated = false;
  267. for (ChildComponentContainer componentContainer : widgetToComponentContainer
  268. .values()) {
  269. if (componentContainer.isComponentRelativeSized(updateDirection)) {
  270. client.handleComponentRelativeSize(componentContainer
  271. .getWidget());
  272. }
  273. updated = true;
  274. }
  275. return updated;
  276. }
  277. private int calculateLayoutDimensions() {
  278. int summedWidgetWidth = 0;
  279. int summedWidgetHeight = 0;
  280. int maxWidgetWidth = 0;
  281. int maxWidgetHeight = 0;
  282. // Calculate layout dimensions from component dimensions
  283. for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
  284. .values()) {
  285. int widgetHeight = 0;
  286. int widgetWidth = 0;
  287. if (childComponentContainer.isComponentRelativeSized(orientation)) {
  288. if (orientation == ORIENTATION_HORIZONTAL) {
  289. widgetHeight = getWidgetHeight(childComponentContainer);
  290. } else {
  291. widgetWidth = getWidgetWidth(childComponentContainer);
  292. }
  293. } else {
  294. widgetWidth = getWidgetWidth(childComponentContainer);
  295. widgetHeight = getWidgetHeight(childComponentContainer);
  296. }
  297. summedWidgetWidth += widgetWidth;
  298. summedWidgetHeight += widgetHeight;
  299. maxWidgetHeight = Math.max(maxWidgetHeight, widgetHeight);
  300. maxWidgetWidth = Math.max(maxWidgetWidth, widgetWidth);
  301. }
  302. if (isHorizontal()) {
  303. summedWidgetWidth += activeSpacing.hSpacing
  304. * (widgetToComponentContainer.size() - 1);
  305. } else {
  306. summedWidgetHeight += activeSpacing.vSpacing
  307. * (widgetToComponentContainer.size() - 1);
  308. }
  309. Size layoutSize = updateLayoutDimensions(summedWidgetWidth,
  310. summedWidgetHeight, maxWidgetWidth, maxWidgetHeight);
  311. int remainingSpace;
  312. if (isHorizontal()) {
  313. remainingSpace = layoutSize.getWidth() - summedWidgetWidth;
  314. } else {
  315. remainingSpace = layoutSize.getHeight() - summedWidgetHeight;
  316. }
  317. if (remainingSpace < 0) {
  318. remainingSpace = 0;
  319. }
  320. // ApplicationConnection.getConsole().log(
  321. // "Layout size: " + activeLayoutSize);
  322. return remainingSpace;
  323. }
  324. private int getWidgetHeight(ChildComponentContainer childComponentContainer) {
  325. Size s = childComponentContainer.getWidgetSize();
  326. return s.getHeight()
  327. + childComponentContainer.getCaptionHeightAboveComponent();
  328. }
  329. private int getWidgetWidth(ChildComponentContainer childComponentContainer) {
  330. Size s = childComponentContainer.getWidgetSize();
  331. int widgetWidth = s.getWidth()
  332. + childComponentContainer.getCaptionWidthAfterComponent();
  333. /*
  334. * If the component does not have a specified size in the main direction
  335. * the caption may determine the space used by the component
  336. */
  337. if (!childComponentContainer.widgetHasSizeSpecified(orientation)) {
  338. int captionWidth = childComponentContainer
  339. .getCaptionRequiredWidth();
  340. if (captionWidth > widgetWidth) {
  341. widgetWidth = captionWidth;
  342. }
  343. }
  344. return widgetWidth;
  345. }
  346. private void calculateAlignments() {
  347. int w = 0;
  348. int h = 0;
  349. if (isHorizontal()) {
  350. // HORIZONTAL
  351. h = activeLayoutSize.getHeight();
  352. if (!isDynamicWidth()) {
  353. w = -1;
  354. }
  355. } else {
  356. // VERTICAL
  357. w = activeLayoutSize.getWidth();
  358. if (!isDynamicHeight()) {
  359. h = -1;
  360. }
  361. }
  362. for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
  363. .values()) {
  364. childComponentContainer.updateAlignments(w, h);
  365. }
  366. }
  367. private void calculateContainerSize() {
  368. /*
  369. * Container size here means the size the container gets from the
  370. * component. The expansion size is not include in this but taken
  371. * separately into account.
  372. */
  373. int height = 0, width = 0;
  374. Iterator<Widget> widgetIterator = iterator();
  375. if (isHorizontal()) {
  376. height = activeLayoutSize.getHeight();
  377. int availableWidth = activeLayoutSize.getWidth();
  378. boolean first = true;
  379. while (widgetIterator.hasNext()) {
  380. ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator
  381. .next();
  382. if (!childComponentContainer
  383. .isComponentRelativeSized(ORIENTATION_HORIZONTAL)) {
  384. /*
  385. * Only components with non-relative size in the main
  386. * direction has a container size
  387. */
  388. width = childComponentContainer.getWidgetSize().getWidth()
  389. + childComponentContainer
  390. .getCaptionWidthAfterComponent();
  391. /*
  392. * If the component does not have a specified size in the
  393. * main direction the caption may determine the space used
  394. * by the component
  395. */
  396. if (!childComponentContainer
  397. .widgetHasSizeSpecified(orientation)) {
  398. int captionWidth = childComponentContainer
  399. .getCaptionRequiredWidth();
  400. // ApplicationConnection.getConsole().log(
  401. // "Component width: " + width
  402. // + ", caption width: " + captionWidth);
  403. if (captionWidth > width) {
  404. width = captionWidth;
  405. }
  406. }
  407. } else {
  408. width = 0;
  409. }
  410. if (!isDynamicWidth()) {
  411. if (availableWidth == 0) {
  412. /*
  413. * Let the overflowing components overflow. IE has
  414. * problems with zero sizes.
  415. */
  416. // width = 0;
  417. // height = 0;
  418. } else if (width > availableWidth) {
  419. width = availableWidth;
  420. if (!first) {
  421. width -= activeSpacing.hSpacing;
  422. }
  423. availableWidth = 0;
  424. } else {
  425. availableWidth -= width;
  426. if (!first) {
  427. availableWidth -= activeSpacing.hSpacing;
  428. }
  429. }
  430. first = false;
  431. }
  432. childComponentContainer.setContainerSize(width, height);
  433. }
  434. } else {
  435. width = activeLayoutSize.getWidth();
  436. while (widgetIterator.hasNext()) {
  437. ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator
  438. .next();
  439. if (!childComponentContainer
  440. .isComponentRelativeSized(ORIENTATION_VERTICAL)) {
  441. /*
  442. * Only components with non-relative size in the main
  443. * direction has a container size
  444. */
  445. height = childComponentContainer.getWidgetSize()
  446. .getHeight()
  447. + childComponentContainer
  448. .getCaptionHeightAboveComponent();
  449. } else {
  450. height = 0;
  451. }
  452. childComponentContainer.setContainerSize(width, height);
  453. }
  454. }
  455. }
  456. private Size updateLayoutDimensions(int totalComponentWidth,
  457. int totalComponentHeight, int maxComponentWidth,
  458. int maxComponentHeight) {
  459. /* Only need to calculate dynamic dimensions */
  460. if (!isDynamicHeight() && !isDynamicWidth()) {
  461. return activeLayoutSize;
  462. }
  463. int activeLayoutWidth = 0;
  464. int activeLayoutHeight = 0;
  465. // Update layout dimensions
  466. if (isHorizontal()) {
  467. // Horizontal
  468. if (isDynamicWidth()) {
  469. activeLayoutWidth = totalComponentWidth;
  470. }
  471. if (isDynamicHeight()) {
  472. activeLayoutHeight = maxComponentHeight;
  473. }
  474. } else {
  475. // Vertical
  476. if (isDynamicWidth()) {
  477. activeLayoutWidth = maxComponentWidth;
  478. }
  479. if (isDynamicHeight()) {
  480. activeLayoutHeight = totalComponentHeight;
  481. }
  482. }
  483. if (isDynamicWidth()) {
  484. setActiveLayoutWidth(activeLayoutWidth);
  485. setOuterLayoutWidth(activeLayoutSize.getWidth());
  486. }
  487. if (isDynamicHeight()) {
  488. setActiveLayoutHeight(activeLayoutHeight);
  489. setOuterLayoutHeight(activeLayoutSize.getHeight());
  490. }
  491. return activeLayoutSize;
  492. }
  493. private void setActiveLayoutWidth(int activeLayoutWidth) {
  494. if (activeLayoutWidth < 0) {
  495. activeLayoutWidth = 0;
  496. }
  497. activeLayoutSize.setWidth(activeLayoutWidth);
  498. }
  499. private void setActiveLayoutHeight(int activeLayoutHeight) {
  500. if (activeLayoutHeight < 0) {
  501. activeLayoutHeight = 0;
  502. }
  503. activeLayoutSize.setHeight(activeLayoutHeight);
  504. }
  505. private void setOuterLayoutWidth(int activeLayoutWidth) {
  506. super.setWidth((activeLayoutWidth + activeMargins.getHorizontal())
  507. + "px");
  508. }
  509. private void setOuterLayoutHeight(int activeLayoutHeight) {
  510. super.setHeight((activeLayoutHeight + activeMargins.getVertical())
  511. + "px");
  512. }
  513. /**
  514. * Updates the spacing between components. Needs to be done only when
  515. * components are added/removed.
  516. */
  517. private void updateContainerMargins() {
  518. ChildComponentContainer firstChildComponent = getFirstChildComponentContainer();
  519. if (firstChildComponent != null) {
  520. firstChildComponent.setMarginLeft(0);
  521. firstChildComponent.setMarginTop(0);
  522. for (ChildComponentContainer childComponent : widgetToComponentContainer
  523. .values()) {
  524. if (childComponent == firstChildComponent) {
  525. continue;
  526. }
  527. if (isHorizontal()) {
  528. childComponent.setMarginLeft(activeSpacing.hSpacing);
  529. } else {
  530. childComponent.setMarginTop(activeSpacing.vSpacing);
  531. }
  532. }
  533. }
  534. }
  535. private boolean isHorizontal() {
  536. return orientation == ORIENTATION_HORIZONTAL;
  537. }
  538. private boolean isVertical() {
  539. return orientation == ORIENTATION_VERTICAL;
  540. }
  541. private ChildComponentContainer createChildContainer(Widget child) {
  542. // Create a container DIV for the child
  543. ChildComponentContainer childComponent = new ChildComponentContainer(
  544. child, orientation);
  545. return childComponent;
  546. }
  547. public RenderSpace getAllocatedSpace(Widget child) {
  548. int width = 0;
  549. int height = 0;
  550. ChildComponentContainer childComponentContainer = getComponentContainer(child);
  551. // WIDTH CALCULATION
  552. if (isVertical()) {
  553. width = activeLayoutSize.getWidth();
  554. width -= childComponentContainer.getCaptionWidthAfterComponent();
  555. } else if (!isDynamicWidth()) {
  556. // HORIZONTAL
  557. width = childComponentContainer.getContSize().getWidth();
  558. width -= childComponentContainer.getCaptionWidthAfterComponent();
  559. }
  560. // HEIGHT CALCULATION
  561. if (isHorizontal()) {
  562. height = activeLayoutSize.getHeight();
  563. height -= childComponentContainer.getCaptionHeightAboveComponent();
  564. } else if (!isDynamicHeight()) {
  565. // VERTICAL
  566. height = childComponentContainer.getContSize().getHeight();
  567. height -= childComponentContainer.getCaptionHeightAboveComponent();
  568. }
  569. // ApplicationConnection.getConsole().log(
  570. // "allocatedSpace for " + Util.getSimpleName(child) + ": "
  571. // + width + "," + height);
  572. RenderSpace space = new RenderSpace(width, height);
  573. return space;
  574. }
  575. private void recalculateLayoutAndComponentSizes() {
  576. recalculateLayout();
  577. if (!(isDynamicHeight() && isDynamicWidth())) {
  578. /* First update relative sized components */
  579. for (ChildComponentContainer componentContainer : widgetToComponentContainer
  580. .values()) {
  581. client.handleComponentRelativeSize(componentContainer
  582. .getWidget());
  583. // Update widget size from DOM
  584. componentContainer.updateWidgetSize();
  585. }
  586. }
  587. if (isDynamicHeight()) {
  588. /*
  589. * Height is not necessarily correct anymore as the height of
  590. * components might have changed if the width has changed.
  591. */
  592. /*
  593. * Get the new widget sizes from DOM and calculate new container
  594. * sizes
  595. */
  596. updateWidgetSizes();
  597. /* Update layout dimensions based on widget sizes */
  598. recalculateLayout();
  599. }
  600. updateRelativeSizesInNonMainDirection();
  601. calculateAlignments();
  602. setRootSize();
  603. }
  604. private void setRootSize() {
  605. root.getStyle().setPropertyPx("width", activeLayoutSize.getWidth());
  606. root.getStyle().setPropertyPx("height", activeLayoutSize.getHeight());
  607. }
  608. public boolean requestLayout(Set<Paintable> children) {
  609. for (Paintable p : children) {
  610. /* Update widget size from DOM */
  611. ChildComponentContainer componentContainer = getComponentContainer((Widget) p);
  612. // This should no longer be needed (after #2563)
  613. // if (isDynamicWidth()) {
  614. // componentContainer.setUnlimitedContainerWidth();
  615. // } else {
  616. // componentContainer.setLimitedContainerWidth(activeLayoutSize
  617. // .getWidth());
  618. // }
  619. componentContainer.updateWidgetSize();
  620. /*
  621. * If this is the result of an caption icon onload event the caption
  622. * size may have changed
  623. */
  624. componentContainer.updateCaptionSize();
  625. }
  626. Size sizeBefore = new Size(activeLayoutSize.getWidth(),
  627. activeLayoutSize.getHeight());
  628. recalculateLayoutAndComponentSizes();
  629. boolean sameSize = (sizeBefore.equals(activeLayoutSize));
  630. if (!sameSize) {
  631. /* Must inform child components about possible size updates */
  632. client.runDescendentsLayout(this);
  633. }
  634. /* Automatically propagated upwards if the size has changed */
  635. return sameSize;
  636. }
  637. @Override
  638. public void setHeight(String height) {
  639. Size sizeBefore = new Size(activeLayoutSize.getWidth(),
  640. activeLayoutSize.getHeight());
  641. super.setHeight(height);
  642. if (height != null && !height.equals("")) {
  643. setActiveLayoutHeight(getOffsetHeight()
  644. - activeMargins.getVertical());
  645. }
  646. if (isRendering) {
  647. sizeHasChangedDuringRendering = true;
  648. } else {
  649. recalculateLayoutAndComponentSizes();
  650. boolean sameSize = (sizeBefore.equals(activeLayoutSize));
  651. if (!sameSize) {
  652. /* Must inform child components about possible size updates */
  653. client.runDescendentsLayout(this);
  654. }
  655. }
  656. }
  657. @Override
  658. public void setWidth(String width) {
  659. if (this.width.equals(width) || !isVisible()) {
  660. return;
  661. }
  662. Size sizeBefore = new Size(activeLayoutSize.getWidth(),
  663. activeLayoutSize.getHeight());
  664. super.setWidth(width);
  665. this.width = width;
  666. if (width != null && !width.equals("")) {
  667. setActiveLayoutWidth(getOffsetWidth()
  668. - activeMargins.getHorizontal());
  669. }
  670. if (isRendering) {
  671. sizeHasChangedDuringRendering = true;
  672. } else {
  673. recalculateLayoutAndComponentSizes();
  674. boolean sameSize = (sizeBefore.equals(activeLayoutSize));
  675. if (!sameSize) {
  676. /* Must inform child components about possible size updates */
  677. client.runDescendentsLayout(this);
  678. }
  679. /*
  680. * If the height changes as a consequence of this we must inform the
  681. * parent also
  682. */
  683. if (isDynamicHeight()
  684. && sizeBefore.getHeight() != activeLayoutSize.getHeight()) {
  685. Util.notifyParentOfSizeChange(this, false);
  686. }
  687. }
  688. }
  689. protected void updateAlignmentsAndExpandRatios(UIDL uidl,
  690. ArrayList<Widget> renderedWidgets) {
  691. /*
  692. */
  693. alignments = uidl.getMapAttribute("alignments");
  694. /*
  695. * UIDL contains a map of paintable ids to expand ratios
  696. */
  697. expandRatios = uidl.getMapAttribute("expandRatios");
  698. expandRatioSum = -1.0;
  699. for (int i = 0; i < renderedWidgets.size(); i++) {
  700. Widget widget = renderedWidgets.get(i);
  701. String pid = client.getPid(widget.getElement());
  702. ChildComponentContainer container = getComponentContainer(widget);
  703. // Calculate alignment info
  704. container.setAlignment(getAlignment(pid));
  705. // Update expand ratio
  706. container.setNormalizedExpandRatio(getExpandRatio(pid));
  707. }
  708. }
  709. private AlignmentInfo getAlignment(String pid) {
  710. if (alignments.containsKey(pid)) {
  711. return new AlignmentInfo(alignments.getInt(pid));
  712. } else {
  713. return AlignmentInfo.TOP_LEFT;
  714. }
  715. }
  716. private double getExpandRatio(String pid) {
  717. if (expandRatioSum < 0) {
  718. expandRatioSum = 0;
  719. JsArrayString keyArray = expandRatios.getKeyArray();
  720. int length = keyArray.length();
  721. for (int i = 0; i < length; i++) {
  722. expandRatioSum += expandRatios.getRawNumber(keyArray.get(i));
  723. }
  724. if (expandRatioSum == 0) {
  725. // by default split equally among components
  726. defaultExpandRatio = 1.0 / widgetToComponentContainer.size();
  727. } else {
  728. defaultExpandRatio = 0;
  729. }
  730. }
  731. if (expandRatios.containsKey(pid)) {
  732. return expandRatios.getRawNumber(pid) / expandRatioSum;
  733. } else {
  734. return defaultExpandRatio;
  735. }
  736. }
  737. public void updateCaption(Paintable component, UIDL uidl) {
  738. ChildComponentContainer componentContainer = getComponentContainer((Widget) component);
  739. componentContainer.updateCaption(uidl, client);
  740. if (!isRendering) {
  741. /*
  742. * This was a component-only update and the possible size change
  743. * must be propagated to the layout
  744. */
  745. client.captionSizeUpdated(component);
  746. }
  747. }
  748. }