use std::collections::HashSet;
use crate::SettingsUI;
use crate::section::collapsing_section;
use super::uniform_controls::show_shader_uniform_controls;
use super::utils::show_reset_button;
use crate::background_tab::shader_channel_settings::{
save_settings_to_shader_metadata, show_per_shader_channel_settings,
};
pub(super) fn show_per_shader_settings(
ui: &mut egui::Ui,
settings: &mut SettingsUI,
shader_name: &str,
metadata: &Option<par_term_config::ShaderMetadata>,
changes_this_frame: &mut bool,
collapsed: &mut HashSet<String>,
) {
let has_override = settings.config.shader_configs.contains_key(shader_name);
let meta_defaults = metadata.as_ref().map(|m| m.defaults.clone());
let current_override = settings.config.shader_configs.get(shader_name).cloned();
{
let effective_value = current_override
.as_ref()
.and_then(|o| o.animation_speed)
.or_else(|| meta_defaults.as_ref().and_then(|m| m.animation_speed))
.unwrap_or(settings.config.shader.custom_shader_animation_speed);
let has_override_val = current_override
.as_ref()
.and_then(|o| o.animation_speed)
.is_some();
let mut value = effective_value;
ui.horizontal(|ui| {
ui.label("Animation speed:");
let response = ui.add(egui::Slider::new(&mut value, 0.0..=5.0));
if response.changed() {
let override_entry = settings.config.get_or_create_shader_override(shader_name);
override_entry.animation_speed = Some(value);
settings.has_changes = true;
*changes_this_frame = true;
}
if show_reset_button(ui, has_override_val)
&& let Some(override_entry) = settings.config.shader_configs.get_mut(shader_name)
{
override_entry.animation_speed = None;
settings.has_changes = true;
*changes_this_frame = true;
}
});
}
{
let effective_value = current_override
.as_ref()
.and_then(|o| o.brightness)
.or_else(|| meta_defaults.as_ref().and_then(|m| m.brightness))
.unwrap_or(settings.config.shader.custom_shader_brightness);
let has_override_val = current_override
.as_ref()
.and_then(|o| o.brightness)
.is_some();
let mut value = effective_value;
ui.horizontal(|ui| {
ui.label("Brightness:");
let response = ui.add(
egui::Slider::new(&mut value, 0.05..=1.0)
.custom_formatter(|v, _| format!("{:.0}%", v * 100.0)),
);
if response.changed() {
let override_entry = settings.config.get_or_create_shader_override(shader_name);
override_entry.brightness = Some(value);
settings.has_changes = true;
*changes_this_frame = true;
}
if show_reset_button(ui, has_override_val)
&& let Some(override_entry) = settings.config.shader_configs.get_mut(shader_name)
{
override_entry.brightness = None;
settings.has_changes = true;
*changes_this_frame = true;
}
});
}
{
let effective_value = current_override
.as_ref()
.and_then(|o| o.text_opacity)
.or_else(|| meta_defaults.as_ref().and_then(|m| m.text_opacity))
.unwrap_or(settings.config.shader.custom_shader_text_opacity);
let has_override_val = current_override
.as_ref()
.and_then(|o| o.text_opacity)
.is_some();
let mut value = effective_value;
ui.horizontal(|ui| {
ui.label("Text opacity:");
let response = ui.add(egui::Slider::new(&mut value, 0.0..=1.0));
if response.changed() {
let override_entry = settings.config.get_or_create_shader_override(shader_name);
override_entry.text_opacity = Some(value);
settings.has_changes = true;
*changes_this_frame = true;
}
if show_reset_button(ui, has_override_val)
&& let Some(override_entry) = settings.config.shader_configs.get_mut(shader_name)
{
override_entry.text_opacity = None;
settings.has_changes = true;
*changes_this_frame = true;
}
});
}
{
let effective_value = current_override
.as_ref()
.and_then(|o| o.full_content)
.or_else(|| meta_defaults.as_ref().and_then(|m| m.full_content))
.unwrap_or(settings.config.shader.custom_shader_full_content);
let has_override_val = current_override
.as_ref()
.and_then(|o| o.full_content)
.is_some();
let mut value = effective_value;
ui.horizontal(|ui| {
if ui
.checkbox(&mut value, "Full content mode")
.on_hover_text("Shader receives and can manipulate full terminal content")
.changed()
{
let override_entry = settings.config.get_or_create_shader_override(shader_name);
override_entry.full_content = Some(value);
settings.has_changes = true;
*changes_this_frame = true;
}
if show_reset_button(ui, has_override_val)
&& let Some(override_entry) = settings.config.shader_configs.get_mut(shader_name)
{
override_entry.full_content = None;
settings.has_changes = true;
*changes_this_frame = true;
}
});
}
{
let effective_value = current_override
.as_ref()
.and_then(|o| o.use_background_as_channel0)
.or_else(|| {
meta_defaults
.as_ref()
.and_then(|m| m.use_background_as_channel0)
})
.unwrap_or(
settings
.config
.shader
.custom_shader_use_background_as_channel0,
);
let has_override_val = current_override
.as_ref()
.and_then(|o| o.use_background_as_channel0)
.is_some();
let mut value = effective_value;
ui.horizontal(|ui| {
if ui
.checkbox(&mut value, "Use background as iChannel0")
.on_hover_text(
"Use the app's background (image or solid color) as iChannel0 instead of a separate texture file",
)
.changed()
{
let override_entry = settings.config.get_or_create_shader_override(shader_name);
override_entry.use_background_as_channel0 = Some(value);
settings.has_changes = true;
*changes_this_frame = true;
}
if show_reset_button(ui, has_override_val)
&& let Some(override_entry) = settings.config.shader_configs.get_mut(shader_name)
{
override_entry.use_background_as_channel0 = None;
settings.has_changes = true;
*changes_this_frame = true;
}
});
}
ui.add_space(4.0);
let meta_defaults_for_channels = meta_defaults.clone();
collapsing_section(
ui,
"Channel Textures",
"per_shader_channels",
false,
collapsed,
|ui| {
show_per_shader_channel_settings(
ui,
settings,
shader_name,
meta_defaults_for_channels.as_ref(),
changes_this_frame,
);
},
);
show_shader_uniform_controls(ui, settings, shader_name, metadata, changes_this_frame);
if has_override {
ui.add_space(8.0);
if ui
.button("Reset All Overrides")
.on_hover_text("Remove all per-shader overrides and use defaults")
.clicked()
{
settings.config.remove_shader_override(shader_name);
settings.has_changes = true;
*changes_this_frame = true;
}
}
ui.add_space(8.0);
ui.separator();
ui.add_space(4.0);
ui.horizontal(|ui| {
if ui
.button("\u{1F4BE} Save Defaults to Shader")
.on_hover_text(
"Write the current effective settings as defaults in the shader file's metadata block.\n\
This will update or create the /*! par-term shader metadata ... */ block.",
)
.clicked()
{
save_settings_to_shader_metadata(settings, shader_name, metadata);
}
if ui
.button("\u{1F50E} Run Lint")
.on_hover_text("Validate this shader and show readability suggestions")
.clicked()
{
settings.run_shader_lint_for_selected_shader();
}
let has_lint_output = settings.shader_lint_result.is_some() || settings.shader_lint_error.is_some();
if ui
.add_enabled(has_lint_output, egui::Button::new("Clear Lint"))
.on_hover_text("Clear the current shader lint/readability output")
.clicked()
{
settings.clear_shader_lint_result();
}
});
show_shader_lint_result(ui, settings);
}
fn show_shader_lint_result(ui: &mut egui::Ui, settings: &SettingsUI) {
if let Some(error) = &settings.shader_lint_error {
ui.add_space(4.0);
ui.colored_label(
egui::Color32::from_rgb(255, 120, 120),
format!("Shader lint failed: {error}"),
);
}
if let Some(result) = &settings.shader_lint_result {
let mut display = result.as_str();
ui.add_space(4.0);
egui::Frame::default()
.fill(egui::Color32::from_rgb(28, 34, 42))
.inner_margin(8.0)
.corner_radius(4.0)
.show(ui, |ui| {
ui.horizontal(|ui| {
ui.label("Shader lint result");
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if ui.small_button("Copy").clicked() {
ui.ctx().copy_text(result.clone());
}
});
});
ui.separator();
ui.add(
egui::TextEdit::multiline(&mut display)
.font(egui::TextStyle::Monospace)
.desired_width(f32::INFINITY)
.desired_rows(10)
.interactive(false),
);
});
}
}