Joel,
Would you please review the attached patch that solves a problem related
when widgets need to be attached/detached from panels for the Maps
InfoWindow.
It addresses issue 156 in gwt-google-apis:
http://code.google.com/p/gwt-google-apis/issues/detail?id=156
M maps/maps/src/com/google/gwt/maps/client/InfoWindow.java
Thanks,
-Eric.
--
Eric Z. Ayers - GWT Team - Atlanta, GA USA
http://code.google.com/webtoolkit/
--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---
Index: maps/maps/src/com/google/gwt/maps/client/InfoWindow.java
===================================================================
--- maps/maps/src/com/google/gwt/maps/client/InfoWindow.java (revision 751)
+++ maps/maps/src/com/google/gwt/maps/client/InfoWindow.java (working copy)
@@ -22,6 +22,7 @@
import com.google.gwt.maps.client.event.InfoWindowRestoreClickHandler;
import com.google.gwt.maps.client.event.InfoWindowRestoreEndHandler;
import com.google.gwt.maps.client.event.MapInfoWindowCloseHandler;
+import com.google.gwt.maps.client.event.MapInfoWindowOpenHandler;
import com.google.gwt.maps.client.event.InfoWindowCloseClickHandler.InfoWindowCloseClickEvent;
import com.google.gwt.maps.client.event.InfoWindowMaximizeClickHandler.InfoWindowMaximizeClickEvent;
import com.google.gwt.maps.client.event.InfoWindowMaximizeEndHandler.InfoWindowMaximizeEndEvent;
@@ -75,6 +76,28 @@
}
}
+ /**
+ * Handlers that cover attaching and removing widgets from the virtual panels
+ * created above.
+ */
+ private MapInfoWindowOpenHandler attachInfoWindowOpenHandler;
+ private MapInfoWindowCloseHandler attachMapInfoWindowCloseHandler;
+ private InfoWindowMaximizeClickHandler attachMaximizeClickHandler;
+ private InfoWindowMaximizeEndHandler attachMaximizeEndHandler;
+ private InfoWindowRestoreClickHandler attachRestoreClickHandler;
+ private InfoWindowRestoreEndHandler attachRestoreEndHandler;
+
+ /**
+ * Private copies of the widgets passed in from InfoWindowContent. Make copies
+ * so that an unsuspecting user doesn't overwrite this information.
+ */
+ private Widget contentMaxWidget;
+ private List<Widget> contentWidgets;
+ private boolean contentWidgetsAttached;
+
+ /**
+ * Tracks handlers attached to this InfoWindow instance.
+ */
private HandlerCollection<InfoWindowCloseClickHandler> infoWindowCloseClickHandlers;
private HandlerCollection<InfoWindowMaximizeClickHandler> infoWindowMaximizeClickHandlers;
private HandlerCollection<InfoWindowMaximizeEndHandler> infoWindowMaximizeEndHandlers;
@@ -82,13 +105,14 @@
private HandlerCollection<InfoWindowRestoreEndHandler> infoWindowRestoreEndHandlers;
private final JavaScriptObject jsoPeer;
-
private final MapWidget map;
/**
- * The virtual panel is used as a point to attach.
+ * Virtual Panels used as a point to attach the maximized/restored state
+ * widgets. This plugs the widget into the GWT Event system.
*/
- private final VirtualPanel virtualPanel = new VirtualPanel();
+ private final VirtualPanel virtualMaximizedPanel = new VirtualPanel();
+ private final VirtualPanel virtualRestoredPanel = new VirtualPanel();
/**
* Package-private constructor to prevent instantiation from outside of the
@@ -275,7 +299,8 @@
* InfoWindow.
*/
public void open(LatLng point, InfoWindowContent content) {
- beginAttach(content);
+ addAttachHandlers(content);
+
switch (content.getType()) {
case InfoWindowContent.TYPE_ELEMENT:
MapImpl.impl.openInfoWindow(map, point, content.getContent(),
@@ -289,7 +314,6 @@
MapImpl.impl.showMapBlowup(map, point, content.getOptions());
break;
}
- finishAttach(content);
}
/**
@@ -300,7 +324,8 @@
* InfoWindow.
*/
public void open(Marker marker, InfoWindowContent content) {
- beginAttach(content);
+ addAttachHandlers(content);
+
switch (content.getType()) {
case InfoWindowContent.TYPE_ELEMENT:
MarkerImpl.impl.openInfoWindow(marker, content.getContent(),
@@ -314,12 +339,12 @@
MarkerImpl.impl.showMapBlowup(marker, content.getOptions());
break;
}
- finishAttach(content);
}
/**
* Removes a single handler of this map previously added with
- * [EMAIL PROTECTED] InfoWindow#addInfoWindowCloseClickHandler(InfoWindowCloseClickHandler)}.
+ * [EMAIL PROTECTED] InfoWindow#addInfoWindowCloseClickHandler(InfoWindowCloseClickHandler)}
+ * .
*
* @param handler the handler to remove
*/
@@ -332,7 +357,8 @@
/**
* Removes a single handler of this map previously added with
- * [EMAIL PROTECTED] InfoWindow#addInfoWindowMaximizeClickHandler(InfoWindowMaximizeClickHandler)}.
+ * [EMAIL PROTECTED] InfoWindow#addInfoWindowMaximizeClickHandler(InfoWindowMaximizeClickHandler)}
+ * .
*
* @param handler the handler to remove
*/
@@ -346,7 +372,8 @@
/**
* Removes a single handler of this map previously added with
- * [EMAIL PROTECTED] InfoWindow#addInfoWindowMaximizeEndHandler(InfoWindowMaximizeEndHandler)}.
+ * [EMAIL PROTECTED] InfoWindow#addInfoWindowMaximizeEndHandler(InfoWindowMaximizeEndHandler)}
+ * .
*
* @param handler the handler to remove
*/
@@ -359,7 +386,8 @@
/**
* Removes a single handler of this map previously added with
- * [EMAIL PROTECTED] InfoWindow#addInfoWindowRestoreClickHandler(InfoWindowRestoreClickHandler)}.
+ * [EMAIL PROTECTED] InfoWindow#addInfoWindowRestoreClickHandler(InfoWindowRestoreClickHandler)}
+ * .
*
* @param handler the handler to remove
*/
@@ -370,11 +398,10 @@
}
}
- // TODO(zundel): Implement reset?
-
/**
* Removes a single handler of this map previously added with
- * [EMAIL PROTECTED] InfoWindow#addInfoWindowRestoreEndHandler(InfoWindowRestoreEndHandler)}.
+ * [EMAIL PROTECTED] InfoWindow#addInfoWindowRestoreEndHandler(InfoWindowRestoreEndHandler)}
+ * .
*
* @param handler the handler to remove
*/
@@ -387,7 +414,8 @@
/**
* Restores the info window to its default (non-maximized) state. The
- * infowindow must have been opened with maxContent or maxTitle options
+ * [EMAIL PROTECTED] InfoWindow} must have been opened with maxContent or maxTitle
+ * options
*/
public void restore() {
InfoWindowImpl.impl.restore(jsoPeer);
@@ -403,6 +431,8 @@
InfoWindowImpl.impl.selectTab(jsoPeer, index);
}
+ // TODO(zundel): Implement reset?
+
/**
* Enables or disables maximization of the info window. A maximizable info
* window expands to fill most of the map with contents specified via the
@@ -412,7 +442,7 @@
* maxTitle will have maximization enabled by default.
*
* Note that if the info window is currently opened and this method is set to
- * disable maximizing, this function will remove the maximize buton but will
+ * disable maximizing, this function will remove the maximize button but will
* not restore the window to its minimized state.
*/
public void setMaximizeEnabled(boolean enabled) {
@@ -495,30 +525,147 @@
infoWindowRestoreEndHandlers.trigger();
}
- private void beginAttach(InfoWindowContent content) {
- List<Widget> contentWidgets = content.getWidgets();
- if (content.getMaxContent() != null) {
- contentWidgets.add(content.getMaxContent());
+ /**
+ * Adds handlers that take care of attaching and detaching widgets from the
+ * hierarchy.
+ *
+ * @param content contains the widgets to attach/detach.
+ */
+ private void addAttachHandlers(InfoWindowContent content) {
+
+ // Make a private copy of the widgets passed in from the content object
+ // so no one can pull the rug out from under our attach handlers.
+ contentWidgets = content.getWidgets();
+ contentMaxWidget = content.getMaxContent();
+
+ attachInfoWindowOpenHandler = new MapInfoWindowOpenHandler() {
+
+ public void onInfoWindowOpen(MapInfoWindowOpenEvent event) {
+ finishAttach();
+ }
+
+ };
+ map.addInfoWindowOpenHandler(attachInfoWindowOpenHandler);
+
+ addMaxContentHandlers();
+ addCloseHandler();
+
+ // Everything is setup. Go ahead and start to attach the widgets for the
+ // restored view.
+ beginAttach();
+ }
+
+ /**
+ * Removes the handlers used to attach / detach widgets.
+ */
+ private void addCloseHandler() {
+
+ attachMapInfoWindowCloseHandler = new MapInfoWindowCloseHandler() {
+
+ public void onInfoWindowClose(MapInfoWindowCloseEvent event) {
+
+ if (contentWidgetsAttached) {
+ removeContentWidgets();
+ contentWidgetsAttached = false;
+ }
+ if (attachRestoreClickHandler != null) {
+ removeInfoWindowRestoreClickHandler(attachRestoreClickHandler);
+ attachRestoreClickHandler = null;
+ }
+ if (attachRestoreEndHandler != null) {
+ removeInfoWindowRestoreEndHandler(attachRestoreEndHandler);
+ attachRestoreEndHandler = null;
+ }
+ if (attachMaximizeEndHandler != null) {
+ removeInfoWindowMaximizeEndHandler(attachMaximizeEndHandler);
+ attachMaximizeEndHandler = null;
+ }
+ if (attachMaximizeClickHandler != null) {
+ removeInfoWindowMaximizeClickHandler(attachMaximizeClickHandler);
+ attachMaximizeClickHandler = null;
+ }
+ if (attachInfoWindowOpenHandler != null) {
+ map.removeInfoWindowOpenHandler(attachInfoWindowOpenHandler);
+ attachInfoWindowOpenHandler = null;
+ }
+
+ // Remove this handler too.
+ if (attachMapInfoWindowCloseHandler != null) {
+ map.removeInfoWindowCloseHandler(attachMapInfoWindowCloseHandler);
+ attachMapInfoWindowCloseHandler = null;
+ }
+ }
+
+ };
+ map.addInfoWindowCloseHandler(attachMapInfoWindowCloseHandler);
+ }
+
+ /**
+ * When the info window is maximized and restored, the widgets need to be
+ * detached and re-attached from the 2 virtual panels, as they are detached
+ * and re-attached to the DOM.
+ */
+ private void addMaxContentHandlers() {
+ if (contentMaxWidget == null) {
+ return;
}
+
+ attachMaximizeClickHandler = new InfoWindowMaximizeClickHandler() {
+
+ public void onMaximizeClick(InfoWindowMaximizeClickEvent event) {
+ virtualMaximizedPanel.beginAttach(contentMaxWidget);
+ }
+
+ };
+ addInfoWindowMaximizeClickHandler(attachMaximizeClickHandler);
+
+ attachMaximizeEndHandler = new InfoWindowMaximizeEndHandler() {
+ public void onMaximizeEnd(InfoWindowMaximizeEndEvent event) {
+ virtualMaximizedPanel.finishAttach(contentMaxWidget);
+ contentWidgetsAttached = false;
+ removeContentWidgets();
+ }
+ };
+ addInfoWindowMaximizeEndHandler(attachMaximizeEndHandler);
+
+ attachRestoreClickHandler = new InfoWindowRestoreClickHandler() {
+
+ public void onRestoreClick(InfoWindowRestoreClickEvent event) {
+ virtualMaximizedPanel.remove(contentMaxWidget);
+ beginAttach();
+ }
+
+ };
+ addInfoWindowRestoreClickHandler(attachRestoreClickHandler);
+
+ attachRestoreEndHandler = new InfoWindowRestoreEndHandler() {
+
+ public void onRestoreEnd(InfoWindowRestoreEndEvent event) {
+ contentWidgetsAttached = false;
+ finishAttach();
+ }
+ };
+ addInfoWindowRestoreEndHandler(attachRestoreEndHandler);
+ }
+
+ /**
+ * Begins attaching the restored view widgets to the InfoWindow.
+ */
+ private void beginAttach() {
for (int i = 0; i < contentWidgets.size(); i++) {
- virtualPanel.beginAttach(contentWidgets.get(i));
+ virtualRestoredPanel.beginAttach(contentWidgets.get(i));
}
}
- private void finishAttach(InfoWindowContent content) {
- final List<Widget> contentWidgets = content.getWidgets();
+ /**
+ * Finish the attach step (must be done after elements are actually attached
+ * to the DOM).
+ */
+ private void finishAttach() {
for (int i = 0; i < contentWidgets.size(); i++) {
- virtualPanel.finishAttach(contentWidgets.get(i));
+ virtualRestoredPanel.finishAttach(contentWidgets.get(i));
}
-
- map.addInfoWindowCloseHandler(new MapInfoWindowCloseHandler() {
-
- public void onInfoWindowClose(MapInfoWindowCloseEvent event) {
- for (int i = 0; i < contentWidgets.size(); i++) {
- virtualPanel.remove(contentWidgets.get(i));
- }
- }
- });
+ contentWidgetsAttached = true;
}
/**
@@ -570,4 +717,10 @@
jsoPeer, MapEvent.RESTOREEND);
}
}
+
+ private void removeContentWidgets() {
+ for (int i = 0; i < contentWidgets.size(); i++) {
+ virtualRestoredPanel.remove(contentWidgets.get(i));
+ }
+ }
}