use std::path::Path;
use crate::app::RunUblxParams;
use crate::config::{
LayoutOverlay, Osc11BackgroundFormat, UblxOpts, UblxOverlay, UblxPaths, load_ublx_toml,
strip_global_only_keys_from_local_overlay, write_ublx_overlay_at,
};
use crate::handlers::state_transitions::PREVIEW_SCROLL_STEP_LINES;
use crate::layout::setup::{SettingsConfigScope, UblxState};
use crate::ui::{UblxAction, show_operation_toast};
use crate::utils::{clamp_selection, opacity_is_solid};
use super::apply_config_reload;
use super::bool_rows;
use super::context;
use super::layout_edit;
pub fn on_enter_settings(state_mut: &mut UblxState, params_ref: &RunUblxParams<'_>) {
state_mut.settings.left_cursor = 0;
state_mut.settings.right_scroll = 0;
state_mut.settings.layout_unlocked = false;
state_mut.settings.opacity_unlocked = false;
context::refresh_editing_metadata(state_mut, params_ref);
let scope = state_mut.settings.scope;
state_mut.settings.left_cursor = clamp_selection(
state_mut.settings.left_cursor,
layout_edit::left_cursor_len(&state_mut.settings, scope),
);
}
fn persist_overlay_reload_refresh(
path: &Path,
overlay: &UblxOverlay,
scope: SettingsConfigScope,
state_mut: &mut UblxState,
params_mut: &mut RunUblxParams<'_>,
ublx_opts_mut: &mut UblxOpts,
after_apply: impl FnOnce(&mut UblxState),
) {
let mut to_write = overlay.clone();
if scope == SettingsConfigScope::Local {
strip_global_only_keys_from_local_overlay(&mut to_write);
}
write_ublx_overlay_at(path, &to_write);
state_mut.config_written_by_us_at = Some(std::time::Instant::now());
apply_config_reload(params_mut, ublx_opts_mut, state_mut, None::<&str>);
after_apply(state_mut);
context::refresh_editing_metadata(state_mut, params_mut);
}
fn submit_settings_bool_row(
state_mut: &mut UblxState,
params_mut: &mut RunUblxParams<'_>,
ublx_opts_mut: &mut UblxOpts,
scope: SettingsConfigScope,
cur: usize,
) {
let Some(path) = state_mut.settings.editing_path.clone() else {
return;
};
let paths = UblxPaths::new(¶ms_mut.dir_to_ublx);
let mut overlay = load_ublx_toml(Some(path.clone()), None).unwrap_or_default();
let merged_before = context::merged_overlay_before_write(&paths, scope, &overlay);
let v = !bool_rows::overlay_bool(&merged_before, scope, cur);
bool_rows::write_bool(&mut overlay, scope, cur, v);
persist_overlay_reload_refresh(
path.as_path(),
&overlay,
scope,
state_mut,
params_mut,
ublx_opts_mut,
|_| {},
);
let label = bool_rows::bool_row_label(scope, cur, false);
let msg = format!("{label} = {v}");
show_operation_toast(
state_mut,
params_mut,
msg,
"settings-bool",
log::Level::Info,
);
}
fn submit_settings_opacity_format_row(
state_mut: &mut UblxState,
params_mut: &mut RunUblxParams<'_>,
ublx_opts_mut: &mut UblxOpts,
scope: SettingsConfigScope,
) {
let Some(path) = state_mut.settings.editing_path.clone() else {
return;
};
let paths = UblxPaths::new(¶ms_mut.dir_to_ublx);
let mut overlay = load_ublx_toml(Some(path.clone()), None).unwrap_or_default();
let merged_before = context::merged_overlay_before_write(&paths, scope, &overlay);
let current = merged_before.opacity_format.unwrap_or_default();
let next = match current {
Osc11BackgroundFormat::Rgba => Osc11BackgroundFormat::Hex8,
Osc11BackgroundFormat::Hex8 => Osc11BackgroundFormat::Rgba,
};
overlay.opacity_format = Some(next);
persist_overlay_reload_refresh(
path.as_path(),
&overlay,
scope,
state_mut,
params_mut,
ublx_opts_mut,
|_| {},
);
let fmt_label = match next {
Osc11BackgroundFormat::Rgba => "rgba",
Osc11BackgroundFormat::Hex8 => "hex8",
};
show_operation_toast(
state_mut,
params_mut,
format!("opacity_format = {fmt_label}"),
"settings-opacity-format",
log::Level::Info,
);
}
fn submit_settings_layout_row(
state_mut: &mut UblxState,
params_mut: &mut RunUblxParams<'_>,
ublx_opts_mut: &mut UblxOpts,
scope: SettingsConfigScope,
layout_btn_index: usize,
) {
if state_mut.settings.layout_unlocked {
let Some(path) = state_mut.settings.editing_path.clone() else {
return;
};
let Some((l, m, r)) = layout_edit::parse_layout_triplet(
&state_mut.settings.layout_left_buf,
&state_mut.settings.layout_mid_buf,
&state_mut.settings.layout_right_buf,
) else {
show_operation_toast(
state_mut,
params_mut,
"layout: three u8 must sum to 100",
"settings-layout",
log::Level::Warn,
);
return;
};
let mut overlay = load_ublx_toml(Some(path.clone()), None).unwrap_or_default();
overlay.layout = Some(LayoutOverlay {
left_pct: l,
middle_pct: m,
right_pct: r,
});
persist_overlay_reload_refresh(
path.as_path(),
&overlay,
scope,
state_mut,
params_mut,
ublx_opts_mut,
|s| {
s.settings.layout_unlocked = false;
},
);
state_mut.settings.left_cursor = clamp_selection(
layout_btn_index,
layout_edit::left_cursor_len(&state_mut.settings, scope),
);
show_operation_toast(
state_mut,
params_mut,
format!("layout {l}/{m}/{r}"),
"settings-layout",
log::Level::Info,
);
} else {
state_mut.settings.layout_unlocked = true;
let paths = UblxPaths::new(¶ms_mut.dir_to_ublx);
if scope == SettingsConfigScope::Local {
let (local_o, merged) = context::local_edit_context(&paths);
let lay_src = context::layout_overlay_for_local_editing(local_o.as_ref(), &merged);
context::sync_layout_buffers_from_overlay(&mut state_mut.settings, lay_src);
} else if let Some(path) = state_mut.settings.editing_path.clone()
&& let Some(overlay) = load_ublx_toml(Some(path), None)
{
context::sync_layout_buffers_from_overlay(&mut state_mut.settings, &overlay);
}
state_mut.settings.left_cursor =
(layout_btn_index + 1).min(layout_edit::max_left_cursor(&state_mut.settings, scope));
}
}
fn submit_settings_bg_opacity_row(
state_mut: &mut UblxState,
params_mut: &mut RunUblxParams<'_>,
ublx_opts_mut: &mut UblxOpts,
scope: SettingsConfigScope,
op_btn: usize,
) {
if state_mut.settings.opacity_unlocked {
let Some(path) = state_mut.settings.editing_path.clone() else {
return;
};
let Some(v) = layout_edit::parse_bg_opacity(&state_mut.settings.opacity_buf) else {
show_operation_toast(
state_mut,
params_mut,
"bg_opacity: enter a number from 0.0 to 1.0",
"settings-opacity",
log::Level::Warn,
);
return;
};
let mut overlay = load_ublx_toml(Some(path.clone()), None).unwrap_or_default();
overlay.bg_opacity = if opacity_is_solid(v) { None } else { Some(v) };
persist_overlay_reload_refresh(
path.as_path(),
&overlay,
scope,
state_mut,
params_mut,
ublx_opts_mut,
|s| {
s.settings.opacity_unlocked = false;
},
);
state_mut.settings.left_cursor = clamp_selection(
op_btn,
layout_edit::left_cursor_len(&state_mut.settings, scope),
);
show_operation_toast(
state_mut,
params_mut,
format!("bg_opacity = {v}"),
"settings-opacity",
log::Level::Info,
);
} else {
state_mut.settings.opacity_unlocked = true;
let paths = UblxPaths::new(¶ms_mut.dir_to_ublx);
if scope == SettingsConfigScope::Local {
let (local_o, merged) = context::local_edit_context(&paths);
let op_src = context::opacity_overlay_for_local_editing(local_o.as_ref(), &merged);
context::sync_opacity_buffer_from_overlay(&mut state_mut.settings, op_src);
} else if let Some(path) = state_mut.settings.editing_path.clone()
&& let Some(overlay) = load_ublx_toml(Some(path), None)
{
context::sync_opacity_buffer_from_overlay(&mut state_mut.settings, &overlay);
}
state_mut.settings.left_cursor =
(op_btn + 1).min(layout_edit::max_left_cursor(&state_mut.settings, scope));
}
}
fn handle_settings_search_submit(
state_mut: &mut UblxState,
params_mut: &mut RunUblxParams<'_>,
ublx_opts_mut: &mut UblxOpts,
scope: SettingsConfigScope,
) {
let layout_btn = layout_edit::layout_button_index(scope);
let cur = state_mut.settings.left_cursor;
if cur < bool_rows::bool_row_count(scope) {
submit_settings_bool_row(state_mut, params_mut, ublx_opts_mut, scope, cur);
} else if Some(cur) == layout_edit::opacity_format_row_index(scope) {
submit_settings_opacity_format_row(state_mut, params_mut, ublx_opts_mut, scope);
} else if cur == layout_btn {
submit_settings_layout_row(state_mut, params_mut, ublx_opts_mut, scope, layout_btn);
} else if cur == layout_edit::opacity_button_index(&state_mut.settings, scope) {
submit_settings_bg_opacity_row(state_mut, params_mut, ublx_opts_mut, scope, cur);
}
}
#[must_use]
pub fn handle_key(
state_mut: &mut UblxState,
params_mut: &mut RunUblxParams<'_>,
ublx_opts_mut: &mut UblxOpts,
action: UblxAction,
) -> bool {
let scope = state_mut.settings.scope;
match action {
UblxAction::Tab => {
state_mut.settings.scope = match state_mut.settings.scope {
SettingsConfigScope::Global => SettingsConfigScope::Local,
SettingsConfigScope::Local => SettingsConfigScope::Global,
};
state_mut.settings.layout_unlocked = false;
state_mut.settings.opacity_unlocked = false;
context::refresh_editing_metadata(state_mut, params_mut);
let sc = state_mut.settings.scope;
state_mut.settings.left_cursor = clamp_selection(
state_mut.settings.left_cursor,
layout_edit::left_cursor_len(&state_mut.settings, sc),
);
true
}
UblxAction::ScrollPreviewUp => {
state_mut.settings.right_scroll = state_mut
.settings
.right_scroll
.saturating_sub(PREVIEW_SCROLL_STEP_LINES);
true
}
UblxAction::ScrollPreviewDown => {
state_mut.settings.right_scroll = state_mut
.settings
.right_scroll
.saturating_add(PREVIEW_SCROLL_STEP_LINES);
true
}
UblxAction::PreviewTop => {
state_mut.settings.right_scroll = 0;
true
}
UblxAction::PreviewBottom => {
state_mut.settings.right_scroll = u16::MAX;
true
}
UblxAction::MoveDown | UblxAction::MoveDownFast => {
layout_edit::bump_settings_cursor(state_mut, scope, true);
true
}
UblxAction::MoveUp | UblxAction::MoveUpFast => {
layout_edit::bump_settings_cursor(state_mut, scope, false);
true
}
UblxAction::FocusCategories
| UblxAction::FocusContents
| UblxAction::ListTop
| UblxAction::ListBottom
| UblxAction::CycleContentSort
| UblxAction::CycleRightPane
| UblxAction::RightPaneViewer
| UblxAction::RightPaneTemplates
| UblxAction::RightPaneMetadata
| UblxAction::RightPaneWriting
| UblxAction::ViewerFullscreenToggle => true,
UblxAction::SearchSubmit => {
handle_settings_search_submit(state_mut, params_mut, ublx_opts_mut, scope);
true
}
_ => false,
}
}