Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ strict-assertions = ["iced_renderer/strict-assertions"]
unconditional-rendering = ["iced_winit/unconditional-rendering"]
# Enables support for the `sipper` library
sipper = ["iced_runtime/sipper"]
# Enables widget animations
animations = ["iced_widget/animations"]

[dependencies]
iced_debug.workspace = true
Expand Down
3 changes: 3 additions & 0 deletions examples/scrollable/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ publish = false
[dependencies]
iced.workspace = true
iced.features = ["debug"]

[features]
animations = ["iced/animations"]
1 change: 1 addition & 0 deletions widget/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ wgpu = ["iced_renderer/wgpu"]
markdown = ["dep:pulldown-cmark", "dep:url"]
highlighter = ["dep:iced_highlighter"]
advanced = []
animations = []
crisp = []

[dependencies]
Expand Down
77 changes: 63 additions & 14 deletions widget/src/radio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,16 @@
//! column![a, b, c, all].into()
//! }
//! ```
use std::time::Duration;

use crate::core::alignment;
use crate::core::animation::{Animation, Easing};
use crate::core::border::{self, Border};
use crate::core::layout;
use crate::core::mouse;
use crate::core::renderer;
use crate::core::text;
use crate::core::time::Instant;
use crate::core::touch;
use crate::core::widget;
use crate::core::widget::tree::{self, Tree};
Expand Down Expand Up @@ -267,6 +271,27 @@ where
}
}

struct State<Paragraph>
where
Paragraph: text::Paragraph,
{
/// The last update instant - used for animations.
pub now: Instant,
/// Animation scaling the dot in and out.
pub scale_in: Animation<bool>,
pub text_state: widget::text::State<Paragraph>,
}

impl<Paragraph> State<Paragraph>
where
Paragraph: text::Paragraph,
{
/// Whether there is an active animation.
fn is_animating(&self) -> bool {
self.scale_in.is_animating(self.now)
}
}

impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for Radio<'_, Message, Theme, Renderer>
where
Expand All @@ -275,11 +300,28 @@ where
Renderer: text::Renderer,
{
fn tag(&self) -> tree::Tag {
tree::Tag::of::<widget::text::State<Renderer::Paragraph>>()
tree::Tag::of::<State<Renderer::Paragraph>>()
}

fn state(&self) -> tree::State {
tree::State::new(widget::text::State::<Renderer::Paragraph>::default())
tree::State::new(State::<Renderer::Paragraph> {
now: Instant::now(),
scale_in: Animation::new(self.is_selected)
.easing(Easing::EaseInOut)
.duration(if cfg!(feature = "animations") {
Duration::from_millis(200)
} else {
Duration::ZERO
}),
text_state: widget::text::State::default(),
})
}

fn diff(&self, tree: &mut Tree) {
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
if self.is_selected != state.scale_in.value() {
state.scale_in.go_mut(self.is_selected, Instant::now());
}
}

fn size(&self) -> Size<Length> {
Expand All @@ -300,12 +342,11 @@ where
self.spacing,
|_| layout::Node::new(Size::new(self.size, self.size)),
|limits| {
let state = tree
.state
.downcast_mut::<widget::text::State<Renderer::Paragraph>>();
let state =
tree.state.downcast_mut::<State<Renderer::Paragraph>>();

widget::text::layout(
state,
&mut state.text_state,
renderer,
limits,
&self.label,
Expand All @@ -327,7 +368,7 @@ where

fn update(
&mut self,
_state: &mut Tree,
tree: &mut Tree,
event: &Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
Expand Down Expand Up @@ -358,8 +399,13 @@ where
}
};

if let Event::Window(window::Event::RedrawRequested(_now)) = event {
if let Event::Window(window::Event::RedrawRequested(now)) = event {
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
state.now = *now;
self.last_status = Some(current_status);
if state.is_animating() {
shell.request_redraw();
}
} else if self
.last_status
.is_some_and(|last_status| last_status != current_status)
Expand Down Expand Up @@ -422,7 +468,11 @@ where
style.background,
);

if self.is_selected {
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
if self.is_selected || state.is_animating() {
let dot_size =
state.scale_in.interpolate(0.0, dot_size, state.now);
let alpha = state.scale_in.interpolate(0.0, 1.0, state.now);
renderer.fill_quad(
renderer::Quad {
bounds: Rectangle {
Expand All @@ -431,24 +481,23 @@ where
width: bounds.width - dot_size,
height: bounds.height - dot_size,
},
border: border::rounded(dot_size / 2.0),
border: border::rounded(size / 2.0),
..renderer::Quad::default()
},
style.dot_color,
style.dot_color.scale_alpha(alpha),
);
}
}

{
let label_layout = children.next().unwrap();
let state: &widget::text::State<Renderer::Paragraph> =
tree.state.downcast_ref();
let state: &State<Renderer::Paragraph> = tree.state.downcast_ref();

crate::text::draw(
renderer,
defaults,
label_layout.bounds(),
state.raw(),
state.text_state.raw(),
crate::text::Style {
color: style.text_color,
},
Expand Down
Loading