Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

AbstractSplitPanel.java 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835
  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.lang.reflect.Method;
  19. import java.util.Collection;
  20. import java.util.Iterator;
  21. import org.jsoup.nodes.Element;
  22. import com.vaadin.event.ConnectorEventListener;
  23. import com.vaadin.event.HasUserOriginated;
  24. import com.vaadin.event.MouseEvents.ClickEvent;
  25. import com.vaadin.server.SizeWithUnit;
  26. import com.vaadin.server.Sizeable;
  27. import com.vaadin.shared.EventId;
  28. import com.vaadin.shared.MouseEventDetails;
  29. import com.vaadin.shared.Registration;
  30. import com.vaadin.shared.ui.splitpanel.AbstractSplitPanelRpc;
  31. import com.vaadin.shared.ui.splitpanel.AbstractSplitPanelState;
  32. import com.vaadin.shared.ui.splitpanel.AbstractSplitPanelState.SplitterState;
  33. import com.vaadin.ui.declarative.DesignAttributeHandler;
  34. import com.vaadin.ui.declarative.DesignContext;
  35. import com.vaadin.ui.declarative.DesignException;
  36. import com.vaadin.util.ReflectTools;
  37. /**
  38. * AbstractSplitPanel.
  39. *
  40. * <code>AbstractSplitPanel</code> is base class for a component container that
  41. * can contain two components. The components are split by a divider element.
  42. *
  43. * @author Vaadin Ltd.
  44. * @since 6.5
  45. */
  46. public abstract class AbstractSplitPanel extends AbstractComponentContainer {
  47. // TODO use Unit in AbstractSplitPanelState and remove these
  48. private Unit posUnit;
  49. private Unit posMinUnit;
  50. private Unit posMaxUnit;
  51. private final AbstractSplitPanelRpc rpc = new AbstractSplitPanelRpc() {
  52. @Override
  53. public void splitterClick(MouseEventDetails mouseDetails) {
  54. fireEvent(new SplitterClickEvent(AbstractSplitPanel.this,
  55. mouseDetails));
  56. }
  57. @Override
  58. public void setSplitterPosition(float position) {
  59. float oldPosition = getSplitPosition();
  60. getSplitterState().position = position;
  61. fireEvent(new SplitPositionChangeEvent(AbstractSplitPanel.this,
  62. true, oldPosition, getSplitPositionUnit(), position,
  63. getSplitPositionUnit()));
  64. }
  65. };
  66. public AbstractSplitPanel() {
  67. registerRpc(rpc);
  68. setSplitPosition(50, Unit.PERCENTAGE, false);
  69. setSplitPositionLimits(0, Unit.PERCENTAGE, 100, Unit.PERCENTAGE);
  70. }
  71. /**
  72. * Modifiable and Serializable Iterator for the components, used by
  73. * {@link AbstractSplitPanel#getComponentIterator()}.
  74. */
  75. private class ComponentIterator
  76. implements Iterator<Component>, Serializable {
  77. int i = 0;
  78. @Override
  79. public boolean hasNext() {
  80. if (i < getComponentCount()) {
  81. return true;
  82. }
  83. return false;
  84. }
  85. @Override
  86. public Component next() {
  87. if (!hasNext()) {
  88. return null;
  89. }
  90. i++;
  91. if (i == 1) {
  92. return (getFirstComponent() == null ? getSecondComponent()
  93. : getFirstComponent());
  94. } else if (i == 2) {
  95. return getSecondComponent();
  96. }
  97. return null;
  98. }
  99. @Override
  100. public void remove() {
  101. if (i == 1) {
  102. if (getFirstComponent() != null) {
  103. setFirstComponent(null);
  104. i = 0;
  105. } else {
  106. setSecondComponent(null);
  107. }
  108. } else if (i == 2) {
  109. setSecondComponent(null);
  110. }
  111. }
  112. }
  113. /**
  114. * Add a component into this container. The component is added to the right
  115. * or under the previous component.
  116. *
  117. * @param c
  118. * the component to be added.
  119. */
  120. @Override
  121. public void addComponent(Component c) {
  122. if (getFirstComponent() == null) {
  123. setFirstComponent(c);
  124. } else if (getSecondComponent() == null) {
  125. setSecondComponent(c);
  126. } else {
  127. throw new UnsupportedOperationException(
  128. "Split panel can contain only two components");
  129. }
  130. }
  131. /**
  132. * Sets the first component of this split panel. Depending on the direction
  133. * the first component is shown at the top or to the left.
  134. *
  135. * @param c
  136. * The component to use as first component
  137. */
  138. public void setFirstComponent(Component c) {
  139. if (getFirstComponent() == c) {
  140. // Nothing to do
  141. return;
  142. }
  143. if (getFirstComponent() != null) {
  144. // detach old
  145. removeComponent(getFirstComponent());
  146. }
  147. getState().firstChild = c;
  148. if (c != null) {
  149. super.addComponent(c);
  150. }
  151. }
  152. /**
  153. * Sets the second component of this split panel. Depending on the direction
  154. * the second component is shown at the bottom or to the right.
  155. *
  156. * @param c
  157. * The component to use as second component
  158. */
  159. public void setSecondComponent(Component c) {
  160. if (getSecondComponent() == c) {
  161. // Nothing to do
  162. return;
  163. }
  164. if (getSecondComponent() != null) {
  165. // detach old
  166. removeComponent(getSecondComponent());
  167. }
  168. getState().secondChild = c;
  169. if (c != null) {
  170. super.addComponent(c);
  171. }
  172. }
  173. /**
  174. * Gets the first component of this split panel. Depending on the direction
  175. * this is either the component shown at the top or to the left.
  176. *
  177. * @return the first component of this split panel
  178. */
  179. public Component getFirstComponent() {
  180. return (Component) getState(false).firstChild;
  181. }
  182. /**
  183. * Gets the second component of this split panel. Depending on the direction
  184. * this is either the component shown at the top or to the left.
  185. *
  186. * @return the second component of this split panel
  187. */
  188. public Component getSecondComponent() {
  189. return (Component) getState(false).secondChild;
  190. }
  191. /**
  192. * Removes the component from this container.
  193. *
  194. * @param c
  195. * the component to be removed.
  196. */
  197. @Override
  198. public void removeComponent(Component c) {
  199. super.removeComponent(c);
  200. if (c == getFirstComponent()) {
  201. getState().firstChild = null;
  202. } else if (c == getSecondComponent()) {
  203. getState().secondChild = null;
  204. }
  205. }
  206. /**
  207. * Gets an iterator to the collection of contained components. Using this
  208. * iterator it is possible to step through all components contained in this
  209. * container and remove components from it.
  210. *
  211. * @return the component iterator.
  212. */
  213. @Override
  214. public Iterator<Component> iterator() {
  215. return new ComponentIterator();
  216. }
  217. /**
  218. * Gets the number of contained components. Consistent with the iterator
  219. * returned by {@link #getComponentIterator()}.
  220. *
  221. * @return the number of contained components (zero, one or two)
  222. */
  223. @Override
  224. public int getComponentCount() {
  225. int count = 0;
  226. if (getFirstComponent() != null) {
  227. count++;
  228. }
  229. if (getSecondComponent() != null) {
  230. count++;
  231. }
  232. return count;
  233. }
  234. /* Documented in superclass */
  235. @Override
  236. public void replaceComponent(Component oldComponent,
  237. Component newComponent) {
  238. if (oldComponent == getFirstComponent()) {
  239. setFirstComponent(newComponent);
  240. } else if (oldComponent == getSecondComponent()) {
  241. setSecondComponent(newComponent);
  242. }
  243. }
  244. /**
  245. * Moves the position of the splitter.
  246. *
  247. * @param pos
  248. * the new size of the first region in the unit that was last
  249. * used (default is percentage). Fractions are only allowed when
  250. * unit is percentage.
  251. */
  252. public void setSplitPosition(float pos) {
  253. setSplitPosition(pos, posUnit, false);
  254. }
  255. /**
  256. * Moves the position of the splitter.
  257. *
  258. * @param pos
  259. * the new size of the region in the unit that was last used
  260. * (default is percentage). Fractions are only allowed when unit
  261. * is percentage.
  262. *
  263. * @param reverse
  264. * if set to true the split splitter position is measured by the
  265. * second region else it is measured by the first region
  266. */
  267. public void setSplitPosition(float pos, boolean reverse) {
  268. setSplitPosition(pos, posUnit, reverse);
  269. }
  270. /**
  271. * Moves the position of the splitter with given position and unit.
  272. *
  273. * @param pos
  274. * the new size of the first region. Fractions are only allowed
  275. * when unit is percentage.
  276. * @param unit
  277. * the unit (from {@link Sizeable}) in which the size is given.
  278. */
  279. public void setSplitPosition(float pos, Unit unit) {
  280. setSplitPosition(pos, unit, false);
  281. }
  282. /**
  283. * Moves the position of the splitter with given position and unit.
  284. *
  285. * @param pos
  286. * the new size of the first region. Fractions are only allowed
  287. * when unit is percentage.
  288. * @param unit
  289. * the unit (from {@link Sizeable}) in which the size is given.
  290. * @param reverse
  291. * if set to true the split splitter position is measured by the
  292. * second region else it is measured by the first region
  293. *
  294. */
  295. public void setSplitPosition(float pos, Unit unit, boolean reverse) {
  296. if (unit != Unit.PERCENTAGE && unit != Unit.PIXELS) {
  297. throw new IllegalArgumentException(
  298. "Only percentage and pixel units are allowed");
  299. }
  300. if (unit != Unit.PERCENTAGE) {
  301. pos = Math.round(pos);
  302. }
  303. float oldPosition = getSplitPosition();
  304. Unit oldUnit = getSplitPositionUnit();
  305. SplitterState splitterState = getSplitterState();
  306. splitterState.position = pos;
  307. splitterState.positionUnit = unit.getSymbol();
  308. splitterState.positionReversed = reverse;
  309. posUnit = unit;
  310. fireEvent(new SplitPositionChangeEvent(AbstractSplitPanel.this, false,
  311. oldPosition, oldUnit, pos, posUnit));
  312. }
  313. /**
  314. * Returns the current position of the splitter, in
  315. * {@link #getSplitPositionUnit()} units.
  316. *
  317. * @return position of the splitter
  318. */
  319. public float getSplitPosition() {
  320. return getSplitterState(false).position;
  321. }
  322. /**
  323. * Returns the unit of position of the splitter
  324. *
  325. * @return unit of position of the splitter
  326. * @see #setSplitPosition(float, Unit)
  327. */
  328. public Unit getSplitPositionUnit() {
  329. return posUnit;
  330. }
  331. /**
  332. * Is the split position reversed. By default the split position is measured
  333. * by the first region, but if split position is reversed the measuring is
  334. * done by the second region instead.
  335. *
  336. * @since 7.3.6
  337. * @return {@code true} if reversed, {@code false} otherwise.
  338. * @see #setSplitPosition(float, boolean)
  339. */
  340. public boolean isSplitPositionReversed() {
  341. return getSplitterState(false).positionReversed;
  342. }
  343. /**
  344. * Sets the minimum split position to the given position and unit. If the
  345. * split position is reversed, maximum and minimum are also reversed.
  346. *
  347. * @param pos
  348. * the minimum position of the split
  349. * @param unit
  350. * the unit (from {@link Sizeable}) in which the size is given.
  351. * Allowed units are UNITS_PERCENTAGE and UNITS_PIXELS
  352. */
  353. public void setMinSplitPosition(float pos, Unit unit) {
  354. setSplitPositionLimits(pos, unit, getSplitterState(false).maxPosition,
  355. posMaxUnit);
  356. }
  357. /**
  358. * Returns the current minimum position of the splitter, in
  359. * {@link #getMinSplitPositionUnit()} units.
  360. *
  361. * @return the minimum position of the splitter
  362. */
  363. public float getMinSplitPosition() {
  364. return getSplitterState(false).minPosition;
  365. }
  366. /**
  367. * Returns the unit of the minimum position of the splitter.
  368. *
  369. * @return the unit of the minimum position of the splitter
  370. */
  371. public Unit getMinSplitPositionUnit() {
  372. return posMinUnit;
  373. }
  374. /**
  375. * Sets the maximum split position to the given position and unit. If the
  376. * split position is reversed, maximum and minimum are also reversed.
  377. *
  378. * @param pos
  379. * the maximum position of the split
  380. * @param unit
  381. * the unit (from {@link Sizeable}) in which the size is given.
  382. * Allowed units are UNITS_PERCENTAGE and UNITS_PIXELS
  383. */
  384. public void setMaxSplitPosition(float pos, Unit unit) {
  385. setSplitPositionLimits(getSplitterState(false).minPosition, posMinUnit,
  386. pos, unit);
  387. }
  388. /**
  389. * Returns the current maximum position of the splitter, in
  390. * {@link #getMaxSplitPositionUnit()} units.
  391. *
  392. * @return the maximum position of the splitter
  393. */
  394. public float getMaxSplitPosition() {
  395. return getSplitterState(false).maxPosition;
  396. }
  397. /**
  398. * Returns the unit of the maximum position of the splitter
  399. *
  400. * @return the unit of the maximum position of the splitter
  401. */
  402. public Unit getMaxSplitPositionUnit() {
  403. return posMaxUnit;
  404. }
  405. /**
  406. * Sets the maximum and minimum position of the splitter. If the split
  407. * position is reversed, maximum and minimum are also reversed.
  408. *
  409. * @param minPos
  410. * the new minimum position
  411. * @param minPosUnit
  412. * the unit (from {@link Sizeable}) in which the minimum position
  413. * is given.
  414. * @param maxPos
  415. * the new maximum position
  416. * @param maxPosUnit
  417. * the unit (from {@link Sizeable}) in which the maximum position
  418. * is given.
  419. */
  420. private void setSplitPositionLimits(float minPos, Unit minPosUnit,
  421. float maxPos, Unit maxPosUnit) {
  422. if ((minPosUnit != Unit.PERCENTAGE && minPosUnit != Unit.PIXELS)
  423. || (maxPosUnit != Unit.PERCENTAGE
  424. && maxPosUnit != Unit.PIXELS)) {
  425. throw new IllegalArgumentException(
  426. "Only percentage and pixel units are allowed");
  427. }
  428. SplitterState state = getSplitterState();
  429. state.minPosition = minPos;
  430. state.minPositionUnit = minPosUnit.getSymbol();
  431. posMinUnit = minPosUnit;
  432. state.maxPosition = maxPos;
  433. state.maxPositionUnit = maxPosUnit.getSymbol();
  434. posMaxUnit = maxPosUnit;
  435. }
  436. /**
  437. * Lock the SplitPanels position, disabling the user from dragging the split
  438. * handle.
  439. *
  440. * @param locked
  441. * Set <code>true</code> if locked, <code>false</code> otherwise.
  442. */
  443. public void setLocked(boolean locked) {
  444. getSplitterState().locked = locked;
  445. }
  446. /**
  447. * Is the SplitPanel handle locked (user not allowed to change split
  448. * position by dragging).
  449. *
  450. * @return <code>true</code> if locked, <code>false</code> otherwise.
  451. */
  452. public boolean isLocked() {
  453. return getSplitterState(false).locked;
  454. }
  455. /**
  456. * <code>SplitterClickListener</code> interface for listening for
  457. * <code>SplitterClickEvent</code> fired by a <code>SplitPanel</code>.
  458. *
  459. * @see SplitterClickEvent
  460. * @since 6.2
  461. */
  462. @FunctionalInterface
  463. public interface SplitterClickListener extends ConnectorEventListener {
  464. public static final Method clickMethod = ReflectTools.findMethod(
  465. SplitterClickListener.class, "splitterClick",
  466. SplitterClickEvent.class);
  467. /**
  468. * SplitPanel splitter has been clicked
  469. *
  470. * @param event
  471. * SplitterClickEvent event.
  472. */
  473. public void splitterClick(SplitterClickEvent event);
  474. }
  475. public static class SplitterClickEvent extends ClickEvent {
  476. public SplitterClickEvent(Component source,
  477. MouseEventDetails mouseEventDetails) {
  478. super(source, mouseEventDetails);
  479. }
  480. }
  481. /**
  482. * Interface for listening for {@link SplitPositionChangeEvent}s fired by a
  483. * SplitPanel.
  484. *
  485. * @since 7.5.0
  486. */
  487. @FunctionalInterface
  488. public interface SplitPositionChangeListener
  489. extends ConnectorEventListener {
  490. public static final Method moveMethod = ReflectTools.findMethod(
  491. SplitPositionChangeListener.class, "onSplitPositionChanged",
  492. SplitPositionChangeEvent.class);
  493. /**
  494. * SplitPanel splitter position has been changed.
  495. *
  496. * @param event
  497. * SplitPositionChangeEvent event.
  498. */
  499. public void onSplitPositionChanged(SplitPositionChangeEvent event);
  500. }
  501. /**
  502. * Event that indicates a change in SplitPanel's splitter position.
  503. *
  504. * @since 7.5.0
  505. */
  506. public static class SplitPositionChangeEvent extends Component.Event
  507. implements HasUserOriginated {
  508. private final float oldPosition;
  509. private final Unit oldUnit;
  510. private final float position;
  511. private final Unit unit;
  512. private final boolean userOriginated;
  513. /**
  514. * Creates a split position change event.
  515. *
  516. * @param source
  517. * split panel from which the event originates
  518. * @param userOriginated
  519. * true if the event is directly based on user actions
  520. * @param oldPosition
  521. * old split position
  522. * @param oldUnit
  523. * old unit of split position
  524. * @param position
  525. * new split position
  526. * @param unit
  527. * new split position unit
  528. * @since 8.1
  529. */
  530. public SplitPositionChangeEvent(final Component source,
  531. final boolean userOriginated, final float oldPosition,
  532. final Unit oldUnit, final float position, final Unit unit) {
  533. super(source);
  534. this.userOriginated = userOriginated;
  535. this.oldUnit = oldUnit;
  536. this.oldPosition = oldPosition;
  537. this.position = position;
  538. this.unit = unit;
  539. }
  540. /**
  541. * Returns the new split position that triggered this change event.
  542. *
  543. * @return the new value of split position
  544. */
  545. public float getSplitPosition() {
  546. return position;
  547. }
  548. /**
  549. * Returns the new split position unit that triggered this change event.
  550. *
  551. * @return the new value of split position
  552. */
  553. public Unit getSplitPositionUnit() {
  554. return unit;
  555. }
  556. /**
  557. * Returns the position of the split before this change event occurred.
  558. *
  559. * @since 8.1
  560. *
  561. * @return the split position previously set to the source of this event
  562. */
  563. public float getOldSplitPosition() {
  564. return oldPosition;
  565. }
  566. /**
  567. * Returns the position unit of the split before this change event
  568. * occurred.
  569. *
  570. * @since 8.1
  571. *
  572. * @return the split position unit previously set to the source of this
  573. * event
  574. */
  575. public Unit getOldSplitPositionUnit() {
  576. return oldUnit;
  577. }
  578. /**
  579. * {@inheritDoc}
  580. *
  581. * @since 8.1
  582. */
  583. @Override
  584. public boolean isUserOriginated() {
  585. return userOriginated;
  586. }
  587. }
  588. public Registration addSplitterClickListener(
  589. SplitterClickListener listener) {
  590. return addListener(EventId.CLICK_EVENT_IDENTIFIER,
  591. SplitterClickEvent.class, listener,
  592. SplitterClickListener.clickMethod);
  593. }
  594. @Deprecated
  595. public void removeSplitterClickListener(SplitterClickListener listener) {
  596. removeListener(EventId.CLICK_EVENT_IDENTIFIER, SplitterClickEvent.class,
  597. listener);
  598. }
  599. /**
  600. * Register a listener to handle {@link SplitPositionChangeEvent}s.
  601. *
  602. * @since 8.0
  603. * @param listener
  604. * {@link SplitPositionChangeListener} to be registered.
  605. */
  606. public Registration addSplitPositionChangeListener(
  607. SplitPositionChangeListener listener) {
  608. return addListener(SplitPositionChangeEvent.class, listener,
  609. SplitPositionChangeListener.moveMethod);
  610. }
  611. /**
  612. * Removes a {@link SplitPositionChangeListener}.
  613. *
  614. * @since 7.5.0
  615. * @param listener
  616. * SplitPositionChangeListener to be removed.
  617. */
  618. @Deprecated
  619. public void removeSplitPositionChangeListener(
  620. SplitPositionChangeListener listener) {
  621. removeListener(SplitPositionChangeEvent.class, listener);
  622. }
  623. @Override
  624. protected AbstractSplitPanelState getState() {
  625. return (AbstractSplitPanelState) super.getState();
  626. }
  627. @Override
  628. protected AbstractSplitPanelState getState(boolean markAsDirty) {
  629. return (AbstractSplitPanelState) super.getState(markAsDirty);
  630. }
  631. private SplitterState getSplitterState() {
  632. return ((AbstractSplitPanelState) super.getState()).splitterState;
  633. }
  634. private SplitterState getSplitterState(boolean markAsDirty) {
  635. return ((AbstractSplitPanelState) super.getState(
  636. markAsDirty)).splitterState;
  637. }
  638. /*
  639. * (non-Javadoc)
  640. *
  641. * @see com.vaadin.ui.AbstractComponent#readDesign(org.jsoup.nodes .Element,
  642. * com.vaadin.ui.declarative.DesignContext)
  643. */
  644. @Override
  645. public void readDesign(Element design, DesignContext designContext) {
  646. // handle default attributes
  647. super.readDesign(design, designContext);
  648. // handle custom attributes, use default values if no explicit value
  649. // set
  650. // There is no setter for reversed, so it will be handled using
  651. // setSplitPosition.
  652. boolean reversed = false;
  653. if (design.hasAttr("reversed")) {
  654. reversed = DesignAttributeHandler.readAttribute("reversed",
  655. design.attributes(), Boolean.class);
  656. setSplitPosition(getSplitPosition(), reversed);
  657. }
  658. if (design.hasAttr("split-position")) {
  659. SizeWithUnit splitPosition = SizeWithUnit.parseStringSize(
  660. design.attr("split-position"), Unit.PERCENTAGE);
  661. setSplitPosition(splitPosition.getSize(), splitPosition.getUnit(),
  662. reversed);
  663. }
  664. if (design.hasAttr("min-split-position")) {
  665. SizeWithUnit minSplitPosition = SizeWithUnit.parseStringSize(
  666. design.attr("min-split-position"), Unit.PERCENTAGE);
  667. setMinSplitPosition(minSplitPosition.getSize(),
  668. minSplitPosition.getUnit());
  669. }
  670. if (design.hasAttr("max-split-position")) {
  671. SizeWithUnit maxSplitPosition = SizeWithUnit.parseStringSize(
  672. design.attr("max-split-position"), Unit.PERCENTAGE);
  673. setMaxSplitPosition(maxSplitPosition.getSize(),
  674. maxSplitPosition.getUnit());
  675. }
  676. // handle children
  677. if (design.children().size() > 2) {
  678. throw new DesignException(
  679. "A split panel can contain at most two components.");
  680. }
  681. for (Element childElement : design.children()) {
  682. Component childComponent = designContext.readDesign(childElement);
  683. if (childElement.hasAttr(":second")) {
  684. setSecondComponent(childComponent);
  685. } else {
  686. addComponent(childComponent);
  687. }
  688. }
  689. }
  690. @Override
  691. protected Collection<String> getCustomAttributes() {
  692. Collection<String> attributes = super.getCustomAttributes();
  693. // the setters of the properties do not accept strings such as "20px"
  694. attributes.add("split-position");
  695. attributes.add("min-split-position");
  696. attributes.add("max-split-position");
  697. // no explicit setter for reversed
  698. attributes.add("reversed");
  699. return attributes;
  700. }
  701. @Override
  702. public void writeDesign(Element design, DesignContext designContext) {
  703. // handle default attributes (also clears children and attributes)
  704. super.writeDesign(design, designContext);
  705. // handle custom attributes (write only if a value is not the
  706. // default value)
  707. AbstractSplitPanel def = designContext.getDefaultInstance(this);
  708. if (getSplitPosition() != def.getSplitPosition()
  709. || !def.getSplitPositionUnit().equals(getSplitPositionUnit())) {
  710. String splitPositionString = asString(getSplitPosition())
  711. + getSplitPositionUnit();
  712. design.attr("split-position", splitPositionString);
  713. }
  714. if (getMinSplitPosition() != def.getMinSplitPosition() || !def
  715. .getMinSplitPositionUnit().equals(getMinSplitPositionUnit())) {
  716. design.attr("min-split-position", asString(getMinSplitPosition())
  717. + getMinSplitPositionUnit());
  718. }
  719. if (getMaxSplitPosition() != def.getMaxSplitPosition() || !def
  720. .getMaxSplitPositionUnit().equals(getMaxSplitPositionUnit())) {
  721. design.attr("max-split-position", asString(getMaxSplitPosition())
  722. + getMaxSplitPositionUnit());
  723. }
  724. if (getSplitterState().positionReversed) {
  725. design.attr("reversed", true);
  726. }
  727. // handle child components
  728. if (!designContext.shouldWriteChildren(this, def)) {
  729. return;
  730. }
  731. Component firstComponent = getFirstComponent();
  732. Component secondComponent = getSecondComponent();
  733. if (firstComponent != null) {
  734. Element childElement = designContext.createElement(firstComponent);
  735. design.appendChild(childElement);
  736. }
  737. if (secondComponent != null) {
  738. Element childElement = designContext.createElement(secondComponent);
  739. if (firstComponent == null) {
  740. childElement.attr(":second", true);
  741. }
  742. design.appendChild(childElement);
  743. }
  744. }
  745. private String asString(float number) {
  746. int truncated = (int) number;
  747. if (truncated == number) {
  748. return "" + truncated;
  749. }
  750. return "" + number;
  751. }
  752. }