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.

CssLayout.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. /*
  2. * Copyright 2011 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.ui;
  17. import java.util.Iterator;
  18. import java.util.LinkedList;
  19. import com.vaadin.event.LayoutEvents.LayoutClickEvent;
  20. import com.vaadin.event.LayoutEvents.LayoutClickListener;
  21. import com.vaadin.event.LayoutEvents.LayoutClickNotifier;
  22. import com.vaadin.shared.Connector;
  23. import com.vaadin.shared.EventId;
  24. import com.vaadin.shared.MouseEventDetails;
  25. import com.vaadin.shared.ui.csslayout.CssLayoutServerRpc;
  26. import com.vaadin.shared.ui.csslayout.CssLayoutState;
  27. /**
  28. * CssLayout is a layout component that can be used in browser environment only.
  29. * It simply renders components and their captions into a same div element.
  30. * Component layout can then be adjusted with css.
  31. * <p>
  32. * In comparison to {@link HorizontalLayout} and {@link VerticalLayout}
  33. * <ul>
  34. * <li>rather similar server side api
  35. * <li>no spacing, alignment or expand ratios
  36. * <li>much simpler DOM that can be styled by skilled web developer
  37. * <li>no abstraction of browser differences (developer must ensure that the
  38. * result works properly on each browser)
  39. * <li>different kind of handling for relative sizes (that are set from server
  40. * side) (*)
  41. * <li>noticeably faster rendering time in some situations as we rely more on
  42. * the browser's rendering engine.
  43. * </ul>
  44. * <p>
  45. * With {@link CustomLayout} one can often achieve similar results (good looking
  46. * layouts with web technologies), but with CustomLayout developer needs to work
  47. * with fixed templates.
  48. * <p>
  49. * By extending CssLayout one can also inject some css rules straight to child
  50. * components using {@link #getCss(Component)}.
  51. *
  52. * <p>
  53. * (*) Relative sizes (set from server side) are treated bit differently than in
  54. * other layouts in Vaadin. In cssLayout the size is calculated relatively to
  55. * CSS layouts content area which is pretty much as in html and css. In other
  56. * layouts the size of component is calculated relatively to the "slot" given by
  57. * layout.
  58. * <p>
  59. * Also note that client side framework in Vaadin modifies inline style
  60. * properties width and height. This happens on each update to component. If one
  61. * wants to set component sizes with CSS, component must have undefined size on
  62. * server side (which is not the default for all components) and the size must
  63. * be defined with class styles - not by directly injecting width and height.
  64. *
  65. * @since 6.1 brought in from "FastLayouts" incubator project
  66. *
  67. */
  68. public class CssLayout extends AbstractLayout implements LayoutClickNotifier {
  69. private CssLayoutServerRpc rpc = new CssLayoutServerRpc() {
  70. @Override
  71. public void layoutClick(MouseEventDetails mouseDetails,
  72. Connector clickedConnector) {
  73. fireEvent(LayoutClickEvent.createEvent(CssLayout.this,
  74. mouseDetails, clickedConnector));
  75. }
  76. };
  77. /**
  78. * Custom layout slots containing the components.
  79. */
  80. protected LinkedList<Component> components = new LinkedList<Component>();
  81. /**
  82. * Constructs an empty CssLayout.
  83. */
  84. public CssLayout() {
  85. registerRpc(rpc);
  86. }
  87. /**
  88. * Constructs a CssLayout with the given components in the given order.
  89. *
  90. * @see #addComponents(Component...)
  91. *
  92. * @param children
  93. * Components to add to the container.
  94. */
  95. public CssLayout(Component... children) {
  96. this();
  97. addComponents(children);
  98. }
  99. /**
  100. * Add a component into this container. The component is added to the right
  101. * or below the previous component.
  102. *
  103. * @param c
  104. * the component to be added.
  105. */
  106. @Override
  107. public void addComponent(Component c) {
  108. // Add to components before calling super.addComponent
  109. // so that it is available to AttachListeners
  110. components.add(c);
  111. try {
  112. super.addComponent(c);
  113. markAsDirty();
  114. } catch (IllegalArgumentException e) {
  115. components.remove(c);
  116. throw e;
  117. }
  118. }
  119. /**
  120. * Adds a component into this container. The component is added to the left
  121. * or on top of the other components.
  122. *
  123. * @param c
  124. * the component to be added.
  125. */
  126. public void addComponentAsFirst(Component c) {
  127. // If c is already in this, we must remove it before proceeding
  128. // see ticket #7668
  129. if (c.getParent() == this) {
  130. removeComponent(c);
  131. }
  132. components.addFirst(c);
  133. try {
  134. super.addComponent(c);
  135. markAsDirty();
  136. } catch (IllegalArgumentException e) {
  137. components.remove(c);
  138. throw e;
  139. }
  140. }
  141. /**
  142. * Adds a component into indexed position in this container.
  143. *
  144. * @param c
  145. * the component to be added.
  146. * @param index
  147. * the index of the component position. The components currently
  148. * in and after the position are shifted forwards.
  149. */
  150. public void addComponent(Component c, int index) {
  151. // If c is already in this, we must remove it before proceeding
  152. // see ticket #7668
  153. if (c.getParent() == this) {
  154. // When c is removed, all components after it are shifted down
  155. if (index > getComponentIndex(c)) {
  156. index--;
  157. }
  158. removeComponent(c);
  159. }
  160. components.add(index, c);
  161. try {
  162. super.addComponent(c);
  163. markAsDirty();
  164. } catch (IllegalArgumentException e) {
  165. components.remove(c);
  166. throw e;
  167. }
  168. }
  169. /**
  170. * Removes the component from this container.
  171. *
  172. * @param c
  173. * the component to be removed.
  174. */
  175. @Override
  176. public void removeComponent(Component c) {
  177. components.remove(c);
  178. super.removeComponent(c);
  179. markAsDirty();
  180. }
  181. /**
  182. * Gets the component container iterator for going trough all the components
  183. * in the container.
  184. *
  185. * @return the Iterator of the components inside the container.
  186. */
  187. @Override
  188. public Iterator<Component> iterator() {
  189. return components.iterator();
  190. }
  191. /**
  192. * Gets the number of contained components. Consistent with the iterator
  193. * returned by {@link #getComponentIterator()}.
  194. *
  195. * @return the number of contained components
  196. */
  197. @Override
  198. public int getComponentCount() {
  199. return components.size();
  200. }
  201. @Override
  202. public void beforeClientResponse(boolean initial) {
  203. super.beforeClientResponse(initial);
  204. // This is an obsolete hack that was required before Map<Conenctor, ?>
  205. // was supported. The workaround is to instead use a Map<String, ?> with
  206. // the connector id as the key, but that can only be used once the
  207. // connector has been attached.
  208. getState().childCss.clear();
  209. for (Iterator<Component> ci = getComponentIterator(); ci.hasNext();) {
  210. Component child = ci.next();
  211. String componentCssString = getCss(child);
  212. if (componentCssString != null) {
  213. getState().childCss.put(child, componentCssString);
  214. }
  215. }
  216. }
  217. @Override
  218. protected CssLayoutState getState() {
  219. return (CssLayoutState) super.getState();
  220. }
  221. /**
  222. * Returns styles to be applied to given component. Override this method to
  223. * inject custom style rules to components.
  224. *
  225. * <p>
  226. * Note that styles are injected over previous styles before actual child
  227. * rendering. Previous styles are not cleared, but overridden.
  228. *
  229. * <p>
  230. * Note that one most often achieves better code style, by separating
  231. * styling to theme (with custom theme and {@link #addStyleName(String)}.
  232. * With own custom styles it is also very easy to break browser
  233. * compatibility.
  234. *
  235. * @param c
  236. * the component
  237. * @return css rules to be applied to component
  238. */
  239. protected String getCss(Component c) {
  240. return null;
  241. }
  242. /* Documented in superclass */
  243. @Override
  244. public void replaceComponent(Component oldComponent, Component newComponent) {
  245. // Gets the locations
  246. int oldLocation = -1;
  247. int newLocation = -1;
  248. int location = 0;
  249. for (final Iterator<Component> i = components.iterator(); i.hasNext();) {
  250. final Component component = i.next();
  251. if (component == oldComponent) {
  252. oldLocation = location;
  253. }
  254. if (component == newComponent) {
  255. newLocation = location;
  256. }
  257. location++;
  258. }
  259. if (oldLocation == -1) {
  260. addComponent(newComponent);
  261. } else if (newLocation == -1) {
  262. removeComponent(oldComponent);
  263. addComponent(newComponent, oldLocation);
  264. } else {
  265. if (oldLocation > newLocation) {
  266. components.remove(oldComponent);
  267. components.add(newLocation, oldComponent);
  268. components.remove(newComponent);
  269. components.add(oldLocation, newComponent);
  270. } else {
  271. components.remove(newComponent);
  272. components.add(oldLocation, newComponent);
  273. components.remove(oldComponent);
  274. components.add(newLocation, oldComponent);
  275. }
  276. markAsDirty();
  277. }
  278. }
  279. @Override
  280. public void addLayoutClickListener(LayoutClickListener listener) {
  281. addListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER,
  282. LayoutClickEvent.class, listener,
  283. LayoutClickListener.clickMethod);
  284. }
  285. /**
  286. * @deprecated As of 7.0, replaced by
  287. * {@link #addLayoutClickListener(LayoutClickListener)}
  288. **/
  289. @Override
  290. @Deprecated
  291. public void addListener(LayoutClickListener listener) {
  292. addLayoutClickListener(listener);
  293. }
  294. @Override
  295. public void removeLayoutClickListener(LayoutClickListener listener) {
  296. removeListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER,
  297. LayoutClickEvent.class, listener);
  298. }
  299. /**
  300. * @deprecated As of 7.0, replaced by
  301. * {@link #removeLayoutClickListener(LayoutClickListener)}
  302. **/
  303. @Override
  304. @Deprecated
  305. public void removeListener(LayoutClickListener listener) {
  306. removeLayoutClickListener(listener);
  307. }
  308. /**
  309. * Returns the index of the given component.
  310. *
  311. * @param component
  312. * The component to look up.
  313. * @return The index of the component or -1 if the component is not a child.
  314. */
  315. public int getComponentIndex(Component component) {
  316. return components.indexOf(component);
  317. }
  318. /**
  319. * Returns the component at the given position.
  320. *
  321. * @param index
  322. * The position of the component.
  323. * @return The component at the given index.
  324. * @throws IndexOutOfBoundsException
  325. * If the index is out of range.
  326. */
  327. public Component getComponent(int index) throws IndexOutOfBoundsException {
  328. return components.get(index);
  329. }
  330. }