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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. /*
  2. * Copyright 2000-2018 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.Collections;
  18. import java.util.Iterator;
  19. import java.util.LinkedList;
  20. import org.jsoup.nodes.Element;
  21. import com.vaadin.event.LayoutEvents.LayoutClickEvent;
  22. import com.vaadin.event.LayoutEvents.LayoutClickListener;
  23. import com.vaadin.event.LayoutEvents.LayoutClickNotifier;
  24. import com.vaadin.shared.Connector;
  25. import com.vaadin.shared.EventId;
  26. import com.vaadin.shared.MouseEventDetails;
  27. import com.vaadin.shared.Registration;
  28. import com.vaadin.shared.ui.csslayout.CssLayoutServerRpc;
  29. import com.vaadin.shared.ui.csslayout.CssLayoutState;
  30. import com.vaadin.ui.declarative.DesignContext;
  31. /**
  32. * CssLayout is a layout component that can be used in browser environment only.
  33. * It simply renders components and their captions into a same div element.
  34. * Component layout can then be adjusted with css.
  35. * <p>
  36. * In comparison to {@link HorizontalLayout} and {@link VerticalLayout}
  37. * <ul>
  38. * <li>rather similar server side api
  39. * <li>no spacing, alignment or expand ratios
  40. * <li>much simpler DOM that can be styled by skilled web developer
  41. * <li>no abstraction of browser differences (developer must ensure that the
  42. * result works properly on each browser)
  43. * <li>different kind of handling for relative sizes (that are set from server
  44. * side) (*)
  45. * <li>noticeably faster rendering time in some situations as we rely more on
  46. * the browser's rendering engine.
  47. * </ul>
  48. * <p>
  49. * With {@link CustomLayout} one can often achieve similar results (good looking
  50. * layouts with web technologies), but with CustomLayout developer needs to work
  51. * with fixed templates.
  52. * <p>
  53. * By extending CssLayout one can also inject some css rules straight to child
  54. * components using {@link #getCss(Component)}.
  55. *
  56. * <p>
  57. * (*) Relative sizes (set from server side) are treated bit differently than in
  58. * other layouts in Vaadin. In cssLayout the size is calculated relatively to
  59. * CSS layouts content area which is pretty much as in html and css. In other
  60. * layouts the size of component is calculated relatively to the "slot" given by
  61. * layout.
  62. * <p>
  63. * Also note that client side framework in Vaadin modifies inline style
  64. * properties width and height. This happens on each update to component. If one
  65. * wants to set component sizes with CSS, component must have undefined size on
  66. * server side (which is not the default for all components) and the size must
  67. * be defined with class styles - not by directly injecting width and height.
  68. *
  69. * @since 6.1 brought in from "FastLayouts" incubator project
  70. *
  71. */
  72. public class CssLayout extends AbstractLayout implements LayoutClickNotifier {
  73. private CssLayoutServerRpc rpc = (MouseEventDetails mouseDetails,
  74. Connector clickedConnector) -> fireEvent(
  75. LayoutClickEvent.createEvent(CssLayout.this, mouseDetails,
  76. clickedConnector));
  77. /**
  78. * Custom layout slots containing the components.
  79. */
  80. protected LinkedList<Component> components = new LinkedList<>();
  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. } catch (IllegalArgumentException e) {
  114. components.remove(c);
  115. throw e;
  116. }
  117. }
  118. /**
  119. * Adds a component into this container. The component is added to the left
  120. * or on top of the other components.
  121. *
  122. * @param c
  123. * the component to be added.
  124. */
  125. public void addComponentAsFirst(Component c) {
  126. // If c is already in this, we must remove it before proceeding
  127. // see ticket #7668
  128. if (equals(c.getParent())) {
  129. removeComponent(c);
  130. }
  131. components.addFirst(c);
  132. try {
  133. super.addComponent(c);
  134. } catch (IllegalArgumentException e) {
  135. components.remove(c);
  136. throw e;
  137. }
  138. }
  139. /**
  140. * Adds a component into indexed position in this container.
  141. *
  142. * @param c
  143. * the component to be added.
  144. * @param index
  145. * the index of the component position. The components currently
  146. * in and after the position are shifted forwards.
  147. */
  148. public void addComponent(Component c, int index) {
  149. // If c is already in this, we must remove it before proceeding
  150. // see ticket #7668
  151. if (equals(c.getParent())) {
  152. // When c is removed, all components after it are shifted down
  153. if (index > getComponentIndex(c)) {
  154. index--;
  155. }
  156. removeComponent(c);
  157. }
  158. components.add(index, c);
  159. try {
  160. super.addComponent(c);
  161. } catch (IllegalArgumentException e) {
  162. components.remove(c);
  163. throw e;
  164. }
  165. }
  166. /**
  167. * Removes the component from this container.
  168. *
  169. * @param c
  170. * the component to be removed.
  171. */
  172. @Override
  173. public void removeComponent(Component c) {
  174. components.remove(c);
  175. super.removeComponent(c);
  176. }
  177. /**
  178. * Gets the component container iterator for going trough all the components
  179. * in the container.
  180. *
  181. * @return the Iterator of the components inside the container.
  182. */
  183. @Override
  184. public Iterator<Component> iterator() {
  185. return Collections.unmodifiableCollection(components).iterator();
  186. }
  187. /**
  188. * Gets the number of contained components. Consistent with the iterator
  189. * returned by {@link #getComponentIterator()}.
  190. *
  191. * @return the number of contained components
  192. */
  193. @Override
  194. public int getComponentCount() {
  195. return components.size();
  196. }
  197. @Override
  198. public void beforeClientResponse(boolean initial) {
  199. super.beforeClientResponse(initial);
  200. // This is an obsolete hack that was required before Map<Conenctor, ?>
  201. // was supported. The workaround is to instead use a Map<String, ?> with
  202. // the connector id as the key, but that can only be used once the
  203. // connector has been attached.
  204. getState().childCss.clear();
  205. for (Iterator<Component> ci = getComponentIterator(); ci.hasNext();) {
  206. Component child = ci.next();
  207. String componentCssString = getCss(child);
  208. if (componentCssString != null) {
  209. getState().childCss.put(child, componentCssString);
  210. }
  211. }
  212. }
  213. @Override
  214. protected CssLayoutState getState() {
  215. return (CssLayoutState) super.getState();
  216. }
  217. @Override
  218. protected CssLayoutState getState(boolean markAsDirty) {
  219. return (CssLayoutState) super.getState(markAsDirty);
  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,
  245. Component newComponent) {
  246. // Gets the locations
  247. int oldLocation = -1;
  248. int newLocation = -1;
  249. int location = 0;
  250. for (final Component component : components) {
  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 Registration addLayoutClickListener(LayoutClickListener listener) {
  281. return addListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER,
  282. LayoutClickEvent.class, listener,
  283. LayoutClickListener.clickMethod);
  284. }
  285. @Override
  286. @Deprecated
  287. public void removeLayoutClickListener(LayoutClickListener listener) {
  288. removeListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER,
  289. LayoutClickEvent.class, listener);
  290. }
  291. /**
  292. * Returns the index of the given component.
  293. *
  294. * @param component
  295. * The component to look up.
  296. * @return The index of the component or -1 if the component is not a child.
  297. */
  298. public int getComponentIndex(Component component) {
  299. return components.indexOf(component);
  300. }
  301. /**
  302. * Returns the component at the given position.
  303. *
  304. * @param index
  305. * The position of the component.
  306. * @return The component at the given index.
  307. * @throws IndexOutOfBoundsException
  308. * If the index is out of range.
  309. */
  310. public Component getComponent(int index) throws IndexOutOfBoundsException {
  311. return components.get(index);
  312. }
  313. /*
  314. * (non-Javadoc)
  315. *
  316. * @see com.vaadin.ui.AbstractComponent#readDesign(org.jsoup.nodes .Element,
  317. * com.vaadin.ui.declarative.DesignContext)
  318. */
  319. @Override
  320. public void readDesign(Element design, DesignContext designContext) {
  321. // process default attributes
  322. super.readDesign(design, designContext);
  323. // handle children
  324. for (Element childComponent : design.children()) {
  325. Component newChild = designContext.readDesign(childComponent);
  326. addComponent(newChild);
  327. }
  328. }
  329. /*
  330. * (non-Javadoc)
  331. *
  332. * @see com.vaadin.ui.AbstractComponent#writeDesign(org.jsoup.nodes.Element
  333. * , com.vaadin.ui.declarative.DesignContext)
  334. */
  335. @Override
  336. public void writeDesign(Element design, DesignContext designContext) {
  337. // write default attributes
  338. super.writeDesign(design, designContext);
  339. CssLayout def = designContext.getDefaultInstance(this);
  340. // handle children
  341. if (!designContext.shouldWriteChildren(this, def)) {
  342. return;
  343. }
  344. Element designElement = design;
  345. for (Component child : this) {
  346. Element childNode = designContext.createElement(child);
  347. designElement.appendChild(childNode);
  348. }
  349. }
  350. }