From c8aabca37dbbbcd2469b8df7161b5a163c85a277 Mon Sep 17 00:00:00 2001 From: Bryan Hyland Date: Sun, 17 Nov 2024 12:28:32 -0800 Subject: [PATCH] Updated spin button widget and example usage --- examples/spin-button/src/main.rs | 27 ++-- iced | 2 +- src/app/mod.rs | 2 +- src/widget/spin_button/mod.rs | 238 +++++++++++++++++-------------- 4 files changed, 146 insertions(+), 123 deletions(-) diff --git a/examples/spin-button/src/main.rs b/examples/spin-button/src/main.rs index cc6b4f84..f3d1f4ed 100644 --- a/examples/spin-button/src/main.rs +++ b/examples/spin-button/src/main.rs @@ -1,5 +1,5 @@ use cosmic::widget::divider::horizontal; -use cosmic::widget::{button, container, text, spin_button, spin_button::Direction}; +use cosmic::widget::{button, container, text, spin_button, spin_button::Orientation}; use cosmic::{ app::{Core, Task}, iced::{ @@ -10,7 +10,7 @@ use cosmic::{ Application, Element, }; -pub struct VertSpinnerApp { +pub struct SpinButtonExamplApp { core: Core, i8_num: i8, i16_num: i16, @@ -34,12 +34,12 @@ pub enum SpinBtnMessages { UpdateSpinnerMsg, } -impl Application for VertSpinnerApp { +impl Application for SpinButtonExamplApp { type Executor = cosmic::executor::Default; type Flags = (); type Message = SpinBtnMessages; - const APP_ID: &'static str = "com.system76.VertSpinnerExample"; + const APP_ID: &'static str = "com.system76.SpinButtonExample"; fn core(&self) -> &Core { &self.core @@ -68,10 +68,7 @@ impl Application for VertSpinnerApp { fn update(&mut self, message: Self::Message) -> Task { match message { - SpinBtnMessages::UpdateI8Num(new_i8) => { - self.i8_num = new_i8; - SpinBtnMessages::UpdateSpinnerMsg; - }, + SpinBtnMessages::UpdateI8Num(new_i8) => self.i8_num = new_i8, 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, @@ -95,7 +92,7 @@ impl Application for VertSpinnerApp { self.i8_num, // current value, this is also what's displayed in the center of the widget -5, // minimum value, if decremented below this the widget's current value rolls to the max value 5, // maximum value, if incremented above this the widget's current value rolls to the min value - Direction::Vertical, // oreintation of the widget + Some(Orientation::Vertical), // oreintation of the widget SpinBtnMessages::UpdateI8Num // message to send to the application's update function ), spin_button( @@ -104,7 +101,7 @@ impl Application for VertSpinnerApp { self.i16_num, 0, 10, - Direction::Vertical, + Some(Orientation::Vertical), SpinBtnMessages::UpdateI16Num ), spin_button( @@ -113,7 +110,7 @@ impl Application for VertSpinnerApp { self.i32_num, 0, 12, - Direction::Vertical, + Some(Orientation::Vertical), SpinBtnMessages::UpdateI32Num ), spin_button( @@ -122,7 +119,7 @@ impl Application for VertSpinnerApp { self.i64_num, 15, 35, - Direction::Vertical, + Some(Orientation::Vertical), SpinBtnMessages::UpdateI64Num ), ] @@ -136,7 +133,7 @@ impl Application for VertSpinnerApp { self.i128_num, -1000, 500, - Direction::Horizontal, + None, // Passing None gives a default of a Horizontal Spin Button widget SpinBtnMessages::UpdateI128Num ), ], @@ -148,7 +145,7 @@ impl Application for VertSpinnerApp { self.f32_num, -35.3, 12.3, - Direction::Horizontal, + Some(Orientation::Horizontal), // It can also be explicitly passed as Orientation::Horizontal SpinBtnMessages::UpdateF32Num ) ], @@ -160,7 +157,7 @@ impl Application for VertSpinnerApp { self.f64_num, 0.0, 3.0, - Direction::Horizontal, + None, SpinBtnMessages::UpdateF64Num ) ], diff --git a/iced b/iced index 25686357..d0c2a037 160000 --- a/iced +++ b/iced @@ -1 +1 @@ -Subproject commit 256863574bacfb1d2797c2a48cba7a3388cbeb59 +Subproject commit d0c2a0371b476866cb0f7045fba0215425493d62 diff --git a/src/app/mod.rs b/src/app/mod.rs index e4b59d62..6c45cc3f 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -102,7 +102,7 @@ pub(crate) fn iced_settings( iced.default_font = settings.default_font; iced.default_text_size = iced::Pixels(settings.default_text_size); let exit_on_close = settings.exit_on_close; - iced.is_daemon = false; + //iced.is_daemon = false; iced.exit_on_close_request = settings.is_daemon; let mut window_settings = iced::window::Settings::default(); window_settings.exit_on_close_request = exit_on_close; diff --git a/src/widget/spin_button/mod.rs b/src/widget/spin_button/mod.rs index bb92b546..fb98b2ed 100644 --- a/src/widget/spin_button/mod.rs +++ b/src/widget/spin_button/mod.rs @@ -1,16 +1,17 @@ // Copyright 2022 System76 // SPDX-License-Identifier: MPL-2.0 +// Updated by Bryan Hyland +// Updated on: 17Nov24 + //! A control for incremental adjustments of a value. -use std::borrow::{Borrow, Cow}; -use std::fmt::{Display, Formatter}; +use std::fmt::Display; use std::ops::{Add, Sub}; use std::marker::PhantomData; use apply::Apply; -use fraction::Decimal; use iced::alignment::Horizontal; -use iced::{Alignment, Border, Length, Shadow}; +use iced::{Alignment, Length}; use crate::{ Element, widget::{ @@ -23,7 +24,8 @@ use crate::{ }, }; -pub enum Direction { +#[derive(Clone, Copy)] +pub enum Orientation { Horizontal, Vertical, } @@ -32,17 +34,23 @@ pub struct SpinButton<'a, T, M> where T: Add + Sub + PartialEq + PartialOrd + Display + Copy { + /// The label that the spin button widget will have. + /// It is placed on the top of and centered on the spin button widget itself. label: String, /// The amount to increment or decrement the value. - pub step: T, + step: T, /// The current value of the spin button. - pub value: T, + /// It is displayed in the center of the spin button widget, no matter the orientation. + value: T, /// The minimum value permitted. - pub min: T, + /// If the value is decremented below this value the current value will rollover to the max value. + min: T, /// The maximum value permitted. - pub max: T, + /// If the value is incremented above this value the current value will rollover to the min value. + max: T, /// The direction that the spin button is laid out; Horizontal (default) or Vertical - pub direction: Direction, + orientation: Option, + /// The message that the spin button emits to the application's update function. on_press: Box M>, phantom_data: PhantomData<&'a M>, } @@ -58,7 +66,7 @@ where value: T, min: T, max: T, - direction: Direction, + orientation: Option, on_press: impl Fn(T) -> M + 'static, ) -> Self { Self { @@ -67,7 +75,7 @@ where value, min, max, - direction, + orientation, on_press: Box::from(on_press), phantom_data: PhantomData, } @@ -81,13 +89,13 @@ pub fn spin_button<'a, T, M>( value: T, min: T, max: T, - direction: Direction, + orientation: Option, on_press: impl Fn(T) -> M + 'static, ) -> SpinButton<'a, T, M> where T: Add + Sub + PartialEq + PartialOrd + Display + Copy { - SpinButton::new(label, step, value, min, max, direction, on_press) + SpinButton::new(label, step, value, min, max, orientation, on_press) } fn increment(step: T, value: T, min: T, max: T) -> T @@ -120,100 +128,118 @@ where 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. - 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) + // Matching on the direction enum given by the developer when the + // widget is initially created in the application's view function. + match this.orientation { + Some(orien) => match orien { + Orientation::Horizontal => create_horizontal_spin_button(&this), + Orientation::Vertical => create_vertical_spin_button(&this) } + // Default behavior is to create a horizontal spin button widget + None => create_horizontal_spin_button(&this), } } } +// Helper Functions +// Create a horizontal spin button +// Implemented to make the creation easier to read in the from function for Element implementation. +fn create_horizontal_spin_button<'a, T, Message>(spin_btn: &SpinButton) -> Element<'a, Message> +where + Message: Clone + 'static, + T: Add + Sub + PartialEq + PartialOrd + Display + Copy +{ + // 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(spin_btn.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((spin_btn.on_press)(decrement::( + spin_btn.step, spin_btn.value, spin_btn.min, spin_btn.max, + )) + ).into(), + // Using the title4 variant of text for consistency. + text::title4(format!("{}", spin_btn.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((spin_btn.on_press)(increment::( + spin_btn.step, spin_btn.value, spin_btn.min, spin_btn.max, + )) + ).into(), + ] + ) + .align_y(Alignment::Center) + ) + .align_x(Alignment::Center); + + // Return the horizontal spin button from the match statement. + Element::new(spinner_container) +} + +// Used to create a vertical spin button widget. +// Implemented to make the creation easier to read in the from function for Element implementation. +fn create_vertical_spin_button<'a, T, Message>(spin_btn: &SpinButton) -> Element<'a, Message> +where + Message: Clone + 'static, + T: Add + Sub + PartialEq + PartialOrd + Display + Copy +{ + // Create a text widget that holds the value + let val_text = text(format!("{}", spin_btn.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((spin_btn.on_press)(increment::( + spin_btn.step, spin_btn.value, spin_btn.min, spin_btn.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((spin_btn.on_press)(decrement::( + spin_btn.step, spin_btn.value, spin_btn.min, spin_btn.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(spin_btn.label.clone())).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. + Element::new(content_list) +} \ No newline at end of file