From e5587b564de9d207a9efba9002dd2cc7d0325d0d Mon Sep 17 00:00:00 2001 From: Nathan Cheshire Date: Fri, 10 Nov 2023 13:02:46 -0600 Subject: [PATCH] more work on component animator --- .../java/cyder/animation/AnimationUtil.java | 257 ------------------ .../cyder/animation/ComponentAnimator.java | 49 +++- .../java/cyder/ui/selection/CyderSwitch.java | 37 +-- 3 files changed, 58 insertions(+), 285 deletions(-) delete mode 100644 src/main/java/cyder/animation/AnimationUtil.java diff --git a/src/main/java/cyder/animation/AnimationUtil.java b/src/main/java/cyder/animation/AnimationUtil.java deleted file mode 100644 index e6580ac50..000000000 --- a/src/main/java/cyder/animation/AnimationUtil.java +++ /dev/null @@ -1,257 +0,0 @@ -package cyder.animation; - -import com.google.common.base.Preconditions; -import cyder.enumerations.Direction; -import cyder.exceptions.IllegalMethodException; -import cyder.strings.CyderStrings; -import cyder.threads.CyderThreadRunner; -import cyder.threads.ThreadUtil; - -import java.awt.*; -import java.time.Duration; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Utilities to animate components. - */ -public final class AnimationUtil { - /** - * Suppress default constructor. - * - * @throws IllegalMethodException if invoked - */ - private AnimationUtil() { - throw new IllegalMethodException(CyderStrings.ATTEMPTED_INSTANTIATION); - } - - public static final class ComponentAnimator { - /** - * The default animation delay. - */ - private static final Duration DEFAULT_DELAY = Duration.ofMillis(8); - - /** - * The default animation increment. - */ - private static final int DEFAULT_INCREMENT = 4; - - /** - * Whether this component is currently animating. - */ - private final AtomicBoolean isAnimating = new AtomicBoolean(false); - - /** - * Whether {@link #stopAnimation()} has been invoked and the animation is being stopped. - */ - private final AtomicBoolean stoppingAnimation = new AtomicBoolean(false); - - /** - * The direction of animation. - */ - private final Direction animationDirection; - - /** - * The component to animate. - */ - private final Component animationComponent; - - /** - * The animation starting position. - */ - private final int animationStart; - - /** - * The animation ending position. - */ - private final int animationEnd; - - /** - * The delay between animation frames. - */ - private Duration animationDelay = DEFAULT_DELAY; - - /** - * The animation increment amount. - */ - private int animationIncrement = DEFAULT_INCREMENT; - - /** - * Constructs a new builder. - * - * @param animationDirection the direction to animate the component towards - * @param animationComponent the component to animate - * @param animationStart the starting value of the animation - * @param animationEnd the ending value of the animation - * @throws NullPointerException if the provided direction or component are null - * @throws IllegalArgumentException if the start is less than or equal to the end - */ - public ComponentAnimator(Direction animationDirection, Component animationComponent, - int animationStart, int animationEnd) { - Preconditions.checkNotNull(animationDirection); - Preconditions.checkNotNull(animationComponent); - Preconditions.checkArgument(animationStart < animationEnd); - - this.animationDirection = animationDirection; - this.animationComponent = animationComponent; - this.animationStart = animationStart; - this.animationEnd = animationEnd; - } - - /** - * Sets the delay between animation frames. - * - * @param animationDelay the delay between animation frames - * @return this builder - * @throws NullPointerException if the provided delay is null - * @throws IllegalArgumentException if the provided delay is negative - */ - public ComponentAnimator setAnimationDelay(Duration animationDelay) { - Preconditions.checkNotNull(animationDelay); - Preconditions.checkArgument(!animationDelay.isNegative()); - this.animationDelay = animationDelay; - return this; - } - - /** - * Sets the amount by which to increment the animation component by each animation frame. - * - * @param animationIncrement the amount to increment the animation by each frame - * @return this builder - * @throws IllegalArgumentException if the provided increment is less than or equal to zero - */ - public ComponentAnimator setAnimationIncrement(int animationIncrement) { - Preconditions.checkArgument(animationIncrement > 0); - this.animationIncrement = animationIncrement; - return this; - } - - /** - * Returns the direction to animate the component towards. - * - * @return the direction to animate the component towards - */ - public Direction getAnimationDirection() { - return animationDirection; - } - - /** - * Returns the component to animate. - * - * @return the component to animate - */ - public Component getAnimationComponent() { - return animationComponent; - } - - /** - * Returns the starting value for the animation. - * - * @return the starting value for the animation - */ - public int getAnimationStart() { - return animationStart; - } - - /** - * Returns the ending value for the animation. - * - * @return the ending value for the animation - */ - public int getAnimationEnd() { - return animationEnd; - } - - /** - * Returns the delay between animation frames. - * - * @return the delay between animation frames - */ - public Duration getAnimationDelay() { - return animationDelay; - } - - /** - * Returns the increment between animation frames. - * - * @return the increment between animation frames - */ - public int getAnimationIncrement() { - return animationIncrement; - } - - /** - * Returns whether this component is animating. - * - * @return whether this component is animating - */ - public boolean isAnimating() { - return isAnimating.get(); - } - - /** - * Animates this component. - * - * @throws IllegalStateException if this component is already animating - */ - public synchronized void animate() { - Preconditions.checkArgument(!isAnimating.get()); - Preconditions.checkArgument(!stoppingAnimation.get()); - - isAnimating.set(true); - - CyderThreadRunner.submit(() -> { - switch (animationDirection) { - case LEFT -> { - int y = animationComponent.getY(); - animationComponent.setLocation(animationStart, y); - for (int i = animationStart ; i >= animationEnd ; i -= animationIncrement) { - if (stoppingAnimation.get()) break; - animationComponent.setLocation(i, y); - ThreadUtil.sleep(animationDelay); - } - animationComponent.setLocation(animationEnd, y); - } - case RIGHT -> { - int y = animationComponent.getY(); - animationComponent.setLocation(animationStart, y); - for (int i = animationStart ; i <= animationEnd ; i += animationIncrement) { - if (stoppingAnimation.get()) break; - animationComponent.setLocation(i, y); - ThreadUtil.sleep(animationDelay); - } - animationComponent.setLocation(animationEnd, y); - } - case TOP -> { - int x = animationComponent.getX(); - animationComponent.setLocation(x, animationStart); - for (int i = animationStart ; i >= animationEnd ; i -= animationIncrement) { - if (stoppingAnimation.get()) break; - animationComponent.setLocation(x, i); - ThreadUtil.sleep(animationDelay); - } - animationComponent.setLocation(x, animationEnd); - } - case BOTTOM -> { - int x = animationComponent.getX(); - animationComponent.setLocation(x, animationStart); - for (int i = animationStart ; i <= animationEnd ; i += animationIncrement) { - if (stoppingAnimation.get()) break; - animationComponent.setLocation(x, i); - ThreadUtil.sleep(animationDelay); - } - animationComponent.setLocation(x, animationEnd); - } - case default -> - throw new IllegalStateException("Invalid animation direction: " + animationDirection); - } - - isAnimating.set(false); - stoppingAnimation.set(false); - }, "todo"); - } - - public void stopAnimation() { - stoppingAnimation.set(true); - } - } -} diff --git a/src/main/java/cyder/animation/ComponentAnimator.java b/src/main/java/cyder/animation/ComponentAnimator.java index e08a56c67..1a11bb056 100644 --- a/src/main/java/cyder/animation/ComponentAnimator.java +++ b/src/main/java/cyder/animation/ComponentAnimator.java @@ -12,6 +12,8 @@ /** * A wrapper for a {@link Component} to perform cardinal direction animations on it. + * This component uses a slight modification on a builder pattern; accessor methods + * return {@code this} instance instead of {@link Void}. */ public final class ComponentAnimator { /** @@ -182,14 +184,17 @@ public boolean isAnimating() { /** * Animates this component. * + * @return this animator * @throws IllegalStateException if this component is already animating */ - public synchronized void animate() { + @CanIgnoreReturnValue + public synchronized ComponentAnimator animate() { Preconditions.checkArgument(!isAnimating.get()); Preconditions.checkArgument(!stoppingAnimation.get()); isAnimating.set(true); + String animationThreadName = getAnimationThreadName(); CyderThreadRunner.submit(() -> { switch (animationDirection) { case LEFT -> { @@ -237,7 +242,9 @@ public synchronized void animate() { isAnimating.set(false); stoppingAnimation.set(false); - }, "todo custom thread name"); + }, animationThreadName); + + return this; } /** @@ -297,13 +304,35 @@ public boolean equals(Object o) { } ComponentAnimator other = (ComponentAnimator) o; - return isAnimating.equals(other.isAnimating) && - stoppingAnimation.equals(other.stoppingAnimation) && - animationDirection.equals(other.animationDirection) && - animationComponent.equals(other.animationComponent) && - animationStart == other.animationStart && - animationEnd == other.animationEnd && - animationDelay.equals(other.animationDelay) && - animationIncrement == other.animationIncrement; + return isAnimating.equals(other.isAnimating) + && stoppingAnimation.equals(other.stoppingAnimation) + && animationDirection.equals(other.animationDirection) + && animationComponent.equals(other.animationComponent) + && animationStart == other.animationStart + && animationEnd == other.animationEnd + && animationDelay.equals(other.animationDelay) + && animationIncrement == other.animationIncrement; + } + + /** + * Returns the name for the thread which animates this component. + * + * @return the name for the thread which animates this component + */ + private String getAnimationThreadName() { + int currentAnimationValue = switch (animationDirection) { + case LEFT, RIGHT -> animationComponent.getX(); + case TOP, BOTTOM -> animationComponent.getY(); + default -> throw new IllegalStateException("Invalid direction: " + animationDirection); + }; + + String currentAnimationValueRepresentation = isAnimating.get() + ? "NOT ANIMATING" : String.valueOf(currentAnimationValue); + + return "ComponentAnimator{direction=" + animationDirection + + ", animationStart=" + animationStart + + ", currentAnimationValue=" + currentAnimationValueRepresentation + + ", animationEnd=" + animationEnd + + "}"; } } diff --git a/src/main/java/cyder/ui/selection/CyderSwitch.java b/src/main/java/cyder/ui/selection/CyderSwitch.java index 006446da8..e3db480ca 100644 --- a/src/main/java/cyder/ui/selection/CyderSwitch.java +++ b/src/main/java/cyder/ui/selection/CyderSwitch.java @@ -2,16 +2,18 @@ import com.google.common.base.Preconditions; import com.google.common.collect.Range; -import cyder.animation.AnimationUtil; +import cyder.animation.ComponentAnimator; import cyder.annotations.ForReadability; import cyder.constants.CyderColors; import cyder.constants.CyderFonts; +import cyder.enumerations.Direction; import cyder.ui.UiUtil; import cyder.ui.button.CyderButton; import javax.swing.*; import javax.swing.border.LineBorder; import java.awt.*; +import java.time.Duration; /** * An animated binary switch with smooth transition animations. @@ -217,28 +219,26 @@ public void setState(CyderSwitchState state) { refreshButtonText(); - switch (state) { - case ON -> { - if (shouldAnimate) { - AnimationUtil.componentRight(switchButton.getX(), - width - switchButton.getWidth() - buttonXPadding, - animationDelay, animationIncrement, switchButton); + if (shouldAnimate) { + switch (state) { + case ON -> { + int animationStart = switchButton.getX(); + int animationEnd = width - switchButton.getWidth() - buttonXPadding; + new ComponentAnimator(Direction.RIGHT, this, + animationStart, animationEnd) + .setAnimationDelay(Duration.ofMillis(animationDelay)) + .setAnimationIncrement(animationIncrement) + .animate(); } - } - case OFF -> { - if (shouldAnimate) { + case OFF -> { AnimationUtil.componentLeft(switchButton.getX(), buttonXPadding, animationDelay, animationIncrement, switchButton); } - } - case INDETERMINATE -> { - if (switchButton.getX() > buttonXPadding) { - if (shouldAnimate) { + case INDETERMINATE -> { + if (switchButton.getX() > buttonXPadding) { AnimationUtil.componentLeft(switchButton.getX(), width / 2 - switchButton.getWidth() / 2, animationDelay, animationIncrement, switchButton); - } - } else { - if (shouldAnimate) { + } else { AnimationUtil.componentRight(buttonXPadding, width / 2 - switchButton.getWidth() / 2, animationDelay, animationIncrement, switchButton); } @@ -477,6 +477,7 @@ public String toString() { + ", onText=" + onText + ", indeterminateText=" + indeterminateText + ", offText=" + offText - + ", animationIncrement=" + animationIncrement + "}"; + + ", animationIncrement=" + animationIncrement + + "}"; } }