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

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