Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

ChildComponentContainer.java 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client.ui.layout;
  5. import java.util.Iterator;
  6. import java.util.NoSuchElementException;
  7. import com.google.gwt.dom.client.DivElement;
  8. import com.google.gwt.dom.client.Document;
  9. import com.google.gwt.dom.client.Element;
  10. import com.google.gwt.dom.client.Style;
  11. import com.google.gwt.dom.client.TableElement;
  12. import com.google.gwt.user.client.ui.Panel;
  13. import com.google.gwt.user.client.ui.Widget;
  14. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  15. import com.vaadin.terminal.gwt.client.BrowserInfo;
  16. import com.vaadin.terminal.gwt.client.Paintable;
  17. import com.vaadin.terminal.gwt.client.UIDL;
  18. import com.vaadin.terminal.gwt.client.Util;
  19. import com.vaadin.terminal.gwt.client.VCaption;
  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.ui.AlignmentInfo;
  23. public class ChildComponentContainer extends Panel {
  24. /**
  25. * Size of the container DIV excluding any margins and also excluding the
  26. * expansion amount (containerExpansion)
  27. */
  28. private Size contSize = new Size(0, 0);
  29. /**
  30. * Size of the widget inside the container DIV
  31. */
  32. private Size widgetSize = new Size(0, 0);
  33. /**
  34. * Size of the caption
  35. */
  36. private int captionRequiredWidth = 0;
  37. private int captionWidth = 0;
  38. private int captionHeight = 0;
  39. /**
  40. *
  41. * Padding added to the container when it is larger than the component.
  42. */
  43. private Size containerExpansion = new Size(0, 0);
  44. private double expandRatio;
  45. private int containerMarginLeft = 0;
  46. private int containerMarginTop = 0;
  47. AlignmentInfo alignment = AlignmentInfo.TOP_LEFT;
  48. private int alignmentLeftOffsetForWidget = 0;
  49. private int alignmentLeftOffsetForCaption = 0;
  50. /**
  51. * Top offset for implementing alignment. Top offset is set to the container
  52. * DIV as it otherwise would have to be set to either the Caption or the
  53. * Widget depending on whether there is a caption and where the caption is
  54. * located.
  55. */
  56. private int alignmentTopOffset = 0;
  57. // private Margins alignmentOffset = new Margins(0, 0, 0, 0);
  58. private VCaption caption = null;
  59. private DivElement containerDIV;
  60. private DivElement widgetDIV;
  61. private Widget widget;
  62. private FloatSize relativeSize = null;
  63. public ChildComponentContainer(Widget widget, int orientation) {
  64. super();
  65. containerDIV = Document.get().createDivElement();
  66. widgetDIV = Document.get().createDivElement();
  67. if (BrowserInfo.get().isFF2()) {
  68. Style style = widgetDIV.getStyle();
  69. // FF2 chokes on some floats very easily. Measuring size escpecially
  70. // becomes terribly slow
  71. TableElement tableEl = Document.get().createTableElement();
  72. tableEl
  73. .setInnerHTML("<tbody><tr><td><div></div></td></tr></tbody>");
  74. DivElement div = (DivElement) tableEl.getFirstChildElement()
  75. .getFirstChildElement().getFirstChildElement()
  76. .getFirstChildElement();
  77. tableEl.setCellPadding(0);
  78. tableEl.setCellSpacing(0);
  79. tableEl.setBorder(0);
  80. div.getStyle().setProperty("padding", "0");
  81. setElement(tableEl);
  82. containerDIV = div;
  83. } else {
  84. setFloat(widgetDIV, "left");
  85. setElement(containerDIV);
  86. containerDIV.getStyle().setProperty("height", "0");
  87. containerDIV.getStyle().setProperty("width", "0px");
  88. containerDIV.getStyle().setProperty("overflow", "hidden");
  89. }
  90. if (BrowserInfo.get().isIE()) {
  91. /*
  92. * IE requires position: relative on overflow:hidden elements if
  93. * they should hide position:relative elements. Without this e.g. a
  94. * 1000x1000 Panel inside an 500x500 OrderedLayout will not be
  95. * clipped but fully shown.
  96. */
  97. containerDIV.getStyle().setProperty("position", "relative");
  98. widgetDIV.getStyle().setProperty("position", "relative");
  99. }
  100. containerDIV.appendChild(widgetDIV);
  101. setOrientation(orientation);
  102. setWidget(widget);
  103. }
  104. public void setWidget(Widget w) {
  105. // Validate
  106. if (w == widget) {
  107. return;
  108. }
  109. // Detach new child.
  110. if (w != null) {
  111. w.removeFromParent();
  112. }
  113. // Remove old child.
  114. if (widget != null) {
  115. remove(widget);
  116. }
  117. // Logical attach.
  118. widget = w;
  119. if (w != null) {
  120. // Physical attach.
  121. widgetDIV.appendChild(widget.getElement());
  122. adopt(w);
  123. }
  124. }
  125. private static void setFloat(Element div, String floatString) {
  126. if (BrowserInfo.get().isIE()) {
  127. div.getStyle().setProperty("styleFloat", floatString);
  128. // IE requires display:inline for margin-left to work together
  129. // with float:left
  130. if (floatString.equals("left")) {
  131. div.getStyle().setProperty("display", "inline");
  132. } else {
  133. div.getStyle().setProperty("display", "block");
  134. }
  135. } else {
  136. div.getStyle().setProperty("cssFloat", floatString);
  137. }
  138. }
  139. public void setOrientation(int orientation) {
  140. if (orientation == CellBasedLayout.ORIENTATION_HORIZONTAL) {
  141. setFloat(getElement(), "left");
  142. } else {
  143. setFloat(getElement(), "");
  144. }
  145. setHeight("0px");
  146. // setWidth("0px");
  147. contSize.setHeight(0);
  148. contSize.setWidth(0);
  149. containerMarginLeft = 0;
  150. containerMarginTop = 0;
  151. containerDIV.getStyle().setProperty("paddingLeft", "0");
  152. containerDIV.getStyle().setProperty("paddingTop", "0");
  153. containerExpansion.setHeight(0);
  154. containerExpansion.setWidth(0);
  155. // Clear old alignments
  156. clearAlignments();
  157. }
  158. public void renderChild(UIDL childUIDL, ApplicationConnection client,
  159. int fixedWidth) {
  160. /*
  161. * Must remove width specification from container before rendering to
  162. * allow components to grow in horizontal direction.
  163. *
  164. * For fixed width layouts we specify the width directly so that height
  165. * is automatically calculated correctly (e.g. for Labels).
  166. */
  167. /*
  168. * This should no longer be needed (after #2563) as all components are
  169. * such that they can be rendered inside a 0x0 DIV.
  170. *
  171. * The exception seems to be complex components (Tree and Table) on
  172. * Opera (#3444).
  173. */
  174. if (fixedWidth < 0 && BrowserInfo.get().isOpera()) {
  175. setUnlimitedContainerWidth();
  176. }
  177. ((Paintable) widget).updateFromUIDL(childUIDL, client);
  178. }
  179. public void setUnlimitedContainerWidth() {
  180. setLimitedContainerWidth(1000000);
  181. }
  182. public void setLimitedContainerWidth(int width) {
  183. containerDIV.getStyle().setProperty("width", width + "px");
  184. }
  185. public void updateWidgetSize() {
  186. /*
  187. * Widget wrapper includes margin which the widget offsetWidth/Height
  188. * does not include
  189. */
  190. int w = Util.getRequiredWidth(widgetDIV);
  191. int h = Util.getRequiredHeight(widgetDIV);
  192. widgetSize.setWidth(w);
  193. widgetSize.setHeight(h);
  194. // ApplicationConnection.getConsole().log(
  195. // Util.getSimpleName(widget) + " size is " + w + "," + h);
  196. }
  197. public void setMarginLeft(int marginLeft) {
  198. containerMarginLeft = marginLeft;
  199. containerDIV.getStyle().setPropertyPx("paddingLeft", marginLeft);
  200. }
  201. public void setMarginTop(int marginTop) {
  202. containerMarginTop = marginTop;
  203. containerDIV.getStyle().setPropertyPx("paddingTop",
  204. marginTop + alignmentTopOffset);
  205. updateContainerDOMSize();
  206. }
  207. public void updateAlignments(int parentWidth, int parentHeight) {
  208. if (parentHeight == -1) {
  209. parentHeight = contSize.getHeight();
  210. }
  211. if (parentWidth == -1) {
  212. parentWidth = contSize.getWidth();
  213. }
  214. alignmentTopOffset = calculateVerticalAlignmentTopOffset(parentHeight);
  215. calculateHorizontalAlignment(parentWidth);
  216. applyAlignments();
  217. }
  218. private void applyAlignments() {
  219. // Update top margin to take alignment into account
  220. setMarginTop(containerMarginTop);
  221. if (caption != null) {
  222. caption.getElement().getStyle().setPropertyPx("marginLeft",
  223. alignmentLeftOffsetForCaption);
  224. }
  225. widgetDIV.getStyle().setPropertyPx("marginLeft",
  226. alignmentLeftOffsetForWidget);
  227. }
  228. public int getCaptionRequiredWidth() {
  229. if (caption == null) {
  230. return 0;
  231. }
  232. return captionRequiredWidth;
  233. }
  234. public int getCaptionWidth() {
  235. if (caption == null) {
  236. return 0;
  237. }
  238. return captionWidth;
  239. }
  240. public int getCaptionHeight() {
  241. if (caption == null) {
  242. return 0;
  243. }
  244. return captionHeight;
  245. }
  246. public int getCaptionWidthAfterComponent() {
  247. if (caption == null || !caption.shouldBePlacedAfterComponent()) {
  248. return 0;
  249. }
  250. return getCaptionWidth();
  251. }
  252. public int getCaptionHeightAboveComponent() {
  253. if (caption == null || caption.shouldBePlacedAfterComponent()) {
  254. return 0;
  255. }
  256. return getCaptionHeight();
  257. }
  258. private int calculateVerticalAlignmentTopOffset(int emptySpace) {
  259. if (alignment.isTop()) {
  260. return 0;
  261. }
  262. if (caption != null) {
  263. if (caption.shouldBePlacedAfterComponent()) {
  264. /*
  265. * Take into account the rare case that the caption on the right
  266. * side of the component AND is higher than the component
  267. */
  268. emptySpace -= Math.max(widgetSize.getHeight(), caption
  269. .getHeight());
  270. } else {
  271. emptySpace -= widgetSize.getHeight();
  272. emptySpace -= getCaptionHeight();
  273. }
  274. } else {
  275. /*
  276. * There is no caption and thus we do not need to take anything but
  277. * the widget into account
  278. */
  279. emptySpace -= widgetSize.getHeight();
  280. }
  281. int top = 0;
  282. if (alignment.isVerticalCenter()) {
  283. top = emptySpace / 2;
  284. } else if (alignment.isBottom()) {
  285. top = emptySpace;
  286. }
  287. if (top < 0) {
  288. top = 0;
  289. }
  290. return top;
  291. }
  292. private void calculateHorizontalAlignment(int emptySpace) {
  293. alignmentLeftOffsetForCaption = 0;
  294. alignmentLeftOffsetForWidget = 0;
  295. if (alignment.isLeft()) {
  296. return;
  297. }
  298. int captionSpace = emptySpace;
  299. int widgetSpace = emptySpace;
  300. if (caption != null) {
  301. // There is a caption
  302. if (caption.shouldBePlacedAfterComponent()) {
  303. /*
  304. * The caption is after component. In this case the caption
  305. * needs no alignment.
  306. */
  307. captionSpace = 0;
  308. widgetSpace -= widgetSize.getWidth();
  309. widgetSpace -= getCaptionWidth();
  310. } else {
  311. /*
  312. * The caption is above the component. Caption and widget needs
  313. * separate alignment offsets.
  314. */
  315. widgetSpace -= widgetSize.getWidth();
  316. captionSpace -= getCaptionWidth();
  317. }
  318. } else {
  319. /*
  320. * There is no caption and thus we do not need to take anything but
  321. * the widget into account
  322. */
  323. captionSpace = 0;
  324. widgetSpace -= widgetSize.getWidth();
  325. }
  326. if (alignment.isHorizontalCenter()) {
  327. alignmentLeftOffsetForCaption = captionSpace / 2;
  328. alignmentLeftOffsetForWidget = widgetSpace / 2;
  329. } else if (alignment.isRight()) {
  330. alignmentLeftOffsetForCaption = captionSpace;
  331. alignmentLeftOffsetForWidget = widgetSpace;
  332. }
  333. if (alignmentLeftOffsetForCaption < 0) {
  334. alignmentLeftOffsetForCaption = 0;
  335. }
  336. if (alignmentLeftOffsetForWidget < 0) {
  337. alignmentLeftOffsetForWidget = 0;
  338. }
  339. }
  340. public void setAlignment(AlignmentInfo alignmentInfo) {
  341. alignment = alignmentInfo;
  342. }
  343. public Size getWidgetSize() {
  344. return widgetSize;
  345. }
  346. public void updateCaption(UIDL uidl, ApplicationConnection client) {
  347. if (VCaption.isNeeded(uidl)) {
  348. // We need a caption
  349. VCaption newCaption = caption;
  350. if (newCaption == null) {
  351. newCaption = new VCaption((Paintable) widget, client);
  352. // Set initial height to avoid Safari flicker
  353. newCaption.setHeight("18px");
  354. // newCaption.setHeight(newCaption.getHeight()); // This might
  355. // be better... ??
  356. if (BrowserInfo.get().isIE()) {
  357. /*
  358. * Must attach caption here so IE sends an immediate onload
  359. * event for images coming from the cache
  360. */
  361. setCaption(newCaption);
  362. }
  363. }
  364. boolean positionChanged = newCaption.updateCaption(uidl);
  365. if (newCaption != caption || positionChanged) {
  366. setCaption(newCaption);
  367. }
  368. } else {
  369. // Caption is not needed
  370. if (caption != null) {
  371. remove(caption);
  372. }
  373. }
  374. updateCaptionSize();
  375. }
  376. public void updateCaptionSize() {
  377. captionWidth = 0;
  378. captionHeight = 0;
  379. if (caption != null) {
  380. captionWidth = caption.getRenderedWidth();
  381. captionHeight = caption.getHeight();
  382. captionRequiredWidth = caption.getRequiredWidth();
  383. /*
  384. * ApplicationConnection.getConsole().log(
  385. * "Caption rendered width: " + captionWidth +
  386. * ", caption required width: " + captionRequiredWidth +
  387. * ", caption height: " + captionHeight);
  388. */
  389. }
  390. }
  391. private void setCaption(VCaption newCaption) {
  392. // Validate
  393. // if (newCaption == caption) {
  394. // return;
  395. // }
  396. // Detach new child.
  397. if (newCaption != null) {
  398. newCaption.removeFromParent();
  399. }
  400. // Remove old child.
  401. if (caption != null && newCaption != caption) {
  402. remove(caption);
  403. }
  404. // Logical attach.
  405. caption = newCaption;
  406. if (caption != null) {
  407. // Physical attach.
  408. if (caption.shouldBePlacedAfterComponent()) {
  409. Util.setFloat(caption.getElement(), "left");
  410. containerDIV.appendChild(caption.getElement());
  411. } else {
  412. Util.setFloat(caption.getElement(), "");
  413. containerDIV.insertBefore(caption.getElement(), widgetDIV);
  414. }
  415. adopt(caption);
  416. }
  417. }
  418. @Override
  419. public boolean remove(Widget child) {
  420. // Validate
  421. if (child != caption && child != widget) {
  422. return false;
  423. }
  424. // Orphan
  425. orphan(child);
  426. // Physical && Logical Detach
  427. if (child == caption) {
  428. containerDIV.removeChild(child.getElement());
  429. caption = null;
  430. } else {
  431. widgetDIV.removeChild(child.getElement());
  432. widget = null;
  433. }
  434. return true;
  435. }
  436. public Iterator<Widget> iterator() {
  437. return new ChildComponentContainerIterator<Widget>();
  438. }
  439. public class ChildComponentContainerIterator<T> implements Iterator<Widget> {
  440. private int id = 0;
  441. public boolean hasNext() {
  442. return (id < size());
  443. }
  444. public Widget next() {
  445. Widget w = get(id);
  446. id++;
  447. return w;
  448. }
  449. private Widget get(int i) {
  450. if (i == 0) {
  451. if (widget != null) {
  452. return widget;
  453. } else if (caption != null) {
  454. return caption;
  455. } else {
  456. throw new NoSuchElementException();
  457. }
  458. } else if (i == 1) {
  459. if (widget != null && caption != null) {
  460. return caption;
  461. } else {
  462. throw new NoSuchElementException();
  463. }
  464. } else {
  465. throw new NoSuchElementException();
  466. }
  467. }
  468. public void remove() {
  469. int toRemove = id - 1;
  470. if (toRemove == 0) {
  471. if (widget != null) {
  472. ChildComponentContainer.this.remove(widget);
  473. } else if (caption != null) {
  474. ChildComponentContainer.this.remove(caption);
  475. } else {
  476. throw new IllegalStateException();
  477. }
  478. } else if (toRemove == 1) {
  479. if (widget != null && caption != null) {
  480. ChildComponentContainer.this.remove(caption);
  481. } else {
  482. throw new IllegalStateException();
  483. }
  484. } else {
  485. throw new IllegalStateException();
  486. }
  487. id--;
  488. }
  489. }
  490. public int size() {
  491. if (widget != null) {
  492. if (caption != null) {
  493. return 2;
  494. } else {
  495. return 1;
  496. }
  497. } else {
  498. if (caption != null) {
  499. return 1;
  500. } else {
  501. return 0;
  502. }
  503. }
  504. }
  505. public Widget getWidget() {
  506. return widget;
  507. }
  508. /**
  509. * Return true if the size of the widget has been specified in the selected
  510. * orientation.
  511. *
  512. * @return
  513. */
  514. public boolean widgetHasSizeSpecified(int orientation) {
  515. String size;
  516. if (orientation == CellBasedLayout.ORIENTATION_HORIZONTAL) {
  517. size = widget.getElement().getStyle().getProperty("width");
  518. } else {
  519. size = widget.getElement().getStyle().getProperty("height");
  520. }
  521. return (size != null && !size.equals(""));
  522. }
  523. public boolean isComponentRelativeSized(int orientation) {
  524. if (relativeSize == null) {
  525. return false;
  526. }
  527. if (orientation == CellBasedLayout.ORIENTATION_HORIZONTAL) {
  528. return relativeSize.getWidth() >= 0;
  529. } else {
  530. return relativeSize.getHeight() >= 0;
  531. }
  532. }
  533. public void setRelativeSize(FloatSize relativeSize) {
  534. this.relativeSize = relativeSize;
  535. }
  536. public Size getContSize() {
  537. return contSize;
  538. }
  539. public void clearAlignments() {
  540. alignmentLeftOffsetForCaption = 0;
  541. alignmentLeftOffsetForWidget = 0;
  542. alignmentTopOffset = 0;
  543. applyAlignments();
  544. }
  545. /**
  546. * Sets the normalized expand ratio of this slot. The fraction that this
  547. * slot will use of "excess space".
  548. *
  549. * @param expandRatio
  550. */
  551. public void setNormalizedExpandRatio(double expandRatio) {
  552. this.expandRatio = expandRatio;
  553. }
  554. public int expand(int orientation, int spaceForExpansion) {
  555. int expansionAmount = (int) (spaceForExpansion * expandRatio);
  556. if (orientation == CellBasedLayout.ORIENTATION_HORIZONTAL) {
  557. // HORIZONTAL
  558. containerExpansion.setWidth(expansionAmount);
  559. } else {
  560. // VERTICAL
  561. containerExpansion.setHeight(expansionAmount);
  562. }
  563. return expansionAmount;
  564. }
  565. public void expandExtra(int orientation, int extra) {
  566. if (orientation == CellBasedLayout.ORIENTATION_HORIZONTAL) {
  567. // HORIZONTAL
  568. containerExpansion.setWidth(containerExpansion.getWidth() + extra);
  569. } else {
  570. // VERTICAL
  571. containerExpansion
  572. .setHeight(containerExpansion.getHeight() + extra);
  573. }
  574. }
  575. public void setContainerSize(int widgetAndCaptionWidth,
  576. int widgetAndCaptionHeight) {
  577. int containerWidth = widgetAndCaptionWidth;
  578. containerWidth += containerExpansion.getWidth();
  579. int containerHeight = widgetAndCaptionHeight;
  580. containerHeight += containerExpansion.getHeight();
  581. // ApplicationConnection.getConsole().log(
  582. // "Setting container size for " + Util.getSimpleName(widget)
  583. // + " to " + containerWidth + "," + containerHeight);
  584. if (containerWidth < 0) {
  585. ApplicationConnection.getConsole().log(
  586. "containerWidth should never be negative: "
  587. + containerWidth);
  588. containerWidth = 0;
  589. }
  590. if (containerHeight < 0) {
  591. ApplicationConnection.getConsole().log(
  592. "containerHeight should never be negative: "
  593. + containerHeight);
  594. containerHeight = 0;
  595. }
  596. contSize.setWidth(containerWidth);
  597. contSize.setHeight(containerHeight);
  598. updateContainerDOMSize();
  599. }
  600. public void updateContainerDOMSize() {
  601. int width = contSize.getWidth();
  602. int height = contSize.getHeight() - alignmentTopOffset;
  603. if (width < 0) {
  604. width = 0;
  605. }
  606. if (height < 0) {
  607. height = 0;
  608. }
  609. setWidth(width + "px");
  610. setHeight(height + "px");
  611. // Also update caption max width
  612. if (caption != null) {
  613. if (caption.shouldBePlacedAfterComponent()) {
  614. caption.setMaxWidth(captionWidth);
  615. } else {
  616. caption.setMaxWidth(width);
  617. }
  618. captionWidth = caption.getRenderedWidth();
  619. // Remove initial height
  620. caption.setHeight("");
  621. }
  622. }
  623. }