mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-06 14:00:55 +03:00
Compare commits
11 Commits
2.2
...
animated-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d7a6ff331 | ||
|
|
23fc3674c9 | ||
|
|
34a7214dd8 | ||
|
|
26d7008c04 | ||
|
|
3b489e8e1a | ||
|
|
ccbf577f46 | ||
|
|
b903f18130 | ||
|
|
9523c89c51 | ||
|
|
aa6fb2fcce | ||
|
|
e4fa2e28ea | ||
|
|
b2245e2246 |
@@ -69,7 +69,13 @@ public abstract class FlatAbstractIcon
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.awt.Component;
|
||||
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
|
||||
@@ -30,7 +31,7 @@ import com.formdev.flatlaf.util.AnimatedIcon;
|
||||
* <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
|
||||
*/
|
||||
@@ -45,11 +46,34 @@ public abstract class FlatAnimatedIcon
|
||||
@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 );
|
||||
}
|
||||
|
||||
@@ -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
|
||||
* {
|
||||
* @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 );
|
||||
*
|
||||
* g.setColor( Color.blue );
|
||||
* g.fillRect( x, y + height - lh, Math.round( width * animatedValues[0] ), lh );
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public Insets getBorderInsets( Component c ) {
|
||||
* return UIScale.scale( new Insets( 4, 4, 4, 4 ) );
|
||||
* }
|
||||
*
|
||||
* @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 );
|
||||
}
|
||||
}
|
||||
@@ -18,62 +18,87 @@ package com.formdev.flatlaf.util;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
import com.formdev.flatlaf.util.Animator.Interpolator;
|
||||
|
||||
/**
|
||||
* Icon that automatically animates painting on component value changes.
|
||||
* <p>
|
||||
* {@link #getValue(Component)} returns the value of the component.
|
||||
* If the value changes, then {@link #paintIconAnimated(Component, Graphics, int, int, float)}
|
||||
* is invoked multiple times with animated value (from old value to new value).
|
||||
* {@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 icon:
|
||||
* <pre>
|
||||
* private class AnimatedMinimalTestIcon
|
||||
* 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; }
|
||||
*
|
||||
* @Override
|
||||
* public void paintIconAnimated( Component c, Graphics g, int x, int y, float animatedValue ) {
|
||||
* int w = getIconWidth();
|
||||
* int h = getIconHeight();
|
||||
*
|
||||
* public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
|
||||
* g.setColor( Color.red );
|
||||
* g.drawRect( x, y, w - 1, h - 1 );
|
||||
* g.fillRect( x, y, Math.round( w * animatedValue ), h );
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public float getValue( Component c ) {
|
||||
* return ((AbstractButton)c).isSelected() ? 1 : 0;
|
||||
* g.drawRect( x, y, width - 1, height - 1 );
|
||||
* g.fillRect( x, y, Math.round( width * animatedValues[0] ), height );
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // sample usage
|
||||
* JCheckBox checkBox = new JCheckBox( "test" );
|
||||
* checkBox.setIcon( new AnimatedMinimalTestIcon() );
|
||||
* checkBox.setIcon( new MyAnimatedIcon() );
|
||||
* </pre>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
public interface AnimatedIcon
|
||||
extends Icon
|
||||
extends Icon, AnimatedPainter
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @since 2
|
||||
*/
|
||||
@Override
|
||||
public default void paintIcon( Component c, Graphics g, int x, int y ) {
|
||||
AnimationSupport.paintIcon( this, c, g, x, y );
|
||||
default float[] getAnimatableValues( Component c ) {
|
||||
// for compatibility
|
||||
return new float[] { getValue( c ) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints the icon for the given animated value.
|
||||
* Invokes {@link #paintWithAnimation(Component, Graphics, int, int, int, int)}.
|
||||
*/
|
||||
@Override
|
||||
default void paintIcon( Component c, Graphics g, int x, int y ) {
|
||||
paintWithAnimation( c, g, x, y, getIconWidth(), getIconHeight() );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @since 2
|
||||
*/
|
||||
@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] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints the icon for the given (animated) value.
|
||||
*
|
||||
* @param c the component that this icon belongs to
|
||||
* @param g the graphics context
|
||||
@@ -82,52 +107,45 @@ public interface AnimatedIcon
|
||||
* @param animatedValue the animated value, which is either equal to what {@link #getValue(Component)}
|
||||
* returned, or somewhere between the previous value and the latest value
|
||||
* that {@link #getValue(Component)} returned
|
||||
*
|
||||
* @deprecated override {@link #paintAnimated(Component, Graphics2D, int, int, int, int, float[])} instead
|
||||
*/
|
||||
void paintIconAnimated( Component c, Graphics g, int x, int y, float animatedValue );
|
||||
@Deprecated
|
||||
default void paintIconAnimated( Component c, Graphics g, int x, int y, float animatedValue ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the component.
|
||||
* Gets the animatable value of the component.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* For a toggle button this could be {@code 0} for off and {@code 1} for on.
|
||||
*
|
||||
* @deprecated override {@link #getAnimatableValues(Component)} instead
|
||||
*/
|
||||
float getValue( Component c );
|
||||
|
||||
/**
|
||||
* Returns whether animation is enabled for this icon (default is {@code true}).
|
||||
*/
|
||||
default boolean isAnimationEnabled() {
|
||||
return true;
|
||||
@Deprecated
|
||||
default float getValue( Component c ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the duration of the animation in milliseconds (default is 150).
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
default int getAnimationDuration() {
|
||||
return 150;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resolution of the animation in milliseconds (default is 10).
|
||||
* Resolution is the amount of time between timing events.
|
||||
*/
|
||||
default int getAnimationResolution() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interpolator for the animation.
|
||||
* Default is {@link CubicBezierEasing#STANDARD_EASING}.
|
||||
*/
|
||||
default Interpolator getAnimationInterpolator() {
|
||||
return CubicBezierEasing.STANDARD_EASING;
|
||||
@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();
|
||||
}
|
||||
@@ -135,115 +153,25 @@ public interface AnimatedIcon
|
||||
//---- class AnimationSupport ---------------------------------------------
|
||||
|
||||
/**
|
||||
* Animation support class that stores the animation state and implements the animation.
|
||||
* Animation support.
|
||||
*/
|
||||
@Deprecated
|
||||
class AnimationSupport
|
||||
{
|
||||
private float startValue;
|
||||
private float targetValue;
|
||||
private float animatedValue;
|
||||
private float fraction;
|
||||
|
||||
private Animator animator;
|
||||
|
||||
// last x,y coordinates of the icon needed to repaint while animating
|
||||
private int x;
|
||||
private int y;
|
||||
|
||||
/**
|
||||
* @deprecated use {@link AnimatedPainter#paintWithAnimation(Component, Graphics, int, int, int, int)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static void paintIcon( AnimatedIcon icon, Component c, Graphics g, int x, int y ) {
|
||||
if( !isAnimationEnabled( icon, 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
|
||||
paintIconImpl( icon, c, g, x, y, null );
|
||||
return;
|
||||
}
|
||||
|
||||
JComponent jc = (JComponent) c;
|
||||
Object key = icon.getClientPropertyKey();
|
||||
AnimationSupport as = (AnimationSupport) jc.getClientProperty( key );
|
||||
if( as == null ) {
|
||||
// painted first time --> do not animate, but remember current component value
|
||||
as = new AnimationSupport();
|
||||
as.startValue = as.targetValue = as.animatedValue = icon.getValue( c );
|
||||
as.x = x;
|
||||
as.y = y;
|
||||
jc.putClientProperty( key, as );
|
||||
} else {
|
||||
// get component value
|
||||
float value = icon.getValue( c );
|
||||
|
||||
if( value != as.targetValue ) {
|
||||
// value changed --> (re)start animation
|
||||
|
||||
if( as.animator == null ) {
|
||||
// create animator
|
||||
AnimationSupport as2 = as;
|
||||
as.animator = new Animator( icon.getAnimationDuration(), fraction -> {
|
||||
// check whether component was removed while animation is running
|
||||
if( !c.isDisplayable() ) {
|
||||
as2.animator.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// compute animated value
|
||||
as2.animatedValue = as2.startValue + ((as2.targetValue - as2.startValue) * fraction);
|
||||
as2.fraction = fraction;
|
||||
|
||||
// repaint icon
|
||||
c.repaint( as2.x, as2.y, icon.getIconWidth(), icon.getIconHeight() );
|
||||
}, () -> {
|
||||
as2.startValue = as2.animatedValue = as2.targetValue;
|
||||
as2.animator = null;
|
||||
} );
|
||||
}
|
||||
|
||||
if( as.animator.isRunning() ) {
|
||||
// if animation is still running, restart it from the current
|
||||
// animated value to the new target value with reduced duration
|
||||
as.animator.cancel();
|
||||
int duration2 = (int) (icon.getAnimationDuration() * as.fraction);
|
||||
if( duration2 > 0 )
|
||||
as.animator.setDuration( duration2 );
|
||||
as.startValue = as.animatedValue;
|
||||
} else {
|
||||
// new animation
|
||||
as.animator.setDuration( icon.getAnimationDuration() );
|
||||
as.animator.setResolution( icon.getAnimationResolution() );
|
||||
as.animator.setInterpolator( icon.getAnimationInterpolator() );
|
||||
|
||||
as.animatedValue = as.startValue;
|
||||
}
|
||||
|
||||
as.targetValue = value;
|
||||
as.animator.start();
|
||||
}
|
||||
|
||||
as.x = x;
|
||||
as.y = y;
|
||||
}
|
||||
|
||||
paintIconImpl( icon, c, g, x, y, as );
|
||||
}
|
||||
|
||||
private static void paintIconImpl( AnimatedIcon icon, Component c, Graphics g, int x, int y, AnimationSupport as ) {
|
||||
float value = (as != null) ? as.animatedValue : icon.getValue( c );
|
||||
icon.paintIconAnimated( c, g, x, y, value );
|
||||
}
|
||||
|
||||
private static boolean isAnimationEnabled( AnimatedIcon icon, Component c ) {
|
||||
return Animator.useAnimation() && icon.isAnimationEnabled() && c instanceof JComponent;
|
||||
AnimatedPainterSupport.paint( icon, c, (Graphics2D) g, x, y, icon.getIconWidth(), icon.getIconHeight() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link AnimatedPainter#saveRepaintLocation(AnimatedPainter, Component, int, int)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static void saveIconLocation( AnimatedIcon icon, Component c, int x, int y ) {
|
||||
if( !isAnimationEnabled( icon, c ) )
|
||||
return;
|
||||
|
||||
AnimationSupport as = (AnimationSupport) ((JComponent)c).getClientProperty( icon.getClientPropertyKey() );
|
||||
if( as != null ) {
|
||||
as.x = x;
|
||||
as.y = y;
|
||||
}
|
||||
AnimatedPainterSupport.saveRepaintLocation( icon, c, x, y );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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 com.formdev.flatlaf.util.Animator.Interpolator;
|
||||
|
||||
/**
|
||||
* Painter that automatically animates painting on component value(s) 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>
|
||||
* See {@link AnimatedBorder} or {@link AnimatedIcon} for examples.
|
||||
* <p>
|
||||
* Animation works only if the component passed to {@link #paintWithAnimation(Component, Graphics, int, int, int, int)}
|
||||
* is an instance of {@link JComponent}.
|
||||
* A client property is set on the component to store the animation state.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
* @since 2
|
||||
*/
|
||||
public interface AnimatedPainter
|
||||
{
|
||||
/**
|
||||
* Gets the animatable value(s) of the component.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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 #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)).
|
||||
*
|
||||
* @param c the component that this painter belongs to
|
||||
* @param g the graphics context
|
||||
* @param x the x coordinate of the paint area
|
||||
* @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
|
||||
*/
|
||||
default void paintWithAnimation( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
AnimatedPainterSupport.paint( this, c, (Graphics2D) g, x, y, width, height );
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints the given (animated) value(s).
|
||||
* <p>
|
||||
* Invoked from {@link #paintWithAnimation(Component, Graphics, int, int, int, int)}.
|
||||
*
|
||||
* @param c the component that this painter belongs to
|
||||
* @param g the graphics context
|
||||
* @param x the x coordinate of the paint area
|
||||
* @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 #getAnimatableValues(Component)}
|
||||
* returned, or somewhere between the previous values and the latest values
|
||||
* that {@link #getAnimatableValues(Component)} returned
|
||||
*/
|
||||
void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues );
|
||||
|
||||
/**
|
||||
* Invoked from animator to repaint an area.
|
||||
* <p>
|
||||
* Useful to limit the repaint region. E.g. if only the bottom border is animated.
|
||||
* If more than one border side is animated (e.g. bottom and right side), then it
|
||||
* makes no sense to do separate repaints because the Swing repaint manager unions
|
||||
* the regions and the whole component is repainted.
|
||||
* <p>
|
||||
* The default implementation repaints the whole given area.
|
||||
*/
|
||||
default void repaintDuringAnimation( Component c, int x, int y, int width, int height ) {
|
||||
c.repaint( x, y, width, height );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether animation is enabled for this painter (default is {@code true}).
|
||||
*/
|
||||
default boolean isAnimationEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the duration of the animation in milliseconds (default is 150).
|
||||
*/
|
||||
default int getAnimationDuration() {
|
||||
return 150;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resolution of the animation in milliseconds (default is 10).
|
||||
* Resolution is the amount of time between timing events.
|
||||
*/
|
||||
default int getAnimationResolution() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interpolator for the animation.
|
||||
* Default is {@link CubicBezierEasing#STANDARD_EASING}.
|
||||
*/
|
||||
default Interpolator getAnimationInterpolator() {
|
||||
return CubicBezierEasing.STANDARD_EASING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the duration of the animation in milliseconds (default is 150)
|
||||
* for the given value index and value.
|
||||
*/
|
||||
default int getAnimationDuration( int valueIndex, float value ) {
|
||||
return getAnimationDuration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resolution of the animation in milliseconds (default is 10)
|
||||
* for the given value index and value.
|
||||
* Resolution is the amount of time between timing events.
|
||||
*/
|
||||
default int getAnimationResolution( int valueIndex, float value ) {
|
||||
return getAnimationResolution();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interpolator for the animation
|
||||
* for the given value index and value.
|
||||
* Default is {@link CubicBezierEasing#STANDARD_EASING}.
|
||||
*/
|
||||
default Interpolator getAnimationInterpolator( int valueIndex, float value ) {
|
||||
return getAnimationInterpolator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the client property key used to store the animation support.
|
||||
*/
|
||||
default Object getAnimationClientPropertyKey() {
|
||||
return getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves location for repainting animated area with
|
||||
* {@link AnimatedPainter#repaintDuringAnimation(Component, int, int, int, int)}.
|
||||
* Only needed when graphics context passed to {@link #paintWithAnimation(Component, Graphics, int, int, int, int)}
|
||||
* uses transformed location.
|
||||
*/
|
||||
static void saveRepaintLocation( AnimatedPainter painter, Component c, int x, int y ) {
|
||||
AnimatedPainterSupport.saveRepaintLocation( painter, c, x, y );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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.Graphics2D;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
/**
|
||||
* Animation support class that stores the animation state and implements the animation.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
* @since 2
|
||||
*/
|
||||
class AnimatedPainterSupport
|
||||
{
|
||||
private final int valueIndex;
|
||||
|
||||
private float startValue;
|
||||
private float targetValue;
|
||||
private float animatedValue;
|
||||
private float fraction;
|
||||
|
||||
private Animator animator;
|
||||
|
||||
// last bounds of the paint area needed to repaint while animating
|
||||
private int x;
|
||||
private int y;
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
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, g, x, y, width, height, values );
|
||||
return;
|
||||
}
|
||||
|
||||
JComponent jc = (JComponent) c;
|
||||
Object key = painter.getAnimationClientPropertyKey();
|
||||
AnimatedPainterSupport[] ass = (AnimatedPainterSupport[]) jc.getClientProperty( key );
|
||||
|
||||
// check whether length of values array has changed
|
||||
if( ass != null && ass.length != values.length ) {
|
||||
// cancel all running animations
|
||||
for( int i = 0; i < ass.length; i++ ) {
|
||||
AnimatedPainterSupport as = ass[i];
|
||||
if( as.animator != null )
|
||||
as.animator.cancel();
|
||||
}
|
||||
ass = null;
|
||||
}
|
||||
|
||||
if( ass == null ) {
|
||||
ass = new AnimatedPainterSupport[values.length];
|
||||
jc.putClientProperty( key, ass );
|
||||
}
|
||||
|
||||
for( int i = 0; i < ass.length; i++ ) {
|
||||
AnimatedPainterSupport as = ass[i];
|
||||
float value = values[i];
|
||||
|
||||
if( as == null ) {
|
||||
// painted first time --> do not animate, but remember current component value
|
||||
as = new AnimatedPainterSupport( i );
|
||||
as.startValue = as.targetValue = as.animatedValue = value;
|
||||
ass[i] = as;
|
||||
} else if( value != as.targetValue ) {
|
||||
// value changed --> (re)start animation
|
||||
|
||||
int animationDuration = painter.getAnimationDuration( as.valueIndex, value );
|
||||
|
||||
// do not animate if animation duration (for current value) is zero
|
||||
if( animationDuration <= 0 ) {
|
||||
if( as.animator != null ) {
|
||||
as.animator.cancel();
|
||||
as.animator = null;
|
||||
}
|
||||
as.startValue = as.targetValue = as.animatedValue = value;
|
||||
as.fraction = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( as.animator == null ) {
|
||||
// create animator
|
||||
AnimatedPainterSupport as2 = as;
|
||||
as.animator = new Animator( 1, fraction -> {
|
||||
// check whether component was removed while animation is running
|
||||
if( !c.isDisplayable() ) {
|
||||
as2.animator.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// compute animated value
|
||||
as2.animatedValue = as2.startValue + ((as2.targetValue - as2.startValue) * fraction);
|
||||
as2.fraction = fraction;
|
||||
|
||||
// repaint
|
||||
painter.repaintDuringAnimation( c, as2.x, as2.y, as2.width, as2.height );
|
||||
}, () -> {
|
||||
as2.startValue = as2.animatedValue = as2.targetValue;
|
||||
as2.animator = null;
|
||||
} );
|
||||
}
|
||||
|
||||
if( as.animator.isRunning() ) {
|
||||
// if animation is still running, restart it from the current
|
||||
// animated value to the new target value with reduced duration
|
||||
as.animator.cancel();
|
||||
int duration2 = (int) (animationDuration * as.fraction);
|
||||
if( duration2 > 0 )
|
||||
as.animator.setDuration( duration2 );
|
||||
as.startValue = as.animatedValue;
|
||||
} else {
|
||||
// new animation
|
||||
as.animator.setDuration( animationDuration );
|
||||
|
||||
as.animatedValue = as.startValue;
|
||||
}
|
||||
|
||||
// update animator for new value
|
||||
as.animator.setResolution( painter.getAnimationResolution( as.valueIndex, value ) );
|
||||
as.animator.setInterpolator( painter.getAnimationInterpolator( as.valueIndex, value ) );
|
||||
|
||||
// start animation
|
||||
as.targetValue = value;
|
||||
as.animator.start();
|
||||
}
|
||||
|
||||
as.x = x;
|
||||
as.y = y;
|
||||
as.width = width;
|
||||
as.height = height;
|
||||
}
|
||||
|
||||
float[] animatedValues = new float[ass.length];
|
||||
for( int i = 0; i < ass.length; i++ )
|
||||
animatedValues[i] = ass[i].animatedValue;
|
||||
|
||||
painter.paintAnimated( c, g, x, y, width, height, animatedValues );
|
||||
}
|
||||
|
||||
private static boolean isAnimationEnabled( AnimatedPainter painter, Component c ) {
|
||||
return Animator.useAnimation() && painter.isAnimationEnabled() && c instanceof JComponent;
|
||||
}
|
||||
|
||||
static void saveRepaintLocation( AnimatedPainter painter, Component c, int x, int y ) {
|
||||
if( !isAnimationEnabled( painter, c ) )
|
||||
return;
|
||||
|
||||
AnimatedPainterSupport[] ass = (AnimatedPainterSupport[]) ((JComponent)c).getClientProperty( painter.getAnimationClientPropertyKey() );
|
||||
if( ass != null ) {
|
||||
for( int i = 0; i < ass.length; i++ ) {
|
||||
AnimatedPainterSupport as = ass[i];
|
||||
as.x = x;
|
||||
as.y = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,438 @@
|
||||
/*
|
||||
* 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.testing;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Insets;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.AbstractBorder;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
import com.formdev.flatlaf.util.AnimatedBorder;
|
||||
import com.formdev.flatlaf.util.ColorFunctions;
|
||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
import net.miginfocom.swing.*;
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatAnimatedBorderTest
|
||||
extends FlatTestPanel
|
||||
{
|
||||
private static final Color CHART_FADE_1 = Color.blue;
|
||||
private static final Color CHART_FADE_2 = Color.red;
|
||||
private static final Color CHART_MATERIAL_1 = Color.green;
|
||||
private static final Color CHART_MATERIAL_2 = Color.magenta;
|
||||
private static final Color CHART_MATERIAL_3 = Color.pink;
|
||||
private static final Color CHART_MATERIAL_4 = Color.cyan;
|
||||
private static final Color CHART_MINIMAL = Color.orange;
|
||||
|
||||
private static final String CHART_COLOR_KEY = "chartColor";
|
||||
|
||||
public static void main( String[] args ) {
|
||||
SwingUtilities.invokeLater( () -> {
|
||||
FlatTestFrame frame = FlatTestFrame.create( args, "FlatAnimatedBorderTest" );
|
||||
frame.showFrame( FlatAnimatedBorderTest::new );
|
||||
} );
|
||||
}
|
||||
|
||||
FlatAnimatedBorderTest() {
|
||||
initComponents();
|
||||
|
||||
fade1TextField.setBorder( new AnimatedFocusFadeBorder() );
|
||||
fade2TextField.setBorder( new AnimatedFocusFadeBorder() );
|
||||
|
||||
material1TextField.setBorder( new AnimatedMaterialBorder() );
|
||||
material2TextField.setBorder( new AnimatedMaterialBorder() );
|
||||
material3TextField.setBorder( new AnimatedMaterialLabeledBorder() );
|
||||
material4TextField.setBorder( new AnimatedMaterialLabeledBorder() );
|
||||
|
||||
minimalTextField.setBorder( new AnimatedMinimalTestBorder() );
|
||||
|
||||
fade1TextField.putClientProperty( CHART_COLOR_KEY, CHART_FADE_1 );
|
||||
fade2TextField.putClientProperty( CHART_COLOR_KEY, CHART_FADE_2 );
|
||||
material1TextField.putClientProperty( CHART_COLOR_KEY, CHART_MATERIAL_1 );
|
||||
material2TextField.putClientProperty( CHART_COLOR_KEY, CHART_MATERIAL_2 );
|
||||
material3TextField.putClientProperty( CHART_COLOR_KEY, CHART_MATERIAL_3 );
|
||||
material4TextField.putClientProperty( CHART_COLOR_KEY, CHART_MATERIAL_4 );
|
||||
minimalTextField.putClientProperty( CHART_COLOR_KEY, CHART_MINIMAL );
|
||||
|
||||
fade1ChartColor.setForeground( CHART_FADE_1 );
|
||||
fade2ChartColor.setForeground( CHART_FADE_2 );
|
||||
material1ChartColor.setForeground( CHART_MATERIAL_1 );
|
||||
material2ChartColor.setForeground( CHART_MATERIAL_2 );
|
||||
material3ChartColor.setForeground( CHART_MATERIAL_3 );
|
||||
material4ChartColor.setForeground( CHART_MATERIAL_4 );
|
||||
minimalChartColor.setForeground( CHART_MINIMAL );
|
||||
|
||||
material3TextField.putClientProperty( AnimatedMaterialLabeledBorder.LABEL_TEXT_KEY, "Label" );
|
||||
material4TextField.putClientProperty( AnimatedMaterialLabeledBorder.LABEL_TEXT_KEY, "Label" );
|
||||
material4TextField.setText( "Text" );
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
|
||||
label3 = new JLabel();
|
||||
lineChartPanel = new LineChartPanel();
|
||||
fade1TextField = new JTextField();
|
||||
fade1ChartColor = new FlatAnimatorTest.JChartColor();
|
||||
fade2TextField = new JTextField();
|
||||
fade2ChartColor = new FlatAnimatorTest.JChartColor();
|
||||
label2 = new JLabel();
|
||||
material1TextField = new JTextField();
|
||||
material1ChartColor = new FlatAnimatorTest.JChartColor();
|
||||
material2TextField = new JTextField();
|
||||
material2ChartColor = new FlatAnimatorTest.JChartColor();
|
||||
material3TextField = new JTextField();
|
||||
material3ChartColor = new FlatAnimatorTest.JChartColor();
|
||||
material4TextField = new JTextField();
|
||||
material4ChartColor = new FlatAnimatorTest.JChartColor();
|
||||
label1 = new JLabel();
|
||||
minimalTextField = new JTextField();
|
||||
minimalChartColor = new FlatAnimatorTest.JChartColor();
|
||||
durationLabel = new JLabel();
|
||||
durationField = new JSpinner();
|
||||
|
||||
//======== this ========
|
||||
setLayout(new MigLayout(
|
||||
"insets dialog,hidemode 3",
|
||||
// columns
|
||||
"[fill]" +
|
||||
"[fill]para" +
|
||||
"[grow,fill]",
|
||||
// rows
|
||||
"[]" +
|
||||
"[]" +
|
||||
"[]para" +
|
||||
"[]" +
|
||||
"[]" +
|
||||
"[]" +
|
||||
"[]" +
|
||||
"[]para" +
|
||||
"[]" +
|
||||
"[]" +
|
||||
"[grow]" +
|
||||
"[]"));
|
||||
|
||||
//---- label3 ----
|
||||
label3.setText("Fade:");
|
||||
add(label3, "cell 0 0");
|
||||
add(lineChartPanel, "cell 2 0 1 12,growy");
|
||||
add(fade1TextField, "cell 0 1");
|
||||
add(fade1ChartColor, "cell 1 1");
|
||||
add(fade2TextField, "cell 0 2");
|
||||
add(fade2ChartColor, "cell 1 2");
|
||||
|
||||
//---- label2 ----
|
||||
label2.setText("Material:");
|
||||
add(label2, "cell 0 3");
|
||||
add(material1TextField, "cell 0 4");
|
||||
add(material1ChartColor, "cell 1 4");
|
||||
add(material2TextField, "cell 0 5");
|
||||
add(material2ChartColor, "cell 1 5");
|
||||
|
||||
//---- material3TextField ----
|
||||
material3TextField.putClientProperty(FlatClientProperties.STYLE_CLASS, "large");
|
||||
add(material3TextField, "cell 0 6");
|
||||
add(material3ChartColor, "cell 1 6");
|
||||
|
||||
//---- material4TextField ----
|
||||
material4TextField.putClientProperty(FlatClientProperties.STYLE_CLASS, "large");
|
||||
add(material4TextField, "cell 0 7");
|
||||
add(material4ChartColor, "cell 1 7");
|
||||
|
||||
//---- label1 ----
|
||||
label1.setText("Minimal:");
|
||||
add(label1, "cell 0 8");
|
||||
add(minimalTextField, "cell 0 9");
|
||||
add(minimalChartColor, "cell 1 9");
|
||||
|
||||
//---- durationLabel ----
|
||||
durationLabel.setText("Duration:");
|
||||
add(durationLabel, "cell 0 11");
|
||||
|
||||
//---- durationField ----
|
||||
durationField.setModel(new SpinnerNumberModel(200, 0, null, 50));
|
||||
add(durationField, "cell 0 11");
|
||||
// JFormDesigner - End of component initialization //GEN-END:initComponents
|
||||
}
|
||||
|
||||
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
|
||||
private JLabel label3;
|
||||
private LineChartPanel lineChartPanel;
|
||||
private JTextField fade1TextField;
|
||||
private FlatAnimatorTest.JChartColor fade1ChartColor;
|
||||
private JTextField fade2TextField;
|
||||
private FlatAnimatorTest.JChartColor fade2ChartColor;
|
||||
private JLabel label2;
|
||||
private JTextField material1TextField;
|
||||
private FlatAnimatorTest.JChartColor material1ChartColor;
|
||||
private JTextField material2TextField;
|
||||
private FlatAnimatorTest.JChartColor material2ChartColor;
|
||||
private JTextField material3TextField;
|
||||
private FlatAnimatorTest.JChartColor material3ChartColor;
|
||||
private JTextField material4TextField;
|
||||
private FlatAnimatorTest.JChartColor material4ChartColor;
|
||||
private JLabel label1;
|
||||
private JTextField minimalTextField;
|
||||
private FlatAnimatorTest.JChartColor minimalChartColor;
|
||||
private JLabel durationLabel;
|
||||
private JSpinner durationField;
|
||||
// JFormDesigner - End of variables declaration //GEN-END:variables
|
||||
|
||||
//---- class AnimatedMaterialBorder ---------------------------------------
|
||||
|
||||
/**
|
||||
* Experimental text field border that:
|
||||
* - animates focus indicator color and border width
|
||||
*/
|
||||
private class AnimatedFocusFadeBorder
|
||||
extends AbstractBorder
|
||||
implements AnimatedBorder
|
||||
{
|
||||
// needed because otherwise the empty paint method in superclass
|
||||
// javax.swing.border.AbstractBorder would be used
|
||||
@Override
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
paintWithAnimation( c, g, x, y, width, height );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
|
||||
float animatedValue = animatedValues[0];
|
||||
FlatUIUtils.setRenderingHints( g );
|
||||
|
||||
// border width is 1 if not focused and 2 if focused
|
||||
float lw = UIScale.scale( 1 + animatedValue );
|
||||
|
||||
// paint border
|
||||
Color color = ColorFunctions.mix( Color.red, Color.lightGray, animatedValue );
|
||||
FlatUIUtils.paintOutlinedComponent( g, x, y, width, height, 0, 0, 0, lw, 0,
|
||||
null, color, null );
|
||||
|
||||
if( animatedValue != 0 && animatedValue != 1 ) {
|
||||
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
||||
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "fade" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||
insets.top = insets.bottom = UIScale.scale( 3 );
|
||||
insets.left = insets.right = UIScale.scale( 7 );
|
||||
return insets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getAnimatableValues( Component c ) {
|
||||
return new float[] { FlatUIUtils.isPermanentFocusOwner( c ) ? 1 : 0 };
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAnimationDuration() {
|
||||
return (Integer) durationField.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
//---- class AnimatedMaterialBorder ---------------------------------------
|
||||
|
||||
/**
|
||||
* Experimental text field border that:
|
||||
* - paint border only at bottom
|
||||
* - animates focus indicator at bottom
|
||||
*/
|
||||
private class AnimatedMaterialBorder
|
||||
extends AbstractBorder
|
||||
implements AnimatedBorder
|
||||
{
|
||||
// needed because otherwise the empty paint method in superclass
|
||||
// javax.swing.border.AbstractBorder would be used
|
||||
@Override
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
paintWithAnimation( c, g, x, y, width, height );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
|
||||
float animatedValue = animatedValues[0];
|
||||
FlatUIUtils.setRenderingHints( g );
|
||||
|
||||
// use paintAtScale1x() for consistent line thickness when scaled
|
||||
HiDPIUtils.paintAtScale1x( g, x, y, width, height,
|
||||
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
||||
float lh = (float) (UIScale.scale( 1f ) * scaleFactor);
|
||||
|
||||
g2d.setColor( Color.gray );
|
||||
g2d.fill( new Rectangle2D.Float( x2, y2 + height2 - lh, width2, lh ) );
|
||||
|
||||
if( animatedValue > 0 ) {
|
||||
lh = (float) (UIScale.scale( 2f ) * scaleFactor);
|
||||
int lw = Math.round( width2 * animatedValue );
|
||||
|
||||
g2d.setColor( Color.red );
|
||||
g2d.fill( new Rectangle2D.Float( x2 + (width2 - lw) / 2, y2 + height2 - lh, lw, lh ) );
|
||||
}
|
||||
} );
|
||||
|
||||
if( animatedValue != 0 && animatedValue != 1 ) {
|
||||
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
||||
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "material" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void repaintDuringAnimation( Component c, int x, int y, int width, int height ) {
|
||||
// limit repaint to bottom border
|
||||
int lh = UIScale.scale( 2 );
|
||||
c.repaint( x, y + height - lh, width, lh );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||
insets.top = insets.bottom = UIScale.scale( 3 );
|
||||
insets.left = insets.right = UIScale.scale( 7 );
|
||||
return insets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getAnimatableValues( Component c ) {
|
||||
return new float[] { FlatUIUtils.isPermanentFocusOwner( c ) ? 1 : 0 };
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAnimationDuration() {
|
||||
return (Integer) durationField.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
//---- class AnimatedMaterialLabeledBorder --------------------------------
|
||||
|
||||
/**
|
||||
* Experimental text field border that:
|
||||
* - paints a label above the text, or in center if text field is empty
|
||||
* - paint border only at bottom
|
||||
* - animates focus indicator at bottom
|
||||
*/
|
||||
private class AnimatedMaterialLabeledBorder
|
||||
extends AnimatedMaterialBorder
|
||||
{
|
||||
static final String LABEL_TEXT_KEY = "JTextField.labelText";
|
||||
|
||||
private static final float LABEL_FONT_SCALE = 0.75f;
|
||||
|
||||
@Override
|
||||
public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
|
||||
super.paintAnimated( c, g, x, y, width, height, animatedValues );
|
||||
|
||||
float animatedValue = animatedValues[0];
|
||||
JComponent jc = (JComponent) c;
|
||||
String label = (String) jc.getClientProperty( LABEL_TEXT_KEY );
|
||||
if( label == null )
|
||||
return;
|
||||
|
||||
FontMetrics fm = c.getFontMetrics( c.getFont() );
|
||||
int labelFontHeight = Math.round( fm.getHeight() * LABEL_FONT_SCALE );
|
||||
|
||||
int tx = UIScale.scale( 7 );
|
||||
int ty = y + labelFontHeight - UIScale.scale( 2 );
|
||||
float sf = LABEL_FONT_SCALE;
|
||||
|
||||
if( ((JTextField)c).getDocument().getLength() == 0 ) {
|
||||
// paint label in center of text field if it is empty
|
||||
int ty2 = ((height - fm.getHeight()) / 2) + labelFontHeight;
|
||||
ty += (ty2 - ty) * (1 - animatedValue);
|
||||
sf += (1 - LABEL_FONT_SCALE) * (1 - animatedValue);
|
||||
}
|
||||
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
try {
|
||||
g2.translate( tx, ty );
|
||||
g2.scale( sf, sf );
|
||||
g2.setColor( ColorFunctions.mix( Color.red, Color.gray, animatedValue ) );
|
||||
|
||||
FlatUIUtils.drawString( jc, g2, label, 0, 0 );
|
||||
} finally {
|
||||
g2.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void repaintDuringAnimation( Component c, int x, int y, int width, int height ) {
|
||||
c.repaint( x, y, width, height );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||
super.getBorderInsets( c, insets );
|
||||
|
||||
FontMetrics fm = c.getFontMetrics( c.getFont() );
|
||||
int labelFontHeight = Math.round( fm.getHeight() * LABEL_FONT_SCALE );
|
||||
insets.top = labelFontHeight;
|
||||
insets.bottom = UIScale.scale( 5 );
|
||||
return insets;
|
||||
}
|
||||
}
|
||||
|
||||
//---- class AnimatedMinimalTestBorder ------------------------------------
|
||||
|
||||
/**
|
||||
* Minimal example for an animated border.
|
||||
*/
|
||||
private class AnimatedMinimalTestBorder
|
||||
implements AnimatedBorder
|
||||
{
|
||||
@Override
|
||||
public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
|
||||
float animatedValue = animatedValues[0];
|
||||
int lh = UIScale.scale( 2 );
|
||||
|
||||
g.setColor( Color.blue );
|
||||
g.fillRect( x, y + height - lh, Math.round( width * animatedValue ), lh );
|
||||
|
||||
if( animatedValue != 0 && animatedValue != 1 ) {
|
||||
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
||||
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "minimal" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getAnimatableValues( Component c ) {
|
||||
return new float[] { FlatUIUtils.isPermanentFocusOwner( c ) ? 1 : 0 };
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAnimationDuration() {
|
||||
return (Integer) durationField.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets( Component c ) {
|
||||
return UIScale.scale( new Insets( 3, 7, 3, 7 ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBorderOpaque() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
JFDML JFormDesigner: "8.3" encoding: "UTF-8"
|
||||
|
||||
new FormModel {
|
||||
contentType: "form/swing"
|
||||
root: new FormRoot {
|
||||
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||
"$layoutConstraints": "insets dialog,hidemode 3"
|
||||
"$columnConstraints": "[fill][fill]para[grow,fill]"
|
||||
"$rowConstraints": "[][][]para[][][][][]para[][][grow][]"
|
||||
} ) {
|
||||
name: "this"
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "label3"
|
||||
"text": "Fade:"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 0"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.LineChartPanel" ) {
|
||||
name: "lineChartPanel"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 2 0 1 12,growy"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JTextField" ) {
|
||||
name: "fade1TextField"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
|
||||
name: "fade1ChartColor"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JTextField" ) {
|
||||
name: "fade2TextField"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 2"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
|
||||
name: "fade2ChartColor"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 2"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "label2"
|
||||
"text": "Material:"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 3"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JTextField" ) {
|
||||
name: "material1TextField"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 4"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
|
||||
name: "material1ChartColor"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 4"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JTextField" ) {
|
||||
name: "material2TextField"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 5"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
|
||||
name: "material2ChartColor"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 5"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JTextField" ) {
|
||||
name: "material3TextField"
|
||||
"$client.FlatLaf.styleClass": "large"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 6"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
|
||||
name: "material3ChartColor"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 6"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JTextField" ) {
|
||||
name: "material4TextField"
|
||||
"$client.FlatLaf.styleClass": "large"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 7"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
|
||||
name: "material4ChartColor"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 7"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "label1"
|
||||
"text": "Minimal:"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 8"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JTextField" ) {
|
||||
name: "minimalTextField"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 9"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
|
||||
name: "minimalChartColor"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 9"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "durationLabel"
|
||||
"text": "Duration:"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 11"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JSpinner" ) {
|
||||
name: "durationField"
|
||||
"model": new javax.swing.SpinnerNumberModel {
|
||||
minimum: 0
|
||||
stepSize: 50
|
||||
value: 200
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 11"
|
||||
} )
|
||||
}, new FormLayoutConstraints( null ) {
|
||||
"location": new java.awt.Point( 0, 0 )
|
||||
"size": new java.awt.Dimension( 725, 465 )
|
||||
} )
|
||||
}
|
||||
}
|
||||
@@ -18,13 +18,14 @@ package com.formdev.flatlaf.testing;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import javax.swing.*;
|
||||
import com.formdev.flatlaf.icons.FlatAnimatedIcon;
|
||||
import com.formdev.flatlaf.util.AnimatedIcon;
|
||||
import com.formdev.flatlaf.util.ColorFunctions;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
import net.miginfocom.swing.*;
|
||||
|
||||
/**
|
||||
@@ -33,6 +34,15 @@ import net.miginfocom.swing.*;
|
||||
public class FlatAnimatedIconTest
|
||||
extends FlatTestPanel
|
||||
{
|
||||
private static final Color CHART_RADIO_BUTTON_1 = Color.blue;
|
||||
private static final Color CHART_RADIO_BUTTON_2 = Color.red;
|
||||
private static final Color CHART_RADIO_BUTTON_3 = Color.green;
|
||||
private static final Color CHART_CHECK_BOX_1 = Color.magenta;
|
||||
private static final Color CHART_CHECK_BOX_2 = Color.orange;
|
||||
private static final Color[] CHART_SWITCH_EX = { Color.red, Color.green, Color.blue };
|
||||
|
||||
private static final String CHART_COLOR_KEY = "chartColor";
|
||||
|
||||
public static void main( String[] args ) {
|
||||
SwingUtilities.invokeLater( () -> {
|
||||
FlatTestFrame frame = FlatTestFrame.create( args, "FlatAnimatedIconTest" );
|
||||
@@ -49,16 +59,36 @@ public class FlatAnimatedIconTest
|
||||
radioButton3.setIcon( radioIcon );
|
||||
|
||||
checkBox1.setIcon( new AnimatedSwitchIcon() );
|
||||
checkBox3.setIcon( new AnimatedSwitchIconEx() );
|
||||
checkBox2.setIcon( new AnimatedMinimalTestIcon() );
|
||||
|
||||
radioButton1.putClientProperty( CHART_COLOR_KEY, CHART_RADIO_BUTTON_1 );
|
||||
radioButton2.putClientProperty( CHART_COLOR_KEY, CHART_RADIO_BUTTON_2 );
|
||||
radioButton3.putClientProperty( CHART_COLOR_KEY, CHART_RADIO_BUTTON_3 );
|
||||
checkBox1.putClientProperty( CHART_COLOR_KEY, CHART_CHECK_BOX_1 );
|
||||
checkBox2.putClientProperty( CHART_COLOR_KEY, CHART_CHECK_BOX_2 );
|
||||
|
||||
radioButton1ChartColor.setForeground( CHART_RADIO_BUTTON_1 );
|
||||
radioButton2ChartColor.setForeground( CHART_RADIO_BUTTON_2 );
|
||||
radioButton3ChartColor.setForeground( CHART_RADIO_BUTTON_3 );
|
||||
checkBox1ChartColor.setForeground( CHART_CHECK_BOX_1 );
|
||||
checkBox2ChartColor.setForeground( CHART_CHECK_BOX_2 );
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
|
||||
radioButton1 = new JRadioButton();
|
||||
radioButton1ChartColor = new FlatAnimatorTest.JChartColor();
|
||||
lineChartPanel = new LineChartPanel();
|
||||
radioButton2 = new JRadioButton();
|
||||
radioButton2ChartColor = new FlatAnimatorTest.JChartColor();
|
||||
radioButton3 = new JRadioButton();
|
||||
radioButton3ChartColor = new FlatAnimatorTest.JChartColor();
|
||||
checkBox1 = new JCheckBox();
|
||||
checkBox1ChartColor = new FlatAnimatorTest.JChartColor();
|
||||
checkBox3 = new JCheckBox();
|
||||
checkBox2 = new JCheckBox();
|
||||
checkBox2ChartColor = new FlatAnimatorTest.JChartColor();
|
||||
durationLabel = new JLabel();
|
||||
durationField = new JSpinner();
|
||||
|
||||
@@ -66,14 +96,16 @@ public class FlatAnimatedIconTest
|
||||
setLayout(new MigLayout(
|
||||
"insets dialog,hidemode 3",
|
||||
// columns
|
||||
"[]para" +
|
||||
"[fill]",
|
||||
"[]" +
|
||||
"[fill]para" +
|
||||
"[grow,fill]",
|
||||
// rows
|
||||
"[]" +
|
||||
"[]" +
|
||||
"[]para" +
|
||||
"[]" +
|
||||
"[]" +
|
||||
"[]" +
|
||||
"[grow]" +
|
||||
"[]"));
|
||||
|
||||
@@ -81,30 +113,40 @@ public class FlatAnimatedIconTest
|
||||
radioButton1.setText("radio 1");
|
||||
radioButton1.setSelected(true);
|
||||
add(radioButton1, "cell 0 0");
|
||||
add(radioButton1ChartColor, "cell 1 0");
|
||||
add(lineChartPanel, "cell 2 0 1 8,growy");
|
||||
|
||||
//---- radioButton2 ----
|
||||
radioButton2.setText("radio 2");
|
||||
add(radioButton2, "cell 0 1");
|
||||
add(radioButton2ChartColor, "cell 1 1");
|
||||
|
||||
//---- radioButton3 ----
|
||||
radioButton3.setText("radio 3");
|
||||
add(radioButton3, "cell 0 2");
|
||||
add(radioButton3ChartColor, "cell 1 2");
|
||||
|
||||
//---- checkBox1 ----
|
||||
checkBox1.setText("switch");
|
||||
add(checkBox1, "cell 0 3");
|
||||
add(checkBox1ChartColor, "cell 1 3");
|
||||
|
||||
//---- checkBox3 ----
|
||||
checkBox3.setText("switch ex");
|
||||
add(checkBox3, "cell 0 4");
|
||||
|
||||
//---- checkBox2 ----
|
||||
checkBox2.setText("minimal");
|
||||
add(checkBox2, "cell 0 4");
|
||||
add(checkBox2, "cell 0 5");
|
||||
add(checkBox2ChartColor, "cell 1 5");
|
||||
|
||||
//---- durationLabel ----
|
||||
durationLabel.setText("Duration:");
|
||||
add(durationLabel, "cell 0 6 2 1");
|
||||
add(durationLabel, "cell 0 7 2 1");
|
||||
|
||||
//---- durationField ----
|
||||
durationField.setModel(new SpinnerNumberModel(200, 100, null, 50));
|
||||
add(durationField, "cell 0 6 2 1");
|
||||
durationField.setModel(new SpinnerNumberModel(200, 0, null, 50));
|
||||
add(durationField, "cell 0 7 2 1");
|
||||
|
||||
//---- buttonGroup1 ----
|
||||
ButtonGroup buttonGroup1 = new ButtonGroup();
|
||||
@@ -116,10 +158,17 @@ public class FlatAnimatedIconTest
|
||||
|
||||
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
|
||||
private JRadioButton radioButton1;
|
||||
private FlatAnimatorTest.JChartColor radioButton1ChartColor;
|
||||
private LineChartPanel lineChartPanel;
|
||||
private JRadioButton radioButton2;
|
||||
private FlatAnimatorTest.JChartColor radioButton2ChartColor;
|
||||
private JRadioButton radioButton3;
|
||||
private FlatAnimatorTest.JChartColor radioButton3ChartColor;
|
||||
private JCheckBox checkBox1;
|
||||
private FlatAnimatorTest.JChartColor checkBox1ChartColor;
|
||||
private JCheckBox checkBox3;
|
||||
private JCheckBox checkBox2;
|
||||
private FlatAnimatorTest.JChartColor checkBox2ChartColor;
|
||||
private JLabel durationLabel;
|
||||
private JSpinner durationField;
|
||||
// JFormDesigner - End of variables declaration //GEN-END:variables
|
||||
@@ -146,7 +195,8 @@ public class FlatAnimatedIconTest
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintIconAnimated( Component c, Graphics g, int x, int y, float animatedValue ) {
|
||||
public void paintIconAnimated( Component c, Graphics2D g, float[] animatedValues ) {
|
||||
float animatedValue = animatedValues[0];
|
||||
Color color = ColorFunctions.mix( onColor, offColor, animatedValue );
|
||||
|
||||
// border
|
||||
@@ -162,12 +212,17 @@ public class FlatAnimatedIconTest
|
||||
float dotDiameter = DOT_SIZE * animatedValue;
|
||||
float xy = (SIZE - dotDiameter) / 2f;
|
||||
g.setColor( color );
|
||||
((Graphics2D)g).fill( new Ellipse2D.Float( xy, xy, dotDiameter, dotDiameter ) );
|
||||
g.fill( new Ellipse2D.Float( xy, xy, dotDiameter, dotDiameter ) );
|
||||
|
||||
if( animatedValue != 0 && animatedValue != 1 ) {
|
||||
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
||||
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "radio" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getValue( Component c ) {
|
||||
return ((JRadioButton)c).isSelected() ? 1 : 0;
|
||||
public float[] getAnimatableValues( Component c ) {
|
||||
return new float[] { ((JRadioButton)c).isSelected() ? 1 : 0 };
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -178,7 +233,7 @@ public class FlatAnimatedIconTest
|
||||
|
||||
//---- class AnimatedSwitchIcon -------------------------------------------
|
||||
|
||||
public class AnimatedSwitchIcon
|
||||
private class AnimatedSwitchIcon
|
||||
extends FlatAnimatedIcon
|
||||
{
|
||||
private final Color offColor = Color.lightGray;
|
||||
@@ -189,22 +244,113 @@ public class FlatAnimatedIconTest
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintIconAnimated( Component c, Graphics g, int x, int y, float animatedValue ) {
|
||||
public void paintIconAnimated( Component c, Graphics2D g, float[] animatedValues ) {
|
||||
float animatedValue = animatedValues[0];
|
||||
Color color = ColorFunctions.mix( onColor, offColor, animatedValue );
|
||||
|
||||
// paint track
|
||||
g.setColor( color );
|
||||
g.fillRoundRect( x, y, width, height, height, height );
|
||||
g.fillRoundRect( 0, 0, width, height, height, height );
|
||||
|
||||
// paint thumb
|
||||
int thumbSize = height - 4;
|
||||
float thumbX = x + 2 + ((width - 4 - thumbSize) * animatedValue);
|
||||
int thumbY = y + 2;
|
||||
float thumbX = 2 + ((width - 4 - thumbSize) * animatedValue);
|
||||
int thumbY = 2;
|
||||
g.setColor( Color.white );
|
||||
((Graphics2D)g).fill( new Ellipse2D.Float( thumbX, thumbY, thumbSize, thumbSize ) );
|
||||
g.fill( new Ellipse2D.Float( thumbX, thumbY, thumbSize, thumbSize ) );
|
||||
|
||||
if( animatedValue != 0 && animatedValue != 1 ) {
|
||||
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
||||
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "switch" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getValue( Component c ) {
|
||||
return ((AbstractButton)c).isSelected() ? 1 : 0;
|
||||
public float[] getAnimatableValues( Component c ) {
|
||||
return new float[] { ((AbstractButton)c).isSelected() ? 1 : 0 };
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAnimationDuration() {
|
||||
return (Integer) durationField.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
//---- class AnimatedSwitchIconEx -----------------------------------------
|
||||
|
||||
private static final int HW = 8;
|
||||
|
||||
private class AnimatedSwitchIconEx
|
||||
extends FlatAnimatedIcon
|
||||
{
|
||||
private final Color offColor = Color.lightGray;
|
||||
private final Color onColor = Color.red;
|
||||
private final Color hoverColor = new Color( 0x4400cc00, true );
|
||||
private final Color pressedColor = new Color( 0x440000cc, true );
|
||||
|
||||
public AnimatedSwitchIconEx() {
|
||||
super( 28 + HW, 16 + HW, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintIconAnimated( Component c, Graphics2D g, float[] animatedValues ) {
|
||||
Color color = ColorFunctions.mix( onColor, offColor, animatedValues[0] );
|
||||
|
||||
int hw2 = HW / 2;
|
||||
int x = hw2;
|
||||
int y = hw2;
|
||||
int width = this.width - HW;
|
||||
int height = this.height - HW;
|
||||
|
||||
// paint track
|
||||
g.setColor( color );
|
||||
g.fillRoundRect( x, y, width, height, height, height );
|
||||
|
||||
// paint thumb
|
||||
int thumbSize = height - 4;
|
||||
float thumbX = x + 2 + ((width - 4 - thumbSize) * animatedValues[0]);
|
||||
int thumbY = y + 2;
|
||||
g.setColor( Color.white );
|
||||
g.fill( new Ellipse2D.Float( thumbX, thumbY, thumbSize, thumbSize ) );
|
||||
|
||||
// paint hover
|
||||
if( animatedValues[1] > 0 ) {
|
||||
g.setColor( hoverColor );
|
||||
paintHoverOrPressed( g, thumbX, thumbY, thumbSize, animatedValues[1] );
|
||||
}
|
||||
|
||||
// paint pressed
|
||||
if( animatedValues[2] > 0 ) {
|
||||
g.setColor( pressedColor );
|
||||
paintHoverOrPressed( g, thumbX, thumbY, thumbSize, animatedValues[2] );
|
||||
}
|
||||
|
||||
for( int i = 0; i < animatedValues.length; i++ ) {
|
||||
float animatedValue = animatedValues[i];
|
||||
if( animatedValue != 0 && animatedValue != 1 )
|
||||
lineChartPanel.addValue( CHART_SWITCH_EX[i], animatedValue, Integer.MIN_VALUE, "switch ex" );
|
||||
}
|
||||
}
|
||||
|
||||
private void paintHoverOrPressed( Graphics2D g, float thumbX, int thumbY, int thumbSize, float animatedValue ) {
|
||||
float hw = (HW + 4) * animatedValue;
|
||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
path.append( new Ellipse2D.Float( thumbX - (hw / 2), thumbY - (hw / 2),
|
||||
thumbSize + hw, thumbSize + hw ), false );
|
||||
path.append( new Ellipse2D.Float( thumbX, thumbY, thumbSize, thumbSize ), false );
|
||||
g.fill( path );
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getAnimatableValues( Component c ) {
|
||||
AbstractButton b = (AbstractButton) c;
|
||||
ButtonModel bm = b.getModel();
|
||||
|
||||
return new float[] {
|
||||
b.isSelected() ? 1 : 0,
|
||||
bm.isRollover() ? 1 : 0,
|
||||
bm.isPressed() ? 1 : 0,
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -223,27 +369,31 @@ public class FlatAnimatedIconTest
|
||||
{
|
||||
@Override
|
||||
public int getIconWidth() {
|
||||
return 100;
|
||||
return UIScale.scale( 50 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconHeight() {
|
||||
return 20;
|
||||
return UIScale.scale( 16 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintIconAnimated( Component c, Graphics g, int x, int y, float animatedValue ) {
|
||||
int w = getIconWidth();
|
||||
int h = getIconHeight();
|
||||
public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
|
||||
float animatedValue = animatedValues[0];
|
||||
|
||||
g.setColor( Color.red );
|
||||
g.drawRect( x, y, w - 1, h - 1 );
|
||||
g.fillRect( x, y, Math.round( w * animatedValue ), h );
|
||||
g.drawRect( x, y, width - 1, height - 1 );
|
||||
g.fillRect( x, y, Math.round( width * animatedValue ), height );
|
||||
|
||||
if( animatedValue != 0 && animatedValue != 1 ) {
|
||||
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
|
||||
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "minimal" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getValue( Component c ) {
|
||||
return ((AbstractButton)c).isSelected() ? 1 : 0;
|
||||
public float[] getAnimatableValues( Component c ) {
|
||||
return new float[] { ((AbstractButton)c).isSelected() ? 1 : 0 };
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
JFDML JFormDesigner: "7.0.2.0.298" Java: "15" encoding: "UTF-8"
|
||||
JFDML JFormDesigner: "8.3" encoding: "UTF-8"
|
||||
|
||||
new FormModel {
|
||||
contentType: "form/swing"
|
||||
root: new FormRoot {
|
||||
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||
"$layoutConstraints": "insets dialog,hidemode 3"
|
||||
"$columnConstraints": "[]para[fill]"
|
||||
"$rowConstraints": "[][][]para[][][grow][]"
|
||||
"$columnConstraints": "[][fill]para[grow,fill]"
|
||||
"$rowConstraints": "[][][]para[][][][grow][]"
|
||||
} ) {
|
||||
name: "this"
|
||||
add( new FormComponent( "javax.swing.JRadioButton" ) {
|
||||
@@ -17,6 +17,16 @@ new FormModel {
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 0"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
|
||||
name: "radioButton1ChartColor"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 0"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.LineChartPanel" ) {
|
||||
name: "lineChartPanel"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 2 0 1 8,growy"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JRadioButton" ) {
|
||||
name: "radioButton2"
|
||||
"text": "radio 2"
|
||||
@@ -24,6 +34,11 @@ new FormModel {
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
|
||||
name: "radioButton2ChartColor"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JRadioButton" ) {
|
||||
name: "radioButton3"
|
||||
"text": "radio 3"
|
||||
@@ -31,37 +46,58 @@ new FormModel {
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 2"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
|
||||
name: "radioButton3ChartColor"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 2"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||
name: "checkBox1"
|
||||
"text": "switch"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 3"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
|
||||
name: "checkBox1ChartColor"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 3"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||
name: "checkBox3"
|
||||
"text": "switch ex"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 4"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||
name: "checkBox2"
|
||||
"text": "minimal"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 4"
|
||||
"value": "cell 0 5"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
|
||||
name: "checkBox2ChartColor"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 5"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "durationLabel"
|
||||
"text": "Duration:"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 6 2 1"
|
||||
"value": "cell 0 7 2 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JSpinner" ) {
|
||||
name: "durationField"
|
||||
"model": new javax.swing.SpinnerNumberModel {
|
||||
minimum: 100
|
||||
minimum: 0
|
||||
stepSize: 50
|
||||
value: 200
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 6 2 1"
|
||||
"value": "cell 0 7 2 1"
|
||||
} )
|
||||
}, new FormLayoutConstraints( null ) {
|
||||
"location": new java.awt.Point( 0, 0 )
|
||||
"size": new java.awt.Dimension( 415, 350 )
|
||||
"size": new java.awt.Dimension( 810, 350 )
|
||||
} )
|
||||
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
|
||||
name: "buttonGroup1"
|
||||
|
||||
@@ -20,6 +20,8 @@ import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import com.formdev.flatlaf.util.Animator;
|
||||
import com.formdev.flatlaf.util.CubicBezierEasing;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
import com.formdev.flatlaf.util.Animator.Interpolator;
|
||||
import net.miginfocom.swing.*;
|
||||
|
||||
/**
|
||||
@@ -28,8 +30,13 @@ import net.miginfocom.swing.*;
|
||||
public class FlatAnimatorTest
|
||||
extends FlatTestPanel
|
||||
{
|
||||
private static final Color CHART_LINEAR = Color.blue;
|
||||
private static final Color CHART_EASE_IN_OUT = Color.magenta;
|
||||
private static final Color CHART_STANDARD_EASING = Color.red;
|
||||
|
||||
private Animator linearAnimator;
|
||||
private Animator easeInOutAnimator;
|
||||
private Animator standardEasingAnimator;
|
||||
|
||||
public static void main( String[] args ) {
|
||||
SwingUtilities.invokeLater( () -> {
|
||||
@@ -40,85 +47,132 @@ public class FlatAnimatorTest
|
||||
|
||||
FlatAnimatorTest() {
|
||||
initComponents();
|
||||
|
||||
linearChartColor.setForeground( CHART_LINEAR );
|
||||
easeInOutChartColor.setForeground( CHART_EASE_IN_OUT );
|
||||
standardEasingChartColor.setForeground( CHART_STANDARD_EASING );
|
||||
}
|
||||
|
||||
private void start() {
|
||||
startLinear();
|
||||
startEaseInOut();
|
||||
linearAnimator = start( linearAnimator, null, linearScrollBar, CHART_LINEAR );
|
||||
easeInOutAnimator = start( easeInOutAnimator, CubicBezierEasing.EASE_IN_OUT, easeInOutScrollBar, CHART_EASE_IN_OUT );
|
||||
standardEasingAnimator = start( standardEasingAnimator, CubicBezierEasing.STANDARD_EASING, standardEasingScrollBar, CHART_STANDARD_EASING );
|
||||
}
|
||||
|
||||
private void startLinear() {
|
||||
if( linearAnimator != null ) {
|
||||
linearAnimator.stop();
|
||||
linearAnimator.start();
|
||||
private Animator start( Animator animator, Interpolator interpolator, JScrollBar scrollBar, Color chartColor ) {
|
||||
if( animator != null ) {
|
||||
animator.stop();
|
||||
animator.start();
|
||||
} else {
|
||||
linearAnimator = new Animator( 1000, fraction -> {
|
||||
linearScrollBar.setValue( Math.round( fraction * linearScrollBar.getMaximum() ) );
|
||||
animator = new Animator( 1000, fraction -> {
|
||||
scrollBar.setValue( Math.round( fraction * scrollBar.getMaximum() ) );
|
||||
lineChartPanel.addValue( chartColor, fraction, Integer.MIN_VALUE, "animator" );
|
||||
} );
|
||||
linearAnimator.start();
|
||||
}
|
||||
}
|
||||
|
||||
private void startEaseInOut() {
|
||||
if( easeInOutAnimator != null ) {
|
||||
easeInOutAnimator.stop();
|
||||
easeInOutAnimator.start();
|
||||
} else {
|
||||
easeInOutAnimator = new Animator( 1000, fraction -> {
|
||||
easeInOutScrollBar.setValue( Math.round( fraction * easeInOutScrollBar.getMaximum() ) );
|
||||
} );
|
||||
easeInOutAnimator.setInterpolator( CubicBezierEasing.EASE_IN_OUT );
|
||||
easeInOutAnimator.start();
|
||||
animator.setInterpolator( interpolator );
|
||||
animator.start();
|
||||
}
|
||||
return animator;
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
|
||||
JLabel label1 = new JLabel();
|
||||
linearLabel = new JLabel();
|
||||
linearChartColor = new FlatAnimatorTest.JChartColor();
|
||||
linearScrollBar = new JScrollBar();
|
||||
JLabel label2 = new JLabel();
|
||||
easeInOutLabel = new JLabel();
|
||||
easeInOutChartColor = new FlatAnimatorTest.JChartColor();
|
||||
easeInOutScrollBar = new JScrollBar();
|
||||
standardEasingLabel = new JLabel();
|
||||
standardEasingChartColor = new FlatAnimatorTest.JChartColor();
|
||||
standardEasingScrollBar = new JScrollBar();
|
||||
startButton = new JButton();
|
||||
lineChartPanel = new LineChartPanel();
|
||||
|
||||
//======== this ========
|
||||
setLayout(new MigLayout(
|
||||
"ltr,insets dialog,hidemode 3",
|
||||
// columns
|
||||
"[fill]" +
|
||||
"[fill]" +
|
||||
"[grow,fill]",
|
||||
// rows
|
||||
"[]" +
|
||||
"[]" +
|
||||
"[]"));
|
||||
"[]" +
|
||||
"[]para" +
|
||||
"[400,grow,fill]"));
|
||||
|
||||
//---- label1 ----
|
||||
label1.setText("Linear:");
|
||||
add(label1, "cell 0 0");
|
||||
//---- linearLabel ----
|
||||
linearLabel.setText("Linear:");
|
||||
add(linearLabel, "cell 0 0");
|
||||
add(linearChartColor, "cell 1 0");
|
||||
|
||||
//---- linearScrollBar ----
|
||||
linearScrollBar.setOrientation(Adjustable.HORIZONTAL);
|
||||
linearScrollBar.setBlockIncrement(1);
|
||||
add(linearScrollBar, "cell 1 0");
|
||||
add(linearScrollBar, "cell 2 0");
|
||||
|
||||
//---- label2 ----
|
||||
label2.setText("Ease in out:");
|
||||
add(label2, "cell 0 1");
|
||||
//---- easeInOutLabel ----
|
||||
easeInOutLabel.setText("Ease in out:");
|
||||
add(easeInOutLabel, "cell 0 1");
|
||||
add(easeInOutChartColor, "cell 1 1");
|
||||
|
||||
//---- easeInOutScrollBar ----
|
||||
easeInOutScrollBar.setOrientation(Adjustable.HORIZONTAL);
|
||||
easeInOutScrollBar.setBlockIncrement(1);
|
||||
add(easeInOutScrollBar, "cell 1 1");
|
||||
add(easeInOutScrollBar, "cell 2 1");
|
||||
|
||||
//---- standardEasingLabel ----
|
||||
standardEasingLabel.setText("Standard easing:");
|
||||
add(standardEasingLabel, "cell 0 2");
|
||||
add(standardEasingChartColor, "cell 1 2");
|
||||
|
||||
//---- standardEasingScrollBar ----
|
||||
standardEasingScrollBar.setOrientation(Adjustable.HORIZONTAL);
|
||||
standardEasingScrollBar.setBlockIncrement(1);
|
||||
add(standardEasingScrollBar, "cell 2 2");
|
||||
|
||||
//---- startButton ----
|
||||
startButton.setText("Start");
|
||||
startButton.addActionListener(e -> start());
|
||||
add(startButton, "cell 0 2");
|
||||
add(startButton, "cell 0 3");
|
||||
add(lineChartPanel, "cell 0 4 3 1");
|
||||
// JFormDesigner - End of component initialization //GEN-END:initComponents
|
||||
}
|
||||
|
||||
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
|
||||
private JLabel linearLabel;
|
||||
private FlatAnimatorTest.JChartColor linearChartColor;
|
||||
private JScrollBar linearScrollBar;
|
||||
private JLabel easeInOutLabel;
|
||||
private FlatAnimatorTest.JChartColor easeInOutChartColor;
|
||||
private JScrollBar easeInOutScrollBar;
|
||||
private JLabel standardEasingLabel;
|
||||
private FlatAnimatorTest.JChartColor standardEasingChartColor;
|
||||
private JScrollBar standardEasingScrollBar;
|
||||
private JButton startButton;
|
||||
private LineChartPanel lineChartPanel;
|
||||
// JFormDesigner - End of variables declaration //GEN-END:variables
|
||||
|
||||
//---- class JChartColor --------------------------------------------------
|
||||
|
||||
static class JChartColor
|
||||
extends JComponent
|
||||
{
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
return new Dimension( UIScale.scale( 24 ), UIScale.scale( 12 ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getMinimumSize() {
|
||||
return getPreferredSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent( Graphics g ) {
|
||||
g.setColor( getForeground() );
|
||||
g.fillRect( 0, 0, UIScale.scale( 24 ), UIScale.scale( 12 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
JFDML JFormDesigner: "7.0.2.0.298" Java: "14.0.2" encoding: "UTF-8"
|
||||
JFDML JFormDesigner: "8.3" encoding: "UTF-8"
|
||||
|
||||
new FormModel {
|
||||
contentType: "form/swing"
|
||||
@@ -8,16 +8,27 @@ new FormModel {
|
||||
}
|
||||
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||
"$layoutConstraints": "ltr,insets dialog,hidemode 3"
|
||||
"$columnConstraints": "[fill][grow,fill]"
|
||||
"$rowConstraints": "[][][]"
|
||||
"$columnConstraints": "[fill][fill][grow,fill]"
|
||||
"$rowConstraints": "[][][][]para[400,grow,fill]"
|
||||
} ) {
|
||||
name: "this"
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "label1"
|
||||
name: "linearLabel"
|
||||
"text": "Linear:"
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": false
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 0"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
|
||||
name: "linearChartColor"
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": false
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JScrollBar" ) {
|
||||
name: "linearScrollBar"
|
||||
"orientation": 0
|
||||
@@ -26,14 +37,25 @@ new FormModel {
|
||||
"JavaCodeGenerator.variableLocal": false
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 0"
|
||||
"value": "cell 2 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "label2"
|
||||
name: "easeInOutLabel"
|
||||
"text": "Ease in out:"
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": false
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
|
||||
name: "easeInOutChartColor"
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": false
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JScrollBar" ) {
|
||||
name: "easeInOutScrollBar"
|
||||
"orientation": 0
|
||||
@@ -42,7 +64,34 @@ new FormModel {
|
||||
"JavaCodeGenerator.variableLocal": false
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 1"
|
||||
"value": "cell 2 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "standardEasingLabel"
|
||||
"text": "Standard easing:"
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": false
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 2"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
|
||||
name: "standardEasingChartColor"
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": false
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 2"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JScrollBar" ) {
|
||||
name: "standardEasingScrollBar"
|
||||
"orientation": 0
|
||||
"blockIncrement": 1
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": false
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 2 2"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JButton" ) {
|
||||
name: "startButton"
|
||||
@@ -52,11 +101,19 @@ new FormModel {
|
||||
}
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "start", false ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 2"
|
||||
"value": "cell 0 3"
|
||||
} )
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.LineChartPanel" ) {
|
||||
name: "lineChartPanel"
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": false
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 4 3 1"
|
||||
} )
|
||||
}, new FormLayoutConstraints( null ) {
|
||||
"location": new java.awt.Point( 0, 0 )
|
||||
"size": new java.awt.Dimension( 415, 350 )
|
||||
"size": new java.awt.Dimension( 625, 625 )
|
||||
} )
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,883 @@
|
||||
/*
|
||||
/*
|
||||
* Copyright 2023 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.testing;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.HierarchyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import javax.swing.*;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.FlatLaf;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
import com.formdev.flatlaf.util.HSLColor;
|
||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
import net.miginfocom.swing.*;
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
class LineChartPanel
|
||||
extends JPanel
|
||||
{
|
||||
LineChartPanel() {
|
||||
initComponents();
|
||||
|
||||
lineChartScrollPane.putClientProperty( FlatClientProperties.SCROLL_PANE_SMOOTH_SCROLLING, false );
|
||||
|
||||
oneSecondWidthChanged();
|
||||
updateChartDelayedChanged();
|
||||
|
||||
// clear chart on startup
|
||||
addHierarchyListener( e -> {
|
||||
if( (e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 && isShowing() )
|
||||
EventQueue.invokeLater( this::clearChart );
|
||||
} );
|
||||
|
||||
// show chart tooltips immediately and forever
|
||||
ToolTipManager.sharedInstance().setInitialDelay( 0 );
|
||||
ToolTipManager.sharedInstance().setDismissDelay( Integer.MAX_VALUE );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNotify() {
|
||||
super.addNotify();
|
||||
|
||||
// allow clearing chart with Alt+C without moving focus to button
|
||||
getRootPane().registerKeyboardAction(
|
||||
e -> clearChart(),
|
||||
KeyStroke.getKeyStroke( "alt " + (char) clearChartButton.getMnemonic() ),
|
||||
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
|
||||
}
|
||||
|
||||
public boolean isYZeroAtTop() {
|
||||
return lineChart.yZeroAtTop;
|
||||
}
|
||||
|
||||
public void setYZeroAtTop( boolean yZeroAtTop ) {
|
||||
lineChart.yZeroAtTop = yZeroAtTop;
|
||||
lineChart.repaint();
|
||||
}
|
||||
|
||||
public boolean isAsynchron() {
|
||||
return lineChart.asynchron;
|
||||
}
|
||||
|
||||
public void setAsynchron( boolean asynchron ) {
|
||||
lineChart.asynchron = asynchron;
|
||||
lineChart.repaint();
|
||||
}
|
||||
|
||||
public boolean isTemporaryValueDetection() {
|
||||
return lineChart.temporaryValueDetection;
|
||||
}
|
||||
|
||||
public void setTemporaryValueDetection( boolean temporaryValueDetection ) {
|
||||
lineChart.temporaryValueDetection = temporaryValueDetection;
|
||||
lineChart.repaint();
|
||||
}
|
||||
|
||||
public String getLegendYValueText() {
|
||||
return yValueLabel.getText();
|
||||
}
|
||||
|
||||
public void setLegendYValueText( String s ) {
|
||||
yValueLabel.setText( s );
|
||||
}
|
||||
|
||||
public String getLegend1Text() {
|
||||
return legend1Label.getText();
|
||||
}
|
||||
|
||||
public void setLegend1Text( String s ) {
|
||||
legend1Label.setText( s );
|
||||
}
|
||||
|
||||
public String getLegend2Text() {
|
||||
return legend2Label.getText();
|
||||
}
|
||||
|
||||
public void setLegend2Text( String s ) {
|
||||
legend2Label.setText( s );
|
||||
}
|
||||
|
||||
public int getOneSecondWidth() {
|
||||
return oneSecondWidthSlider.getValue();
|
||||
}
|
||||
|
||||
public void setOneSecondWidth( int oneSecondWidth ) {
|
||||
oneSecondWidthSlider.setValue( oneSecondWidth );
|
||||
}
|
||||
|
||||
public boolean isUpdateChartDelayed() {
|
||||
return updateChartDelayedCheckBox.isSelected();
|
||||
}
|
||||
|
||||
public void setUpdateChartDelayed( boolean updateChartDelayed ) {
|
||||
updateChartDelayedCheckBox.setSelected( updateChartDelayed );
|
||||
updateChartDelayedChanged();
|
||||
}
|
||||
|
||||
void addValue( Color chartColor, double value, int ivalue, String name ) {
|
||||
lineChart.addValue( chartColor, value, ivalue, null, false, name );
|
||||
}
|
||||
|
||||
void addValueWithDot( Color chartColor, double value, int ivalue, Color dotColor, String name ) {
|
||||
if( dotColor == null )
|
||||
dotColor = chartColor;
|
||||
lineChart.addValue( chartColor, value, ivalue, dotColor, false, name );
|
||||
}
|
||||
|
||||
void addDot( Color chartColor, double value, int ivalue, Color dotColor, String name ) {
|
||||
if( dotColor == null )
|
||||
dotColor = chartColor;
|
||||
lineChart.addValue( chartColor, value, ivalue, dotColor, true, name );
|
||||
}
|
||||
|
||||
void addMethodHighlight( String classAndMethod, String highlightColor ) {
|
||||
lineChart.methodHighlightMap.put( classAndMethod, highlightColor );
|
||||
}
|
||||
|
||||
private void oneSecondWidthChanged() {
|
||||
int oneSecondWidth = oneSecondWidthSlider.getValue();
|
||||
int msPerLineX =
|
||||
oneSecondWidth <= 2000 ? 100 :
|
||||
oneSecondWidth <= 4000 ? 50 :
|
||||
oneSecondWidth <= 8000 ? 25 :
|
||||
10;
|
||||
|
||||
lineChart.oneSecondWidth = oneSecondWidth;
|
||||
lineChart.msPerLineX = msPerLineX;
|
||||
lineChart.revalidate();
|
||||
lineChart.repaint();
|
||||
|
||||
if( xLabelText == null )
|
||||
xLabelText = xLabel.getText();
|
||||
xLabel.setText( MessageFormat.format( xLabelText, msPerLineX ) );
|
||||
}
|
||||
private String xLabelText;
|
||||
|
||||
private void updateChartDelayedChanged() {
|
||||
lineChart.updateDelayed = updateChartDelayedCheckBox.isSelected();
|
||||
}
|
||||
|
||||
private void clearChart() {
|
||||
lineChart.clear();
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents @formatter:off
|
||||
lineChartScrollPane = new JScrollPane();
|
||||
lineChart = new LineChartPanel.LineChart();
|
||||
JPanel legendPanel = new JPanel();
|
||||
xLabel = new JLabel();
|
||||
legend1Label = new JLabel();
|
||||
JLabel yLabel = new JLabel();
|
||||
yValueLabel = new JLabel();
|
||||
JLabel yLabel2 = new JLabel();
|
||||
JPanel hSpacer1 = new JPanel(null);
|
||||
legend2Label = new JLabel();
|
||||
JLabel oneSecondWidthLabel = new JLabel();
|
||||
oneSecondWidthSlider = new JSlider();
|
||||
updateChartDelayedCheckBox = new JCheckBox();
|
||||
clearChartButton = new JButton();
|
||||
|
||||
//======== this ========
|
||||
setLayout(new MigLayout(
|
||||
"hidemode 3",
|
||||
// columns
|
||||
"[grow,fill]",
|
||||
// rows
|
||||
"[100:300,grow,fill]" +
|
||||
"[]"));
|
||||
|
||||
//======== lineChartScrollPane ========
|
||||
{
|
||||
lineChartScrollPane.setViewportView(lineChart);
|
||||
}
|
||||
add(lineChartScrollPane, "cell 0 0");
|
||||
|
||||
//======== legendPanel ========
|
||||
{
|
||||
legendPanel.setLayout(new MigLayout(
|
||||
"insets 0,hidemode 3,gapy 0",
|
||||
// columns
|
||||
"[fill]para" +
|
||||
"[fill]",
|
||||
// rows
|
||||
"[]" +
|
||||
"[]"));
|
||||
|
||||
//---- xLabel ----
|
||||
xLabel.setText("X: time ({0}ms per line)");
|
||||
legendPanel.add(xLabel, "cell 0 0");
|
||||
legendPanel.add(legend1Label, "cell 1 0");
|
||||
|
||||
//---- yLabel ----
|
||||
yLabel.setText("Y: ");
|
||||
legendPanel.add(yLabel, "cell 0 1,gapx 0 0");
|
||||
|
||||
//---- yValueLabel ----
|
||||
yValueLabel.setText("value");
|
||||
legendPanel.add(yValueLabel, "cell 0 1,gapx 0 0");
|
||||
|
||||
//---- yLabel2 ----
|
||||
yLabel2.setText(" (10% per line)");
|
||||
legendPanel.add(yLabel2, "cell 0 1,gapx 0 0");
|
||||
legendPanel.add(hSpacer1, "cell 0 1,growx");
|
||||
legendPanel.add(legend2Label, "cell 1 1");
|
||||
}
|
||||
add(legendPanel, "cell 0 1");
|
||||
|
||||
//---- oneSecondWidthLabel ----
|
||||
oneSecondWidthLabel.setText("Scale X:");
|
||||
oneSecondWidthLabel.setDisplayedMnemonic('A');
|
||||
oneSecondWidthLabel.setLabelFor(oneSecondWidthSlider);
|
||||
add(oneSecondWidthLabel, "cell 0 1,alignx right,growx 0");
|
||||
|
||||
//---- oneSecondWidthSlider ----
|
||||
oneSecondWidthSlider.setMinimum(100);
|
||||
oneSecondWidthSlider.setMaximum(10000);
|
||||
oneSecondWidthSlider.setSnapToTicks(true);
|
||||
oneSecondWidthSlider.setMajorTickSpacing(100);
|
||||
oneSecondWidthSlider.setValue(500);
|
||||
oneSecondWidthSlider.addChangeListener(e -> oneSecondWidthChanged());
|
||||
add(oneSecondWidthSlider, "cell 0 1,alignx right,growx 0");
|
||||
|
||||
//---- updateChartDelayedCheckBox ----
|
||||
updateChartDelayedCheckBox.setText("Update chart delayed");
|
||||
updateChartDelayedCheckBox.setMnemonic('P');
|
||||
updateChartDelayedCheckBox.addActionListener(e -> updateChartDelayedChanged());
|
||||
add(updateChartDelayedCheckBox, "cell 0 1,alignx right,growx 0");
|
||||
|
||||
//---- clearChartButton ----
|
||||
clearChartButton.setText("Clear Chart");
|
||||
clearChartButton.setMnemonic('C');
|
||||
clearChartButton.addActionListener(e -> clearChart());
|
||||
add(clearChartButton, "cell 0 1,alignx right,growx 0");
|
||||
// JFormDesigner - End of component initialization //GEN-END:initComponents @formatter:on
|
||||
}
|
||||
|
||||
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables @formatter:off
|
||||
private JScrollPane lineChartScrollPane;
|
||||
private LineChartPanel.LineChart lineChart;
|
||||
private JLabel xLabel;
|
||||
private JLabel legend1Label;
|
||||
private JLabel yValueLabel;
|
||||
private JLabel legend2Label;
|
||||
private JSlider oneSecondWidthSlider;
|
||||
private JCheckBox updateChartDelayedCheckBox;
|
||||
private JButton clearChartButton;
|
||||
// JFormDesigner - End of variables declaration //GEN-END:variables @formatter:on
|
||||
|
||||
|
||||
//---- class LineChart ----------------------------------------------------
|
||||
|
||||
private static class LineChart
|
||||
extends JComponent
|
||||
implements Scrollable
|
||||
{
|
||||
private static final int UPDATE_DELAY_MS = 20;
|
||||
private static final int NEW_SEQUENCE_TIME_LAG_MS = 500;
|
||||
private static final int NEW_SEQUENCE_GAP_MS = 100;
|
||||
private static final int HIT_OFFSET = 4;
|
||||
|
||||
private static final boolean TEST = false;
|
||||
|
||||
// asynchron means that chart for each color starts at x=0
|
||||
private boolean asynchron;
|
||||
private boolean temporaryValueDetection;
|
||||
private boolean yZeroAtTop;
|
||||
private int oneSecondWidth = 500;
|
||||
private int msPerLineX = 100;
|
||||
private final HashMap<String, String> methodHighlightMap = new HashMap<>();
|
||||
|
||||
private static class Data {
|
||||
final double value;
|
||||
final int ivalue;
|
||||
final Color chartColor;
|
||||
final Color dotColor;
|
||||
final boolean dotOnly;
|
||||
final long time; // in milliseconds
|
||||
final String name;
|
||||
final Exception stack;
|
||||
|
||||
Data( double value, int ivalue, Color chartColor, Color dotColor,
|
||||
boolean dotOnly, long time, String name, Exception stack )
|
||||
{
|
||||
this.value = value;
|
||||
this.ivalue = ivalue;
|
||||
this.chartColor = chartColor;
|
||||
this.dotColor = dotColor;
|
||||
this.dotOnly = dotOnly;
|
||||
this.time = time;
|
||||
this.name = name;
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// for debugging
|
||||
return "value=" + value + ", ivalue=" + ivalue + ", dotColor=" + dotColor
|
||||
+ ", dotOnly=" + dotOnly + ", time=" + time + ", name=" + name;
|
||||
}
|
||||
}
|
||||
|
||||
private final List<Data> syncChartData = new ArrayList<>();
|
||||
private final Map<Color, List<Data>> asyncColor2dataMap = new HashMap<>();
|
||||
private final Timer repaintTime;
|
||||
private Color lastUsedChartColor;
|
||||
private boolean updateDelayed;
|
||||
|
||||
private final List<Point> lastPoints = new ArrayList<>();
|
||||
private final List<Data> lastDatas = new ArrayList<>();
|
||||
private double lastSystemScaleFactor = 1;
|
||||
private String lastToolTipPrinted;
|
||||
|
||||
LineChart() {
|
||||
repaintTime = new Timer( UPDATE_DELAY_MS, e -> repaintAndRevalidate() );
|
||||
repaintTime.setRepeats( false );
|
||||
|
||||
ToolTipManager.sharedInstance().registerComponent( this );
|
||||
|
||||
if( TEST )
|
||||
initTestData();
|
||||
}
|
||||
|
||||
void addValue( Color chartColor, double value, int ivalue, Color dotColor, boolean dotOnly, String name ) {
|
||||
if( TEST )
|
||||
return;
|
||||
|
||||
List<Data> chartData = asyncColor2dataMap.computeIfAbsent( chartColor, k -> new ArrayList<>() );
|
||||
Data data = new Data( value, ivalue, chartColor, dotColor, dotOnly, System.nanoTime() / 1_000_000, name, new Exception() );
|
||||
if( asynchron )
|
||||
chartData.add( data );
|
||||
else
|
||||
syncChartData.add( data );
|
||||
|
||||
lastUsedChartColor = chartColor;
|
||||
|
||||
if( updateDelayed ) {
|
||||
repaintTime.stop();
|
||||
repaintTime.start();
|
||||
} else
|
||||
repaintAndRevalidate();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
if( TEST ) {
|
||||
repaint();
|
||||
return;
|
||||
}
|
||||
|
||||
syncChartData.clear();
|
||||
asyncColor2dataMap.clear();
|
||||
lastUsedChartColor = null;
|
||||
|
||||
repaint();
|
||||
revalidate();
|
||||
}
|
||||
|
||||
private void repaintAndRevalidate() {
|
||||
repaint();
|
||||
revalidate();
|
||||
|
||||
// scroll horizontally
|
||||
if( lastUsedChartColor != null ) {
|
||||
// compute chart width of last used color and start of last sequence
|
||||
int[] lastSeqX = new int[1];
|
||||
int cw = chartWidth( asynchron ? asyncColor2dataMap.get( lastUsedChartColor ) : syncChartData, lastSeqX );
|
||||
|
||||
// scroll to end of last sequence (of last used color)
|
||||
int lastSeqWidth = cw - lastSeqX[0];
|
||||
int width = Math.min( lastSeqWidth, getParent().getWidth() );
|
||||
int x = cw - width;
|
||||
scrollRectToVisible( new Rectangle( x, 0, width, getHeight() ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent( Graphics g ) {
|
||||
Graphics g2 = g.create();
|
||||
try {
|
||||
HiDPIUtils.paintAtScale1x( (Graphics2D) g2, this, this::paintAt1x );
|
||||
} finally {
|
||||
g2.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void paintAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
||||
FlatUIUtils.setRenderingHints( g );
|
||||
|
||||
int oneSecondWidth = (int) (UIScale.scale( this.oneSecondWidth ) * scaleFactor);
|
||||
int seqGapWidth = (oneSecondWidth * NEW_SEQUENCE_GAP_MS) / 1000;
|
||||
int hitOffset = (int) Math.round( UIScale.scale( HIT_OFFSET ) * scaleFactor );
|
||||
|
||||
Color lineColor = FlatUIUtils.getUIColor( "Component.borderColor", Color.lightGray );
|
||||
Color lineColor2 = FlatLaf.isLafDark()
|
||||
? new HSLColor( lineColor ).adjustTone( 30 )
|
||||
: new HSLColor( lineColor ).adjustShade( 30 );
|
||||
|
||||
g.translate( x, y );
|
||||
|
||||
// fill background
|
||||
g.setColor( UIManager.getColor( "Table.background" ) );
|
||||
g.fillRect( x, y, width, height );
|
||||
|
||||
// paint horizontal lines
|
||||
for( int i = 1; i < 10; i++ ) {
|
||||
int hy = (height * i) / 10;
|
||||
g.setColor( (i != 5) ? lineColor : lineColor2 );
|
||||
g.drawLine( 0, hy, width, hy );
|
||||
}
|
||||
|
||||
// paint vertical lines
|
||||
int perLineXWidth = Math.round( (oneSecondWidth / 1000f) * msPerLineX );
|
||||
for( int i = 1, xv = perLineXWidth; xv < width; xv += perLineXWidth, i++ ) {
|
||||
g.setColor( (i % 5 != 0) ? lineColor : lineColor2 );
|
||||
g.drawLine( xv, 0, xv, height );
|
||||
}
|
||||
|
||||
lastPoints.clear();
|
||||
lastDatas.clear();
|
||||
lastSystemScaleFactor = scaleFactor;
|
||||
|
||||
// paint lines
|
||||
for( Map.Entry<Color, List<Data>> e : asyncColor2dataMap.entrySet() ) {
|
||||
List<Data> chartData = asynchron ? e.getValue() : syncChartData;
|
||||
Color chartColor = e.getKey();
|
||||
if( FlatLaf.isLafDark() )
|
||||
chartColor = new HSLColor( chartColor ).adjustTone( 50 );
|
||||
Color temporaryValueColor = fade( chartColor, FlatLaf.isLafDark() ? 0.7f : 0.3f );
|
||||
Color dataPointColor = fade( chartColor, FlatLaf.isLafDark() ? 0.6f : 0.2f );
|
||||
|
||||
// sequence start time and x coordinate
|
||||
long seqStartTime = 0;
|
||||
int seqStartX = 0;
|
||||
|
||||
// "previous" data point time and x coordinate (used for "new sequence" detection)
|
||||
long ptime = Long.MIN_VALUE;
|
||||
int px = 0;
|
||||
|
||||
// "line" data point x/y coordinates
|
||||
int lx = -1;
|
||||
int ly = -1;
|
||||
|
||||
boolean isTemporaryValue = false;
|
||||
int lastTemporaryValueIndex = -1;
|
||||
|
||||
int size = chartData.size();
|
||||
for( int i = 0; i < size; i++ ) {
|
||||
Data data = chartData.get( i );
|
||||
boolean useData = (data.chartColor == chartColor);
|
||||
|
||||
// start new sequence if there is a larger time gap to previous data point
|
||||
boolean newSeq = (data.time > ptime + NEW_SEQUENCE_TIME_LAG_MS);
|
||||
ptime = data.time;
|
||||
|
||||
if( newSeq ) {
|
||||
// start new sequence
|
||||
seqStartTime = data.time;
|
||||
seqStartX = (i > 0) ? px + seqGapWidth : 0;
|
||||
px = seqStartX;
|
||||
lx = -1;
|
||||
ly = -1;
|
||||
isTemporaryValue = false;
|
||||
}
|
||||
|
||||
// x/y coordinates of current data point
|
||||
int dx = (int) (seqStartX + (((data.time - seqStartTime) / 1000.) * oneSecondWidth));
|
||||
int dy = (int) ((height - 1) * data.value);
|
||||
if( !yZeroAtTop )
|
||||
dy = height - 1 - dy;
|
||||
|
||||
// remember x coordinate for "new sequence" detection
|
||||
px = dx;
|
||||
|
||||
if( !useData )
|
||||
continue;
|
||||
|
||||
// remember data point for tooltip
|
||||
lastPoints.add( new Point( dx, dy ) );
|
||||
lastDatas.add( data );
|
||||
|
||||
// paint rectangle to indicate data point
|
||||
g.setColor( dataPointColor );
|
||||
g.drawRect( dx - hitOffset, dy - hitOffset, hitOffset * 2, hitOffset * 2 );
|
||||
|
||||
// paint dot
|
||||
if( data.dotColor != null ) {
|
||||
int s1 = (int) Math.round( UIScale.scale( 1 ) * scaleFactor );
|
||||
int s3 = (int) Math.round( UIScale.scale( 3 ) * scaleFactor );
|
||||
g.setColor( data.dotColor );
|
||||
g.fillRect( dx - s1, dy - s1, s3, s3 );
|
||||
|
||||
if( data.dotOnly )
|
||||
continue;
|
||||
}
|
||||
|
||||
// start of line?
|
||||
if( lx < 0 ) {
|
||||
// remember x/y coordinates for first line
|
||||
lx = dx;
|
||||
ly = dy;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( isTemporaryValue && i > lastTemporaryValueIndex )
|
||||
isTemporaryValue = false;
|
||||
|
||||
// draw line in sequence
|
||||
g.setColor( isTemporaryValue ? temporaryValueColor : chartColor );
|
||||
g.drawLine( lx, ly, dx, dy );
|
||||
|
||||
// remember x/y coordinates for next line
|
||||
lx = dx;
|
||||
ly = dy;
|
||||
|
||||
// check next data points for "temporary" value(s)
|
||||
if( temporaryValueDetection && !isTemporaryValue ) {
|
||||
// one or two values between two equal values are considered "temporary",
|
||||
// which means that they are the target value for the following scroll animation
|
||||
int stage = 0;
|
||||
for( int j = i + 1; j < size && stage <= 2 && !isTemporaryValue; j++ ) {
|
||||
Data nextData = chartData.get( j );
|
||||
if( nextData.dotOnly )
|
||||
continue; // ignore dots
|
||||
|
||||
// check whether next data point is within 10 milliseconds
|
||||
if( nextData.time > data.time + 10 )
|
||||
break;
|
||||
|
||||
if( stage >= 1 && stage <= 2 && nextData.value == data.value ) {
|
||||
isTemporaryValue = true;
|
||||
lastTemporaryValueIndex = j;
|
||||
}
|
||||
stage++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int chartWidth() {
|
||||
int width = 0;
|
||||
if( asynchron ) {
|
||||
for( List<Data> chartData : asyncColor2dataMap.values() )
|
||||
width = Math.max( width, chartWidth( chartData, null ) );
|
||||
} else
|
||||
width = Math.max( width, chartWidth( syncChartData, null ) );
|
||||
return width;
|
||||
}
|
||||
|
||||
private int chartWidth( List<Data> chartData, int[] lastSeqX ) {
|
||||
long seqTime = 0;
|
||||
int seqX = 0;
|
||||
long ptime = 0;
|
||||
int px = 0;
|
||||
int oneSecondWidth = UIScale.scale( this.oneSecondWidth );
|
||||
int seqGapWidth = (oneSecondWidth * NEW_SEQUENCE_GAP_MS) / 1000;
|
||||
|
||||
int size = chartData.size();
|
||||
for( int i = 0; i < size; i++ ) {
|
||||
Data data = chartData.get( i );
|
||||
|
||||
if( data.time > ptime + NEW_SEQUENCE_TIME_LAG_MS ) {
|
||||
// start new sequence
|
||||
seqTime = data.time;
|
||||
seqX = (i > 0) ? px + seqGapWidth : 0;
|
||||
px = seqX;
|
||||
} else {
|
||||
// line in sequence
|
||||
int dx = (int) (seqX + (((data.time - seqTime) / 1000.) * oneSecondWidth ));
|
||||
px = dx;
|
||||
}
|
||||
|
||||
ptime = data.time;
|
||||
}
|
||||
|
||||
if( lastSeqX != null )
|
||||
lastSeqX[0] = seqX;
|
||||
|
||||
return px;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
return new Dimension( chartWidth(), 200 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredScrollableViewportSize() {
|
||||
return new Dimension( chartWidth(), 200 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScrollableUnitIncrement( Rectangle visibleRect, int orientation, int direction ) {
|
||||
return UIScale.scale( oneSecondWidth );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScrollableBlockIncrement( Rectangle visibleRect, int orientation, int direction ) {
|
||||
JViewport viewport = (JViewport) SwingUtilities.getAncestorOfClass( JViewport.class, this );
|
||||
return (viewport != null) ? viewport.getWidth() : 200;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getScrollableTracksViewportWidth() {
|
||||
JViewport viewport = (JViewport) SwingUtilities.getAncestorOfClass( JViewport.class, this );
|
||||
return (viewport != null) ? viewport.getWidth() > chartWidth() : true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getScrollableTracksViewportHeight() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToolTipText( MouseEvent e ) {
|
||||
int x = (int) Math.round( e.getX() * lastSystemScaleFactor );
|
||||
int y = (int) Math.round( e.getY() * lastSystemScaleFactor );
|
||||
int hitOffset = (int) Math.round( UIScale.scale( HIT_OFFSET ) * lastSystemScaleFactor );
|
||||
StringBuilder buf = null;
|
||||
|
||||
int pointsCount = lastPoints.size();
|
||||
for( int i = 0; i < pointsCount; i++ ) {
|
||||
Point pt = lastPoints.get( i );
|
||||
|
||||
// check X/Y coordinates
|
||||
if( x < pt.x - hitOffset || x > pt.x + hitOffset ||
|
||||
y < pt.y - hitOffset || y > pt.y + hitOffset )
|
||||
continue;
|
||||
|
||||
if( buf == null ) {
|
||||
buf = new StringBuilder( 5000 );
|
||||
buf.append( "<html>" );
|
||||
}
|
||||
|
||||
Data data = lastDatas.get( i );
|
||||
buf.append( "<h2>" );
|
||||
if( data.dotOnly )
|
||||
buf.append( "DOT: " );
|
||||
buf.append( data.name );
|
||||
if( data.ivalue != Integer.MIN_VALUE )
|
||||
buf.append( ' ' ).append( data.ivalue );
|
||||
buf.append( " (" ).append( String.format( "%.3f", data.value ) ).append( ')' );
|
||||
buf.append( "</h2>" );
|
||||
|
||||
StackTraceElement[] stackTrace = data.stack.getStackTrace();
|
||||
for( int j = 0; j < stackTrace.length; j++ ) {
|
||||
StackTraceElement stackElement = stackTrace[j];
|
||||
String className = stackElement.getClassName();
|
||||
String methodName = stackElement.getMethodName();
|
||||
String classAndMethod = className + '.' + methodName;
|
||||
|
||||
// ignore methods from this class
|
||||
if( className.startsWith( LineChartPanel.class.getName() ) )
|
||||
continue;
|
||||
|
||||
int repeatCount = 0;
|
||||
for( int k = j + 1; k < stackTrace.length; k++ ) {
|
||||
if( !stackElement.equals( stackTrace[k] ) )
|
||||
break;
|
||||
repeatCount++;
|
||||
}
|
||||
j += repeatCount;
|
||||
|
||||
String highlight = methodHighlightMap.get( classAndMethod );
|
||||
if( highlight == null )
|
||||
highlight = methodHighlightMap.get( className );
|
||||
if( highlight == null )
|
||||
highlight = methodHighlightMap.get( methodName );
|
||||
if( highlight != null )
|
||||
buf.append( "<span color=\"" ).append( highlight ).append( "\">" );
|
||||
|
||||
// append method
|
||||
buf.append( className )
|
||||
.append( ".<b>" )
|
||||
.append( methodName )
|
||||
.append( "</b>" );
|
||||
if( highlight != null )
|
||||
buf.append( "</span>" );
|
||||
|
||||
// append source
|
||||
buf.append( " <span color=\"#888888\">" );
|
||||
if( stackElement.getFileName() != null ) {
|
||||
buf.append( '(' );
|
||||
buf.append( stackElement.getFileName() );
|
||||
if( stackElement.getLineNumber() >= 0 )
|
||||
buf.append( ':' ).append( stackElement.getLineNumber() );
|
||||
buf.append( ')' );
|
||||
} else
|
||||
buf.append( "(Unknown Source)" );
|
||||
buf.append( "</span>" );
|
||||
|
||||
// append repeat count
|
||||
if( repeatCount > 0 )
|
||||
buf.append( " <b>" ).append( repeatCount + 1 ).append( "x</b>" );
|
||||
buf.append( "<br>" );
|
||||
|
||||
// break at some methods to make stack smaller
|
||||
if( classAndMethod.equals( "java.awt.event.InvocationEvent.dispatch" ) ||
|
||||
classAndMethod.equals( "java.awt.Component.processMouseEvent" ) ||
|
||||
classAndMethod.equals( "java.awt.Component.processMouseWheelEvent" ) ||
|
||||
classAndMethod.equals( "java.awt.Component.processMouseMotionEvent" ) ||
|
||||
classAndMethod.equals( "javax.swing.JComponent.processKeyBinding" ) ||
|
||||
classAndMethod.equals( "javax.swing.JComponent.paintComponent" ) ||
|
||||
classAndMethod.equals( "com.formdev.flatlaf.util.Animator.timingEvent" ) )
|
||||
break;
|
||||
}
|
||||
buf.append( "..." );
|
||||
}
|
||||
|
||||
if( buf == null )
|
||||
return null;
|
||||
|
||||
buf.append( "<html>" );
|
||||
String toolTip = buf.toString();
|
||||
|
||||
// print to console
|
||||
if( !Objects.equals( toolTip, lastToolTipPrinted ) ) {
|
||||
lastToolTipPrinted = toolTip;
|
||||
|
||||
System.out.println( toolTip
|
||||
.replace( "<br>", "\n" )
|
||||
.replace( "<h2>", "\n---- " )
|
||||
.replace( "</h2>", " ----\n" )
|
||||
.replaceAll( "<[^>]+>", "" ) );
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private void initTestData() {
|
||||
// asynchron = true;
|
||||
|
||||
addTestSimpleLine( Color.red, 0.0, "red" );
|
||||
addTestSimpleLine( Color.green, 0.1, "green" );
|
||||
addTestSimpleLine( Color.blue, 0.2, "blue" );
|
||||
addTestSimpleLine( Color.magenta, 0.3, "magenta" );
|
||||
|
||||
addTestMiddleDotOnly( Color.red, 0.0, "red" );
|
||||
addTestMiddleDotOnly( Color.green, 0.1, "green" );
|
||||
addTestMiddleDotOnly( Color.blue, 0.2, "blue" );
|
||||
addTestMiddleDotOnly( Color.magenta, 0.3, "magenta" );
|
||||
|
||||
addTestLeadingDotOnly( Color.red, 0.0, "red" );
|
||||
addTestLeadingDotOnly( Color.green, 0.1, "green" );
|
||||
addTestLeadingDotOnly( Color.blue, 0.2, "blue" );
|
||||
addTestLeadingDotOnly( Color.magenta, 0.3, "magenta" );
|
||||
|
||||
addTestTrailingDotOnly( Color.red, 0.0, "red" );
|
||||
addTestTrailingDotOnly( Color.green, 0.1, "green" );
|
||||
addTestTrailingDotOnly( Color.blue, 0.2, "blue" );
|
||||
addTestTrailingDotOnly( Color.magenta, 0.3, "magenta" );
|
||||
|
||||
addTestSingleData( Color.red, 0.0, "red" );
|
||||
addTestSingleData( Color.green, 0.1, "green" );
|
||||
addTestSingleData( Color.blue, 0.2, "blue" );
|
||||
addTestSingleData( Color.magenta, 0.3, "magenta" );
|
||||
|
||||
temporaryValueDetection = true;
|
||||
addTestWithTemporaryValues( Color.red, 0.0, "red" );
|
||||
addTestWithTemporaryValues( Color.green, 0.1, "green" );
|
||||
addTestWithTemporaryValues( Color.blue, 0.2, "blue" );
|
||||
addTestWithTemporaryValues( Color.magenta, 0.3, "magenta" );
|
||||
}
|
||||
|
||||
private void addTestSimpleLine( Color chartColor, double baseValue, String name ) {
|
||||
addTestValue( 0, chartColor, baseValue + 0.0, null, false, name );
|
||||
addTestValue( 50, chartColor, baseValue + 0.1, null, false, name );
|
||||
addTestValue( 50, chartColor, baseValue + 0.4, null, false, name );
|
||||
testTime += 1000;
|
||||
}
|
||||
|
||||
private void addTestMiddleDotOnly( Color chartColor, double baseValue, String name ) {
|
||||
addTestValue( 0, chartColor, baseValue + 0.0, null, false, name );
|
||||
addTestValue( 20, chartColor, baseValue + 0.3, chartColor, true, name );
|
||||
addTestValue( 30, chartColor, baseValue + 0.1, null, false, name );
|
||||
addTestValue( 20, chartColor, baseValue + 0.05, chartColor, true, name );
|
||||
addTestValue( 30, chartColor, baseValue + 0.4, null, false, name );
|
||||
testTime += 1000;
|
||||
}
|
||||
|
||||
private void addTestLeadingDotOnly( Color chartColor, double baseValue, String name ) {
|
||||
addTestValue( 0, chartColor, baseValue + 0.05, chartColor, true, name );
|
||||
addTestValue( 20, chartColor, baseValue + 0.0, null, false, name );
|
||||
addTestValue( 50, chartColor, baseValue + 0.1, null, false, name );
|
||||
addTestValue( 30, chartColor, baseValue + 0.4, null, false, name );
|
||||
testTime += 1000;
|
||||
}
|
||||
|
||||
private void addTestTrailingDotOnly( Color chartColor, double baseValue, String name ) {
|
||||
addTestValue( 0, chartColor, baseValue + 0.0, null, false, name );
|
||||
addTestValue( 50, chartColor, baseValue + 0.1, null, false, name );
|
||||
addTestValue( 30, chartColor, baseValue + 0.4, null, false, name );
|
||||
addTestValue( 20, chartColor, baseValue + 0.05, chartColor, true, name );
|
||||
testTime += 1000;
|
||||
}
|
||||
|
||||
private void addTestSingleData( Color chartColor, double baseValue, String name ) {
|
||||
addTestValue( 0, chartColor, baseValue + 0.15, chartColor, false, name );
|
||||
testTime += 1000;
|
||||
}
|
||||
|
||||
private void addTestWithTemporaryValues( Color chartColor, double baseValue, String name ) {
|
||||
addTestValue( 0, chartColor, baseValue + 0.0, null, false, name );
|
||||
addTestValue( 50, chartColor, baseValue + 0.1, null, false, name );
|
||||
addTestValue( 5, chartColor, baseValue + 0.4, null, false, name );
|
||||
addTestValue( 5, chartColor, baseValue + 0.1, null, false, name );
|
||||
addTestValue( 40, chartColor, baseValue + 0.3, null, false, name );
|
||||
testTime += 1000;
|
||||
}
|
||||
|
||||
private void addTestValue( int timeDelta, Color chartColor, double value, Color dotColor, boolean dotOnly, String name ) {
|
||||
testTime += timeDelta;
|
||||
|
||||
List<Data> chartData = asyncColor2dataMap.computeIfAbsent( chartColor, k -> new ArrayList<>() );
|
||||
Data data = new Data( value, testIValue++, chartColor, dotColor, dotOnly, testTime, name, new Exception() );
|
||||
if( asynchron )
|
||||
chartData.add( data );
|
||||
else
|
||||
syncChartData.add( data );
|
||||
|
||||
lastUsedChartColor = chartColor;
|
||||
}
|
||||
|
||||
private int testIValue;
|
||||
private long testTime;
|
||||
|
||||
//TODO remove and use ColorFunctions.fade() when merging to main
|
||||
private static Color fade( Color color, float amount ) {
|
||||
int newAlpha = Math.round( 255 * amount );
|
||||
return new Color( (color.getRGB() & 0xffffff) | (newAlpha << 24), true );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
JFDML JFormDesigner: "8.3" encoding: "UTF-8"
|
||||
|
||||
new FormModel {
|
||||
contentType: "form/swing"
|
||||
root: new FormRoot {
|
||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||
"$layoutConstraints": "hidemode 3"
|
||||
"$columnConstraints": "[grow,fill]"
|
||||
"$rowConstraints": "[100:300,grow,fill][]"
|
||||
} ) {
|
||||
name: "this"
|
||||
add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) {
|
||||
name: "lineChartScrollPane"
|
||||
add( new FormComponent( "com.formdev.flatlaf.testing.LineChartPanel$LineChart" ) {
|
||||
name: "lineChart"
|
||||
} )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 0"
|
||||
} )
|
||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||
"$layoutConstraints": "insets 0,hidemode 3,gapy 0"
|
||||
"$columnConstraints": "[fill]para[fill]"
|
||||
"$rowConstraints": "[][]"
|
||||
} ) {
|
||||
name: "legendPanel"
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": true
|
||||
}
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "xLabel"
|
||||
"text": "X: time ({0}ms per line)"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "legend1Label"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "yLabel"
|
||||
"text": "Y: "
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": true
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1,gapx 0 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "yValueLabel"
|
||||
"text": "value"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1,gapx 0 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "yLabel2"
|
||||
"text": " (10% per line)"
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": true
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1,gapx 0 0"
|
||||
} )
|
||||
add( new FormComponent( "com.jformdesigner.designer.wrapper.HSpacer" ) {
|
||||
name: "hSpacer1"
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": true
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1,growx"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "legend2Label"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 1"
|
||||
} )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "oneSecondWidthLabel"
|
||||
"text": "Scale X:"
|
||||
"displayedMnemonic": 65
|
||||
"labelFor": new FormReference( "oneSecondWidthSlider" )
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": true
|
||||
}
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1,alignx right,growx 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JSlider" ) {
|
||||
name: "oneSecondWidthSlider"
|
||||
"minimum": 100
|
||||
"maximum": 10000
|
||||
"snapToTicks": true
|
||||
"majorTickSpacing": 100
|
||||
"value": 500
|
||||
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "oneSecondWidthChanged", false ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1,alignx right,growx 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||
name: "updateChartDelayedCheckBox"
|
||||
"text": "Update chart delayed"
|
||||
"mnemonic": 80
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "updateChartDelayedChanged", false ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1,alignx right,growx 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JButton" ) {
|
||||
name: "clearChartButton"
|
||||
"text": "Clear Chart"
|
||||
"mnemonic": 67
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "clearChart", false ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1,alignx right,growx 0"
|
||||
} )
|
||||
}, new FormLayoutConstraints( null ) {
|
||||
"location": new java.awt.Point( 0, 0 )
|
||||
"size": new java.awt.Dimension( 880, 300 )
|
||||
} )
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user