Skip to content

Commit d58c81e

Browse files
authored
Merge pull request #5 from pop-os/basic-style-support
Basic style support & Iced 0.8
2 parents 081601e + 23558f2 commit d58c81e

File tree

21 files changed

+2369
-215
lines changed

21 files changed

+2369
-215
lines changed

Cargo.lock

Lines changed: 353 additions & 121 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ members = [
99
]
1010

1111
[dependencies]
12-
iced = { version = "0.6.0", features = [ "tokio" ] }
13-
iced_native = "0.7.0"
14-
iced_futures = "0.5"
15-
iced_core = "0.6.2"
12+
iced = { version = "0.8.0", features = [ "tokio" ] }
13+
iced_native = "0.9.1"
14+
iced_futures = "0.6.0"
15+
iced_core = "0.8.1"
16+
iced_style = "0.7.0"

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,22 @@ TODOs before release:
77
- [x] Looping animations
88
- [x] Animation easing
99
- [x] add space widget
10-
- [ ] add button widget
10+
- [x] add button widget
1111
- [ ] add row widget
1212
- [ ] add column widget
1313
- [ ] add toggle button widget
14-
- [ ] Use iced 0.7
15-
- [ ] use iced 0.7's framerate subscription
14+
- [x] Use iced 0.8
15+
- [x] use iced 0.8's framerate subscription
1616
- [x] Add logic for different animation Ease values
1717
- [ ] Documentation
1818
- [ ] Add `Cosmic` cargo feature for compatibility with both iced and System76's temporary fork.
1919
- [x] optimize for `as_subscription` logic
20+
- [ ] Add pause for looping animations
2021

2122
Other TODOs:
2223
- [x] test for easing
2324
- [ ] general animation logic tests
2425
- [ ] physics based animations
25-
- [ ] Add pause for looping animations
2626
- [ ] Low motion accesability detection to disable animations.
2727
- [ ] Figure out what else needs to be on this list
2828
- [ ] Work on web via wasm-unknown-unknown builds

examples/counter/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ edition = "2021"
66
publish = false
77

88
[dependencies]
9-
iced = "0.6.0"
9+
iced = "0.8.0"
1010
cosmic-time = { path = "../.."}
1111
once_cell = "1.15"

examples/counter/src/main.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use iced::widget::{button, column, text};
22
use iced::{
3-
executor, Alignment, Application, Command, Element, Length, Settings, Subscription, Theme,
3+
executor, Alignment, Application, Command, Element, Event, Length, Settings, Subscription,
4+
Theme,
45
};
56
use std::time::Duration;
67

@@ -43,30 +44,30 @@ impl Application for Counter {
4344
// .loop_forever() // Uncomment this line to loop the animation!
4445
.link(
4546
keyframes::Container::new(Duration::ZERO)
46-
.width(Length::Units(0))
47-
.height(Length::Units(100)),
47+
.width(Length::Fixed(0.))
48+
.height(Length::Fixed(100.)),
4849
)
4950
.link(
5051
keyframes::Container::new(Duration::from_secs(2))
51-
.width(Length::Units(200))
52-
.height(Length::Units(100)),
52+
.width(Length::Fixed(200.))
53+
.height(Length::Fixed(100.)),
5354
)
5455
.link(
5556
keyframes::Container::new(Duration::from_secs(4))
56-
.width(Length::Units(200))
57-
.height(Length::Units(300))
57+
.width(Length::Fixed(200.))
58+
.height(Length::Fixed(300.))
5859
.padding([0, 0, 0, 0]),
5960
)
6061
.link(
6162
keyframes::Container::new(Duration::from_secs(6))
62-
.width(Length::Units(700))
63-
.height(Length::Units(300))
63+
.width(Length::Fixed(700.))
64+
.height(Length::Fixed(300.))
6465
.padding([0, 0, 0, 500]),
6566
)
6667
.link(
6768
keyframes::Container::new(Duration::from_secs(8))
68-
.width(Length::Units(150))
69-
.height(Length::Units(150))
69+
.width(Length::Fixed(150.))
70+
.height(Length::Fixed(150.))
7071
.padding([0, 0, 0, 0]),
7172
);
7273

@@ -104,7 +105,9 @@ impl Application for Counter {
104105
// at what timeline you have built and decides for you how often your
105106
// application should redraw for you! When the animation is done idle
106107
// or finished, cosmic-time will keep your applicaiton idle!
107-
self.timeline.as_subscription().map(|_| Message::Tick)
108+
self.timeline
109+
.as_subscription::<Event>()
110+
.map(|_| Message::Tick)
108111
}
109112

110113
fn update(&mut self, message: Message) -> Command<Message> {

examples/pokedex/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ edition = "2021"
66
publish = false
77

88
[dependencies]
9-
iced = { version = "0.6.0", features = ["image", "debug", "tokio"] }
9+
iced = { version = "0.8.0", features = ["image", "debug", "tokio"] }
1010
cosmic-time = { path = "../.." }
1111
once_cell = "1.15"
1212
serde_json = "1.0"

examples/pokedex/src/main.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use iced::futures;
22
use iced::widget::{self, column, container, image, row, text};
33
use iced::{
4-
Alignment, Application, Color, Command, Element, Length, Settings, Subscription, Theme,
4+
Alignment, Application, Color, Command, Element, Event, Length, Settings, Subscription, Theme,
55
};
66

77
use cosmic_time::{
@@ -94,9 +94,10 @@ impl Application for Pokedex {
9494

9595
fn subscription(&self) -> Subscription<Message> {
9696
match self {
97-
Pokedex::Loaded { pokemon } => {
98-
pokemon.timeline.as_subscription().map(|_| Message::Tick)
99-
}
97+
Pokedex::Loaded { pokemon } => pokemon
98+
.timeline
99+
.as_subscription::<Event>()
100+
.map(|_| Message::Tick),
100101
_ => Subscription::none(),
101102
}
102103
}
@@ -185,7 +186,7 @@ impl Pokemon {
185186
]
186187
.spacing(20),
187188
]
188-
.height(Length::Units(400))
189+
.height(Length::Fixed(400.))
189190
.spacing(20)
190191
.align_items(Alignment::Center)
191192
.into()
@@ -251,15 +252,15 @@ impl Pokemon {
251252
);
252253

253254
let animation = cosmic_time::space::Chain::new(SPACE.clone())
254-
.link(keyframes::Space::new(Duration::ZERO).height(Length::Units(50)))
255+
.link(keyframes::Space::new(Duration::ZERO).height(Length::Fixed(50.)))
255256
.link(
256257
keyframes::Space::new(Duration::from_millis(1500))
257-
.height(Length::Units(250))
258+
.height(Length::Fixed(250.))
258259
.ease(EASE_IN[rand]),
259260
)
260261
.link(
261262
keyframes::Space::new(Duration::from_millis(3000))
262-
.height(Length::Units(50))
263+
.height(Length::Fixed(50.))
263264
.ease(EASE_OUT[rand]),
264265
)
265266
.loop_forever();

examples/stopwatch/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "stopwatch"
3+
version = "0.1.0"
4+
authors = ["Héctor Ramón Jiménez <[email protected]>"]
5+
edition = "2021"
6+
publish = false
7+
8+
[dependencies]
9+
iced = { version = "0.8.0", features = ["smol"] }
10+
cosmic-time = { path = "../.."}
11+
once_cell = "1.15"
12+

examples/stopwatch/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
## Stopwatch
2+
3+
A watch with start/stop and reset buttons showcasing how to listen to time.
4+
5+
The __[`main`]__ file contains all the code of the example.
6+
7+
<div align="center">
8+
<a href="https://gfycat.com/granularenviousgoitered-rust-gui">
9+
<img src="https://thumbs.gfycat.com/GranularEnviousGoitered-small.gif">
10+
</a>
11+
</div>
12+
13+
You can run it with `cargo run`:
14+
```
15+
cargo run --package stopwatch
16+
```
17+
18+
[`main`]: src/main.rs

examples/stopwatch/src/main.rs

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
use iced::alignment;
2+
use iced::executor;
3+
use iced::theme::{self, Theme};
4+
use iced::time;
5+
use iced::widget::{button, column, container, row, text};
6+
use iced::{Alignment, Application, Command, Element, Event, Length, Settings, Subscription};
7+
8+
use cosmic_time::{
9+
self,
10+
style_button::{self, StyleButton},
11+
Timeline,
12+
};
13+
use once_cell::sync::Lazy;
14+
15+
static BUTTON: Lazy<style_button::Id> = Lazy::new(style_button::Id::unique);
16+
17+
use std::time::{Duration, Instant};
18+
19+
pub fn main() -> iced::Result {
20+
Stopwatch::run(Settings::default())
21+
}
22+
23+
struct Stopwatch {
24+
timeline: Timeline,
25+
duration: Duration,
26+
state: State,
27+
}
28+
29+
enum State {
30+
Idle,
31+
Ticking { last_tick: Instant },
32+
}
33+
34+
#[derive(Debug, Clone)]
35+
enum Message {
36+
Toggle,
37+
Reset,
38+
Tick(Instant),
39+
}
40+
41+
impl Application for Stopwatch {
42+
type Message = Message;
43+
type Theme = Theme;
44+
type Executor = executor::Default;
45+
type Flags = ();
46+
47+
fn new(_flags: ()) -> (Stopwatch, Command<Message>) {
48+
(
49+
Stopwatch {
50+
timeline: Timeline::new(),
51+
duration: Duration::default(),
52+
state: State::Idle,
53+
},
54+
Command::none(),
55+
)
56+
}
57+
58+
fn title(&self) -> String {
59+
String::from("Stopwatch - Iced")
60+
}
61+
62+
fn update(&mut self, message: Message) -> Command<Message> {
63+
match message {
64+
Message::Toggle => match self.state {
65+
State::Idle => {
66+
self.state = State::Ticking {
67+
last_tick: Instant::now(),
68+
};
69+
self.timeline
70+
.set_chain(anim_to_destructive().into())
71+
.start();
72+
}
73+
State::Ticking { .. } => {
74+
self.state = State::Idle;
75+
self.timeline.set_chain(anim_to_primary().into()).start();
76+
}
77+
},
78+
Message::Tick(now) => {
79+
if let State::Ticking { last_tick } = &mut self.state {
80+
self.duration += now - *last_tick;
81+
*last_tick = now;
82+
}
83+
}
84+
Message::Reset => {
85+
self.duration = Duration::default();
86+
}
87+
}
88+
89+
Command::none()
90+
}
91+
92+
fn subscription(&self) -> Subscription<Message> {
93+
Subscription::batch(vec![
94+
match self.state {
95+
State::Idle => Subscription::none(),
96+
State::Ticking { .. } => time::every(Duration::from_millis(10)).map(Message::Tick),
97+
},
98+
self.timeline.as_subscription::<Event>().map(Message::Tick),
99+
])
100+
}
101+
102+
fn view(&self) -> Element<Message> {
103+
const MINUTE: u64 = 60;
104+
const HOUR: u64 = 60 * MINUTE;
105+
106+
let seconds = self.duration.as_secs();
107+
108+
let duration = text(format!(
109+
"{:0>2}:{:0>2}:{:0>2}.{:0>2}",
110+
seconds / HOUR,
111+
(seconds % HOUR) / MINUTE,
112+
seconds % MINUTE,
113+
self.duration.subsec_millis() / 10,
114+
))
115+
.size(40);
116+
117+
let button = |label| {
118+
button(text(label).horizontal_alignment(alignment::Horizontal::Center))
119+
.padding(10)
120+
.width(Length::Fixed(80.))
121+
};
122+
123+
// must match the same order that the function used to parse into `u8`s
124+
let buttons = |i: u8| match i {
125+
0 => theme::Button::Primary,
126+
1 => theme::Button::Secondary,
127+
2 => theme::Button::Positive,
128+
3 => theme::Button::Destructive,
129+
4 => theme::Button::Text,
130+
_ => panic!("custom is not supported"),
131+
};
132+
133+
let toggle_button = {
134+
let label = match self.state {
135+
State::Idle => "Start",
136+
State::Ticking { .. } => "Stop",
137+
};
138+
139+
StyleButton::as_widget(
140+
BUTTON.clone(),
141+
buttons,
142+
&self.timeline,
143+
text(label).horizontal_alignment(alignment::Horizontal::Center),
144+
)
145+
.padding(10)
146+
.width(Length::Fixed(80.))
147+
.on_press(Message::Toggle)
148+
};
149+
150+
let reset_button = button("Reset")
151+
.style(theme::Button::Secondary)
152+
.on_press(Message::Reset);
153+
154+
let controls = row![toggle_button, reset_button].spacing(20);
155+
156+
let content = column![duration, controls]
157+
.align_items(Alignment::Center)
158+
.spacing(20);
159+
160+
container(content)
161+
.width(Length::Fill)
162+
.height(Length::Fill)
163+
.center_x()
164+
.center_y()
165+
.into()
166+
}
167+
}
168+
169+
fn anim_to_primary() -> style_button::Chain {
170+
style_button::Chain::new(BUTTON.clone())
171+
.link(StyleButton::new(Duration::ZERO).style(as_u8(theme::Button::Destructive)))
172+
.link(StyleButton::new(Duration::from_millis(500)).style(as_u8(theme::Button::Primary)))
173+
}
174+
175+
fn anim_to_destructive() -> style_button::Chain {
176+
style_button::Chain::new(BUTTON.clone())
177+
.link(StyleButton::new(Duration::ZERO).style(as_u8(theme::Button::Primary)))
178+
.link(StyleButton::new(Duration::from_millis(500)).style(as_u8(theme::Button::Destructive)))
179+
}
180+
181+
// Style implementations
182+
183+
// the enum's default must be 0
184+
fn as_u8(style: theme::Button) -> u8 {
185+
match style {
186+
theme::Button::Primary => 0,
187+
theme::Button::Secondary => 1,
188+
theme::Button::Positive => 2,
189+
theme::Button::Destructive => 3,
190+
theme::Button::Text => 4,
191+
_ => panic!("Custom is not supported"),
192+
}
193+
}

0 commit comments

Comments
 (0)