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.

VAccordion.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. /*
  2. * Copyright 2000-2016 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;
  17. import java.util.HashSet;
  18. import java.util.Iterator;
  19. import java.util.Set;
  20. import com.google.gwt.dom.client.Element;
  21. import com.google.gwt.dom.client.Style.Unit;
  22. import com.google.gwt.dom.client.Style.Visibility;
  23. import com.google.gwt.event.dom.client.ClickEvent;
  24. import com.google.gwt.event.dom.client.ClickHandler;
  25. import com.google.gwt.user.client.DOM;
  26. import com.google.gwt.user.client.Event;
  27. import com.google.gwt.user.client.ui.ComplexPanel;
  28. import com.google.gwt.user.client.ui.Widget;
  29. import com.vaadin.client.ComponentConnector;
  30. import com.vaadin.client.VCaption;
  31. import com.vaadin.client.WidgetUtil;
  32. import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler;
  33. import com.vaadin.shared.ComponentConstants;
  34. import com.vaadin.shared.ui.accordion.AccordionState;
  35. import com.vaadin.shared.ui.tabsheet.TabState;
  36. import com.vaadin.shared.ui.tabsheet.TabsheetServerRpc;
  37. import com.vaadin.shared.util.SharedUtil;
  38. public class VAccordion extends VTabsheetBase {
  39. public static final String CLASSNAME = AccordionState.PRIMARY_STYLE_NAME;
  40. private Set<Widget> widgets = new HashSet<>();
  41. private StackItem openTab;
  42. /** For internal use only. May be removed or replaced in the future. */
  43. public int selectedItemIndex = -1;
  44. private final TouchScrollHandler touchScrollHandler;
  45. public VAccordion() {
  46. super(CLASSNAME);
  47. touchScrollHandler = TouchScrollDelegate.enableTouchScrolling(this);
  48. }
  49. @Override
  50. public void renderTab(TabState tabState, int index) {
  51. StackItem item;
  52. int itemIndex;
  53. if (getWidgetCount() <= index) {
  54. // Create stackItem and render caption
  55. item = new StackItem();
  56. if (getWidgetCount() == 0) {
  57. item.addStyleDependentName("first");
  58. }
  59. itemIndex = getWidgetCount();
  60. add(item, getElement());
  61. } else {
  62. item = getStackItem(index);
  63. itemIndex = index;
  64. }
  65. item.updateCaption(tabState);
  66. item.updateTabStyleName(tabState.styleName);
  67. item.setVisible(tabState.visible);
  68. item.setId(tabState.id);
  69. }
  70. @Override
  71. public void selectTab(int index) {
  72. selectedItemIndex = index;
  73. }
  74. @Override
  75. public void setStylePrimaryName(String style) {
  76. super.setStylePrimaryName(style);
  77. updateStyleNames(style);
  78. }
  79. @Override
  80. public void setStyleName(String style) {
  81. super.setStyleName(style);
  82. updateStyleNames(style);
  83. }
  84. protected void updateStyleNames(String primaryStyleName) {
  85. for (Widget w : getChildren()) {
  86. if (w instanceof StackItem) {
  87. StackItem item = (StackItem) w;
  88. item.updateStyleNames(primaryStyleName);
  89. }
  90. }
  91. }
  92. /** For internal use only. May be removed or replaced in the future. */
  93. public void open(int itemIndex) {
  94. StackItem item = (StackItem) getWidget(itemIndex);
  95. boolean alreadyOpen = false;
  96. if (openTab != null) {
  97. if (openTab.isOpen()) {
  98. if (openTab == item) {
  99. alreadyOpen = true;
  100. } else {
  101. openTab.close();
  102. }
  103. }
  104. }
  105. if (!alreadyOpen) {
  106. item.open();
  107. activeTabIndex = itemIndex;
  108. openTab = item;
  109. }
  110. }
  111. /** For internal use only. May be removed or replaced in the future. */
  112. public void close(StackItem item) {
  113. if (!item.isOpen()) {
  114. return;
  115. }
  116. item.close();
  117. activeTabIndex = -1;
  118. openTab = null;
  119. }
  120. public void onSelectTab(StackItem item) {
  121. final int index = getWidgetIndex(item);
  122. if (index != activeTabIndex && !disabled && !readonly
  123. && !disabledTabKeys.contains(tabKeys.get(index))) {
  124. addStyleDependentName("loading");
  125. connector.getRpcProxy(TabsheetServerRpc.class)
  126. .setSelected(tabKeys.get(index).toString());
  127. }
  128. }
  129. /**
  130. * A StackItem has always two children, Child 0 is a VCaption, Child 1 is
  131. * the actual child widget.
  132. */
  133. public class StackItem extends ComplexPanel implements ClickHandler {
  134. private Widget widget;
  135. private String id;
  136. public void setHeight(int height) {
  137. if (height == -1) {
  138. super.setHeight("");
  139. content.getStyle().setHeight(0, Unit.PX);
  140. } else {
  141. super.setHeight((height + getCaptionHeight()) + "px");
  142. content.getStyle().setHeight(height, Unit.PX);
  143. content.getStyle().setTop(getCaptionHeight(), Unit.PX);
  144. }
  145. }
  146. public void setId(String newId) {
  147. if (!SharedUtil.equals(newId, id)) {
  148. if (id != null) {
  149. getElement().removeAttribute("id");
  150. }
  151. id = newId;
  152. if (id != null && !id.isEmpty()) {
  153. getElement().setId(id);
  154. }
  155. }
  156. }
  157. public Widget getComponent() {
  158. return widget;
  159. }
  160. @Override
  161. public void setVisible(boolean visible) {
  162. super.setVisible(visible);
  163. }
  164. public void setHeightFromWidget() {
  165. Widget widget = getChildWidget();
  166. if (widget == null) {
  167. return;
  168. }
  169. int paintableHeight = widget.getElement().getOffsetHeight();
  170. setHeight(paintableHeight);
  171. }
  172. /**
  173. * Returns caption width including padding
  174. *
  175. * @return
  176. */
  177. public int getCaptionWidth() {
  178. if (caption == null) {
  179. return 0;
  180. }
  181. int captionWidth = caption.getRequiredWidth();
  182. int padding = WidgetUtil.measureHorizontalPaddingAndBorder(
  183. caption.getElement(), 18);
  184. return captionWidth + padding;
  185. }
  186. public void setWidth(int width) {
  187. if (width == -1) {
  188. super.setWidth("");
  189. } else {
  190. super.setWidth(width + "px");
  191. }
  192. }
  193. public int getHeight() {
  194. return getOffsetHeight();
  195. }
  196. public int getCaptionHeight() {
  197. return captionNode.getOffsetHeight();
  198. }
  199. private VCaption caption;
  200. private boolean open = false;
  201. private Element content = DOM.createDiv();
  202. private Element captionNode = DOM.createDiv();
  203. private String styleName;
  204. public StackItem() {
  205. setElement(DOM.createDiv());
  206. caption = new VCaption(client);
  207. caption.addClickHandler(this);
  208. super.add(caption, captionNode);
  209. DOM.appendChild(captionNode, caption.getElement());
  210. DOM.appendChild(getElement(), captionNode);
  211. DOM.appendChild(getElement(), content);
  212. updateStyleNames(VAccordion.this.getStylePrimaryName());
  213. touchScrollHandler.addElement(getContainerElement());
  214. close();
  215. }
  216. private void updateStyleNames(String primaryStyleName) {
  217. content.removeClassName(getStylePrimaryName() + "-content");
  218. captionNode.removeClassName(getStylePrimaryName() + "-caption");
  219. setStylePrimaryName(primaryStyleName + "-item");
  220. updateTabStyleName(getStylePrimaryName());
  221. captionNode.addClassName(getStylePrimaryName() + "-caption");
  222. content.addClassName(getStylePrimaryName() + "-content");
  223. }
  224. @Override
  225. public void onBrowserEvent(Event event) {
  226. onSelectTab(this);
  227. }
  228. public com.google.gwt.user.client.Element getContainerElement() {
  229. return DOM.asOld(content);
  230. }
  231. public Widget getChildWidget() {
  232. return widget;
  233. }
  234. public void replaceWidget(Widget newWidget) {
  235. if (widget != null) {
  236. widgets.remove(widget);
  237. if (open) {
  238. remove(widget);
  239. }
  240. }
  241. widget = newWidget;
  242. widgets.add(newWidget);
  243. if (open) {
  244. add(widget, content);
  245. }
  246. }
  247. public void open() {
  248. add(widget, content);
  249. open = true;
  250. content.getStyle().setTop(getCaptionHeight(), Unit.PX);
  251. content.getStyle().setLeft(0, Unit.PX);
  252. content.getStyle().clearVisibility();
  253. addStyleDependentName("open");
  254. }
  255. public void hide() {
  256. content.getStyle().setVisibility(Visibility.HIDDEN);
  257. }
  258. public void close() {
  259. if (widget != null) {
  260. remove(widget);
  261. }
  262. content.getStyle().setVisibility(Visibility.HIDDEN);
  263. content.getStyle().setTop(-100000, Unit.PX);
  264. content.getStyle().setLeft(-100000, Unit.PX);
  265. removeStyleDependentName("open");
  266. setHeight(-1);
  267. setWidth("");
  268. open = false;
  269. }
  270. public boolean isOpen() {
  271. return open;
  272. }
  273. /**
  274. * Updates the content of the open tab of the accordion.
  275. *
  276. * This method is mostly for internal use and may change in future
  277. * versions.
  278. *
  279. * @since 7.2
  280. * @param newWidget
  281. * new content
  282. */
  283. public void setContent(Widget newWidget) {
  284. if (widget == null) {
  285. widget = newWidget;
  286. widgets.add(newWidget);
  287. } else if (widget != newWidget) {
  288. replaceWidget(newWidget);
  289. }
  290. if (isOpen() && isDynamicHeight()) {
  291. setHeightFromWidget();
  292. }
  293. }
  294. @Override
  295. public void onClick(ClickEvent event) {
  296. onSelectTab(this);
  297. }
  298. public void updateCaption(TabState tabState) {
  299. // TODO need to call this because the caption does not have an owner
  300. caption.setCaptionAsHtml(isTabCaptionsAsHtml());
  301. caption.updateCaptionWithoutOwner(tabState.caption,
  302. !tabState.enabled, hasAttribute(tabState.description),
  303. hasAttribute(tabState.componentError),
  304. tabState.componentErrorLevel,
  305. connector.getResourceUrl(
  306. ComponentConstants.ICON_RESOURCE + tabState.key),
  307. tabState.iconAltText);
  308. }
  309. private boolean hasAttribute(String string) {
  310. return string != null && !string.trim().isEmpty();
  311. }
  312. /**
  313. * Updates a tabs stylename from the child UIDL
  314. *
  315. * @param uidl
  316. * The child UIDL of the tab
  317. */
  318. private void updateTabStyleName(String newStyleName) {
  319. if (newStyleName != null && !newStyleName.isEmpty()) {
  320. if (!newStyleName.equals(styleName)) {
  321. // If we have a new style name
  322. if (styleName != null && !styleName.isEmpty()) {
  323. // Remove old style name if present
  324. removeStyleDependentName(styleName);
  325. }
  326. // Set new style name
  327. addStyleDependentName(newStyleName);
  328. styleName = newStyleName;
  329. }
  330. } else if (styleName != null) {
  331. // Remove the set stylename if no stylename is present in the
  332. // uidl
  333. removeStyleDependentName(styleName);
  334. styleName = null;
  335. }
  336. }
  337. public int getWidgetWidth() {
  338. return DOM.getFirstChild(content).getOffsetWidth();
  339. }
  340. public boolean contains(ComponentConnector p) {
  341. return (getChildWidget() == p.getWidget());
  342. }
  343. public boolean isCaptionVisible() {
  344. return caption.isVisible();
  345. }
  346. }
  347. @Override
  348. protected void clearPaintables() {
  349. clear();
  350. }
  351. @Override
  352. public Iterator<Widget> getWidgetIterator() {
  353. return widgets.iterator();
  354. }
  355. @Override
  356. public int getTabCount() {
  357. return getWidgetCount();
  358. }
  359. @Override
  360. public void removeTab(int index) {
  361. StackItem item = getStackItem(index);
  362. remove(item);
  363. if (selectedItemIndex == index) {
  364. selectedItemIndex = -1;
  365. }
  366. touchScrollHandler.removeElement(item.getContainerElement());
  367. }
  368. @Override
  369. public ComponentConnector getTab(int index) {
  370. if (index < getWidgetCount()) {
  371. StackItem stackItem = getStackItem(index);
  372. if (stackItem == null) {
  373. return null;
  374. }
  375. Widget w = stackItem.getChildWidget();
  376. if (w != null) {
  377. return getConnectorForWidget(w);
  378. }
  379. }
  380. return null;
  381. }
  382. /** For internal use only. May be removed or replaced in the future. */
  383. public StackItem getStackItem(int index) {
  384. return (StackItem) getWidget(index);
  385. }
  386. public Iterable<StackItem> getStackItems() {
  387. return (Iterable) getChildren();
  388. }
  389. public StackItem getOpenStackItem() {
  390. return openTab;
  391. }
  392. }