Skip to content

Commit 229b60b

Browse files
committed
🎉 Functional animation pauseing w/ resume
Example added to the stopwatch example. The background now animates between colors and pauses with the timer.
1 parent dd7a8aa commit 229b60b

File tree

8 files changed

+458
-98
lines changed

8 files changed

+458
-98
lines changed

examples/counter/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ impl Application for Counter {
9797
}
9898

9999
fn title(&self) -> String {
100-
String::from("Counter - Iced")
100+
String::from("Counter - Cosmic-Time")
101101
}
102102

103103
fn subscription(&self) -> Subscription<Message> {

examples/pokedex/src/main.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,6 @@ use once_cell::sync::Lazy;
1313

1414
static SPACE: Lazy<keyframes::space::Id> = Lazy::new(keyframes::space::Id::unique);
1515

16-
//Linear,
17-
//Quadratic,
18-
//Cubic,
19-
//Quartic,
20-
//Quintic,
21-
//Sinusoidal,
22-
//Exponential,
23-
//Circular,
24-
//Elastic,
25-
//Back,
26-
//Bounce
2716
const EASE_IN: [Ease; 10] = [
2817
Ease::Linear(Linear::InOut),
2918
Ease::Quadratic(Quadratic::In),

examples/stopwatch/src/main.rs

Lines changed: 114 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
use iced::alignment;
22
use iced::executor;
3-
use iced::theme::{self, Theme};
43
use iced::time;
54
use iced::widget::{button, column, container, row, text};
6-
use iced::{Alignment, Application, Command, Element, Event, Length, Settings, Subscription};
5+
use iced::{Alignment, Application, Command, Event, Length, Settings, Subscription};
6+
7+
mod theme;
8+
use self::widget::Element;
9+
use theme::Theme;
710

811
use cosmic_time::{
912
self,
1013
style_button::{self, StyleButton},
11-
Timeline,
14+
style_container::{self, StyleContainer},
15+
Sinusoidal, Timeline,
1216
};
1317
use once_cell::sync::Lazy;
1418

1519
static BUTTON: Lazy<style_button::Id> = Lazy::new(style_button::Id::unique);
20+
static CONTAINER: Lazy<style_container::Id> = Lazy::new(style_container::Id::unique);
1621

1722
use std::time::{Duration, Instant};
1823

@@ -45,9 +50,11 @@ impl Application for Stopwatch {
4550
type Flags = ();
4651

4752
fn new(_flags: ()) -> (Stopwatch, Command<Message>) {
53+
let mut timeline = Timeline::new();
54+
timeline.set_chain_paused(anim_background()).start();
4855
(
4956
Stopwatch {
50-
timeline: Timeline::new(),
57+
timeline: timeline,
5158
duration: Duration::default(),
5259
state: State::Idle,
5360
},
@@ -56,7 +63,7 @@ impl Application for Stopwatch {
5663
}
5764

5865
fn title(&self) -> String {
59-
String::from("Stopwatch - Iced")
66+
String::from("Stopwatch - Cosmic-Time")
6067
}
6168

6269
fn update(&mut self, message: Message) -> Command<Message> {
@@ -66,11 +73,17 @@ impl Application for Stopwatch {
6673
self.state = State::Ticking {
6774
last_tick: Instant::now(),
6875
};
69-
self.timeline.set_chain(anim_to_destructive()).start();
76+
self.timeline
77+
.set_chain(anim_to_destructive())
78+
.resume(CONTAINER.clone())
79+
.start();
7080
}
7181
State::Ticking { .. } => {
7282
self.state = State::Idle;
73-
self.timeline.set_chain(anim_to_primary()).start();
83+
self.timeline
84+
.set_chain(anim_to_primary())
85+
.pause(CONTAINER.clone())
86+
.start();
7487
}
7588
},
7689
Message::Tick(now) => {
@@ -82,6 +95,10 @@ impl Application for Stopwatch {
8295
}
8396
Message::Reset => {
8497
self.duration = Duration::default();
98+
match self.state {
99+
State::Idle => self.timeline.set_chain_paused(anim_background()).start(),
100+
State::Ticking { .. } => self.timeline.set_chain(anim_background()).start(),
101+
}
85102
}
86103
}
87104

@@ -156,31 +173,73 @@ impl Application for Stopwatch {
156173
.align_items(Alignment::Center)
157174
.spacing(20);
158175

159-
container(content)
160-
.width(Length::Fill)
161-
.height(Length::Fill)
162-
.center_x()
163-
.center_y()
164-
.into()
176+
StyleContainer::as_widget(
177+
CONTAINER.clone(),
178+
// Cool! Because we implemented the function on our custom, theme's type, adding
179+
// the map argument is easy!
180+
theme::Container::map(),
181+
&self.timeline,
182+
content,
183+
)
184+
.width(Length::Fill)
185+
.height(Length::Fill)
186+
.center_x()
187+
.center_y()
188+
.into()
165189
}
166190
}
167191

168192
fn anim_to_primary() -> style_button::Chain {
169193
style_button::Chain::new(BUTTON.clone())
170-
.link(StyleButton::new(Duration::ZERO).style(as_u8(theme::Button::Destructive)))
171-
.link(StyleButton::new(Duration::from_millis(500)).style(as_u8(theme::Button::Primary)))
194+
.link(StyleButton::new(Duration::ZERO).style(button_u8(theme::Button::Destructive)))
195+
.link(StyleButton::new(Duration::from_millis(500)).style(button_u8(theme::Button::Primary)))
172196
}
173197

174198
fn anim_to_destructive() -> style_button::Chain {
175199
style_button::Chain::new(BUTTON.clone())
176-
.link(StyleButton::new(Duration::ZERO).style(as_u8(theme::Button::Primary)))
177-
.link(StyleButton::new(Duration::from_millis(500)).style(as_u8(theme::Button::Destructive)))
200+
.link(StyleButton::new(Duration::ZERO).style(button_u8(theme::Button::Primary)))
201+
.link(
202+
StyleButton::new(Duration::from_millis(500))
203+
.style(button_u8(theme::Button::Destructive)),
204+
)
205+
}
206+
207+
fn anim_background() -> style_container::Chain {
208+
style_container::Chain::new(CONTAINER.clone())
209+
.link(StyleContainer::new(Duration::ZERO).style(theme::Container::Red))
210+
.link(
211+
StyleContainer::new(Duration::from_secs(3))
212+
// Notice how we can just pass the enum value here, where in the `anim_to_primary/destructive`
213+
// we have to use the fucntion `button_u8`? Because we use a implemented a custom iced theme,
214+
// we can just impl Into<u8> on the enum, and it works here!
215+
.style(theme::Container::Green)
216+
.ease(Sinusoidal::In),
217+
)
218+
.link(
219+
StyleContainer::new(Duration::from_secs(6))
220+
.style(theme::Container::Blue)
221+
.ease(Sinusoidal::In),
222+
)
223+
.link(
224+
StyleContainer::new(Duration::from_secs(9))
225+
.style(theme::Container::Red)
226+
.ease(Sinusoidal::In),
227+
)
228+
.loop_forever()
178229
}
179230

180231
// Style implementations
181232

233+
// Here the button example uses Iced's default theme
234+
// enum. So we have to have some helper functions to make it work.
235+
// we also have another closture, `buttons`, in `fn view()`
236+
//
237+
// For themining reasons, this actually isn't iced's default
238+
// button theme, but the implementation here for button is what you
239+
// would have to do to use the iced type in your project.
240+
182241
// the enum's default must be 0
183-
fn as_u8(style: theme::Button) -> u8 {
242+
fn button_u8(style: theme::Button) -> u8 {
184243
match style {
185244
theme::Button::Primary => 0,
186245
theme::Button::Secondary => 1,
@@ -190,3 +249,40 @@ fn as_u8(style: theme::Button) -> u8 {
190249
_ => panic!("Custom is not supported"),
191250
}
192251
}
252+
253+
// But! if we are useing a custom theme then
254+
// the code cleans up quite a bit.
255+
256+
impl From<theme::Container> for u8 {
257+
fn from(style: theme::Container) -> Self {
258+
match style {
259+
theme::Container::White => 0,
260+
theme::Container::Red => 1,
261+
theme::Container::Green => 2,
262+
theme::Container::Blue => 3,
263+
}
264+
}
265+
}
266+
267+
impl theme::Container {
268+
fn map() -> fn(u8) -> theme::Container {
269+
|i: u8| match i {
270+
0 => theme::Container::White,
271+
1 => theme::Container::Red,
272+
2 => theme::Container::Green,
273+
3 => theme::Container::Blue,
274+
_ => panic!("Impossible"),
275+
}
276+
}
277+
}
278+
279+
// Just for themeing, not a part of this example.
280+
mod widget {
281+
#![allow(dead_code)]
282+
use crate::theme::Theme;
283+
284+
pub type Renderer = iced::Renderer<Theme>;
285+
pub type Element<'a, Message> = iced::Element<'a, Message, Renderer>;
286+
pub type Container<'a, Message> = iced::widget::Container<'a, Message, Renderer>;
287+
pub type Button<'a, Message> = iced::widget::Button<'a, Message, Renderer>;
288+
}

examples/stopwatch/src/theme.rs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* This file is not specific to cosmic-time.
3+
* The relevant code to this example is in main.rs.
4+
* This is just code to make an iced theme, so
5+
* the stopwatch example can be prettier, and
6+
* show the andvantages of style animations
7+
* with a custom theme.
8+
*
9+
*/
10+
11+
use iced::theme::palette::{self, Background};
12+
use iced::widget::{button, container, text};
13+
use iced::Background as B;
14+
use iced::{application, color, Color, Vector};
15+
16+
#[derive(Default)]
17+
pub struct Theme;
18+
19+
impl application::StyleSheet for Theme {
20+
type Style = ();
21+
22+
fn appearance(&self, _style: &Self::Style) -> application::Appearance {
23+
application::Appearance {
24+
background_color: color!(0xff, 0xff, 0xff),
25+
text_color: color!(0xff, 0x00, 0x00),
26+
}
27+
}
28+
}
29+
30+
impl text::StyleSheet for Theme {
31+
type Style = ();
32+
33+
fn appearance(&self, _style: Self::Style) -> text::Appearance {
34+
text::Appearance {
35+
color: color!(0xff, 0xff, 0xff).into(),
36+
}
37+
}
38+
}
39+
40+
#[derive(Debug, Clone, Copy, Default)]
41+
pub enum Container {
42+
#[default]
43+
White,
44+
Red,
45+
Green,
46+
Blue,
47+
}
48+
49+
impl container::StyleSheet for Theme {
50+
type Style = Container;
51+
52+
fn appearance(&self, style: &Self::Style) -> container::Appearance {
53+
match style {
54+
Container::White => container::Appearance {
55+
background: Some(B::Color(color!(0xd1, 0xd5, 0xdb))),
56+
text_color: Some(color!(0x00, 0x00, 0x00)),
57+
..Default::default()
58+
},
59+
Container::Red => container::Appearance {
60+
background: Some(B::Color(color!(0xfc, 0xa5, 0xa5))),
61+
text_color: Some(color!(0x00, 0x00, 0x00)),
62+
..Default::default()
63+
},
64+
Container::Green => container::Appearance {
65+
background: Some(B::Color(color!(0xb3, 0xf2, 0x64))),
66+
text_color: Some(color!(0x00, 0x00, 0x00)),
67+
..Default::default()
68+
},
69+
Container::Blue => container::Appearance {
70+
background: Some(B::Color(color!(0x93, 0xc5, 0xfd))),
71+
text_color: Some(color!(0x00, 0x00, 0x00)),
72+
..Default::default()
73+
},
74+
}
75+
}
76+
}
77+
78+
#[derive(Default)]
79+
pub enum Button {
80+
#[default]
81+
Primary,
82+
Secondary,
83+
Positive,
84+
Destructive,
85+
Text,
86+
Custom(Box<dyn button::StyleSheet<Style = Theme>>),
87+
}
88+
89+
impl button::StyleSheet for Theme {
90+
type Style = Button;
91+
92+
fn active(&self, style: &Self::Style) -> button::Appearance {
93+
match style {
94+
Button::Primary => button::Appearance {
95+
background: color!(0x25, 0x63, 0xeb).into(),
96+
text_color: color!(0x00, 0x00, 0x00),
97+
border_radius: 10.0,
98+
border_width: 10.0,
99+
shadow_offset: Vector::new(3., 3.),
100+
border_color: color!(0x25, 0x63, 0xeb),
101+
..Default::default()
102+
},
103+
Button::Secondary => button::Appearance {
104+
background: color!(0x3c, 0x38, 0x36).into(),
105+
border_radius: 10.0,
106+
shadow_offset: Vector::new(3., 3.),
107+
text_color: color!(0xff, 0xff, 0xff),
108+
..Default::default()
109+
},
110+
Button::Destructive => button::Appearance {
111+
background: color!(0xdc, 0x26, 0x26).into(),
112+
text_color: color!(0xff, 0xff, 0xff),
113+
border_radius: 10.0,
114+
shadow_offset: Vector::new(5., 5.),
115+
border_color: color!(0xdc, 0x26, 0x26),
116+
border_width: 10.0,
117+
..Default::default()
118+
},
119+
_ => panic!("This isn't a custom style exmaple, just skipping these for now"),
120+
}
121+
}
122+
123+
fn hovered(&self, style: &Self::Style) -> button::Appearance {
124+
self.active(style)
125+
}
126+
127+
fn pressed(&self, style: &Self::Style) -> button::Appearance {
128+
self.active(style)
129+
}
130+
}

src/keyframes/style_button.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ impl StyleButton {
154154
self
155155
}
156156

157-
pub fn style(mut self, style: u8) -> Self {
157+
pub fn style(mut self, style: impl Into<u8>) -> Self {
158+
let style = style.into();
158159
self.style = Some(style);
159160
self
160161
}

src/keyframes/style_container.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ impl StyleContainer {
180180
self
181181
}
182182

183-
pub fn style(mut self, style: u8) -> Self {
183+
pub fn style(mut self, style: impl Into<u8>) -> Self {
184+
let style = style.into();
184185
self.style = Some(style);
185186
self
186187
}

0 commit comments

Comments
 (0)