From 90f7c9b0cfd91ca679a9c5cce69474f6b1d605ed Mon Sep 17 00:00:00 2001 From: Bryan Hyland Date: Sat, 16 Nov 2024 22:59:44 -0800 Subject: [PATCH] Updated spin button and example - incomplete --- .../Cargo.toml | 0 examples/spin-button/src/main.rs | 213 +++++++++++ examples/vertical-spin-button/src/main.rs | 159 -------- src/widget/mod.rs | 2 +- src/widget/spin_button/mod.rs | 348 +++++++++--------- src/widget/spin_button/model.rs | 156 -------- src/widget/spin_button/spin_button.rs | 196 ---------- 7 files changed, 387 insertions(+), 687 deletions(-) rename examples/{vertical-spin-button => spin-button}/Cargo.toml (100%) create mode 100644 examples/spin-button/src/main.rs delete mode 100644 examples/vertical-spin-button/src/main.rs delete mode 100644 src/widget/spin_button/model.rs delete mode 100644 src/widget/spin_button/spin_button.rs diff --git a/examples/vertical-spin-button/Cargo.toml b/examples/spin-button/Cargo.toml similarity index 100% rename from examples/vertical-spin-button/Cargo.toml rename to examples/spin-button/Cargo.toml diff --git a/examples/spin-button/src/main.rs b/examples/spin-button/src/main.rs new file mode 100644 index 00000000..c63aace9 --- /dev/null +++ b/examples/spin-button/src/main.rs @@ -0,0 +1,213 @@ +use cosmic::widget::divider::horizontal; +use cosmic::widget::{button, container, text, spin_button, spin_button::Direction}; +use cosmic::{ + app::{Core, Task}, + iced::{ + alignment::{Horizontal, Vertical}, + widget::{column, row, vertical_space}, + Alignment, Size, + }, + Application, Element, +}; + +pub struct VertSpinnerApp { + core: Core, + i8_num: i8, + i16_num: i16, + i32_num: i32, + i64_num: i64, + i128_num: i128, + f16_num: f16, + f32_num: f32, + f64_num: f64, + f128_num: f128, + spinner_msg: String, +} + +#[derive(Debug, Clone)] +pub enum SpinBtnMessages { + UpdateI8Num(i8), + UpdateI16Num(i16), + UpdateI32Num(i32), + UpdateI64Num(i64), + UpdateI128Num(i128), + UpdateF16Num(f16), + UpdateF32Num(f32), + UpdateF64Num(f64), + UpdateF128Num(f128), + UpdateSpinnerMsg, +} + +impl Application for VertSpinnerApp { + type Executor = cosmic::executor::Default; + type Flags = (); + type Message = SpinBtnMessages; + + const APP_ID: &'static str = "com.system76.VertSpinnerExample"; + + fn core(&self) -> &Core { + &self.core + } + + fn core_mut(&mut self) -> &mut Core { + &mut self.core + } + + fn init(core: Core, _flags: Self::Flags) -> (Self, Task) { + ( + Self { + core, + i8_num: 0, + i16_num: 0, + i32_num: 0, + i64_num: 0, + i128_num: 0, + f16_num: 0., + f32_num: 0., + f64_num: 0., + f128_num: 0., + spinner_msg: String::new(), + }, + Task::none(), + ) + } + + fn update(&mut self, message: Self::Message) -> Task { + match message { + SpinBtnMessages::UpdateI8Num(new_i8) => { + self.i8_num = new_i8; + SpinBtnMessages::UpdateSpinnerMsg + }, + SpinBtnMessages::UpdateI16Num(new_i16) => self.i16_num = new_i16, + SpinBtnMessages::UpdateI32Num(new_i32) => self.i32_num = new_i32, + SpinBtnMessages::UpdateI64Num(new_i64) => self.i64_num = new_i64, + SpinBtnMessages::UpdateI128Num(new_i128) => self.i128_num = new_i128, + SpinBtnMessages::UpdateF16Num(new_f16) => self.f16_num = new_f16, + SpinBtnMessages::UpdateF32Num(new_f32) => self.f32_num = new_f32, + SpinBtnMessages::UpdateF64Num(new_f64) => self.f64_num = new_f64, + SpinBtnMessages::UpdateF128Num(new_f128) => self.f128_num = new_f128, + SpinBtnMessages::UpdateSpinnerMsg => { + self.spinner_msg = format!("i8: {}, i16: {}, i32: {}, i64: {}, i128: {}\nf16: {}, f32: {}, f64: {}, f128: {}"); + } + } + + Task::none() + } + + fn view(&self) -> Element { + let vert_spinner_row = row![ + spin_button( + "i8", + 1, + self.i8_num, + -5, + 5, + Direction::Vertical, + SpinBtnMessages::UpdateI8Num + ), + spin_button( + "i16", + 1, + self.i16_num, + 0, + 10, + Direction::Vertical, + SpinBtnMessages::UpdateI16Num + ), + spin_button( + "i32", + 1, + self.i32_num, + 0, + 12, + Direction::Vertical, + SpinBtnMessages::UpdateI32Num + ), + spin_button( + "i64", + 15, + self.i64_num, + 15, + 35, + Direction::Vertical, + SpinBtnMessages::UpdateI64Num + ), + ] + .align_y(Vertical::Center); + + let horiz_spinner_row = column![ + row![ + spin_button( + "i128", + 100, + self.i128_num, + -1000, + i128::MAX, + Direction::Horizontal, + SpinBtnMessages::UpdateI128Num + ) + ].into(), + row![ + spin_button( + "f16", + 0.2, + self.f16_num, + -2.2, + 4.8, + Direction::Horizontal, + SpinBtnMessages::UpdateF16Num + ) + ].into(), + row![ + spin_button( + "f32", + 1.5, + self.f32_num, + -35.3, + 12.3, + Direction::Horizontal, + SpinBtnMessages::UpdateF32Num + ) + ].into(), + row![ + spin_button( + "f64", + 1.0, + self.f64_num, + 0.0, + 3.0, + Direction::Horizontal, + SpinBtnMessages::UpdateF64Num + ) + ].into(), + ] + .into() + .align_x(Alignment::Center); + + let status_row = row![ + text(self.spinner_msg.clone()), + ]; + + let final_col = column![ + vert_spinner_row, + vertical_space().height(5), + horiz_spinner_row, + button::standard("Show Spinner Values Passed").on_press(SpinBtnMessages::UpdateSpinnerMsg), + vertical_space().height(10), + status_row, + ] + .align_x(Alignment::Center); + + container(final_col) + .align_x(Horizontal::Center) + .align_y(Vertical::Center) + .into() + } +} + +fn main() -> Result<(), Box> { + let settings = cosmic::app::Settings::default().size(Size::new(550., 1024.)); + cosmic::app::run::(settings, ())?; + + Ok(()) +} diff --git a/examples/vertical-spin-button/src/main.rs b/examples/vertical-spin-button/src/main.rs deleted file mode 100644 index 46a5de7a..00000000 --- a/examples/vertical-spin-button/src/main.rs +++ /dev/null @@ -1,159 +0,0 @@ -use cosmic::widget::{button, container, text, spin_button::vertical_spin_button, spin_button::spin_button}; -use cosmic::{ - app::{Core, Task}, - iced::{ - alignment::{Horizontal, Vertical}, - widget::{column, row, vertical_space}, - Alignment, Size, - }, - Application, Element, -}; - -pub struct VertSpinnerApp { - core: Core, - hours: i32, - mins: i16, - secs: i8, - spin_button_num: f32, - time_msg: String, - spin_btn_msg: String, -} - -#[derive(Debug, Clone)] -pub enum SpinBtnMessages { - UpdateHours(i32), - UpdateMins(i16), - UpdateSecs(i8), - UpdateSpinBtnVal(f32), - UpdateTimeMessage, -} - -impl Application for VertSpinnerApp { - type Executor = cosmic::executor::Default; - type Flags = (); - type Message = SpinBtnMessages; - - const APP_ID: &'static str = "com.system76.VertSpinnerExample"; - - fn core(&self) -> &Core { - &self.core - } - - fn core_mut(&mut self) -> &mut Core { - &mut self.core - } - - fn init(core: Core, _flags: Self::Flags) -> (Self, Task) { - ( - Self { - core, - hours: 0, - mins: 0, - secs: 0, - spin_button_num: 0.0, - time_msg: String::new(), - spin_btn_msg: String::new(), - }, - Task::none(), - ) - } - - fn update(&mut self, message: Self::Message) -> Task { - match message { - SpinBtnMessages::UpdateHours(hours) => self.hours = hours, - SpinBtnMessages::UpdateMins(mins) => self.mins = mins, - SpinBtnMessages::UpdateSecs(secs) => self.secs = secs, - SpinBtnMessages::UpdateSpinBtnVal(spin_btn_val) => self.spin_button_num = spin_btn_val, - SpinBtnMessages::UpdateTimeMessage => { - self.time_msg = format!( - "{}:{}:{}", - self.hours, - // Add a zero in front of a single digit number - if self.mins < 10 { - format!("0{}", self.mins) - } else { - self.mins.to_string() - }, - // Add a zero in front of a single digit number - if self.secs < 10 { - format!("0{}", self.secs) - } else { - self.secs.to_string() - } - ); - - self.spin_btn_msg = format!("This came from the spin button: {}", self.spin_button_num); - } - } - - Task::none() - } - - fn view(&self) -> Element { - let spinner_row = row![ - spin_button::spin_button( - "Hours", - 1, - self.hours, - 0, - 12, - spin_button::Direction::Vertical, - SpinBtnMessages::UpdateHours - ), - spin_button::spin_button( - "Minutes", - 1, - self.mins, - 0, - 59, - spin_button::Direction::Vertical, - SpinBtnMessages::UpdateMins - ), - spin_button::spin_button( - "Seconds", - 1, - self.secs, - 0, - 59, - spin_button::Direction::Vertical, - SpinBtnMessages::UpdateSecs - ), - spin_button::spin_button( - "Spin Button Demo", - 0.5, - self.spin_button_num, - 0.0, - 10.0, - spin_button::Direction::Horizontal, - SpinBtnMessages::UpdateSpinBtnVal - ) - ] - .align_y(Vertical::Center); - - let status_row = row![ - text(self.time_msg.clone()), - cosmic::widget::horizontal_space().width(10), - text(self.spin_btn_msg.clone()) - ]; - - let final_col = column![ - spinner_row, - button::standard("Update Time").on_press(SpinBtnMessages::UpdateTimeMessage), - vertical_space().height(10), - status_row, - ] - .align_x(Alignment::Center); - - container(final_col) - .align_x(Horizontal::Center) - .align_y(Vertical::Center) - .into() - } -} - -fn main() -> Result<(), Box> { - let settings = cosmic::app::Settings::default().size(Size::new(550., 300.)); - cosmic::app::run::(settings, ())?; - - Ok(()) -} diff --git a/src/widget/mod.rs b/src/widget/mod.rs index 2bd1a0f1..52acf06d 100644 --- a/src/widget/mod.rs +++ b/src/widget/mod.rs @@ -311,7 +311,7 @@ pub mod settings; pub mod spin_button; #[doc(inline)] -pub use spin_button::{spin_button::spin_button, SpinButton}; +pub use spin_button::{spin_button, SpinButton}; pub mod tab_bar; diff --git a/src/widget/spin_button/mod.rs b/src/widget/spin_button/mod.rs index 110b93c2..30f1fea0 100644 --- a/src/widget/spin_button/mod.rs +++ b/src/widget/spin_button/mod.rs @@ -3,219 +3,217 @@ //! A control for incremental adjustments of a value. -mod model; -pub mod spin_button; -use std::borrow::Cow; - -pub use self::model::{Message, Model}; - -use crate::widget::{button, column, container, icon, row, text}; -use crate::{theme, Element}; -use std::fmt::Display; -use std::marker::PhantomData; +use std::borrow::{Borrow, Cow}; +use std::fmt::{Display, Formatter}; use std::ops::{Add, Sub}; +use std::marker::PhantomData; use apply::Apply; -use iced::{Alignment, Length}; +use fraction::Decimal; use iced::alignment::Horizontal; -use iced_core::{Border, Shadow}; +use iced::{Alignment, Border, Length, Shadow}; +use crate::{ + Element, + widget::{ + button, + column, + container, + icon, + row, + text, + }, +}; -pub struct SpinButton<'a, Message> { - label: Cow<'a, str>, - on_change: Box Message + 'static>, +pub enum Direction { + Horizontal, + Vertical, } -/// A control for incremental adjustments of a value. -pub fn spin_button<'a, Message: 'static>( - label: impl Into>, - on_change: impl Fn(model::Message) -> Message + 'static, -) -> SpinButton<'a, Message> { - SpinButton::new(label, on_change) -} - -impl<'a, Message: 'static> SpinButton<'a, Message> { - pub fn new( - label: impl Into>, - on_change: impl Fn(model::Message) -> Message + 'static, - ) -> Self { - Self { - on_change: Box::from(on_change), - label: label.into(), - } - } - - #[must_use] - pub fn into_element(self) -> Element<'a, Message> { - let Self { on_change, label } = self; - container( - row::with_children(vec![ - icon::from_name("list-remove-symbolic") - .size(16) - .apply(container) - .center(Length::Fixed(32.0)) - .apply(button::custom) - .width(Length::Fixed(32.0)) - .height(Length::Fixed(32.0)) - .class(theme::Button::Text) - .on_press(model::Message::Decrement) - .into(), - text::title4(label) - .apply(container) - .center_x(Length::Fixed(48.0)) - .align_y(Alignment::Center) - .into(), - icon::from_name("list-add-symbolic") - .size(16) - .apply(container) - .center(Length::Fixed(32.0)) - .apply(button::custom) - .width(Length::Fixed(32.0)) - .height(Length::Fixed(32.0)) - .class(theme::Button::Text) - .on_press(model::Message::Increment) - .into(), - ]) - .width(Length::Shrink) - .height(Length::Fixed(32.0)) - .align_y(Alignment::Center), - ) - .width(Length::Shrink) - .center_y(Length::Fixed(32.0)) - .class(theme::Container::custom(container_style)) - .apply(Element::from) - .map(on_change) - } -} - -impl<'a, Message: 'static> From> for Element<'a, Message> { - fn from(spin_button: SpinButton<'a, Message>) -> Self { - spin_button.into_element() - } -} - -#[allow(clippy::trivially_copy_pass_by_ref)] -fn container_style(theme: &crate::Theme) -> iced_widget::container::Style { - let basic = &theme.cosmic(); - let mut neutral_10 = basic.palette.neutral_10; - neutral_10.alpha = 0.1; - let accent = &basic.accent; - let corners = &basic.corner_radii; - iced_widget::container::Style { - icon_color: Some(basic.palette.neutral_10.into()), - text_color: Some(basic.palette.neutral_10.into()), - background: None, - border: Border { - radius: corners.radius_s.into(), - width: 0.0, - color: accent.base.into(), - }, - shadow: Shadow::default(), - } -} - - - -/// VerticalSpinButton is the state/model of the vertical spin button widget. -/// Restricts T to Add, Sub, Eq, Ord, Display, and Copy so that -/// T can only be numerical values. -pub struct VerticalSpinButton<'a, T, M> +pub struct SpinButton<'a, T, M> where - T: Add + Sub + Eq + Ord + Display + Copy, + T: Add + Sub + PartialEq + PartialOrd + Display + Copy { label: String, - step: T, - value: T, - min: T, - max: T, - on_select: Box M>, + /// The amount to increment or decrement the value. + pub step: T, + /// The current value of the spin button. + pub value: T, + /// The minimum value permitted. + pub min: T, + /// The maximum value permitted. + pub max: T, + /// The direction that the spin button is laid out; Horizontal (default) or Vertical + pub direction: Direction, + on_press: Box M>, phantom_data: PhantomData<&'a M>, } -impl<'a, T, M> VerticalSpinButton<'a, T, M> +impl<'a, T, M> SpinButton<'a, T, M> where - T: Add + Sub + Eq + Ord + Display + Copy, + T: Add + Sub + PartialEq + PartialOrd + Display + Copy { - /// Creates a new vertical spin button widget + /// Create a new default spin button pub fn new( label: impl Into, step: T, value: T, min: T, max: T, - on_select: impl Fn(T) -> M + 'static, + direction: Direction, + on_press: impl Fn(T) -> M + 'static, ) -> Self { - VerticalSpinButton { + Self { label: label.into(), step, value, min, max, - on_select: Box::new(on_select), + direction, + on_press: Box::from(on_press), phantom_data: PhantomData, } } } -pub fn vertical_spin_button( +/// Shorthand function to create a default spin button +pub fn spin_button<'a, T, M>( label: impl Into, step: T, value: T, min: T, max: T, - on_select: impl Fn(T) -> M + 'static, -) -> VerticalSpinButton<'static, T, M> -where - T: Add + Sub + Eq + Ord + Display + Copy, + direction: Direction, + on_press: impl Fn(T) -> M + 'static, +) -> SpinButton<'a, T, M> +where + T: Add + Sub + PartialEq + PartialOrd + Copy { - VerticalSpinButton::new(label, step, value, min, max, on_select) + SpinButton::new(label, step, value, min, max, direction, on_press) } -fn increment(step: T, val: T, min: T, max: T) -> T -where - T: Add + Sub + Eq + Ord + Display + Copy, -{ - std::cmp::min(std::cmp::max(val + step, min), max) -} - -fn decrement(step: T, val: T, min: T, max: T) -> T -where - T: Add + Sub + Eq + Ord + Display + Copy, -{ - std::cmp::max(std::cmp::min(val - step, max), min) -} - -impl<'a, T, Message> From> for Element<'a, Message> -where - Message: Clone + 'static, - T: Add + Sub + Eq + Ord + Display + Copy, -{ - fn from(this: VerticalSpinButton) -> Self { - let val_text = text(format!("{}", this.value)).size(14); - let spinner_container = column::with_capacity(3) - .push( - button::icon(icon::from_name("list-add-symbolic")) - .padding([0, 12]) - .on_press((this.on_select)(increment::( - this.step, this.value, this.min, this.max, - ))), - ) - .push(val_text) - .push( - button::icon(icon::from_name("list-remove-symbolic")) - .padding([0, 12]) - .on_press((this.on_select)(decrement::( - this.step, this.value, this.min, this.max, - ))), - ) - .align_x(Horizontal::Center); - - let content_list = column::with_children(vec![ - row::with_capacity(1).push(text(this.label)).into(), - row::with_children(vec![Element::from(spinner_container)]).into(), - ]) - .width(75) - .padding([8, 0]) - .align_x(Alignment::Center); - - Self::new(content_list) +fn increment(step: T, value: T, min: T, max: T) -> T +where + T: Add + Sub + PartialEq + PartialOrd + Copy +{ + //! Make it roll over back to min if the increase is too high + if value + step > max { + min + } else { + value + step } } + +fn decrement(step: T, value: T, min: T, max: T) -> T +where + T: Add + Sub + PartialEq + PartialOrd + Copy +{ + //! Make it roll over back to max if the decrese is too low + if value - step < min { + max + } else { + value - step + } +} + +impl<'a, T, Message> From> for Element<'a, Message> +where + Message: Clone + 'static, + T: Add + Sub + PartialEq + PartialOrd + Copy +{ + fn from(this: SpinButton<'a, T, Message>) -> Self { + //! Matching on the direction enum given by the developer when the + //! widget is initially created in the application's view function. + match this.direction { + Direction::Horizontal => { + // Create a spinner container variable that contains the row with all of + // the combined widgets that make up the widget. + let spinner_container = column::with_capacity(2) + .push( + row::with_children( + vec![ + // Using the title4 variant of text, just like the original spin button did. + text::title4(this.label.clone()) + .apply(container) + .center_x(Length::Fill) + .align_y(Alignment::Center) + .into(), + ] + ) + ) + .push( + row::with_children( + vec![ + // Using an button instead of an icon for the decrement functionality. + button::icon(icon::from_name("list-remove-symbolic")) + .padding([0, 12]) + .on_press((this.on_press)(decrement::( + this.step, this.value, this.min, this.max, + )) + ) + .into(), + // Using the title4 variant of text for consistency. + text::title4(format!("{}", this.value)) + .apply(container) + .center_x(Length::Fixed(48.0)) + .align_y(Alignment::Center) + .into(), + // Using another button for the increment functionality. + button::icon(icon::from_name("list-add-symbolic")) + .padding([0, 12]) + .on_press((this.on_press)(increment::( + this.step, this.value, this.min, this.max, + )) + ) + .into(), + ] + ) + .align_y(Alignment::Center) + ) + .align_x(Alignment::Center); + + // Return the horizontal spin button from the match statement. + Self::new(spinner_container) + }, + Direction::Vertical => { + // Create a text widget that holds the value + let val_text = text(format!("{}", this.value)).size(14); + // Create a spinner container variable that contains the column with all of + // the combined widgets that make up the widget. + let spinner_container = column::with_capacity(3) + .push( + // Use a button for the increment functionality + button::icon(icon::from_name("list-add-symbolic")) + .padding([0, 12]) + .on_press((this.on_press)(increment::( + this.step, this.value, this.min, this.max, + ))), + ) + // Add the text widget that holds the current value + .push(val_text) + .push( + // Use a button for the decrement functionality + button::icon(icon::from_name("list-remove-symbolic")) + .padding([0, 12]) + .on_press((this.on_press)(decrement::( + this.step, this.value, this.min, this.max, + ))), + ) + .align_x(Horizontal::Center); + + // Create a column that contains two rows: + // First Row -> The label/title for the spin button. + // Second Row -> The spin button container from above. + let content_list = column::with_children(vec![ + row::with_capacity(1).push(text(this.label)).into(), + row::with_children(vec![Element::from(spinner_container)]).into(), + ]) + .width(75) + .padding([8, 0]) + .align_x(Alignment::Center); + + // Return the vertical spin button from the match statement. + Self::new(content_list) + } + } + } +} + diff --git a/src/widget/spin_button/model.rs b/src/widget/spin_button/model.rs deleted file mode 100644 index e617bc87..00000000 --- a/src/widget/spin_button/model.rs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2022 System76 -// SPDX-License-Identifier: MPL-2.0 - -use derive_setters::Setters; -use fraction::{Bounded, Decimal}; -use std::hash::Hash; -use std::ops::{Add, Sub}; - -/// A message emitted by the [`SpinButton`](super) widget. -#[derive(Clone, Copy, Debug, Hash)] -pub enum Message { - Increment, - Decrement, -} - -#[derive(Setters)] -pub struct Model { - /// The current value of the spin button. - #[setters(into)] - pub value: T, - /// The amount to increment the value. - #[setters(into)] - pub step: T, - /// The minimum value permitted. - #[setters(into)] - pub min: T, - /// The maximum value permitted. - #[setters(into)] - pub max: T, -} - -impl Model -where - T: Copy + Hash + Sub + Add + Ord, -{ - pub fn update(&mut self, message: Message) { - self.value = match message { - Message::Increment => { - std::cmp::min(std::cmp::max(self.value + self.step, self.min), self.max) - } - Message::Decrement => { - std::cmp::max(std::cmp::min(self.value - self.step, self.max), self.min) - } - } - } -} - -impl Default for Model { - fn default() -> Self { - Self { - value: 0, - step: 1, - min: i8::MIN, - max: i8::MAX, - } - } -} - -impl Default for Model { - fn default() -> Self { - Self { - value: 0, - step: 1, - min: i16::MIN, - max: i16::MAX, - } - } -} - -impl Default for Model { - fn default() -> Self { - Self { - value: 0, - step: 1, - min: i32::MIN, - max: i32::MAX, - } - } -} - -impl Default for Model { - fn default() -> Self { - Self { - value: 0, - step: 1, - min: isize::MIN, - max: isize::MAX, - } - } -} - -impl Default for Model { - fn default() -> Self { - Self { - value: 0, - step: 1, - min: u8::MIN, - max: u8::MAX, - } - } -} - -impl Default for Model { - fn default() -> Self { - Self { - value: 0, - step: 1, - min: u16::MIN, - max: u16::MAX, - } - } -} - -impl Default for Model { - fn default() -> Self { - Self { - value: 0, - step: 1, - min: u32::MIN, - max: u32::MAX, - } - } -} - -impl Default for Model { - fn default() -> Self { - Self { - value: 0, - step: 1, - min: usize::MIN, - max: usize::MAX, - } - } -} - -impl Default for Model { - fn default() -> Self { - Self { - value: 0, - step: 1, - min: u64::MIN, - max: u64::MAX, - } - } -} - -impl Default for Model { - fn default() -> Self { - Self { - value: Decimal::from(0.0), - step: Decimal::from(0.0), - min: Decimal::min_positive_value(), - max: Decimal::max_value(), - } - } -} diff --git a/src/widget/spin_button/spin_button.rs b/src/widget/spin_button/spin_button.rs deleted file mode 100644 index 862be4f2..00000000 --- a/src/widget/spin_button/spin_button.rs +++ /dev/null @@ -1,196 +0,0 @@ -use std::borrow::{Borrow, Cow}; -use std::fmt::Display; -use std::ops::{Add, Sub}; -use std::marker::PhantomData; -use apply::Apply; -use iced::alignment::Horizontal; -use iced::{Alignment, Length}; -use crate::{ - Element, - widget::{ - button, - column, - container, - icon, - row, - text, - }, -}; - -pub enum Direction { - Horizontal, - Vertical, -} - -pub struct SpinButton<'a, T, Message> -where - T: Add + Sub + PartialEq + PartialOrd + Display + Copy -{ - label: String, - step: T, - value: T, - min: T, - max: T, - direction: Direction, - on_press: Box Message>, - phantom_data: PhantomData<&'a Message>, -} - -impl<'a, T, Message> SpinButton<'a, T, Message> -where - T: Add + Sub + PartialEq + PartialOrd + Display + Copy -{ - pub fn new( - label: impl Into, - step: T, - value: T, - min: T, - max: T, - direction: Direction, - on_press: impl Fn(T) -> Message + 'static, - ) -> Self { - Self { - label: label.into(), - step, - value, - min, - max, - direction, - on_press: Box::from(on_press), - phantom_data: PhantomData, - } - } -} - -pub fn spin_button<'a, T, Message>( - label: impl Into, - step: T, - value: T, - min: T, - max: T, - direction: Direction, - on_press: impl Fn(T) -> Message + 'static, -) -> SpinButton<'a, T, Message> -where - T: Add + Sub + PartialEq + PartialOrd + Display + Copy -{ - SpinButton::new(label, step, value, min, max, direction, on_press) -} - -fn increment(step: T, value: T, min: T, max: T) -> T -where - T: Add + Sub + PartialEq + PartialOrd + Display + Copy -{ - //! Make it roll over back to min if the increase is too high - if value + step > max { - min - } else { - value + step - } -} - -fn decrement(step: T, value: T, min: T, max: T) -> T -where - T: Add + Sub + PartialEq + PartialOrd + Display + Copy -{ - //! Make it roll over back to max if the decrese is too low - if value - step < min { - max - } else { - value - step - } -} - -impl<'a, T, Message> From> for Element<'a, Message> -where - Message: Clone + 'static, - T: Add + Sub + PartialEq + PartialOrd + Display + Copy -{ - fn from(this: SpinButton<'a, T, Message>) -> Self { - //! Matching on the direction enum given by the developer when the - //! widget is initially created in the application's view function. - - // NOTE: I can add a bult-in function that can toggle the widget - // enabled or disabled if the feature is requested. However, for - // the first implementation, I didn't want to go into the complexity - // of doing so. - match this.direction { - Direction::Horizontal => { - // Create a spinner container variable that contains the row with all of - // the combined widgets that make up the widget. - let spinner_container = row::with_children(vec![ - // Using the title4 variant of text, just like the original spin button did. - text::title4(this.label.clone()) - .apply(container) - .center_x(Length::Fill) - .align_y(Alignment::Center) - .into(), - // Using an button instead of an icon for the decrement functionality. - button::icon(icon::from_name("list-remove-symbolic")) - .padding([0, 12]) - .on_press((this.on_press)(decrement::( - this.step, this.value, this.min, this.max, - ))) - .into(), - // Using the title4 variant of text for consistency. - text::title4(format!("{}", this.value)) - .apply(container) - .center_x(Length::Fixed(48.0)) - .align_y(Alignment::Center) - .into(), - // Using another button for the increment functionality. - button::icon(icon::from_name("list-add-symbolic")) - .padding([0, 12]) - .on_press((this.on_press)(increment::( - this.step, this.value, this.min, this.max, - ))) - .into(), - ]) - .align_y(Alignment::Center); - - // Return the horizontal spin button from the match statement. - Self::new(spinner_container) - }, - Direction::Vertical => { - // Create a text widget that holds the value - let val_text = text(format!("{}", this.value)).size(14); - // Create a spinner container variable that contains the column with all of - // the combined widgets that make up the widget. - let spinner_container = column::with_capacity(3) - .push( - // Use a button for the increment functionality - button::icon(icon::from_name("list-add-symbolic")) - .padding([0, 12]) - .on_press((this.on_press)(increment::( - this.step, this.value, this.min, this.max, - ))), - ) - // Add the text widget that holds the current value - .push(val_text) - .push( - // Use a button for the decrement functionality - button::icon(icon::from_name("list-remove-symbolic")) - .padding([0, 12]) - .on_press((this.on_press)(decrement::( - this.step, this.value, this.min, this.max, - ))), - ) - .align_x(Horizontal::Center); - - // Create a column that contains two rows: - // First Row -> The label/title for the spin button. - // Second Row -> The spin button container from above. - let content_list = column::with_children(vec![ - row::with_capacity(1).push(text(this.label)).into(), - row::with_children(vec![Element::from(spinner_container)]).into(), - ]) - .width(75) - .padding([8, 0]) - .align_x(Alignment::Center); - - // Return the vertical spin button from the match statement. - Self::new(content_list) - } - } - } -} \ No newline at end of file