feat: Re-orderable positioning of items in segmented button

Calling `model.swap_position(key1, key2)` will swap the positions of
these two items in the model.
This commit is contained in:
Michael Aaron Murphy 2023-01-09 19:26:31 +01:00 committed by Michael Murphy
parent a55f41fc42
commit c4bd0fa3d8
3 changed files with 68 additions and 22 deletions

View file

@ -63,6 +63,7 @@ pub struct State {
pub icon_theme: segmented_button::SingleSelectModel<&'static str>,
pub multi_selection: segmented_button::MultiSelectModel<MultiOption>,
pub pick_list_selected: Option<&'static str>,
pub pick_list_options: Vec<&'static str>,
pub selection: segmented_button::SingleSelectModel<()>,
pub slider_value: f32,
pub spin_button: SpinButtonModel<i32>,
@ -75,6 +76,7 @@ impl Default for State {
State {
checkbox_value: false,
pick_list_selected: Some("Option 1"),
pick_list_options: vec!["Option 1", "Option 2", "Option 3", "Option 4"],
slider_value: 50.0,
spin_button: SpinButtonModel::default().min(-10).max(10),
toggler_value: false,
@ -200,7 +202,7 @@ impl State {
.add(settings::item(
"Pick List (TODO)",
pick_list(
vec!["Option 1", "Option 2", "Option 3", "Option 4"],
&self.pick_list_options,
self.pick_list_selected,
Message::PickListSelected,
)

View file

@ -1,6 +1,8 @@
// Copyright 2022 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
use std::collections::VecDeque;
use super::selection_modes::{MultiSelect, Selectable, SingleSelect};
use super::SegmentedItem;
use slotmap::{SecondaryMap, SlotMap};
@ -38,30 +40,37 @@ pub struct WidgetModel<SelectionMode> {
/// The content used for drawing segmented items.
pub(super) items: SlotMap<Key, SegmentedItem>,
/// Order which the items will be displayed.
pub(super) order: VecDeque<Key>,
/// Manages selections
pub(super) selection: SelectionMode,
}
impl<Component> Model<SingleSelect, Component> {
/// Activates the item in the model.
pub fn activate(&mut self, key: Key) {
self.widget.selection.active = key;
}
/// Get an immutable reference to the component associated with the active item.
#[must_use]
pub fn active_component(&self) -> Option<&Component> {
self.component(self.active())
}
/// Get a mutable reference to the component associated with the active item.
#[must_use]
pub fn active_component_mut(&mut self) -> Option<&mut Component> {
self.component_mut(self.active())
}
/// Deactivates the active item.
pub fn deactivate(&mut self) {
self.widget.selection.active = Key::default();
}
/// The ID of the active button.
/// The ID of the active item.
#[must_use]
pub fn active(&self) -> Key {
self.widget.selection.active
@ -69,12 +78,14 @@ impl<Component> Model<SingleSelect, Component> {
}
impl<Component> Model<MultiSelect, Component> {
/// Activates the item in the model.
pub fn activate(&mut self, key: Key) {
if !self.widget.selection.active.insert(key) {
self.widget.selection.active.remove(&key);
}
}
/// Deactivates the item in the model.
pub fn deactivate(&mut self, key: Key) {
self.widget.selection.active.remove(&key);
}
@ -89,6 +100,7 @@ impl<SelectionMode, Component> Model<SelectionMode, Component>
where
SelectionMode: Selectable,
{
/// Creates a builder for initializing a model.
#[must_use]
pub fn builder() -> ModelBuilder<SelectionMode, Component> {
ModelBuilder(Self {
@ -103,34 +115,37 @@ where
Batch(self)
}
/// Enables or disables a button
/// Get an immutable reference to an item in the model.
#[must_use]
pub fn content(&self, key: Key) -> Option<&SegmentedItem> {
self.widget.items.get(key)
}
/// Enables or disables a button
/// Get a mutable reference to an item in the model.
#[must_use]
pub fn content_mut(&mut self, key: Key) -> Option<&mut SegmentedItem> {
pub fn item_mut(&mut self, key: Key) -> Option<&mut SegmentedItem> {
self.widget.items.get_mut(key)
}
/// Get an immutable reference to a component associated with an item.
pub fn component(&self, key: Key) -> Option<&Component> {
self.app.0.get(key)
}
/// Get a mutable reference to a component associated with an item.
pub fn component_mut(&mut self, key: Key) -> Option<&mut Component> {
self.app.0.get_mut(key)
}
/// Insert a new button.
/// Insert a new item in the model.
pub fn insert(&mut self, content: impl Into<SegmentedItem>, component: Component) -> Key {
let key = self.widget.items.insert(content.into());
self.widget.order.push_back(key);
self.app.0.insert(key, component);
key
}
/// Inserts and activates a button.
/// Inserts and activates an item into the model.
pub fn insert_active(
&mut self,
content: impl Into<SegmentedItem>,
@ -141,27 +156,53 @@ where
key
}
/// Checks if the item is active in the model.
#[must_use]
pub fn is_active(&self, key: Key) -> bool {
self.widget.selection.is_active(key)
}
/// Removes a button.
/// The position of the item in the model.
pub fn position(&self, key: Key) -> Option<usize> {
self.widget.order.iter().position(|k| *k == key)
}
/// Removes an item from the model.
pub fn remove(&mut self, key: Key) {
self.widget.items.remove(key);
self.widget.selection.deactivate(key);
self.app.0.remove(key);
if let Some(index) = self.position(key) {
self.widget.order.remove(index);
}
}
/// Swap the position of two items in the model.
pub fn swap_position(&mut self, first: Key, second: Key) {
let Some(first_index) = self.position(first) else {
return
};
let Some(second_index) = self.position(second) else {
return
};
self.widget.order.swap(first_index, second_index);
}
}
pub struct ModelBuilder<SelectionMode, Component>(Model<SelectionMode, Component>);
impl<SelectionMode: Selectable, Component> ModelBuilder<SelectionMode, Component> {
/// Inserts a new item and its associated component into the model.
#[must_use]
pub fn insert(mut self, content: impl Into<SegmentedItem>, component: Component) -> Self {
self.0.insert(content, component);
self
}
/// Inserts and activates an new item.
#[must_use]
pub fn insert_active(
mut self,

View file

@ -148,21 +148,22 @@ where
/// Focus the previous item in the widget.
fn focus_previous(&mut self, state: &mut LocalState) -> event::Status {
let mut previous_key = None;
let mut keys = self.model.order.iter().copied().rev();
for key in self.model.items.keys() {
while let Some(key) = keys.next() {
if key == state.focused_key {
if let Some(key) = previous_key {
for key in keys {
// Skip disabled buttons.
if !self.is_enabled(key) {
continue;
}
state.focused_key = key;
return event::Status::Captured;
}
break;
}
if self.is_enabled(key) {
previous_key = Some(key);
}
}
state.focused_key = Key::default();
@ -171,7 +172,7 @@ where
/// Focus the next item in the widget.
fn focus_next(&mut self, state: &mut LocalState) -> event::Status {
let mut keys = self.model.items.keys();
let mut keys = self.model.order.iter().copied();
while let Some(key) = keys.next() {
if key == state.focused_key {
@ -258,7 +259,7 @@ where
fn state(&self) -> tree::State {
tree::State::new(LocalState {
first: self.model.items.keys().next().unwrap_or_default(),
first: self.model.order.iter().copied().next().unwrap_or_default(),
..LocalState::default()
})
}
@ -289,10 +290,10 @@ where
let state = tree.state.downcast_mut::<LocalState>();
if bounds.contains(cursor_position) {
for (nth, (key, content)) in self.model.items.iter().enumerate() {
for (nth, key) in self.model.order.iter().copied().enumerate() {
let bounds = self.variant_button_bounds(bounds, nth);
if bounds.contains(cursor_position) {
if content.enabled {
if self.model.items[key].enabled {
if let Some(on_activate) = self.on_activate.as_ref() {
if let Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
| Event::Touch(touch::Event::FingerLifted { .. }) = event
@ -363,12 +364,12 @@ where
let bounds = layout.bounds();
if bounds.contains(cursor_position) {
for (nth, content) in self.model.items.values().enumerate() {
for (nth, key) in self.model.order.iter().copied().enumerate() {
if self
.variant_button_bounds(bounds, nth)
.contains(cursor_position)
{
return if content.enabled {
return if self.model.items[key].enabled {
iced_native::mouse::Interaction::Pointer
} else {
iced_native::mouse::Interaction::Idle
@ -410,7 +411,9 @@ where
}
// Draw each of the items in the widget.
for (nth, (key, content)) in self.model.items.iter().enumerate() {
for (nth, key) in self.model.order.iter().copied().enumerate() {
let content = &self.model.items[key];
let mut bounds = self.variant_button_bounds(bounds, nth);
let (status_appearance, font) = if state.focused_key == key {