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

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