From 4d7a6ff3315fafbe382e3c233f185ca4a4e6fbda Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Thu, 13 Mar 2025 11:46:14 +0100 Subject: [PATCH] AnimatedPainter: - renamed `getValues()` to `getAnimatableValues()` - renamed `getClientPropertyKey()` to `getAnimationClientPropertyKey()` - AnimatedPainterSupport improvements --- .../formdev/flatlaf/util/AnimatedBorder.java | 16 +++-- .../formdev/flatlaf/util/AnimatedIcon.java | 67 +++++++++++++------ .../formdev/flatlaf/util/AnimatedPainter.java | 42 ++++++------ .../flatlaf/util/AnimatedPainterSupport.java | 27 ++++---- .../testing/FlatAnimatedBorderTest.java | 6 +- .../flatlaf/testing/FlatAnimatedIconTest.java | 8 +-- 6 files changed, 100 insertions(+), 66 deletions(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/AnimatedBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/AnimatedBorder.java index afb7a8e2..aa0ec72b 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/AnimatedBorder.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/AnimatedBorder.java @@ -25,10 +25,12 @@ import javax.swing.border.Border; /** * Border that automatically animates painting on component value changes. *

- * {@link #getValues(Component)} returns the value(s) of the component. + * {@link #getAnimatableValues(Component)} returns the animatable value(s) of the component. * If the value(s) have changed, then {@link #paintAnimated(Component, Graphics2D, int, int, int, int, float[])} * is invoked multiple times with animated value(s) (from old value(s) to new value(s)). - * If {@link #getValues(Component)} returns multiple values, then each value gets its own independent animation. + * If {@link #getAnimatableValues(Component)} returns multiple values, then each value + * gets its own independent animation, which may start/end at different points in time, + * may have different duration, resolution and interpolator. *

* Example for an animated border: *

@@ -36,6 +38,11 @@ import javax.swing.border.Border;
  *     implements AnimatedBorder
  * {
  *     @Override
+ *     public float[] getAnimatableValues( Component c ) {
+ *         return new float[] { c.isFocusOwner() ? 1 : 0 };
+ *     }
+ *
+ *     @Override
  *     public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
  *         int lh = UIScale.scale( 2 );
  *
@@ -44,11 +51,6 @@ import javax.swing.border.Border;
  *     }
  *
  *     @Override
- *     public float[] getValues( Component c ) {
- *         return new float[] { c.isFocusOwner() ? 1 : 0 };
- *     }
- *
- *     @Override
  *     public Insets getBorderInsets( Component c ) {
  *         return UIScale.scale( new Insets( 4, 4, 4, 4 ) );
  *     }
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/AnimatedIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/AnimatedIcon.java
index 5f351bcc..2381bb91 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/AnimatedIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/AnimatedIcon.java
@@ -25,16 +25,23 @@ import javax.swing.JComponent;
 /**
  * Icon that automatically animates painting on component value changes.
  * 

- * {@link #getValues(Component)} returns the value(s) of the component. + * {@link #getAnimatableValues(Component)} returns the animatable value(s) of the component. * If the value(s) have changed, then {@link #paintAnimated(Component, Graphics2D, int, int, int, int, float[])} * is invoked multiple times with animated value(s) (from old value(s) to new value(s)). - * If {@link #getValues(Component)} returns multiple values, then each value gets its own independent animation. + * If {@link #getAnimatableValues(Component)} returns multiple values, then each value + * gets its own independent animation, which may start/end at different points in time, + * may have different duration, resolution and interpolator. *

* Example for an animated icon: *

  * private class MyAnimatedIcon
  *     implements AnimatedIcon
  * {
+ *     @Override
+ *     public float[] getAnimatableValues( Component c ) {
+ *         return new float[] { ((AbstractButton)c).isSelected() ? 1 : 0 };
+ *     }
+ *
  *     @Override public int getIconWidth() { return 100; }
  *     @Override public int getIconHeight() { return 20; }
  *
@@ -44,11 +51,6 @@ import javax.swing.JComponent;
  *         g.drawRect( x, y, width - 1, height - 1 );
  *         g.fillRect( x, y, Math.round( width * animatedValues[0] ), height );
  *     }
- *
- *     @Override
- *     public float[] getValues( Component c ) {
- *         return new float[] { ((AbstractButton)c).isSelected() ? 1 : 0 };
- *     }
  * }
  *
  * // sample usage
@@ -57,7 +59,7 @@ import javax.swing.JComponent;
  * 
* * Animation works only if the component passed to {@link #paintIcon(Component, Graphics, int, int)} - * is a instance of {@link JComponent}. + * is an instance of {@link JComponent}. * A client property is set on the component to store the animation state. * * @author Karl Tauber @@ -65,6 +67,17 @@ import javax.swing.JComponent; public interface AnimatedIcon extends Icon, AnimatedPainter { + /** + * {@inheritDoc} + * + * @since 2 + */ + @Override + default float[] getAnimatableValues( Component c ) { + // for compatibility + return new float[] { getValue( c ) }; + } + /** * Invokes {@link #paintWithAnimation(Component, Graphics, int, int, int, int)}. */ @@ -80,6 +93,7 @@ public interface AnimatedIcon */ @Override default void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) { + // for compatibility paintIconAnimated( c, g, x, y, animatedValues[0] ); } @@ -101,30 +115,41 @@ public interface AnimatedIcon } /** - * {@inheritDoc} - * - * @since 2 - */ - @Override - default float[] getValues( Component c ) { - return new float[] { getValue( c ) }; - } - - /** - * Gets the value of the component. + * Gets the animatable value of the component. *

* This can be any value and depends on the component. * If the value changes, then this class animates from the old value to the new one. *

* For a toggle button this could be {@code 0} for off and {@code 1} for on. * - * @deprecated override {@link #getValues(Component)} instead + * @deprecated override {@link #getAnimatableValues(Component)} instead */ @Deprecated default float getValue( Component c ) { return 0; } + /** + * {@inheritDoc} + * + * @since TODO + */ + @Override + default Object getAnimationClientPropertyKey() { + // for compatibility + return getClientPropertyKey(); + } + + /** + * Returns the client property key used to store the animation support. + * + * @deprecated override {@link #getAnimationClientPropertyKey()} instead + */ + @Deprecated + default Object getClientPropertyKey() { + return getClass(); + } + //---- class AnimationSupport --------------------------------------------- /** @@ -138,7 +163,7 @@ public interface AnimatedIcon */ @Deprecated public static void paintIcon( AnimatedIcon icon, Component c, Graphics g, int x, int y ) { - AnimatedPainterSupport.paint( icon, c, g, x, y, icon.getIconWidth(), icon.getIconHeight() ); + AnimatedPainterSupport.paint( icon, c, (Graphics2D) g, x, y, icon.getIconWidth(), icon.getIconHeight() ); } /** diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/AnimatedPainter.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/AnimatedPainter.java index 0ff83de7..58270a06 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/AnimatedPainter.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/AnimatedPainter.java @@ -25,15 +25,17 @@ import com.formdev.flatlaf.util.Animator.Interpolator; /** * Painter that automatically animates painting on component value(s) changes. *

- * {@link #getValues(Component)} returns the value(s) of the component. + * {@link #getAnimatableValues(Component)} returns the animatable value(s) of the component. * If the value(s) have changed, then {@link #paintAnimated(Component, Graphics2D, int, int, int, int, float[])} * is invoked multiple times with animated value(s) (from old value(s) to new value(s)). - * If {@link #getValues(Component)} returns multiple values, then each value gets its own independent animation. + * If {@link #getAnimatableValues(Component)} returns multiple values, then each value + * gets its own independent animation, which may start/end at different points in time, + * may have different duration, resolution and interpolator. *

* See {@link AnimatedBorder} or {@link AnimatedIcon} for examples. *

* Animation works only if the component passed to {@link #paintWithAnimation(Component, Graphics, int, int, int, int)} - * is a instance of {@link JComponent}. + * is an instance of {@link JComponent}. * A client property is set on the component to store the animation state. * * @author Karl Tauber @@ -41,10 +43,23 @@ import com.formdev.flatlaf.util.Animator.Interpolator; */ public interface AnimatedPainter { + /** + * Gets the animatable value(s) of the component. + *

+ * This can be any value(s) and depends on the component. + * If the value(s) changes, then this class animates from the old value(s) to the new ones. + * If multiple values are returned, then each value gets its own independent animation. + *

+ * For a toggle button this could be {@code 0} for off and {@code 1} for on. + * A complex check box could return values for selected, hover, pressed and focused states. + * The painter then can show independent animations for those states. + */ + float[] getAnimatableValues( Component c ); + /** * Starts painting. * Either invokes {@link #paintAnimated(Component, Graphics2D, int, int, int, int, float[])} - * once to paint current value(s) (see {@link #getValues(Component)}. Or if value(s) has + * once to paint current value(s) (see {@link #getAnimatableValues(Component)}. Or if value(s) has * changed, compared to last painting, then it starts an animation and invokes * {@link #paintAnimated(Component, Graphics2D, int, int, int, int, float[])} * multiple times with animated value(s) (from old value(s) to new value(s)). @@ -57,7 +72,7 @@ public interface AnimatedPainter * @param height the height of the paint area */ default void paintWithAnimation( Component c, Graphics g, int x, int y, int width, int height ) { - AnimatedPainterSupport.paint( this, c, g, x, y, width, height ); + AnimatedPainterSupport.paint( this, c, (Graphics2D) g, x, y, width, height ); } /** @@ -71,9 +86,9 @@ public interface AnimatedPainter * @param y the y coordinate of the paint area * @param width the width of the paint area * @param height the height of the paint area - * @param animatedValues the animated values, which are either equal to what {@link #getValues(Component)} + * @param animatedValues the animated values, which are either equal to what {@link #getAnimatableValues(Component)} * returned, or somewhere between the previous values and the latest values - * that {@link #getValues(Component)} returned + * that {@link #getAnimatableValues(Component)} returned */ void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ); @@ -91,17 +106,6 @@ public interface AnimatedPainter c.repaint( x, y, width, height ); } - /** - * Gets the value(s) of the component. - *

- * This can be any value and depends on the component. - * If the value(s) changes, then this class animates from the old value(s) to the new ones. - * If multiple values are returned, then each value gets its own independent animation. - *

- * For a toggle button this could be {@code 0} for off and {@code 1} for on. - */ - float[] getValues( Component c ); - /** * Returns whether animation is enabled for this painter (default is {@code true}). */ @@ -161,7 +165,7 @@ public interface AnimatedPainter /** * Returns the client property key used to store the animation support. */ - default Object getClientPropertyKey() { + default Object getAnimationClientPropertyKey() { return getClass(); } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/AnimatedPainterSupport.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/AnimatedPainterSupport.java index 1492c31f..4a41fdbe 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/AnimatedPainterSupport.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/AnimatedPainterSupport.java @@ -17,7 +17,6 @@ package com.formdev.flatlaf.util; import java.awt.Component; -import java.awt.Graphics; import java.awt.Graphics2D; import javax.swing.JComponent; @@ -29,7 +28,8 @@ import javax.swing.JComponent; */ class AnimatedPainterSupport { - private int valueIndex; + private final int valueIndex; + private float startValue; private float targetValue; private float animatedValue; @@ -43,22 +43,26 @@ class AnimatedPainterSupport private int width; private int height; - static void paint( AnimatedPainter painter, Component c, Graphics g, + private AnimatedPainterSupport( int valueIndex ) { + this.valueIndex = valueIndex; + } + + static void paint( AnimatedPainter painter, Component c, Graphics2D g, int x, int y, int width, int height ) { + // get animatable component values + float[] values = painter.getAnimatableValues( c ); + if( !isAnimationEnabled( painter, c ) ) { // paint without animation if animation is disabled or // component is not a JComponent and therefore does not support // client properties, which are required to keep animation state - painter.paintAnimated( c, (Graphics2D) g, x, y, width, height, painter.getValues( c ) ); + painter.paintAnimated( c, g, x, y, width, height, values ); return; } - // get component values - float values[] = painter.getValues( c ); - JComponent jc = (JComponent) c; - Object key = painter.getClientPropertyKey(); + Object key = painter.getAnimationClientPropertyKey(); AnimatedPainterSupport[] ass = (AnimatedPainterSupport[]) jc.getClientProperty( key ); // check whether length of values array has changed @@ -83,8 +87,7 @@ class AnimatedPainterSupport if( as == null ) { // painted first time --> do not animate, but remember current component value - as = new AnimatedPainterSupport(); - as.valueIndex = i; + as = new AnimatedPainterSupport( i ); as.startValue = as.targetValue = as.animatedValue = value; ass[i] = as; } else if( value != as.targetValue ) { @@ -159,7 +162,7 @@ class AnimatedPainterSupport for( int i = 0; i < ass.length; i++ ) animatedValues[i] = ass[i].animatedValue; - painter.paintAnimated( c, (Graphics2D) g, x, y, width, height, animatedValues ); + painter.paintAnimated( c, g, x, y, width, height, animatedValues ); } private static boolean isAnimationEnabled( AnimatedPainter painter, Component c ) { @@ -170,7 +173,7 @@ class AnimatedPainterSupport if( !isAnimationEnabled( painter, c ) ) return; - AnimatedPainterSupport[] ass = (AnimatedPainterSupport[]) ((JComponent)c).getClientProperty( painter.getClientPropertyKey() ); + AnimatedPainterSupport[] ass = (AnimatedPainterSupport[]) ((JComponent)c).getClientProperty( painter.getAnimationClientPropertyKey() ); if( ass != null ) { for( int i = 0; i < ass.length; i++ ) { AnimatedPainterSupport as = ass[i]; diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatAnimatedBorderTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatAnimatedBorderTest.java index 46e17f96..fa84f7c3 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatAnimatedBorderTest.java +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatAnimatedBorderTest.java @@ -244,7 +244,7 @@ public class FlatAnimatedBorderTest } @Override - public float[] getValues( Component c ) { + public float[] getAnimatableValues( Component c ) { return new float[] { FlatUIUtils.isPermanentFocusOwner( c ) ? 1 : 0 }; } @@ -315,7 +315,7 @@ public class FlatAnimatedBorderTest } @Override - public float[] getValues( Component c ) { + public float[] getAnimatableValues( Component c ) { return new float[] { FlatUIUtils.isPermanentFocusOwner( c ) ? 1 : 0 }; } @@ -416,7 +416,7 @@ public class FlatAnimatedBorderTest } @Override - public float[] getValues( Component c ) { + public float[] getAnimatableValues( Component c ) { return new float[] { FlatUIUtils.isPermanentFocusOwner( c ) ? 1 : 0 }; } diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatAnimatedIconTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatAnimatedIconTest.java index 12f8bc22..3cf352c1 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatAnimatedIconTest.java +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatAnimatedIconTest.java @@ -221,7 +221,7 @@ public class FlatAnimatedIconTest } @Override - public float[] getValues( Component c ) { + public float[] getAnimatableValues( Component c ) { return new float[] { ((JRadioButton)c).isSelected() ? 1 : 0 }; } @@ -266,7 +266,7 @@ public class FlatAnimatedIconTest } @Override - public float[] getValues( Component c ) { + public float[] getAnimatableValues( Component c ) { return new float[] { ((AbstractButton)c).isSelected() ? 1 : 0 }; } @@ -342,7 +342,7 @@ public class FlatAnimatedIconTest } @Override - public float[] getValues( Component c ) { + public float[] getAnimatableValues( Component c ) { AbstractButton b = (AbstractButton) c; ButtonModel bm = b.getModel(); @@ -392,7 +392,7 @@ public class FlatAnimatedIconTest } @Override - public float[] getValues( Component c ) { + public float[] getAnimatableValues( Component c ) { return new float[] { ((AbstractButton)c).isSelected() ? 1 : 0 }; }