mirror of
https://github.com/Zenithsiz/zsw.git
synced 2026-02-05 10:30:30 +00:00
Replaced panel groups with vectors of panels.
This commit is contained in:
parent
92738a96a9
commit
13ee128558
@ -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]
|
||||
|
||||
@ -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
8
panels/multiple.yaml
Normal 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
8
panels/quarter.yaml
Normal 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
|
||||
@ -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![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
168
zsw/src/main.rs
168
zsw/src/main.rs
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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(), &[]);
|
||||
|
||||
|
||||
@ -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)]
|
||||
|
||||
@ -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
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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>,
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user