fixing the column and adding a scrollable
Some checks are pending
/ test (push) Waiting to run

This commit is contained in:
Chris Cochrun 2025-09-23 14:32:30 -05:00
parent 8e7e6df35c
commit 1e1912d419
2 changed files with 179 additions and 48 deletions

View file

@ -10,7 +10,8 @@ use cosmic::iced::alignment::Vertical;
use cosmic::iced::keyboard::{Key, Modifiers}; use cosmic::iced::keyboard::{Key, Modifiers};
use cosmic::iced::window::{Mode, Position}; use cosmic::iced::window::{Mode, Position};
use cosmic::iced::{ 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_core::text::Wrapping;
use cosmic::iced_futures::Subscription; 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::nav_bar::nav_bar_style;
use cosmic::widget::tooltip::Position as TPosition; use cosmic::widget::tooltip::Position as TPosition;
use cosmic::widget::{ use cosmic::widget::{
button, horizontal_space, mouse_area, nav_bar, nav_bar_toggle, button, context_menu, horizontal_space, mouse_area, nav_bar,
responsive, search_input, tooltip, vertical_space, Space, nav_bar_toggle, responsive, scrollable, search_input, tooltip,
vertical_space, Space,
}; };
use cosmic::widget::{container, text}; use cosmic::widget::{container, text};
use cosmic::widget::{icon, slider}; use cosmic::widget::{icon, slider};
@ -129,6 +131,7 @@ struct App {
library_dragged_item: Option<ServiceItem>, library_dragged_item: Option<ServiceItem>,
fontdb: Arc<fontdb::Database>, fontdb: Arc<fontdb::Database>,
menu_keys: HashMap<KeyBind, MenuAction>, menu_keys: HashMap<KeyBind, MenuAction>,
context_menu: Option<usize>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -152,9 +155,11 @@ enum Message {
AddSelectServiceItem(usize), AddSelectServiceItem(usize),
HoveredServiceItem(Option<usize>), HoveredServiceItem(Option<usize>),
AddServiceItem(usize, ServiceItem), AddServiceItem(usize, ServiceItem),
RemoveServiceItem(usize),
AddServiceItemDrop(usize), AddServiceItemDrop(usize),
AppendServiceItem(ServiceItem), AppendServiceItem(ServiceItem),
ReorderService(usize, usize), ReorderService(usize, usize),
ContextMenuItem(usize),
SearchFocus, SearchFocus,
Search(String), Search(String),
CloseSearch, CloseSearch,
@ -175,6 +180,7 @@ enum MenuAction {
SaveAs, SaveAs,
Open, Open,
OpenSettings, OpenSettings,
DeleteItem(usize),
} }
impl menu::Action for MenuAction { impl menu::Action for MenuAction {
@ -187,6 +193,9 @@ impl menu::Action for MenuAction {
MenuAction::SaveAs => Message::SaveAs, MenuAction::SaveAs => Message::SaveAs,
MenuAction::Open => Message::Open, MenuAction::Open => Message::Open,
MenuAction::OpenSettings => Message::OpenSettings, MenuAction::OpenSettings => Message::OpenSettings,
MenuAction::DeleteItem(index) => {
Message::RemoveServiceItem(*index)
}
} }
} }
} }
@ -324,6 +333,7 @@ impl cosmic::Application for App {
fontdb: Arc::clone(&fontdb), fontdb: Arc::clone(&fontdb),
menu_keys, menu_keys,
hovered_item: None, hovered_item: None,
context_menu: None,
}; };
let mut batch = vec![]; let mut batch = vec![];
@ -1093,6 +1103,15 @@ impl cosmic::Application for App {
self.presenter.update_items(self.service.clone()); self.presenter.update_items(self.service.clone());
Task::none() 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) => { Message::AddServiceItemDrop(index) => {
if let Some(item) = &self.library_dragged_item { if let Some(item) = &self.library_dragged_item {
self.service.insert(index, item.clone()); self.service.insert(index, item.clone());
@ -1484,6 +1503,7 @@ where
&item.title, &item.title,
size.width, size.width,
)) ))
.align_y(Vertical::Center)
.wrapping(Wrapping::None) .wrapping(Wrapping::None)
.into() .into()
}); });
@ -1492,6 +1512,7 @@ where
.align_y(Vertical::Center) .align_y(Vertical::Center)
.spacing(cosmic::theme::spacing().space_xs), .spacing(cosmic::theme::spacing().space_xs),
) )
.height(cosmic::theme::spacing().space_xl)
.padding(cosmic::theme::spacing().space_s) .padding(cosmic::theme::spacing().space_s)
.class(cosmic::theme::style::Container::Secondary) .class(cosmic::theme::style::Container::Secondary)
.style(move |t| { .style(move |t| {
@ -1524,9 +1545,44 @@ where
index, index,
)) ))
.on_drag(Message::None) .on_drag(Message::None)
.on_right_press(Message::ContextMenuItem(index))
.on_release(Message::SelectServiceItem(index)); .on_release(Message::SelectServiceItem(index));
let tooltip = tooltip( let single_item = if let Some(context_menu_item) =
self.context_menu
{
if context_menu_item == index {
let context_menu = context_menu(
mouse_area, 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(
single_item,
text::body(item.kind.to_string()), text::body(item.kind.to_string()),
TPosition::Right, TPosition::Right,
) )
@ -1555,14 +1611,10 @@ where
.into() .into()
}); });
let column = column![ let scrollable = scrollable(
text::heading("Service List") draggable::column::column(list)
.center() .spacing(10)
.width(Length::Fill), .on_drag(|event| match event {
iced::widget::horizontal_rule(1),
draggable::column::column(list).spacing(10).on_drag(
|event| {
match event {
draggable::DragEvent::Picked { .. } => { draggable::DragEvent::Picked { .. } => {
Message::None Message::None
} }
@ -1570,16 +1622,44 @@ where
index, index,
target_index, target_index,
.. ..
} => Message::ReorderService( } => Message::ReorderService(index, target_index),
index,
target_index,
),
draggable::DragEvent::Canceled { .. } => { draggable::DragEvent::Canceled { .. } => {
Message::None 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),
scrollable
]
.padding(10)
.spacing(10);
let container = Container::new(stack![
dnd_destination( dnd_destination(
vertical_space().width(Length::Fill), vertical_space().width(Length::Fill),
vec!["application/service-item".into()] vec!["application/service-item".into()]
@ -1601,11 +1681,10 @@ where
debug!(?item); debug!(?item);
Message::AppendServiceItem(item) Message::AppendServiceItem(item)
} }
) ),
] column
.padding(10) ])
.spacing(10); .style(nav_bar_style);
let container = Container::new(column).style(nav_bar_style);
container.center(Length::FillPortion(2)).into() container.center(Length::FillPortion(2)).into()
} }

View file

@ -363,7 +363,7 @@ where
} }
fn diff(&mut self, tree: &mut Tree) { 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<Length> { fn size(&self) -> Size<Length> {
@ -410,9 +410,14 @@ where
.iter() .iter()
.zip(&mut tree.children) .zip(&mut tree.children)
.zip(layout.children()) .zip(layout.children())
.for_each(|((child, state), layout)| { .for_each(|((child, state), c_layout)| {
child.as_widget().operate( 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() .iter_mut()
.zip(&mut tree.children) .zip(&mut tree.children)
.zip(layout.children()) .zip(layout.children())
.map(|((child, state), layout)| { .map(|((child, state), c_layout)| {
child.as_widget_mut().on_event( child.as_widget_mut().on_event(
state, state,
event.clone(), event.clone(),
layout, c_layout
.with_virtual_offset(layout.virtual_offset()),
cursor, cursor,
renderer, renderer,
clipboard, clipboard,
@ -592,9 +598,14 @@ where
.iter() .iter()
.zip(&tree.children) .zip(&tree.children)
.zip(layout.children()) .zip(layout.children())
.map(|((child, state), layout)| { .map(|((child, state), c_layout)| {
child.as_widget().mouse_interaction( child.as_widget().mouse_interaction(
state, layout, cursor, viewport, renderer, state,
c_layout
.with_virtual_offset(layout.virtual_offset()),
cursor,
viewport,
renderer,
) )
}) })
.max() .max()
@ -606,7 +617,7 @@ where
tree: &Tree, tree: &Tree,
renderer: &mut Renderer, renderer: &mut Renderer,
theme: &Theme, theme: &Theme,
defaults: &renderer::Style, default_style: &renderer::Style,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
viewport: &Rectangle, viewport: &Rectangle,
@ -672,7 +683,7 @@ where
state, state,
renderer, renderer,
theme, theme,
defaults, default_style,
child_layout, child_layout,
cursor, cursor,
viewport, viewport,
@ -712,7 +723,7 @@ where
state, state,
renderer, renderer,
theme, theme,
defaults, default_style,
child_layout, child_layout,
cursor, cursor,
viewport, viewport,
@ -762,20 +773,39 @@ where
} }
_ => { _ => {
// Draw all children normally when not dragging // Draw all children normally when not dragging
for ((child, state), layout) in self if let Some(clipped_viewport) =
layout.bounds().intersection(viewport)
{
let viewport = if self.clip {
&clipped_viewport
} else {
viewport
};
for ((child, state), c_layout) in self
.children .children
.iter() .iter()
.zip(&tree.children) .zip(&tree.children)
.zip(layout.children()) .zip(layout.children())
.filter(|(_, layout)| {
layout.bounds().intersects(viewport)
})
{ {
child.as_widget().draw( child.as_widget().draw(
state, renderer, theme, defaults, layout, state,
cursor, viewport, renderer,
theme,
default_style,
c_layout.with_virtual_offset(
layout.virtual_offset(),
),
cursor,
viewport,
); );
} }
} }
} }
} }
}
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
@ -792,6 +822,28 @@ where
translation, 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> impl<'a, Message, Theme, Renderer>