feat: Add hierarchical tag grouping with collapsible headers#1247
feat: Add hierarchical tag grouping with collapsible headers#1247reddraconi wants to merge 5 commits intoTagStudioDev:mainfrom
Conversation
Groups thumbnails using collapsible groups based on tag selection. Thumbnails within the groups respect the user's other sorting choices. Features: - Hierarchical tag dropdown with grouping of parent/children tags in a table view so large amounts of tags can be handled (hopefully) - Collapsible/expandable groups with count badges in thumbnail view - The tags that act as headers respect the user's configuration and use the tags' configured colors - Auto-refreshes the thumbnail pane and group-by-tag dropdown on tag creation/modification no matter if it was through the tag manager or the right-hand pane - Works with all existing search/filter operations Core Library Changes: - Add `group_entries_by_tag()` method to generate a tag hierarchy - Add `get_grouping_tag_ids()` to fetch grouping tags - Extend `BrowsingState` with `group_by_tag_id` field - Add `GroupedSearchResult` dataclass with `TagGroup` hierarchy UI Components: - Added a new dropdown in main toolbar for tag selection to sort by. This works with parent tags so multiple tag groups are created in the thumbnail view. If an item has multiple sibling tags, it'll be put under a "Multiple Tags" grouping. - Implemented `GroupHeaderWidget` to create collapsible group headers - Extended `ThumbGridLayout` to render hierarchical grouped entries - Updated `update_browsing_state()` to handle grouped results Files Added: - src/tagstudio/qt/mixed/group_header.py - src/tagstudio/qt/mixed/group_by_tag_delegate.py Note: The QT components and the hierarchy generation were written with tool-assistance (Claude Code 4.5)
I got overzealous in updating the UI by starting to update the search element to handle a multi-columned layout. This isn't necessary for this feature, so I'm reverting it to main.
src/tagstudio/qt/ts_qt.py
Outdated
| block_signals: If True, block signals during population. | ||
| """ | ||
| if block_signals: | ||
| self.main_window.group_by_tag_combobox.blockSignals(True) |
There was a problem hiding this comment.
This is a QT thing so I would suggest adding the method to the ignore list for this rule.
There was a problem hiding this comment.
I added a quick check for Mock in the new code to prevent the math issues that occurred. I also went back and fixed up the linting issues caused by QT convention.
Added a check for Mock to the new code to handle tests to make sure they don't fail. Re-ran mypy and ruff. Fixes: - Added type check for view.columnWidth() to handle Mock objects in tests - Fixed test_title_update failures (There was a TypeError with Mock math) - Fixed test_add_tag_callback Qt event loop errors - Removed unused imports from group_by_tag_delegate.py (QRect, Qt) - Added type signatures for Qt method overrides (QModelIndex | QPersistentModelIndex) - Added linter suppressions for Qt API conventions (noqa: N802, FBT003) Changes: - src/tagstudio/qt/ts_qt.py: Added isinstance() check for Mock - src/tagstudio/qt/mixed/group_by_tag_delegate.py: Fixed imports and type signatures
|
Sorry for the churn. I missed the 'ruff format' command the pipeline was wanting. I'm not sure what the details are of the other CI/CD commands since they're GitHub hooks (or whatever the pre-build things are called), so hopefully I won't cause too much more pain! |
|
Instead of "Multiple Tags", I think it might be more useful to have the entry appear under each group it belongs to |
|
Also, if it's at all possible, it would be nice if the grouping functionality was more generic to allow for other types of grouping to be added in the future, such as for #68 |
Like a section that shows the common tags per sibling in the collection, or just duplicate a thumbnail into each group of the siblings where it's appropriate? I tested the first option where, like in the examples one thumbnail has "red" "green", etc. and generated a group per present sibling combination. It got pretty overwhelming and (I felt) cluttered. |
I was looking at that, but wasn't sure how to approach it, architecture-wise. Would we use something like invisible tags to do that? (e.g., a case-insensitive file type tag)? I'm happy to work on it, but didn't want to make big decisions without chatting first. |
Duplicate the thumbnails, similar to how tag categories function. Each group would sort of function as if you were searching for that tag. This is up to you though, this is just how I would personally prefer it to function. |
I can do that pretty easily. I was doing it like that in my first round of testing. I guess we could also make it a user option. Not sure how much more code complexity that would add, though. |
I can try taking a look at it once I get the chance, I haven't looked to closely into how you've handled grouping yet |
Rewrote the grouping system to make it extensible using grouping "strategies" like file extensions, file sizes, tags (already here), or others I haven't thought of yet, ## Changes - Added GroupingType enum, GroupingCriteria, and EntryGroup abstractions - Created TagGroupingStrategy and FiletypeGroupingStrategy - Updated BrowsingState to use GroupingCriteria instead of group_by_tag_id - Added strategy registry in Library class for future grouping strats ## Group-by-tag changes: - Tag grouping duplicates entries across multiple child tag groups insteaad of using a "Multiple Tags" catch-all. - Added "By Filetype" grouping option - Group headers now support generic group types (tags, filetypes, etc.) and will jam a separator between custom strategies and tags. - Empty child tag groups are now hidden instead of being placed in the "No Tag" group ## New stuff: - src/tagstudio/core/library/alchemy/grouping.py - Core grouping strat abstractions - src/tagstudio/core/library/alchemy/strategies.py - Grouping Implementations Note: I used ClaudeCode 4.5 to help out with the QT code.
|
Moved this back in to draft since I don't want to trigger the CI/CDD pipeline needlessly. Changes from last push
Feature Planning / ImplementationI've attempted to genericise the tag sorting code to support multiple grouping strategies. I've tested this successfully on my local system and I think I'm ready for feedback. I also made Claude come up with an example of adding a new strategy. It chose a group-by-date strategy (I haven't tested it because it doesn't account for locales or translations, but it's a solid example): ClaudeCode 4.5 Example (Untested) follows This example shows how to add a "Group by Date" feature that organizes entries by creation date. Step 1: Add Enum ValueEdit Step 2: Implement StrategyCreate or edit Step 3: Register StrategyEdit Step 4: Add UI IntegrationTo expose the new grouping in the UI, edit Key ConceptsGroupingStrategy InterfaceAll strategies must implement:
EntryGroup StructureGroups contain:
GroupingCriteriaSpecifies what to group by:
Advanced Example: Custom MetadataStrategies can attach metadata to groups for UI rendering: TestingAdd tests in Performance Considerations
TODO
|
Adds a feature to group thumbnails in the main window by a tag or by a parent tag.
Grouped thumbnails in each section respect the user's other sorting choices (filename/date created, sort direction, etc.)
Features:
Core Library Changes:
group_entries_by_tag()method to generate a tag hierarchyget_grouping_tag_ids()to fetch grouping tagsBrowsingStatewithgroup_by_tag_idfieldGroupedSearchResultdataclass withTagGrouphierarchyUI Components:
GroupHeaderWidgetto create collapsible group headersThumbGridLayoutto render hierarchical grouped entriesupdate_browsing_state()to handle grouped resultsFiles Added:
Notes:
black -l100, mypy, and ruff. I did my best to stick to the existing coding style.delete_tag()only containedpass. I implemented a fulldelete_tag()method to fix this so the sort-by-tag dropdown would be able to automatically update correctly.Summary
Adds a feature to group thumbnails in the main window by a single tag or by a parent tag.
Grouped thumbnails in each section respect the user's other sorting choices (filename/date created, sort direction, etc.)
Tasks Completed
Screenshots
New Toolbar Dropdown
Selecting a Parent Tag to Group By
Sorting complete
Example Item with Multiple Sibling Tags