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.

Slot.java 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. /*
  2. * Copyright 2000-2021 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 java.util.Locale;
  19. import com.google.gwt.aria.client.Roles;
  20. import com.google.gwt.core.client.GWT;
  21. import com.google.gwt.dom.client.Document;
  22. import com.google.gwt.dom.client.Element;
  23. import com.google.gwt.user.client.DOM;
  24. import com.google.gwt.user.client.Event;
  25. import com.google.gwt.user.client.Timer;
  26. import com.google.gwt.user.client.ui.SimplePanel;
  27. import com.google.gwt.user.client.ui.Widget;
  28. import com.vaadin.client.LayoutManager;
  29. import com.vaadin.client.StyleConstants;
  30. import com.vaadin.client.WidgetUtil;
  31. import com.vaadin.client.WidgetUtil.ErrorUtil;
  32. import com.vaadin.client.ui.FontIcon;
  33. import com.vaadin.client.ui.HasErrorIndicatorElement;
  34. import com.vaadin.client.ui.Icon;
  35. import com.vaadin.client.ui.ImageIcon;
  36. import com.vaadin.client.ui.layout.ElementResizeListener;
  37. import com.vaadin.shared.ui.AlignmentInfo;
  38. import com.vaadin.shared.ui.ErrorLevel;
  39. /**
  40. * Represents a slot which contains the actual widget in the layout.
  41. */
  42. public class Slot extends SimplePanel implements HasErrorIndicatorElement {
  43. private static final String ALIGN_CLASS_PREFIX = "v-align-";
  44. // this must be set at construction time and not changed afterwards
  45. private VAbstractOrderedLayout layout;
  46. /** The default classname for this widget. */
  47. public static final String SLOT_CLASSNAME = "v-slot";
  48. private Element spacer;
  49. private Element captionWrap;
  50. private Element caption;
  51. private Element captionText;
  52. private Icon icon;
  53. private Element errorIcon;
  54. private Element requiredIcon;
  55. private ElementResizeListener captionResizeListener;
  56. private ElementResizeListener widgetResizeListener;
  57. private ElementResizeListener spacingResizeListener;
  58. // Caption is placed after component unless there is some part which
  59. // moves it above.
  60. private CaptionPosition captionPosition = CaptionPosition.RIGHT;
  61. private AlignmentInfo alignment;
  62. private double expandRatio = -1;
  63. /**
  64. * Constructs a slot.
  65. *
  66. * When using this constructor, the layout and widget must be set before any
  67. * other operations are performed on the slot.
  68. *
  69. * @since 7.6
  70. */
  71. public Slot() {
  72. setStyleName(SLOT_CLASSNAME);
  73. }
  74. /**
  75. * Set the layout in which this slot is. This method must be called exactly
  76. * once at slot construction time when using the default constructor.
  77. *
  78. * The method should normally only be called by
  79. * {@link VAbstractOrderedLayout#createSlot(Widget)}.
  80. *
  81. * @since 7.6
  82. * @param layout
  83. * the layout containing the slot
  84. */
  85. public void setLayout(VAbstractOrderedLayout layout) {
  86. this.layout = layout;
  87. }
  88. /**
  89. * Constructs a slot.
  90. *
  91. * @param layout
  92. * The layout to which this slot belongs
  93. * @param widget
  94. * The widget to put in the slot
  95. * @deprecated use {@link GWT#create(Class)}, {@link #setWidget(Widget)} and
  96. * {@link #setLayout(VAbstractOrderedLayout)} instead
  97. */
  98. @Deprecated
  99. public Slot(VAbstractOrderedLayout layout, Widget widget) {
  100. setLayout(layout);
  101. setStyleName(SLOT_CLASSNAME);
  102. setWidget(widget);
  103. }
  104. /*
  105. * (non-Javadoc)
  106. *
  107. * @see com.google.gwt.user.client.ui.SimplePanel#remove(com.google.gwt.user
  108. * .client.ui.Widget)
  109. */
  110. @Override
  111. public boolean remove(Widget w) {
  112. detachListeners();
  113. return super.remove(w);
  114. }
  115. /*
  116. * (non-Javadoc)
  117. *
  118. * @see com.google.gwt.user.client.ui.SimplePanel#setWidget(com.google.gwt
  119. * .user.client.ui.Widget)
  120. */
  121. @Override
  122. public void setWidget(Widget w) {
  123. detachListeners();
  124. super.setWidget(w);
  125. attachListeners();
  126. }
  127. /**
  128. * Attaches resize listeners to the widget, caption and spacing elements
  129. */
  130. private void attachListeners() {
  131. if (getWidget() != null && layout.getLayoutManager() != null) {
  132. LayoutManager lm = layout.getLayoutManager();
  133. if (getCaptionElement() != null && captionResizeListener != null) {
  134. lm.addElementResizeListener(getCaptionElement(),
  135. captionResizeListener);
  136. }
  137. if (widgetResizeListener != null) {
  138. lm.addElementResizeListener(getWidget().getElement(),
  139. widgetResizeListener);
  140. }
  141. if (getSpacingElement() != null && spacingResizeListener != null) {
  142. lm.addElementResizeListener(getSpacingElement(),
  143. spacingResizeListener);
  144. }
  145. }
  146. }
  147. /**
  148. * Detaches resize listeners from the widget, caption and spacing elements
  149. */
  150. private void detachListeners() {
  151. if (getWidget() != null && layout.getLayoutManager() != null) {
  152. LayoutManager lm = layout.getLayoutManager();
  153. if (getCaptionElement() != null && captionResizeListener != null) {
  154. lm.removeElementResizeListener(getCaptionElement(),
  155. captionResizeListener);
  156. }
  157. if (widgetResizeListener != null) {
  158. lm.removeElementResizeListener(getWidget().getElement(),
  159. widgetResizeListener);
  160. }
  161. // in many cases, the listener has already been removed by
  162. // setSpacing(false)
  163. if (getSpacingElement() != null && spacingResizeListener != null) {
  164. lm.removeElementResizeListener(getSpacingElement(),
  165. spacingResizeListener);
  166. }
  167. }
  168. }
  169. /**
  170. * Returns the caption resize listener for this slot if one has been set.
  171. *
  172. * @return the listener or {@code null} if not set
  173. */
  174. public ElementResizeListener getCaptionResizeListener() {
  175. return captionResizeListener;
  176. }
  177. /**
  178. * Sets the caption resize listener for this slot.
  179. *
  180. * @param captionResizeListener
  181. * the listener to set, or {@code null} to remove a previously
  182. * set listener
  183. */
  184. public void setCaptionResizeListener(
  185. ElementResizeListener captionResizeListener) {
  186. detachListeners();
  187. this.captionResizeListener = captionResizeListener;
  188. attachListeners();
  189. }
  190. /**
  191. * Returns the widget resize listener for this slot if one has been set.
  192. *
  193. * @return the listener or {@code null} if not set
  194. */
  195. public ElementResizeListener getWidgetResizeListener() {
  196. return widgetResizeListener;
  197. }
  198. /**
  199. * Sets the widget resize listener for this slot.
  200. *
  201. * @param widgetResizeListener
  202. * the listener to set, or {@code null} to remove a previously
  203. * set listener
  204. */
  205. public void setWidgetResizeListener(
  206. ElementResizeListener widgetResizeListener) {
  207. detachListeners();
  208. this.widgetResizeListener = widgetResizeListener;
  209. attachListeners();
  210. }
  211. /**
  212. * Returns the spacing element resize listener for this slot if one has been
  213. * set.
  214. *
  215. * @return the listener or {@code null} if not set
  216. */
  217. public ElementResizeListener getSpacingResizeListener() {
  218. return spacingResizeListener;
  219. }
  220. /**
  221. * Sets the spacing element resize listener for this slot.
  222. *
  223. * @param spacingResizeListener
  224. * the listener to set, or {@code null} to remove a previously
  225. * set listener
  226. */
  227. public void setSpacingResizeListener(
  228. ElementResizeListener spacingResizeListener) {
  229. detachListeners();
  230. this.spacingResizeListener = spacingResizeListener;
  231. attachListeners();
  232. }
  233. /**
  234. * Returns the alignment for the slot.
  235. *
  236. * @return the alignment
  237. */
  238. public AlignmentInfo getAlignment() {
  239. return alignment;
  240. }
  241. /**
  242. * Sets the style names for the slot containing the widget.
  243. *
  244. * @param stylenames
  245. * The style names for the slot
  246. */
  247. protected void setStyleNames(String... stylenames) {
  248. setStyleName(SLOT_CLASSNAME);
  249. if (stylenames != null) {
  250. for (String stylename : stylenames) {
  251. addStyleDependentName(stylename);
  252. }
  253. }
  254. // Ensure alignment style names are correct
  255. setAlignment(alignment);
  256. }
  257. /**
  258. * Sets how the widget is aligned inside the slot.
  259. *
  260. * @param alignment
  261. * The alignment inside the slot
  262. */
  263. public void setAlignment(AlignmentInfo alignment) {
  264. this.alignment = alignment;
  265. if (alignment != null && alignment.isHorizontalCenter()) {
  266. addStyleName(ALIGN_CLASS_PREFIX + "center");
  267. removeStyleName(ALIGN_CLASS_PREFIX + "right");
  268. } else if (alignment != null && alignment.isRight()) {
  269. addStyleName(ALIGN_CLASS_PREFIX + "right");
  270. removeStyleName(ALIGN_CLASS_PREFIX + "center");
  271. } else {
  272. removeStyleName(ALIGN_CLASS_PREFIX + "right");
  273. removeStyleName(ALIGN_CLASS_PREFIX + "center");
  274. }
  275. if (alignment != null && alignment.isVerticalCenter()) {
  276. addStyleName(ALIGN_CLASS_PREFIX + "middle");
  277. removeStyleName(ALIGN_CLASS_PREFIX + "bottom");
  278. } else if (alignment != null && alignment.isBottom()) {
  279. addStyleName(ALIGN_CLASS_PREFIX + "bottom");
  280. removeStyleName(ALIGN_CLASS_PREFIX + "middle");
  281. } else {
  282. removeStyleName(ALIGN_CLASS_PREFIX + "middle");
  283. removeStyleName(ALIGN_CLASS_PREFIX + "bottom");
  284. }
  285. }
  286. /**
  287. * Set how the slot should be expanded relative to the other slots. 0 means
  288. * that the slot should not participate in the division of space based on
  289. * the expand ratios but instead be allocated space based on its natural
  290. * size. Other values causes the slot to get a share of the otherwise
  291. * unallocated space in proportion to the slot's expand ratio value.
  292. *
  293. * @param expandRatio
  294. * The ratio of the space the slot should occupy
  295. *
  296. */
  297. public void setExpandRatio(double expandRatio) {
  298. this.expandRatio = expandRatio;
  299. }
  300. /**
  301. * Get the expand ratio for the slot. The expand ratio describes how the
  302. * slot should be resized compared to other slots in the layout
  303. *
  304. * @return the expand ratio of the slot
  305. *
  306. * @see #setExpandRatio(double)
  307. */
  308. public double getExpandRatio() {
  309. return expandRatio;
  310. }
  311. /**
  312. * Set the spacing for the slot. The spacing determines if there should be
  313. * empty space around the slot when the slot.
  314. *
  315. * @param spacing
  316. * Should spacing be enabled
  317. */
  318. public void setSpacing(boolean spacing) {
  319. if (spacing && spacer == null) {
  320. spacer = DOM.createDiv();
  321. spacer.addClassName("v-spacing");
  322. /*
  323. * This has to be done here for the initial render. In other cases
  324. * where the spacer already exists onAttach will handle it.
  325. */
  326. getElement().getParentElement().insertBefore(spacer, getElement());
  327. } else if (!spacing && spacer != null) {
  328. // Remove listener before spacer to avoid memory leak
  329. LayoutManager lm = layout.getLayoutManager();
  330. if (lm != null && spacingResizeListener != null) {
  331. lm.removeElementResizeListener(spacer, spacingResizeListener);
  332. }
  333. spacer.removeFromParent();
  334. spacer = null;
  335. }
  336. }
  337. /**
  338. * Get the element which is added to make the spacing.
  339. *
  340. * @return the spacing element
  341. */
  342. @SuppressWarnings("deprecation")
  343. public com.google.gwt.user.client.Element getSpacingElement() {
  344. return DOM.asOld(spacer);
  345. }
  346. /**
  347. * Does the slot have spacing.
  348. *
  349. * @return {@code true} if the slot has spacing, {@code false} otherwise
  350. */
  351. public boolean hasSpacing() {
  352. return getSpacingElement() != null;
  353. }
  354. /**
  355. * Get the vertical amount in pixels of the spacing.
  356. *
  357. * @return the height of the spacing element or zero if this slot doesn't
  358. * have spacing
  359. */
  360. protected int getVerticalSpacing() {
  361. if (spacer == null) {
  362. return 0;
  363. } else if (layout.getLayoutManager() != null) {
  364. return layout.getLayoutManager().getOuterHeight(spacer);
  365. }
  366. return spacer.getOffsetHeight();
  367. }
  368. /**
  369. * Get the horizontal amount of pixels of the spacing.
  370. *
  371. * @return the width of the spacing element or zero if this slot doesn't
  372. * have spacing
  373. */
  374. protected int getHorizontalSpacing() {
  375. if (spacer == null) {
  376. return 0;
  377. } else if (layout.getLayoutManager() != null) {
  378. return layout.getLayoutManager().getOuterWidth(spacer);
  379. }
  380. return spacer.getOffsetWidth();
  381. }
  382. /**
  383. * Set the position of the caption relative to the slot.
  384. *
  385. * @param captionPosition
  386. * The position of the caption
  387. */
  388. public void setCaptionPosition(CaptionPosition captionPosition) {
  389. if (caption == null) {
  390. return;
  391. }
  392. captionWrap.removeClassName("v-caption-on-"
  393. + this.captionPosition.name().toLowerCase(Locale.ROOT));
  394. this.captionPosition = captionPosition;
  395. if (captionPosition == CaptionPosition.BOTTOM
  396. || captionPosition == CaptionPosition.RIGHT) {
  397. captionWrap.appendChild(caption);
  398. } else {
  399. captionWrap.insertFirst(caption);
  400. }
  401. captionWrap.addClassName("v-caption-on-"
  402. + captionPosition.name().toLowerCase(Locale.ROOT));
  403. }
  404. /**
  405. * Get the position of the caption relative to the slot.
  406. *
  407. * @return the position
  408. */
  409. public CaptionPosition getCaptionPosition() {
  410. return captionPosition;
  411. }
  412. /**
  413. * Set the caption of the slot.
  414. *
  415. * @param captionText
  416. * The text of the caption
  417. * @param iconUrl
  418. * The icon URL, must already be run trough translateVaadinUri()
  419. * @param styles
  420. * The style names
  421. * @param error
  422. * The error message
  423. * @param showError
  424. * Should the error message be shown
  425. * @param required
  426. * Is the (field) required
  427. * @param enabled
  428. * Is the component enabled
  429. *
  430. * @deprecated Use
  431. * {@link #setCaption(String, Icon, List, String, boolean, boolean, boolean)}
  432. * instead
  433. */
  434. @Deprecated
  435. public void setCaption(String captionText, String iconUrl,
  436. List<String> styles, String error, boolean showError,
  437. boolean required, boolean enabled) {
  438. Icon icon;
  439. if (FontIcon.isFontIconUri(iconUrl)) {
  440. icon = GWT.create(FontIcon.class);
  441. } else {
  442. icon = GWT.create(ImageIcon.class);
  443. }
  444. icon.setUri(iconUrl);
  445. setCaption(captionText, icon, styles, error, showError, required,
  446. enabled);
  447. }
  448. /**
  449. * Set the caption of the slot as text.
  450. *
  451. * @param captionText
  452. * The text of the caption
  453. * @param icon
  454. * The icon
  455. * @param styles
  456. * The style names
  457. * @param error
  458. * The error message
  459. * @param showError
  460. * Should the error message be shown
  461. * @param required
  462. * Is the (field) required
  463. * @param enabled
  464. * Is the component enabled
  465. */
  466. public void setCaption(String captionText, Icon icon, List<String> styles,
  467. String error, boolean showError, boolean required,
  468. boolean enabled) {
  469. setCaption(captionText, icon, styles, error, showError, required,
  470. enabled, false);
  471. }
  472. /**
  473. * Set the caption of the slot.
  474. *
  475. * @param captionText
  476. * The text of the caption
  477. * @param icon
  478. * The icon
  479. * @param styles
  480. * The style names
  481. * @param error
  482. * The error message
  483. * @param showError
  484. * Should the error message be shown
  485. * @param required
  486. * Is the (field) required
  487. * @param enabled
  488. * Is the component enabled
  489. * @param captionAsHtml
  490. * true if the caption should be rendered as HTML, false
  491. * otherwise
  492. */
  493. public void setCaption(String captionText, Icon icon, List<String> styles,
  494. String error, boolean showError, boolean required, boolean enabled,
  495. boolean captionAsHtml) {
  496. setCaption(captionText, icon, styles, error, null, showError, required,
  497. enabled, captionAsHtml);
  498. }
  499. /**
  500. * Set the caption of the slot.
  501. *
  502. * @param captionText
  503. * The text of the caption
  504. * @param icon
  505. * The icon
  506. * @param styles
  507. * The style names
  508. * @param error
  509. * The error message
  510. * @param errorLevel
  511. * The error level
  512. * @param showError
  513. * Should the error message be shown
  514. * @param required
  515. * Is the (field) required
  516. * @param enabled
  517. * Is the component enabled
  518. * @param captionAsHtml
  519. * true if the caption should be rendered as HTML, false
  520. * otherwise
  521. * @since 8.2
  522. */
  523. public void setCaption(String captionText, Icon icon, List<String> styles,
  524. String error, ErrorLevel errorLevel, boolean showError,
  525. boolean required, boolean enabled, boolean captionAsHtml) {
  526. // TODO place for optimization: check if any of these have changed
  527. // since last time, and only run those changes
  528. // Caption wrappers
  529. Widget widget = getWidget();
  530. final Element focusedElement = WidgetUtil.getFocusedElement();
  531. // By default focus will not be lost
  532. boolean focusLost = false;
  533. if (captionText != null || icon != null || error != null || required) {
  534. if (caption == null) {
  535. caption = DOM.createDiv();
  536. captionWrap = DOM.createDiv();
  537. captionWrap.addClassName(StyleConstants.UI_WIDGET);
  538. captionWrap.addClassName("v-has-caption");
  539. getElement().appendChild(captionWrap);
  540. orphan(widget);
  541. captionWrap.appendChild(widget.getElement());
  542. adopt(widget);
  543. // Made changes to DOM. Focus can be lost if it was in the
  544. // widget.
  545. focusLost = (focusedElement == null ? false
  546. : widget.getElement().isOrHasChild(focusedElement));
  547. }
  548. } else if (caption != null) {
  549. orphan(widget);
  550. getElement().appendChild(widget.getElement());
  551. adopt(widget);
  552. captionWrap.removeFromParent();
  553. caption = null;
  554. captionWrap = null;
  555. // Made changes to DOM. Focus can be lost if it was in the widget.
  556. focusLost = (focusedElement == null ? false
  557. : widget.getElement().isOrHasChild(focusedElement));
  558. }
  559. // Caption text
  560. if (captionText != null) {
  561. if (this.captionText == null) {
  562. this.captionText = DOM.createSpan();
  563. this.captionText.addClassName("v-captiontext");
  564. caption.appendChild(this.captionText);
  565. }
  566. if (captionText.trim().isEmpty()) {
  567. this.captionText.setInnerHTML("&nbsp;");
  568. } else {
  569. if (captionAsHtml) {
  570. this.captionText.setInnerHTML(captionText);
  571. } else {
  572. this.captionText.setInnerText(captionText);
  573. }
  574. }
  575. } else if (this.captionText != null) {
  576. this.captionText.removeFromParent();
  577. this.captionText = null;
  578. }
  579. // Icon
  580. if (this.icon != null) {
  581. this.icon.getElement().removeFromParent();
  582. }
  583. if (icon != null) {
  584. caption.insertFirst(icon.getElement());
  585. }
  586. this.icon = icon;
  587. // Required
  588. if (required) {
  589. if (requiredIcon == null) {
  590. requiredIcon = DOM.createSpan();
  591. // TODO decide something better (e.g. use CSS to insert the
  592. // character)
  593. requiredIcon.setInnerHTML("*");
  594. requiredIcon.setClassName("v-required-field-indicator");
  595. // The star should not be read by the screen reader, as it is
  596. // purely visual. Required state is set at the element level for
  597. // the screen reader.
  598. Roles.getTextboxRole().setAriaHiddenState(requiredIcon, true);
  599. }
  600. caption.appendChild(requiredIcon);
  601. } else if (requiredIcon != null) {
  602. requiredIcon.removeFromParent();
  603. requiredIcon = null;
  604. }
  605. // Error
  606. if (error != null && showError) {
  607. setErrorIndicatorElementVisible(true);
  608. ErrorUtil.setErrorLevelStyle(getErrorIndicatorElement(),
  609. StyleConstants.STYLE_NAME_ERROR_INDICATOR, errorLevel);
  610. } else {
  611. setErrorIndicatorElementVisible(false);
  612. }
  613. if (caption != null) {
  614. // Styles
  615. caption.setClassName("v-caption");
  616. if (styles != null) {
  617. for (String style : styles) {
  618. caption.addClassName("v-caption-" + style);
  619. }
  620. }
  621. if (enabled) {
  622. caption.removeClassName("v-disabled");
  623. } else {
  624. caption.addClassName("v-disabled");
  625. }
  626. // Caption position
  627. if (captionText != null || icon != null) {
  628. setCaptionPosition(CaptionPosition.TOP);
  629. } else {
  630. setCaptionPosition(CaptionPosition.RIGHT);
  631. }
  632. }
  633. if (focusLost) {
  634. // Find out what element is currently focused.
  635. Element currentFocus = WidgetUtil.getFocusedElement();
  636. if (currentFocus != null
  637. && currentFocus.equals(Document.get().getBody())) {
  638. // Focus has moved to BodyElement and should be moved back to
  639. // original location. This happened because of adding or
  640. // removing the captionWrap
  641. focusedElement.focus();
  642. } else if (currentFocus != focusedElement) {
  643. // Focus is either moved somewhere else on purpose or IE has
  644. // lost it. Investigate further.
  645. Timer focusTimer = new Timer() {
  646. @Override
  647. public void run() {
  648. if (WidgetUtil.getFocusedElement() == null) {
  649. // This should never become an infinite loop and
  650. // even if it does it will be stopped once something
  651. // is done with the browser.
  652. schedule(25);
  653. } else if (WidgetUtil.getFocusedElement()
  654. .equals(Document.get().getBody())) {
  655. // Focus found it's way to BodyElement. Now it can
  656. // be restored
  657. focusedElement.focus();
  658. }
  659. }
  660. };
  661. // Newer IE versions can handle things immediately.
  662. focusTimer.run();
  663. }
  664. }
  665. }
  666. /**
  667. * Does the slot have a caption.
  668. *
  669. * @return {@code true} if the slot has a caption, {@code false} otherwise
  670. */
  671. public boolean hasCaption() {
  672. return caption != null;
  673. }
  674. /**
  675. * Get the slots caption element.
  676. *
  677. * @return the caption element or {@code null} if there is no caption
  678. */
  679. @SuppressWarnings("deprecation")
  680. public com.google.gwt.user.client.Element getCaptionElement() {
  681. return DOM.asOld(caption);
  682. }
  683. private boolean relativeWidth = false;
  684. /**
  685. * Set if the slot has a relative width.
  686. *
  687. * @param relativeWidth
  688. * True if slot uses relative width, false if the slot has a
  689. * static width
  690. */
  691. public void setRelativeWidth(boolean relativeWidth) {
  692. this.relativeWidth = relativeWidth;
  693. updateRelativeSize(relativeWidth, "width");
  694. }
  695. /**
  696. * Returns whether the slot's width is relative.
  697. *
  698. * @return {@code true} if the slot uses relative width, {@code false} if
  699. * the slot has a static width
  700. */
  701. public boolean hasRelativeWidth() {
  702. return relativeWidth;
  703. }
  704. private boolean relativeHeight = false;
  705. /**
  706. * Set if the slot has a relative height.
  707. *
  708. * @param relativeHeight
  709. * True if the slot uses a relative height, false if the slot has
  710. * a static height
  711. */
  712. public void setRelativeHeight(boolean relativeHeight) {
  713. this.relativeHeight = relativeHeight;
  714. updateRelativeSize(relativeHeight, "height");
  715. }
  716. /**
  717. * Returns whether the slot's height is relative.
  718. *
  719. * @return {@code true} if the slot uses relative height, {@code false} if
  720. * the slot has a static height
  721. */
  722. public boolean hasRelativeHeight() {
  723. return relativeHeight;
  724. }
  725. /**
  726. * Updates the captions size if the slot is relative
  727. *
  728. * @param isRelativeSize
  729. * Is the slot relatively sized
  730. * @param direction
  731. * The direction of the caption
  732. */
  733. private void updateRelativeSize(boolean isRelativeSize, String direction) {
  734. if (isRelativeSize && hasCaption()) {
  735. captionWrap.getStyle().setProperty(direction,
  736. getWidget().getElement().getStyle().getProperty(direction));
  737. captionWrap.addClassName("v-has-" + direction);
  738. } else if (hasCaption()) {
  739. if (direction.equals("height")) {
  740. captionWrap.getStyle().clearHeight();
  741. } else {
  742. captionWrap.getStyle().clearWidth();
  743. }
  744. captionWrap.removeClassName("v-has-" + direction);
  745. captionWrap.getStyle().clearPaddingTop();
  746. captionWrap.getStyle().clearPaddingRight();
  747. captionWrap.getStyle().clearPaddingBottom();
  748. captionWrap.getStyle().clearPaddingLeft();
  749. caption.getStyle().clearMarginTop();
  750. caption.getStyle().clearMarginRight();
  751. caption.getStyle().clearMarginBottom();
  752. caption.getStyle().clearMarginLeft();
  753. }
  754. }
  755. /*
  756. * (non-Javadoc)
  757. *
  758. * @see com.google.gwt.user.client.ui.Widget#onBrowserEvent(com.google.gwt
  759. * .user.client.Event)
  760. */
  761. @Override
  762. public void onBrowserEvent(Event event) {
  763. super.onBrowserEvent(event);
  764. if (DOM.eventGetType(event) == Event.ONLOAD && icon != null
  765. && icon.getElement() == DOM.eventGetTarget(event)) {
  766. if (layout.getLayoutManager() != null) {
  767. layout.getLayoutManager().layoutLater();
  768. } else {
  769. layout.updateCaptionOffset(caption);
  770. }
  771. }
  772. }
  773. /*
  774. * (non-Javadoc)
  775. *
  776. * @see com.google.gwt.user.client.ui.SimplePanel#getContainerElement()
  777. */
  778. @SuppressWarnings("deprecation")
  779. @Override
  780. protected com.google.gwt.user.client.Element getContainerElement() {
  781. if (captionWrap == null) {
  782. return getElement();
  783. } else {
  784. return DOM.asOld(captionWrap);
  785. }
  786. }
  787. /*
  788. * (non-Javadoc)
  789. *
  790. * @see com.google.gwt.user.client.ui.Widget#onDetach()
  791. */
  792. @Override
  793. protected void onDetach() {
  794. if (spacer != null) {
  795. spacer.removeFromParent();
  796. }
  797. super.onDetach();
  798. }
  799. /*
  800. * (non-Javadoc)
  801. *
  802. * @see com.google.gwt.user.client.ui.Widget#onAttach()
  803. */
  804. @Override
  805. protected void onAttach() {
  806. super.onAttach();
  807. if (spacer != null) {
  808. getElement().getParentElement().insertBefore(spacer, getElement());
  809. }
  810. }
  811. /**
  812. * Returns whether this slot has relative size in the indicated direction.
  813. *
  814. * @param vertical
  815. * {@code true} if the height should be checked, {@code false} if
  816. * the width should be checked
  817. * @return {@code true} if the slot's indicated dimension is relative,
  818. * {@code false} otherwise
  819. */
  820. public boolean isRelativeInDirection(boolean vertical) {
  821. if (vertical) {
  822. return hasRelativeHeight();
  823. } else {
  824. return hasRelativeWidth();
  825. }
  826. }
  827. @Override
  828. public Element getErrorIndicatorElement() {
  829. return errorIcon;
  830. }
  831. @Override
  832. public void setErrorIndicatorElementVisible(boolean visible) {
  833. if (visible) {
  834. if (errorIcon == null) {
  835. errorIcon = ErrorUtil.createErrorIndicatorElement();
  836. }
  837. caption.appendChild(errorIcon);
  838. } else if (errorIcon != null) {
  839. errorIcon.removeFromParent();
  840. errorIcon = null;
  841. }
  842. }
  843. }