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.

IPanel.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.itmill.toolkit.terminal.gwt.client.ui;
  5. import com.google.gwt.user.client.DOM;
  6. import com.google.gwt.user.client.Element;
  7. import com.google.gwt.user.client.Event;
  8. import com.google.gwt.user.client.ui.SimplePanel;
  9. import com.google.gwt.user.client.ui.Widget;
  10. import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
  11. import com.itmill.toolkit.terminal.gwt.client.BrowserInfo;
  12. import com.itmill.toolkit.terminal.gwt.client.ContainerResizedListener;
  13. import com.itmill.toolkit.terminal.gwt.client.IErrorMessage;
  14. import com.itmill.toolkit.terminal.gwt.client.Paintable;
  15. import com.itmill.toolkit.terminal.gwt.client.UIDL;
  16. import com.itmill.toolkit.terminal.gwt.client.Util;
  17. public class IPanel extends SimplePanel implements Paintable,
  18. ContainerResizedListener {
  19. public static final String CLASSNAME = "i-panel";
  20. ApplicationConnection client;
  21. String id;
  22. private final Element captionNode = DOM.createDiv();
  23. private final Element captionText = DOM.createSpan();
  24. private Icon icon;
  25. private final Element bottomDecoration = DOM.createDiv();
  26. private final Element contentNode = DOM.createDiv();
  27. private Element errorIndicatorElement;
  28. private IErrorMessage errorMessage;
  29. private String height;
  30. private Paintable layout;
  31. ShortcutActionHandler shortcutHandler;
  32. private String width;
  33. private Element geckoCaptionMeter;
  34. private int scrollTop;
  35. private int scrollLeft;
  36. public IPanel() {
  37. super();
  38. DOM.appendChild(getElement(), captionNode);
  39. DOM.appendChild(captionNode, captionText);
  40. DOM.appendChild(getElement(), contentNode);
  41. DOM.appendChild(getElement(), bottomDecoration);
  42. setStyleName(CLASSNAME);
  43. DOM
  44. .setElementProperty(captionNode, "className", CLASSNAME
  45. + "-caption");
  46. DOM
  47. .setElementProperty(contentNode, "className", CLASSNAME
  48. + "-content");
  49. DOM.setElementProperty(bottomDecoration, "className", CLASSNAME
  50. + "-deco");
  51. DOM.sinkEvents(getElement(), Event.ONKEYDOWN);
  52. DOM.sinkEvents(contentNode, Event.ONSCROLL);
  53. }
  54. protected Element getContainerElement() {
  55. return contentNode;
  56. }
  57. private void setCaption(String text) {
  58. DOM.setInnerText(captionText, text);
  59. }
  60. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  61. // Ensure correct implementation
  62. if (client.updateComponent(this, uidl, false)) {
  63. return;
  64. }
  65. this.client = client;
  66. id = uidl.getId();
  67. // Panel size. Height needs to be saved for later use
  68. height = uidl.hasVariable("height") ? uidl.getStringVariable("height")
  69. : null;
  70. setWidth(uidl.hasVariable("width") ? uidl.getStringVariable("width")
  71. : "");
  72. // Restore default stylenames
  73. DOM
  74. .setElementProperty(captionNode, "className", CLASSNAME
  75. + "-caption");
  76. DOM
  77. .setElementProperty(contentNode, "className", CLASSNAME
  78. + "-content");
  79. DOM.setElementProperty(bottomDecoration, "className", CLASSNAME
  80. + "-deco");
  81. // Handle caption displaying
  82. boolean hasCaption = false;
  83. if (uidl.hasAttribute("caption")
  84. && !uidl.getStringAttribute("caption").equals("")) {
  85. setCaption(uidl.getStringAttribute("caption"));
  86. hasCaption = true;
  87. } else {
  88. setCaption("");
  89. DOM.setElementProperty(captionNode, "className", CLASSNAME
  90. + "-nocaption");
  91. }
  92. setIconUri(uidl, client);
  93. handleDescription(uidl);
  94. handleError(uidl);
  95. // Add proper stylenames for all elements. This way we can prevent
  96. // unwanted CSS selector inheritance.
  97. if (uidl.hasAttribute("style")) {
  98. final String[] styles = uidl.getStringAttribute("style").split(" ");
  99. final String captionBaseClass = CLASSNAME
  100. + (hasCaption ? "-caption" : "-nocaption");
  101. final String contentBaseClass = CLASSNAME + "-content";
  102. final String decoBaseClass = CLASSNAME + "-deco";
  103. String captionClass = captionBaseClass;
  104. String contentClass = contentBaseClass;
  105. String decoClass = decoBaseClass;
  106. for (int i = 0; i < styles.length; i++) {
  107. captionClass += " " + captionBaseClass + "-" + styles[i];
  108. contentClass += " " + contentBaseClass + "-" + styles[i];
  109. decoClass += " " + decoBaseClass + "-" + styles[i];
  110. }
  111. DOM.setElementProperty(captionNode, "className", captionClass);
  112. DOM.setElementProperty(contentNode, "className", contentClass);
  113. DOM.setElementProperty(bottomDecoration, "className", decoClass);
  114. }
  115. // Height adjustment
  116. iLayout(false);
  117. // Render content
  118. final UIDL layoutUidl = uidl.getChildUIDL(0);
  119. final Paintable newLayout = client.getPaintable(layoutUidl);
  120. if (newLayout != layout) {
  121. if (layout != null) {
  122. client.unregisterPaintable(layout);
  123. }
  124. setWidget((Widget) newLayout);
  125. layout = newLayout;
  126. }
  127. (layout).updateFromUIDL(layoutUidl, client);
  128. // We may have actions attached to this panel
  129. if (uidl.getChildCount() > 1) {
  130. final int cnt = uidl.getChildCount();
  131. for (int i = 1; i < cnt; i++) {
  132. UIDL childUidl = uidl.getChildUIDL(i);
  133. if (childUidl.getTag().equals("actions")) {
  134. if (shortcutHandler == null) {
  135. shortcutHandler = new ShortcutActionHandler(id, client);
  136. }
  137. shortcutHandler.updateActionMap(childUidl);
  138. }
  139. }
  140. }
  141. if (uidl.hasVariable("scrollTop")
  142. && uidl.getIntVariable("scrollTop") != scrollTop) {
  143. scrollTop = uidl.getIntVariable("scrollTop");
  144. DOM.setElementPropertyInt(contentNode, "scrollTop", scrollTop);
  145. }
  146. if (uidl.hasVariable("scrollLeft")
  147. && uidl.getIntVariable("scrollLeft") != scrollLeft) {
  148. scrollLeft = uidl.getIntVariable("scrollLeft");
  149. DOM.setElementPropertyInt(contentNode, "scrollLeft", scrollLeft);
  150. }
  151. }
  152. private void handleError(UIDL uidl) {
  153. if (uidl.hasAttribute("error")) {
  154. final UIDL errorUidl = uidl.getErrors();
  155. if (errorIndicatorElement == null) {
  156. errorIndicatorElement = DOM.createDiv();
  157. DOM.setElementProperty(errorIndicatorElement, "className",
  158. "i-errorindicator");
  159. DOM.sinkEvents(errorIndicatorElement, Event.MOUSEEVENTS);
  160. sinkEvents(Event.MOUSEEVENTS);
  161. }
  162. DOM.insertBefore(captionNode, errorIndicatorElement, captionText);
  163. if (errorMessage == null) {
  164. errorMessage = new IErrorMessage();
  165. }
  166. errorMessage.updateFromUIDL(errorUidl);
  167. } else if (errorIndicatorElement != null) {
  168. DOM.removeChild(captionNode, errorIndicatorElement);
  169. errorIndicatorElement = null;
  170. }
  171. }
  172. private void handleDescription(UIDL uidl) {
  173. DOM.setElementProperty(captionText, "title", uidl
  174. .hasAttribute("description") ? uidl
  175. .getStringAttribute("description") : "");
  176. }
  177. private void setIconUri(UIDL uidl, ApplicationConnection client) {
  178. final String iconUri = uidl.hasAttribute("icon") ? uidl
  179. .getStringAttribute("icon") : null;
  180. if (iconUri == null) {
  181. if (icon != null) {
  182. DOM.removeChild(captionNode, icon.getElement());
  183. icon = null;
  184. }
  185. } else {
  186. if (icon == null) {
  187. icon = new Icon(client);
  188. DOM.insertChild(captionNode, icon.getElement(), 0);
  189. }
  190. icon.setUri(iconUri);
  191. }
  192. }
  193. public void iLayout() {
  194. iLayout(true);
  195. }
  196. public void iLayout(boolean runGeckoFix) {
  197. if (height != null && height != "") {
  198. final boolean hasChildren = getWidget() != null;
  199. Element contentEl = null;
  200. String origPositioning = null;
  201. // save scroll position
  202. int scrollTop = DOM.getElementPropertyInt(contentNode, "scrollTop");
  203. int scrollLeft = DOM.getElementPropertyInt(contentNode,
  204. "scrollLeft");
  205. if (hasChildren) {
  206. // Remove children temporary form normal flow to detect proper
  207. // size
  208. contentEl = getWidget().getElement();
  209. origPositioning = DOM.getStyleAttribute(contentEl, "position");
  210. DOM.setStyleAttribute(contentEl, "position", "absolute");
  211. }
  212. // Set defaults
  213. DOM.setStyleAttribute(contentNode, "overflow", "hidden");
  214. DOM.setStyleAttribute(contentNode, "height", "");
  215. // Calculate target height
  216. super.setHeight(height);
  217. final int targetHeight = getOffsetHeight();
  218. // Calculate used height
  219. super.setHeight("");
  220. final int usedHeight = DOM.getElementPropertyInt(bottomDecoration,
  221. "offsetTop")
  222. + DOM.getElementPropertyInt(bottomDecoration,
  223. "offsetHeight")
  224. - DOM.getElementPropertyInt(getElement(), "offsetTop");
  225. // Calculate content area height (don't allow negative values)
  226. int h = targetHeight - usedHeight;
  227. if (h < 0) {
  228. h = 0;
  229. }
  230. // Set proper values for content element
  231. DOM.setStyleAttribute(contentNode, "height", h + "px");
  232. DOM.setStyleAttribute(contentNode, "overflow", "auto");
  233. // Restore content to flow
  234. if (hasChildren) {
  235. ApplicationConnection.getConsole().log(
  236. "positioning:" + origPositioning);
  237. DOM.setStyleAttribute(contentEl, "position", origPositioning);
  238. }
  239. // restore scroll position
  240. DOM.setElementPropertyInt(contentNode, "scrollTop", scrollTop);
  241. DOM.setElementPropertyInt(contentNode, "scrollLeft", scrollLeft);
  242. } else {
  243. DOM.setStyleAttribute(contentNode, "height", "");
  244. }
  245. if (runGeckoFix && BrowserInfo.get().isGecko()) {
  246. // workaround for #1764
  247. if (width == null || width.equals("")) {
  248. if (geckoCaptionMeter == null) {
  249. geckoCaptionMeter = DOM.createDiv();
  250. DOM.appendChild(captionNode, geckoCaptionMeter);
  251. }
  252. int captionWidth = DOM.getElementPropertyInt(captionText,
  253. "offsetWidth");
  254. int availWidth = DOM.getElementPropertyInt(geckoCaptionMeter,
  255. "offsetWidth");
  256. if (captionWidth == availWidth) {
  257. /*
  258. * Caption width defines panel width -> Gecko based browsers
  259. * somehow fails to float things right, without the
  260. * "noncode" below
  261. */
  262. setWidth(getOffsetWidth() + "px");
  263. } else {
  264. DOM.setStyleAttribute(captionNode, "width", "");
  265. }
  266. }
  267. }
  268. Util.runDescendentsLayout(this);
  269. }
  270. public void onBrowserEvent(Event event) {
  271. final Element target = DOM.eventGetTarget(event);
  272. final int type = DOM.eventGetType(event);
  273. if (type == Event.ONKEYDOWN && shortcutHandler != null) {
  274. shortcutHandler.handleKeyboardEvent(event);
  275. return;
  276. }
  277. if (type == Event.ONSCROLL) {
  278. int newscrollTop = DOM.getElementPropertyInt(contentNode,
  279. "scrollTop");
  280. int newscrollLeft = DOM.getElementPropertyInt(contentNode,
  281. "scrollLeft");
  282. if (client != null
  283. && (newscrollLeft != scrollLeft || newscrollTop != scrollTop)) {
  284. ApplicationConnection.getConsole().log("scrollded panel");
  285. scrollLeft = newscrollLeft;
  286. scrollTop = newscrollTop;
  287. client.updateVariable(id, "scrollTop", scrollTop, false);
  288. client.updateVariable(id, "scrollLeft", scrollLeft, false);
  289. }
  290. } else if (errorIndicatorElement != null
  291. && DOM.compare(target, errorIndicatorElement)) {
  292. switch (type) {
  293. case Event.ONMOUSEOVER:
  294. if (errorMessage != null) {
  295. errorMessage.showAt(errorIndicatorElement);
  296. }
  297. break;
  298. case Event.ONMOUSEOUT:
  299. if (errorMessage != null) {
  300. errorMessage.hide();
  301. }
  302. break;
  303. case Event.ONCLICK:
  304. ApplicationConnection.getConsole().log(
  305. DOM.getInnerHTML(errorMessage.getElement()));
  306. return;
  307. default:
  308. break;
  309. }
  310. }
  311. }
  312. /**
  313. * Panel handles dimensions by itself.
  314. */
  315. public void setHeight(String height) {
  316. // NOP
  317. }
  318. /**
  319. * Panel handles dimensions by itself.
  320. */
  321. public void setWidth(String width) {
  322. this.width = width;
  323. // Let browser handle 100% width (DIV element takes all size by
  324. // default).
  325. // This way we can specify borders for Panel's outer element.
  326. if (width.equals("100%")) {
  327. super.setWidth("");
  328. } else {
  329. super.setWidth(width);
  330. }
  331. }
  332. }