SwingTreeStyledComponentUI.java
package swingtree.api.laf;
import sprouts.Observable;
import sprouts.Val;
import swingtree.SwingTreeConfigurator;
import swingtree.animation.LifeTime;
import swingtree.api.AnimatedStyler;
import swingtree.api.Styler;
import swingtree.style.ComponentStyleDelegate;
import swingtree.style.StyleSheet;
import javax.swing.*;
import java.awt.*;
import java.util.function.Supplier;
/**
* This interface is intended to be implemented by the various {@link javax.swing.plaf.ComponentUI} extensions
* of a custom <i>Look and Feel</i> which desires to fully integrate with the SwingTree style engine.<br>
* For context, it is important to note that <i>SwingTree</i> ships with a rich style rendering engine
* and three main ways for configuring styles.<br>
* The most prominent once, which are typically used to build an application, are:
* <ul>
* <li>
* <b>Global Styling:</b>
* see {@link swingtree.style.StyleSheet},
* {@link swingtree.UI#use(StyleSheet, Supplier)},
* {@link swingtree.SwingTree#initializeUsing(SwingTreeConfigurator)}
* </li>
* <li>
* <b>Direct Styling in the GUI:</b><br>
* see {@link swingtree.UIForAnySwing#withStyle(Styler)}<br>
* also {@link swingtree.UIForAnySwing#withTransitionalStyle(Val, LifeTime, AnimatedStyler)}<br>
* and {@link swingtree.UIForAnySwing#withTransitoryStyle(Observable, LifeTime, AnimatedStyler)}
* </li>
* </ul>
* <b>
* The third major way of styling is through the {@link javax.swing.plaf.ComponentUI}
* of a custom <i>Look and Feel</i> implementing this marker interface {@link SwingTreeStyledComponentUI}.
* </b><br>
* <p>
* So if you develop your own <i>Look and Feel</i>, then you can make it compatible with <i>SwingTree</i> by having your
* {@link javax.swing.plaf.ComponentUI} extensions implement this {@link SwingTreeStyledComponentUI} interface.
* <i>SwingTree</i> will cooperate with the {@code ComponentUI} in two major ways:
* </p>
* <ol>
* <li>
* Gathering style information to the <i>SwingTree</i> style engine
* by invoking the {@link #style(ComponentStyleDelegate)} implementation.
* </li>
* <li>
* Checking if {@link #canForwardPaintingToSwingTree()} returns {@code true} and then expecting
* the {@link javax.swing.plaf.ComponentUI#paint(Graphics, JComponent)} to delegate to <i>SwingTree</i>s
* {@link swingtree.style.ComponentExtension#paintBackground(Graphics, swingtree.api.Painter)} method.
* </li>
* </ol>
* When SwingTree resolves a style, it will detect that the {@link javax.swing.plaf.ComponentUI}
* of a particular {@link JComponent} implements this interface, and then invoke the
* {@link #style(ComponentStyleDelegate)} method to gather style information.<br>
* That way a custom look and feel can delegate the complex rendering directly to the SwingTree style engine.
* You as <i>Look and Feel</i> 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.<br>
* <p>
* Note that the <b>order</b> in which a <b>renderable style</b> is computed in
* SwingTree follows the following order:
* <ol>
* <li><b>Global Defaults</b> - (see {@link StyleSheet})</li>
* <li><b>Component Look and Feel</b> - (this {@link SwingTreeStyledComponentUI})</li>
* <li><b>Component Declaration</b> - (see {@link swingtree.UIForAnySwing#withStyle(Styler)})</li>
* </ol>
* In other words:<br>
* A {@link ComponentStyleDelegate} will be sent through
* a {@link StyleSheet}, then it will go through this {@link SwingTreeStyledComponentUI}
* and finally {@link swingtree.UIForAnySwing#withStyle(Styler)} on a concrete component.
* <br>
* <p>
* But for full interoperability you may also want to have {@link #canForwardPaintingToSwingTree()}
* always return {@code true}, and then implement {@link javax.swing.plaf.ComponentUI#paint(Graphics, JComponent)}
* to delegate to <i>SwingTree</i> like so:
* <pre>{@code
* @Override
* public void paint(
* Graphics g,
* JComponent comp
* ) {
* ComponentExtension.from(comp)
* .paintBackground(g, g2d->{
* super.paint(g2d, comp);
* });
* }
* @Override
* public boolean canForwardPaintingToSwingTree() {
* return true;
* }
* }</pre>
* That way, the <i>SwingTree</i>s style engine works reliably for all components!
*
* @param <C> The type of {@link JComponent} for which a particular {@link javax.swing.plaf.ComponentUI} is designed.
*/
public interface SwingTreeStyledComponentUI<C extends JComponent>
{
/**
* This method must be implemented through {@link javax.swing.plaf.ComponentUI#installUI(JComponent)}!
* It configures the specified component appropriately for a <i>SwingTree</i> compatible look and feel.
* This method is invoked when the <code>ComponentUI</code> instance 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:
* <ol>
* <li>Install default property values for color, fonts, borders,
* icons, opacity, etc. on the component. Whenever possible,
* property values initialized by the client program should <i>not</i>
* be overridden.
* <li>Install a <code>LayoutManager</code> on the component if necessary.
* <li>Create/add any required subcomponents to the component.
* <li>Create/install event listeners on the component.
* <li>Create/install a <code>PropertyChangeListener</code> on the component in order
* to detect and respond to component property changes appropriately.
* <li>Install keyboard UI (mnemonics, traversal, etc.) on the component.
* <li>Initialize any appropriate instance data.
* </ol><br>
* <p>
* <b>IMPORTANT:</b><br>
* For full <i>SwingTree</i> interoperability, implementations of this
* should invoke {@link swingtree.style.ComponentExtension#gatherApplyAndInstallStyle(boolean)}
* to ensure that the <i>SwingTree</i> style of a particular component is installed correctly.<br>
* So an implementation may look something like this:
* </p>
* <pre>{@code
* // Override
* public void installUI(
* JComponent comp
* ) {
* ComponentExtension.from(comp)
* .gatherApplyAndInstallStyle(true);
* }
* }</pre>
*
* @param c the component where this UI delegate is being installed
*
* @see javax.swing.plaf.ComponentUI#uninstallUI
* @see javax.swing.JComponent#updateUI
*/
void installUI(JComponent c);
/**
* This method must be implemented through {@link javax.swing.plaf.ComponentUI#paint(Graphics, JComponent)}!
* It paints the specified component appropriately for the look and feel.
* This method is invoked from the {@link javax.swing.plaf.ComponentUI#update(Graphics, JComponent)}
* method when the specified component is being painted. Subclasses should override
* this method and use the specified <code>Graphics</code> object to
* render the content of the component.<br>
* <b>
* For full <i>SwingTree</i> interoperability, you
* should override {@link #canForwardPaintingToSwingTree()} to return {@code true}
* and then forward the paint request to <i>SwingTree</i> like so:
* </b>
* <pre>{@code
* // Override
* public void paint(
* Graphics g,
* JComponent comp
* ) {
* ComponentExtension.from(comp)
* .paintBackground(g, g2d->{
* super.paint(g2d, comp);
* });
* }
* }</pre>
*
* @param g the <code>Graphics</code> context in which to paint
* @param c 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 #canForwardPaintingToSwingTree()
*/
void paint(Graphics g, JComponent c);
/**
* Receives a {@link ComponentStyleDelegate} and applies style information to it by
* transforming it to a new {@link ComponentStyleDelegate}. <br>
* <b>
* This styling will happen after/on-top of the style supplied by a {@link StyleSheet}
* but it will happen before the style directly declared for a specific component instance
* using the {@link swingtree.UIForAnySwing#withStyle(Styler)} method.
* </b>
* <br>
* Note that this method is designed to only be invoked by <i>SwingTree</i> internal code.
* You should never invoke it yourself.
*
* @param delegate The {@link ComponentStyleDelegate} to apply the style to.
* @return A new {@link ComponentStyleDelegate} that has the style applied.
* @throws Exception if the style could not be applied by the client code.
*/
ComponentStyleDelegate<C> style( ComponentStyleDelegate<C> delegate ) throws Exception;
/**
* If you want to achieve full compatibility and interoperability with SwingTree in your <i>Look and Feel</i>
* you have to forward {@link javax.swing.plaf.ComponentUI#paint(Graphics, JComponent)} and
* {@link javax.swing.plaf.ComponentUI#update(Graphics, JComponent)} draw calls to SwingTrees's
* {@link swingtree.style.ComponentExtension#paintBackground(Graphics, swingtree.api.Painter)} method!
* To inform SwingTree that you are going to do this, you also have to override <b>this method</b> and
* make it return {@code true}. This will turn your {@link javax.swing.plaf.ComponentUI} implementation
* into the main way in which <i>SwingTree</i> renders its style onto a specific component type.<br>
* So your implementation would look something like this:
* <pre>{@code
* // Override
* public void paint(
* Graphics g,
* JComponent comp
* ) {
* ComponentExtension.from(comp)
* .paintBackground(g, g2d->{
* super.paint(g2d, comp);
* });
* }
* // Override
* public boolean canForwardPaintingToSwingTree() {
* return true;
* }
* }</pre>
* <p>
* <b>WARNING:</b> <br>
* If this method returns {@code true}, but the {@link javax.swing.plaf.ComponentUI#paint(Graphics, JComponent)}
* implementation does NOT delegate to {@link swingtree.style.ComponentExtension#paintBackground(Graphics, swingtree.api.Painter)},
* <b>then you will effectively break the style rendering of your components!</b><br>
* This is because in that case, <i>SwingTree</i> relies entirely on your override
* to be the sole way of hooking into <i>Swing</i>s component rendering.
* </p>
* @return A flag which informs <i>SwingTree</i> if it can rely on your {@link javax.swing.plaf.ComponentUI#paint(Graphics, JComponent)}
* implementation to be used for hooking its style engine into the rendering pipeline of a component.
*/
default boolean canForwardPaintingToSwingTree() {
return false;
}
}