Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: segmented_button::Model custom elements #408

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ where
}

/// Allows COSMIC to integrate with your application's [`nav_bar::Model`].
fn nav_model(&self) -> Option<&nav_bar::Model> {
fn nav_model(&self) -> Option<&nav_bar::Model<Message<Self::Message>>> {
None
}

Expand Down
16 changes: 8 additions & 8 deletions src/widget/color_picker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ pub enum ColorPickerUpdate {
}

#[derive(Setters)]
pub struct ColorPickerModel {
pub struct ColorPickerModel<Message> {
#[setters(skip)]
segmented_model: Model<SingleSelect>,
segmented_model: Model<SingleSelect, Message>,
#[setters(skip)]
active_color: palette::Hsv,
#[setters(skip)]
Expand All @@ -86,7 +86,7 @@ pub struct ColorPickerModel {
copied_at: Option<Instant>,
}

impl ColorPickerModel {
impl<Message> ColorPickerModel<Message> {
#[must_use]
pub fn new(
hex: impl Into<Cow<'static, str>> + Clone,
Expand Down Expand Up @@ -118,19 +118,19 @@ impl ColorPickerModel {

/// Get a color picker button that displays the applied color
///
pub fn picker_button<'a, Message: 'static, T: Fn(ColorPickerUpdate) -> Message>(
pub fn picker_button<'a, T: Fn(ColorPickerUpdate) -> Message>(
&self,
f: T,
icon_portion: Option<u16>,
) -> crate::widget::Button<'a, Message, crate::Theme, crate::Renderer> {
) -> crate::widget::Button<'a, Message, crate::Theme, crate::Renderer> where Message: 'static {
color_button(
Some(f(ColorPickerUpdate::ToggleColorPicker)),
self.applied_color,
Length::FillPortion(icon_portion.unwrap_or(12)),
)
}

pub fn update<Message>(&mut self, update: ColorPickerUpdate) -> Command<Message> {
pub fn update(&mut self, update: ColorPickerUpdate) -> Command<Message> {
match update {
ColorPickerUpdate::ActiveColor(c) => {
self.must_clear_cache.store(true, Ordering::SeqCst);
Expand Down Expand Up @@ -222,7 +222,7 @@ impl ColorPickerModel {
}

#[must_use]
pub fn builder<Message>(
pub fn builder(
&self,
on_update: fn(ColorPickerUpdate) -> Message,
) -> ColorPickerBuilder<Message> {
Expand All @@ -246,7 +246,7 @@ impl ColorPickerModel {
#[derive(Setters, Clone)]
pub struct ColorPickerBuilder<'a, Message> {
#[setters(skip)]
model: &'a Model<SingleSelect>,
model: &'a Model<SingleSelect, Message>,
#[setters(skip)]
active_color: palette::Hsv,
#[setters(skip)]
Expand Down
6 changes: 3 additions & 3 deletions src/widget/nav_bar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ use crate::{theme, Theme};
use super::dnd_destination::DragId;

pub type Id = segmented_button::Entity;
pub type Model = segmented_button::SingleSelectModel;
pub type Model<Message> = segmented_button::SingleSelectModel<Message>;

/// Navigation side panel for switching between views.
///
/// For details on the model, see the [`segmented_button`] module for more details.
pub fn nav_bar<Message: Clone + 'static>(
model: &segmented_button::SingleSelectModel,
model: &Model<Message>,
on_activate: fn(segmented_button::Entity) -> Message,
) -> NavBar<Message> {
NavBar {
Expand All @@ -35,7 +35,7 @@ pub fn nav_bar<Message: Clone + 'static>(
/// Navigation side panel for switching between views.
/// Can receive drag and drop events.
pub fn nav_bar_dnd<Message, D: AllowedMimeTypes>(
model: &segmented_button::SingleSelectModel,
model: &Model<Message>,
on_activate: fn(segmented_button::Entity) -> Message,
on_dnd_enter: impl Fn(segmented_button::Entity, Vec<String>) -> Message + 'static,
on_dnd_leave: impl Fn(segmented_button::Entity) -> Message + 'static,
Expand Down
6 changes: 3 additions & 3 deletions src/widget/segmented_button/horizontal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,18 @@ pub struct Horizontal;
///
/// For details on the model, see the [`segmented_button`](super) module for more details.
pub fn horizontal<SelectionMode: Default, Message>(
model: &Model<SelectionMode>,
model: &Model<SelectionMode, Message>,
) -> SegmentedButton<Horizontal, SelectionMode, Message>
where
Model<SelectionMode>: Selectable,
Model<SelectionMode, Message>: Selectable,
{
SegmentedButton::new(model)
}

impl<'a, SelectionMode, Message> SegmentedVariant
for SegmentedButton<'a, Horizontal, SelectionMode, Message>
where
Model<SelectionMode>: Selectable,
Model<SelectionMode, Message>: Selectable,
SelectionMode: Default,
{
fn variant_appearance(
Expand Down
26 changes: 16 additions & 10 deletions src/widget/segmented_button/model/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,44 @@ use crate::widget::icon::Icon;
use std::borrow::Cow;

/// A builder for a [`Model`].
#[derive(Default)]
pub struct ModelBuilder<SelectionMode: Default>(Model<SelectionMode>);
pub struct ModelBuilder<SelectionMode: Default, Message>(Model<SelectionMode, Message>);

//TODO: Default derive ends up requiring Message to implement Default
impl<SelectionMode: Default, Message> Default for ModelBuilder<SelectionMode, Message> {
fn default() -> Self {
Self(Model::default())
}
}

/// Constructs a new item for the [`ModelBuilder`].
pub struct BuilderEntity<SelectionMode: Default> {
model: ModelBuilder<SelectionMode>,
pub struct BuilderEntity<SelectionMode: Default, Message> {
model: ModelBuilder<SelectionMode, Message>,
id: Entity,
}

impl<SelectionMode: Default> ModelBuilder<SelectionMode>
impl<SelectionMode: Default, Message> ModelBuilder<SelectionMode, Message>
where
Model<SelectionMode>: Selectable,
Model<SelectionMode, Message>: Selectable,
{
/// Inserts a new item and its associated data into the model.
#[must_use]
pub fn insert(
mut self,
builder: impl Fn(BuilderEntity<SelectionMode>) -> BuilderEntity<SelectionMode>,
builder: impl Fn(BuilderEntity<SelectionMode, Message>) -> BuilderEntity<SelectionMode, Message>,
) -> Self {
let id = self.0.insert().id();
builder(BuilderEntity { model: self, id }).model
}

/// Consumes the builder and returns the model.
pub fn build(self) -> Model<SelectionMode> {
pub fn build(self) -> Model<SelectionMode, Message> {
self.0
}
}

impl<SelectionMode: Default> BuilderEntity<SelectionMode>
impl<SelectionMode: Default, Message> BuilderEntity<SelectionMode, Message>
where
Model<SelectionMode>: Selectable,
Model<SelectionMode, Message>: Selectable,
{
/// Activates the newly-inserted item.
#[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
Expand Down
8 changes: 4 additions & 4 deletions src/widget/segmented_button/model/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ use crate::widget::Icon;
use super::{Entity, Model, Selectable};

/// A newly-inserted item which may have additional actions applied to it.
pub struct EntityMut<'a, SelectionMode: Default> {
pub struct EntityMut<'a, SelectionMode: Default, Message> {
pub(super) id: Entity,
pub(super) model: &'a mut Model<SelectionMode>,
pub(super) model: &'a mut Model<SelectionMode, Message>,
}

impl<'a, SelectionMode: Default> EntityMut<'a, SelectionMode>
impl<'a, SelectionMode: Default, Message> EntityMut<'a, SelectionMode, Message>
where
Model<SelectionMode>: Selectable,
Model<SelectionMode, Message>: Selectable,
{
/// Activates the newly-inserted item.
///
Expand Down
38 changes: 28 additions & 10 deletions src/widget/segmented_button/model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub use self::entity::EntityMut;
mod selection;
pub use self::selection::{MultiSelect, Selectable, SingleSelect};

use crate::widget::Icon;
use crate::{Element, widget::Icon};
use slotmap::{SecondaryMap, SlotMap};
use std::any::{Any, TypeId};
use std::borrow::Cow;
Expand All @@ -37,27 +37,29 @@ impl Default for Settings {
}

/// A model for single-select button selection.
pub type SingleSelectModel = Model<SingleSelect>;
pub type SingleSelectModel<Message> = Model<SingleSelect, Message>;

/// Single-select variant of an [`EntityMut`].
pub type SingleSelectEntityMut<'a> = EntityMut<'a, SingleSelect>;
pub type SingleSelectEntityMut<'a, Message> = EntityMut<'a, SingleSelect, Message>;

/// A model for multi-select button selection.
pub type MultiSelectModel = Model<MultiSelect>;
pub type MultiSelectModel<Message> = Model<MultiSelect, Message>;

/// Multi-select variant of an [`EntityMut`].
pub type MultiSelectEntityMut<'a> = EntityMut<'a, MultiSelect>;
pub type MultiSelectEntityMut<'a, Message> = EntityMut<'a, MultiSelect, Message>;

/// The portion of the model used only by the application.
#[derive(Debug, Default)]
pub(super) struct Storage(HashMap<TypeId, SecondaryMap<Entity, Box<dyn Any>>>);

/// The model held by the application, containing the unique IDs and data of each inserted item.
#[derive(Default)]
pub struct Model<SelectionMode: Default> {
pub struct Model<SelectionMode: Default, Message> {
/// The content used for drawing segmented items.
pub(super) items: SlotMap<Entity, Settings>,

/// Elements optionally-defined for each item.
pub(super) elements: SecondaryMap<Entity, Element<'static, Message>>,

/// Icons optionally-defined for each item.
pub(super) icons: SecondaryMap<Entity, Icon>,

Expand All @@ -77,7 +79,23 @@ pub struct Model<SelectionMode: Default> {
pub(super) storage: Storage,
}

impl<SelectionMode: Default> Model<SelectionMode>
//TODO: Default derive ends up requiring Message to implement Default
impl<SelectionMode: Default, Message> Default for Model<SelectionMode, Message> {
fn default() -> Self {
Self {
items: SlotMap::default(),
elements: SecondaryMap::default(),
icons: SecondaryMap::default(),
indents: SecondaryMap::default(),
text: SecondaryMap::default(),
order: VecDeque::default(),
selection: SelectionMode::default(),
storage: Storage::default(),
}
}
}

impl<SelectionMode: Default, Message> Model<SelectionMode, Message>
where
Self: Selectable,
{
Expand Down Expand Up @@ -110,7 +128,7 @@ where
/// .build();
/// ```
#[must_use]
pub fn builder() -> ModelBuilder<SelectionMode> {
pub fn builder() -> ModelBuilder<SelectionMode, Message> {
ModelBuilder::default()
}

Expand Down Expand Up @@ -259,7 +277,7 @@ where
/// let id = model.insert().text("Item A").icon("custom-icon").id();
/// ```
#[must_use]
pub fn insert(&mut self) -> EntityMut<SelectionMode> {
pub fn insert(&mut self) -> EntityMut<SelectionMode, Message> {
let id = self.items.insert(Settings::default());
self.order.push_back(id);
EntityMut { model: self, id }
Expand Down
8 changes: 4 additions & 4 deletions src/widget/segmented_button/model/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub struct SingleSelect {
pub active: Entity,
}

impl Selectable for Model<SingleSelect> {
impl<Message> Selectable for Model<SingleSelect, Message> {
fn activate(&mut self, id: Entity) {
if !self.items.contains_key(id) {
return;
Expand All @@ -44,7 +44,7 @@ impl Selectable for Model<SingleSelect> {
}
}

impl Model<SingleSelect> {
impl<Message> Model<SingleSelect, Message> {
/// Get an immutable reference to the data associated with the active item.
#[must_use]
pub fn active_data<Data: 'static>(&self) -> Option<&Data> {
Expand Down Expand Up @@ -75,7 +75,7 @@ pub struct MultiSelect {
pub active: HashSet<Entity>,
}

impl Selectable for Model<MultiSelect> {
impl<Message> Selectable for Model<MultiSelect, Message> {
fn activate(&mut self, id: Entity) {
if !self.items.contains_key(id) {
return;
Expand All @@ -95,7 +95,7 @@ impl Selectable for Model<MultiSelect> {
}
}

impl Model<MultiSelect> {
impl<Message> Model<MultiSelect, Message> {
/// Deactivates the item in the model.
pub fn deactivate(&mut self, id: Entity) {
Selectable::deactivate(self, id);
Expand Down
6 changes: 3 additions & 3 deletions src/widget/segmented_button/vertical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ pub type VerticalSegmentedButton<'a, SelectionMode, Message> =
///
/// For details on the model, see the [`segmented_button`](super) module for more details.
pub fn vertical<SelectionMode, Message>(
model: &Model<SelectionMode>,
model: &Model<SelectionMode, Message>,
) -> SegmentedButton<Vertical, SelectionMode, Message>
where
Model<SelectionMode>: Selectable,
Model<SelectionMode, Message>: Selectable,
SelectionMode: Default,
{
SegmentedButton::new(model)
Expand All @@ -33,7 +33,7 @@ where
impl<'a, SelectionMode, Message> SegmentedVariant
for SegmentedButton<'a, Vertical, SelectionMode, Message>
where
Model<SelectionMode>: Selectable,
Model<SelectionMode, Message>: Selectable,
SelectionMode: Default,
{
fn variant_appearance(
Expand Down
12 changes: 6 additions & 6 deletions src/widget/segmented_button/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ pub trait SegmentedVariant {
#[must_use]
pub struct SegmentedButton<'a, Variant, SelectionMode, Message>
where
Model<SelectionMode>: Selectable,
Model<SelectionMode, Message>: Selectable,
SelectionMode: Default,
{
/// The model borrowed from the application create this widget.
#[setters(skip)]
pub(super) model: &'a Model<SelectionMode>,
pub(super) model: &'a Model<SelectionMode, Message>,
/// iced widget ID
pub(super) id: Id,
/// The icon used for the close button.
Expand Down Expand Up @@ -151,10 +151,10 @@ where
impl<'a, Variant, SelectionMode, Message> SegmentedButton<'a, Variant, SelectionMode, Message>
where
Self: SegmentedVariant,
Model<SelectionMode>: Selectable,
Model<SelectionMode, Message>: Selectable,
SelectionMode: Default,
{
pub fn new(model: &'a Model<SelectionMode>) -> Self {
pub fn new(model: &'a Model<SelectionMode, Message>) -> Self {
Self {
model,
id: Id::unique(),
Expand Down Expand Up @@ -536,7 +536,7 @@ impl<'a, Variant, SelectionMode, Message> Widget<Message, crate::Theme, Renderer
for SegmentedButton<'a, Variant, SelectionMode, Message>
where
Self: SegmentedVariant,
Model<SelectionMode>: Selectable,
Model<SelectionMode, Message>: Selectable,
SelectionMode: Default,
Message: 'static + Clone,
{
Expand Down Expand Up @@ -1555,7 +1555,7 @@ impl<'a, Variant, SelectionMode, Message> From<SegmentedButton<'a, Variant, Sele
where
SegmentedButton<'a, Variant, SelectionMode, Message>: SegmentedVariant,
Variant: 'static,
Model<SelectionMode>: Selectable,
Model<SelectionMode, Message>: Selectable,
SelectionMode: Default,
Message: 'static + Clone,
{
Expand Down
Loading
Loading