Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

VOrderedLayout.java 34KB

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