diff --git a/examples/table-view/src/main.rs b/examples/table-view/src/main.rs index 2cb4bf0a..f4c34070 100644 --- a/examples/table-view/src/main.rs +++ b/examples/table-view/src/main.rs @@ -10,7 +10,7 @@ use cosmic::widget::nav_bar; use cosmic::widget::table; use cosmic::{executor, iced, Element}; -#[derive(Debug, Default, PartialEq, Eq, Clone, Copy, Hash)] +#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] pub enum Category { #[default] Name, @@ -29,11 +29,11 @@ impl std::fmt::Display for Category { } impl table::ItemCategory for Category { - fn width(&self) -> u16 { + fn width(&self) -> iced::Length { match self { - Self::Name => 300, - Self::Date => 200, - Self::Size => 150, + Self::Name => iced::Length::Fill, + Self::Date => iced::Length::Fixed(200.0), + Self::Size => iced::Length::Fixed(150.0), } } } @@ -63,11 +63,11 @@ impl table::ItemInterface for Item { } } - fn get_text(&self, category: Category) -> Option { + fn get_text(&self, category: Category) -> std::borrow::Cow<'static, str> { match category { - Category::Name => Some(self.name.clone()), - Category::Date => Some(self.date.format("%Y/%m/%d").to_string()), - Category::Size => Some(format!("{} items", self.size)), + Category::Name => self.name.clone().into(), + Category::Date => self.date.format("%Y/%m/%d").to_string().into(), + Category::Size => format!("{} items", self.size).into(), } } @@ -192,30 +192,19 @@ impl cosmic::Application for App { /// Creates a view after each update. fn view(&self) -> Element { - // cosmic::widget::responsive(|size| { - // if size.width < 600.0 { - // table::SingleSelectTableView::new(&self.table_model) - // .on_item_select(Message::ItemSelect) - // .on_category_select(Message::CategorySelect) - // .element_compact() - // } else { - // table::SingleSelectTableView::new(&self.table_model) - // .on_item_select(Message::ItemSelect) - // .on_category_select(Message::CategorySelect) - // .element_standard() - // } - // }) - // .into() - cosmic::widget::row() - .push(Element::new( + cosmic::widget::responsive(|size| { + if size.width < 600.0 { table::SingleSelectTableView::new(&self.table_model) - .on_item_left(Message::ItemSelect), - )) - .push( + .on_item_select(Message::ItemSelect) + .on_category_select(Message::CategorySelect) + .element_compact() + } else { table::SingleSelectTableView::new(&self.table_model) - .on_item_left(Message::ItemSelect) - .element_standard(), - ) - .into() + .on_item_select(Message::ItemSelect) + .on_category_select(Message::CategorySelect) + .element_standard() + } + }) + .into() } } diff --git a/src/widget/table/model/category.rs b/src/widget/table/model/category.rs index 94f9dee5..efa4624a 100644 --- a/src/widget/table/model/category.rs +++ b/src/widget/table/model/category.rs @@ -6,12 +6,12 @@ use crate::widget::Icon; /// Ideally, this is implemented on an enum. pub trait ItemCategory: Default + std::fmt::Display + Clone + Copy + PartialEq + Eq { /// Function that gets the width of the data - fn width(&self) -> u16; + fn width(&self) -> iced::Length; } pub trait ItemInterface: Default { fn get_icon(&self, category: Category) -> Option; - fn get_text(&self, category: Category) -> Option; + fn get_text(&self, category: Category) -> Cow<'static, str>; fn compare(&self, other: &Self, category: Category) -> std::cmp::Ordering; } diff --git a/src/widget/table/widget/mod.rs b/src/widget/table/widget/mod.rs index 8236d92b..73cb60c3 100644 --- a/src/widget/table/widget/mod.rs +++ b/src/widget/table/widget/mod.rs @@ -1,24 +1,4 @@ mod state; -use std::{ - collections::HashMap, - hash::{DefaultHasher, Hash, Hasher}, -}; - -use iced_core::{ - image::{self, Renderer as ImageRenderer}, - Layout, -}; -use iced_core::{layout, text::Renderer as TextRenderer}; -use iced_core::{renderer::Quad, Renderer as IcedRenderer}; - -use derive_setters::Setters; -use iced_core::{ - text::{LineHeight, Shaping, Wrapping}, - widget::{tree, Tree}, - Text, Widget, -}; -use palette::named::BLACK; -use slotmap::SecondaryMap; use state::State; use super::model::{ @@ -29,216 +9,46 @@ use super::model::{ use crate::{ ext::CollectionWidget, theme, - widget::{ - self, container, divider, - dnd_destination::DragId, - menu::{self, menu_roots_children, menu_roots_diff, MenuBarState}, - }, + widget::{self, container, divider, menu}, Apply, Element, }; -use iced::{ - alignment, - clipboard::{dnd::DndAction, mime::AllowedMimeTypes}, - Alignment, Background, Border, Length, Padding, Point, Rectangle, Size, Vector, -}; +use iced::{Alignment, Border, Length, Padding}; // THIS IS A PLACEHOLDER UNTIL A MORE SOPHISTICATED WIDGET CAN BE DEVELOPED -#[derive(Setters)] #[must_use] pub struct TableView<'a, SelectionMode, Item, Category, Message> where - Category: ItemCategory + Hash + 'static, + Category: ItemCategory, Item: ItemInterface, Model: Selectable, SelectionMode: Default, Message: Clone + 'static, { - /// iced widget ID - pub(super) id: widget::Id, - /// The table model - #[setters(skip)] pub(super) model: &'a Model, - // === Element Layout === - /// Desired width of the widget. - pub(super) width: Length, - /// Desired height of the widget. - pub(super) height: Length, - /// Spacing between items and the dividers pub(super) item_spacing: u16, - /// Spacing between text and icons in items - pub(super) icon_spacing: u16, - /// Size of the icon - pub(super) icon_size: u16, - /// The size of a single indent - pub(super) indent_spacing: u16, - /// The padding for the entire table - #[setters(into)] pub(super) element_padding: Padding, - /// The padding for each item - #[setters(into)] pub(super) item_padding: Padding, - /// the horizontal padding for each divider - #[setters(into)] pub(super) divider_padding: Padding, - /// Size of the font. - pub(super) font_size: f32, + pub(super) item_context_tree: Option>>, + pub(super) category_context_tree: Option>>, - /// The context tree to show on right clicking an item - #[setters(skip)] - pub(super) item_context_tree: Option>>, - /// The context tree to show on right clicking a category - #[setters(skip)] - pub(super) category_context_tree: Option>>, - - // === Item Mouse Events === - /// Message to emit when the user left clicks on a table item - #[setters(skip)] - pub(super) on_item_left: Option Message + 'a>>, - /// Message to emit when the user double clicks on a table item - #[setters(skip)] - pub(super) on_item_double: Option Message + 'a>>, - /// Message to emit when the user middle clicks on a table item - #[setters(skip)] - pub(super) on_item_middle: Option Message + 'a>>, - /// Message to emit when the user right clicks on a table item - #[setters(skip)] - pub(super) on_item_right: Option Message + 'a>>, - - // === Category Mouse Events === - /// Message to emit when the user clicks on a category - #[setters(skip)] - pub(super) on_category_select: Option Message + 'a>>, - /// Message to emit when the user right clicks on a category - #[setters(skip)] + pub(super) on_item_select: Option Message + 'a>>, + pub(super) on_item_context: Option Message + 'a>>, + pub(super) on_category_select: Option Message + 'a>>, pub(super) on_category_context: Option Message + 'a>>, - - // === Drag n Drop === - /// Message to emit on the DND drop event - #[setters(skip)] - pub(super) on_dnd_drop: - Option, String, DndAction) -> Message + 'static>>, - /// MIME Types for the Drag n Drop - pub(super) mimes: Vec, - /// Message to emit on the DND enter event - #[setters(skip)] - pub(super) on_dnd_enter: Option) -> Message + 'static>>, - /// Message to emit on the DND leave event - #[setters(skip)] - pub(super) on_dnd_leave: Option Message + 'static>>, - /// The Drag ID of the table - #[setters(strip_option)] - pub(super) drag_id: Option, } -// PRIVATE INTERFACE -impl<'a, SelectionMode, Item, Category, Message> - TableView<'a, SelectionMode, Item, Category, Message> -where - Category: ItemCategory + Hash + 'static, - Item: ItemInterface, - Model: Selectable, - SelectionMode: Default, - Message: Clone + 'static, -{ - fn max_item_dimensions( - &self, - state: &mut State, - renderer: &crate::Renderer, - ) -> (f32, f32) { - let mut width = 0.0f32; - let mut height = 0.0f32; - let font = renderer.default_font(); - - for key in self.model.iter() { - let (button_width, button_height) = self.item_dimensions(state, font, key); - - state - .item_layout - .push(Size::new(button_width, button_height)); - - height = height.max(button_height); - width = width.max(button_width); - } - - for size in &mut state.item_layout { - size.height = height; - } - - (width, height) - } - - fn item_dimensions( - &self, - state: &mut State, - font: crate::font::Font, - id: Entity, - ) -> (f32, f32) { - let mut width = 0f32; - let mut height = 0f32; - let mut icon_spacing = 0f32; - - if let Some((item, paragraphs)) = self.model.item(id).zip(state.paragraphs.entry(id)) { - let paragraphs = paragraphs.or_default(); - for category in self.model.categories.iter().copied() { - if let Some(text) = item.get_text(category) { - if !text.is_empty() { - icon_spacing = f32::from(self.icon_spacing); - let paragraph = if let Some(entry) = paragraphs.get(&category) { - entry.clone() - } else { - crate::Plain::new(Text { - content: text.as_str(), - size: iced::Pixels(self.font_size), - bounds: Size::INFINITY - .apply(|inf| Size::new(category.width() as f32, inf.height)), - font, - horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Center, - shaping: Shaping::Advanced, - wrapping: Wrapping::default(), - line_height: LineHeight::default(), - }) - }; - - let size = paragraph.min_bounds(); - width += size.width; - height = height.max(size.height); - } - } - // Add icon to measurement if icon was given. - if let Some(_) = item.get_icon(category) { - width += f32::from(self.icon_size) + icon_spacing; - height = height.max(f32::from(self.icon_size)); - } - } - } - - // Add indent to measurement if found. - if let Some(indent) = self.model.indent(id) { - width = f32::from(indent).mul_add(f32::from(self.indent_spacing), width); - } - - // Add button padding to the max size found - width += f32::from(self.item_padding.left) + f32::from(self.item_padding.right); - height += f32::from(self.item_padding.top) + f32::from(self.item_padding.top); - - (width, height) - } -} - -// PUBLIC INTERFACE impl<'a, SelectionMode, Item, Category, Message> TableView<'a, SelectionMode, Item, Category, Message> where SelectionMode: Default, Model: Selectable, - Category: ItemCategory + Hash + 'static, + Category: ItemCategory, Item: ItemInterface, Message: Clone + 'static, { - /// Creates a new table view with the given model pub fn new(model: &'a Model) -> Self { let cosmic_theme::Spacing { space_xxxs, @@ -247,67 +57,53 @@ where } = theme::active().cosmic().spacing; Self { - id: widget::Id::unique(), model, - width: Length::Fill, - height: Length::Shrink, item_spacing: 0, element_padding: Padding::from(0), divider_padding: Padding::from(0).left(space_xxxs).right(space_xxxs), item_padding: Padding::from(space_xxs).into(), - icon_spacing: space_xxxs, - icon_size: 24, - indent_spacing: space_xxs, - font_size: 14.0, + on_item_select: None, + on_item_context: None, item_context_tree: None, - category_context_tree: None, on_category_select: None, on_category_context: None, - on_item_left: None, - on_item_double: None, - on_item_middle: None, - on_item_right: None, - on_dnd_drop: None, - mimes: Vec::new(), - on_dnd_enter: None, - on_dnd_leave: None, - drag_id: None, + category_context_tree: None, } } - /// Sets the message to be emitted on left click - pub fn on_item_left(mut self, on_left: F) -> Self - where - F: Fn(Entity) -> Message + 'a, - { - self.on_item_left = Some(Box::new(on_left)); + pub fn item_spacing(mut self, spacing: u16) -> Self { + self.item_spacing = spacing; self } - /// Sets the message to be emitted on double click - pub fn on_item_double(mut self, on_double: F) -> Self - where - F: Fn(Entity) -> Message + 'a, - { - self.on_item_double = Some(Box::new(on_double)); + pub fn element_padding(mut self, padding: impl Into) -> Self { + self.element_padding = padding.into(); self } - /// Sets the message to be emitted on middle click - pub fn on_item_middle(mut self, on_middle: F) -> Self - where - F: Fn(Entity) -> Message + 'a, - { - self.on_item_middle = Some(Box::new(on_middle)); + pub fn divider_padding(mut self, padding: u16) -> Self { + self.divider_padding = Padding::from(0).left(padding).right(padding); self } - /// Sets the message to be emitted on right click - pub fn on_item_right(mut self, on_right: F) -> Self + pub fn item_padding(mut self, padding: impl Into) -> Self { + self.item_padding = padding.into(); + self + } + + pub fn on_item_select(mut self, on_select: F) -> Self where F: Fn(Entity) -> Message + 'a, { - self.on_item_right = Some(Box::new(on_right)); + self.on_item_select = Some(Box::new(on_select)); + self + } + + pub fn on_item_context(mut self, on_select: F) -> Self + where + F: Fn(Entity) -> Message + 'a, + { + self.on_item_context = Some(Box::new(on_select)); self } @@ -327,7 +123,7 @@ where pub fn on_category_select(mut self, on_select: F) -> Self where - F: Fn(Category) -> Message + 'a, + F: Fn(Category, bool) -> Message + 'a, { self.on_category_select = Some(Box::new(on_select)); self @@ -341,448 +137,6 @@ where self } - /// Handle the dnd drop event. - pub fn on_dnd_drop( - mut self, - dnd_drop_handler: impl Fn(Entity, Option, DndAction) -> Message + 'static, - ) -> Self { - self.on_dnd_drop = Some(Box::new(move |entity, data, mime, action| { - dnd_drop_handler(entity, D::try_from((data, mime)).ok(), action) - })); - self.mimes = D::allowed().iter().cloned().collect(); - self - } - - /// Handle the dnd enter event. - pub fn on_dnd_enter( - mut self, - dnd_enter_handler: impl Fn(Entity, Vec) -> Message + 'static, - ) -> Self { - self.on_dnd_enter = Some(Box::new(dnd_enter_handler)); - self - } - - /// Handle the dnd leave event. - pub fn on_dnd_leave(mut self, dnd_leave_handler: impl Fn(Entity) -> Message + 'static) -> Self { - self.on_dnd_leave = Some(Box::new(dnd_leave_handler)); - self - } -} - -// Widget implementation -impl<'a, SelectionMode, Item, Category, Message> Widget - for TableView<'a, SelectionMode, Item, Category, Message> -where - SelectionMode: Default, - Model: Selectable, - Category: ItemCategory + Hash + 'static, - Item: ItemInterface, - Message: Clone + 'static, -{ - fn children(&self) -> Vec { - let mut children = Vec::new(); - - // Item context tree - if let Some(ref item_context) = self.item_context_tree { - let mut tree = Tree::empty(); - tree.state = tree::State::new(MenuBarState::default()); - tree.children = menu_roots_children(&item_context); - children.push(tree); - } - - // Category context tree - if let Some(ref category_context) = self.category_context_tree { - let mut tree = Tree::empty(); - tree.state = tree::State::new(MenuBarState::default()); - tree.children = menu_roots_children(&category_context); - children.push(tree); - } - - children - } - - fn tag(&self) -> tree::Tag { - tree::Tag::of::>() - } - - fn state(&self) -> tree::State { - #[allow(clippy::default_trait_access)] - tree::State::new(State:: { - num_items: self.model.order.len(), - selected: self.model.active.clone(), - paragraphs: SecondaryMap::new(), - item_layout: Vec::new(), - text_hashes: SecondaryMap::new(), - - sort_hash: HashMap::new(), - header_paragraphs: HashMap::new(), - category_layout: Vec::new(), - }) - } - - fn diff(&mut self, tree: &mut Tree) { - let state = tree.state.downcast_mut::>(); - - let sort_state = self.model.sort; - let mut hasher = DefaultHasher::new(); - sort_state.hash(&mut hasher); - let cat_hash = hasher.finish(); - for category in self.model.categories.iter().copied() { - if let Some(prev_hash) = state.sort_hash.insert(category, cat_hash) { - if prev_hash == cat_hash { - continue; - } - } - - let category_name = category.to_string(); - let text = Text { - content: category_name.as_str(), - size: iced::Pixels(self.font_size), - bounds: Size::INFINITY, - font: crate::font::bold(), - horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Center, - shaping: Shaping::Advanced, - wrapping: Wrapping::default(), - line_height: LineHeight::default(), - }; - - if let Some(header) = state.header_paragraphs.get_mut(&category) { - header.update(text); - } else { - state - .header_paragraphs - .insert(category, crate::Plain::new(text)); - } - } - - for key in self.model.iter() { - if let Some(item) = self.model.item(key) { - // TODO: add hover support if design approves it - let button_state = self.model.is_active(key); - - let mut hasher = DefaultHasher::new(); - button_state.hash(&mut hasher); - // hash each text - for category in self.model.categories.iter().copied() { - let text = item.get_text(category); - - text.hash(&mut hasher); - } - let text_hash = hasher.finish(); - - if let Some(prev_hash) = state.text_hashes.insert(key, text_hash) { - // If the text didn't change, don't update the paragraph - if prev_hash == text_hash { - continue; - } - } - - state.selected.insert(key, button_state); - - // Update the paragraph if the text changed - for category in self.model.categories.iter().copied() { - if let Some(text) = item.get_text(category) { - let text = Text { - content: text.as_ref(), - size: iced::Pixels(self.font_size), - bounds: Size::INFINITY, - font: crate::font::default(), - horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Center, - shaping: Shaping::Advanced, - wrapping: Wrapping::default(), - line_height: LineHeight::default(), - }; - - if let Some(item) = state.paragraphs.get_mut(key) { - if let Some(paragraph) = item.get_mut(&category) { - paragraph.update(text); - } else { - item.insert(category, crate::Plain::new(text)); - } - } else { - let mut hm = HashMap::new(); - hm.insert(category, crate::Plain::new(text)); - state.paragraphs.insert(key, hm); - } - } - } - } - } - - // BUG: IF THE CATEGORY_CONTEXT AND ITEM_CONTEXT ARE CLEARED AND A DIFFERENT ONE IS SET, IT BREAKS - // Diff the item context menu - if let Some(ref mut item_context) = self.item_context_tree { - if let Some(ref mut category_context) = self.category_context_tree { - if tree.children.is_empty() { - let mut child_tree = Tree::empty(); - child_tree.state = tree::State::new(MenuBarState::default()); - tree.children.push(child_tree); - - let mut child_tree = Tree::empty(); - child_tree.state = tree::State::new(MenuBarState::default()); - tree.children.push(child_tree); - } else { - tree.children.truncate(2); - } - menu_roots_diff(item_context, &mut tree.children[0]); - menu_roots_diff(category_context, &mut tree.children[1]); - } else { - if tree.children.is_empty() { - let mut child_tree = Tree::empty(); - child_tree.state = tree::State::new(MenuBarState::default()); - tree.children.push(child_tree); - } else { - tree.children.truncate(1); - } - menu_roots_diff(item_context, &mut tree.children[0]); - } - } else { - if let Some(ref mut category_context) = self.category_context_tree { - if tree.children.is_empty() { - let mut child_tree = Tree::empty(); - child_tree.state = tree::State::new(MenuBarState::default()); - tree.children.push(child_tree); - } else { - tree.children.truncate(1); - } - menu_roots_diff(category_context, &mut tree.children[0]); - } else { - tree.children.clear(); - } - } - } - - fn size(&self) -> iced::Size { - Size::new(self.width, self.height) - } - - fn layout( - &self, - tree: &mut Tree, - renderer: &crate::Renderer, - limits: &layout::Limits, - ) -> layout::Node { - let state = tree.state.downcast_mut::>(); - let size = { - state.item_layout.clear(); - state.category_layout.clear(); - let limits = limits.width(self.width); - - for category in self.model.categories.iter().copied() { - state - .category_layout - .push(Size::new(category.width() as f32, 20.0)); - } - - let (width, item_height) = self.max_item_dimensions(state, renderer); - - for size in &mut state.item_layout { - size.width = width.max(limits.max().width); - } - - let spacing = f32::from(self.item_spacing); - let mut height = state.category_layout[0].height; - for _ in self.model.iter() { - height += spacing + 1.0 + spacing; - height += item_height; - } - - limits.height(Length::Fixed(height)).resolve( - self.width, - self.height, - Size::new(width, height), - ) - }; - layout::Node::new(size) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut crate::Renderer, - theme: &crate::Theme, - style: &iced_core::renderer::Style, - layout: iced_core::Layout<'_>, - cursor: iced_core::mouse::Cursor, - viewport: &iced::Rectangle, - ) { - let state = tree.state.downcast_ref::>(); - let cosmic = theme.cosmic(); - let accent = theme::active().cosmic().accent_color(); - - let bounds = layout.bounds().shrink(self.element_padding); - - let header_quad = Rectangle::new(bounds.position(), Size::new(bounds.width, 20.0)).shrink( - Padding::new(0.0) - .left(self.item_padding.left) - .right(self.item_padding.right), - ); - - // TODO: HEADER TEXT - let mut category_origin = header_quad.position(); - for (nth, category) in self.model.categories.iter().copied().enumerate() { - let category_quad = Rectangle::new(category_origin, state.category_layout[nth]); - if let Some(category_quad) = category_quad.intersection(&bounds) { - renderer.fill_paragraph( - state.header_paragraphs.get(&category).unwrap().raw(), - Point::new(category_quad.position().x, category_quad.center_y()), - cosmic.on_bg_color().into(), - category_quad, - ); - } else { - break; - } - - category_origin.x += category_quad.width; - } - - let body_quad = Rectangle::new( - layout.position() + Vector::new(0.0, 20.0), - Size::new(bounds.width, bounds.height - 20.0), - ); - - if self.model.order.is_empty() { - let divider_quad = layout - .bounds() - .shrink(self.divider_padding) - .apply(|mut dq| { - dq.height = 1.0; - dq - }); - - // If empty, draw a single divider and quit - renderer.fill_quad( - Quad { - bounds: divider_quad, - border: Default::default(), - shadow: Default::default(), - }, - Background::Color(cosmic.bg_divider().into()), - ); - } else { - let mut divider_quad = body_quad.shrink(self.divider_padding).apply(|mut dq| { - dq.height = 1.0; - dq - }); - let mut item_quad = body_quad.shrink(self.element_padding); - for (nth, entity) in self.model.iter().enumerate() { - // draw divider above - - renderer.fill_quad( - Quad { - bounds: divider_quad, - border: Default::default(), - shadow: Default::default(), - }, - Background::Color(cosmic.bg_divider().into()), - ); - - divider_quad.y += 1.0; - item_quad.y += 1.0; - item_quad.width = state.item_layout[nth].width; - item_quad.height = state.item_layout[nth].height; - - if state.selected.get(entity).copied().unwrap_or(false) { - renderer.fill_quad( - Quad { - bounds: item_quad, - border: Border { - color: iced::Color::TRANSPARENT, - width: 0.0, - radius: cosmic.radius_xs().into(), - }, - shadow: Default::default(), - }, - Background::Color(accent.into()), - ); - } - - let content_quad = item_quad.clone().shrink(self.item_padding); - let mut item_content_quad = content_quad.clone(); - if let Some(item) = self.model.item(entity) { - for (nth, category) in self.model.categories.iter().copied().enumerate() { - // TODO: Icons and text - let mut icon_spacing = 0; - if let Some((_, _)) = item.get_icon(category).zip(item.get_text(category)) { - icon_spacing = self.icon_spacing; - } - - item_content_quad.width = state.category_layout[nth].width; - - if let Some(mut item_content_quad) = - item_content_quad.intersection(&content_quad) - { - let mut offset = Point::::default(); - if let Some(icon) = item.get_icon(category) { - let layout_node = layout::Node::new(Size { - width: self.icon_size as f32, - height: self.icon_size as f32, - }) - .move_to(Point { - x: item_content_quad.x, - y: item_content_quad.y, - }); - Widget::::draw( - Element::::from(icon.clone()).as_widget(), - &Tree::empty(), - renderer, - theme, - style, - Layout::new(&layout_node), - cursor, - viewport, - ); - offset.x += self.icon_size as f32; - } - - if let Some(text) = item.get_text(category) { - offset.x += icon_spacing as f32; - - item_content_quad.x = item_content_quad.x + offset.x; - item_content_quad.width -= offset.x; - - renderer.fill_paragraph( - state - .paragraphs - .get(entity) - .unwrap() - .get(&category) - .unwrap() - .raw(), - Point::new( - item_content_quad.x, - item_content_quad.y + item_content_quad.height / 2.0, - ), - cosmic.on_bg_color().into(), - item_content_quad, - ); - } - } else { - break; - } - - item_content_quad.x += state.category_layout[nth].width; - } - } - - item_quad.y += state.item_layout[nth].height; - divider_quad.y += item_quad.height; - } - } - } -} - -impl<'a, SelectionMode, Item, Category, Message> - TableView<'a, SelectionMode, Item, Category, Message> -where - SelectionMode: Default, - Model: Selectable, - Category: ItemCategory + Hash + 'static, - Item: ItemInterface, - Message: Clone + 'static, -{ #[must_use] pub fn element_standard(&self) -> Element<'a, Message> { let cosmic_theme::Spacing { space_xxxs, .. } = theme::active().cosmic().spacing; @@ -791,13 +145,12 @@ where .model .categories .iter() - .copied() .map(|category| { widget::row() .spacing(space_xxxs) .push(widget::text::heading(category.to_string())) .push_maybe(if let Some(sort) = self.model.sort { - if sort.0 == category { + if sort.0 == *category { match sort.1 { true => Some(widget::icon::from_name("pan-up-symbolic").icon()), false => Some(widget::icon::from_name("pan-down-symbolic").icon()), @@ -809,11 +162,27 @@ where None }) .apply(container) + .padding( + Padding::default() + .left(self.item_padding.left) + .right(self.item_padding.right), + ) .width(category.width()) .apply(widget::mouse_area) .apply(|mouse_area| { if let Some(ref on_category_select) = self.on_category_select { - mouse_area.on_press((on_category_select)(category)) + mouse_area.on_press((on_category_select)( + *category, + if let Some(sort) = self.model.sort { + if sort.0 == *category { + !sort.1 + } else { + false + } + } else { + false + }, + )) } else { mouse_area } @@ -822,11 +191,6 @@ where }) .collect::>>() .apply(widget::row::with_children) - .padding( - Padding::default() - .left(self.item_padding.left) - .right(self.item_padding.right), - ) .apply(Element::from); let items_full = if self.model.items.is_empty() { vec![divider::horizontal::default() @@ -850,15 +214,9 @@ where .iter() .map(|category| { widget::row() - .spacing(self.icon_spacing) - .push_maybe( - item.get_icon(*category) - .map(|icon| icon.size(self.icon_size)), - ) - .push_maybe( - item.get_text(*category) - .map(|text| widget::text::body(text)), - ) + .spacing(space_xxxs) + .push_maybe(item.get_icon(*category).map(|icon| icon.size(24))) + .push(widget::text::body(item.get_text(*category))) .align_y(Alignment::Center) .apply(container) .width(category.width()) @@ -897,7 +255,7 @@ where })) .apply(widget::mouse_area) .apply(|mouse_area| { - if let Some(ref on_item_select) = self.on_item_left { + if let Some(ref on_item_select) = self.on_item_select { mouse_area.on_press((on_item_select)(entity)) } else { mouse_area @@ -943,10 +301,7 @@ where ) .push( widget::column() - .push_maybe( - item.get_text(Category::default()) - .map(|text| widget::text::body(text)), - ) + .push(widget::text::body(item.get_text(Category::default()))) .push({ let mut elements = self .model @@ -954,16 +309,11 @@ where .iter() .skip_while(|cat| **cat != Category::default()) .map(|category| { - item.get_text(*category) - .map(|text| { - vec![ - widget::text::caption(text) - .apply(Element::from), - widget::text::caption("-") - .apply(Element::from), - ] - }) - .unwrap_or_default() + vec![ + widget::text::caption(item.get_text(*category)) + .apply(Element::from), + widget::text::caption("-").apply(Element::from), + ] }) .flatten() .collect::>>(); @@ -1005,7 +355,7 @@ where })) .apply(widget::mouse_area) .apply(|ma| { - if let Some(on_item_select) = &self.on_item_left { + if let Some(on_item_select) = &self.on_item_select { ma.on_press((on_item_select)(entity)) } else { ma diff --git a/src/widget/table/widget/state.rs b/src/widget/table/widget/state.rs index 82fb88db..4d529db4 100644 --- a/src/widget/table/widget/state.rs +++ b/src/widget/table/widget/state.rs @@ -1,21 +1,8 @@ -use std::collections::HashMap; - -use iced::Size; use slotmap::SecondaryMap; -use crate::widget::table::{Entity, ItemCategory}; +use crate::widget::table::Entity; -pub struct State -where - Category: ItemCategory + 'static, -{ +pub struct State { pub(super) num_items: usize, pub(super) selected: SecondaryMap, - pub(super) paragraphs: SecondaryMap>, - pub(super) text_hashes: SecondaryMap, - pub(super) item_layout: Vec, - - pub(super) sort_hash: HashMap, - pub(super) header_paragraphs: HashMap, - pub(super) category_layout: Vec, }