Skip to content

Commit 4f39272

Browse files
committed
Fix toggler widget & generall toggler improvements
1 parent 36610c7 commit 4f39272

File tree

8 files changed

+179
-138
lines changed

8 files changed

+179
-138
lines changed

src/keyframes.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
pub mod toggler;
2-
31
mod button;
42
mod column;
53
mod container;
@@ -8,6 +6,7 @@ mod row;
86
mod space;
97
mod style_button;
108
mod style_container;
9+
mod toggler;
1110

1211
#[cfg(feature = "libcosmic")]
1312
use cosmic::iced_core::{widget, Length};
@@ -19,7 +18,9 @@ pub use column::Column;
1918
pub use container::Container;
2019
pub use helpers::id;
2120
pub use helpers::lazy;
22-
pub use helpers::{button, column, container, row, space, style_button, style_container, chain};
21+
pub use helpers::{
22+
button, chain, column, container, row, space, style_button, style_container, toggler,
23+
};
2324
pub use row::Row;
2425
pub use space::Space;
2526
pub use style_button::StyleButton;

src/keyframes/helpers.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::keyframes::{Button, Column, Container, Row, Space, StyleButton, StyleContainer};
1+
use crate::keyframes::{
2+
Button, Column, Container, Row, Space, StyleButton, StyleContainer, Toggler,
3+
};
24
use crate::MovementType;
35

46
/// Create a button keyframe.
@@ -31,6 +33,12 @@ pub fn space(at: impl Into<MovementType>) -> Space {
3133
Space::new(at)
3234
}
3335

36+
/// Create a toggler keyframe.
37+
/// Needs to be added into a chain. See [`crate::chain!`] macro.
38+
pub fn toggler(at: impl Into<MovementType>) -> Toggler {
39+
Toggler::new(at)
40+
}
41+
3442
/// Create a style_button keyframe.
3543
/// Needs to be added into a chain. See [`crate::chain!`] macro.
3644
pub fn style_button(at: impl Into<MovementType>) -> StyleButton {
@@ -45,7 +53,9 @@ pub fn style_container(at: impl Into<MovementType>) -> StyleContainer {
4553

4654
/// A slightly different import to clean up makeing lazy keyframes.
4755
pub mod lazy {
48-
use crate::keyframes::{Button, Column, Container, Row, Space, StyleButton, StyleContainer};
56+
use crate::keyframes::{
57+
Button, Column, Container, Row, Space, StyleButton, StyleContainer, Toggler,
58+
};
4959
use crate::MovementType;
5060

5161
/// Create a lazy button keyframe.
@@ -78,6 +88,12 @@ pub mod lazy {
7888
Space::lazy(at)
7989
}
8090

91+
/// Create a lazy toggler keyframe.
92+
/// Needs to be added into a chain. See [`crate::chain!`] macro.
93+
pub fn toggler(at: impl Into<MovementType>) -> Toggler {
94+
Toggler::lazy(at)
95+
}
96+
8197
/// Create a lazy style_button keyframe.
8298
/// Needs to be added into a chain. See [`crate::chain!`] macro.
8399
pub fn style_button(at: impl Into<MovementType>) -> StyleButton {
@@ -103,5 +119,5 @@ pub mod id {
103119
/// Direct access to `Chain`s for widget that may return an animation
104120
/// in a message.
105121
pub mod chain {
106-
pub use crate::keyframes::toggler::Chain as Toggler;
122+
pub use crate::keyframes::toggler::Chain as Toggler;
107123
}

src/keyframes/toggler.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ use iced_native::{text, widget, widget::Id as IcedId, Renderer as IcedRenderer};
88

99
use crate::keyframes::Repeat;
1010
use crate::timeline::Frame;
11-
use crate::{Ease, Linear, MovementType};
11+
use crate::{chain, lazy::toggler as lazy, toggler, Duration, Ease, Linear, MovementType};
1212

1313
/// A Toggler's animation Id. Used for linking animation built in `update()` with widget output in `view()`
1414
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1515
pub struct Id(IcedId);
16+
const ANIM_DURATION: f32 = 100.;
1617

1718
impl Id {
1819
/// Creates a custom [`Id`].
@@ -36,6 +37,22 @@ impl Id {
3637
pub fn into_chain_with_children(self, children: Vec<Toggler>) -> Chain {
3738
Chain::with_children(self, children)
3839
}
40+
41+
/// Used by [`crate::anim!`] macro
42+
pub fn as_widget<'a, Message, Renderer, F>(
43+
self,
44+
timeline: &crate::Timeline,
45+
label: impl Into<Option<String>>,
46+
is_toggled: bool,
47+
f: F,
48+
) -> crate::widget::Toggler<'a, Message, Renderer>
49+
where
50+
Renderer: IcedRenderer + text::Renderer,
51+
Renderer::Theme: widget::toggler::StyleSheet,
52+
F: 'a + Fn(Chain, bool) -> Message,
53+
{
54+
Toggler::as_widget(self, timeline, label, is_toggled, f)
55+
}
3956
}
4057

4158
impl From<Id> for IcedId {
@@ -97,6 +114,26 @@ impl Chain {
97114
self.repeat = Repeat::Never;
98115
self
99116
}
117+
118+
/// Returns the default animation for animating the toggler to "on"
119+
pub fn on(id: Id, anim_multiplier: f32) -> Self {
120+
let duration = (ANIM_DURATION * anim_multiplier.round()) as u64;
121+
chain!(
122+
id,
123+
lazy(Duration::ZERO),
124+
toggler(Duration::from_millis(duration)).percent(1.0),
125+
)
126+
}
127+
128+
/// Returns the default animation for animating the toggler to "off"
129+
pub fn off(id: Id, anim_multiplier: f32) -> Self {
130+
let duration = (ANIM_DURATION * anim_multiplier.round()) as u64;
131+
chain!(
132+
id,
133+
lazy(Duration::ZERO),
134+
toggler(Duration::from_millis(duration)).percent(0.0),
135+
)
136+
}
100137
}
101138

102139
impl From<Chain> for crate::timeline::Chain {
@@ -115,6 +152,7 @@ impl From<Chain> for crate::timeline::Chain {
115152

116153
#[must_use = "Keyframes are intended to be used in an animation chain."]
117154
#[derive(Debug, Clone, Copy)]
155+
///
118156
pub struct Toggler {
119157
at: MovementType,
120158
ease: Ease,

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ pub mod widget;
138138
mod keyframes;
139139

140140
pub use crate::keyframes::{
141-
button, column, container, id, lazy, row, space, style_button, style_container, chain,
141+
button, chain, column, container, id, lazy, row, space, style_button, style_container, toggler,
142142
};
143143
pub use crate::timeline::{Chain, Timeline};
144144

src/widget/container.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,10 @@ where
127127
self
128128
}
129129

130-
/// Sets the animatable style variant of this [`Container`].
130+
/// Set the appearance this [`Container`] to a blend of two styles.
131+
///
132+
/// Percent is a f32 of 0.0 -> 1.0.
133+
/// Where 0 is 100% style1 and 1 is 100% style2.
131134
pub fn blend_style(
132135
mut self,
133136
style1: <Renderer::Theme as StyleSheet>::Style,

src/widget/cosmic_button.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
//! A [`Button`] has some local [`State`].
44
use cosmic::iced_runtime::core::widget::Id;
55
use cosmic::iced_runtime::{keyboard, Command};
6-
use std::borrow::Cow;
76

87
use crate::widget::StyleType;
98
use cosmic::iced_core::event::{self, Event};
@@ -458,7 +457,7 @@ impl State {
458457
/// Processes the given [`Event`] and updates the [`State`] of a [`Button`]
459458
/// accordingly.
460459
pub fn update<'a, Message: Clone>(
461-
id: Id,
460+
_id: Id,
462461
event: Event,
463462
layout: Layout<'_>,
464463
cursor_position: Point,

src/widget/cosmic_toggler.rs

Lines changed: 46 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,15 @@ use cosmic::iced_core::layout;
55
use cosmic::iced_core::mouse;
66
use cosmic::iced_core::renderer;
77
use cosmic::iced_core::text;
8-
use cosmic::iced_core::time::Duration;
98
use cosmic::iced_core::widget::Tree;
109
use cosmic::iced_core::{
1110
Alignment, Clipboard, Element, Event, Layout, Length, Pixels, Point, Rectangle, Shell, Widget,
1211
};
1312
use cosmic::iced_widget::{Row, Text};
1413

15-
use crate::keyframes::{self, toggler::Chain};
14+
use crate::{chain, id, lerp};
1615
pub use cosmic::iced_style::toggler::{Appearance, StyleSheet};
1716

18-
/// The default animation duration. Change here for custom widgets. Or at runtime with `.anim_multiplier`
19-
const ANIM_DURATION: f32 = 100.;
20-
2117
/// A toggler widget.
2218
///
2319
/// # Example
@@ -40,9 +36,9 @@ where
4036
Renderer: text::Renderer,
4137
Renderer::Theme: StyleSheet,
4238
{
43-
id: crate::keyframes::toggler::Id,
39+
id: id::Toggler,
4440
is_toggled: bool,
45-
on_toggle: Box<dyn Fn(Chain, bool) -> Message + 'a>,
41+
on_toggle: Box<dyn Fn(chain::Toggler, bool) -> Message + 'a>,
4642
label: Option<String>,
4743
width: Length,
4844
size: f32,
@@ -73,14 +69,9 @@ where
7369
/// * a function that will be called when the [`Toggler`] is toggled. It
7470
/// will receive the new state of the [`Toggler`] and must produce a
7571
/// `Message`.
76-
pub fn new<F>(
77-
id: crate::keyframes::toggler::Id,
78-
label: impl Into<Option<String>>,
79-
is_toggled: bool,
80-
f: F,
81-
) -> Self
72+
pub fn new<F>(id: id::Toggler, label: impl Into<Option<String>>, is_toggled: bool, f: F) -> Self
8273
where
83-
F: 'a + Fn(Chain, bool) -> Message,
74+
F: 'a + Fn(chain::Toggler, bool) -> Message,
8475
{
8576
Toggler {
8677
id,
@@ -216,30 +207,14 @@ where
216207
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
217208
let mouse_over = layout.bounds().contains(cursor_position);
218209

219-
// To prevent broken animations, only send message if
220-
// toggler is clicked after animation is finished.
221-
// TODO this should be possible to fix once redirectable
222-
// animations are implemented.
223-
if mouse_over && (self.percent == 0.0 || self.percent == 1.0) {
210+
if mouse_over {
224211
if self.is_toggled {
225-
let off_animation = Chain::new(self.id.clone())
226-
.link(keyframes::toggler::Toggler::new(Duration::ZERO).percent(1.0))
227-
.link(
228-
keyframes::toggler::Toggler::new(Duration::from_millis(
229-
(ANIM_DURATION * self.anim_multiplier.round()) as u64,
230-
))
231-
.percent(0.0),
232-
);
212+
let off_animation =
213+
chain::Toggler::off(self.id.clone(), self.anim_multiplier);
233214
shell.publish((self.on_toggle)(off_animation, !self.is_toggled));
234215
} else {
235-
let on_animation = Chain::new(self.id.clone())
236-
.link(keyframes::toggler::Toggler::new(Duration::ZERO).percent(0.0))
237-
.link(
238-
keyframes::toggler::Toggler::new(Duration::from_millis(
239-
(ANIM_DURATION * self.anim_multiplier.round()) as u64,
240-
))
241-
.percent(1.0),
242-
);
216+
let on_animation =
217+
chain::Toggler::on(self.id.clone(), self.anim_multiplier);
243218
shell.publish((self.on_toggle)(on_animation, !self.is_toggled));
244219
}
245220

@@ -310,9 +285,17 @@ where
310285
let is_mouse_over = bounds.contains(cursor_position);
311286

312287
let style = if is_mouse_over {
313-
theme.hovered(&self.style, self.is_toggled)
288+
blend_appearances(
289+
theme.hovered(&self.style, false),
290+
theme.hovered(&self.style, true),
291+
self.percent,
292+
)
314293
} else {
315-
theme.active(&self.style, self.is_toggled)
294+
blend_appearances(
295+
theme.active(&self.style, false),
296+
theme.active(&self.style, true),
297+
self.percent,
298+
)
316299
};
317300

318301
let border_radius = bounds.height / BORDER_RADIUS_RATIO;
@@ -337,11 +320,11 @@ where
337320

338321
let toggler_foreground_bounds = Rectangle {
339322
x: bounds.x
340-
+ if self.is_toggled {
341-
bounds.width - 2.0 * space - (bounds.height - (4.0 * space))
342-
} else {
343-
2.0 * space
344-
},
323+
+ lerp(
324+
2.0 * space,
325+
bounds.width - 2.0 * space - (bounds.height - (4.0 * space)),
326+
self.percent,
327+
),
345328
y: bounds.y + (2.0 * space),
346329
width: bounds.height - (4.0 * space),
347330
height: bounds.height - (4.0 * space),
@@ -369,3 +352,24 @@ where
369352
Element::new(toggler)
370353
}
371354
}
355+
356+
fn blend_appearances(one: Appearance, mut two: Appearance, percent: f32) -> Appearance {
357+
if percent == 0. {
358+
one
359+
} else if percent == 1. {
360+
two
361+
} else {
362+
let background: [f32; 4] = one
363+
.background
364+
.into_linear()
365+
.iter()
366+
.zip(two.background.into_linear().iter())
367+
.map(|(o, t)| o * (1.0 - percent) + t * percent)
368+
.collect::<Vec<f32>>()
369+
.try_into()
370+
.unwrap();
371+
372+
two.background = background.into();
373+
two
374+
}
375+
}

0 commit comments

Comments
 (0)