Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
Jeremy Soller
f081161d97
Work around issues with derive(Default) 2024-04-23 09:25:13 -06:00
Jeremy Soller
9a0c338876
WIP: segmented_button::Model custom elements 2024-04-22 16:04:06 -06:00
12 changed files with 84 additions and 60 deletions

View file

@ -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
}

View file

@ -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)]
@ -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,
@ -118,11 +118,11 @@ 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,
@ -130,7 +130,7 @@ impl ColorPickerModel {
)
}
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);
@ -222,7 +222,7 @@ impl ColorPickerModel {
}
#[must_use]
pub fn builder<Message>(
pub fn builder(
&self,
on_update: fn(ColorPickerUpdate) -> Message,
) -> ColorPickerBuilder<Message> {
@ -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)]

View file

@ -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 {
@ -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,

View file

@ -22,10 +22,10 @@ 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)
}
@ -33,7 +33,7 @@ where
impl<'a, SelectionMode, Message> SegmentedVariant
for SegmentedButton<'a, Horizontal, SelectionMode, Message>
where
Model<SelectionMode>: Selectable,
Model<SelectionMode, Message>: Selectable,
SelectionMode: Default,
{
fn variant_appearance(

View file

@ -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)]

View file

@ -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.
///

View file

@ -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;
@ -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>,
@ -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,
{
@ -110,7 +128,7 @@ where
/// .build();
/// ```
#[must_use]
pub fn builder() -> ModelBuilder<SelectionMode> {
pub fn builder() -> ModelBuilder<SelectionMode, Message> {
ModelBuilder::default()
}
@ -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 }

View file

@ -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;
@ -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> {
@ -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;
@ -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);

View file

@ -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)
@ -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(

View file

@ -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.
@ -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(),
@ -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,
{
@ -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,
{

View file

@ -15,10 +15,10 @@ use super::segmented_button::{
///
/// For details on the model, see the [`segmented_button`] module for more details.
pub fn horizontal<SelectionMode: Default, Message>(
model: &Model<SelectionMode>,
model: &Model<SelectionMode, Message>,
) -> HorizontalSegmentedButton<SelectionMode, Message>
where
Model<SelectionMode>: Selectable,
Model<SelectionMode, Message>: Selectable,
{
let theme = crate::theme::active();
let space_s = theme.cosmic().space_s();
@ -40,10 +40,10 @@ where
///
/// For details on the model, see the [`segmented_button`] module for more details.
pub fn vertical<SelectionMode, Message>(
model: &Model<SelectionMode>,
model: &Model<SelectionMode, Message>,
) -> VerticalSegmentedButton<SelectionMode, Message>
where
Model<SelectionMode>: Selectable,
Model<SelectionMode, Message>: Selectable,
SelectionMode: Default,
{
let theme = crate::theme::active();

View file

@ -15,10 +15,10 @@ use super::segmented_button::{
///
/// For details on the model, see the [`segmented_button`] module for more details.
pub fn horizontal<SelectionMode: Default, Message>(
model: &Model<SelectionMode>,
model: &Model<SelectionMode, Message>,
) -> HorizontalSegmentedButton<SelectionMode, Message>
where
Model<SelectionMode>: Selectable,
Model<SelectionMode, Message>: Selectable,
{
let theme = crate::theme::active();
let space_s = theme.cosmic().space_s();
@ -38,10 +38,10 @@ where
/// The data for the widget comes from a model that is maintained the application.
/// For details on the model, see the [`segmented_button`] module for more details.
pub fn vertical<SelectionMode, Message>(
model: &Model<SelectionMode>,
model: &Model<SelectionMode, Message>,
) -> VerticalSegmentedButton<SelectionMode, Message>
where
Model<SelectionMode>: Selectable,
Model<SelectionMode, Message>: Selectable,
SelectionMode: Default,
{
let theme = crate::theme::active();