Replaced panel groups with vectors of panels.

This commit is contained in:
Filipe Rodrigues 2024-07-26 01:46:57 +01:00
parent 92738a96a9
commit 13ee128558
Signed by: zenithsiz
SSH Key Fingerprint: SHA256:Mb5ppb3Sh7IarBO/sBTXLHbYEOz37hJAlslLQPPAPaU
11 changed files with 281 additions and 332 deletions

View File

@ -5,4 +5,4 @@ shaders_dir: shaders/
upscale_cache_dir: null # Use default
upscale_cmd: null # None
upscale_exclude: [] # None
default_panel_group: panels/example1.yaml
default_panels: [panels/quarter.yaml, panels/multiple.yaml]

View File

@ -1,17 +0,0 @@
panels:
- geometries: [geometry: '960x540+0+540', geometry: '960x1080+960+0']
state:
duration: 300
fade_point: 250
parallax_ratio: 0.98
parallax_exp: 2.0
reverse_parallax: true
playlist: playlists/example1.yaml
- geometries: [geometry: '960x540+0+0']
state:
duration: 300
fade_point: 250
parallax_ratio: 0.98
parallax_exp: 2.0
reverse_parallax: true
playlist: playlists/example1.yaml

8
panels/multiple.yaml Normal file
View File

@ -0,0 +1,8 @@
geometries: [geometry: '960x540+0+540', geometry: '960x1080+960+0']
state:
duration: 300
fade_point: 250
parallax_ratio: 0.98
parallax_exp: 2.0
reverse_parallax: true
playlist: playlists/example1.yaml

8
panels/quarter.yaml Normal file
View File

@ -0,0 +1,8 @@
geometries: [geometry: '960x540+0+0']
state:
duration: 300
fade_point: 250
parallax_ratio: 0.98
parallax_exp: 2.0
reverse_parallax: true
playlist: playlists/example1.yaml

View File

@ -47,9 +47,9 @@ pub struct Config {
#[serde(default)]
pub upscale_exclude: HashSet<PathBuf>,
/// Default panel group
/// Default panels
#[serde(default)]
pub default_panel_group: Option<PathBuf>,
pub default_panels: Vec<PathBuf>,
}
impl Config {
@ -105,7 +105,7 @@ impl Default for Config {
upscale_cache_dir: None,
upscale_cmd: None,
upscale_exclude: HashSet::new(),
default_panel_group: None,
default_panels: vec![],
}
}
}

View File

@ -18,7 +18,8 @@
generic_const_exprs,
hash_raw_entry,
must_not_suspend,
strict_provenance
strict_provenance,
anonymous_lifetime_in_impl_trait
)]
#![expect(incomplete_features)]
@ -39,7 +40,7 @@ mod window;
use {
self::{
config::Config,
panel::{PanelShader, PanelsManager, PanelsRenderer},
panel::{Panel, PanelShader, PanelsManager, PanelsRenderer},
settings_menu::SettingsMenu,
shared::Shared,
},
@ -49,7 +50,7 @@ use {
clap::Parser,
crossbeam::atomic::AtomicCell,
directories::ProjectDirs,
futures::Future,
futures::{stream::FuturesUnordered, Future, StreamExt},
std::{path::PathBuf, sync::Arc},
tokio::sync::{Mutex, RwLock},
winit::{
@ -140,7 +141,7 @@ async fn run(dirs: &ProjectDirs, config: &Config) -> Result<(), AppError> {
panels_manager,
image_requester,
playlists_manager,
cur_panel_group: Mutex::new(None),
cur_panels: Mutex::new(vec![]),
panels_renderer_shader: RwLock::new(panels_renderer_shader),
playlists: RwLock::new(playlists),
};
@ -150,10 +151,10 @@ async fn run(dirs: &ProjectDirs, config: &Config) -> Result<(), AppError> {
let (panels_updater_output_tx, panels_updater_output_rx) = meetup::channel();
self::spawn_task("Load default panel group", {
self::spawn_task("Load default panels", {
let shared = Arc::clone(&shared);
let default_panel_group = config.default_panel_group.clone();
|| self::load_default_panel_group(default_panel_group, shared)
let default_panels = config.default_panels.clone();
|| self::load_default_panels(default_panels, shared)
});
self::spawn_task("Renderer", {
@ -204,32 +205,34 @@ async fn run(dirs: &ProjectDirs, config: &Config) -> Result<(), AppError> {
Ok(())
}
/// Loads the default panel group
async fn load_default_panel_group(default_panel_group: Option<PathBuf>, shared: Arc<Shared>) -> Result<(), AppError> {
// If we don't have a default, don't do anything
let Some(default_panel_group) = &default_panel_group else {
return Ok(());
};
/// Loads the default panels
async fn load_default_panels(default_panels: Vec<PathBuf>, shared: Arc<Shared>) -> Result<(), AppError> {
// Load the panels
let shared = &shared;
let loaded_panels = default_panels
.iter()
.map(|default_panel| async move {
shared
.panels_manager
.load(default_panel, shared)
.await
.inspect(|panel| tracing::debug!(?panel, "Loaded default panel"))
.inspect_err(|err| tracing::warn!("Unable to load default panel {default_panel:?}: {err:?}"))
.ok()
})
.collect::<FuturesUnordered<_>>()
.filter_map(async move |opt| opt)
.collect::<Vec<Panel>>()
.await;
// Else load the panel group
let loaded_panel_group = match shared.panels_manager.load(default_panel_group, &shared).await {
Ok(panel_group) => {
tracing::debug!(?panel_group, "Loaded default panel group");
panel_group
},
Err(err) => {
tracing::warn!("Unable to load default panel group: {err:?}");
return Ok(());
},
};
// And set it as the current one
// Add the default panels to the current panels
{
let mut panel_group = shared.cur_panel_group.lock().await;
*panel_group = Some(loaded_panel_group);
let mut cur_panels = shared.cur_panels.lock().await;
cur_panels.extend(loaded_panels);
}
{
// Finally at the end set the shader, if any panels were loaded
if !default_panels.is_empty() {
let mut panels_renderer_shader = shared.panels_renderer_shader.write().await;
panels_renderer_shader.shader = PanelShader::FadeOut { strength: 1.5 };
}
@ -285,23 +288,21 @@ async fn renderer(
.context("Unable to start frame")?;
// Render the panels
{
let mut panel_group = shared.cur_panel_group.lock().await;
if let Some(panel_group) = &mut *panel_group {
let cursor_pos = shared.cursor_pos.load();
let cur_panels = shared.cur_panels.lock().await;
let cursor_pos = shared.cursor_pos.load();
let panels_renderer_shader = shared.panels_renderer_shader.read().await;
panels_renderer
.render(
&mut frame,
&wgpu_renderer,
&shared.wgpu,
&shared.panels_renderer_layout,
Point2::new(cursor_pos.x as i32, cursor_pos.y as i32),
panel_group,
&panels_renderer_shader,
)
.context("Unable to render panels")?;
}
let panels_renderer_shader = shared.panels_renderer_shader.read().await;
panels_renderer
.render(
&mut frame,
&wgpu_renderer,
&shared.wgpu,
&shared.panels_renderer_layout,
Point2::new(cursor_pos.x as i32, cursor_pos.y as i32),
&*cur_panels,
&panels_renderer_shader,
)
.context("Unable to render panels")?;
}
// Render egui
@ -335,14 +336,12 @@ async fn panels_updater(
) -> Result<!, AppError> {
loop {
{
let mut panel_group = shared.cur_panel_group.lock().await;
let mut cur_panels = shared.cur_panels.lock().await;
if let Some(panel_group) = &mut *panel_group {
for panel in panel_group.panels_mut() {
panel
.update(&shared.wgpu, &shared.panels_renderer_layout, &shared.image_requester)
.await;
}
for panel in &mut *cur_panels {
panel
.update(&shared.wgpu, &shared.panels_renderer_layout, &shared.image_requester)
.await;
}
}
@ -368,14 +367,12 @@ async fn egui_painter(
{
let cursor_pos = shared.cursor_pos.load();
let cursor_pos = Point2::new(cursor_pos.x as i32, cursor_pos.y as i32);
let mut panel_group = shared.cur_panel_group.lock().block_on();
if let Some(panel_group) = &mut *panel_group {
for panel in panel_group.panels_mut() {
for geometry in &panel.geometries {
if geometry.geometry.contains(cursor_pos) {
panel.state.paused ^= true;
break;
}
let mut cur_panels = shared.cur_panels.lock().block_on();
for panel in &mut *cur_panels {
for geometry in &panel.geometries {
if geometry.geometry.contains(cursor_pos) {
panel.state.paused ^= true;
break;
}
}
}
@ -388,17 +385,14 @@ async fn egui_painter(
{
let cursor_pos = shared.cursor_pos.load();
let cursor_pos = Point2::new(cursor_pos.x as i32, cursor_pos.y as i32);
let mut panel_group = shared.cur_panel_group.lock().block_on();
if let Some(panel_group) = &mut *panel_group {
for panel in panel_group.panels_mut() {
for geometry in &panel.geometries {
if geometry.geometry.contains(cursor_pos) {
match panel.images.state() {
panel::ImagesState::Empty => (),
panel::ImagesState::PrimaryOnly =>
panel.state.cur_progress = panel.state.fade_point,
panel::ImagesState::Both => panel.state.cur_progress = panel.state.duration,
}
let mut cur_panels = shared.cur_panels.lock().block_on();
for panel in &mut *cur_panels {
for geometry in &panel.geometries {
if geometry.geometry.contains(cursor_pos) {
match panel.images.state() {
panel::ImagesState::Empty => (),
panel::ImagesState::PrimaryOnly => panel.state.cur_progress = panel.state.fade_point,
panel::ImagesState::Both => panel.state.cur_progress = panel.state.duration,
}
}
}
@ -411,25 +405,23 @@ async fn egui_painter(
let delta = ctx.input(|input| input.scroll_delta.y);
let cursor_pos = shared.cursor_pos.load();
let cursor_pos = Point2::new(cursor_pos.x as i32, cursor_pos.y as i32);
let mut panel_group = shared.cur_panel_group.lock().block_on();
if let Some(panel_group) = &mut *panel_group {
for panel in panel_group.panels_mut() {
let max = match panel.images.state() {
panel::ImagesState::Empty => 0,
panel::ImagesState::PrimaryOnly => panel.state.fade_point,
panel::ImagesState::Both => panel.state.duration,
};
let mut cur_panels = shared.cur_panels.lock().block_on();
for panel in &mut *cur_panels {
let max = match panel.images.state() {
panel::ImagesState::Empty => 0,
panel::ImagesState::PrimaryOnly => panel.state.fade_point,
panel::ImagesState::Both => panel.state.duration,
};
let speed = (panel.state.duration as f32) / 240.0;
let speed = (panel.state.duration as f32) / 240.0;
for geometry in &panel.geometries {
if geometry.geometry.contains(cursor_pos) {
panel.state.cur_progress = panel
.state
.cur_progress
.saturating_add_signed((-delta * speed) as i64)
.clamp(0, max);
}
for geometry in &panel.geometries {
if geometry.geometry.contains(cursor_pos) {
panel.state.cur_progress = panel
.state
.cur_progress
.saturating_add_signed((-delta * speed) as i64)
.clamp(0, max);
}
}
}

View File

@ -40,57 +40,46 @@ impl PanelsManager {
Self {}
}
/// Loads a panel group from a path
pub async fn load(&self, path: &Path, shared: &Arc<Shared>) -> Result<PanelGroup, AppError> {
/// Loads a panel from a path
pub async fn load(&self, path: &Path, shared: &Arc<Shared>) -> Result<Panel, AppError> {
// Try to read the file
tracing::debug!(?path, "Loading panel group");
let panel_group_yaml = tokio::fs::read(path).await.context("Unable to open file")?;
tracing::debug!(?path, "Loading panel");
let panel_yaml = tokio::fs::read(path).await.context("Unable to open file")?;
// Then parse it
let panel_group =
serde_yaml::from_slice::<ser::PanelGroup>(&panel_group_yaml).context("Unable to parse panel group")?;
let panel = serde_yaml::from_slice::<ser::Panel>(&panel_yaml).context("Unable to parse panel")?;
// Finally convert it
let panels = panel_group
.panels
.into_iter()
.map(|panel| {
let geometries = panel.geometries.into_iter().map(|geometry| geometry.geometry).collect();
let state = PanelState {
paused: false,
cur_progress: 0,
duration: panel.state.duration,
fade_point: panel.state.fade_point,
parallax: PanelParallaxState {
ratio: panel.state.parallax_ratio,
exp: panel.state.parallax_exp,
reverse: panel.state.reverse_parallax,
},
};
let playlist = panel.playlist;
let geometries = panel.geometries.into_iter().map(|geometry| geometry.geometry).collect();
let state = PanelState {
paused: false,
cur_progress: 0,
duration: panel.state.duration,
fade_point: panel.state.fade_point,
parallax: PanelParallaxState {
ratio: panel.state.parallax_ratio,
exp: panel.state.parallax_exp,
reverse: panel.state.reverse_parallax,
},
};
let playlist = panel.playlist;
let panel = Panel::new(&shared.wgpu, &shared.panels_renderer_layout, geometries, state)
.context("Unable to create panel")?;
let panel = Panel::new(&shared.wgpu, &shared.panels_renderer_layout, geometries, state)
.context("Unable to create panel")?;
crate::spawn_task(format!("Load panel group {path:?}"), {
let playlist_player = Arc::clone(&panel.playlist_player);
let shared = Arc::clone(shared);
|| async move {
Self::load_playlist_into(&playlist_player, &playlist, &shared)
.await
.context("Unable to load playlist")?;
crate::spawn_task(format!("Load panel {path:?}"), {
let playlist_player = Arc::clone(&panel.playlist_player);
let shared = Arc::clone(shared);
|| async move {
Self::load_playlist_into(&playlist_player, &playlist, &shared)
.await
.context("Unable to load playlist")?;
Ok(())
}
});
Ok(())
}
});
Ok::<_, AppError>(panel)
})
.collect::<Result<Vec<_>, _>>()
.context("Unable to create panels")?;
let panel_group = PanelGroup::new(panels);
Ok(panel_group)
Ok(panel)
}
/// Loads `playlist` into `playlist_player`.
@ -229,30 +218,6 @@ impl PanelsManager {
}
}
/// Panel group
#[derive(Debug)]
pub struct PanelGroup {
/// All panels
panels: Vec<Panel>,
}
impl PanelGroup {
/// Creates panels from a list of panels
pub fn new(panels: Vec<Panel>) -> Self {
Self { panels }
}
/// Returns all panels
pub fn panels(&self) -> &[Panel] {
&self.panels
}
/// Returns all panels, mutably
pub fn panels_mut(&mut self) -> &mut Vec<Panel> {
&mut self.panels
}
}
/// Panel
#[derive(Debug)]
pub struct Panel {

View File

@ -10,7 +10,7 @@ pub use self::{uniform::PanelUniforms, vertex::PanelVertex};
// Imports
use {
self::uniform::PanelImageUniforms,
super::{PanelGroup, PanelImage},
super::{Panel, PanelImage},
crate::panel::PanelGeometry,
anyhow::Context,
cgmath::Point2,
@ -142,7 +142,7 @@ impl PanelsRenderer {
needs_reload
}
/// Renders a panel group
/// Renders a panel
#[expect(clippy::too_many_arguments)] // TODO: Refactor
pub fn render(
&mut self,
@ -151,7 +151,7 @@ impl PanelsRenderer {
wgpu_shared: &WgpuShared,
layouts: &PanelsRendererLayouts,
cursor_pos: Point2<i32>,
panel_group: &PanelGroup,
panels: impl IntoIterator<Item = &'_ Panel>,
shader: &PanelsRendererShader,
) -> Result<(), AppError> {
// Update the shader, if requested
@ -210,7 +210,7 @@ impl PanelsRenderer {
render_pass.set_vertex_buffer(0, self.vertices.slice(..));
// And draw each panel
for panel in panel_group.panels() {
for panel in panels {
// Bind the panel-shared image bind group
render_pass.set_bind_group(1, panel.images.image_bind_group(), &[]);

View File

@ -3,13 +3,6 @@
// Imports
use {std::path::PathBuf, zsw_util::Rect};
/// Serialized panel group
#[derive(Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct PanelGroup {
pub panels: Vec<Panel>,
}
/// Serialized panel
#[derive(Debug)]
#[derive(serde::Serialize, serde::Deserialize)]

View File

@ -217,166 +217,166 @@ fn choose_load_playlist_from_file(
/// Draws the panels editor
// TODO: Not edit the values as-is, as that breaks some invariants of panels (such as duration versus image states)
fn draw_panels_editor(add_playlist_state: &mut AddPlaylistState, ui: &mut egui::Ui, shared: &Arc<Shared>) {
let mut panel_group = shared.cur_panel_group.lock().block_on();
match &mut *panel_group {
Some(panel_group) =>
for (panel_idx, panel) in panel_group.panels_mut().iter_mut().enumerate() {
ui.collapsing(format!("Panel {panel_idx}"), |ui| {
ui.checkbox(&mut panel.state.paused, "Paused");
let mut cur_panels = shared.cur_panels.lock().block_on();
ui.collapsing("Geometries", |ui| {
for (geometry_idx, geometry) in panel.geometries.iter_mut().enumerate() {
ui.horizontal(|ui| {
ui.label(format!("#{}: ", geometry_idx + 1));
self::draw_rect(ui, &mut geometry.geometry);
});
}
});
if cur_panels.is_empty() {
ui.label("None loaded");
return;
}
for (panel_idx, panel) in cur_panels.iter_mut().enumerate() {
ui.collapsing(format!("Panel {panel_idx}"), |ui| {
ui.checkbox(&mut panel.state.paused, "Paused");
ui.collapsing("Geometries", |ui| {
for (geometry_idx, geometry) in panel.geometries.iter_mut().enumerate() {
ui.horizontal(|ui| {
// Note: We only allow up until the duration - 1 so that you don't get stuck
// skipping images when you hold it at the max value
ui.label("Cur progress");
egui::Slider::new(
&mut panel.state.cur_progress,
0..=panel.state.duration.saturating_sub(1),
)
.clamp_to_range(true)
.ui(ui);
// Then clamp to the current max
// Note: We don't just use this max above so the slider doesn't jitter when the max changes
let cur_max = match panel.images.state() {
panel::ImagesState::Empty => 0,
panel::ImagesState::PrimaryOnly => panel.state.fade_point,
panel::ImagesState::Both => panel.state.duration,
};
panel.state.cur_progress = panel.state.cur_progress.clamp(0, cur_max);
ui.label(format!("#{}: ", geometry_idx + 1));
self::draw_rect(ui, &mut geometry.geometry);
});
}
});
ui.horizontal(|ui| {
ui.label("Fade Point");
let min = panel.state.duration / 2;
let max = panel.state.duration.saturating_sub(1);
egui::Slider::new(&mut panel.state.fade_point, min..=max).ui(ui);
});
ui.horizontal(|ui| {
// Note: We only allow up until the duration - 1 so that you don't get stuck
// skipping images when you hold it at the max value
ui.label("Cur progress");
egui::Slider::new(
&mut panel.state.cur_progress,
0..=panel.state.duration.saturating_sub(1),
)
.clamp_to_range(true)
.ui(ui);
ui.horizontal(|ui| {
ui.label("Duration");
egui::Slider::new(&mut panel.state.duration, 0..=10800).ui(ui);
});
// Then clamp to the current max
// Note: We don't just use this max above so the slider doesn't jitter when the max changes
let cur_max = match panel.images.state() {
panel::ImagesState::Empty => 0,
panel::ImagesState::PrimaryOnly => panel.state.fade_point,
panel::ImagesState::Both => panel.state.duration,
};
panel.state.cur_progress = panel.state.cur_progress.clamp(0, cur_max);
});
ui.horizontal(|ui| {
ui.label("Parallax ratio");
egui::Slider::new(&mut panel.state.parallax.ratio, 0.0..=1.0).ui(ui);
});
ui.horizontal(|ui| {
ui.label("Fade Point");
let min = panel.state.duration / 2;
let max = panel.state.duration.saturating_sub(1);
egui::Slider::new(&mut panel.state.fade_point, min..=max).ui(ui);
});
ui.horizontal(|ui| {
ui.label("Parallax exp");
egui::Slider::new(&mut panel.state.parallax.exp, 0.0..=4.0).ui(ui);
});
ui.horizontal(|ui| {
ui.label("Duration");
egui::Slider::new(&mut panel.state.duration, 0..=10800).ui(ui);
});
ui.horizontal(|ui| {
ui.label("Parallax ratio");
egui::Slider::new(&mut panel.state.parallax.ratio, 0.0..=1.0).ui(ui);
});
ui.horizontal(|ui| {
ui.label("Parallax exp");
egui::Slider::new(&mut panel.state.parallax.exp, 0.0..=4.0).ui(ui);
});
ui.horizontal(|ui| {
ui.checkbox(&mut panel.state.parallax.reverse, "Reverse parallax");
});
ui.horizontal(|ui| {
ui.checkbox(&mut panel.state.parallax.reverse, "Reverse parallax");
});
ui.horizontal(|ui| {
ui.label("Skip");
if ui.button("🔄").clicked() {
match panel.images.state() {
panel::ImagesState::Empty => (),
panel::ImagesState::PrimaryOnly => panel.state.cur_progress = panel.state.fade_point,
panel::ImagesState::Both => panel.state.cur_progress = panel.state.duration,
ui.horizontal(|ui| {
ui.label("Skip");
if ui.button("🔄").clicked() {
match panel.images.state() {
panel::ImagesState::Empty => (),
panel::ImagesState::PrimaryOnly => panel.state.cur_progress = panel.state.fade_point,
panel::ImagesState::Both => panel.state.cur_progress = panel.state.duration,
}
}
});
ui.collapsing("Images", |ui| {
match panel.images.state() {
panel::ImagesState::Empty => (),
panel::ImagesState::PrimaryOnly => {
ui.collapsing("Front", |ui| self::draw_panel_image(ui, panel.images.front_mut()));
},
panel::ImagesState::Both => {
ui.collapsing("Front", |ui| self::draw_panel_image(ui, panel.images.front_mut()));
ui.collapsing("Back", |ui| self::draw_panel_image(ui, panel.images.back_mut()));
},
};
});
ui.collapsing("Playlist player", |ui| {
let playlist_player = panel.playlist_player.write().block_on();
let row_height = ui.text_style_height(&egui::TextStyle::Body);
if ui.button("↹ (Replace)").clicked() {
// TODO: Stop everything that could be inserting items still?
if let Some((playlist_path, playlist)) =
self::choose_load_playlist_from_file(add_playlist_state, shared)
{
crate::spawn_task(format!("Replace playlist {playlist:?}"), {
let playlist_player = Arc::clone(&panel.playlist_player);
let shared = Arc::clone(shared);
|| async move {
{
let mut playlist_player = playlist_player.write().await;
playlist_player.remove_all();
}
PanelsManager::load_playlist_into(&playlist_player, &playlist_path, &shared)
.await
.context("Unable to load playlist")?;
Ok(())
}
}
});
});
}
};
ui.collapsing("Images", |ui| {
match panel.images.state() {
panel::ImagesState::Empty => (),
panel::ImagesState::PrimaryOnly => {
ui.collapsing("Front", |ui| self::draw_panel_image(ui, panel.images.front_mut()));
},
panel::ImagesState::Both => {
ui.collapsing("Front", |ui| self::draw_panel_image(ui, panel.images.front_mut()));
ui.collapsing("Back", |ui| self::draw_panel_image(ui, panel.images.back_mut()));
},
};
});
ui.collapsing("Playlist player", |ui| {
let playlist_player = panel.playlist_player.write().block_on();
let row_height = ui.text_style_height(&egui::TextStyle::Body);
if ui.button("↹ (Replace)").clicked() {
// TODO: Stop everything that could be inserting items still?
if let Some((playlist_path, playlist)) =
self::choose_load_playlist_from_file(add_playlist_state, shared)
{
crate::spawn_task(format!("Replace playlist {playlist:?}"), {
let playlist_player = Arc::clone(&panel.playlist_player);
let shared = Arc::clone(shared);
|| async move {
{
let mut playlist_player = playlist_player.write().await;
playlist_player.remove_all();
}
PanelsManager::load_playlist_into(&playlist_player, &playlist_path, &shared)
.await
.context("Unable to load playlist")?;
Ok(())
}
});
ui.collapsing("Prev", |ui| {
egui::ScrollArea::new([false, true])
.auto_shrink([false, true])
.stick_to_right(true)
.max_height(row_height * 10.0)
.show_rows(ui, row_height, playlist_player.prev_items().len(), |ui, idx| {
for item in playlist_player.prev_items().take(idx.end).skip(idx.start) {
self::draw_openable_path(ui, item);
}
};
ui.collapsing("Prev", |ui| {
egui::ScrollArea::new([false, true])
.auto_shrink([false, true])
.stick_to_right(true)
.max_height(row_height * 10.0)
.show_rows(ui, row_height, playlist_player.prev_items().len(), |ui, idx| {
for item in playlist_player.prev_items().take(idx.end).skip(idx.start) {
self::draw_openable_path(ui, item);
}
});
});
ui.collapsing("Next", |ui| {
egui::ScrollArea::new([false, true])
.auto_shrink([false, true])
.stick_to_right(true)
.max_height(row_height * 10.0)
.show_rows(ui, row_height, playlist_player.peek_next_items().len(), |ui, idx| {
for item in playlist_player.peek_next_items().take(idx.end).skip(idx.start) {
self::draw_openable_path(ui, item);
}
});
});
ui.collapsing("All", |ui| {
egui::ScrollArea::new([false, true])
.auto_shrink([false, true])
.stick_to_right(true)
.max_height(row_height * 10.0)
.show_rows(ui, row_height, playlist_player.all_items().len(), |ui, idx| {
for item in playlist_player.all_items().take(idx.end).skip(idx.start) {
self::draw_openable_path(ui, item);
}
});
});
// TODO: Allow a "Go back" button. Or even a full playlist solution
});
});
},
None => {
ui.label("None loaded");
},
ui.collapsing("Next", |ui| {
egui::ScrollArea::new([false, true])
.auto_shrink([false, true])
.stick_to_right(true)
.max_height(row_height * 10.0)
.show_rows(ui, row_height, playlist_player.peek_next_items().len(), |ui, idx| {
for item in playlist_player.peek_next_items().take(idx.end).skip(idx.start) {
self::draw_openable_path(ui, item);
}
});
});
ui.collapsing("All", |ui| {
egui::ScrollArea::new([false, true])
.auto_shrink([false, true])
.stick_to_right(true)
.max_height(row_height * 10.0)
.show_rows(ui, row_height, playlist_player.all_items().len(), |ui, idx| {
for item in playlist_player.all_items().take(idx.end).skip(idx.start) {
self::draw_openable_path(ui, item);
}
});
});
// TODO: Allow a "Go back" button. Or even a full playlist solution
});
});
}
}

View File

@ -4,7 +4,7 @@
use {
crate::{
image_loader::ImageRequester,
panel::{PanelGroup, PanelsManager, PanelsRendererLayouts, PanelsRendererShader},
panel::{Panel, PanelsManager, PanelsRendererLayouts, PanelsRendererShader},
playlist::{Playlists, PlaylistsManager},
Resize,
},
@ -29,7 +29,7 @@ pub struct Shared {
pub image_requester: ImageRequester,
pub playlists_manager: PlaylistsManager,
pub cur_panel_group: Mutex<Option<PanelGroup>>,
pub cur_panels: Mutex<Vec<Panel>>,
pub panels_renderer_shader: RwLock<PanelsRendererShader>,
pub playlists: RwLock<Playlists>,
}