ComponentStyleDelegate.java

package swingtree.style;

import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import swingtree.UI;
import swingtree.api.*;
import swingtree.api.Painter;
import swingtree.layout.LayoutConstraint;
import swingtree.layout.Size;

import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;

/**
 *  A {@link ComponentStyleDelegate} is a delegate for a {@link JComponent} and its {@link StyleConf} configuration
 *  used to apply further specify the style of said {@link JComponent}.
 *  Instances of this will be exposed to you via the {@link swingtree.UIForAnySwing#withStyle(Styler)}
 *  method, where you can specify a lambda that takes a {@link ComponentStyleDelegate} and returns a
 *  transformed {@link StyleConf} object, as well as inside of {@link StyleSheet} extensions
 *  where you can declare similar styling lambdas for {@link StyleTrait}s, which are
 *  styling rules... <br>
 *
 * @param <C> The type of {@link JComponent} this {@link ComponentStyleDelegate} is for.
 */
public final class ComponentStyleDelegate<C extends JComponent>
{
    private static final Logger log = org.slf4j.LoggerFactory.getLogger(ComponentStyleDelegate.class);

    private final C _component;
    private final StyleConf _styleConf;


    ComponentStyleDelegate( C component, StyleConf styleConf) {
        _component = Objects.requireNonNull(component);
        _styleConf = Objects.requireNonNull(styleConf);
    }

    ComponentStyleDelegate<C> _withStyle( StyleConf styleConf) {
        return new ComponentStyleDelegate<>(_component, styleConf);
    }

    /**
     *  Returns the {@link JComponent} this {@link ComponentStyleDelegate} is defining a {@link StyleConf} for.
     *  This is useful if you want to make the styling of a component based on its state,
     *  like for example determining the background color of a {@link JCheckBox} based on
     *  whether it is selected or not...
     * 
     * @return The {@link JComponent} this {@link ComponentStyleDelegate} is for.
     */
    public C component() { return _component; }

    /**
     *  Exposes the parent {@link Container} of the {@link JComponent} delegated by this {@link ComponentStyleDelegate}
     *  through an {@link Optional} in case the parent is null.
     *  You may use this to make your styling dependent on the properties of the parent container.
     *
     * @return An optional parent {@link Container} of the {@link JComponent} this {@link ComponentStyleDelegate} is for.
     */
    public Optional<Container> parent() { return Optional.ofNullable(_component.getParent()); }

    /**
     *  Use this to peek at the {@link JComponent} of this {@link ComponentStyleDelegate}
     *  to perform some style-related component specific actions on it
     *  which are otherwise not found in the {@link ComponentStyleDelegate} API.
     *
     * @param peeker A {@link Peeker} that takes the {@link JComponent} of this {@link ComponentStyleDelegate}
     * @return This {@link ComponentStyleDelegate} instance.
     */
    public ComponentStyleDelegate<C> peek( Peeker<C> peeker )
    {
        Objects.requireNonNull(peeker);
        try {
            peeker.accept(_component);
        } catch( Exception e ) {
            log.error("Peeker threw an exception: " + e.getMessage(), e);
            // We don't want to crash the application if the peeker throws an exception.
        }
        return this;
    }

    /**
     *   Allows you to apply styles based on a condition.
     *   So if the first argument, the condition, is true,
     *   then it causes the supplied {@link Styler} to
     *   update the style, if however the condition is false,
     *   then the styler will simply be ignored
     *   and the style will not be updated.
     *   <br>
     *   Here a simple usage example:
     *   <pre>{@code
     *       UI.panel().withStyle( it -> it
     *          .border(3, Color.BLACK)
     *          .borderRadius(24)
     *          .applyIf(it.component().isEnabled(), it2 -> it2
     *              .borderColor(Color.LIGHT_GRAY)
     *              .backgroundColor(Color.CYAN)
     *          )
     *          .margin(3)
     *          .padding(4)
     *       );
     *   }</pre>
     *   This is conceptually similar to {@link swingtree.UIForAnySwing#applyIf(boolean, Consumer)}
     *   with the difference that it is based on a {@link Styler} instead of a consumer,
     *   as the style API is based on immutable types whose updated results must be returned
     *   by the conditional scope.
     *
     * @param condition The condition determining if the provided {@code styler} should be executed.
     * @param styler A supplier for
     * @return This instance if the condition is false, or the supplied {@code styler} threw an exception,
     *         a new style delegate updated according to the {@code styler}.
     */
    public ComponentStyleDelegate<C> applyIf(
        boolean condition,
        Styler<C> styler
    ) {
        Objects.requireNonNull(styler);

        if ( !condition )
            return this;

        try {
            return styler.style(this);
        } catch( Exception e ) {
            log.error("Conditional styler threw an exception: " + e.getMessage(), e);
            // We don't want to crash the application if the conditional styler throws an exception.
        }
        return this;
    }

    /**
     *  Returns the {@link StyleConf} this {@link ComponentStyleDelegate} is defining for the {@link JComponent}
     *  returned by {@link #component()}.
     * <p>
     * @return The {@link StyleConf} this {@link ComponentStyleDelegate} is for.
     */
    StyleConf style() { return _styleConf; }

    /**
     *  Creates a new {@link StyleConf} with the provided top, right, left and bottom margin distances.
     *  It determines the amount of space between the component's outer bounds and the beginning
     *  of the inner border, background region and shadow frame
     *  (see {@link #borderWidth(double)}, {@link #backgroundColor(Color)}, {@link #shadowColor(Color)}).
     * 
     * @param top The top padding distance in pixels.
     * @param right The right padding distance in pixels.
     * @param bottom The bottom padding distance in pixels.
     * @param left The left padding distance in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided padding distances.
     */
    public ComponentStyleDelegate<C> margin( double top, double right, double bottom, double left ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withMargin(Outline.of(top, right, bottom, left))));
    }

    /**
     *  Creates a new {@link StyleConf} with the provided margin distance for all sides of the component.
     *  The margin determines the amount of space between the component's outer bounds and the beginning
     *  of the inner border, background region and shadow frame
     *  (see {@link #borderWidth(double)}, {@link #backgroundColor(Color)}, {@link #shadowColor(Color)}).
     *
     * @param margin The margin distance in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided margin distance.
     */
    public ComponentStyleDelegate<C> margin( double margin ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withMargin(Outline.of((float) margin))));
    }

    /**
     *  Creates a new {@link StyleConf} with the provided margin distance for the top side of the component.
     *  The margin determines the amount of space between the component's outer bounds and the beginning
     *  of the inner border, background region and shadow frame
     *  (see {@link #borderWidth(double)}, {@link #backgroundColor(Color)}, {@link #shadowColor(Color)}).
     * 
     * @param margin The margin distance in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided margin distance.
     */
    public ComponentStyleDelegate<C> marginTop( double margin ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withMargin(_styleConf.border().margin().withTop((float) margin))));
    }

    /**
     *  Creates a new {@link StyleConf} with the provided margin distance for the right side of the component.
     *  The margin determines the amount of space between the component's outer bounds and the beginning
     *  of the inner border, background region and shadow frame
     *  (see {@link #borderWidth(double)}, {@link #backgroundColor(Color)}, {@link #shadowColor(Color)}).
     *
     * @param margin The margin distance in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided margin distance.
     */
    public ComponentStyleDelegate<C> marginRight( double margin ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withMargin(_styleConf.border().margin().withRight((float) margin))));
    }

    /**
     *  Creates a new {@link StyleConf} with the provided margin distance for the bottom side of the component.
     *  The margin determines the amount of space between the component's outer bounds and the beginning
     *  of the inner border, background region and shadow frame
     *  (see {@link #borderWidth(double)}, {@link #backgroundColor(Color)}, {@link #shadowColor(Color)}).
     *
     * @param margin The margin distance in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided margin distance.
     */
    public ComponentStyleDelegate<C> marginBottom( double margin ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withMargin(_styleConf.border().margin().withBottom((float) margin))));
    }

    /**
     *  Creates a new {@link StyleConf} with the provided margin distance for the left side of the component.
     *  The margin determines the amount of space between the component's outer bounds and the beginning
     *  of the inner border, background region and shadow frame
     *  (see {@link #borderWidth(double)}, {@link #backgroundColor(Color)}, {@link #shadowColor(Color)}).
     *
     * @param margin The margin distance in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided margin distance.
     */
    public ComponentStyleDelegate<C> marginLeft( double margin ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withMargin(_styleConf.border().margin().withLeft((float) margin))));
    }

    /**
     *  Creates a new {@link StyleConf} with the provided margin distance for the top and bottom sides of the component.
     *  The margin determines the amount of space between the component's outer bounds and the beginning
     *  of the inner border, background region and shadow frame
     *  (see {@link #borderWidth(double)}, {@link #backgroundColor(Color)}, {@link #shadowColor(Color)}).
     *
     * @param margin The margin distance in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided margin distance.
     */
    public ComponentStyleDelegate<C> marginVertical( double margin ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withMargin(_styleConf.border().margin().withTop((float) margin).withBottom((float) margin))));
    }

    /**
     *  Creates a new {@link StyleConf} with the provided margin distance for the left and right sides of the component.
     *  The margin determines the amount of space between the component's outer bounds and the beginning
     *  of the inner border, background region and shadow frame
     *  (see {@link #borderWidth(double)}, {@link #backgroundColor(Color)}, {@link #shadowColor(Color)}).
     *
     * @param margin The margin distance in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided margin distance.
     */
    public ComponentStyleDelegate<C> marginHorizontal( double margin ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withMargin(_styleConf.border().margin().withLeft((float) margin).withRight((float) margin))));
    }

    /**
     *  Creates a new {@link StyleConf} with the provided top, right, left and bottom pad distances.
     *  It determines the amount of space between the inner bounds (the inner border, background region and shadow frame)
     *  and the component's content.
     *  (see {@link #borderWidth(double)}, {@link #backgroundColor(Color)}, {@link #shadowColor(Color)}).
     *
     * @param top The top padding distance in pixels.
     * @param right The right padding distance in pixels.
     * @param bottom The bottom padding distance in pixels.
     * @param left The left padding distance in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided padding distances.
     */
    public ComponentStyleDelegate<C> padding( double top, double right, double bottom, double left ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withPadding(Outline.of(top, right, bottom, left))));
    }

    /**
     *  Creates a new {@link StyleConf} with the provided padding distance for all sides of the component.
     *  It determines the amount of space between the inner bounds (the inner border, background region and shadow frame)
     *  and the component's content.
     *  (see {@link #borderWidth(double)}, {@link #backgroundColor(Color)}, {@link #shadowColor(Color)}).
     *
     * @param padding The padding distance in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided padding distance.
     */
    public ComponentStyleDelegate<C> padding( double padding ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withPadding(Outline.of((float) padding))));
    }

    /**
     *  Creates a new {@link StyleConf} with the provided padding distance for the top side of the component.
     *  The padding determines the amount of space between the inner bounds (the inner border, background region and shadow frame)
     *  and the component's content.
     *  (see {@link #borderWidth(double)}, {@link #backgroundColor(Color)}, {@link #shadowColor(Color)}).
     *
     * @param padding The padding distance in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided padding distance.
     */
    public ComponentStyleDelegate<C> paddingTop( double padding ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withPadding(_styleConf.border().padding().withTop((float) padding))));
    }

    /**
     *  Creates a new {@link StyleConf} with the provided padding distance for the right side of the component.
     *  The padding determines the amount of space between the inner bounds (the inner border, background region and shadow frame)
     *  and the component's content.
     *  of the inner border, background region and shadow frame
     *  (see {@link #borderWidth(double)}, {@link #backgroundColor(Color)}, {@link #shadowColor(Color)}).
     *
     * @param padding The padding distance in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided padding distance.
     */
    public ComponentStyleDelegate<C> paddingRight( double padding ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withPadding(_styleConf.border().padding().withRight((float) padding))));
    }

    /**
     *  Creates a new {@link StyleConf} with the provided padding distance for the bottom side of the component.
     *  The padding determines the amount of space between the inner bounds (the inner border, background region and shadow frame)
     *  and the component's content.
     *  (see {@link #borderWidth(double)}, {@link #backgroundColor(Color)}, {@link #shadowColor(Color)}).
     *
     * @param padding The padding distance in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided padding distance.
     */
    public ComponentStyleDelegate<C> paddingBottom( double padding ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withPadding(_styleConf.border().padding().withBottom((float) padding))));
    }

    /**
     *  Creates a new {@link StyleConf} with the provided padding distance for the left side of the component.
     *  The padding determines the amount of space between the inner bounds (the inner border, background region and shadow frame)
     *  and the component's content.
     *  (see {@link #borderWidth(double)}, {@link #backgroundColor(Color)}, {@link #shadowColor(Color)}).
     *
     * @param padding The padding distance in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided padding distance.
     */
    public ComponentStyleDelegate<C> paddingLeft( double padding ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withPadding(_styleConf.border().padding().withLeft((float) padding))));
    }

    /**
     *  Creates a new {@link StyleConf} with the provided padding distance for the top and bottom sides of the component.
     *  The padding determines the amount of space between the inner bounds (the inner border, background region and shadow frame)
     *  and the component's content.
     *  (see {@link #borderWidth(double)}, {@link #backgroundColor(Color)}, {@link #shadowColor(Color)}).
     *
     * @param padding The padding distance in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided padding distance.
     */
    public ComponentStyleDelegate<C> paddingVertical( double padding ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withPadding(_styleConf.border().padding().withTop((float) padding).withBottom((float) padding))));
    }

    /**
     *  Creates a new {@link StyleConf} with the provided padding distance for the left and right sides of the component.
     *  The padding determines the amount of space between the inner bounds (the inner border, background region and shadow frame)
     *  and the component's content.
     *  (see {@link #borderWidth(double)}, {@link #backgroundColor(Color)}, {@link #shadowColor(Color)}).
     * 
     * @param padding The padding distance in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided padding distance.
     */
    public ComponentStyleDelegate<C> paddingHorizontal( double padding ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withPadding(_styleConf.border().padding().withLeft((float) padding).withRight((float) padding))));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided border width and border color.
     *  The border will be rendered with an inset space based on the margin defined by the {@link StyleConf}.
     *
     * @param width The border width in pixels.
     * @param color The border color.
     * @return A new {@link ComponentStyleDelegate} with the provided border width and border color.
     */
    public ComponentStyleDelegate<C> border( double width, Color color ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withWidth(width).withColor(color)));
    }

    /**
     *   Returns a new {@link StyleConf} with the provided border width and border colors.
     *   The border will be rendered with an inset space based on the margin defined by the {@link StyleConf}.
     *   You may configure the border colors for each side of the component individually.
     * @param width The border width in pixels.
     * @param top The color for the top part of the border.
     * @param right The color for the right part of the border.
     * @param bottom The color for the bottom part of the border.
     * @param left The color for the left part of the border.
     * @return A new {@link ComponentStyleDelegate} with the provided border width and border colors.
     */
    public ComponentStyleDelegate<C> border( double width, Color top, Color right, Color bottom, Color left ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withWidth(width).withColors(top, right, bottom, left)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided border widths and border color.
     *  The border will be rendered with an inset space based on the margin defined by the {@link StyleConf}.
     *
     * @param top The border width in pixels for the top side of the component.
     * @param right The border width in pixels for the right side of the component.
     * @param bottom The border width in pixels for the bottom side of the component.
     * @param left The border width in pixels for the left side of the component.
     * @param color The border color.
     * @return A new {@link ComponentStyleDelegate} with the provided border widths and border color.
     */
    public ComponentStyleDelegate<C> border( double top, double right, double bottom, double left, Color color ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withWidths(Outline.of(top, right, bottom, left)).withColor(color)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided border width and border color in the form of a string.
     *  The string can be either a hex color string, a color name or a color constant from the system properties
     *  (See {@link swingtree.UI#color(String)} for more information on the supported color formats).
     *  The border will be rendered with an inset space based on the padding defined by the {@link StyleConf}.
     *
     * @param width The border width in pixels.
     * @param colorString The border color.
     * @return A new {@link ComponentStyleDelegate} with the provided border width and border color.
     */
    public ComponentStyleDelegate<C> border( double width, String colorString ) {
        Objects.requireNonNull(colorString);
        Color newColor;
        try {
            newColor = UI.color(colorString);
        } catch ( Exception e ) {
            log.error("Failed to parse color string: '"+colorString+"'", e);
            return this;
        }
        return _withStyle(_styleConf._withBorder(_styleConf.border().withWidth(width).withColor(newColor)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided border width and border colors in the form of strings.
     *  The strings can be either hex color strings, color names or color constants from the system properties.
     *  The border will be rendered with an inset space based on the padding defined by the {@link StyleConf}.
     *
     * @param width The border width in pixels.
     * @param top The color for the top part of the border.
     * @param right The color for the right part of the border.
     * @param bottom The color for the bottom part of the border.
     * @param left The color for the left part of the border.
     * @return A new {@link ComponentStyleDelegate} with the provided border width and border colors.
     */
    public ComponentStyleDelegate<C> border( double width, String top, String right, String bottom, String left ) {
        Color topColor = UI.color(top);
        Color rightColor = UI.color(right);
        Color bottomColor = UI.color(bottom);
        Color leftColor = UI.color(left);
        return _withStyle(_styleConf._withBorder(_styleConf.border().withWidth(width).withColors(topColor, rightColor, bottomColor, leftColor)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided border width.
     *  <p>
     *  Note that in order for the border to be visible you also
     *  have to specify it's color, which you can do through
     *  {@link #borderColor(Color)} or {@link #borderColor(String)}.
     *  You may also specify different colors for each side of the border
     *  through {@link #borderColors(Color, Color, Color, Color)} or {@link #borderColors(String, String, String, String)}.
     *
     * @param width The border width in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided border width.
     */
    public ComponentStyleDelegate<C> borderWidth( double width ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withWidth(width)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided border colors,
     *  which are stored in the order of top, right, bottom, left.
     *  Note that a component border will be rendered with an inset
     *  space based on the padding defined by the {@link StyleConf}.
     *  <p>
     *  Instead of null, the {@link swingtree.UI.Color#UNDEFINED}
     *  constant is used to indicate that a border color is not set.
     *
     * @param top The color for the top part of the border.
     * @param right The color for the right part of the border.
     * @param bottom The color for the bottom part of the border.
     * @param left The color for the left part of the border.
     * @return A new {@link ComponentStyleDelegate} with the provided border colors.
     */
    public ComponentStyleDelegate<C> borderColors( Color top, Color right, Color bottom, Color left ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withColors(top, right, bottom, left)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided border colors in the form of strings.
     *  The strings can be either hex color strings, color names or color constants from the system properties
     *  (See {@link swingtree.UI#color(String)} for more information on the supported color formats).
     *  The border will be rendered with an inset space based on the padding defined by the {@link StyleConf}.
     *
     * @param top The color for the top part of the border.
     * @param right The color for the right part of the border.
     * @param bottom The color for the bottom part of the border.
     * @param left The color for the left part of the border.
     * @return A new {@link ComponentStyleDelegate} with the provided border colors.
     */
    public ComponentStyleDelegate<C> borderColors( String top, String right, String bottom, String left ) {
        Color topColor = UI.color(top);
        Color rightColor = UI.color(right);
        Color bottomColor = UI.color(bottom);
        Color leftColor = UI.color(left);
        return _withStyle(_styleConf._withBorder(_styleConf.border().withColors(topColor, rightColor, bottomColor, leftColor)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided border width for the specified edge.
     *  <p>
     *  Note that in order for the border to be visible you also
     *  have to specify it's color, which you can do through
     *  {@link #borderColor(Color)} or {@link #borderColor(String)}.
     *
     * @param edge The edge to set the border width for.
     * @param width The border width in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided border width for the specified edge.
     */
    public ComponentStyleDelegate<C> borderWidthAt( UI.Edge edge, double width ) {
        Objects.requireNonNull(edge);
        return _withStyle(_styleConf._withBorder(_styleConf.border().withWidthAt(edge, (float) width)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided width and color used to define
     *  the border for the specified edge of the component.
     *  The border will be rendered with an inset space based on the padding defined by the {@link StyleConf}.
     *
     * @param edge The edge to set the border width and color for, you may use {@link UI.Edge#EVERY}
     *             to set the same width and color for all edges.
     * @param width The border width in pixels.
     * @param color The border color.
     * @return A new {@link ComponentStyleDelegate} with the provided border width and color for the specified edge.
     */
    public ComponentStyleDelegate<C> borderAt( UI.Edge edge, double width, Color color ) {
        Objects.requireNonNull(edge);
        return _withStyle(_styleConf._withBorder(_styleConf.border().withWidthAt(edge, (float) width).withColorAt(edge, color)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided width and color string used to define
     *  the border for the specified edge of the component.
     *  The border will be rendered with an inset space based on the padding defined by the {@link StyleConf}.
     *  <p>
     *  The color is specified as a string, which can be either a hex color string, a color name or a color constant
     *  from the system properties (See {@link swingtree.UI#color(String)} for more information on the supported color formats).
     *
     * @param edge The edge to set the border width and color for, you may use {@link UI.Edge#EVERY}
     *             to set the same width and color for all edges.
     * @param width The border width in pixels.
     * @param colorString The border color.
     * @return A new {@link ComponentStyleDelegate} with the provided border width and color for the specified edge.
     */
    public ComponentStyleDelegate<C> borderAt( UI.Edge edge, double width, String colorString ) {
        Objects.requireNonNull(edge);
        Color newColor;
        try {
            newColor = UI.color(colorString);
        } catch ( Exception e ) {
            log.error("Failed to parse color string: '"+colorString+"'", e);
            return this;
        }
        return _withStyle(_styleConf._withBorder(_styleConf.border().withWidthAt(edge, (float) width).withColorAt(edge, newColor)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided top, right, bottom and left border widths.
     *  <p>
     *  The border widths are specified in the following order: top, right, bottom, left.
     *  <p>
     *  Example:
     *  <pre>{@code
     *      UI.panel().withStyle( it -> it.borderWidths(1, 2, 3, 4) )
     *  }</pre>
     *  <p>
     *  Note that in order for the border to be visible you also
     *  have to specify it's color, which you can do through
     *  {@link #borderColor(Color)} or {@link #borderColor(String)}.
     *
     * @param top The top border width in pixels.
     * @param right The right border width in pixels.
     * @param bottom The bottom border width in pixels.
     * @param left The left border width in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided top, right, bottom and left border widths.
     * @see #borderWidth(double)
     * @see #borderWidthAt(UI.Edge, double)
     */
    public ComponentStyleDelegate<C> borderWidths( double top, double right, double bottom, double left ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withWidths(Outline.of(top, right, bottom, left))));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided top/bottom and left/right border widths.
     *  <p>
     *  Example:
     *  <pre>{@code
     *      UI.panel().withStyle( it -> it.borderWidths(1, 2) )
     *  }</pre>
     *  <p>
     *  Note that in order for the border to be visible you also
     *  have to specify it's color, which you can do through
     *  {@link #borderColor(Color)} or {@link #borderColor(String)}.
     *
     * @param topBottom The top and bottom border width in pixels.
     * @param leftRight The left and right border width in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided top/bottom and left/right border widths.
     * @see #borderWidth(double)
     * @see #borderWidthAt(UI.Edge, double)
     */
    public ComponentStyleDelegate<C> borderWidths( double topBottom, double leftRight ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withWidths(Outline.of(topBottom, leftRight, topBottom, leftRight))));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided border color.
     *  The border will be rendered with an inset space based on the padding defined by the {@link StyleConf}.
     *
     * @param color The border color.
     * @return A new {@link ComponentStyleDelegate} with the provided border color.
     */
    public ComponentStyleDelegate<C> borderColor( Color color ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withColor(color)));
    }

    /**
     *  Returns an updated {@link StyleConf} with the provided border color in the form of a string.
     *  The string can be either a hex color string, a color name or a color constant from the system properties.
     *  The border will be rendered with an inset space based on the padding defined by the {@link StyleConf}.
     *
     * @param colorString The border color.
     * @return A new {@link ComponentStyleDelegate} with the provided border color.
     */
    public ComponentStyleDelegate<C> borderColor( String colorString ) {
        Objects.requireNonNull(colorString);
        Color newColor;
        try {
            newColor = UI.color(colorString);
        } catch ( Exception e ) {
            log.error("Failed to parse color string: '{}'", colorString, e);
            return this;
        }
        return _withStyle(_styleConf._withBorder(_styleConf.border().withColor(newColor)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided border radius
     *  set for all 4 corners of the target component.
     *  This will override both the arc width and arc height of each corner.
     *  The border will be rendered with an inset space based on the padding defined by this {@link StyleConf}.
     *
     * @param radius The border radius in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided border radius.
     */
    public ComponentStyleDelegate<C> borderRadius( double radius ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withArcWidth(radius).withArcHeight(radius)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided border arc width and arc height
     *  set for all 4 corners of the target component.
     *  Note that the border will be rendered with an inset space based on the padding defined by this {@link StyleConf}.
     *
     * @param arcWidth The border arc width in pixels.
     * @param arcHeight The border arc height in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided border arc width and arc height.
     */
    public ComponentStyleDelegate<C> borderRadius( double arcWidth, double arcHeight ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withArcWidth(arcWidth).withArcHeight(arcHeight)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided border arc width and arc height for the specified corner.
     *  Note that the border will be rendered with an inset space based on the padding defined by this {@link StyleConf}.
     *
     * @param corner The corner to apply the border radius to.
     * @param arcWidth The border arc width in pixels.
     * @param arcHeight The border arc height in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided border arc width and arc height for the specified corner.
     */
    public ComponentStyleDelegate<C> borderRadiusAt( UI.Corner corner, double arcWidth, double arcHeight ) {
        return _withStyle(_styleConf._withBorder(_styleConf.border().withArcWidthAt(corner, arcWidth).withArcHeightAt(corner, arcHeight)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided border radius for the specified corner.
     *  This will override both the arc width and arc height of the border.
     *  Note that the border will be rendered with an inset space based on the padding defined by this {@link StyleConf}.
     *
     * @param corner The corner to apply the border radius to.
     * @param radius The border radius in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided border radius for the specified corner.
     */
    public ComponentStyleDelegate<C> borderRadiusAt( UI.Corner corner, double radius ) {
        return this.borderRadiusAt(corner, radius, radius);
    }

    /**
     *  Returns a new {@link StyleConf} with the provided {@link ImageIcon} as the icon
     *  for the current component (see {@link #component()}).
     *  Note that this will only produce a result for components that actually support icons.
     *  Like for example all the various {@link AbstractButton} subclasses, {@link JLabel}
     *  and {@link swingtree.components.JIcon}.
     *
     * @param icon The icon.
     * @return A new {@link ComponentStyleDelegate} with the provided icon.
     */
    public ComponentStyleDelegate<C> icon( ImageIcon icon ) {
        return _withStyle(_styleConf._withBase(_styleConf.base().icon(icon)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided {@link ImageIcon} as the icon
     *  for the current component (see {@link #component()}) and the provided fit mode
     *  determining how the icon should be fitted to the component.
     *  Note that this will only work for components that support icons.
     *  Like for example all the various {@link AbstractButton} subclasses, {@link JLabel}
     *  and {@link swingtree.components.JIcon}.
     *
     * @param icon The icon in the form of an {@link ImageIcon}.
     * @param fit The fit mode for the icon (mostly intended for {@link SvgIcon}).
     * @return A new {@link ComponentStyleDelegate} with the provided icon.
     */
    public ComponentStyleDelegate<C> icon( ImageIcon icon, UI.FitComponent fit ) {
        return _withStyle(_styleConf._withBase(_styleConf.base().icon(icon).fit(fit)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided {@link IconDeclaration} as the
     *  source for the icon of the current component (see {@link #component()}).
     *  Note that this will only have an effect for components that support icons.
     *  Like for example all the various {@link AbstractButton} subclasses, {@link JLabel}
     *  and {@link swingtree.components.JIcon}.
     *
     * @param icon The icon declaration, which will be resolved to an {@link ImageIcon}.
     * @return A new {@link ComponentStyleDelegate} with the provided icon.
     */
    public ComponentStyleDelegate<C> icon( IconDeclaration icon ) {
        return icon.find().map(this::icon).orElse(this);
    }

    /**
     *  Returns a new {@link StyleConf} with the provided {@link IconDeclaration} as the
     *  source for the icon of the current component (see {@link #component()}) and the provided fit mode
     *  determining how the icon should be fitted to the component.
     *  Note that this will only have an effect for components that support icons.
     *  Like for example all the various {@link AbstractButton} subclasses, {@link JLabel}
     *  and {@link swingtree.components.JIcon}.
     *
     * @param icon The icon declaration, which will be resolved to an {@link ImageIcon}.
     * @param fit The fit mode for the icon (mostly intended for {@link SvgIcon}).
     * @return A new {@link ComponentStyleDelegate} with the provided icon.
     */
    public ComponentStyleDelegate<C> icon( IconDeclaration icon, UI.FitComponent fit ) {
        return icon.find().map(it -> icon(it, fit)).orElse(this);
    }

    /**
     *  Returns a new {@link StyleConf} with the provided background foundation color.
     *  The foundation color covers the {@link UI.ComponentArea#EXTERIOR}, which
     *  starts at the outer bounds of the component and the beginning of the border.
     *  So the space spanned by the margins of the component including the additional
     *  exterior space exposed by the border radius.
     *
     * @param color The background color.
     * @return A new {@link ComponentStyleDelegate} with the provided background color.
     */
    public ComponentStyleDelegate<C> foundationColor( Color color ) {
        Objects.requireNonNull(color, "Use 'UI.Color.UNDEFINED' instead of 'null'.");
        return _withStyle(_styleConf._withBase(_styleConf.base().foundationColor(color)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided background foundation color in the form of a string.
     *  The string can be either a hex color string, a color name or a color constant from the system properties.
     *  The foundation color covers the {@link UI.ComponentArea#EXTERIOR}, which
     *  starts at the outer bounds of the component and the beginning of the border.
     *  So the space spanned by the margins of the component including the additional
     *  exterior space exposed by the border radius.
     *
     * @param colorString The background color.
     * @return A new {@link ComponentStyleDelegate} with the provided background color.
     */
    public ComponentStyleDelegate<C> foundationColor( String colorString ) {
        Objects.requireNonNull(colorString);
        Color newColor;
        try {
            newColor = UI.color(colorString);
        } catch ( Exception e ) {
            log.error("Failed to parse color string: '"+colorString+"'", e);
            return this;
        }
        return _withStyle(_styleConf._withBase(_styleConf.base().foundationColor(newColor)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided background foundation color
     *  defined by the supplied red, green and blue color channels in
     *  the form of doubles expected to be in the range of 0.0 to 1.0.
     *  The foundation color covers the {@link UI.ComponentArea#EXTERIOR}, which
     *  starts at the outer bounds of the component and the beginning of the border.
     *  So the space spanned by the margins of the component including the additional
     *  exterior space exposed by the border radius.
     *
     * @param r The red component of the background color in the range of 0.0 to 1.0.
     * @param g The green component of the background color in the range of 0.0 to 1.0.
     * @param b The blue component of the background color in the range of 0.0 to 1.0.
     * @return A new {@link ComponentStyleDelegate} with the provided background color.
     */
    public ComponentStyleDelegate<C> foundationColor( double r, double g, double b ) {
        return foundationColor(new Color((float) r, (float) g, (float) b));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided background foundation color
     *  defined by the supplied red, green, blue and alpha color channels in
     *  the form of doubles expected to be in the range of 0.0 to 1.0.
     *  The foundation color covers the {@link UI.ComponentArea#EXTERIOR}, which
     *  starts at the outer bounds of the component and the beginning of the border.
     *  So the space spanned by the margins of the component including the additional
     *  exterior space exposed by the border radius.
     *
     * @param r The red component of the background color in the range of 0.0 to 1.0.
     * @param g The green component of the background color in the range of 0.0 to 1.0.
     * @param b The blue component of the background color in the range of 0.0 to 1.0.
     * @param a The alpha component of the background color in the range of 0.0 to 1.0.
     * @return A new {@link ComponentStyleDelegate} with the provided background color.
     */
    public ComponentStyleDelegate<C> foundationColor( double r, double g, double b, double a ) {
        return foundationColor(new Color((float) r, (float) g, (float) b, (float) a));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided inner Background color.
     *  The background color covers the {@link UI.ComponentArea#INTERIOR}, which, when going inwards,
     *  starts at the end of the component's border area ({@link UI.ComponentArea#BORDER}),
     *  (which is defined by {@link UI.ComponentBoundary#BORDER_TO_INTERIOR})
     *  and then completely fills the component's inner bounds ({@link UI.ComponentArea#INTERIOR}),
     *  including both the space spanned by the padding and the content area.
     *
     * @param color The inner background color.
     * @return A new {@link ComponentStyleDelegate} with the provided inner background color.
     */
    public ComponentStyleDelegate<C> backgroundColor( Color color ) {
        Objects.requireNonNull(color, "Use 'UI.Color.UNDEFINED' instead of 'null'.");
        return _withStyle(_styleConf._withBase(_styleConf.base().backgroundColor(color)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided inner Background color
     *  defined by the supplied red, green and blue color channels in
     *  the form of doubles expected to be in the range of 0.0 to 1.0.
     *  The background color covers the {@link UI.ComponentArea#INTERIOR}, which, when going inwards,
     *  starts at the end of the component's border area ({@link UI.ComponentArea#BORDER}),
     *  (which is defined by {@link UI.ComponentBoundary#BORDER_TO_INTERIOR})
     *  and then completely fills the component's inner bounds ({@link UI.ComponentArea#INTERIOR}),
     *  including both the space spanned by the padding and the content area.
     *
     * @param r The red component of the inner background color in the range of 0.0 to 1.0.
     * @param g The green component of the inner background color in the range of 0.0 to 1.0.
     * @param b The blue component of the inner background color in the range of 0.0 to 1.0.
     * @return A new {@link ComponentStyleDelegate} with the provided inner background color.
     */
    public ComponentStyleDelegate<C> backgroundColor( double r, double g, double b ) {
        return backgroundColor(new Color((float) r, (float) g, (float) b));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided inner Background color
     *  defined by the supplied red, green, blue and alpha color channels in
     *  the form of doubles expected to be in the range of 0.0 to 1.0.
     *  The background color covers the {@link UI.ComponentArea#INTERIOR}, which, when going inwards,
     *  starts at the end of the component's border area ({@link UI.ComponentArea#BORDER}),
     *  (which is defined by {@link UI.ComponentBoundary#BORDER_TO_INTERIOR})
     *  and then completely fills the component's inner bounds ({@link UI.ComponentArea#INTERIOR}),
     *  including both the space spanned by the padding and the content area.
     *
     * @param r The red component of the inner background color in the range of 0.0 to 1.0.
     * @param g The green component of the inner background color in the range of 0.0 to 1.0.
     * @param b The blue component of the inner background color in the range of 0.0 to 1.0.
     * @param a The alpha component of the inner background color in the range of 0.0 to 1.0.
     * @return A new {@link ComponentStyleDelegate} with the provided inner background color.
     */
    public ComponentStyleDelegate<C> backgroundColor( double r, double g, double b, double a ) {
        return backgroundColor(new Color((float) r, (float) g, (float) b, (float) a));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided inner Background color in the form of a string.
     *  The string can be either a hex color string, a color name or a color constant from the system properties.
     *  The background color covers the {@link UI.ComponentArea#INTERIOR}, which, when going inwards,
     *  starts at the end of the component's border area ({@link UI.ComponentArea#BORDER}),
     *  (which is defined by {@link UI.ComponentBoundary#BORDER_TO_INTERIOR})
     *  and then completely fills the component's inner bounds ({@link UI.ComponentArea#INTERIOR}),
     *  including both the space spanned by the padding and the content area.
     *
     * @param colorString The inner background color.
     * @return A new {@link ComponentStyleDelegate} with the provided inner background color.
     */
    public ComponentStyleDelegate<C> backgroundColor( String colorString ) {
        Objects.requireNonNull(colorString);
        Color newColor;
        try {
            newColor = UI.color(colorString);
        } catch ( Exception e ) {
            log.error("Failed to parse color string: '"+colorString+"'", e);
            return this;
        }
        return _withStyle(_styleConf._withBase(_styleConf.base().backgroundColor(newColor)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided custom {@link swingtree.api.Painter}, which
     *  will be called using the {@link Graphics2D} of the current component.
     *  You may use this to render a custom background for the component.
     *  <br><br>
     *  <b>
     *    Note that your custom painter will yield the best performance if they are value based in the sense that
     *    they have {@link Object#hashCode()} and {@link Object#equals(Object)} implementation which
     *    are based on the data that the painter uses to render the component.
     *    This is because it allows SwingTree to cache the rendering of the painters and avoid unnecessary repaints. <br>
     *    If you do not want to create a custom class just for painting but instead
     *    just want to pass an immutable cache key to a painter, then consider using the
     *    {@link Painter#of(Object, Painter)} factory method to create a painter that has the
     *    with {@link Object#hashCode()} and {@link Object#equals(Object)} implemented
     *    based on the provided cache key.
     *  </b>
     *
     * @param layer The layer on which the painter should do its work.
     *              It is an enum instance which
     *              gives the painter a particular rank in the painting order.
     *              So the {@link swingtree.UI.Layer#BACKGROUND} will be painted first,
     *              followed by the {@link swingtree.UI.Layer#CONTENT} and so on...
     *              <br>
     *              The following layers are available:
     *              <ul>
     *                  <li>{@link UI.Layer#BACKGROUND}</li>
     *                  <li>{@link UI.Layer#CONTENT}</li>
     *                  <li>{@link UI.Layer#BORDER}</li>
     *                  <li>{@link UI.Layer#FOREGROUND}</li>
     *              </ul>
     * @param painter A custom painter, which receives the {@link Graphics2D} instance of the current component.
     * @return A new {@link ComponentStyleDelegate} with the provided background renderer.
     */
    public ComponentStyleDelegate<C> painter( UI.Layer layer, swingtree.api.Painter painter ) {
        return _withStyle(_styleConf.painter(layer, UI.ComponentArea.INTERIOR, StyleUtil.DEFAULT_KEY, painter));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided custom {@link swingtree.api.Painter}, which
     *  will be called using the {@link Graphics2D} of the current component.
     *  You may use this to render a custom background for the component on the specified {@link swingtree.UI.Layer}
     *  and {@link swingtree.UI.ComponentArea}.
     *  <br><br>
     *  <b>
     *    Note that your custom painter will yield the best performance if they are value based in the sense that
     *    they have {@link Object#hashCode()} and {@link Object#equals(Object)} implementation which
     *    are based on the data that the painter uses to render the component.
     *    This is because it allows SwingTree to cache the rendering of the painters and avoid unnecessary repaints. <br>
     *    If you do not want to create a custom class just for painting but instead
     *    just want to pass an immutable cache key to a painter, then consider using the
     *    {@link Painter#of(Object, Painter)} factory method to create a painter that has the
     *    with {@link Object#hashCode()} and {@link Object#equals(Object)} implemented
     *    based on the provided cache key.
     *  </b>
     * @param layer The layer on which the painter should do its work.
     *              It is an enum instance which
     *              gives the painter a particular rank in the painting order.
     *              So the {@link swingtree.UI.Layer#BACKGROUND} will be painted first,
     *              followed by the {@link swingtree.UI.Layer#CONTENT} and so on...
     *              <br>
     *              The following layers are available:
     *              <ul>
     *                  <li>{@link UI.Layer#BACKGROUND}</li>
     *                  <li>{@link UI.Layer#CONTENT}</li>
     *                  <li>{@link UI.Layer#BORDER}</li>
     *                  <li>{@link UI.Layer#FOREGROUND}</li>
     *              </ul>
     * @param clipArea The area to which the painting should be confined. Paint operations outside of this area will be clipped away.
     *                 The following areas are available:
     *                 <ul>
     *                      <li>{@link UI.ComponentArea#ALL}</li>
     *                      <li>{@link UI.ComponentArea#EXTERIOR}</li>
     *                      <li>{@link UI.ComponentArea#BORDER}</li>
     *                      <li>{@link UI.ComponentArea#INTERIOR}</li>
     *                      <li>{@link UI.ComponentArea#BODY}</li>
     *                 </ul>
     * @param painter A custom painter, which receives the {@link Graphics2D} instance of the current component.
     * @return A new {@link ComponentStyleDelegate} with the provided background renderer.
     */
    public ComponentStyleDelegate<C> painter(
        UI.Layer              layer,
        UI.ComponentArea      clipArea,
        swingtree.api.Painter painter
    ) {
        return _withStyle(_styleConf.painter(layer, clipArea, StyleUtil.DEFAULT_KEY, painter));
    }


    /**
     *  Returns a new {@link StyleConf} with the provided named {@link swingtree.api.Painter}, which
     *  will be called using the {@link Graphics2D} instance of the current component.
     *  You may use this to render custom styles for the component... <br>
     *  The name can be used to override {@link swingtree.api.Painter} instances with that same name
     *  or use a unique name to ensure that you style is not overridden by another style.
     *  This allows you to attach an arbitrary number of custom painters to a component.
     *  <br><br>
     *  <b>
     *    Note that your custom painter will yield the best performance if they are value based in the sense that
     *    they have {@link Object#hashCode()} and {@link Object#equals(Object)} implementation which
     *    are based on the data that the painter uses to render the component.
     *    This is because it allows SwingTree to cache the rendering of the painters and avoid unnecessary repaints. <br>
     *    If you do not want to create a custom class just for painting but instead
     *    just want to pass an immutable cache key to a painter, then consider using the
     *    {@link Painter#of(Object, Painter)} factory method to create a painter that has the
     *    with {@link Object#hashCode()} and {@link Object#equals(Object)} implemented
     *    based on the provided cache key.
     *  </b>
     *
     * @param layer The layer on which the painter should do its work.
     *              It is an enum instance which
     *              gives the painter a particular rank in the painting order.
     *              So the {@link swingtree.UI.Layer#BACKGROUND} will be painted first,
     *              followed by the {@link swingtree.UI.Layer#CONTENT} and so on...
     *              <br>
     *              The following layers are available:
     *              <ul>
     *                  <li>{@link UI.Layer#BACKGROUND}</li>
     *                  <li>{@link UI.Layer#CONTENT}</li>
     *                  <li>{@link UI.Layer#BORDER}</li>
     *                  <li>{@link UI.Layer#FOREGROUND}</li>
     *              </ul>
     * @param painterName The name of the painter.
     * @param painter The custom painter lambda to which the {@link Graphics2D} instance of the current component will be passed.
     * @return A new {@link ComponentStyleDelegate} with the provided background renderer.
     */
    public ComponentStyleDelegate<C> painter( UI.Layer layer, String painterName, swingtree.api.Painter painter ) {
        return _withStyle(_styleConf.painter(layer, UI.ComponentArea.INTERIOR, painterName, painter));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided named {@link swingtree.api.Painter}, which
     *  will be called using the {@link Graphics2D} instance of the current component.
     *  You may use this to render custom styles for the component... <br>
     *  The name can be used to override {@link swingtree.api.Painter} instances with that same name
     *  or use a unique name to ensure that you style is not overridden by another style.
     *  This allows you to attach an arbitrary number of custom painters to a component.
     *  <br><br>
     *  <b>
     *    Note that your custom painter will yield the best performance if they are value based in the sense that
     *    they have {@link Object#hashCode()} and {@link Object#equals(Object)} implementation which
     *    are based on the data that the painter uses to render the component.
     *    This is because it allows SwingTree to cache the rendering of the painters and avoid unnecessary repaints. <br>
     *    If you do not want to create a custom class just for painting but instead
     *    just want to pass an immutable cache key to a painter, then consider using the
     *    {@link Painter#of(Object, Painter)} factory method to create a painter that has the
     *    with {@link Object#hashCode()} and {@link Object#equals(Object)} implemented
     *    based on the provided cache key.
     *  </b>
     *
     * @param layer The layer on which the painter should do its work.
     *              It is an enum instance which
     *              gives the painter a particular rank in the painting order.
     *              So the {@link swingtree.UI.Layer#BACKGROUND} will be painted first,
     *              followed by the {@link swingtree.UI.Layer#CONTENT} and so on...
     *              <br>
     *              The following layers are available:
     *              <ul>
     *                  <li>{@link UI.Layer#BACKGROUND}</li>
     *                  <li>{@link UI.Layer#CONTENT}</li>
     *                  <li>{@link UI.Layer#BORDER}</li>
     *                  <li>{@link UI.Layer#FOREGROUND}</li>
     *              </ul>
     * @param clipArea The area to which the painting should be confined. Paint operations outside of this area will be clipped away.
     *                 The following areas are available:
     *                 <ul>
     *                      <li>{@link UI.ComponentArea#ALL}</li>
     *                      <li>{@link UI.ComponentArea#EXTERIOR}</li>
     *                      <li>{@link UI.ComponentArea#BORDER}</li>
     *                      <li>{@link UI.ComponentArea#INTERIOR}</li>
     *                      <li>{@link UI.ComponentArea#BODY}</li>
     *                 </ul>
     * @param painterName The name of the painter.
     * @param painter The custom painter lambda to which the {@link Graphics2D} instance of the current component will be passed.
     * @return A new {@link ComponentStyleDelegate} with the provided background renderer.
     */
    public ComponentStyleDelegate<C> painter(
        UI.Layer              layer,
        UI.ComponentArea      clipArea,
        String                painterName,
        swingtree.api.Painter painter
    ) {
        return _withStyle(_styleConf.painter(layer, clipArea, painterName, painter));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided foreground color.
     *
     * @param color The foreground color.
     * @return A new {@link ComponentStyleDelegate} with the provided foreground color.
     */
    public ComponentStyleDelegate<C> foregroundColor( Color color ) {
        Objects.requireNonNull(color, "Use 'UI.Color.UNDEFINED' instead of 'null'.");
        return _withStyle(_styleConf._withBase(_styleConf.base().foregroundColor(color)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided foreground color in the form of a string.
     *  The string can be either a hex color string, a color name or a color constant from the system properties.
     *
     * @param colorString The foreground color.
     * @return A new {@link ComponentStyleDelegate} with the provided foreground color.
     */
    public ComponentStyleDelegate<C> foregroundColor( String colorString ) {
        Objects.requireNonNull(colorString);
        Color newColor;
        try {
            newColor = UI.color(colorString);
        } catch ( Exception e ) {
            log.error("Failed to parse color string: '"+colorString+"'", e);
            return this;
        }
        return _withStyle(_styleConf._withBase(_styleConf.base().foregroundColor(newColor)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided horizontal shadow offset applied to all shadow configs.
     *  The shadow will be rendered with an inset space based on the padding defined by this {@link StyleConf}.
     *  Note that in order to see the shadow, you may also need to call {@link #shadowSpreadRadius(double)},
     *  {@link #shadowBlurRadius(double)} and {@link #shadowColor(Color)}. <br>
     *  Note that this property will not only be applied to the default shadow, but also any
     *  other named shadow that you may have defined using {@link #shadow(String, Configurator)}.
     *
     * @param offset The shadow offset in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided horizontal shadow offset.
     */
    public ComponentStyleDelegate<C> shadowHorizontalOffset( double offset ) {
        return _withStyle(_styleConf._withShadow(shadow -> shadow.horizontalOffset((float) offset)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided vertical shadow offset applied to all shadow configs.
     *  The shadow will be rendered with an inset space based on the padding defined by this {@link StyleConf}.
     *  Note that in order to see the shadow, you may also need to call {@link #shadowSpreadRadius(double)},
     *  {@link #shadowBlurRadius(double)} and {@link #shadowColor(Color)}. <br>
     *  Note that this property will not only be applied to the default shadow, but also any
     *  other named shadow that you may have defined using {@link #shadow(String, Configurator)}.
     *
     * @param offset The shadow offset in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided vertical shadow offset.
     */
    public ComponentStyleDelegate<C> shadowVerticalOffset( double offset ) {
        return _withStyle(_styleConf._withShadow(shadow -> shadow.verticalOffset((float) offset)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided shadow offset applied to all shadow configs.
     *  The shadow will be rendered with an inset space based on the padding defined by this {@link StyleConf}.
     *  Note that in order to see the shadow, you may also need to call {@link #shadowSpreadRadius(double)},
     *  {@link #shadowBlurRadius(double)} and {@link #shadowColor(Color)}. <br>
     *  Note that this property will not only be applied to the default shadow, but also any
     *  other named shadow that you may have defined using {@link #shadow(String, Configurator)}.
     *
     * @param horizontalOffset The horizontal shadow offset in pixels.
     * @param verticalOffset The vertical shadow offset in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided shadow offset.
     */
    public ComponentStyleDelegate<C> shadowOffset( double horizontalOffset, double verticalOffset ) {
        return _withStyle(_styleConf._withShadow(shadow -> shadow.horizontalOffset((float) horizontalOffset).verticalOffset((float) verticalOffset)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided horizontal and vertical shadow offset.
     *  The shadow will be rendered with an inset space based on the padding defined by this {@link StyleConf}.
     *  Note that in order to see the shadow, you may also need to call {@link #shadowSpreadRadius(double)},
     *  {@link #shadowBlurRadius(double)} and {@link #shadowColor(Color)}. <br>
     *  Note that this property will not only be applied to the default shadow, but also any
     *  other named shadow that you may have defined using {@link #shadow(String, Configurator)}.
     *
     * @param horizontalAndVerticalOffset The horizontal and vertical shadow offset in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided shadow offset.
     */
    public ComponentStyleDelegate<C> shadowOffset( double horizontalAndVerticalOffset ) {
        return _withStyle(_styleConf._withShadow(shadow -> shadow.horizontalOffset((float) horizontalAndVerticalOffset).verticalOffset((float) horizontalAndVerticalOffset)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided shadow blur radius applied to all shadow configs.
     *  The shadow will be rendered with an inset space based on the padding defined by this {@link StyleConf}.
     *  Note that in order to see the shadow, you may also need to call
     *  {@link #shadowSpreadRadius(double)} and {@link #shadowColor(Color)}. <br>
     *  Note that this property will not only be applied to the default shadow, but also any
     *  other named shadow that you may have defined using {@link #shadow(String, Configurator)}.
     *
     * @param radius The shadow blur radius in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided shadow blur radius.
     */
    public ComponentStyleDelegate<C> shadowBlurRadius( double radius ) {
        return _withStyle(_styleConf._withShadow(shadow -> shadow.blurRadius((float) radius)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided shadow spread radius applied to all shadow configs.
     *  The shadow will be rendered with an inset space based on the padding defined by this {@link StyleConf}.
     *  Note that in order to see the shadow, you may also need to call
     *  {@link #shadowBlurRadius(double)} and {@link #shadowColor(Color)}. <br>
     *  Note that this property will not only be applied to the default shadow, but also any
     *  other named shadow that you may have defined using {@link #shadow(String, Configurator)}.
     *
     * @param radius The shadow spread radius in pixels.
     * @return A new {@link ComponentStyleDelegate} with the provided shadow spread radius.
     */
    public ComponentStyleDelegate<C> shadowSpreadRadius( double radius ) {
        return _withStyle(_styleConf._withShadow(shadow -> shadow.spreadRadius((float) radius)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided shadow color applied to the default shadow.
     *  Note that in order to see the shadow, you may also need to call
     *  {@link #shadowBlurRadius(double)} and {@link #shadowSpreadRadius(double)}. <br>
     *  The shadow will be rendered on the {@link UI.Layer#CONTENT} layer,
     *  if you want it to be rendered on a different layer, you
     *  may want to take a look at {@link #shadow(UI.Layer, String, Configurator)}. <br>
     *  <br>
     *  Note that this property will not only be applied to the default shadow, but also any
     *  other named shadow that you may have defined using {@link #shadow(String, Configurator)}
     *  (and which are also rendered on the {@link UI.Layer#CONTENT} layer).
     *
     * @param color The shadow color.
     * @return A new {@link ComponentStyleDelegate} with the provided shadow color.
     */
    public ComponentStyleDelegate<C> shadowColor( Color color ) {
        return _withStyle(_styleConf._withShadow(ShadowConf.DEFAULT_LAYER, shadow -> shadow.color(color)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided shadow color in the form of a string.
     *  The string can be either a hex color string, a color name or a color constant from the system properties.
     *  Note that in order to see the shadow, you may also need to call
     *  {@link #shadowBlurRadius(double)} and {@link #shadowSpreadRadius(double)}. <br>
     *  The shadow will be rendered on the {@link UI.Layer#CONTENT} layer,
     *  if you want it to be rendered on a different layer, you
     *  may want to take a look at {@link #shadow(UI.Layer, String, Configurator)}. <br>
     *  <br>
     *  Note that this property will not only be applied to the default shadow, but also any
     *  other named shadow that you may have defined using {@link #shadow(String, Configurator)}
     *  (and which are also rendered on the {@link UI.Layer#CONTENT} layer).
     *
     * @param colorString The shadow color.
     * @return A new {@link ComponentStyleDelegate} with the provided shadow color.
     */
    public ComponentStyleDelegate<C> shadowColor( String colorString ) {
        Objects.requireNonNull(colorString);
        Color newColor;
        try {
            newColor = UI.color(colorString);
        } catch ( Exception e ) {
            log.error("Failed to parse color string: '"+colorString+"'", e);
            return this;
        }
        return _withStyle(_styleConf._withShadow(ShadowConf.DEFAULT_LAYER, shadow -> shadow.color(newColor)));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided shadow color
     *  defined by the supplied red, green and blue color channels in
     *  the form of doubles expected to be in the range of 0.0 to 1.0.
     *  Note that in order to see the shadow, you may also need to call
     *  {@link #shadowBlurRadius(double)} and {@link #shadowSpreadRadius(double)}. <br>
     *  The shadow will be rendered on the {@link UI.Layer#CONTENT} layer,
     *  if you want it to be rendered on a different layer, you
     *  may want to take a look at {@link #shadow(UI.Layer, String, Configurator)}. <br>
     *  <br>
     *  Note that this property will not only be applied to the default shadow, but also any
     *  other named shadow that you may have defined using {@link #shadow(String, Configurator)}
     *  (and which are also rendered on the {@link UI.Layer#CONTENT} layer).
     *
     * @param r The red component of the shadow color in the range of 0.0 to 1.0.
     * @param g The green component of the shadow color in the range of 0.0 to 1.0.
     * @param b The blue component of the shadow color in the range of 0.0 to 1.0.
     * @return A new {@link ComponentStyleDelegate} with the provided shadow color.
     */
    public ComponentStyleDelegate<C> shadowColor( double r, double g, double b ) {
        return shadowColor(new Color((float) r, (float) g, (float) b));
    }

    /**
     *  Returns a new {@link StyleConf} with the provided shadow color
     *  defined by the supplied red, green, blue and alpha color channels in
     *  the form of doubles expected to be in the range of 0.0 to 1.0.
     *  Note that in order to see the shadow, you may also need to call
     *  {@link #shadowBlurRadius(double)} and {@link #shadowSpreadRadius(double)}. <br>
     *  The shadow will be rendered on the {@link UI.Layer#CONTENT} layer,
     *  if you want it to be rendered on a different layer, you
     *  may want to take a look at {@link #shadow(UI.Layer, String, Configurator)}. <br>
     *  <br>
     *  Note that this property will not only be applied to the default shadow, but also any
     *  other named shadow that you may have defined using {@link #shadow(String, Configurator)}
     *  (and which are also rendered on the {@link UI.Layer#CONTENT} layer).
     *
     * @param r The red component of the shadow color in the range of 0.0 to 1.0.
     * @param g The green component of the shadow color in the range of 0.0 to 1.0.
     * @param b The blue component of the shadow color in the range of 0.0 to 1.0.
     * @param a The alpha component of the shadow color in the range of 0.0 to 1.0.
     * @return A new {@link ComponentStyleDelegate} with the provided shadow color.
     */
    public ComponentStyleDelegate<C> shadowColor( double r, double g, double b, double a ) {
        return shadowColor(new Color((float) r, (float) g, (float) b, (float) a));
    }

    /**
     *  Use this to control whether your shadows should be rendered inwards or outwards. <br>
     *  Note that this property will be applied to all shadow effects of all layers, including
     *  the default shadow and named shadows defined using {@link #shadow(String, Configurator)}. <br>
     *  The default value is {@code false}.
     *
     * @param inwards Whether the shadow should be rendered inwards or outwards.
     * @return A new {@link ComponentStyleDelegate} with the provided shadow inset flag.
     */
    public ComponentStyleDelegate<C> shadowIsInset( boolean inwards ) {
        return _withStyle(_styleConf._withShadow(shadow -> shadow.isInset(inwards)));
    }

    /**
     *  This method makes it possible to define multiple shadows for a single component
     *  on the {@link UI.Layer#CONTENT} layer, by giving the shadow config a unique name.
     *  This is useful when you want to do advanced shadow effects, such as neumorphism a.k.a. soft UI. <br>
     *  Here is an example of how to use this method:
     *  <pre>{@code
     *      UI.panel()
     *      .withStyle( it -> it
     *          .shadow("dark shading", shadow -> shadow
     *              .color("#000000")
     *              .horizontalOffset(5)
     *              .verticalOffset(5)
     *              .blurRadius(10)
     *              .spreadRadius(0)
     *          )
     *          .shadow("light shading", shadow -> shadow
     *              .color("#ffffff")
     *              .horizontalOffset(-5)
     *              .verticalOffset(-5)
     *              .blurRadius(10)
     *              .spreadRadius(0)
     *          )
     *  }</pre>
     *  Note that the shadows will be rendered in
     *  alphabetical order based on their name (within a particular layer).
     *
     * @param shadowName The name of the shadow.
     * @param styler A function that takes a {@link ShadowConf} and returns a new {@link ShadowConf}.
     * @return A new {@link ComponentStyleDelegate} with a named shadow defined by the provided styler lambda.
     */
    public ComponentStyleDelegate<C> shadow( String shadowName, Configurator<ShadowConf> styler ) {
        return shadow(ShadowConf.DEFAULT_LAYER, shadowName, styler);
    }

    /**
     *  This method makes it possible to define multiple shadows for a single component
     *  on a custom layer, by giving the shadow config a unique name.
     *  This is useful when you want to do advanced shadow effects, such as neumorphism a.k.a. soft UI. <br>
     *  Here is an example of how to use this method:
     *  <pre>{@code
     *      UI.panel()
     *      .withStyle( it -> it
     *          .shadow(UI.Layer.CONTENT, "dark shading", shadow -> shadow
     *              .color("#000000")
     *              .horizontalOffset(5)
     *              .verticalOffset(5)
     *              .blurRadius(10)
     *              .spreadRadius(0)
     *          )
     *          .shadow(UI.Layer.CONTENT, "light shading", shadow -> shadow
     *              .color("#ffffff")
     *              .horizontalOffset(-5)
     *              .verticalOffset(-5)
     *              .blurRadius(10)
     *              .spreadRadius(0)
     *          )
     *  }</pre>
     *  Note that the shadows will be rendered in
     *  alphabetical order based on their name (within a particular layer).
     *
     * @param layer The layer of the shadow is an enum instance which
     *              gives the shadow effect a rank in the painting order.
     *              So the {@link swingtree.UI.Layer#BACKGROUND} will be painted first,
     *              followed by the {@link swingtree.UI.Layer#CONTENT} and so on...
     *              <br>
     *              The following layers are available:
     *              <ul>
     *                  <li>{@link UI.Layer#BACKGROUND}</li>
     *                  <li>{@link UI.Layer#CONTENT}</li>
     *                  <li>{@link UI.Layer#BORDER}</li>
     *                  <li>{@link UI.Layer#FOREGROUND}</li>
     *              </ul>
     * @param shadowName The name of the shadow.
     * @param styler A function that takes a {@link ShadowConf} and returns a new {@link ShadowConf}.
     * @return A new {@link ComponentStyleDelegate} with a named shadow defined by the provided styler lambda.
     */
    public ComponentStyleDelegate<C> shadow(
        UI.Layer layer,
        String shadowName,
        Configurator<ShadowConf> styler
    ) {
        Objects.requireNonNull(shadowName);
        Objects.requireNonNull(styler);
        ShadowConf shadow = Optional.ofNullable(_styleConf.shadow(layer, shadowName)).orElse(ShadowConf.none());
        // We clone the shadow map:
        NamedConfigs<ShadowConf> newShadows = _styleConf.shadowsMap(layer).withNamedStyle(shadowName, styler.configure(shadow));
        return _withStyle(_styleConf._withShadow(layer, newShadows));
    }

    /**
     *  This method makes it possible to define multiple background gradient for a single component
     *  on the {@link UI.Layer#BACKGROUND} layer, by giving the gradient config a unique name.
     *  This is useful when you want to do advanced background effects, such as neumorphism a.k.a. soft UI. <br>
     *  Here is an example of how to use this method:
     *  <pre>{@code
     *    UI.panel()
     *    .withStyle( it -> it
     *      .gradient("dark shading", conf -> conf
     *        .colors("#000000", "#000000")
     *        .transition(UI.Transition.TOP_TO_BOTTOM)
     *      )
     *      .gradient("light shading", conf -> conf
     *        .colors("#ffffff", "#ffffff")
     *        .transition(UI.Transition.TOP_TO_BOTTOM))
     *      )
     *    )
     * }</pre>
     * Note that the background shades will be rendered in alphabetical order based on the name of the shade.<br>
     * This method translates to {@link #gradient(UI.Layer, String, Configurator)} but with the
     * layer set to {@link UI.Layer#BACKGROUND}.
     *
     * @param shadeName The name of the background shade.
     * @param styler A function that takes a {@link GradientConf} and returns a new {@link GradientConf}.
     * @return A new {@link ComponentStyleDelegate} with a named background shade defined by the provided styler lambda.
     */
    public ComponentStyleDelegate<C> gradient( String shadeName, Configurator<GradientConf> styler ) {
        return gradient(GradientConf.DEFAULT_LAYER, shadeName, styler);
    }

    /**
     *  This method makes it possible to define multiple background gradient for a single component
     *  on a particular layer, by giving the gradient config a unique name.
     *  This is useful when you want to do advanced background effects, such as neumorphism a.k.a. soft UI. <br>
     *  Here is an example of how to use this method:
     *  <pre>{@code
     *    UI.panel()
     *    .withStyle( it -> it
     *      .gradient(UI.Layer.BACKGROUND, "dark shading", conf -> conf
     *        .colors("#000000", "#000000")
     *        .transition(UI.Transition.TOP_TO_BOTTOM)
     *      )
     *      .gradient(UI.Layer.BACKGROUND, "light shading", conf -> conf
     *        .colors("#ffffff", "#ffffff")
     *        .transition(UI.Transition.TOP_TO_BOTTOM))
     *      )
     *    )
     * }</pre>
     * Note that within a particular layer the gradients will be rendered in alphabetical order
     * based on the provided name.
     *
     * @param layer The layer on which the gradient should be rendered.
     * @param shadeName The name of the background shade.
     * @param styler A function that takes a {@link GradientConf} and returns a new {@link GradientConf}.
     * @return A new {@link ComponentStyleDelegate} with a named background shade defined by the provided styler lambda.
     */
    public ComponentStyleDelegate<C> gradient(
        UI.Layer layer,
        String shadeName,
        Configurator<GradientConf> styler
    ) {
        Objects.requireNonNull(shadeName);
        Objects.requireNonNull(styler);
        return _withStyle(_styleConf.gradient(layer, shadeName, styler));
    }

    /**
     *  This method makes it possible to define a background shade for your components.
     *  This is useful when you want to do advanced background effects, such as neumorphism a.k.a. soft UI. <br>
     *  Here is an example of how to use this method:
     *  <pre>{@code
     *    UI.panel()
     *    .withStyle( it -> it
     *        .gradient( conf -> conf
     *            .colors("#000000", "#000000")
     *            .transition(UI.Transition.TOP_TO_BOTTOM)
     *        )
     *    )
     * }</pre>
     * This method translates to {@link #gradient(UI.Layer, String, Configurator)} but with the
     * layer set to {@link UI.Layer#BACKGROUND} and the name being the "default" style name
     *
     * @param styler A function that takes a {@link GradientConf} and returns a new {@link GradientConf}.
     * @return A new {@link ComponentStyleDelegate} with a background shade defined by the provided styler lambda.
     */
    public ComponentStyleDelegate<C> gradient( Configurator<GradientConf> styler ) {
        return gradient(GradientConf.DEFAULT_LAYER, StyleUtil.DEFAULT_KEY, styler);
    }

    /**
     *  This method makes it possible to define a gradient effect on a particular layer for your components.
     *  This is useful when you want to do advanced background effects, such as neumorphism a.k.a. soft UI. <br>
     *  Here is an example of how to use this method:
     *  <pre>{@code
     *    UI.panel()
     *    .withStyle( it -> it
     *        .gradient(UI.Layer.BACKGROUND, conf -> conf
     *            .colors("#000000", "#000000")
     *            .transition(UI.Transition.TOP_TO_BOTTOM)
     *        )
     *    )
     * }</pre>
     * Note that this method translates to {@link #gradient(UI.Layer, String, Configurator)} but with the
     * name being the "default" style name.
     *
     * @param layer The layer on which the gradient should be rendered.
     * @param styler A function that takes a {@link GradientConf} and returns a new {@link GradientConf}.
     * @return A new {@link ComponentStyleDelegate} with a background shade defined by the provided styler lambda.
     */
    public ComponentStyleDelegate<C> gradient( UI.Layer layer, Configurator<GradientConf> styler ) {
        Objects.requireNonNull(styler);
        return _withStyle(_styleConf.gradient(layer, StyleUtil.DEFAULT_KEY, styler));
    }

    /**
     *  This method makes it possible to define a background noise for your components.
     *  This is useful when you want to give component surfaces some naturally looking texture
     *  or special effects. <br>
     *  <br>
     *  Here is an example of how to use it:
     *  <pre>{@code
     *    UI.panel()
     *    .withStyle( it -> it
     *        .noise("my-noise" conf -> conf
     *            .scale(2, 3).rotation(45)
     *            .colors(Color.BLACK, Color.WHITE)
     *            .offset(64,85)
     *        )
     *    )
     * }</pre>
     * Note that this method translates to {@link #noise(UI.Layer, String, Configurator)} but with the
     * layer set to {@link UI.Layer#BACKGROUND}.
     *
     * @param noiseName The name of the noise which is used to create, identify and possibly override a noise with the same name.
     * @param styler A function that takes a {@link NoiseConf} and returns a new {@link NoiseConf}.
     * @return A new {@link ComponentStyleDelegate} with a background noise defined by the provided styler lambda.
     */
    public ComponentStyleDelegate<C> noise( String noiseName, Configurator<NoiseConf> styler ) {
        Objects.requireNonNull(noiseName);
        Objects.requireNonNull(styler);
        return noise(NoiseConf.DEFAULT_LAYER, noiseName, styler);
    }

    /**
     *  This method makes it possible to define a background noise for your components.
     *  This is useful when you want to give component surfaces some naturally looking texture
     *  or special effects. <br>
     *  <br>
     *  Here is an example of how to use the method:
     *  <pre>{@code
     *    UI.panel()
     *    .withStyle( it -> it
     *        .noise(UI.Layer.BACKGROUND, "my-noise" conf -> conf
     *            .scale(2, 3).rotation(45)
     *            .colors(Color.BLACK, Color.WHITE)
     *            .offset(64,85)
     *        )
     *    )
     * }</pre>
     *
     * @param layer The layer on which the noise should be rendered.
     * @param styler A function that takes a {@link NoiseConf} and returns a new {@link NoiseConf}.
     * @return A new {@link ComponentStyleDelegate} with a background noise defined by the provided styler lambda.
     */
    public ComponentStyleDelegate<C> noise( UI.Layer layer, String noiseName, Configurator<NoiseConf> styler ) {
        Objects.requireNonNull(noiseName);
        Objects.requireNonNull(styler);
        return _withStyle(_styleConf.noise(layer, noiseName, styler));
    }

    /**
     *  This method makes it possible to define a background noise for your components.
     *  This is useful when you want to give component surfaces some naturally looking texture
     *  or special effects. <br>
     *  <br>
     *  Here is an example of how to use this method:
     *  <pre>{@code
     *    UI.panel()
     *    .withStyle( it -> it
     *        .noise( conf -> conf
     *            .scale(2, 3).rotation(45)
     *            .colors(Color.BLACK, Color.WHITE)
     *            .offset(64,85)
     *        )
     *    )
     * }</pre>
     * Note that this method translates to {@link #noise(UI.Layer, String, Configurator)} but with the
     * layer set to {@link UI.Layer#BACKGROUND} and the name being the "default" style name.
     *
     * @param styler A function that takes a {@link NoiseConf} and returns a new {@link NoiseConf}.
     * @return A new {@link ComponentStyleDelegate} with a background noise defined by the provided styler lambda.
     */
    public ComponentStyleDelegate<C> noise( Configurator<NoiseConf> styler ) {
        Objects.requireNonNull(styler);
        return noise(NoiseConf.DEFAULT_LAYER, StyleUtil.DEFAULT_KEY, styler);
    }

    /**
     *  This method makes it possible to define multiple background styles for a single component
     *  rendered on the {@link UI.Layer#BACKGROUND} layer, by giving the background config a unique name.
     *  This is useful when you want to do advanced backgrounds
     *  displaying multiple images on top of each other. <br>
     *  Here is an example of how to use this method:
     *  <pre>{@code
     *    UI.panel()
     *    .withStyle( it -> it
     *      .image("ground 1", image -> image
     *        .image(loadImageFrom("my/path/to/image1.png"))
     *      )
     *      .ground("ground 2", ground -> ground
     *        .color("blue")
     *      )
     *    )
     * }</pre>
     * Note that the background images will be rendered in alphabetical order based on the name of the image.
     *
     * @param imageName The name of the background image.
     * @param styler A function that takes a {@link ImageConf} and returns a new {@link ImageConf}.
     * @return A new {@link ComponentStyleDelegate} with a named background image defined by the provided styler lambda.
     */
    public ComponentStyleDelegate<C> image( String imageName, Configurator<ImageConf> styler ) {
        Objects.requireNonNull(imageName);
        Objects.requireNonNull(styler);
        return image(ImageConf.DEFAULT_LAYER, imageName, styler);
    }

    /**
     *  This method makes it possible to define multiple background styles for a single component
     *  rendered on a particular layer, by giving the background config a unique name.
     *  This is useful when you want to do advanced layer backgrounds
     *  displaying multiple images on top of each other. <br>
     *  Here is an example of how to use this method:
     *  <pre>{@code
     *    UI.panel()
     *    .withStyle( it -> it
     *      .image(UI.Layer.BACKGROUND, "ground 1", image -> image
     *        .image(loadImageFrom("my/path/to/image1.png"))
     *      )
     *      .ground(UI.Layer.BACKGROUND, "ground 2", ground -> ground
     *        .color("blue")
     *      )
     *    )
     * }</pre>
     * Note that the background images will be rendered in alphabetical order based on the name of the image.
     *
     * @param layer The layer defines at which step in the rendering process the image should be rendered.
     *              The default layer is the background layer, which will be rendered first.
     *              Here a list of available layers:
     *              <ul>
     *                  <li>{@link swingtree.UI.Layer#BACKGROUND}</li>
     *                  <li>{@link swingtree.UI.Layer#CONTENT}</li>
     *                  <li>{@link swingtree.UI.Layer#BORDER}</li>
     *                  <li>{@link swingtree.UI.Layer#FOREGROUND}</li>
     *              </ul>
     * @param imageName The name of the background image.
     * @param styler A function that takes a {@link ImageConf} and returns a new {@link ImageConf}.
     * @return A new {@link ComponentStyleDelegate} with a named background image defined by the provided styler lambda.
     */
    public ComponentStyleDelegate<C> image( UI.Layer layer, String imageName, Configurator<ImageConf> styler ) {
        Objects.requireNonNull(imageName);
        Objects.requireNonNull(styler);
        return _withStyle(_styleConf.images(layer, imageName, styler));
    }

    /**
     *  Allows for the rendering of a background image on your components.
     *  This is useful when you want to do advanced backgrounds
     *  displaying multiple images on top of each other. <br>
     *  Here is an example of how to use this method:
     *  <pre>{@code
     *    UI.panel()
     *    .withStyle( it -> it
     *        .image( image -> image
     *            .image(loadImageFrom("my/path/to/image1.png"))
     *            .color("green")
     *        )
     *    )
     * }</pre>
     * Note that this method translates to {@link #image(UI.Layer, String, Configurator)} but with the
     * layer set to {@link UI.Layer#BACKGROUND} and the name being the "default" style name.
     *
     * @param styler A function that takes a {@link ImageConf} and returns a new {@link ImageConf}.
     * @return A new {@link ComponentStyleDelegate} with a background image defined by the provided styler lambda.
     */
    public ComponentStyleDelegate<C> image( Configurator<ImageConf> styler ) {
        Objects.requireNonNull(styler);
        return image(ImageConf.DEFAULT_LAYER, StyleUtil.DEFAULT_KEY, styler);
    }

    /**
     *  Allows for the rendering of an image on a particular component layer.
     *  This is useful when you want to do advanced layer backgrounds
     *  displaying multiple images on top of each other. <br>
     *  Here is an example of how to use this method:
     *  <pre>{@code
     *    UI.panel()
     *    .withStyle( it -> it
     *        .image(UI.Layer.CONTENT, image -> image
     *            .image(loadImageFrom("my/path/to/image1.png"))
     *            .color("green")
     *        )
     *    )
     * }</pre>
     * Note that this method translates to {@link #image(UI.Layer, String, Configurator)} but with the
     * name being the "default" style name.
     *
     * @param layer The layer on which the image should be rendered.
     * @param styler A function that takes a {@link ImageConf} and returns a new {@link ImageConf}.
     * @return A new {@link ComponentStyleDelegate} with a background image defined by the provided styler lambda.
     */
    public ComponentStyleDelegate<C> image( UI.Layer layer, Configurator<ImageConf> styler ) {
        Objects.requireNonNull(layer);
        Objects.requireNonNull(styler);
        return _withStyle(_styleConf.images(layer, StyleUtil.DEFAULT_KEY, styler));
    }

    /**
     *  Returns an updated {@link StyleConf} with a named text style
     *  configurator for the default {@link TextConf} of the component. <br>
     *  The sub-style exposed by this method adds <b>support for text rendering to
     *  all components not just text components</b>. <br>
     *  If you only want to style the {@link JComponent#getFont()} property of the component,
     *  you can use {@link #componentFont(Configurator)} instead. <br>
     *  <br>
     *  The first parameter is the name of the text style, which allows
     *  you to define any number of text styles for a single component
     *  by using different names. <br>
     *  Two sub-styles with the same name will override each other. <br>
     *
     * @param styler A configurator function that takes a {@link TextConf} and returns an updated {@link TextConf}.
     *               The configurator function is called with the default text style of the component.
     * @return A new {@link ComponentStyleDelegate} with the provided text style.
     *         Each unique name creates an additional text style for the component.
     * @see #text(UI.Layer, String, Configurator)
     * @see #text(Configurator)
     * @throws NullPointerException If the provided styler or textName is {@code null}.
     */
    public ComponentStyleDelegate<C> text( String textName, Configurator<TextConf> styler ) {
        Objects.requireNonNull(textName);
        Objects.requireNonNull(styler);
        return text(TextConf.DEFAULT_LAYER, textName, styler);
    }

    /**
     *  Returns an updated {@link StyleConf} with the provided named text style
     *  configurator for the default {@link TextConf} of the component. <br>
     *  The sub-style exposed by this method adds <b>support for text rendering to
     *  all components not just text components</b>. <br>
     *  If you only want to style the {@link JComponent#getFont()} property of the component,
     *  you can use {@link #componentFont(Configurator)} instead. <br>
     *  <br>
     *  The first parameter is the name of the text style, which allows
     *  you to define any number of text styles for a single component
     *  by using different names. <br>
     *  Two sub-styles with the same name will override each other. <br>
     *
     * @param layer The layer on which the text should be rendered.
     * @param textName The name of the text style that you want to define.
     *                 Each unique name creates an additional text style for the component.
     * @param styler A configurator function that takes a {@link TextConf} and returns an updated {@link TextConf}.
     *               The configurator function is called with the default text style of the component.
     * @return A new {@link ComponentStyleDelegate} with the provided text style.
     * @see #text(String, Configurator)
     * @see #text(Configurator)
     * @throws NullPointerException If the provided styler or textName is {@code null}.
     */
    public ComponentStyleDelegate<C> text( UI.Layer layer, String textName, Configurator<TextConf> styler ) {
        Objects.requireNonNull(textName);
        Objects.requireNonNull(styler);
        return _withStyle(_styleConf.text(layer, textName, styler));
    }

    /**
     *  Returns an updated {@link StyleConf} with the provided text style
     *  configurator for the default {@link TextConf} of the component. <br>
     *  The sub-style exposed by this method adds <b>support for text rendering to
     *  all components not just text components</b>. <br>
     *  If you only want to style the {@link JComponent#getFont()} property of the component,
     *  you can use {@link #componentFont(Configurator)} instead. <br>
     *
     * @param styler A configurator function that takes a {@link TextConf} and returns an updated {@link TextConf}.
     *               The configurator function is called with the default text style of the component.
     * @return A new {@link ComponentStyleDelegate} with the provided text style.
     * @see #text(UI.Layer, String, Configurator)
     * @see #text(String, Configurator)
     * @throws NullPointerException If the provided styler is {@code null}.
     */
    public ComponentStyleDelegate<C> text( Configurator<TextConf> styler ) {
        Objects.requireNonNull(styler);
        return text(TextConf.DEFAULT_LAYER, StyleUtil.DEFAULT_KEY, styler);
    }

    /**
     *  Allow for the specification of client properties on the styled component.
     *  This is useful when you want to store arbitrary configuration data on the component,
     *  which is usually read and used by look and feel implementations to
     *  apply custom appearance and behavior to the component. <br>
     *  <br>
     *  If you want a particular property to be removed, you can pass and empty String {@code ""} as the value. <br>
     *  <b>A {@code null} reference is not allowed as a value and will throw a {@link NullPointerException}.</b>
     *
     * @param key The key of the property.
     * @param value The value of the property.
     * @return A new {@link ComponentStyleDelegate} with the provided client property.
     * @see JComponent#putClientProperty(Object, Object)
     * @see JComponent#getClientProperty(Object)
     * @throws NullPointerException If the value is {@code null}! (Use {@code ""} to remove a property)
     */
    public ComponentStyleDelegate<C> property( String key, String value ) {
        Objects.requireNonNull(key);
        Objects.requireNonNull(value);
        return _withStyle(_styleConf.property(key, value));
    }

    public ComponentStyleDelegate<C> parentFilter( Configurator<FilterConf> filterStyler ) {
        Objects.requireNonNull(filterStyler);
        return _withStyle(_styleConf._withLayers(_styleConf.layers().filter(filterStyler)));
    }

    private ComponentStyleDelegate<C> _withFont( Configurator<FontConf> fontStyler ) {
        Objects.requireNonNull(fontStyler);
        StyleConf updatedStyle = _styleConf._withFont(fontStyler.configure(_styleConf.font()));
        // We also update the text style, if it exists:
        updatedStyle = updatedStyle.text( text -> text.font(fontStyler) );
        return _withStyle(updatedStyle);
    }

    /**
     *  Returns a new {@link StyleConf} with the provided font style applied to the
     *  font property of the component (see {@link JComponent#getFont()}). <br>
     *  If you want to style the text of the entire component, which includes both
     *  the component font property as well as the style engine based font render
     *  (see {@link #text(String, Configurator)}), you can simply
     *  call the regular font styling methods such as {@link #font(String, int)},
     *  {@link #font(Font)}, {@link #fontFamily(String)}, {@link #fontSize(int)},
     *  {@link #fontBold(boolean)}, {@link #fontItalic(boolean)}, {@link #fontUnderline(boolean)}...
     *
     * @param fontStyler A function that takes a {@link FontConf} and returns a new {@link FontConf}
     *                   that is exclusively applied to the font property of the component.
     * @return A new {@link ComponentStyleDelegate} with the provided font style
     *          applied to the font property of the component.
     */
    public final ComponentStyleDelegate<C> componentFont( Configurator<FontConf> fontStyler ) {
        Objects.requireNonNull(fontStyler);
        StyleConf updatedStyle = _styleConf._withFont(fontStyler.configure(_styleConf.font()));
        return _withStyle(updatedStyle);
    }

    /**
     *  Returns a new {@link StyleConf} with the provided font name and size.<br>
     *  Note that the font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}).<br>
     *  <p>
     *  Note that this font style will be applied to both the component font property
     *  and the style engine based text (see {@link #text(String, Configurator)}).
     *  If you only want to style the component font property, you can use
     *  {@link #componentFont(Configurator)}.
     *
     * @param name The font name.
     * @param size The font size.
     * @return A new {@link ComponentStyleDelegate} with the provided font name and size.
     */
    public ComponentStyleDelegate<C> font( String name, int size ) {
        Objects.requireNonNull(name);
        return _withFont( f -> f.family(name).size(size) );
    }

    /**
     *  Returns a new {@link StyleConf} with the provided font family name.
     *  Note that font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}).<br>
     *  <p>
     *  Note that this font style will be applied to both the component font property
     *  and the style engine based text (see {@link #text(String, Configurator)}).
     *  If you only want to style the component font property, you can use
     *  {@link #componentFont(Configurator)}.
     *
     * @param name The font name.
     * @return A new {@link ComponentStyleDelegate} with the provided font name.
     */
    public ComponentStyleDelegate<C> fontFamily( String name ) {
        Objects.requireNonNull(name);
        return _withFont( f -> f.family(name) );
    }

    /**
     *  Returns a new {@link StyleConf} with the provided {@link Font}.
     *  Note that font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}).<br>
     *  <p>
     *  Note that this font style will be applied to both the component font property
     *  and the style engine based text (see {@link #text(String, Configurator)}).
     *  If you only want to style the component font property, you can use
     *  {@link #componentFont(Configurator)}.
     *
     * @param font The {@link Font}.
     * @return A new {@link ComponentStyleDelegate} with the provided {@link Font}.
     * @throws NullPointerException If the font is {@code null}.
     *         Use {@link UI.Font#UNDEFINED} to remove the font style.
     */
    public ComponentStyleDelegate<C> font( Font font ) {
        Objects.requireNonNull(font, "The font cannot be null! Use UI.FONT_UNDEFINED to remove the font style.");
        return _withFont( f -> f.withPropertiesFromFont(font) );
    }

    /**
     *  Returns a new {@link StyleConf} with the provided font size.
     *  Note that font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}).<br>
     *  <p>
     *  Note that this font style will be applied to both the component font property
     *  and the style engine based text (see {@link #text(String, Configurator)}).
     *  If you only want to style the component font property, you can use
     *  {@link #componentFont(Configurator)}.
     *
     * @param size The font size.
     * @return A new {@link ComponentStyleDelegate} with the provided font size.
     */
    public ComponentStyleDelegate<C> fontSize( int size ) {
        return _withFont( f -> f.size(size) );
    }

    /**
     *  Makes the font bold or not bold depending on the value of the {@code isBold} parameter.
     *  Note that font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}).<br>
     *  <p>
     *  Note that this font style will be applied to both the component font property
     *  and the style engine based text (see {@link #text(String, Configurator)}).
     *  If you only want to style the component font property, you can use
     *  {@link #componentFont(Configurator)}.
     *
     * @param bold Whether the font should be bold or not.
     * @return A new {@link ComponentStyleDelegate} with the provided font boldness.
     */
    public ComponentStyleDelegate<C> fontBold( boolean bold ) {
        return _withFont( f -> f.weight( bold ? 2 : 1 ) );
    }

    /**
     *  Makes the font italic or not italic depending on the value of the {@code italic} parameter.
     *  Note that font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}).<br>
     *  <p>
     *  Note that this font style will be applied to both the component font property
     *  and the style engine based text (see {@link #text(String, Configurator)}).
     *  If you only want to style the component font property, you can use
     *  {@link #componentFont(Configurator)}.
     *
     * @param italic Whether the font should be italic or not.
     * @return A new {@link ComponentStyleDelegate} with the provided font italicness.
     */
    public ComponentStyleDelegate<C> fontItalic( boolean italic ) {
        return _withFont( f -> f.posture( italic ? 0.2f : 0f ) );
    }

    /**
     *  Determines if the font should be plain, bold, italic or bold and italic
     *  based on the provided {@link UI.FontStyle} parameter,
     *  which may be {@link UI.FontStyle#PLAIN}, {@link UI.FontStyle#BOLD},
     *  {@link UI.FontStyle#ITALIC} or {@link UI.FontStyle#BOLD_ITALIC}.<br>
     *  <b>
     *      Note that this will override any previous bold or italic settings.
     *  </b>
     * @param style The font style in form of a {@link UI.FontStyle}.
     * @return An updated {@link ComponentStyleDelegate} with the provided font style.
     */
    public ComponentStyleDelegate<C> fontStyle( UI.FontStyle style ) {
        return _withFont( f -> f.style(style) );
    }

    /**
     *  Makes the font underlined or not underlined depending on the value of the {@code underline} parameter.
     *  Note that font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}).<br>
     *  <p>
     *  Note that this font style will be applied to both the component font property
     *  and the style engine based text (see {@link #text(String, Configurator)}).
     *  If you only want to style the component font property, you can use
     *  {@link #componentFont(Configurator)}.
     *
     * @param underline Whether the font should be underlined or not.
     * @return A new {@link ComponentStyleDelegate} with the provided font underlinedness.
     */
    public ComponentStyleDelegate<C> fontUnderline( boolean underline ) {
        return _withFont( f -> f.underlined(underline) );
    }

    /**
     *  Makes the font struck through or not struck through depending on the value of the {@code strikeThrough} parameter.
     *  Note that font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}).<br>
     *  <p>
     *  Note that this font style will be applied to both the component font property
     *  and the style engine based text (see {@link #text(String, Configurator)}).
     *  If you only want to style the component font property, you can use
     *  {@link #componentFont(Configurator)}.
     *
     * @param strikeThrough Whether the font should be struck through or not.
     * @return A new {@link ComponentStyleDelegate} with the provided font struck throughness.
     */
    public ComponentStyleDelegate<C> fontStrikeThrough( boolean strikeThrough ) {
        return _withFont( f -> f.strikeThrough(strikeThrough) );
    }

    /**
     *  Creates a new {@link StyleConf} where the font color is set to the provided {@link Color}.
     *  Note that font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}).<br>
     *  <p>
     *  Note that this font style will be applied to both the component font property
     *  and the style engine based text (see {@link #text(String, Configurator)}).
     *  If you only want to style the component font property, you can use
     *  {@link #componentFont(Configurator)}.
     *
     * @param color The {@link Color}.
     * @return A new {@link ComponentStyleDelegate} with the provided font color.
     * @throws NullPointerException If the color is {@code null}.
     *         Use {@link UI.Color#UNDEFINED} to remove the font color style.
     */
    public ComponentStyleDelegate<C> fontColor( Color color ) {
        Objects.requireNonNull(color, "The color cannot be null! Use UI.Color.UNDEFINED to remove the font color style.");
        return _withFont( f -> f.color(color) );
    }

    /**
     *  Creates a new {@link StyleConf} where the font color is set to a color parsed from the provided string.
     *  Note that font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}).<br>
     *  <p>
     *  Note that this font style will be applied to both the component font property
     *  and the style engine based text (see {@link #text(String, Configurator)}).
     *  If you only want to style the component font property, you can use
     *  {@link #componentFont(Configurator)}.
     *
     * @param colorString The {@link Color} as a string.
     * @return A new {@link ComponentStyleDelegate} with the provided font color.
     */
    public ComponentStyleDelegate<C> fontColor( String colorString ) {
        Objects.requireNonNull(colorString, "The color string cannot be null! Use an empty string to remove the font color style.");
        return _withFont( f -> f.color(colorString) );
    }

    /**
     *  Creates a new {@link StyleConf} where the font background color is set to the provided {@link Color}.
     *  Note that font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}).<br>
     *  <p>
     *  Note that this font style will be applied to both the component font property
     *  and the style engine based text (see {@link #text(String, Configurator)}).
     *  If you only want to style the component font property, you can use
     *  {@link #componentFont(Configurator)}.
     *
     * @param color The {@link Color}.
     * @return A new {@link ComponentStyleDelegate} with the provided font background color.
     * @throws NullPointerException If the color is {@code null}.
     *          Use {@link UI.Color#UNDEFINED} to remove the font background color style.
     */
    public ComponentStyleDelegate<C> fontBackgroundColor( Color color ) {
        Objects.requireNonNull(color);
        return _withFont( f -> f.backgroundColor(color) );
    }

    /**
     *  Creates a new {@link StyleConf} where the font color is set to a color parsed from the provided string.
     *  Note that font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}).<br>
     *  <p>
     *  Note that this font style will be applied to both the component font property
     *  and the style engine based text (see {@link #text(String, Configurator)}).
     *  If you only want to style the component font property, you can use
     *  {@link #componentFont(Configurator)}.
     *
     * @param colorString The {@link Color} as a string.
     * @return A new {@link ComponentStyleDelegate} with the provided font color.
     */
    public ComponentStyleDelegate<C> fontBackgroundColor( String colorString ) {
        Objects.requireNonNull(colorString);
        return _withFont( f -> f.backgroundColor(colorString) );
    }

    /**
     *  Creates a new {@link StyleConf} where the font selection color is set to the provided {@link Color}.
     *  Note that font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}).<br>
     *  <p>
     *  Note that this font style will be applied to both the component font property
     *  and the style engine based text (see {@link #text(String, Configurator)}).
     *  If you only want to style the component font property, you can use
     *  {@link #componentFont(Configurator)}.
     *
     * @param color The {@link Color}.
     * @return A new {@link ComponentStyleDelegate} with the provided font selection color.
     * @throws NullPointerException If the color is {@code null}.
     *         Use {@link UI.Color#UNDEFINED} to remove the font selection color style.
     */
    public ComponentStyleDelegate<C> fontSelectionColor( Color color ) {
        Objects.requireNonNull(color);
        return _withFont( f -> f.selectionColor(color) );
    }

    /**
     *  Creates a new {@link StyleConf} where the font selection color is set to a color parsed from the provided string.
     *  Note that font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}).<br>
     *  <p>
     *  Note that this font style will be applied to both the component font property
     *  and the style engine based text (see {@link #text(String, Configurator)}).
     *  If you only want to style the component font property, you can use
     *  {@link #componentFont(Configurator)}.
     *
     * @param colorString The {@link Color} as a string.
     * @return A new {@link ComponentStyleDelegate} with the provided font selection color.
     */
    public ComponentStyleDelegate<C> fontSelectionColor( String colorString ) {
        return _withFont( f -> f.selectionColor(colorString) );
    }

    /**
     *  The {@link AffineTransform} property of a font defines how the font is
     *  rotated, scaled, skewed or translated. This method allows you to apply
     *  a custom {@link AffineTransform} to the fonts of the component.
     *
     * @param transform The {@link AffineTransform} to apply to the font.
     * @return A new {@link ComponentStyleDelegate} with the provided font transform.
     */
    public ComponentStyleDelegate<C> fontTransform( @Nullable AffineTransform transform ) {
        return _withStyle(_styleConf._withFont(_styleConf.font().transform(transform)));
    }

    /**
     *  Creates an updated style config with the provided font paint
     *  applied to all font configurations of the component.
     *
     * @param paint The {@link Paint} to use for the foreground of the font, which translates to the
     *              {@link java.awt.font.TextAttribute#FOREGROUND} attribute.
     * @return A new {@link ComponentStyleDelegate} with the provided font paint.
     */
    public ComponentStyleDelegate<C> fontPaint( @Nullable Paint paint ) {
        return _withFont( f-> f.paint(paint) );
    }

    /**
     *  Updates this style delegate with the supplied {@link Paint} object
     *  used for the background of the font, which translates to the
     *  {@link java.awt.font.TextAttribute#BACKGROUND} attribute.
     *  
     * @param paint The {@link Paint} to use for the background of the font, which translates to the
     *              {@link java.awt.font.TextAttribute#BACKGROUND} attribute.
     * @return A new {@link ComponentStyleDelegate} with the provided font background paint.
     */
    public ComponentStyleDelegate<C> fontBackgroundPaint( @Nullable Paint paint ) {
        return _withFont( f -> f.backgroundPaint(paint) );
    }


    /**
     *  Use this to define the weight of the default font of the component.
     *  The default value is 1.0 (see {@link java.awt.font.TextAttribute#WEIGHT_REGULAR}),
     *  whereas a bold font typically has a font weight
     *  of 2.0 (see {@link java.awt.font.TextAttribute#WEIGHT_BOLD}).
     *  <p>
     *  Note that font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}).<br>
     *  <p>
     *  Note that this font style will be applied to both the component font property
     *  and the style engine based text (see {@link #text(String, Configurator)}).
     *  If you only want to style the component font property, you can use
     *  {@link #componentFont(Configurator)}.
     * @param weight The weight of the font.
     * @return A new {@link ComponentStyleDelegate} with the provided font weight.
     */
    public ComponentStyleDelegate<C> fontWeight( float weight ) {
        return _withFont( f -> f.weight(weight) );
    }

    /**
     *  The font spacing, which is also known as tracking, is the space between characters in a font.
     *  See {@link java.awt.font.TextAttribute#TRACKING} for more information.
     *
     * @param spacing The spacing of the default font of the component, which translates to the
     *                {@link java.awt.font.TextAttribute#TRACKING} attribute.
     * @return A new {@link ComponentStyleDelegate} with the provided font spacing.
     */
    public ComponentStyleDelegate<C> fontSpacing( float spacing ) {
        return _withFont( f -> f.spacing(spacing) );
    }

    /**
     *  Use this to define the horizontal alignment of the default font of the component.
     *  Note that font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}). <br>
     *  Also note that not all text based components support text alignment.
     *  @param alignment The horizontal alignment of the font.
     *                   See {@link UI.HorizontalAlignment} for more information.
     *  @return A new {@link ComponentStyleDelegate} with the provided font alignment.
     * @throws NullPointerException If the alignment is {@code null}.
     *         Use {@link UI.HorizontalAlignment#UNDEFINED} to remove the font alignment style.
     */
    public ComponentStyleDelegate<C> fontAlignment( UI.HorizontalAlignment alignment ) {
        Objects.requireNonNull(alignment);
        return _withFont( f -> f.horizontalAlignment(alignment) );
    }

    /**
     *  Use this to define the vertical alignment of the default font of the component.
     *  Note that font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}). <br>
     *  Also note that not all text based components support text alignment.
     *  @param alignment The vertical alignment of the font.
     *                   See {@link UI.VerticalAlignment} for more information.
     *  @return A new {@link ComponentStyleDelegate} with the provided font alignment.
     *  throws NullPointerException If the alignment is {@code null}.
     *       Use {@link UI.VerticalAlignment#UNDEFINED} to remove the font alignment style.
     */
    public ComponentStyleDelegate<C> fontAlignment( UI.VerticalAlignment alignment ) {
        Objects.requireNonNull(alignment);
        return _withFont( f -> f.verticalAlignment(alignment) );
    }

    /**
     *  Use this to define the horizontal and vertical alignment of the default font of the component.
     *  Note that font styles will only apply if the component that is being rendered
     *  also supports displaying text, or has a custom text style (see {@link TextConf}). <br>
     *  Also note that not all text based components support text alignment.
     *  @param alignment The horizontal and vertical alignment of the font.
     *                   See {@link UI.Alignment} for more information.
     *  @return A new {@link ComponentStyleDelegate} with the provided font alignment.
     *  throws NullPointerException If the alignment is {@code null}.
     *          Use {@link UI.Alignment#UNDEFINED} to remove the font alignment style.
     */
    public ComponentStyleDelegate<C> fontAlignment( UI.Alignment alignment ) {
        Objects.requireNonNull(alignment);
        return fontAlignment(alignment.getHorizontal()).fontAlignment(alignment.getVertical());
    }

    /**
     *  Defines the minimum {@link Dimension} for this {@link JComponent}. <br>
     *  This ultimately translates to {@link JComponent#setMinimumSize(Dimension)} on the underlying component,
     *  which will be called when all the other styles are applied and rendered. <br>
     * @param width The minimum width.
     * @param height The minimum height.
     * @return A new {@link ComponentStyleDelegate} with the provided minimum {@link Dimension} set to be later
     *          applied to the underlying component when the final {@link StyleConf} is applied.
     */
    public ComponentStyleDelegate<C> minSize( int width, int height ) {
        return _withStyle(_styleConf._withDimensionality(_styleConf.dimensionality()._withMinWidth(width)._withMinHeight(height)));
    }

    /**
     *  Defines the minimum size for this {@link JComponent} in the form of a {@link Size} object. <br>
     *  This ultimately translates to {@link JComponent#setMinimumSize(Dimension)} on the underlying component,
     *  which will be called when all the other styles are applied and rendered. <br>
     * @param size The minimum {@link Size}.
     * @return A new {@link ComponentStyleDelegate} with the provided minimum {@link Size} set to be later
     *          applied to the underlying component when the final {@link StyleConf} is applied.
     */
    public ComponentStyleDelegate<C> minSize( Size size ) {
        Objects.requireNonNull(size);
        return _withStyle(_styleConf._withDimensionality(_styleConf.dimensionality()._withMinSize(size)));
    }

    /**
     *  Defines the minimum width for this {@link JComponent}. <br>
     *  This ultimately translates to {@link JComponent#setMinimumSize(Dimension)} on the underlying component,
     *  which will be called when all the other styles are applied and rendered. <br>
     * @param minWidth The minimum width.
     * @return A new {@link ComponentStyleDelegate} with the provided minimum width set to be later
     *          applied to the underlying component when the final {@link StyleConf} is applied.
     */
    public ComponentStyleDelegate<C> minWidth( int minWidth ) {
        return _withStyle(_styleConf._withDimensionality(_styleConf.dimensionality()._withMinWidth(minWidth)));
    }

    /**
     *  Defines the minimum height for this {@link JComponent}. <br>
     *  This ultimately translates to {@link JComponent#setMinimumSize(Dimension)} on the underlying component,
     *  which will be called when all the other styles are applied and rendered. <br>
     * @param minHeight The minimum height.
     * @return A new {@link ComponentStyleDelegate} with the provided minimum height set to be later
     *          applied to the underlying component when the final {@link StyleConf} is applied.
     */
    public ComponentStyleDelegate<C> minHeight( int minHeight ) {
        return _withStyle(_styleConf._withDimensionality(_styleConf.dimensionality()._withMinHeight(minHeight)));
    }

    /**
     *  Defines the maximum {@link Dimension} for this {@link JComponent}. <br>
     *  This ultimately translates to {@link JComponent#setMaximumSize(Dimension)} on the underlying component. <br>
     *  The passed {@link Dimension} will be applied when all the other styles are applied and rendered. <br>
     *
     * @param width The maximum width.
     * @param height The maximum height.
     * @return A new {@link ComponentStyleDelegate} with the provided maximum {@link Dimension} set to be later
     *          applied to the underlying component when the final {@link StyleConf} is applied.
     */
    public ComponentStyleDelegate<C> maxSize( int width, int height ) {
        return _withStyle(_styleConf._withDimensionality(_styleConf.dimensionality()._withMaxWidth(width)._withMaxHeight(height)));
    }

    /**
     *  Defines the maximum {@link Size} for this {@link JComponent}. <br>
     *  This ultimately translates to {@link JComponent#setMaximumSize(Dimension)} on the underlying component. <br>
     *  The passed {@link Size} will be applied when all the other styles are applied and rendered. <br>
     *
     * @param maxSize The maximum {@link Size}.
     * @return A new {@link ComponentStyleDelegate} with the provided maximum {@link Size} set to be later
     *          applied to the underlying component when the final {@link StyleConf} is applied.
     */
    public ComponentStyleDelegate<C> maxSize( Size maxSize ) {
        Objects.requireNonNull(maxSize);
        return _withStyle(_styleConf._withDimensionality(_styleConf.dimensionality()._withMaxSize(maxSize)));
    }

    /**
     *  Defines the maximum width for this {@link JComponent}. <br>
     *  This ultimately translates to {@link JComponent#setMaximumSize(Dimension)} on the underlying component. <br>
     *  The passed width will be applied when all the other styles are applied and rendered. <br>
     *
     * @param maxWidth The maximum width.
     * @return A new {@link ComponentStyleDelegate} with the provided maximum width set to be later
     *          applied to the underlying component when the final {@link StyleConf} is applied.
     */
    public ComponentStyleDelegate<C> maxWidth( int maxWidth ) {
        return _withStyle(_styleConf._withDimensionality(_styleConf.dimensionality()._withMaxWidth(maxWidth)));
    }

    /**
     *  Defines the maximum height for this {@link JComponent}. <br>
     *  This ultimately translates to {@link JComponent#setMaximumSize(Dimension)} on the underlying component. <br>
     *  The passed height will be applied when all the other styles are applied and rendered. <br>
     *
     * @param maxHeight The maximum height.
     * @return A new {@link ComponentStyleDelegate} with the provided maximum height set to be later
     *          applied to the underlying component when the final {@link StyleConf} is applied.
     */
    public ComponentStyleDelegate<C> maxHeight( int maxHeight ) {
        return _withStyle(_styleConf._withDimensionality(_styleConf.dimensionality()._withMaxHeight(maxHeight)));
    }

    /**
     *  Defines the preferred {@link Size} for this {@link JComponent}. <br>
     *  This ultimately translates to {@link JComponent#setPreferredSize(Dimension)} on the underlying component. <br>
     *  The passed {@link Size} will be applied when all the other styles are applied and rendered. <br>
     *
     * @param preferredSize The preferred {@link Size}.
     * @return A new {@link ComponentStyleDelegate} with the provided preferred {@link Size} set to be later
     *          applied to the underlying component when the final {@link StyleConf} is applied.
     */
    public ComponentStyleDelegate<C> prefSize( Size preferredSize ) {
        Objects.requireNonNull(preferredSize);
        return _withStyle(_styleConf._withDimensionality(_styleConf.dimensionality()._withPreferredSize(preferredSize)));
    }

    /**
     *  Defines the preferred {@link Dimension} for this {@link JComponent}. <br>
     *  This ultimately translates to {@link JComponent#setPreferredSize(Dimension)} on the underlying component. <br>
     *  The passed {@link Dimension} will be applied when all the other styles are applied and rendered. <br>
     *
     * @param width The preferred width.
     * @param height The preferred height.
     * @return A new {@link ComponentStyleDelegate} with the provided preferred {@link Dimension} set to be later
     *          applied to the underlying component when the final {@link StyleConf} is applied.
     */
    public ComponentStyleDelegate<C> prefSize( int width, int height ) {
        return _withStyle(_styleConf._withDimensionality(_styleConf.dimensionality()._withPreferredWidth(width)._withPreferredHeight(height)));
    }

    /**
     *  Defines the preferred width for this {@link JComponent}. <br>
     *  This ultimately translates to {@link JComponent#setPreferredSize(Dimension)} on the underlying component. <br>
     *  The passed width will be applied when all the other styles are applied and rendered. <br>
     *
     * @param preferredWidth The preferred width.
     * @return A new {@link ComponentStyleDelegate} with the provided preferred width set to be later
     *          applied to the underlying component when the final {@link StyleConf} is applied.
     */
    public ComponentStyleDelegate<C> prefWidth( int preferredWidth ) {
        return _withStyle(_styleConf._withDimensionality(_styleConf.dimensionality()._withPreferredWidth(preferredWidth)));
    }

    /**
     *  Defines the preferred height for this {@link JComponent}. <br>
     *  This ultimately translates to {@link JComponent#setPreferredSize(Dimension)} on the underlying component. <br>
     *  The passed height will be applied when all the other styles are applied and rendered. <br>
     *
     * @param preferredHeight The preferred height.
     * @return A new {@link ComponentStyleDelegate} with the provided preferred height set to be later
     *          applied to the underlying component when the final {@link StyleConf} is applied.
     */
    public ComponentStyleDelegate<C> prefHeight( int preferredHeight ) {
        return _withStyle(_styleConf._withDimensionality(_styleConf.dimensionality()._withPreferredHeight(preferredHeight)));
    }

    /**
     *  Defines the size of this {@link JComponent}. <br>
     *  This ultimately translates to {@link JComponent#setSize(Dimension)} on the underlying component. <br>
     * @param size The width and height size {@link Dimension}.
     * @return A new {@link ComponentStyleDelegate} with the provided {@link Size} (width and height) set to be later
     *          applied to the underlying component when the final {@link StyleConf} is applied.
     */
    public ComponentStyleDelegate<C> size( Size size ) {
        Objects.requireNonNull(size);
        return _withStyle(_styleConf._withDimensionality(_styleConf.dimensionality()._withSize(size)));
    }

    /**
     *  Defines the size of this {@link JComponent}. <br>
     *  This ultimately translates to {@link JComponent#setSize(Dimension)} on the underlying component. <br>
     * @param width The width.
     * @param height The height.
     * @return A new {@link ComponentStyleDelegate} with the provided size (width and height) {@link Dimension} set to be later
     *          applied to the underlying component when the final {@link StyleConf} is applied.
     */
    public ComponentStyleDelegate<C> size( int width, int height ) {
        return _withStyle(_styleConf._withDimensionality(_styleConf.dimensionality()._withWidth(width)._withHeight(height)));
    }


    /**
     *  Defines the width of this {@link JComponent}. <br>
     *  This ultimately translates to {@link JComponent#setSize(Dimension)} on the underlying component. <br>
     * @param width The width.
     * @return A new {@link ComponentStyleDelegate} with the provided width set to be later
     *          applied to the underlying component when the final {@link StyleConf} is applied.
     */
    public ComponentStyleDelegate<C> width( int width ) {
        return _withStyle(_styleConf._withDimensionality(_styleConf.dimensionality()._withWidth(width)));
    }

    /**
     *  Defines the height of this {@link JComponent}. <br>
     *  This ultimately translates to {@link JComponent#setSize(Dimension)} on the underlying component. <br>
     * @param height The height.
     * @return A new {@link ComponentStyleDelegate} with the provided height set to be later
     *          applied to the underlying component when the final {@link StyleConf} is applied.
     */
    public ComponentStyleDelegate<C> height( int height ) {
        return _withStyle(_styleConf._withDimensionality(_styleConf.dimensionality()._withHeight(height)));
    }

    /**
     *  Defines the cursor type for this {@link JComponent} based on
     *  the predefined {@link UI.Cursor} values. <br>
     *  If you want to specify a custom cursor implementation,
     *  use {@link #cursor(Cursor)} instead. <br>
     *
     * @param cursor The {@link UI.Cursor} value.
     * @return A new {@link ComponentStyleDelegate} with the provided cursor type set to be later
     */
    public ComponentStyleDelegate<C> cursor( UI.Cursor cursor ) {
        Objects.requireNonNull(cursor);
        return this.cursor(cursor.toAWTCursor());
    }

    /**
     *  Defines the cursor type for this {@link JComponent} based on
     *  the provided {@link Cursor} value. <br>
     *  Use this method if you want to specify a custom cursor implementation,
     *  in case you merely want to pick one of the many predefined {@link UI.Cursor} values,
     *  use {@link #cursor(UI.Cursor)} instead. <br>
     *
     * @param cursor The {@link Cursor} value.
     * @return A new {@link ComponentStyleDelegate} with the provided cursor type set to be later
     */
    public ComponentStyleDelegate<C> cursor( Cursor cursor ) {
        Objects.requireNonNull(cursor);
        return _withStyle(_styleConf._withBase(_styleConf.base().cursor(cursor)));
    }

    /**
     *  Determines how the component is oriented, typically with respect to
     *  the text direction and where content originates. <br>
     *  This translates to {@link JComponent#setComponentOrientation(ComponentOrientation)}
     *  on the underlying component. <br>
     *  <br>
     *  Note that although all components support this property, it may not always
     *  have a noticeable effect on the component. <br>
     *  How this property is interpreted depends heavily on the component, it's layout manager
     *  and the look and feel implementation. <br>
     * @param orientation The {@link UI.ComponentOrientation}, which maps 1:1 to the AWT {@link ComponentOrientation} constants.
     * @return A new {@link ComponentStyleDelegate} with the provided {@link UI.ComponentOrientation} set to be later
     *         applied to the underlying component when the final {@link StyleConf} is applied.
     */
    public ComponentStyleDelegate<C> orientation( UI.ComponentOrientation orientation ) {
        Objects.requireNonNull(orientation);
        return _withStyle(_styleConf._withBase(_styleConf.base().orientation(orientation)));
    }

    /**
     *  Use this to define the layout manager for this {@link JComponent}
     *  using a {@link Layout} object. <br>
     *  Checkout the factory methods in {@link Layout} for creating
     *  different types of layout managers like {@link Layout#flow()}, {@link Layout#mig(String)}
     *  or {@link Layout#grid(int, int)}. <br>
     *
     * @param installer The {@link Layout} to use for installing the layout.
     * @return A new {@link ComponentStyleDelegate} with the provided {@link Layout} set to be later
     */
    public ComponentStyleDelegate<C> layout( Layout installer ) {
        return _withStyle(_styleConf._withLayout(_styleConf.layout().layout(installer)));
    }

    /**
     *  Defines the layout {@link net.miginfocom.swing.MigLayout} constraints for
     *  this {@link JComponent} in the form of a {@link String}. <br>
     *  This ultimately translates to {@link net.miginfocom.swing.MigLayout#setLayoutConstraints(Object)}
     *  on the underlying component. <br>
     *  <br>
     *  Note that if this property is specified, the style engine will automatically
     *  install a {@link net.miginfocom.swing.MigLayout} on the component if it does not already have one. <br>
     *
     * @param constraints The layout constraints as a {@link String}.
     * @return A new {@link ComponentStyleDelegate} with the provided layout constraints set to be later
     */
    public ComponentStyleDelegate<C> layout( String constraints ) {
        Objects.requireNonNull(constraints);
        if ( _styleConf.layout().layout() instanceof Layout.ForMigLayout ) {
            Layout.ForMigLayout migInstaller = (Layout.ForMigLayout) _styleConf.layout().layout();
            migInstaller = migInstaller.withConstraint(constraints);
            return _withStyle(_styleConf._withLayout(_styleConf.layout().layout(migInstaller)));
        }
        return _withStyle(_styleConf._withLayout(_styleConf.layout().layout(Layout.mig(constraints, "", ""))));
    }

    /**
     *  Defines the {@link net.miginfocom.swing.MigLayout} based layout constraints
     *  and column layout constraints of this {@link JComponent} in the form of a {@link String}. <br>
     *  This ultimately translates to {@link net.miginfocom.swing.MigLayout#setLayoutConstraints(Object)}
     *  as well as {@link net.miginfocom.swing.MigLayout#setColumnConstraints(Object)}
     *  on the layout manager of the underlying component. <br>
     *  <br>
     *  Note that if this property is specified, the style engine will automatically
     *  install a {@link net.miginfocom.swing.MigLayout} on the component if it does not already have one. <br>
     *
     * @param constraints The layout constraints as a {@link String}.
     * @param columnConstraints The column constraints as a {@link String}.
     * @return A new {@link ComponentStyleDelegate} with the provided layout constraints set to be later
     */
    public ComponentStyleDelegate<C> layout( String constraints, String columnConstraints ) {
        Objects.requireNonNull(constraints);
        Objects.requireNonNull(columnConstraints);
        if ( _styleConf.layout().layout() instanceof Layout.ForMigLayout) {
            Layout.ForMigLayout migInstaller = (Layout.ForMigLayout) _styleConf.layout().layout();
            migInstaller = migInstaller.withConstraint(constraints).withColumnConstraint(columnConstraints);
            return _withStyle(_styleConf._withLayout(_styleConf.layout().layout(migInstaller)));
        }
        return _withStyle(_styleConf._withLayout(_styleConf.layout().layout(Layout.mig(constraints, columnConstraints, ""))));
    }

    /**
     *  Defines the {@link net.miginfocom.swing.MigLayout} based layout constraints
     *  column layout constraints and row layout constraints of this {@link JComponent} in the form of a {@link String}. <br>
     *  This ultimately translates to {@link net.miginfocom.swing.MigLayout#setLayoutConstraints(Object)}
     *  as well as {@link net.miginfocom.swing.MigLayout#setColumnConstraints(Object)}
     *  and {@link net.miginfocom.swing.MigLayout#setRowConstraints(Object)}
     *  on the layout manager of the underlying component. <br>
     *  <br>
     *  Note that if this property is specified, the style engine will automatically
     *  install a {@link net.miginfocom.swing.MigLayout} on the component if it does not already have one. <br>
     *
     * @param constraints The layout constraints as a {@link String}.
     * @param columnConstraints The column constraints as a {@link String}.
     * @param rowConstraints The row constraints as a {@link String}.
     * @return A new {@link ComponentStyleDelegate} with the provided layout constraints set to be later
     */
    public ComponentStyleDelegate<C> layout( String constraints, String columnConstraints, String rowConstraints ) {
        Objects.requireNonNull(constraints);
        Objects.requireNonNull(columnConstraints);
        Objects.requireNonNull(rowConstraints);
        return _withStyle(_styleConf._withLayout(_styleConf.layout().layout(Layout.mig(constraints, columnConstraints, rowConstraints))));
    }

    /**
     *  Defines the component constraints of this component with respect to the parent component
     *  and its layout manager, in the form of a {@link String}. <br>
     *  This ultimately translates to {@link net.miginfocom.swing.MigLayout#setComponentConstraints(Component, Object)}
     *  on the layout manager of the parent component. <br>
     *  <br>
     *  Note that if this property is specified, the style engine will automatically
     *  install a {@link net.miginfocom.swing.MigLayout} on the parent component if it does not already have one. <br>
     *
     * @param constraints The component constraints as a {@link String}.
     * @return A new {@link ComponentStyleDelegate} with the provided component constraints set to be later
     */
    public ComponentStyleDelegate<C> addConstraint( Object constraints ) {
        return _withStyle(_styleConf._withLayout(_styleConf.layout().constraint(constraints)));
    }

    /**
     *  Defines the layout {@link net.miginfocom.swing.MigLayout} constraints for
     *  this {@link JComponent} in the form of a {@link LayoutConstraint}
     *  (see {@link UI#FILL}, {@link UI#FILL_X}, {@link UI#FILL_Y}...). <br>
     *  This ultimately translates to {@link net.miginfocom.swing.MigLayout#setLayoutConstraints(Object)}
     *  on the underlying component. <br>
     *  <br>
     *  Note that if this property is specified, the style engine will automatically
     *  install a {@link net.miginfocom.swing.MigLayout} on the component if it does not already have one. <br>
     *
     * @param constraintAttr The layout constraints as a {@link LayoutConstraint}.
     * @return A new {@link ComponentStyleDelegate} with the provided layout constraints set to be later
     */
    public ComponentStyleDelegate<C> layout( LayoutConstraint constraintAttr ) {
        Objects.requireNonNull(constraintAttr);
        return layout(constraintAttr.toString());
    }

    /**
     *  Defines the alignment percentage alongside the X axis for a component (see {@link JComponent#setAlignmentX(float)}). <br>
     *  Note that the alignment may not have an effect on all components
     *  as it depends on the layout manager of the component. <br>
     *
     * @param percentage The alignment percentage in terms of a number between 0 and 1 alongside the X axis.
     * @return A new {@link ComponentStyleDelegate} with the provided alignment percentage alongside the X axis set to be later
     */
    public ComponentStyleDelegate<C> alignmentX( float percentage ) {
        return _withStyle(_styleConf._withLayout(_styleConf.layout().alignmentX(percentage)));
    }

    /**
     *  Defines the alignment percentage alongside the Y axis for a component (see {@link JComponent#setAlignmentY(float)}). <br>
     *  Note that the alignment may not have an effect on all components
     *  as it depends on the layout manager of the component. <br>
     *
     * @param percentage The alignment percentage in terms of a number between 0 and 1 alongside the Y axis.
     * @return A new {@link ComponentStyleDelegate} with the provided alignment percentage alongside the Y axis set to be later
     */
    public ComponentStyleDelegate<C> alignmentY( float percentage ) {
        return _withStyle(_styleConf._withLayout(_styleConf.layout().alignmentY(percentage)));
    }

    /**
     *  A convenient delegate method to {@link UI#scale()} which exposes the current UI scale factor
     *  that is used to scale the UI for high resolution displays (high dots-per-inch, or DPI).
     *  Use this scale factor when writing custom rendering code against the {@link Graphics2D} API.
     *
     * @return The current UI scale factor, which is used to scale the UI
     *         for high resolution displays (high dots-per-inch, or DPI).
     */
    public float getScale() { return UI.scale(); }

    /**
     *  A convenient delegate method to {@link UI#scale()} which exposes the current UI scale factor
     *  that is used to scale the UI for high resolution displays (high dots-per-inch, or DPI).
     *  Use this scale factor when writing custom rendering code against the {@link Graphics2D} API.
     *
     * @return The current UI scale factor, which is used to scale the UI
     *         for high resolution displays (high dots-per-inch, or DPI).
     */
    public float scale() { return UI.scale(); }

    /**
     *  Use this method inside custom {@link swingtree.api.Painter} implementations (see {@link #painter(UI.Layer, swingtree.api.Painter)})
     *  to scale an {@code int} value by the current UI scale factor to ensure
     *  that the UI is scaled properly for high resolution displays (high dots-per-inch, or DPI).
     *  @param value The {@code int} value to scale.
     *  @return The scaled {@code int} value.
     */
    public int scale( int value ) { return UI.scale(value); }

    /**
     *  Use this method inside custom {@link swingtree.api.Painter} implementations (see {@link #painter(UI.Layer, swingtree.api.Painter)})
     *  to scale a {@code float} value by the current UI scale factor to ensure
     *  that the UI is scaled properly for high resolution displays (high dots-per-inch, or DPI).
     *  @param value The {@code float} value to scale.
     *  @return The scaled {@code float} value.
     */
    public float scale( float value ) { return UI.scale(value); }

    /**
     *  Use this method inside custom {@link swingtree.api.Painter} implementations (see {@link #painter(UI.Layer, Painter)})
     *  to scale a {@code double} value by the current UI scale factor to ensure
     *  that the UI is scaled properly for high resolution displays (high dots-per-inch, or DPI).
     *  @param value The {@code double} value to scale.
     *  @return The scaled {@code double} value.
     */
    public double scale( double value ) { return UI.scale(value); }

    @Override
    public String toString() {
        try {
            return this.getClass().getSimpleName() + "[" +
                        "styleConf=" + _styleConf + ", " +
                        "component=" + _component + ", " +
                    "]";
        } catch ( Exception e ) {
            return this.getClass().getSimpleName() + "[toString() failed: " + e + "]";
        }
    }
}