mirror of
https://github.com/danbulant/cushy
synced 2026-07-05 03:00:43 +00:00
Easing functions as styles
This commit is contained in:
parent
1bf1b082af
commit
6f5ffd80b4
6 changed files with 189 additions and 59 deletions
137
src/animation.rs
137
src/animation.rs
|
|
@ -17,7 +17,7 @@
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use std::time::Duration;
|
//! use std::time::Duration;
|
||||||
//!
|
//!
|
||||||
//! use gooey::animation::{AnimationTarget, Spawn};
|
//! use gooey::animation::{AnimationTarget, EaseInOutElastic, Spawn};
|
||||||
//! use gooey::value::Dynamic;
|
//! use gooey::value::Dynamic;
|
||||||
//!
|
//!
|
||||||
//! let value = Dynamic::new(0);
|
//! let value = Dynamic::new(0);
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
//! value
|
//! value
|
||||||
//! .transition_to(100)
|
//! .transition_to(100)
|
||||||
//! .over(Duration::from_millis(100))
|
//! .over(Duration::from_millis(100))
|
||||||
|
//! .with_easing(EaseInOutElastic)
|
||||||
//! .launch();
|
//! .launch();
|
||||||
//!
|
//!
|
||||||
//! let mut reader = value.into_reader();
|
//! let mut reader = value.into_reader();
|
||||||
|
|
@ -37,9 +38,9 @@
|
||||||
|
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::ops::{ControlFlow, Deref};
|
use std::ops::{ControlFlow, Deref};
|
||||||
use std::sync::{Condvar, Mutex, MutexGuard, OnceLock, PoisonError};
|
use std::panic::{RefUnwindSafe, UnwindSafe};
|
||||||
|
use std::sync::{Arc, Condvar, Mutex, MutexGuard, OnceLock, PoisonError};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
|
@ -47,6 +48,7 @@ use alot::{LotId, Lots};
|
||||||
use kempt::Set;
|
use kempt::Set;
|
||||||
use kludgine::Color;
|
use kludgine::Color;
|
||||||
|
|
||||||
|
use crate::styles::Component;
|
||||||
use crate::value::Dynamic;
|
use crate::value::Dynamic;
|
||||||
|
|
||||||
static ANIMATIONS: Mutex<Animating> = Mutex::new(Animating::new());
|
static ANIMATIONS: Mutex<Animating> = Mutex::new(Animating::new());
|
||||||
|
|
@ -224,7 +226,7 @@ where
|
||||||
{
|
{
|
||||||
value: Target,
|
value: Target,
|
||||||
duration: Duration,
|
duration: Duration,
|
||||||
_easing: PhantomData<Easing>,
|
easing: Easing,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Animation<T, Linear>
|
impl<T> Animation<T, Linear>
|
||||||
|
|
@ -235,16 +237,16 @@ where
|
||||||
Self {
|
Self {
|
||||||
value,
|
value,
|
||||||
duration,
|
duration,
|
||||||
_easing: PhantomData,
|
easing: Linear,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns this animation with a different easing function.
|
/// Returns this animation with a different easing function.
|
||||||
pub fn with_easing<Easing: self::Easing>(self) -> Animation<T, Easing> {
|
pub fn with_easing<Easing: self::Easing>(self, easing: Easing) -> Animation<T, Easing> {
|
||||||
Animation {
|
Animation {
|
||||||
value: self.value,
|
value: self.value,
|
||||||
duration: self.duration,
|
duration: self.duration,
|
||||||
_easing: PhantomData,
|
easing,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -261,7 +263,7 @@ where
|
||||||
target: self.value.begin(),
|
target: self.value.begin(),
|
||||||
duration: self.duration,
|
duration: self.duration,
|
||||||
elapsed: Duration::ZERO,
|
elapsed: Duration::ZERO,
|
||||||
_easing: PhantomData,
|
easing: self.easing,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -401,7 +403,7 @@ where
|
||||||
self.target.finish();
|
self.target.finish();
|
||||||
ControlFlow::Break(remaining_elapsed)
|
ControlFlow::Break(remaining_elapsed)
|
||||||
} else {
|
} else {
|
||||||
let progress = Easing::ease(ZeroToOne::new(
|
let progress = self.easing.ease(ZeroToOne::new(
|
||||||
self.elapsed.as_secs_f32() / self.duration.as_secs_f32(),
|
self.elapsed.as_secs_f32() / self.duration.as_secs_f32(),
|
||||||
));
|
));
|
||||||
self.target.update(progress);
|
self.target.update(progress);
|
||||||
|
|
@ -421,7 +423,7 @@ pub struct RunningAnimation<T, Easing> {
|
||||||
target: T,
|
target: T,
|
||||||
duration: Duration,
|
duration: Duration,
|
||||||
elapsed: Duration,
|
elapsed: Duration,
|
||||||
_easing: PhantomData<Easing>,
|
easing: Easing,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A handle to a spawned animation. When dropped, the associated animation will
|
/// A handle to a spawned animation. When dropped, the associated animation will
|
||||||
|
|
@ -653,6 +655,20 @@ impl_lerp_for_int!(i64, u64, f32);
|
||||||
impl_lerp_for_int!(i128, u128, f64);
|
impl_lerp_for_int!(i128, u128, f64);
|
||||||
impl_lerp_for_int!(isize, usize, f64);
|
impl_lerp_for_int!(isize, usize, f64);
|
||||||
|
|
||||||
|
impl LinearInterpolate for f32 {
|
||||||
|
fn lerp(&self, target: &Self, percent: f32) -> Self {
|
||||||
|
let delta = *target - *self;
|
||||||
|
*self + delta * percent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LinearInterpolate for f64 {
|
||||||
|
fn lerp(&self, target: &Self, percent: f32) -> Self {
|
||||||
|
let delta = *target - *self;
|
||||||
|
*self + delta * f64::from(percent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn integer_lerps() {
|
fn integer_lerps() {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
|
|
@ -768,21 +784,57 @@ impl LinearInterpolate for ZeroToOne {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs easing for value interpolation.
|
/// An easing function for customizing animations.
|
||||||
pub trait Easing: Send + Sync + 'static {
|
#[derive(Debug, Clone)]
|
||||||
/// Eases a value ranging between zero and one. The resulting value does not
|
pub enum EasingFunction {
|
||||||
/// need to be bounded between zero and one.
|
/// A function pointer to use as an easing function.
|
||||||
fn ease(progress: ZeroToOne) -> f32;
|
Fn(fn(ZeroToOne) -> f32),
|
||||||
|
/// A custom easing implementation.
|
||||||
|
Custom(Arc<dyn Easing>),
|
||||||
}
|
}
|
||||||
|
|
||||||
// /// An [`Easing`] function that produces a steady, linear transition.
|
impl Easing for EasingFunction {
|
||||||
// pub enum Linear {}
|
fn ease(&self, progress: ZeroToOne) -> f32 {
|
||||||
|
match self {
|
||||||
|
EasingFunction::Fn(func) => func(progress),
|
||||||
|
EasingFunction::Custom(func) => func.ease(progress),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// impl Easing for Linear {
|
impl From<EasingFunction> for Component {
|
||||||
// fn ease(progress: ZeroToOne) -> f32 {
|
fn from(value: EasingFunction) -> Self {
|
||||||
// *progress
|
Component::Easing(value)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Component> for EasingFunction {
|
||||||
|
type Error = Component;
|
||||||
|
|
||||||
|
fn try_from(value: Component) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
Component::Easing(easing) => Ok(easing),
|
||||||
|
other => Err(other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs easing for value interpolation.
|
||||||
|
pub trait Easing: Debug + Send + Sync + RefUnwindSafe + UnwindSafe + 'static {
|
||||||
|
/// Eases a value ranging between zero and one. The resulting value does not
|
||||||
|
/// need to be bounded between zero and one.
|
||||||
|
fn ease(&self, progress: ZeroToOne) -> f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An [`Easing`] function that produces a steady, linear transition.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Linear;
|
||||||
|
|
||||||
|
impl Easing for Linear {
|
||||||
|
fn ease(&self, progress: ZeroToOne) -> f32 {
|
||||||
|
*progress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Think this macro has a long name? This is to ensure rustfmt wraps the closure
|
// Think this macro has a long name? This is to ensure rustfmt wraps the closure
|
||||||
// onto its own line. Seriously, try shortening the name and see how it changes
|
// onto its own line. Seriously, try shortening the name and see how it changes
|
||||||
|
|
@ -793,14 +845,31 @@ macro_rules! declare_easing_function_implementation {
|
||||||
/// An [`Easing`] function that eases
|
/// An [`Easing`] function that eases
|
||||||
#[doc = $description]
|
#[doc = $description]
|
||||||
#[doc = concat!(".\n\nSee <https://easings.net/#", stringify!($anchor_name), "> for a visualization and more information.")]
|
#[doc = concat!(".\n\nSee <https://easings.net/#", stringify!($anchor_name), "> for a visualization and more information.")]
|
||||||
pub enum $name {}
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct $name;
|
||||||
|
|
||||||
impl Easing for $name {
|
impl $name {
|
||||||
fn ease(progress: ZeroToOne) -> f32 {
|
/// Eases
|
||||||
|
#[doc = $description]
|
||||||
|
#[doc = concat!(".\n\nSee <https://easings.net/#", stringify!($anchor_name), "> for a visualization and more information.")]
|
||||||
|
#[must_use]
|
||||||
|
pub fn ease(progress: ZeroToOne) -> f32 {
|
||||||
let closure = force_closure_type($closure);
|
let closure = force_closure_type($closure);
|
||||||
closure(*progress)
|
closure(*progress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Easing for $name {
|
||||||
|
fn ease(&self, progress: ZeroToOne) -> f32 {
|
||||||
|
Self::ease(progress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<$name> for EasingFunction {
|
||||||
|
fn from(_function: $name) -> Self {
|
||||||
|
Self::Fn($name::ease)
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -809,22 +878,6 @@ fn force_closure_type(f: impl Fn(f32) -> f32) -> impl Fn(f32) -> f32 {
|
||||||
f
|
f
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An [`Easing`] function that produces a steady, linear transition.
|
|
||||||
pub enum Linear {}
|
|
||||||
|
|
||||||
impl Easing for Linear {
|
|
||||||
fn ease(progress: ZeroToOne) -> f32 {
|
|
||||||
*progress
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare_easing_function_implementation!(
|
|
||||||
EaseInSine,
|
|
||||||
easeInSine,
|
|
||||||
"in using a sine wave",
|
|
||||||
|percent| 1. - (percent * PI).cos() / 2.
|
|
||||||
);
|
|
||||||
|
|
||||||
declare_easing_function_implementation!(
|
declare_easing_function_implementation!(
|
||||||
EaseOutSine,
|
EaseOutSine,
|
||||||
easeOutSine,
|
easeOutSine,
|
||||||
|
|
@ -1088,7 +1141,7 @@ declare_easing_function_implementation!(
|
||||||
EaseInBounce,
|
EaseInBounce,
|
||||||
easeInBounce,
|
easeInBounce,
|
||||||
"in using a curve that bounces progressively closer as it progresses",
|
"in using a curve that bounces progressively closer as it progresses",
|
||||||
|percent| 1. - EaseOutBounce::ease(ZeroToOne(percent))
|
|percent| 1. - EaseOutBounce.ease(ZeroToOne(percent))
|
||||||
);
|
);
|
||||||
|
|
||||||
declare_easing_function_implementation!(
|
declare_easing_function_implementation!(
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use std::collections::{hash_map, HashMap};
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::animation::{EasingFunction, ZeroToOne};
|
||||||
use crate::names::Name;
|
use crate::names::Name;
|
||||||
use crate::utils::Lazy;
|
use crate::utils::Lazy;
|
||||||
use crate::value::{IntoValue, Value};
|
use crate::value::{IntoValue, Value};
|
||||||
|
|
@ -145,9 +146,11 @@ pub enum Component {
|
||||||
/// A single-dimension measurement.
|
/// A single-dimension measurement.
|
||||||
Dimension(Dimension),
|
Dimension(Dimension),
|
||||||
/// A percentage between 0.0 and 1.0.
|
/// A percentage between 0.0 and 1.0.
|
||||||
Percent(f32),
|
Percent(ZeroToOne),
|
||||||
/// A custom component type.
|
/// A custom component type.
|
||||||
Custom(CustomComponent),
|
Custom(CustomComponent),
|
||||||
|
/// An easing function for animations.
|
||||||
|
Easing(EasingFunction),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Color> for Component {
|
impl From<Color> for Component {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use std::borrow::Cow;
|
||||||
use kludgine::figures::units::Lp;
|
use kludgine::figures::units::Lp;
|
||||||
use kludgine::Color;
|
use kludgine::Color;
|
||||||
|
|
||||||
|
use crate::animation::{EaseInQuadradic, EaseOutQuadradic, EasingFunction};
|
||||||
use crate::styles::{ComponentDefinition, ComponentName, Dimension, Global, NamedComponent};
|
use crate::styles::{ComponentDefinition, ComponentName, Dimension, Global, NamedComponent};
|
||||||
|
|
||||||
/// The [`Dimension`] to use as the size to render text.
|
/// The [`Dimension`] to use as the size to render text.
|
||||||
|
|
@ -97,3 +98,64 @@ impl ComponentDefinition for IntrinsicPadding {
|
||||||
Dimension::Lp(Lp::points(5))
|
Dimension::Lp(Lp::points(5))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The [`EasingFunction`] to apply to animations that have no inherent
|
||||||
|
/// directionality.
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
||||||
|
pub struct Easing;
|
||||||
|
|
||||||
|
impl NamedComponent for Easing {
|
||||||
|
fn name(&self) -> Cow<'_, ComponentName> {
|
||||||
|
Cow::Owned(ComponentName::named::<Global>("easing"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentDefinition for Easing {
|
||||||
|
type ComponentType = EasingFunction;
|
||||||
|
|
||||||
|
fn default_value(&self) -> Self::ComponentType {
|
||||||
|
EasingFunction::from(EaseInQuadradic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [`EasingFunction`] to apply to animations that transition a value from
|
||||||
|
/// "nothing" to "something". For example, if an widget is animating a color's
|
||||||
|
/// alpha channel towards opaqueness, it would query for this style component.
|
||||||
|
/// Otherwise, it would use [`EasingOut`].
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
||||||
|
pub struct EasingIn;
|
||||||
|
|
||||||
|
impl NamedComponent for EasingIn {
|
||||||
|
fn name(&self) -> Cow<'_, ComponentName> {
|
||||||
|
Cow::Owned(ComponentName::named::<Global>("easing_in"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentDefinition for EasingIn {
|
||||||
|
type ComponentType = EasingFunction;
|
||||||
|
|
||||||
|
fn default_value(&self) -> Self::ComponentType {
|
||||||
|
EasingFunction::from(EaseInQuadradic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [`EasingFunction`] to apply to animations that transition a value from
|
||||||
|
/// "something" to "nothing". For example, if an widget is animating a color's
|
||||||
|
/// alpha channel towards transparency, it would query for this style component.
|
||||||
|
/// Otherwise, it would use [`EasingIn`].
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
||||||
|
pub struct EasingOut;
|
||||||
|
|
||||||
|
impl NamedComponent for EasingOut {
|
||||||
|
fn name(&self) -> Cow<'_, ComponentName> {
|
||||||
|
Cow::Owned(ComponentName::named::<Global>("easing_out"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentDefinition for EasingOut {
|
||||||
|
type ComponentType = EasingFunction;
|
||||||
|
|
||||||
|
fn default_value(&self) -> Self::ComponentType {
|
||||||
|
EasingFunction::from(EaseOutQuadradic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ impl Align {
|
||||||
let (left, right, width) = horizontal.measure(available_space.width, content_size.width);
|
let (left, right, width) = horizontal.measure(available_space.width, content_size.width);
|
||||||
let (top, bottom, height) = vertical.measure(available_space.height, content_size.height);
|
let (top, bottom, height) = vertical.measure(available_space.height, content_size.height);
|
||||||
|
|
||||||
dbg!(Layout {
|
Layout {
|
||||||
margin: Edges {
|
margin: Edges {
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
|
|
@ -60,7 +60,7 @@ impl Align {
|
||||||
bottom,
|
bottom,
|
||||||
},
|
},
|
||||||
content: Size::new(width, height),
|
content: Size::new(width, height),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use kludgine::Color;
|
||||||
use crate::animation::{AnimationHandle, AnimationTarget, Spawn};
|
use crate::animation::{AnimationHandle, AnimationTarget, Spawn};
|
||||||
use crate::context::{EventContext, GraphicsContext, WidgetContext};
|
use crate::context::{EventContext, GraphicsContext, WidgetContext};
|
||||||
use crate::names::Name;
|
use crate::names::Name;
|
||||||
use crate::styles::components::{HighlightColor, IntrinsicPadding, TextColor};
|
use crate::styles::components::{Easing, HighlightColor, IntrinsicPadding, TextColor};
|
||||||
use crate::styles::{ComponentDefinition, ComponentGroup, ComponentName, NamedComponent};
|
use crate::styles::{ComponentDefinition, ComponentGroup, ComponentName, NamedComponent};
|
||||||
use crate::value::{Dynamic, IntoValue, Value};
|
use crate::value::{Dynamic, IntoValue, Value};
|
||||||
use crate::widget::{Callback, EventHandling, Widget, HANDLED, IGNORED};
|
use crate::widget::{Callback, EventHandling, Widget, HANDLED, IGNORED};
|
||||||
|
|
@ -66,6 +66,7 @@ impl Button {
|
||||||
&ButtonActiveBackground,
|
&ButtonActiveBackground,
|
||||||
&ButtonBackground,
|
&ButtonBackground,
|
||||||
&ButtonHoverBackground,
|
&ButtonHoverBackground,
|
||||||
|
&Easing,
|
||||||
]);
|
]);
|
||||||
let background_color = if context.active() {
|
let background_color = if context.active() {
|
||||||
styles.get_or_default(&ButtonActiveBackground)
|
styles.get_or_default(&ButtonActiveBackground)
|
||||||
|
|
@ -80,6 +81,7 @@ impl Button {
|
||||||
self.background_color_animation = dynamic
|
self.background_color_animation = dynamic
|
||||||
.transition_to(background_color)
|
.transition_to(background_color)
|
||||||
.over(Duration::from_millis(150))
|
.over(Duration::from_millis(150))
|
||||||
|
.with_easing(styles.get_or_default(&Easing))
|
||||||
.spawn();
|
.spawn();
|
||||||
}
|
}
|
||||||
(true, Some(dynamic)) => {
|
(true, Some(dynamic)) => {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ use kludgine::Color;
|
||||||
|
|
||||||
use crate::animation::{AnimationHandle, AnimationTarget, IntoAnimate, Spawn, ZeroToOne};
|
use crate::animation::{AnimationHandle, AnimationTarget, IntoAnimate, Spawn, ZeroToOne};
|
||||||
use crate::context::{AsEventContext, EventContext};
|
use crate::context::{AsEventContext, EventContext};
|
||||||
|
use crate::styles::components::{EasingIn, EasingOut};
|
||||||
use crate::styles::{
|
use crate::styles::{
|
||||||
ComponentDefinition, ComponentGroup, ComponentName, Dimension, NamedComponent,
|
ComponentDefinition, ComponentGroup, ComponentName, Dimension, NamedComponent,
|
||||||
};
|
};
|
||||||
|
|
@ -95,6 +96,23 @@ impl Scroll {
|
||||||
self.scroll.set(clamped);
|
self.scroll.set(clamped);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show_scrollbars(&mut self, context: &mut EventContext<'_, '_>) {
|
||||||
|
let styles = context.query_styles(&[&EasingIn, &EasingOut]);
|
||||||
|
self.scrollbar_opacity_animation = self
|
||||||
|
.scrollbar_opacity
|
||||||
|
.transition_to(ZeroToOne::ONE)
|
||||||
|
.over(Duration::from_millis(300))
|
||||||
|
.with_easing(styles.get_or_default(&EasingIn))
|
||||||
|
.and_then(Duration::from_secs(1))
|
||||||
|
.and_then(
|
||||||
|
self.scrollbar_opacity
|
||||||
|
.transition_to(ZeroToOne::ZERO)
|
||||||
|
.over(Duration::from_millis(300))
|
||||||
|
.with_easing(styles.get_or_default(&EasingOut)),
|
||||||
|
)
|
||||||
|
.spawn();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for Scroll {
|
impl Widget for Scroll {
|
||||||
|
|
@ -102,25 +120,16 @@ impl Widget for Scroll {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hover(&mut self, _location: Point<Px>, _context: &mut EventContext<'_, '_>) {
|
fn hover(&mut self, _location: Point<Px>, context: &mut EventContext<'_, '_>) {
|
||||||
self.scrollbar_opacity_animation = self
|
self.show_scrollbars(context);
|
||||||
.scrollbar_opacity
|
|
||||||
.transition_to(ZeroToOne::ONE)
|
|
||||||
.over(Duration::from_millis(300))
|
|
||||||
.and_then(Duration::from_secs(1))
|
|
||||||
.and_then(
|
|
||||||
self.scrollbar_opacity
|
|
||||||
.transition_to(ZeroToOne::ZERO)
|
|
||||||
.over(Duration::from_millis(300)),
|
|
||||||
)
|
|
||||||
.spawn();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unhover(&mut self, _context: &mut EventContext<'_, '_>) {
|
fn unhover(&mut self, context: &mut EventContext<'_, '_>) {
|
||||||
self.scrollbar_opacity_animation = self
|
self.scrollbar_opacity_animation = self
|
||||||
.scrollbar_opacity
|
.scrollbar_opacity
|
||||||
.transition_to(ZeroToOne::ZERO)
|
.transition_to(ZeroToOne::ZERO)
|
||||||
.over(Duration::from_millis(300))
|
.over(Duration::from_millis(300))
|
||||||
|
.with_easing(context.query_style(&EasingOut))
|
||||||
.spawn();
|
.spawn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,6 +265,7 @@ impl Widget for Scroll {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.scroll.map_mut(|scroll| *scroll += amount.cast());
|
self.scroll.map_mut(|scroll| *scroll += amount.cast());
|
||||||
|
self.show_scrollbars(context);
|
||||||
context.set_needs_redraw();
|
context.set_needs_redraw();
|
||||||
|
|
||||||
// TODO make this only returned handled if we actually scrolled.
|
// TODO make this only returned handled if we actually scrolled.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue