I have been working a little more with Espial/Espresso and some dummy
peers against the JDK, which is quite interesting and helps alot to get
our AWT core straight. Here's what I found and fixed:
- reshape() only needs to invalidate the component itself when it has
actually been resized. For moved-only components we only need to
invalidate the parent.
- Repainting in reshape() is now slightly more accurate.
- The paint() and update() methods have been cleared up. I did some more
tests against the JDK and hope it's alrighth now. Unfortunately I can't
think of an easy way to check this in Mauve (I basically examined
stacktraces that I dumped in plain Component and Container subclasses).
- repaint() doesn't call ComponentPeer.repaint() but instead does send
an UPDATE event itself. Also, it only sends events to heavyweight
components (I don't like the concept of a lightweight peer anyway) and
translates the rectangle accordingly. This should take a little burden
away from the peers, since this is peer-independent anyway.
I couldn't observe any regressions with that patch, so I think it's good
to have it in. Espial/Espresso looks quite good now :-) Unfortunately it
isn't free software, but still a nice block of AWT code to test
Classpath against.
2006-07-27 Roman Kennke <[EMAIL PROTECTED]>
* java/awt/Component.java
(reshape): Invalidate the component itself only when the
size has changed. Invalidate the parent always. Fixed
repainting. Pulled out the notification into
notifyReshape().
(notifyReshape): New helper method. Notify interested listeners
about a reshape.
(update): Simply call paint() without clearing the background.
This is done in Container.update() if appropriate.
(repaint): Delagate the repaint to the nearest heavyweight
parent (for lightweights) and send an UPDATE event, rather than
calling ComponentPeer.repaint().
* java/awt/Container.java
(backCleared): Removed field.
(paint): Removed handling of backCleared flag.
(update): Only paint if the container is actually
showing. Removed handling of backCleared flag.
/Roman
Index: java/awt/Component.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/awt/Component.java,v
retrieving revision 1.138
diff -u -1 -2 -r1.138 Component.java
--- java/awt/Component.java 26 Jul 2006 19:09:50 -0000 1.138
+++ java/awt/Component.java 27 Jul 2006 14:28:46 -0000
@@ -1488,97 +1488,104 @@
*/
public void reshape(int x, int y, int width, int height)
{
// We need to lock the tree here, otherwise we risk races and
// inconsistencies.
synchronized (getTreeLock())
{
int oldx = this.x;
int oldy = this.y;
int oldwidth = this.width;
int oldheight = this.height;
- if (this.x == x && this.y == y && this.width == width
- && this.height == height)
- return;
-
- invalidate();
-
- this.x = x;
- this.y = y;
- this.width = width;
- this.height = height;
- if (peer != null)
- peer.setBounds (x, y, width, height);
-
- // Erase old bounds and repaint new bounds for lightweights.
- if (isLightweight() && isShowing())
- {
- if (parent != null)
- {
- Rectangle oldBounds = new Rectangle(oldx, oldy, oldwidth,
- oldheight);
- Rectangle newBounds = new Rectangle(x, y, width, height);
- Rectangle destroyed = oldBounds.union(newBounds);
- if (!destroyed.isEmpty())
- parent.repaint(0, destroyed.x, destroyed.y, destroyed.width,
- destroyed.height);
- }
- }
-
boolean resized = oldwidth != width || oldheight != height;
boolean moved = oldx != x || oldy != y;
- // Only post an event if this component actually has a listener
- // or has this event explicitly enabled.
- if (componentListener != null
- || (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0)
+
+ if (resized || moved)
{
- // Fire component event on this component.
- if (moved)
+ // Update the fields.
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+
+ if (peer != null)
{
- ComponentEvent ce = new ComponentEvent(this,
- ComponentEvent.COMPONENT_MOVED);
- getToolkit().getSystemEventQueue().postEvent(ce);
+ peer.setBounds (x, y, width, height);
+ if (resized)
+ invalidate();
+ if (parent != null && parent.valid)
+ parent.invalidate();
}
- if (resized)
+
+ // Send some events to interested listeners.
+ notifyReshape(resized, moved);
+
+ // Repaint this component and the parent if appropriate.
+ if (parent != null && peer instanceof LightweightPeer
+ && isShowing())
{
- ComponentEvent ce = new ComponentEvent(this,
- ComponentEvent.COMPONENT_RESIZED);
- getToolkit().getSystemEventQueue().postEvent(ce);
+ // The parent repaints the area that we occupied before.
+ parent.repaint(oldx, oldy, oldwidth, oldheight);
+ // This component repaints the area that we occupy now.
+ repaint();
}
}
- else
+ }
+ }
+
+ private void notifyReshape(boolean resized, boolean moved)
+ {
+ // Only post an event if this component actually has a listener
+ // or has this event explicitly enabled.
+ if (componentListener != null
+ || (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0)
+ {
+ // Fire component event on this component.
+ if (moved)
{
- // Otherwise we might need to notify child components when this is
- // a Container.
- if (this instanceof Container)
+ ComponentEvent ce = new ComponentEvent(this,
+ ComponentEvent.COMPONENT_MOVED);
+ getToolkit().getSystemEventQueue().postEvent(ce);
+ }
+ if (resized)
+ {
+ ComponentEvent ce = new ComponentEvent(this,
+ ComponentEvent.COMPONENT_RESIZED);
+ getToolkit().getSystemEventQueue().postEvent(ce);
+ }
+ }
+ else
+ {
+ // Otherwise we might need to notify child components when this is
+ // a Container.
+ if (this instanceof Container)
+ {
+ Container cont = (Container) this;
+ if (resized)
{
- Container cont = (Container) this;
- if (resized)
+ for (int i = 0; i < cont.getComponentCount(); i++)
{
- for (int i = 0; i < cont.getComponentCount(); i++)
- {
- Component child = cont.getComponent(i);
- child.fireHierarchyEvent(HierarchyEvent.ANCESTOR_RESIZED,
- this, parent, 0);
- }
+ Component child = cont.getComponent(i);
+ child.fireHierarchyEvent(HierarchyEvent.ANCESTOR_RESIZED,
+ this, parent, 0);
}
- if (moved)
+ }
+ if (moved)
+ {
+ for (int i = 0; i < cont.getComponentCount(); i++)
{
- for (int i = 0; i < cont.getComponentCount(); i++)
- {
- Component child = cont.getComponent(i);
- child.fireHierarchyEvent(HierarchyEvent.ANCESTOR_MOVED,
- this, parent, 0);
- }
+ Component child = cont.getComponent(i);
+ child.fireHierarchyEvent(HierarchyEvent.ANCESTOR_MOVED,
+ this, parent, 0);
}
}
}
}
}
/**
* Sets the bounding rectangle for this component to the specified
* rectangle. Note that these coordinates are relative to the parent, not
* to the screen.
*
* @param r the new bounding rectangle
@@ -2141,57 +2148,45 @@
* are not painted.
*
* @param g the graphics context for this paint job
* @see #update(Graphics)
*/
public void paint(Graphics g)
{
// This is a callback method and is meant to be overridden by subclasses
// that want to perform custom painting.
}
/**
- * Updates this component. This is called in response to
- * <code>repaint</code>. This method fills the component with the
- * background color, then sets the foreground color of the specified
- * graphics context to the foreground color of this component and calls
- * the <code>paint()</code> method. The coordinates of the graphics are
+ * Updates this component. This is called for heavyweight components in
+ * response to [EMAIL PROTECTED] #repaint()}. The default implementation simply forwards
+ * to [EMAIL PROTECTED] #paint(Graphics)}. The coordinates of the graphics are
* relative to this component. Subclasses should call either
* <code>super.update(g)</code> or <code>paint(g)</code>.
*
* @param g the graphics context for this update
*
* @see #paint(Graphics)
* @see #repaint()
- *
- * @specnote In contrast to what the spec says, tests show that the exact
- * behaviour is to clear the background on lightweight and
- * top-level components only. Heavyweight components are not
- * affected by this method and only call paint().
*/
public void update(Graphics g)
{
- // Tests show that the clearing of the background is only done in
- // two cases:
- // - If the component is lightweight (yes this is in contrast to the spec).
- // or
- // - If the component is a toplevel container.
- if (isLightweight() || getParent() == null)
- {
- Rectangle clip = g.getClipBounds();
- if (clip == null)
- g.clearRect(0, 0, width, height);
- else
- g.clearRect(clip.x, clip.y, clip.width, clip.height);
- }
+ // Note 1: We used to clear the background here for lightweights and
+ // toplevel components. Tests show that this is not what the JDK does
+ // here. Note that there is some special handling and background
+ // clearing code in Container.update(Graphics).
+
+ // Note 2 (for peer implementors): The JDK doesn't seem call update() for
+ // toplevel components, even when an UPDATE event is sent (as a result
+ // of repaint).
paint(g);
}
/**
* Paints this entire component, including any sub-components.
*
* @param g the graphics context for this paint job
*
* @see #paint(Graphics)
*/
public void paintAll(Graphics g)
{
@@ -2249,29 +2244,64 @@
* approximately the specified number of milliseconds. The coordinates
* are relative to this component.
*
* @param tm milliseconds before this component should be repainted
* @param x the X coordinate of the upper left of the region to repaint
* @param y the Y coordinate of the upper left of the region to repaint
* @param width the width of the region to repaint
* @param height the height of the region to repaint
* @see #update(Graphics)
*/
public void repaint(long tm, int x, int y, int width, int height)
{
- if (isShowing())
- {
- ComponentPeer p = peer;
- if (p != null)
- p.repaint(tm, x, y, width, height);
+ // The repaint() call has previously been delegated to
+ // [EMAIL PROTECTED] ComponentPeer.repaint()}. Testing on the JDK using some
+ // dummy peers show that this methods is never called. I think it makes
+ // sense to actually perform the tasks below here, since it's pretty
+ // much peer independent anyway, and makes sure only heavyweights are
+ // bothered by this.
+ ComponentPeer p = peer;
+
+ // Let the nearest heavyweight parent handle repainting for lightweight
+ // components.
+ // This goes up the hierarchy until we hit
+ // a heavyweight component that handles this and translates the
+ // rectangle while doing so.
+
+ // We perform some boundary checking to restrict the paint
+ // region to this component.
+ int px = (x < 0 ? 0 : x);
+ int py = (y < 0 ? 0 : y);
+ int pw = width;
+ int ph = height;
+ Component par = this;
+ while (par != null && p instanceof LightweightPeer)
+ {
+ px += par.x;
+ py += par.y;
+ // We perform some boundary checking to restrict the paint
+ // region to this component.
+ pw = Math.min(pw, par.width);
+ ph = Math.min(ph, par.height);
+ par = par.parent;
+ p = par.peer;
+ }
+
+ // Now send an UPDATE event to the heavyweight component that we've found.
+ if (par != null && par.isVisible() && p != null && pw > 0 && ph > 0)
+ {
+ assert ! (p instanceof LightweightPeer);
+ PaintEvent pe = new PaintEvent(par, PaintEvent.UPDATE,
+ new Rectangle(px, py, pw, ph));
+ getToolkit().getSystemEventQueue().postEvent(pe);
}
}
/**
* Prints this component. This method is provided so that printing can be
* done in a different manner from painting. However, the implementation
* in this class simply calls the <code>paint()</code> method.
*
* @param g the graphics context of the print device
*
* @see #paint(Graphics)
*/
Index: java/awt/Container.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/awt/Container.java,v
retrieving revision 1.101
diff -u -1 -2 -r1.101 Container.java
--- java/awt/Container.java 25 Jul 2006 13:21:04 -0000 1.101
+++ java/awt/Container.java 27 Jul 2006 14:28:46 -0000
@@ -80,29 +80,24 @@
* Compatible with JDK 1.0+.
*/
private static final long serialVersionUID = 4613797578919906343L;
/* Serialized fields from the serialization spec. */
int ncomponents;
Component[] component;
LayoutManager layoutMgr;
Dimension maxSize;
/**
- * Keeps track if the Container was cleared during a paint/update.
- */
- private boolean backCleared;
-
- /**
* @since 1.4
*/
boolean focusCycleRoot;
/**
* Indicates if this container provides a focus traversal policy.
*
* @since 1.5
*/
private boolean focusTraversalPolicyProvider;
int containerSerializedDataVersion;
@@ -850,31 +845,28 @@
/**
* Paints this container. The implementation of this method in this
* class forwards to any lightweight components in this container. If
* this method is subclassed, this method should still be invoked as
* a superclass method so that lightweight components are properly
* drawn.
*
* @param g - The graphics context for this paint job.
*/
public void paint(Graphics g)
{
- if (!isShowing())
- return;
-
- // Visit heavyweights if the background was cleared
- // for this container.
- visitChildren(g, GfxPaintVisitor.INSTANCE, !backCleared);
- backCleared = false;
+ if (isShowing())
+ {
+ visitChildren(g, GfxPaintVisitor.INSTANCE, true);
+ }
}
/**
* Updates this container. The implementation of this method in this
* class forwards to any lightweight components in this container. If
* this method is subclassed, this method should still be invoked as
* a superclass method so that lightweight components are properly
* drawn.
*
* @param g The graphics context for this update.
*
* @specnote The specification suggests that this method forwards the
@@ -886,32 +878,33 @@
* be painted.
*/
public void update(Graphics g)
{
// It seems that the JDK clears the background of containers like Panel
// and Window (within this method) but not of 'plain' Containers or
// JComponents. This could
// lead to the assumption that it only clears heavyweight containers.
// However that is not quite true. In a test with a custom Container
// that overrides isLightweight() to return false, the background is
// also not cleared. So we do a check on !(peer instanceof LightweightPeer)
// instead.
- ComponentPeer p = peer;
- if (p != null && ! (p instanceof LightweightPeer))
+ if (isShowing())
{
- g.clearRect(0, 0, getWidth(), getHeight());
- backCleared = true;
+ ComponentPeer p = peer;
+ if (! (p instanceof LightweightPeer))
+ {
+ g.clearRect(0, 0, getWidth(), getHeight());
+ }
+ paint(g);
}
-
- paint(g);
}
/**
* Prints this container. The implementation of this method in this
* class forwards to any lightweight components in this container. If
* this method is subclassed, this method should still be invoked as
* a superclass method so that lightweight components are properly
* drawn.
*
* @param g The graphics context for this print job.
*/
public void print(Graphics g)