Skip to content

Remember expanded cleaner sections across sessions - #2259

Open
namelessweakl1ng wants to merge 2 commits into
bleachbit:masterfrom
namelessweakl1ng:feature/remember-tree-expansion
Open

namelessweakl1ng wants to merge 2 commits into
bleachbit:masterfrom
namelessweakl1ng:feature/remember-tree-expansion

Conversation

@namelessweakl1ng

Copy link
Copy Markdown

Summary

This PR remembers the expanded/collapsed state of top-level cleaner sections between application sessions.

Changes

  • Capture expanded top-level cleaner sections when the application exits.
  • Save the expanded cleaner IDs using the existing configuration system.
  • Restore the expansion state after the cleaner tree is rebuilt.
  • Preserve the default behavior when no saved state exists.

Fixes #2200.

@MohammedAlkindi MohammedAlkindi 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.

Nice feature — this is one I'd use. But I think the restore path never actually fires, and I was able to measure it without a GUI.

The guard and the writer disagree about where the value lives. set_list stores under a section named list/<key>; has_option(option, section='bleachbit') looks for an option named <key> inside the bleachbit section. Nothing ever creates that option, so the guard is always false:

# bleachbit/Options.py
def has_option(self, option, section='bleachbit'):
    return self.config.has_option(section, option)      # section 'bleachbit'

def set_list(self, key, values):
    section = f"list/{key}"                             # section 'list/expanded_cleaners'
    ...

Round-tripped it directly against bleachbit.Options on Windows 11 (Python 3.13.13), no GTK involved:

before write:
  has_option('expanded_cleaners') -> False
  get_list('expanded_cleaners')   -> None

after set_list(['firefox', 'system']):
  has_option('expanded_cleaners') -> False      <- the guard this PR uses
  get_list('expanded_cleaners')   -> ['firefox', 'system']   <- the data is there

sections now present: ['list/expanded_cleaners']

So capture_tree_state() writes correctly and restore_tree_state() returns at the first line every time — the sections would be saved but never re-expanded.

Suggested fix — guard on the accessor that knows about list/ sections:

expanded = options.get_list("expanded_cleaners")
if expanded is None:
    return
expanded = set(expanded)

Use is None, not a truthiness check, and this is the bit worth being deliberate about: set_list(key, []) produces get_list(key) == [], which is a meaningful state — "the user collapsed everything". if not expanded: return would skip the restore and leave whatever GTK's default expansion is, so collapsing everything wouldn't survive a restart. is None distinguishes "never saved" from "saved as empty", and the else: self.view.collapse_row(path) branch you already have then does the right thing.

Two smaller notes:

  1. model[treeiter][2] appears in both new methods as a bare column index. There's a TreeDisplayModel nearby — if it exposes a named constant for the cleaner-id column, using it would keep these from silently reading the wrong column if the model gains a field.
  2. Worth confirming the save point covers a Windows close (title-bar X / delete-event) and not just the app's own quit action — on Windows those are easy to route differently, and a state-persistence feature that misses the most common exit path would look intermittent rather than broken.

What I could not do: tests/TestGUI.py is the only file referencing GuiWindow, and it can't run here — PyGObject (gi) isn't installed on this machine and I'm not installing it. So I have not exercised the GTK path at all, and I'm not reporting a suite run as evidence. Everything above is either read from the diff or measured against bleachbit.Options in isolation.


Disclosure: I used an AI assistant while reviewing. The Options round-trip above was executed by me on the Windows machine described; the GTK-side behaviour is reasoned from the diff and explicitly untested.

@namelessweakl1ng

Copy link
Copy Markdown
Author

Thanks for catching this. I changed the restore logic to use get_list() with an explicit is None check, so an empty saved list correctly represents a fully collapsed tree. I also tested the updated behavior locally.

@az0 az0 added this to the 6.1.0 milestone Sep 11, 2026
@XhmikosR

Copy link
Copy Markdown
Contributor

@namelessweakl1ng: this needs to be properly rebased

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.

suggestion - remember sections that are collapsed or expanded

4 participants