From 10849a046e7bb3a48200d6fad30a82457b4163a4 Mon Sep 17 00:00:00 2001 From: Szymon Wiszczuk Date: Sun, 7 Jan 2024 17:22:37 +0100 Subject: [PATCH] feat: implement basic input styling --- examples/widget-gallery/src/btn.rs | 166 ++++++++++++++ examples/widget-gallery/src/checkboxes.rs | 89 ++++++++ examples/widget-gallery/src/inputs.rs | 71 ++++++ examples/widget-gallery/src/main.rs | 265 +++------------------- oxytail-base/src/themes.rs | 81 +------ oxytail-base/src/widgets/button.rs | 4 +- oxytail-base/src/widgets/text_input.rs | 33 ++- oxytail-theme-dark/src/lib.rs | 56 ++++- 8 files changed, 442 insertions(+), 323 deletions(-) create mode 100644 examples/widget-gallery/src/btn.rs create mode 100644 examples/widget-gallery/src/checkboxes.rs create mode 100644 examples/widget-gallery/src/inputs.rs diff --git a/examples/widget-gallery/src/btn.rs b/examples/widget-gallery/src/btn.rs new file mode 100644 index 0000000..0d27ced --- /dev/null +++ b/examples/widget-gallery/src/btn.rs @@ -0,0 +1,166 @@ +use floem::{ + view::View, + views::{label, stack, v_stack, Decorators}, +}; +use oxytail_base::widgets::{ + button::{button, ButtonProps}, + common_props::{OxySize, OxyVariant}, +}; + +pub fn btn_variants() -> impl View { + v_stack(( + label(|| "Button variants"), + stack(( + button(|| "Default", None), + button( + || "Neutral", + Some(ButtonProps { + variant: OxyVariant::Neutral, + ..Default::default() + }), + ), + button( + || "Primary", + Some(ButtonProps { + variant: OxyVariant::Primary, + ..Default::default() + }), + ), + button( + || "Secondary", + Some(ButtonProps { + variant: OxyVariant::Secondary, + ..Default::default() + }), + ), + button( + || "Accent", + Some(ButtonProps { + variant: OxyVariant::Accent, + ..Default::default() + }), + ), + button( + || "Ghost", + Some(ButtonProps { + variant: OxyVariant::Ghost, + ..Default::default() + }), + ), + button( + || "Link", + Some(ButtonProps { + variant: OxyVariant::Link, + ..Default::default() + }), + ), + button( + || "Info", + Some(ButtonProps { + variant: OxyVariant::Info, + ..Default::default() + }), + ), + button( + || "Success", + Some(ButtonProps { + variant: OxyVariant::Success, + ..Default::default() + }), + ), + button( + || "Warning", + Some(ButtonProps { + variant: OxyVariant::Warning, + ..Default::default() + }), + ), + button( + || "Error", + Some(ButtonProps { + variant: OxyVariant::Error, + ..Default::default() + }), + ), + )) + .style(|s| s.gap(4., 4.)), + )) +} + +pub fn btn_sizes() -> impl View { + v_stack(( + label(|| "Button sizes"), + stack(( + button( + || "Large", + Some(ButtonProps { + size: OxySize::Large, + ..Default::default() + }), + ), + button( + || "Normal", + Some(ButtonProps { + size: OxySize::Normal, + ..Default::default() + }), + ), + button( + || "Small", + Some(ButtonProps { + size: OxySize::Small, + ..Default::default() + }), + ), + button( + || "Tiny", + Some(ButtonProps { + size: OxySize::Tiny, + ..Default::default() + }), + ), + )) + .style(|s| s.gap(4., 4.)), + )) +} + +pub fn btn_outlines() -> impl View { + v_stack(( + label(|| "Outlined buttons"), + stack(( + button( + || "Info", + Some(ButtonProps { + variant: OxyVariant::Info, + outlined: true, + ..Default::default() + }), + ), + button( + || "Success", + Some(ButtonProps { + variant: OxyVariant::Success, + outlined: true, + ..Default::default() + }), + ), + button( + || "Warning", + Some(ButtonProps { + variant: OxyVariant::Warning, + outlined: true, + ..Default::default() + }), + ), + button( + || "Error", + Some(ButtonProps { + variant: OxyVariant::Error, + outlined: true, + ..Default::default() + }), + ), + )) + .style(|s| s.gap(4., 4.)), + )) +} diff --git a/examples/widget-gallery/src/checkboxes.rs b/examples/widget-gallery/src/checkboxes.rs new file mode 100644 index 0000000..b688f2e --- /dev/null +++ b/examples/widget-gallery/src/checkboxes.rs @@ -0,0 +1,89 @@ +use floem::{ + reactive::create_signal, + view::View, + views::{h_stack, label, v_stack, Decorators}, +}; +use oxytail_base::widgets::{ + checkbox::{checkbox, CheckboxProps}, + common_props::{OxySize, OxyVariant}, +}; + +fn checkbox_with_state(props: Option) -> impl View { + let (checked, set_checked) = create_signal(true); + + checkbox(checked, props).on_click_stop(move |_| { + set_checked.update(|checked| *checked = !*checked); + }) +} + +pub fn checkboxes_sizes() -> impl View { + v_stack(( + label(|| "Checkbox sizes"), + h_stack(( + checkbox_with_state(Some(CheckboxProps { + size: OxySize::Large, + ..Default::default() + })), + checkbox_with_state(None), + checkbox_with_state(Some(CheckboxProps { + size: OxySize::Small, + ..Default::default() + })), + checkbox_with_state(Some(CheckboxProps { + size: OxySize::Tiny, + ..Default::default() + })), + )) + .style(|s| s.gap(4., 4.)), + )) +} + +pub fn checkboxes_variants() -> impl View { + v_stack(( + label(|| "Checkbox variants"), + h_stack(( + checkbox_with_state(None), + checkbox_with_state(Some(CheckboxProps { + variant: OxyVariant::Neutral, + ..Default::default() + })), + checkbox_with_state(Some(CheckboxProps { + variant: OxyVariant::Primary, + ..Default::default() + })), + checkbox_with_state(Some(CheckboxProps { + variant: OxyVariant::Secondary, + ..Default::default() + })), + checkbox_with_state(Some(CheckboxProps { + variant: OxyVariant::Accent, + ..Default::default() + })), + checkbox_with_state(Some(CheckboxProps { + variant: OxyVariant::Ghost, + ..Default::default() + })), + checkbox_with_state(Some(CheckboxProps { + variant: OxyVariant::Link, + ..Default::default() + })), + checkbox_with_state(Some(CheckboxProps { + variant: OxyVariant::Info, + ..Default::default() + })), + checkbox_with_state(Some(CheckboxProps { + variant: OxyVariant::Success, + ..Default::default() + })), + checkbox_with_state(Some(CheckboxProps { + variant: OxyVariant::Warning, + ..Default::default() + })), + checkbox_with_state(Some(CheckboxProps { + variant: OxyVariant::Error, + ..Default::default() + })), + )) + .style(|s| s.gap(4., 4.)), + )) +} diff --git a/examples/widget-gallery/src/inputs.rs b/examples/widget-gallery/src/inputs.rs new file mode 100644 index 0000000..a0d0269 --- /dev/null +++ b/examples/widget-gallery/src/inputs.rs @@ -0,0 +1,71 @@ +use floem::{ + reactive::create_rw_signal, + view::View, + views::{h_stack, label, Decorators}, +}; +use oxytail_base::widgets::{ + common_props::{OxySize, OxyVariant}, + text_input::{text_input, InputProps}, +}; + +pub fn text_input_variants() -> impl View { + let default_text = create_rw_signal(String::from("I am default!")); + let primary_text = create_rw_signal(String::from("I am primary!")); + let secondary_text = create_rw_signal(String::from("I am secondary!")); + + h_stack(( + label(|| "Text input variants(same as buttons, only a few shown)"), + text_input(default_text, None), + text_input( + primary_text, + Some(InputProps { + variant: OxyVariant::Primary, + ..Default::default() + }), + ), + text_input( + secondary_text, + Some(InputProps { + variant: OxyVariant::Secondary, + ..Default::default() + }), + ), + )) + .style(|s| s.justify_start().items_start().gap(5., 5.)) +} + +pub fn text_input_sizes() -> impl View { + let large_text = create_rw_signal(String::from("I am large!")); + let normal_text = create_rw_signal(String::from("I am normal!")); + let small_text = create_rw_signal(String::from("I am small!")); + let tiny_text = create_rw_signal(String::from("I am tiny!")); + + h_stack(( + label(|| "Text input sizes"), + h_stack(( + text_input( + large_text, + Some(InputProps { + size: OxySize::Large, + ..Default::default() + }), + ), + text_input(normal_text, None), + text_input( + small_text, + Some(InputProps { + size: OxySize::Small, + ..Default::default() + }), + ), + text_input( + tiny_text, + Some(InputProps { + size: OxySize::Tiny, + ..Default::default() + }), + ), + )) + .style(|s| s.gap(5., 5.).margin_left(10.)), + )) +} diff --git a/examples/widget-gallery/src/main.rs b/examples/widget-gallery/src/main.rs index e92ea32..0ca3bf2 100644 --- a/examples/widget-gallery/src/main.rs +++ b/examples/widget-gallery/src/main.rs @@ -1,254 +1,47 @@ +use btn::{btn_outlines, btn_sizes, btn_variants}; +use checkboxes::{checkboxes_sizes, checkboxes_variants}; use floem::{ kurbo::Size, peniko::Color, reactive::create_signal, view::View, - views::{h_stack, label, stack, v_stack, Decorators}, - widgets::slider::slider, + views::{label, v_stack, Decorators}, window::WindowConfig, - Application, EventPropagation, + Application, }; + +use inputs::{text_input_sizes, text_input_variants}; use oxytail_base::{ init_theme, - widgets::{ - button::{button, ButtonProps}, - checkbox::labeled_checkbox, - checkbox::{checkbox, CheckboxProps}, - common_props::{OxySize, OxyVariant}, - }, + widgets::{checkbox::labeled_checkbox, checkbox::CheckboxProps, common_props::OxyVariant}, }; use oxytail_theme_dark::Theme; - -fn checkbox_with_state(props: Option) -> impl View { - let (checked, set_checked) = create_signal(true); - - checkbox(checked, props).on_click_stop(move |_| { - set_checked.update(|checked| *checked = !*checked); - }) -} +mod btn; +mod checkboxes; +mod inputs; fn app_view() -> impl View { let (checked, set_checked) = create_signal(true); - stack((v_stack(( + v_stack(( label(|| "Buttons").style(|s| { s.width_full() .font_size(20.) .items_center() .justify_center() }), - label(|| "Button variants"), - stack(( - button(|| "Default", None), - button( - || "Neutral", - Some(ButtonProps { - variant: OxyVariant::Neutral, - ..Default::default() - }), - ), - button( - || "Primary", - Some(ButtonProps { - variant: OxyVariant::Primary, - ..Default::default() - }), - ), - button( - || "Secondary", - Some(ButtonProps { - variant: OxyVariant::Secondary, - ..Default::default() - }), - ), - button( - || "Accent", - Some(ButtonProps { - variant: OxyVariant::Accent, - ..Default::default() - }), - ), - button( - || "Ghost", - Some(ButtonProps { - variant: OxyVariant::Ghost, - ..Default::default() - }), - ), - button( - || "Link", - Some(ButtonProps { - variant: OxyVariant::Link, - ..Default::default() - }), - ), - button( - || "Info", - Some(ButtonProps { - variant: OxyVariant::Info, - ..Default::default() - }), - ), - button( - || "Success", - Some(ButtonProps { - variant: OxyVariant::Success, - ..Default::default() - }), - ), - button( - || "Warning", - Some(ButtonProps { - variant: OxyVariant::Warning, - ..Default::default() - }), - ), - button( - || "Error", - Some(ButtonProps { - variant: OxyVariant::Error, - ..Default::default() - }), - ), - )) - .style(|s| s.gap(4., 4.)), - label(|| "Button sizes"), - stack(( - button( - || "Large", - Some(ButtonProps { - size: OxySize::Large, - ..Default::default() - }), - ), - button( - || "Normal", - Some(ButtonProps { - size: OxySize::Normal, - ..Default::default() - }), - ), - button( - || "Small", - Some(ButtonProps { - size: OxySize::Small, - ..Default::default() - }), - ), - button( - || "Tiny", - Some(ButtonProps { - size: OxySize::Tiny, - ..Default::default() - }), - ), - )) - .style(|s| s.gap(4., 4.)), - label(|| "Outlined buttons"), - stack(( - button( - || "Info", - Some(ButtonProps { - variant: OxyVariant::Info, - outlined: true, - ..Default::default() - }), - ), - button( - || "Success", - Some(ButtonProps { - variant: OxyVariant::Success, - outlined: true, - ..Default::default() - }), - ), - button( - || "Warning", - Some(ButtonProps { - variant: OxyVariant::Warning, - outlined: true, - ..Default::default() - }), - ), - button( - || "Error", - Some(ButtonProps { - variant: OxyVariant::Error, - outlined: true, - ..Default::default() - }), - ), - )) - .style(|s| s.gap(4., 4.)), + btn_variants(), + btn_sizes(), + btn_outlines(), label(|| "Checkboxes").style(|s| { s.width_full() .font_size(20.) .items_center() .justify_center() }), - label(|| "Checkbox sizes"), - h_stack(( - checkbox_with_state(Some(CheckboxProps { - size: OxySize::Large, - ..Default::default() - })), - checkbox_with_state(None), - checkbox_with_state(Some(CheckboxProps { - size: OxySize::Small, - ..Default::default() - })), - checkbox_with_state(Some(CheckboxProps { - size: OxySize::Tiny, - ..Default::default() - })), - )) - .style(|s| s.gap(4., 4.)), - label(|| "Checkbox variants"), - h_stack(( - checkbox_with_state(None), - checkbox_with_state(Some(CheckboxProps { - variant: OxyVariant::Neutral, - ..Default::default() - })), - checkbox_with_state(Some(CheckboxProps { - variant: OxyVariant::Primary, - ..Default::default() - })), - checkbox_with_state(Some(CheckboxProps { - variant: OxyVariant::Secondary, - ..Default::default() - })), - checkbox_with_state(Some(CheckboxProps { - variant: OxyVariant::Accent, - ..Default::default() - })), - checkbox_with_state(Some(CheckboxProps { - variant: OxyVariant::Ghost, - ..Default::default() - })), - checkbox_with_state(Some(CheckboxProps { - variant: OxyVariant::Link, - ..Default::default() - })), - checkbox_with_state(Some(CheckboxProps { - variant: OxyVariant::Info, - ..Default::default() - })), - checkbox_with_state(Some(CheckboxProps { - variant: OxyVariant::Success, - ..Default::default() - })), - checkbox_with_state(Some(CheckboxProps { - variant: OxyVariant::Warning, - ..Default::default() - })), - checkbox_with_state(Some(CheckboxProps { - variant: OxyVariant::Error, - ..Default::default() - })), - )) - .style(|s| s.gap(4., 4.)), - label(|| "Labeled checkboxes"), + checkboxes_sizes(), + checkboxes_variants(), v_stack(( + label(|| "Labeled checkboxes"), labeled_checkbox(checked, || "I am the default!", None).on_click_stop(move |_| { set_checked.update(|checked| *checked = !*checked); }), @@ -264,20 +57,28 @@ fn app_view() -> impl View { set_checked.update(|checked| *checked = !*checked); }), )) - .style(|s| s.justify_start().items_start().gap(5., 5.)), // label(|| "LABELED CHECKBOXES"), - // labeled_checkbox(checked, || "oxytail labeled", None).on_click_stop(move |_| { - // set_checked.update(|checked| *checked = !*checked); - // }), - // label(|| "SLIDERS"), - // slider(|| 50.0).style(|s| s.height(15).width(200)), - )),)) + .style(|s| s.justify_start().items_start().gap(5., 5.)), + label(|| "Text inputs").style(|s| { + s.width_full() + .font_size(20.) + .items_center() + .justify_center() + }), + text_input_variants(), + text_input_sizes().style(|s| s.margin_top(20.)), + // labeled_checkbox(checked, || "oxytail labeled", None).on_click_stop(move |_| { + // set_checked.update(|checked| *checked = !*checked); + // }), + // label(|| "SLIDERS"), + // slider(|| 50.0).style(|s| s.height(15).width(200)), + )) } fn main() { let window_config = WindowConfig::default() .size(Size { width: 1200.0, - height: 500.0, + height: 800.0, }) .themed(false); diff --git a/oxytail-base/src/themes.rs b/oxytail-base/src/themes.rs index aa8866e..2dffef6 100644 --- a/oxytail-base/src/themes.rs +++ b/oxytail-base/src/themes.rs @@ -1,81 +1,6 @@ -use floem::{peniko::Color, style::Style}; +use floem::style::Style; -use crate::widgets::{button::ButtonProps, checkbox::CheckboxProps}; - -// IDEA: Allow any style that implements ThemeStyle to be a valid style? -// So that external styles can be created -// TODO: Figure out a nice "stylesheet" that would create a theme - -/* -This should work like the following - -1. User ".enhance(Theme)"s their style -2. fn enhance() reads all the functions declared below to determine the stylesheets and how they should look like - -*/ - -/* -CURRENT LIMITATION: all the stylesheets need to have pre-defined types, so the "stylesheet" provided will have to use the same naming etc. -Classes is the same across themes; only what the class name maps to changes. -This should be ok tho. There needs to be a line somewhere. -*/ - -// pub trait StyleEnhancer { -// fn enhance(self) -> Self; -// } - -// impl StyleEnhancer for Style { -// fn enhance(mut self) -> Self { -// let selected_theme = GLOBAL_THEME.get().expect("The theme is uninitialized. Did you forget to run `init_theme(Theme)` in your `main` function?"); -// match selected_theme { -// Theme::Dark => { -// let border = Color::rgb8(140, 140, 140); -// let padding = 5.0; -// let border_radius = 5.0; - -// let hover_bg_color = Color::rgb8(20, 25, 30); -// let focus_hover_bg_color = Color::rgb8(20, 25, 30); -// let active_bg_color = Color::rgb8(20, 25, 30); -// let light_hover_bg_color = Color::rgb8(250, 252, 248); -// let light_focus_hover_bg_color = Color::rgb8(250, 249, 251); - -// let focus_applied_style = Style::new().border_color(Color::rgb8(114, 74, 140)); -// let focus_visible_applied_style = Style::new().outline(3.0); - -// let focus_style = Style::new() -// .outline_color(Color::rgba8(213, 208, 216, 150)) -// .focus(|_| focus_applied_style.clone()) -// .focus_visible(|_| focus_visible_applied_style.clone()); - -// let border_style = Style::new() -// .disabled(|s| s.border_color(Color::rgb8(131, 145, 123).with_alpha_factor(0.3))) -// .border(1.0) -// .border_color(border) -// .padding(padding) -// .border_radius(border_radius) -// .apply(focus_style.clone()); - -// let base_labeled_checkbox_style = Style::new() -// .gap(padding, 0.0) -// .hover(|s| s.background(hover_bg_color)) -// .padding(padding) -// .transition(Background, Transition::linear(0.04)) -// .border_radius(border_radius) -// .active(|s| s.class(OxyCheckboxClass, |s| s.background(active_bg_color))) -// .focus(|s| { -// s.class(OxyCheckboxClass, |_| focus_applied_style.clone()) -// .hover(|s| s.background(focus_hover_bg_color)) -// }) -// .disabled(|s| { -// s.color(Color::GRAY).class(OxyCheckboxClass, |s| { -// s.background(Color::rgb8(180, 188, 175).with_alpha_factor(0.3)) -// .color(Color::GRAY) -// .hover(|s| { -// s.background(Color::rgb8(180, 188, 175).with_alpha_factor(0.3)) -// }) -// }) -// }) -// .apply(focus_style.clone()); +use crate::widgets::{button::ButtonProps, checkbox::CheckboxProps, text_input::InputProps}; // let input_style = Style::new() // .background(Color::WHITE) @@ -122,7 +47,7 @@ pub trait ThemeStyling { /// To be implemented by themes; Defines how a button style should look like. fn get_button_style(&self, button_props: ButtonProps) -> Box Style>; fn get_checkbox_style(&self, checkbox_props: CheckboxProps) -> Box Style>; - // fn get_labeled_checkbox_style(&self, labeled_checkbox_style: ) -> Box Style>; + fn get_input_style(&self, checkbox_props: InputProps) -> Box Style>; } pub struct ButtonStyle { diff --git a/oxytail-base/src/widgets/button.rs b/oxytail-base/src/widgets/button.rs index 90ded63..3cea6cd 100644 --- a/oxytail-base/src/widgets/button.rs +++ b/oxytail-base/src/widgets/button.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use floem::{style::Style, view::View, views::Decorators, widgets::button as upstreambutton}; -use crate::GLOBAL_THEME; +use crate::get_current_theme; use super::common_props::{OxySize, OxyVariant}; @@ -18,7 +18,7 @@ pub fn button( props: Option, ) -> impl View { let base_widget = upstreambutton(label); - let theme = GLOBAL_THEME.get().unwrap(); + let theme = get_current_theme(); let props = props.unwrap_or(ButtonProps::default()); diff --git a/oxytail-base/src/widgets/text_input.rs b/oxytail-base/src/widgets/text_input.rs index 0b72dc9..88a7cc2 100644 --- a/oxytail-base/src/widgets/text_input.rs +++ b/oxytail-base/src/widgets/text_input.rs @@ -1,5 +1,32 @@ -use floem::{reactive::RwSignal, views::TextInput, widgets::text_input as upstream_text_input}; +use floem::{ + reactive::RwSignal, + style::Style, + views::{Decorators, TextInput}, + widgets::text_input as upstream_text_input, +}; -pub fn text_input(buffer: RwSignal) -> TextInput { - upstream_text_input(buffer) //.class(OxyTextInputClass) +use crate::get_current_theme; + +use super::common_props::{OxySize, OxyVariant}; + +#[derive(Default, Clone, Copy)] +pub struct InputProps { + pub variant: OxyVariant, + pub size: OxySize, +} + +pub fn text_input(buffer: RwSignal, props: Option) -> TextInput { + let base_widget = upstream_text_input(buffer); + + let theme = get_current_theme(); + + let props = props.unwrap_or(InputProps::default()); + + let styles_enhancer = theme.get_input_style(props); + let enhanced_style = styles_enhancer(Style::new()); + + // .style() does not exist on an input + let styled_input = base_widget.base_style(move |_| enhanced_style.clone()); + + styled_input } diff --git a/oxytail-theme-dark/src/lib.rs b/oxytail-theme-dark/src/lib.rs index 4920c78..db10b9b 100644 --- a/oxytail-theme-dark/src/lib.rs +++ b/oxytail-theme-dark/src/lib.rs @@ -1,7 +1,7 @@ use floem::{ cosmic_text::Weight, peniko::Color, - style::{Background, Style, StyleValue, Transition}, + style::{Background, CursorStyle, Style, StyleValue, Transition}, }; use oxytail_base::{ themes::ThemeStyling, @@ -9,6 +9,7 @@ use oxytail_base::{ button::ButtonProps, checkbox::CheckboxProps, common_props::{OxySize, OxyVariant}, + text_input::InputProps, }, }; @@ -166,15 +167,8 @@ impl ThemeStyling for Theme { let style_creator = move |s: Style| { let curr_variant_color = get_variant_colors(checkbox_props.variant); let base_checkbox_style = s - // .width(20.) - // .height(20.) .background(Color::rgb8(166, 174, 188)) - // .active(|s| s.background(active_bg_color)) .transition(Background, Transition::linear(0.04)) - // .hover(|s| s.background(hover_bg_color)) - // .focus(|s| s.hover(|s| s.background(focus_hover_bg_color))) - // .apply(border_style.clone()) - // .apply(focus_style.clone()) .disabled(|s| { s.background(Color::rgb8(180, 188, 175).with_alpha_factor(0.3)) .color(Color::GRAY) @@ -203,4 +197,50 @@ impl ThemeStyling for Theme { Box::new(style_creator) } + + fn get_input_style(&self, checkbox_props: InputProps) -> Box Style> { + let reusables = self.get_reusables(); + + let style_creator = move |s: Style| { + let input_style = s + .cursor(CursorStyle::Text) + .outline(0.4) + .border_radius(0.5 * 16.) + .disabled(|s| { + s.background(Color::rgb8(180, 188, 175).with_alpha_factor(0.3)) + .color(Color::GRAY) + }) + .items_center() + .justify_center(); + let curr_variant_color = get_variant_colors(checkbox_props.variant); + + let variant_enhancer = |s: Style| match checkbox_props.variant { + OxyVariant::Default => s.outline_color(reusables.checkbox_default_color), + OxyVariant::Neutral => s.outline_color(reusables.checkbox_default_color), + _ => s.outline_color(curr_variant_color), + }; + let size_enhancer = |s: Style| match checkbox_props.size { + OxySize::Large => s + .height(4 * 16) + .font_size(16. * 1.125) + .padding_horiz(1.5 * 16.), + OxySize::Normal => s.height(3 * 16).padding_horiz(1 * 16), + OxySize::Small => s + .height(2 * 16) + .font_size(16. * 0.875) + .padding_horiz(0.75 * 16.), + OxySize::Tiny => s + .height(1.5 * 16.) + .font_size(16. * 0.75) + .padding_horiz(0.5 * 16.), + }; + + let enhanced_style = variant_enhancer(input_style); + let enhanced_style = size_enhancer(enhanced_style); + + enhanced_style + }; + + Box::new(style_creator) + } }