This commit is contained in:
parent
bb1057e950
commit
991feb18c8
11 changed files with 219 additions and 374 deletions
33
Cargo.lock
generated
33
Cargo.lock
generated
|
|
@ -3888,27 +3888,6 @@ version = "0.5.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
|
checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lexpr"
|
|
||||||
version = "0.2.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6a84de6a9df442363b08f5dbf0cd5b92edc70097b89c4ce4bfea4679fe48bc67"
|
|
||||||
dependencies = [
|
|
||||||
"itoa",
|
|
||||||
"lexpr-macros",
|
|
||||||
"ryu",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lexpr-macros"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "36b5cb8bb985c81a8ac1a0f8b5c4865214f574ddd64397ef7a99c236e21f35bb"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.175"
|
version = "0.2.175"
|
||||||
|
|
@ -4105,7 +4084,6 @@ dependencies = [
|
||||||
"gstreamer-app",
|
"gstreamer-app",
|
||||||
"iced_video_player",
|
"iced_video_player",
|
||||||
"image",
|
"image",
|
||||||
"lexpr",
|
|
||||||
"libcosmic",
|
"libcosmic",
|
||||||
"miette",
|
"miette",
|
||||||
"mupdf",
|
"mupdf",
|
||||||
|
|
@ -4117,7 +4095,6 @@ dependencies = [
|
||||||
"rodio",
|
"rodio",
|
||||||
"ron 0.8.1",
|
"ron 0.8.1",
|
||||||
"serde",
|
"serde",
|
||||||
"serde-lexpr",
|
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"strum",
|
"strum",
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
|
|
@ -6223,16 +6200,6 @@ dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde-lexpr"
|
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bb4cda13396159f59e7946118cdac0beadeecfb7cf76b197f4147e546f4ead6f"
|
|
||||||
dependencies = [
|
|
||||||
"lexpr",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_core"
|
name = "serde_core"
|
||||||
version = "1.0.225"
|
version = "1.0.225"
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,9 @@ description = "A cli presentation system"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.20", features = ["debug", "derive"] }
|
clap = { version = "4.5.20", features = ["debug", "derive"] }
|
||||||
lexpr = "0.2.7"
|
|
||||||
miette = { version = "7.2.0", features = ["fancy"] }
|
miette = { version = "7.2.0", features = ["fancy"] }
|
||||||
pretty_assertions = "1.4.1"
|
pretty_assertions = "1.4.1"
|
||||||
serde = { version = "1.0.213", features = ["derive"] }
|
serde = { version = "1.0.213", features = ["derive"] }
|
||||||
serde-lexpr = "0.1.3"
|
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
tracing-log = "0.2.0"
|
tracing-log = "0.2.0"
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["fmt", "std", "chrono", "time", "local-time", "env-filter"] }
|
tracing-subscriber = { version = "0.3.18", features = ["fmt", "std", "chrono", "time", "local-time", "env-filter"] }
|
||||||
|
|
|
||||||
3
justfile
3
justfile
|
|
@ -8,6 +8,8 @@ build:
|
||||||
sbuild:
|
sbuild:
|
||||||
RUST_LOG=debug sccache cargo build
|
RUST_LOG=debug sccache cargo build
|
||||||
run:
|
run:
|
||||||
|
RUST_LOG=debug cargo run -- {{ui}}
|
||||||
|
run-file:
|
||||||
RUST_LOG=debug cargo run -- {{ui}} {{file}}
|
RUST_LOG=debug cargo run -- {{ui}} {{file}}
|
||||||
srun:
|
srun:
|
||||||
RUST_LOG=debug sccache cargo run -- {{ui}} {{file}}
|
RUST_LOG=debug sccache cargo run -- {{ui}} {{file}}
|
||||||
|
|
@ -20,5 +22,6 @@ profile:
|
||||||
|
|
||||||
alias b := build
|
alias b := build
|
||||||
alias r := run
|
alias r := run
|
||||||
|
alias rf := run-file
|
||||||
alias sr := srun
|
alias sr := srun
|
||||||
alias c := clean
|
alias c := clean
|
||||||
|
|
|
||||||
157
src/core/lisp.rs
157
src/core/lisp.rs
|
|
@ -1,157 +0,0 @@
|
||||||
use lexpr::Value;
|
|
||||||
use strum_macros::EnumString;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, EnumString)]
|
|
||||||
pub(crate) enum Symbol {
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
Slide,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
Image,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
Text,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
Video,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
Song,
|
|
||||||
#[strum(disabled)]
|
|
||||||
ImageFit(ImageFit),
|
|
||||||
#[strum(disabled)]
|
|
||||||
VerseOrder(VerseOrder),
|
|
||||||
#[strum(disabled)]
|
|
||||||
#[default]
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, EnumString)]
|
|
||||||
pub(crate) enum Keyword {
|
|
||||||
ImageFit(ImageFit),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq, EnumString)]
|
|
||||||
pub(crate) enum ImageFit {
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
#[default]
|
|
||||||
Cover,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
Fill,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
Crop,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq, EnumString)]
|
|
||||||
pub(crate) enum VerseOrder {
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
#[default]
|
|
||||||
V1,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
V2,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
V3,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
V4,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
V5,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
V6,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
C1,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
C2,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
C3,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
C4,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
B1,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
B2,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
B3,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
B4,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
O1,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
O2,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
O3,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
O4,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
E1,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
E2,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
I1,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
I2,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, EnumString)]
|
|
||||||
pub(crate) enum SongKeyword {
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
Title,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
Author,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
Ccli,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
Audio,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
Font,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
FontSize,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
Background,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
VerseOrder(VerseOrder),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, EnumString)]
|
|
||||||
pub(crate) enum ImageKeyword {
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
Source,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
Fit,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, EnumString)]
|
|
||||||
pub(crate) enum VideoKeyword {
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
Source,
|
|
||||||
#[strum(ascii_case_insensitive)]
|
|
||||||
Fit,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_lists(exp: &Value) -> Vec<Value> {
|
|
||||||
if exp.is_cons() {
|
|
||||||
exp.as_cons().unwrap().to_vec().0
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_list() {
|
|
||||||
// let lisp =
|
|
||||||
// read_to_string("./test_presentation.lisp").expect("oops");
|
|
||||||
// // println!("{lisp}");
|
|
||||||
// let mut parser =
|
|
||||||
// Parser::from_str_custom(&lisp, Options::elisp());
|
|
||||||
// for atom in parser.value_iter() {
|
|
||||||
// match atom {
|
|
||||||
// Ok(atom) => {
|
|
||||||
// // println!("{atom}");
|
|
||||||
// let lists = get_lists(&atom);
|
|
||||||
// assert_eq!(lists, vec![Value::Null])
|
|
||||||
// }
|
|
||||||
// Err(e) => {
|
|
||||||
// panic!("{e}");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
pub mod content;
|
pub mod content;
|
||||||
pub mod images;
|
pub mod images;
|
||||||
pub mod kinds;
|
pub mod kinds;
|
||||||
pub mod lisp;
|
|
||||||
pub mod model;
|
pub mod model;
|
||||||
pub mod presentations;
|
pub mod presentations;
|
||||||
pub mod service_items;
|
pub mod service_items;
|
||||||
|
|
|
||||||
307
src/main.rs
307
src/main.rs
|
|
@ -9,9 +9,11 @@ use cosmic::app::{Core, Settings, Task};
|
||||||
use cosmic::iced::alignment::Vertical;
|
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::{self, event, window, Color, Length, Point};
|
use cosmic::iced::{
|
||||||
|
self, event, window, Background as IcedBackground, Border, Length,
|
||||||
|
};
|
||||||
|
use cosmic::iced_core::text::Wrapping;
|
||||||
use cosmic::iced_futures::Subscription;
|
use cosmic::iced_futures::Subscription;
|
||||||
use cosmic::iced_runtime::dnd::DndAction;
|
|
||||||
use cosmic::iced_widget::{column, row, stack};
|
use cosmic::iced_widget::{column, row, stack};
|
||||||
use cosmic::theme;
|
use cosmic::theme;
|
||||||
use cosmic::widget::dnd_destination::dnd_destination;
|
use cosmic::widget::dnd_destination::dnd_destination;
|
||||||
|
|
@ -20,8 +22,8 @@ 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, dnd_source, horizontal_space, mouse_area, nav_bar,
|
button, horizontal_space, mouse_area, nav_bar, responsive,
|
||||||
search_input, tooltip, vertical_space, RcElementWrapper, Space,
|
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};
|
||||||
|
|
@ -58,7 +60,7 @@ struct Cli {
|
||||||
watch: bool,
|
watch: bool,
|
||||||
#[arg(short = 'i', long)]
|
#[arg(short = 'i', long)]
|
||||||
ui: bool,
|
ui: bool,
|
||||||
file: PathBuf,
|
file: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
|
|
@ -111,6 +113,7 @@ struct App {
|
||||||
windows: Vec<window::Id>,
|
windows: Vec<window::Id>,
|
||||||
service: Vec<ServiceItem>,
|
service: Vec<ServiceItem>,
|
||||||
current_item: (usize, usize),
|
current_item: (usize, usize),
|
||||||
|
hovered_item: Option<usize>,
|
||||||
presentation_open: bool,
|
presentation_open: bool,
|
||||||
cli_mode: bool,
|
cli_mode: bool,
|
||||||
library: Option<Library>,
|
library: Option<Library>,
|
||||||
|
|
@ -134,7 +137,7 @@ enum Message {
|
||||||
File(PathBuf),
|
File(PathBuf),
|
||||||
OpenWindow,
|
OpenWindow,
|
||||||
CloseWindow(Option<window::Id>),
|
CloseWindow(Option<window::Id>),
|
||||||
WindowOpened(window::Id, Option<Point>),
|
WindowOpened(window::Id),
|
||||||
WindowClosed(window::Id),
|
WindowClosed(window::Id),
|
||||||
AddLibrary(Library),
|
AddLibrary(Library),
|
||||||
LibraryToggle,
|
LibraryToggle,
|
||||||
|
|
@ -143,10 +146,10 @@ enum Message {
|
||||||
None,
|
None,
|
||||||
EditorToggle(bool),
|
EditorToggle(bool),
|
||||||
ChangeServiceItem(usize),
|
ChangeServiceItem(usize),
|
||||||
|
HoveredServiceItem(Option<usize>),
|
||||||
AddServiceItem(usize, ServiceItem),
|
AddServiceItem(usize, ServiceItem),
|
||||||
AddServiceItemDrop(usize),
|
AddServiceItemDrop(usize),
|
||||||
AppendServiceItem(ServiceItem),
|
AppendServiceItem(ServiceItem),
|
||||||
AddService(Vec<ServiceItem>),
|
|
||||||
SearchFocus,
|
SearchFocus,
|
||||||
Search(String),
|
Search(String),
|
||||||
CloseSearch,
|
CloseSearch,
|
||||||
|
|
@ -213,30 +216,36 @@ impl cosmic::Application for App {
|
||||||
windows.push(core.main_window_id().unwrap());
|
windows.push(core.main_window_id().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
let items = match read_to_string(input.file) {
|
let items = if let Some(file) = input.file {
|
||||||
Ok(lisp) => {
|
match read_to_string(file) {
|
||||||
let mut service_items = vec![];
|
Ok(lisp) => {
|
||||||
let lisp = crisp::reader::read(&lisp);
|
let mut service_items = vec![];
|
||||||
match lisp {
|
let lisp = crisp::reader::read(&lisp);
|
||||||
Value::List(vec) => {
|
match lisp {
|
||||||
// let items = vec
|
Value::List(vec) => {
|
||||||
// .into_par_iter()
|
// let items = vec
|
||||||
// .map(|value| parse_lisp(value))
|
// .into_par_iter()
|
||||||
// .collect();
|
// .map(|value| parse_lisp(value))
|
||||||
// slide_vector.append(items);
|
// .collect();
|
||||||
for value in vec {
|
// slide_vector.append(items);
|
||||||
let mut inner_vector = parse_lisp(value);
|
for value in vec {
|
||||||
service_items.append(&mut inner_vector);
|
let mut inner_vector =
|
||||||
|
parse_lisp(value);
|
||||||
|
service_items
|
||||||
|
.append(&mut inner_vector);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
_ => todo!(),
|
service_items
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Missing file or could not read: {e}");
|
||||||
|
vec![]
|
||||||
}
|
}
|
||||||
service_items
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Missing file or could not read: {e}");
|
|
||||||
vec![]
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
};
|
};
|
||||||
|
|
||||||
let items: Vec<ServiceItem> = items
|
let items: Vec<ServiceItem> = items
|
||||||
|
|
@ -308,6 +317,7 @@ impl cosmic::Application for App {
|
||||||
library_dragged_item: None,
|
library_dragged_item: None,
|
||||||
fontdb: Arc::clone(&fontdb),
|
fontdb: Arc::clone(&fontdb),
|
||||||
menu_keys,
|
menu_keys,
|
||||||
|
hovered_item: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut batch = vec![];
|
let mut batch = vec![];
|
||||||
|
|
@ -530,23 +540,33 @@ impl cosmic::Application for App {
|
||||||
debug!("Closing window");
|
debug!("Closing window");
|
||||||
Some(Message::CloseWindow(Some(id)))
|
Some(Message::CloseWindow(Some(id)))
|
||||||
}
|
}
|
||||||
window::Event::Opened {
|
window::Event::Opened { .. } => {
|
||||||
position, ..
|
|
||||||
} => {
|
|
||||||
debug!(?window_event, ?id);
|
debug!(?window_event, ?id);
|
||||||
Some(Message::WindowOpened(id, position))
|
Some(Message::WindowOpened(id))
|
||||||
}
|
}
|
||||||
window::Event::Closed => {
|
window::Event::Closed => {
|
||||||
debug!("Closed window");
|
debug!("Closed window");
|
||||||
Some(Message::WindowClosed(id))
|
Some(Message::WindowClosed(id))
|
||||||
}
|
}
|
||||||
|
window::Event::FileHovered(file) => {
|
||||||
|
debug!(?file);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
window::Event::FileDropped(file) => {
|
||||||
|
debug!(?file);
|
||||||
|
None
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
iced::Event::Touch(_touch) => None,
|
iced::Event::Touch(_touch) => None,
|
||||||
iced::Event::A11y(_id, _action_request) => None,
|
iced::Event::A11y(_id, _action_request) => None,
|
||||||
iced::Event::Dnd(_dnd_event) => None,
|
iced::Event::Dnd(_dnd_event) => {
|
||||||
iced::Event::PlatformSpecific(_platform_specific) => {
|
// debug!(?dnd_event);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
iced::Event::PlatformSpecific(platform_specific) => {
|
||||||
|
debug!(?platform_specific);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -676,7 +696,7 @@ impl cosmic::Application for App {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
song_editor::Action::UpdateSong(song) => {
|
song_editor::Action::UpdateSong(song) => {
|
||||||
if let Some(library) = &mut self.library {
|
if let Some(_) = &mut self.library {
|
||||||
self.update(Message::Library(
|
self.update(Message::Library(
|
||||||
library::Message::UpdateSong(song),
|
library::Message::UpdateSong(song),
|
||||||
))
|
))
|
||||||
|
|
@ -914,9 +934,7 @@ impl cosmic::Application for App {
|
||||||
.set_window_title(format!("window_{count}"), id);
|
.set_window_title(format!("window_{count}"), id);
|
||||||
|
|
||||||
spawn_window.map(|id| {
|
spawn_window.map(|id| {
|
||||||
cosmic::Action::App(Message::WindowOpened(
|
cosmic::Action::App(Message::WindowOpened(id))
|
||||||
id, None,
|
|
||||||
))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Message::CloseWindow(id) => {
|
Message::CloseWindow(id) => {
|
||||||
|
|
@ -926,7 +944,7 @@ impl cosmic::Application for App {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::WindowOpened(id, _) => {
|
Message::WindowOpened(id) => {
|
||||||
debug!(?id, "Window opened");
|
debug!(?id, "Window opened");
|
||||||
if self.cli_mode
|
if self.cli_mode
|
||||||
|| id > self.core.main_window_id().expect("Cosmic core seems to be missing a main window, was this started in cli mode?")
|
|| id > self.core.main_window_id().expect("Cosmic core seems to be missing a main window, was this started in cli mode?")
|
||||||
|
|
@ -969,10 +987,6 @@ impl cosmic::Application for App {
|
||||||
self.library = Some(library);
|
self.library = Some(library);
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::AddService(service) => {
|
|
||||||
self.service = service;
|
|
||||||
Task::none()
|
|
||||||
}
|
|
||||||
Message::None => Task::none(),
|
Message::None => Task::none(),
|
||||||
Message::EditorToggle(edit) => {
|
Message::EditorToggle(edit) => {
|
||||||
if edit {
|
if edit {
|
||||||
|
|
@ -988,6 +1002,10 @@ impl cosmic::Application for App {
|
||||||
self.search_id.clone(),
|
self.search_id.clone(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Message::HoveredServiceItem(index) => {
|
||||||
|
self.hovered_item = index;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
Message::ChangeServiceItem(index) => {
|
Message::ChangeServiceItem(index) => {
|
||||||
if let Some((index, item)) = self
|
if let Some((index, item)) = self
|
||||||
.service
|
.service
|
||||||
|
|
@ -1283,9 +1301,8 @@ where
|
||||||
});
|
});
|
||||||
self.windows.push(id);
|
self.windows.push(id);
|
||||||
_ = self.set_window_title("Lumina Presenter".to_owned(), id);
|
_ = self.set_window_title("Lumina Presenter".to_owned(), id);
|
||||||
spawn_window.map(|id| {
|
spawn_window
|
||||||
cosmic::Action::App(Message::WindowOpened(id, None))
|
.map(|id| cosmic::Action::App(Message::WindowOpened(id)))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_library(&self) -> Task<Message> {
|
fn add_library(&self) -> Task<Message> {
|
||||||
|
|
@ -1309,36 +1326,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_service(
|
|
||||||
&self,
|
|
||||||
items: Vec<ServiceItem>,
|
|
||||||
fontdb: Arc<fontdb::Database>,
|
|
||||||
) -> Task<Message> {
|
|
||||||
Task::perform(
|
|
||||||
async move {
|
|
||||||
let items: Vec<ServiceItem> = items
|
|
||||||
.into_par_iter()
|
|
||||||
.map(|mut item| {
|
|
||||||
item.slides = item
|
|
||||||
.slides
|
|
||||||
.into_par_iter()
|
|
||||||
.map(|mut slide| {
|
|
||||||
text_svg::text_svg_generator(
|
|
||||||
&mut slide,
|
|
||||||
Arc::clone(&fontdb),
|
|
||||||
);
|
|
||||||
slide
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
item
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
items
|
|
||||||
},
|
|
||||||
|x| cosmic::Action::App(Message::AddService(x)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_key_press(
|
fn process_key_press(
|
||||||
&mut self,
|
&mut self,
|
||||||
key: Key,
|
key: Key,
|
||||||
|
|
@ -1411,56 +1398,123 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn service_list(&self) -> Element<Message> {
|
fn service_list(&self) -> Element<Message> {
|
||||||
let list = self
|
let list =
|
||||||
.service
|
self.service.iter().enumerate().map(|(index, item)| {
|
||||||
.iter()
|
let icon = match item.kind {
|
||||||
.enumerate()
|
ServiceItemKind::Song(_) => {
|
||||||
.map(|(index, item)| {
|
icon::from_name("folder-music-symbolic")
|
||||||
let button = button::standard(item.title.clone())
|
}
|
||||||
.leading_icon({
|
ServiceItemKind::Video(_) => {
|
||||||
match item.kind {
|
icon::from_name("folder-videos-symbolic")
|
||||||
core::kinds::ServiceItemKind::Song(_) => {
|
}
|
||||||
icon::from_name("folder-music-symbolic")
|
ServiceItemKind::Image(_) => {
|
||||||
},
|
icon::from_name("folder-pictures-symbolic")
|
||||||
core::kinds::ServiceItemKind::Video(_) => {
|
}
|
||||||
icon::from_name("folder-videos-symbolic")
|
ServiceItemKind::Presentation(_) => {
|
||||||
},
|
icon::from_name(
|
||||||
core::kinds::ServiceItemKind::Image(_) => {
|
"x-office-presentation-symbolic",
|
||||||
icon::from_name("folder-pictures-symbolic")
|
)
|
||||||
},
|
}
|
||||||
core::kinds::ServiceItemKind::Presentation(_) => {
|
ServiceItemKind::Content(_) => icon::from_name(
|
||||||
icon::from_name("x-office-presentation-symbolic")
|
"x-office-presentation-symbolic",
|
||||||
},
|
),
|
||||||
core::kinds::ServiceItemKind::Content(_) => {
|
};
|
||||||
icon::from_name("x-office-presentation-symbolic")
|
let title = responsive(|size| {
|
||||||
},
|
text::heading(library::elide_text(
|
||||||
}
|
&item.title,
|
||||||
})
|
size.width,
|
||||||
// .icon_size(cosmic::theme::spacing().space_l)
|
))
|
||||||
.class(cosmic::theme::style::Button::HeaderBar)
|
.wrapping(Wrapping::None)
|
||||||
// .spacing(cosmic::theme::spacing().space_l)
|
|
||||||
.height(cosmic::theme::spacing().space_xl)
|
|
||||||
.width(Length::Fill)
|
|
||||||
.on_press(Message::ChangeServiceItem(index));
|
|
||||||
let tooltip = tooltip(button,
|
|
||||||
text::body(item.kind.to_string()),
|
|
||||||
TPosition::Right);
|
|
||||||
dnd_destination(tooltip, vec!["application/service-item".into()])
|
|
||||||
.data_received_for::<ServiceItem>( move |item| {
|
|
||||||
if let Some(item) = item {
|
|
||||||
Message::AddServiceItem(index, item)
|
|
||||||
} else {
|
|
||||||
Message::None
|
|
||||||
}
|
|
||||||
}).on_finish(move |mime, data, action, x, y| {
|
|
||||||
debug!(mime, ?data, ?action, x, y);
|
|
||||||
let Ok(item) = ServiceItem::try_from((data, mime)) else {
|
|
||||||
return Message::None;
|
|
||||||
};
|
|
||||||
debug!(?item);
|
|
||||||
Message::AddServiceItem(index, item)
|
|
||||||
})
|
|
||||||
.into()
|
.into()
|
||||||
|
});
|
||||||
|
let container = container(
|
||||||
|
row![icon, title]
|
||||||
|
.align_y(Vertical::Center)
|
||||||
|
.spacing(cosmic::theme::spacing().space_xs),
|
||||||
|
)
|
||||||
|
.padding(cosmic::theme::spacing().space_s)
|
||||||
|
.class(cosmic::theme::style::Container::Secondary)
|
||||||
|
.style(move |t| {
|
||||||
|
container::Style::default()
|
||||||
|
.background(IcedBackground::Color(
|
||||||
|
if self.hovered_item.is_some_and(
|
||||||
|
|hovered_index| {
|
||||||
|
index == hovered_index
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
t.cosmic().button.hover.into()
|
||||||
|
} else {
|
||||||
|
t.cosmic().button.base.into()
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.border(Border::default().rounded(
|
||||||
|
t.cosmic().corner_radii.radius_m,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.width(Length::Fill);
|
||||||
|
let mouse_area = mouse_area(container)
|
||||||
|
.on_enter(Message::HoveredServiceItem(Some(
|
||||||
|
index,
|
||||||
|
)))
|
||||||
|
.on_exit(Message::HoveredServiceItem(None))
|
||||||
|
.on_press(Message::ChangeServiceItem(index));
|
||||||
|
// let button = button::standard(item.title.clone())
|
||||||
|
// .leading_icon({
|
||||||
|
// match item.kind {
|
||||||
|
// core::kinds::ServiceItemKind::Song(_) => {
|
||||||
|
// icon::from_name("folder-music-symbolic")
|
||||||
|
// },
|
||||||
|
// core::kinds::ServiceItemKind::Video(_) => {
|
||||||
|
// icon::from_name("folder-videos-symbolic")
|
||||||
|
// },
|
||||||
|
// core::kinds::ServiceItemKind::Image(_) => {
|
||||||
|
// icon::from_name("folder-pictures-symbolic")
|
||||||
|
// },
|
||||||
|
// core::kinds::ServiceItemKind::Presentation(_) => {
|
||||||
|
// icon::from_name("x-office-presentation-symbolic")
|
||||||
|
// },
|
||||||
|
// core::kinds::ServiceItemKind::Content(_) => {
|
||||||
|
// icon::from_name("x-office-presentation-symbolic")
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// // .icon_size(cosmic::theme::spacing().space_l)
|
||||||
|
// .class(cosmic::theme::style::Button::HeaderBar)
|
||||||
|
// // .spacing(cosmic::theme::spacing().space_l)
|
||||||
|
// .height(cosmic::theme::spacing().space_xl)
|
||||||
|
// .width(Length::Fill)
|
||||||
|
// .on_press(Message::ChangeServiceItem(index));
|
||||||
|
let tooltip = tooltip(
|
||||||
|
mouse_area,
|
||||||
|
text::body(item.kind.to_string()),
|
||||||
|
TPosition::Right,
|
||||||
|
)
|
||||||
|
.gap(cosmic::theme::spacing().space_xs);
|
||||||
|
dnd_destination(
|
||||||
|
tooltip,
|
||||||
|
vec![
|
||||||
|
"application/service-item".into(),
|
||||||
|
"video/mp4".into(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.data_received_for::<ServiceItem>(move |item| {
|
||||||
|
if let Some(item) = item {
|
||||||
|
Message::AddServiceItem(index, item)
|
||||||
|
} else {
|
||||||
|
Message::None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on_finish(move |mime, data, action, x, y| {
|
||||||
|
debug!(mime, ?data, ?action, x, y);
|
||||||
|
let Ok(item) =
|
||||||
|
ServiceItem::try_from((data, mime))
|
||||||
|
else {
|
||||||
|
return Message::None;
|
||||||
|
};
|
||||||
|
debug!(?item);
|
||||||
|
Message::AddServiceItem(index, item)
|
||||||
|
})
|
||||||
|
.into()
|
||||||
});
|
});
|
||||||
|
|
||||||
let column = column![
|
let column = column![
|
||||||
|
|
@ -1469,6 +1523,7 @@ where
|
||||||
.width(Length::Fill),
|
.width(Length::Fill),
|
||||||
iced::widget::horizontal_rule(1),
|
iced::widget::horizontal_rule(1),
|
||||||
column(list).spacing(10),
|
column(list).spacing(10),
|
||||||
|
// service::service(&self.service),
|
||||||
dnd_destination(
|
dnd_destination(
|
||||||
vertical_space().width(Length::Fill),
|
vertical_space().width(Length::Fill),
|
||||||
vec!["application/service-item".into()]
|
vec!["application/service-item".into()]
|
||||||
|
|
@ -1494,9 +1549,7 @@ where
|
||||||
]
|
]
|
||||||
.padding(10)
|
.padding(10)
|
||||||
.spacing(10);
|
.spacing(10);
|
||||||
let container = Container::new(column)
|
let container = Container::new(column).style(nav_bar_style);
|
||||||
// .height(Length::Fill)
|
|
||||||
.style(nav_bar_style);
|
|
||||||
|
|
||||||
container.center(Length::FillPortion(2)).into()
|
container.center(Length::FillPortion(2)).into()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,6 @@ pub struct Library {
|
||||||
enum MenuMessage {
|
enum MenuMessage {
|
||||||
Delete((LibraryKind, i32)),
|
Delete((LibraryKind, i32)),
|
||||||
Open,
|
Open,
|
||||||
None,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MenuAction for MenuMessage {
|
impl MenuAction for MenuMessage {
|
||||||
|
|
@ -63,7 +62,6 @@ impl MenuAction for MenuMessage {
|
||||||
Message::DeleteItem((*kind, *index))
|
Message::DeleteItem((*kind, *index))
|
||||||
}
|
}
|
||||||
MenuMessage::Open => todo!(),
|
MenuMessage::Open => todo!(),
|
||||||
MenuMessage::None => todo!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -827,7 +825,7 @@ async fn add_db() -> Result<SqlitePool> {
|
||||||
SqlitePool::connect(&db_url).await.into_diagnostic()
|
SqlitePool::connect(&db_url).await.into_diagnostic()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn elide_text(text: impl AsRef<str>, width: f32) -> String {
|
pub fn elide_text(text: impl AsRef<str>, width: f32) -> String {
|
||||||
const CHAR_SIZE: f32 = 8.0;
|
const CHAR_SIZE: f32 = 8.0;
|
||||||
let text: String = text.as_ref().to_owned();
|
let text: String = text.as_ref().to_owned();
|
||||||
let text_length = text.len() as f32 * CHAR_SIZE;
|
let text_length = text.len() as f32 * CHAR_SIZE;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
use std::{fs::File, io::BufReader, path::PathBuf, sync::Arc};
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::BufReader,
|
||||||
|
path::PathBuf,
|
||||||
|
sync::{Arc, LazyLock},
|
||||||
|
};
|
||||||
|
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
iced::{
|
iced::{
|
||||||
|
|
@ -31,7 +36,8 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
const REFERENCE_WIDTH: f32 = 1920.0;
|
const REFERENCE_WIDTH: f32 = 1920.0;
|
||||||
const REFERENCE_HEIGHT: f32 = 1080.0;
|
static DEFAULT_SLIDE: LazyLock<Slide> =
|
||||||
|
LazyLock::new(|| Slide::default());
|
||||||
|
|
||||||
// #[derive(Default, Clone, Debug)]
|
// #[derive(Default, Clone, Debug)]
|
||||||
pub(crate) struct Presenter {
|
pub(crate) struct Presenter {
|
||||||
|
|
@ -147,14 +153,22 @@ impl Presenter {
|
||||||
let total_slides: usize =
|
let total_slides: usize =
|
||||||
items.iter().fold(0, |a, item| a + item.slides.len());
|
items.iter().fold(0, |a, item| a + item.slides.len());
|
||||||
|
|
||||||
|
let slide =
|
||||||
|
items.get(0).map(|item| item.slides.get(0)).flatten();
|
||||||
|
let audio = items
|
||||||
|
.get(0)
|
||||||
|
.map(|item| item.slides.get(0).map(|slide| slide.audio()))
|
||||||
|
.flatten()
|
||||||
|
.flatten();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
current_slide: items[0].slides[0].clone(),
|
current_slide: slide.unwrap_or(&DEFAULT_SLIDE).clone(),
|
||||||
current_item: 0,
|
current_item: 0,
|
||||||
current_slide_index: 0,
|
current_slide_index: 0,
|
||||||
absolute_slide_index: 0,
|
absolute_slide_index: 0,
|
||||||
total_slides,
|
total_slides,
|
||||||
video,
|
video,
|
||||||
audio: items[0].slides[0].audio(),
|
audio,
|
||||||
service: items,
|
service: items,
|
||||||
video_position: 0.0,
|
video_position: 0.0,
|
||||||
hovered_slide: None,
|
hovered_slide: None,
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,19 @@
|
||||||
use std::any::Any;
|
use cosmic::iced::Size;
|
||||||
|
|
||||||
use cosmic::iced::{self, Size};
|
|
||||||
use cosmic::iced_core::window;
|
|
||||||
|
|
||||||
|
use cosmic::iced_core::widget::tree;
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
iced::{
|
iced::{
|
||||||
clipboard::dnd::{DndAction, DndEvent, SourceEvent},
|
clipboard::dnd::{DndEvent, SourceEvent},
|
||||||
event, mouse, overlay, Event, Length, Point, Rectangle,
|
event, mouse, Event, Length, Point, Rectangle, Vector,
|
||||||
Vector,
|
|
||||||
},
|
},
|
||||||
iced_core::{
|
iced_core::{
|
||||||
self, layout, renderer,
|
self, image::Renderer, layout, renderer, widget::Tree,
|
||||||
widget::{tree, Tree},
|
|
||||||
Clipboard, Shell,
|
Clipboard, Shell,
|
||||||
},
|
},
|
||||||
widget::{container, Id, Widget},
|
widget::Widget,
|
||||||
Element,
|
Element,
|
||||||
};
|
};
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::core::service_items::ServiceItem;
|
use crate::core::service_items::ServiceItem;
|
||||||
|
|
||||||
|
|
@ -133,6 +130,10 @@ impl<Message: Clone + 'static>
|
||||||
layout::atomic(limits, self.width, self.height)
|
layout::atomic(limits, self.width, self.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn state(&self) -> iced_core::widget::tree::State {
|
||||||
|
tree::State::new(State::new())
|
||||||
|
}
|
||||||
|
|
||||||
// fn operate(
|
// fn operate(
|
||||||
// &self,
|
// &self,
|
||||||
// tree: &mut Tree,
|
// tree: &mut Tree,
|
||||||
|
|
@ -231,6 +232,7 @@ impl<Message: Clone + 'static>
|
||||||
_ => return event::Status::Ignored,
|
_ => return event::Status::Ignored,
|
||||||
},
|
},
|
||||||
Event::Dnd(DndEvent::Source(SourceEvent::Cancelled)) => {
|
Event::Dnd(DndEvent::Source(SourceEvent::Cancelled)) => {
|
||||||
|
debug!("canceled");
|
||||||
if state.is_dragging {
|
if state.is_dragging {
|
||||||
if let Some(m) = self.on_cancelled.as_ref() {
|
if let Some(m) = self.on_cancelled.as_ref() {
|
||||||
shell.publish(m.clone());
|
shell.publish(m.clone());
|
||||||
|
|
@ -241,6 +243,7 @@ impl<Message: Clone + 'static>
|
||||||
return event::Status::Ignored;
|
return event::Status::Ignored;
|
||||||
}
|
}
|
||||||
Event::Dnd(DndEvent::Source(SourceEvent::Finished)) => {
|
Event::Dnd(DndEvent::Source(SourceEvent::Finished)) => {
|
||||||
|
debug!("dropped");
|
||||||
if state.is_dragging {
|
if state.is_dragging {
|
||||||
if let Some(m) = self.on_finish.as_ref() {
|
if let Some(m) = self.on_finish.as_ref() {
|
||||||
shell.publish(m.clone());
|
shell.publish(m.clone());
|
||||||
|
|
@ -250,6 +253,7 @@ impl<Message: Clone + 'static>
|
||||||
}
|
}
|
||||||
return event::Status::Ignored;
|
return event::Status::Ignored;
|
||||||
}
|
}
|
||||||
|
Event::Dnd(event) => debug!(?event),
|
||||||
_ => return event::Status::Ignored,
|
_ => return event::Status::Ignored,
|
||||||
}
|
}
|
||||||
event::Status::Ignored
|
event::Status::Ignored
|
||||||
|
|
@ -286,10 +290,8 @@ impl<Message: Clone + 'static>
|
||||||
cursor_position: mouse::Cursor,
|
cursor_position: mouse::Cursor,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) {
|
) {
|
||||||
let state = tree.state.downcast_mut::<State>();
|
// let state = tree.state.downcast_mut::<State>();
|
||||||
for item in self.service {
|
for item in self.service {}
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn overlay<'b>(
|
// fn overlay<'b>(
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::{io, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
dialog::file_chooser::{open::Dialog, FileFilter},
|
dialog::file_chooser::{open::Dialog, FileFilter},
|
||||||
iced::{alignment::Vertical, Font, Length},
|
iced::{alignment::Vertical, Length},
|
||||||
iced_wgpu::graphics::text::cosmic_text::fontdb,
|
iced_wgpu::graphics::text::cosmic_text::fontdb,
|
||||||
iced_widget::{column, row},
|
iced_widget::{column, row},
|
||||||
theme,
|
theme,
|
||||||
|
|
|
||||||
|
|
@ -354,14 +354,6 @@ impl TextSvg {
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn text_spans(&self) -> Vec<String> {
|
|
||||||
self.text
|
|
||||||
.lines()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, t)| format!("<tspan x=\"50%\">{t}</tspan>"))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shadow(
|
pub fn shadow(
|
||||||
|
|
@ -408,27 +400,3 @@ pub fn text_svg_generator(
|
||||||
slide.text_svg = Some(text_svg);
|
slide.text_svg = Some(text_svg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
|
|
||||||
use super::TextSvg;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_text_spans() {
|
|
||||||
let mut text = TextSvg::new("yes");
|
|
||||||
text.text = "This is
|
|
||||||
multiline
|
|
||||||
text."
|
|
||||||
.into();
|
|
||||||
assert_eq!(
|
|
||||||
vec![
|
|
||||||
String::from("<tspan>This is</tspan>"),
|
|
||||||
String::from("<tspan>multiline</tspan>"),
|
|
||||||
String::from("<tspan>text.</tspan>"),
|
|
||||||
],
|
|
||||||
text.text_spans()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue