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

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