feat: MultiSelect support for segmented buttons

This commit is contained in:
Michael Aaron Murphy 2023-01-06 16:18:25 +01:00 committed by Michael Murphy
parent b1cbcfaf5b
commit 8988b25b6a
9 changed files with 263 additions and 112 deletions

View file

@ -13,7 +13,7 @@ use cosmic::{
theme::{self, Theme}, theme::{self, Theme},
widget::{ widget::{
header_bar, icon, list, nav_bar, nav_button, scrollable, header_bar, icon, list, nav_bar, nav_button, scrollable,
segmented_button::{self, cosmic::vertical_view_switcher}, segmented_button::{self, cosmic::vertical_view_switcher, Selectable, SingleSelect},
settings, settings,
}, },
Element, ElementExt, Element, ElementExt,
@ -136,7 +136,7 @@ pub struct Window {
debug: bool, debug: bool,
demo: demo::State, demo: demo::State,
desktop: desktop::State, desktop: desktop::State,
nav_bar_pages: segmented_button::State<Page>, nav_bar_pages: segmented_button::State<SingleSelect, Page>,
nav_bar_toggled_condensed: bool, nav_bar_toggled_condensed: bool,
nav_bar_toggled: bool, nav_bar_toggled: bool,
page: Page, page: Page,

View file

@ -3,7 +3,9 @@ use cosmic::{
iced::{Alignment, Length}, iced::{Alignment, Length},
theme::{Button as ButtonTheme, Theme}, theme::{Button as ButtonTheme, Theme},
widget::{ widget::{
button, settings, button,
segmented_button::{MultiSelect, Selectable, SingleSelect},
settings,
spin_button::{SpinButtonModel, SpinMessage}, spin_button::{SpinButtonModel, SpinMessage},
toggler, toggler,
}, },
@ -25,12 +27,21 @@ pub enum DemoView {
TabC, TabC,
} }
pub enum MultiOption {
OptionA,
OptionB,
OptionC,
OptionD,
OptionE,
}
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum Message { pub enum Message {
ButtonPressed, ButtonPressed,
CheckboxToggled(bool), CheckboxToggled(bool),
Debug(bool), Debug(bool),
IconTheme(segmented_button::Key), IconTheme(segmented_button::Key),
MultiSelection(segmented_button::Key),
PickListSelected(&'static str), PickListSelected(&'static str),
RowSelected(usize), RowSelected(usize),
Selection(segmented_button::Key), Selection(segmented_button::Key),
@ -48,13 +59,14 @@ pub enum Output {
pub struct State { pub struct State {
pub checkbox_value: bool, pub checkbox_value: bool,
pub icon_theme: segmented_button::State<&'static str>, pub icon_theme: segmented_button::State<SingleSelect, &'static str>,
pub multi_selection: segmented_button::State<MultiSelect, MultiOption>,
pub pick_list_selected: Option<&'static str>, pub pick_list_selected: Option<&'static str>,
pub selection: segmented_button::State<()>, pub selection: segmented_button::State<SingleSelect, ()>,
pub slider_value: f32, pub slider_value: f32,
pub spin_button: SpinButtonModel<i32>, pub spin_button: SpinButtonModel<i32>,
pub toggler_value: bool, pub toggler_value: bool,
pub view_switcher: segmented_button::State<DemoView>, pub view_switcher: segmented_button::State<SingleSelect, DemoView>,
} }
impl Default for State { impl Default for State {
@ -74,6 +86,13 @@ impl Default for State {
.insert("Choice B", ()) .insert("Choice B", ())
.insert("Choice C", ()) .insert("Choice C", ())
.build(), .build(),
multi_selection: segmented_button::State::builder()
.insert("Option A", MultiOption::OptionA)
.insert("Option B", MultiOption::OptionB)
.insert("Option C", MultiOption::OptionC)
.insert("Option D", MultiOption::OptionD)
.insert("Option E", MultiOption::OptionE)
.build(),
view_switcher: segmented_button::State::builder() view_switcher: segmented_button::State::builder()
.insert_active("Controls", DemoView::TabA) .insert_active("Controls", DemoView::TabA)
.insert("Segmented Button", DemoView::TabB) .insert("Segmented Button", DemoView::TabB)
@ -91,6 +110,7 @@ impl State {
Message::Debug(value) => return Some(Output::Debug(value)), Message::Debug(value) => return Some(Output::Debug(value)),
Message::PickListSelected(value) => self.pick_list_selected = Some(value), Message::PickListSelected(value) => self.pick_list_selected = Some(value),
Message::RowSelected(row) => println!("Selected row {row}"), Message::RowSelected(row) => println!("Selected row {row}"),
Message::MultiSelection(key) => self.multi_selection.activate(key),
Message::Selection(key) => self.selection.activate(key), Message::Selection(key) => self.selection.activate(key),
Message::SliderChanged(value) => self.slider_value = value, Message::SliderChanged(value) => self.slider_value = value,
Message::SpinButton(msg) => self.spin_button.update(msg), Message::SpinButton(msg) => self.spin_button.update(msg),
@ -226,9 +246,9 @@ impl State {
.spacing(8) .spacing(8)
.on_activate(Message::Selection) .on_activate(Message::Selection)
.into(), .into(),
cosmic::iced::widget::text("Vertical").into(), cosmic::iced::widget::text("Vertical (Multi-Select)").into(),
vertical_segmented_selection(&self.selection) vertical_segmented_selection(&self.multi_selection)
.on_activate(Message::Selection) .on_activate(Message::MultiSelection)
.into(), .into(),
cosmic::iced::widget::text("Vertical With Spacing").into(), cosmic::iced::widget::text("Vertical With Spacing").into(),
cosmic::iced::widget::row(vec![ cosmic::iced::widget::row(vec![

View file

@ -10,11 +10,11 @@ use iced_core::Color;
use crate::{theme, Theme}; use crate::{theme, Theme};
use super::segmented_button::{self, cosmic::vertical_view_switcher}; use super::segmented_button::{self, cosmic::vertical_view_switcher, SingleSelect};
/// A container holding a vertical view switcher with the n style /// A container holding a vertical view switcher with the n style
pub fn nav_bar<Data, Message>( pub fn nav_bar<Data, Message>(
state: &segmented_button::State<Data>, state: &segmented_button::State<SingleSelect, Data>,
on_activate: impl Fn(segmented_button::Key) -> Message + 'static, on_activate: impl Fn(segmented_button::Key) -> Message + 'static,
) -> iced::widget::Container<Message, crate::Renderer> ) -> iced::widget::Container<Message, crate::Renderer>
where where
@ -26,7 +26,7 @@ where
.button_padding([16, 10, 16, 10]) .button_padding([16, 10, 16, 10])
.button_spacing(8) .button_spacing(8)
.icon_size(16) .icon_size(16)
.spacing(14) .spacing(8)
.apply(scrollable) .apply(scrollable)
.apply(container) .apply(container)
.height(Length::Fill) .height(Length::Fill)

View file

@ -1,15 +1,20 @@
// Copyright 2022 System76 <info@system76.com> // Copyright 2022 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use super::{HorizontalSegmentedButton, SegmentedButton, State, VerticalSegmentedButton}; use super::{
state::Selectable, HorizontalSegmentedButton, SegmentedButton, State, VerticalSegmentedButton,
};
/// Appears as a collection of tabs for developing a tabbed interface. /// Appears as a collection of tabs for developing a tabbed interface.
/// ///
/// The data for the widget comes from a [`State`] that is maintained the application. /// The data for the widget comes from a [`State`] that is maintained the application.
#[must_use] #[must_use]
pub fn horizontal_view_switcher<Message, Data>( pub fn horizontal_view_switcher<Selection, Message, Data>(
state: &State<Data>, state: &State<Selection, Data>,
) -> HorizontalSegmentedButton<Message, crate::Renderer> { ) -> HorizontalSegmentedButton<Selection, Message, crate::Renderer>
where
Selection: Selectable,
{
SegmentedButton::new(&state.inner) SegmentedButton::new(&state.inner)
.button_padding([16, 0, 16, 0]) .button_padding([16, 0, 16, 0])
.button_height(48) .button_height(48)
@ -21,9 +26,12 @@ pub fn horizontal_view_switcher<Message, Data>(
/// ///
/// The data for the widget comes from a [`State`] that is maintained the application. /// The data for the widget comes from a [`State`] that is maintained the application.
#[must_use] #[must_use]
pub fn horizontal_segmented_selection<Message, Data>( pub fn horizontal_segmented_selection<Selection, Message, Data>(
state: &State<Data>, state: &State<Selection, Data>,
) -> HorizontalSegmentedButton<Message, crate::Renderer> { ) -> HorizontalSegmentedButton<Selection, Message, crate::Renderer>
where
Selection: Selectable,
{
SegmentedButton::new(&state.inner) SegmentedButton::new(&state.inner)
.button_padding([16, 0, 16, 0]) .button_padding([16, 0, 16, 0])
.button_height(32) .button_height(32)
@ -35,9 +43,12 @@ pub fn horizontal_segmented_selection<Message, Data>(
/// ///
/// The data for the widget comes from a [`State`] that is maintained the application. /// The data for the widget comes from a [`State`] that is maintained the application.
#[must_use] #[must_use]
pub fn vertical_segmented_selection<Message, Data>( pub fn vertical_segmented_selection<Selection, Message, Data>(
state: &State<Data>, state: &State<Selection, Data>,
) -> VerticalSegmentedButton<Message, crate::Renderer> { ) -> VerticalSegmentedButton<Selection, Message, crate::Renderer>
where
Selection: Selectable,
{
SegmentedButton::new(&state.inner) SegmentedButton::new(&state.inner)
.button_padding([16, 0, 16, 0]) .button_padding([16, 0, 16, 0])
.button_height(32) .button_height(32)
@ -49,9 +60,12 @@ pub fn vertical_segmented_selection<Message, Data>(
/// ///
/// The data for the widget comes from a [`State`] that is maintained the application. /// The data for the widget comes from a [`State`] that is maintained the application.
#[must_use] #[must_use]
pub fn vertical_view_switcher<Message, Data>( pub fn vertical_view_switcher<Selection, Message, Data>(
state: &State<Data>, state: &State<Selection, Data>,
) -> VerticalSegmentedButton<Message, crate::Renderer> { ) -> VerticalSegmentedButton<Selection, Message, crate::Renderer>
where
Selection: Selectable,
{
SegmentedButton::new(&state.inner) SegmentedButton::new(&state.inner)
.button_padding([16, 0, 16, 0]) .button_padding([16, 0, 16, 0])
.button_height(48) .button_height(48)

View file

@ -1,7 +1,7 @@
// Copyright 2022 System76 <info@system76.com> // Copyright 2022 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use super::state::State; use super::state::{Selectable, State};
use super::style::StyleSheet; use super::style::StyleSheet;
use super::widget::{SegmentedButton, SegmentedVariant}; use super::widget::{SegmentedButton, SegmentedVariant};
@ -12,31 +12,34 @@ use iced_native::layout;
pub struct Horizontal; pub struct Horizontal;
/// Horizontal [`SegmentedButton`]. /// Horizontal [`SegmentedButton`].
pub type HorizontalSegmentedButton<'a, Message, Renderer> = pub type HorizontalSegmentedButton<'a, Selection, Message, Renderer> =
SegmentedButton<'a, Horizontal, Message, Renderer>; SegmentedButton<'a, Horizontal, Selection, Message, Renderer>;
/// Horizontal implementation of the [`SegmentedButton`]. /// Horizontal implementation of the [`SegmentedButton`].
#[must_use] #[must_use]
pub fn horizontal_segmented_button<Message, Renderer, Data>( pub fn horizontal_segmented_button<Selection, Message, Renderer, Data>(
state: &State<Data>, state: &State<Selection, Data>,
) -> SegmentedButton<Horizontal, Message, Renderer> ) -> SegmentedButton<Horizontal, Selection, Message, Renderer>
where where
Renderer: iced_native::Renderer Renderer: iced_native::Renderer
+ iced_native::text::Renderer + iced_native::text::Renderer
+ iced_native::image::Renderer + iced_native::image::Renderer
+ iced_native::svg::Renderer, + iced_native::svg::Renderer,
Renderer::Theme: StyleSheet, Renderer::Theme: StyleSheet,
Selection: Selectable,
{ {
SegmentedButton::new(&state.inner) SegmentedButton::new(&state.inner)
} }
impl<'a, Message, Renderer> SegmentedVariant for SegmentedButton<'a, Horizontal, Message, Renderer> impl<'a, Selection, Message, Renderer> SegmentedVariant
for SegmentedButton<'a, Horizontal, Selection, Message, Renderer>
where where
Renderer: iced_native::Renderer Renderer: iced_native::Renderer
+ iced_native::text::Renderer + iced_native::text::Renderer
+ iced_native::image::Renderer + iced_native::image::Renderer
+ iced_native::svg::Renderer, + iced_native::svg::Renderer,
Renderer::Theme: StyleSheet, Renderer::Theme: StyleSheet,
Selection: Selectable,
{ {
type Renderer = Renderer; type Renderer = Renderer;

View file

@ -45,13 +45,16 @@
pub mod cosmic; pub mod cosmic;
mod horizontal; mod horizontal;
mod state; mod state;
mod style; mod style;
mod vertical; mod vertical;
mod widget; mod widget;
pub use self::horizontal::{horizontal_segmented_button, Horizontal, HorizontalSegmentedButton}; pub use self::horizontal::{horizontal_segmented_button, Horizontal, HorizontalSegmentedButton};
pub use self::state::{Content, Key, SecondaryState, SharedWidgetState, State}; pub use self::state::{
Content, Key, MultiSelect, SecondaryState, Selectable, SharedWidgetState, SingleSelect, State,
};
pub use self::style::{Appearance, ButtonAppearance, ButtonStatusAppearance, StyleSheet}; pub use self::style::{Appearance, ButtonAppearance, ButtonStatusAppearance, StyleSheet};
pub use self::vertical::{vertical_segmented_button, Vertical, VerticalSegmentedButton}; pub use self::vertical::{vertical_segmented_button, Vertical, VerticalSegmentedButton};
pub use self::widget::{SegmentedButton, SegmentedVariant}; pub use self::widget::{SegmentedButton, SegmentedVariant};

View file

@ -3,7 +3,7 @@
use derive_setters::Setters; use derive_setters::Setters;
use slotmap::{SecondaryMap, SlotMap}; use slotmap::{SecondaryMap, SlotMap};
use std::borrow::Cow; use std::{borrow::Cow, collections::HashSet};
use crate::widget::IconSource; use crate::widget::IconSource;
@ -13,15 +13,15 @@ slotmap::new_key_type! {
} }
/// Contains all state for interacting with a segmented button. /// Contains all state for interacting with a segmented button.
pub struct State<Data> { pub struct State<Selection, Data> {
/// State that is shared with widget drawing. /// State that is shared with widget drawing.
pub inner: SharedWidgetState, pub inner: SharedWidgetState<Selection>,
/// State unique to the application. /// State unique to the application.
pub data: SecondaryState<Data>, pub data: SecondaryState<Data>,
} }
impl<Data> Default for State<Data> { impl<Selection: Default, Data> Default for State<Selection, Data> {
fn default() -> Self { fn default() -> Self {
Self { Self {
inner: SharedWidgetState::default(), inner: SharedWidgetState::default(),
@ -32,43 +32,97 @@ impl<Data> Default for State<Data> {
/// State which is most useful to the widget. /// State which is most useful to the widget.
#[derive(Default)] #[derive(Default)]
pub struct SharedWidgetState { pub struct SharedWidgetState<Variant> {
/// The content used for drawing segmented buttons. /// The content used for drawing segmented buttons.
pub buttons: SlotMap<Key, Content>, pub buttons: SlotMap<Key, Content>,
/// The actively-selected segmented button. /// Manages selections
pub active: Key, pub selection: Variant,
} }
/// State which is most useful to the application. impl<Data> State<SingleSelect, Data> {
pub type SecondaryState<Data> = SecondaryMap<Key, Data>; pub fn activate(&mut self, key: Key) {
self.inner.selection.activate(key);
impl<Data> State<Data> {
#[must_use]
pub fn builder() -> Builder<Data> {
Builder(Self::default())
} }
/// Activates this button. pub fn deactivate(&mut self, key: Key) {
pub fn activate(&mut self, key: Key) { self.inner.selection.deactivate(key);
self.inner.active = key; }
#[must_use]
pub fn is_active(&self, key: Key) -> bool {
self.inner.selection.is_active(key)
} }
/// The ID of the active button. /// The ID of the active button.
#[must_use] #[must_use]
pub fn active(&self) -> Key { pub fn active(&self) -> Key {
self.inner.active self.inner.selection.active
} }
/// Get the application data for the active button. /// Get the application data for the active button.
#[must_use] #[must_use]
pub fn active_data(&self) -> Option<&Data> { pub fn active_data(&self) -> Option<&Data> {
self.data(self.active()) self.data.get(self.inner.selection.active)
}
/// Mutable application data for the active button.
#[must_use]
pub fn active_data_mut(&mut self) -> Option<&mut Data> {
self.data.get_mut(self.inner.selection.active)
}
}
impl<Data> State<MultiSelect, Data> {
pub fn activate(&mut self, key: Key) {
if self.inner.selection.is_active(key) {
self.inner.selection.deactivate(key);
} else {
self.inner.selection.activate(key);
}
}
pub fn deactivate(&mut self, key: Key) {
self.inner.selection.deactivate(key);
}
#[must_use]
pub fn is_active(&self, key: Key) -> bool {
self.inner.selection.is_active(key)
}
/// The IDs of the active buttons.
pub fn active(&self) -> impl Iterator<Item = Key> + '_ {
self.inner.selection.active.iter().copied()
}
/// Get the application data for the active buttons.
pub fn active_data(&self) -> impl Iterator<Item = (Key, &Data)> {
self.inner.buttons.keys().filter_map(|key| {
if self.inner.selection.is_active(key) {
self.data.get(key).map(|data| (key, data))
} else {
None
}
})
}
}
/// State which is most useful to the application.
pub type SecondaryState<Data> = SecondaryMap<Key, Data>;
impl<Selection, Data> State<Selection, Data>
where
Selection: Selectable,
{
#[must_use]
pub fn builder() -> Builder<Selection, Data> {
Builder(Self::default())
} }
/// Convenience method for batching multiple operations /// Convenience method for batching multiple operations
#[must_use] #[must_use]
pub fn batch(&mut self) -> Batch<Data> { pub fn batch(&mut self) -> Batch<Selection, Data> {
Batch(self) Batch(self)
} }
@ -106,17 +160,104 @@ impl<Data> State<Data> {
/// Inserts and activates a button. /// Inserts and activates a button.
pub fn insert_active(&mut self, content: impl Into<Content>, data: Data) -> Key { pub fn insert_active(&mut self, content: impl Into<Content>, data: Data) -> Key {
let key = self.insert(content, data); let key = self.insert(content, data);
self.activate(key); self.inner.selection.activate(key);
key key
} }
/// Removes a button. /// Removes a button.
pub fn remove(&mut self, key: Key) -> Option<Data> { pub fn remove(&mut self, key: Key) -> Option<Data> {
self.inner.buttons.remove(key); self.inner.buttons.remove(key);
self.inner.selection.deactivate(key);
self.data.remove(key) self.data.remove(key)
} }
} }
pub trait Selectable: Default {
fn activate(&mut self, key: Key);
fn deactivate(&mut self, key: Key);
fn is_active(&self, key: Key) -> bool;
}
#[derive(Default)]
pub struct SingleSelect {
pub active: Key,
}
impl Selectable for SingleSelect {
fn activate(&mut self, key: Key) {
self.active = key;
}
fn deactivate(&mut self, _key: Key) {
self.active = Key::default();
}
fn is_active(&self, key: Key) -> bool {
self.active == key
}
}
#[derive(Default)]
pub struct MultiSelect {
pub active: HashSet<Key>,
}
impl Selectable for MultiSelect {
fn activate(&mut self, key: Key) {
self.active.insert(key);
}
fn deactivate(&mut self, key: Key) {
self.active.remove(&key);
}
fn is_active(&self, key: Key) -> bool {
self.active.contains(&key)
}
}
pub struct Builder<Selection, Data>(State<Selection, Data>);
impl<Selection: Selectable, Data> Builder<Selection, Data> {
pub fn insert(mut self, content: impl Into<Content>, data: Data) -> Self {
self.0.insert(content, data);
self
}
pub fn insert_active(mut self, content: impl Into<Content>, data: Data) -> Self {
self.0.insert_active(content, data);
self
}
pub fn build(self) -> State<Selection, Data> {
self.0
}
}
/// Convenience type for batching multiple operations
pub struct Batch<'a, Selection, Data>(&'a mut State<Selection, Data>);
impl<'a, Selection: Selectable, Data> Batch<'a, Selection, Data> {
/// Insert a new button.
pub fn insert(self, content: impl Into<Content>, data: Data) -> Self {
self.0.insert(content, data);
self
}
/// Inserts and activates a button.
pub fn insert_active(self, content: impl Into<Content>, data: Data) -> Self {
self.0.insert_active(content, data);
self
}
/// Removes a button.
pub fn remove(&mut self, key: Key) {
self.0.remove(key);
}
}
/// Data to be drawn in a segmented button. /// Data to be drawn in a segmented button.
#[derive(Default, Setters)] #[derive(Default, Setters)]
pub struct Content { pub struct Content {
@ -149,43 +290,3 @@ impl From<Cow<'static, str>> for Content {
Content::default().text(text) Content::default().text(text)
} }
} }
pub struct Builder<Data>(State<Data>);
impl<Data> Builder<Data> {
pub fn insert(mut self, content: impl Into<Content>, data: Data) -> Self {
self.0.insert(content, data);
self
}
pub fn insert_active(mut self, content: impl Into<Content>, data: Data) -> Self {
self.0.insert_active(content, data);
self
}
pub fn build(self) -> State<Data> {
self.0
}
}
/// Convenience type for batching multiple operations
pub struct Batch<'a, Data>(&'a mut State<Data>);
impl<'a, Data> Batch<'a, Data> {
/// Insert a new button.
pub fn insert(self, content: impl Into<Content>, data: Data) -> Self {
self.0.insert(content, data);
self
}
/// Inserts and activates a button.
pub fn insert_active(self, content: impl Into<Content>, data: Data) -> Self {
self.0.insert_active(content, data);
self
}
/// Removes a button.
pub fn remove(&mut self, key: Key) {
self.0.remove(key);
}
}

View file

@ -1,7 +1,7 @@
// Copyright 2022 System76 <info@system76.com> // Copyright 2022 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use super::state::State; use super::state::{Selectable, State};
use super::style::StyleSheet; use super::style::StyleSheet;
use super::widget::{SegmentedButton, SegmentedVariant}; use super::widget::{SegmentedButton, SegmentedVariant};
@ -12,31 +12,34 @@ use iced_native::layout;
pub struct Vertical; pub struct Vertical;
/// Vertical [`SegmentedButton`]. /// Vertical [`SegmentedButton`].
pub type VerticalSegmentedButton<'a, Message, Renderer> = pub type VerticalSegmentedButton<'a, Selection, Message, Renderer> =
SegmentedButton<'a, Vertical, Message, Renderer>; SegmentedButton<'a, Vertical, Selection, Message, Renderer>;
/// Vertical implementation of the [`SegmentedButton`]. /// Vertical implementation of the [`SegmentedButton`].
#[must_use] #[must_use]
pub fn vertical_segmented_button<Message, Renderer, Data>( pub fn vertical_segmented_button<Selection, Message, Renderer, Data>(
state: &State<Data>, state: &State<Selection, Data>,
) -> SegmentedButton<Vertical, Message, Renderer> ) -> SegmentedButton<Vertical, Selection, Message, Renderer>
where where
Renderer: iced_native::Renderer Renderer: iced_native::Renderer
+ iced_native::text::Renderer + iced_native::text::Renderer
+ iced_native::image::Renderer + iced_native::image::Renderer
+ iced_native::svg::Renderer, + iced_native::svg::Renderer,
Renderer::Theme: StyleSheet, Renderer::Theme: StyleSheet,
Selection: Selectable,
{ {
SegmentedButton::new(&state.inner) SegmentedButton::new(&state.inner)
} }
impl<'a, Message, Renderer> SegmentedVariant for SegmentedButton<'a, Vertical, Message, Renderer> impl<'a, Selection, Message, Renderer> SegmentedVariant
for SegmentedButton<'a, Vertical, Selection, Message, Renderer>
where where
Renderer: iced_native::Renderer Renderer: iced_native::Renderer
+ iced_native::text::Renderer + iced_native::text::Renderer
+ iced_native::image::Renderer + iced_native::image::Renderer
+ iced_native::svg::Renderer, + iced_native::svg::Renderer,
Renderer::Theme: StyleSheet, Renderer::Theme: StyleSheet,
Selection: Selectable,
{ {
type Renderer = Renderer; type Renderer = Renderer;

View file

@ -3,7 +3,7 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use super::state::{Key, SharedWidgetState}; use super::state::{Key, Selectable, SharedWidgetState};
use super::style::StyleSheet; use super::style::StyleSheet;
use derive_setters::Setters; use derive_setters::Setters;
@ -35,17 +35,18 @@ pub trait SegmentedVariant {
} }
#[derive(Setters)] #[derive(Setters)]
pub struct SegmentedButton<'a, Variant, Message, Renderer> pub struct SegmentedButton<'a, Variant, Selection, Message, Renderer>
where where
Renderer: iced_native::Renderer Renderer: iced_native::Renderer
+ iced_native::text::Renderer + iced_native::text::Renderer
+ iced_native::image::Renderer + iced_native::image::Renderer
+ iced_native::svg::Renderer, + iced_native::svg::Renderer,
Renderer::Theme: StyleSheet, Renderer::Theme: StyleSheet,
Selection: Selectable,
{ {
/// Contains application state also used for drawing. /// Contains application state also used for drawing.
#[setters(skip)] #[setters(skip)]
pub(super) state: &'a SharedWidgetState, pub(super) state: &'a SharedWidgetState<Selection>,
/// Padding around a button. /// Padding around a button.
pub(super) button_padding: [u16; 4], pub(super) button_padding: [u16; 4],
/// Desired height of a button. /// Desired height of a button.
@ -77,7 +78,8 @@ where
variant: PhantomData<Variant>, variant: PhantomData<Variant>,
} }
impl<'a, Variant, Message, Renderer> SegmentedButton<'a, Variant, Message, Renderer> impl<'a, Variant, Selection, Message, Renderer>
SegmentedButton<'a, Variant, Selection, Message, Renderer>
where where
Renderer: iced_native::Renderer Renderer: iced_native::Renderer
+ iced_native::text::Renderer + iced_native::text::Renderer
@ -85,9 +87,10 @@ where
+ iced_native::svg::Renderer, + iced_native::svg::Renderer,
Renderer::Theme: StyleSheet, Renderer::Theme: StyleSheet,
Self: SegmentedVariant<Renderer = Renderer>, Self: SegmentedVariant<Renderer = Renderer>,
Selection: Selectable,
{ {
#[must_use] #[must_use]
pub fn new(state: &'a SharedWidgetState) -> Self { pub fn new(state: &'a SharedWidgetState<Selection>) -> Self {
Self { Self {
state, state,
button_padding: [4, 4, 4, 4], button_padding: [4, 4, 4, 4],
@ -153,8 +156,8 @@ where
} }
} }
impl<'a, Variant, Message, Renderer> Widget<Message, Renderer> impl<'a, Variant, Selection, Message, Renderer> Widget<Message, Renderer>
for SegmentedButton<'a, Variant, Message, Renderer> for SegmentedButton<'a, Variant, Selection, Message, Renderer>
where where
Renderer: iced_native::Renderer Renderer: iced_native::Renderer
+ iced_native::text::Renderer + iced_native::text::Renderer
@ -162,6 +165,7 @@ where
+ iced_native::svg::Renderer, + iced_native::svg::Renderer,
Renderer::Theme: StyleSheet, Renderer::Theme: StyleSheet,
Self: SegmentedVariant<Renderer = Renderer>, Self: SegmentedVariant<Renderer = Renderer>,
Selection: Selectable,
Message: 'static + Clone, Message: 'static + Clone,
{ {
fn tag(&self) -> tree::Tag { fn tag(&self) -> tree::Tag {
@ -273,7 +277,7 @@ where
for (nth, (key, content)) in self.state.buttons.iter().enumerate() { for (nth, (key, content)) in self.state.buttons.iter().enumerate() {
let mut bounds = self.variant_button_bounds(bounds, nth); let mut bounds = self.variant_button_bounds(bounds, nth);
let (status_appearance, font) = if self.state.active == key { let (status_appearance, font) = if self.state.selection.is_active(key) {
(appearance.active, &self.font_active) (appearance.active, &self.font_active)
} else if state.hovered == key { } else if state.hovered == key {
(appearance.hover, &self.font_hovered) (appearance.hover, &self.font_hovered)
@ -392,7 +396,8 @@ where
} }
} }
impl<'a, Variant, Message, Renderer> From<SegmentedButton<'a, Variant, Message, Renderer>> impl<'a, Variant, Selection, Message, Renderer>
From<SegmentedButton<'a, Variant, Selection, Message, Renderer>>
for Element<'a, Message, Renderer> for Element<'a, Message, Renderer>
where where
Renderer: iced_native::Renderer Renderer: iced_native::Renderer
@ -401,11 +406,13 @@ where
+ iced_native::svg::Renderer + iced_native::svg::Renderer
+ 'a, + 'a,
Renderer::Theme: StyleSheet, Renderer::Theme: StyleSheet,
SegmentedButton<'a, Variant, Message, Renderer>: SegmentedVariant<Renderer = Renderer>, SegmentedButton<'a, Variant, Selection, Message, Renderer>:
SegmentedVariant<Renderer = Renderer>,
Variant: 'static, Variant: 'static,
Selection: Selectable,
Message: 'static + Clone, Message: 'static + Clone,
{ {
fn from(mut widget: SegmentedButton<'a, Variant, Message, Renderer>) -> Self { fn from(mut widget: SegmentedButton<'a, Variant, Selection, Message, Renderer>) -> Self {
if widget.state.buttons.is_empty() { if widget.state.buttons.is_empty() {
widget.spacing = 0; widget.spacing = 0;
} }