Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

ITabsheet.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.itmill.toolkit.terminal.gwt.client.ui;
  5. import java.util.HashMap;
  6. import java.util.Iterator;
  7. import com.google.gwt.user.client.Command;
  8. import com.google.gwt.user.client.DOM;
  9. import com.google.gwt.user.client.DeferredCommand;
  10. import com.google.gwt.user.client.Element;
  11. import com.google.gwt.user.client.Event;
  12. import com.google.gwt.user.client.ui.ClickListener;
  13. import com.google.gwt.user.client.ui.ComplexPanel;
  14. import com.google.gwt.user.client.ui.Label;
  15. import com.google.gwt.user.client.ui.Widget;
  16. import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
  17. import com.itmill.toolkit.terminal.gwt.client.ICaption;
  18. import com.itmill.toolkit.terminal.gwt.client.ContainerResizedListener;
  19. import com.itmill.toolkit.terminal.gwt.client.Paintable;
  20. import com.itmill.toolkit.terminal.gwt.client.UIDL;
  21. import com.itmill.toolkit.terminal.gwt.client.Util;
  22. public class ITabsheet extends ITabsheetBase implements
  23. ContainerResizedListener {
  24. class TabBar extends ComplexPanel implements ClickListener {
  25. private Element tr = DOM.createTR();
  26. private Element spacerTd = DOM.createTD();
  27. TabBar() {
  28. Element el = DOM.createTable();
  29. Element tbody = DOM.createTBody();
  30. DOM.appendChild(el, tbody);
  31. DOM.appendChild(tbody, tr);
  32. setStyleName(spacerTd, CLASSNAME + "-spacertd");
  33. DOM.appendChild(tr, spacerTd);
  34. DOM.appendChild(spacerTd, DOM.createDiv());
  35. setElement(el);
  36. }
  37. protected Element getContainerElement() {
  38. return tr;
  39. }
  40. private Widget oldSelected;
  41. public int getTabCount() {
  42. return getWidgetCount();
  43. }
  44. public void addTab(ICaption c) {
  45. Element td = DOM.createTD();
  46. setStyleName(td, CLASSNAME + "-tabitemcell");
  47. if (getWidgetCount() == 0) {
  48. setStyleName(td, CLASSNAME + "-tabitemcell-first", true);
  49. }
  50. Element div = DOM.createDiv();
  51. setStyleName(div, CLASSNAME + "-tabitem");
  52. DOM.appendChild(td, div);
  53. DOM.insertBefore(tr, td, spacerTd);
  54. c.addClickListener(this);
  55. add(c, div);
  56. }
  57. public void onClick(Widget sender) {
  58. int index = getWidgetIndex(sender);
  59. onTabSelected(index);
  60. }
  61. public void selectTab(int index) {
  62. Widget newSelected = getWidget(index);
  63. Widget.setStyleName(DOM.getParent(newSelected.getElement()),
  64. CLASSNAME + "-tabitem-selected", true);
  65. if (oldSelected != null && oldSelected != newSelected) {
  66. Widget.setStyleName(DOM.getParent(oldSelected.getElement()),
  67. CLASSNAME + "-tabitem-selected", false);
  68. }
  69. oldSelected = newSelected;
  70. }
  71. public void removeTab(int i) {
  72. remove(i);
  73. }
  74. public boolean remove(Widget w) {
  75. ((ICaption) w).removeClickListener(this);
  76. return super.remove(w);
  77. }
  78. public ICaption getTab(int index) {
  79. if (index >= getWidgetCount()) {
  80. return null;
  81. }
  82. return (ICaption) getWidget(index);
  83. }
  84. }
  85. public static final String CLASSNAME = "i-tabsheet";
  86. public static final String TABS_CLASSNAME = "i-tabsheet-tabcontainer";
  87. public static final String SCROLLER_CLASSNAME = "i-tabsheet-scroller";
  88. private final Element tabs; // tabbar and 'scroller' container
  89. private final Element scroller; // tab-scroller element
  90. private final Element scrollerNext; // tab-scroller next button element
  91. private final Element scrollerPrev; // tab-scroller prev button element
  92. private int scrollerIndex = 0;
  93. private final TabBar tb;
  94. private final ITabsheetPanel tp;
  95. private final Element contentNode, deco;
  96. private final HashMap captions = new HashMap();
  97. private String height;
  98. private String width;
  99. private boolean waitingForResponse;
  100. /**
  101. * Previous visible widget is set invisible with CSS (not display: none, but
  102. * visibility: hidden), to avoid flickering during render process. Normal
  103. * visibility must be returned later when new widget is rendered.
  104. */
  105. private Widget previousVisibleWidget;
  106. private void onTabSelected(final int tabIndex) {
  107. if (disabled || waitingForResponse) {
  108. return;
  109. }
  110. final Object tabKey = tabKeys.get(tabIndex);
  111. if (disabledTabKeys.contains(tabKey)) {
  112. return;
  113. }
  114. if (client != null && activeTabIndex != tabIndex) {
  115. tb.selectTab(tabIndex);
  116. addStyleDependentName("loading");
  117. // run updating variables in deferred command to bypass some
  118. // FF
  119. // optimization issues
  120. DeferredCommand.addCommand(new Command() {
  121. public void execute() {
  122. previousVisibleWidget = tp.getWidget(tp.getVisibleWidget());
  123. DOM.setStyleAttribute(previousVisibleWidget.getElement(),
  124. "visibility", "hidden");
  125. client.updateVariable(id, "selected", tabKeys.get(tabIndex)
  126. .toString(), true);
  127. }
  128. });
  129. waitingForResponse = true;
  130. }
  131. }
  132. public ITabsheet() {
  133. super(CLASSNAME);
  134. // Tab scrolling
  135. DOM.setStyleAttribute(getElement(), "overflow", "hidden");
  136. tabs = DOM.createDiv();
  137. DOM.setElementProperty(tabs, "className", TABS_CLASSNAME);
  138. scroller = DOM.createDiv();
  139. DOM.setElementProperty(scroller, "className", SCROLLER_CLASSNAME);
  140. scrollerPrev = DOM.createButton();
  141. DOM.setElementProperty(scrollerPrev, "className", SCROLLER_CLASSNAME
  142. + "Prev");
  143. DOM.sinkEvents(scrollerPrev, Event.ONCLICK);
  144. scrollerNext = DOM.createButton();
  145. DOM.setElementProperty(scrollerNext, "className", SCROLLER_CLASSNAME
  146. + "Next");
  147. DOM.sinkEvents(scrollerNext, Event.ONCLICK);
  148. DOM.appendChild(getElement(), tabs);
  149. // Tabs
  150. tb = new TabBar();
  151. tp = new ITabsheetPanel();
  152. tp.setStyleName(CLASSNAME + "-tabsheetpanel");
  153. contentNode = DOM.createDiv();
  154. deco = DOM.createDiv();
  155. addStyleDependentName("loading"); // Indicate initial progress
  156. tb.setStyleName(CLASSNAME + "-tabs");
  157. DOM
  158. .setElementProperty(contentNode, "className", CLASSNAME
  159. + "-content");
  160. DOM.setElementProperty(deco, "className", CLASSNAME + "-deco");
  161. add(tb, tabs);
  162. DOM.appendChild(scroller, scrollerPrev);
  163. DOM.appendChild(scroller, scrollerNext);
  164. DOM.appendChild(getElement(), contentNode);
  165. add(tp, contentNode);
  166. DOM.appendChild(getElement(), deco);
  167. DOM.appendChild(tabs, scroller);
  168. // TODO Use for Safari only. Fix annoying 1px first cell in TabBar.
  169. // DOM.setStyleAttribute(DOM.getFirstChild(DOM.getFirstChild(DOM
  170. // .getFirstChild(tb.getElement()))), "display", "none");
  171. }
  172. public void onBrowserEvent(Event event) {
  173. // Tab scrolling
  174. if (isScrolledTabs()
  175. && DOM.compare(DOM.eventGetTarget(event), scrollerPrev)) {
  176. if (scrollerIndex > 0) {
  177. scrollerIndex--;
  178. DOM.setStyleAttribute(DOM.getChild(DOM.getFirstChild(DOM
  179. .getFirstChild(tb.getElement())), scrollerIndex),
  180. "display", "");
  181. updateTabScroller();
  182. }
  183. } else if (isClippedTabs()
  184. && DOM.compare(DOM.eventGetTarget(event), scrollerNext)) {
  185. int tabs = tb.getTabCount();
  186. if (scrollerIndex + 1 <= tabs) {
  187. DOM.setStyleAttribute(DOM.getChild(DOM.getFirstChild(DOM
  188. .getFirstChild(tb.getElement())), scrollerIndex),
  189. "display", "none");
  190. scrollerIndex++;
  191. updateTabScroller();
  192. }
  193. } else {
  194. super.onBrowserEvent(event);
  195. }
  196. }
  197. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  198. super.updateFromUIDL(uidl, client);
  199. // Add proper stylenames for all elements (easier to prevent unwanted
  200. // style inheritance)
  201. if (uidl.hasAttribute("style")) {
  202. final String[] styles = uidl.getStringAttribute("style").split(" ");
  203. final String contentBaseClass = CLASSNAME + "-content";
  204. String contentClass = contentBaseClass;
  205. final String decoBaseClass = CLASSNAME + "-deco";
  206. String decoClass = decoBaseClass;
  207. for (int i = 0; i < styles.length; i++) {
  208. tb.addStyleDependentName(styles[i]);
  209. contentClass += " " + contentBaseClass + "-" + styles[i];
  210. decoClass += " " + decoBaseClass + "-" + styles[i];
  211. }
  212. DOM.setElementProperty(contentNode, "className", contentClass);
  213. DOM.setElementProperty(deco, "className", decoClass);
  214. } else {
  215. tb.setStyleName(CLASSNAME + "-tabs");
  216. DOM.setElementProperty(contentNode, "className", CLASSNAME
  217. + "-content");
  218. DOM.setElementProperty(deco, "className", CLASSNAME + "-deco");
  219. }
  220. if (uidl.hasAttribute("hidetabs")) {
  221. tb.setVisible(false);
  222. addStyleName(CLASSNAME + "-hidetabs");
  223. } else {
  224. tb.setVisible(true);
  225. removeStyleName(CLASSNAME + "-hidetabs");
  226. }
  227. // tabs; push or not
  228. if (uidl.hasAttribute("width")) {
  229. // update width later, in updateTabScroller();
  230. DOM.setStyleAttribute(tabs, "width", "1px");
  231. DOM.setStyleAttribute(tabs, "overflow", "hidden");
  232. } else {
  233. showAllTabs();
  234. DOM.setStyleAttribute(tabs, "width", "");
  235. DOM.setStyleAttribute(tabs, "overflow", "visible");
  236. }
  237. updateTabScroller();
  238. waitingForResponse = false;
  239. }
  240. protected void renderTab(final UIDL tabUidl, int index, boolean selected) {
  241. ICaption c = tb.getTab(index);
  242. if (c == null) {
  243. c = new ICaption(null, client);
  244. tb.addTab(c);
  245. }
  246. c.updateCaption(tabUidl);
  247. captions.put("" + index, c);
  248. if (selected) {
  249. renderContent(tabUidl.getChildUIDL(0));
  250. tb.selectTab(index);
  251. } else {
  252. if (tabUidl.getChildCount() > 0) {
  253. // updating a drawn child on hidden tab
  254. Paintable paintable = client.getPaintable(tabUidl
  255. .getChildUIDL(0));
  256. if (tp.getWidgetIndex((Widget) paintable) < 0) {
  257. tp.insert((Widget) paintable, index);
  258. }
  259. paintable.updateFromUIDL(tabUidl.getChildUIDL(0), client);
  260. } else if (tp.getWidgetCount() <= index) {
  261. tp.add(new Label(""));
  262. }
  263. }
  264. }
  265. protected void selectTab(int index, final UIDL contentUidl) {
  266. if (index != activeTabIndex) {
  267. activeTabIndex = index;
  268. tb.selectTab(activeTabIndex);
  269. }
  270. renderContent(contentUidl);
  271. }
  272. private void renderContent(final UIDL contentUIDL) {
  273. final Paintable content = client.getPaintable(contentUIDL);
  274. if (tp.getWidgetCount() > activeTabIndex) {
  275. Widget old = tp.getWidget(activeTabIndex);
  276. if (old != content) {
  277. tp.remove(activeTabIndex);
  278. if (old instanceof Paintable) {
  279. client.unregisterPaintable((Paintable) old);
  280. }
  281. tp.insert((Widget) content, activeTabIndex);
  282. }
  283. } else {
  284. tp.add((Widget) content);
  285. }
  286. tp.showWidget(activeTabIndex);
  287. ITabsheet.this.iLayout();
  288. (content).updateFromUIDL(contentUIDL, client);
  289. ITabsheet.this.removeStyleDependentName("loading");
  290. if (previousVisibleWidget != null) {
  291. DOM.setStyleAttribute(previousVisibleWidget.getElement(),
  292. "visibility", "");
  293. previousVisibleWidget = null;
  294. }
  295. }
  296. public void setHeight(String height) {
  297. if (this.height == null && height == null) {
  298. return;
  299. }
  300. String oldHeight = this.height;
  301. this.height = height;
  302. if ((this.height != null && height == null)
  303. || (this.height == null && height != null)
  304. || !height.equals(oldHeight)) {
  305. iLayout();
  306. }
  307. }
  308. public void setWidth(String width) {
  309. String oldWidth = this.width;
  310. this.width = width;
  311. if ("100%".equals(width)) {
  312. // Allow browser to calculate width
  313. super.setWidth("");
  314. } else {
  315. super.setWidth(width);
  316. }
  317. if ((this.width != null && width == null)
  318. || (this.width == null && width != null)
  319. || !width.equals(oldWidth)) {
  320. // Run descendant layout functions
  321. Util.runDescendentsLayout(this);
  322. }
  323. }
  324. public void iLayout() {
  325. if (height != null && height != "") {
  326. super.setHeight(height);
  327. final int contentHeight = getOffsetHeight()
  328. - DOM.getElementPropertyInt(deco, "offsetHeight")
  329. - tb.getOffsetHeight();
  330. // Set proper values for content element
  331. DOM.setStyleAttribute(contentNode, "height", contentHeight + "px");
  332. DOM.setStyleAttribute(contentNode, "overflow", "auto");
  333. tp.setHeight("100%");
  334. } else {
  335. DOM.setStyleAttribute(contentNode, "height", "");
  336. DOM.setStyleAttribute(contentNode, "overflow", "");
  337. }
  338. Util.runDescendentsLayout(this);
  339. updateTabScroller();
  340. }
  341. /**
  342. * Layouts the tab-scroller elements, and applies styles.
  343. */
  344. private void updateTabScroller() {
  345. if (width != null) {
  346. DOM.setStyleAttribute(tabs, "width", width);
  347. }
  348. if (scrollerIndex > tb.getTabCount()) {
  349. scrollerIndex = 0;
  350. }
  351. boolean scrolled = isScrolledTabs();
  352. boolean clipped = isClippedTabs();
  353. if (tb.isVisible() && (scrolled || clipped)) {
  354. DOM.setStyleAttribute(scroller, "display", "");
  355. DOM.setElementProperty(scrollerPrev, "className",
  356. SCROLLER_CLASSNAME + (scrolled ? "Prev" : "Prev-disabled"));
  357. DOM.setElementProperty(scrollerNext, "className",
  358. SCROLLER_CLASSNAME + (clipped ? "Next" : "Next-disabled"));
  359. } else {
  360. DOM.setStyleAttribute(scroller, "display", "none");
  361. }
  362. }
  363. private void showAllTabs() {
  364. scrollerIndex = 0;
  365. Element tr = DOM.getFirstChild(DOM.getFirstChild(tb.getElement()));
  366. for (int i = 0; i < tb.getTabCount(); i++) {
  367. DOM.setStyleAttribute(DOM.getChild(tr, i), "display", "");
  368. }
  369. }
  370. private boolean isScrolledTabs() {
  371. return scrollerIndex > 0;
  372. }
  373. private boolean isClippedTabs() {
  374. return tb.getOffsetWidth() > getOffsetWidth();
  375. }
  376. protected void clearPaintables() {
  377. int i = tb.getTabCount();
  378. while (i > 0) {
  379. tb.removeTab(--i);
  380. }
  381. tp.clear();
  382. }
  383. protected Iterator getPaintableIterator() {
  384. return tp.iterator();
  385. }
  386. public boolean hasChildComponent(Widget component) {
  387. if (tp.getWidgetIndex(component) < 0) {
  388. return false;
  389. } else {
  390. return true;
  391. }
  392. }
  393. public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
  394. int widgetIndex = tp.getWidgetIndex(oldComponent);
  395. tp.remove(oldComponent);
  396. tp.insert(newComponent, widgetIndex);
  397. }
  398. public void updateCaption(Paintable component, UIDL uidl) {
  399. int i = tp.getWidgetIndex((Widget) component);
  400. ICaption c = (ICaption) captions.get("" + i);
  401. c.updateCaption(uidl);
  402. }
  403. }