Segmented button

- Implemented the segmented button widget
This commit is contained in:
Eduardo Flores 2022-10-14 19:24:14 -07:00
parent 34b3d8a54b
commit f630d66c67
3 changed files with 145 additions and 6 deletions

View file

@ -9,7 +9,7 @@ use cosmic::{
iced_lazy::responsive, iced_lazy::responsive,
iced_winit::window::{drag, toggle_maximize, minimize}, iced_winit::window::{drag, toggle_maximize, minimize},
theme::{self, Theme}, theme::{self, Theme},
widget::{button, nav_button, nav_bar, header_bar, settings, scrollable, toggler, nav_bar_item}, widget::{button, nav_button, nav_bar, header_bar, settings, scrollable, toggler, nav_bar_item, segmented_button},
Element, Element,
ElementExt, ElementExt,
}; };
@ -62,6 +62,7 @@ pub enum Message {
TogglerToggled(bool), TogglerToggled(bool),
PickListSelected(&'static str), PickListSelected(&'static str),
RowSelected(usize), RowSelected(usize),
SegmentatedButtonPressed(usize),
Close, Close,
ToggleSidebar, ToggleSidebar,
Drag, Drag,
@ -110,6 +111,9 @@ impl Application for Window {
Message::Minimize => return minimize(true), Message::Minimize => return minimize(true),
Message::Maximize => return toggle_maximize(), Message::Maximize => return toggle_maximize(),
Message::RowSelected(row) => println!("Selected row {row}"), Message::RowSelected(row) => println!("Selected row {row}"),
Message::SegmentatedButtonPressed(index) => {
println!("Segmented button at index: {index}")
}
} }
Command::none() Command::none()
@ -143,6 +147,7 @@ impl Application for Window {
let content = responsive(|size| { let content = responsive(|size| {
let condensed = size.width < 900.0; let condensed = size.width < 900.0;
let sidebar: Element<_> = nav_bar() let sidebar: Element<_> = nav_bar()
// TODO: Make this take an iterator
.source(BTreeMap::from([ .source(BTreeMap::from([
( (
nav_bar_item() nav_bar_item()
@ -272,9 +277,49 @@ impl Application for Window {
.width(Length::Units(250)) .width(Length::Units(250))
.height(Length::Units(4)) .height(Length::Units(4))
)) ))
.into() .add(settings::item(
"Segmented Button",
segmented_button()
.options(vec!["Vertical", "Horizontal"])
.on_button_pressed(Box::new(Message::SegmentatedButtonPressed))
))
.add(settings::item(
"Checkbox",
checkbox("Checkbox", self.checkbox_value, Message::CheckboxToggled)
))
.into(),
settings::view_section("Expander")
.add(settings::item(
"Expander",
expander()
.title("Label")
.subtitle("Caption")
.icon(String::from("edit-paste"))
.on_row_selected(Box::new(Message::RowSelected))
.rows(vec![
list_row()
.title("Label")
.subtitle("Caption")
.icon(String::from("help-about")),
list_row().subtitle("Caption").title("Label"),
list_row().title("Label")
]) ])
.into(); )).into(),
settings::view_section("List Box")
.add(settings::item(
"List Box",
list_box()
.style(theme::Container::Custom(list_section_style))
.children(vec![
cosmic::list_box_row!("Title").into(),
cosmic::list_box_row!("Title", "Subtitle").into(),
cosmic::list_box_row!("Title", "", "edit-paste").into(),
cosmic::list_box_row!("", "Subtitle", "edit-paste").into(),
cosmic::list_box_row!("Title", "Subtitle", "edit-paste").into()
])
.render()
)).into(),
]);
let mut widgets = Vec::with_capacity(2); let mut widgets = Vec::with_capacity(2);

View file

@ -28,4 +28,7 @@ mod scrollable;
pub use scrollable::*; pub use scrollable::*;
pub mod separator; pub mod separator;
pub use separator::{horizontal_rule, vertical_rule}; pub use separator::*;
pub mod segmented_button;
pub use segmented_button::*;

View file

@ -0,0 +1,91 @@
use derive_setters::Setters;
use iced::{
alignment::{Horizontal, Vertical},
theme,
widget::{button, container, text, Row},
Background, Element, Length, Renderer, Theme,
};
use iced_lazy::Component;
#[derive(Setters)]
pub struct SegmentedButton<'a, Message> {
options: Vec<&'a str>,
#[setters(strip_option)]
on_button_pressed: Option<Box<dyn Fn(usize) -> Message>>,
}
pub fn segmented_button<'a, Message>() -> SegmentedButton<'a, Message> {
SegmentedButton {
options: vec![],
on_button_pressed: None,
}
}
#[derive(Clone)]
pub enum SegmentedButtonEvent {
Pressed(usize),
}
impl<'a, Message> Component<Message, Renderer> for SegmentedButton<'a, Message> {
type State = usize;
type Event = SegmentedButtonEvent;
fn update(&mut self, state: &mut Self::State, event: Self::Event) -> Option<Message> {
match event {
SegmentedButtonEvent::Pressed(index) => {
*state = index;
self.on_button_pressed
.as_ref()
.map(|on_button_pressed| (on_button_pressed)(index))
}
}
}
fn view(&self, state: &Self::State) -> Element<Self::Event> {
let buttons: Vec<Element<'a, Self::Event>> = self
.options
.iter()
.enumerate()
.map(|(index, option)| {
button(
container(text(*option))
.width(Length::Fill)
.height(Length::Fill)
.align_x(Horizontal::Center)
.align_y(Vertical::Center),
)
.width(Length::Units(136))
.height(Length::Units(32))
.style(if *state == index {
theme::Button::Secondary.into()
} else {
theme::Button::Primary.into()
})
.on_press(SegmentedButtonEvent::Pressed(index))
.into()
})
.collect();
container(Row::with_children(buttons).spacing(5).width(Length::Shrink))
.padding(2)
.style(theme::Container::Custom(view_switcher_container_style))
.into()
}
}
pub fn view_switcher_container_style(theme: &Theme) -> iced_style::container::Appearance {
let accent = &theme.cosmic().accent;
iced_style::container::Appearance {
text_color: Default::default(),
background: Some(Background::Color(accent.base.into())),
border_radius: 24.0,
border_width: 2.0,
border_color: accent.base.into(),
}
}
impl<'a, Message: 'a> From<SegmentedButton<'a, Message>> for Element<'a, Message> {
fn from(segmented_button: SegmentedButton<'a, Message>) -> Self {
iced_lazy::component(segmented_button)
}
}