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 13KB

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