Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. /*
  2. * Copyright 2000-2013 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.HashMap;
  18. import java.util.HashSet;
  19. import java.util.Iterator;
  20. import java.util.Set;
  21. import com.google.gwt.event.dom.client.ClickEvent;
  22. import com.google.gwt.event.dom.client.ClickHandler;
  23. import com.google.gwt.user.client.DOM;
  24. import com.google.gwt.user.client.Element;
  25. import com.google.gwt.user.client.Event;
  26. import com.google.gwt.user.client.ui.ComplexPanel;
  27. import com.google.gwt.user.client.ui.Widget;
  28. import com.vaadin.client.ComponentConnector;
  29. import com.vaadin.client.ConnectorMap;
  30. import com.vaadin.client.UIDL;
  31. import com.vaadin.client.Util;
  32. import com.vaadin.client.VCaption;
  33. import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler;
  34. import com.vaadin.shared.ui.tabsheet.TabsheetBaseConstants;
  35. import com.vaadin.shared.ui.tabsheet.TabsheetConstants;
  36. public class VAccordion extends VTabsheetBase {
  37. public static final String CLASSNAME = "v-accordion";
  38. private Set<Widget> widgets = new HashSet<Widget>();
  39. /** For internal use only. May be removed or replaced in the future. */
  40. public HashMap<StackItem, UIDL> lazyUpdateMap = new HashMap<StackItem, UIDL>();
  41. private StackItem openTab = null;
  42. /** For internal use only. May be removed or replaced in the future. */
  43. public int selectedUIDLItemIndex = -1;
  44. private final TouchScrollHandler touchScrollHandler;
  45. public VAccordion() {
  46. super(CLASSNAME);
  47. touchScrollHandler = TouchScrollDelegate.enableTouchScrolling(this);
  48. }
  49. @Override
  50. public void renderTab(UIDL tabUidl, int index, boolean selected,
  51. boolean hidden) {
  52. StackItem item;
  53. int itemIndex;
  54. if (getWidgetCount() <= index) {
  55. // Create stackItem and render caption
  56. item = new StackItem(tabUidl);
  57. if (getWidgetCount() == 0) {
  58. item.addStyleDependentName("first");
  59. }
  60. itemIndex = getWidgetCount();
  61. add(item, getElement());
  62. } else {
  63. item = getStackItem(index);
  64. item = moveStackItemIfNeeded(item, index, tabUidl);
  65. itemIndex = index;
  66. }
  67. item.updateCaption(tabUidl);
  68. item.updateTabStyleName(tabUidl
  69. .getStringAttribute(TabsheetConstants.TAB_STYLE_NAME));
  70. item.setVisible(!hidden);
  71. if (selected) {
  72. selectedUIDLItemIndex = itemIndex;
  73. }
  74. if (tabUidl.getChildCount() > 0) {
  75. lazyUpdateMap.put(item, tabUidl.getChildUIDL(0));
  76. }
  77. }
  78. @Override
  79. public void setStylePrimaryName(String style) {
  80. super.setStylePrimaryName(style);
  81. updateStyleNames(style);
  82. }
  83. @Override
  84. public void setStyleName(String style) {
  85. super.setStyleName(style);
  86. updateStyleNames(style);
  87. }
  88. protected void updateStyleNames(String primaryStyleName) {
  89. for (Widget w : getChildren()) {
  90. if (w instanceof StackItem) {
  91. StackItem item = (StackItem) w;
  92. item.updateStyleNames(primaryStyleName);
  93. }
  94. }
  95. }
  96. /**
  97. * This method tries to find out if a tab has been rendered with a different
  98. * index previously. If this is the case it re-orders the children so the
  99. * same StackItem is used for rendering this time. E.g. if the first tab has
  100. * been removed all tabs which contain cached content must be moved 1 step
  101. * up to preserve the cached content.
  102. *
  103. * @param item
  104. * @param newIndex
  105. * @param tabUidl
  106. * @return
  107. */
  108. private StackItem moveStackItemIfNeeded(StackItem item, int newIndex,
  109. UIDL tabUidl) {
  110. UIDL tabContentUIDL = null;
  111. ComponentConnector tabContent = null;
  112. if (tabUidl.getChildCount() > 0) {
  113. tabContentUIDL = tabUidl.getChildUIDL(0);
  114. tabContent = client.getPaintable(tabContentUIDL);
  115. }
  116. Widget itemWidget = item.getComponent();
  117. if (tabContent != null) {
  118. if (tabContent.getWidget() != itemWidget) {
  119. /*
  120. * This is not the same widget as before, find out if it has
  121. * been moved
  122. */
  123. int oldIndex = -1;
  124. StackItem oldItem = null;
  125. for (int i = 0; i < getWidgetCount(); i++) {
  126. Widget w = getWidget(i);
  127. oldItem = (StackItem) w;
  128. if (tabContent == oldItem.getComponent()) {
  129. oldIndex = i;
  130. break;
  131. }
  132. }
  133. if (oldIndex != -1 && oldIndex > newIndex) {
  134. /*
  135. * The tab has previously been rendered in another position
  136. * so we must move the cached content to correct position.
  137. * We move only items with oldIndex > newIndex to prevent
  138. * moving items already rendered in this update. If for
  139. * instance tabs 1,2,3 are removed and added as 3,2,1 we
  140. * cannot re-use "1" when we get to the third tab.
  141. */
  142. insert(oldItem, getElement(), newIndex, true);
  143. return oldItem;
  144. }
  145. }
  146. } else {
  147. // Tab which has never been loaded. Must assure we use an empty
  148. // StackItem
  149. Widget oldWidget = item.getComponent();
  150. if (oldWidget != null) {
  151. oldWidget.removeFromParent();
  152. }
  153. }
  154. return item;
  155. }
  156. /** For internal use only. May be removed or replaced in the future. */
  157. public void open(int itemIndex) {
  158. StackItem item = (StackItem) getWidget(itemIndex);
  159. boolean alreadyOpen = false;
  160. if (openTab != null) {
  161. if (openTab.isOpen()) {
  162. if (openTab == item) {
  163. alreadyOpen = true;
  164. } else {
  165. openTab.close();
  166. }
  167. }
  168. }
  169. if (!alreadyOpen) {
  170. item.open();
  171. activeTabIndex = itemIndex;
  172. openTab = item;
  173. }
  174. }
  175. /** For internal use only. May be removed or replaced in the future. */
  176. public void close(StackItem item) {
  177. if (!item.isOpen()) {
  178. return;
  179. }
  180. item.close();
  181. activeTabIndex = -1;
  182. openTab = null;
  183. }
  184. public void onSelectTab(StackItem item) {
  185. final int index = getWidgetIndex(item);
  186. if (index != activeTabIndex && !disabled && !readonly
  187. && !disabledTabKeys.contains(tabKeys.get(index))) {
  188. addStyleDependentName("loading");
  189. client.updateVariable(id, "selected", "" + tabKeys.get(index), true);
  190. }
  191. }
  192. /**
  193. * A StackItem has always two children, Child 0 is a VCaption, Child 1 is
  194. * the actual child widget.
  195. */
  196. public class StackItem extends ComplexPanel implements ClickHandler {
  197. public void setHeight(int height) {
  198. if (height == -1) {
  199. super.setHeight("");
  200. DOM.setStyleAttribute(content, "height", "0px");
  201. } else {
  202. super.setHeight((height + getCaptionHeight()) + "px");
  203. DOM.setStyleAttribute(content, "height", height + "px");
  204. DOM.setStyleAttribute(content, "top", getCaptionHeight() + "px");
  205. }
  206. }
  207. public Widget getComponent() {
  208. if (getWidgetCount() < 2) {
  209. return null;
  210. }
  211. return getWidget(1);
  212. }
  213. @Override
  214. public void setVisible(boolean visible) {
  215. super.setVisible(visible);
  216. }
  217. public void setHeightFromWidget() {
  218. Widget widget = getChildWidget();
  219. if (widget == null) {
  220. return;
  221. }
  222. int paintableHeight = widget.getElement().getOffsetHeight();
  223. setHeight(paintableHeight);
  224. }
  225. /**
  226. * Returns caption width including padding
  227. *
  228. * @return
  229. */
  230. public int getCaptionWidth() {
  231. if (caption == null) {
  232. return 0;
  233. }
  234. int captionWidth = caption.getRequiredWidth();
  235. int padding = Util.measureHorizontalPaddingAndBorder(
  236. caption.getElement(), 18);
  237. return captionWidth + padding;
  238. }
  239. public void setWidth(int width) {
  240. if (width == -1) {
  241. super.setWidth("");
  242. } else {
  243. super.setWidth(width + "px");
  244. }
  245. }
  246. public int getHeight() {
  247. return getOffsetHeight();
  248. }
  249. public int getCaptionHeight() {
  250. return captionNode.getOffsetHeight();
  251. }
  252. private VCaption caption;
  253. private boolean open = false;
  254. private Element content = DOM.createDiv();
  255. private Element captionNode = DOM.createDiv();
  256. private String styleName;
  257. public StackItem(UIDL tabUidl) {
  258. setElement(DOM.createDiv());
  259. caption = new VCaption(client);
  260. caption.addClickHandler(this);
  261. super.add(caption, captionNode);
  262. DOM.appendChild(captionNode, caption.getElement());
  263. DOM.appendChild(getElement(), captionNode);
  264. DOM.appendChild(getElement(), content);
  265. updateStyleNames(VAccordion.this.getStylePrimaryName());
  266. touchScrollHandler.addElement(getContainerElement());
  267. close();
  268. }
  269. private void updateStyleNames(String primaryStyleName) {
  270. content.removeClassName(getStylePrimaryName() + "-content");
  271. captionNode.removeClassName(getStylePrimaryName() + "-caption");
  272. setStylePrimaryName(primaryStyleName + "-item");
  273. updateTabStyleName(getStylePrimaryName());
  274. captionNode.addClassName(getStylePrimaryName() + "-caption");
  275. content.addClassName(getStylePrimaryName() + "-content");
  276. }
  277. @Override
  278. public void onBrowserEvent(Event event) {
  279. onSelectTab(this);
  280. }
  281. public Element getContainerElement() {
  282. return content;
  283. }
  284. public Widget getChildWidget() {
  285. if (getWidgetCount() > 1) {
  286. return getWidget(1);
  287. } else {
  288. return null;
  289. }
  290. }
  291. public void replaceWidget(Widget newWidget) {
  292. if (getWidgetCount() > 1) {
  293. Widget oldWidget = getWidget(1);
  294. widgets.remove(oldWidget);
  295. }
  296. add(newWidget, content);
  297. widgets.add(newWidget);
  298. }
  299. public void open() {
  300. open = true;
  301. DOM.setStyleAttribute(content, "top", getCaptionHeight() + "px");
  302. DOM.setStyleAttribute(content, "left", "0px");
  303. DOM.setStyleAttribute(content, "visibility", "");
  304. addStyleDependentName("open");
  305. }
  306. public void hide() {
  307. DOM.setStyleAttribute(content, "visibility", "hidden");
  308. }
  309. public void close() {
  310. DOM.setStyleAttribute(content, "visibility", "hidden");
  311. DOM.setStyleAttribute(content, "top", "-100000px");
  312. DOM.setStyleAttribute(content, "left", "-100000px");
  313. removeStyleDependentName("open");
  314. setHeight(-1);
  315. setWidth("");
  316. open = false;
  317. }
  318. public boolean isOpen() {
  319. return open;
  320. }
  321. public void setContent(UIDL contentUidl) {
  322. final ComponentConnector newPntbl = client
  323. .getPaintable(contentUidl);
  324. Widget newWidget = newPntbl.getWidget();
  325. if (getChildWidget() == null) {
  326. add(newWidget, content);
  327. widgets.add(newWidget);
  328. } else if (getChildWidget() != newWidget) {
  329. replaceWidget(newWidget);
  330. }
  331. if (isOpen() && isDynamicHeight()) {
  332. setHeightFromWidget();
  333. }
  334. }
  335. @Override
  336. public void onClick(ClickEvent event) {
  337. onSelectTab(this);
  338. }
  339. public void updateCaption(UIDL uidl) {
  340. // TODO need to call this because the caption does not have an owner
  341. caption.updateCaptionWithoutOwner(
  342. uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_CAPTION),
  343. uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DISABLED),
  344. uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DESCRIPTION),
  345. uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ERROR_MESSAGE),
  346. uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ICON));
  347. }
  348. /**
  349. * Updates a tabs stylename from the child UIDL
  350. *
  351. * @param uidl
  352. * The child uidl of the tab
  353. */
  354. private void updateTabStyleName(String newStyleName) {
  355. if (newStyleName != null && newStyleName.length() != 0) {
  356. if (!newStyleName.equals(styleName)) {
  357. // If we have a new style name
  358. if (styleName != null && styleName.length() != 0) {
  359. // Remove old style name if present
  360. removeStyleDependentName(styleName);
  361. }
  362. // Set new style name
  363. addStyleDependentName(newStyleName);
  364. styleName = newStyleName;
  365. }
  366. } else if (styleName != null) {
  367. // Remove the set stylename if no stylename is present in the
  368. // uidl
  369. removeStyleDependentName(styleName);
  370. styleName = null;
  371. }
  372. }
  373. public int getWidgetWidth() {
  374. return DOM.getFirstChild(content).getOffsetWidth();
  375. }
  376. public boolean contains(ComponentConnector p) {
  377. return (getChildWidget() == p.getWidget());
  378. }
  379. public boolean isCaptionVisible() {
  380. return caption.isVisible();
  381. }
  382. }
  383. @Override
  384. protected void clearPaintables() {
  385. clear();
  386. }
  387. boolean isDynamicWidth() {
  388. ComponentConnector paintable = ConnectorMap.get(client).getConnector(
  389. this);
  390. return paintable.isUndefinedWidth();
  391. }
  392. boolean isDynamicHeight() {
  393. ComponentConnector paintable = ConnectorMap.get(client).getConnector(
  394. this);
  395. return paintable.isUndefinedHeight();
  396. }
  397. @Override
  398. public Iterator<Widget> getWidgetIterator() {
  399. return widgets.iterator();
  400. }
  401. @Override
  402. public int getTabCount() {
  403. return getWidgetCount();
  404. }
  405. @Override
  406. public void removeTab(int index) {
  407. StackItem item = getStackItem(index);
  408. remove(item);
  409. touchScrollHandler.removeElement(item.getContainerElement());
  410. }
  411. @Override
  412. public ComponentConnector getTab(int index) {
  413. if (index < getWidgetCount()) {
  414. StackItem stackItem = getStackItem(index);
  415. if (stackItem == null) {
  416. return null;
  417. }
  418. Widget w = stackItem.getChildWidget();
  419. if (w != null) {
  420. return ConnectorMap.get(client).getConnector(w);
  421. }
  422. }
  423. return null;
  424. }
  425. /** For internal use only. May be removed or replaced in the future. */
  426. public StackItem getStackItem(int index) {
  427. return (StackItem) getWidget(index);
  428. }
  429. public Iterable<StackItem> getStackItems() {
  430. return (Iterable) getChildren();
  431. }
  432. public StackItem getOpenStackItem() {
  433. return openTab;
  434. }
  435. }