diff --git a/REUSE.toml b/REUSE.toml
index 93089cd2..3f4ddce5 100644
--- a/REUSE.toml
+++ b/REUSE.toml
@@ -34,17 +34,18 @@ SPDX-License-Identifier = "GPL-3.0-or-later"
[[annotations]]
path = [
+ "src/tagstudio/resources/qt/images/bxs-clipboard-regular.png",
"src/tagstudio/resources/qt/images/bxs-left-arrow.png",
+ "src/tagstudio/resources/qt/images/bxs-pencil-solid.png",
"src/tagstudio/resources/qt/images/bxs-right-arrow.png",
+ "src/tagstudio/resources/qt/images/bxs-trash-solid.png",
+ "src/tagstudio/resources/qt/images/bxs-volume-full-solid.png",
"src/tagstudio/resources/qt/images/file_icons/database.png",
]
SPDX-FileCopyrightText = "(c) 2026 Boxicons"
SPDX-License-Identifier = "MIT"
[[annotations]]
-path = [
- "src/tagstudio/resources/qt/images/volume.svg",
- "src/tagstudio/resources/qt/images/volume_mute.svg",
-]
+path = ["src/tagstudio/resources/qt/images/dupe_file_stat.png"]
SPDX-FileCopyrightText = "(c) github:google/material-design-icons Contributors"
SPDX-License-Identifier = "Apache-2.0"
diff --git a/docs/assets/favicon.ico b/docs/assets/favicon.ico
new file mode 120000
index 00000000..ca85593d
--- /dev/null
+++ b/docs/assets/favicon.ico
@@ -0,0 +1 @@
+../../src/tagstudio/resources/icon.ico
\ No newline at end of file
diff --git a/docs/assets/github_header.png b/docs/assets/github_header.png
deleted file mode 100644
index 28132f65..00000000
Binary files a/docs/assets/github_header.png and /dev/null differ
diff --git a/docs/assets/icon.ico b/docs/assets/icon.ico
deleted file mode 100644
index 71335bd5..00000000
Binary files a/docs/assets/icon.ico and /dev/null differ
diff --git a/docs/assets/icon.png b/docs/assets/icon.png
deleted file mode 100644
index dd3d2772..00000000
Binary files a/docs/assets/icon.png and /dev/null differ
diff --git a/docs/assets/icon_mono.svg b/docs/assets/icon_mono.svg
index 67060eb0..db328eab 100644
--- a/docs/assets/icon_mono.svg
+++ b/docs/assets/icon_mono.svg
@@ -1,10 +1,7 @@
-
'
)
+ self.links_label.setStyleSheet("QLabel {color: #809782ff}")
self.links_label.setWordWrap(True)
self.links_label.setOpenExternalLinks(True)
self.links_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
@@ -171,12 +221,27 @@ class AboutModal(QWidget):
# Add Widgets to Layouts -----------------------------------------------
self.content_layout.addWidget(self.logo_widget)
- self.content_layout.addWidget(self.title_label)
+ self.content_layout.addWidget(self.version_label)
self.content_layout.addWidget(self.desc_label)
self.content_layout.addWidget(self.system_info_widget)
- self.content_layout.addWidget(self.links_label)
self.content_layout.addStretch(1)
+ self.content_layout.addWidget(self.links_label)
+ self.content_layout.addWidget(self.copyright_label)
self.content_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
+ self.bg_image = self.rm.about_bg
+ self.bg_image.setDevicePixelRatio(pixel_ratio)
+ self.bg_image = self.bg_image.scaled(
+ QSize(
+ math.floor(self.width() * pixel_ratio),
+ math.floor(self.maximumHeight() * pixel_ratio),
+ ),
+ Qt.AspectRatioMode.IgnoreAspectRatio,
+ Qt.TransformationMode.SmoothTransformation,
+ )
+ palette = QPalette()
+ palette.setBrush(QPalette.ColorRole.Window, self.bg_image)
+ self.setPalette(palette)
+
self.root_layout.addWidget(self.content_widget)
self.root_layout.addWidget(self.button_widget)
diff --git a/src/tagstudio/qt/mixed/field_widget.py b/src/tagstudio/qt/mixed/field_widget.py
index 04ee3c37..dc99771a 100644
--- a/src/tagstudio/qt/mixed/field_widget.py
+++ b/src/tagstudio/qt/mixed/field_widget.py
@@ -2,39 +2,28 @@
# SPDX-License-Identifier: GPL-3.0-only
-import math
from collections.abc import Callable
-from pathlib import Path
from typing import override
from warnings import catch_warnings
import structlog
-from PIL import Image, ImageQt
-from PySide6.QtCore import QEvent, Qt
+from PIL import ImageQt
+from PySide6.QtCore import QEvent, QSize, Qt
from PySide6.QtGui import QEnterEvent, QPixmap, QResizeEvent
from PySide6.QtWidgets import QHBoxLayout, QLabel, QPushButton, QVBoxLayout, QWidget
from tagstudio.core.enums import Theme
+from tagstudio.qt.helpers.color_overlay import auto_theme_overlay
+from tagstudio.qt.resource_manager import ResourceManager
logger = structlog.get_logger(__name__)
class FieldContainer(QWidget):
- # TODO: reference a resources folder rather than path.parents[2]?
- clipboard_icon_128: Image.Image = Image.open(
- str(Path(__file__).parents[2] / "resources/qt/images/clipboard_icon_128.png")
- ).resize((math.floor(24 * 1.25), math.floor(24 * 1.25)))
- clipboard_icon_128.load()
-
- edit_icon_128: Image.Image = Image.open(
- str(Path(__file__).parents[2] / "resources/qt/images/edit_icon_128.png")
- ).resize((math.floor(24 * 1.25), math.floor(24 * 1.25)))
- edit_icon_128.load()
-
- trash_icon_128: Image.Image = Image.open(
- str(Path(__file__).parents[2] / "resources/qt/images/trash_icon_128.png")
- ).resize((math.floor(24 * 1.25), math.floor(24 * 1.25)))
- trash_icon_128.load()
+ rm: ResourceManager = ResourceManager()
+ copy_icon = auto_theme_overlay(rm.copy, inverse=True)
+ edit_icon = auto_theme_overlay(rm.edit, inverse=True)
+ trash_icon = auto_theme_overlay(rm.trash, inverse=True)
# TODO: There should be a global button theme somewhere.
container_style = (
@@ -93,7 +82,8 @@ class FieldContainer(QWidget):
self.copy_button.setMinimumSize(button_size, button_size)
self.copy_button.setMaximumSize(button_size, button_size)
self.copy_button.setFlat(True)
- self.copy_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(self.clipboard_icon_128)))
+ self.copy_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(FieldContainer.copy_icon)))
+ self.copy_button.setIconSize(QSize(20, 20))
self.copy_button.setCursor(Qt.CursorShape.PointingHandCursor)
self.title_layout.addWidget(self.copy_button)
self.copy_button.setHidden(True)
@@ -103,7 +93,8 @@ class FieldContainer(QWidget):
self.edit_button.setMinimumSize(button_size, button_size)
self.edit_button.setMaximumSize(button_size, button_size)
self.edit_button.setFlat(True)
- self.edit_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(self.edit_icon_128)))
+ self.edit_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(FieldContainer.edit_icon)))
+ self.edit_button.setIconSize(QSize(20, 20))
self.edit_button.setCursor(Qt.CursorShape.PointingHandCursor)
self.title_layout.addWidget(self.edit_button)
self.edit_button.setHidden(True)
@@ -113,7 +104,8 @@ class FieldContainer(QWidget):
self.remove_button.setMinimumSize(button_size, button_size)
self.remove_button.setMaximumSize(button_size, button_size)
self.remove_button.setFlat(True)
- self.remove_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(self.trash_icon_128)))
+ self.remove_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(FieldContainer.trash_icon)))
+ self.remove_button.setIconSize(QSize(20, 20))
self.remove_button.setCursor(Qt.CursorShape.PointingHandCursor)
self.title_layout.addWidget(self.remove_button)
self.remove_button.setHidden(True)
diff --git a/src/tagstudio/qt/mixed/item_thumb.py b/src/tagstudio/qt/mixed/item_thumb.py
index 81223316..8cbd361c 100644
--- a/src/tagstudio/qt/mixed/item_thumb.py
+++ b/src/tagstudio/qt/mixed/item_thumb.py
@@ -8,7 +8,6 @@ from pathlib import Path
from typing import TYPE_CHECKING, override
import structlog
-from PIL import Image, ImageQt
from PySide6.QtCore import QEvent, QMimeData, QSize, Qt, QUrl
from PySide6.QtGui import QAction, QDrag, QEnterEvent, QGuiApplication, QMouseEvent, QPixmap
from PySide6.QtWidgets import QBoxLayout, QCheckBox, QHBoxLayout, QLabel, QVBoxLayout, QWidget
@@ -62,17 +61,7 @@ def badge_update_lock(func):
class ItemThumb(FlowWidget):
- """The thumbnail widget for a library item (Entry, Collation, Tag Group, etc.)."""
-
- collation_icon_128: Image.Image = Image.open(
- str(Path(__file__).parents[2] / "resources/qt/images/collation_icon_128.png")
- )
- collation_icon_128.load()
-
- tag_group_icon_128: Image.Image = Image.open(
- str(Path(__file__).parents[2] / "resources/qt/images/tag_group_icon_128.png")
- )
- tag_group_icon_128.load()
+ """The thumbnail widget for a library item (Entry, Entry Group, etc.)."""
small_text_style = (
"background-color:rgba(0, 0, 0, 192);"
@@ -141,8 +130,8 @@ class ItemThumb(FlowWidget):
# | ARC FAV| Top Right: Favorite & Archived Badges
# | |
# | |
- # |EXT #| Lower Left: File Type, Tag Group Icon, or Collation Icon
- # +----------+ Lower Right: Collation Count, Video Length, or Word Count
+ # |EXT #| Lower Left: File Type or Entry Group Icon
+ # +----------+ Lower Right: Entry Group Count, Video Length, or Word Count
#
# Filename Underneath: (Optional) Filename
@@ -221,19 +210,19 @@ class ItemThumb(FlowWidget):
# Static Badges ========================================================
# Item Type Badge ------------------------------------------------------
- # Used for showing the Tag Group / Collation icons.
+ # Used for showing the Entry Group icons.
# Mutually exclusive with the File Extension Badge.
self.item_type_badge = QLabel()
self.item_type_badge.setObjectName("itemBadge")
- self.item_type_badge.setPixmap(
- QPixmap.fromImage(
- ImageQt.ImageQt(
- ItemThumb.collation_icon_128.resize(
- (check_size, check_size), Image.Resampling.BILINEAR
- )
- )
- )
- )
+ # self.item_type_badge.setPixmap(
+ # QPixmap.fromImage(
+ # ImageQt.ImageQt(
+ # ItemThumb.collation_icon_128.resize(
+ # (check_size, check_size), Image.Resampling.BILINEAR
+ # )
+ # )
+ # )
+ # )
self.item_type_badge.setMinimumSize(check_size, check_size)
self.item_type_badge.setMaximumSize(check_size, check_size)
self.bottom_layout.addWidget(self.item_type_badge)
@@ -247,7 +236,7 @@ class ItemThumb(FlowWidget):
self.bottom_layout.addStretch(2)
# Count Badge ----------------------------------------------------------
- # Used for Tag Group + Collation counts, video length, word count, etc.
+ # Used for Entry Group counts, video length, word count, etc.
self.count_badge = QLabel()
self.count_badge.setObjectName("countBadge")
self.count_badge.setText("-:--")
@@ -343,7 +332,7 @@ class ItemThumb(FlowWidget):
self.count_badge.setStyleSheet(ItemThumb.small_text_style)
self.count_badge.setHidden(True)
self.ext_badge.setHidden(True)
- elif mode == ItemType.COLLATION:
+ elif mode == ItemType.ENTRY_GROUP:
self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, on=False)
self.thumb_button.setCursor(Qt.CursorShape.PointingHandCursor)
self.thumb_button.setHidden(False)
@@ -352,13 +341,6 @@ class ItemThumb(FlowWidget):
self.count_badge.setStyleSheet(ItemThumb.med_text_style)
self.count_badge.setHidden(False)
self.item_type_badge.setHidden(False)
- elif mode == ItemType.TAG_GROUP:
- self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, on=False)
- self.thumb_button.setCursor(Qt.CursorShape.PointingHandCursor)
- self.thumb_button.setHidden(False)
- self.ext_badge.setHidden(True)
- self.count_badge.setHidden(False)
- self.item_type_badge.setHidden(False)
self.mode = mode
def set_extension(self, filename: Path) -> None:
diff --git a/src/tagstudio/qt/mixed/landing.py b/src/tagstudio/qt/mixed/landing.py
index 7f5f6ba7..50cc4fae 100644
--- a/src/tagstudio/qt/mixed/landing.py
+++ b/src/tagstudio/qt/mixed/landing.py
@@ -4,7 +4,6 @@
import sys
import typing
-from pathlib import Path
import structlog
from PIL import Image, ImageQt
@@ -12,7 +11,8 @@ from PySide6.QtCore import QEasingCurve, QPoint, QPropertyAnimation, Qt
from PySide6.QtGui import QPixmap
from PySide6.QtWidgets import QLabel, QPushButton, QVBoxLayout, QWidget
-from tagstudio.qt.helpers.color_overlay import gradient_overlay, theme_fg_overlay
+from tagstudio.qt.helpers.color_overlay import auto_theme_overlay
+from tagstudio.qt.resource_manager import ResourceManager
from tagstudio.qt.translations import Translations
from tagstudio.qt.views.clickable_label import ClickableLabel
@@ -24,6 +24,10 @@ logger = structlog.get_logger(__name__)
class LandingWidget(QWidget):
+ rm: ResourceManager = ResourceManager()
+ mono_logo: Image.Image = rm.ts_logo_text_mono
+ color_logo: Image.Image = rm.ts_logo_text_color
+
def __init__(self, driver: "QtDriver", pixel_ratio: float):
super().__init__()
self.driver = driver
@@ -39,10 +43,6 @@ class LandingWidget(QWidget):
self.setLayout(self.landing_layout)
# Create landing logo --------------------------------------------------
- # self.landing_logo_pixmap = QPixmap(":/images/tagstudio_logo_text_mono.png")
- self.logo_raw: Image.Image = Image.open(
- Path(__file__).parents[2] / "resources/qt/images/tagstudio_logo_text_mono.png"
- )
self.landing_pixmap: QPixmap = QPixmap()
self.update_logo_color()
self.logo_label.clicked.connect(self._update_special_click)
@@ -58,9 +58,9 @@ class LandingWidget(QWidget):
# Create "Open/Create Library" button ----------------------------------
if sys.platform == "darwin":
- open_shortcut_text = "(⌘+O)"
+ open_shortcut_text = "(⌘ + O)"
else:
- open_shortcut_text = "(Ctrl+O)"
+ open_shortcut_text = "(Ctrl + O)"
self.open_button: QPushButton = QPushButton(
Translations.format("landing.open_create_library", shortcut=open_shortcut_text)
)
@@ -83,21 +83,22 @@ class LandingWidget(QWidget):
self.landing_layout.addWidget(self.open_button, alignment=Qt.AlignmentFlag.AlignCenter)
self.landing_layout.addWidget(self.status_label, alignment=Qt.AlignmentFlag.AlignCenter)
- def update_logo_color(self, style: typing.Literal["mono", "gradient"] = "mono"):
+ def update_logo_color(self, style: typing.Literal["mono", "color"] = "mono"):
"""Update the color of the TagStudio logo.
Args:
- style (str): = The style of the logo. Either "mono" or "gradient".
+ style (str): = The style of the logo. Either "mono" or "color".
"""
if style == "mono":
- logo_im = theme_fg_overlay(self.logo_raw)
- elif style == "gradient":
- gradient_colors: list[str] = ["#d27bf4", "#7992f5", "#63c6e3", "#63f5cf"]
- logo_im = gradient_overlay(self.logo_raw, gradient_colors)
+ logo_im = auto_theme_overlay(LandingWidget.mono_logo)
+ elif style == "color":
+ logo_im = LandingWidget.color_logo
- logo_final: Image.Image = Image.new(mode="RGBA", size=self.logo_raw.size, color="#00000000")
+ logo_final: Image.Image = Image.new(
+ mode="RGBA", size=LandingWidget.mono_logo.size, color="#00000000"
+ )
- logo_final.paste(logo_im, (0, 0), mask=self.logo_raw)
+ logo_final.paste(logo_im, (0, 0), mask=LandingWidget.mono_logo)
self.landing_pixmap = QPixmap.fromImage(ImageQt.ImageQt(logo_im))
self.landing_pixmap.setDevicePixelRatio(self._pixel_ratio)
@@ -105,7 +106,10 @@ class LandingWidget(QWidget):
self._logo_width, Qt.TransformationMode.SmoothTransformation
)
self.logo_label.setMaximumHeight(
- int(self.logo_raw.size[1] * (self.logo_raw.size[0] / self._logo_width))
+ int(
+ LandingWidget.mono_logo.size[1]
+ * (LandingWidget.mono_logo.size[0] / self._logo_width)
+ )
)
self.logo_label.setMaximumWidth(self._logo_width)
self.logo_label.setPixmap(self.landing_pixmap)
@@ -119,17 +123,13 @@ class LandingWidget(QWidget):
if self._special_click_count >= 0:
self._special_click_count += 1
if self._special_click_count >= 10:
- self.update_logo_color("gradient")
+ self.update_logo_color("color")
self.animate_logo_pop()
self._special_click_count = -1
def animate_logo_in(self):
- """Animate in the TagStudio logo."""
- # NOTE: Sometimes, mostly on startup without a library open, the
- # y position of logo_label is something like 10. I'm not sure what
- # the cause of this is, so I've just done this workaround to disable
- # the animation if the y position is too incorrect.
- if self.logo_label.y() > 50:
+ """Animate the TagStudio logo in, if not opening a library on start."""
+ if not self.driver.settings.open_last_loaded_on_startup and not self.driver.args.open:
self.logo_pos_anim.setStartValue(QPoint(self.logo_label.x(), self.logo_label.y() - 100))
self.logo_pos_anim.setEndValue(self.logo_label.pos())
self.logo_pos_anim.start()
diff --git a/src/tagstudio/qt/mixed/media_player.py b/src/tagstudio/qt/mixed/media_player.py
index c180964a..46e2c07b 100644
--- a/src/tagstudio/qt/mixed/media_player.py
+++ b/src/tagstudio/qt/mixed/media_player.py
@@ -5,10 +5,10 @@
import typing
from pathlib import Path
from time import gmtime
-from typing import cast, override
+from typing import override
import structlog
-from PIL import Image, ImageDraw
+from PIL import Image, ImageDraw, ImageQt
from PySide6.QtCore import QEvent, QObject, QRectF, QSize, Qt, QUrl, QVariantAnimation
from PySide6.QtGui import (
QAction,
@@ -18,12 +18,12 @@ from PySide6.QtGui import (
QLinearGradient,
QMouseEvent,
QPen,
+ QPixmap,
QRegion,
QResizeEvent,
)
from PySide6.QtMultimedia import QAudioOutput, QMediaDevices, QMediaPlayer
from PySide6.QtMultimediaWidgets import QGraphicsVideoItem
-from PySide6.QtSvgWidgets import QSvgWidget
from PySide6.QtWidgets import (
QGraphicsScene,
QGraphicsView,
@@ -31,10 +31,12 @@ from PySide6.QtWidgets import (
QLabel,
QSizePolicy,
QSlider,
+ QToolButton,
QVBoxLayout,
QWidget,
)
+from tagstudio.qt.helpers.color_overlay import light_overlay
from tagstudio.qt.translations import Translations
from tagstudio.qt.views.clickable_slider import ClickableSlider
@@ -55,6 +57,18 @@ class MediaPlayer(QGraphicsView):
def __init__(self, driver: "QtDriver") -> None:
super().__init__()
self.driver = driver
+ self.play_icon = QPixmap.fromImage(
+ ImageQt.ImageQt(light_overlay(self.driver.rm.bxs_right_arrow, use_alpha=False))
+ )
+ self.pause_icon = QPixmap.fromImage(
+ ImageQt.ImageQt(light_overlay(self.driver.rm.pause_icon, use_alpha=False))
+ )
+ self.mute_icon = QPixmap.fromImage(
+ ImageQt.ImageQt(light_overlay(self.driver.rm.mute_icon, use_alpha=False))
+ )
+ self.volume_icon = QPixmap.fromImage(
+ ImageQt.ImageQt(light_overlay(self.driver.rm.volume_icon, use_alpha=False))
+ )
slider_style = """
QSlider {
@@ -169,27 +183,27 @@ class MediaPlayer(QGraphicsView):
self.sub_controls.setStyleSheet("background: transparent;")
self.sub_controls.setMinimumHeight(16)
- self.play_pause = QSvgWidget()
+ self.play_pause = QToolButton()
+ self.play_pause.setStyleSheet("QToolButton { border: none; background: transparent; }")
self.play_pause.setCursor(Qt.CursorShape.PointingHandCursor)
- self.play_pause.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, on=True)
- self.play_pause.setMouseTracking(True)
- self.play_pause.installEventFilter(self)
+ self.play_pause.clicked.connect(self.toggle_play)
self.load_toggle_play_icon(playing=False)
- self.play_pause.resize(16, 16)
+ self.play_pause.setIconSize(QSize(20, 20))
+ self.play_pause.resize(20, 20)
self.play_pause.setSizePolicy(fixed_policy)
- self.play_pause.setStyleSheet("background: transparent;")
self.play_pause.hide()
sub_layout.addWidget(self.play_pause)
sub_layout.setAlignment(self.play_pause, Qt.AlignmentFlag.AlignLeft)
- self.mute_unmute = QSvgWidget()
+ self.mute_unmute = QToolButton()
+ self.mute_unmute.setStyleSheet("QToolButton { border: none; background: transparent; }")
self.mute_unmute.setCursor(Qt.CursorShape.PointingHandCursor)
- self.mute_unmute.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, on=True)
- self.mute_unmute.setMouseTracking(True)
+ self.mute_unmute.clicked.connect(self.toggle_mute)
self.mute_unmute.installEventFilter(self)
self.load_mute_unmute_icon(muted=False)
- self.mute_unmute.resize(16, 16)
+ self.mute_unmute.setIconSize(QSize(20, 20))
+ self.mute_unmute.resize(20, 20)
self.mute_unmute.setSizePolicy(fixed_policy)
self.mute_unmute.hide()
@@ -210,7 +224,7 @@ class MediaPlayer(QGraphicsView):
sub_layout.addStretch()
self.position_label = QLabel("0:00")
- self.position_label.setStyleSheet("color: white;")
+ self.position_label.setStyleSheet("color: white; font-family: Oxanium; font-weight:bold;")
sub_layout.addWidget(self.position_label)
root_layout.setAlignment(self.position_label, Qt.AlignmentFlag.AlignRight)
self.position_label.hide()
@@ -253,21 +267,10 @@ class MediaPlayer(QGraphicsView):
"""Apply a rounded corner effect to the video player."""
width: int = int(max(self.contentsRect().size().width(), 0))
height: int = int(max(self.contentsRect().size().height(), 0))
- mask = Image.new(
- "RGBA",
- (
- width,
- height,
- ),
- (0, 0, 0, 255),
- )
+ mask = Image.new("RGBA", (width, height), (0, 0, 0, 255))
draw = ImageDraw.Draw(mask)
- draw.rounded_rectangle(
- (0, 0) + (width, height),
- radius=8,
- fill=(0, 0, 0, 0),
- )
- final_mask = mask.getchannel("A").toqpixmap()
+ draw.rounded_rectangle((0, 0) + (width, height), radius=8, fill=(0, 0, 0, 0))
+ final_mask: QPixmap = mask.getchannel("A").toqpixmap() # pyright: ignore[reportUnknownVariableType]
self.setMask(QRegion(QBitmap(final_mask)))
def set_tint_opacity(self, opacity: int) -> None:
@@ -322,15 +325,7 @@ class MediaPlayer(QGraphicsView):
@override
def eventFilter(self, arg__1: QObject, arg__2: QEvent) -> bool:
"""Manage events for the media player."""
- if (
- arg__2.type() == QEvent.Type.MouseButtonPress
- and arg__2.button() == Qt.MouseButton.LeftButton # pyright: ignore[reportAttributeAccessIssue]
- ):
- if arg__1 == self.play_pause:
- self.toggle_play()
- elif arg__1 == self.mute_unmute:
- self.toggle_mute()
- elif arg__2.type() is QEvent.Type.Enter:
+ if arg__2.type() is QEvent.Type.Enter:
if arg__1 == self or arg__1 == self.video_preview:
self.underMouse()
elif arg__1 == self.mute_unmute:
@@ -414,12 +409,10 @@ class MediaPlayer(QGraphicsView):
self.player.play()
def load_toggle_play_icon(self, playing: bool) -> None:
- icon = cast(bytes, self.driver.rm.pause_icon if playing else self.driver.rm.play_icon)
- self.play_pause.load(icon)
+ self.play_pause.setIcon(self.pause_icon if playing else self.play_icon)
def load_mute_unmute_icon(self, muted: bool) -> None:
- icon = cast(bytes, self.driver.rm.volume_mute_icon if muted else self.driver.rm.volume_icon)
- self.mute_unmute.load(icon)
+ self.mute_unmute.setIcon(self.mute_icon if muted else self.volume_icon)
def slider_value_changed(self, value: int) -> None:
if self.timeline_slider.isSliderDown():
diff --git a/src/tagstudio/qt/mixed/pagination.py b/src/tagstudio/qt/mixed/pagination.py
index a1e77c1c..989c7e5b 100644
--- a/src/tagstudio/qt/mixed/pagination.py
+++ b/src/tagstudio/qt/mixed/pagination.py
@@ -11,7 +11,7 @@ from PySide6.QtCore import QSize, Signal
from PySide6.QtGui import QIntValidator, QPixmap
from PySide6.QtWidgets import QHBoxLayout, QLabel, QLineEdit, QSizePolicy, QWidget
-from tagstudio.qt.helpers.color_overlay import theme_fg_overlay
+from tagstudio.qt.helpers.color_overlay import auto_theme_overlay
from tagstudio.qt.resource_manager import ResourceManager
from tagstudio.qt.views.qbutton_wrapper import QPushButtonWrapper
@@ -44,8 +44,8 @@ class Pagination(QWidget):
# [<] ----------------------------------
self.prev_button = QPushButtonWrapper()
- prev_icon: Image.Image = self.rm.get("bxs-left-arrow") # pyright: ignore[reportAssignmentType]
- prev_icon = theme_fg_overlay(prev_icon, use_alpha=False)
+ prev_icon: Image.Image = self.rm.bxs_left_arrow
+ prev_icon = auto_theme_overlay(prev_icon, use_alpha=False)
self.prev_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(prev_icon)))
self.prev_button.setIconSize(QSize(12, 12))
self.prev_button.setMinimumSize(self.button_size)
@@ -97,8 +97,8 @@ class Pagination(QWidget):
# ---------------------------------- [>]
self.next_button = QPushButtonWrapper()
- next_icon: Image.Image = self.rm.get("bxs-right-arrow") # pyright: ignore[reportAssignmentType]
- next_icon = theme_fg_overlay(next_icon, use_alpha=False)
+ next_icon: Image.Image = self.rm.bxs_right_arrow
+ next_icon = auto_theme_overlay(next_icon, use_alpha=False)
self.next_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(next_icon)))
self.next_button.setIconSize(QSize(12, 12))
self.next_button.setMinimumSize(self.button_size)
diff --git a/src/tagstudio/qt/mixed/settings_panel.py b/src/tagstudio/qt/mixed/settings_panel.py
index 5e8c022d..fe76f83d 100644
--- a/src/tagstudio/qt/mixed/settings_panel.py
+++ b/src/tagstudio/qt/mixed/settings_panel.py
@@ -56,6 +56,7 @@ class SettingsPanel(PanelWidget):
Splash.CLASSIC: Translations["settings.splash.option.classic"],
Splash.GOO_GEARS: Translations["settings.splash.option.goo_gears"],
Splash.NINETY_FIVE: Translations["settings.splash.option.ninety_five"],
+ Splash.AURORA: Translations["settings.splash.option.aurora"],
}
tag_click_action_map: dict[TagClickActionOption, str] = {
diff --git a/src/tagstudio/qt/previews/renderer.py b/src/tagstudio/qt/previews/renderer.py
index 866e3df3..b7f6232d 100644
--- a/src/tagstudio/qt/previews/renderer.py
+++ b/src/tagstudio/qt/previews/renderer.py
@@ -73,7 +73,7 @@ from tagstudio.core.media_types import MediaCategories, MediaType
from tagstudio.core.utils.encoding import detect_char_encoding
from tagstudio.core.utils.types import unwrap
from tagstudio.qt.global_settings import DEFAULT_CACHED_IMAGE_RES
-from tagstudio.qt.helpers.color_overlay import theme_fg_overlay
+from tagstudio.qt.helpers.color_overlay import auto_theme_overlay
from tagstudio.qt.helpers.file_tester import is_readable_video
from tagstudio.qt.helpers.gradients import four_corner_gradient
from tagstudio.qt.helpers.image_effects import replace_transparent_pixels
@@ -449,11 +449,10 @@ class ThumbRenderer(QObject):
)
# Get icon by name
- icon: Image.Image | None = self.rm.get(name) # pyright: ignore[reportAssignmentType]
+ icon = self.rm.get(name)
+ assert isinstance(icon, Image.Image) or icon is None
if not icon:
- icon = self.rm.get("file_generic") # pyright: ignore[reportAssignmentType]
- if not icon:
- icon = Image.new(mode="RGBA", size=(32, 32), color="magenta")
+ icon = self.rm.file_generic
# Resize icon to fit icon_ratio
icon = icon.resize((math.ceil(size[0] // icon_ratio), math.ceil(size[1] // icon_ratio)))
@@ -547,11 +546,10 @@ class ThumbRenderer(QObject):
)
# Get icon by name
- icon: Image.Image | None = self.rm.get(name) # pyright: ignore[reportAssignmentType]
+ icon = self.rm.get(name)
+ assert isinstance(icon, Image.Image)
if not icon:
- icon = self.rm.get("file_generic") # pyright: ignore[reportAssignmentType]
- if not icon:
- icon = Image.new(mode="RGBA", size=(32, 32), color="magenta")
+ icon = self.rm.file_generic
# Resize icon to fit icon_ratio
icon = icon.resize(
@@ -1102,7 +1100,7 @@ class ThumbRenderer(QObject):
y_offset += (len(text_wrapped.split("\n")) + lines_of_padding) * draw.textbbox(
(0, 0), "A", font=font
)[-1]
- im = theme_fg_overlay(bg, use_alpha=False)
+ im = auto_theme_overlay(bg, use_alpha=False)
except OSError as e:
logger.error("Couldn't render thumbnail", filepath=filepath, error=type(e).__name__)
return im
@@ -1597,7 +1595,7 @@ class ThumbRenderer(QObject):
padding_factor = 18
im_ = im
- icon: Image.Image = self.rm.get("ignored") # pyright: ignore[reportAssignmentType]
+ icon: Image.Image = self.rm.ignored
icon = icon.resize(
(
diff --git a/src/tagstudio/qt/previews/vendored/ffmpeg.py b/src/tagstudio/qt/previews/vendored/ffmpeg.py
index 84d6ba19..e3b3d57b 100644
--- a/src/tagstudio/qt/previews/vendored/ffmpeg.py
+++ b/src/tagstudio/qt/previews/vendored/ffmpeg.py
@@ -27,6 +27,7 @@ FFMPEG_MACOS_LOCATIONS: list[str] = [
]
+# TODO: Make this more intuitive to use in other classes
def _get_ffprobe_location() -> str:
cmd: str = "ffprobe"
if platform.system() == "Darwin":
@@ -40,6 +41,7 @@ def _get_ffprobe_location() -> str:
return cmd
+# TODO: Make this more intuitive to use in other classes
def _get_ffmpeg_location() -> str:
cmd: str = "ffmpeg"
if platform.system() == "Darwin":
diff --git a/src/tagstudio/qt/resource_manager.py b/src/tagstudio/qt/resource_manager.py
index f1d387ab..d769a4ce 100644
--- a/src/tagstudio/qt/resource_manager.py
+++ b/src/tagstudio/qt/resource_manager.py
@@ -3,31 +3,23 @@
from pathlib import Path
-from typing import Literal, TypedDict
import structlog
import ujson
-from PIL import Image, ImageFile
+from PIL import Image
from PySide6.QtGui import QPixmap
logger = structlog.get_logger(__name__)
-class TResourceJsonAttrDict(TypedDict):
- path: str
- mode: Literal["qpixmap", "pil", "rb", "r"]
-
-
-TData = bytes | str | ImageFile.ImageFile | QPixmap
-
RESOURCE_FOLDER: Path = Path(__file__).parents[1]
class ResourceManager:
"""A resource manager for retrieving resources."""
- _map: dict[str, TResourceJsonAttrDict] = {}
- _cache: dict[str, TData] = {}
+ _map: dict[str, dict[str, str]] = {}
+ _cache: dict[str, bytes | str | Image.Image | QPixmap] = {}
_instance: "ResourceManager | None" = None
def __new__(cls):
@@ -43,7 +35,7 @@ class ResourceManager:
return ResourceManager._instance
@staticmethod
- def get_path(id: str) -> Path | None:
+ def get_path(id: str):
"""Get a resource's path from the ResourceManager.
Args:
@@ -52,12 +44,21 @@ class ResourceManager:
Returns:
Path: The resource path if found, else None.
"""
- res: TResourceJsonAttrDict | None = ResourceManager._map.get(id)
- if res is not None:
- return RESOURCE_FOLDER / "resources" / res.get("path")
- return None
+ try:
+ res = ResourceManager._map.get(id)
+ if res is None:
+ raise AttributeError
+ resource_path = res.get("path")
+ if resource_path is None:
+ raise FileNotFoundError
- def get(self, id: str) -> TData | None:
+ except (FileNotFoundError, AttributeError) as e:
+ logger.error("[ResourceManager]: Could not find path for resource: ", id=str, error=e)
+ return None
+
+ return RESOURCE_FOLDER / "resources" / resource_path
+
+ def get(self, id: str):
"""Get a resource from the ResourceManager.
Args:
@@ -70,42 +71,51 @@ class ResourceManager:
QPixmap: When the data is in PySide6.QtGui.QPixmap format.
None: If resource couldn't load.
"""
- cached_res: TData | None = ResourceManager._cache.get(id)
+ cached_res = ResourceManager._cache.get(id)
if cached_res is not None:
return cached_res
else:
- res: TResourceJsonAttrDict | None = ResourceManager._map.get(id)
- if res is None:
+ res: dict[str, str] | None = ResourceManager._map.get(id)
+
+ try:
+ if res is None:
+ raise AttributeError
+ resource_path = res.get("path")
+ if resource_path is None:
+ raise FileNotFoundError
+ except (FileNotFoundError, AttributeError) as e:
+ logger.error("[ResourceManager]: Could not find resource", id=id, error=e)
return None
- file_path: Path = RESOURCE_FOLDER / "resources" / res.get("path")
+ file_path = RESOURCE_FOLDER / "resources" / resource_path
mode = res.get("mode")
- data: TData | None = None
+ data = None
try:
match mode:
case "r":
data = file_path.read_text()
-
case "rb":
data = file_path.read_bytes()
-
case "pil":
data = Image.open(file_path)
data.load()
-
case "qpixmap":
data = QPixmap(file_path.as_posix())
+ case _:
+ raise AttributeError
- except FileNotFoundError:
- logger.error("[ResourceManager][ERROR]: Could not find resource: ", path=file_path)
+ except (FileNotFoundError, AttributeError) as e:
+ logger.error(
+ "[ResourceManager]: Could not find resource", path=file_path, id=id, error=e
+ )
+ return None
- if data is not None:
- ResourceManager._cache[id] = data
+ ResourceManager._cache[id] = data
return data
- def __getattr__(self, __name: str) -> TData:
+ def __getattr__(self, __name: str):
attr = self.get(__name)
if attr is not None:
return attr
diff --git a/src/tagstudio/qt/resource_manager.pyi b/src/tagstudio/qt/resource_manager.pyi
new file mode 100644
index 00000000..be327a0f
--- /dev/null
+++ b/src/tagstudio/qt/resource_manager.pyi
@@ -0,0 +1,63 @@
+# SPDX-FileCopyrightText: (c) TagStudio Contributors
+# SPDX-License-Identifier: GPL-3.0-only
+
+from collections.abc import Callable
+from pathlib import Path
+
+from PIL import Image
+from PySide6.QtGui import QPixmap
+
+class ResourceManager:
+ # Methods
+ get: Callable[..., Image.Image | bytes | None]
+ get_path: Callable[..., Path | None]
+
+ # Attributes
+ _map: dict[str, dict[str, str]]
+ _cache: dict[str, bytes | str | Image.Image | QPixmap]
+ _instance: ResourceManager | None
+
+ # Resources IDs from "resources.json"
+ about_bg: QPixmap
+ adobe_illustrator: Image.Image
+ adobe_photoshop: Image.Image
+ affinity_photo: Image.Image
+ archive: Image.Image
+ audio: Image.Image
+ broken_link_icon: Image.Image
+ bxs_left_arrow: Image.Image
+ bxs_right_arrow: Image.Image
+ copy: Image.Image
+ database: Image.Image
+ document: Image.Image
+ dupe_file_stat: Image.Image
+ ebook: Image.Image
+ edit: Image.Image
+ file_generic: Image.Image
+ font: Image.Image
+ icon: Image.Image
+ ignored_stat: Image.Image
+ ignored: Image.Image
+ image_vector: Image.Image
+ image: Image.Image
+ material: Image.Image
+ model: Image.Image
+ mute_icon: Image.Image
+ pause_icon: Image.Image
+ presentation: Image.Image
+ program: Image.Image
+ shader: Image.Image
+ shortcut: Image.Image
+ splash_95: QPixmap
+ splash_aurora: QPixmap
+ splash_classic: QPixmap
+ splash_goo_gears: QPixmap
+ spreadsheet: Image.Image
+ text: Image.Image
+ thumb_loading: Image.Image
+ trash: Image.Image
+ ts_logo_text_color: Image.Image
+ ts_logo_text_mono: Image.Image
+ unlinked_stat: Image.Image
+ video: Image.Image
+ volume_icon: Image.Image
diff --git a/src/tagstudio/qt/resources.json b/src/tagstudio/qt/resources.json
index f7bae77c..48b6f8f3 100644
--- a/src/tagstudio/qt/resources.json
+++ b/src/tagstudio/qt/resources.json
@@ -1,150 +1,174 @@
{
- "splash_classic": {
- "path": "qt/images/splash/classic.png",
- "mode": "qpixmap"
- },
- "splash_goo_gears": {
- "path": "qt/images/splash/goo_gears.png",
- "mode": "qpixmap"
- },
- "splash_95": {
- "path": "qt/images/splash/95.png",
- "mode": "qpixmap"
- },
- "icon": {
- "path": "icon.png",
- "mode": "pil"
- },
- "play_icon": {
- "path": "qt/images/play.svg",
- "mode": "rb"
- },
- "pause_icon": {
- "path": "qt/images/pause.svg",
- "mode": "rb"
- },
- "volume_icon": {
- "path": "qt/images/volume.svg",
- "mode": "rb"
- },
- "volume_mute_icon": {
- "path": "qt/images/volume_mute.svg",
- "mode": "rb"
- },
- "broken_link_icon": {
- "path": "qt/images/broken_link_icon.png",
- "mode": "pil"
- },
- "ignored": {
- "path": "qt/images/ignored_128.png",
- "mode": "pil"
+ "about_bg": {
+ "mode": "qpixmap",
+ "path": "qt/images/about_bg.jpg"
},
"adobe_illustrator": {
- "path": "qt/images/file_icons/adobe_illustrator.png",
- "mode": "pil"
+ "mode": "pil",
+ "path": "qt/images/file_icons/adobe_illustrator.png"
},
"adobe_photoshop": {
- "path": "qt/images/file_icons/adobe_photoshop.png",
- "mode": "pil"
+ "mode": "pil",
+ "path": "qt/images/file_icons/adobe_photoshop.png"
},
"affinity_photo": {
- "path": "qt/images/file_icons/affinity_photo.png",
- "mode": "pil"
+ "mode": "pil",
+ "path": "qt/images/file_icons/affinity_photo.png"
},
"archive": {
- "path": "qt/images/file_icons/archive.png",
- "mode": "pil"
+ "mode": "pil",
+ "path": "qt/images/file_icons/archive.png"
},
"audio": {
- "path": "qt/images/file_icons/audio.png",
- "mode": "pil"
+ "mode": "pil",
+ "path": "qt/images/file_icons/audio.png"
+ },
+ "broken_link_icon": {
+ "mode": "pil",
+ "path": "qt/images/broken_link_icon.png"
+ },
+ "bxs_left_arrow": {
+ "mode": "pil",
+ "path": "qt/images/bxs-left-arrow.png"
+ },
+ "bxs_right_arrow": {
+ "mode": "pil",
+ "path": "qt/images/bxs-right-arrow.png"
+ },
+ "copy": {
+ "mode": "pil",
+ "path": "qt/images/bxs-clipboard-regular.png"
},
"database": {
- "path": "qt/images/file_icons/database.png",
- "mode": "pil"
+ "mode": "pil",
+ "path": "qt/images/file_icons/database.png"
},
"document": {
- "path": "qt/images/file_icons/document.png",
- "mode": "pil"
- },
- "ebook": {
- "path": "qt/images/file_icons/ebook.png",
- "mode": "pil"
- },
- "file_generic": {
- "path": "qt/images/file_icons/file_generic.png",
- "mode": "pil"
- },
- "font": {
- "path": "qt/images/file_icons/font.png",
- "mode": "pil"
- },
- "image": {
- "path": "qt/images/file_icons/image.png",
- "mode": "pil"
- },
- "image_vector": {
- "path": "qt/images/file_icons/image_vector.png",
- "mode": "pil"
- },
- "material": {
- "path": "qt/images/file_icons/material.png",
- "mode": "pil"
- },
- "model": {
- "path": "qt/images/file_icons/model.png",
- "mode": "pil"
- },
- "presentation": {
- "path": "qt/images/file_icons/presentation.png",
- "mode": "pil"
- },
- "program": {
- "path": "qt/images/file_icons/program.png",
- "mode": "pil"
- },
- "shader": {
- "path": "qt/images/file_icons/shader.png",
- "mode": "pil"
- },
- "shortcut": {
- "path": "qt/images/file_icons/shortcut.png",
- "mode": "pil"
- },
- "spreadsheet": {
- "path": "qt/images/file_icons/spreadsheet.png",
- "mode": "pil"
- },
- "text": {
- "path": "qt/images/file_icons/text.png",
- "mode": "pil"
- },
- "video": {
- "path": "qt/images/file_icons/video.png",
- "mode": "pil"
- },
- "thumb_loading": {
- "path": "qt/images/thumb_loading.png",
- "mode": "pil"
- },
- "bxs-left-arrow": {
- "path": "qt/images/bxs-left-arrow.png",
- "mode": "pil"
- },
- "bxs-right-arrow": {
- "path": "qt/images/bxs-right-arrow.png",
- "mode": "pil"
- },
- "unlinked_stat": {
- "path": "qt/images/unlinked_stat.png",
- "mode": "pil"
- },
- "ignored_stat": {
- "path": "qt/images/ignored_stat.png",
- "mode": "pil"
+ "mode": "pil",
+ "path": "qt/images/file_icons/document.png"
},
"dupe_file_stat": {
- "path": "qt/images/dupe_file_stat.png",
- "mode": "pil"
+ "mode": "pil",
+ "path": "qt/images/dupe_file_stat.png"
+ },
+ "ebook": {
+ "mode": "pil",
+ "path": "qt/images/file_icons/ebook.png"
+ },
+ "edit": {
+ "mode": "pil",
+ "path": "qt/images/bxs-pencil-solid.png"
+ },
+ "file_generic": {
+ "mode": "pil",
+ "path": "qt/images/file_icons/file_generic.png"
+ },
+ "font": {
+ "mode": "pil",
+ "path": "qt/images/file_icons/font.png"
+ },
+ "icon": {
+ "mode": "pil",
+ "path": "icon.png"
+ },
+ "ignored": {
+ "mode": "pil",
+ "path": "qt/images/ignored_128.png"
+ },
+ "ignored_stat": {
+ "mode": "pil",
+ "path": "qt/images/ignored_stat.png"
+ },
+ "image": {
+ "mode": "pil",
+ "path": "qt/images/file_icons/image.png"
+ },
+ "image_vector": {
+ "mode": "pil",
+ "path": "qt/images/file_icons/image_vector.png"
+ },
+ "material": {
+ "mode": "pil",
+ "path": "qt/images/file_icons/material.png"
+ },
+ "model": {
+ "mode": "pil",
+ "path": "qt/images/file_icons/model.png"
+ },
+ "mute_icon": {
+ "mode": "pil",
+ "path": "qt/images/bxs-volume-mute-solid.png"
+ },
+ "pause_icon": {
+ "mode": "pil",
+ "path": "qt/images/pause.png"
+ },
+ "presentation": {
+ "mode": "pil",
+ "path": "qt/images/file_icons/presentation.png"
+ },
+ "program": {
+ "mode": "pil",
+ "path": "qt/images/file_icons/program.png"
+ },
+ "shader": {
+ "mode": "pil",
+ "path": "qt/images/file_icons/shader.png"
+ },
+ "shortcut": {
+ "mode": "pil",
+ "path": "qt/images/file_icons/shortcut.png"
+ },
+ "splash_95": {
+ "mode": "qpixmap",
+ "path": "qt/images/splash/95.png"
+ },
+ "splash_aurora": {
+ "mode": "qpixmap",
+ "path": "qt/images/splash/aurora.png"
+ },
+ "splash_classic": {
+ "mode": "qpixmap",
+ "path": "qt/images/splash/classic.png"
+ },
+ "splash_goo_gears": {
+ "mode": "qpixmap",
+ "path": "qt/images/splash/goo_gears.png"
+ },
+ "spreadsheet": {
+ "mode": "pil",
+ "path": "qt/images/file_icons/spreadsheet.png"
+ },
+ "text": {
+ "mode": "pil",
+ "path": "qt/images/file_icons/text.png"
+ },
+ "thumb_loading": {
+ "mode": "pil",
+ "path": "qt/images/thumb_loading.png"
+ },
+ "trash": {
+ "mode": "pil",
+ "path": "qt/images/bxs-trash-solid.png"
+ },
+ "ts_logo_text_color": {
+ "mode": "pil",
+ "path": "qt/images/tagstudio_logo-text_color.png"
+ },
+ "ts_logo_text_mono": {
+ "mode": "pil",
+ "path": "qt/images/tagstudio_logo-text_mono.png"
+ },
+ "unlinked_stat": {
+ "mode": "pil",
+ "path": "qt/images/unlinked_stat.png"
+ },
+ "video": {
+ "mode": "pil",
+ "path": "qt/images/file_icons/video.png"
+ },
+ "volume_icon": {
+ "mode": "pil",
+ "path": "qt/images/bxs-volume-full-solid.png"
}
}
diff --git a/src/tagstudio/qt/resources.qrc b/src/tagstudio/qt/resources.qrc
index fcaf44c8..3d5b5ab2 100644
--- a/src/tagstudio/qt/resources.qrc
+++ b/src/tagstudio/qt/resources.qrc
@@ -7,8 +7,5 @@
../../resources/qt/images/star_icon_filled_128.png
../../resources/qt/images/box_icon_empty_128.png
../../resources/qt/images/box_icon_filled_128.png
-
-
-
diff --git a/src/tagstudio/qt/views/library_info_window_view.py b/src/tagstudio/qt/views/library_info_window_view.py
index bfd06c0a..97852551 100644
--- a/src/tagstudio/qt/views/library_info_window_view.py
+++ b/src/tagstudio/qt/views/library_info_window_view.py
@@ -21,7 +21,7 @@ from PySide6.QtWidgets import (
QWidget,
)
-from tagstudio.qt.helpers.color_overlay import theme_fg_overlay
+from tagstudio.qt.helpers.color_overlay import auto_theme_overlay
from tagstudio.qt.platform_strings import open_file_str
from tagstudio.qt.translations import Translations
@@ -268,7 +268,7 @@ class LibraryInfoWindowView(QWidget):
)
self.unlinked_icon = QLabel()
- unlinked_image: Image.Image = self.driver.rm.get("unlinked_stat") # pyright: ignore[reportAssignmentType]
+ unlinked_image: Image.Image = self.driver.rm.unlinked_stat
unlinked_pixmap = QPixmap.fromImage(ImageQt.ImageQt(unlinked_image))
unlinked_pixmap.setDevicePixelRatio(self.devicePixelRatio())
unlinked_pixmap = unlinked_pixmap.scaledToWidth(
@@ -278,7 +278,7 @@ class LibraryInfoWindowView(QWidget):
self.unlinked_icon.setPixmap(unlinked_pixmap)
self.ignored_icon = QLabel()
- ignored_image: Image.Image = self.driver.rm.get("ignored_stat") # pyright: ignore[reportAssignmentType]
+ ignored_image: Image.Image = self.driver.rm.ignored_stat
ignored_pixmap = QPixmap.fromImage(ImageQt.ImageQt(ignored_image))
ignored_pixmap.setDevicePixelRatio(self.devicePixelRatio())
ignored_pixmap = ignored_pixmap.scaledToWidth(
@@ -288,9 +288,9 @@ class LibraryInfoWindowView(QWidget):
self.ignored_icon.setPixmap(ignored_pixmap)
self.dupe_file_icon = QLabel()
- dupe_file_image: Image.Image = self.driver.rm.get("dupe_file_stat") # pyright: ignore[reportAssignmentType]
+ dupe_file_image: Image.Image = self.driver.rm.dupe_file_stat
dupe_file_pixmap = QPixmap.fromImage(
- ImageQt.ImageQt(theme_fg_overlay(dupe_file_image, use_alpha=False))
+ ImageQt.ImageQt(auto_theme_overlay(dupe_file_image, use_alpha=False))
)
dupe_file_pixmap.setDevicePixelRatio(self.devicePixelRatio())
dupe_file_pixmap = dupe_file_pixmap.scaledToWidth(
diff --git a/src/tagstudio/qt/views/main_window.py b/src/tagstudio/qt/views/main_window.py
index b9229fdd..4fc94bfa 100644
--- a/src/tagstudio/qt/views/main_window.py
+++ b/src/tagstudio/qt/views/main_window.py
@@ -37,7 +37,7 @@ from PySide6.QtWidgets import (
from tagstudio.core.enums import ShowFilepathOption
from tagstudio.core.library.alchemy.enums import SortingModeEnum, TagColorEnum
from tagstudio.qt.controllers.preview_panel_controller import PreviewPanel
-from tagstudio.qt.helpers.color_overlay import theme_fg_overlay
+from tagstudio.qt.helpers.color_overlay import auto_theme_overlay
from tagstudio.qt.mixed.landing import LandingWidget
from tagstudio.qt.mixed.pagination import Pagination
from tagstudio.qt.mixed.tag_widget import get_border_color, get_highlight_color, get_text_color
@@ -547,8 +547,8 @@ class MainWindow(QMainWindow):
self.search_bar_layout.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize)
self.back_button = QPushButton(self.central_widget)
- back_icon: Image.Image = self.rm.get("bxs-left-arrow") # pyright: ignore[reportAssignmentType]
- back_icon = theme_fg_overlay(back_icon, use_alpha=False)
+ back_icon: Image.Image = self.rm.bxs_left_arrow
+ back_icon = auto_theme_overlay(back_icon, use_alpha=False)
self.back_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(back_icon)))
self.back_button.setObjectName("back_button")
self.back_button.setMinimumSize(QSize(32, 32))
@@ -556,8 +556,8 @@ class MainWindow(QMainWindow):
self.search_bar_layout.addWidget(self.back_button)
self.forward_button = QPushButton(self.central_widget)
- forward_icon: Image.Image = self.rm.get("bxs-right-arrow") # pyright: ignore[reportAssignmentType]
- forward_icon = theme_fg_overlay(forward_icon, use_alpha=False)
+ forward_icon: Image.Image = self.rm.bxs_right_arrow
+ forward_icon = auto_theme_overlay(forward_icon, use_alpha=False)
self.forward_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(forward_icon)))
self.forward_button.setIconSize(QSize(16, 16))
self.forward_button.setObjectName("forward_button")
diff --git a/src/tagstudio/qt/views/splash.py b/src/tagstudio/qt/views/splash.py
index 220c4c5c..8f63c78b 100644
--- a/src/tagstudio/qt/views/splash.py
+++ b/src/tagstudio/qt/views/splash.py
@@ -10,9 +10,10 @@ from PySide6.QtCore import QRect, Qt
from PySide6.QtGui import QColor, QFont, QPainter, QPen, QPixmap
from PySide6.QtWidgets import QSplashScreen, QWidget
-from tagstudio.core.constants import VERSION, VERSION_BRANCH
+from tagstudio.core.constants import COPYRIGHT, COPYRIGHT_COMPACT, VERSION, VERSION_BRANCH
from tagstudio.qt.global_settings import Splash
from tagstudio.qt.resource_manager import ResourceManager
+from tagstudio.qt.translations import Translations
logger = structlog.get_logger(__name__)
@@ -20,12 +21,8 @@ logger = structlog.get_logger(__name__)
class SplashScreen:
"""The custom splash screen widget for TagStudio."""
- COPYRIGHT_YEARS: str = "2021-2025"
- COPYRIGHT_STR: str = f"© {COPYRIGHT_YEARS} Travis Abendshien (CyanVoxel)"
- VERSION_STR: str = (
- f"Version {VERSION} {(' (' + VERSION_BRANCH + ')') if VERSION_BRANCH else ''}"
- )
- DEFAULT_SPLASH = Splash.GOO_GEARS
+ VERSION_STR: str = f"{Translations['about.version']} {VERSION} {(' (' + VERSION_BRANCH + ')') if VERSION_BRANCH else ''}" # noqa: E501
+ DEFAULT_SPLASH = Splash.AURORA
def __init__(
self,
@@ -50,7 +47,8 @@ class SplashScreen:
def get_pixmap(self) -> QPixmap:
"""Get the pixmap used for the splash screen."""
- pixmap: QPixmap | None = self.rm.get(f"splash_{self.splash_name}") # pyright: ignore[reportAssignmentType]
+ pixmap = self.rm.get(f"splash_{self.splash_name}")
+ assert isinstance(pixmap, QPixmap)
if not pixmap:
logger.error("[Splash] Splash screen not found:", splash_name=self.splash_name)
pixmap = QPixmap(960, 540)
@@ -70,19 +68,19 @@ class SplashScreen:
font = painter.font()
font.setPointSize(math.floor(22 * point_size_scale))
painter.setFont(font)
- pen = QPen(QColor("#9782ff"))
- painter.setPen(pen)
- painter.drawText(
- QRect(0, -50, 960, 540),
- int(Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignHCenter),
- SplashScreen.COPYRIGHT_STR,
- )
- # Version
pen = QPen(QColor("#809782ff"))
painter.setPen(pen)
painter.drawText(
QRect(0, -25, 960, 540),
int(Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignHCenter),
+ COPYRIGHT,
+ )
+ # Version
+ pen = QPen(QColor("#9782ff"))
+ painter.setPen(pen)
+ painter.drawText(
+ QRect(0, -50, 960, 540),
+ int(Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignHCenter),
SplashScreen.VERSION_STR,
)
@@ -91,20 +89,20 @@ class SplashScreen:
font = painter.font()
font.setPointSize(math.floor(22 * point_size_scale))
painter.setFont(font)
- pen = QPen(QColor("#9782ff"))
+ pen = QPen(QColor("#809782ff"))
painter.setPen(pen)
painter.drawText(
QRect(40, 450, 960, 540),
- SplashScreen.COPYRIGHT_STR,
+ COPYRIGHT_COMPACT,
)
# Version
font = painter.font()
font.setPointSize(math.floor(22 * point_size_scale))
painter.setFont(font)
- pen = QPen(QColor("#809782ff"))
+ pen = QPen(QColor("#9782ff"))
painter.setPen(pen)
painter.drawText(
- QRect(40, 475, 960, 540),
+ QRect(40, 420, 960, 540),
SplashScreen.VERSION_STR,
)
@@ -121,7 +119,7 @@ class SplashScreen:
painter.drawText(
QRect(88, -25, 960, 540),
int(Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignLeft),
- SplashScreen.COPYRIGHT_STR,
+ COPYRIGHT,
)
# Version
font.setPointSize(math.floor(22 * point_size_scale))
@@ -134,17 +132,33 @@ class SplashScreen:
SplashScreen.VERSION_STR,
)
+ case Splash.AURORA:
+ # Copyright
+ font = painter.font()
+ font.setPointSize(math.floor(22 * point_size_scale))
+ painter.setFont(font)
+ pen = QPen(QColor("#907758FF"))
+ painter.setPen(pen)
+ painter.drawText(
+ QRect(0, -25, 960, 540),
+ int(Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignHCenter),
+ COPYRIGHT,
+ )
+ # Version
+ pen = QPen(QColor("#7758FF"))
+ painter.setPen(pen)
+ painter.drawText(
+ QRect(0, -50, 960, 540),
+ int(Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignHCenter),
+ SplashScreen.VERSION_STR,
+ )
+
case _:
pass
pixmap.setDevicePixelRatio(self.ratio)
pixmap = pixmap.scaledToWidth(
- math.floor(
- min(
- (self.screen_width * self.ratio) / 4,
- pixmap.width(),
- )
- ),
+ math.floor(min((self.screen_width * self.ratio) / 4, pixmap.width())), # pyright: ignore[reportCallIssue]
Qt.TransformationMode.SmoothTransformation,
)
diff --git a/src/tagstudio/resources/icon.icns b/src/tagstudio/resources/icon.icns
index 2c84be07..100f5309 100644
Binary files a/src/tagstudio/resources/icon.icns and b/src/tagstudio/resources/icon.icns differ
diff --git a/src/tagstudio/resources/icon.ico b/src/tagstudio/resources/icon.ico
index 71335bd5..e011c3ac 100644
Binary files a/src/tagstudio/resources/icon.ico and b/src/tagstudio/resources/icon.ico differ
diff --git a/src/tagstudio/resources/icon.png b/src/tagstudio/resources/icon.png
index dd3d2772..be9e7a82 100644
Binary files a/src/tagstudio/resources/icon.png and b/src/tagstudio/resources/icon.png differ
diff --git a/src/tagstudio/resources/qt/images/about_bg.jpg b/src/tagstudio/resources/qt/images/about_bg.jpg
new file mode 100644
index 00000000..8ff0a6d5
Binary files /dev/null and b/src/tagstudio/resources/qt/images/about_bg.jpg differ
diff --git a/src/tagstudio/resources/qt/images/box_icon_empty_128 - Copy.png b/src/tagstudio/resources/qt/images/box_icon_empty_128 - Copy.png
deleted file mode 100644
index 4fc14bc6..00000000
Binary files a/src/tagstudio/resources/qt/images/box_icon_empty_128 - Copy.png and /dev/null differ
diff --git a/src/tagstudio/resources/qt/images/box_icon_filled_128 - Copy.png b/src/tagstudio/resources/qt/images/box_icon_filled_128 - Copy.png
deleted file mode 100644
index 67dd6d5b..00000000
Binary files a/src/tagstudio/resources/qt/images/box_icon_filled_128 - Copy.png and /dev/null differ
diff --git a/src/tagstudio/resources/qt/images/bxs-clipboard-regular.png b/src/tagstudio/resources/qt/images/bxs-clipboard-regular.png
new file mode 100644
index 00000000..1166820a
Binary files /dev/null and b/src/tagstudio/resources/qt/images/bxs-clipboard-regular.png differ
diff --git a/src/tagstudio/resources/qt/images/bxs-pencil-solid.png b/src/tagstudio/resources/qt/images/bxs-pencil-solid.png
new file mode 100644
index 00000000..9841f739
Binary files /dev/null and b/src/tagstudio/resources/qt/images/bxs-pencil-solid.png differ
diff --git a/src/tagstudio/resources/qt/images/bxs-trash-solid.png b/src/tagstudio/resources/qt/images/bxs-trash-solid.png
new file mode 100644
index 00000000..f1c0c078
Binary files /dev/null and b/src/tagstudio/resources/qt/images/bxs-trash-solid.png differ
diff --git a/src/tagstudio/resources/qt/images/bxs-volume-full-solid.png b/src/tagstudio/resources/qt/images/bxs-volume-full-solid.png
new file mode 100644
index 00000000..f24ede9e
Binary files /dev/null and b/src/tagstudio/resources/qt/images/bxs-volume-full-solid.png differ
diff --git a/src/tagstudio/resources/qt/images/bxs-volume-mute-solid.png b/src/tagstudio/resources/qt/images/bxs-volume-mute-solid.png
new file mode 100644
index 00000000..1e8bca80
Binary files /dev/null and b/src/tagstudio/resources/qt/images/bxs-volume-mute-solid.png differ
diff --git a/src/tagstudio/resources/qt/images/clipboard_icon_128.png b/src/tagstudio/resources/qt/images/clipboard_icon_128.png
deleted file mode 100644
index 9261b476..00000000
Binary files a/src/tagstudio/resources/qt/images/clipboard_icon_128.png and /dev/null differ
diff --git a/src/tagstudio/resources/qt/images/edit_icon_128.png b/src/tagstudio/resources/qt/images/edit_icon_128.png
deleted file mode 100644
index 85f85d33..00000000
Binary files a/src/tagstudio/resources/qt/images/edit_icon_128.png and /dev/null differ
diff --git a/src/tagstudio/resources/qt/images/edit_icon_dark_128.png b/src/tagstudio/resources/qt/images/edit_icon_dark_128.png
deleted file mode 100644
index 249da512..00000000
Binary files a/src/tagstudio/resources/qt/images/edit_icon_dark_128.png and /dev/null differ
diff --git a/src/tagstudio/resources/qt/images/pause.png b/src/tagstudio/resources/qt/images/pause.png
new file mode 100644
index 00000000..7e70585e
Binary files /dev/null and b/src/tagstudio/resources/qt/images/pause.png differ
diff --git a/src/tagstudio/resources/qt/images/pause.svg b/src/tagstudio/resources/qt/images/pause.svg
deleted file mode 100644
index f7777470..00000000
--- a/src/tagstudio/resources/qt/images/pause.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/tagstudio/resources/qt/images/play.svg b/src/tagstudio/resources/qt/images/play.svg
deleted file mode 100644
index 3d5f6506..00000000
--- a/src/tagstudio/resources/qt/images/play.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/tagstudio/resources/qt/images/splash/aurora.png b/src/tagstudio/resources/qt/images/splash/aurora.png
new file mode 100644
index 00000000..c1902c99
Binary files /dev/null and b/src/tagstudio/resources/qt/images/splash/aurora.png differ
diff --git a/src/tagstudio/resources/qt/images/splitter_handle_128.png b/src/tagstudio/resources/qt/images/splitter_handle_128.png
deleted file mode 100644
index 328a0dd5..00000000
Binary files a/src/tagstudio/resources/qt/images/splitter_handle_128.png and /dev/null differ
diff --git a/src/tagstudio/resources/qt/images/star_icon_empty_128 - Copy.png b/src/tagstudio/resources/qt/images/star_icon_empty_128 - Copy.png
deleted file mode 100644
index ab1ef1ff..00000000
Binary files a/src/tagstudio/resources/qt/images/star_icon_empty_128 - Copy.png and /dev/null differ
diff --git a/src/tagstudio/resources/qt/images/star_icon_filled_128 - Copy.png b/src/tagstudio/resources/qt/images/star_icon_filled_128 - Copy.png
deleted file mode 100644
index 0bfcf2b1..00000000
Binary files a/src/tagstudio/resources/qt/images/star_icon_filled_128 - Copy.png and /dev/null differ
diff --git a/src/tagstudio/resources/qt/images/tag_group_icon_128.png b/src/tagstudio/resources/qt/images/tag_group_icon_128.png
deleted file mode 100644
index 18510d5d..00000000
Binary files a/src/tagstudio/resources/qt/images/tag_group_icon_128.png and /dev/null differ
diff --git a/src/tagstudio/resources/qt/images/tag_group_icon_dark_128.png b/src/tagstudio/resources/qt/images/tag_group_icon_dark_128.png
deleted file mode 100644
index a698c030..00000000
Binary files a/src/tagstudio/resources/qt/images/tag_group_icon_dark_128.png and /dev/null differ
diff --git a/src/tagstudio/resources/qt/images/tagstudio_logo-text_color.png b/src/tagstudio/resources/qt/images/tagstudio_logo-text_color.png
new file mode 100644
index 00000000..440b5837
Binary files /dev/null and b/src/tagstudio/resources/qt/images/tagstudio_logo-text_color.png differ
diff --git a/src/tagstudio/resources/qt/images/tagstudio_logo-text_mono.png b/src/tagstudio/resources/qt/images/tagstudio_logo-text_mono.png
new file mode 100644
index 00000000..a464e7a7
Binary files /dev/null and b/src/tagstudio/resources/qt/images/tagstudio_logo-text_mono.png differ
diff --git a/src/tagstudio/resources/qt/images/tagstudio_logo_text_mono.png b/src/tagstudio/resources/qt/images/tagstudio_logo_text_mono.png
deleted file mode 100644
index a5d33347..00000000
Binary files a/src/tagstudio/resources/qt/images/tagstudio_logo_text_mono.png and /dev/null differ
diff --git a/src/tagstudio/resources/qt/images/trash_icon_128.png b/src/tagstudio/resources/qt/images/trash_icon_128.png
deleted file mode 100644
index a1d11843..00000000
Binary files a/src/tagstudio/resources/qt/images/trash_icon_128.png and /dev/null differ
diff --git a/src/tagstudio/resources/qt/images/trash_icon_dark_128.png b/src/tagstudio/resources/qt/images/trash_icon_dark_128.png
deleted file mode 100644
index 775b55f1..00000000
Binary files a/src/tagstudio/resources/qt/images/trash_icon_dark_128.png and /dev/null differ
diff --git a/src/tagstudio/resources/qt/images/volume.svg b/src/tagstudio/resources/qt/images/volume.svg
deleted file mode 100644
index d240d7ae..00000000
--- a/src/tagstudio/resources/qt/images/volume.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/tagstudio/resources/qt/images/volume_mute.svg b/src/tagstudio/resources/qt/images/volume_mute.svg
deleted file mode 100644
index 5ce8f0a4..00000000
--- a/src/tagstudio/resources/qt/images/volume_mute.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/tagstudio/resources/translations/en.json b/src/tagstudio/resources/translations/en.json
index 1035c151..80b27989 100644
--- a/src/tagstudio/resources/translations/en.json
+++ b/src/tagstudio/resources/translations/en.json
@@ -1,14 +1,16 @@
{
"about.config_path": "Config Path",
+ "about.app_cache_path": "App Cache Path",
"about.description": "TagStudio is a photo and file organization application with an underlying tag-based system that focuses on giving freedom and flexibility to the user. No proprietary programs or formats, no sea of sidecar files, and no complete upheaval of your filesystem structure.",
"about.documentation": "Documentation",
- "about.license": "License",
"about.module.found": "Found",
"about.title": "About TagStudio",
+ "about.version": "Version",
"about.website": "Website",
"app.git": "Git Commit",
"app.pre_release": "Pre-Release",
"app.title": "{base_title} - Library '{library_dir}'",
+ "color_manager.title": "Manage Tag Colors",
"color.color_border": "Use Secondary Color for Border",
"color.confirm_delete": "Are you sure you want to delete the color \"{color_name}\"?",
"color.delete": "Delete Tag",
@@ -22,7 +24,6 @@
"color.primary_required": "Primary Color (Required)",
"color.secondary": "Secondary Color",
"color.title.no_color": "No Color",
- "color_manager.title": "Manage Tag Colors",
"dependency.missing.title": "{dependency} Not Found",
"drop_import.description": "The following files match file paths that already exist in the library",
"drop_import.duplicates_choice.plural": "The following {count} files match file paths that already exist in the library.",
@@ -72,6 +73,13 @@
"entries.unlinked.unlinked_count": "Unlinked Entries: {count}",
"ffmpeg.missing.description": "FFmpeg and/or FFprobe were not found. FFmpeg is required for multimedia playback and thumbnails.",
"ffmpeg.missing.status": "{ffmpeg}: {ffmpeg_status}
{ffprobe}: {ffprobe_status}",
+ "field_template_manager.title": "Library Field Templates",
+ "field_template.all_field_templates": "All Field Templates",
+ "field_template.create": "Create Field Template",
+ "field_template.create_add": "Create && Add \"{query}\"",
+ "field_type.datetime": "Datetime",
+ "field_type.text": "Text",
+ "field_type.unknown": "Unknown Type",
"field.add": "Add Field",
"field.add.plural": "Add Fields",
"field.confirm_remove": "Are you sure you want to remove this \"{name}\" field?",
@@ -80,13 +88,6 @@
"field.mixed_data": "Mixed Data",
"field.paste": "Paste Field",
"field.remove": "Remove Field",
- "field_template.all_field_templates": "All Field Templates",
- "field_template.create": "Create Field Template",
- "field_template.create_add": "Create && Add \"{query}\"",
- "field_template_manager.title": "Library Field Templates",
- "field_type.datetime": "Datetime",
- "field_type.text": "Text",
- "field_type.unknown": "Unknown Type",
"file.date_added": "Date Added",
"file.date_created": "Date Created",
"file.date_modified": "Date Modified",
@@ -100,8 +101,8 @@
"file.duplicates.fix": "Fix Duplicate Files",
"file.duplicates.matches": "Duplicate File Matches: {count}",
"file.duplicates.matches_uninitialized": "Duplicate File Matches: N/A",
- "file.duplicates.mirror.description": "Mirror the Entry data across each duplicate match set, combining all data while not removing or duplicating fields. This operation will not delete any files or data.",
"file.duplicates.mirror_entries": "&Mirror Entries",
+ "file.duplicates.mirror.description": "Mirror the Entry data across each duplicate match set, combining all data while not removing or duplicating fields. This operation will not delete any files or data.",
"file.duration": "Length",
"file.not_found": "File Not Found",
"file.open_file": "Open file",
@@ -150,11 +151,11 @@
"generic.skip_alt": "&Skip",
"generic.yes": "Yes",
"home.search": "Search",
- "home.search.view_limit": "View Limit:",
"home.search_entries": "Search Entries",
"home.search_field_templates": "Search Field Templates",
"home.search_library": "Search Library",
"home.search_tags": "Search Tags",
+ "home.search.view_limit": "View Limit:",
"home.show_hidden_entries": "Show Hidden Entries",
"home.thumbnail_size": "Thumbnail Size",
"home.thumbnail_size.extra_large": "Extra Large Thumbnails",
@@ -187,13 +188,6 @@
"json_migration.title.new_lib": "v9.5+ Library
",
"json_migration.title.old_lib": "v9.4 Library
",
"landing.open_create_library": "Open/Create Library {shortcut}",
- "library.missing": "Library Location is Missing",
- "library.name": "Library",
- "library.refresh.scanning.plural": "Scanning Directories for New Files...\n{searched_count} Files Searched, {found_count} New Files Found",
- "library.refresh.scanning.singular": "Scanning Directories for New Files...\n{searched_count} File Searched, {found_count} New Files Found",
- "library.refresh.scanning_preparing": "Scanning Directories for New Files...\nPreparing...",
- "library.refresh.title": "Refreshing Directories",
- "library.scan_library.title": "Scanning Library",
"library_info.cleanup": "Cleanup",
"library_info.cleanup.backups": "Library Backups:",
"library_info.cleanup.dupe_files": "Duplicate Files:",
@@ -213,6 +207,13 @@
"library_object.name_required": "Name (Required)",
"library_object.slug": "ID Slug",
"library_object.slug_required": "ID Slug (Required)",
+ "library.missing": "Library Location is Missing",
+ "library.name": "Library",
+ "library.refresh.scanning_preparing": "Scanning Directories for New Files...\nPreparing...",
+ "library.refresh.scanning.plural": "Scanning Directories for New Files...\n{searched_count} Files Searched, {found_count} New Files Found",
+ "library.refresh.scanning.singular": "Scanning Directories for New Files...\n{searched_count} File Searched, {found_count} New Files Found",
+ "library.refresh.title": "Refreshing Directories",
+ "library.scan_library.title": "Scanning Library",
"macros.running.dialog.new_entries": "Running Configured Macros on {count}/{total} New File Entries...",
"macros.running.dialog.title": "Running Macros on New Entries",
"media_player.autoplay": "Autoplay",
@@ -288,6 +289,7 @@
"settings.show_filenames_in_grid": "Show Filenames in Grid",
"settings.show_recent_libraries": "Show Recent Libraries",
"settings.splash.label": "Splash Screen",
+ "settings.splash.option.aurora": "Aurora (9.6)",
"settings.splash.option.classic": "Classic (9.0)",
"settings.splash.option.default": "Default",
"settings.splash.option.goo_gears": "Open Source (9.4)",
@@ -323,11 +325,12 @@
"status.library_version_found": "Found:",
"status.library_version_mismatch": "Library Version Mismatch!",
"status.results": "Results",
- "status.results.invalid_syntax": "Invalid Search Syntax:",
"status.results_found": "{count} Results Found ({time_span})",
+ "status.results.invalid_syntax": "Invalid Search Syntax:",
+ "tag_manager.title": "Library Tags",
"tag.add": "Add Tag",
- "tag.add.plural": "Add Tags",
"tag.add_to_search": "Add to Search",
+ "tag.add.plural": "Add Tags",
"tag.aliases": "Aliases",
"tag.all_tags": "All Tags",
"tag.choose_color": "Choose Tag Color",
@@ -348,7 +351,6 @@
"tag.search_for_tag": "Search for Tag",
"tag.shorthand": "Shorthand",
"tag.tag_name_required": "Tag Name (Required)",
- "tag_manager.title": "Library Tags",
"trash.context.ambiguous": "Move file(s) to {trash_term}",
"trash.context.plural": "Move files to {trash_term}",
"trash.context.singular": "Move file to {trash_term}",
diff --git a/tests/qt/test_resource_manager.py b/tests/qt/test_resource_manager.py
index 5a6d72df..dd6afbb4 100644
--- a/tests/qt/test_resource_manager.py
+++ b/tests/qt/test_resource_manager.py
@@ -13,6 +13,6 @@ logger = structlog.get_logger()
def test_get():
rm = ResourceManager()
- for res in rm._map: # pyright: ignore[reportPrivateUsage]
+ for res in rm._map:
assert rm.get(res), f"Could not get resource '{res}'"
assert unwrap(rm.get_path(res)).exists(), f"Filepath for resource '{res}' does not exist"
diff --git a/tests/test_translations.py b/tests/test_translations.py
index 8d0533f3..5a699de5 100644
--- a/tests/test_translations.py
+++ b/tests/test_translations.py
@@ -3,6 +3,7 @@
import string
+import warnings
from pathlib import Path
import pytest
@@ -57,6 +58,8 @@ def test_format_key_validity(translation_filename: str):
def test_for_unnecessary_translations(translation_filename: str):
default_translation = load_translation("en.json")
translation = load_translation(translation_filename)
- assert set(default_translation.keys()).issuperset(translation.keys()), (
- f"Translation {translation_filename} has unnecessary keys ({set(translation.keys()).difference(default_translation.keys())})" # noqa: E501
- )
+ if not set(default_translation.keys()).issuperset(translation.keys()):
+ message = str(
+ f"Translation {translation_filename} has unnecessary keys ({set(translation.keys()).difference(default_translation.keys())})", # noqa: E501
+ )
+ warnings.warn(message, stacklevel=1)