From b272a2d78dcb51b5e6f4b7b343b141a0404b78c4 Mon Sep 17 00:00:00 2001 From: Filipe Rodrigues Date: Tue, 9 Sep 2025 21:28:24 +0100 Subject: [PATCH] Moved panels tab to it's own module. --- zsw/src/settings_menu.rs | 253 +------------------------------ zsw/src/settings_menu/panels.rs | 254 ++++++++++++++++++++++++++++++++ 2 files changed, 260 insertions(+), 247 deletions(-) create mode 100644 zsw/src/settings_menu/panels.rs diff --git a/zsw/src/settings_menu.rs b/zsw/src/settings_menu.rs index 5667267..4d06109 100644 --- a/zsw/src/settings_menu.rs +++ b/zsw/src/settings_menu.rs @@ -3,18 +3,18 @@ // Lints #![allow(unused_results)] // Egui produces a lot of results we don't need to use +// Modules +mod panels; + // Imports use { - crate::{ - AppEvent, - panel::{Panel, PanelFadeImage, PanelFadeShader, PanelFadeState, PanelGeometry, PanelNoneState, PanelState}, - }, + crate::{AppEvent, panel::Panel}, core::{ops::RangeInclusive, time::Duration}, egui::Widget, std::path::Path, strum::IntoEnumIterator, winit::{dpi::LogicalPosition, event_loop::EventLoopProxy}, - zsw_util::{AppError, Rect, TokioTaskBlockOn}, + zsw_util::{AppError, Rect}, zsw_wgpu::Wgpu, }; @@ -68,152 +68,13 @@ impl SettingsMenu { ui.separator(); match self.cur_tab { - Tab::Panels => self::draw_panels_tab(ui, wgpu, panels, window_geometry), + Tab::Panels => panels::draw_panels_tab(ui, wgpu, panels, window_geometry), Tab::Settings => self::draw_settings_tab(ui, event_loop_proxy), } }); } } -/// Draws the panels tab -fn draw_panels_tab(ui: &mut egui::Ui, wgpu: &Wgpu, panels: &mut [Panel], window_geometry: Rect) { - self::draw_panels_editor(ui, wgpu, panels, window_geometry); - ui.separator(); -} -/// 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(ui: &mut egui::Ui, wgpu: &Wgpu, panels: &mut [Panel], window_geometry: Rect) { - if panels.is_empty() { - ui.label("None loaded"); - return; - } - - for panel in panels { - let mut name = egui::WidgetText::from(panel.display_name.to_string()); - if panel - .geometries - .iter() - .all(|geometry| window_geometry.intersection(geometry.geometry).is_none()) - { - name = name.weak(); - } - - ui.collapsing(name, |ui| { - match &mut panel.state { - PanelState::None(_) => (), - PanelState::Fade(state) => - self::draw_fade_panel_editor(ui, wgpu, window_geometry, state, &mut panel.geometries), - } - - ui.collapsing("Shader", |ui| { - self::draw_shader_select(ui, &mut panel.state); - }); - }); - } -} - -/// Draws the fade panel editor -fn draw_fade_panel_editor( - ui: &mut egui::Ui, - wgpu: &Wgpu, - window_geometry: Rect, - panel_state: &mut PanelFadeState, - geometries: &mut [PanelGeometry], -) { - { - let mut is_paused = panel_state.is_paused(); - ui.checkbox(&mut is_paused, "Paused"); - panel_state.set_paused(is_paused); - } - - ui.collapsing("Geometries", |ui| { - for (geometry_idx, geometry) in geometries.iter_mut().enumerate() { - ui.horizontal(|ui| { - let mut name = egui::WidgetText::from(format!("#{}: ", geometry_idx + 1)); - if window_geometry.intersection(geometry.geometry).is_none() { - name = name.weak(); - } - - ui.label(name); - self::draw_rect(ui, &mut geometry.geometry); - }); - } - }); - - ui.horizontal(|ui| { - ui.label("Cur progress"); - - // 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 - // TODO: This max needs to be `duration - min_frame_duration` to not skip ahead. - let max = panel_state.duration().mul_f32(0.99); - let mut progress = panel_state.progress(); - self::draw_duration(ui, &mut progress, Duration::ZERO..=max); - panel_state.set_progress(progress); - }); - - ui.horizontal(|ui| { - ui.label("Fade Duration"); - let min = Duration::ZERO; - let max = panel_state.duration() / 2; - - let mut fade_duration = panel_state.fade_duration(); - self::draw_duration(ui, &mut fade_duration, min..=max); - panel_state.set_fade_duration(fade_duration); - }); - - ui.horizontal(|ui| { - ui.label("Duration"); - - let mut duration = panel_state.duration(); - self::draw_duration(ui, &mut duration, Duration::ZERO..=Duration::from_secs_f32(180.0)); - panel_state.set_duration(duration); - }); - - ui.horizontal(|ui| { - ui.label("Skip"); - if ui.button("🔄").clicked() { - panel_state.skip(wgpu).block_on(); - } - }); - - ui.collapsing("Images", |ui| { - ui.collapsing("Previous", |ui| { - self::draw_panel_image(ui, &mut panel_state.images_mut().prev); - }); - ui.collapsing("Current", |ui| { - self::draw_panel_image(ui, &mut panel_state.images_mut().cur); - }); - ui.collapsing("Next", |ui| { - self::draw_panel_image(ui, &mut panel_state.images_mut().next); - }); - }); - - ui.collapsing("Playlist", |ui| { - let playlist_player = panel_state.playlist_player().lock().block_on(); - - let row_height = ui.text_style_height(&egui::TextStyle::Body); - - ui.label(format!("Position: {}", playlist_player.cur_pos())); - - ui.label(format!( - "Remaining until shuffle: {}", - playlist_player.remaining_until_shuffle() - )); - - ui.collapsing("Items", |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); - } - }); - }); - }); -} /// Draws the settings tab fn draw_settings_tab(ui: &mut egui::Ui, event_loop_proxy: &EventLoopProxy) { @@ -238,108 +99,6 @@ fn draw_openable_path(ui: &mut egui::Ui, path: &Path) { }); } -/// Draws a panel image -fn draw_panel_image(ui: &mut egui::Ui, image: &mut PanelFadeImage) { - match image { - PanelFadeImage::Empty => { - ui.label("[Unloaded]"); - }, - PanelFadeImage::Loaded { - size, - swap_dir, - image_path, - .. - } => { - self::draw_openable_path(ui, image_path); - ui.label(format!("Size: {}x{}", size.x, size.y)); - ui.checkbox(swap_dir, "Swap direction"); - }, - } -} - -/// Draws the shader select -fn draw_shader_select(ui: &mut egui::Ui, state: &mut PanelState) { - egui::ComboBox::from_id_salt("Shader selection menu") - .selected_text(match state { - PanelState::None(_) => "None", - PanelState::Fade(_) => "Fade", - }) - .show_ui(ui, |ui| { - // TODO: Review these defaults? - type CreateShader = fn() -> PanelState; - let create_shaders: [(_, _, CreateShader); _] = [ - ("None", matches!(state, PanelState::None(_)), || { - PanelState::None(PanelNoneState::new([0.0; 4])) - }), - ("Fade", matches!(state, PanelState::Fade(_)), || { - PanelState::Fade(PanelFadeState::new( - Duration::from_secs(60), - Duration::from_secs(5), - PanelFadeShader::Out { strength: 1.5 }, - )) - }), - ]; - - for (name, checked, create_shader) in create_shaders { - if ui.selectable_label(checked, name).clicked() && !checked { - *state = create_shader(); - } - } - }); - - match state { - PanelState::None(state) => - _ = ui.horizontal(|ui| { - ui.label("Background color"); - let mut color = egui::Rgba::from_rgba_premultiplied( - state.background_color[0], - state.background_color[1], - state.background_color[2], - state.background_color[3], - ); - egui::color_picker::color_edit_button_rgba(ui, &mut color, egui::color_picker::Alpha::OnlyBlend); - state.background_color = color.to_array(); - }), - PanelState::Fade(state) => { - egui::ComboBox::from_id_salt("Fade shader menu") - .selected_text(state.shader().name()) - .show_ui(ui, |ui| { - // TODO: Not have default values here? - let shaders = [ - PanelFadeShader::Basic, - PanelFadeShader::White { strength: 1.0 }, - PanelFadeShader::Out { strength: 0.2 }, - PanelFadeShader::In { strength: 0.2 }, - ]; - for shader in shaders { - ui.selectable_value(state.shader_mut(), shader, shader.name()); - } - }); - - match state.shader_mut() { - PanelFadeShader::Basic => (), - PanelFadeShader::White { strength } => { - ui.horizontal(|ui| { - ui.label("Strength"); - egui::Slider::new(strength, 0.0..=20.0).ui(ui); - }); - }, - PanelFadeShader::Out { strength } => { - ui.horizontal(|ui| { - ui.label("Strength"); - egui::Slider::new(strength, 0.0..=2.0).ui(ui); - }); - }, - PanelFadeShader::In { strength } => { - ui.horizontal(|ui| { - ui.label("Strength"); - egui::Slider::new(strength, 0.0..=2.0).ui(ui); - }); - }, - } - }, - } -} /// Draws a geometry rectangle fn draw_rect(ui: &mut egui::Ui, geometry: &mut Rect) { diff --git a/zsw/src/settings_menu/panels.rs b/zsw/src/settings_menu/panels.rs new file mode 100644 index 0000000..7b90047 --- /dev/null +++ b/zsw/src/settings_menu/panels.rs @@ -0,0 +1,254 @@ +//! Settings menu "Panels" tab + +// Imports +use { + crate::panel::{Panel, PanelFadeImage, PanelFadeShader, PanelFadeState, PanelGeometry, PanelNoneState, PanelState}, + core::time::Duration, + egui::Widget, + zsw_util::{Rect, TokioTaskBlockOn}, + zsw_wgpu::Wgpu, +}; + +/// Draws the panels tab +pub fn draw_panels_tab(ui: &mut egui::Ui, wgpu: &Wgpu, panels: &mut [Panel], window_geometry: Rect) { + self::draw_panels_editor(ui, wgpu, panels, window_geometry); + ui.separator(); +} + +/// 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(ui: &mut egui::Ui, wgpu: &Wgpu, panels: &mut [Panel], window_geometry: Rect) { + if panels.is_empty() { + ui.label("None loaded"); + return; + } + + for panel in panels { + let mut name = egui::WidgetText::from(panel.display_name.to_string()); + if panel + .geometries + .iter() + .all(|geometry| window_geometry.intersection(geometry.geometry).is_none()) + { + name = name.weak(); + } + + ui.collapsing(name, |ui| { + match &mut panel.state { + PanelState::None(_) => (), + PanelState::Fade(state) => + self::draw_fade_panel_editor(ui, wgpu, window_geometry, state, &mut panel.geometries), + } + + ui.collapsing("Shader", |ui| { + self::draw_shader_select(ui, &mut panel.state); + }); + }); + } +} + +/// Draws the fade panel editor +fn draw_fade_panel_editor( + ui: &mut egui::Ui, + wgpu: &Wgpu, + window_geometry: Rect, + panel_state: &mut PanelFadeState, + geometries: &mut [PanelGeometry], +) { + { + let mut is_paused = panel_state.is_paused(); + ui.checkbox(&mut is_paused, "Paused"); + panel_state.set_paused(is_paused); + } + + ui.collapsing("Geometries", |ui| { + for (geometry_idx, geometry) in geometries.iter_mut().enumerate() { + ui.horizontal(|ui| { + let mut name = egui::WidgetText::from(format!("#{}: ", geometry_idx + 1)); + if window_geometry.intersection(geometry.geometry).is_none() { + name = name.weak(); + } + + ui.label(name); + super::draw_rect(ui, &mut geometry.geometry); + }); + } + }); + + ui.horizontal(|ui| { + ui.label("Cur progress"); + + // 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 + // TODO: This max needs to be `duration - min_frame_duration` to not skip ahead. + let max = panel_state.duration().mul_f32(0.99); + let mut progress = panel_state.progress(); + super::draw_duration(ui, &mut progress, Duration::ZERO..=max); + panel_state.set_progress(progress); + }); + + ui.horizontal(|ui| { + ui.label("Fade Duration"); + let min = Duration::ZERO; + let max = panel_state.duration() / 2; + + let mut fade_duration = panel_state.fade_duration(); + super::draw_duration(ui, &mut fade_duration, min..=max); + panel_state.set_fade_duration(fade_duration); + }); + + ui.horizontal(|ui| { + ui.label("Duration"); + + let mut duration = panel_state.duration(); + super::draw_duration(ui, &mut duration, Duration::ZERO..=Duration::from_secs_f32(180.0)); + panel_state.set_duration(duration); + }); + + ui.horizontal(|ui| { + ui.label("Skip"); + if ui.button("🔄").clicked() { + panel_state.skip(wgpu).block_on(); + } + }); + + ui.collapsing("Images", |ui| { + ui.collapsing("Previous", |ui| { + self::draw_panel_image(ui, &mut panel_state.images_mut().prev); + }); + ui.collapsing("Current", |ui| { + self::draw_panel_image(ui, &mut panel_state.images_mut().cur); + }); + ui.collapsing("Next", |ui| { + self::draw_panel_image(ui, &mut panel_state.images_mut().next); + }); + }); + + ui.collapsing("Playlist", |ui| { + let playlist_player = panel_state.playlist_player().lock().block_on(); + + let row_height = ui.text_style_height(&egui::TextStyle::Body); + + ui.label(format!("Position: {}", playlist_player.cur_pos())); + + ui.label(format!( + "Remaining until shuffle: {}", + playlist_player.remaining_until_shuffle() + )); + + ui.collapsing("Items", |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) { + super::draw_openable_path(ui, item); + } + }); + }); + }); +} + +/// Draws a panel image +fn draw_panel_image(ui: &mut egui::Ui, image: &mut PanelFadeImage) { + match image { + PanelFadeImage::Empty => { + ui.label("[Unloaded]"); + }, + PanelFadeImage::Loaded { + size, + swap_dir, + image_path, + .. + } => { + super::draw_openable_path(ui, image_path); + ui.label(format!("Size: {}x{}", size.x, size.y)); + ui.checkbox(swap_dir, "Swap direction"); + }, + } +} + +/// Draws the shader select +fn draw_shader_select(ui: &mut egui::Ui, state: &mut PanelState) { + egui::ComboBox::from_id_salt("Shader selection menu") + .selected_text(match state { + PanelState::None(_) => "None", + PanelState::Fade(_) => "Fade", + }) + .show_ui(ui, |ui| { + // TODO: Review these defaults? + type CreateShader = fn() -> PanelState; + let create_shaders: [(_, _, CreateShader); _] = [ + ("None", matches!(state, PanelState::None(_)), || { + PanelState::None(PanelNoneState::new([0.0; 4])) + }), + ("Fade", matches!(state, PanelState::Fade(_)), || { + PanelState::Fade(PanelFadeState::new( + Duration::from_secs(60), + Duration::from_secs(5), + PanelFadeShader::Out { strength: 1.5 }, + )) + }), + ]; + + for (name, checked, create_shader) in create_shaders { + if ui.selectable_label(checked, name).clicked() && !checked { + *state = create_shader(); + } + } + }); + + match state { + PanelState::None(state) => + _ = ui.horizontal(|ui| { + ui.label("Background color"); + let mut color = egui::Rgba::from_rgba_premultiplied( + state.background_color[0], + state.background_color[1], + state.background_color[2], + state.background_color[3], + ); + egui::color_picker::color_edit_button_rgba(ui, &mut color, egui::color_picker::Alpha::OnlyBlend); + state.background_color = color.to_array(); + }), + PanelState::Fade(state) => { + egui::ComboBox::from_id_salt("Fade shader menu") + .selected_text(state.shader().name()) + .show_ui(ui, |ui| { + // TODO: Not have default values here? + let shaders = [ + PanelFadeShader::Basic, + PanelFadeShader::White { strength: 1.0 }, + PanelFadeShader::Out { strength: 0.2 }, + PanelFadeShader::In { strength: 0.2 }, + ]; + for shader in shaders { + ui.selectable_value(state.shader_mut(), shader, shader.name()); + } + }); + + match state.shader_mut() { + PanelFadeShader::Basic => (), + PanelFadeShader::White { strength } => { + ui.horizontal(|ui| { + ui.label("Strength"); + egui::Slider::new(strength, 0.0..=20.0).ui(ui); + }); + }, + PanelFadeShader::Out { strength } => { + ui.horizontal(|ui| { + ui.label("Strength"); + egui::Slider::new(strength, 0.0..=2.0).ui(ui); + }); + }, + PanelFadeShader::In { strength } => { + ui.horizontal(|ui| { + ui.label("Strength"); + egui::Slider::new(strength, 0.0..=2.0).ui(ui); + }); + }, + } + }, + } +}