Skip to content

Commit

Permalink
Implement radio button
Browse files Browse the repository at this point in the history
  • Loading branch information
pieterdd committed Jan 5, 2024
1 parent f807095 commit 9974c31
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 0 deletions.
3 changes: 3 additions & 0 deletions examples/widget-gallery/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod images;
pub mod inputs;
pub mod labels;
pub mod lists;
pub mod radio_buttons;
pub mod rich_text;

use floem::{
Expand All @@ -30,6 +31,7 @@ fn app_view() -> impl View {
"Label",
"Button",
"Checkbox",
"Radio",
"Input",
"List",
"Menu",
Expand Down Expand Up @@ -150,6 +152,7 @@ fn app_view() -> impl View {
"Label" => container_box(labels::label_view()),
"Button" => container_box(buttons::button_view()),
"Checkbox" => container_box(checkbox::checkbox_view()),
"Radio" => container_box(radio_buttons::radio_buttons_view()),
"Input" => container_box(inputs::text_input_view()),
"List" => container_box(lists::virt_list_view()),
"Menu" => container_box(context_menu::menu_view()),
Expand Down
127 changes: 127 additions & 0 deletions examples/widget-gallery/src/radio_buttons.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use std::fmt::Display;

use floem::{
reactive::create_signal,
view::View,
views::{v_stack, Decorators},
widgets::{labeled_radio_button, radio_button},
};

use crate::form::{form, form_item};

#[derive(PartialEq, Eq, Clone)]
enum OperatingSystem {
Windows,
MacOS,
Linux,
}

impl Display for OperatingSystem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
OperatingSystem::Windows => write!(f, "Windows"),
OperatingSystem::MacOS => write!(f, "MacOS"),
OperatingSystem::Linux => write!(f, "Linux"),
}
}
}

pub fn radio_buttons_view() -> impl View {
let width = 160.0;
let (operating_system, set_operating_system) = create_signal(OperatingSystem::Windows);
form({
(
form_item("Radio Buttons:".to_string(), width, move || {
v_stack((
radio_button(OperatingSystem::Windows, operating_system).on_click_stop(
move |_| {
set_operating_system.set(OperatingSystem::Windows);
},
),
radio_button(OperatingSystem::MacOS, operating_system).on_click_stop(
move |_| {
set_operating_system.set(OperatingSystem::MacOS);
},
),
radio_button(OperatingSystem::Linux, operating_system).on_click_stop(
move |_| {
set_operating_system.set(OperatingSystem::Linux);
},
),
))
.style(|s| s.gap(0.0, 10.0).margin_left(5.0))
}),
form_item("Disabled Radio Buttons:".to_string(), width, move || {
v_stack((
radio_button(OperatingSystem::Windows, operating_system)
.on_click_stop(move |_| {
set_operating_system.set(OperatingSystem::Windows);
})
.disabled(|| true),
radio_button(OperatingSystem::MacOS, operating_system)
.on_click_stop(move |_| {
set_operating_system.set(OperatingSystem::MacOS);
})
.disabled(|| true),
radio_button(OperatingSystem::Linux, operating_system)
.on_click_stop(move |_| {
set_operating_system.set(OperatingSystem::Linux);
})
.disabled(|| true),
))
.style(|s| s.gap(0.0, 10.0).margin_left(5.0))
}),
form_item("Labelled Radio Buttons:".to_string(), width, move || {
v_stack((
labeled_radio_button(OperatingSystem::Windows, operating_system, || {
OperatingSystem::Windows
})
.on_click_stop(move |_| {
set_operating_system.set(OperatingSystem::Windows);
}),
labeled_radio_button(OperatingSystem::MacOS, operating_system, || {
OperatingSystem::MacOS
})
.on_click_stop(move |_| {
set_operating_system.set(OperatingSystem::MacOS);
}),
labeled_radio_button(OperatingSystem::Linux, operating_system, || {
OperatingSystem::Linux
})
.on_click_stop(move |_| {
set_operating_system.set(OperatingSystem::Linux);
}),
))
}),
form_item(
"Disabled Labelled Radio Buttons:".to_string(),
width,
move || {
v_stack((
labeled_radio_button(OperatingSystem::Windows, operating_system, || {
OperatingSystem::Windows
})
.on_click_stop(move |_| {
set_operating_system.set(OperatingSystem::Windows);
})
.disabled(|| true),
labeled_radio_button(OperatingSystem::MacOS, operating_system, || {
OperatingSystem::MacOS
})
.on_click_stop(move |_| {
set_operating_system.set(OperatingSystem::MacOS);
})
.disabled(|| true),
labeled_radio_button(OperatingSystem::Linux, operating_system, || {
OperatingSystem::Linux
})
.on_click_stop(move |_| {
set_operating_system.set(OperatingSystem::Linux);
})
.disabled(|| true),
))
},
),
)
})
}
56 changes: 56 additions & 0 deletions src/widgets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
};
use peniko::Color;
use std::rc::Rc;
use taffy::style::AlignItems;

mod checkbox;
pub use checkbox::*;
Expand All @@ -32,6 +33,9 @@ pub mod slider;
mod button;
pub use button::*;

mod radio_button;
pub use radio_button::*;

mod text_input;
pub use text_input::*;

Expand Down Expand Up @@ -132,6 +136,55 @@ pub(crate) fn default_theme() -> Theme {
})
.apply(focus_style.clone());

let radio_button_style = Style::new()
.width(20.)
.height(20.)
.align_items(AlignItems::Center)
.justify_center()
.background(Color::WHITE)
.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())
.padding(0.)
.border_radius(100.0)
.apply(focus_style.clone())
.disabled(|s| {
s.background(Color::rgb8(180, 188, 175).with_alpha_factor(0.3))
.color(Color::GRAY)
});

let radio_button_dot_style = Style::new()
.width(8.)
.height(8.)
.border_radius(100.0)
.background(Color::BLACK)
.disabled(|s| {
s.background(Color::rgb(0.5, 0.5, 0.5))
.hover(|s| s.background(Color::rgb(0.5, 0.5, 0.5)))
});

let labeled_radio_button_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(RadioButtonClass, |s| s.background(active_bg_color)))
.focus(|s| {
s.class(RadioButtonClass, |_| focus_applied_style.clone())
.hover(|s| s.background(focus_hover_bg_color))
})
.disabled(|s| {
s.color(Color::GRAY).class(RadioButtonClass, |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());

const FONT_SIZE: f32 = 12.0;

let input_style = Style::new()
Expand Down Expand Up @@ -166,6 +219,9 @@ pub(crate) fn default_theme() -> Theme {
})
.class(LabeledCheckboxClass, |_| labeled_checkbox_style)
.class(CheckboxClass, |_| checkbox_style)
.class(RadioButtonClass, |_| radio_button_style)
.class(RadioButtonDotClass, |_| radio_button_dot_style)
.class(LabeledRadioButtonClass, |_| labeled_radio_button_style)
.class(TextInputClass, |_| input_style)
.class(ButtonClass, |_| button_style)
.class(scroll::Handle, |s| {
Expand Down
50 changes: 50 additions & 0 deletions src/widgets/radio_button.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use crate::{
style_class,
view::View,
views::{self, container, empty, h_stack, Decorators},
};
use floem_reactive::ReadSignal;

style_class!(pub RadioButtonClass);
style_class!(pub RadioButtonDotClass);
style_class!(pub RadioButtonDotSelectedClass);
style_class!(pub LabeledRadioButtonClass);

fn radio_button_svg<T>(represented_value: T, actual_value: ReadSignal<T>) -> impl View
where
T: Eq + PartialEq + Clone + 'static,
{
container(empty().class(RadioButtonDotClass).style(move |s| {
s.apply_if(actual_value.get() != represented_value, |s| {
s.display(taffy::style::Display::None)
})
}))
.class(RadioButtonClass)
}

/// Renders a radio button that appears as selected if the signal equals the given enum value.
/// Can be combined with a label and a stack with a click event (as in `examples/widget-gallery`).
pub fn radio_button<T>(represented_value: T, actual_value: ReadSignal<T>) -> impl View
where
T: Eq + PartialEq + Clone + 'static,
{
radio_button_svg(represented_value, actual_value).keyboard_navigatable()
}

/// Renders a radio button that appears as selected if the signal equals the given enum value.
pub fn labeled_radio_button<S: std::fmt::Display + 'static, T>(
represented_value: T,
actual_value: ReadSignal<T>,
label: impl Fn() -> S + 'static,
) -> impl View
where
T: Eq + PartialEq + Clone + 'static,
{
h_stack((
radio_button_svg(represented_value, actual_value),
views::label(label),
))
.class(LabeledRadioButtonClass)
.style(|s| s.items_center())
.keyboard_navigatable()
}

0 comments on commit 9974c31

Please sign in to comment.