Interface SwingTreeStyledComponentUI<C extends JComponent>
- Type Parameters:
C- The type ofJComponentfor which a particularComponentUIis designed.
ComponentUI extensions
of a custom Look and Feel which desires to fully integrate with the SwingTree style engine.For context, it is important to note that SwingTree ships with a rich style rendering engine and three main ways for configuring styles.
The most prominent once, which are typically used to build an application, are:
-
Global Styling:
see
StyleSheet,UIFactoryMethods.use(StyleSheet, Supplier),SwingTree.initializeUsing(SwingTreeConfigurator) -
Direct Styling in the GUI:
seeUIForAnySwing.withStyle(Styler)
alsoUIForAnySwing.withTransitionalStyle(Val, LifeTime, AnimatedStyler)
andUIForAnySwing.withTransitoryStyle(Observable, LifeTime, AnimatedStyler)
ComponentUI
of a custom Look and Feel implementing this marker interface SwingTreeStyledComponentUI.
So if you develop your own Look and Feel, then you can make it compatible with SwingTree by having your
ComponentUI extensions implement this SwingTreeStyledComponentUI interface.
SwingTree will cooperate with the ComponentUI in two major ways:
-
Gathering style information to the SwingTree style engine
by invoking the
style(ComponentStyleDelegate)implementation. -
Checking if
canForwardPaintingToSwingTree()returnstrueand then expecting theComponentUI.paint(Graphics, JComponent)to delegate to SwingTreesComponentExtension.paintBackground(Graphics, swingtree.api.Painter)method.
ComponentUI
of a particular JComponent implements this interface, and then invoke the
style(ComponentStyleDelegate) method to gather style information.That way a custom look and feel can delegate the complex rendering directly to the SwingTree style engine. You as Look and Feel developer can thereby focus on writing code which conveys the intent of how a particular type of component ought to look like much more clearly.
Note that the order in which a renderable style is computed in SwingTree follows the following order:
- Global Defaults - (see
StyleSheet) - Component Look and Feel - (this
SwingTreeStyledComponentUI) - Component Declaration - (see
UIForAnySwing.withStyle(Styler))
A
ComponentStyleDelegate will be sent through
a StyleSheet, then it will go through this SwingTreeStyledComponentUI
and finally UIForAnySwing.withStyle(Styler) on a concrete component.
But for full interoperability you may also want to have canForwardPaintingToSwingTree()
always return true, and then implement ComponentUI.paint(Graphics, JComponent)
to delegate to SwingTree like so:
//Override
public void paint(
Graphics g,
JComponent comp
) {
ComponentExtension.from(comp)
.paintBackground(g, g2d -> {
super.paint(g2d, comp);
});
}
//Override
public boolean canForwardPaintingToSwingTree() {
return true;
}
That way, the SwingTree's style engine works reliably for all components!
Another important aspect to consider if you want your look and feel to integrate
well with SwingTree is UI scaling for high DPI screens!.
SwingTree scales style renderings for you automatically, but when you do custom painting
in your Look and Feel then positions and dimensions must be multiplied by UI.scale().
HiDPI fonts: ask SwingTree, don't hardcode
The most common pitfall when porting an existing
BasicLookAndFeel to SwingTree is to
install fixed-size fonts in
javax.swing.plaf.basic.BasicLookAndFeel#initComponentDefaults(javax.swing.UIDefaults).
SwingTree already computes a properly platform-scaled default font
for the active display (Windows screen scaling, GNOME / KDE Xft DPI,
macOS system font, …) and exposes it through
SwingTree.getScaledDefaultFont(). It just cannot
force your LAF to use it. If you put
new Font(Font.DIALOG, PLAIN, 13) into Label.font,
you get a 13-pt font on a 4K screen too, which looks like six
points to a human.
initComponentDefaults(..) read the platform-scaled font
through SwingTree.get().getScaledDefaultFont() and use it for every
*.font key (including "defaultFont" itself, so the
engine and your LAF agree on the same single value). The method
returns a FontUIResource ready to be
installed directly into UIDefaults, and lazily
bootstraps the library on first call, so the order in which
setLookAndFeel(..) and UI.show(..) are invoked no
longer matters.
Optional but recommended for dynamic HiDPI: subscribe to
SwingTree.getScaledDefaultFontView() from your
LookAndFeel#initialize() hook and, on every fire, re-push
the new font into your *.font keys and call
SwingUtilities.updateComponentTreeUI(window) on each open
window. That makes screen text track changes the host pushes
through the UIManager — a new "defaultFont",
a "Label.font" swap, or another LAF being installed — without a
restart. Hold the returned Viewable in a strong
field — Sprouts holds change listeners weakly and the
subscription is dropped otherwise.
Building-A-Look-And-Feel.md wiki page (section
"HiDPI fonts") for the complete worked example.-
Method Summary
Modifier and TypeMethodDescriptiondefault booleanIf you want to achieve full compatibility and interoperability with SwingTree in your Look and Feel you have to forwardComponentUI.paint(Graphics, JComponent)andComponentUI.update(Graphics, JComponent)draw calls to SwingTrees'sComponentExtension.paintBackground(Graphics, swingtree.api.Painter)method! To inform SwingTree that you are going to do this, you also have to override this method and make it returntrue.voidThis method must be implemented throughComponentUI.installUI(JComponent)! It configures the specified component appropriately for a SwingTree compatible look and feel.voidpaint(Graphics g, JComponent c) This method must be implemented throughComponentUI.paint(Graphics, JComponent)! It paints the specified component appropriately for the look and feel.style(ComponentStyleDelegate<C> delegate) Receives aComponentStyleDelegateand applies style information to it by transforming it to a newComponentStyleDelegate.
-
Method Details
-
installUI
This method must be implemented throughComponentUI.installUI(JComponent)! It configures the specified component appropriately for a SwingTree compatible look and feel. This method is invoked when theComponentUIinstance is being installed as the UI delegate on the specified component. This method should completely configure the component for the look and feel, including the following:- Install default property values for color, fonts, borders, icons, opacity, etc. on the component. Whenever possible, property values initialized by the client program should not be overridden.
- Install a
LayoutManageron the component if necessary. - Create/add any required subcomponents to the component.
- Create/install event listeners on the component.
- Create/install a
PropertyChangeListeneron the component in order to detect and respond to component property changes appropriately. - Install keyboard UI (mnemonics, traversal, etc.) on the component.
- Initialize any appropriate instance data.
IMPORTANT:
For full SwingTree interoperability, implementations of this should invokeComponentExtension.gatherApplyAndInstallStyle(boolean)to ensure that the SwingTree style of a particular component is installed correctly.
So an implementation may look something like this:// Override public void installUI( JComponent comp ) { ComponentExtension.from(comp) .gatherApplyAndInstallStyle(true); }- Parameters:
c- the component where this UI delegate is being installed- See Also:
-
paint
This method must be implemented throughComponentUI.paint(Graphics, JComponent)! It paints the specified component appropriately for the look and feel. This method is invoked from theComponentUI.update(Graphics, JComponent)method when the specified component is being painted. Subclasses should override this method and use the specifiedGraphicsobject to render the content of the component.
For full SwingTree interoperability, you should overridecanForwardPaintingToSwingTree()to returntrueand then forward the paint request to SwingTree like so:// Override public void paint( Graphics g, JComponent comp ) { ComponentExtension.from(comp) .paintBackground(g, g2d->{ super.paint(g2d, comp); }); }- Parameters:
g- theGraphicscontext in which to paintc- the component being painted; this argument is often ignored, but might be used if the UI object is stateless and shared by multiple components- See Also:
-
style
Receives aComponentStyleDelegateand applies style information to it by transforming it to a newComponentStyleDelegate.
This styling will happen after/on-top of the style supplied by aStyleSheetbut it will happen before the style directly declared for a specific component instance using theUIForAnySwing.withStyle(Styler)method.
Note that this method is designed to only be invoked by SwingTree internal code. You should never invoke it yourself.- Parameters:
delegate- TheComponentStyleDelegateto apply the style to.- Returns:
- A new
ComponentStyleDelegatethat has the style applied. - Throws:
Exception- if the style could not be applied by the client code.
-
canForwardPaintingToSwingTree
default boolean canForwardPaintingToSwingTree()If you want to achieve full compatibility and interoperability with SwingTree in your Look and Feel you have to forwardComponentUI.paint(Graphics, JComponent)andComponentUI.update(Graphics, JComponent)draw calls to SwingTrees'sComponentExtension.paintBackground(Graphics, swingtree.api.Painter)method! To inform SwingTree that you are going to do this, you also have to override this method and make it returntrue. This will turn yourComponentUIimplementation into the main way in which SwingTree renders its style onto a specific component type.
So your implementation would look something like this:// Override public void paint( Graphics g, JComponent comp ) { ComponentExtension.from(comp) .paintBackground(g, g2d->{ super.paint(g2d, comp); }); } // Override public boolean canForwardPaintingToSwingTree() { return true; }WARNING:
If this method returnstrue, but theComponentUI.paint(Graphics, JComponent)implementation does NOT delegate toComponentExtension.paintBackground(Graphics, swingtree.api.Painter), then you will effectively break the style rendering of your components!
This is because in that case, SwingTree relies entirely on your override to be the sole way of hooking into Swings component rendering.- Returns:
- A flag which informs SwingTree if it can rely on your
ComponentUI.paint(Graphics, JComponent)implementation to be used for hooking its style engine into the rendering pipeline of a component.
-