[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [albireo-dev] Updated RelayoutExampleView

Gordon Hirsch wrote:
> There's some odd behavior with the new example that needs some 
> investigation. Not sure if it is a bug in the example, strange SWT 
> behavior, or a bug in albireo.
> 
> For example, growing both controls at once is not always as symmetrical 
> as I would have expected.

While thinking about this issue, I also noticed a thinko regarding the
inhibitSizePropagationToAWT mechanism. Namely, it did not consider the
case that an SWT layout operation, on behalf of a SwingControl, could
set the size of other SwingControl. But this is precisely what happens in
the new RelayoutExampleView.

The fix is to generalize inhibitSizePropagationToAWT as follows.

Index: src/org/eclipse/albireo/core/SwingControl.java
===================================================================
RCS file: /cvsroot/technology/org.eclipse.albireo/org.eclipse.albireo.core/src/org/eclipse/albireo/core/SwingControl.java,v
retrieving revision 1.39
diff -c -3 -r1.39 SwingControl.java
*** src/org/eclipse/albireo/core/SwingControl.java	25 Feb 2008 19:33:47 -0000	1.39
--- src/org/eclipse/albireo/core/SwingControl.java	25 Feb 2008 20:07:37 -0000
***************
*** 17,24 ****
--- 17,27 ----
  import java.awt.Frame;
  import java.awt.Toolkit;
  import java.util.ArrayList;
+ import java.util.Collections;
+ import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
+ import java.util.Map;
  
  import javax.swing.JApplet;
  import javax.swing.JComponent;
***************
*** 435,443 ****
      // We have bidirectional size propagation, from AWT to SWT, and from
      // SWT to AWT. To avoid endless recursion in weird cases (for example,
      // when the AWT component refuses to accept the size that the SWT
!     // component assigns it), this variable is used. It is accessed only
!     // from the SWT event thread.
!     boolean inhibitSizePropagationToAWT = false;
  
      /**
       * Given the minimum, preferred, and maximum sizes of the Swing
--- 438,485 ----
      // We have bidirectional size propagation, from AWT to SWT, and from
      // SWT to AWT. To avoid endless recursion in weird cases (for example,
      // when the AWT component refuses to accept the size that the SWT
!     // component assigns it), we inhibit propagation in this situation:
!     // AWT rootpane.validate() ---> SWT layout() -|->  AWT frame.setBounds.
!     //
!     // When more than one SwingControl is involved, the situation is more
!     // complicated:
!     // AWT rootpane1.validate() ---> SWT layout() -|->  AWT frame1.setBounds.
!     //                                            --->  AWT frame2.setBounds.
!     // The notification from SWT to the AWT frame that triggered the layout is
!     // inhibited. The notification from SWT to another AWT frame frame2 is
!     // passed through, however - except if frame2 has been validated since
!     // then. In the latter case, the SWT layout potentially used outdated
!     // sizes from frame2; it *must*not* clobber the new size of frame2.
!     //
!     // In order to determine the "since then", we need a clock that runs in the
!     // AWT thread.
! 
!     /**
!      * The current "time" of the AWT thread. It is incremented when any
!      * SwingControl is validated. It's an integer modulo 2^32 (wraps around).
!      * Accessed only from the AWT thread. Therefore copies of this integer
!      * have to be compared with  <code>a - b < 0</code>  rather than
!      * <code>a < b</code>.
!      */
!     private static int currentAWTTime;
! 
!     /**
!      * The time at which this component's JRootPane was last validated.
!      * Accessed from the AWT thread and the SWT thread, therefore 'volatile'
!      * (could also be protected by a lock).
!      */
!     volatile int lastValidatedAWTTime;
! 
!     /**
!      * When running in the SWT thread on behalf of a notification from the AWT
!      * thread, this variable keeps track of the AWT-time that was in effect
!      * when this notification was sent.
!      * The map key is an SWT thread, belonging to a Display. There can be
!      * several of them, therefore a Map.
!      * Accessed only from the SWT thread(s).
!      */
!     static Map /*<Thread,Integer>*/ onBehalfAWTTimes =
!         Collections.synchronizedMap(new HashMap /*<Thread,Integer>*/ ());
  
      /**
       * Given the minimum, preferred, and maximum sizes of the Swing
***************
*** 448,453 ****
--- 490,499 ----
          assert EventQueue.isDispatchThread();    // On AWT event thread
          if (verboseSizeLayout)
              System.err.println("AWT thread: updated component sizes: " + min + " <= " + pref + " <= " + max);
+ 
+         // Increment and memoize the current AWT time.
+         lastValidatedAWTTime = ++currentAWTTime;
+ 
          boolean mustNotify;
  
          synchronized (this) {
***************
*** 476,495 ****
              // Preferred (and min/max) sizes are available for the AWT
              // component for the first time. Layout the composite so that those
              // sizes can be taken into account.
              ThreadingHandler.getInstance().asyncExec(display, new Runnable() {
                  public void run() {
                      if (verboseSizeLayout)
                          System.err.println("AWT->SWT thread: Laying out after size update");
                      if (!isDisposed()) {
                          try {
!                             inhibitSizePropagationToAWT = true;
                              Point minPoint = new Point(min.width, min.height);
                              Point prefPoint = new Point(pref.width, pref.height);
                              Point maxPoint = new Point(max.width, max.height);
                              preferredSizeChanged(minPoint, prefPoint, maxPoint);
                              firePreferredSizeChangedEvent(minPoint, prefPoint, maxPoint);
                          } finally {
!                             inhibitSizePropagationToAWT = false;
                          }
                      }
                  }
--- 522,542 ----
              // Preferred (and min/max) sizes are available for the AWT
              // component for the first time. Layout the composite so that those
              // sizes can be taken into account.
+             final int onBehalfAWTTime = lastValidatedAWTTime;
              ThreadingHandler.getInstance().asyncExec(display, new Runnable() {
                  public void run() {
                      if (verboseSizeLayout)
                          System.err.println("AWT->SWT thread: Laying out after size update");
                      if (!isDisposed()) {
                          try {
!                             onBehalfAWTTimes.put(Thread.currentThread(), new Integer(onBehalfAWTTime));
                              Point minPoint = new Point(min.width, min.height);
                              Point prefPoint = new Point(pref.width, pref.height);
                              Point maxPoint = new Point(max.width, max.height);
                              preferredSizeChanged(minPoint, prefPoint, maxPoint);
                              firePreferredSizeChangedEvent(minPoint, prefPoint, maxPoint);
                          } finally {
!                             onBehalfAWTTimes.remove(Thread.currentThread());
                          }
                      }
                  }
***************
*** 523,539 ****
          assert Display.getCurrent() != null;     // On SWT event thread
          if (verboseSizeLayout)
              System.err.println("SWT thread: Preparing to set size: " + width + " x " + height + " for " + swingComponent);
!         EventQueue.invokeLater(
!             new Runnable() {
!                 public void run() {
!                     if (verboseSizeLayout)
!                         System.err.println("SWT->AWT thread: Setting size: " + width + " x " + height + " for " + swingComponent);
!                     if (frame != null) {
!                         frame.setBounds(0, 0, Math.max(width, 0), Math.max(height, 0));
!                         frame.validate();
                      }
!                 }
!             });
      }
  
      /**
--- 570,601 ----
          assert Display.getCurrent() != null;     // On SWT event thread
          if (verboseSizeLayout)
              System.err.println("SWT thread: Preparing to set size: " + width + " x " + height + " for " + swingComponent);
!         // Get the AWT time of the notification that triggered this processing.
!         final Integer onBehalfAWTTime = (Integer)onBehalfAWTTimes.get(Thread.currentThread());
!         // Shortcut to post a Runnable that has no effect.
!         // (lastValidatedAWTTime can only increase until the Runnable is
!         // actually run.)
!         if (onBehalfAWTTime == null
!             || lastValidatedAWTTime - onBehalfAWTTime.intValue() < 0) {
!             // Switch to the AWT thread.
!             EventQueue.invokeLater(
!                 new Runnable() {
!                     public void run() {
!                         // Compare the AWT time of the notification with the
!                         // time at which the rootpane was last validated.
!                         if (onBehalfAWTTime == null
!                             || lastValidatedAWTTime - onBehalfAWTTime.intValue() < 0) {
!                             // Set the frame's (and thus also the rootpane's) size.
!                             if (verboseSizeLayout)
!                                 System.err.println("SWT->AWT thread: Setting size: " + width + " x " + height + " for " + swingComponent);
!                             if (frame != null) {
!                                 frame.setBounds(0, 0, Math.max(width, 0), Math.max(height, 0));
!                                 frame.validate();
!                             }
!                         }
                      }
!                 });
!         }
      }
  
      /**
***************
*** 564,572 ****
              if ((cachedSizesInitialized >= 2) || 
                      (Platform.isGtk() && Platform.JAVA_VERSION < Platform.javaVersion(1, 6, 0)) ||
                      (Platform.isWin32() && Platform.SWT_VERSION < Platform.SWT_34)) {
!                 if (!inhibitSizePropagationToAWT) {
!                     setAWTSize(width, height);
!                 }
              }
          }
      }
--- 626,632 ----
              if ((cachedSizesInitialized >= 2) || 
                      (Platform.isGtk() && Platform.JAVA_VERSION < Platform.javaVersion(1, 6, 0)) ||
                      (Platform.isWin32() && Platform.SWT_VERSION < Platform.SWT_34)) {
!                 setAWTSize(width, height);
              }
          }
      }