Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
RE: [albireo-dev] avoiding layout flickering

This change sounds reasonable, but I think we need a name different from getLayoutableAncestor(). Even getLayoutAncestor() would be preferable to me.

I'm glad that the preferredSizeChanged() option is still available for more advanced cases. Now that the method is no longer abstract, I think the advanced case would work even better with an event and listener. Events are more flexible because listeners can be added and removed as needed, and separately from the SwingControl subclass implementation. I've committed a SizeEvent and SizeListener. I'll commit an example that uses them tomorrow. With the event in place we really don't need the ability to override the preferredSizeChanged() method as well. If it's useful to the SwingControl implementation, then fine, but I think all the advanced cases can use the event.

________________________________________
From: albireo-dev-bounces@xxxxxxxxxxx [albireo-dev-bounces@xxxxxxxxxxx] On Behalf Of Bruno Haible [haible@xxxxxxx]
Sent: Wednesday, February 13, 2008 4:16 PM
To: albireo-dev@xxxxxxxxxxx
Subject: [albireo-dev] avoiding layout flickering

Hi Gordon,

The RelayoutExample shows a flickering effect that comes from SWT Layout:
The SWT panel containing the SwingControl relayouts itself after the latter
has determined its size, and the SwingControl's contents also relayouts itself
once initially. Buttons and labels are shifted around. It makes it look
unprofessional.

I've found a method setLayoutDeferred(), which does the trick. It requires
to generalize the preferredSizeChanged() method; I'm replacing it with a
getLayoutableAncestor() method.

Do you think this should be customizable (ability to turn off), or is the
result now always satisfactory?

Bruno


### Eclipse Workspace Patch 1.0
#P org.eclipse.albireo.core
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.33
diff -u -r1.33 SwingControl.java
--- src/org/eclipse/albireo/core/SwingControl.java	14 Feb 2008 03:44:39 -0000	1.33
+++ src/org/eclipse/albireo/core/SwingControl.java	14 Feb 2008 05:23:58 -0000
@@ -16,6 +16,9 @@
 import java.awt.EventQueue;
 import java.awt.Frame;
 import java.awt.Toolkit;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
 
 import javax.swing.JApplet;
 import javax.swing.JComponent;
@@ -486,6 +489,7 @@
                             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;
                         }
@@ -609,36 +613,40 @@
      * changes of this control. It is usually on this ancestor control that
      * you want to call <code>layout()</code> when the preferred size of this
      * control has changed.
+     *  
      * @return the parent, grandparent, or other ancestor of this control, or
      *         <code>null</code>
      * @see #preferredSizeChanged(Point, Point, Point)
      */
     public abstract Composite getLayoutableAncestor();
 
+	// TODO: remove this method and just leave the listener for advanced users?
     /**
      * Called when the preferred sizes of this control, as computed by
-     * {@link #computeSize(int, int, boolean)}, have changed. This method
-     * should change the size of this SWT control and of other SWT controls
+     * AWT, have changed. This method 
+     * should update the size of this SWT control and of other SWT controls
      * in the window (as appropriate).
      * <p>
+     * This method is a more flexible alternative to {@link #getLayoutableAncestor()}. 
+     * You should implement that method to return null if you are overriding this method.
+     * A still more flexible way to be informed of preferred size changes is to install
+     * a {@link SizeListener} with {@link #addSizeListener(SizeListener)}. 
+     * <p>
      * This method is part of the size propagation from AWT to SWT.
      * It is called on the SWT event thread.
      * <p>
-     * You should implement this method, usually by calling
-     * <code>layout()</code> on the uppermost parent of this control that
-     * is influenced by size changes of this control. Usually this is done
-     * through a call <code>getLayoutableAncestor().layout()</code>.
-     * The default implementation calls
-     * <code>getLayoutableAncestor().layout()</code>.
+     * The default implementation of this method calls
+     * <code>getLayoutableAncestor().layout()</code>, if getLayoutableAncestor() returns
+     * a non-null. Otherwise, it does nothing. 
      * <p>
      * The parameters <var>minPoint</var>, <var>prefPoint</var>,
-     * <var>maxPoint</var> can usually be ignored: It is enough to rely on the
-     * {@link #computeSize(int, int, boolean)} method.
+     * <var>maxPoint</var> can usually be ignored: It is often enough to rely on the
+     * {@link #layout()} method.
      * @param minSize The new minimum size for this control, as reported by AWT.
      * @param prefSize The new preferred size for this control, as reported by AWT.
      * @param maxSize The new maximum size for this control, as reported by AWT.
      */
-    public void preferredSizeChanged(Point minSize, Point prefSize, Point maxSize) {
+    protected void preferredSizeChanged(Point minSize, Point prefSize, Point maxSize) {
         Composite ancestor = getLayoutableAncestor();
         if (ancestor != null)
             ancestor.layout();
@@ -841,4 +849,74 @@
             }
         }
     }
+    // ============================= Events and Listeners =============================
+
+    private List sizeListeners = new ArrayList();
+    
+    /**
+     * Adds the listener to the collection of listeners who will
+     * be notified when the embedded Swing control has changed its size
+     * preferences. 
+     *
+     * @param listener the listener which should be notified
+     *
+     * @exception IllegalArgumentException <ul>
+     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+     * </ul>
+     * @exception SWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+     * </ul>
+     *
+     * @see SizeListener
+     * @see #removeSizeListener(SizeListener)
+     */
+    public void addSizeListener(SizeListener listener) {
+        checkWidget();
+        if (listener == null) {
+            SWT.error(SWT.ERROR_NULL_ARGUMENT);
+        }
+        sizeListeners.add(listener);
+    }
+    
+    /**
+     * Removes the listener from the collection of listeners who will
+     * be notified when the embedded swing control has changed its size
+     * preferences. 
+     *
+     * @param listener the listener which should no longer be notified
+     *
+     * @exception IllegalArgumentException <ul>
+     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+     * </ul>
+     * @exception SWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+     * </ul>
+     *
+     * @see SizeListener
+     * @see #addSizeListener(SizeListener)
+     */
+    public void removeSizeListener(SizeListener listener) {
+        checkWidget();
+        if (listener == null) {
+            SWT.error(SWT.ERROR_NULL_ARGUMENT);
+        }
+        sizeListeners.remove(listener);
+    }
+
+    protected void firePreferredSizeChangedEvent(Point minPoint, Point prefPoint, Point maxPoint) {
+        assert Display.getCurrent() != null;     // On SWT event thread
+        SizeEvent event = new SizeEvent(this, minPoint, prefPoint, maxPoint);
+        for (Iterator iterator = sizeListeners.iterator(); iterator.hasNext();) {
+            SizeListener listener = (SizeListener)iterator.next();
+            listener.preferredSizeChanged(event);
+        }
+    }
+    
+    public String toString() {
+        return super.toString() + " [frame=" + ((frame != null) ? frame.getName() : "null") + "]";
+    }
+    
+    
 }
Index: src/org/eclipse/albireo/core/SizeListener.java
===================================================================
RCS file: src/org/eclipse/albireo/core/SizeListener.java
diff -N src/org/eclipse/albireo/core/SizeListener.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/albireo/core/SizeListener.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,17 @@
+package org.eclipse.albireo.core;
+
+import java.util.EventListener;
+
+/**
+ * A listener that is notified on events related to 
+ * the size of the embedded Swing control 
+ */
+public interface SizeListener extends EventListener {
+    /**
+     * See {@link SwingControl#preferredSizeChanged(org.eclipse.swt.graphics.Point, org.eclipse.swt.graphics.Point, org.eclipse.swt.graphics.Point)}
+     * for information on when this method is called. 
+     * 
+     * @param event
+     */
+    void preferredSizeChanged(SizeEvent event);
+}
Index: src/org/eclipse/albireo/core/SizeEvent.java
===================================================================
RCS file: src/org/eclipse/albireo/core/SizeEvent.java
diff -N src/org/eclipse/albireo/core/SizeEvent.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/albireo/core/SizeEvent.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,62 @@
+package org.eclipse.albireo.core;
+
+import java.util.EventObject;
+
+import org.eclipse.swt.graphics.Point;
+
+/**
+ * Instances of this class are sent as a result of
+ * size changes for an embedded Swing control
+ *
+ * @see SizeListener
+ */
+public class SizeEvent extends EventObject {
+    /** the minimum size for this control, as reported by AWT. */
+    public final Point minimum;
+    /** the preferred size for this control, as reported by AWT. */
+    public final Point preferred;
+    /** the maximum size for this control, as reported by AWT. */
+    public final Point maximum;
+    
+    public SizeEvent(SwingControl source, Point min, Point pref, Point max) {
+        super(source);
+        minimum = min;
+        preferred = pref;
+        maximum = max;
+    }
+    
+    /**
+     * Returns the SwingControl that is the source of this event
+     * @return SwingControl 
+     */
+    public SwingControl getSwingControl() {
+        return (SwingControl)source;
+    }
+    
+    /**
+     * Returns the name of the event. This is the name of
+     * the class without the package name.
+     *
+     * @return the name of the event
+     */
+    protected String getName () {
+        String string = getClass ().getName ();
+        int index = string.lastIndexOf ('.');
+        if (index == -1) return string;
+        return string.substring (index + 1, string.length ());
+    }
+
+    /**
+     * Returns a string containing a concise, human-readable
+     * description of the receiver.
+     *
+     * @return a string representation of the event
+     */
+    public String toString() {
+        return getName ()
+            + "{" + source //$NON-NLS-1$
+            + " min=" + minimum //$NON-NLS-1$
+            + " pref=" + preferred //$NON-NLS-1$
+            + " max=" + maximum //$NON-NLS-1$
+            + "}"; //$NON-NLS-1$
+    }}

Back to the top