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 25KB

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