EVALUATION
The final idea of the fix is as follows. setComponentZOrder() actually verifies the return value of the isRemoveNotifyNeeded(Component comp, Container oldContainer, Container newContainer) to check whether it's necessary to remove the peer of the component. Thefore it is the isRemoveNotifyNeeded() method that should be fixed.
Our implementation of the method does some common checks:
1. If the oldContainer is null or the component has no peer curretnly, there's no need to remove the peer.
2. If the newContainer doesn't have a peer, we need to remove the peer from the component.
As the second step this method separates handling of LW and HW components.
1. If the component is LW:
a) If it's a Container, then return the result of the hasHeavyweightDescendants() method. That means that the peer gets destroyed if the LW container has any HW descendants.
b) Otherwise, simply return flase. The peer should not be destroyed if this is a "simple" LW.
2. If the component is HW:
At first, we retrieve the nearest heavyweight containers of both the oldContainer and the newContainer.
a) If the nearest HW containers are the same, we return the result of the !newHWContainer.isRestackSupported(). I.e. if the 'restack' operation is supported, there's no need to remove the peer.
b) If the HW containers differ: we return the result of the !comp.peer.isReparentSupported(), i.e. we check whether the component may safely be reparented.
The problem described at this CR actually happens at the step 1. In other words, we should handle a LW container having HW descendants just the same way as a regular HW component (just like, say, the HW/LW mixing code assumes, see CR 4811096 for more information).
The proposed fix changes the handling at the second step in the following way:
1. If the component is LW:
If it's not a Container or contains no HW descendants, the method returns false, meaning there's no need to destroy the peer. Otherwise proceed to the step 2.
2. If this point is reached, the component is either a regular HW, or a LW container with HW descendants. The handling of this case is performed in the old way.
This successfully fixes the problem described at the Description. However, there's one concern left: we cannot be sure that it's correct to query the peer of a LW component about whether the "reparent" operation is supported as it happens at step 2-b). It seems that the support of the "reparent" operation should solely be the responsibility of the container (either the old one, or the new one, or even both). Only the operation itself should slightly depend on the kind of the component (we should either reparent the component itself if it's a HW, or should recursevely reparent all the HW descendants of the LW container we're currently reparenting).
|
SUGGESTED FIX
$ sccs diffs -C Container.java
------- Container.java -------
*** /tmp/sccs.Vo4fHN 2007-08-01 17:37:48.000000000 +0400
--- Container.java 2007-08-01 17:30:20.000000000 +0400
***************
*** 622,649 ****
// If component is lightweight non-Container or lightweight Container with all but heavyweight
// children there is no need to call remove notify
if (comp.isLightweight()) {
! if (comp instanceof Container) {
! // If it has heavyweight children then removeNotify is required
! return ((Container)comp).hasHeavyweightDescendants();
! } else {
! // Just a lightweight
return false;
}
}
// All three components have peers, check for peer change
Container newNativeContainer = oldContainer.getHeavyweightContainer();
Container oldNativeContainer = newContainer.getHeavyweightContainer();
if (newNativeContainer != oldNativeContainer) {
// Native containers change - check whether or not current platform supports
// changing of widget hierarchy on native level without recreation.
return !comp.peer.isReparentSupported();
} else {
// if container didn't change we still might need to recreate component's window as
// changes to zorder should be reflected in native window stacking order and it might
// not be supported by the platform. This is important only for heavyweight child
! return !comp.isLightweight() &&
! !((ContainerPeer)(newNativeContainer.peer)).isRestackSupported();
}
}
--- 622,650 ----
// If component is lightweight non-Container or lightweight Container with all but heavyweight
// children there is no need to call remove notify
if (comp.isLightweight()) {
! boolean isContainer = comp instanceof Container;
!
! if (!isContainer || (isContainer && !((Container)comp).hasHeavyweightDescendants())) {
return false;
}
}
+ // If this point is reached, then the comp is either a HW or a LW container with HW descendants.
+
// All three components have peers, check for peer change
Container newNativeContainer = oldContainer.getHeavyweightContainer();
Container oldNativeContainer = newContainer.getHeavyweightContainer();
if (newNativeContainer != oldNativeContainer) {
// Native containers change - check whether or not current platform supports
// changing of widget hierarchy on native level without recreation.
+ //TODO: it's quite hard to say if the LightweightPeer ever tells the truth here...
+ // We should probably query either the new or the old container, but which one?...
return !comp.peer.isReparentSupported();
} else {
// if container didn't change we still might need to recreate component's window as
// changes to zorder should be reflected in native window stacking order and it might
// not be supported by the platform. This is important only for heavyweight child
! return !((ContainerPeer)(newNativeContainer.peer)).isRestackSupported();
}
}
|