Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [albireo-dev] size/layout management

> If you open the "Relayout Example" view and click the "grow" 
> button a few times, you'll see that the label gets truncated and the 
> "..." characters are shown. Originally, the label would be properly 
> resized, at least until the containing GridLayout ran out of space.

If in the example, I change

        composite.getDisplay().asyncExec(
            new Runnable() {
                public void run() {
                    composite.layout();
                }
            });

to

        composite.getDisplay().asyncExec(
            new Runnable() {
                public void run() {
                    composite.layout();
                    SwingUtilities.invokeLater(
                        new Runnable() {
                            public void run() {
                                composite.getDisplay().asyncExec(
                                    new Runnable() {
                                        public void run() {
                                            composite.layout();
                                        }
                                    });
                            }
                        });
                }
            });

the results look fine. The reason is the following sequence of calls:
  1. JLabel.setText() calls revalidate(), which only sets a mark bit but
     does not recompute sizes, and posts an invokeLater to do that.
  2. composite.layout() asks the SwingControl for its preferred size.
     This returns the old size. Then layout calls setBounds, which causes
     a call to setAWTSize(160,135).
  3. Then the posted invokeLater is executed. The new label size is computed.
  4. The second call to composite.layout() runs, retrieves the new, corrected
     size; then it calls setBounds, which causes a call to setAWTSize(190,135).

You were going in the right direction with the RepaintManager. But I prefer
to hook into the JApplet's validateTree() call because
  - The default RepaintManager "collects" all invalidation requests, in
    order to minimize the validate() calls. For example, when someone does
       label1.setText(string1);
       label2.setText(string2);
    then, assuming label1 and label2 have common "validation root", the
    validate() call on this validation root will be executed only once.
    (That's also the reason why it does an invokeLater: Swing doesn't have
    startModifyingLayout()/endModifyingLayout() calls. Instead, all calls
    in a single AWT-Event handling are considered all together.)
    We lose this optimization if we hook into
    RepaintManager.addInvalidComponent.
  - When a programmer does
      validateRoot.invalidate(); validateRoot.validate();
    the RepaintManager is not involved at all.

This patch fixes the "Relayout Example". All it does is to trigger a
composite.layout() call after the validate() call, when the sizes have
changed.

Bruno


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.19
diff -c -3 -r1.19 SwingControl.java
*** src/org/eclipse/albireo/core/SwingControl.java	7 Feb 2008 19:02:23 -0000	1.19
--- src/org/eclipse/albireo/core/SwingControl.java	8 Feb 2008 20:06:17 -0000
***************
*** 328,335 ****
--- 328,345 ----
      private Dimension cachedMinSize = new Dimension(0,0);
      private Dimension cachedPrefSize = new Dimension(0,0);
      private Dimension cachedMaxSize = new Dimension(0,0);
+     // Since the swingComponent is not already initialized in the constructor,   
+     // this control initially has no notion of what its preferred size could
+     // be.
      private boolean cachedSizesInitialized = false;
  
+     // 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
       * component, this method stores them in the cache and updates
***************
*** 338,353 ****
      protected void updateCachedAWTSizes(Dimension min, Dimension pref, Dimension max) {
          assert EventQueue.isDispatchThread();    // On AWT event thread
          // System.out.println("Updated component sizes from AWT: " + min + " <= " + pref + " <= " + max);
!         boolean firstSizeUpdate = !cachedSizesInitialized;
  
          synchronized (this) {
              cachedSizesInitialized = true;
              cachedMinSize = min;
              cachedPrefSize = pref;
              cachedMaxSize = max;
          }
  
!         if (firstSizeUpdate) {
              // 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.
--- 348,369 ----
      protected void updateCachedAWTSizes(Dimension min, Dimension pref, Dimension max) {
          assert EventQueue.isDispatchThread();    // On AWT event thread
          // System.out.println("Updated component sizes from AWT: " + min + " <= " + pref + " <= " + max);
!         boolean mustNotify;
  
          synchronized (this) {
+             mustNotify = !cachedSizesInitialized;
+             if (!mustNotify) {
+                 mustNotify = !(min.equals(cachedMinSize)
+                                && pref.equals(cachedPrefSize)
+                                && max.equals(cachedMaxSize));
+             }
              cachedSizesInitialized = true;
              cachedMinSize = min;
              cachedPrefSize = pref;
              cachedMaxSize = max;
          }
  
!         if (mustNotify) {
              // 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.
***************
*** 355,361 ****
                  public void run() {
                      // System.out.println("Laying out after first size update");
                      if (!isDisposed()) {
!                         getParent().layout();
                      }
                  }
              });
--- 371,382 ----
                  public void run() {
                      // System.out.println("Laying out after first size update");
                      if (!isDisposed()) {
!                         try {
!                             inhibitSizePropagationToAWT = true;
!                             getParent().layout();
!                         } finally {
!                             inhibitSizePropagationToAWT = false;
!                         }
                      }
                  }
              });
***************
*** 410,416 ****
              // since computeSize was called. If so, we'll be passing a bad size to the embedded
              // component. This is not a big problem because the component will be resized again 
              // very quickly (with, possibly, some flicker). 
!             if (cachedSizesInitialized) {
                  setAWTSize(width, height);
              }
          }
--- 431,437 ----
              // since computeSize was called. If so, we'll be passing a bad size to the embedded
              // component. This is not a big problem because the component will be resized again 
              // very quickly (with, possibly, some flicker). 
!             if (cachedSizesInitialized && !inhibitSizePropagationToAWT) {
                  setAWTSize(width, height);
              }
          }


Back to the top