Public (stephen.haberman@gmail.com):

Fixes GWT issue 5419, Panels do not emit AttachEvent
http://code.google.com/p/google-web-toolkit/issues/detail?id=5419

Review by: rjrjr@google.com

Review at http://gwt-code-reviews.appspot.com/998801


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9062 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/Composite.java b/user/src/com/google/gwt/user/client/ui/Composite.java
index 4f2c795..c90cc4e 100644
--- a/user/src/com/google/gwt/user/client/ui/Composite.java
+++ b/user/src/com/google/gwt/user/client/ui/Composite.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.event.logical.shared.AttachEvent;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Event;
 
@@ -109,12 +110,14 @@
 
     // Call onLoad() directly, because we're not calling super.onAttach().
     onLoad();
+    AttachEvent.fire(this, true);
   }
 
   @Override
   protected void onDetach() {
     try {
       onUnload();
+      AttachEvent.fire(this, false);
     } finally {
       // We don't want an exception in user code to keep us from calling the
       // super implementation (or event listeners won't get cleaned up and
diff --git a/user/src/com/google/gwt/user/client/ui/Panel.java b/user/src/com/google/gwt/user/client/ui/Panel.java
index 6aa07c8..42515a7 100644
--- a/user/src/com/google/gwt/user/client/ui/Panel.java
+++ b/user/src/com/google/gwt/user/client/ui/Panel.java
@@ -176,26 +176,6 @@
   }
 
   /**
-   * A Panel's onLoad method will be called after all of its children are
-   * attached.
-   * 
-   * @see Widget#onLoad()
-   */
-  @Override
-  protected void onLoad() {
-  }
-
-  /**
-   * A Panel's onUnload method will be called before its children become
-   * detached themselves.
-   * 
-   * @see Widget#onLoad()
-   */
-  @Override
-  protected void onUnload() {
-  }
-
-  /**
    * <p>
    * This method must be called as part of the remove method of any Panel. It
    * ensures that the Widget's parent is cleared. This method should be called
diff --git a/user/src/com/google/gwt/user/client/ui/Widget.java b/user/src/com/google/gwt/user/client/ui/Widget.java
index c80a25a..e0138a0 100644
--- a/user/src/com/google/gwt/user/client/ui/Widget.java
+++ b/user/src/com/google/gwt/user/client/ui/Widget.java
@@ -290,7 +290,7 @@
    * <p>
    * This method is called when a widget is attached to the browser's document.
    * To receive notification after a Widget has been added to the document,
-   * override the {@link #onLoad} method.
+   * override the {@link #onLoad} method or use {@link #addAttachHandler}.
    * </p>
    * <p>
    * It is strongly recommended that you override {@link #onLoad()} or
@@ -328,13 +328,14 @@
     // the attached flag is set. This allows widgets to be notified when they
     // are fully attached, and panels when all of their children are attached.
     onLoad();
+    AttachEvent.fire(this, true);
   }
 
   /**
    * <p>
    * This method is called when a widget is detached from the browser's
    * document. To receive notification before a Widget is removed from the
-   * document, override the {@link #onUnload} method.
+   * document, override the {@link #onUnload} method or use {@link #addAttachHandler}.
    * </p>
    * <p>
    * It is strongly recommended that you override {@link #onUnload()} or
@@ -363,6 +364,7 @@
       // onUnload() gets called *before* everything else (the opposite of
       // onLoad()).
       onUnload();
+      AttachEvent.fire(this, false);
     } finally {
       // Put this in a finally, just in case onUnload throws an exception.
       try {
@@ -377,22 +379,16 @@
 
   /**
    * This method is called immediately after a widget becomes attached to the
-   * browser's document. This default implementation notifies the widgets
-   * {@link com.google.gwt.event.logical.shared.AttachEvent.Handler
-   * AttachEvent.Handler}s.
+   * browser's document.
    */
   protected void onLoad() {
-    AttachEvent.fire(this, true);
   }
 
   /**
    * This method is called immediately before a widget will be detached from the
-   * browser's document. This default implementation notifies the widget's
-   * {@link com.google.gwt.event.logical.shared.AttachEvent.Handler
-   * AttachEvent.Handler}s.
+   * browser's document.
    */
   protected void onUnload() {
-    AttachEvent.fire(this, false);
   }
 
   /**
diff --git a/user/test/com/google/gwt/user/client/ui/CompositeTest.java b/user/test/com/google/gwt/user/client/ui/CompositeTest.java
index 37abcf8..902c56c 100644
--- a/user/test/com/google/gwt/user/client/ui/CompositeTest.java
+++ b/user/test/com/google/gwt/user/client/ui/CompositeTest.java
@@ -19,6 +19,7 @@
 import com.google.gwt.event.dom.client.BlurHandler;
 import com.google.gwt.event.dom.client.FocusEvent;
 import com.google.gwt.event.dom.client.FocusHandler;
+import com.google.gwt.event.logical.shared.AttachEvent;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.Command;
 import com.google.gwt.user.client.DOM;
@@ -30,6 +31,8 @@
  */
 public class CompositeTest extends GWTTestCase {
 
+  static int orderIndex;
+
   @Override
   public String getModuleName() {
     return "com.google.gwt.user.User";
@@ -132,4 +135,43 @@
    */
   public void testNothing() {
   }
+
+  public void testAttachAndDetachOrder() {
+    class TestAttachHandler implements AttachEvent.Handler {
+      int delegateAttachOrder;
+      int delegateDetachOrder;
+
+      public void onAttachOrDetach(AttachEvent event) {
+        if (event.isAttached()) {
+          delegateAttachOrder = ++orderIndex;
+        } else {
+          delegateDetachOrder = ++orderIndex;
+        }
+      }
+    }
+
+    class TestComposite extends Composite {
+      TextBox tb = new TextBox();
+
+      public TestComposite() {
+        initWidget(tb);
+      }
+    }
+
+    TestComposite c = new TestComposite();
+    TestAttachHandler ca = new TestAttachHandler();
+    TestAttachHandler wa = new TestAttachHandler();
+
+    c.addAttachHandler(ca);
+    c.tb.addAttachHandler(wa);
+
+    RootPanel.get().add(c);
+    RootPanel.get().remove(c);
+
+    assertTrue(ca.delegateAttachOrder > 0);
+    assertTrue(ca.delegateDetachOrder > 0);
+    assertTrue(ca.delegateAttachOrder > wa.delegateAttachOrder);
+    assertTrue(ca.delegateDetachOrder < wa.delegateDetachOrder);
+  }
+
 }
diff --git a/user/test/com/google/gwt/user/client/ui/WidgetOnLoadTest.java b/user/test/com/google/gwt/user/client/ui/WidgetOnLoadTest.java
index 51293de..dc6170f 100644
--- a/user/test/com/google/gwt/user/client/ui/WidgetOnLoadTest.java
+++ b/user/test/com/google/gwt/user/client/ui/WidgetOnLoadTest.java
@@ -108,50 +108,68 @@
 
   public void testOnLoadAndUnloadOrder() {
     class TestAttachHandler implements AttachEvent.Handler {
-      int delegateAttachOrder;
-      int delegateDetachOrder;
+      int handleAttachOrder;
+      int handleDetachOrder;
 
       public void onAttachOrDetach(AttachEvent event) {
         if (event.isAttached()) {
-          delegateAttachOrder = ++orderIndex;
+          handleAttachOrder = ++orderIndex;
         } else {
-          delegateDetachOrder = ++orderIndex;
+          handleDetachOrder = ++orderIndex;
         }
       }
     }
 
     TestPanel tp = new TestPanel();
+    TestAttachHandler tpa = new TestAttachHandler();
+    tp.addAttachHandler(tpa);
+
     TestWidget tw = new TestWidget();
-    TestAttachHandler ta = new TestAttachHandler();
-    tw.addAttachHandler(ta);
+    TestAttachHandler twa = new TestAttachHandler();
+    tw.addAttachHandler(twa);
 
     tp.add(tw);
     RootPanel.get().add(tp);
     RootPanel.get().remove(tp);
 
-    // Trivial tests. Ensure that each panel/widget's onAttach/onDetach are
-    // called before their associated onLoad/onUnload.
+    /*
+     * Ensure that each panel/widget's onAttach/onDetach are called before their
+     * associated onLoad/onUnload, and before attach events are fired
+     */
     assertTrue(tp.onAttachOrder < tp.onLoadOrder);
     assertTrue(tp.onDetachOrder < tp.onUnloadOrder);
     assertTrue(tw.onAttachOrder < tw.onLoadOrder);
-    assertTrue(tw.onLoadOrder < ta.delegateAttachOrder);
+    assertTrue(tw.onLoadOrder < twa.handleAttachOrder);
+    assertTrue(tp.onLoadOrder < tpa.handleAttachOrder);
     assertTrue(tw.onDetachOrder < tw.onUnloadOrder);
-    assertTrue(tw.onUnloadOrder < ta.delegateDetachOrder);
+    assertTrue(tw.onUnloadOrder < twa.handleDetachOrder);
+    assertTrue(tp.onUnloadOrder < tpa.handleDetachOrder);
 
-    // Also trivial. Ensure that the panel's onAttach/onDetach is called before
-    // its child's onAttach/onDetach.
+    /*
+     * Ensure that the panel's onAttach/onDetach is called before its child's
+     * onAttach/onDetach.
+     */
     assertTrue(tp.onAttachOrder < tw.onAttachOrder);
+    assertTrue(tp.onDetachOrder < tw.onDetachOrder);
 
-    // Ensure that the panel's onLoad is only called after its widgets are
-    // attached/loaded.
+    /*
+     * Ensure that the panel's onLoad is only called after its widgets are
+     * attached/loaded, and likewise for the attach event listeners
+     */
     assertTrue(tp.onLoadOrder > tw.onLoadOrder);
+    assertTrue(tpa.handleAttachOrder > twa.handleAttachOrder);
 
-    // Ensure that the panel's onUnload is called before its widgets are
-    // detached/unloaded.
+    /*
+     * Ensure that the panel's onUnload is called before its widgets are
+     * detached/unloaded.
+     */
     assertTrue(tp.onUnloadOrder < tw.onUnloadOrder);
+    assertTrue(tpa.handleDetachOrder < twa.handleDetachOrder);
 
-    // Make sure each widget/panel's elements are actually still attached to the
-    // DOM during onLoad/onUnload.
+    /*
+     * Make sure each widget/panel's elements are actually still attached to the
+     * DOM during onLoad/onUnload.
+     */
     assertTrue(tp.domAttachedOnLoad);
     assertTrue(tp.domAttachedOnUnload);
     assertTrue(tw.domAttachedOnLoad);