[fix] Right click menu in service and library
Some checks failed
/ clippy (push) Failing after 6m32s
/ test (push) Failing after 6m38s

This commit is contained in:
Chris Cochrun 2026-05-27 10:05:07 -05:00
parent f746227ae2
commit 784e13b6ac
2 changed files with 115 additions and 57 deletions

View file

@ -13,8 +13,8 @@ use cosmic::iced::keyboard::{Key, Modifiers};
use cosmic::iced::widget::{column, row, stack};
use cosmic::iced::window::Position;
use cosmic::iced::{
self, Background as IcedBackground, Border, Color, Length, Subscription, event,
window,
self, Background as IcedBackground, Border, Color, Length, Point, Subscription,
event, window,
};
use cosmic::widget::dnd_destination::dnd_destination;
use cosmic::widget::image::Handle;
@ -24,8 +24,8 @@ use cosmic::widget::nav_bar::nav_bar_style;
use cosmic::widget::space::{self, horizontal};
use cosmic::widget::{
Container, Space, button, container, context_menu, divider, icon, menu, mouse_area,
nav_bar, nav_bar_toggle, responsive, scrollable, search_input, settings, slider,
text, text_input, tooltip,
nav_bar, nav_bar_toggle, popover, responsive, scrollable, search_input, settings,
slider, text, text_input, tooltip,
};
use cosmic::{
Application, ApplicationExt, Apply, Element, cosmic_config, executor, theme,
@ -211,6 +211,8 @@ struct App {
obs_connection: String,
view_mode: ViewMode,
genius_token_hidden: bool,
hovered_point: iced::Point,
context_point: iced::Point,
}
#[allow(dead_code)]
@ -247,7 +249,7 @@ enum Message {
AppendServiceItem(ServiceItem),
AppendServiceItemKind(ServiceItemKind),
ReorderService(usize, usize),
ContextMenuItem(usize),
ContextMenuItem(Option<usize>),
SearchFocus,
Search(String),
CloseSearch,
@ -273,6 +275,7 @@ enum Message {
InsertThumbnail((iced::core::image::Allocation, usize)),
ClearFooterMsg,
SavingReport,
ContextPoint(Point),
}
#[allow(dead_code)]
@ -464,6 +467,8 @@ impl cosmic::Application for App {
hovered_item: None,
hovered_dnd: None,
context_menu: None,
hovered_point: Point::ORIGIN,
context_point: Point::ORIGIN,
modifiers_pressed: None,
settings_open: false,
settings,
@ -1434,7 +1439,12 @@ impl cosmic::Application for App {
Task::none()
}
Message::ContextMenuItem(index) => {
self.context_menu = Some(index);
self.context_menu = index;
self.context_point = self.hovered_point;
Task::none()
}
Message::ContextPoint(point) => {
self.hovered_point = point;
Task::none()
}
Message::AddServiceItemDrop(index) => {
@ -2172,21 +2182,41 @@ where
}
(Key::Character(k), _) if k == *"/" => self.update(Message::SearchFocus),
(Key::Named(iced::keyboard::key::Named::ArrowRight), _) => {
self.update(Message::Present(presenter::Message::NextSlide))
if self.editor_mode.is_none() {
self.update(Message::Present(presenter::Message::NextSlide))
} else {
Task::none()
}
}
(Key::Named(iced::keyboard::key::Named::ArrowLeft), _) => {
self.update(Message::Present(presenter::Message::PrevSlide))
if self.editor_mode.is_none() {
self.update(Message::Present(presenter::Message::PrevSlide))
} else {
Task::none()
}
}
(Key::Character(k), _) if k == *" " => {
self.update(Message::Present(presenter::Message::NextSlide))
if self.editor_mode.is_none() {
self.update(Message::Present(presenter::Message::NextSlide))
} else {
Task::none()
}
}
(Key::Character(k), _) if k == *"j" || k == *"l" => {
self.update(Message::Present(presenter::Message::NextSlide))
if self.editor_mode.is_none() {
self.update(Message::Present(presenter::Message::NextSlide))
} else {
Task::none()
}
}
(Key::Character(k), _) if k == *"k" || k == *"h" => {
self.update(Message::Present(presenter::Message::PrevSlide))
if self.editor_mode.is_none() {
self.update(Message::Present(presenter::Message::PrevSlide))
} else {
Task::none()
}
}
(Key::Character(k), _) if k == *"q" => self.update(Message::Quit),
// (Key::Character(k), _) if k == *"q" => self.update(Message::Quit),
_ => Task::none(),
}
}
@ -2251,32 +2281,35 @@ where
container
};
let mouse_area = mouse_area(visual_item)
.on_move(|point| Message::ContextPoint(point))
.on_enter(Message::HoveredServiceItem(Some(index)))
.on_exit(Message::HoveredServiceItem(None))
.on_double_press(Message::ChangeServiceItem(index))
.on_right_press(Message::ContextMenuItem(index))
.on_right_press(Message::ContextMenuItem(Some(index)))
.on_release(Message::SelectServiceItem(index));
let single_item = if let Some(context_menu_item) = self.context_menu {
let menu_item = |label, message| {
menu::menu_button(vec![
text(label).into(),
space::horizontal().into(),
])
.on_press(message)
};
let delete_button: Element<Message> =
menu_item("Delete", Message::RemoveServiceItem(index)).into();
let menu = column![delete_button]
.spacing(theme::spacing().space_s)
.apply(cosmic::widget::container)
.width(300)
.padding(theme::spacing().space_s)
.class(theme::Container::Dropdown);
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);
let context_menu = popover(mouse_area)
.position(popover::Position::Point(self.context_point))
.on_close(Message::ContextMenuItem(None))
.popup(menu);
Element::from(context_menu)
} else {
Element::from(mouse_area)

View file

@ -9,14 +9,14 @@ use cosmic::iced::core::text::{Ellipsize, EllipsizeHeightLimit};
use cosmic::iced::core::widget::tree::State as TreeState;
use cosmic::iced::keyboard::Modifiers;
use cosmic::iced::widget::{column, row as rowm, text as textm};
use cosmic::iced::{Background, Border, Color, Length};
use cosmic::iced::{Background, Border, Color, Length, Point};
use cosmic::widget::menu::{self, Action as MenuAction};
use cosmic::widget::nav_bar::nav_bar_style;
use cosmic::widget::space::{self, horizontal};
use cosmic::widget::{
Container, DndSource, Space, button, container, context_menu, divider,
dnd_destination, icon, indeterminate_circular, mouse_area, row, scrollable, text,
text_input,
dnd_destination, icon, indeterminate_circular, mouse_area, popover, row, scrollable,
text, text_input,
};
use cosmic::{Apply, Element, Task, theme};
use itertools::Itertools;
@ -53,6 +53,8 @@ pub struct Library {
modifiers_pressed: Option<Modifiers>,
state: State,
video_popup_input: String,
hovered_point: cosmic::iced::Point,
context_point: cosmic::iced::Point,
}
#[derive(Debug, Clone, PartialEq)]
@ -108,7 +110,7 @@ pub enum Message {
UpdatePresentation(Presentation),
PresentationChanged,
Error(String),
OpenContext(i32),
OpenContext(Option<i32>),
None,
AddFiles(Vec<ServiceItemKind>),
ReaddSongs(Vec<Song>),
@ -124,6 +126,7 @@ pub enum Message {
AddSongFromEditor(Song),
PopupUpdate(String),
PopupSearch(String),
HoverPoint(cosmic::iced::Point),
}
impl<'a> Library {
@ -148,6 +151,8 @@ impl<'a> Library {
modifiers_pressed: None,
state: State::Idle,
video_popup_input: String::new(),
hovered_point: Point::ORIGIN,
context_point: Point::ORIGIN,
}
}
@ -526,10 +531,14 @@ impl<'a> Library {
}
Message::PresentationChanged => (),
Message::Error(_) => (),
Message::OpenContext(index) => {
Message::OpenContext(None) => {
self.context_menu = None;
}
Message::OpenContext(Some(index)) => {
let Some(kind) = self.library_open else {
return Action::None;
};
self.context_point = self.hovered_point;
debug!(index, "should context");
let Some(items) = self.selected_items.as_mut() else {
self.selected_items = vec![(kind, index)].into();
@ -547,6 +556,7 @@ impl<'a> Library {
self.context_menu = Some(index);
}
Message::AddFiles(items) => return self.add_files(items),
Message::HoverPoint(point) => self.hovered_point = point,
Message::PopupUpdate(_) => todo!(),
Message::PopupSearch(_) => todo!(),
}
@ -707,16 +717,17 @@ impl<'a> Library {
let kind = model.kind;
let visual_item = self.single_item(index, item, model);
DndSource::<Message, KindWrapper>::new({
let item = DndSource::<Message, KindWrapper>::new({
let mouse_area = mouse_area(visual_item);
let mouse_area = mouse_area
.on_move(|point| Message::HoverPoint(point))
.on_enter(Message::HoverItem(Some((
model.kind, i32_index,
))))
.on_double_click(Message::OpenItem(Some((
model.kind, i32_index,
))))
.on_right_press(Message::OpenContext(i32_index))
.on_right_press(Message::OpenContext(Some(i32_index)))
.on_exit(Message::HoverItem(None))
.on_press(Message::SelectItem(Some((
model.kind, i32_index,
@ -747,8 +758,9 @@ impl<'a> Library {
(icon.into(), state, i)
}
})
.drag_content(move || KindWrapper((kind, i32_index)))
.into()
.drag_content(move || KindWrapper((kind, i32_index)));
self.context_menu(item.into(), kind, i32_index).into()
})
})
.spacing(2)
@ -764,8 +776,7 @@ impl<'a> Library {
.on_press(Message::AddItem)
)
.align_y(Vertical::Center);
let context_menu = self.context_menu(items.into());
let library_column = column![library_toolbar, context_menu].spacing(3);
let library_column = column![library_toolbar, items].spacing(3);
Container::new(library_column).padding(5)
} else {
Container::new(Space::new())
@ -925,22 +936,36 @@ impl<'a> Library {
.into()
}
fn context_menu<'b>(&self, items: Element<'b, Message>) -> Element<'b, Message> {
if self.context_menu.is_some() {
let menu_items = vec![
menu::Item::Button("Open", None, MenuMessage::Open),
menu::Item::Button("Delete", None, MenuMessage::Delete),
];
let context_menu = context_menu(
items,
self.context_menu.map_or_else(
|| None,
|_| Some(menu::items(&self.menu_keys, menu_items)),
),
);
fn context_menu<'b>(
&self,
item: Element<'b, Message>,
library: LibraryKind,
id: i32,
) -> Element<'b, Message> {
if self.context_menu.is_some_and(|index| index == id)
&& self.library_open.is_some_and(|kind| kind == library)
{
let menu_item = |label, message| {
menu::menu_button(vec![text(label).into(), space::horizontal().into()])
.on_press(message)
};
let menu_items = column![
menu_item("Open", Message::OpenItem(Some((library, id)))),
menu_item("Delete", Message::DeleteItem),
]
.spacing(theme::spacing().space_s)
.apply(container)
.width(300)
.padding(theme::spacing().space_s)
.class(theme::Container::Dropdown);
let context_menu = popover(item)
.position(popover::Position::Point(self.context_point))
.on_close(Message::OpenContext(None))
.popup(menu_items);
Element::from(context_menu)
} else {
items
item
}
}