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.

AbstractOrderedLayoutConnector.java 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client.ui.orderedlayout;
  17. import java.util.List;
  18. import com.google.gwt.core.client.Scheduler;
  19. import com.google.gwt.core.client.Scheduler.ScheduledCommand;
  20. import com.google.gwt.dom.client.Element;
  21. import com.google.gwt.dom.client.Style.Unit;
  22. import com.google.gwt.user.client.ui.Widget;
  23. import com.vaadin.client.ApplicationConnection;
  24. import com.vaadin.client.ComponentConnector;
  25. import com.vaadin.client.ConnectorHierarchyChangeEvent;
  26. import com.vaadin.client.LayoutManager;
  27. import com.vaadin.client.Profiler;
  28. import com.vaadin.client.ServerConnector;
  29. import com.vaadin.client.TooltipInfo;
  30. import com.vaadin.client.Util;
  31. import com.vaadin.client.WidgetUtil;
  32. import com.vaadin.client.communication.StateChangeEvent;
  33. import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
  34. import com.vaadin.client.ui.AbstractFieldConnector;
  35. import com.vaadin.client.ui.AbstractLayoutConnector;
  36. import com.vaadin.client.ui.Icon;
  37. import com.vaadin.client.ui.LayoutClickEventHandler;
  38. import com.vaadin.client.ui.aria.AriaHelper;
  39. import com.vaadin.client.ui.layout.ElementResizeEvent;
  40. import com.vaadin.client.ui.layout.ElementResizeListener;
  41. import com.vaadin.shared.AbstractFieldState;
  42. import com.vaadin.shared.ComponentConstants;
  43. import com.vaadin.shared.communication.URLReference;
  44. import com.vaadin.shared.ui.AlignmentInfo;
  45. import com.vaadin.shared.ui.LayoutClickRpc;
  46. import com.vaadin.shared.ui.MarginInfo;
  47. import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutServerRpc;
  48. import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutState;
  49. /**
  50. * Base class for vertical and horizontal ordered layouts
  51. */
  52. public abstract class AbstractOrderedLayoutConnector extends
  53. AbstractLayoutConnector {
  54. /*
  55. * Handlers & Listeners
  56. */
  57. private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler(
  58. this) {
  59. @Override
  60. protected ComponentConnector getChildComponent(
  61. com.google.gwt.user.client.Element element) {
  62. return Util.getConnectorForElement(getConnection(), getWidget(),
  63. element);
  64. }
  65. @Override
  66. protected LayoutClickRpc getLayoutClickRPC() {
  67. return getRpcProxy(AbstractOrderedLayoutServerRpc.class);
  68. }
  69. };
  70. private StateChangeHandler childStateChangeHandler = new StateChangeHandler() {
  71. @Override
  72. public void onStateChanged(StateChangeEvent stateChangeEvent) {
  73. // Child state has changed, update stuff it hasn't already been done
  74. updateInternalState();
  75. /*
  76. * Some changes must always be done after each child's own state
  77. * change handler has been run because it might have changed some
  78. * styles that are overridden here.
  79. */
  80. ServerConnector child = stateChangeEvent.getConnector();
  81. if (child instanceof ComponentConnector) {
  82. ComponentConnector component = (ComponentConnector) child;
  83. Slot slot = getWidget().getSlot(component.getWidget());
  84. slot.setRelativeWidth(component.isRelativeWidth());
  85. slot.setRelativeHeight(component.isRelativeHeight());
  86. }
  87. }
  88. };
  89. private ElementResizeListener slotCaptionResizeListener = new ElementResizeListener() {
  90. @Override
  91. public void onElementResize(ElementResizeEvent e) {
  92. // Get all needed element references
  93. Element captionElement = e.getElement();
  94. // Caption position determines if the widget element is the first or
  95. // last child inside the caption wrap
  96. CaptionPosition pos = getWidget().getCaptionPositionFromElement(
  97. captionElement.getParentElement());
  98. // The default is the last child
  99. Element widgetElement = captionElement.getParentElement()
  100. .getLastChild().cast();
  101. // ...but if caption position is bottom or right, the widget is the
  102. // first child
  103. if (pos == CaptionPosition.BOTTOM || pos == CaptionPosition.RIGHT) {
  104. widgetElement = captionElement.getParentElement()
  105. .getFirstChildElement().cast();
  106. }
  107. if (captionElement == widgetElement) {
  108. // Caption element already detached
  109. Slot slot = getWidget().getSlot(widgetElement);
  110. if (slot != null) {
  111. slot.setCaptionResizeListener(null);
  112. }
  113. return;
  114. }
  115. String widgetWidth = widgetElement.getStyle().getWidth();
  116. String widgetHeight = widgetElement.getStyle().getHeight();
  117. if (widgetHeight.endsWith("%")
  118. && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
  119. getWidget().updateCaptionOffset(captionElement);
  120. } else if (widgetWidth.endsWith("%")
  121. && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT)) {
  122. getWidget().updateCaptionOffset(captionElement);
  123. }
  124. updateLayoutHeight();
  125. if (needsExpand()) {
  126. getWidget().updateExpandCompensation();
  127. }
  128. }
  129. };
  130. private ElementResizeListener childComponentResizeListener = new ElementResizeListener() {
  131. @Override
  132. public void onElementResize(ElementResizeEvent e) {
  133. updateLayoutHeight();
  134. if (needsExpand()) {
  135. getWidget().updateExpandCompensation();
  136. }
  137. }
  138. };
  139. private ElementResizeListener spacingResizeListener = new ElementResizeListener() {
  140. @Override
  141. public void onElementResize(ElementResizeEvent e) {
  142. if (needsExpand()) {
  143. getWidget().updateExpandCompensation();
  144. }
  145. }
  146. };
  147. /*
  148. * (non-Javadoc)
  149. *
  150. * @see com.vaadin.client.ui.AbstractComponentConnector#init()
  151. */
  152. @Override
  153. public void init() {
  154. super.init();
  155. getWidget().setLayoutManager(getLayoutManager());
  156. }
  157. /*
  158. * (non-Javadoc)
  159. *
  160. * @see com.vaadin.client.ui.AbstractLayoutConnector#getState()
  161. */
  162. @Override
  163. public AbstractOrderedLayoutState getState() {
  164. return (AbstractOrderedLayoutState) super.getState();
  165. }
  166. /*
  167. * (non-Javadoc)
  168. *
  169. * @see com.vaadin.client.ui.AbstractComponentConnector#getWidget()
  170. */
  171. @Override
  172. public VAbstractOrderedLayout getWidget() {
  173. return (VAbstractOrderedLayout) super.getWidget();
  174. }
  175. /**
  176. * Keep track of whether any child has relative height. Used to determine
  177. * whether measurements are needed to make relative child heights work
  178. * together with undefined container height.
  179. */
  180. private boolean hasChildrenWithRelativeHeight = false;
  181. /**
  182. * Keep track of whether any child has relative width. Used to determine
  183. * whether measurements are needed to make relative child widths work
  184. * together with undefined container width.
  185. */
  186. private boolean hasChildrenWithRelativeWidth = false;
  187. /**
  188. * Keep track of whether any child is middle aligned. Used to determine if
  189. * measurements are needed to make middle aligned children work.
  190. */
  191. private boolean hasChildrenWithMiddleAlignment = false;
  192. /**
  193. * Keeps track of whether slots should be expanded based on available space.
  194. */
  195. private boolean needsExpand = false;
  196. /**
  197. * The id of the previous response for which state changes have been
  198. * processed. If this is the same as the
  199. * {@link ApplicationConnection#getLastSeenServerSyncId()}, it means that we can
  200. * skip some quite expensive calculations because we know that the state
  201. * hasn't changed since the last time the values were calculated.
  202. */
  203. private int processedResponseId = -1;
  204. /*
  205. * (non-Javadoc)
  206. *
  207. * @see com.vaadin.client.HasComponentsConnector#updateCaption(com.vaadin
  208. * .client.ComponentConnector)
  209. */
  210. @Override
  211. public void updateCaption(ComponentConnector connector) {
  212. /*
  213. * Don't directly update captions here to avoid calling e.g.
  214. * updateLayoutHeight() before everything is initialized.
  215. * updateInternalState() will ensure all captions are updated when
  216. * appropriate.
  217. */
  218. updateInternalState();
  219. }
  220. private void updateCaptionInternal(ComponentConnector child) {
  221. Slot slot = getWidget().getSlot(child.getWidget());
  222. String caption = child.getState().caption;
  223. URLReference iconUrl = child.getState().resources
  224. .get(ComponentConstants.ICON_RESOURCE);
  225. String iconUrlString = iconUrl != null ? iconUrl.getURL() : null;
  226. Icon icon = child.getConnection().getIcon(iconUrlString);
  227. List<String> styles = child.getState().styles;
  228. String error = child.getState().errorMessage;
  229. boolean showError = error != null;
  230. if (child.getState() instanceof AbstractFieldState) {
  231. AbstractFieldState abstractFieldState = (AbstractFieldState) child
  232. .getState();
  233. showError = showError && !abstractFieldState.hideErrors;
  234. }
  235. boolean required = false;
  236. if (child instanceof AbstractFieldConnector) {
  237. required = ((AbstractFieldConnector) child).isRequired();
  238. }
  239. boolean enabled = child.isEnabled();
  240. if (slot.hasCaption() && null == caption) {
  241. slot.setCaptionResizeListener(null);
  242. }
  243. slot.setCaption(caption, icon, styles, error, showError, required,
  244. enabled, child.getState().captionAsHtml);
  245. AriaHelper.handleInputRequired(child.getWidget(), required);
  246. AriaHelper.handleInputInvalid(child.getWidget(), showError);
  247. AriaHelper.bindCaption(child.getWidget(), slot.getCaptionElement());
  248. if (slot.hasCaption()) {
  249. CaptionPosition pos = slot.getCaptionPosition();
  250. slot.setCaptionResizeListener(slotCaptionResizeListener);
  251. if (child.isRelativeHeight()
  252. && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
  253. getWidget().updateCaptionOffset(slot.getCaptionElement());
  254. } else if (child.isRelativeWidth()
  255. && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT)) {
  256. getWidget().updateCaptionOffset(slot.getCaptionElement());
  257. }
  258. }
  259. }
  260. /*
  261. * (non-Javadoc)
  262. *
  263. * @see com.vaadin.client.ui.AbstractComponentContainerConnector#
  264. * onConnectorHierarchyChange
  265. * (com.vaadin.client.ConnectorHierarchyChangeEvent)
  266. */
  267. @Override
  268. public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
  269. Profiler.enter("AOLC.onConnectorHierarchyChange");
  270. List<ComponentConnector> previousChildren = event.getOldChildren();
  271. int currentIndex = 0;
  272. VAbstractOrderedLayout layout = getWidget();
  273. // remove spacing as it is exists as separate elements that cannot be
  274. // removed easily after reordering the contents
  275. Profiler.enter("AOLC.onConnectorHierarchyChange temporarily remove spacing");
  276. layout.setSpacing(false);
  277. Profiler.leave("AOLC.onConnectorHierarchyChange temporarily remove spacing");
  278. for (ComponentConnector child : getChildComponents()) {
  279. Profiler.enter("AOLC.onConnectorHierarchyChange add children");
  280. Slot slot = layout.getSlot(child.getWidget());
  281. if (slot.getParent() != layout) {
  282. Profiler.enter("AOLC.onConnectorHierarchyChange add state change handler");
  283. child.addStateChangeHandler(childStateChangeHandler);
  284. Profiler.leave("AOLC.onConnectorHierarchyChange add state change handler");
  285. }
  286. Profiler.enter("AOLC.onConnectorHierarchyChange addOrMoveSlot");
  287. layout.addOrMoveSlot(slot, currentIndex++, false);
  288. Profiler.leave("AOLC.onConnectorHierarchyChange addOrMoveSlot");
  289. Profiler.leave("AOLC.onConnectorHierarchyChange add children");
  290. }
  291. // re-add spacing for the elements that should have it
  292. Profiler.enter("AOLC.onConnectorHierarchyChange setSpacing");
  293. // spacings were removed above
  294. if (getState().spacing) {
  295. layout.setSpacing(true);
  296. }
  297. Profiler.leave("AOLC.onConnectorHierarchyChange setSpacing");
  298. for (ComponentConnector child : previousChildren) {
  299. Profiler.enter("AOLC.onConnectorHierarchyChange remove children");
  300. if (child.getParent() != this) {
  301. Slot slot = layout.getSlot(child.getWidget());
  302. slot.setWidgetResizeListener(null);
  303. if (slot.hasCaption()) {
  304. slot.setCaptionResizeListener(null);
  305. }
  306. slot.setSpacingResizeListener(null);
  307. child.removeStateChangeHandler(childStateChangeHandler);
  308. layout.removeWidget(child.getWidget());
  309. }
  310. Profiler.leave("AOLC.onConnectorHierarchyChange remove children");
  311. }
  312. Profiler.leave("AOLC.onConnectorHierarchyChange");
  313. updateInternalState();
  314. }
  315. /*
  316. * (non-Javadoc)
  317. *
  318. * @see
  319. * com.vaadin.client.ui.AbstractComponentConnector#onStateChanged(com.vaadin
  320. * .client.communication.StateChangeEvent)
  321. */
  322. @Override
  323. public void onStateChanged(StateChangeEvent stateChangeEvent) {
  324. super.onStateChanged(stateChangeEvent);
  325. clickEventHandler.handleEventHandlerRegistration();
  326. getWidget().setMargin(new MarginInfo(getState().marginsBitmask));
  327. getWidget().setSpacing(getState().spacing);
  328. updateInternalState();
  329. }
  330. /*
  331. * (non-Javadoc)
  332. *
  333. * @see
  334. * com.vaadin.client.ui.AbstractComponentConnector#getTooltipInfo(com.google
  335. * .gwt.dom.client.Element)
  336. */
  337. @Override
  338. public TooltipInfo getTooltipInfo(com.google.gwt.dom.client.Element element) {
  339. if (element != getWidget().getElement()) {
  340. Slot slot = WidgetUtil.findWidget(element, Slot.class);
  341. if (slot != null && slot.getCaptionElement() != null
  342. && slot.getParent() == getWidget()
  343. && slot.getCaptionElement().isOrHasChild(element)) {
  344. ComponentConnector connector = Util.findConnectorFor(slot
  345. .getWidget());
  346. if (connector != null) {
  347. return connector.getTooltipInfo(element);
  348. }
  349. }
  350. }
  351. return super.getTooltipInfo(element);
  352. }
  353. /*
  354. * (non-Javadoc)
  355. *
  356. * @see com.vaadin.client.ui.AbstractComponentConnector#hasTooltip()
  357. */
  358. @Override
  359. public boolean hasTooltip() {
  360. /*
  361. * Tooltips are fetched from child connectors -> there's no quick way of
  362. * checking whether there might a tooltip hiding somewhere
  363. */
  364. return true;
  365. }
  366. /**
  367. * Updates DOM properties and listeners based on the current state of this
  368. * layout and its children.
  369. */
  370. private void updateInternalState() {
  371. // Avoid updating again for the same data
  372. int lastResponseId = getConnection().getLastSeenServerSyncId();
  373. if (processedResponseId == lastResponseId) {
  374. return;
  375. }
  376. Profiler.enter("AOLC.updateInternalState");
  377. // Remember that everything is updated for this response
  378. processedResponseId = lastResponseId;
  379. hasChildrenWithRelativeHeight = false;
  380. hasChildrenWithRelativeWidth = false;
  381. hasChildrenWithMiddleAlignment = false;
  382. needsExpand = getWidget().vertical ? !isUndefinedHeight()
  383. : !isUndefinedWidth();
  384. boolean onlyZeroExpands = true;
  385. if (needsExpand) {
  386. for (ComponentConnector child : getChildComponents()) {
  387. double expandRatio = getState().childData.get(child).expandRatio;
  388. if (expandRatio != 0) {
  389. onlyZeroExpands = false;
  390. break;
  391. }
  392. }
  393. }
  394. // First update bookkeeping for all children
  395. for (ComponentConnector child : getChildComponents()) {
  396. Slot slot = getWidget().getSlot(child.getWidget());
  397. slot.setRelativeWidth(child.isRelativeWidth());
  398. slot.setRelativeHeight(child.isRelativeHeight());
  399. if (child.delegateCaptionHandling()) {
  400. updateCaptionInternal(child);
  401. }
  402. // Update slot style names
  403. List<String> childStyles = child.getState().styles;
  404. if (childStyles == null) {
  405. getWidget().setSlotStyleNames(child.getWidget(),
  406. (String[]) null);
  407. } else {
  408. getWidget().setSlotStyleNames(child.getWidget(),
  409. childStyles.toArray(new String[childStyles.size()]));
  410. }
  411. AlignmentInfo alignment = new AlignmentInfo(
  412. getState().childData.get(child).alignmentBitmask);
  413. slot.setAlignment(alignment);
  414. if (alignment.isVerticalCenter()) {
  415. hasChildrenWithMiddleAlignment = true;
  416. }
  417. double expandRatio = onlyZeroExpands ? 1 : getState().childData
  418. .get(child).expandRatio;
  419. slot.setExpandRatio(expandRatio);
  420. if (child.isRelativeHeight()) {
  421. hasChildrenWithRelativeHeight = true;
  422. }
  423. if (child.isRelativeWidth()) {
  424. hasChildrenWithRelativeWidth = true;
  425. }
  426. }
  427. if (needsFixedHeight()) {
  428. // Add resize listener to ensure the widget itself is measured
  429. getLayoutManager().addElementResizeListener(
  430. getWidget().getElement(), childComponentResizeListener);
  431. } else {
  432. getLayoutManager().removeElementResizeListener(
  433. getWidget().getElement(), childComponentResizeListener);
  434. }
  435. // Then update listeners based on bookkeeping
  436. updateAllSlotListeners();
  437. // Update the layout at this point to ensure it's OK even if we get no
  438. // element resize events
  439. updateLayoutHeight();
  440. if (needsExpand()) {
  441. getWidget().updateExpandedSizes();
  442. // updateExpandedSizes causes fixed size components to temporarily
  443. // lose their size. updateExpandCompensation must be delayed until
  444. // the browser has a chance to measure them.
  445. Scheduler.get().scheduleFinally(new ScheduledCommand() {
  446. @Override
  447. public void execute() {
  448. getWidget().updateExpandCompensation();
  449. }
  450. });
  451. } else {
  452. getWidget().clearExpand();
  453. }
  454. Profiler.leave("AOLC.updateInternalState");
  455. }
  456. /**
  457. * Does the layout need a fixed height?
  458. */
  459. private boolean needsFixedHeight() {
  460. boolean isVertical = getWidget().vertical;
  461. if (isVertical) {
  462. // Doesn't need height fix for vertical layouts
  463. return false;
  464. }
  465. else if (!isUndefinedHeight()) {
  466. // Fix not needed unless the height is undefined
  467. return false;
  468. }
  469. else if (!hasChildrenWithRelativeHeight
  470. && !hasChildrenWithMiddleAlignment) {
  471. // Already works if there are no relative heights or middle aligned
  472. // children
  473. return false;
  474. }
  475. return true;
  476. }
  477. /**
  478. * Does the layout need to expand?
  479. */
  480. private boolean needsExpand() {
  481. return needsExpand;
  482. }
  483. /**
  484. * Add slot listeners
  485. */
  486. private void updateAllSlotListeners() {
  487. for (ComponentConnector child : getChildComponents()) {
  488. updateSlotListeners(child);
  489. }
  490. }
  491. /**
  492. * Add/remove necessary ElementResizeListeners for one slot. This should be
  493. * called after each update to the slot's or it's widget.
  494. */
  495. private void updateSlotListeners(ComponentConnector child) {
  496. Slot slot = getWidget().getSlot(child.getWidget());
  497. // Clear all possible listeners first
  498. slot.setWidgetResizeListener(null);
  499. if (slot.hasCaption()) {
  500. slot.setCaptionResizeListener(null);
  501. }
  502. if (slot.hasSpacing()) {
  503. slot.setSpacingResizeListener(null);
  504. }
  505. // Add all necessary listeners
  506. if (needsFixedHeight()) {
  507. slot.setWidgetResizeListener(childComponentResizeListener);
  508. if (slot.hasCaption()) {
  509. slot.setCaptionResizeListener(slotCaptionResizeListener);
  510. }
  511. } else if ((hasChildrenWithRelativeHeight || hasChildrenWithRelativeWidth)
  512. && slot.hasCaption()) {
  513. /*
  514. * If the slot has caption, we need to listen for its size changes
  515. * in order to update the padding/margin offset for relative sized
  516. * components.
  517. *
  518. * TODO might only be needed if the caption is in the same direction
  519. * as the relative size?
  520. */
  521. slot.setCaptionResizeListener(slotCaptionResizeListener);
  522. }
  523. if (needsExpand()) {
  524. // TODO widget resize only be needed for children without expand?
  525. slot.setWidgetResizeListener(childComponentResizeListener);
  526. if (slot.hasSpacing()) {
  527. slot.setSpacingResizeListener(spacingResizeListener);
  528. }
  529. }
  530. }
  531. /**
  532. * Re-calculate the layout height
  533. */
  534. private void updateLayoutHeight() {
  535. if (needsFixedHeight()) {
  536. int h = getMaxHeight();
  537. if (h < 0) {
  538. // Postpone change if there are elements that have not yet been
  539. // measured
  540. return;
  541. }
  542. h += getLayoutManager().getBorderHeight(getWidget().getElement())
  543. + getLayoutManager().getPaddingHeight(
  544. getWidget().getElement());
  545. getWidget().getElement().getStyle().setHeight(h, Unit.PX);
  546. getLayoutManager().setNeedsMeasure(this);
  547. }
  548. }
  549. /**
  550. * Measures the maximum height of the layout in pixels
  551. */
  552. private int getMaxHeight() {
  553. int highestNonRelative = -1;
  554. int highestRelative = -1;
  555. LayoutManager layoutManager = getLayoutManager();
  556. for (ComponentConnector child : getChildComponents()) {
  557. Widget childWidget = child.getWidget();
  558. Slot slot = getWidget().getSlot(childWidget);
  559. Element captionElement = slot.getCaptionElement();
  560. CaptionPosition captionPosition = slot.getCaptionPosition();
  561. int pixelHeight = layoutManager.getOuterHeight(childWidget
  562. .getElement());
  563. if (pixelHeight == -1) {
  564. // Height has not yet been measured -> postpone actions that
  565. // depend on the max height
  566. return -1;
  567. }
  568. boolean hasRelativeHeight = slot.hasRelativeHeight();
  569. boolean captionSizeShouldBeAddedtoComponentHeight = captionPosition == CaptionPosition.TOP
  570. || captionPosition == CaptionPosition.BOTTOM;
  571. boolean includeCaptionHeight = captionElement != null
  572. && captionSizeShouldBeAddedtoComponentHeight;
  573. if (includeCaptionHeight) {
  574. int captionHeight = layoutManager
  575. .getOuterHeight(captionElement)
  576. - getLayoutManager().getMarginHeight(captionElement);
  577. if (captionHeight == -1) {
  578. // Height has not yet been measured -> postpone actions that
  579. // depend on the max height
  580. return -1;
  581. }
  582. pixelHeight += captionHeight;
  583. }
  584. if (!hasRelativeHeight) {
  585. if (pixelHeight > highestNonRelative) {
  586. highestNonRelative = pixelHeight;
  587. }
  588. } else {
  589. if (pixelHeight > highestRelative) {
  590. highestRelative = pixelHeight;
  591. }
  592. }
  593. }
  594. return highestNonRelative > -1 ? highestNonRelative : highestRelative;
  595. }
  596. /*
  597. * (non-Javadoc)
  598. *
  599. * @see com.vaadin.client.ui.AbstractComponentConnector#onUnregister()
  600. */
  601. @Override
  602. public void onUnregister() {
  603. // Cleanup all ElementResizeListeners
  604. for (ComponentConnector child : getChildComponents()) {
  605. Slot slot = getWidget().getSlot(child.getWidget());
  606. if (slot.hasCaption()) {
  607. slot.setCaptionResizeListener(null);
  608. }
  609. if (slot.getSpacingElement() != null) {
  610. slot.setSpacingResizeListener(null);
  611. }
  612. slot.setWidgetResizeListener(null);
  613. }
  614. super.onUnregister();
  615. }
  616. }