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:
parent
a55f41fc42
commit
c4bd0fa3d8
3 changed files with 68 additions and 22 deletions
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue