Painter.java

  1. package swingtree.api;


  2. import swingtree.UI;
  3. import swingtree.animation.Animation;
  4. import swingtree.animation.AnimationStatus;

  5. import java.awt.Dimension;
  6. import java.awt.Graphics2D;
  7. import java.awt.Insets;
  8. import java.awt.Rectangle;
  9. import java.awt.geom.RoundRectangle2D;
  10. import java.util.concurrent.TimeUnit;

  11. /**
  12.  *  A functional interface for doing custom painting on a component
  13.  *  using the {@link Graphics2D} API.
  14.  *  This is typically used to paint <b>custom styles on components</b> as part of the style API
  15.  *  exposed by {@link swingtree.UIForAnySwing#withStyle(Styler)}, like so:
  16.  *  <pre>{@code
  17.  *  UI.label("I am a label")
  18.  *  .withStyle( it -> it
  19.  *    .size(120, 50)
  20.  *    .padding(6)
  21.  *    .painter(UI.Layer.BACKGROUND, g -> {
  22.  *      g.setColor(Color.ORANGE);
  23.  *      var e = new Ellipse2D.Double(5,5,25,25);
  24.  *      g.fill(UI.scale(e);
  25.  *    })
  26.  *    .fontSize(12)
  27.  *  )
  28.  *  }</pre>
  29.  *  Which is based on the {@link swingtree.style.ComponentStyleDelegate#painter(UI.Layer, Painter)}.
  30.  *  You may also want to take a look at <br>
  31.  *  {@link swingtree.style.ComponentStyleDelegate#painter(UI.Layer, UI.ComponentArea, Painter)}, <br>
  32.  *  {@link swingtree.style.ComponentStyleDelegate#painter(UI.Layer, String, Painter)} and <br>
  33.  *  {@link swingtree.style.ComponentStyleDelegate#painter(UI.Layer, UI.ComponentArea, String, Painter)}. <br>
  34.  *  <br>
  35.  *  You can also use painter implementations
  36.  *  for <b>defining custom component event based animations</b> by registering through the
  37.  *  {@link swingtree.ComponentDelegate#paint(AnimationStatus, Painter)} method
  38.  *  inside of an {@link Animation} registered through
  39.  *  {@link swingtree.ComponentDelegate#animateFor(double, TimeUnit, Animation)}.
  40.  *  <br><br>
  41.  *  Note that inside the painter the {@link Graphics2D} context may not
  42.  *  be scaled according to the current UI scale factor (for high DPI displays). <br>
  43.  *  Check out the following methods for scaling your paint operations: <br>
  44.  *  <ul>
  45.  *      <li>{@link UI#scale()} - returns the current UI scale factor.</li>
  46.  *      <li>{@link UI#scale(Graphics2D)} - scales the given graphics context according to the current UI scale factor.</li>
  47.  *      <li>{@link UI#scale(double)} - scales the given value according to the current UI scale factor.</li>
  48.  *      <li>{@link UI#scale(float)} - scales the given value according to the current UI scale factor.</li>
  49.  *      <li>{@link UI#scale(int)} - scales the given value according to the current UI scale factor.</li>
  50.  *      <li>{@link UI#scale(Insets)} - scales the given insets according to the current UI scale factor.</li>
  51.  *      <li>{@link UI#scale(Dimension)} - scales the given dimension according to the current UI scale factor.</li>
  52.  *      <li>{@link UI#scale(Rectangle)} - scales the given rectangle according to the current UI scale factor.</li>
  53.  *      <li>{@link UI#scale(RoundRectangle2D)} - scales the given round rectangle according to the current UI scale factor.</li>
  54.  *      <li>{@link UI#scale(java.awt.geom.Ellipse2D)} - scales the given ellipse according to the current UI scale factor.</li>
  55.  *  </ul><br>
  56.  *  <br>
  57.  *  Note that your custom painters will yield the best performance if they are stateless and immutable
  58.  *  as well has if they have a good {@link Object#hashCode()} and {@link Object#equals(Object)} implementation.
  59.  *  This is because it allows SwingTree to cache the rendering of the painters and avoid unnecessary repaints. <br>
  60.  *  <b>If you do not want to create a custom class just for painting but instead
  61.  *  just want to pass an immutable cache key to a painter, then consider using the
  62.  *  {@link #of(Object, Painter)} factory method to create a painter that has the
  63.  *  with {@link Object#hashCode()} and {@link Object#equals(Object)} implemented as a delegate to the data object.</b>
  64.  *  <p>
  65.  *  <b>Also consider taking a look at the <br> <a href="https://globaltcad.github.io/swing-tree/">living swing-tree documentation</a>
  66.  *  where you can browse a large collection of examples demonstrating how to use the API of Swing-Tree in general.</b>
  67.  *
  68.  */
  69. @FunctionalInterface
  70. public interface Painter
  71. {
  72.     /**
  73.      *  Exposes a constant painter that paints nothing.
  74.      *  This is useful as a no-op null object pattern.
  75.      * @return A painter that paints nothing.
  76.      */
  77.     static Painter none() { return Constants.PAINTER_NONE; }

  78.     /**
  79.      *  Allows you to create a cacheable painter that uses the given data object as a cache key.
  80.      *  The provided data object should be immutable and have exhaustive {@link Object#hashCode()}
  81.      *  and {@link Object#equals(Object)} implementations. <br>
  82.      *  The data object is expected to be the sole source of information for the painter's painting operations.
  83.      *  Otherwise, the usage of this method is discouraged as cache based
  84.      *  rendering may not reflect the actual state of the component. <br>
  85.      *
  86.      * @param data The data object to use as a cache key, must be immutable and have
  87.      *             exhaustive {@link Object#hashCode()} and {@link Object#equals(Object)} implementations.
  88.      * @param painter The painter to use for painting, must be stateless and immutable as well.
  89.      *                It is expected to use the given data object as the sole source of information
  90.      *                for its painting operations.
  91.      * @return A cache friendly painter that uses the given data object as a cache key.
  92.      * @param <D> The type of the data object to use as a cache key.
  93.      */
  94.     static <D> Painter of( D data, Painter painter ) {
  95.         return new CachablePainter(data, painter);
  96.     }

  97.     /**
  98.      * Paints a custom style on a component using the given graphics context.
  99.      * @param g2d the graphics context to use for painting.
  100.      */
  101.     void paint( Graphics2D g2d );

  102.     /**
  103.      *  If a painter implementation reports that it can be cached, SwingTree will
  104.      *  use the painter as a cache key and the result of its painting operations
  105.      *  will be cached and reused for equal cache keys. <br>
  106.      *  So a painter that can be cached should be stateless and immutable as well as
  107.      *  have exhaustive {@link Object#hashCode()} and
  108.      *  {@link Object#equals(Object)} implementations. <br>
  109.      *
  110.      * @return true If the painting operation is cachable, false otherwise.
  111.      */
  112.     default boolean canBeCached() { return false; }

  113.     /**
  114.      * Returns a new painter that paints this painter's style and then the given painter's style.
  115.      * @param after the painter to paint after this painter.
  116.      * @return a new painter that paints this painter's style and then the given painter's style.
  117.      */
  118.     default Painter andThen( Painter after ) {
  119.         return new AndThenPainter(this, after);
  120.     }

  121. }