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.

VAbsoluteLayout.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. package com.vaadin.terminal.gwt.client.ui;
  2. import java.util.HashMap;
  3. import java.util.HashSet;
  4. import java.util.Iterator;
  5. import java.util.Map;
  6. import java.util.Set;
  7. import java.util.Map.Entry;
  8. import com.google.gwt.dom.client.DivElement;
  9. import com.google.gwt.dom.client.Document;
  10. import com.google.gwt.dom.client.Style;
  11. import com.google.gwt.event.dom.client.ClickEvent;
  12. import com.google.gwt.event.dom.client.ClickHandler;
  13. import com.google.gwt.event.shared.HandlerRegistration;
  14. import com.google.gwt.user.client.DOM;
  15. import com.google.gwt.user.client.Element;
  16. import com.google.gwt.user.client.ui.ComplexPanel;
  17. import com.google.gwt.user.client.ui.SimplePanel;
  18. import com.google.gwt.user.client.ui.Widget;
  19. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  20. import com.vaadin.terminal.gwt.client.BrowserInfo;
  21. import com.vaadin.terminal.gwt.client.Container;
  22. import com.vaadin.terminal.gwt.client.MouseEventDetails;
  23. import com.vaadin.terminal.gwt.client.Paintable;
  24. import com.vaadin.terminal.gwt.client.RenderSpace;
  25. import com.vaadin.terminal.gwt.client.UIDL;
  26. import com.vaadin.terminal.gwt.client.VCaption;
  27. public class VAbsoluteLayout extends ComplexPanel implements Container,
  28. ClickHandler {
  29. /** Tag name for widget creation */
  30. public static final String TAGNAME = "absolutelayout";
  31. /** Class name, prefix in styling */
  32. public static final String CLASSNAME = "v-absolutelayout";
  33. public static final String CLICK_EVENT_IDENTIFIER = "click";
  34. private DivElement marginElement;
  35. protected final Element canvas = DOM.createDiv();
  36. private int excessPixelsHorizontal;
  37. private int excessPixelsVertical;
  38. private Object previousStyleName;
  39. private Map<String, AbsoluteWrapper> pidToComponentWrappper = new HashMap<String, AbsoluteWrapper>();
  40. protected ApplicationConnection client;
  41. private boolean rendering;
  42. public VAbsoluteLayout() {
  43. setElement(Document.get().createDivElement());
  44. setStyleName(CLASSNAME);
  45. marginElement = Document.get().createDivElement();
  46. canvas.getStyle().setProperty("position", "relative");
  47. canvas.getStyle().setProperty("overflow", "hidden");
  48. marginElement.appendChild(canvas);
  49. getElement().appendChild(marginElement);
  50. addDomHandler(this, ClickEvent.getType());
  51. }
  52. public RenderSpace getAllocatedSpace(Widget child) {
  53. // TODO needs some special handling for components with only on edge
  54. // horizontally or vertically defined
  55. AbsoluteWrapper wrapper = (AbsoluteWrapper) child.getParent();
  56. int w;
  57. if (wrapper.left != null && wrapper.right != null) {
  58. w = wrapper.getOffsetWidth();
  59. } else if (wrapper.right != null) {
  60. // left == null
  61. // available width == right edge == offsetleft + width
  62. w = wrapper.getOffsetWidth() + wrapper.getElement().getOffsetLeft();
  63. } else {
  64. // left != null && right == null || left == null &&
  65. // right == null
  66. // available width == canvas width - offset left
  67. w = canvas.getOffsetWidth() - wrapper.getElement().getOffsetLeft();
  68. }
  69. int h;
  70. if (wrapper.top != null && wrapper.bottom != null) {
  71. h = wrapper.getOffsetHeight();
  72. } else if (wrapper.bottom != null) {
  73. // top not defined, available space 0... bottom of wrapper
  74. h = wrapper.getElement().getOffsetTop() + wrapper.getOffsetHeight();
  75. } else {
  76. // top defined or both undefined, available space == canvas - top
  77. h = canvas.getOffsetHeight() - wrapper.getElement().getOffsetTop();
  78. }
  79. return new RenderSpace(w, h);
  80. }
  81. public boolean hasChildComponent(Widget component) {
  82. for (Iterator<Entry<String, AbsoluteWrapper>> iterator = pidToComponentWrappper
  83. .entrySet().iterator(); iterator.hasNext();) {
  84. if (iterator.next().getValue().paintable == component) {
  85. return true;
  86. }
  87. }
  88. return false;
  89. }
  90. public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
  91. for (Widget wrapper : getChildren()) {
  92. AbsoluteWrapper w = (AbsoluteWrapper) wrapper;
  93. if (w.getWidget() == oldComponent) {
  94. w.setWidget(newComponent);
  95. return;
  96. }
  97. }
  98. }
  99. public boolean requestLayout(Set<Paintable> children) {
  100. // component inside an absolute panel never affects parent nor the
  101. // layout
  102. return true;
  103. }
  104. public void updateCaption(Paintable component, UIDL uidl) {
  105. AbsoluteWrapper parent2 = (AbsoluteWrapper) ((Widget) component)
  106. .getParent();
  107. parent2.updateCaption(uidl);
  108. }
  109. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  110. rendering = true;
  111. this.client = client;
  112. // TODO margin handling
  113. if (client.updateComponent(this, uidl, true)) {
  114. rendering = false;
  115. return;
  116. }
  117. handleHandlerRegistration();
  118. HashSet<String> unrenderedPids = new HashSet<String>(
  119. pidToComponentWrappper.keySet());
  120. for (Iterator<Object> childIterator = uidl.getChildIterator(); childIterator
  121. .hasNext();) {
  122. UIDL cc = (UIDL) childIterator.next();
  123. UIDL componentUIDL = cc.getChildUIDL(0);
  124. unrenderedPids.remove(componentUIDL.getId());
  125. getWrapper(client, componentUIDL).updateFromUIDL(cc);
  126. }
  127. for (String pid : unrenderedPids) {
  128. AbsoluteWrapper absoluteWrapper = pidToComponentWrappper.get(pid);
  129. pidToComponentWrappper.remove(pid);
  130. absoluteWrapper.destroy();
  131. }
  132. rendering = false;
  133. }
  134. private HandlerRegistration clickHandlerRegistration;
  135. private void handleHandlerRegistration() {
  136. // Handle registering/unregistering of click handler depending on if
  137. // server side listeners have been added or removed.
  138. if (client.hasEventListeners(this, CLICK_EVENT_IDENTIFIER)) {
  139. if (clickHandlerRegistration == null) {
  140. clickHandlerRegistration = addDomHandler(this, ClickEvent
  141. .getType());
  142. }
  143. } else {
  144. if (clickHandlerRegistration != null) {
  145. clickHandlerRegistration.removeHandler();
  146. clickHandlerRegistration = null;
  147. }
  148. }
  149. }
  150. private AbsoluteWrapper getWrapper(ApplicationConnection client,
  151. UIDL componentUIDL) {
  152. AbsoluteWrapper wrapper = pidToComponentWrappper.get(componentUIDL
  153. .getId());
  154. if (wrapper == null) {
  155. wrapper = new AbsoluteWrapper(client.getPaintable(componentUIDL));
  156. pidToComponentWrappper.put(componentUIDL.getId(), wrapper);
  157. add(wrapper);
  158. }
  159. return wrapper;
  160. }
  161. @Override
  162. public void add(Widget child) {
  163. super.add(child, canvas);
  164. }
  165. @Override
  166. public void setStyleName(String style) {
  167. super.setStyleName(style);
  168. if (previousStyleName == null || !previousStyleName.equals(style)) {
  169. excessPixelsHorizontal = -1;
  170. excessPixelsVertical = -1;
  171. }
  172. }
  173. @Override
  174. public void setWidth(String width) {
  175. super.setWidth(width);
  176. // TODO do this so that canvas gets the sized properly (the area
  177. // inside marginals)
  178. canvas.getStyle().setProperty("width", width);
  179. if (!rendering) {
  180. if (BrowserInfo.get().isIE6()) {
  181. relayoutWrappersForIe6();
  182. }
  183. relayoutRelativeChildren();
  184. }
  185. }
  186. private void relayoutRelativeChildren() {
  187. for (Widget widget : getChildren()) {
  188. if (widget instanceof AbsoluteWrapper) {
  189. AbsoluteWrapper w = (AbsoluteWrapper) widget;
  190. client.handleComponentRelativeSize(w.getWidget());
  191. w.updateCaptionPosition();
  192. }
  193. }
  194. }
  195. @Override
  196. public void setHeight(String height) {
  197. super.setHeight(height);
  198. // TODO do this so that canvas gets the sized properly (the area
  199. // inside marginals)
  200. canvas.getStyle().setProperty("height", height);
  201. if (!rendering) {
  202. if (BrowserInfo.get().isIE6()) {
  203. relayoutWrappersForIe6();
  204. }
  205. relayoutRelativeChildren();
  206. }
  207. }
  208. private void relayoutWrappersForIe6() {
  209. for (Widget wrapper : getChildren()) {
  210. if (wrapper instanceof AbsoluteWrapper) {
  211. ((AbsoluteWrapper) wrapper).ie6Layout();
  212. }
  213. }
  214. }
  215. public class AbsoluteWrapper extends SimplePanel {
  216. private String css;
  217. private String left;
  218. private String top;
  219. private String right;
  220. private String bottom;
  221. private String zIndex;
  222. private Paintable paintable;
  223. private VCaption caption;
  224. public AbsoluteWrapper(Paintable paintable) {
  225. this.paintable = paintable;
  226. setStyleName(CLASSNAME + "-wrapper");
  227. }
  228. public void updateCaption(UIDL uidl) {
  229. boolean captionIsNeeded = VCaption.isNeeded(uidl);
  230. if (captionIsNeeded) {
  231. if (caption == null) {
  232. caption = new VCaption(paintable, client);
  233. VAbsoluteLayout.this.add(caption);
  234. }
  235. caption.updateCaption(uidl);
  236. updateCaptionPosition();
  237. } else {
  238. if (caption != null) {
  239. caption.removeFromParent();
  240. caption = null;
  241. }
  242. }
  243. }
  244. public void destroy() {
  245. if (caption != null) {
  246. caption.removeFromParent();
  247. }
  248. client.unregisterPaintable(paintable);
  249. removeFromParent();
  250. }
  251. public void updateFromUIDL(UIDL componentUIDL) {
  252. setPosition(componentUIDL.getStringAttribute("css"));
  253. if (getWidget() != paintable) {
  254. setWidget((Widget) paintable);
  255. }
  256. UIDL childUIDL = componentUIDL.getChildUIDL(0);
  257. paintable.updateFromUIDL(childUIDL, client);
  258. if (childUIDL.hasAttribute("cached")) {
  259. // child may need relative size adjustment if wrapper details
  260. // have changed this could be optimized (check if wrapper size
  261. // has changed)
  262. client.handleComponentRelativeSize((Widget) paintable);
  263. }
  264. }
  265. public void setPosition(String stringAttribute) {
  266. if (css == null || !css.equals(stringAttribute)) {
  267. css = stringAttribute;
  268. top = right = bottom = left = zIndex = null;
  269. if (!css.equals("")) {
  270. String[] properties = css.split(";");
  271. for (int i = 0; i < properties.length; i++) {
  272. String[] keyValue = properties[i].split(":");
  273. if (keyValue[0].equals("left")) {
  274. left = keyValue[1];
  275. } else if (keyValue[0].equals("top")) {
  276. top = keyValue[1];
  277. } else if (keyValue[0].equals("right")) {
  278. right = keyValue[1];
  279. } else if (keyValue[0].equals("bottom")) {
  280. bottom = keyValue[1];
  281. } else if (keyValue[0].equals("z-index")) {
  282. zIndex = keyValue[1];
  283. }
  284. }
  285. }
  286. // ensure ne values
  287. Style style = getElement().getStyle();
  288. /*
  289. * IE8 dies when nulling zIndex, even in IE7 mode. All other css
  290. * properties (and even in older IE's) accept null values just
  291. * fine. Assign empty string instead of null.
  292. */
  293. if (zIndex != null) {
  294. style.setProperty("zIndex", zIndex);
  295. } else {
  296. style.setProperty("zIndex", "");
  297. }
  298. style.setProperty("top", top);
  299. style.setProperty("left", left);
  300. style.setProperty("right", right);
  301. style.setProperty("bottom", bottom);
  302. if (BrowserInfo.get().isIE6()) {
  303. ie6Layout();
  304. }
  305. }
  306. updateCaptionPosition();
  307. }
  308. private void updateCaptionPosition() {
  309. if (caption != null) {
  310. Style style = caption.getElement().getStyle();
  311. style.setProperty("position", "absolute");
  312. style.setPropertyPx("left", getElement().getOffsetLeft());
  313. style.setPropertyPx("top", getElement().getOffsetTop()
  314. - caption.getHeight());
  315. }
  316. }
  317. private void ie6Layout() {
  318. // special handling for IE6 is needed, it does not support
  319. // setting both left/right or top/bottom
  320. Style style = getElement().getStyle();
  321. if (bottom != null && top != null) {
  322. // define height for wrapper to simulate bottom property
  323. int bottompixels = measureForIE6(bottom, true);
  324. ApplicationConnection.getConsole().log("ALB" + bottompixels);
  325. int height = canvas.getOffsetHeight() - bottompixels
  326. - getElement().getOffsetTop();
  327. ApplicationConnection.getConsole().log("ALB" + height);
  328. if (height < 0) {
  329. height = 0;
  330. }
  331. style.setPropertyPx("height", height);
  332. } else {
  333. // reset possibly existing value
  334. style.setProperty("height", "");
  335. }
  336. if (left != null && right != null) {
  337. // define width for wrapper to simulate right property
  338. int rightPixels = measureForIE6(right, false);
  339. ApplicationConnection.getConsole().log("ALR" + rightPixels);
  340. int width = canvas.getOffsetWidth() - rightPixels
  341. - getElement().getOffsetLeft();
  342. ApplicationConnection.getConsole().log("ALR" + width);
  343. if (width < 0) {
  344. width = 0;
  345. }
  346. style.setPropertyPx("width", width);
  347. } else {
  348. // reset possibly existing value
  349. style.setProperty("width", "");
  350. }
  351. }
  352. }
  353. private Element measureElement;
  354. private int measureForIE6(String cssLength, boolean vertical) {
  355. if (measureElement == null) {
  356. measureElement = DOM.createDiv();
  357. measureElement.getStyle().setProperty("position", "absolute");
  358. canvas.appendChild(measureElement);
  359. }
  360. if (vertical) {
  361. measureElement.getStyle().setProperty("height", cssLength);
  362. return measureElement.getOffsetHeight();
  363. } else {
  364. measureElement.getStyle().setProperty("width", cssLength);
  365. return measureElement.getOffsetWidth();
  366. }
  367. }
  368. public void onClick(ClickEvent event) {
  369. // This is only called if there are click listeners registered on server
  370. // side
  371. Paintable childComponent = getChildComponent((Element) event
  372. .getNativeEvent().getEventTarget().cast());
  373. final MouseEventDetails details = new MouseEventDetails(event
  374. .getNativeEvent());
  375. Object[] parameters = new Object[] { details, childComponent };
  376. client.updateVariable(client.getPid(this), CLICK_EVENT_IDENTIFIER,
  377. parameters, true);
  378. }
  379. private Paintable getChildComponent(Element target) {
  380. while (target != null && target != canvas) {
  381. Paintable paintable = client.getPaintable(target);
  382. if (paintable != null) {
  383. String pid = client.getPid(paintable);
  384. AbsoluteWrapper wrapper = pidToComponentWrappper.get(pid);
  385. if (wrapper != null) {
  386. return paintable;
  387. }
  388. }
  389. target = DOM.getParent(target);
  390. }
  391. return null;
  392. }
  393. }