cosmic_time/keyframes/
style_button.rs

1use iced_native::{widget, Element, Length, Padding};
2use iced_style::button::StyleSheet;
3
4use crate::keyframes::{as_f32, get_length, Repeat};
5use crate::timeline::{Frame, Interped};
6use crate::{Ease, Linear, MovementType};
7
8/// A Button's animation Id. Used for linking animation built in `update()` with widget output in `view()`
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
10pub struct Id(iced_native::widget::Id);
11
12impl Id {
13    /// Creates a custom [`Id`].
14    pub fn new(id: impl Into<std::borrow::Cow<'static, str>>) -> Self {
15        Self(widget::Id::new(id))
16    }
17
18    /// Creates a unique [`Id`].
19    ///
20    /// This function produces a different [`Id`] every time it is called.
21    pub fn unique() -> Self {
22        Self(widget::Id::unique())
23    }
24
25    /// Used by [`crate::chain!`] macro
26    pub fn into_chain(self) -> Chain {
27        Chain::new(self)
28    }
29
30    /// Used by [`crate::chain!`] macro
31    pub fn into_chain_with_children(self, children: Vec<StyleButton>) -> Chain {
32        Chain::with_children(self, children)
33    }
34
35    /// Used by [`crate::anim!`] macro
36    pub fn as_widget<'a, Message, Renderer>(
37        self,
38        style: fn(u8) -> <Renderer::Theme as StyleSheet>::Style,
39        timeline: &crate::Timeline,
40        content: impl Into<Element<'a, Message, Renderer>>,
41    ) -> crate::widget::Button<'a, Message, Renderer>
42    where
43        Renderer: iced_native::Renderer,
44        Renderer::Theme: widget::button::StyleSheet,
45    {
46        StyleButton::as_widget(self, style, timeline, content)
47    }
48}
49
50impl From<Id> for widget::Id {
51    fn from(id: Id) -> Self {
52        id.0
53    }
54}
55
56#[derive(Debug)]
57pub struct Chain {
58    id: Id,
59    links: Vec<StyleButton>,
60    repeat: Repeat,
61}
62
63impl Chain {
64    pub fn new(id: Id) -> Self {
65        Chain {
66            id,
67            links: Vec::new(),
68            repeat: Repeat::Never,
69        }
70    }
71
72    pub fn with_children(id: Id, children: Vec<StyleButton>) -> Self {
73        Chain {
74            id,
75            links: children,
76            repeat: Repeat::Never,
77        }
78    }
79
80    pub fn link(mut self, button: StyleButton) -> Self {
81        self.links.push(button);
82        self
83    }
84
85    pub fn loop_forever(mut self) -> Self {
86        self.repeat = Repeat::Forever;
87        self
88    }
89
90    pub fn loop_once(mut self) -> Self {
91        self.repeat = Repeat::Never;
92        self
93    }
94}
95
96impl From<Chain> for crate::timeline::Chain {
97    fn from(chain: Chain) -> Self {
98        crate::timeline::Chain::new(
99            chain.id.into(),
100            chain.repeat,
101            chain
102                .links
103                .into_iter()
104                .map(|b| b.into())
105                .collect::<Vec<_>>(),
106        )
107    }
108}
109
110#[must_use = "Keyframes are intended to be used in an animation chain."]
111#[derive(Debug, Clone, Copy)]
112pub struct StyleButton {
113    at: MovementType,
114    ease: Ease,
115    width: Option<Length>,
116    height: Option<Length>,
117    padding: Option<Padding>,
118    style: Option<u8>,
119    is_eager: bool,
120}
121
122impl StyleButton {
123    pub fn new(at: impl Into<MovementType>) -> StyleButton {
124        let at = at.into();
125        StyleButton {
126            at,
127            ease: Linear::InOut.into(),
128            width: None,
129            height: None,
130            padding: None,
131            style: None,
132            is_eager: true,
133        }
134    }
135
136    pub fn lazy(at: impl Into<MovementType>) -> StyleButton {
137        let at = at.into();
138        StyleButton {
139            at,
140            ease: Linear::InOut.into(),
141            width: None,
142            height: None,
143            padding: None,
144            style: None,
145            is_eager: false,
146        }
147    }
148
149    // Returns a cosmic-time button, not a default iced button. The difference shouldn't
150    // matter to the end user. Though it is an implementation detail.
151    pub fn as_widget<'a, Message, Renderer>(
152        id: Id,
153        style: fn(u8) -> <Renderer::Theme as StyleSheet>::Style,
154        timeline: &crate::Timeline,
155        content: impl Into<Element<'a, Message, Renderer>>,
156    ) -> crate::widget::Button<'a, Message, Renderer>
157    where
158        Renderer: iced_native::Renderer,
159        Renderer::Theme: widget::button::StyleSheet,
160    {
161        let id: widget::Id = id.into();
162
163        let button = crate::widget::Button::new(content)
164            .width(get_length(&id, timeline, 0, Length::Shrink))
165            .height(get_length(&id, timeline, 1, Length::Shrink))
166            .padding([
167                timeline.get(&id, 2).map(|m| m.value).unwrap_or(5.0),
168                timeline.get(&id, 3).map(|m| m.value).unwrap_or(5.0),
169                timeline.get(&id, 4).map(|m| m.value).unwrap_or(5.0),
170                timeline.get(&id, 5).map(|m| m.value).unwrap_or(5.0),
171            ]);
172
173        if let Some(Interped {
174            previous,
175            next,
176            percent,
177            ..
178        }) = timeline.get(&id, 6)
179        {
180            button.blend_style(style(previous as u8), style(next as u8), percent)
181        } else {
182            button
183        }
184    }
185
186    pub fn width(mut self, width: Length) -> Self {
187        self.width = Some(width);
188        self
189    }
190
191    pub fn height(mut self, height: Length) -> Self {
192        self.height = Some(height);
193        self
194    }
195
196    pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
197        self.padding = Some(padding.into());
198        self
199    }
200
201    pub fn ease<E: Into<Ease>>(mut self, ease: E) -> Self {
202        self.ease = ease.into();
203        self
204    }
205
206    pub fn style(mut self, style: impl Into<u8>) -> Self {
207        let style = style.into();
208        self.style = Some(style);
209        self
210    }
211}
212
213#[rustfmt::skip]
214impl From<StyleButton> for Vec<Option<Frame>> {
215    fn from(button: StyleButton) -> Vec<Option<Frame>> {
216      if button.is_eager {
217        vec![as_f32(button.width).map(|w| Frame::eager(button.at, w, button.ease)),  // 0 = width
218             as_f32(button.height).map(|h| Frame::eager(button.at, h, button.ease)), // 1 = height
219             button.padding.map(|p| Frame::eager(button.at, p.top, button.ease)),    // 2 = padding[0] (top)
220             button.padding.map(|p| Frame::eager(button.at, p.right, button.ease)),  // 3 = padding[1] (right)
221             button.padding.map(|p| Frame::eager(button.at, p.bottom, button.ease)), // 4 = padding[2] (bottom)
222             button.padding.map(|p| Frame::eager(button.at, p.left, button.ease)),  // 5 = padding[3] (left)
223             button.style.map(|s| Frame::eager(button.at, s as f32, button.ease)),  // 6 = style blend (passed to widget to mix values at `draw` time)
224        ]
225      } else {
226        vec![Some(Frame::lazy(button.at, 0., button.ease)), // 0 = width
227             Some(Frame::lazy(button.at, 0., button.ease)), // 1 = height
228             Some(Frame::lazy(button.at, 5., button.ease)), // 2 = padding[0] (top)
229             Some(Frame::lazy(button.at, 5., button.ease)), // 3 = padding[1] (right)
230             Some(Frame::lazy(button.at, 5., button.ease)), // 4 = padding[2] (bottom)
231             Some(Frame::lazy(button.at, 5., button.ease)), // 5 = padding[3] (left)
232             Some(Frame::lazy(button.at, 0., button.ease)), // 6 = style blend (passed to widget to mix values at `draw` time)
233        ]
234      }
235    }
236}