Moved panels tab to it's own module.

This commit is contained in:
Filipe Rodrigues 2025-09-09 21:28:24 +01:00
parent b5c2fe854e
commit b272a2d78d
Signed by: zenithsiz
SSH Key Fingerprint: SHA256:Mb5ppb3Sh7IarBO/sBTXLHbYEOz37hJAlslLQPPAPaU
2 changed files with 260 additions and 247 deletions

View File

@ -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<i32, u32>) {
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<i32, u32>) {
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<i32, u32>,
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<AppEvent>) {
@ -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<i32, u32>) {

View File

@ -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<i32, u32>) {
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<i32, u32>) {
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<i32, u32>,
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);
});
},
}
},
}
}