Class ComponentExtension<C extends JComponent>

java.lang.Object
swingtree.style.ComponentExtension<C>
Type Parameters:
C - The type of the component to which this extension is attached as a client property (see JComponent.putClientProperty(Object, Object)).

public final class ComponentExtension<C extends JComponent> extends Object
Is attached to UI components in the form of a client property. It exists to give Swing-Tree components some custom style and animation capabilities.
  • Method Details

    • from

      public static <C extends JComponent> ComponentExtension<C> from(C comp)
      Returns the ComponentExtension associated with the given component. If the component does not have an extension, a new one is created and associated with the component.
      Type Parameters:
      C - The type of the component.
      Parameters:
      comp - The component for which to get the extension.
      Returns:
      The extension associated with the component.
    • initializeFor

      public static void initializeFor(JComponent comp)
      Initializes the given component with a new ComponentExtension. This method is called by a SwingTree builder node when it receives and builds a new component. The former extension of the component is replaced by a new one.
      Parameters:
      comp - The component to initialize.
    • localUiScaleFactor

      public sprouts.Viewable<Float> localUiScaleFactor()
      Returns a reactive view of the current UI scale factor as applied to this component. You can use it to register change listeners onto it and react to changes to the factor. The value in the property always reflects SwingTree.getUiScaleFactor() and it is automatically updated when it changes...
      Returns:
      A reactive/read-only float based property which will always reflect the state of the library global SwingTree.getUiScaleFactor() value.
    • paintBackground

      public void paintBackground(Graphics graphics, Painter lookAndFeelPainting)

      Primary painting entry point for SwingTree-compatible ComponentUI implementations to ensure correct SwingTree style installation and UI.Layer.BACKGROUND rendering.

      This method is specifically designed to be called from within the ComponentUI.paint(Graphics, JComponent) method of any ComponentUI that implements the SwingTreeStyledComponentUI interface and returns true from SwingTreeStyledComponentUI.canForwardPaintingToSwingTree().

      Typical Usage:
      
       //Override
       public void paint(Graphics g, JComponent comp) {
           ComponentExtension.from(comp).paintBackground(g, g2d -> {
               super.paint(g2d, comp); // Native look and feel painting
           });
       }
       
      What This Does Generally:
      Parameters:
      graphics - The graphics context to paint into
      lookAndFeelPainting - Callback that performs native Swing painting (will be clipped to the UI.ComponentArea.BODY, which is based on the current style)
      Throws:
      ClassCastException - if the provided graphics is not a Graphics2D instance (should never happen in standard Swing usage)
      See Also:
    • storeBoundObservable

      public void storeBoundObservable(sprouts.Observable observable)
      Stores the given observable in the extension in order to ensure that it is not garbage collected before the component is garbage collected. The Sprouts library is based on the idea of event systems being weakly referenced by theirs event sources, which means that if the observable is not stored in the extension, the binding will be lost when the observable is garbage collected.
      Parameters:
      observable - The observable to store using a strong reference to ensure it is not garbage collected.
    • freeBoundObservables

      public void freeBoundObservables()
      Frees all bound observables from the extension. This is useful when the component is no longer needed and is about to be garbage collected.
    • addDragAwayConf

      public void addDragAwayConf(Function<Position,DragAwayComponentConf<C>> supplier)
      Adds a drag away configurator to this component extension. The configurator will be invoked to provide drag away configuration based on the mouse position.
      Parameters:
      supplier - A function that provides a drag away configuration given a mouse position.
      Throws:
      IllegalStateException - If a drag away configurator has already been set.
    • getDragAwayConf

      public Optional<DragAwayComponentConf<C>> getDragAwayConf(Position mousePosition)
      If it exists, this method invokes the user internal configurator function previously set by the addDragAwayConf(Function) method and returns an optional of the resulting DragAwayComponentConf object, which holds the configuration for starting a drag away operation using the AWT native DragSource.
      Parameters:
      mousePosition - The current mouse position.
      Returns:
      An optional of the resulting DragAwayComponentConf object.
    • getOrSet

      public <P> P getOrSet(Class<P> type, Supplier<P> fetcher)
      Allows for extra state to be attached to the component extension. (Conceptually similar to how Swing components can have client properties.)
      If the component already has an object of the given type attached, that object is returned. Otherwise, the given fetcher is used to create a new object of the given type, which is then attached to the component and returned.
      Type Parameters:
      P - The type of the extra state.
      Parameters:
      type - The type of the extra state to attach.
      fetcher - A supplier which is used to create a new object of the given type.
      Returns:
      The extra state object of the given type which is attached to the component.
    • get

      public <P> Optional<P> get(Class<P> type)
      Looks up an extra-state object of the given type previously attached to this component extension (see getOrSet(Class, Supplier)), without creating or attaching anything. This is the pure-read companion to getOrSet(Class, Supplier): it never has side effects and simply tells you whether a plugin of the given type is currently present, and what it is.
      Type Parameters:
      P - The type of the extra state.
      Parameters:
      type - The type of the extra state to look up.
      Returns:
      An Optional holding the attached object of the given type, or an empty Optional if none is currently attached.
    • setStyleGroups

      public void setStyleGroups(String... groupTags)
      This method is used by UIForAnySwing.group(String...) to attach so called group tags to a component.
      They are used by the SwingTree style engine to apply styles with the same tags, which is conceptually similar to CSS classes.
      It is advised to use the setStyleGroups(Enum[]) method instead of this method, as the usage of enums for modelling group tags offers much better compile time type safety!
      Parameters:
      groupTags - An array of group tags.
    • setStyleGroups

      @SafeVarargs public final <E extends Enum<E>> void setStyleGroups(E... groupTags)
      This method is used by UIForAnySwing.group(String...) to attach so-called group tags to a component.
      They are used by the SwingTree style engine to apply styles with the same tags, which is conceptually similar to CSS classes.
      It is advised to use this method over the setStyleGroups(String[]) method, as the usage of enums for modelling group tags offers much better compile time type safety!
      Type Parameters:
      E - The type of the enum.
      Parameters:
      groupTags - An array of group tags.
    • setId

      public void setId(String id)
      Sets the id of the component. The id is used by the SwingTree style engine to apply styles to components with the same id, which is conceptually similar to CSS ids.
      The preferred way to set the id is by using an enum to avoid typos and to get better compile time type safety.
      Parameters:
      id - The id to set.
    • setId

      public <E extends Enum<E>> void setId(E id)
      Sets the id of the component based on an enum. The id is used by the SwingTree style engine to apply styles to components with the same id, which is conceptually similar to CSS ids.
      This is the preferred way to set the id, as it offers better compile time type safety. Also check out the UIForAnySwing.id(Enum) to set the id as part of a UI declaration.
      Type Parameters:
      E - The type of the enum.
      Parameters:
      id - The id to set.
    • hasId

      public boolean hasId(String id)
      Checks if the component has the given id.
      Parameters:
      id - The id to check.
      Returns:
      true if the component has the given id.
    • hasId

      public boolean hasId(Enum<?> id)
      Checks if the component has the given id.
      Parameters:
      id - The id to check.
      Returns:
      true if the component has the given id.
    • getStyleGroups

      public sprouts.Tuple<String> getStyleGroups()
      A component can have multiple group tags, which are used by the SwingTree style engine to apply styles with the same tags, which is conceptually similar to CSS classes. This method returns the group tags associated with the component.
      Returns:
      The group tags associated with the component in the form of an unmodifiable tuple of Strings.
    • belongsToGroup

      public boolean belongsToGroup(String group)
      A style group is a tag which is used by the SwingTree style engine to apply styles to things with the same tags making it conceptually similar to CSS classes. This method lets you check if the component belongs to a given String based group.
      Parameters:
      group - The group to check.
      Returns:
      true if the component belongs to the given group.
    • belongsToGroup

      public boolean belongsToGroup(Enum<?> group)
      A style group is a tag which is used by the SwingTree style engine to apply styles to things with the same tags making it conceptually similar to CSS classes. This method lets you check if the component belongs to a given enum based group.
      Parameters:
      group - The group to check.
      Returns:
      true if the component belongs to the given group.
    • getStyle

      public StyleConf getStyle()
      Exposes the current StyleConf configuration of the component, which holds all the SwingTree style information needed to render the component.
      Returns:
      The current StyleConf configuration of the component which is calculated based on the Styler lambdas associated with the component.
    • cachedRendering

      public sprouts.Tuple<BufferedImage> cachedRendering(UI.Layer layer)
      The fully rendered cache images standing by for the given style UI.Layer of this component, in the order they are painted. When there are any, the next repaint of that layer will be served from them rather than by re-running the style renderer. This is a per-component window into the internal LayerPartitionCache pipeline for tests and tooling, not a hard guarantee about the next paint - cache entries are weakly referenced and may be reclaimed under memory pressure. Caching only kicks in for layers with at least one heavy style ingredient (rounded backgrounds with a base/foundation colour, borders with width and colour, gradients, shadows, noise, painted text, sized icons); other layers permanently report an empty tuple because caching them would not pay for itself.

      How many images to expect. There is no limit on how much style a single layer may carry, and not all of it caches the same way,

      • none - nothing about this layer is currently cached.
      • one - the whole layer rasterizes into a single image, which is the ordinary case.
      • several - the cache split the layer, because one part of it can be cached in a way another part cannot. Painting the layer then means painting these in order, with the uncached parts rendered in between.
      The one split that happens today is a layer carrying a noise while its component is being resized: noise pixels vary with every pixel position, so a noise cannot live in a size independent exemplar - but it is cheap to simply draw again. So for the duration of the resize it is lifted out and replayed on every paint, and what sits under and over it is cached separately, as exemplars that survive the resize. Either of those two may also be absent, when that side of the noise holds no style (or none worth caching), which is why such a layer can report two images, one, or none at all. The dimensions of each image reveal how that part is cached: style whose pixels are constant along the component edges (flat colours, borders, shadows) is stored as a small, size independent exemplar rendering which may be much smaller than the component itself (it is stretch tiled back to any actual size on paint, see SwingTree.setCacheTilingEnabled(boolean)), while everything else is cached at exactly the component size.

      The returned images are defensive copies: a cached rendering is shared by all components with an equal style, so callers may examine (or even modify) the copies freely without corrupting anyone's painting.

      Parameters:
      layer - The style layer to query (typically UI.Layer.BACKGROUND for the cases people care about most).
      Returns:
      Copies of the rendered cache images currently associated with the given layer of this component, in paint order, or an empty tuple if there are none.
    • cacheHitCount

      public int cacheHitCount(UI.Layer layer)
      Returns the number of times this component's given style UI.Layer was painted entirely from a cached image, i.e. the style renderer was not invoked. The counter is local to this component instance and increases on every paint that is served from the cache.

      This counter is cumulative for the lifetime of this component instance. It does not automatically reset when the underlying cache entry is freed, invalidated, or rebuilt; such events only affect whether future paints contribute to this counter or to cacheMissCount(UI.Layer). Together the two therefore say how often this component painted the layer at all — a paint which put no pixels anywhere, because the layer holds no style worth drawing, is counted by neither, since nothing was painted for a cache to have served.

      One nuance for layers carrying a noise: while such a component is being resized the noise is lifted out of the cached image and drawn again on every paint (see cachedRendering(UI.Layer)). Those paints still count as hits, because redrawing a noise is cheap by construction — it is blitted from pre-rendered tiles, or is a single fill — and counting them as renders would hide the saving the split exists to make. A paint which did draw something, but which no cache took part in, is always a miss.

      Parameters:
      layer - The style layer to query.
      Returns:
      Number of paint calls served from the cache, since this component was constructed.
    • cacheMissCount

      public int cacheMissCount(UI.Layer layer)
      Returns the number of times this component's given style UI.Layer was painted without a cache image serving it – either because the style renderer had to be invoked (caching is disabled for that layer, or the cache was not yet populated), or because there was nothing for a cache to hold in the first place, which happens to a layer whose entire style is a noise while it is being resized: the noise is then lifted out and replayed, and nothing is left on either side of it to cache. The counter is local to this component instance.
      Parameters:
      layer - The style layer to query.
      Returns:
      Number of paint calls that had to render fresh, since this component was constructed.
    • globalRenderCacheEntryCounts

      public static sprouts.Association<String,Integer> globalRenderCacheEntryCounts()
      A live snapshot of SwingTree's global rendering caches: one entry per cache with its current number of cached items, in a stable, display-friendly order. The counts react to painting, to garbage collection (the caches are weakly keyed), and to configuration changes like SwingTree.setCacheMode(swingtree.SwingTreeInitConfig.CacheMode) — which makes this the observation point for monitoring (the dev tool displays it live) and for tests pinning the cache-budget contract.
      Returns:
      An ordered association (immutable map) from cache display name to its live entry count.
    • globalStyleLayerCacheBytesReserved

      public static long globalStyleLayerCacheBytesReserved()
      How much memory the cached style layer renderings have reserved, in bytes.

      Reserved, not allocated: an entry claims its bytes the moment it exists, whereas a large rendering only allocates its buffer after a warm-up of cache hits has shown it is worth it. The two figures differ for as long as that warm-up lasts, and this is the higher of them - which is the honest one to report, because it is what the budget is enforced against, and because the difference is memory already promised away. What it counts is the pixel memory reserved by each entry's backing images (including any stretch tiles), counted from the moment the entry exists rather than only after the backing buffers are allocated.

      This is the observation point for the memory promise a SwingTreeInitConfig.CacheMode makes: style layer images are the only rendering cache big enough for that promise to be about, and it is the one bounded by measuring itself rather than by counting entries, because its entries range from a few kilobytes to tens of megabytes. Counting them (which is what globalRenderCacheEntryCounts() reports) therefore says nothing about memory, which is why this exists alongside it.

      Call this on the UI thread. Unlike the entry counts, arriving at this figure means walking the cache, and painting writes to it - so a call from another thread races the next repaint.
      Returns:
      Bytes currently reserved by the global style layer render cache.
    • globalStyleLayerCacheByteBudget

      public static long globalStyleLayerCacheByteBudget()
      The ceiling globalStyleLayerCacheBytesReserved() is held under: the share of the current SwingTreeInitConfig.CacheMode budget that style layer renderings may spend.

      Reported next to the figure it bounds because neither number means much alone - the budget scales with the machine's RAM and with the configured mode, so "eleven megabytes cached" is only interpretable against the ceiling in force at the time.
      Returns:
      Bytes the global style layer render cache may retain at most, right now.
    • updateAllCachesFromLibraryConfig

      public static void updateAllCachesFromLibraryConfig()
      The single bridge through which the swingtree package tells the rendering caches in the swingtree.style package that the library configuration may have changed (see SwingTree.setCacheMode(swingtree.SwingTreeInitConfig.CacheMode)). It does two things, in this order:
      1. marks the shared CacheBudget as needing to re-resolve the current SwingTreeInitConfig.CacheMode (on the next paint), and
      2. empties every global rendering cache — style layers, rasterised text, noise tiles, shadow gradients and text layouts — so that memory is released immediately rather than only as old entries are evicted.
      Each cache then repopulates lazily under the new budget. This is deliberately recursion-safe: it never reads SwingTree.get() itself (so it is safe to call while the SwingTree singleton is still being constructed); the budget is only resolved later, lazily, from the painting thread.

      Call this on the UI thread. It writes the very maps painting reads, and the two library setters which reach it (SwingTree.setCacheMode(swingtree.SwingTreeInitConfig.CacheMode) and SwingTree.setCacheTilingEnabled(boolean)) marshal onto that thread before they do. Calling it from anywhere else races the next repaint.

    • getComponentArea

      public Optional<Shape> getComponentArea(UI.ComponentArea area)
      Allows for the retrieval of a specific Shape which represents a specific area of the component identified by the given UI.ComponentArea. The following areas are available:
      • UI.ComponentArea.ALL - The entire component, which is the union of all other clip areas (INTERIOR + EXTERIOR + BORDER + CONTENT).
      • UI.ComponentArea.INTERIOR - The inner component area, which is defined as ALL - EXTERIOR - BORDER.
      • UI.ComponentArea.EXTERIOR - The outer component area, which can be expressed as ALL - INTERIOR - BORDER, or ALL - CONTENT.
      • UI.ComponentArea.BORDER - The border of the component, which is the area between the inner and outer component area and which can be expressed as ALL - INTERIOR - EXTERIOR.
      • UI.ComponentArea.BODY - The body of the component is the inner component area including the border area. It can be expressed as ALL - EXTERIOR, or INTERIOR + BORDER.
      Parameters:
      area - The area of the component to retrieve.
      Returns:
      An optional Shape which represents the given area of the component or an empty optional. If the area is not available, then this means that the style of the component did not lead to the calculation of the given area. This may happen for the EXTERIOR in case of there being no margin or corner radius, and the BORDER in case of there being no border width.
    • clearAnimations

      public void clearAnimations()
      Removes all animations from the component. This includes both Painter based animations as well as Styler based animations.
    • addAnimatedPainter

      public void addAnimatedPainter(AnimationStatus status, UI.Layer layer, UI.ComponentArea clipArea, Painter painter)
      Use this to add a Painter based animation to the component.
      Parameters:
      status - The AnimationStatus which defines when the animation is active.
      layer - The UI.Layer which defines the layer on which the animation is rendered.
      clipArea - The UI.ComponentArea which defines the area of the component which is animated.
      painter - The Painter which defines how the animation is rendered.
    • addAnimatedStyler

      public void addAnimatedStyler(AnimationStatus state, Styler<C> styler)
      Use this to add a Styler based animation to the component.
      Parameters:
      state - The AnimationStatus which defines when the animation is active.
      styler - The Styler which defines how the style of the component is changed during the animation.
    • installCustomUIIfPossible

      public void installCustomUIIfPossible()
      SwingTree overrides the default Swing look and feel to enable custom styling and animation capabilities. This method is used to install the custom look and feel for the component, if possible.
    • addStyler

      public void addStyler(Styler<C> styler)
      Adds a Styler to the component. The styler will be used to calculate the style of the component.
      Parameters:
      styler - The styler to add.
    • gatherStyle

      public StyleConf gatherStyle()
      Calculates a new StyleConf object based on the Styler lambdas associated with the component...
      Returns:
      A new immutable StyleConf configuration.
    • gatherApplyAndInstallStyle

      public void gatherApplyAndInstallStyle(boolean force)
      Calculates a new StyleConf object based on the Styler lambdas associated with the component and then applies it to the component after which a new StyleEngine is installed for the component. If the calculated style is the same as the current style, nothing happens except in case the force parameter is set to true.
      Parameters:
      force - If set to true, the style will be applied even if it is the same as the current style.
    • applyAndInstallStyle

      public void applyAndInstallStyle(StyleConf styleConf, boolean force)
      Applies the given StyleConf to the component after which a new StyleEngine is installed for the component. If the given style is the same as the current style, nothing happens except in case the force parameter is set to true.
      Parameters:
      styleConf - The style to apply.
      force - If set to true, the style will be applied even if it is the same as the current style.
    • viewStateHashCode

      public int viewStateHashCode()
      This method tries to hash everything relevant in the visual appearance of the component and it subcomponents into a single integer value. It is based on the current SwingTree style information as well as more general component information like the current value of a slider, text of a text component, etc.

      You may use this for rough cache invalidation purposes. So when you want to render the component into a BufferedImage and then only rerender it if the state hash changes, you can use this method.

      But keep in mind however, it cannot capture look and feel related changes which are not controlled by SwingTree.
      So this hash code is not a perfect solution, but it can be useful in some cases. Like visualizing a drag and drop of a component...

      Returns:
      The current state hash of the component and all of it subcomponents, which includes SwingTree style information, as well as component specific information.