Closed Mazi-S closed 2 weeks ago
If a component has a TransitionalStyle and is not displayed
Ok, so let me give you some overarching background information to give you a picture:
So I have the suspicion that this is probably based on the fundamental problem that Swing does not really give SwinTree a clean place and time where to hook into and update itself irrespective of the component state.
There are 4 places where we currently hook into the update cycle of
a component to trigger all of the Styler
lambda stuff and ultimately the StyleInstaller
.
Border
interfaceComponentUI
interface.paintComponent(Graphics)
method (native SwingTree components only)paintChildren(Graphics)
method (native SwingTree components only)The first one is the StyleAndAnimationBorder
, it renders the border layer and
also delegates to any former border of the component.
For some components SwingTree has custom ComponentUI
implementations
(see DynamicLaF
in the StyleInstaller
).
And the last two places are found in implementations of the StylableComponent
interface
usually the ones declared in the UI
class.
So for example:
/** {inheritDoc} */
public static class Slider extends JSlider implements StylableComponent {
@Override public void paintComponent(Graphics g){ paintBackground(g, super::paintComponent); }
@Override public void paintChildren(Graphics g) { paintForeground(g, super::paintChildren); }
@Override public void setUISilently( ComponentUI ui ) { this.ui = ui; }
}
SwingTree works well with any of these hooks. A single one is enough to trigger an update for the whole styling stuff to take affect...
The problem with all of these hooks however is that they are at the mercy of the
public void paint(Graphics g)
method of the JComponent
.
This is what orchestrates calls to the formerly mentioned places...
But this method may not decide to continue its execution in all cases
public void paint(Graphics g) {
boolean shouldClearPaintFlags = false;
if ((getWidth() <= 0) || (getHeight() <= 0)) {
return;
}
[...]
[...]
}
This is why you will encounter the following condition in the AnimationRunner
:
if ( component.getParent() == null || (component.getWidth() <= 0) || (component.getHeight() <= 0) ) {
ComponentExtension.from((JComponent) component).gatherApplyAndInstallStyle(false);
component.revalidate();
component.repaint();
}
So the first problem you outline might be because somewhere in the
Swing code it checks if JComponent::isVisible
is false
and then just returns
without ever triggering a paint cycle (which makes total sense! But it is unfortunate for
the SwingTree logic).
So from having done debugging before, my assumption is that maybe the above condition should also check for the visibility of the component. But this is just a guess.
It seems that when the component is displayed, the lambda is executed correctly once, but the style is not applied. My wild guess is that a
revalidate()
is missing inswingtree.style.StyleInstaller#_applyDimensionalityStyleTo
, but I'm not familiar with the code.
No this is unexpected and I would assume the layout manager to react to the size changes
automatically. At least, that is what the assumption is in the StyleInstaller
code.
So you might be right that there is indeed a revalidate()
missing there.
But we should be careful with these statements in there, because there is a danger of triggering repeated repaint/revalidate cycles...
Maybe invalidate() is enough?
What exactly is the issue you are facing here in terms of a scenario. How can I reproduce this?
If you have two panels with a transitional style based on a the same property (a foldable panel) placed in different tabs.
.withTransitionalStyle(isOpen, LifeTime.of(0.5, TimeUnit.SECONDS), (status, style) -> {
double h = style.component().getPreferredSize().height * status.fadeIn();
style.component().setBackground( h == 0 ? Color.orange : Color.lightGray);
System.out.printf("TransitionalStyle[%s]: h=%f\n", content, h);
return style.minHeight(h).maxHeight(h);
})
The invisible foldable panel one is not displayed correctly after the property changes.
Initial state. Both expanded.
After clicking the Show button on the first tab.
Another invalid state. The BG color seems to be displayed correctly. Only the size seems to be affected.
When both are displayed, everything works perfectly:
Framework: We want to synchronize the run panels between tabs.
If a component has a TransitionalStyle and is not displayed when the style changes, the style is incorrect when the component is displayed again.
It seems that when the component is displayed, the lambda is executed correctly once, but the style is not applied. My wild guess is that a
revalidate()
is missing inswingtree.style.StyleInstaller#_applyDimensionalityStyleTo
, but I'm not familiar with the code.It is a bit hard to explain, but I will push example that makes it clear.