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.

AbsoluteLayout.java 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  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.io.Serializable;
  18. import java.util.HashMap;
  19. import java.util.Iterator;
  20. import java.util.LinkedHashMap;
  21. import java.util.Map;
  22. import com.vaadin.event.LayoutEvents.LayoutClickEvent;
  23. import com.vaadin.event.LayoutEvents.LayoutClickListener;
  24. import com.vaadin.event.LayoutEvents.LayoutClickNotifier;
  25. import com.vaadin.server.Sizeable;
  26. import com.vaadin.shared.Connector;
  27. import com.vaadin.shared.EventId;
  28. import com.vaadin.shared.MouseEventDetails;
  29. import com.vaadin.shared.ui.absolutelayout.AbsoluteLayoutServerRpc;
  30. import com.vaadin.shared.ui.absolutelayout.AbsoluteLayoutState;
  31. /**
  32. * AbsoluteLayout is a layout implementation that mimics html absolute
  33. * positioning.
  34. *
  35. */
  36. @SuppressWarnings("serial")
  37. public class AbsoluteLayout extends AbstractLayout implements
  38. LayoutClickNotifier {
  39. private AbsoluteLayoutServerRpc rpc = new AbsoluteLayoutServerRpc() {
  40. @Override
  41. public void layoutClick(MouseEventDetails mouseDetails,
  42. Connector clickedConnector) {
  43. fireEvent(LayoutClickEvent.createEvent(AbsoluteLayout.this,
  44. mouseDetails, clickedConnector));
  45. }
  46. };
  47. // Maps each component to a position
  48. private LinkedHashMap<Component, ComponentPosition> componentToCoordinates = new LinkedHashMap<Component, ComponentPosition>();
  49. /**
  50. * Creates an AbsoluteLayout with full size.
  51. */
  52. public AbsoluteLayout() {
  53. registerRpc(rpc);
  54. setSizeFull();
  55. }
  56. @Override
  57. protected AbsoluteLayoutState getState() {
  58. return (AbsoluteLayoutState) super.getState();
  59. }
  60. /**
  61. * Gets an iterator for going through all components enclosed in the
  62. * absolute layout.
  63. */
  64. @Override
  65. public Iterator<Component> iterator() {
  66. return componentToCoordinates.keySet().iterator();
  67. }
  68. /**
  69. * Gets the number of contained components. Consistent with the iterator
  70. * returned by {@link #getComponentIterator()}.
  71. *
  72. * @return the number of contained components
  73. */
  74. @Override
  75. public int getComponentCount() {
  76. return componentToCoordinates.size();
  77. }
  78. /**
  79. * Replaces one component with another one. The new component inherits the
  80. * old components position.
  81. */
  82. @Override
  83. public void replaceComponent(Component oldComponent, Component newComponent) {
  84. ComponentPosition position = getPosition(oldComponent);
  85. removeComponent(oldComponent);
  86. addComponent(newComponent, position);
  87. }
  88. /*
  89. * (non-Javadoc)
  90. *
  91. * @see
  92. * com.vaadin.ui.AbstractComponentContainer#addComponent(com.vaadin.ui.Component
  93. * )
  94. */
  95. @Override
  96. public void addComponent(Component c) {
  97. addComponent(c, new ComponentPosition());
  98. }
  99. /**
  100. * Adds a component to the layout. The component can be positioned by
  101. * providing a string formatted in CSS-format.
  102. * <p>
  103. * For example the string "top:10px;left:10px" will position the component
  104. * 10 pixels from the left and 10 pixels from the top. The identifiers:
  105. * "top","left","right" and "bottom" can be used to specify the position.
  106. * </p>
  107. *
  108. * @param c
  109. * The component to add to the layout
  110. * @param cssPosition
  111. * The css position string
  112. */
  113. public void addComponent(Component c, String cssPosition) {
  114. ComponentPosition position = new ComponentPosition();
  115. position.setCSSString(cssPosition);
  116. addComponent(c, position);
  117. }
  118. /**
  119. * Adds the component using the given position. Ensures the position is only
  120. * set if the component is added correctly.
  121. *
  122. * @param c
  123. * The component to add
  124. * @param position
  125. * The position info for the component. Must not be null.
  126. * @throws IllegalArgumentException
  127. * If adding the component failed
  128. */
  129. private void addComponent(Component c, ComponentPosition position)
  130. throws IllegalArgumentException {
  131. /*
  132. * Create position instance and add it to componentToCoordinates map. We
  133. * need to do this before we call addComponent so the attachListeners
  134. * can access this position. #6368
  135. */
  136. internalSetPosition(c, position);
  137. try {
  138. super.addComponent(c);
  139. } catch (IllegalArgumentException e) {
  140. internalRemoveComponent(c);
  141. throw e;
  142. }
  143. markAsDirty();
  144. }
  145. /**
  146. * Removes the component from all internal data structures. Does not
  147. * actually remove the component from the layout (this is assumed to have
  148. * been done by the caller).
  149. *
  150. * @param c
  151. * The component to remove
  152. */
  153. private void internalRemoveComponent(Component c) {
  154. componentToCoordinates.remove(c);
  155. }
  156. @Override
  157. public void beforeClientResponse(boolean initial) {
  158. super.beforeClientResponse(initial);
  159. // This could be in internalRemoveComponent and internalSetComponent if
  160. // Map<Connector,String> was supported. We cannot get the child
  161. // connectorId unless the component is attached to the application so
  162. // the String->String map cannot be populated in internal* either.
  163. Map<String, String> connectorToPosition = new HashMap<String, String>();
  164. for (Iterator<Component> ci = getComponentIterator(); ci.hasNext();) {
  165. Component c = ci.next();
  166. connectorToPosition.put(c.getConnectorId(), getPosition(c)
  167. .getCSSString());
  168. }
  169. getState().connectorToCssPosition = connectorToPosition;
  170. }
  171. /*
  172. * (non-Javadoc)
  173. *
  174. * @see
  175. * com.vaadin.ui.AbstractComponentContainer#removeComponent(com.vaadin.ui
  176. * .Component)
  177. */
  178. @Override
  179. public void removeComponent(Component c) {
  180. internalRemoveComponent(c);
  181. super.removeComponent(c);
  182. markAsDirty();
  183. }
  184. /**
  185. * Gets the position of a component in the layout. Returns null if component
  186. * is not attached to the layout.
  187. * <p>
  188. * Note that you cannot update the position by updating this object. Call
  189. * {@link #setPosition(Component, ComponentPosition)} with the updated
  190. * {@link ComponentPosition} object.
  191. * </p>
  192. *
  193. * @param component
  194. * The component which position is needed
  195. * @return An instance of ComponentPosition containing the position of the
  196. * component, or null if the component is not enclosed in the
  197. * layout.
  198. */
  199. public ComponentPosition getPosition(Component component) {
  200. return componentToCoordinates.get(component);
  201. }
  202. /**
  203. * Sets the position of a component in the layout.
  204. *
  205. * @param component
  206. * @param position
  207. */
  208. public void setPosition(Component component, ComponentPosition position) {
  209. if (!componentToCoordinates.containsKey(component)) {
  210. throw new IllegalArgumentException(
  211. "Component must be a child of this layout");
  212. }
  213. internalSetPosition(component, position);
  214. }
  215. /**
  216. * Updates the position for a component. Caller must ensure component is a
  217. * child of this layout.
  218. *
  219. * @param component
  220. * The component. Must be a child for this layout. Not enforced.
  221. * @param position
  222. * New position. Must not be null.
  223. */
  224. private void internalSetPosition(Component component,
  225. ComponentPosition position) {
  226. componentToCoordinates.put(component, position);
  227. markAsDirty();
  228. }
  229. /**
  230. * The CompontPosition class represents a components position within the
  231. * absolute layout. It contains the attributes for left, right, top and
  232. * bottom and the units used to specify them.
  233. */
  234. public class ComponentPosition implements Serializable {
  235. private int zIndex = -1;
  236. private Float topValue = null;
  237. private Float rightValue = null;
  238. private Float bottomValue = null;
  239. private Float leftValue = null;
  240. private Unit topUnits = Unit.PIXELS;
  241. private Unit rightUnits = Unit.PIXELS;
  242. private Unit bottomUnits = Unit.PIXELS;
  243. private Unit leftUnits = Unit.PIXELS;
  244. /**
  245. * Sets the position attributes using CSS syntax. Attributes not
  246. * included in the string are reset to their unset states.
  247. *
  248. * <code><pre>
  249. * setCSSString("top:10px;left:20%;z-index:16;");
  250. * </pre></code>
  251. *
  252. * @param css
  253. */
  254. public void setCSSString(String css) {
  255. topValue = rightValue = bottomValue = leftValue = null;
  256. topUnits = rightUnits = bottomUnits = leftUnits = Unit.PIXELS;
  257. zIndex = -1;
  258. if (css == null) {
  259. return;
  260. }
  261. String[] cssProperties = css.split(";");
  262. for (int i = 0; i < cssProperties.length; i++) {
  263. String[] keyValuePair = cssProperties[i].split(":");
  264. String key = keyValuePair[0].trim();
  265. if (key.equals("")) {
  266. continue;
  267. }
  268. if (key.equals("z-index")) {
  269. zIndex = Integer.parseInt(keyValuePair[1].trim());
  270. } else {
  271. String value;
  272. if (keyValuePair.length > 1) {
  273. value = keyValuePair[1].trim();
  274. } else {
  275. value = "";
  276. }
  277. String symbol = value.replaceAll("[0-9\\.\\-]+", "");
  278. if (!symbol.equals("")) {
  279. value = value.substring(0, value.indexOf(symbol))
  280. .trim();
  281. }
  282. float v = Float.parseFloat(value);
  283. Unit unit = Unit.getUnitFromSymbol(symbol);
  284. if (key.equals("top")) {
  285. topValue = v;
  286. topUnits = unit;
  287. } else if (key.equals("right")) {
  288. rightValue = v;
  289. rightUnits = unit;
  290. } else if (key.equals("bottom")) {
  291. bottomValue = v;
  292. bottomUnits = unit;
  293. } else if (key.equals("left")) {
  294. leftValue = v;
  295. leftUnits = unit;
  296. }
  297. }
  298. }
  299. markAsDirty();
  300. }
  301. /**
  302. * Converts the internal values into a valid CSS string.
  303. *
  304. * @return A valid CSS string
  305. */
  306. public String getCSSString() {
  307. String s = "";
  308. if (topValue != null) {
  309. s += "top:" + topValue + topUnits.getSymbol() + ";";
  310. }
  311. if (rightValue != null) {
  312. s += "right:" + rightValue + rightUnits.getSymbol() + ";";
  313. }
  314. if (bottomValue != null) {
  315. s += "bottom:" + bottomValue + bottomUnits.getSymbol() + ";";
  316. }
  317. if (leftValue != null) {
  318. s += "left:" + leftValue + leftUnits.getSymbol() + ";";
  319. }
  320. if (zIndex >= 0) {
  321. s += "z-index:" + zIndex + ";";
  322. }
  323. return s;
  324. }
  325. /**
  326. * Sets the 'top' attribute; distance from the top of the component to
  327. * the top edge of the layout.
  328. *
  329. * @param topValue
  330. * The value of the 'top' attribute
  331. * @param topUnits
  332. * The unit of the 'top' attribute. See UNIT_SYMBOLS for a
  333. * description of the available units.
  334. */
  335. public void setTop(Float topValue, Unit topUnits) {
  336. this.topValue = topValue;
  337. this.topUnits = topUnits;
  338. markAsDirty();
  339. }
  340. /**
  341. * Sets the 'right' attribute; distance from the right of the component
  342. * to the right edge of the layout.
  343. *
  344. * @param rightValue
  345. * The value of the 'right' attribute
  346. * @param rightUnits
  347. * The unit of the 'right' attribute. See UNIT_SYMBOLS for a
  348. * description of the available units.
  349. */
  350. public void setRight(Float rightValue, Unit rightUnits) {
  351. this.rightValue = rightValue;
  352. this.rightUnits = rightUnits;
  353. markAsDirty();
  354. }
  355. /**
  356. * Sets the 'bottom' attribute; distance from the bottom of the
  357. * component to the bottom edge of the layout.
  358. *
  359. * @param bottomValue
  360. * The value of the 'bottom' attribute
  361. * @param units
  362. * The unit of the 'bottom' attribute. See UNIT_SYMBOLS for a
  363. * description of the available units.
  364. */
  365. public void setBottom(Float bottomValue, Unit bottomUnits) {
  366. this.bottomValue = bottomValue;
  367. this.bottomUnits = bottomUnits;
  368. markAsDirty();
  369. }
  370. /**
  371. * Sets the 'left' attribute; distance from the left of the component to
  372. * the left edge of the layout.
  373. *
  374. * @param leftValue
  375. * The value of the 'left' attribute
  376. * @param units
  377. * The unit of the 'left' attribute. See UNIT_SYMBOLS for a
  378. * description of the available units.
  379. */
  380. public void setLeft(Float leftValue, Unit leftUnits) {
  381. this.leftValue = leftValue;
  382. this.leftUnits = leftUnits;
  383. markAsDirty();
  384. }
  385. /**
  386. * Sets the 'z-index' attribute; the visual stacking order
  387. *
  388. * @param zIndex
  389. * The z-index for the component.
  390. */
  391. public void setZIndex(int zIndex) {
  392. this.zIndex = zIndex;
  393. markAsDirty();
  394. }
  395. /**
  396. * Sets the value of the 'top' attribute; distance from the top of the
  397. * component to the top edge of the layout.
  398. *
  399. * @param topValue
  400. * The value of the 'left' attribute
  401. */
  402. public void setTopValue(Float topValue) {
  403. this.topValue = topValue;
  404. markAsDirty();
  405. }
  406. /**
  407. * Gets the 'top' attributes value in current units.
  408. *
  409. * @see #getTopUnits()
  410. * @return The value of the 'top' attribute, null if not set
  411. */
  412. public Float getTopValue() {
  413. return topValue;
  414. }
  415. /**
  416. * Gets the 'right' attributes value in current units.
  417. *
  418. * @return The value of the 'right' attribute, null if not set
  419. * @see #getRightUnits()
  420. */
  421. public Float getRightValue() {
  422. return rightValue;
  423. }
  424. /**
  425. * Sets the 'right' attribute value (distance from the right of the
  426. * component to the right edge of the layout). Currently active units
  427. * are maintained.
  428. *
  429. * @param rightValue
  430. * The value of the 'right' attribute
  431. * @see #setRightUnits(int)
  432. */
  433. public void setRightValue(Float rightValue) {
  434. this.rightValue = rightValue;
  435. markAsDirty();
  436. }
  437. /**
  438. * Gets the 'bottom' attributes value using current units.
  439. *
  440. * @return The value of the 'bottom' attribute, null if not set
  441. * @see #getBottomUnits()
  442. */
  443. public Float getBottomValue() {
  444. return bottomValue;
  445. }
  446. /**
  447. * Sets the 'bottom' attribute value (distance from the bottom of the
  448. * component to the bottom edge of the layout). Currently active units
  449. * are maintained.
  450. *
  451. * @param bottomValue
  452. * The value of the 'bottom' attribute
  453. * @see #setBottomUnits(int)
  454. */
  455. public void setBottomValue(Float bottomValue) {
  456. this.bottomValue = bottomValue;
  457. markAsDirty();
  458. }
  459. /**
  460. * Gets the 'left' attributes value using current units.
  461. *
  462. * @return The value of the 'left' attribute, null if not set
  463. * @see #getLeftUnits()
  464. */
  465. public Float getLeftValue() {
  466. return leftValue;
  467. }
  468. /**
  469. * Sets the 'left' attribute value (distance from the left of the
  470. * component to the left edge of the layout). Currently active units are
  471. * maintained.
  472. *
  473. * @param leftValue
  474. * The value of the 'left' CSS-attribute
  475. * @see #setLeftUnits(int)
  476. */
  477. public void setLeftValue(Float leftValue) {
  478. this.leftValue = leftValue;
  479. markAsDirty();
  480. }
  481. /**
  482. * Gets the unit for the 'top' attribute
  483. *
  484. * @return See {@link Sizeable} UNIT_SYMBOLS for a description of the
  485. * available units.
  486. */
  487. public Unit getTopUnits() {
  488. return topUnits;
  489. }
  490. /**
  491. * Sets the unit for the 'top' attribute
  492. *
  493. * @param topUnits
  494. * See {@link Sizeable} UNIT_SYMBOLS for a description of the
  495. * available units.
  496. */
  497. public void setTopUnits(Unit topUnits) {
  498. this.topUnits = topUnits;
  499. markAsDirty();
  500. }
  501. /**
  502. * Gets the unit for the 'right' attribute
  503. *
  504. * @return See {@link Sizeable} UNIT_SYMBOLS for a description of the
  505. * available units.
  506. */
  507. public Unit getRightUnits() {
  508. return rightUnits;
  509. }
  510. /**
  511. * Sets the unit for the 'right' attribute
  512. *
  513. * @param rightUnits
  514. * See {@link Sizeable} UNIT_SYMBOLS for a description of the
  515. * available units.
  516. */
  517. public void setRightUnits(Unit rightUnits) {
  518. this.rightUnits = rightUnits;
  519. markAsDirty();
  520. }
  521. /**
  522. * Gets the unit for the 'bottom' attribute
  523. *
  524. * @return See {@link Sizeable} UNIT_SYMBOLS for a description of the
  525. * available units.
  526. */
  527. public Unit getBottomUnits() {
  528. return bottomUnits;
  529. }
  530. /**
  531. * Sets the unit for the 'bottom' attribute
  532. *
  533. * @param bottomUnits
  534. * See {@link Sizeable} UNIT_SYMBOLS for a description of the
  535. * available units.
  536. */
  537. public void setBottomUnits(Unit bottomUnits) {
  538. this.bottomUnits = bottomUnits;
  539. markAsDirty();
  540. }
  541. /**
  542. * Gets the unit for the 'left' attribute
  543. *
  544. * @return See {@link Sizeable} UNIT_SYMBOLS for a description of the
  545. * available units.
  546. */
  547. public Unit getLeftUnits() {
  548. return leftUnits;
  549. }
  550. /**
  551. * Sets the unit for the 'left' attribute
  552. *
  553. * @param leftUnits
  554. * See {@link Sizeable} UNIT_SYMBOLS for a description of the
  555. * available units.
  556. */
  557. public void setLeftUnits(Unit leftUnits) {
  558. this.leftUnits = leftUnits;
  559. markAsDirty();
  560. }
  561. /**
  562. * Gets the 'z-index' attribute.
  563. *
  564. * @return the zIndex The z-index attribute
  565. */
  566. public int getZIndex() {
  567. return zIndex;
  568. }
  569. /*
  570. * (non-Javadoc)
  571. *
  572. * @see java.lang.Object#toString()
  573. */
  574. @Override
  575. public String toString() {
  576. return getCSSString();
  577. }
  578. }
  579. @Override
  580. public void addLayoutClickListener(LayoutClickListener listener) {
  581. addListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER,
  582. LayoutClickEvent.class, listener,
  583. LayoutClickListener.clickMethod);
  584. }
  585. /**
  586. * @deprecated As of 7.0, replaced by
  587. * {@link #addLayoutClickListener(LayoutClickListener)}
  588. **/
  589. @Override
  590. @Deprecated
  591. public void addListener(LayoutClickListener listener) {
  592. addLayoutClickListener(listener);
  593. }
  594. @Override
  595. public void removeLayoutClickListener(LayoutClickListener listener) {
  596. removeListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER,
  597. LayoutClickEvent.class, listener);
  598. }
  599. /**
  600. * @deprecated As of 7.0, replaced by
  601. * {@link #removeLayoutClickListener(LayoutClickListener)}
  602. **/
  603. @Override
  604. @Deprecated
  605. public void removeListener(LayoutClickListener listener) {
  606. removeLayoutClickListener(listener);
  607. }
  608. }