This commit is contained in:
parent
51c184fe37
commit
ee08280f22
2 changed files with 255 additions and 168 deletions
20
src/main.rs
20
src/main.rs
|
|
@ -135,6 +135,7 @@ struct App {
|
||||||
fontdb: Arc<fontdb::Database>,
|
fontdb: Arc<fontdb::Database>,
|
||||||
menu_keys: HashMap<KeyBind, MenuAction>,
|
menu_keys: HashMap<KeyBind, MenuAction>,
|
||||||
context_menu: Option<usize>,
|
context_menu: Option<usize>,
|
||||||
|
modifiers_pressed: Option<Modifiers>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -176,6 +177,7 @@ enum Message {
|
||||||
Save(Option<PathBuf>),
|
Save(Option<PathBuf>),
|
||||||
SaveAs,
|
SaveAs,
|
||||||
OpenSettings,
|
OpenSettings,
|
||||||
|
ModifiersPressed(Modifiers),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
|
@ -341,6 +343,7 @@ impl cosmic::Application for App {
|
||||||
menu_keys,
|
menu_keys,
|
||||||
hovered_item: None,
|
hovered_item: None,
|
||||||
context_menu: None,
|
context_menu: None,
|
||||||
|
modifiers_pressed: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut batch = vec![];
|
let mut batch = vec![];
|
||||||
|
|
@ -594,6 +597,9 @@ impl cosmic::Application for App {
|
||||||
modifiers,
|
modifiers,
|
||||||
..
|
..
|
||||||
} => Some(Message::Key(key, modifiers)),
|
} => Some(Message::Key(key, modifiers)),
|
||||||
|
iced::keyboard::Event::ModifiersChanged(
|
||||||
|
modifiers,
|
||||||
|
) => Some(Message::ModifiersPressed(modifiers)),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
iced::Event::Mouse(_event) => None,
|
iced::Event::Mouse(_event) => None,
|
||||||
|
|
@ -1251,6 +1257,20 @@ impl cosmic::Application for App {
|
||||||
debug!("Opening settings");
|
debug!("Opening settings");
|
||||||
Task::none()
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ use cosmic::{
|
||||||
dialog::file_chooser::open::Dialog,
|
dialog::file_chooser::open::Dialog,
|
||||||
iced::{
|
iced::{
|
||||||
alignment::Vertical, clipboard::dnd::DndAction,
|
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_core::widget::tree::State,
|
||||||
iced_widget::{column, row as rowm, text as textm},
|
iced_widget::{column, row as rowm, text as textm},
|
||||||
|
|
@ -18,9 +19,9 @@ use cosmic::{
|
||||||
},
|
},
|
||||||
Element, Task,
|
Element, Task,
|
||||||
};
|
};
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{miette, IntoDiagnostic, Result};
|
||||||
use rapidfuzz::distance::levenshtein;
|
use rapidfuzz::distance::levenshtein;
|
||||||
use sqlx::{migrate, pool::PoolConnection, Sqlite, SqlitePool};
|
use sqlx::{migrate, SqlitePool};
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
|
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
|
|
@ -41,12 +42,13 @@ pub struct Library {
|
||||||
presentation_library: Model<Presentation>,
|
presentation_library: Model<Presentation>,
|
||||||
library_open: Option<LibraryKind>,
|
library_open: Option<LibraryKind>,
|
||||||
library_hovered: Option<LibraryKind>,
|
library_hovered: Option<LibraryKind>,
|
||||||
selected_item: Option<(LibraryKind, i32)>,
|
selected_items: Option<Vec<(LibraryKind, i32)>>,
|
||||||
hovered_item: Option<(LibraryKind, i32)>,
|
hovered_item: Option<(LibraryKind, i32)>,
|
||||||
editing_item: Option<(LibraryKind, i32)>,
|
editing_item: Option<(LibraryKind, i32)>,
|
||||||
db: SqlitePool,
|
db: SqlitePool,
|
||||||
menu_keys: std::collections::HashMap<menu::KeyBind, MenuMessage>,
|
menu_keys: std::collections::HashMap<menu::KeyBind, MenuMessage>,
|
||||||
context_menu: Option<i32>,
|
context_menu: Option<i32>,
|
||||||
|
modifiers_pressed: Option<Modifiers>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Copy)]
|
#[derive(Debug, Clone, Eq, PartialEq, Copy)]
|
||||||
|
|
@ -116,12 +118,13 @@ impl<'a> Library {
|
||||||
.await,
|
.await,
|
||||||
library_open: None,
|
library_open: None,
|
||||||
library_hovered: None,
|
library_hovered: None,
|
||||||
selected_item: None,
|
selected_items: None,
|
||||||
hovered_item: None,
|
hovered_item: None,
|
||||||
editing_item: None,
|
editing_item: None,
|
||||||
db,
|
db,
|
||||||
menu_keys: HashMap::new(),
|
menu_keys: HashMap::new(),
|
||||||
context_menu: None,
|
context_menu: None,
|
||||||
|
modifiers_pressed: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,154 +136,7 @@ impl<'a> Library {
|
||||||
match message {
|
match message {
|
||||||
Message::None => (),
|
Message::None => (),
|
||||||
Message::DeleteItem((kind, index)) => {
|
Message::DeleteItem((kind, index)) => {
|
||||||
match kind {
|
return self.delete_items();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
Message::AddItem => {
|
Message::AddItem => {
|
||||||
let kind =
|
let kind =
|
||||||
|
|
@ -374,7 +230,63 @@ impl<'a> Library {
|
||||||
self.hovered_item = item;
|
self.hovered_item = item;
|
||||||
}
|
}
|
||||||
Message::SelectItem(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) => {
|
Message::DragItem(item) => {
|
||||||
debug!(?item);
|
debug!(?item);
|
||||||
|
|
@ -795,11 +707,8 @@ impl<'a> Library {
|
||||||
.center_x(Length::Fill);
|
.center_x(Length::Fill);
|
||||||
let subtext = container(responsive(move |size| {
|
let subtext = container(responsive(move |size| {
|
||||||
let color: Color = if item.background().is_some() {
|
let color: Color = if item.background().is_some() {
|
||||||
if let Some((library, selected)) = self.selected_item
|
if let Some(items) = &self.selected_items {
|
||||||
{
|
if items.contains(&(model.kind, index as i32)) {
|
||||||
if model.kind == library
|
|
||||||
&& selected == index as i32
|
|
||||||
{
|
|
||||||
theme::active().cosmic().control_0().into()
|
theme::active().cosmic().control_0().into()
|
||||||
} else {
|
} else {
|
||||||
theme::active()
|
theme::active()
|
||||||
|
|
@ -813,10 +722,8 @@ impl<'a> Library {
|
||||||
.accent_text_color()
|
.accent_text_color()
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
} else if let Some((library, selected)) =
|
} else if let Some(items) = &self.selected_items {
|
||||||
self.selected_item
|
if items.contains(&(model.kind, index as i32)) {
|
||||||
{
|
|
||||||
if model.kind == library && selected == index as i32 {
|
|
||||||
theme::active().cosmic().control_0().into()
|
theme::active().cosmic().control_0().into()
|
||||||
} else {
|
} else {
|
||||||
theme::active()
|
theme::active()
|
||||||
|
|
@ -851,11 +758,8 @@ impl<'a> Library {
|
||||||
.style(move |t| {
|
.style(move |t| {
|
||||||
container::Style::default()
|
container::Style::default()
|
||||||
.background(Background::Color(
|
.background(Background::Color(
|
||||||
if let Some((library, selected)) =
|
if let Some(items) = &self.selected_items {
|
||||||
self.selected_item
|
if items.contains(&(model.kind, index as i32))
|
||||||
{
|
|
||||||
if model.kind == library
|
|
||||||
&& selected == index as i32
|
|
||||||
{
|
{
|
||||||
t.cosmic().accent.selected.into()
|
t.cosmic().accent.selected.into()
|
||||||
} else if let Some((library, hovered)) =
|
} else if let Some((library, hovered)) =
|
||||||
|
|
@ -965,6 +869,169 @@ impl<'a> Library {
|
||||||
pub fn get_image(&self, index: i32) -> Option<&Image> {
|
pub fn get_image(&self, index: i32) -> Option<&Image> {
|
||||||
self.image_library.get_item(index)
|
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>> {
|
async fn add_images() -> Option<Vec<Image>> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue