Skip to content

Commit

Permalink
feat: radio buttons (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
golota60 authored Jan 10, 2024
1 parent 534c025 commit 7fab7cd
Show file tree
Hide file tree
Showing 9 changed files with 352 additions and 6 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ Short term roadmap(primitives only):

- [x] Toggles
- [ ] Visual Dividers
- [ ] Radio buttons
- [x] Radio buttons
- [ ] H1/H2/H3/H4/H5 Equivalents
- [ ] Dropdowns/Selects
- [ ] Tooltips
Expand Down
7 changes: 6 additions & 1 deletion examples/widget-gallery/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,23 @@ use floem::{
use inputs::{text_input_sizes, text_input_variants};
use oxytail_base::{init_theme, widgets::button::button};
use oxytail_theme_dark::Theme;
use radio_buttons::{
disabled_labeled_radio_variants, labeled_radio_variants, radio_sizes, radio_variants,
};
use toggles::{toggle_sizes, toggle_variants};

mod btn;
mod checkboxes;
mod inputs;
mod radio_buttons;
mod toggles;

fn padded_container_box(child: impl View + 'static) -> ContainerBox {
container_box(child).style(|s| s.padding(10.))
}

fn app_view() -> impl View {
let tabs: im::Vector<&str> = vec!["Intro", "Button", "Checkbox", "Input", "Toggle"]
let tabs: im::Vector<&str> = vec!["Intro", "Button", "Radio", "Checkbox", "Input", "Toggle"]
.into_iter()
.collect();
let (tabs, _set_tabs) = create_signal(tabs);
Expand Down Expand Up @@ -149,6 +153,7 @@ fn app_view() -> impl View {
label(|| "Floem itself, in addition to widgets also provides more components. You can mix&match them as you please."))
)),
"Button" => padded_container_box(v_stack((btn_variants(), btn_outlines(), btn_sizes()))),
"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(),
checkboxes_sizes(),
Expand Down
171 changes: 171 additions & 0 deletions examples/widget-gallery/src/radio_buttons.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
use std::fmt::Display;

use floem::{
reactive::create_signal,
view::View,
views::{h_stack, label, v_stack, Decorators},
};
use oxytail_base::widgets::{
common_props::{OxySize, OxyVariant},
radio_button::{labeled_radio_button, radio_button, RadioProps},
};

#[derive(PartialEq, Eq, Clone)]
enum RgbSpace {
Red,
Green,
Blue,
}

impl Display for RgbSpace {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
RgbSpace::Red => write!(f, "Red"),
RgbSpace::Green => write!(f, "Green"),
RgbSpace::Blue => write!(f, "Blue"),
}
}
}

fn labeled_radio_variant_with_state(variant: OxyVariant, size: OxySize) -> impl View {
let (color, set_color) = create_signal(RgbSpace::Green);

v_stack((
labeled_radio_button(
RgbSpace::Red,
color,
|| RgbSpace::Red,
Some(RadioProps {
variant,
size,
..Default::default()
}),
)
.on_click_stop(move |_| {
set_color.set(RgbSpace::Red);
}),
labeled_radio_button(
RgbSpace::Green,
color,
|| RgbSpace::Green,
Some(RadioProps {
variant,
size,
..Default::default()
}),
)
.on_click_stop(move |_| {
set_color.set(RgbSpace::Green);
}),
labeled_radio_button(
RgbSpace::Blue,
color,
|| RgbSpace::Blue,
Some(RadioProps {
variant,
size,
..Default::default()
}),
)
.on_click_stop(move |_| {
set_color.set(RgbSpace::Blue);
}),
))
}

fn radio_variant_with_state(variant: OxyVariant, size: OxySize) -> impl View {
let (color, set_color) = create_signal(RgbSpace::Green);

v_stack((
radio_button(
RgbSpace::Red,
color,
Some(RadioProps {
variant,
size,
..Default::default()
}),
)
.on_click_stop(move |_| {
set_color.set(RgbSpace::Red);
}),
radio_button(
RgbSpace::Green,
color,
Some(RadioProps {
variant,
size,
..Default::default()
}),
)
.on_click_stop(move |_| {
set_color.set(RgbSpace::Green);
}),
radio_button(
RgbSpace::Blue,
color,
Some(RadioProps {
variant,
size,
..Default::default()
}),
)
.on_click_stop(move |_| {
set_color.set(RgbSpace::Blue);
}),
))
}

pub fn radio_variants() -> impl View {
v_stack((
label(|| "Radio button variants"),
h_stack((
radio_variant_with_state(OxyVariant::Default, OxySize::Normal),
radio_variant_with_state(OxyVariant::Neutral, OxySize::Normal),
radio_variant_with_state(OxyVariant::Primary, OxySize::Normal),
radio_variant_with_state(OxyVariant::Secondary, OxySize::Normal),
radio_variant_with_state(OxyVariant::Accent, OxySize::Normal),
radio_variant_with_state(OxyVariant::Ghost, OxySize::Normal),
radio_variant_with_state(OxyVariant::Link, OxySize::Normal),
radio_variant_with_state(OxyVariant::Info, OxySize::Normal),
radio_variant_with_state(OxyVariant::Success, OxySize::Normal),
radio_variant_with_state(OxyVariant::Warning, OxySize::Normal),
radio_variant_with_state(OxyVariant::Error, OxySize::Normal),
))
.style(|s| s.gap(10, 10)),
))
}
pub fn radio_sizes() -> impl View {
v_stack((
label(|| "Radio button sizes"),
h_stack((
radio_variant_with_state(OxyVariant::Primary, OxySize::Large),
radio_variant_with_state(OxyVariant::Primary, OxySize::Normal),
radio_variant_with_state(OxyVariant::Primary, OxySize::Small),
radio_variant_with_state(OxyVariant::Primary, OxySize::Tiny),
))
.style(|s| s.gap(10, 10)),
))
}
pub fn labeled_radio_variants() -> impl View {
v_stack((
label(|| "Labeled radio buttons"),
h_stack((
labeled_radio_variant_with_state(OxyVariant::Primary, OxySize::Normal),
labeled_radio_variant_with_state(OxyVariant::Secondary, OxySize::Normal),
))
.style(|s| s.gap(10, 10)),
))
}
pub fn disabled_labeled_radio_variants() -> impl View {
v_stack((
label(|| "Disabled state"),
h_stack((
labeled_radio_variant_with_state(OxyVariant::Primary, OxySize::Normal)
.disabled(|| true),
labeled_radio_variant_with_state(OxyVariant::Secondary, OxySize::Normal)
.disabled(|| true),
))
.style(|s| s.gap(10, 10)),
))
}
9 changes: 8 additions & 1 deletion oxytail-base/src/themes.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use floem::style::Style;

use crate::widgets::{
button::ButtonProps, checkbox::CheckboxProps, text_input::InputProps, toggle::ToggleProps,
button::ButtonProps, checkbox::CheckboxProps, radio_button::RadioProps, text_input::InputProps,
toggle::ToggleProps,
};

// TODO: LOAD TTF FROM FILE SO THAT ITS CONSISTENT. FONT IDEA: ROBOTO
Expand All @@ -27,6 +28,12 @@ pub trait ThemeStyling {
fn get_input_style(&self, checkbox_props: InputProps) -> Box<dyn Fn(Style) -> Style>;
/// Defines how a toggle should look like.
fn get_toggle_style(&self, toggle_props: ToggleProps) -> Box<dyn Fn(Style) -> Style>;
/// Defined how a radio button should look like.
/// Returns a tuple, where first argument styles the "dot" of the active radio and the second one is the "outer circle", containing the default state of the radio.
fn get_radio_style(
&self,
radio_props: RadioProps,
) -> (Box<dyn Fn(Style) -> Style>, Box<dyn Fn(Style) -> Style>);
}

pub struct ButtonStyle<T> {
Expand Down
2 changes: 1 addition & 1 deletion oxytail-base/src/widgets/checkbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ pub fn labeled_checkbox<S: Display + 'static>(
props: Option<CheckboxProps>,
) -> impl View {
h_stack((checkbox_svg(checked, props), views::label(label)))
.style(|s| s.items_center().justify_center())
.style(|s| s.gap(8, 0))
.keyboard_navigatable()
}
1 change: 1 addition & 0 deletions oxytail-base/src/widgets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ pub mod checkbox;
pub mod text_input;
pub mod common_props;
pub mod toggle;
pub mod radio_button;
90 changes: 90 additions & 0 deletions oxytail-base/src/widgets/radio_button.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use std::fmt::Display;

use floem::{
reactive::ReadSignal,
style::Style,
view::View,
views::{self, container, empty, h_stack, Decorators},
};

use crate::get_current_theme;

use super::common_props::{OxySize, OxyVariant};

#[derive(Default, Clone, Copy)]
pub struct RadioProps {
pub variant: OxyVariant,
pub outlined: bool,
pub size: OxySize,
}

fn radio_button_svg<T>(
represented_value: T,
actual_value: ReadSignal<T>,
inner_style_enhancer: Box<dyn Fn(Style) -> Style>,
outer_style_enhancer: Box<dyn Fn(Style) -> Style>,
) -> impl View
where
T: Eq + PartialEq + Clone + 'static,
{
container(empty().style(move |s| {
inner_style_enhancer(s).apply_if(actual_value.get() != represented_value, |s| {
s.display(floem::style::Display::None)
})
}))
.style(move |s| outer_style_enhancer(s))
}

fn upstream_radio_button<T>(
represented_value: T,
actual_value: ReadSignal<T>,
inner_style_enhancer: Box<dyn Fn(Style) -> Style>,
outer_style_enhancer: Box<dyn Fn(Style) -> Style>,
) -> impl View
where
T: Eq + PartialEq + Clone + 'static,
{
radio_button_svg(
represented_value,
actual_value,
inner_style_enhancer,
outer_style_enhancer,
)
.keyboard_navigatable()
}

pub fn radio_button<T>(
represented_value: T,
actual_value: ReadSignal<T>,
props: Option<RadioProps>,
) -> impl View
where
T: Eq + PartialEq + Clone + 'static,
{
let theme = get_current_theme();

let props = props.unwrap_or(RadioProps::default());

let (inner_style, outer_style) = theme.get_radio_style(props);

let base_widget =
upstream_radio_button(represented_value, actual_value, inner_style, outer_style);

base_widget
}

pub fn labeled_radio_button<T, S: Display + 'static>(
represented_value: T,
actual_value: ReadSignal<T>,
label: impl Fn() -> S + 'static,
props: Option<RadioProps>,
) -> impl View
where
T: Eq + PartialEq + Clone + 'static,
{
h_stack((
radio_button(represented_value, actual_value, props),
views::label(label),
))
.style(|s| s.gap(8, 0))
}
4 changes: 2 additions & 2 deletions oxytail-base/src/widgets/toggle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ pub struct ToggleProps {

fn toggle_ball_svg(enabled: ReadSignal<bool>, props: Option<ToggleProps>) -> impl View {
const OFF_SVG: &str = r#"<svg viewBox="0 0 24 12" xmlns="http://www.w3.org/2000/svg" fill="none">
<rect x="0.5" y="0.5" width="23" height="11" rx="6" stroke="currentColor" stroke-width="1" />
<rect x="0.5" y="0.5" width="23" height="11" rx="6" stroke="currentColor" stroke-width="0.5" />
<circle cx="6" cy="6" r="4.5" fill="currentColor"/>
</svg>
"#;
const ON_SVG: &str = r#"<svg viewBox="0 0 24 12" xmlns="http://www.w3.org/2000/svg" fill="none">
<rect x="0.5" y="0.5" width="23" height="11" rx="6" stroke="currentColor" stroke-width="1" />
<rect x="0.5" y="0.5" width="23" height="11" rx="6" stroke="currentColor" stroke-width="0.5" />
<circle cx="18" cy="6" r="4.5" fill="currentColor"/>
</svg>
"#;
Expand Down
Loading

0 comments on commit 7fab7cd

Please sign in to comment.