allowing option_if_let_else and fixing a lot of them
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:
Chris Cochrun 2026-02-16 15:07:20 -06:00
parent 40ae229391
commit 01bffe2014
7 changed files with 492 additions and 495 deletions

View file

@ -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 },

View file

@ -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();

View file

@ -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(

View file

@ -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);

View file

@ -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)

View file

@ -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)),