ODS polish + reusable confirm modal + file-dialog focus/overwrite fixes.

ODS writer:
- Header row styled bold on a grey background; data rows alternate
  light-grey / white (zebra). Auto-filter buttons enabled per sheet
  via `<table:database-range table:display-filter-buttons="true">`
  — sheet names are now single-quoted in the target-range-address so
  LibreOffice actually parses ranges that contain spaces.
- First row of every sheet frozen via `settings.xml` (view setting).
  The settings layout follows LibreOffice's own writer exactly:
  `xmlns:ooo` + `xmlns:xlink` declared on the root, `ActiveTable` and
  view-level zoom/grid/headers placed *after* the Tables map (the
  order LibreOffice expects to read back). HorizontalSplitMode = 0,
  VerticalSplitMode = 2 → only the first row is frozen, the first
  column scrolls normally. ActiveSplitRange = 2 (bottom-left pane).
  `settings.xml` registered in `META-INF/manifest.xml`.

Reusable Yes/No modal:
- New `screen_confirm.cpp` exposing `Tui::ShowConfirm(msg, on_yes)`.
  Centred `borderRounded` popup, `No` first (safer default), Enter
  confirms the focused button, Esc cancels (treated as No).
- Stacked into the Modal chain in `Run()` (between file-dialog and
  error). The outer `CatchEvent` cedes events when it's open.

File dialog:
- Picks `OK` button focus reliably — previously the focus indices in
  the Renderer were remapped against the Container::Vertical child
  indices, so the OK label only flagged "focused" while the actual
  focused child was the filename input.
- OK button uses a custom `ButtonOption::transform` that renders the
  label transparent when idle, inverted when focused. No more
  cyan-fill, no more double-inversion via FocusLabel.
- After confirmation, if the picked path already exists, pops a
  `ShowConfirm("File … already exists. Overwrite?")` before invoking
  the caller's action.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-16 13:36:05 +02:00
parent aa59d1a041
commit 3be5bc3f6e
5 changed files with 288 additions and 26 deletions

View File

@@ -112,6 +112,11 @@ class Tui {
bool error_open = false;
std::string error_message;
// ---- Generic Yes/No confirmation dialog (modal) ----
bool confirm_open = false;
std::string confirm_message;
std::function<void()> confirm_on_yes;
// ---- Generic file-picker dialog (modal) ----
// Reused for any "pick a path" interaction (export, save, restore, …).
// `persist_key` ties the dir + filename to a key persisted under the
@@ -251,10 +256,15 @@ private:
ftxui::Component BuildHelpScreen();
ftxui::Component BuildFileDialog();
ftxui::Component BuildErrorModal();
ftxui::Component BuildConfirmModal();
// Pop a centred modal with `msg` and an OK button. Esc / Enter close
// it. Use for actionable failures the user must see (write errors,
// bad inputs, etc.) — for normal feedback keep `Print()`.
void ShowError(const std::string &msg);
// Pop a centred Yes/No modal. `on_yes` runs only if the user picks
// Yes (Esc and No cancel). Use for destructive confirmations
// (overwrite a file, delete a snapshot, …).
void ShowConfirm(const std::string &msg, std::function<void()> on_yes);
// Open the picker modal. `persist_key` controls where the last-used
// dir + filename are stored (one tiny file per key under the user
// data directory). `on_confirm` runs when the user presses Enter on