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.

VAbstractSplitPanel.java 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
  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.client.ui;
  17. import java.util.Collections;
  18. import java.util.List;
  19. import com.google.gwt.dom.client.Element;
  20. import com.google.gwt.dom.client.Node;
  21. import com.google.gwt.dom.client.Style;
  22. import com.google.gwt.dom.client.Style.Position;
  23. import com.google.gwt.dom.client.Style.Unit;
  24. import com.google.gwt.event.dom.client.TouchCancelEvent;
  25. import com.google.gwt.event.dom.client.TouchCancelHandler;
  26. import com.google.gwt.event.dom.client.TouchEndEvent;
  27. import com.google.gwt.event.dom.client.TouchEndHandler;
  28. import com.google.gwt.event.dom.client.TouchMoveEvent;
  29. import com.google.gwt.event.dom.client.TouchMoveHandler;
  30. import com.google.gwt.event.dom.client.TouchStartEvent;
  31. import com.google.gwt.event.dom.client.TouchStartHandler;
  32. import com.google.gwt.event.shared.EventHandler;
  33. import com.google.gwt.event.shared.GwtEvent;
  34. import com.google.gwt.user.client.DOM;
  35. import com.google.gwt.user.client.Event;
  36. import com.google.gwt.user.client.ui.ComplexPanel;
  37. import com.google.gwt.user.client.ui.Widget;
  38. import com.vaadin.client.ApplicationConnection;
  39. import com.vaadin.client.BrowserInfo;
  40. import com.vaadin.client.ComponentConnector;
  41. import com.vaadin.client.ConnectorMap;
  42. import com.vaadin.client.LayoutManager;
  43. import com.vaadin.client.VConsole;
  44. import com.vaadin.client.WidgetUtil;
  45. import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler;
  46. import com.vaadin.client.ui.VAbstractSplitPanel.SplitterMoveHandler.SplitterMoveEvent;
  47. import com.vaadin.shared.ui.Orientation;
  48. public abstract class VAbstractSplitPanel extends ComplexPanel {
  49. private boolean enabled = false;
  50. public static final String CLASSNAME = "v-splitpanel";
  51. private static final int MIN_SIZE = 30;
  52. private Orientation orientation = Orientation.HORIZONTAL;
  53. Widget firstChild;
  54. Widget secondChild;
  55. private final Element wrapper = DOM.createDiv();
  56. private final Element firstContainer = DOM.createDiv();
  57. private final Element secondContainer = DOM.createDiv();
  58. /** For internal use only. May be removed or replaced in the future. */
  59. public final Element splitter = DOM.createDiv();
  60. private boolean resizing;
  61. private boolean resized = false;
  62. private int origX;
  63. private int origY;
  64. private int origMouseX;
  65. private int origMouseY;
  66. private boolean locked = false;
  67. private boolean positionReversed = false;
  68. /** For internal use only. May be removed or replaced in the future. */
  69. public List<String> componentStyleNames = Collections.emptyList();
  70. private Element draggingCurtain;
  71. /** For internal use only. May be removed or replaced in the future. */
  72. public ApplicationConnection client;
  73. /** For internal use only. May be removed or replaced in the future. */
  74. public boolean immediate;
  75. /**
  76. * The current position of the split handle in either percentages or pixels
  77. * <p>
  78. * For internal use only. May be removed or replaced in the future.
  79. */
  80. public String position;
  81. /** For internal use only. May be removed or replaced in the future. */
  82. public String maximumPosition;
  83. /** For internal use only. May be removed or replaced in the future. */
  84. public String minimumPosition;
  85. private TouchScrollHandler touchScrollHandler;
  86. protected Element scrolledContainer;
  87. protected int origScrollTop;
  88. public VAbstractSplitPanel() {
  89. this(Orientation.HORIZONTAL);
  90. }
  91. public VAbstractSplitPanel(Orientation orientation) {
  92. setElement(DOM.createDiv());
  93. switch (orientation) {
  94. case HORIZONTAL:
  95. setStyleName(CLASSNAME + "-horizontal");
  96. break;
  97. case VERTICAL:
  98. default:
  99. setStyleName(CLASSNAME + "-vertical");
  100. break;
  101. }
  102. // size below will be overridden in update from uidl, initial size
  103. // needed to keep IE alive
  104. setWidth(MIN_SIZE + "px");
  105. setHeight(MIN_SIZE + "px");
  106. constructDom();
  107. setOrientation(orientation);
  108. sinkEvents(Event.MOUSEEVENTS);
  109. makeScrollable();
  110. addDomHandler(new TouchCancelHandler() {
  111. @Override
  112. public void onTouchCancel(TouchCancelEvent event) {
  113. // TODO When does this actually happen??
  114. VConsole.log("TOUCH CANCEL");
  115. }
  116. }, TouchCancelEvent.getType());
  117. addDomHandler(new TouchStartHandler() {
  118. @Override
  119. public void onTouchStart(TouchStartEvent event) {
  120. Node target = event.getTouches().get(0).getTarget().cast();
  121. if (splitter.isOrHasChild(target)) {
  122. onMouseDown(Event.as(event.getNativeEvent()));
  123. }
  124. }
  125. }, TouchStartEvent.getType());
  126. addDomHandler(new TouchMoveHandler() {
  127. @Override
  128. public void onTouchMove(TouchMoveEvent event) {
  129. if (resizing) {
  130. onMouseMove(Event.as(event.getNativeEvent()));
  131. }
  132. }
  133. }, TouchMoveEvent.getType());
  134. addDomHandler(new TouchEndHandler() {
  135. @Override
  136. public void onTouchEnd(TouchEndEvent event) {
  137. if (resizing) {
  138. onMouseUp(Event.as(event.getNativeEvent()));
  139. }
  140. }
  141. }, TouchEndEvent.getType());
  142. }
  143. protected void constructDom() {
  144. DOM.appendChild(splitter, DOM.createDiv()); // for styling
  145. DOM.appendChild(getElement(), wrapper);
  146. wrapper.getStyle().setPosition(Position.RELATIVE);
  147. wrapper.getStyle().setWidth(100, Unit.PCT);
  148. wrapper.getStyle().setHeight(100, Unit.PCT);
  149. DOM.appendChild(wrapper, firstContainer);
  150. DOM.appendChild(wrapper, splitter);
  151. DOM.appendChild(wrapper, secondContainer);
  152. splitter.getStyle().setPosition(Position.ABSOLUTE);
  153. secondContainer.getStyle().setPosition(Position.ABSOLUTE);
  154. setStylenames();
  155. }
  156. private void setOrientation(Orientation orientation) {
  157. this.orientation = orientation;
  158. if (orientation == Orientation.HORIZONTAL) {
  159. splitter.getStyle().setHeight(100, Unit.PCT);
  160. splitter.getStyle().setTop(0, Unit.PX);
  161. firstContainer.getStyle().setHeight(100, Unit.PCT);
  162. secondContainer.getStyle().setTop(0, Unit.PX);
  163. secondContainer.getStyle().setHeight(100, Unit.PCT);
  164. } else {
  165. splitter.getStyle().setWidth(100, Unit.PCT);
  166. splitter.getStyle().setLeft(0, Unit.PX);
  167. firstContainer.getStyle().setWidth(100, Unit.PCT);
  168. secondContainer.getStyle().setWidth(100, Unit.PCT);
  169. }
  170. }
  171. @Override
  172. public boolean remove(Widget w) {
  173. boolean removed = super.remove(w);
  174. if (removed) {
  175. if (firstChild == w) {
  176. firstChild = null;
  177. } else {
  178. secondChild = null;
  179. }
  180. }
  181. return removed;
  182. }
  183. /** For internal use only. May be removed or replaced in the future. */
  184. public void setLocked(boolean newValue) {
  185. if (locked != newValue) {
  186. locked = newValue;
  187. splitterSize = -1;
  188. setStylenames();
  189. }
  190. }
  191. /** For internal use only. May be removed or replaced in the future. */
  192. public void setPositionReversed(boolean reversed) {
  193. if (positionReversed != reversed) {
  194. if (orientation == Orientation.HORIZONTAL) {
  195. splitter.getStyle().clearRight();
  196. splitter.getStyle().clearLeft();
  197. } else if (orientation == Orientation.VERTICAL) {
  198. splitter.getStyle().clearTop();
  199. splitter.getStyle().clearBottom();
  200. }
  201. positionReversed = reversed;
  202. }
  203. }
  204. /**
  205. * Converts given split position string (in pixels or percentage) to a
  206. * floating point pixel value.
  207. *
  208. * @param pos
  209. * @return
  210. */
  211. private float convertToPixels(String pos) {
  212. float posAsFloat;
  213. if (pos.indexOf("%") > 0) {
  214. posAsFloat = Math
  215. .round(Float.parseFloat(pos.substring(0, pos.length() - 1))
  216. / 100 * (orientation == Orientation.HORIZONTAL
  217. ? getOffsetWidth() : getOffsetHeight()));
  218. } else {
  219. posAsFloat = Float.parseFloat(pos.substring(0, pos.length() - 2));
  220. }
  221. return posAsFloat;
  222. }
  223. /**
  224. * Converts given split position string (in pixels or percentage) to a float
  225. * percentage value.
  226. *
  227. * @param pos
  228. * @return
  229. */
  230. private float convertToPercentage(String pos) {
  231. if (pos.endsWith("px")) {
  232. float pixelPosition = Float
  233. .parseFloat(pos.substring(0, pos.length() - 2));
  234. int offsetLength = orientation == Orientation.HORIZONTAL
  235. ? getOffsetWidth() : getOffsetHeight();
  236. // Take splitter size into account at the edge
  237. if (pixelPosition + getSplitterSize() >= offsetLength) {
  238. return 100;
  239. }
  240. return pixelPosition / offsetLength * 100;
  241. } else {
  242. assert pos.endsWith("%");
  243. return Float.parseFloat(pos.substring(0, pos.length() - 1));
  244. }
  245. }
  246. /**
  247. * Returns the given position clamped to the range between current minimum
  248. * and maximum positions.
  249. *
  250. * TODO Should this be in the connector?
  251. *
  252. * @param pos
  253. * Position of the splitter as a CSS string, either pixels or a
  254. * percentage.
  255. * @return minimumPosition if pos is less than minimumPosition;
  256. * maximumPosition if pos is greater than maximumPosition; pos
  257. * otherwise.
  258. */
  259. private String checkSplitPositionLimits(String pos) {
  260. float positionAsFloat = convertToPixels(pos);
  261. if (maximumPosition != null
  262. && convertToPixels(maximumPosition) < positionAsFloat) {
  263. pos = maximumPosition;
  264. } else if (minimumPosition != null
  265. && convertToPixels(minimumPosition) > positionAsFloat) {
  266. pos = minimumPosition;
  267. }
  268. return pos;
  269. }
  270. /**
  271. * Converts given string to the same units as the split position is.
  272. *
  273. * @param pos
  274. * position to be converted
  275. * @return converted position string
  276. */
  277. private String convertToPositionUnits(String pos) {
  278. if (position.indexOf("%") != -1 && pos.indexOf("%") == -1) {
  279. // position is in percentage, pos in pixels
  280. pos = convertToPercentage(pos) + "%";
  281. } else if (position.indexOf("px") > 0 && pos.indexOf("px") == -1) {
  282. // position is in pixels and pos in percentage
  283. pos = convertToPixels(pos) + "px";
  284. }
  285. return pos;
  286. }
  287. public void setSplitPosition(String pos) {
  288. setSplitPosition(pos, true);
  289. }
  290. private void setSplitPosition(String pos, boolean rememberPosition) {
  291. if (pos == null) {
  292. return;
  293. }
  294. pos = checkSplitPositionLimits(pos);
  295. if (rememberPosition && !pos.equals(position)) {
  296. position = convertToPositionUnits(pos);
  297. }
  298. // Convert percentage values to pixels
  299. if (pos.indexOf("%") > 0) {
  300. int size = orientation == Orientation.HORIZONTAL ? getOffsetWidth()
  301. : getOffsetHeight();
  302. float percentage = Float
  303. .parseFloat(pos.substring(0, pos.length() - 1));
  304. pos = percentage / 100 * size + "px";
  305. }
  306. String attributeName;
  307. if (orientation == Orientation.HORIZONTAL) {
  308. if (positionReversed) {
  309. attributeName = "right";
  310. } else {
  311. attributeName = "left";
  312. }
  313. } else {
  314. if (positionReversed) {
  315. attributeName = "bottom";
  316. } else {
  317. attributeName = "top";
  318. }
  319. }
  320. Style style = splitter.getStyle();
  321. if (!pos.equals(style.getProperty(attributeName))) {
  322. style.setProperty(attributeName, pos);
  323. updateSizes();
  324. }
  325. }
  326. /** For internal use only. May be removed or replaced in the future. */
  327. public void updateSizes() {
  328. if (!isAttached()) {
  329. return;
  330. }
  331. switch (orientation) {
  332. case HORIZONTAL:
  333. horizontalOrientationUpdateSizes();
  334. break;
  335. case VERTICAL:
  336. verticalOrientationUpdateSizes();
  337. break;
  338. }
  339. }
  340. private void verticalOrientationUpdateSizes() {
  341. int wholeSize = DOM.getElementPropertyInt(wrapper, "clientHeight");
  342. int pixelPosition = DOM.getElementPropertyInt(splitter, "offsetTop");
  343. // reposition splitter in case it is out of box
  344. if ((pixelPosition > 0 && pixelPosition + getSplitterSize() > wholeSize)
  345. || (positionReversed && pixelPosition < 0)) {
  346. pixelPosition = wholeSize - getSplitterSize();
  347. if (pixelPosition < 0) {
  348. pixelPosition = 0;
  349. }
  350. // Move splitter within bounds, but don't remember the new value
  351. setSplitPosition(pixelPosition + "px", false);
  352. return;
  353. }
  354. firstContainer.getStyle().setHeight(pixelPosition, Unit.PX);
  355. int secondContainerHeight = (wholeSize - pixelPosition
  356. - getSplitterSize());
  357. if (secondContainerHeight < 0) {
  358. secondContainerHeight = 0;
  359. }
  360. secondContainer.getStyle().setHeight(secondContainerHeight, Unit.PX);
  361. secondContainer.getStyle().setTop(pixelPosition + getSplitterSize(),
  362. Unit.PX);
  363. LayoutManager layoutManager = LayoutManager.get(client);
  364. ConnectorMap connectorMap = ConnectorMap.get(client);
  365. if (firstChild != null) {
  366. ComponentConnector connector = connectorMap
  367. .getConnector(firstChild);
  368. if (connector.isRelativeHeight()) {
  369. layoutManager.reportHeightAssignedToRelative(connector,
  370. pixelPosition);
  371. } else {
  372. layoutManager.setNeedsMeasure(connector);
  373. }
  374. }
  375. if (secondChild != null) {
  376. ComponentConnector connector = connectorMap
  377. .getConnector(secondChild);
  378. if (connector.isRelativeHeight()) {
  379. layoutManager.reportHeightAssignedToRelative(connector,
  380. secondContainerHeight);
  381. } else {
  382. layoutManager.setNeedsMeasure(connector);
  383. }
  384. }
  385. }
  386. private void horizontalOrientationUpdateSizes() {
  387. int wholeSize = DOM.getElementPropertyInt(wrapper, "clientWidth");
  388. int pixelPosition = DOM.getElementPropertyInt(splitter, "offsetLeft");
  389. // reposition splitter in case it is out of box
  390. if ((pixelPosition > 0 && pixelPosition + getSplitterSize() > wholeSize)
  391. || (positionReversed && pixelPosition < 0)) {
  392. pixelPosition = wholeSize - getSplitterSize();
  393. if (pixelPosition < 0) {
  394. pixelPosition = 0;
  395. }
  396. // Move splitter within bounds, but don't remember the new value
  397. setSplitPosition(pixelPosition + "px", false);
  398. return;
  399. }
  400. firstContainer.getStyle().setWidth(pixelPosition, Unit.PX);
  401. int secondContainerWidth = (wholeSize - pixelPosition
  402. - getSplitterSize());
  403. if (secondContainerWidth < 0) {
  404. secondContainerWidth = 0;
  405. }
  406. secondContainer.getStyle().setWidth(secondContainerWidth, Unit.PX);
  407. secondContainer.getStyle().setLeft(pixelPosition + getSplitterSize(),
  408. Unit.PX);
  409. LayoutManager layoutManager = LayoutManager.get(client);
  410. ConnectorMap connectorMap = ConnectorMap.get(client);
  411. if (firstChild != null) {
  412. ComponentConnector connector = connectorMap
  413. .getConnector(firstChild);
  414. if (connector.isRelativeWidth()) {
  415. layoutManager.reportWidthAssignedToRelative(connector,
  416. pixelPosition);
  417. } else {
  418. layoutManager.setNeedsMeasure(connector);
  419. }
  420. }
  421. if (secondChild != null) {
  422. ComponentConnector connector = connectorMap
  423. .getConnector(secondChild);
  424. if (connector.isRelativeWidth()) {
  425. layoutManager.reportWidthAssignedToRelative(connector,
  426. secondContainerWidth);
  427. } else {
  428. layoutManager.setNeedsMeasure(connector);
  429. }
  430. }
  431. // previous layout pass may have changed the position already, needs to
  432. // be reset before calculating which positioning should be used
  433. secondContainer.getStyle().setPosition(Position.ABSOLUTE);
  434. if (getOffsetHeight() == 0) {
  435. secondContainer.getStyle().setPosition(Position.RELATIVE);
  436. }
  437. }
  438. /** For internal use only. May be removed or replaced in the future. */
  439. public void setFirstWidget(Widget w) {
  440. if (firstChild == w) {
  441. return;
  442. }
  443. if (firstChild != null) {
  444. firstChild.removeFromParent();
  445. }
  446. if (w != null) {
  447. super.add(w, firstContainer);
  448. }
  449. firstChild = w;
  450. }
  451. public Widget getFirstWidget() {
  452. return firstChild;
  453. }
  454. /** For internal use only. May be removed or replaced in the future. */
  455. public void setSecondWidget(Widget w) {
  456. if (secondChild == w) {
  457. return;
  458. }
  459. if (secondChild != null) {
  460. secondChild.removeFromParent();
  461. }
  462. if (w != null) {
  463. super.add(w, secondContainer);
  464. }
  465. secondChild = w;
  466. }
  467. public Widget getSecondWidget() {
  468. return secondChild;
  469. }
  470. @Override
  471. public void onBrowserEvent(Event event) {
  472. switch (DOM.eventGetType(event)) {
  473. case Event.ONMOUSEMOVE:
  474. // case Event.ONTOUCHMOVE:
  475. if (resizing) {
  476. onMouseMove(event);
  477. }
  478. break;
  479. case Event.ONMOUSEDOWN:
  480. // case Event.ONTOUCHSTART:
  481. onMouseDown(event);
  482. break;
  483. case Event.ONMOUSEOUT:
  484. // Dragging curtain interferes with click events if added in
  485. // mousedown so we add it only when needed i.e., if the mouse moves
  486. // outside the splitter.
  487. if (resizing) {
  488. showDraggingCurtain();
  489. }
  490. break;
  491. case Event.ONMOUSEUP:
  492. // case Event.ONTOUCHEND:
  493. if (resizing) {
  494. onMouseUp(event);
  495. }
  496. break;
  497. case Event.ONCLICK:
  498. stopResize();
  499. resizing = false;
  500. break;
  501. }
  502. // Only fire click event listeners if the splitter isn't moved
  503. if (WidgetUtil.isTouchEvent(event) || !resized) {
  504. super.onBrowserEvent(event);
  505. } else if (DOM.eventGetType(event) == Event.ONMOUSEUP) {
  506. // Reset the resized flag after a mouseup has occured so the next
  507. // mousedown/mouseup can be interpreted as a click.
  508. resized = false;
  509. }
  510. }
  511. public void onMouseDown(Event event) {
  512. if (locked || !isEnabled()) {
  513. return;
  514. }
  515. final Element trg = event.getEventTarget().cast();
  516. if (trg == splitter || trg == DOM.getChild(splitter, 0)) {
  517. startResize();
  518. resizing = true;
  519. DOM.setCapture(getElement());
  520. origX = DOM.getElementPropertyInt(splitter, "offsetLeft");
  521. origY = DOM.getElementPropertyInt(splitter, "offsetTop");
  522. origMouseX = WidgetUtil.getTouchOrMouseClientX(event);
  523. origMouseY = WidgetUtil.getTouchOrMouseClientY(event);
  524. event.stopPropagation();
  525. event.preventDefault();
  526. }
  527. }
  528. /**
  529. * Called when starting drag resize
  530. *
  531. * @since 7.5.1
  532. */
  533. abstract protected void startResize();
  534. /**
  535. * Called when stopping drag resize
  536. *
  537. * @since 7.5.1
  538. */
  539. abstract protected void stopResize();
  540. /**
  541. * Gets the first container
  542. *
  543. * @since 7.5.1
  544. * @return the firstContainer
  545. */
  546. protected Element getFirstContainer() {
  547. return firstContainer;
  548. }
  549. /**
  550. * Gets the second container
  551. *
  552. * @since 7.5.1
  553. * @return the secondContainer
  554. */
  555. protected Element getSecondContainer() {
  556. return secondContainer;
  557. }
  558. public void onMouseMove(Event event) {
  559. switch (orientation) {
  560. case HORIZONTAL:
  561. final int x = WidgetUtil.getTouchOrMouseClientX(event);
  562. onHorizontalMouseMove(x);
  563. break;
  564. case VERTICAL:
  565. default:
  566. final int y = WidgetUtil.getTouchOrMouseClientY(event);
  567. onVerticalMouseMove(y);
  568. break;
  569. }
  570. }
  571. private void onHorizontalMouseMove(int x) {
  572. int newX = origX + x - origMouseX;
  573. if (newX < 0) {
  574. newX = 0;
  575. }
  576. if (newX + getSplitterSize() > getOffsetWidth()) {
  577. newX = getOffsetWidth() - getSplitterSize();
  578. }
  579. if (position.indexOf("%") > 0) {
  580. position = convertToPositionUnits(newX + "px");
  581. } else {
  582. // Reversed position
  583. if (positionReversed) {
  584. position = (getOffsetWidth() - newX - getSplitterSize()) + "px";
  585. } else {
  586. position = newX + "px";
  587. }
  588. }
  589. if (origX != newX) {
  590. resized = true;
  591. }
  592. // Reversed position
  593. if (positionReversed) {
  594. newX = getOffsetWidth() - newX - getSplitterSize();
  595. }
  596. setSplitPosition(newX + "px");
  597. }
  598. private void onVerticalMouseMove(int y) {
  599. int newY = origY + y - origMouseY;
  600. if (newY < 0) {
  601. newY = 0;
  602. }
  603. if (newY + getSplitterSize() > getOffsetHeight()) {
  604. newY = getOffsetHeight() - getSplitterSize();
  605. }
  606. if (position.indexOf("%") > 0) {
  607. position = convertToPositionUnits(newY + "px");
  608. } else {
  609. // Reversed position
  610. if (positionReversed) {
  611. position = (getOffsetHeight() - newY - getSplitterSize())
  612. + "px";
  613. } else {
  614. position = newY + "px";
  615. }
  616. }
  617. if (origY != newY) {
  618. resized = true;
  619. }
  620. // Reversed position
  621. if (positionReversed) {
  622. newY = getOffsetHeight() - newY - getSplitterSize();
  623. }
  624. setSplitPosition(newY + "px");
  625. }
  626. public void onMouseUp(Event event) {
  627. DOM.releaseCapture(getElement());
  628. hideDraggingCurtain();
  629. stopResize();
  630. resizing = false;
  631. if (!WidgetUtil.isTouchEvent(event)) {
  632. onMouseMove(event);
  633. }
  634. fireEvent(new SplitterMoveEvent(this));
  635. }
  636. public interface SplitterMoveHandler extends EventHandler {
  637. public void splitterMoved(SplitterMoveEvent event);
  638. public static class SplitterMoveEvent
  639. extends GwtEvent<SplitterMoveHandler> {
  640. public static final Type<SplitterMoveHandler> TYPE = new Type<>();
  641. private Widget splitPanel;
  642. public SplitterMoveEvent(Widget splitPanel) {
  643. this.splitPanel = splitPanel;
  644. }
  645. @Override
  646. public com.google.gwt.event.shared.GwtEvent.Type<SplitterMoveHandler> getAssociatedType() {
  647. return TYPE;
  648. }
  649. @Override
  650. protected void dispatch(SplitterMoveHandler handler) {
  651. handler.splitterMoved(this);
  652. }
  653. }
  654. }
  655. /** For internal use only. May be removed or replaced in the future. */
  656. public String getSplitterPosition() {
  657. return position;
  658. }
  659. /**
  660. * Used in FF to avoid losing mouse capture when pointer is moved on an
  661. * iframe.
  662. */
  663. private void showDraggingCurtain() {
  664. if (!isDraggingCurtainRequired()) {
  665. return;
  666. }
  667. if (draggingCurtain == null) {
  668. draggingCurtain = DOM.createDiv();
  669. draggingCurtain.getStyle().setPosition(Position.ABSOLUTE);
  670. draggingCurtain.getStyle().setTop(0, Unit.PX);
  671. draggingCurtain.getStyle().setLeft(0, Unit.PX);
  672. draggingCurtain.getStyle().setWidth(100, Unit.PCT);
  673. draggingCurtain.getStyle().setHeight(100, Unit.PCT);
  674. draggingCurtain.getStyle().setZIndex(VOverlay.Z_INDEX);
  675. DOM.appendChild(wrapper, draggingCurtain);
  676. }
  677. }
  678. /**
  679. * A dragging curtain is required in Gecko and Webkit.
  680. *
  681. * @return true if the browser requires a dragging curtain
  682. */
  683. private boolean isDraggingCurtainRequired() {
  684. return (BrowserInfo.get().isGecko() || BrowserInfo.get().isWebkit());
  685. }
  686. /**
  687. * Hides dragging curtain
  688. */
  689. private void hideDraggingCurtain() {
  690. if (draggingCurtain != null) {
  691. DOM.removeChild(wrapper, draggingCurtain);
  692. draggingCurtain = null;
  693. }
  694. }
  695. private int splitterSize = -1;
  696. private int getSplitterSize() {
  697. if (splitterSize < 0) {
  698. if (isAttached()) {
  699. switch (orientation) {
  700. case HORIZONTAL:
  701. splitterSize = DOM.getElementPropertyInt(splitter,
  702. "offsetWidth");
  703. break;
  704. default:
  705. splitterSize = DOM.getElementPropertyInt(splitter,
  706. "offsetHeight");
  707. break;
  708. }
  709. }
  710. }
  711. return splitterSize;
  712. }
  713. /** For internal use only. May be removed or replaced in the future. */
  714. public void setStylenames() {
  715. final String splitterClass = CLASSNAME
  716. + (orientation == Orientation.HORIZONTAL ? "-hsplitter"
  717. : "-vsplitter");
  718. final String firstContainerClass = CLASSNAME + "-first-container";
  719. final String secondContainerClass = CLASSNAME + "-second-container";
  720. final String lockedSuffix = locked ? "-locked" : "";
  721. splitter.setClassName(splitterClass + lockedSuffix);
  722. firstContainer.setClassName(firstContainerClass);
  723. secondContainer.setClassName(secondContainerClass);
  724. for (String styleName : componentStyleNames) {
  725. splitter.addClassName(
  726. splitterClass + "-" + styleName + lockedSuffix);
  727. firstContainer.addClassName(firstContainerClass + "-" + styleName);
  728. secondContainer
  729. .addClassName(secondContainerClass + "-" + styleName);
  730. }
  731. }
  732. public void setEnabled(boolean enabled) {
  733. this.enabled = enabled;
  734. }
  735. public boolean isEnabled() {
  736. return enabled;
  737. }
  738. /**
  739. * Ensures the panels are scrollable eg. after style name changes
  740. * <p>
  741. * For internal use only. May be removed or replaced in the future.
  742. */
  743. public void makeScrollable() {
  744. if (touchScrollHandler == null) {
  745. touchScrollHandler = TouchScrollDelegate.enableTouchScrolling(this);
  746. }
  747. touchScrollHandler.addElement(firstContainer);
  748. touchScrollHandler.addElement(secondContainer);
  749. }
  750. }