提交 1c202c45 编写于 作者: A anthony

6852592: invalidate() must be smarter

Summary: Introduce validate roots in AWT
Reviewed-by: alexp, art, dcherepanov
上级 354840fa
...@@ -229,6 +229,21 @@ public class Applet extends Panel { ...@@ -229,6 +229,21 @@ public class Applet extends Panel {
resize(d.width, d.height); resize(d.width, d.height);
} }
/**
* Indicates if this container is a validate root.
* <p>
* {@code Applet} objects are the validate roots, and, therefore, they
* override this method to return {@code true}.
*
* @return {@code true}
* @since 1.7
* @see java.awt.Container#isValidateRoot
*/
@Override
public boolean isValidateRoot() {
return true;
}
/** /**
* Requests that the argument string be displayed in the * Requests that the argument string be displayed in the
* "status window". Many browsers and applet viewers * "status window". Many browsers and applet viewers
......
...@@ -2764,8 +2764,11 @@ public abstract class Component implements ImageObserver, MenuContainer, ...@@ -2764,8 +2764,11 @@ public abstract class Component implements ImageObserver, MenuContainer,
} }
/** /**
* Ensures that this component has a valid layout. This method is * Validates this component.
* primarily intended to operate on instances of <code>Container</code>. * <p>
* The meaning of the term <i>validating</i> is defined by the ancestors of
* this class. See {@link Container#validate} for more details.
*
* @see #invalidate * @see #invalidate
* @see #doLayout() * @see #doLayout()
* @see LayoutManager * @see LayoutManager
...@@ -2794,12 +2797,24 @@ public abstract class Component implements ImageObserver, MenuContainer, ...@@ -2794,12 +2797,24 @@ public abstract class Component implements ImageObserver, MenuContainer,
} }
/** /**
* Invalidates this component. This component and all parents * Invalidates this component and its ancestors.
* above it are marked as needing to be laid out. This method can * <p>
* be called often, so it needs to execute quickly. * All the ancestors of this component up to the nearest validate root are
* marked invalid also. If there is no a validate root container for this
* component, all of its ancestors up to the root of the hierarchy are
* marked invalid as well. Marking a container <i>invalid</i> indicates
* that the container needs to be laid out.
* <p>
* This method is called automatically when any layout-related information
* changes (e.g. setting the bounds of the component, or adding the
* component to a container).
* <p>
* This method might be called often, so it should work fast.
*
* @see #validate * @see #validate
* @see #doLayout * @see #doLayout
* @see LayoutManager * @see LayoutManager
* @see java.awt.Container#isValidateRoot
* @since JDK1.0 * @since JDK1.0
*/ */
public void invalidate() { public void invalidate() {
...@@ -2818,9 +2833,18 @@ public abstract class Component implements ImageObserver, MenuContainer, ...@@ -2818,9 +2833,18 @@ public abstract class Component implements ImageObserver, MenuContainer,
if (!isMaximumSizeSet()) { if (!isMaximumSizeSet()) {
maxSize = null; maxSize = null;
} }
if (parent != null) { invalidateParent();
parent.invalidateIfValid(); }
} }
/**
* Invalidates the parent of this component if any.
*
* This method MUST BE invoked under the TreeLock.
*/
void invalidateParent() {
if (parent != null) {
parent.invalidateIfValid();
} }
} }
......
...@@ -1492,20 +1492,59 @@ public class Container extends Component { ...@@ -1492,20 +1492,59 @@ public class Container extends Component {
} }
/** /**
* Invalidates the container. The container and all parents * Indicates if this container is a <i>validate root</i>.
* above it are marked as needing to be laid out. This method can * <p>
* be called often, so it needs to execute quickly. * Layout-related changes, such as bounds of the validate root descendants,
* do not affect the layout of the validate root parent. This peculiarity
* enables the {@code invalidate()} method to stop invalidating the
* component hierarchy when the method encounters a validate root.
* <p>
* If a component hierarchy contains validate roots, the {@code validate()}
* method must be invoked on the validate root of a previously invalidated
* component, rather than on the top-level container (such as a {@code
* Frame} object) to restore the validity of the hierarchy later.
* <p>
* The {@code Window} class and the {@code Applet} class are the validate
* roots in AWT. Swing introduces more validate roots.
* *
* <p> If the {@code LayoutManager} installed on this container is * @return whether this container is a validate root
* an instance of {@code LayoutManager2}, then * @see #invalidate
* {@link LayoutManager2#invalidateLayout(Container)} is invoked on * @see java.awt.Component#invalidate
* it supplying this {@code Container} as the argument. * @see javax.swing.JComponent#isValidateRoot
* @see javax.swing.JComponent#revalidate
* @since 1.7
*/
public boolean isValidateRoot() {
return false;
}
/**
* Invalidates the parent of the container unless the container
* is a validate root.
*/
@Override
void invalidateParent() {
if (!isValidateRoot()) {
super.invalidateParent();
}
}
/**
* Invalidates the container.
* <p>
* If the {@code LayoutManager} installed on this container is an instance
* of the {@code LayoutManager2} interface, then
* the {@link LayoutManager2#invalidateLayout(Container)} method is invoked
* on it supplying this {@code Container} as the argument.
* <p>
* Afterwards this method marks this container invalid, and invalidates its
* ancestors. See the {@link Component#invalidate} method for more details.
* *
* @see #validate * @see #validate
* @see #layout * @see #layout
* @see LayoutManager * @see LayoutManager2
* @see LayoutManager2#invalidateLayout(Container)
*/ */
@Override
public void invalidate() { public void invalidate() {
LayoutManager layoutMgr = this.layoutMgr; LayoutManager layoutMgr = this.layoutMgr;
if (layoutMgr instanceof LayoutManager2) { if (layoutMgr instanceof LayoutManager2) {
...@@ -1518,35 +1557,39 @@ public class Container extends Component { ...@@ -1518,35 +1557,39 @@ public class Container extends Component {
/** /**
* Validates this container and all of its subcomponents. * Validates this container and all of its subcomponents.
* <p> * <p>
* The <code>validate</code> method is used to cause a container * Validating a container means laying out its subcomponents.
* to lay out its subcomponents again. It should be invoked when * Layout-related changes, such as setting the bounds of a component, or
* this container's subcomponents are modified (added to or * adding a component to the container, invalidate the container
* removed from the container, or layout-related information * automatically. Note that the ancestors of the container may be
* changed) after the container has been displayed. * invalidated also (see {@link Component#invalidate} for details.)
* * Therefore, to restore the validity of the hierarchy, the {@code
* <p>If this {@code Container} is not valid, this method invokes * validate()} method should be invoked on a validate root of an
* invalidated component, or on the top-most container if the hierarchy
* does not contain validate roots.
* <p>
* Validating the container may be a quite time-consuming operation. For
* performance reasons a developer may postpone the validation of the
* hierarchy till a set of layout-related operations completes, e.g. after
* adding all the children to the container.
* <p>
* If this {@code Container} is not valid, this method invokes
* the {@code validateTree} method and marks this {@code Container} * the {@code validateTree} method and marks this {@code Container}
* as valid. Otherwise, no action is performed. * as valid. Otherwise, no action is performed.
* <p>
* Note that the {@code invalidate()} method may invalidate not only the
* component it is called upon, but also the parents of the component.
* Therefore, to restore the validity of the hierarchy, the {@code
* validate()} method must be invoked on the top-most invalid container of
* the hierarchy. For performance reasons a developer may postpone the
* validation of the hierarchy till a bunch of layout-related operations
* completes, e.g. after adding all the children to the container.
* *
* @see #add(java.awt.Component) * @see #add(java.awt.Component)
* @see #invalidate * @see #invalidate
* @see Container#isValidateRoot
* @see javax.swing.JComponent#revalidate() * @see javax.swing.JComponent#revalidate()
* @see #validateTree * @see #validateTree
*/ */
public void validate() { public void validate() {
/* Avoid grabbing lock unless really necessary. */ /* Avoid grabbing lock unless really necessary. */
if (!isValid()) { if (!isValid() || descendUnconditionallyWhenValidating) {
boolean updateCur = false; boolean updateCur = false;
synchronized (getTreeLock()) { synchronized (getTreeLock()) {
if (!isValid() && peer != null) { if ((!isValid() || descendUnconditionallyWhenValidating)
&& peer != null)
{
ContainerPeer p = null; ContainerPeer p = null;
if (peer instanceof ContainerPeer) { if (peer instanceof ContainerPeer) {
p = (ContainerPeer) peer; p = (ContainerPeer) peer;
...@@ -1557,7 +1600,11 @@ public class Container extends Component { ...@@ -1557,7 +1600,11 @@ public class Container extends Component {
validateTree(); validateTree();
if (p != null) { if (p != null) {
p.endValidate(); p.endValidate();
updateCur = isVisible(); // Avoid updating cursor if this is an internal call.
// See validateUnconditionally() for details.
if (!descendUnconditionallyWhenValidating) {
updateCur = isVisible();
}
} }
} }
} }
...@@ -1567,6 +1614,39 @@ public class Container extends Component { ...@@ -1567,6 +1614,39 @@ public class Container extends Component {
} }
} }
/**
* Indicates whether valid containers should also traverse their
* children and call the validateTree() method on them.
*
* Synchronization: TreeLock.
*
* The field is allowed to be static as long as the TreeLock itself is
* static.
*
* @see #validateUnconditionally()
*/
private static boolean descendUnconditionallyWhenValidating = false;
/**
* Unconditionally validate the component hierarchy.
*/
final void validateUnconditionally() {
boolean updateCur = false;
synchronized (getTreeLock()) {
descendUnconditionallyWhenValidating = true;
validate();
if (peer instanceof ContainerPeer) {
updateCur = isVisible();
}
descendUnconditionallyWhenValidating = false;
}
if (updateCur) {
updateCursorImmediately();
}
}
/** /**
* Recursively descends the container tree and recomputes the * Recursively descends the container tree and recomputes the
* layout for any subtrees marked as needing it (those marked as * layout for any subtrees marked as needing it (those marked as
...@@ -1578,16 +1658,20 @@ public class Container extends Component { ...@@ -1578,16 +1658,20 @@ public class Container extends Component {
*/ */
protected void validateTree() { protected void validateTree() {
checkTreeLock(); checkTreeLock();
if (!isValid()) { if (!isValid() || descendUnconditionallyWhenValidating) {
if (peer instanceof ContainerPeer) { if (peer instanceof ContainerPeer) {
((ContainerPeer)peer).beginLayout(); ((ContainerPeer)peer).beginLayout();
} }
doLayout(); if (!isValid()) {
doLayout();
}
for (int i = 0; i < component.size(); i++) { for (int i = 0; i < component.size(); i++) {
Component comp = component.get(i); Component comp = component.get(i);
if ( (comp instanceof Container) if ( (comp instanceof Container)
&& !(comp instanceof Window) && !(comp instanceof Window)
&& !comp.isValid()) { && (!comp.isValid() ||
descendUnconditionallyWhenValidating))
{
((Container)comp).validateTree(); ((Container)comp).validateTree();
} else { } else {
comp.validate(); comp.validate();
......
...@@ -767,7 +767,7 @@ public class Window extends Container implements Accessible { ...@@ -767,7 +767,7 @@ public class Window extends Container implements Accessible {
isPacked = true; isPacked = true;
} }
validate(); validateUnconditionally();
} }
/** /**
...@@ -943,7 +943,7 @@ public class Window extends Container implements Accessible { ...@@ -943,7 +943,7 @@ public class Window extends Container implements Accessible {
if (peer == null) { if (peer == null) {
addNotify(); addNotify();
} }
validate(); validateUnconditionally();
isInShow = true; isInShow = true;
if (visible) { if (visible) {
...@@ -2599,6 +2599,21 @@ public class Window extends Container implements Accessible { ...@@ -2599,6 +2599,21 @@ public class Window extends Container implements Accessible {
super.addPropertyChangeListener(propertyName, listener); super.addPropertyChangeListener(propertyName, listener);
} }
/**
* Indicates if this container is a validate root.
* <p>
* {@code Window} objects are the validate roots, and, therefore, they
* override this method to return {@code true}.
*
* @return {@code true}
* @since 1.7
* @see java.awt.Container#isValidateRoot
*/
@Override
public boolean isValidateRoot() {
return true;
}
/** /**
* Dispatches an event to this window or one of its sub components. * Dispatches an event to this window or one of its sub components.
* @param e the event * @param e the event
......
...@@ -4878,7 +4878,9 @@ public abstract class JComponent extends Container implements Serializable, ...@@ -4878,7 +4878,9 @@ public abstract class JComponent extends Container implements Serializable,
* @see #revalidate * @see #revalidate
* @see java.awt.Component#invalidate * @see java.awt.Component#invalidate
* @see java.awt.Container#validate * @see java.awt.Container#validate
* @see java.awt.Container#isValidateRoot
*/ */
@Override
public boolean isValidateRoot() { public boolean isValidateRoot() {
return false; return false;
} }
......
...@@ -725,8 +725,10 @@ public class JRootPane extends JComponent implements Accessible { ...@@ -725,8 +725,10 @@ public class JRootPane extends JComponent implements Accessible {
* because both classes override <code>isValidateRoot</code> to return true. * because both classes override <code>isValidateRoot</code> to return true.
* *
* @see JComponent#isValidateRoot * @see JComponent#isValidateRoot
* @see java.awt.Container#isValidateRoot
* @return true * @return true
*/ */
@Override
public boolean isValidateRoot() { public boolean isValidateRoot() {
return true; return true;
} }
......
...@@ -453,10 +453,12 @@ public class JScrollPane extends JComponent implements ScrollPaneConstants, Acce ...@@ -453,10 +453,12 @@ public class JScrollPane extends JComponent implements ScrollPaneConstants, Acce
* @see java.awt.Container#validate * @see java.awt.Container#validate
* @see JComponent#revalidate * @see JComponent#revalidate
* @see JComponent#isValidateRoot * @see JComponent#isValidateRoot
* @see java.awt.Container#isValidateRoot
* *
* @beaninfo * @beaninfo
* hidden: true * hidden: true
*/ */
@Override
public boolean isValidateRoot() { public boolean isValidateRoot() {
return true; return true;
} }
......
...@@ -947,10 +947,12 @@ public class JSplitPane extends JComponent implements Accessible ...@@ -947,10 +947,12 @@ public class JSplitPane extends JComponent implements Accessible
* *
* @return true * @return true
* @see JComponent#revalidate * @see JComponent#revalidate
* @see java.awt.Container#isValidateRoot
* *
* @beaninfo * @beaninfo
* hidden: true * hidden: true
*/ */
@Override
public boolean isValidateRoot() { public boolean isValidateRoot() {
return true; return true;
} }
......
...@@ -288,7 +288,9 @@ public class JTextField extends JTextComponent implements SwingConstants { ...@@ -288,7 +288,9 @@ public class JTextField extends JTextComponent implements SwingConstants {
* *
* @see JComponent#revalidate * @see JComponent#revalidate
* @see JComponent#isValidateRoot * @see JComponent#isValidateRoot
* @see java.awt.Container#isValidateRoot
*/ */
@Override
public boolean isValidateRoot() { public boolean isValidateRoot() {
return SwingUtilities2.getViewport(this) == null; return SwingUtilities2.getViewport(this) == null;
} }
......
...@@ -469,49 +469,12 @@ public class JViewport extends JComponent implements Accessible ...@@ -469,49 +469,12 @@ public class JViewport extends JComponent implements Accessible
* is the synchronous version of a <code>revalidate</code>. * is the synchronous version of a <code>revalidate</code>.
*/ */
private void validateView() { private void validateView() {
Component validateRoot = null; Component validateRoot = SwingUtilities.getValidateRoot(this, false);
/* Find the first JComponent ancestor of this component whose
* isValidateRoot() method returns true.
*/
for(Component c = this; c != null; c = c.getParent()) {
if ((c instanceof CellRendererPane) || (c.getPeer() == null)) {
return;
}
if ((c instanceof JComponent) &&
(((JComponent)c).isValidateRoot())) {
validateRoot = c;
break;
}
}
// If no validateRoot, nothing to validate from.
if (validateRoot == null) { if (validateRoot == null) {
return; return;
} }
// Make sure all ancestors are visible.
Component root = null;
for(Component c = validateRoot; c != null; c = c.getParent()) {
// We don't check isVisible here, otherwise if the component
// is contained in something like a JTabbedPane when the
// component is made visible again it won't have scrolled
// to the correct location.
if (c.getPeer() == null) {
return;
}
if ((c instanceof Window) || (c instanceof Applet)) {
root = c;
break;
}
}
// Make sure there is a Window ancestor.
if (root == null) {
return;
}
// Validate the root. // Validate the root.
validateRoot.validate(); validateRoot.validate();
......
...@@ -310,47 +310,13 @@ public class RepaintManager ...@@ -310,47 +310,13 @@ public class RepaintManager
delegate.addInvalidComponent(invalidComponent); delegate.addInvalidComponent(invalidComponent);
return; return;
} }
Component validateRoot = null; Component validateRoot =
SwingUtilities.getValidateRoot(invalidComponent, true);
/* Find the first JComponent ancestor of this component whose
* isValidateRoot() method returns true.
*/
for(Component c = invalidComponent; c != null; c = c.getParent()) {
if ((c instanceof CellRendererPane) || (c.getPeer() == null)) {
return;
}
if ((c instanceof JComponent) && (((JComponent)c).isValidateRoot())) {
validateRoot = c;
break;
}
}
/* There's no validateRoot to apply validate to, so we're done.
*/
if (validateRoot == null) { if (validateRoot == null) {
return; return;
} }
/* If the validateRoot and all of its ancestors aren't visible
* then we don't do anything. While we're walking up the tree
* we find the root Window or Applet.
*/
Component root = null;
for(Component c = validateRoot; c != null; c = c.getParent()) {
if (!c.isVisible() || (c.getPeer() == null)) {
return;
}
if ((c instanceof Window) || (c instanceof Applet)) {
root = c;
break;
}
}
if (root == null) {
return;
}
/* Lazily create the invalidateComponents vector and add the /* Lazily create the invalidateComponents vector and add the
* validateRoot if it's not there already. If this validateRoot * validateRoot if it's not there already. If this validateRoot
* is already in the vector, we're done. * is already in the vector, we're done.
......
...@@ -1967,4 +1967,54 @@ public class SwingUtilities implements SwingConstants ...@@ -1967,4 +1967,54 @@ public class SwingUtilities implements SwingConstants
SwingUtilities.updateComponentTreeUI(component); SwingUtilities.updateComponentTreeUI(component);
} }
} }
/**
* Retrieves the validate root of a given container.
*
* If the container is contained within a {@code CellRendererPane}, this
* method returns {@code null} due to the synthetic nature of the {@code
* CellRendererPane}.
* <p>
* The component hierarchy must be displayable up to the toplevel component
* (either a {@code Frame} or an {@code Applet} object.) Otherwise this
* method returns {@code null}.
* <p>
* If the {@code visibleOnly} argument is {@code true}, the found validate
* root and all its parents up to the toplevel component must also be
* visible. Otherwise this method returns {@code null}.
*
* @return the validate root of the given container or null
* @see java.awt.Component#isDisplayable()
* @see java.awt.Component#isVisible()
* @since 1.7
*/
static Container getValidateRoot(Container c, boolean visibleOnly) {
Container root = null;
for (; c != null; c = c.getParent())
{
if (!c.isDisplayable() || c instanceof CellRendererPane) {
return null;
}
if (c.isValidateRoot()) {
root = c;
break;
}
}
if (root == null) {
return null;
}
for (; c != null; c = c.getParent()) {
if (!c.isDisplayable() || (visibleOnly && !c.isVisible())) {
return null;
}
if (c instanceof Window || c instanceof Applet) {
return root;
}
}
return null;
}
} }
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/*
@test
@bug 6852592
@summary invalidate() must stop when it encounters a validate root
@author anthony.petrov@sun.com
@run main InvalidateMustRespectValidateRoots
*/
import javax.swing.*;
import java.awt.event.*;
public class InvalidateMustRespectValidateRoots {
private static volatile JRootPane rootPane;
public static void main(String args[]) throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
// The JRootPane is a validate root. We'll check if
// invalidate() stops on the root pane, or goes further
// up to the frame.
JFrame frame = new JFrame();
final JButton button = new JButton();
frame.add(button);
// To enable running the test manually: use the Ctrl-Shift-F1
// to print the component hierarchy to the console
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
if (button.isValid()) {
button.invalidate();
} else {
button.revalidate();
}
}
});
rootPane = frame.getRootPane();
// Now the component hierarchy looks like:
// frame
// --> rootPane
// --> layered pane
// --> content pane
// --> button
// Make all components valid first via showing the frame
// We have to make the frame visible. Otherwise revalidate() is
// useless (see RepaintManager.addInvalidComponent()).
frame.pack(); // To enable running this test manually
frame.setVisible(true);
if (!frame.isValid()) {
throw new RuntimeException(
"setVisible(true) failed to validate the frame");
}
// Now invalidate the button
button.invalidate();
// Check if the 'valid' status is what we expect it to be
if (rootPane.isValid()) {
throw new RuntimeException(
"invalidate() failed to invalidate the root pane");
}
if (!frame.isValid()) {
throw new RuntimeException(
"invalidate() invalidated the frame");
}
// Now validate the hierarchy again
button.revalidate();
// Now let the validation happen on the EDT
}
});
Thread.sleep(1000);
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
// Check if the root pane finally became valid
if (!rootPane.isValid()) {
throw new RuntimeException(
"revalidate() failed to validate the hierarchy");
}
}
});
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册