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 33KB

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