selecting and deleting more items works now
Some checks are pending
/ test (push) Waiting to run

This commit is contained in:
Chris Cochrun 2025-09-26 09:41:20 -05:00
parent 51c184fe37
commit ee08280f22
2 changed files with 255 additions and 168 deletions

View file

@ -135,6 +135,7 @@ struct App {
fontdb: Arc<fontdb::Database>,
menu_keys: HashMap<KeyBind, MenuAction>,
context_menu: Option<usize>,
modifiers_pressed: Option<Modifiers>,
}
#[derive(Debug, Clone)]
@ -176,6 +177,7 @@ enum Message {
Save(Option<PathBuf>),
SaveAs,
OpenSettings,
ModifiersPressed(Modifiers),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -341,6 +343,7 @@ impl cosmic::Application for App {
menu_keys,
hovered_item: None,
context_menu: None,
modifiers_pressed: None,
};
let mut batch = vec![];
@ -594,6 +597,9 @@ impl cosmic::Application for App {
modifiers,
..
} => Some(Message::Key(key, modifiers)),
iced::keyboard::Event::ModifiersChanged(
modifiers,
) => Some(Message::ModifiersPressed(modifiers)),
_ => None,
},
iced::Event::Mouse(_event) => None,
@ -1251,6 +1257,20 @@ impl cosmic::Application for App {
debug!("Opening settings");
Task::none()
}
Message::ModifiersPressed(modifiers) => {
if modifiers.is_empty() {
self.modifiers_pressed = None;
if let Some(library) = self.library.as_mut() {
library.set_modifiers(None);
}
return Task::none();
}
if let Some(library) = self.library.as_mut() {
library.set_modifiers(Some(modifiers));
}
self.modifiers_pressed = Some(modifiers);
Task::none()
}
}
}

View file

@ -4,7 +4,8 @@ use cosmic::{
dialog::file_chooser::open::Dialog,
iced::{
alignment::Vertical, clipboard::dnd::DndAction,
futures::FutureExt, Background, Border, Color, Length,
futures::FutureExt, keyboard::Modifiers, Background, Border,
Color, Length,
},
iced_core::widget::tree::State,
iced_widget::{column, row as rowm, text as textm},
@ -18,9 +19,9 @@ use cosmic::{
},
Element, Task,
};
use miette::{IntoDiagnostic, Result};
use miette::{miette, IntoDiagnostic, Result};
use rapidfuzz::distance::levenshtein;
use sqlx::{migrate, pool::PoolConnection, Sqlite, SqlitePool};
use sqlx::{migrate, SqlitePool};
use tracing::{debug, error, warn};
use crate::core::{
@ -41,12 +42,13 @@ pub struct Library {
presentation_library: Model<Presentation>,
library_open: Option<LibraryKind>,
library_hovered: Option<LibraryKind>,
selected_item: Option<(LibraryKind, i32)>,
selected_items: Option<Vec<(LibraryKind, i32)>>,
hovered_item: Option<(LibraryKind, i32)>,
editing_item: Option<(LibraryKind, i32)>,
db: SqlitePool,
menu_keys: std::collections::HashMap<menu::KeyBind, MenuMessage>,
context_menu: Option<i32>,
modifiers_pressed: Option<Modifiers>,
}
#[derive(Debug, Clone, Eq, PartialEq, Copy)]
@ -116,12 +118,13 @@ impl<'a> Library {
.await,
library_open: None,
library_hovered: None,
selected_item: None,
selected_items: None,
hovered_item: None,
editing_item: None,
db,
menu_keys: HashMap::new(),
context_menu: None,
modifiers_pressed: None,
}
}
@ -133,154 +136,7 @@ impl<'a> Library {
match message {
Message::None => (),
Message::DeleteItem((kind, index)) => {
match kind {
LibraryKind::Song => {
let Some(song) =
self.song_library.get_item(index)
else {
error!(
"There appears to not be a song here"
);
return Action::None;
};
let song = song.clone();
if let Err(e) =
self.song_library.remove_item(index)
{
error!(?e);
} else {
let task =
Task::future(self.db.acquire())
.and_then(move |db| {
Task::perform(
songs::remove_from_db(
db, song.id,
),
|r| {
match r {
Err(e) => {
error!(?e)
}
_ => (),
}
Message::None
},
)
});
return Action::Task(task);
}
}
LibraryKind::Video => {
let Some(video) =
self.video_library.get_item(index)
else {
error!(
"There appears to not be a video here"
);
return Action::None;
};
let video = video.clone();
if let Err(e) =
self.video_library.remove_item(index)
{
error!(?e);
} else {
let task =
Task::future(self.db.acquire())
.and_then(move |db| {
Task::perform(
videos::remove_from_db(
db, video.id,
),
|r| {
match r {
Err(e) => {
error!(?e)
}
_ => (),
}
Message::None
},
)
});
return Action::Task(task);
}
}
LibraryKind::Image => {
let Some(image) =
self.image_library.get_item(index)
else {
error!(
"There appears to not be a image here"
);
return Action::None;
};
let image = image.clone();
if let Err(e) =
self.image_library.remove_item(index)
{
error!(?e);
} else {
let task =
Task::future(self.db.acquire())
.and_then(move |db| {
Task::perform(
images::remove_from_db(
db, image.id,
),
|r| {
match r {
Err(e) => {
error!(?e)
}
_ => (),
}
Message::None
},
)
});
return Action::Task(task);
}
}
LibraryKind::Presentation => {
let Some(presentation) =
self.presentation_library.get_item(index)
else {
error!(
"There appears to not be a presentation here"
);
return Action::None;
};
let presentation = presentation.clone();
if let Err(e) = self
.presentation_library
.remove_item(index)
{
error!(?e);
} else {
let task =
Task::future(self.db.acquire())
.and_then(move |db| {
Task::perform(
presentations::remove_from_db(
db,
presentation.id,
),
|r| {
match r {
Err(e) => {
error!(?e)
}
_ => (),
}
Message::None
},
)
});
return Action::Task(task);
}
}
};
return self.delete_items();
}
Message::AddItem => {
let kind =
@ -374,7 +230,63 @@ impl<'a> Library {
self.hovered_item = item;
}
Message::SelectItem(item) => {
self.selected_item = item;
let Some(modifiers) = self.modifiers_pressed else {
let Some(item) = item else {
return Action::None;
};
self.selected_items = Some(vec![item]);
return Action::None;
};
if modifiers.is_empty() {
let Some(item) = item else {
return Action::None;
};
self.selected_items = Some(vec![item]);
return Action::None;
}
if modifiers.shift() {
let Some(first_item) = self
.selected_items
.as_ref()
.map(|items| {
items
.iter()
.next()
.map(|(_, index)| index)
})
.flatten()
else {
let Some(item) = item else {
return Action::None;
};
self.selected_items = Some(vec![item]);
return Action::None;
};
let Some((kind, index)) = item else {
return Action::None;
};
if first_item < &index {
for id in *first_item..=index {
self.selected_items = self
.selected_items
.clone()
.and_then(|mut items| {
items.push((kind, id));
Some(items)
});
}
} else if first_item > &index {
for id in index..*first_item {
self.selected_items = self
.selected_items
.clone()
.and_then(|mut items| {
items.push((kind, id));
Some(items)
});
}
}
}
}
Message::DragItem(item) => {
debug!(?item);
@ -795,11 +707,8 @@ impl<'a> Library {
.center_x(Length::Fill);
let subtext = container(responsive(move |size| {
let color: Color = if item.background().is_some() {
if let Some((library, selected)) = self.selected_item
{
if model.kind == library
&& selected == index as i32
{
if let Some(items) = &self.selected_items {
if items.contains(&(model.kind, index as i32)) {
theme::active().cosmic().control_0().into()
} else {
theme::active()
@ -813,10 +722,8 @@ impl<'a> Library {
.accent_text_color()
.into()
}
} else if let Some((library, selected)) =
self.selected_item
{
if model.kind == library && selected == index as i32 {
} else if let Some(items) = &self.selected_items {
if items.contains(&(model.kind, index as i32)) {
theme::active().cosmic().control_0().into()
} else {
theme::active()
@ -851,11 +758,8 @@ impl<'a> Library {
.style(move |t| {
container::Style::default()
.background(Background::Color(
if let Some((library, selected)) =
self.selected_item
{
if model.kind == library
&& selected == index as i32
if let Some(items) = &self.selected_items {
if items.contains(&(model.kind, index as i32))
{
t.cosmic().accent.selected.into()
} else if let Some((library, hovered)) =
@ -965,6 +869,169 @@ impl<'a> Library {
pub fn get_image(&self, index: i32) -> Option<&Image> {
self.image_library.get_item(index)
}
pub fn set_modifiers(&mut self, modifiers: Option<Modifiers>) {
self.modifiers_pressed = modifiers;
}
fn delete_items(&mut self) -> Action {
// Need to make this function collect tasks to be run off of
// who should be deleted
let Some(ref items) = self.selected_items else {
return Action::None;
};
let tasks: Vec<Task<Message>> = items
.iter()
.map(|(kind, index)| match kind {
LibraryKind::Song => {
if let Some(song) =
self.song_library.get_item(*index)
{
let song = song.clone();
if let Err(e) =
self.song_library.remove_item(*index)
{
error!(?e);
Task::none()
} else {
let task =
Task::future(self.db.acquire())
.and_then(move |db| {
Task::perform(
songs::remove_from_db(
db, song.id,
),
|r| {
match r {
Err(e) => {
error!(?e)
}
_ => (),
}
Message::None
},
)
});
task
}
} else {
Task::none()
}
}
LibraryKind::Video => {
if let Some(video) =
self.video_library.get_item(*index)
{
let video = video.clone();
if let Err(e) =
self.video_library.remove_item(*index)
{
error!(?e);
Task::none()
} else {
let task =
Task::future(self.db.acquire())
.and_then(move |db| {
Task::perform(
videos::remove_from_db(
db, video.id,
),
|r| {
match r {
Err(e) => {
error!(?e)
}
_ => (),
}
Message::None
},
)
});
task
}
} else {
Task::none()
}
}
LibraryKind::Image => {
if let Some(image) =
self.image_library.get_item(*index)
{
let image = image.clone();
if let Err(e) =
self.image_library.remove_item(*index)
{
error!(?e);
Task::none()
} else {
let task =
Task::future(self.db.acquire())
.and_then(move |db| {
Task::perform(
images::remove_from_db(
db, image.id,
),
|r| {
match r {
Err(e) => {
error!(?e)
}
_ => (),
}
Message::None
},
)
});
task
}
} else {
Task::none()
}
}
LibraryKind::Presentation => {
if let Some(presentation) =
self.presentation_library.get_item(*index)
{
let presentation = presentation.clone();
if let Err(e) = self
.presentation_library
.remove_item(*index)
{
error!(?e);
Task::none()
} else {
let task =
Task::future(self.db.acquire())
.and_then(move |db| {
Task::perform(
presentations::remove_from_db(
db,
presentation.id,
),
|r| {
match r {
Err(e) => {
error!(?e)
}
_ => (),
}
Message::None
},
)
});
task
}
} else {
Task::none()
}
}
})
.collect();
if tasks.len() > 0 {
self.selected_items = None;
}
Action::Task(Task::batch(tasks))
}
}
async fn add_images() -> Option<Vec<Image>> {