// Copyright 2022 System76 // SPDX-License-Identifier: MPL-2.0 use derive_setters::Setters; use slotmap::{SecondaryMap, SlotMap}; use std::{borrow::Cow, collections::HashSet}; use crate::widget::IconSource; slotmap::new_key_type! { /// An ID for a segmented button pub struct Key; } /// Contains all state for interacting with a segmented button. pub struct State { /// State that is shared with widget drawing. pub inner: SharedWidgetState, /// State unique to the application. pub data: SecondaryState, } impl Default for State { fn default() -> Self { Self { inner: SharedWidgetState::default(), data: SecondaryState::default(), } } } /// State which is most useful to the widget. #[derive(Default)] pub struct SharedWidgetState { /// The content used for drawing segmented buttons. pub buttons: SlotMap, /// Manages selections pub selection: Variant, } impl State { pub fn activate(&mut self, key: Key) { 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 ID of the active button. #[must_use] pub fn active(&self) -> Key { self.inner.selection.active } /// Get the application data for the active button. #[must_use] pub fn active_data(&self) -> Option<&Data> { 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 State { 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 + '_ { self.inner.selection.active.iter().copied() } /// Get the application data for the active buttons. pub fn active_data(&self) -> impl Iterator { 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 = SecondaryMap; impl State where Selection: Selectable, { #[must_use] pub fn builder() -> Builder { Builder(Self::default()) } /// Convenience method for batching multiple operations #[must_use] pub fn batch(&mut self) -> Batch { Batch(self) } /// Enables or disables a button #[must_use] pub fn content(&self, key: Key) -> Option<&Content> { self.inner.buttons.get(key) } /// Enables or disables a button #[must_use] pub fn content_mut(&mut self, key: Key) -> Option<&mut Content> { self.inner.buttons.get_mut(key) } /// Get the application data for a button. #[must_use] pub fn data(&self, key: Key) -> Option<&Data> { self.data.get(key) } /// Get mutable application data for a button. #[must_use] pub fn data_mut(&mut self, key: Key) -> Option<&mut Data> { self.data.get_mut(key) } /// Insert a new button. pub fn insert(&mut self, content: impl Into, data: Data) -> Key { let key = self.inner.buttons.insert(content.into()); self.data.insert(key, data); key } /// Inserts and activates a button. pub fn insert_active(&mut self, content: impl Into, data: Data) -> Key { let key = self.insert(content, data); self.inner.selection.activate(key); key } /// Removes a button. pub fn remove(&mut self, key: Key) -> Option { self.inner.buttons.remove(key); self.inner.selection.deactivate(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, } 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(State); impl Builder { pub fn insert(mut self, content: impl Into, data: Data) -> Self { self.0.insert(content, data); self } pub fn insert_active(mut self, content: impl Into, data: Data) -> Self { self.0.insert_active(content, data); self } pub fn build(self) -> State { self.0 } } /// Convenience type for batching multiple operations pub struct Batch<'a, Selection, Data>(&'a mut State); impl<'a, Selection: Selectable, Data> Batch<'a, Selection, Data> { /// Insert a new button. pub fn insert(self, content: impl Into, data: Data) -> Self { self.0.insert(content, data); self } /// Inserts and activates a button. pub fn insert_active(self, content: impl Into, 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. #[derive(Default, Setters)] pub struct Content { #[setters(strip_option, into)] /// The label to display in this button. pub text: Option>, #[setters(strip_option, into)] /// An optionally-displayed icon beside the label. pub icon: Option>, /// Whether the button is clickable or not. pub enabled: bool, } impl From for Content { fn from(text: String) -> Self { Self::from(Cow::Owned(text)) } } impl From<&'static str> for Content { fn from(text: &'static str) -> Self { Self::from(Cow::Borrowed(text)) } } impl From> for Content { fn from(text: Cow<'static, str>) -> Self { Content::default().text(text) } }