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.

IAccordion.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. package com.itmill.toolkit.terminal.gwt.client.ui;
  2. import java.util.ArrayList;
  3. import java.util.HashMap;
  4. import java.util.HashSet;
  5. import java.util.Iterator;
  6. import java.util.Set;
  7. import com.google.gwt.user.client.DOM;
  8. import com.google.gwt.user.client.Element;
  9. import com.google.gwt.user.client.ui.ClickListener;
  10. import com.google.gwt.user.client.ui.ComplexPanel;
  11. import com.google.gwt.user.client.ui.Widget;
  12. import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
  13. import com.itmill.toolkit.terminal.gwt.client.BrowserInfo;
  14. import com.itmill.toolkit.terminal.gwt.client.ContainerResizedListener;
  15. import com.itmill.toolkit.terminal.gwt.client.ICaption;
  16. import com.itmill.toolkit.terminal.gwt.client.Paintable;
  17. import com.itmill.toolkit.terminal.gwt.client.RenderInformation;
  18. import com.itmill.toolkit.terminal.gwt.client.RenderSpace;
  19. import com.itmill.toolkit.terminal.gwt.client.UIDL;
  20. import com.itmill.toolkit.terminal.gwt.client.Util;
  21. public class IAccordion extends ITabsheetBase implements
  22. ContainerResizedListener {
  23. public static final String CLASSNAME = "i-accordion";
  24. private ArrayList<StackItem> stack = new ArrayList<StackItem>();
  25. private Set<Paintable> paintables = new HashSet<Paintable>();
  26. private String height;
  27. private String width;
  28. private HashMap<StackItem, UIDL> lazyUpdateMap = new HashMap<StackItem, UIDL>();
  29. private RenderSpace renderSpace = new RenderSpace(0, 0, true);
  30. private StackItem openTab = null;
  31. private boolean rendering = false;
  32. private int selectedUIDLItemIndex = -1;
  33. private RenderInformation renderInformation = new RenderInformation();
  34. public IAccordion() {
  35. super(CLASSNAME);
  36. // IE6 needs this to calculate offsetHeight correctly
  37. if (BrowserInfo.get().isIE6()) {
  38. DOM.setStyleAttribute(getElement(), "zoom", "1");
  39. }
  40. }
  41. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  42. rendering = true;
  43. selectedUIDLItemIndex = -1;
  44. super.updateFromUIDL(uidl, client);
  45. /*
  46. * Render content after all tabs have been created and we know how large
  47. * the content area is
  48. */
  49. if (selectedUIDLItemIndex >= 0) {
  50. StackItem selectedItem = stack.get(selectedUIDLItemIndex);
  51. UIDL selectedTabUIDL = lazyUpdateMap.remove(selectedItem);
  52. open(selectedUIDLItemIndex);
  53. selectedItem.setContent(selectedTabUIDL);
  54. } else if (!uidl.getBooleanAttribute("cached") && openTab != null) {
  55. close(openTab);
  56. }
  57. iLayout();
  58. // finally render possible hidden tabs
  59. if (lazyUpdateMap.size() > 0) {
  60. for (Iterator iterator = lazyUpdateMap.keySet().iterator(); iterator
  61. .hasNext();) {
  62. StackItem item = (StackItem) iterator.next();
  63. item.setContent(lazyUpdateMap.get(item));
  64. }
  65. lazyUpdateMap.clear();
  66. }
  67. renderInformation.updateSize(getElement());
  68. rendering = false;
  69. }
  70. protected void renderTab(UIDL tabUidl, int index, boolean selected,
  71. boolean hidden) {
  72. StackItem item;
  73. int itemIndex;
  74. if (stack.size() <= index) {
  75. // Create stackItem and render caption
  76. item = new StackItem(tabUidl);
  77. if (stack.size() == 0) {
  78. item.addStyleDependentName("first");
  79. }
  80. stack.add(item);
  81. itemIndex = stack.size() - 1;
  82. add(item, getElement());
  83. } else {
  84. item = stack.get(index);
  85. itemIndex = index;
  86. item.updateCaption(tabUidl);
  87. }
  88. item.setVisible(!hidden);
  89. if (selected) {
  90. selectedUIDLItemIndex = itemIndex;
  91. }
  92. if (tabUidl.getChildCount() > 0) {
  93. lazyUpdateMap.put(item, tabUidl.getChildUIDL(0));
  94. }
  95. }
  96. private void open(int itemIndex) {
  97. StackItem item = stack.get(itemIndex);
  98. if (openTab != null) {
  99. if (openTab.isOpen()) {
  100. if (openTab == item) {
  101. return;
  102. } else {
  103. openTab.close();
  104. }
  105. }
  106. }
  107. item.open();
  108. activeTabIndex = itemIndex;
  109. openTab = item;
  110. // Update the size for the open tab
  111. updateOpenTabSize();
  112. }
  113. private void close(StackItem item) {
  114. if (!item.isOpen()) {
  115. return;
  116. }
  117. item.close();
  118. activeTabIndex = -1;
  119. openTab = null;
  120. }
  121. protected void selectTab(final int index, final UIDL contentUidl) {
  122. StackItem item = stack.get(index);
  123. if (index != activeTabIndex) {
  124. open(index);
  125. iLayout();
  126. // TODO Check if this is needed
  127. client.runDescendentsLayout(this);
  128. }
  129. item.setContent(contentUidl);
  130. }
  131. public void onSelectTab(StackItem item) {
  132. final int index = stack.indexOf(item);
  133. if (index != activeTabIndex && !disabled && !readonly
  134. && !disabledTabKeys.contains(tabKeys.get(index))) {
  135. addStyleDependentName("loading");
  136. client
  137. .updateVariable(id, "selected", "" + tabKeys.get(index),
  138. true);
  139. }
  140. }
  141. public void setWidth(String width) {
  142. super.setWidth(width);
  143. this.width = width;
  144. if (!rendering) {
  145. updateOpenTabSize();
  146. }
  147. }
  148. public void setHeight(String height) {
  149. super.setHeight(height);
  150. this.height = height;
  151. if (!rendering) {
  152. updateOpenTabSize();
  153. }
  154. }
  155. /**
  156. * Sets the size of the open tab
  157. */
  158. private void updateOpenTabSize() {
  159. if (openTab == null) {
  160. renderSpace.setHeight(0);
  161. renderSpace.setWidth(0);
  162. return;
  163. }
  164. // WIDTH
  165. if (!isDynamicWidth()) {
  166. int w = getOffsetWidth();
  167. openTab.setWidth(w);
  168. renderSpace.setWidth(w);
  169. } else {
  170. renderSpace.setWidth(0);
  171. }
  172. // HEIGHT
  173. if (!isDynamicHeight()) {
  174. int usedPixels = 0;
  175. for (Iterator iterator = stack.iterator(); iterator.hasNext();) {
  176. StackItem item = (StackItem) iterator.next();
  177. if (item == openTab) {
  178. usedPixels += item.getCaptionHeight();
  179. } else {
  180. // This includes the captionNode borders
  181. usedPixels += item.getHeight();
  182. }
  183. }
  184. int offsetHeight = getOffsetHeight();
  185. int spaceForOpenItem = offsetHeight - usedPixels;
  186. if (spaceForOpenItem < 0) {
  187. spaceForOpenItem = 0;
  188. }
  189. renderSpace.setHeight(spaceForOpenItem);
  190. openTab.setHeight(spaceForOpenItem);
  191. } else {
  192. renderSpace.setHeight(0);
  193. openTab.setHeightFromWidget();
  194. }
  195. }
  196. public void iLayout() {
  197. if (openTab == null) {
  198. return;
  199. }
  200. if (isDynamicWidth()) {
  201. int maxWidth = 40;
  202. for (StackItem si : stack) {
  203. int captionWidth = si.getCaptionWidth();
  204. if (captionWidth > maxWidth) {
  205. maxWidth = captionWidth;
  206. }
  207. }
  208. int widgetWidth = openTab.getWidgetWidth();
  209. if (widgetWidth > maxWidth) {
  210. maxWidth = widgetWidth;
  211. }
  212. super.setWidth(maxWidth + "px");
  213. openTab.setWidth(maxWidth);
  214. }
  215. Util.runWebkitOverflowAutoFix(openTab.getContainerElement());
  216. }
  217. /**
  218. *
  219. */
  220. protected class StackItem extends ComplexPanel implements ClickListener {
  221. public void setHeight(int height) {
  222. if (height == -1) {
  223. super.setHeight("");
  224. DOM.setStyleAttribute(content, "height", "0px");
  225. } else {
  226. super.setHeight((height + getCaptionHeight()) + "px");
  227. DOM.setStyleAttribute(content, "height", height + "px");
  228. DOM
  229. .setStyleAttribute(content, "top", getCaptionHeight()
  230. + "px");
  231. }
  232. }
  233. public void setVisible(boolean visible) {
  234. super.setVisible(visible);
  235. }
  236. public void setHeightFromWidget() {
  237. Widget paintable = getPaintable();
  238. if (paintable == null) {
  239. return;
  240. }
  241. int paintableHeight = (paintable).getElement().getOffsetHeight();
  242. setHeight(paintableHeight);
  243. }
  244. /**
  245. * Returns caption width including padding
  246. *
  247. * @return
  248. */
  249. public int getCaptionWidth() {
  250. if (caption == null) {
  251. return 0;
  252. }
  253. int captionWidth = caption.getRequiredWidth();
  254. int padding = Util.measureHorizontalPadding(caption.getElement(),
  255. 18);
  256. return captionWidth + padding;
  257. }
  258. public void setWidth(int width) {
  259. if (width == -1) {
  260. super.setWidth("");
  261. } else {
  262. super.setWidth(width + "px");
  263. }
  264. }
  265. public int getHeight() {
  266. return getOffsetHeight();
  267. }
  268. public int getCaptionHeight() {
  269. return captionNode.getOffsetHeight();
  270. }
  271. private ICaption caption;
  272. private boolean open = false;
  273. private Element content = DOM.createDiv();
  274. private Element captionNode = DOM.createDiv();
  275. public StackItem(UIDL tabUidl) {
  276. setElement(DOM.createDiv());
  277. caption = new ICaption(null, client);
  278. caption.addClickListener(this);
  279. super.add(caption, captionNode);
  280. DOM.appendChild(captionNode, caption.getElement());
  281. DOM.appendChild(getElement(), captionNode);
  282. DOM.appendChild(getElement(), content);
  283. setStylePrimaryName(CLASSNAME + "-item");
  284. DOM.setElementProperty(content, "className", CLASSNAME
  285. + "-item-content");
  286. DOM.setElementProperty(captionNode, "className", CLASSNAME
  287. + "-item-caption");
  288. close();
  289. updateCaption(tabUidl);
  290. }
  291. public Element getContainerElement() {
  292. return content;
  293. }
  294. public Widget getPaintable() {
  295. if (getWidgetCount() > 1) {
  296. return getWidget(1);
  297. } else {
  298. return null;
  299. }
  300. }
  301. public void replacePaintable(Paintable newPntbl) {
  302. if (getWidgetCount() > 1) {
  303. client.unregisterPaintable((Paintable) getWidget(1));
  304. paintables.remove(getWidget(1));
  305. remove(1);
  306. }
  307. add((Widget) newPntbl, content);
  308. paintables.add(newPntbl);
  309. }
  310. public void open() {
  311. open = true;
  312. DOM.setStyleAttribute(content, "top", getCaptionHeight() + "px");
  313. DOM.setStyleAttribute(content, "left", "0px");
  314. DOM.setStyleAttribute(content, "visibility", "");
  315. addStyleDependentName("open");
  316. }
  317. public void hide() {
  318. DOM.setStyleAttribute(content, "visibility", "hidden");
  319. }
  320. public void close() {
  321. DOM.setStyleAttribute(content, "visibility", "hidden");
  322. DOM.setStyleAttribute(content, "top", "-100000px");
  323. DOM.setStyleAttribute(content, "left", "-100000px");
  324. removeStyleDependentName("open");
  325. setHeight(-1);
  326. open = false;
  327. }
  328. public boolean isOpen() {
  329. return open;
  330. }
  331. public void setContent(UIDL contentUidl) {
  332. final Paintable newPntbl = client.getPaintable(contentUidl);
  333. if (getPaintable() == null) {
  334. add((Widget) newPntbl, content);
  335. paintables.add(newPntbl);
  336. } else if (getPaintable() != newPntbl) {
  337. replacePaintable(newPntbl);
  338. }
  339. newPntbl.updateFromUIDL(contentUidl, client);
  340. if (isOpen() && isDynamicHeight()) {
  341. setHeightFromWidget();
  342. }
  343. }
  344. public void onClick(Widget sender) {
  345. onSelectTab(this);
  346. }
  347. public void updateCaption(UIDL uidl) {
  348. caption.updateCaption(uidl);
  349. }
  350. public int getWidgetWidth() {
  351. return DOM.getFirstChild(content).getOffsetWidth();
  352. }
  353. public boolean contains(Paintable p) {
  354. return (getPaintable() == p);
  355. }
  356. public boolean isCaptionVisible() {
  357. return caption.isVisible();
  358. }
  359. }
  360. protected void clearPaintables() {
  361. stack.clear();
  362. clear();
  363. }
  364. public boolean isDynamicHeight() {
  365. return height == null || height.equals("");
  366. }
  367. public boolean isDynamicWidth() {
  368. return width == null || width.equals("");
  369. }
  370. protected Iterator getPaintableIterator() {
  371. return paintables.iterator();
  372. }
  373. public boolean hasChildComponent(Widget component) {
  374. if (paintables.contains(component)) {
  375. return true;
  376. } else {
  377. return false;
  378. }
  379. }
  380. public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
  381. for (StackItem item : stack) {
  382. if (item.getPaintable() == oldComponent) {
  383. item.replacePaintable((Paintable) newComponent);
  384. return;
  385. }
  386. }
  387. }
  388. public void updateCaption(Paintable component, UIDL uidl) {
  389. for (Iterator iterator = stack.iterator(); iterator.hasNext();) {
  390. StackItem si = (StackItem) iterator.next();
  391. if (si.getPaintable() == component) {
  392. boolean visible = si.isVisible();
  393. si.updateCaption(uidl);
  394. if (si.isCaptionVisible() != visible) {
  395. si.setVisible(si.isCaptionVisible());
  396. }
  397. return;
  398. }
  399. }
  400. }
  401. public boolean requestLayout(Set<Paintable> child) {
  402. if (!isDynamicHeight() && !isDynamicWidth()) {
  403. /*
  404. * If the height and width has been specified for this container the
  405. * child components cannot make the size of the layout change
  406. */
  407. return true;
  408. }
  409. updateOpenTabSize();
  410. if (renderInformation.updateSize(getElement())) {
  411. /*
  412. * Size has changed so we let the child components know about the
  413. * new size.
  414. */
  415. iLayout();
  416. // TODO Check if this is needed
  417. client.runDescendentsLayout(this);
  418. return false;
  419. } else {
  420. /*
  421. * Size has not changed so we do not need to propagate the event
  422. * further
  423. */
  424. return true;
  425. }
  426. }
  427. public RenderSpace getAllocatedSpace(Widget child) {
  428. return renderSpace;
  429. }
  430. }