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

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