您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

AbsoluteLayout.java 25KB

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