Skip to content

Commit b3a2c03

Browse files
authored
refactor(ui): extend inspector and thumb grid, floating pagination bar, MVC splits (#1531)
* ui: tweak UI, full height inspector, floating pagination bar * ui: update search results text style * refactor(ui): MVC split pagination widget * refactor(ui): MVC split + rename FieldContainer(s) to EntryDataBox(List) * refactor(ui): use constants for common padding and radius values * refactor(ui): rename FieldContainers to Tiles * fix: wacky loop in color_data.py * fix: clean up tile_list.py, remove redundant disconnect step * refactor: move leftover controller logic on TagDataView to TagData * refactor: more tweaks in pagination.py * refactor(ui): MVC split PreviewThumb + PreviewThumbView * fix(ui): use consistent corner radius for preview thumb * fix(ui): fix scaling and updating issues with previews * chore: update stale class names in test comments
1 parent f9a6129 commit b3a2c03

56 files changed

Lines changed: 1543 additions & 1957 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎LICENSES/CC-BY-SA-4.0.txt‎

Lines changed: 0 additions & 428 deletions
This file was deleted.

‎README.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ Tags currently consist of the following attributes:
6969
- **Parent Tags**: Other tags in which this tag inherits from. In practice, this means that this tag can be substituted in searches for any listed parent tags.
7070
- Parent tags checked with the "disambiguation" checkbox next to them will be used to help disambiguate tag names that may not be unique.
7171
- For example: If you had a tag for "Freddy Fazbear", you might add "Five Nights at Freddy's" as one of the parent tags. If the disambiguation box is checked next to "Five Nights at Freddy's" parent tag, then the tag "Freddy Fazbear" will display as "Freddy Fazbear (Five Nights at Freddy's)". Furthermore, if the "Five Nights at Freddy's" tag has a shorthand like "FNAF", then the "Freddy Fazbear" tag will display as "Freddy Fazbear (FNAF)".
72-
- **Is Category**: A property that when checked, treats this tag as a category in the preview panel.
72+
- **Is Category**: A property that when checked, treats this tag as a category in the Inspector panel.
7373

7474
Fields, like tags, are additional pieces of custom metadata that you can add to your file entries. Fields currently have several hardcoded names (e.g. "Title", "Author", "Series") but custom field names are planned for an upcoming update.
7575

@@ -108,19 +108,19 @@ You can manage your library of tags from opening the "Tag Manager" panel from Ed
108108

109109
### Editing Tags
110110

111-
To edit a tag, click on it inside the preview panel or right-click the tag and select "Edit Tag" from the context menu.
111+
To edit a tag, click on it inside the Inspector or right-click the tag and select "Edit Tag" from the context menu.
112112

113113
### Adding Tags to File Entries
114114

115115
Access the "Add Tag" search box by either clicking on the "Add Tag" button at the bottom of the right sidebar, accessing the "Add Tags to Selected" option from the File menu, or by pressing <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>T</kbd> (macOS: <kbd>⌘ Command</kbd>+<kbd>Shift</kbd>+<kbd>T</kbd>).
116116

117117
From here you can search for existing tags or create a new one if the one you're looking for doesn't exist. Click the "+" button next to any tags you want to the currently selected file entries. To quickly add the top result, press the <kbd>Enter</kbd>/<kbd>Return</kbd> key to add the top-most tag and reset the tag search. Press <kbd>Enter</kbd>/<kbd>Return</kbd> once more to close the dialog box. By using this method, you can quickly add various tags in quick succession just by using the keyboard!
118118

119-
To remove a tag from a file entry, hover over the tag in the preview panel and click on the "-" icon that appears.
119+
To remove a tag from a file entry, hover over the tag in the Inspector and click on the "-" icon that appears.
120120

121121
### Adding Fields to File Entries
122122

123-
To add a metadata field to a file entry, start by clicking the "Add Field" button at the bottom of the preview panel. From the dropdown menu, select the type of metadata field you’d like to add to the entry
123+
To add a metadata field to a file entry, start by clicking the "Add Field" button at the bottom of the Inspector. From the dropdown menu, select the type of metadata field you’d like to add to the entry
124124

125125
### Editing Fields
126126

‎docs/entries.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ File entry data is stored within the `ts_library.sqlite` file inside each librar
1616

1717
## Appearance
1818

19-
File entries appear as thumbnails inside the grid display. The inspector shows a more detailed preview of the file, along with extra file stats and all attached TagStudio tags and fields.
19+
File entries appear as thumbnails inside the grid display. The Inspector panel shows a more detailed preview of the file, along with extra file stats and all attached TagStudio tags and fields.
2020

2121
## Unlinked Entries
2222

23-
If the file that an entry is referencing has been moved, renamed, or deleted on disk, then TagStudio will display its unlinked status with a red chain-link icon instead of its thumbnail image. Certain uncached stats such as the file size and image dimensions will also be unavailable to see in the inspector.
23+
If the file that an entry is referencing has been moved, renamed, or deleted on disk, then TagStudio will display its unlinked status with a red chain-link icon instead of its thumbnail image. Certain uncached stats such as the file size and image dimensions will also be unavailable to see in the Inspector.
2424

2525
To fix file entries that have become unlinked, select the "Fix Unlinked Entries" option from the Tools menu. From there, refresh the unlinked entry count and choose whether to search and relink you files, and/or delete the file entries from your library. This will NOT delete or modify any files on disk.
2626

‎docs/fields.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Fields are extra pieces of information you can add to [file entries](./entries.m
1212

1313
Unlike tags, fields are based on [templates](#templates) that contain pre-filled information such as the field type and title, and that information is _copied_ to fields when adding them to entries. Editing field information on entries **does not** modify the template it was created from.
1414

15-
To add a field to an entry, click the "Add Field" button at the bottom of the inspector. From there you can search and/or select a [field template](#field-templates) to choose from, or create a new one from the search bar. Alternatively you can create new field templates from **Edit -> Manage Field Templates**.
15+
To add a field to an entry, click the "Add Field" button at the bottom of the Inspector panel. From there you can search and/or select a [field template](#field-templates) to choose from, or create a new one from the search bar. Alternatively you can create new field templates from **Edit -> Manage Field Templates**.
1616

1717
<figure markdown="span">
1818
![Fields Example](assets/fields_example.png)

‎docs/preview-support.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ TagStudio offers built-in preview and thumbnail support for a wide variety of fi
1212

1313
### :material-image-outline: Images
1414

15-
Images will generate thumbnails the first time they are viewed or since the last time they were modified. Thumbnails are used in the grid view, but not in the Preview Panel. Animated images will play in the Preview Panel.
15+
Images will generate thumbnails the first time they are viewed or since the last time they were modified. Thumbnails are used in the grid view, but not in the Inspector panel. Animated images will play in the Inspector.
1616

1717
| Filetype | Extensions | Animation |
1818
| -------------------- | -------------------------------------------------- | :---------------------------------: |
@@ -47,7 +47,7 @@ Images will generate thumbnails the first time they are viewed or since the last
4747

4848
### :material-movie-open: Videos
4949

50-
Video thumbnails will default to the closest viable frame from the middle of the video. Both thumbnail generation and video playback in the Preview Panel requires [FFmpeg](install.md#optional-dependencies) installed on your system.
50+
Video thumbnails will default to the closest viable frame from the middle of the video. Both thumbnail generation and video playback in the Inspector requires [FFmpeg](install.md#optional-dependencies) installed on your system.
5151

5252
| Filetype | Extensions | Dependencies |
5353
| --------------------- | ----------------------- | :----------: |
@@ -65,7 +65,7 @@ Video thumbnails will default to the closest viable frame from the middle of the
6565

6666
### :material-sine-wave: Audio
6767

68-
Audio thumbnails will default to embedded cover art (if any) and fallback to generated waveform thumbnails. Audio file playback is supported in the Preview Panel if you have [FFmpeg](install.md#optional-dependencies) installed on your system. Audio waveforms are currently not cached.
68+
Audio thumbnails will default to embedded cover art (if any) and fallback to generated waveform thumbnails. Audio file playback is supported in the Inspector if you have [FFmpeg](install.md#optional-dependencies) installed on your system. Audio waveforms are currently not cached.
6969

7070
| Filetype | Extensions | Dependencies |
7171
| ------------------- | ------------------------ | :----------: |
@@ -103,7 +103,7 @@ Preview support for office documents or well-known project file formats varies b
103103

104104
### :material-archive: Archives
105105

106-
Archive thumbnails will display the first image from the archive within the Preview Panel.
106+
Archive thumbnails will display the first image from the archive within the Inspector.
107107

108108
| Filetype | Extensions |
109109
| -------- | -------------- |
@@ -129,7 +129,7 @@ Archive thumbnails will display the first image from the archive within the Prev
129129

130130
### :material-format-font: Fonts
131131

132-
Font thumbnails will use a "Aa" example preview of the font, with a full alphanumeric of the font available in the Preview Panel.
132+
Font thumbnails will use a "Aa" example preview of the font, with a full alphanumeric of the font available in the Inspector.
133133

134134
| Filetype | Extensions |
135135
| -------------------- | ----------------- |
@@ -143,7 +143,7 @@ Font thumbnails will use a "Aa" example preview of the font, with a full alphanu
143143
!!! info "Plain Text Support"
144144
TagStudio supports the *vast* majority of files considered to be "[plain text](https://en.wikipedia.org/wiki/Plain_text)". If an extension or format is not listed here, odds are it's still supported anyway.
145145

146-
Text files render the first 256 bytes of text information to an image preview for thumbnails and the Preview Panel. Improved thumbnails, full scrollable text, and syntax highlighting are on our [roadmap](roadmap.md#uiux) for future features.
146+
Text files render the first 256 bytes of text information to an image preview for thumbnails and the Inspector. Improved thumbnails, full scrollable text, and syntax highlighting are on our [roadmap](roadmap.md#uiux) for future features.
147147

148148
| Filetype | Extensions | Syntax Highlighting |
149149
| ---------- | --------------------------------------------- | :--------------------------------: |

‎docs/roadmap.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ Discrete library objects representing [attributes](<https://en.wikipedia.org/wik
203203
- [ ] User-Defined Icons :material-chevron-triple-up:{ .priority-high title="High Priority" } **[v9.7.x]**
204204
- [ ] Tint Icons with Text Color :material-chevron-triple-up:{ .priority-high title="High Priority" } **[v9.7.x]**
205205
- [x] [Category Property](tags.md#is-category) **[[v9.5.0](changelog.md#950-march-3rd-2025)]**
206-
- [x] Property available for tags that allow the tag and any inheriting from it to be displayed separately in the inspector under a title
206+
- [x] Property available for tags that allow the tag and any inheriting from it to be displayed separately in the Inspector panel under a title
207207
- [x] Fine-tuned exclusion from categories :material-chevron-triple-up:{ .priority-high title="High Priority" } **[v9.6.x]**
208208
- [x] Hidden Property **[[v9.5.7](changelog.md#957-may-5th-2026)]**
209209
- [x] Built-in "Archived" tag has this property by default **[[v9.5.7](changelog.md#957-may-5th-2026)]**

‎docs/tags.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ Properties are special attributes of tags that change their behavior in some way
100100

101101
#### Is Category
102102

103-
The "Is Category" property of tags determines if a tag should be treated as a category itself when being organized inside the inspector. If this tag or any tags inheriting from this tag (i.e. tags that have this tag as a "[Parent Tag](#parent-tags)"), then these tags will appear under a separated group that's named after this tag. Tags inheriting from multiple "category tags" will still show up under any applicable category.
103+
The "Is Category" property of tags determines if a tag should be treated as a category itself when being organized inside the Inspector panel. If this tag or any tags inheriting from this tag (i.e. tags that have this tag as a "[Parent Tag](#parent-tags)"), then these tags will appear under a separated group that's named after this tag. Tags inheriting from multiple "category tags" will still show up under any applicable category.
104104

105105
This means that duplicates of tags can appear on entries if the tag inherits from multiple parent categories, however this is by design and reflects the nature of multiple inheritance. Any tags not inheriting from a category tag will simply show under a default "Tag" section.
106106

‎docs/usage.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Files that become moved, renamed, or modified will try to be automatically relin
2828

2929
## :material-tag-text: Tagging
3030

31-
With one or more file entries selected, you can **search for** or **create a new tag** by clicking the "Add Tag" button at the bottom of the inspector or by pressing <kbd>Ctrl</kbd>+<kbd>T</kbd>.
31+
With one or more file entries selected, you can **search for** or **create a new tag** by clicking the "Add Tag" button at the bottom of the Inspector panel or by pressing <kbd>Ctrl</kbd>+<kbd>T</kbd>.
3232

3333
This will open the the **"Tag Bar"**, a combination search/creation bar that is designed to allow you to quickly create and apply tags that don't exist yet, find and apply existing tags from your library, and verify if certain tags already exist and/or are applied to your selection.
3434

‎src/tagstudio/previews/file_renderer.py‎

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,9 @@ def __init__(self, library: Library, settings: AppSettings) -> None:
9696
self.settings = settings
9797

9898
# Cached thumbnail elements.
99-
# Key: Size + Pixel Ratio Tuple + Radius Scale
100-
# (Ex. (512, 512, 1.25, 4))
101-
self.thumb_masks: dict[tuple[int, int, float, float], Image.Image] = {}
99+
# Key: Size + Pixel Ratio Tuple
100+
# (Ex. (512, 512, 1.25))
101+
self.thumb_masks: dict[tuple[int, int, float], Image.Image] = {}
102102
self.raised_edges: dict[tuple[int, int, float], tuple[Image.Image, Image.Image]] = {}
103103

104104
# Key: ("name", UiColor, 512, 512, 1.25)
@@ -129,27 +129,19 @@ def _get_resource_id(self, url: Path) -> str:
129129
return "file_generic"
130130

131131
# NOTE: This method will be replaced with frontend specific decorations (Qt painting)
132-
def _get_mask(
133-
self, size: tuple[int, int], pixel_ratio: float, scale_radius: bool = False
134-
) -> Image.Image:
135-
"""Return a thumbnail mask given a size, pixel ratio, and radius scaling option.
132+
def _get_mask(self, size: tuple[int, int], pixel_ratio: float) -> Image.Image:
133+
"""Return a thumbnail mask given a size and pixel ratio.
136134
137135
If one is not already cached, a new one will be rendered.
138136
139137
Args:
140138
size (tuple[int, int]): The size of the graphic.
141139
pixel_ratio (float): The screen pixel ratio.
142-
scale_radius (bool): Option to scale the radius up (Used for Preview Panel).
143140
"""
144-
thumb_scale: int = 512
145-
radius_scale: float = 1
146-
if scale_radius:
147-
radius_scale = max(size[0], size[1]) / thumb_scale
148-
149-
item: Image.Image | None = self.thumb_masks.get((*size, pixel_ratio, radius_scale))
141+
item: Image.Image | None = self.thumb_masks.get((*size, pixel_ratio))
150142
if not item:
151-
item = self._render_mask(size, pixel_ratio, radius_scale)
152-
self.thumb_masks[(*size, pixel_ratio, radius_scale)] = item
143+
item = self._render_mask(size, pixel_ratio)
144+
self.thumb_masks[(*size, pixel_ratio)] = item
153145
return item
154146

155147
# NOTE: This method will be replaced with frontend specific decorations (Qt painting)
@@ -215,15 +207,12 @@ def _get_icon(
215207
return item
216208

217209
# NOTE: This method will be replaced with frontend specific decorations (Qt painting)
218-
def _render_mask(
219-
self, size: tuple[int, int], pixel_ratio: float, radius_scale: float = 1
220-
) -> Image.Image:
210+
def _render_mask(self, size: tuple[int, int], pixel_ratio: float) -> Image.Image:
221211
"""Render a thumbnail mask graphic.
222212
223213
Args:
224214
size (tuple[int,int]): The size of the graphic.
225215
pixel_ratio (float): The screen pixel ratio.
226-
radius_scale (float): The scale factor of the border radius (Used by Preview Panel).
227216
"""
228217
smooth_factor: int = 2
229218
radius_factor: int = 8
@@ -236,7 +225,7 @@ def _render_mask(
236225
draw = ImageDraw.Draw(im)
237226
draw.rounded_rectangle(
238227
(0, 0) + tuple([d - 1 for d in im.size]),
239-
radius=math.ceil(radius_factor * smooth_factor * pixel_ratio * radius_scale),
228+
radius=math.ceil(radius_factor * smooth_factor * pixel_ratio),
240229
fill="white",
241230
)
242231
im = im.resize(
@@ -682,11 +671,7 @@ def fetch_cached_image(file_name: Path):
682671
if not filepath.exists() or filepath.is_dir()
683672
else render_default((512, 512), 2)
684673
)
685-
render_mask_and_edge = False
686-
mask = self._get_mask(image.size, dpi_scale, scale_radius=True)
687-
bg = Image.new("RGBA", image.size, (0, 0, 0, 0))
688-
bg.paste(image, mask=mask.getchannel(0))
689-
image = bg
674+
image = image.convert("RGBA")
690675

691676
# If the image couldn't be rendered, use a default media image.
692677
if not image:

‎src/tagstudio/qt/controllers/banner.py‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222

2323
from tagstudio.qt.views.banner_view import BannerView
2424
from tagstudio.qt.views.styles.stylesheets import (
25-
BANNER_CORNER_RADIUS,
25+
PAD,
26+
RADIUS,
2627
banner_notice_bg_color,
2728
banner_notice_style,
2829
banner_progress_bg_color,
@@ -54,7 +55,7 @@ def paintEvent(self, event: QPaintEvent) -> None:
5455
painter = QPainter(self)
5556
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
5657
path = QPainterPath()
57-
path.addRoundedRect(QRectF(self.rect()), BANNER_CORNER_RADIUS, BANNER_CORNER_RADIUS)
58+
path.addRoundedRect(QRectF(self.rect()), RADIUS, RADIUS)
5859
painter.fillPath(path, self._bg_color)
5960
painter.end()
6061

@@ -66,8 +67,7 @@ class Banner(QWidget):
6667
"""
6768

6869
CONTENT_HEIGHT = 36
69-
GAP = 6
70-
HEIGHT = CONTENT_HEIGHT + GAP
70+
HEIGHT = CONTENT_HEIGHT + PAD
7171
ANIMATION_MS = 250
7272
COLOR_ANIMATION_MS = 250
7373
MIN_VISIBLE_MS = 3000
@@ -81,7 +81,7 @@ def __init__(self, parent: QWidget | None = None):
8181
self.setMaximumHeight(0)
8282

8383
outer_layout = QVBoxLayout(self)
84-
outer_layout.setContentsMargins(0, 0, 0, self.GAP)
84+
outer_layout.setContentsMargins(0, 0, 0, PAD)
8585
outer_layout.setSpacing(0)
8686

8787
self._background = _BannerBackground(self)
@@ -104,7 +104,7 @@ def __init__(self, parent: QWidget | None = None):
104104
self._progress_bg_color = banner_progress_bg_color()
105105
self._background.setStyleSheet(self._notice_style)
106106
self._background.set_bg_color(self._notice_bg_color)
107-
self.view.progress_bar.set_corner_radius(BANNER_CORNER_RADIUS)
107+
self.view.progress_bar.set_corner_radius(RADIUS)
108108
self.view.progress_bar.set_chunk_color(banner_progress_chunk_color())
109109

110110
self._card_color_anim = QVariantAnimation(self)

0 commit comments

Comments
 (0)