Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
428 changes: 0 additions & 428 deletions LICENSES/CC-BY-SA-4.0.txt

This file was deleted.

8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Tags currently consist of the following attributes:
- **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.
- Parent tags checked with the "disambiguation" checkbox next to them will be used to help disambiguate tag names that may not be unique.
- 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)".
- **Is Category**: A property that when checked, treats this tag as a category in the preview panel.
- **Is Category**: A property that when checked, treats this tag as a category in the Inspector panel.

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.

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

### Editing Tags

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

### Adding Tags to File Entries

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>).

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!

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

### Adding Fields to File Entries

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
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

### Editing Fields

Expand Down
4 changes: 2 additions & 2 deletions docs/entries.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ File entry data is stored within the `ts_library.sqlite` file inside each librar

## Appearance

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

## Unlinked Entries

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

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.

Expand Down
2 changes: 1 addition & 1 deletion docs/fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Fields are extra pieces of information you can add to [file entries](./entries.m

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.

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**.
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**.

<figure markdown="span">
![Fields Example](assets/fields_example.png)
Expand Down
12 changes: 6 additions & 6 deletions docs/preview-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ TagStudio offers built-in preview and thumbnail support for a wide variety of fi

### :material-image-outline: Images

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

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

### :material-movie-open: Videos

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

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

### :material-sine-wave: Audio

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

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

### :material-archive: Archives

Archive thumbnails will display the first image from the archive within the Preview Panel.
Archive thumbnails will display the first image from the archive within the Inspector.

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

### :material-format-font: Fonts

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

| Filetype | Extensions |
| -------------------- | ----------------- |
Expand All @@ -143,7 +143,7 @@ Font thumbnails will use a "Aa" example preview of the font, with a full alphanu
!!! info "Plain Text Support"
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.

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

| Filetype | Extensions | Syntax Highlighting |
| ---------- | --------------------------------------------- | :--------------------------------: |
Expand Down
2 changes: 1 addition & 1 deletion docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ Discrete library objects representing [attributes](<https://en.wikipedia.org/wik
- [ ] User-Defined Icons :material-chevron-triple-up:{ .priority-high title="High Priority" } **[v9.7.x]**
- [ ] Tint Icons with Text Color :material-chevron-triple-up:{ .priority-high title="High Priority" } **[v9.7.x]**
- [x] [Category Property](tags.md#is-category) **[[v9.5.0](changelog.md#950-march-3rd-2025)]**
- [x] Property available for tags that allow the tag and any inheriting from it to be displayed separately in the inspector under a title
- [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
- [x] Fine-tuned exclusion from categories :material-chevron-triple-up:{ .priority-high title="High Priority" } **[v9.6.x]**
- [x] Hidden Property **[[v9.5.7](changelog.md#957-may-5th-2026)]**
- [x] Built-in "Archived" tag has this property by default **[[v9.5.7](changelog.md#957-may-5th-2026)]**
Expand Down
2 changes: 1 addition & 1 deletion docs/tags.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Properties are special attributes of tags that change their behavior in some way

#### Is Category

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

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.

Expand Down
2 changes: 1 addition & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Files that become moved, renamed, or modified will try to be automatically relin

## :material-tag-text: Tagging

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

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.

Expand Down
37 changes: 11 additions & 26 deletions src/tagstudio/previews/file_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ def __init__(self, library: Library, settings: AppSettings) -> None:
self.settings = settings

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

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

# NOTE: This method will be replaced with frontend specific decorations (Qt painting)
def _get_mask(
self, size: tuple[int, int], pixel_ratio: float, scale_radius: bool = False
) -> Image.Image:
"""Return a thumbnail mask given a size, pixel ratio, and radius scaling option.
def _get_mask(self, size: tuple[int, int], pixel_ratio: float) -> Image.Image:
"""Return a thumbnail mask given a size and pixel ratio.

If one is not already cached, a new one will be rendered.

Args:
size (tuple[int, int]): The size of the graphic.
pixel_ratio (float): The screen pixel ratio.
scale_radius (bool): Option to scale the radius up (Used for Preview Panel).
"""
thumb_scale: int = 512
radius_scale: float = 1
if scale_radius:
radius_scale = max(size[0], size[1]) / thumb_scale

item: Image.Image | None = self.thumb_masks.get((*size, pixel_ratio, radius_scale))
item: Image.Image | None = self.thumb_masks.get((*size, pixel_ratio))
if not item:
item = self._render_mask(size, pixel_ratio, radius_scale)
self.thumb_masks[(*size, pixel_ratio, radius_scale)] = item
item = self._render_mask(size, pixel_ratio)
self.thumb_masks[(*size, pixel_ratio)] = item
return item

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

# NOTE: This method will be replaced with frontend specific decorations (Qt painting)
def _render_mask(
self, size: tuple[int, int], pixel_ratio: float, radius_scale: float = 1
) -> Image.Image:
def _render_mask(self, size: tuple[int, int], pixel_ratio: float) -> Image.Image:
"""Render a thumbnail mask graphic.

Args:
size (tuple[int,int]): The size of the graphic.
pixel_ratio (float): The screen pixel ratio.
radius_scale (float): The scale factor of the border radius (Used by Preview Panel).
"""
smooth_factor: int = 2
radius_factor: int = 8
Expand All @@ -236,7 +225,7 @@ def _render_mask(
draw = ImageDraw.Draw(im)
draw.rounded_rectangle(
(0, 0) + tuple([d - 1 for d in im.size]),
radius=math.ceil(radius_factor * smooth_factor * pixel_ratio * radius_scale),
radius=math.ceil(radius_factor * smooth_factor * pixel_ratio),
fill="white",
)
im = im.resize(
Expand Down Expand Up @@ -686,11 +675,7 @@ def fetch_cached_image(file_name: Path):
if not filepath.exists() or filepath.is_dir()
else render_default((512, 512), 2)
)
render_mask_and_edge = False
mask = self._get_mask(image.size, dpi_scale, scale_radius=True)
bg = Image.new("RGBA", image.size, (0, 0, 0, 0))
bg.paste(image, mask=mask.getchannel(0))
image = bg
image = image.convert("RGBA")

# If the image couldn't be rendered, use a default media image.
if not image:
Expand Down
12 changes: 6 additions & 6 deletions src/tagstudio/qt/controllers/banner.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@

from tagstudio.qt.views.banner_view import BannerView
from tagstudio.qt.views.styles.stylesheets import (
BANNER_CORNER_RADIUS,
PAD,
RADIUS,
banner_notice_bg_color,
banner_notice_style,
banner_progress_bg_color,
Expand Down Expand Up @@ -54,7 +55,7 @@ def paintEvent(self, event: QPaintEvent) -> None:
painter = QPainter(self)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
path = QPainterPath()
path.addRoundedRect(QRectF(self.rect()), BANNER_CORNER_RADIUS, BANNER_CORNER_RADIUS)
path.addRoundedRect(QRectF(self.rect()), RADIUS, RADIUS)
painter.fillPath(path, self._bg_color)
painter.end()

Expand All @@ -66,8 +67,7 @@ class Banner(QWidget):
"""

CONTENT_HEIGHT = 36
GAP = 6
HEIGHT = CONTENT_HEIGHT + GAP
HEIGHT = CONTENT_HEIGHT + PAD
ANIMATION_MS = 250
COLOR_ANIMATION_MS = 250
MIN_VISIBLE_MS = 3000
Expand All @@ -81,7 +81,7 @@ def __init__(self, parent: QWidget | None = None):
self.setMaximumHeight(0)

outer_layout = QVBoxLayout(self)
outer_layout.setContentsMargins(0, 0, 0, self.GAP)
outer_layout.setContentsMargins(0, 0, 0, PAD)
outer_layout.setSpacing(0)

self._background = _BannerBackground(self)
Expand All @@ -104,7 +104,7 @@ def __init__(self, parent: QWidget | None = None):
self._progress_bg_color = banner_progress_bg_color()
self._background.setStyleSheet(self._notice_style)
self._background.set_bg_color(self._notice_bg_color)
self.view.progress_bar.set_corner_radius(BANNER_CORNER_RADIUS)
self.view.progress_bar.set_corner_radius(RADIUS)
self.view.progress_bar.set_chunk_color(banner_progress_chunk_color())

self._card_color_anim = QVariantAnimation(self)
Expand Down
Loading
Loading