diff --git a/examples/cosmic/src/window.rs b/examples/cosmic/src/window.rs index f6a629e..33199c0 100644 --- a/examples/cosmic/src/window.rs +++ b/examples/cosmic/src/window.rs @@ -10,7 +10,7 @@ use cosmic::{ iced_lazy::responsive, iced_winit::window::{drag, toggle_maximize, minimize}, theme::{self, Theme}, - widget::{button, nav_button, nav_bar, nav_bar_page, nav_bar_section, header_bar, settings, scrollable, toggler}, + widget::{button, nav_button, nav_bar, nav_bar_page, nav_bar_section, header_bar, settings, scrollable, toggler, spin_button}, Element, ElementExt, }; @@ -24,6 +24,7 @@ pub struct Window { debug: bool, theme: Theme, slider_value: f32, + spin_value: i32, checkbox_value: bool, toggler_value: bool, pick_list_selected: Option<&'static str>, @@ -68,6 +69,13 @@ pub enum Message { Minimize, Maximize, InputChanged, + SpinButton(SpinMessage) +} + +#[derive(Clone, Copy, Debug, Hash)] +pub enum SpinMessage { + Increment, + Decrement, } impl Application for Window { @@ -111,6 +119,8 @@ impl Application for Window { Message::Maximize => return toggle_maximize(window::Id::new(0)), Message::RowSelected(row) => println!("Selected row {row}"), Message::InputChanged => {}, + Message::SpinButton(SpinMessage::Decrement) => self.spin_value -= 1, + Message::SpinButton(SpinMessage::Increment) => self.spin_value += 1, } @@ -293,6 +303,12 @@ impl Application for Window { .add(settings::item_row(vec![ checkbox("Checkbox", self.checkbox_value, Message::CheckboxToggled).into() ])) + .add(settings::item( + "Spin Button", + spin_button(self.spin_value, SpinMessage::Increment, SpinMessage::Decrement) + .into_element() + .map(Message::SpinButton) + )) .into() ]) .into(); diff --git a/src/theme/mod.rs b/src/theme/mod.rs index 9c88037..3eaf607 100644 --- a/src/theme/mod.rs +++ b/src/theme/mod.rs @@ -51,7 +51,7 @@ lazy_static::lazy_static! { }; } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Theme { Light, Dark, diff --git a/src/widget/mod.rs b/src/widget/mod.rs index 6e1aa05..6645dcb 100644 --- a/src/widget/mod.rs +++ b/src/widget/mod.rs @@ -19,6 +19,9 @@ pub use self::nav_button::{NavButton, nav_button}; pub mod navigation; pub use navigation::*; +pub mod popup; +pub use popup::*; + mod toggler; pub use toggler::toggler; @@ -28,7 +31,7 @@ mod scrollable; pub use scrollable::*; pub mod separator; - -pub mod popup; -pub use popup::*; pub use separator::{horizontal_rule, vertical_rule}; + +pub mod spin_button; +pub use spin_button::{SpinButton, spin_button}; \ No newline at end of file diff --git a/src/widget/popup.rs b/src/widget/popup.rs index 780edc0..5035a8a 100644 --- a/src/widget/popup.rs +++ b/src/widget/popup.rs @@ -1,3 +1,6 @@ +// Copyright 2022 System76 +// SPDX-License-Identifier: MPL-2.0 + use iced::futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; use iced::futures::SinkExt; use iced::{ diff --git a/src/widget/spin_button.rs b/src/widget/spin_button.rs new file mode 100644 index 0000000..18f826b --- /dev/null +++ b/src/widget/spin_button.rs @@ -0,0 +1,103 @@ +// Copyright 2022 System76 +// SPDX-License-Identifier: MPL-2.0 + +use std::{hash::{Hash, Hasher}, collections::hash_map::DefaultHasher}; + +use crate::{theme, Element}; +use iced::{ + alignment::{Horizontal, Vertical}, + widget::{button, container, row, text}, + Alignment, Background, Length, +}; + +pub fn spin_button(value: T, on_increment: Message, on_decrement: Message) -> SpinButton +where T: 'static + Clone + Hash + ToString, + Message: 'static + Clone + Hash +{ + SpinButton::new(value, on_increment, on_decrement) +} + +#[derive(Hash)] +pub struct SpinButton { + on_increment: Message, + on_decrement: Message, + value: T, +} + +impl SpinButton +where T: 'static + Clone + Hash + ToString, + Message: 'static + Clone + Hash +{ + pub fn new(value: T, on_increment: Message, on_decrement: Message) -> Self { + Self { on_increment, on_decrement, value } + } + + pub fn into_element(self) -> Element<'static, Message> { + let mut hasher = DefaultHasher::new(); + self.hash(&mut hasher); + + iced_lazy::lazy(hasher.finish(), move || -> Element<'static, Message> { + container( + row![ + button( + container(text("-").size(26).vertical_alignment(Vertical::Center)) + .width(Length::Fill) + .height(Length::Fill) + .align_x(Horizontal::Center) + .align_y(Vertical::Center), + ) + .width(Length::Fill) + .height(Length::Fill) + .style(theme::Button::Text) + .on_press(self.on_decrement.clone()), + container(text(self.value.clone()).vertical_alignment(Vertical::Center)) + .width(Length::Fill) + .height(Length::Fill) + .align_x(Horizontal::Center) + .align_y(Vertical::Center), + button( + container(text("+").size(26).vertical_alignment(Vertical::Center)) + .width(Length::Fill) + .height(Length::Fill) + .align_x(Horizontal::Center) + .align_y(Vertical::Center), + ) + .width(Length::Fill) + .height(Length::Fill) + .style(theme::Button::Text) + .on_press(self.on_increment.clone()), + ] + .width(Length::Fill) + .height(Length::Units(32)) + .align_items(Alignment::Center), + ) + .padding([4, 4]) + .align_y(Vertical::Center) + .width(Length::Units(95)) + .height(Length::Units(32)) + .style(theme::Container::Custom(container_style)) + .into() + }).into() + } +} + +impl<'a, T, Message> From> for Element<'a, Message> +where T: 'static + Clone + Hash + ToString, + Message: 'static + Clone + Hash +{ + fn from(spin_button: SpinButton) -> Self { + spin_button.into_element() + } +} + +fn container_style(theme: &crate::Theme) -> iced_style::container::Appearance { + let secondary = &theme.cosmic().secondary; + let accent = &theme.cosmic().accent; + iced_style::container::Appearance { + text_color: None, + background: Some(Background::Color(secondary.component.base.into())), + border_radius: 24.0, + border_width: 0.0, + border_color: accent.base.into(), + } +} \ No newline at end of file