Skip to content

refactor(tui): group view state into sub-structs, add spawn_job helper - #113

Merged
SickleFire merged 1 commit into
SickleFire:devfrom
Samanyu-dev:refactor/107-tui-state
Aug 19, 2026
Merged

SickleFire merged 1 commit into
SickleFire:devfrom
Samanyu-dev:refactor/107-tui-state

Conversation

@Samanyu-dev

Copy link
Copy Markdown
Collaborator

Summary

Addresses #107. Two changes to src/ui/tui.rs:

1. Group view state into owned sub-structs

App had 40+ flat fields, and five view types each carried their own copy of the same selected/scroll/select_next/select_prev pattern (proc_list_*, tree_*, histogram_*, settings_*, pointer_tree_*). Selection/scroll state is now a shared ViewState, and each view owns one:

#[derive(Default)]
struct ViewState {
    pub selected: usize,
    pub scroll: usize,
}

impl ViewState {
    fn next(&mut self, max: usize, wrap: bool) { ... }
    fn prev(&mut self, max: usize, wrap: bool) { ... }
}

struct AllocTreeView {
    pub rows: Vec<TreeDisplayRow>,
    pub collapsed: std::collections::HashSet<u32>,
    pub total_memory: u64,
    pub state: ViewState,
}

App now holds proc_list: ProcListView, alloc_tree: AllocTreeView, pointer_tree: PointerTreeView, histogram: HistogramView, hex_dump: HexDumpView, settings_view: SettingsView instead of ~19 loose fields, and the five duplicated *_select_next/*_select_prev impls collapse into calls like:

Focus::Tree => self.alloc_tree.state.next(self.alloc_tree.rows.len(), false),
HeapViewMode::Histogram => self.histogram.state.next(SIZE_BUCKETS.len(), false),

2. spawn_job helper for command dispatch

handle_command had eight near-identical "spawn thread → run command → send AppEvent" blocks (baseline, diff <proc>, diff <a> <b>, save, leak, leak-m, and both scan variants), each repeating the same thread-spawn boilerplate. Added:

fn spawn_job<F>(tx: std::sync::mpsc::Sender<AppEvent>, job: F)
where
    F: FnOnce(std::sync::mpsc::Sender<AppEvent>) -> Result<(), String> + Send + 'static,
{
    std::thread::spawn(move || {
        if let Err(e) = job(tx.clone()) {
            tx.send(AppEvent::Output(Line::raw(format!("error: {}", e)))).ok();
        }
    });
}

Each command's closure keeps whatever success/error events it needs to send (e.g. scan still sends ScanError on failure so is_loading resets correctly), it just no longer has to write the std::thread::spawn(move || { ... }) wrapper by hand:

["baseline", _proc] => {
    let proc = _proc.to_string();
    spawn_job(self.tx.clone(), move |tx| {
        let result = commands::scan(vec!["scan", &proc, "-h"])?;
        tx.send(AppEvent::BaseLine(result)).ok();
        tx.send(AppEvent::Output(Line::raw("Baseline set".to_string()))).ok();
        Ok(())
    });
}

No behavioral changes — this is a pure state/structure refactor to make the file easier to extend and read.

Test plan

  • cargo build — clean, no new warnings
  • cargo test — 108/108 lib tests + 10/10 integration tests pass
  • cargo fmt -- --check src/ui/tui.rs — clean
  • cargo clippy --lib — no new warnings introduced

🤖 Generated with Claude Code

… helper

App had 40+ flat fields and five duplicated select_next/select_prev
pairs (proc_list, tree, histogram, settings, pointer_tree). Each view's
selection/scroll state now lives in a shared ViewState, owned by a
per-view struct (ProcListView, AllocTreeView, PointerTreeView,
HistogramView, HexDumpView, SettingsView), collapsing the five
duplicated pairs into ViewState::next/prev.

handle_command also had six-plus near-identical
"spawn thread -> run command -> send AppEvent" blocks. A spawn_job
helper now wraps the thread spawn and sends a generic error Output
event on failure, used by baseline, diff, diff <a> <b>, save, leak,
leak-m, and both scan variants.

Closes SickleFire#107.
Copilot AI lite review requested due to automatic review settings August 19, 2026 12:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Samanyu-dev
Samanyu-dev requested a review from SickleFire August 19, 2026 12:45

@SickleFire SickleFire left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Good refactor on src/ui/tui.rs

Approved!

@SickleFire
SickleFire merged commit bb32315 into SickleFire:dev Aug 19, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants