tweaks to ui
This commit is contained in:
parent
c9225680c3
commit
e82a9c161b
264
src/main.rs
264
src/main.rs
|
@ -3,12 +3,18 @@ use cosmic::app::{Core, Settings, Task};
|
||||||
use cosmic::dialog::ashpd::url::Url;
|
use cosmic::dialog::ashpd::url::Url;
|
||||||
use cosmic::iced::keyboard::Key;
|
use cosmic::iced::keyboard::Key;
|
||||||
use cosmic::iced::window::Position;
|
use cosmic::iced::window::Position;
|
||||||
use cosmic::iced::{self, event, window, ContentFit, Font, Length, Point};
|
use cosmic::iced::{
|
||||||
|
self, event, window, ContentFit, Font, Length, Point,
|
||||||
|
};
|
||||||
use cosmic::iced_core::{id, SmolStr};
|
use cosmic::iced_core::{id, SmolStr};
|
||||||
use cosmic::iced_widget::{stack, text};
|
use cosmic::iced_widget::{row, stack, text};
|
||||||
use cosmic::widget::{button, image, nav_bar};
|
use cosmic::widget::icon::{self, Named};
|
||||||
use cosmic::{executor, iced_wgpu, Also, Application, ApplicationExt, Element};
|
use cosmic::widget::{button, image, nav_bar, Space};
|
||||||
|
use cosmic::{
|
||||||
|
executor, iced_wgpu, Also, Application, ApplicationExt, Element,
|
||||||
|
};
|
||||||
use cosmic::{widget::Container, Theme};
|
use cosmic::{widget::Container, Theme};
|
||||||
|
use iced_video_player::VideoPlayer;
|
||||||
use lexpr::{parse, Value};
|
use lexpr::{parse, Value};
|
||||||
use miette::{miette, Result};
|
use miette::{miette, Result};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -34,8 +40,9 @@ struct Cli {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let timer =
|
let timer = tracing_subscriber::fmt::time::ChronoLocal::new(
|
||||||
tracing_subscriber::fmt::time::ChronoLocal::new("%Y-%m-%d_%I:%M:%S%.6f %P".to_owned());
|
"%Y-%m-%d_%I:%M:%S%.6f %P".to_owned(),
|
||||||
|
);
|
||||||
let filter = EnvFilter::builder()
|
let filter = EnvFilter::builder()
|
||||||
.with_default_directive(LevelFilter::WARN.into())
|
.with_default_directive(LevelFilter::WARN.into())
|
||||||
.parse_lossy("lumina=debug");
|
.parse_lossy("lumina=debug");
|
||||||
|
@ -55,10 +62,12 @@ fn main() -> Result<()> {
|
||||||
if args.ui {
|
if args.ui {
|
||||||
settings = Settings::default().debug(false);
|
settings = Settings::default().debug(false);
|
||||||
} else {
|
} else {
|
||||||
settings = Settings::default().debug(false).no_main_window(true);
|
settings =
|
||||||
|
Settings::default().debug(false).no_main_window(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(cosmic::app::run::<App>(settings, args).map_err(|e| miette!("Invalid things... {}", e))?)
|
Ok(cosmic::app::run::<App>(settings, args)
|
||||||
|
.map_err(|e| miette!("Invalid things... {}", e))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn theme(_state: &App) -> Theme {
|
fn theme(_state: &App) -> Theme {
|
||||||
|
@ -76,7 +85,9 @@ struct App {
|
||||||
impl Default for App {
|
impl Default for App {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let initial_slide = SlideBuilder::new()
|
let initial_slide = SlideBuilder::new()
|
||||||
.background(PathBuf::from("/home/chris/vids/test/chosensmol.mp4"))
|
.background(PathBuf::from(
|
||||||
|
"/home/chris/vids/test/chosensmol.mp4",
|
||||||
|
))
|
||||||
.expect("oops video")
|
.expect("oops video")
|
||||||
.text("Hello")
|
.text("Hello")
|
||||||
.build()
|
.build()
|
||||||
|
@ -106,14 +117,17 @@ impl cosmic::Application for App {
|
||||||
type Executor = executor::Default;
|
type Executor = executor::Default;
|
||||||
type Flags = Cli;
|
type Flags = Cli;
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
const APP_ID: &'static str = "org.chriscochrun.Lumina";
|
const APP_ID: &'static str = "lumina";
|
||||||
fn core(&self) -> &Core {
|
fn core(&self) -> &Core {
|
||||||
&self.core
|
&self.core
|
||||||
}
|
}
|
||||||
fn core_mut(&mut self) -> &mut Core {
|
fn core_mut(&mut self) -> &mut Core {
|
||||||
&mut self.core
|
&mut self.core
|
||||||
}
|
}
|
||||||
fn init(core: Core, input: Self::Flags) -> (Self, Task<Self::Message>) {
|
fn init(
|
||||||
|
core: Core,
|
||||||
|
input: Self::Flags,
|
||||||
|
) -> (Self, Task<Self::Message>) {
|
||||||
debug!("init");
|
debug!("init");
|
||||||
let mut nav_model = nav_bar::Model::default();
|
let mut nav_model = nav_bar::Model::default();
|
||||||
|
|
||||||
|
@ -127,7 +141,9 @@ impl cosmic::Application for App {
|
||||||
}
|
}
|
||||||
let initial_slide = SlideBuilder::new()
|
let initial_slide = SlideBuilder::new()
|
||||||
.background(
|
.background(
|
||||||
PathBuf::from("/home/chris/vids/test/chosensmol.mp4")
|
PathBuf::from(
|
||||||
|
"/home/chris/vids/test/camprules2024.mp4",
|
||||||
|
)
|
||||||
.canonicalize()
|
.canonicalize()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
)
|
)
|
||||||
|
@ -167,7 +183,10 @@ impl cosmic::Application for App {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when a navigation item is selected.
|
/// Called when a navigation item is selected.
|
||||||
fn on_nav_select(&mut self, id: nav_bar::Id) -> Task<Self::Message> {
|
fn on_nav_select(
|
||||||
|
&mut self,
|
||||||
|
id: nav_bar::Id,
|
||||||
|
) -> Task<Self::Message> {
|
||||||
self.nav_model.activate(id);
|
self.nav_model.activate(id);
|
||||||
self.update_title()
|
self.update_title()
|
||||||
}
|
}
|
||||||
|
@ -184,7 +203,9 @@ impl cosmic::Application for App {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscription(&self) -> cosmic::iced_futures::Subscription<Self::Message> {
|
fn subscription(
|
||||||
|
&self,
|
||||||
|
) -> cosmic::iced_futures::Subscription<Self::Message> {
|
||||||
event::listen_with(|event, _, id| {
|
event::listen_with(|event, _, id| {
|
||||||
if let iced::Event::Window(window_event) = event {
|
if let iced::Event::Window(window_event) = event {
|
||||||
match window_event {
|
match window_event {
|
||||||
|
@ -211,20 +232,34 @@ impl cosmic::Application for App {
|
||||||
location: _,
|
location: _,
|
||||||
modifiers: _,
|
modifiers: _,
|
||||||
} => match key {
|
} => match key {
|
||||||
Key::Named(iced::keyboard::key::Named::ArrowRight) => {
|
Key::Named(
|
||||||
Some(Message::Present(ui::presenter::Message::NextSlide))
|
iced::keyboard::key::Named::ArrowRight,
|
||||||
}
|
) => Some(Message::Present(
|
||||||
Key::Named(iced::keyboard::key::Named::ArrowLeft) => {
|
ui::presenter::Message::NextSlide,
|
||||||
Some(Message::Present(ui::presenter::Message::NextSlide))
|
)),
|
||||||
}
|
Key::Named(
|
||||||
Key::Named(iced::keyboard::key::Named::Space) => {
|
iced::keyboard::key::Named::ArrowLeft,
|
||||||
Some(Message::Present(ui::presenter::Message::NextSlide))
|
) => Some(Message::Present(
|
||||||
}
|
ui::presenter::Message::NextSlide,
|
||||||
|
)),
|
||||||
|
Key::Named(
|
||||||
|
iced::keyboard::key::Named::Space,
|
||||||
|
) => Some(Message::Present(
|
||||||
|
ui::presenter::Message::NextSlide,
|
||||||
|
)),
|
||||||
Key::Character(key) => {
|
Key::Character(key) => {
|
||||||
if key == SmolStr::from("j") || key == SmolStr::from("l") {
|
if key == SmolStr::from("j")
|
||||||
Some(Message::Present(ui::presenter::Message::NextSlide))
|
|| key == SmolStr::from("l")
|
||||||
} else if key == SmolStr::from("k") || key == SmolStr::from("h") {
|
{
|
||||||
Some(Message::Present(ui::presenter::Message::PrevSlide))
|
Some(Message::Present(
|
||||||
|
ui::presenter::Message::NextSlide,
|
||||||
|
))
|
||||||
|
} else if key == SmolStr::from("k")
|
||||||
|
|| key == SmolStr::from("h")
|
||||||
|
{
|
||||||
|
Some(Message::Present(
|
||||||
|
ui::presenter::Message::PrevSlide,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -239,7 +274,10 @@ impl cosmic::Application for App {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, message: Message) -> cosmic::Task<cosmic::app::Message<Message>> {
|
fn update(
|
||||||
|
&mut self,
|
||||||
|
message: Message,
|
||||||
|
) -> cosmic::Task<cosmic::app::Message<Message>> {
|
||||||
match message {
|
match message {
|
||||||
Message::Present(message) => {
|
Message::Present(message) => {
|
||||||
self.presenter.update(message);
|
self.presenter.update(message);
|
||||||
|
@ -252,7 +290,8 @@ impl cosmic::Application for App {
|
||||||
Message::OpenWindow => {
|
Message::OpenWindow => {
|
||||||
let count = self.windows.len() + 1;
|
let count = self.windows.len() + 1;
|
||||||
|
|
||||||
let (id, spawn_window) = window::open(window::Settings {
|
let (id, spawn_window) =
|
||||||
|
window::open(window::Settings {
|
||||||
position: Position::Centered,
|
position: Position::Centered,
|
||||||
exit_on_close_request: count % 2 == 0,
|
exit_on_close_request: count % 2 == 0,
|
||||||
decorations: false,
|
decorations: false,
|
||||||
|
@ -260,9 +299,16 @@ impl cosmic::Application for App {
|
||||||
});
|
});
|
||||||
|
|
||||||
self.windows.push(id);
|
self.windows.push(id);
|
||||||
_ = self.set_window_title(format!("window_{}", count), id);
|
_ = self.set_window_title(
|
||||||
|
format!("window_{}", count),
|
||||||
|
id,
|
||||||
|
);
|
||||||
|
|
||||||
spawn_window.map(|id| cosmic::app::Message::App(Message::WindowOpened(id, None)))
|
spawn_window.map(|id| {
|
||||||
|
cosmic::app::Message::App(Message::WindowOpened(
|
||||||
|
id, None,
|
||||||
|
))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
Message::CloseWindow(id) => window::close(id),
|
Message::CloseWindow(id) => window::close(id),
|
||||||
Message::WindowOpened(id, _) => {
|
Message::WindowOpened(id, _) => {
|
||||||
|
@ -270,7 +316,11 @@ impl cosmic::Application for App {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::WindowClosed(id) => {
|
Message::WindowClosed(id) => {
|
||||||
let window = self.windows.iter().position(|w| *w == id).unwrap();
|
let window = self
|
||||||
|
.windows
|
||||||
|
.iter()
|
||||||
|
.position(|w| *w == id)
|
||||||
|
.unwrap();
|
||||||
self.windows.remove(window);
|
self.windows.remove(window);
|
||||||
// This closes the app if using the cli example
|
// This closes the app if using the cli example
|
||||||
if self.windows.len() == 0 {
|
if self.windows.len() == 0 {
|
||||||
|
@ -286,11 +336,54 @@ impl cosmic::Application for App {
|
||||||
fn view(&self) -> Element<Message> {
|
fn view(&self) -> Element<Message> {
|
||||||
let text = text!("This is frodo").size(20);
|
let text = text!("This is frodo").size(20);
|
||||||
let text = Container::new(text).center(Length::Fill);
|
let text = Container::new(text).center(Length::Fill);
|
||||||
let image =
|
let slide = self
|
||||||
Container::new(image("/home/chris/pics/frodo.jpg").content_fit(ContentFit::Cover))
|
.presenter
|
||||||
.center(Length::FillPortion(2));
|
.slides
|
||||||
let stack = stack!(image, text).width(Length::Fill).height(Length::Fill);
|
.get(self.presenter.current_slide as usize)
|
||||||
stack.into()
|
.unwrap();
|
||||||
|
let container = match slide.background().kind {
|
||||||
|
crate::BackgroundKind::Image => Container::new(
|
||||||
|
image("/home/chris/pics/frodo.jpg")
|
||||||
|
.content_fit(ContentFit::Cover)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.height(Length::Fill),
|
||||||
|
),
|
||||||
|
crate::BackgroundKind::Video => {
|
||||||
|
if let Some(video) = &self.presenter.video {
|
||||||
|
Container::new(
|
||||||
|
VideoPlayer::new(&video)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.height(Length::Fill)
|
||||||
|
.content_fit(ContentFit::Contain),
|
||||||
|
)
|
||||||
|
.center(Length::Fill)
|
||||||
|
} else {
|
||||||
|
Container::new(Space::new(0, 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let preview = stack!(container, text);
|
||||||
|
let icon_left = icon::from_name("arrow-left").size(50).scale(2).prefer_svg(true);
|
||||||
|
debug!(?icon_left);
|
||||||
|
let icon_right = icon::from_name("arrow-right").size(50).scale(2).prefer_svg(true);
|
||||||
|
debug!(?icon_right);
|
||||||
|
let row = row![
|
||||||
|
Container::new(
|
||||||
|
button::icon(icon_left)
|
||||||
|
.width(Length::Fill)
|
||||||
|
)
|
||||||
|
.center(Length::FillPortion(1)),
|
||||||
|
preview.width(Length::FillPortion(3)),
|
||||||
|
Container::new(
|
||||||
|
button::icon(icon_right)
|
||||||
|
.width(Length::Fill)
|
||||||
|
)
|
||||||
|
.center(Length::FillPortion(1)),
|
||||||
|
]
|
||||||
|
.width(Length::Fill)
|
||||||
|
.height(Length::Fill)
|
||||||
|
.spacing(20);
|
||||||
|
row.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
// View for presentation
|
// View for presentation
|
||||||
|
@ -301,20 +394,6 @@ impl cosmic::Application for App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// struct VidPlayer<'a, Message, Theme, Renderer>(VideoPlayer<'a, Message, Theme, Renderer>);
|
|
||||||
|
|
||||||
// impl<'a, Message, Theme, Renderer> Into<Element<'a, Message>>
|
|
||||||
// for VidPlayer<'a, Message, Theme, Renderer>
|
|
||||||
// where
|
|
||||||
// Renderer: 'a + iced_wgpu::primitive::Renderer,
|
|
||||||
// Message: 'a + Clone,
|
|
||||||
// Theme: 'a,
|
|
||||||
// {
|
|
||||||
// fn into(self) -> Element<'a, Message> {
|
|
||||||
// Element::new(&self)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
impl App
|
impl App
|
||||||
where
|
where
|
||||||
Self: cosmic::Application,
|
Self: cosmic::Application,
|
||||||
|
@ -346,7 +425,9 @@ 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| cosmic::app::Message::App(Message::WindowOpened(id, None)))
|
spawn_window.map(|id| {
|
||||||
|
cosmic::app::Message::App(Message::WindowOpened(id, None))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,7 +444,9 @@ fn test_slide<'a>() -> Element<'a, Message> {
|
||||||
let font = Font::with_name("Noto Sans");
|
let font = Font::with_name("Noto Sans");
|
||||||
let stack = stack!(
|
let stack = stack!(
|
||||||
image(slide.background().path.clone()),
|
image(slide.background().path.clone()),
|
||||||
text(slide.text()).size(slide.font_size() as u16).font(font)
|
text(slide.text())
|
||||||
|
.size(slide.font_size() as u16)
|
||||||
|
.font(font)
|
||||||
);
|
);
|
||||||
|
|
||||||
stack.into()
|
stack.into()
|
||||||
|
@ -372,81 +455,6 @@ fn test_slide<'a>() -> Element<'a, Message> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn build_slide<'a>(
|
|
||||||
// lisp_data: &Value,
|
|
||||||
// layer: LayerContainer<'a, Message, Renderer>,
|
|
||||||
// ) -> LayerContainer<'a, Message, Renderer> {
|
|
||||||
// let current_symbol;
|
|
||||||
// // let current_element;
|
|
||||||
// let slide_builder = SlideBuilder::new();
|
|
||||||
// for atom in lisp_data.list_iter().unwrap() {
|
|
||||||
// match atom {
|
|
||||||
// Value::Symbol(symbol) => {
|
|
||||||
// let symbol = atom.as_symbol().unwrap();
|
|
||||||
// debug!(symbol);
|
|
||||||
// match symbol {
|
|
||||||
// "slide" => {
|
|
||||||
// current_symbol = "slide";
|
|
||||||
// debug!("I am a slide");
|
|
||||||
// return layer;
|
|
||||||
// }
|
|
||||||
// "song" => {
|
|
||||||
// current_symbol = "song";
|
|
||||||
// debug!("I am a song");
|
|
||||||
// return layer;
|
|
||||||
// }
|
|
||||||
// "image" => {
|
|
||||||
// current_symbol = "image";
|
|
||||||
// debug!("I am an image");
|
|
||||||
// return layer;
|
|
||||||
// }
|
|
||||||
// "text" => {
|
|
||||||
// current_symbol = "text";
|
|
||||||
// debug!("I am some text");
|
|
||||||
// return layer;
|
|
||||||
// }
|
|
||||||
// _ => {
|
|
||||||
// error!("We shouldn't get here");
|
|
||||||
// return layer;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return layer;
|
|
||||||
// }
|
|
||||||
// Value::Keyword(keyword) => {
|
|
||||||
// debug!(keyword);
|
|
||||||
// return layer;
|
|
||||||
// }
|
|
||||||
// Value::Cons(cons) => {
|
|
||||||
// debug!(?cons);
|
|
||||||
// let stack = build_slide(cons.car(), layer);
|
|
||||||
// return stack;
|
|
||||||
// }
|
|
||||||
// Value::String(string) => {
|
|
||||||
// debug!(string);
|
|
||||||
// return layer;
|
|
||||||
// }
|
|
||||||
// Value::Bool(b) => {
|
|
||||||
// debug!(b);
|
|
||||||
// return layer;
|
|
||||||
// }
|
|
||||||
// Value::Number(int) => {
|
|
||||||
// debug!(?int);
|
|
||||||
// return layer;
|
|
||||||
// }
|
|
||||||
// _ => {
|
|
||||||
// debug!("idk");
|
|
||||||
// return layer;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// layer
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn create_image(item: Value) -> Element<Message> {
|
|
||||||
// // We expect this value to look like (image :source "./something.jpg")
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,11 @@ use std::path::PathBuf;
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
dialog::ashpd::url::Url,
|
dialog::ashpd::url::Url,
|
||||||
iced::{widget::text, ContentFit, Length},
|
iced::{widget::text, ContentFit, Length},
|
||||||
iced_widget::stack,
|
iced_widget::{row, stack},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widget::{container, image, Container, Space},
|
widget::{
|
||||||
|
button, container, icon::Named, image, Container, Space,
|
||||||
|
},
|
||||||
Task,
|
Task,
|
||||||
};
|
};
|
||||||
use iced_video_player::{Video, VideoPlayer};
|
use iced_video_player::{Video, VideoPlayer};
|
||||||
|
@ -16,9 +18,9 @@ use crate::core::slide::Slide;
|
||||||
|
|
||||||
// #[derive(Default, Clone, Debug)]
|
// #[derive(Default, Clone, Debug)]
|
||||||
pub(crate) struct Presenter {
|
pub(crate) struct Presenter {
|
||||||
slides: Vec<Slide>,
|
pub slides: Vec<Slide>,
|
||||||
current_slide: i16,
|
pub current_slide: i16,
|
||||||
video: Option<Video>,
|
pub video: Option<Video>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -53,7 +55,10 @@ impl Presenter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, message: Message) -> Task<cosmic::app::Message<Message>> {
|
pub fn update(
|
||||||
|
&mut self,
|
||||||
|
message: Message,
|
||||||
|
) -> Task<cosmic::app::Message<Message>> {
|
||||||
match message {
|
match message {
|
||||||
Message::NextSlide => {
|
Message::NextSlide => {
|
||||||
debug!("next slide");
|
debug!("next slide");
|
||||||
|
|
Loading…
Reference in a new issue