Compare commits

...
Sign in to create a new pull request.

6 commits

Author SHA1 Message Date
54b681de7c A plan for updating the db and model better
Some checks failed
/ clippy (push) Failing after 4m46s
/ test (push) Failing after 5m40s
2026-04-06 16:50:44 -05:00
2e5fe439b0 fix some issues 2026-04-06 14:43:27 -05:00
69410e3b6e fixing lint issues and style issues
Some checks failed
/ clippy (push) Failing after 5m7s
/ test (push) Failing after 5m43s
2026-04-06 11:31:07 -05:00
896eda5b9d a working build of the new update
Some checks failed
/ clippy (push) Failing after 5m8s
/ test (push) Failing after 12m14s
2026-04-03 15:14:07 -05:00
b05f29d7b5 getting closer
Some checks failed
/ clippy (push) Failing after 13m8s
/ test (push) Failing after 5m59s
2026-04-03 11:23:13 -05:00
11cca05de4 working on updating
Some checks failed
/ clippy (push) Failing after 14m41s
/ test (push) Failing after 7m36s
2026-04-02 15:27:53 -05:00
21 changed files with 2308 additions and 2163 deletions

3693
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -54,11 +54,11 @@ serde_json = "1.0.149"
[dependencies.libcosmic]
git = "https://github.com/pop-os/libcosmic"
default-features = false
features = ["debug", "winit", "desktop", "winit_wgpu", "winit_tokio", "tokio", "wayland", "rfd", "dbus-config", "a11y", "wgpu", "multi-window", "process"]
features = ["debug", "winit", "desktop", "wayland", "tokio", "rfd", "dbus-config", "a11y", "wgpu", "multi-window", "process"]
[dependencies.iced_video_player]
git = "https://github.com/jackpot51/iced_video_player.git"
branch = "cosmic"
git = "https://github.com/wash2/iced_video_player.git"
branch = "iced-rebase"
features = ["wgpu"]
# [profile.dev]

View file

@ -12,6 +12,10 @@ This is working but the right click context menu is all the way on the edge of t
Let's build some tests that ensure that these functions are working for the models. Make sure the models are built in such a way as to make sure that they are testable and work fast for the user.
By making the db functions take the vector of items in the model, we can drain the model, pass an owned version of those items to the async db function(adding, updating, deleting, etc) and then return an updated list of the items back in the Result.
We should probably return a tuple with the original vector of items in case the db function fails somehow.
* TODO [#B] Font in the song editor doesn't always use the original version
There seems to be some issue with fontdb not able to decipher all the versions of some fonts that are OTF and then end up loading the wrong ones in some issues.

View file

@ -67,6 +67,7 @@
libGL
cargo-flamegraph
bacon
openssl
fontconfig
glib

View file

@ -114,10 +114,10 @@ pub async fn get_genius_lyrics(
root.inner_html()
})
.collect::<String>();
let lyrics = lyrics.find("[").map_or_else(
let lyrics = lyrics.find('[').map_or_else(
|| {
lyrics.find("</div></div></div>").map_or(
lyrics.clone(),
lyrics.find("</div></div></div>").map_or_else(
|| lyrics.clone(),
|position| {
lyrics.split_at(position + 18).1.to_string()
},

View file

@ -789,7 +789,7 @@ pub async fn remove_from_db(
.map(|_| ())
}
pub async fn add_song_to_db(
pub async fn new_song_from_db(
db: PoolConnection<Sqlite>,
) -> Result<Song> {
let mut song = Song::default();
@ -1357,7 +1357,7 @@ You saved my soul"
async fn fill_db(db: &SqlitePool) -> Result<()> {
for _ in 0..20 {
let conn = db.acquire().await.into_diagnostic()?;
let db_song = add_song_to_db(conn).await?;
let db_song = new_song_from_db(conn).await?;
let mut song = test_song();
song.id = db_song.id;
let conn = db.acquire().await.into_diagnostic()?;

View file

@ -10,7 +10,7 @@ use cosmic::cosmic_config::{Config, CosmicConfigEntry};
use cosmic::dialog::file_chooser::{open, save};
use cosmic::iced::alignment::Vertical;
use cosmic::iced::keyboard::{Key, Modifiers};
use cosmic::iced::window::{Mode, Position};
use cosmic::iced::window::Position;
use cosmic::iced::{
self, Background as IcedBackground, Border, Color, Length, event,
window,
@ -18,18 +18,19 @@ use cosmic::iced::{
use cosmic::iced_core::text::Wrapping;
use cosmic::iced_futures::Subscription;
use cosmic::iced_widget::{column, row, stack};
use cosmic::prelude::*;
use cosmic::widget::dnd_destination::dnd_destination;
use cosmic::widget::menu::key_bind::Modifier;
use cosmic::widget::menu::{ItemWidth, KeyBind};
use cosmic::widget::nav_bar::nav_bar_style;
use cosmic::widget::space::horizontal;
use cosmic::widget::tooltip::Position as TPosition;
use cosmic::widget::{
Container, divider, menu, settings, text_input,
};
use cosmic::widget::{
Space, button, context_menu, horizontal_space, mouse_area,
nav_bar, nav_bar_toggle, responsive, scrollable, search_input,
tooltip,
Space, button, context_menu, mouse_area, nav_bar, nav_bar_toggle,
responsive, scrollable, search_input, tooltip,
};
use cosmic::widget::{container, text};
use cosmic::widget::{icon, slider};
@ -699,6 +700,7 @@ impl cosmic::Application for App {
// debug!(?platform_specific);
None
}
iced::Event::InputMethod(_event) => todo!(),
}
}
event::Status::Captured => None,
@ -736,7 +738,7 @@ impl cosmic::Application for App {
row![
column![title, subtitle]
.spacing(space_xxs),
horizontal_space(),
horizontal(),
tooltip(
icon::from_name("add")
.symbolic(true).apply(button::icon)
@ -785,7 +787,9 @@ impl cosmic::Application for App {
.center_x(Length::Fill)
.align_top(Length::Fill);
let mouse_stack = stack!(
Space::new(Length::Fill, Length::Fill)
Space::new()
.height(Length::Fill)
.width(Length::Fill)
.apply(container)
.style(|_| {
container::background(
@ -824,7 +828,7 @@ impl cosmic::Application for App {
.padding(space_s)
.align_right(Length::Fill)
.align_top(60),
horizontal_space().height(space_xxl),
horizontal().height(space_xxl),
settings::section()
.title("Obs Settings")
.add(obs_socket)
@ -845,7 +849,9 @@ impl cosmic::Application for App {
.apply(container)
.padding([space_xxl, space_xxxl * 2]);
let mouse_stack = stack!(
Space::new(Length::Fill, Length::Fill)
Space::new()
.height(Length::Fill)
.width(Length::Fill)
.apply(container)
.style(|_| {
container::background(
@ -1239,7 +1245,7 @@ impl cosmic::Application for App {
if let Some(video) = &mut self.presenter.video {
video.set_muted(false);
}
window::change_mode(id, Mode::Fullscreen)
window::maximize(id, true)
} else {
Task::none()
}
@ -1290,10 +1296,12 @@ impl cosmic::Application for App {
)
}
Message::HoveredServiceItem(index) => {
debug!(index);
self.hovered_item = index;
Task::none()
}
Message::HoveredServiceDrop(index) => {
debug!(index);
self.hovered_dnd = index;
Task::none()
}
@ -1656,7 +1664,7 @@ impl cosmic::Application for App {
);
let slide_preview = column![
Space::with_height(Length::Fill),
Space::new().height(Length::Fill),
Container::new(
self.presenter.view_preview().map(Message::Present),
)
@ -1685,7 +1693,7 @@ impl cosmic::Application for App {
row![]
})
.center_x(Length::Fill),
Space::with_height(Length::Fill),
Space::new().height(Length::Fill)
]
.spacing(3);
@ -1696,7 +1704,7 @@ impl cosmic::Application for App {
let library = if self.library_open {
Container::new(
Container::new(self.library.as_ref().map_or_else(
|| Element::from(Space::new(0, 0)),
|| Element::from(Space::new()),
|library| library.view().map(Message::Library),
))
.style(nav_bar_style),
@ -1704,11 +1712,11 @@ impl cosmic::Application for App {
.padding(space_s)
.width(Length::FillPortion(2))
} else {
Container::new(horizontal_space().width(0))
Container::new(horizontal().width(0))
};
let editor = self.editor_mode.as_ref().map_or_else(
|| Element::from(Space::new(0, 0)),
|| Element::from(Space::new()),
|mode| match mode {
EditorMode::Song => {
self.song_editor.view().map(Message::SongEditor)
@ -1763,7 +1771,7 @@ impl cosmic::Application for App {
let preview_bar = if self.editor_mode.is_none() {
if self.service.is_empty() {
Container::new(horizontal_space())
Container::new(horizontal())
} else {
Container::new(
self.presenter
@ -1775,7 +1783,7 @@ impl cosmic::Application for App {
.center_y(180)
}
} else {
Container::new(horizontal_space())
Container::new(horizontal())
};
let main_area = self.editor_mode.as_ref().map_or_else(
@ -1788,7 +1796,7 @@ impl cosmic::Application for App {
if self.library_open {
library.width(Length::FillPortion(1))
} else {
container(Space::new(0, 0))
container(Space::new())
},
main_area.width(Length::FillPortion(4))
]
@ -1850,6 +1858,7 @@ where
},
)
})
// if let Some(library) = self.library.clone() {
// Task::perform(
// async move { library.search_items(query).await },
@ -1915,11 +1924,9 @@ where
) => self.update(Message::Present(
presenter::Message::PrevSlide,
)),
(Key::Named(iced::keyboard::key::Named::Space), _) => {
self.update(Message::Present(
presenter::Message::NextSlide,
))
}
(Key::Character(k), _) if k == *" " => self.update(
Message::Present(presenter::Message::NextSlide),
),
(Key::Character(k), _) if k == *"j" || k == *"l" => self
.update(Message::Present(
presenter::Message::NextSlide,
@ -2001,11 +2008,11 @@ where
let color = t.cosmic().accent_color();
cosmic::iced_widget::rule::Style {
color: color.into(),
width: 2,
snap: true,
radius: t.cosmic().corner_radii.radius_xs.into(),
fill_mode: cosmic::iced_widget::rule::FillMode::Full,
}
} ));
} )).width(2);
Container::new(column![divider, container].spacing(theme::spacing().space_s))
} else { container };
let mouse_area = mouse_area(visual_item)
@ -2145,7 +2152,7 @@ where
text::heading("Service List")
.center()
.width(Length::Fill),
iced::widget::horizontal_rule(1),
divider::horizontal::light(),
scrollable
]
.padding(10)

View file

@ -8,8 +8,8 @@ use cosmic::{
iced_widget::{column, row},
theme,
widget::{
self, Space, button, container, horizontal_space, icon, text,
text_input,
self, Space, button, container, icon, space::horizontal,
text, text_input,
},
};
use tracing::{debug, error, warn};
@ -94,7 +94,7 @@ impl ImageEditor {
#[must_use]
pub fn view(&self) -> Element<Message> {
let container = self.image.as_ref().map_or_else(
|| Space::new(0, 0).apply(container),
|| Space::new().apply(container),
|pic| widget::image(pic.path.clone()).apply(container),
);
let column = column![
@ -120,7 +120,7 @@ impl ImageEditor {
row![
text::body("Title:"),
title_box,
horizontal_space(),
horizontal(),
image_selector
]
.align_y(Vertical::Center)

View file

@ -12,9 +12,11 @@ use cosmic::{
theme,
widget::{
Container, DndSource, Space, button, container, context_menu,
dnd_destination, horizontal_space, icon,
divider, dnd_destination, icon,
menu::{self, Action as MenuAction},
mouse_area, responsive, row, scrollable, text, text_input,
mouse_area, responsive, row, scrollable,
space::{self, horizontal},
text, text_input,
},
};
use miette::{IntoDiagnostic, Result};
@ -32,7 +34,7 @@ use crate::core::{
update_presentation_in_db,
},
service_items::ServiceItem,
songs::{self, Song, add_song_to_db, update_song_in_db},
songs::{self, Song, new_song_from_db, update_song_in_db},
videos::{self, Video, add_video_to_db, update_video_in_db},
};
@ -169,17 +171,25 @@ impl<'a> Library {
self.library_open.unwrap_or(LibraryKind::Song);
match kind {
LibraryKind::Song => {
let _song = Song::default();
let task = Task::future(self.db.acquire()).and_then(move |db| {
Task::perform(add_song_to_db(db), move |res| {
match res {
Ok(song) => {
Message::AddSong(song)
},
Err(e) => {error!(?e, "couldn't add song to db"); Message::None}
}
let index = (self.song_library.items.len()
- 1)
as i32;
let task = Task::future(self.db.acquire())
.map_err(|e| {
miette::miette!("Database error: {e}")
})
});
.and_then(move |db| {
Task::perform(
new_song_from_db(db),
move |res| {
res.map(|song| {
Message::AddSong(song)
})
},
)
})
.map(|r| r.unwrap_or(Message::None));
return Action::Task(task);
}
LibraryKind::Video => {
@ -208,7 +218,7 @@ impl<'a> Library {
// Check if empty
let mut tasks = Vec::new();
if let Some(videos) = videos {
let len = videos.len();
// let len = videos.len();
for video in videos {
if let Err(e) =
self.video_library.add_item(video.clone())
@ -216,6 +226,9 @@ impl<'a> Library {
error!(?e);
}
let task = Task::future(self.db.acquire())
.map_err(|e| {
miette::miette!("Database error: {e}")
})
.and_then(move |db| {
Task::perform(
add_video_to_db(
@ -223,25 +236,16 @@ impl<'a> Library {
db,
),
move |res| {
debug!(
len,
index, "added to db"
);
if let Err(e) = res {
error!(?e);
}
if len == index {
debug!("open the pres");
res.map(|_| {
Message::OpenItem(Some((
LibraryKind::Video,
index as i32,
)))
} else {
Message::None
}
})
},
)
});
})
.map(|r| r.unwrap_or(Message::None));
tasks.push(task);
index += 1;
}
@ -265,23 +269,22 @@ impl<'a> Library {
error!(?e);
}
return Action::Task(
Task::future(self.db.acquire()).and_then(
move |db| {
Task::future(self.db.acquire())
.map_err(|e| {
miette::miette!("Database error: {e}")
})
.and_then(move |db| {
Task::perform(
add_presentation_to_db(
presentation.clone(),
db,
),
move |res| {
debug!("added to db");
if let Err(e) = res {
error!(?e);
}
Message::None
res.map(|_| Message::None)
},
)
},
),
})
.map(|r| r.unwrap_or(Message::None)),
);
}
}
@ -291,7 +294,7 @@ impl<'a> Library {
// Check if empty
let mut tasks = Vec::new();
if let Some(presentations) = presentations {
let len = presentations.len();
// let len = presentations.len();
for presentation in presentations {
if let Err(e) = self
.presentation_library
@ -302,32 +305,16 @@ impl<'a> Library {
let task = Task::future(
self.db.acquire(),
)
.map_err(|e| miette::miette!("Database error: {e}"))
.and_then(move |db| {
Task::perform(
add_presentation_to_db(
presentation.clone(),
db,
),
move |res| {
debug!(
len,
index, "added to db"
);
if let Err(e) = res {
error!(?e);
}
if len == index {
debug!("open the pres");
Message::OpenItem(Some((
LibraryKind::Presentation,
index as i32,
)))
} else {
Message::None
}
},
move |res| res.map(|_| Message::OpenItem(Some((LibraryKind::Presentation, index as i32))))
)
});
}).map(|r| r.unwrap_or(Message::None));
tasks.push(task);
index += 1;
}
@ -348,7 +335,7 @@ impl<'a> Library {
// Check if empty
let mut tasks = Vec::new();
if let Some(images) = images {
let len = images.len();
// let len = images.len();
for image in images {
if let Err(e) =
self.image_library.add_item(image.clone())
@ -356,6 +343,9 @@ impl<'a> Library {
error!(?e);
}
let task = Task::future(self.db.acquire())
.map_err(|e| {
miette::miette!("Database error: {e}")
})
.and_then(move |db| {
Task::perform(
add_image_to_db(
@ -363,25 +353,16 @@ impl<'a> Library {
db,
),
move |res| {
debug!(
len,
index, "added to db"
);
if let Err(e) = res {
error!(?e);
}
if len == index {
debug!("open the pres");
res.map(|_| {
Message::OpenItem(Some((
LibraryKind::Image,
index as i32,
)))
} else {
Message::None
}
})
},
)
});
})
.map(|r| r.unwrap_or(Message::None));
tasks.push(task);
index += 1;
}
@ -516,20 +497,17 @@ impl<'a> Library {
}
return Action::Task(
Task::future(self.db.acquire()).and_then(
move |conn| {
Task::future(self.db.acquire())
.map_err(|e| {
miette::miette!("Database error: {e}")
})
.and_then(move |conn| {
Task::perform(
update_song_in_db(song.clone(), conn),
|r| match r {
Ok(()) => Message::SongChanged,
Err(e) => {
error!(?e);
Message::None
}
},
|r| r.map(|_| Message::SongChanged),
)
},
),
})
.map(|r| r.unwrap_or(Message::None)),
);
}
Message::SongChanged => {
@ -566,23 +544,20 @@ impl<'a> Library {
}
return Action::Task(
Task::future(self.db.acquire()).and_then(
move |conn| {
Task::future(self.db.acquire())
.map_err(|e| {
miette::miette!("Database error: {e}")
})
.and_then(move |conn| {
Task::perform(
update_image_in_db(
image.clone(),
conn,
),
|r| match r {
Ok(()) => Message::ImageChanged,
Err(e) => {
error!(?e);
Message::None
}
},
|r| r.map(|_| Message::ImageChanged),
)
},
),
})
.map(|r| r.unwrap_or(Message::None)),
);
}
Message::ImageChanged => (),
@ -607,23 +582,20 @@ impl<'a> Library {
}
return Action::Task(
Task::future(self.db.acquire()).and_then(
move |conn| {
Task::future(self.db.acquire())
.map_err(|e| {
miette::miette!("Database error: {e}")
})
.and_then(move |conn| {
Task::perform(
update_video_in_db(
video.clone(),
conn,
),
|r| match r {
Ok(()) => Message::VideoChanged,
Err(e) => {
error!(?e);
Message::None
}
},
|r| r.map(|_| Message::VideoChanged),
)
},
),
})
.map(|r| r.unwrap_or(Message::None)),
);
}
Message::VideoChanged => debug!("vid shoulda changed"),
@ -651,23 +623,19 @@ impl<'a> Library {
.update_item(presentation.clone(), index)
{
Ok(()) => return Action::Task(
Task::future(self.db.acquire()).and_then(
Task::future(self.db.acquire()).map_err(|e| {
miette::miette!("Database error: {e}")
}).and_then(
move |conn| {
Task::perform(
update_presentation_in_db(
presentation.clone(),
conn,
),
|r| match r {
Ok(()) => Message::PresentationChanged,
Err(e) => {
error!(?e);
Message::None
}
},
|r| r.map(|_| Message::PresentationChanged)
)
},
),
).map(|r| r.unwrap_or(Message::None)),
),
Err(_) => todo!(),
}
@ -715,26 +683,27 @@ impl<'a> Library {
.add_item(song.clone())
.err()
else {
let task =
Task::future(self.db.acquire())
.and_then(move |db| {
Task::perform(
add_song_to_db(db),
{
move |res| {
if let Err(
e,
) = res
{
error!(
?e
);
}
Message::None
}
},
)
});
let task = Task::future(
self.db.acquire(),
)
.map_err(|e| {
miette::miette!(
"Database error: {e}"
)
})
.and_then(move |db| {
Task::perform(
new_song_from_db(db),
{
move |res| {
res.map(|_song| {
Message::None
})
}
},
)
})
.map(|r| r.unwrap_or(Message::None));
tasks.push(task);
continue;
};
@ -746,30 +715,29 @@ impl<'a> Library {
.add_item(video.clone())
.err()
else {
let task = Task::future(
self.db.acquire(),
)
.and_then(move |db| {
Task::perform(
add_video_to_db(
video.clone(),
db,
),
{
let video = video.clone();
move |res| {
debug!(
?video,
"added to db"
);
if let Err(e) = res {
error!(?e);
}
Message::None
}
},
)
});
let task =
Task::future(self.db.acquire())
.map_err(|e| {
miette::miette!(
"Database error: {e}"
)
})
.and_then(move |db| {
Task::perform(
add_video_to_db(
video.clone(),
db,
),
move |res| {
res.map(|_| {
Message::None
})
},
)
})
.map(|r| {
r.unwrap_or(Message::None)
});
tasks.push(task);
continue;
};
@ -781,30 +749,29 @@ impl<'a> Library {
.add_item(image.clone())
.err()
else {
let task = Task::future(
self.db.acquire(),
)
.and_then(move |db| {
Task::perform(
add_image_to_db(
image.clone(),
db,
),
{
let image = image.clone();
move |res| {
debug!(
?image,
"added to db"
);
if let Err(e) = res {
error!(?e);
}
Message::None
}
},
)
});
let task =
Task::future(self.db.acquire())
.map_err(|e| {
miette::miette!(
"Database error: {e}"
)
})
.and_then(move |db| {
Task::perform(
add_image_to_db(
image.clone(),
db,
),
move |res| {
res.map(|_| {
Message::None
})
},
)
})
.map(|r| {
r.unwrap_or(Message::None)
});
tasks.push(task);
continue;
};
@ -820,6 +787,11 @@ impl<'a> Library {
else {
let task =
Task::future(self.db.acquire())
.map_err(|e| {
miette::miette!(
"Database error: {e}"
)
})
.and_then(move |db| {
Task::perform(
add_presentation_to_db(
@ -827,20 +799,16 @@ impl<'a> Library {
db,
),
{
let presentation =
presentation.clone();
move |res| {
debug!(
?presentation,
"added to db"
);
if let Err(e) = res {
error!(?e);
}
Message::None
res.map(|_| {
Message::None
})
}
},
)
})
.map(|r| {
r.unwrap_or(Message::None)
});
tasks.push(task);
@ -871,7 +839,7 @@ impl<'a> Library {
let library_column = column![
text::heading("Library").center().width(Length::Fill),
cosmic::iced::widget::horizontal_rule(1),
divider::horizontal::light(),
song_library,
image_library,
video_library,
@ -950,7 +918,7 @@ impl<'a> Library {
where
T: Content,
{
let mut row = row::<Message>().spacing(5);
let mut row = row::with_capacity(5).spacing(5);
match &model.kind {
LibraryKind::Song => {
row = row
@ -981,7 +949,7 @@ impl<'a> Library {
}
}
let item_count = model.items.len();
row = row.push(horizontal_space());
row = row.push(space::horizontal());
row = row
.push(textm!("{}", item_count).align_y(Vertical::Center));
row = row.push(
@ -1118,7 +1086,7 @@ impl<'a> Library {
column![library_toolbar, context_menu].spacing(3);
Container::new(library_column).padding(5)
} else {
Container::new(Space::new(0, 0))
Container::new(Space::new())
};
column![library_button, lib_container].into()
}
@ -1133,6 +1101,9 @@ impl<'a> Library {
where
T: Content,
{
let cosmic::cosmic_theme::Spacing {
space_xxs, space_s, ..
} = theme::spacing();
let text = Container::new(responsive(|size| {
text::heading(elide_text(item.title(), size.width))
.center()
@ -1183,11 +1154,10 @@ impl<'a> Library {
let texts = column([text.into(), subtext.into()]);
Container::new(
rowm![horizontal_space().width(0), texts]
rowm![horizontal().width(0), texts]
.spacing(10)
.align_y(Vertical::Center),
)
// .padding(5)
.width(Length::Fill)
.style(move |t| {
container::Style::default()
@ -1228,7 +1198,7 @@ impl<'a> Library {
.rounded(t.cosmic().corner_radii.radius_m),
)
})
.padding([3, 0])
.padding([space_xxs, space_s])
.into()
}
@ -1372,6 +1342,7 @@ impl<'a> Library {
Message::None
},
)
.map(|m| Ok(m))
},
)
}
@ -1403,6 +1374,7 @@ impl<'a> Library {
Message::None
},
)
.map(|m| Ok(m))
},
)
}
@ -1436,6 +1408,7 @@ impl<'a> Library {
Message::None
},
)
.map(|m| Ok(m))
},
)
}
@ -1469,6 +1442,7 @@ impl<'a> Library {
Message::None
},
)
.map(|m| Ok(m))
},
)
}
@ -1477,6 +1451,11 @@ impl<'a> Library {
}
}
})
.map(|t| {
t.map(
|r| if let Ok(r) = r { r } else { Message::None },
)
})
.collect();
if !tasks.is_empty() {
self.selected_items = None;

View file

@ -5,7 +5,7 @@ pub mod image_editor;
pub mod library;
pub mod presentation_editor;
pub mod presenter;
pub mod service;
// pub mod service;
pub mod slide_editor;
pub mod song_editor;
pub mod text_svg;

View file

@ -13,9 +13,11 @@ use cosmic::{
iced_widget::{column, row},
theme,
widget::{
self, Space, button, container, context_menu,
horizontal_space, icon, image::Handle, menu, mouse_area,
scrollable, text, text_input,
self, Space, button, container, context_menu, icon,
image::Handle,
menu, mouse_area, scrollable,
space::{self, horizontal},
text, text_input,
},
};
use miette::{IntoDiagnostic, Result, miette};
@ -335,7 +337,7 @@ impl PresentationEditor {
pub fn view(&self) -> Element<Message> {
let presentation = self.current_slide.as_ref().map_or_else(
|| container(Space::new(0, 0)),
|| container(Space::new()),
|slide| {
container(
widget::image(slide)
@ -350,7 +352,7 @@ impl PresentationEditor {
);
let pdf_pages: Vec<Element<Message>> =
self.slides.as_ref().map_or_else(
|| vec![horizontal_space().into()],
|| vec![horizontal().into()],
|pages| {
pages
.iter()
@ -421,7 +423,7 @@ impl PresentationEditor {
let control_buttons = row![
button::standard("Previous Page")
.on_press(Message::PrevPage),
horizontal_space(),
space::horizontal(),
button::standard("Next Page").on_press(Message::NextPage),
];
let column =
@ -450,7 +452,7 @@ impl PresentationEditor {
row![
text::body("Title:"),
title_box,
horizontal_space(),
space::horizontal(),
presentation_selector
]
.align_y(Vertical::Center)

View file

@ -19,12 +19,13 @@ use cosmic::{
scrollable::{
AbsoluteOffset, Direction, Scrollbar, scroll_to,
},
stack, vertical_rule,
stack,
},
prelude::*,
widget::{
Container, Id, Row, Space, container, context_menu, image,
menu, mouse_area, responsive, scrollable, text,
Container, Id, Row, Space, container, context_menu,
divider::vertical, image, menu, mouse_area, responsive,
scrollable, text,
},
};
use derive_more::Debug;
@ -454,7 +455,10 @@ impl Presenter {
};
let mut tasks = vec![];
tasks.push(scroll_to(self.scroll_id.clone(), offset));
tasks.push(scroll_to(
self.scroll_id.clone(),
offset.into(),
));
if self.slide_action_map.is_some() {
debug!("Found slide actions, running them");
@ -723,7 +727,7 @@ impl Presenter {
.align_top(Length::Fill)
.align_left(Length::Fill)
.padding([0, 0, 0, 35]);
let divider = vertical_rule(2);
let divider = vertical::light();
items.push(
container(stack!(row, label_container))
.padding([5, 2])
@ -981,7 +985,7 @@ pub(crate) fn slide_view<'a>(
) -> Element<'a, Message> {
responsive(move |size| {
let width = size.height * 16.0 / 9.0;
let black = Container::new(Space::new(0, 0))
let black = Container::new(Space::new())
.style(|_| {
container::background(Background::Color(Color::BLACK))
})

View file

@ -121,10 +121,10 @@ impl<'a> Program<SlideWidget, cosmic::Theme, cosmic::Renderer>
fn update(
&self,
_state: &mut Self::State,
event: canvas::Event,
event: &canvas::Event,
bounds: cosmic::iced::Rectangle,
_cursor: cosmic::iced_core::mouse::Cursor,
) -> (canvas::event::Status, Option<SlideWidget>) {
) -> Option<cosmic::iced_widget::Action<SlideWidget>> {
match event {
canvas::Event::Mouse(event) => match event {
cosmic::iced::mouse::Event::CursorEntered => {
@ -157,8 +157,15 @@ impl<'a> Program<SlideWidget, cosmic::Theme, cosmic::Renderer>
},
canvas::Event::Touch(_event) => debug!("test"),
canvas::Event::Keyboard(_event) => debug!("test"),
canvas::Event::Window(_event) => todo!(),
canvas::Event::InputMethod(_event) => todo!(),
canvas::Event::A11y(_id, _action_request) => todo!(),
canvas::Event::Dnd(_dnd_event) => todo!(),
canvas::Event::PlatformSpecific(_platform_specific) => {
todo!()
}
}
(canvas::event::Status::Ignored, None)
None
}
fn mouse_interaction(

View file

@ -35,8 +35,9 @@ use cosmic::{
combo_box, container, divider, dnd_destination, dnd_source,
dropdown,
grid::{self},
horizontal_space, icon, mouse_area, popover, progress_bar,
scrollable, text, text_editor, text_input, tooltip,
icon, mouse_area, popover, progress_bar, scrollable,
space::{self, horizontal},
text, text_editor, text_input, tooltip,
},
};
use derive_more::Debug;
@ -900,7 +901,7 @@ impl SongEditor {
pub fn view(&self) -> Element<Message> {
let video_elements: Element<Message> =
self.video.as_ref().map_or_else(
|| horizontal_space().into(),
|| horizontal().into(),
|video| {
let play_button =
button::icon(if video.paused() {
@ -913,8 +914,8 @@ impl SongEditor {
0.0..=video.duration().as_secs_f32(),
video.position().as_secs_f32(),
)
.height(cosmic::theme::spacing().space_s)
.width(Length::Fill);
.girth(cosmic::theme::spacing().space_s)
.length(Length::Fill);
container(
row![play_button, video_track]
.align_y(Vertical::Center)
@ -947,7 +948,7 @@ impl SongEditor {
fn slide_preview(&self) -> Element<Message> {
self.song_slides.as_ref().map_or_else(
|| horizontal_space().into(),
|| space::horizontal().into(),
|slides| {
let slides: Vec<Element<Message>> = slides
.iter()
@ -1107,7 +1108,7 @@ impl SongEditor {
if let Some(hovered_chip) =
self.hovered_dnd_verse_chip
&& index == hovered_chip {
let phantom_chip = horizontal_space().width(60).height(19)
let phantom_chip = space::horizontal().width(60).height(19)
.apply(container)
.padding(
Padding::new(space_xxs.into())
@ -1119,7 +1120,7 @@ impl SongEditor {
.background(ContainerBackground::Color(
Color::from(t.cosmic().secondary.base).scale_alpha(0.5)
))
.border(Border::default().rounded(space_m).width(2))
.border(Border::default().rounded(space_m as u8).width(2))
})));
chip = row![
phantom_chip,
@ -1166,7 +1167,7 @@ impl SongEditor {
let mut verse_order_row = if self.dragging_verse_chip {
let ending_dnd_dest = dnd_destination(
horizontal_space().height(19),
space::horizontal().height(19),
vec!["application/verse".into()],
)
.on_enter(|_, _, _| {
@ -1213,7 +1214,7 @@ impl SongEditor {
if self.editing_verse_order {
Element::from(verse_options)
} else {
Element::from(horizontal_space())
Element::from(space::horizontal())
}
]
.spacing(space_s),
@ -1236,7 +1237,7 @@ impl SongEditor {
.spacing(5);
let verse_list = self.verses.as_ref().map_or_else(
|| Element::from(horizontal_space()),
|| Element::from(space::horizontal()),
|verse_list| {
Element::from(
column(verse_list.iter().enumerate().map(
@ -1347,7 +1348,7 @@ impl SongEditor {
.on_close(Message::FontSelectorOpen(false))
.width(300),
container(if self.font_selector_open {
Element::from(horizontal_space())
Element::from(space::horizontal())
} else {
Element::from(
icon::from_name("arrow-down").size(space_m),
@ -1383,7 +1384,7 @@ impl SongEditor {
.on_close(Message::FontSizeOpen(false))
.width(space_xxxl),
container(if self.font_size_open {
Element::from(horizontal_space())
Element::from(space::horizontal())
} else {
Element::from(
icon::from_name("arrow-down").size(space_m),
@ -1797,7 +1798,7 @@ impl SongEditor {
shadow_tools_button,
divider::vertical::default().height(space_l),
text_alignment_popup,
horizontal_space(),
space::horizontal(),
background_selector
]
.align_y(Vertical::Center)
@ -1974,7 +1975,9 @@ fn verse_chip(
))
.color(text_color)
.border(
Border::default().rounded(space_m).width(2),
Border::default()
.rounded(space_m as u8)
.width(2),
)
})));
let button = button::icon(icon::from_name("view-close"))
@ -2006,7 +2009,9 @@ fn verse_chip(
))
.color(text_color)
.border(
Border::default().rounded(space_m).width(2),
Border::default()
.rounded(space_m as u8)
.width(2),
)
})))
.into()

View file

@ -518,7 +518,13 @@ impl TextSvg {
pub fn view<'a>(&self) -> Element<'a, Message> {
self.handle.clone().map_or_else(
|| Element::from(Space::new(Length::Fill, Length::Fill)),
|| {
Element::from(
Space::new()
.height(Length::Fill)
.width(Length::Fill),
)
},
|handle| {
Image::new(handle)
.content_fit(ContentFit::Cover)

View file

@ -7,8 +7,9 @@ use cosmic::{
iced_widget::{column, row},
theme,
widget::{
Space, button, container, horizontal_space, icon,
progress_bar, text, text_input,
Space, button, container, icon, progress_bar,
space::{self, horizontal},
text, text_input,
},
};
use iced_video_player::{Video, VideoPlayer};
@ -110,7 +111,7 @@ impl VideoEditor {
pub fn view(&self) -> Element<Message> {
let video_elements = self.video.as_ref().map_or_else(
|| container(horizontal_space()),
|| container(horizontal()),
|video| {
let play_button = button::icon(if video.paused() {
icon::from_name("media-playback-start")
@ -122,8 +123,8 @@ impl VideoEditor {
0.0..=video.duration().as_secs_f32(),
video.position().as_secs_f32(),
)
.height(cosmic::theme::spacing().space_s)
.width(Length::Fill);
.girth(cosmic::theme::spacing().space_s)
.length(Length::Fill);
container(
row![play_button, video_track]
.align_y(Vertical::Center)
@ -135,7 +136,7 @@ impl VideoEditor {
);
let video_player = self.video.as_ref().map_or_else(
|| Element::from(Space::new(0, 0)),
|| Element::from(Space::new()),
|video| Element::from(VideoPlayer::new(video)),
);
@ -164,7 +165,7 @@ impl VideoEditor {
row![
text::body("Title:"),
title_box,
horizontal_space(),
space::horizontal(),
video_selector
]
.align_y(Vertical::Center)
@ -177,6 +178,7 @@ impl VideoEditor {
}
fn update_entire_video(&mut self, video: &videos::Video) {
debug!(?video);
let Ok(mut player_video) =
Url::from_file_path(video.path.clone())
.map(|url| Video::new(&url).expect("Should be here"))

View file

@ -27,7 +27,7 @@ use cosmic::iced::advanced::layout::{self, Layout};
use cosmic::iced::advanced::widget::{Operation, Tree, Widget, tree};
use cosmic::iced::advanced::{Clipboard, Shell, overlay, renderer};
use cosmic::iced::alignment::{self, Alignment};
use cosmic::iced::event::{self, Event};
use cosmic::iced::event::Event;
use cosmic::iced::{self, Transformation, mouse};
use cosmic::iced::{
Background, Border, Color, Element, Length, Padding, Pixels,
@ -376,7 +376,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -392,53 +392,49 @@ where
self.padding,
self.spacing,
self.align,
&self.children,
self.children.as_mut(),
&mut tree.children,
)
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn Operation,
) {
operation.container(
None,
layout.bounds(),
&mut |operation| {
self.children
.iter()
.zip(&mut tree.children)
.zip(layout.children())
.for_each(|((child, state), c_layout)| {
child.as_widget().operate(
state,
c_layout.with_virtual_offset(
layout.virtual_offset(),
),
renderer,
operation,
);
});
},
);
operation.container(None, layout.bounds());
operation.traverse(&mut |operation| {
self.children
.iter_mut()
.zip(&mut tree.children)
.zip(layout.children())
.for_each(|((child, state), c_layout)| {
child.as_widget_mut().operate(
state,
c_layout.with_virtual_offset(
layout.virtual_offset(),
),
renderer,
operation,
);
});
});
}
fn on_event(
fn update(
&mut self,
tree: &mut Tree,
event: Event,
event: &Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
) -> event::Status {
let mut event_status = event::Status::Ignored;
) {
let action = tree.state.downcast_mut::<Action>();
match event {
@ -459,7 +455,7 @@ where
index,
origin: cursor_position,
};
event_status = event::Status::Captured;
shell.capture_event();
break;
}
}
@ -484,7 +480,7 @@ where
DragEvent::Picked { index },
));
}
event_status = event::Status::Captured;
shell.capture_event();
}
}
Action::Dragging { origin, index, .. } => {
@ -496,7 +492,7 @@ where
origin,
index,
};
event_status = event::Status::Captured;
shell.capture_event();
}
}
_ => {}
@ -529,8 +525,7 @@ where
drop_position,
},
));
event_status =
event::Status::Captured;
shell.capture_event();
}
} else if let Some(on_reorder) =
&self.on_drag
@ -538,8 +533,7 @@ where
shell.publish(on_reorder(
DragEvent::Canceled { index },
));
event_status =
event::Status::Captured;
shell.capture_event();
}
}
*action = Action::Idle;
@ -554,15 +548,14 @@ where
_ => {}
}
let child_status = self
.children
self.children
.iter_mut()
.zip(&mut tree.children)
.zip(layout.children())
.map(|((child, state), c_layout)| {
child.as_widget_mut().on_event(
.for_each(|((child, state), c_layout)| {
child.as_widget_mut().update(
state,
event.clone(),
&event.clone(),
c_layout
.with_virtual_offset(layout.virtual_offset()),
cursor,
@ -571,10 +564,7 @@ where
shell,
viewport,
)
})
.fold(event::Status::Ignored, event::Status::merge);
event::Status::merge(event_status, child_status)
});
}
fn mouse_interaction(
@ -807,8 +797,9 @@ where
fn overlay<'b>(
&'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
layout: Layout<'b>,
renderer: &Renderer,
viewport: &Rectangle,
translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
overlay::from_children(
@ -816,6 +807,7 @@ where
tree,
layout,
renderer,
viewport,
translation,
)
}

View file

@ -27,7 +27,7 @@ use cosmic::iced::advanced::layout::{self, Layout};
use cosmic::iced::advanced::widget::{Operation, Tree, Widget, tree};
use cosmic::iced::advanced::{Clipboard, Shell, overlay, renderer};
use cosmic::iced::alignment::{self, Alignment};
use cosmic::iced::event::{self, Event};
use cosmic::iced::event::Event;
use cosmic::iced::{self, Transformation, mouse};
use cosmic::iced::{
Background, Border, Color, Element, Length, Padding, Pixels,
@ -371,7 +371,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -385,48 +385,43 @@ where
self.padding,
self.spacing,
self.align,
&self.children,
&mut self.children,
&mut tree.children,
)
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn Operation,
) {
operation.container(
None,
layout.bounds(),
&mut |operation| {
self.children
.iter()
.zip(&mut tree.children)
.zip(layout.children())
.for_each(|((child, state), layout)| {
child.as_widget().operate(
state, layout, renderer, operation,
);
});
},
);
operation.container(None, layout.bounds());
operation.traverse(&mut |operation| {
self.children
.iter_mut()
.zip(&mut tree.children)
.zip(layout.children())
.for_each(|((child, state), layout)| {
child
.as_widget_mut()
.operate(state, layout, renderer, operation);
});
});
}
fn on_event(
fn update(
&mut self,
tree: &mut Tree,
event: Event,
event: &Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
) -> event::Status {
let mut event_status = event::Status::Ignored;
) {
let action = tree.state.downcast_mut::<Action>();
match event {
@ -447,7 +442,7 @@ where
index,
origin: cursor_position,
};
event_status = event::Status::Captured;
shell.capture_event();
break;
}
}
@ -472,7 +467,7 @@ where
DragEvent::Picked { index },
));
}
event_status = event::Status::Captured;
shell.capture_event();
}
}
Action::Dragging { origin, index, .. } => {
@ -484,7 +479,7 @@ where
origin,
index,
};
event_status = event::Status::Captured;
shell.capture_event();
}
}
_ => {}
@ -517,8 +512,7 @@ where
drop_position,
},
));
event_status =
event::Status::Captured;
shell.capture_event();
}
} else if let Some(on_reorder) =
&self.on_drag
@ -526,8 +520,7 @@ where
shell.publish(on_reorder(
DragEvent::Canceled { index },
));
event_status =
event::Status::Captured;
shell.capture_event();
}
}
*action = Action::Idle;
@ -542,15 +535,14 @@ where
_ => {}
}
let child_status = self
.children
self.children
.iter_mut()
.zip(&mut tree.children)
.zip(layout.children())
.map(|((child, state), layout)| {
child.as_widget_mut().on_event(
.for_each(|((child, state), layout)| {
child.as_widget_mut().update(
state,
event.clone(),
&event.clone(),
layout,
cursor,
renderer,
@ -558,10 +550,7 @@ where
shell,
viewport,
)
})
.fold(event::Status::Ignored, event::Status::merge);
event::Status::merge(event_status, child_status)
});
}
fn mouse_interaction(
@ -769,8 +758,9 @@ where
fn overlay<'b>(
&'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
layout: Layout<'b>,
renderer: &Renderer,
viewport: &Rectangle,
translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
overlay::from_children(
@ -778,6 +768,7 @@ where
tree,
layout,
renderer,
viewport,
translation,
)
}
@ -833,7 +824,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -875,8 +866,8 @@ where
}
};
for (i, child) in self.row.children.iter().enumerate() {
let node = child.as_widget().layout(
for (i, child) in self.row.children.iter_mut().enumerate() {
let node = child.as_widget_mut().layout(
&mut tree.children[i],
renderer,
&limits,
@ -927,7 +918,7 @@ where
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@ -936,18 +927,18 @@ where
self.row.operate(tree, layout, renderer, operation);
}
fn on_event(
fn update(
&mut self,
tree: &mut Tree,
event: Event,
event: &Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
) -> event::Status {
self.row.on_event(
) {
self.row.update(
tree, event, layout, cursor, renderer, clipboard, shell,
viewport,
)
@ -984,11 +975,18 @@ where
fn overlay<'b>(
&'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
layout: Layout<'b>,
renderer: &Renderer,
viewport: &Rectangle,
translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
self.row.overlay(tree, layout, renderer, translation)
self.row.overlay(
tree,
layout,
renderer,
viewport,
translation,
)
}
}

View file

@ -2,5 +2,5 @@
#[allow(clippy::nursery)]
#[allow(clippy::pedantic)]
pub mod draggable;
pub mod slide_text;
// pub mod slide_text;
pub mod verse_editor;

View file

@ -40,7 +40,7 @@ where
}
fn layout(
&self,
&mut self,
_tree: &mut widget::Tree,
_renderer: &Renderer,
_limits: &layout::Limits,

View file

@ -5,8 +5,7 @@ use cosmic::{
iced_widget::{column, row},
theme,
widget::{
button, combo_box, container, horizontal_space, icon,
text_editor,
button, combo_box, container, icon, space, text_editor,
},
};
@ -62,12 +61,12 @@ impl VerseEditor {
let verse = self.verse_name;
Action::UpdateVerse((verse, lyrics))
}
text_editor::Action::Scroll { pixels } => {
text_editor::Action::Scroll { lines } => {
if self.content.line_count() > 6 {
self.content.perform(action);
Action::None
} else {
Action::ScrollVerses(pixels)
Action::ScrollVerses(lines as f32)
}
}
_ => {
@ -109,12 +108,12 @@ impl VerseEditor {
);
let verse_title =
row![combo, horizontal_space(), delete_button];
row![combo, space::horizontal(), delete_button];
let lyric: Element<Message> = if self.verse_name
== VerseName::Blank
{
horizontal_space().into()
space::horizontal().into()
} else {
text_editor(&self.content)
.on_action(Message::UpdateLyric)
@ -131,15 +130,11 @@ impl VerseEditor {
.into(),
),
border: Border::default()
.rounded(space_s)
.rounded(space_s as u8)
.width(2)
.color(
t.cosmic().bg_component_divider(),
),
icon: t
.cosmic()
.primary_component_color()
.into(),
placeholder: neutral
.with_alpha(0.7)
.into(),
@ -147,13 +142,15 @@ impl VerseEditor {
selection: t.cosmic().accent.base.into(),
};
let hovered_border = Border::default()
.rounded(space_s)
.rounded(space_s as u8)
.width(3)
.color(t.cosmic().accent.hover);
match s {
text_editor::Status::Active => base_style,
text_editor::Status::Hovered
| text_editor::Status::Focused => {
| text_editor::Status::Focused {
..
} => {
base_style.border = hovered_border;
base_style
}