diff --git a/examples/table-view/src/main.rs b/examples/table-view/src/main.rs index d7dd8ae8..a80714a9 100644 --- a/examples/table-view/src/main.rs +++ b/examples/table-view/src/main.rs @@ -101,7 +101,8 @@ fn main() -> Result<(), Box> { #[derive(Clone, Debug)] pub enum Message { ItemSelect(table::Entity), - CategorySelect(Category, bool), + CategorySelect(Category), + PrintMsg(String), NoOp, } @@ -187,9 +188,16 @@ impl cosmic::Application for App { fn update(&mut self, message: Self::Message) -> Task { match message { Message::ItemSelect(entity) => self.table_model.activate(entity), - Message::CategorySelect(category, descending) => { - self.table_model.sort(category, descending) + Message::CategorySelect(category) => { + let mut ascending = true; + if let Some(old_sort) = self.table_model.get_sort() { + if old_sort.0 == category { + ascending = !old_sort.1; + } + } + self.table_model.sort(category, ascending) } + Message::PrintMsg(string) => tracing_log::log::info!("{}", string), Message::NoOp => {} } Task::none() @@ -200,7 +208,7 @@ impl cosmic::Application for App { cosmic::widget::responsive(|size| { if size.width < 600.0 { widget::compact_table(&self.table_model) - .on_item_select(Message::ItemSelect) + .on_item_left_click(Message::ItemSelect) .item_context(|item| { Some(widget::menu::items( &HashMap::new(), @@ -214,8 +222,8 @@ impl cosmic::Application for App { .apply(Element::from) } else { widget::table(&self.table_model) - .on_item_select(Message::ItemSelect) - .on_category_select(Message::CategorySelect) + .on_item_left_click(Message::ItemSelect) + .on_category_left_click(Message::CategorySelect) .item_context(|item| { Some(widget::menu::items( &HashMap::new(), @@ -227,53 +235,21 @@ impl cosmic::Application for App { )) }) .category_context(|category| { - Some(match category { - Category::Name => widget::menu::items( - &HashMap::new(), - vec![ - widget::menu::Item::Button( - "Action on Name Category", - None, - Action::None, - ), - widget::menu::Item::Button( - "Other action on Name", - None, - Action::None, - ), - ], - ), - Category::Date => widget::menu::items( - &HashMap::new(), - vec![ - widget::menu::Item::Button( - "Action on Date Category", - None, - Action::None, - ), - widget::menu::Item::Button( - "Other action on Date", - None, - Action::None, - ), - ], - ), - Category::Size => widget::menu::items( - &HashMap::new(), - vec![ - widget::menu::Item::Button( - "Action on Size Category", - None, - Action::None, - ), - widget::menu::Item::Button( - "Other action on Size", - None, - Action::None, - ), - ], - ), - }) + Some(widget::menu::items( + &HashMap::new(), + vec![ + widget::menu::Item::Button( + format!("Action on {} category", category.to_string()), + None, + Action::None, + ), + widget::menu::Item::Button( + format!("Other action on {} category", category.to_string()), + None, + Action::None, + ), + ], + )) }) .apply(Element::from) } diff --git a/src/widget/table/model/mod.rs b/src/widget/table/model/mod.rs index b6935435..99a7bd67 100644 --- a/src/widget/table/model/mod.rs +++ b/src/widget/table/model/mod.rs @@ -340,6 +340,11 @@ where } } + /// Get the sort data + pub fn get_sort(&self) -> Option<(Category, bool)> { + self.sort + } + /// Sorts items in the model, this should be called before it is drawn after all items have been added for the view pub fn sort(&mut self, category: Category, ascending: bool) { self.sort = Some((category, ascending)); diff --git a/src/widget/table/widget/compact.rs b/src/widget/table/widget/compact.rs index c7555751..0264be71 100644 --- a/src/widget/table/widget/compact.rs +++ b/src/widget/table/widget/compact.rs @@ -35,13 +35,17 @@ where #[setters(into)] pub(super) divider_padding: Padding, + // === Item Interaction === + #[setters(skip)] + pub(super) on_item_mb_left: Option Message + 'static>>, + #[setters(skip)] + pub(super) on_item_mb_double: Option Message + 'static>>, + #[setters(skip)] + pub(super) on_item_mb_mid: Option Message + 'static>>, + #[setters(skip)] + pub(super) on_item_mb_right: Option Message + 'static>>, #[setters(skip)] pub(super) item_context_builder: Box Option>>>, - - #[setters(skip)] - pub(super) on_item_select: Option Message + 'a>>, - #[setters(skip)] - pub(super) on_item_context: Option Message + 'a>>, } impl<'a, SelectionMode, Item, Category, Message> @@ -132,11 +136,36 @@ where } })) .apply(widget::mouse_area) - .apply(|ma| { - if let Some(on_item_select) = &val.on_item_select { - ma.on_press((on_item_select)(entity)) + // Left click + .apply(|mouse_area| { + if let Some(ref on_item_mb) = val.on_item_mb_left { + mouse_area.on_press((on_item_mb)(entity)) } else { - ma + mouse_area + } + }) + // Double click + .apply(|mouse_area| { + if let Some(ref on_item_mb) = val.on_item_mb_left { + mouse_area.on_double_click((on_item_mb)(entity)) + } else { + mouse_area + } + }) + // Middle click + .apply(|mouse_area| { + if let Some(ref on_item_mb) = val.on_item_mb_mid { + mouse_area.on_middle_press((on_item_mb)(entity)) + } else { + mouse_area + } + }) + // Right click + .apply(|mouse_area| { + if let Some(ref on_item_mb) = val.on_item_mb_right { + mouse_area.on_right_press((on_item_mb)(entity)) + } else { + mouse_area } }) .apply(|ma| widget::context_menu(ma, context_menu)), @@ -177,25 +206,43 @@ where item_spacing: 0, icon_size: 48, + on_item_mb_left: None, + on_item_mb_double: None, + on_item_mb_mid: None, + on_item_mb_right: None, item_context_builder: Box::new(|_| None), - on_item_select: None, - on_item_context: None, } } - pub fn on_item_select(mut self, on_select: F) -> Self + pub fn on_item_left_click(mut self, on_click: F) -> Self where - F: Fn(Entity) -> Message + 'a, + F: Fn(Entity) -> Message + 'static, { - self.on_item_select = Some(Box::new(on_select)); + self.on_item_mb_left = Some(Box::new(on_click)); self } - pub fn on_item_context(mut self, on_select: F) -> Self + pub fn on_item_double_click(mut self, on_click: F) -> Self where - F: Fn(Entity) -> Message + 'a, + F: Fn(Entity) -> Message + 'static, { - self.on_item_context = Some(Box::new(on_select)); + self.on_item_mb_double = Some(Box::new(on_click)); + self + } + + pub fn on_item_middle_click(mut self, on_click: F) -> Self + where + F: Fn(Entity) -> Message + 'static, + { + self.on_item_mb_mid = Some(Box::new(on_click)); + self + } + + pub fn on_item_right_click(mut self, on_click: F) -> Self + where + F: Fn(Entity) -> Message + 'static, + { + self.on_item_mb_right = Some(Box::new(on_click)); self } diff --git a/src/widget/table/widget/standard.rs b/src/widget/table/widget/standard.rs index 4f84646e..35762eea 100644 --- a/src/widget/table/widget/standard.rs +++ b/src/widget/table/widget/standard.rs @@ -10,7 +10,7 @@ use crate::{ widget::{self, container, divider, menu}, Apply, Element, }; -use iced::{Alignment, Border, Padding}; +use iced::{Alignment, Border, Length, Padding}; // THIS IS A PLACEHOLDER UNTIL A MORE SOPHISTICATED WIDGET CAN BE DEVELOPED @@ -28,28 +28,45 @@ where #[setters(into)] pub(super) element_padding: Padding, + #[setters(into)] + pub(super) width: Length, + #[setters(into)] + pub(super) height: Length, #[setters(into)] pub(super) item_padding: Padding, pub(super) item_spacing: u16, + pub(super) icon_spacing: u16, pub(super) icon_size: u16, #[setters(into)] pub(super) divider_padding: Padding, + // === Item Interaction === + #[setters(skip)] + pub(super) on_item_mb_left: Option Message + 'static>>, + #[setters(skip)] + pub(super) on_item_mb_double: Option Message + 'static>>, + #[setters(skip)] + pub(super) on_item_mb_mid: Option Message + 'static>>, + #[setters(skip)] + pub(super) on_item_mb_right: Option Message + 'static>>, #[setters(skip)] pub(super) item_context_builder: Box Option>>>, - #[setters(skip)] - pub(super) category_contexts: Box Option>>>, + // Item DND + // === Category Interaction === #[setters(skip)] - pub(super) on_item_select: Option Message + 'a>>, + pub(super) on_category_mb_left: Option Message + 'static>>, #[setters(skip)] - pub(super) on_item_context: Option Message + 'a>>, + pub(super) on_category_mb_double: Option Message + 'static>>, #[setters(skip)] - pub(super) on_category_select: Option Message + 'a>>, + pub(super) on_category_mb_mid: Option Message + 'static>>, #[setters(skip)] - pub(super) on_category_context: Option Message + 'a>>, + pub(super) on_category_mb_right: Option Message + 'static>>, + #[setters(skip)] + pub(super) category_context_builder: + Box Option>>>, } impl<'a, SelectionMode, Item, Category, Message> @@ -62,15 +79,14 @@ where Message: Clone + 'static, { fn from(val: TableView<'a, SelectionMode, Item, Category, Message>) -> Self { - let cosmic_theme::Spacing { space_xxxs, .. } = theme::active().cosmic().spacing; - + // Header row let header_row = val .model .categories .iter() .cloned() .map(|category| { - let cat_context_tree = (val.category_contexts)(category); + let cat_context_tree = (val.category_context_builder)(category); let mut sort_state = 0; @@ -86,7 +102,7 @@ where // Build the category header widget::row() - .spacing(space_xxxs) + .spacing(val.icon_spacing) .push(widget::text::heading(category.to_string())) .push_maybe(match sort_state { 1 => Some(widget::icon::from_name("pan-up-symbolic").icon()), @@ -102,19 +118,8 @@ where .width(category.width()) .apply(widget::mouse_area) .apply(|mouse_area| { - if let Some(ref on_category_select) = val.on_category_select { - mouse_area.on_press((on_category_select)( - category, - if let Some(sort) = val.model.sort { - if sort.0 == category { - !sort.1 - } else { - false - } - } else { - false - }, - )) + if let Some(ref on_category_select) = val.on_category_mb_left { + mouse_area.on_press((on_category_select)(category)) } else { mouse_area } @@ -149,7 +154,7 @@ where .iter() .map(|category| { widget::row() - .spacing(space_xxxs) + .spacing(val.icon_spacing) .push_maybe( item.get_icon(*category) .map(|icon| icon.size(val.icon_size)), @@ -192,9 +197,34 @@ where } })) .apply(widget::mouse_area) + // Left click .apply(|mouse_area| { - if let Some(ref on_item_select) = val.on_item_select { - mouse_area.on_press((on_item_select)(entity)) + if let Some(ref on_item_mb) = val.on_item_mb_left { + mouse_area.on_press((on_item_mb)(entity)) + } else { + mouse_area + } + }) + // Double click + .apply(|mouse_area| { + if let Some(ref on_item_mb) = val.on_item_mb_left { + mouse_area.on_double_click((on_item_mb)(entity)) + } else { + mouse_area + } + }) + // Middle click + .apply(|mouse_area| { + if let Some(ref on_item_mb) = val.on_item_mb_mid { + mouse_area.on_middle_press((on_item_mb)(entity)) + } else { + mouse_area + } + }) + // Right click + .apply(|mouse_area| { + if let Some(ref on_item_mb) = val.on_item_mb_right { + mouse_area.on_right_press((on_item_mb)(entity)) } else { mouse_area } @@ -211,6 +241,8 @@ where .flatten() .collect::>>() .apply(widget::column::with_children) + .width(val.width) + .height(val.height) .spacing(val.item_spacing) .padding(val.element_padding) .apply(Element::from) @@ -235,37 +267,61 @@ where Self { model, - element_padding: Padding::from(0), - divider_padding: Padding::from(0).left(space_xxxs).right(space_xxxs), + element_padding: Padding::from(0), + width: Length::Fill, + height: Length::Shrink, item_padding: Padding::from(space_xxs).into(), item_spacing: 0, + icon_spacing: space_xxxs, icon_size: 24, - on_item_select: None, - on_item_context: None, + divider_padding: Padding::from(0).left(space_xxxs).right(space_xxxs), + + on_item_mb_left: None, + on_item_mb_double: None, + on_item_mb_mid: None, + on_item_mb_right: None, item_context_builder: Box::new(|_| None), - on_category_select: None, - on_category_context: None, - category_contexts: Box::new(|_| None), + on_category_mb_left: None, + on_category_mb_double: None, + on_category_mb_mid: None, + on_category_mb_right: None, + category_context_builder: Box::new(|_| None), } } - pub fn on_item_select(mut self, on_select: F) -> Self + pub fn on_item_left_click(mut self, on_click: F) -> Self where - F: Fn(Entity) -> Message + 'a, + F: Fn(Entity) -> Message + 'static, { - self.on_item_select = Some(Box::new(on_select)); + self.on_item_mb_left = Some(Box::new(on_click)); self } - pub fn on_item_context(mut self, on_select: F) -> Self + pub fn on_item_double_click(mut self, on_click: F) -> Self where - F: Fn(Entity) -> Message + 'a, + F: Fn(Entity) -> Message + 'static, { - self.on_item_context = Some(Box::new(on_select)); + self.on_item_mb_double = Some(Box::new(on_click)); + self + } + + pub fn on_item_middle_click(mut self, on_click: F) -> Self + where + F: Fn(Entity) -> Message + 'static, + { + self.on_item_mb_mid = Some(Box::new(on_click)); + self + } + + pub fn on_item_right_click(mut self, on_click: F) -> Self + where + F: Fn(Entity) -> Message + 'static, + { + self.on_item_mb_right = Some(Box::new(on_click)); self } @@ -278,28 +334,42 @@ where self } + pub fn on_category_left_click(mut self, on_select: F) -> Self + where + F: Fn(Category) -> Message + 'static, + { + self.on_category_mb_left = Some(Box::new(on_select)); + self + } + pub fn on_category_double_click(mut self, on_select: F) -> Self + where + F: Fn(Category) -> Message + 'static, + { + self.on_category_mb_double = Some(Box::new(on_select)); + self + } + pub fn on_category_middle_click(mut self, on_select: F) -> Self + where + F: Fn(Category) -> Message + 'static, + { + self.on_category_mb_mid = Some(Box::new(on_select)); + self + } + + pub fn on_category_right_click(mut self, on_select: F) -> Self + where + F: Fn(Category) -> Message + 'static, + { + self.on_category_mb_right = Some(Box::new(on_select)); + self + } + pub fn category_context(mut self, context_menu_builder: F) -> Self where F: Fn(Category) -> Option>> + 'static, Message: 'static, { - self.category_contexts = Box::new(context_menu_builder); - self - } - - pub fn on_category_select(mut self, on_select: F) -> Self - where - F: Fn(Category, bool) -> Message + 'a, - { - self.on_category_select = Some(Box::new(on_select)); - self - } - - pub fn on_category_context(mut self, on_select: F) -> Self - where - F: Fn(Category) -> Message + 'a, - { - self.on_category_context = Some(Box::new(on_select)); + self.category_context_builder = Box::new(context_menu_builder); self } }