allowing option_if_let_else and fixing a lot of them
All checks were successful
/ test (push) Successful in 5m58s
All checks were successful
/ test (push) Successful in 5m58s
I was misunderstanding how to handle the references in these closure contexts again. But now some of that code is a bit more idiomatic/readable
This commit is contained in:
parent
40ae229391
commit
01bffe2014
7 changed files with 492 additions and 495 deletions
84
src/main.rs
84
src/main.rs
|
|
@ -1,4 +1,4 @@
|
|||
#![allow(clippy::option_if_let_else)]
|
||||
#![allow(clippy::missing_panics_doc)]
|
||||
use clap::Parser;
|
||||
use core::service_items::ServiceItem;
|
||||
use core::slide::{
|
||||
|
|
@ -299,18 +299,13 @@ impl cosmic::Application for App {
|
|||
|
||||
let (config_handler, settings) = (input.1, input.2);
|
||||
|
||||
let items = if let Some(file) = input.0.file {
|
||||
let items = input.0.file.map_or_else(Vec::new, |file| {
|
||||
match read_to_string(file) {
|
||||
Ok(lisp) => {
|
||||
let mut service_items = vec![];
|
||||
let lisp = crisp::reader::read(&lisp);
|
||||
match lisp {
|
||||
Value::List(vec) => {
|
||||
// let items = vec
|
||||
// .into_par_iter()
|
||||
// .map(|value| parse_lisp(value))
|
||||
// .collect();
|
||||
// slide_vector.append(items);
|
||||
for value in vec {
|
||||
let mut inner_vector =
|
||||
parse_lisp(value);
|
||||
|
|
@ -327,9 +322,7 @@ impl cosmic::Application for App {
|
|||
vec![]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
});
|
||||
|
||||
let items: Vec<ServiceItem> = items
|
||||
.into_par_iter()
|
||||
|
|
@ -1101,14 +1094,11 @@ impl cosmic::Application for App {
|
|||
} else {
|
||||
// debug!("Change slide to previous items slides");
|
||||
let previous_item_slides_length =
|
||||
if let Some(item) = self
|
||||
.service
|
||||
self.service
|
||||
.get(item_index - 1)
|
||||
{
|
||||
item.slides.len()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
.map_or(0, |item| {
|
||||
item.slides.len()
|
||||
});
|
||||
self.current_item = (
|
||||
item_index - 1,
|
||||
previous_item_slides_length - 1,
|
||||
|
|
@ -1229,11 +1219,7 @@ impl cosmic::Application for App {
|
|||
})
|
||||
}
|
||||
Message::CloseWindow(id) => {
|
||||
if let Some(id) = id {
|
||||
window::close(id)
|
||||
} else {
|
||||
Task::none()
|
||||
}
|
||||
id.map_or_else(Task::none, window::close)
|
||||
}
|
||||
Message::WindowOpened(id) => {
|
||||
debug!(?id, "Window opened");
|
||||
|
|
@ -1454,7 +1440,7 @@ impl cosmic::Application for App {
|
|||
Task::none()
|
||||
}
|
||||
Message::Search(query) => {
|
||||
self.search_query = query.clone();
|
||||
self.search_query.clone_from(&query);
|
||||
self.search(query)
|
||||
}
|
||||
Message::UpdateSearchResults(items) => {
|
||||
|
|
@ -1616,24 +1602,27 @@ impl cosmic::Application for App {
|
|||
);
|
||||
|
||||
let video_button_icon =
|
||||
if let Some(video) = &self.presenter.video {
|
||||
let (icon_name, tooltip) = if video.paused() {
|
||||
("media-play", "Play")
|
||||
} else {
|
||||
("media-pause", "Pause")
|
||||
};
|
||||
button::icon(icon::from_name(icon_name))
|
||||
.tooltip(tooltip)
|
||||
.on_press(Message::Present(
|
||||
presenter::Message::StartVideo,
|
||||
))
|
||||
} else {
|
||||
button::icon(icon::from_name("media-play"))
|
||||
.tooltip("Play")
|
||||
.on_press(Message::Present(
|
||||
presenter::Message::StartVideo,
|
||||
))
|
||||
};
|
||||
self.presenter.video.as_ref().map_or_else(
|
||||
|| {
|
||||
button::icon(icon::from_name("media-play"))
|
||||
.tooltip("Play")
|
||||
.on_press(Message::Present(
|
||||
presenter::Message::StartVideo,
|
||||
))
|
||||
},
|
||||
|video| {
|
||||
let (icon_name, tooltip) = if video.paused() {
|
||||
("media-play", "Play")
|
||||
} else {
|
||||
("media-pause", "Pause")
|
||||
};
|
||||
button::icon(icon::from_name(icon_name))
|
||||
.tooltip(tooltip)
|
||||
.on_press(Message::Present(
|
||||
presenter::Message::StartVideo,
|
||||
))
|
||||
},
|
||||
);
|
||||
|
||||
let slide_preview = column![
|
||||
Space::with_height(Length::Fill),
|
||||
|
|
@ -1675,13 +1664,10 @@ impl cosmic::Application for App {
|
|||
|
||||
let library = if self.library_open {
|
||||
Container::new(
|
||||
Container::new(
|
||||
if let Some(library) = &self.library {
|
||||
library.view().map(Message::Library)
|
||||
} else {
|
||||
Element::from(Space::new(0, 0))
|
||||
},
|
||||
)
|
||||
Container::new(self.library.as_ref().map_or_else(
|
||||
|| Element::from(Space::new(0, 0)),
|
||||
|library| library.view().map(Message::Library),
|
||||
))
|
||||
.style(nav_bar_style),
|
||||
)
|
||||
.padding(space_s)
|
||||
|
|
@ -1822,7 +1808,7 @@ where
|
|||
.map(|id| cosmic::Action::App(Message::WindowOpened(id)))
|
||||
}
|
||||
|
||||
fn search(&mut self, query: String) -> Task<Message> {
|
||||
fn search(&self, query: String) -> Task<Message> {
|
||||
self.library.clone().map_or_else(Task::none, |library| {
|
||||
Task::perform(
|
||||
async move { library.search_items(query).await },
|
||||
|
|
|
|||
|
|
@ -137,6 +137,9 @@ impl<'a> Library {
|
|||
self.song_library.get_item(index)
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn update(&'a mut self, message: Message) -> Action {
|
||||
match message {
|
||||
Message::None => (),
|
||||
|
|
@ -652,6 +655,7 @@ impl<'a> Library {
|
|||
Message::AddFiles(items) => {
|
||||
let mut tasks = Vec::new();
|
||||
let last_item = &items.last();
|
||||
|
||||
let after_task = match last_item {
|
||||
Some(ServiceItemKind::Image(_image)) => {
|
||||
Task::done(Message::OpenItem(Some((
|
||||
|
|
@ -896,6 +900,7 @@ impl<'a> Library {
|
|||
container(library_dnd).padding(2).into()
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn library_item<T>(
|
||||
&'a self,
|
||||
model: &'a Model<T>,
|
||||
|
|
@ -953,18 +958,28 @@ impl<'a> Library {
|
|||
.style(|t| {
|
||||
container::Style::default()
|
||||
.background({
|
||||
match self.library_hovered {
|
||||
Some(lib) => Background::Color(
|
||||
if lib == model.kind {
|
||||
t.cosmic().button.hover.into()
|
||||
} else {
|
||||
t.cosmic().button.base.into()
|
||||
},
|
||||
),
|
||||
None => Background::Color(
|
||||
t.cosmic().button.base.into(),
|
||||
),
|
||||
}
|
||||
self.library_hovered.map_or_else(
|
||||
|| {
|
||||
Background::Color(
|
||||
t.cosmic().button.base.into(),
|
||||
)
|
||||
},
|
||||
|library| {
|
||||
Background::Color(
|
||||
if library == model.kind {
|
||||
t.cosmic()
|
||||
.button
|
||||
.hover
|
||||
.into()
|
||||
} else {
|
||||
t.cosmic()
|
||||
.button
|
||||
.base
|
||||
.into()
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
.border(Border::default().rounded(
|
||||
t.cosmic().corner_radii.radius_s,
|
||||
|
|
@ -987,6 +1002,7 @@ impl<'a> Library {
|
|||
column({
|
||||
model.items.iter().enumerate().map(
|
||||
|(index, item)| {
|
||||
let i32_index = i32::try_from(index).expect("shouldn't be negative");
|
||||
let kind = model.kind;
|
||||
let visual_item = self
|
||||
.single_item(index, item, model)
|
||||
|
|
@ -997,21 +1013,21 @@ impl<'a> Library {
|
|||
let mouse_area = mouse_area.on_enter(Message::HoverItem(
|
||||
Some((
|
||||
model.kind,
|
||||
index as i32,
|
||||
i32_index ,
|
||||
)),
|
||||
))
|
||||
.on_double_click(
|
||||
Message::OpenItem(Some((
|
||||
model.kind,
|
||||
index as i32,
|
||||
i32_index,
|
||||
))),
|
||||
)
|
||||
.on_right_press(Message::OpenContext(index as i32))
|
||||
.on_right_press(Message::OpenContext(i32_index ))
|
||||
.on_exit(Message::HoverItem(None))
|
||||
.on_press(Message::SelectItem(
|
||||
Some((
|
||||
model.kind,
|
||||
index as i32,
|
||||
i32_index,
|
||||
)),
|
||||
));
|
||||
|
||||
|
|
@ -1038,7 +1054,7 @@ impl<'a> Library {
|
|||
)
|
||||
}})
|
||||
.drag_content(move || {
|
||||
KindWrapper((kind, index as i32))
|
||||
KindWrapper((kind, i32_index))
|
||||
})
|
||||
.into()
|
||||
},
|
||||
|
|
@ -1065,6 +1081,7 @@ impl<'a> Library {
|
|||
column![library_button, lib_container].into()
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn single_item<T>(
|
||||
&'a self,
|
||||
index: usize,
|
||||
|
|
@ -1084,30 +1101,28 @@ impl<'a> Library {
|
|||
.center_x(Length::Fill);
|
||||
let subtext = container(responsive(move |size| {
|
||||
let color: Color = if item.background().is_some() {
|
||||
if let Some(items) = &self.selected_items {
|
||||
if items.contains(&(model.kind, index as i32)) {
|
||||
theme::active().cosmic().control_0().into()
|
||||
} else {
|
||||
theme::active()
|
||||
.cosmic()
|
||||
.accent_text_color()
|
||||
.into()
|
||||
}
|
||||
if let Some(items) = &self.selected_items
|
||||
&& items.contains(&(
|
||||
model.kind,
|
||||
i32::try_from(index)
|
||||
.expect("Should never be negative"),
|
||||
))
|
||||
{
|
||||
theme::active().cosmic().control_0().into()
|
||||
} else {
|
||||
theme::active()
|
||||
.cosmic()
|
||||
.accent_text_color()
|
||||
.into()
|
||||
}
|
||||
} 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()
|
||||
.cosmic()
|
||||
.destructive_text_color()
|
||||
.into()
|
||||
}
|
||||
} else if let Some(items) = &self.selected_items
|
||||
&& items.contains(&(
|
||||
model.kind,
|
||||
i32::try_from(index)
|
||||
.expect("Should never be negative"),
|
||||
))
|
||||
{
|
||||
theme::active().cosmic().control_0().into()
|
||||
} else {
|
||||
theme::active()
|
||||
.cosmic()
|
||||
|
|
@ -1135,15 +1150,16 @@ impl<'a> Library {
|
|||
.style(move |t| {
|
||||
container::Style::default()
|
||||
.background(Background::Color(
|
||||
if let Some(items) = &self.selected_items {
|
||||
if items.contains(&(model.kind, index as i32))
|
||||
{
|
||||
if let Some(items) = &self.selected_items
|
||||
&& let Ok(index) = i32::try_from(index)
|
||||
{
|
||||
if items.contains(&(model.kind, index)) {
|
||||
t.cosmic().accent.selected.into()
|
||||
} else if let Some((library, hovered)) =
|
||||
self.hovered_item
|
||||
{
|
||||
if model.kind == library
|
||||
&& hovered == index as i32
|
||||
&& hovered == index
|
||||
{
|
||||
t.cosmic().button.hover.into()
|
||||
} else {
|
||||
|
|
@ -1154,10 +1170,9 @@ impl<'a> Library {
|
|||
}
|
||||
} else if let Some((library, hovered)) =
|
||||
self.hovered_item
|
||||
&& let Ok(index) = i32::try_from(index)
|
||||
{
|
||||
if model.kind == library
|
||||
&& hovered == index as i32
|
||||
{
|
||||
if model.kind == library && hovered == index {
|
||||
t.cosmic().button.hover.into()
|
||||
} else {
|
||||
t.cosmic().button.base.into()
|
||||
|
|
@ -1209,45 +1224,36 @@ impl<'a> Library {
|
|||
query: String,
|
||||
) -> Vec<ServiceItemKind> {
|
||||
let query = query.to_lowercase();
|
||||
let mut items: Vec<ServiceItemKind> = self
|
||||
let items = self
|
||||
.song_library
|
||||
.items
|
||||
.clone()
|
||||
.into_iter()
|
||||
.iter()
|
||||
.filter(|song| song.title.to_lowercase().contains(&query))
|
||||
.map(ServiceItemKind::Song)
|
||||
.collect();
|
||||
let videos: Vec<ServiceItemKind> = self
|
||||
.map(|song| ServiceItemKind::Song(song.clone()));
|
||||
let videos = self
|
||||
.video_library
|
||||
.items
|
||||
.clone()
|
||||
.into_iter()
|
||||
.iter()
|
||||
.filter(|vid| vid.title.to_lowercase().contains(&query))
|
||||
.map(ServiceItemKind::Video)
|
||||
.collect();
|
||||
let images: Vec<ServiceItemKind> = self
|
||||
.map(|video| ServiceItemKind::Video(video.clone()));
|
||||
let images = self
|
||||
.image_library
|
||||
.items
|
||||
.clone()
|
||||
.into_iter()
|
||||
.iter()
|
||||
.filter(|image| {
|
||||
image.title.to_lowercase().contains(&query)
|
||||
})
|
||||
.map(ServiceItemKind::Image)
|
||||
.collect();
|
||||
let presentations: Vec<ServiceItemKind> = self
|
||||
.map(|image| ServiceItemKind::Image(image.clone()));
|
||||
let presentations = self
|
||||
.presentation_library
|
||||
.items
|
||||
.clone()
|
||||
.into_iter()
|
||||
.iter()
|
||||
.filter(|pres| pres.title.to_lowercase().contains(&query))
|
||||
.map(ServiceItemKind::Presentation)
|
||||
.collect();
|
||||
items.extend(videos);
|
||||
items.extend(images);
|
||||
items.extend(presentations);
|
||||
.map(|pres| ServiceItemKind::Presentation(pres.clone()));
|
||||
let items = items.chain(videos);
|
||||
let items = items.chain(images);
|
||||
let items = items.chain(presentations);
|
||||
let mut items: Vec<(usize, ServiceItemKind)> = items
|
||||
.into_iter()
|
||||
.map(|item| {
|
||||
(
|
||||
levenshtein::distance(
|
||||
|
|
@ -1288,6 +1294,7 @@ impl<'a> Library {
|
|||
self.modifiers_pressed = modifiers;
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn delete_items(&mut self) -> Action {
|
||||
// Need to make this function collect tasks to be run off of
|
||||
// who should be deleted
|
||||
|
|
@ -1482,14 +1489,20 @@ async fn add_presentations() -> Option<Vec<Presentation>> {
|
|||
}
|
||||
|
||||
async fn add_db() -> Result<SqlitePool> {
|
||||
let mut data = dirs::data_local_dir().unwrap();
|
||||
let mut data = dirs::data_local_dir()
|
||||
.expect("Should always find a data dir");
|
||||
data.push("lumina");
|
||||
data.push("library-db.sqlite3");
|
||||
let mut db_url = String::from("sqlite://");
|
||||
db_url.push_str(data.to_str().unwrap());
|
||||
db_url.push_str(
|
||||
data.to_str().expect("Should always be a file here"),
|
||||
);
|
||||
SqlitePool::connect(&db_url).await.into_diagnostic()
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
pub fn elide_text(text: impl AsRef<str>, width: f32) -> String {
|
||||
const CHAR_SIZE: f32 = 8.0;
|
||||
let text: String = text.as_ref().to_owned();
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ impl PresentationEditor {
|
|||
context_menu_id: None,
|
||||
}
|
||||
}
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn update(&mut self, message: Message) -> Action {
|
||||
match message {
|
||||
Message::ChangePresentation(presentation) => {
|
||||
|
|
@ -122,7 +123,7 @@ impl PresentationEditor {
|
|||
}
|
||||
}
|
||||
Message::ChangeTitle(title) => {
|
||||
self.title = title.clone();
|
||||
self.title.clone_from(&title);
|
||||
if let Some(presentation) = &self.presentation {
|
||||
let mut presentation = presentation.clone();
|
||||
presentation.title = title;
|
||||
|
|
@ -147,17 +148,17 @@ impl PresentationEditor {
|
|||
let task = Task::perform(
|
||||
pick_presentation(),
|
||||
move |presentation_result| {
|
||||
if let Ok(presentation) = presentation_result
|
||||
{
|
||||
let mut presentation =
|
||||
Presentation::from(presentation);
|
||||
presentation.id = presentation_id;
|
||||
Message::ChangePresentationFile(
|
||||
presentation,
|
||||
)
|
||||
} else {
|
||||
Message::None
|
||||
}
|
||||
presentation_result.map_or(
|
||||
Message::None,
|
||||
|presentation| {
|
||||
let mut presentation =
|
||||
Presentation::from(presentation);
|
||||
presentation.id = presentation_id;
|
||||
Message::ChangePresentationFile(
|
||||
presentation,
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
);
|
||||
return Action::Task(task);
|
||||
|
|
@ -198,14 +199,16 @@ impl PresentationEditor {
|
|||
Message::NextPage => {
|
||||
let next_index =
|
||||
self.current_slide_index.unwrap_or_default() + 1;
|
||||
let mut last_index =
|
||||
self.page_count.unwrap_or_default();
|
||||
if let Some(presentation) = self.presentation.as_ref()
|
||||
|
||||
let last_index = if let Some(presentation) =
|
||||
self.presentation.as_ref()
|
||||
&& let PresKind::Pdf { ending_index, .. } =
|
||||
presentation.kind
|
||||
{
|
||||
last_index = ending_index;
|
||||
}
|
||||
ending_index
|
||||
} else {
|
||||
self.page_count.unwrap_or_default()
|
||||
};
|
||||
|
||||
if next_index > last_index {
|
||||
return Action::None;
|
||||
|
|
@ -241,14 +244,16 @@ impl PresentationEditor {
|
|||
Message::PrevPage => {
|
||||
let previous_index =
|
||||
self.current_slide_index.unwrap_or_default() - 1;
|
||||
let mut first_index =
|
||||
self.page_count.unwrap_or_default();
|
||||
if let Some(presentation) = self.presentation.as_ref()
|
||||
|
||||
let first_index = if let Some(presentation) =
|
||||
self.presentation.as_ref()
|
||||
&& let PresKind::Pdf { starting_index, .. } =
|
||||
presentation.kind
|
||||
{
|
||||
first_index = starting_index;
|
||||
}
|
||||
starting_index
|
||||
} else {
|
||||
self.page_count.unwrap_or_default()
|
||||
};
|
||||
|
||||
if previous_index < first_index {
|
||||
return Action::None;
|
||||
|
|
@ -279,8 +284,9 @@ impl PresentationEditor {
|
|||
Message::ChangeSlide(index) => {
|
||||
self.current_slide =
|
||||
self.document.as_ref().and_then(|doc| {
|
||||
let page =
|
||||
doc.load_page(index as i32).ok()?;
|
||||
let page = doc
|
||||
.load_page(i32::try_from(index).ok()?)
|
||||
.ok()?;
|
||||
let matrix = Matrix::IDENTITY;
|
||||
let colorspace = Colorspace::device_rgb();
|
||||
let pixmap = page
|
||||
|
|
@ -298,13 +304,13 @@ impl PresentationEditor {
|
|||
pixmap.samples().to_vec(),
|
||||
))
|
||||
});
|
||||
self.current_slide_index = Some(index as i32);
|
||||
self.current_slide_index = i32::try_from(index).ok();
|
||||
}
|
||||
Message::HoverSlide(slide) => {
|
||||
self.hovered_slide = slide;
|
||||
}
|
||||
Message::ContextMenu(index) => {
|
||||
self.context_menu_id = Some(index as i32);
|
||||
self.context_menu_id = i32::try_from(index).ok();
|
||||
}
|
||||
Message::SplitBefore => {
|
||||
if let Ok((first, second)) = self.split_before() {
|
||||
|
|
@ -327,66 +333,74 @@ impl PresentationEditor {
|
|||
}
|
||||
|
||||
pub fn view(&self) -> Element<Message> {
|
||||
let presentation = if let Some(slide) = &self.current_slide {
|
||||
container(
|
||||
widget::image(slide)
|
||||
.content_fit(ContentFit::ScaleDown),
|
||||
)
|
||||
.style(|_| {
|
||||
container::background(Background::Color(
|
||||
cosmic::iced::Color::WHITE,
|
||||
))
|
||||
})
|
||||
} else {
|
||||
container(Space::new(0, 0))
|
||||
};
|
||||
let pdf_pages: Vec<Element<Message>> = if let Some(pages) =
|
||||
&self.slides
|
||||
{
|
||||
pages
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, page)| {
|
||||
let image = widget::image(page)
|
||||
.height(theme::spacing().space_xxxl * 3)
|
||||
.content_fit(ContentFit::ScaleDown);
|
||||
let slide = container(image).style(|_| {
|
||||
container::background(Background::Color(
|
||||
cosmic::iced::Color::WHITE,
|
||||
))
|
||||
});
|
||||
let clickable_slide = container(
|
||||
mouse_area(slide)
|
||||
.on_enter(Message::HoverSlide(Some(
|
||||
index as i32,
|
||||
)))
|
||||
.on_exit(Message::HoverSlide(None))
|
||||
.on_right_press(Message::ContextMenu(
|
||||
index,
|
||||
))
|
||||
.on_press(Message::ChangeSlide(index)),
|
||||
)
|
||||
.padding(theme::spacing().space_m)
|
||||
.clip(true)
|
||||
.class(
|
||||
if let Some(hovered_index) =
|
||||
self.hovered_slide
|
||||
{
|
||||
if index as i32 == hovered_index {
|
||||
theme::Container::Primary
|
||||
} else {
|
||||
theme::Container::Card
|
||||
}
|
||||
} else {
|
||||
theme::Container::Card
|
||||
},
|
||||
);
|
||||
clickable_slide.into()
|
||||
let presentation = self.current_slide.as_ref().map_or_else(
|
||||
|| container(Space::new(0, 0)),
|
||||
|slide| {
|
||||
container(
|
||||
widget::image(slide)
|
||||
.content_fit(ContentFit::ScaleDown),
|
||||
)
|
||||
.style(|_| {
|
||||
container::background(Background::Color(
|
||||
cosmic::iced::Color::WHITE,
|
||||
))
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![horizontal_space().into()]
|
||||
};
|
||||
},
|
||||
);
|
||||
let pdf_pages: Vec<Element<Message>> =
|
||||
self.slides.as_ref().map_or_else(
|
||||
|| vec![horizontal_space().into()],
|
||||
|pages| {
|
||||
pages
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, page)| {
|
||||
let image = widget::image(page)
|
||||
.height(
|
||||
theme::spacing().space_xxxl * 3,
|
||||
)
|
||||
.content_fit(ContentFit::ScaleDown);
|
||||
let slide = container(image).style(|_| {
|
||||
container::background(Background::Color(
|
||||
cosmic::iced::Color::WHITE,
|
||||
))
|
||||
});
|
||||
let clickable_slide = container(
|
||||
mouse_area(slide)
|
||||
.on_enter(Message::HoverSlide(
|
||||
i32::try_from(index).ok(),
|
||||
))
|
||||
.on_exit(Message::HoverSlide(
|
||||
None,
|
||||
))
|
||||
.on_right_press(
|
||||
Message::ContextMenu(index),
|
||||
)
|
||||
.on_press(Message::ChangeSlide(
|
||||
index,
|
||||
)),
|
||||
)
|
||||
.padding(theme::spacing().space_m)
|
||||
.clip(true)
|
||||
.class(self.hovered_slide.map_or(
|
||||
theme::Container::Card,
|
||||
|hovered_index| {
|
||||
if i32::try_from(index).is_ok_and(
|
||||
|index| {
|
||||
index == hovered_index
|
||||
},
|
||||
) {
|
||||
theme::Container::Primary
|
||||
} else {
|
||||
theme::Container::Card
|
||||
}
|
||||
},
|
||||
));
|
||||
clickable_slide.into()
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
);
|
||||
let pages_column = container(
|
||||
self.context_menu(
|
||||
scrollable(
|
||||
|
|
@ -485,7 +499,7 @@ impl PresentationEditor {
|
|||
presentation: &Presentation,
|
||||
) {
|
||||
self.presentation = Some(presentation.clone());
|
||||
self.title = presentation.title.clone();
|
||||
self.title.clone_from(&presentation.title);
|
||||
self.document =
|
||||
Document::open(&presentation.path.as_path()).ok();
|
||||
self.page_count = self
|
||||
|
|
@ -615,7 +629,9 @@ fn get_pages(
|
|||
pages
|
||||
.enumerate()
|
||||
.filter_map(|(index, page)| {
|
||||
if !range.contains(&(index as i32)) {
|
||||
if !range.contains(&i32::try_from(index).expect(
|
||||
"looking for a pdf index that is way too large",
|
||||
)) {
|
||||
return None;
|
||||
}
|
||||
let page = page.ok()?;
|
||||
|
|
@ -649,7 +665,9 @@ async fn pick_presentation() -> Result<PathBuf, PresentationError> {
|
|||
error!(?e);
|
||||
PresentationError::DialogClosed
|
||||
})
|
||||
.map(|file| file.url().to_file_path().unwrap())
|
||||
.map(|file| {
|
||||
file.url().to_file_path().expect("Should be a file here")
|
||||
})
|
||||
// rfd::AsyncFileDialog::new()
|
||||
// .set_title("Choose a background...")
|
||||
// .add_filter(
|
||||
|
|
|
|||
|
|
@ -255,35 +255,29 @@ impl Presenter {
|
|||
|
||||
pub fn with_items(items: Vec<ServiceItem>) -> Self {
|
||||
let video = {
|
||||
if let Some(item) = items.first() {
|
||||
if let Some(slide) = item.slides.first() {
|
||||
items.first().and_then(|item| {
|
||||
item.slides.first().and_then(|slide| {
|
||||
let path = slide.background().path.clone();
|
||||
if path.exists() {
|
||||
let url = Url::from_file_path(path).expect(
|
||||
"There should be a video file here",
|
||||
);
|
||||
let result = Video::new(&url);
|
||||
match result {
|
||||
Ok(mut v) => {
|
||||
v.set_paused(true);
|
||||
Some(v)
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Had an error creating the video object: {e}, likely the first slide isn't a video"
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
if !path.exists() {
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
let url = Url::from_file_path(path).expect(
|
||||
"There should be a video file here",
|
||||
);
|
||||
match Video::new(&url) {
|
||||
Ok(mut v) => {
|
||||
v.set_paused(true);
|
||||
Some(v)
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Had an error creating the video object: {e}, likely the first slide isn't a video"
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
};
|
||||
let total_slides: usize =
|
||||
items.iter().fold(0, |a, item| a + item.slides.len());
|
||||
|
|
@ -470,11 +464,9 @@ impl Presenter {
|
|||
// self.current_slide_index = slide;
|
||||
debug!("cloning slide...");
|
||||
self.current_slide = slide.clone();
|
||||
let font = if let Some(font) = slide.font() {
|
||||
font.get_name()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let font = slide
|
||||
.font()
|
||||
.map_or_else(String::new, |font| font.get_name());
|
||||
let _ = self.update(Message::ChangeFont(font));
|
||||
debug!("changing video now...");
|
||||
if !backgrounds_match {
|
||||
|
|
@ -508,6 +500,7 @@ impl Presenter {
|
|||
|
||||
debug!(target_item);
|
||||
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
let offset = AbsoluteOffset {
|
||||
x: {
|
||||
if target_item > 2 {
|
||||
|
|
@ -541,25 +534,8 @@ impl Presenter {
|
|||
}
|
||||
debug!("{:?}", new_audio);
|
||||
if new_audio.exists() {
|
||||
let old_audio = self.audio.clone();
|
||||
if let Some(current_audio) = old_audio
|
||||
&& current_audio != *new_audio
|
||||
{
|
||||
debug!(
|
||||
?new_audio,
|
||||
?current_audio,
|
||||
"audio needs to change"
|
||||
);
|
||||
self.audio = Some(new_audio);
|
||||
tasks.push(self.start_audio());
|
||||
} else {
|
||||
debug!(
|
||||
?new_audio,
|
||||
"could not find audio, need to change"
|
||||
);
|
||||
self.audio = Some(new_audio);
|
||||
tasks.push(self.start_audio());
|
||||
}
|
||||
self.audio = Some(new_audio);
|
||||
tasks.push(self.start_audio());
|
||||
} else {
|
||||
self.audio = None;
|
||||
self.update(Message::EndAudio);
|
||||
|
|
|
|||
|
|
@ -381,7 +381,10 @@ impl SongEditor {
|
|||
}
|
||||
}
|
||||
if let Some(font_size) = song.font_size {
|
||||
self.font_size = font_size as usize;
|
||||
self.font_size = usize::try_from(font_size)
|
||||
.expect(
|
||||
"There shouldn't be a negative font_size",
|
||||
);
|
||||
}
|
||||
if let Some(verse_order) = song.verse_order {
|
||||
self.verse_order = verse_order
|
||||
|
|
@ -876,29 +879,35 @@ impl SongEditor {
|
|||
}
|
||||
|
||||
pub fn view(&self) -> Element<Message> {
|
||||
let video_elements = if let Some(video) = &self.video {
|
||||
let play_button = button::icon(if video.paused() {
|
||||
icon::from_name("media-playback-start")
|
||||
} else {
|
||||
icon::from_name("media-playback-pause")
|
||||
})
|
||||
.on_press(Message::PauseVideo);
|
||||
let video_track = progress_bar(
|
||||
0.0..=video.duration().as_secs_f32(),
|
||||
video.position().as_secs_f32(),
|
||||
)
|
||||
.height(cosmic::theme::spacing().space_s)
|
||||
.width(Length::Fill);
|
||||
container(
|
||||
row![play_button, video_track]
|
||||
.align_y(Vertical::Center)
|
||||
.spacing(cosmic::theme::spacing().space_m),
|
||||
)
|
||||
.padding(cosmic::theme::spacing().space_s)
|
||||
.center_x(Length::FillPortion(2))
|
||||
} else {
|
||||
container(horizontal_space())
|
||||
};
|
||||
let video_elements: Element<Message> =
|
||||
self.video.as_ref().map_or_else(
|
||||
|| horizontal_space().into(),
|
||||
|video| {
|
||||
let play_button =
|
||||
button::icon(if video.paused() {
|
||||
icon::from_name("media-playback-start")
|
||||
} else {
|
||||
icon::from_name("media-playback-pause")
|
||||
})
|
||||
.on_press(Message::PauseVideo);
|
||||
let video_track = progress_bar(
|
||||
0.0..=video.duration().as_secs_f32(),
|
||||
video.position().as_secs_f32(),
|
||||
)
|
||||
.height(cosmic::theme::spacing().space_s)
|
||||
.width(Length::Fill);
|
||||
container(
|
||||
row![play_button, video_track]
|
||||
.align_y(Vertical::Center)
|
||||
.spacing(
|
||||
cosmic::theme::spacing().space_m,
|
||||
),
|
||||
)
|
||||
.padding(cosmic::theme::spacing().space_s)
|
||||
.center_x(Length::FillPortion(2))
|
||||
.into()
|
||||
},
|
||||
);
|
||||
let slide_preview = container(self.slide_preview())
|
||||
.width(Length::FillPortion(2));
|
||||
|
||||
|
|
@ -918,44 +927,42 @@ impl SongEditor {
|
|||
}
|
||||
|
||||
fn slide_preview(&self) -> Element<Message> {
|
||||
if let Some(slides) = &self.song_slides {
|
||||
let slides: Vec<Element<Message>> = slides
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, slide)| {
|
||||
container(
|
||||
slide_view(
|
||||
slide,
|
||||
if index == 0 {
|
||||
self.video.as_ref()
|
||||
} else {
|
||||
None
|
||||
},
|
||||
false,
|
||||
false,
|
||||
self.song_slides.as_ref().map_or_else(
|
||||
|| horizontal_space().into(),
|
||||
|slides| {
|
||||
let slides: Vec<Element<Message>> = slides
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, slide)| {
|
||||
container(
|
||||
slide_view(
|
||||
slide,
|
||||
if index == 0 {
|
||||
self.video.as_ref()
|
||||
} else {
|
||||
None
|
||||
},
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.map(|_| Message::None),
|
||||
)
|
||||
.map(|_| Message::None),
|
||||
)
|
||||
.height(250) // need to find out how to do this differently
|
||||
.center_x(Length::Fill)
|
||||
.padding([0, 20])
|
||||
.clip(true)
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
scrollable(
|
||||
cosmic::widget::column::with_children(slides)
|
||||
.spacing(theme::active().cosmic().space_l()),
|
||||
)
|
||||
.height(Length::Fill)
|
||||
.width(Length::Fill)
|
||||
.into()
|
||||
} else {
|
||||
horizontal_space().into()
|
||||
}
|
||||
// self.slide_state
|
||||
// .view(Font::with_name("Quicksand Bold"))
|
||||
// .map(|_s| Message::None)
|
||||
.height(250) // need to find out how to do this differently
|
||||
.center_x(Length::Fill)
|
||||
.padding([0, 20])
|
||||
.clip(true)
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
scrollable(
|
||||
cosmic::widget::column::with_children(slides)
|
||||
.spacing(theme::active().cosmic().space_l()),
|
||||
)
|
||||
.height(Length::Fill)
|
||||
.width(Length::Fill)
|
||||
.into()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn left_column(&self) -> Element<Message> {
|
||||
|
|
@ -987,62 +994,64 @@ impl SongEditor {
|
|||
// .on_input(Message::ChangeVerseOrder);
|
||||
|
||||
let verse_option_chips: Vec<Element<Message>> =
|
||||
if let Some(song) = &self.song {
|
||||
if let Some(verse_map) = &song.verse_map {
|
||||
verse_map
|
||||
.keys()
|
||||
.sorted()
|
||||
.map(|verse| {
|
||||
let verse = *verse;
|
||||
let chip = verse_chip(verse, None);
|
||||
let verse_chip_wrapped =
|
||||
RcElementWrapper::<Message>::new(
|
||||
chip,
|
||||
);
|
||||
Element::from(
|
||||
dnd_source::<Message, Box<VerseName>>(
|
||||
verse_chip_wrapped.clone(),
|
||||
)
|
||||
.on_start(Some(
|
||||
Message::DraggingChipStart,
|
||||
))
|
||||
.on_finish(Some(
|
||||
Message::DraggingChipStart,
|
||||
))
|
||||
.on_cancel(Some(
|
||||
Message::DraggingChipStart,
|
||||
))
|
||||
.drag_content(move || Box::new(verse))
|
||||
.drag_icon(
|
||||
move |_| {
|
||||
let state: tree::State =
|
||||
cosmic::widget::Widget::<
|
||||
Message,
|
||||
_,
|
||||
_,
|
||||
>::state(
|
||||
&verse_chip_wrapped
|
||||
);
|
||||
(
|
||||
Element::from(
|
||||
verse_chip_wrapped
|
||||
.clone(),
|
||||
)
|
||||
.map(|_| ()),
|
||||
state,
|
||||
Vector::new(-5.0, -15.0),
|
||||
self.song.as_ref().map_or_else(Vec::new, |song| {
|
||||
song.verse_map.as_ref().map_or_else(
|
||||
Vec::new,
|
||||
|verse_map| {
|
||||
verse_map
|
||||
.keys()
|
||||
.sorted()
|
||||
.map(|verse| {
|
||||
let verse = *verse;
|
||||
let chip = verse_chip(verse, None);
|
||||
let verse_chip_wrapped =
|
||||
RcElementWrapper::<Message>::new(
|
||||
chip,
|
||||
);
|
||||
Element::from(
|
||||
dnd_source::<
|
||||
Message,
|
||||
Box<VerseName>,
|
||||
>(
|
||||
verse_chip_wrapped.clone()
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
.on_start(Some(
|
||||
Message::DraggingChipStart,
|
||||
))
|
||||
.on_finish(Some(
|
||||
Message::DraggingChipStart,
|
||||
))
|
||||
.on_cancel(Some(
|
||||
Message::DraggingChipStart,
|
||||
))
|
||||
.drag_content(move || {
|
||||
Box::new(verse)
|
||||
})
|
||||
.drag_icon(move |_| {
|
||||
let state: tree::State =
|
||||
cosmic::widget::Widget::<
|
||||
Message,
|
||||
_,
|
||||
_,
|
||||
>::state(
|
||||
&verse_chip_wrapped,
|
||||
);
|
||||
(
|
||||
Element::from(
|
||||
verse_chip_wrapped
|
||||
.clone(),
|
||||
)
|
||||
.map(|_| ()),
|
||||
state,
|
||||
Vector::new(-5.0, -15.0),
|
||||
)
|
||||
}),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
let verse_options = container(
|
||||
scrollable(row(verse_option_chips).spacing(space_s))
|
||||
|
|
@ -1062,76 +1071,69 @@ impl SongEditor {
|
|||
})
|
||||
.on_press(Message::EditVerseOrder);
|
||||
|
||||
let verse_order_items: Vec<Element<Message>> = if let Some(
|
||||
song,
|
||||
) =
|
||||
&self.song
|
||||
{
|
||||
if let Some(verses) = &song.verses {
|
||||
verses
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, verse)| {
|
||||
let verse = *verse;
|
||||
let hovered_chip = self.hovered_verse_chip.filter(|hovered_index| hovered_index == &index);
|
||||
let mut chip =
|
||||
verse_chip(verse, hovered_chip).apply(mouse_area)
|
||||
.on_enter(Message::ChipHovered(Some(index)))
|
||||
.on_exit(Message::ChipHovered(None))
|
||||
.into();
|
||||
if let Some(hovered_chip) =
|
||||
self.hovered_dnd_verse_chip
|
||||
&& index == hovered_chip {
|
||||
let phantom_chip = horizontal_space().width(60).height(19)
|
||||
.apply(container)
|
||||
.padding(
|
||||
Padding::new(space_xxs.into())
|
||||
.right(space_s)
|
||||
.left(space_s),
|
||||
)
|
||||
.class(theme::Container::Custom(Box::new(move |t| {
|
||||
container::Style::default()
|
||||
.background(ContainerBackground::Color(
|
||||
Color::from(t.cosmic().secondary.base).scale_alpha(0.5)
|
||||
))
|
||||
.border(Border::default().rounded(space_m).width(2))
|
||||
})));
|
||||
chip = row![
|
||||
phantom_chip,
|
||||
chip
|
||||
]
|
||||
.spacing(space_s)
|
||||
let verse_order_items: Vec<Element<Message>> =
|
||||
self.song.as_ref().map_or_else(Vec::new, |song| {
|
||||
song.verses.as_ref().map_or_else(Vec::new, |verses| {
|
||||
verses
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, verse)| {
|
||||
let verse = *verse;
|
||||
let hovered_chip = self.hovered_verse_chip.filter(|hovered_index| hovered_index == &index);
|
||||
let mut chip =
|
||||
verse_chip(verse, hovered_chip).apply(mouse_area)
|
||||
.on_enter(Message::ChipHovered(Some(index)))
|
||||
.on_exit(Message::ChipHovered(None))
|
||||
.into();
|
||||
}
|
||||
let verse_chip_wrapped =
|
||||
RcElementWrapper::<Message>::new(chip);
|
||||
Element::from(
|
||||
dnd_destination(
|
||||
verse_chip_wrapped,
|
||||
vec!["application/verse".into()],
|
||||
if let Some(hovered_chip) =
|
||||
self.hovered_dnd_verse_chip
|
||||
&& index == hovered_chip {
|
||||
let phantom_chip = horizontal_space().width(60).height(19)
|
||||
.apply(container)
|
||||
.padding(
|
||||
Padding::new(space_xxs.into())
|
||||
.right(space_s)
|
||||
.left(space_s),
|
||||
)
|
||||
.class(theme::Container::Custom(Box::new(move |t| {
|
||||
container::Style::default()
|
||||
.background(ContainerBackground::Color(
|
||||
Color::from(t.cosmic().secondary.base).scale_alpha(0.5)
|
||||
))
|
||||
.border(Border::default().rounded(space_m).width(2))
|
||||
})));
|
||||
chip = row![
|
||||
phantom_chip,
|
||||
chip
|
||||
]
|
||||
.spacing(space_s)
|
||||
.into();
|
||||
}
|
||||
let verse_chip_wrapped =
|
||||
RcElementWrapper::<Message>::new(chip);
|
||||
Element::from(
|
||||
dnd_destination(
|
||||
verse_chip_wrapped,
|
||||
vec!["application/verse".into()],
|
||||
)
|
||||
.on_enter(move |x, y, mimes| {
|
||||
debug!(x, y, ?mimes);
|
||||
Message::ChipDndHovered(Some(index))
|
||||
})
|
||||
.on_leave(move || {
|
||||
Message::ChipDndHovered(None)
|
||||
})
|
||||
.on_finish(
|
||||
move |mime, data, action, _x, _y| {
|
||||
debug!(mime, ?data, ?action);
|
||||
Message::ChipDropped((index, data, mime))
|
||||
},
|
||||
),
|
||||
)
|
||||
.on_enter(move |x, y, mimes| {
|
||||
debug!(x, y, ?mimes);
|
||||
Message::ChipDndHovered(Some(index))
|
||||
})
|
||||
.on_leave(move || {
|
||||
Message::ChipDndHovered(None)
|
||||
})
|
||||
.on_finish(
|
||||
move |mime, data, action, _x, _y| {
|
||||
debug!(mime, ?data, ?action);
|
||||
Message::ChipDropped((index, data, mime))
|
||||
},
|
||||
),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
});
|
||||
|
||||
let verse_order_items = if self.dragging_verse_chip {
|
||||
Element::from(row(verse_order_items).spacing(space_s))
|
||||
|
|
@ -1214,27 +1216,28 @@ impl SongEditor {
|
|||
]
|
||||
.spacing(5);
|
||||
|
||||
let verse_list = if let Some(verse_list) = &self.verses {
|
||||
Element::from(
|
||||
column(verse_list.iter().enumerate().map(
|
||||
|(index, v)| {
|
||||
column![
|
||||
v.view().map(move |message| {
|
||||
Message::VerseEditorMessage((
|
||||
index, message,
|
||||
))
|
||||
}),
|
||||
divider::horizontal::heavy()
|
||||
]
|
||||
.spacing(space_m)
|
||||
.into()
|
||||
},
|
||||
))
|
||||
.spacing(space_m),
|
||||
)
|
||||
} else {
|
||||
Element::from(horizontal_space())
|
||||
};
|
||||
let verse_list = self.verses.as_ref().map_or_else(
|
||||
|| Element::from(horizontal_space()),
|
||||
|verse_list| {
|
||||
Element::from(
|
||||
column(verse_list.iter().enumerate().map(
|
||||
|(index, v)| {
|
||||
column![
|
||||
v.view().map(move |message| {
|
||||
Message::VerseEditorMessage((
|
||||
index, message,
|
||||
))
|
||||
}),
|
||||
divider::horizontal::heavy()
|
||||
]
|
||||
.spacing(space_m)
|
||||
.into()
|
||||
},
|
||||
))
|
||||
.spacing(space_m),
|
||||
)
|
||||
},
|
||||
);
|
||||
let verse_scroller = scrollable(
|
||||
verse_list
|
||||
.apply(container)
|
||||
|
|
|
|||
|
|
@ -89,14 +89,12 @@ impl VideoEditor {
|
|||
let task = Task::perform(
|
||||
pick_video(),
|
||||
move |video_result| {
|
||||
if let Ok(video) = video_result {
|
||||
video_result.map_or(Message::None, |video| {
|
||||
let mut video =
|
||||
videos::Video::from(video);
|
||||
video.id = video_id;
|
||||
Message::UpdateVideoFile(video)
|
||||
} else {
|
||||
Message::None
|
||||
}
|
||||
})
|
||||
},
|
||||
);
|
||||
return Action::Task(task);
|
||||
|
|
@ -111,29 +109,30 @@ impl VideoEditor {
|
|||
}
|
||||
|
||||
pub fn view(&self) -> Element<Message> {
|
||||
let video_elements = if let Some(video) = &self.video {
|
||||
let play_button = button::icon(if video.paused() {
|
||||
icon::from_name("media-playback-start")
|
||||
} else {
|
||||
icon::from_name("media-playback-pause")
|
||||
})
|
||||
.on_press(Message::PauseVideo);
|
||||
let video_track = progress_bar(
|
||||
0.0..=video.duration().as_secs_f32(),
|
||||
video.position().as_secs_f32(),
|
||||
)
|
||||
.height(cosmic::theme::spacing().space_s)
|
||||
.width(Length::Fill);
|
||||
container(
|
||||
row![play_button, video_track]
|
||||
.align_y(Vertical::Center)
|
||||
.spacing(cosmic::theme::spacing().space_m),
|
||||
)
|
||||
.padding(cosmic::theme::spacing().space_s)
|
||||
.center_x(Length::FillPortion(2))
|
||||
} else {
|
||||
container(horizontal_space())
|
||||
};
|
||||
let video_elements = self.video.as_ref().map_or_else(
|
||||
|| container(horizontal_space()),
|
||||
|video| {
|
||||
let play_button = button::icon(if video.paused() {
|
||||
icon::from_name("media-playback-start")
|
||||
} else {
|
||||
icon::from_name("media-playback-pause")
|
||||
})
|
||||
.on_press(Message::PauseVideo);
|
||||
let video_track = progress_bar(
|
||||
0.0..=video.duration().as_secs_f32(),
|
||||
video.position().as_secs_f32(),
|
||||
)
|
||||
.height(cosmic::theme::spacing().space_s)
|
||||
.width(Length::Fill);
|
||||
container(
|
||||
row![play_button, video_track]
|
||||
.align_y(Vertical::Center)
|
||||
.spacing(cosmic::theme::spacing().space_m),
|
||||
)
|
||||
.padding(cosmic::theme::spacing().space_s)
|
||||
.center_x(Length::FillPortion(2))
|
||||
},
|
||||
);
|
||||
|
||||
let video_player = self.video.as_ref().map_or_else(
|
||||
|| Element::from(Space::new(0, 0)),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue