diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b3eb6e..4a1dbeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Collapse`, `OverlayLayer`, and `Progress` all honor the theme components `EasingIn` and `EasingOut` rather than hard-coded easing functions. +- `Collapse` widgets now apply child size changes immediately rather than + animating the transition. The previous behavior caused nested collapsed + widgets to grow and shrink in an accordian-like fashion rather than animating + together. ### Added diff --git a/src/widgets/collapse.rs b/src/widgets/collapse.rs index 878b5d3..2e2e936 100644 --- a/src/widgets/collapse.rs +++ b/src/widgets/collapse.rs @@ -6,8 +6,8 @@ use figures::{Size, Zero}; use crate::animation::{AnimationHandle, AnimationTarget, Spawn}; use crate::context::LayoutContext; use crate::styles::components::{EasingIn, EasingOut}; -use crate::value::{Dynamic, IntoDynamic, Source}; -use crate::widget::{MakeWidget, WidgetRef, WrappedLayout, WrapperWidget}; +use crate::value::{Destination, Dynamic, Generation, IntoDynamic, Source}; +use crate::widget::{MakeWidget, WidgetInstance, WidgetRef, WrappedLayout, WrapperWidget}; use crate::ConstraintLimit; /// A widget that collapses/hides its contents based on a [`Dynamic`]. @@ -15,25 +15,32 @@ use crate::ConstraintLimit; pub struct Collapse { child: WidgetRef, collapse: Dynamic, + collapse_generation: Generation, size: Dynamic, collapse_animation: Option, vertical: bool, } impl Collapse { + fn new(collapse: Dynamic, child: WidgetInstance, vertical: bool) -> Self { + let collapse_generation = collapse.generation(); + Self { + collapse, + collapse_generation, + child: WidgetRef::new(child), + size: Dynamic::default(), + vertical, + collapse_animation: None, + } + } + /// Returns a widget that collapses `child` vertically based on the dynamic /// boolean value. /// /// This widget will be collapsed when the dynamic contains `true`, and /// revealed when the dynamic contains `false`. pub fn vertical(collapse_when: impl IntoDynamic, child: impl MakeWidget) -> Self { - Self { - collapse: collapse_when.into_dynamic(), - child: WidgetRef::new(child), - size: Dynamic::default(), - vertical: true, - collapse_animation: None, - } + Self::new(collapse_when.into_dynamic(), child.make_widget(), true) } /// Returns a widget that collapses `child` horizontally based on the @@ -42,23 +49,33 @@ impl Collapse { /// This widget will be collapsed when the dynamic contains `true`, and /// revealed when the dynamic contains `false`. pub fn horizontal(collapse_when: impl IntoDynamic, child: impl MakeWidget) -> Self { - Self { - collapse: collapse_when.into_dynamic(), - child: WidgetRef::new(child), - size: Dynamic::default(), - vertical: false, - collapse_animation: None, - } + Self::new(collapse_when.into_dynamic(), child.make_widget(), false) } - fn note_child_size(&mut self, size: Px, context: &mut LayoutContext<'_, '_, '_, '_>) { - let (easing, target) = if self.collapse.get_tracking_invalidate(context) { + fn note_child_size( + &mut self, + size: Px, + current_size: Px, + context: &mut LayoutContext<'_, '_, '_, '_>, + ) -> Px { + context.invalidate_when_changed(&self.collapse); + let (generation, collapse) = self.collapse.map_generational(|c| (c.generation(), *c)); + let (easing, target) = if collapse { (context.get(&EasingOut), Px::ZERO) } else { (context.get(&EasingIn), size) }; + if generation == self.collapse_generation {} match &self.collapse_animation { Some(state) if state.target == target => {} + Some(_) if generation == self.collapse_generation => { + // The resize happened from a reason other than our toggle. + // Immediately apply it. + let mut stored_size = self.size.lock(); + stored_size.prevent_notifications(); + *stored_size = target; + return target; + } _ => { // If this is our first setup, immediately give the child the // space they request. @@ -78,6 +95,8 @@ impl Collapse { }); } } + self.collapse_generation = generation; + current_size } } @@ -94,13 +113,13 @@ impl WrapperWidget for Collapse { ) -> WrappedLayout { let clip_size = self.size.get_tracking_invalidate(context); if self.vertical { - self.note_child_size(size.height, context); + let height = self.note_child_size(size.height, clip_size, context); - Size::new(size.width, clip_size) + Size::new(size.width, height) } else { - self.note_child_size(size.width, context); + let width = self.note_child_size(size.width, clip_size, context); - Size::new(clip_size, size.height) + Size::new(width, size.height) } .into() }