Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

VPopupView.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. /*
  2. * Copyright 2000-2014 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.HashSet;
  19. import java.util.Iterator;
  20. import java.util.Set;
  21. import com.google.gwt.dom.client.Element;
  22. import com.google.gwt.event.dom.client.ClickEvent;
  23. import com.google.gwt.event.dom.client.ClickHandler;
  24. import com.google.gwt.event.dom.client.KeyCodes;
  25. import com.google.gwt.event.dom.client.KeyDownEvent;
  26. import com.google.gwt.event.dom.client.KeyDownHandler;
  27. import com.google.gwt.event.logical.shared.CloseEvent;
  28. import com.google.gwt.event.logical.shared.CloseHandler;
  29. import com.google.gwt.event.shared.HandlerRegistration;
  30. import com.google.gwt.user.client.DOM;
  31. import com.google.gwt.user.client.Event;
  32. import com.google.gwt.user.client.ui.Focusable;
  33. import com.google.gwt.user.client.ui.HTML;
  34. import com.google.gwt.user.client.ui.HasWidgets;
  35. import com.google.gwt.user.client.ui.Label;
  36. import com.google.gwt.user.client.ui.PopupPanel;
  37. import com.google.gwt.user.client.ui.RootPanel;
  38. import com.google.gwt.user.client.ui.Widget;
  39. import com.vaadin.client.ApplicationConnection;
  40. import com.vaadin.client.ComponentConnector;
  41. import com.vaadin.client.Util;
  42. import com.vaadin.client.VCaptionWrapper;
  43. import com.vaadin.client.VConsole;
  44. import com.vaadin.client.communication.StateChangeEvent;
  45. import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
  46. import com.vaadin.client.ui.popupview.VisibilityChangeEvent;
  47. import com.vaadin.client.ui.popupview.VisibilityChangeHandler;
  48. public class VPopupView extends HTML implements Iterable<Widget> {
  49. public static final String CLASSNAME = "v-popupview";
  50. /**
  51. * For server-client communication.
  52. * <p>
  53. * For internal use only. May be removed or replaced in the future.
  54. */
  55. public String uidlId;
  56. /** For internal use only. May be removed or replaced in the future. */
  57. public ApplicationConnection client;
  58. /**
  59. * Helps to communicate popup visibility to the server.
  60. * <p>
  61. * For internal use only. May be removed or replaced in the future.
  62. */
  63. public boolean hostPopupVisible;
  64. /** For internal use only. May be removed or replaced in the future. */
  65. public final CustomPopup popup;
  66. private final Label loading = new Label();
  67. /**
  68. * loading constructor
  69. */
  70. public VPopupView() {
  71. super();
  72. popup = new CustomPopup();
  73. setStyleName(CLASSNAME);
  74. popup.setStyleName(CLASSNAME + "-popup");
  75. loading.setStyleName(CLASSNAME + "-loading");
  76. setHTML("");
  77. popup.setWidget(loading);
  78. // When we click to open the popup...
  79. addClickHandler(new ClickHandler() {
  80. @Override
  81. public void onClick(ClickEvent event) {
  82. preparePopup(popup);
  83. showPopup(popup);
  84. center();
  85. fireEvent(new VisibilityChangeEvent(true));
  86. }
  87. });
  88. // ..and when we close it
  89. popup.addCloseHandler(new CloseHandler<PopupPanel>() {
  90. @Override
  91. public void onClose(CloseEvent<PopupPanel> event) {
  92. fireEvent(new VisibilityChangeEvent(false));
  93. }
  94. });
  95. // TODO: Enable animations once GWT fix has been merged
  96. popup.setAnimationEnabled(false);
  97. popup.setAutoHideOnHistoryEventsEnabled(false);
  98. }
  99. /** For internal use only. May be removed or replaced in the future. */
  100. public void preparePopup(final CustomPopup popup) {
  101. popup.setVisible(true);
  102. popup.setWidget(loading);
  103. popup.show();
  104. }
  105. /**
  106. * Determines the correct position for a popup and displays the popup at
  107. * that position.
  108. *
  109. * By default, the popup is shown centered relative to its host component,
  110. * ensuring it is visible on the screen if possible.
  111. *
  112. * Can be overridden to customize the popup position.
  113. *
  114. * @param popup
  115. */
  116. public void showPopup(final CustomPopup popup) {
  117. popup.setPopupPosition(0, 0);
  118. }
  119. /** For internal use only. May be removed or replaced in the future. */
  120. public void center() {
  121. int windowTop = RootPanel.get().getAbsoluteTop();
  122. int windowLeft = RootPanel.get().getAbsoluteLeft();
  123. int windowRight = windowLeft + RootPanel.get().getOffsetWidth();
  124. int windowBottom = windowTop + RootPanel.get().getOffsetHeight();
  125. int offsetWidth = popup.getOffsetWidth();
  126. int offsetHeight = popup.getOffsetHeight();
  127. int hostHorizontalCenter = VPopupView.this.getAbsoluteLeft()
  128. + VPopupView.this.getOffsetWidth() / 2;
  129. int hostVerticalCenter = VPopupView.this.getAbsoluteTop()
  130. + VPopupView.this.getOffsetHeight() / 2;
  131. int left = hostHorizontalCenter - offsetWidth / 2;
  132. int top = hostVerticalCenter - offsetHeight / 2;
  133. // Don't show the popup outside the screen.
  134. if ((left + offsetWidth) > windowRight) {
  135. left -= (left + offsetWidth) - windowRight;
  136. }
  137. if ((top + offsetHeight) > windowBottom) {
  138. top -= (top + offsetHeight) - windowBottom;
  139. }
  140. if (left < 0) {
  141. left = 0;
  142. }
  143. if (top < 0) {
  144. top = 0;
  145. }
  146. popup.setPopupPosition(left, top);
  147. }
  148. /**
  149. * Make sure that we remove the popup when the main widget is removed.
  150. *
  151. * @see com.google.gwt.user.client.ui.Widget#onUnload()
  152. */
  153. @Override
  154. protected void onDetach() {
  155. popup.hide();
  156. super.onDetach();
  157. }
  158. private static native void nativeBlur(Element e)
  159. /*-{
  160. if(e && e.blur) {
  161. e.blur();
  162. }
  163. }-*/;
  164. /**
  165. * This class is only public to enable overriding showPopup, and is
  166. * currently not intended to be extended or otherwise used directly. Its API
  167. * (other than it being a VOverlay) is to be considered private and
  168. * potentially subject to change.
  169. */
  170. public class CustomPopup extends VOverlay implements
  171. StateChangeEvent.StateChangeHandler {
  172. private ComponentConnector popupComponentConnector = null;
  173. /** For internal use only. May be removed or replaced in the future. */
  174. public Widget popupComponentWidget = null;
  175. /** For internal use only. May be removed or replaced in the future. */
  176. public VCaptionWrapper captionWrapper = null;
  177. private boolean hasHadMouseOver = false;
  178. private boolean hideOnMouseOut = true;
  179. private final Set<Element> activeChildren = new HashSet<Element>();
  180. private ShortcutActionHandler shortcutActionHandler;
  181. public CustomPopup() {
  182. super(true, false, true); // autoHide, not modal, dropshadow
  183. setOwner(VPopupView.this);
  184. // Delegate popup keyboard events to the relevant handler. The
  185. // events do not propagate automatically because the popup is
  186. // directly attached to the RootPanel.
  187. addDomHandler(new KeyDownHandler() {
  188. @Override
  189. public void onKeyDown(KeyDownEvent event) {
  190. if (shortcutActionHandler != null) {
  191. shortcutActionHandler.handleKeyboardEvent(Event
  192. .as(event.getNativeEvent()));
  193. }
  194. }
  195. }, KeyDownEvent.getType());
  196. }
  197. // For some reason ONMOUSEOUT events are not always received, so we have
  198. // to use ONMOUSEMOVE that doesn't target the popup
  199. @Override
  200. public boolean onEventPreview(Event event) {
  201. Element target = DOM.eventGetTarget(event);
  202. boolean eventTargetsPopup = DOM.isOrHasChild(getElement(), target);
  203. int type = DOM.eventGetType(event);
  204. // Catch children that use keyboard, so we can unfocus them when
  205. // hiding
  206. if (eventTargetsPopup && type == Event.ONKEYPRESS) {
  207. activeChildren.add(target);
  208. }
  209. if (eventTargetsPopup && type == Event.ONMOUSEMOVE) {
  210. hasHadMouseOver = true;
  211. }
  212. if (!eventTargetsPopup && type == Event.ONMOUSEMOVE) {
  213. if (hasHadMouseOver && hideOnMouseOut) {
  214. hide();
  215. return true;
  216. }
  217. }
  218. // Was the TAB key released outside of our popup?
  219. if (!eventTargetsPopup && type == Event.ONKEYUP
  220. && event.getKeyCode() == KeyCodes.KEY_TAB) {
  221. // Should we hide on focus out (mouse out)?
  222. if (hideOnMouseOut) {
  223. hide();
  224. return true;
  225. }
  226. }
  227. return super.onEventPreview(event);
  228. }
  229. @Override
  230. public void hide(boolean autoClosed) {
  231. VConsole.log("Hiding popupview");
  232. syncChildren();
  233. clearPopupComponentConnector();
  234. hasHadMouseOver = false;
  235. shortcutActionHandler = null;
  236. super.hide(autoClosed);
  237. }
  238. @Override
  239. public void show() {
  240. // Find the shortcut action handler that should handle keyboard
  241. // events from the popup. The events do not propagate automatically
  242. // because the popup is directly attached to the RootPanel.
  243. Widget widget = VPopupView.this;
  244. while (shortcutActionHandler == null && widget != null) {
  245. if (widget instanceof ShortcutActionHandlerOwner) {
  246. shortcutActionHandler = ((ShortcutActionHandlerOwner) widget)
  247. .getShortcutActionHandler();
  248. }
  249. widget = widget.getParent();
  250. }
  251. super.show();
  252. }
  253. /**
  254. * Try to sync all known active child widgets to server
  255. */
  256. public void syncChildren() {
  257. // Notify children with focus
  258. if ((popupComponentWidget instanceof Focusable)) {
  259. ((Focusable) popupComponentWidget).setFocus(false);
  260. } else {
  261. checkForRTE(popupComponentWidget);
  262. }
  263. // Notify children that have used the keyboard
  264. for (Element e : activeChildren) {
  265. try {
  266. nativeBlur(e);
  267. } catch (Exception ignored) {
  268. }
  269. }
  270. activeChildren.clear();
  271. }
  272. private void checkForRTE(Widget popupComponentWidget2) {
  273. if (popupComponentWidget2 instanceof VRichTextArea) {
  274. ComponentConnector rtaConnector = Util
  275. .findConnectorFor(popupComponentWidget2);
  276. if (rtaConnector != null) {
  277. rtaConnector.flush();
  278. }
  279. } else if (popupComponentWidget2 instanceof HasWidgets) {
  280. HasWidgets hw = (HasWidgets) popupComponentWidget2;
  281. Iterator<Widget> iterator = hw.iterator();
  282. while (iterator.hasNext()) {
  283. checkForRTE(iterator.next());
  284. }
  285. }
  286. }
  287. private void clearPopupComponentConnector() {
  288. if (popupComponentConnector != null) {
  289. popupComponentConnector.removeStateChangeHandler(this);
  290. }
  291. popupComponentConnector = null;
  292. popupComponentWidget = null;
  293. captionWrapper = null;
  294. }
  295. @Override
  296. public boolean remove(Widget w) {
  297. clearPopupComponentConnector();
  298. return super.remove(w);
  299. }
  300. public void setPopupConnector(ComponentConnector newPopupComponent) {
  301. if (newPopupComponent != popupComponentConnector) {
  302. if (popupComponentConnector != null) {
  303. popupComponentConnector.removeStateChangeHandler(this);
  304. }
  305. Widget newWidget = newPopupComponent.getWidget();
  306. setWidget(newWidget);
  307. popupComponentWidget = newWidget;
  308. popupComponentConnector = newPopupComponent;
  309. popupComponentConnector.addStateChangeHandler("height", this);
  310. popupComponentConnector.addStateChangeHandler("width", this);
  311. }
  312. }
  313. public void setHideOnMouseOut(boolean hideOnMouseOut) {
  314. this.hideOnMouseOut = hideOnMouseOut;
  315. }
  316. @Override
  317. public com.google.gwt.user.client.Element getContainerElement() {
  318. return super.getContainerElement();
  319. }
  320. @Override
  321. public void onStateChanged(StateChangeEvent stateChangeEvent) {
  322. positionOrSizeUpdated();
  323. }
  324. }// class CustomPopup
  325. public HandlerRegistration addVisibilityChangeHandler(
  326. final VisibilityChangeHandler visibilityChangeHandler) {
  327. return addHandler(visibilityChangeHandler,
  328. VisibilityChangeEvent.getType());
  329. }
  330. @Override
  331. public Iterator<Widget> iterator() {
  332. return Collections.singleton((Widget) popup).iterator();
  333. }
  334. }// class VPopupView