Skip to content

PICARD-3196: Implement custom persistence for column state in ConfigurableColumnsHeader#3043

Merged
phw merged 4 commits intometabrainz:masterfrom
phw:PICARD-3196-fix-column-persistence
Feb 24, 2026
Merged

PICARD-3196: Implement custom persistence for column state in ConfigurableColumnsHeader#3043
phw merged 4 commits intometabrainz:masterfrom
phw:PICARD-3196-fix-column-persistence

Conversation

@phw
Copy link
Member

@phw phw commented Feb 15, 2026

Summary

  • This is a…
    • Bug fix
    • Feature addition
    • Refactoring
    • Minor / simple change (like a typo)
    • Other
  • Describe this change in 1-2 sentences:

Problem

Changing the default columns for the main views can break the column display as the persisted state does not match the column model anymore. See e.g. #3040 and the example screenshot there.

Solution

Instead of relying on an opaque binary structure based on logical column indices use a custom structure which stores the state of each column based on its name. This fixes issues with column state breaking if the indices of the default columns change, e.g. when new default columns are addded or removed.

As there is not clean upgrade path there is a config upgrade to remove the old states. The columns will reset to their default state on upgrade.

AI Usage

In accordance with the AI use policy portion of the MetaBrainz Contribution Guidelines, the level of AI/LLM use in the development of this Pull Request is:

  • No AI/LLM use
  • Minimal use (e.g. autocompletion)
  • Moderate use (e.g. suggestions regarding code fragments)
  • Significant use (e.g. code structure, tests development, etc.)
  • Primarily AI developed
  • Other (please specify below)

Action

Additional actions required:

  • Update Picard documentation (please include a reference to this PR)
  • Other (please specify below)

…rableColumnsHeader

Instead of relying on an opaque binary structure based on logical column
indices use a custom structure which stores the state of each column based
on its name. This fixes issues with column state breaking if the indices
of the default columns change, e.g. when new default columns are addded
or removed.

As there is not clean upgrade path there is a config upgrade to remove the
old states. The columns will reset to their default state on upgrade.
@phw
Copy link
Member Author

phw commented Feb 20, 2026

I found some other issue: After unlocking and then adding / removing columns the header does not directly update. Only after clicking on the header again. It's rather strange, but I cannot reproduce on master.

@phw phw requested a review from zas February 24, 2026 07:27
Specifically setting the width for the last "stretch" column broke adding
new columns to the view.
@phw phw force-pushed the PICARD-3196-fix-column-persistence branch from 12a85ae to e2e1216 Compare February 24, 2026 07:31
@phw
Copy link
Member Author

phw commented Feb 24, 2026

@zas Please test again. I fixed the issues we discussed above. I re-implemented the lock handling to not interfere with the default Qt based state restore.

The issue about not being able to add new columns after restore was caused by setting the width for all columns. If you run resizeSection on a column that has resize mode set to "stretch" (the last column in the view) Qt gets confused.

@zas
Copy link
Collaborator

zas commented Feb 24, 2026

@zas Please test again. I fixed the issues we discussed above. I re-implemented the lock handling to not interfere with the default Qt based state restore.

The issue about not being able to add new columns after restore was caused by setting the width for all columns. If you run resizeSection on a column that has resize mode set to "stretch" (the last column in the view) Qt gets confused.

Actually found some issues when reviewing and testing.
They should be addressed by phw#29
I added extensive comments and split the code in smaller methods since this code is rather obscure (as it depends on Qt internals).
Tested heavily, I tried to move columns, lock, remove, change sort indicator, etc, and it seems to work in all cases now.

The restore_columns_state() method had a critical bug that prevented
correct restoration of column positions:

Columns were repositioned during iteration in logical index order, but
Qt's moveSection() operates on visual indices. Each move changes the
visual positions of other columns, causing cascading displacement that
prevented correct restoration of the saved layout.

This commit refactors the method into two clear phases:

Phase 1: Restore visibility, resize modes, widths, and sort indicator
  - Must be done first as visibility/resize modes affect moveSection()
  - Sort indicator uses logical indices, independent of positioning

Phase 2: _restore_column_positions()
  - Processes target positions in ascending order (0, 1, 2, ...)
  - This ensures each column reaches its final position without being
    displaced by subsequent moves
  - Also fixes position 0 exclusion (currently not an issue since first
    columns are unmovable, but future-proofs the code)

The code includes inline comments explaining Qt's behavior and the
rationale for the specific ordering.

Also cleaned up redundant `.get(column.key, None)` calls to use the
default `.get(column.key)` behavior.
Copy link
Collaborator

@zas zas left a comment

Choose a reason for hiding this comment

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

LGTM

@phw phw merged commit 765bd7c into metabrainz:master Feb 24, 2026
51 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.

2 participants