Skip to content
Draft
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,13 @@ public void paintIcon( Component c, Graphics g, int x, int y ) {
}
}

protected abstract void paintIcon( Component c, Graphics2D g2 );
/**
* Paint the icon at {@code [0,0]} location.
* <p>
* The given graphics context is scaled.
* Use unscaled coordinates, width and height for painting.
*/
protected abstract void paintIcon( Component c, Graphics2D g );

@Override
public int getIconWidth() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.awt.Graphics;
import java.awt.Graphics2D;
import com.formdev.flatlaf.util.AnimatedIcon;
import com.formdev.flatlaf.util.AnimatedPainter;

/**
* Base class for animated icons that scales width and height, creates and initializes
Expand All @@ -30,7 +31,7 @@
* <p>
* This class does not store any state information (needed for animation) in its instance.
* Instead a client property is set on the painted component.
* This makes it possible to use a share icon instance for multiple components.
* This makes it possible to use a shared icon instance for multiple components.
*
* @author Karl Tauber
*/
Expand All @@ -45,11 +46,34 @@ public FlatAnimatedIcon( int width, int height, Color color ) {
@Override
public void paintIcon( Component c, Graphics g, int x, int y ) {
super.paintIcon( c, g, x, y );
AnimatedIcon.AnimationSupport.saveIconLocation( this, c, x, y );
AnimatedPainter.saveRepaintLocation( this, c, x, y );
}

@Override
protected void paintIcon( Component c, Graphics2D g ) {
AnimatedIcon.AnimationSupport.paintIcon( this, c, g, 0, 0 );
paintWithAnimation( c, g, 0, 0, getIconWidth(), getIconHeight() );
}

/**
* Delegates painting to {@link #paintIconAnimated(Component, Graphics2D, float[])}.
* Ignores the given bounds because {@code [x,y]} are always {@code [0,0]} and
* {@code [width,height]} are scaled, but painting code should use unscaled width
* and height because given graphics context is scaled.
*
* @since 2
*/
@Override
public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
paintIconAnimated( c, g, animatedValues );
}

/**
* Paint the icon at {@code 0,0} location.
* <p>
* The given graphics context is scaled.
* Use unscaled coordinates, width and height for painting.
*
* @since 2
*/
protected abstract void paintIconAnimated( Component c, Graphics2D g, float[] animatedValues );
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.formdev.flatlaf.util;

import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import javax.swing.border.Border;

/**
* Border that automatically animates painting on component value changes.
* <p>
* {@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 #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.
* <p>
* Example for an animated border:
* <pre>
* private class MyAnimatedBorder
* implements AnimatedBorder
* {
* &#64;Override
* public float[] getAnimatableValues( Component c ) {
* return new float[] { c.isFocusOwner() ? 1 : 0 };
* }
*
* &#64;Override
* public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
* int lh = UIScale.scale( 2 );
*
* g.setColor( Color.blue );
* g.fillRect( x, y + height - lh, Math.round( width * animatedValues[0] ), lh );
* }
*
* &#64;Override
* public Insets getBorderInsets( Component c ) {
* return UIScale.scale( new Insets( 4, 4, 4, 4 ) );
* }
*
* &#64;Override public boolean isBorderOpaque() { return false; }
* }
*
* // sample usage
* JTextField textField = new JTextField();
* textField.setBorder( new MyAnimatedBorder() );
* </pre>
*
* Animation works only if the component passed to {@link #paintBorder(Component, Graphics, int, int, int, int)}
* is a instance of {@link JComponent}.
* A client property is set on the component to store the animation state.
*
* @author Karl Tauber
* @since 2
*/
public interface AnimatedBorder
extends Border, AnimatedPainter
{
/**
* Invokes {@link #paintWithAnimation(Component, Graphics, int, int, int, int)}.
*/
@Override
default void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
paintWithAnimation( c, g, x, y, width, height );
}
}
Loading
Loading