From 1e1912d41978eca337ae10bea71b8d30808fad08 Mon Sep 17 00:00:00 2001 From: Chris Cochrun Date: Tue, 23 Sep 2025 14:32:30 -0500 Subject: [PATCH] fixing the column and adding a scrollable --- src/main.rs | 137 +++++++++++++++++++++++------ src/ui/widgets/draggable/column.rs | 90 +++++++++++++++---- 2 files changed, 179 insertions(+), 48 deletions(-) diff --git a/src/main.rs b/src/main.rs index f85c841..323ff31 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,8 @@ use cosmic::iced::alignment::Vertical; use cosmic::iced::keyboard::{Key, Modifiers}; use cosmic::iced::window::{Mode, Position}; use cosmic::iced::{ - self, event, window, Background as IcedBackground, Border, Length, + self, event, window, Background as IcedBackground, Border, Color, + Length, }; use cosmic::iced_core::text::Wrapping; use cosmic::iced_futures::Subscription; @@ -22,8 +23,9 @@ use cosmic::widget::menu::{ItemWidth, KeyBind}; use cosmic::widget::nav_bar::nav_bar_style; use cosmic::widget::tooltip::Position as TPosition; use cosmic::widget::{ - button, horizontal_space, mouse_area, nav_bar, nav_bar_toggle, - responsive, search_input, tooltip, vertical_space, Space, + button, context_menu, horizontal_space, mouse_area, nav_bar, + nav_bar_toggle, responsive, scrollable, search_input, tooltip, + vertical_space, Space, }; use cosmic::widget::{container, text}; use cosmic::widget::{icon, slider}; @@ -129,6 +131,7 @@ struct App { library_dragged_item: Option, fontdb: Arc, menu_keys: HashMap, + context_menu: Option, } #[derive(Debug, Clone)] @@ -152,9 +155,11 @@ enum Message { AddSelectServiceItem(usize), HoveredServiceItem(Option), AddServiceItem(usize, ServiceItem), + RemoveServiceItem(usize), AddServiceItemDrop(usize), AppendServiceItem(ServiceItem), ReorderService(usize, usize), + ContextMenuItem(usize), SearchFocus, Search(String), CloseSearch, @@ -175,6 +180,7 @@ enum MenuAction { SaveAs, Open, OpenSettings, + DeleteItem(usize), } impl menu::Action for MenuAction { @@ -187,6 +193,9 @@ impl menu::Action for MenuAction { MenuAction::SaveAs => Message::SaveAs, MenuAction::Open => Message::Open, MenuAction::OpenSettings => Message::OpenSettings, + MenuAction::DeleteItem(index) => { + Message::RemoveServiceItem(*index) + } } } } @@ -324,6 +333,7 @@ impl cosmic::Application for App { fontdb: Arc::clone(&fontdb), menu_keys, hovered_item: None, + context_menu: None, }; let mut batch = vec![]; @@ -1093,6 +1103,15 @@ impl cosmic::Application for App { self.presenter.update_items(self.service.clone()); Task::none() } + Message::RemoveServiceItem(index) => { + self.service.remove(index); + self.presenter.update_items(self.service.clone()); + Task::none() + } + Message::ContextMenuItem(index) => { + self.context_menu = Some(index); + Task::none() + } Message::AddServiceItemDrop(index) => { if let Some(item) = &self.library_dragged_item { self.service.insert(index, item.clone()); @@ -1484,6 +1503,7 @@ where &item.title, size.width, )) + .align_y(Vertical::Center) .wrapping(Wrapping::None) .into() }); @@ -1492,6 +1512,7 @@ where .align_y(Vertical::Center) .spacing(cosmic::theme::spacing().space_xs), ) + .height(cosmic::theme::spacing().space_xl) .padding(cosmic::theme::spacing().space_s) .class(cosmic::theme::style::Container::Secondary) .style(move |t| { @@ -1524,9 +1545,44 @@ where index, )) .on_drag(Message::None) + .on_right_press(Message::ContextMenuItem(index)) .on_release(Message::SelectServiceItem(index)); + let single_item = if let Some(context_menu_item) = + self.context_menu + { + if context_menu_item == index { + let context_menu = context_menu( + mouse_area, + self.context_menu.map_or_else( + || None, + |i| { + if i == index { + let menu = + vec![menu::Item::Button( + "Delete", + None, + MenuAction::DeleteItem(index), + )]; + Some(menu::items( + &HashMap::new(), + menu, + )) + } else { + None + } + }, + ), + ) + .close_on_escape(true); + Element::from(context_menu) + } else { + Element::from(mouse_area) + } + } else { + Element::from(mouse_area) + }; let tooltip = tooltip( - mouse_area, + single_item, text::body(item.kind.to_string()), TPosition::Right, ) @@ -1555,31 +1611,55 @@ where .into() }); + let scrollable = scrollable( + draggable::column::column(list) + .spacing(10) + .on_drag(|event| match event { + draggable::DragEvent::Picked { .. } => { + Message::None + } + draggable::DragEvent::Dropped { + index, + target_index, + .. + } => Message::ReorderService(index, target_index), + draggable::DragEvent::Canceled { .. } => { + Message::None + } + }) + .style(|t| draggable::column::Style { + scale: 1.05, + moved_item_overlay: Color::from( + t.cosmic().primary.base, + ) + .scale_alpha(0.2) + .into(), + ghost_border: Border { + width: 1.0, + color: t.cosmic().secondary.base.into(), + radius: t.cosmic().radius_m().into(), + }, + ghost_background: Color::from( + t.cosmic().secondary.base, + ) + .scale_alpha(0.2) + .into(), + }) + .height(Length::Shrink), + ) + .anchor_top() + .height(Length::Fill); + let column = column![ text::heading("Service List") .center() .width(Length::Fill), iced::widget::horizontal_rule(1), - draggable::column::column(list).spacing(10).on_drag( - |event| { - match event { - draggable::DragEvent::Picked { .. } => { - Message::None - } - draggable::DragEvent::Dropped { - index, - target_index, - .. - } => Message::ReorderService( - index, - target_index, - ), - draggable::DragEvent::Canceled { .. } => { - Message::None - } - } - } - ), + scrollable + ] + .padding(10) + .spacing(10); + let container = Container::new(stack![ dnd_destination( vertical_space().width(Length::Fill), vec!["application/service-item".into()] @@ -1601,11 +1681,10 @@ where debug!(?item); Message::AppendServiceItem(item) } - ) - ] - .padding(10) - .spacing(10); - let container = Container::new(column).style(nav_bar_style); + ), + column + ]) + .style(nav_bar_style); container.center(Length::FillPortion(2)).into() } diff --git a/src/ui/widgets/draggable/column.rs b/src/ui/widgets/draggable/column.rs index be181d6..edc2ab9 100644 --- a/src/ui/widgets/draggable/column.rs +++ b/src/ui/widgets/draggable/column.rs @@ -363,7 +363,7 @@ where } fn diff(&mut self, tree: &mut Tree) { - tree.diff_children(&mut self.children); + tree.diff_children(self.children.as_mut_slice()); } fn size(&self) -> Size { @@ -410,9 +410,14 @@ where .iter() .zip(&mut tree.children) .zip(layout.children()) - .for_each(|((child, state), layout)| { + .for_each(|((child, state), c_layout)| { child.as_widget().operate( - state, layout, renderer, operation, + state, + c_layout.with_virtual_offset( + layout.virtual_offset(), + ), + renderer, + operation, ); }); }, @@ -557,11 +562,12 @@ where .iter_mut() .zip(&mut tree.children) .zip(layout.children()) - .map(|((child, state), layout)| { + .map(|((child, state), c_layout)| { child.as_widget_mut().on_event( state, event.clone(), - layout, + c_layout + .with_virtual_offset(layout.virtual_offset()), cursor, renderer, clipboard, @@ -592,9 +598,14 @@ where .iter() .zip(&tree.children) .zip(layout.children()) - .map(|((child, state), layout)| { + .map(|((child, state), c_layout)| { child.as_widget().mouse_interaction( - state, layout, cursor, viewport, renderer, + state, + c_layout + .with_virtual_offset(layout.virtual_offset()), + cursor, + viewport, + renderer, ) }) .max() @@ -606,7 +617,7 @@ where tree: &Tree, renderer: &mut Renderer, theme: &Theme, - defaults: &renderer::Style, + default_style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, @@ -672,7 +683,7 @@ where state, renderer, theme, - defaults, + default_style, child_layout, cursor, viewport, @@ -712,7 +723,7 @@ where state, renderer, theme, - defaults, + default_style, child_layout, cursor, viewport, @@ -762,16 +773,35 @@ where } _ => { // Draw all children normally when not dragging - for ((child, state), layout) in self - .children - .iter() - .zip(&tree.children) - .zip(layout.children()) + if let Some(clipped_viewport) = + layout.bounds().intersection(viewport) { - child.as_widget().draw( - state, renderer, theme, defaults, layout, - cursor, viewport, - ); + let viewport = if self.clip { + &clipped_viewport + } else { + viewport + }; + for ((child, state), c_layout) in self + .children + .iter() + .zip(&tree.children) + .zip(layout.children()) + .filter(|(_, layout)| { + layout.bounds().intersects(viewport) + }) + { + child.as_widget().draw( + state, + renderer, + theme, + default_style, + c_layout.with_virtual_offset( + layout.virtual_offset(), + ), + cursor, + viewport, + ); + } } } } @@ -792,6 +822,28 @@ where translation, ) } + + fn drag_destinations( + &self, + state: &Tree, + layout: Layout<'_>, + renderer: &Renderer, + dnd_rectangles: &mut cosmic::iced_core::clipboard::DndDestinationRectangles, + ) { + for ((e, c_layout), state) in self + .children + .iter() + .zip(layout.children()) + .zip(state.children.iter()) + { + e.as_widget().drag_destinations( + state, + c_layout.with_virtual_offset(layout.virtual_offset()), + renderer, + dnd_rectangles, + ); + } + } } impl<'a, Message, Theme, Renderer>