From f89bb4ddbdc112a07db3c78a53007923859b5a9f Mon Sep 17 00:00:00 2001 From: Szymon Wiszczuk <37072867+golota60@users.noreply.github.com> Date: Sun, 28 Jan 2024 21:44:03 +0100 Subject: [PATCH] feat: badges (#20) --- examples/widget-gallery/Cargo.toml | 6 +- examples/widget-gallery/src/badges.rs | 85 +++++++++++++++++++++++++++ examples/widget-gallery/src/main.rs | 8 ++- oxytail-base/src/themes.rs | 5 +- oxytail-base/src/widgets/badge.rs | 27 +++++++++ oxytail-base/src/widgets/mod.rs | 1 + oxytail-theme-dark/Cargo.toml | 6 +- oxytail-theme-dark/src/lib.rs | 7 ++- oxytail-theme-defaults/Cargo.toml | 3 +- oxytail-theme-defaults/src/lib.rs | 58 +++++++++++++++++- 10 files changed, 196 insertions(+), 10 deletions(-) create mode 100644 examples/widget-gallery/src/badges.rs create mode 100644 oxytail-base/src/widgets/badge.rs diff --git a/examples/widget-gallery/Cargo.toml b/examples/widget-gallery/Cargo.toml index 670fde4..09d554f 100644 --- a/examples/widget-gallery/Cargo.toml +++ b/examples/widget-gallery/Cargo.toml @@ -8,6 +8,6 @@ rust-version = "1.75" [dependencies] im = "15.1.0" -oxytail-theme-dark = "0.1.1" -oxytail-base = "0.1.1" -floem = "0.1.1" \ No newline at end of file +oxytail-theme-dark = { path = "../../oxytail-theme-dark" } +oxytail-base = { path = "../../oxytail-base" } +floem = "0.1.1" diff --git a/examples/widget-gallery/src/badges.rs b/examples/widget-gallery/src/badges.rs new file mode 100644 index 0000000..bcb713b --- /dev/null +++ b/examples/widget-gallery/src/badges.rs @@ -0,0 +1,85 @@ +use floem::{ + style::Style, + view::View, + views::{h_stack, Decorators}, +}; +use oxytail_base::widgets::{ + badge::{badge, BadgeProps}, + common_props::{OxySize, OxyVariant}, +}; + +fn variant_badge(name: &'static str, variant: OxyVariant) -> impl View { + badge( + name, + Some(BadgeProps { + variant, + ..Default::default() + }), + ) +} + +pub fn badges_variants() -> impl View { + h_stack(( + variant_badge("default", OxyVariant::Default), + variant_badge("neutral", OxyVariant::Neutral), + variant_badge("primary", OxyVariant::Primary), + variant_badge("secondary", OxyVariant::Secondary), + variant_badge("accent", OxyVariant::Accent), + variant_badge("ghost", OxyVariant::Ghost), + variant_badge("link", OxyVariant::Link), + variant_badge("info", OxyVariant::Info), + variant_badge("success", OxyVariant::Success), + variant_badge("warning", OxyVariant::Warning), + variant_badge("error", OxyVariant::Error), + )) + .style(|s: Style| s.gap(4., 4.)) +} + +fn size_badge(name: &'static str, size: OxySize) -> impl View { + badge( + name, + Some(BadgeProps { + size, + ..Default::default() + }), + ) +} + +pub fn badges_sizes() -> impl View { + h_stack(( + size_badge("large", OxySize::Large), + size_badge("normal", OxySize::Normal), + size_badge("small", OxySize::Small), + size_badge("tiny", OxySize::Tiny), + )) + .style(|s: Style| s.gap(4., 4.)) +} + +pub fn badges_outlines() -> impl View { + h_stack(( + badge( + "outlined default", + Some(BadgeProps { + outlined: true, + ..Default::default() + }), + ), + badge( + "outlined primary", + Some(BadgeProps { + outlined: true, + variant: OxyVariant::Primary, + ..Default::default() + }), + ), + badge( + "outlined secondary", + Some(BadgeProps { + outlined: true, + variant: OxyVariant::Secondary, + ..Default::default() + }), + ), + )) + .style(|s: Style| s.gap(4., 4.)) +} diff --git a/examples/widget-gallery/src/main.rs b/examples/widget-gallery/src/main.rs index 825c855..c4c46f5 100644 --- a/examples/widget-gallery/src/main.rs +++ b/examples/widget-gallery/src/main.rs @@ -1,3 +1,4 @@ +use badges::{badges_outlines, badges_sizes, badges_variants}; use btn::{btn_outlines, btn_sizes, btn_variants}; use checkboxes::{checkboxes_sizes, checkboxes_variants, labeled_checkboxes}; use floem::{ @@ -30,6 +31,7 @@ use radio_buttons::{ use toggles::{toggle_sizes, toggle_variants}; use tooltips::{button_tooltips, label_tooltips, sizes_tooltips}; +pub mod badges; mod btn; mod checkboxes; pub mod headers; @@ -44,7 +46,7 @@ fn padded_container_box(child: impl View + 'static) -> ContainerBox { fn app_view() -> impl View { let tabs: im::Vector<&str> = vec![ - "Intro", "Header", "Button", "Radio", "Checkbox", "Input", "Tooltip", "Toggle", + "Intro", "Header", "Button", "Badge", "Radio", "Checkbox", "Input", "Tooltip", "Toggle", ] .into_iter() .collect(); @@ -165,6 +167,10 @@ fn app_view() -> impl View { s.width_full() }), "Button" => padded_container_box(v_stack((btn_variants(), btn_outlines(), btn_sizes()))), + "Badge" => padded_container_box(v_stack((text_header("Variants", None), + text_divider(),badges_variants(), text_header("Sizes", None), + text_divider(), badges_sizes(), text_header("Outlines", None), + text_divider(), badges_outlines()))), "Radio" => padded_container_box(v_stack((radio_variants(),radio_sizes(),labeled_radio_variants(),disabled_labeled_radio_variants()))), "Checkbox" => padded_container_box(v_stack(( checkboxes_variants(), diff --git a/oxytail-base/src/themes.rs b/oxytail-base/src/themes.rs index d0634e9..d651842 100644 --- a/oxytail-base/src/themes.rs +++ b/oxytail-base/src/themes.rs @@ -1,7 +1,7 @@ use floem::{peniko::Color, style::Style}; use crate::widgets::{ - button::ButtonProps, checkbox::CheckboxProps, common_props::OxyVariant, + badge::BadgeProps, button::ButtonProps, checkbox::CheckboxProps, common_props::OxyVariant, radio_button::RadioProps, text_header::HeaderProps, text_input::InputProps, toggle::ToggleProps, tooltip::TooltipProps, }; @@ -76,4 +76,7 @@ pub trait ThemeStyling { /// Defines how a `tooltip` should look like. fn get_tooltip_style(&self, tooltip_props: TooltipProps) -> Box Style + '_>; + + /// Defines how a `badge` should look like. + fn get_badge_style(&self, badge_props: BadgeProps) -> Box Style + '_>; } diff --git a/oxytail-base/src/widgets/badge.rs b/oxytail-base/src/widgets/badge.rs new file mode 100644 index 0000000..8623035 --- /dev/null +++ b/oxytail-base/src/widgets/badge.rs @@ -0,0 +1,27 @@ +use floem::{ + view::View, + views::{label as upstreamlabel, Decorators}, +}; + +use crate::get_current_theme; + +use super::common_props::{OxySize, OxyVariant}; + +#[derive(Default, Copy, Clone)] +pub struct BadgeProps { + pub variant: OxyVariant, + pub size: OxySize, + pub outlined: bool, +} + +pub fn badge(label: &'static str, props: Option) -> impl View { + let base_widget = upstreamlabel(move || label); + let theme = get_current_theme(); + + let props = props.unwrap_or(BadgeProps::default()); + let styles_enhancer = theme.get_badge_style(props); + + let styled_badge = base_widget.style(move |s| styles_enhancer(s)); + + styled_badge +} diff --git a/oxytail-base/src/widgets/mod.rs b/oxytail-base/src/widgets/mod.rs index 59fc877..c47d1d8 100644 --- a/oxytail-base/src/widgets/mod.rs +++ b/oxytail-base/src/widgets/mod.rs @@ -7,3 +7,4 @@ pub mod radio_button; pub mod text_header; pub mod text_divider; pub mod tooltip; +pub mod badge; diff --git a/oxytail-theme-dark/Cargo.toml b/oxytail-theme-dark/Cargo.toml index fd1688d..027af2a 100644 --- a/oxytail-theme-dark/Cargo.toml +++ b/oxytail-theme-dark/Cargo.toml @@ -14,5 +14,7 @@ rust-version = "1.75" [dependencies] floem = "0.1.1" -oxytail-base = "0.1.1" -oxytail-theme-defaults = "0.1.1" +oxytail-base = { path = "../oxytail-base" } +# oxytail-base = "0.1.1" +oxytail-theme-defaults = { path = "../oxytail-theme-defaults" } +# oxytail-theme-defaults = "0.1.1" diff --git a/oxytail-theme-dark/src/lib.rs b/oxytail-theme-dark/src/lib.rs index 5d111ab..4a5e22c 100644 --- a/oxytail-theme-dark/src/lib.rs +++ b/oxytail-theme-dark/src/lib.rs @@ -2,7 +2,7 @@ use floem::{peniko::Color, style::Style}; use oxytail_base::{ themes::{DefaultThemeProps, ThemeStyling}, widgets::{ - button::ButtonProps, checkbox::CheckboxProps, common_props::OxyVariant, + badge::BadgeProps, button::ButtonProps, checkbox::CheckboxProps, common_props::OxyVariant, radio_button::RadioProps, text_header::HeaderProps, text_input::InputProps, toggle::ToggleProps, tooltip::TooltipProps, }, @@ -121,4 +121,9 @@ impl ThemeStyling for Theme { self.theme_defaults(), ) } + + /// Defines how a `badge` should look like. + fn get_badge_style(&self, badge_props: BadgeProps) -> Box Style + '_> { + oxytail_theme_defaults::ThemeDefault::get_badge_style(badge_props, self.theme_defaults()) + } } diff --git a/oxytail-theme-defaults/Cargo.toml b/oxytail-theme-defaults/Cargo.toml index ce3d708..f799c52 100644 --- a/oxytail-theme-defaults/Cargo.toml +++ b/oxytail-theme-defaults/Cargo.toml @@ -14,4 +14,5 @@ rust-version = "1.75" [dependencies] floem = "0.1.1" -oxytail-base = "0.1.1" +oxytail-base = { path = "../oxytail-base" } +# oxytail-base = "0.1.1" diff --git a/oxytail-theme-defaults/src/lib.rs b/oxytail-theme-defaults/src/lib.rs index 85bc074..04b7d49 100644 --- a/oxytail-theme-defaults/src/lib.rs +++ b/oxytail-theme-defaults/src/lib.rs @@ -2,11 +2,12 @@ use floem::{ cosmic_text::Weight, peniko::Color, style::{Background, CursorStyle, Display, Style, StyleValue, Transition}, - unit::Pct, + unit::{Pct, Px}, }; use oxytail_base::{ themes::DefaultThemeProps, widgets::{ + badge::BadgeProps, button::ButtonProps, checkbox::CheckboxProps, common_props::{OxySize, OxyVariant}, @@ -371,4 +372,59 @@ impl ThemeDefault { Box::new(styles_creator) } + + /// Defines how a `badge` should look like. + pub fn get_badge_style( + badge_props: BadgeProps, + theme_defaults: DefaultThemeProps, + ) -> Box Style> { + let styles_creator = move |s: Style| { + let base_style = s + .padding_vert(2.) + .padding_horiz(9.) + .font_size(13.) + .border_radius(Px(14.)) + .items_center() + .justify_center() + .height(18.); + + let curr_variant_color = (theme_defaults.get_variant_colors)(badge_props.variant); + + let variant_enhancer = |s: Style| match badge_props.variant { + OxyVariant::Default => s + .background((theme_defaults.get_variant_colors)(OxyVariant::Neutral)) + .color(theme_defaults.light_text_color), + OxyVariant::Neutral => s + .background(curr_variant_color) + .color(theme_defaults.light_text_color), + _ => s + .background(curr_variant_color) + .color(theme_defaults.dark_text_color), + }; + + let size_enhancer = |s: Style| match badge_props.size { + OxySize::Large => s.font_size(16.).height(22), + OxySize::Normal => s, + OxySize::Small => s.font_size(10.).height(15), + OxySize::Tiny => s.font_size(8.).height(11), + }; + + let outlined_enhancer = |s: Style| match badge_props.outlined { + true => s + .border_color(curr_variant_color) + .border(0.5) + .background(Color::TRANSPARENT) + .color(curr_variant_color), + false => s, + }; + + let enhanced_style = variant_enhancer(base_style); + let enhanced_style = size_enhancer(enhanced_style); + let enhanced_style = outlined_enhancer(enhanced_style); + + enhanced_style + }; + + Box::new(styles_creator) + } }