mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-08-22 20:10:21 +02:00
begin making renderers classes, improve text thumbnails
This commit is contained in:
+15
-4
@@ -23,12 +23,12 @@ path = [
|
||||
".gitignore",
|
||||
"flake.lock",
|
||||
]
|
||||
SPDX-FileCopyrightText = "(c) TagStudio Contributors"
|
||||
SPDX-FileCopyrightText = "TagStudio Contributors"
|
||||
SPDX-License-Identifier = "GPL-3.0-only"
|
||||
|
||||
[[annotations]]
|
||||
path = ["src/tagstudio/resources/translations/*.json"]
|
||||
SPDX-FileCopyrightText = "(c) TagStudio Contributors"
|
||||
SPDX-FileCopyrightText = "TagStudio Contributors"
|
||||
SPDX-License-Identifier = "GPL-3.0-or-later"
|
||||
|
||||
[[annotations]]
|
||||
@@ -41,7 +41,7 @@ path = [
|
||||
"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-FileCopyrightText = "2026 Boxicons"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
@@ -53,5 +53,16 @@ path = [
|
||||
"src/tagstudio/resources/qt/images/hint_tag_added.png",
|
||||
"src/tagstudio/resources/qt/images/hint_tag_create.png",
|
||||
]
|
||||
SPDX-FileCopyrightText = "(c) github:google/material-design-icons Contributors"
|
||||
SPDX-FileCopyrightText = "github:google/material-design-icons Contributors"
|
||||
SPDX-License-Identifier = "Apache-2.0"
|
||||
|
||||
[[annotations]]
|
||||
path = ["src/tagstudio/resources/qt/fonts/Oxanium-Bold.ttf"]
|
||||
SPDX-FileCopyrightText = "2019 The Oxanium Project Authors (https://github.com/sevmeyer/oxanium)"
|
||||
SPDX-License-Identifier = "OFL-1.1"
|
||||
|
||||
|
||||
[[annotations]]
|
||||
path = ["src/tagstudio/resources/fonts/JetBrainsMono/**"]
|
||||
SPDX-FileCopyrightText = "2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)"
|
||||
SPDX-License-Identifier = "OFL-1.1"
|
||||
|
||||
@@ -31,7 +31,7 @@ class TagClickActionOption(enum.IntEnum):
|
||||
DEFAULT = OPEN_EDIT
|
||||
|
||||
|
||||
class Theme(enum.StrEnum):
|
||||
class ThemePalette(enum.StrEnum):
|
||||
COLOR_BG_DARK = "#65000000"
|
||||
COLOR_BG_LIGHT = "#22000000"
|
||||
COLOR_DARK_LABEL = "#DD000000"
|
||||
@@ -44,6 +44,13 @@ class Theme(enum.StrEnum):
|
||||
COLOR_FORBIDDEN_BG = "#65440D12"
|
||||
|
||||
|
||||
class Theme(enum.IntEnum):
|
||||
DARK = 0
|
||||
LIGHT = 1
|
||||
SYSTEM = 2
|
||||
DEFAULT = SYSTEM
|
||||
|
||||
|
||||
class OpenStatus(enum.IntEnum):
|
||||
NOT_FOUND = 0
|
||||
SUCCESS = 1
|
||||
|
||||
@@ -105,7 +105,14 @@ class RefreshTracker:
|
||||
shell=True,
|
||||
encoding="UTF-8",
|
||||
)
|
||||
compiled_ignore_path.unlink()
|
||||
try:
|
||||
compiled_ignore_path.unlink()
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"[Refresh] Could not remove compiled ignore path",
|
||||
path=compiled_ignore_path,
|
||||
error=e,
|
||||
)
|
||||
|
||||
if result.stderr:
|
||||
logger.error(result.stderr)
|
||||
|
||||
+591
-248
@@ -37,8 +37,8 @@ class _Type:
|
||||
|
||||
class MediaTypeGroup:
|
||||
def __init__(self, name_key: str, types: list[_Type]) -> None:
|
||||
self.searchable: frozenset[str]
|
||||
self.renderable: frozenset[str]
|
||||
self.searchable: frozenset[str] # TODO: Make these dynamic
|
||||
self.renderable: frozenset[str] # TODO: Make these dynamic
|
||||
self.name_key = name_key
|
||||
self.types = types
|
||||
# NOTE: Does self.types need to exist?
|
||||
@@ -78,254 +78,597 @@ class MediaTypeGroup:
|
||||
|
||||
|
||||
class MediaTypes:
|
||||
SEARCH, RENDER = Context.SEARCH, Context.RENDER
|
||||
|
||||
# Adobe ----------------------------------------------------------------------------------------
|
||||
adobe_photoshop = MediaTypeGroup(
|
||||
"adobe.photoshop",
|
||||
[
|
||||
_Type(".pdd", SEARCH),
|
||||
_Type(".psb", SEARCH),
|
||||
_Type(".psd", SEARCH),
|
||||
],
|
||||
)
|
||||
adobe_illustrator = MediaTypeGroup(
|
||||
"adobe.illustrator",
|
||||
[
|
||||
_Type(".ai", SEARCH),
|
||||
],
|
||||
)
|
||||
pdf = MediaTypeGroup(
|
||||
"pdf",
|
||||
[
|
||||
_Type(".pdf", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
adobe = MediaTypeGroup("adobe", adobe_photoshop.types + adobe_illustrator.types + pdf.types)
|
||||
|
||||
# Affinity -------------------------------------------------------------------------------------
|
||||
affinity_photo = MediaTypeGroup(
|
||||
"affinity.photo",
|
||||
[_Type(".afphoto", [SEARCH, RENDER])],
|
||||
)
|
||||
affinity_designer = MediaTypeGroup(
|
||||
"affinity.designer",
|
||||
[_Type(".afdesign", [SEARCH, RENDER])],
|
||||
)
|
||||
affinity_publisher = MediaTypeGroup(
|
||||
"affinity.publisher",
|
||||
[_Type([".afpublisher", ".afpub"], [SEARCH, RENDER])],
|
||||
)
|
||||
|
||||
affinity = MediaTypeGroup(
|
||||
"affinity",
|
||||
affinity_photo.types
|
||||
+ affinity_designer.types
|
||||
+ affinity_publisher.types
|
||||
+ [_Type(".af", [SEARCH, RENDER])],
|
||||
)
|
||||
|
||||
# MIDI -----------------------------------------------------------------------------------------
|
||||
midi = MediaTypeGroup("midi", [_Type([".mid", ".midi"], SEARCH)])
|
||||
|
||||
# Audio ----------------------------------------------------------------------------------------
|
||||
audio = MediaTypeGroup(
|
||||
"audio",
|
||||
[
|
||||
_Type(".aac", [RENDER, SEARCH]),
|
||||
_Type(
|
||||
[".aif", ".aiff", ".aifc"],
|
||||
[RENDER, SEARCH],
|
||||
),
|
||||
_Type(".caf", [RENDER, SEARCH]),
|
||||
_Type(".flac", [RENDER, SEARCH]),
|
||||
_Type(".m4a", [RENDER, SEARCH]),
|
||||
_Type(".m4p", [RENDER, SEARCH]),
|
||||
_Type(".mp3", [RENDER, SEARCH]),
|
||||
_Type(".ogg", [RENDER, SEARCH]),
|
||||
_Type(".wav", [RENDER, SEARCH]),
|
||||
_Type(".wma", [RENDER, SEARCH]),
|
||||
]
|
||||
+ midi.types,
|
||||
)
|
||||
|
||||
# RAW Images -----------------------------------------------------------------------------------
|
||||
raw_image = MediaTypeGroup(
|
||||
"image.raw",
|
||||
[
|
||||
_Type(".arw", [SEARCH, RENDER]),
|
||||
_Type(".cr2", [SEARCH, RENDER]),
|
||||
_Type(".cr3", [SEARCH, RENDER]),
|
||||
_Type(".crw", [SEARCH, RENDER]),
|
||||
_Type(".dng", [SEARCH, RENDER]),
|
||||
_Type(".nef", [SEARCH, RENDER]),
|
||||
_Type(".nrw", [SEARCH, RENDER]),
|
||||
_Type(".orf", [SEARCH, RENDER]),
|
||||
_Type(".r3d", [SEARCH, RENDER]),
|
||||
_Type(".raf", [SEARCH, RENDER]),
|
||||
_Type(".raw", [SEARCH, RENDER]),
|
||||
_Type(".rw2", [SEARCH, RENDER]),
|
||||
_Type(".srf", [SEARCH, RENDER]),
|
||||
_Type(".srf2", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
# Raster Images --------------------------------------------------------------------------------
|
||||
raster_image = MediaTypeGroup(
|
||||
"image.raster",
|
||||
[
|
||||
_Type(".apng", [SEARCH, RENDER]),
|
||||
_Type(".avif", [SEARCH, RENDER]),
|
||||
_Type(".bmp", [SEARCH, RENDER]),
|
||||
_Type(".exr", [SEARCH, RENDER]),
|
||||
_Type(".gif", [SEARCH, RENDER]),
|
||||
_Type(
|
||||
[
|
||||
".jfif",
|
||||
".jpeg_large",
|
||||
".jpeg",
|
||||
".jpg_large",
|
||||
".jpg",
|
||||
],
|
||||
[SEARCH, RENDER],
|
||||
),
|
||||
_Type(".jxl", [SEARCH, RENDER]),
|
||||
_Type(".png", [SEARCH, RENDER]),
|
||||
_Type(".psb", RENDER),
|
||||
_Type(".psd", RENDER),
|
||||
_Type(".webp", [SEARCH, RENDER]),
|
||||
_Type([".heic", ".heif"], [SEARCH, RENDER]),
|
||||
_Type([".j2k", ".jp2", ".jpg2"], [SEARCH, RENDER]),
|
||||
_Type([".tif", ".tiff"], [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
# Vector Images --------------------------------------------------------------------------------
|
||||
vector = MediaTypeGroup(
|
||||
"image.vector",
|
||||
[
|
||||
_Type(".ai", RENDER),
|
||||
_Type(".eps", SEARCH),
|
||||
_Type(".epsf", SEARCH),
|
||||
_Type(".epsi", SEARCH),
|
||||
_Type(".svg", [SEARCH, RENDER]),
|
||||
_Type(".svgz", SEARCH),
|
||||
],
|
||||
)
|
||||
|
||||
binary = MediaTypeGroup(
|
||||
"binary",
|
||||
[
|
||||
_Type(".dll", [RENDER, SEARCH]),
|
||||
_Type(".dylib", [RENDER, SEARCH]),
|
||||
_Type(".exe", [RENDER, SEARCH]),
|
||||
_Type(".o", [RENDER, SEARCH]),
|
||||
_Type(".pyc", [RENDER, SEARCH]),
|
||||
_Type(".pyd", [RENDER, SEARCH]),
|
||||
_Type(".pyo", [RENDER, SEARCH]),
|
||||
],
|
||||
)
|
||||
|
||||
python = MediaTypeGroup(
|
||||
"python",
|
||||
[
|
||||
_Type(".ipynb", [RENDER, SEARCH]),
|
||||
_Type(".py", [RENDER, SEARCH]),
|
||||
_Type(".pyc", SEARCH),
|
||||
_Type(".pyd", SEARCH),
|
||||
_Type(".pyi", [RENDER, SEARCH]),
|
||||
_Type(".pyo", SEARCH),
|
||||
],
|
||||
)
|
||||
|
||||
javascript = MediaTypeGroup(
|
||||
"javascript",
|
||||
[
|
||||
_Type(".cjs", [SEARCH, RENDER]),
|
||||
_Type(".js", [SEARCH, RENDER]),
|
||||
_Type(".jsx", [SEARCH, RENDER]),
|
||||
_Type(".mjs", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
typescript = MediaTypeGroup(
|
||||
"typescript",
|
||||
[
|
||||
_Type(".cts", [SEARCH, RENDER]),
|
||||
_Type(".mts", [SEARCH, RENDER]),
|
||||
# _Type(".ts", [SEARCH, RENDER]),
|
||||
_Type(".tsx", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
# Shell Script ---------------------------------------------------------------------------------
|
||||
shell = MediaTypeGroup(
|
||||
"shell",
|
||||
[
|
||||
_Type(".bat", [SEARCH, RENDER]),
|
||||
_Type(".csh", [SEARCH, RENDER]),
|
||||
_Type(".fish", [SEARCH, RENDER]),
|
||||
_Type(".ps1", [SEARCH, RENDER]),
|
||||
_Type(".sh", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
# Markdown -------------------------------------------------------------------------------------
|
||||
markdown = MediaTypeGroup(
|
||||
"markdown",
|
||||
[
|
||||
_Type(
|
||||
[
|
||||
".markdown",
|
||||
".md",
|
||||
".mkd",
|
||||
".rmd",
|
||||
],
|
||||
[SEARCH, RENDER],
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
# Plaintext ------------------------------------------------------------------------------------
|
||||
plaintext = MediaTypeGroup(
|
||||
"plaintext",
|
||||
[_Type([".txt", ".text"], [SEARCH, RENDER])] + markdown.types,
|
||||
)
|
||||
|
||||
# Code -----------------------------------------------------------------------------------------
|
||||
code_types = MediaTypeGroup(
|
||||
"type.code",
|
||||
python.types + javascript.types + typescript.types + shell.types,
|
||||
)
|
||||
|
||||
# Video ----------------------------------------------------------------------------------------
|
||||
video = MediaTypeGroup(
|
||||
"video",
|
||||
[
|
||||
_Type(".3gp", [SEARCH, RENDER]),
|
||||
_Type(".avi", [SEARCH, RENDER]),
|
||||
_Type(".flv", [SEARCH, RENDER]),
|
||||
_Type(".gifv", [SEARCH, RENDER]),
|
||||
_Type(".hevc", [SEARCH, RENDER]),
|
||||
_Type(".m4p", [SEARCH, RENDER]),
|
||||
_Type(".m4v", [SEARCH, RENDER]),
|
||||
_Type(".mkv", [SEARCH, RENDER]),
|
||||
_Type(".mov", [SEARCH, RENDER]),
|
||||
_Type(".mp4", [SEARCH, RENDER]),
|
||||
_Type(".webm", [SEARCH, RENDER]),
|
||||
_Type(".wmv", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
# ------------------
|
||||
# TODO: Implement filename equivalencies
|
||||
# TODO: Implement in_all_groups or something, if needed
|
||||
# TODO: Implement collision detection (e.g. ".ts")
|
||||
|
||||
@staticmethod
|
||||
def all_media_types():
|
||||
static_methods = [
|
||||
name for name, attr in MediaTypes.__dict__.items() if isinstance(attr, MediaTypeGroup)
|
||||
]
|
||||
return static_methods
|
||||
def register(group: MediaTypeGroup) -> None:
|
||||
setattr(MediaTypes, group.name_key.replace(".", "_"), group)
|
||||
|
||||
|
||||
SEARCH, RENDER = Context.SEARCH, Context.RENDER
|
||||
|
||||
# Adobe ----------------------------------------------------------------------------------------
|
||||
|
||||
# TODO: Don't have these floating in the global space
|
||||
_adobe_photoshop = MediaTypeGroup(
|
||||
"adobe.photoshop",
|
||||
[
|
||||
_Type(".pdd", SEARCH),
|
||||
_Type(".psb", SEARCH),
|
||||
_Type(".psd", SEARCH),
|
||||
],
|
||||
)
|
||||
MediaTypes.register(_adobe_photoshop)
|
||||
|
||||
_adobe_illustrator = MediaTypeGroup(
|
||||
"adobe.illustrator",
|
||||
[
|
||||
_Type(".ai", SEARCH),
|
||||
],
|
||||
)
|
||||
MediaTypes.register(_adobe_illustrator)
|
||||
|
||||
_pdf = MediaTypeGroup(
|
||||
"pdf",
|
||||
[
|
||||
_Type(".pdf", [SEARCH, RENDER]),
|
||||
_Type(".ai", RENDER),
|
||||
],
|
||||
)
|
||||
MediaTypes.register(_pdf)
|
||||
|
||||
adobe = MediaTypeGroup("adobe", _adobe_photoshop.types + _adobe_illustrator.types + _pdf.types)
|
||||
MediaTypes.register(adobe)
|
||||
|
||||
# Affinity -------------------------------------------------------------------------------------
|
||||
affinity_photo = MediaTypeGroup(
|
||||
"affinity.photo",
|
||||
[_Type(".afphoto", [SEARCH, RENDER])],
|
||||
)
|
||||
affinity_designer = MediaTypeGroup(
|
||||
"affinity.designer",
|
||||
[_Type(".afdesign", [SEARCH, RENDER])],
|
||||
)
|
||||
affinity_publisher = MediaTypeGroup(
|
||||
"affinity.publisher",
|
||||
[_Type([".afpublisher", ".afpub"], [SEARCH, RENDER])],
|
||||
)
|
||||
|
||||
affinity = MediaTypeGroup(
|
||||
"affinity",
|
||||
affinity_photo.types
|
||||
+ affinity_designer.types
|
||||
+ affinity_publisher.types
|
||||
+ [_Type(".af", [SEARCH, RENDER])],
|
||||
)
|
||||
|
||||
# Blender --------------------------------------------------------------------------------------
|
||||
_blender = MediaTypeGroup(
|
||||
"blender",
|
||||
[
|
||||
_Type(".blen_tc", [SEARCH, RENDER]),
|
||||
_Type(".blend", [SEARCH, RENDER]),
|
||||
# Numbered Blender auto-backup files (.blend1 - .blend32)
|
||||
_Type([f".blend{i}" for i in range(1, 33)], [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
MediaTypes.register(_blender)
|
||||
|
||||
# Clip Studio Paint ----------------------------------------------------------------------------
|
||||
clip_studio_paint = MediaTypeGroup(
|
||||
"clip_studio_paint",
|
||||
[
|
||||
_Type(".lip", [SEARCH, RENDER]),
|
||||
_Type(".clip", [SEARCH, RENDER]),
|
||||
_Type(".cmc", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
# Krita ----------------------------------------------------------------------------------------
|
||||
krita = MediaTypeGroup(
|
||||
"krita",
|
||||
[
|
||||
_Type(".kra", [SEARCH, RENDER]),
|
||||
_Type(".krz", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
# MediBang Paint / FireAlpaca ------------------------------------------------------------------
|
||||
medibang_paint = MediaTypeGroup("medibang_paint", [_Type(".mdp", [SEARCH, RENDER])])
|
||||
|
||||
# Paint.NET ------------------------------------------------------------------------------------
|
||||
paint_dot_net = MediaTypeGroup("paint_dot_net", [_Type(".pdn", [SEARCH, RENDER])])
|
||||
|
||||
# Archives -------------------------------------------------------------------------------------
|
||||
archive = MediaTypeGroup(
|
||||
"archive",
|
||||
[
|
||||
_Type(".7z", [SEARCH, RENDER]),
|
||||
_Type(".gz", SEARCH),
|
||||
_Type(".rar", [SEARCH, RENDER]),
|
||||
_Type(".s7z", [SEARCH, RENDER]),
|
||||
_Type(".tar", [SEARCH, RENDER]),
|
||||
_Type(".tgz", [SEARCH, RENDER]),
|
||||
_Type(".zip", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
# eBooks -------------------------------------------------------------------------------------
|
||||
ebook = MediaTypeGroup(
|
||||
"ebook",
|
||||
[
|
||||
_Type(".azw", [SEARCH, RENDER]),
|
||||
_Type(".azw3", [SEARCH, RENDER]),
|
||||
_Type(".cb7", [SEARCH, RENDER]),
|
||||
_Type(".cba", [SEARCH, RENDER]),
|
||||
_Type(".cbr", [SEARCH, RENDER]),
|
||||
_Type(".cbt", [SEARCH, RENDER]),
|
||||
_Type(".cbz", [SEARCH, RENDER]),
|
||||
_Type(".djvu", [SEARCH, RENDER]),
|
||||
_Type(".epub", [SEARCH, RENDER]),
|
||||
_Type(".fb2", [SEARCH, RENDER]),
|
||||
_Type(".ibook", [SEARCH, RENDER]),
|
||||
_Type(".inf", [SEARCH, RENDER]),
|
||||
_Type(".kfx", [SEARCH, RENDER]),
|
||||
_Type(".lit", [SEARCH, RENDER]),
|
||||
_Type(".mobi", [SEARCH, RENDER]),
|
||||
_Type(".pdb", [SEARCH, RENDER]),
|
||||
_Type(".prc", [SEARCH, RENDER]),
|
||||
_Type(".mscz", RENDER),
|
||||
],
|
||||
)
|
||||
|
||||
# Fonts --------------------------------------------------------------------------------------
|
||||
font = MediaTypeGroup(
|
||||
"font",
|
||||
[
|
||||
_Type(".fon", SEARCH),
|
||||
_Type(".otf", [SEARCH, RENDER]),
|
||||
_Type(".ttc", [SEARCH, RENDER]),
|
||||
_Type(".ttf", [SEARCH, RENDER]),
|
||||
_Type(".woff", [SEARCH, RENDER]),
|
||||
_Type(".woff2", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
# Office & Documents ---------------------------------------------------------------------------
|
||||
document = MediaTypeGroup(
|
||||
"document",
|
||||
[
|
||||
_Type(".doc", SEARCH),
|
||||
_Type(".docm", SEARCH),
|
||||
_Type(".docx", SEARCH),
|
||||
_Type(".dot", SEARCH),
|
||||
_Type(".dotm", SEARCH),
|
||||
_Type(".dotx", SEARCH),
|
||||
_Type(".odt", SEARCH),
|
||||
_Type(".pages", SEARCH),
|
||||
_Type(".pdf", SEARCH),
|
||||
_Type(".pxd", SEARCH),
|
||||
_Type(".rtf", SEARCH),
|
||||
_Type(".tex", SEARCH),
|
||||
_Type(".wpd", SEARCH),
|
||||
_Type(".wps", SEARCH),
|
||||
],
|
||||
)
|
||||
open_document = MediaTypeGroup(
|
||||
"open_document",
|
||||
[
|
||||
_Type(".fodg", [SEARCH, RENDER]),
|
||||
_Type(".fodp", [SEARCH, RENDER]),
|
||||
_Type(".fods", [SEARCH, RENDER]),
|
||||
_Type(".fodt", [SEARCH, RENDER]),
|
||||
_Type(".mscz", SEARCH),
|
||||
_Type(".odf", [SEARCH, RENDER]),
|
||||
_Type(".odg", [SEARCH, RENDER]),
|
||||
_Type(".odp", [SEARCH, RENDER]),
|
||||
_Type(".ods", [SEARCH, RENDER]),
|
||||
_Type(".odt", [SEARCH, RENDER]),
|
||||
_Type(".ora", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
iwork = MediaTypeGroup(
|
||||
"iwork",
|
||||
[
|
||||
_Type(".key", [SEARCH, RENDER]),
|
||||
_Type(".numbers", [SEARCH, RENDER]),
|
||||
_Type(".pages", [SEARCH, RENDER]),
|
||||
_Type(".pxd", RENDER),
|
||||
],
|
||||
)
|
||||
|
||||
presentation = MediaTypeGroup(
|
||||
"presentation",
|
||||
[
|
||||
_Type(".key", SEARCH),
|
||||
_Type(".odp", SEARCH),
|
||||
_Type(".ppt", SEARCH),
|
||||
_Type(".pptx", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
spreadsheet = MediaTypeGroup(
|
||||
"spreadsheet",
|
||||
[
|
||||
_Type(".csv", SEARCH),
|
||||
_Type(".numbers", SEARCH),
|
||||
_Type(".ods", SEARCH),
|
||||
_Type(".xls", SEARCH),
|
||||
_Type(".xlsx", SEARCH),
|
||||
],
|
||||
)
|
||||
|
||||
# 3D -------------------------------------------------------------------------------------------
|
||||
model = MediaTypeGroup(
|
||||
"model",
|
||||
[
|
||||
_Type(".3ds", SEARCH),
|
||||
_Type(".fbx", SEARCH),
|
||||
_Type(".obj", SEARCH),
|
||||
_Type(".stl", SEARCH),
|
||||
],
|
||||
)
|
||||
material = MediaTypeGroup(
|
||||
"material",
|
||||
[
|
||||
_Type(".mtl", SEARCH),
|
||||
],
|
||||
)
|
||||
shader = MediaTypeGroup(
|
||||
"shader",
|
||||
[
|
||||
_Type(".effect", SEARCH),
|
||||
_Type(".frag", SEARCH),
|
||||
_Type(".fsh", SEARCH),
|
||||
_Type(".glsl", SEARCH),
|
||||
_Type(".shader", SEARCH),
|
||||
_Type(".vert", SEARCH),
|
||||
_Type(".vsh", SEARCH),
|
||||
],
|
||||
)
|
||||
|
||||
# System & Misc ------------------------------------------------------------------------------
|
||||
database = MediaTypeGroup(
|
||||
"database",
|
||||
[
|
||||
_Type(".accdb", SEARCH),
|
||||
_Type(".mdb", SEARCH),
|
||||
_Type(".pdb", SEARCH),
|
||||
_Type(".db", SEARCH),
|
||||
_Type(".sqlite", SEARCH),
|
||||
_Type(".sqlite3", SEARCH),
|
||||
],
|
||||
)
|
||||
disk_image = MediaTypeGroup(
|
||||
"disk_image",
|
||||
[
|
||||
_Type(".bios", SEARCH),
|
||||
_Type(".dmg", SEARCH),
|
||||
_Type(".fhdx", SEARCH),
|
||||
_Type(".iso", SEARCH),
|
||||
],
|
||||
)
|
||||
installer = MediaTypeGroup(
|
||||
"installer",
|
||||
[
|
||||
_Type(".appx", SEARCH),
|
||||
_Type(".msi", SEARCH),
|
||||
_Type(".msix", SEARCH),
|
||||
],
|
||||
)
|
||||
package = MediaTypeGroup(
|
||||
"package",
|
||||
[
|
||||
_Type(".aab", SEARCH),
|
||||
_Type(".akp", SEARCH),
|
||||
_Type(".apk", SEARCH),
|
||||
_Type(".apkm", SEARCH),
|
||||
_Type(".apks", SEARCH),
|
||||
_Type(".pkg", SEARCH),
|
||||
_Type(".xapk", SEARCH),
|
||||
],
|
||||
)
|
||||
program = MediaTypeGroup(
|
||||
"program",
|
||||
[
|
||||
_Type(".app", SEARCH),
|
||||
_Type(".bin", SEARCH),
|
||||
_Type(".exe", SEARCH),
|
||||
],
|
||||
)
|
||||
shortcut = MediaTypeGroup(
|
||||
"shortcut",
|
||||
[
|
||||
_Type(".desktop", SEARCH),
|
||||
_Type(".lnk", SEARCH),
|
||||
_Type(".url", SEARCH),
|
||||
],
|
||||
)
|
||||
|
||||
# Valve Source Engine --------------------------------------------------------------------------
|
||||
source_engine = MediaTypeGroup(
|
||||
"source_engine",
|
||||
[_Type(".vtf", [SEARCH, RENDER])],
|
||||
)
|
||||
|
||||
# MIDI -----------------------------------------------------------------------------------------
|
||||
midi = MediaTypeGroup("midi", [_Type([".mid", ".midi"], SEARCH)])
|
||||
|
||||
# Audio ----------------------------------------------------------------------------------------
|
||||
audio = MediaTypeGroup(
|
||||
"audio",
|
||||
[
|
||||
_Type(".aac", [RENDER, SEARCH]),
|
||||
_Type(
|
||||
[".aif", ".aiff", ".aifc"],
|
||||
[RENDER, SEARCH],
|
||||
),
|
||||
_Type(".caf", [RENDER, SEARCH]),
|
||||
_Type(".flac", [RENDER, SEARCH]),
|
||||
_Type(".m4a", [RENDER, SEARCH]),
|
||||
_Type(".m4p", [RENDER, SEARCH]),
|
||||
_Type(".mp3", [RENDER, SEARCH]),
|
||||
_Type(".ogg", [RENDER, SEARCH]),
|
||||
_Type(".wav", [RENDER, SEARCH]),
|
||||
_Type(".wma", [RENDER, SEARCH]),
|
||||
]
|
||||
+ midi.types,
|
||||
)
|
||||
|
||||
# RAW Images -----------------------------------------------------------------------------------
|
||||
raw_image = MediaTypeGroup(
|
||||
"image.raw",
|
||||
[
|
||||
_Type(".arw", [SEARCH, RENDER]),
|
||||
_Type(".cr2", [SEARCH, RENDER]),
|
||||
_Type(".cr3", [SEARCH, RENDER]),
|
||||
_Type(".crw", [SEARCH, RENDER]),
|
||||
_Type(".dng", [SEARCH, RENDER]),
|
||||
_Type(".nef", [SEARCH, RENDER]),
|
||||
_Type(".nrw", [SEARCH, RENDER]),
|
||||
_Type(".orf", [SEARCH, RENDER]),
|
||||
_Type(".r3d", [SEARCH, RENDER]),
|
||||
_Type(".raf", [SEARCH, RENDER]),
|
||||
_Type(".raw", [SEARCH, RENDER]),
|
||||
_Type(".rw2", [SEARCH, RENDER]),
|
||||
_Type(".srf", [SEARCH, RENDER]),
|
||||
_Type(".srf2", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
# Raster Images --------------------------------------------------------------------------------
|
||||
raster_image = MediaTypeGroup(
|
||||
"image.raster",
|
||||
[
|
||||
_Type(".apng", [SEARCH, RENDER]),
|
||||
_Type(".avif", [SEARCH, RENDER]),
|
||||
_Type(".bmp", [SEARCH, RENDER]),
|
||||
_Type(".exr", [SEARCH, RENDER]),
|
||||
_Type(".gif", [SEARCH, RENDER]),
|
||||
_Type(
|
||||
[
|
||||
".jfif",
|
||||
".jpeg_large",
|
||||
".jpeg",
|
||||
".jpg_large",
|
||||
".jpg",
|
||||
],
|
||||
[SEARCH, RENDER],
|
||||
),
|
||||
_Type(".jxl", [SEARCH, RENDER]),
|
||||
_Type(".png", [SEARCH, RENDER]),
|
||||
_Type(".psb", RENDER),
|
||||
_Type(".psd", RENDER),
|
||||
_Type(".webp", [SEARCH, RENDER]),
|
||||
_Type([".heic", ".heif"], [SEARCH, RENDER]),
|
||||
_Type([".j2k", ".jp2", ".jpg2"], [SEARCH, RENDER]),
|
||||
_Type([".tif", ".tiff"], [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
# Vector Images --------------------------------------------------------------------------------
|
||||
vector_image = MediaTypeGroup(
|
||||
"image.vector",
|
||||
[
|
||||
_Type(".eps", SEARCH),
|
||||
_Type(".epsf", SEARCH),
|
||||
_Type(".epsi", SEARCH),
|
||||
_Type(".svg", [SEARCH, RENDER]),
|
||||
_Type(".svgz", SEARCH),
|
||||
],
|
||||
)
|
||||
|
||||
# Animated Images ------------------------------------------------------------------------------
|
||||
animated_image = MediaTypeGroup(
|
||||
"image.animated",
|
||||
[
|
||||
_Type(".gif", [SEARCH, RENDER]),
|
||||
_Type(".apng", [SEARCH, RENDER]),
|
||||
_Type(".webp", [SEARCH, RENDER]),
|
||||
_Type(".jxl", SEARCH),
|
||||
],
|
||||
)
|
||||
|
||||
# Binary ---------------------------------------------------------------------------------------
|
||||
binary = MediaTypeGroup(
|
||||
"binary",
|
||||
[
|
||||
_Type(".dll", [RENDER, SEARCH]),
|
||||
_Type(".dylib", [RENDER, SEARCH]),
|
||||
_Type(".exe", [RENDER, SEARCH]),
|
||||
_Type(".o", [RENDER, SEARCH]),
|
||||
_Type(".pyc", [RENDER, SEARCH]),
|
||||
_Type(".pyd", [RENDER, SEARCH]),
|
||||
_Type(".pyo", [RENDER, SEARCH]),
|
||||
],
|
||||
)
|
||||
|
||||
# Python ---------------------------------------------------------------------------------------
|
||||
python = MediaTypeGroup(
|
||||
"python",
|
||||
[
|
||||
_Type(".ipynb", [RENDER, SEARCH]),
|
||||
_Type(".py", [RENDER, SEARCH]),
|
||||
_Type(".pyc", SEARCH),
|
||||
_Type(".pyd", SEARCH),
|
||||
_Type(".pyi", [RENDER, SEARCH]),
|
||||
_Type(".pyo", SEARCH),
|
||||
],
|
||||
)
|
||||
|
||||
# JavaScript -----------------------------------------------------------------------------------
|
||||
javascript = MediaTypeGroup(
|
||||
"javascript",
|
||||
[
|
||||
_Type(".cjs", [SEARCH, RENDER]),
|
||||
_Type(".js", [SEARCH, RENDER]),
|
||||
_Type(".jsx", [SEARCH, RENDER]),
|
||||
_Type(".mjs", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
# TypeScript -----------------------------------------------------------------------------------
|
||||
typescript = MediaTypeGroup(
|
||||
"typescript",
|
||||
[
|
||||
_Type(".cts", [SEARCH, RENDER]),
|
||||
_Type(".mts", [SEARCH, RENDER]),
|
||||
# _Type(".ts", [SEARCH, RENDER]),
|
||||
_Type(".tsx", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
# Shell Script ---------------------------------------------------------------------------------
|
||||
shell = MediaTypeGroup(
|
||||
"shell",
|
||||
[
|
||||
_Type(".bat", [SEARCH, RENDER]),
|
||||
_Type(".csh", [SEARCH, RENDER]),
|
||||
_Type(".fish", [SEARCH, RENDER]),
|
||||
_Type(".nu", [SEARCH, RENDER]),
|
||||
_Type(".ps1", [SEARCH, RENDER]),
|
||||
_Type(".sh", [SEARCH, RENDER]),
|
||||
_Type("activate", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
# Markdown -------------------------------------------------------------------------------------
|
||||
markdown = MediaTypeGroup(
|
||||
"markdown",
|
||||
[
|
||||
_Type(
|
||||
[
|
||||
".markdown",
|
||||
".md",
|
||||
".mkd",
|
||||
".rmd",
|
||||
],
|
||||
[SEARCH, RENDER],
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
# Markup ---------------------------------------------------------------------------------------
|
||||
markup = MediaTypeGroup(
|
||||
"markup",
|
||||
[
|
||||
_Type(
|
||||
[
|
||||
".yml",
|
||||
".yaml",
|
||||
],
|
||||
[SEARCH, RENDER],
|
||||
),
|
||||
_Type(
|
||||
[
|
||||
".json",
|
||||
".jsonc",
|
||||
".json5",
|
||||
".jsonl",
|
||||
],
|
||||
[SEARCH, RENDER],
|
||||
),
|
||||
_Type(
|
||||
[
|
||||
".xml",
|
||||
".xul",
|
||||
],
|
||||
[SEARCH, RENDER],
|
||||
),
|
||||
_Type(
|
||||
[
|
||||
".html",
|
||||
".htm",
|
||||
".xhtml",
|
||||
".shtml",
|
||||
".dhtml",
|
||||
],
|
||||
[SEARCH, RENDER],
|
||||
),
|
||||
_Type(".toml", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
# Code -----------------------------------------------------------------------------------------
|
||||
code = MediaTypeGroup(
|
||||
"code",
|
||||
[
|
||||
_Type(".lock", [SEARCH, RENDER]),
|
||||
_Type(".log", [SEARCH, RENDER]),
|
||||
]
|
||||
+ python.types
|
||||
+ javascript.types
|
||||
+ typescript.types
|
||||
+ shell.types
|
||||
+ markdown.types
|
||||
+ markup.types,
|
||||
)
|
||||
MediaTypes.register(code)
|
||||
|
||||
# Plaintext ------------------------------------------------------------------------------------
|
||||
plaintext = MediaTypeGroup(
|
||||
"plaintext",
|
||||
[
|
||||
_Type(".i3u", [SEARCH, RENDER]),
|
||||
_Type(".lang", [SEARCH, RENDER]),
|
||||
_Type("contributing", [SEARCH, RENDER]),
|
||||
_Type("license", [SEARCH, RENDER]),
|
||||
_Type("readme", [SEARCH, RENDER]),
|
||||
_Type([".txt", ".text"], [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
MediaTypes.register(plaintext)
|
||||
|
||||
# Video ----------------------------------------------------------------------------------------
|
||||
video = MediaTypeGroup(
|
||||
"video",
|
||||
[
|
||||
_Type(".3gp", [SEARCH, RENDER]),
|
||||
_Type(".avi", [SEARCH, RENDER]),
|
||||
_Type(".flv", [SEARCH, RENDER]),
|
||||
_Type(".gifv", [SEARCH, RENDER]),
|
||||
_Type(".hevc", [SEARCH, RENDER]),
|
||||
_Type(".m4p", [SEARCH, RENDER]),
|
||||
_Type(".m4v", [SEARCH, RENDER]),
|
||||
_Type(".mkv", [SEARCH, RENDER]),
|
||||
_Type(".mov", [SEARCH, RENDER]),
|
||||
_Type(".mp4", [SEARCH, RENDER]),
|
||||
_Type(".webm", [SEARCH, RENDER]),
|
||||
_Type(".wmv", [SEARCH, RENDER]),
|
||||
],
|
||||
)
|
||||
|
||||
# ------------------
|
||||
|
||||
# @staticmethod
|
||||
# def all_media_types():
|
||||
# static_methods = [
|
||||
# name for name, attr in MediaTypes.__dict__.items() if isinstance(attr, MediaTypeGroup)
|
||||
# ]
|
||||
# return static_methods
|
||||
|
||||
|
||||
FILETYPE_EQUIVALENTS = [
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
# pyright: standard
|
||||
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from PIL.Image import Image
|
||||
|
||||
from tagstudio.core.enums import Theme
|
||||
|
||||
|
||||
class BasePreview:
|
||||
"""A base preview renderer class."""
|
||||
|
||||
# The attribute name used for identifying the MediaType used with the preview renderer.
|
||||
media_type_name: str
|
||||
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def render(
|
||||
cls,
|
||||
filepath: Path,
|
||||
theme: Theme,
|
||||
size: tuple[int, int],
|
||||
dpi_scale: float,
|
||||
) -> Image | None:
|
||||
raise NotImplementedError
|
||||
@@ -14,6 +14,7 @@ import structlog
|
||||
from PIL import Image, ImageChops, ImageDraw, ImageEnhance, ImageFile, UnidentifiedImageError
|
||||
from PIL.Image import DecompressionBombError
|
||||
|
||||
from tagstudio.core.enums import Theme
|
||||
from tagstudio.core.exceptions import NoRendererError
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.core.library.ignore import Ignore
|
||||
@@ -25,6 +26,7 @@ from tagstudio.core.media_types import (
|
||||
MediaTypes,
|
||||
)
|
||||
from tagstudio.core.utils.types import unwrap
|
||||
from tagstudio.previews.base_preview import BasePreview
|
||||
from tagstudio.previews.gradients import four_corner_gradient
|
||||
from tagstudio.previews.renderers.archive import (
|
||||
apple_embedded_thumb,
|
||||
@@ -34,8 +36,9 @@ from tagstudio.previews.renderers.archive import (
|
||||
powerpoint_thumb,
|
||||
)
|
||||
from tagstudio.previews.renderers.audio import audio_album_thumb, audio_thumb, audio_waveform_thumb
|
||||
from tagstudio.previews.renderers.blender import blender_thumb
|
||||
from tagstudio.previews.renderers.blender import BlenderPreview, _blender_thumb
|
||||
from tagstudio.previews.renderers.clip_studio import clip_studio_thumb
|
||||
from tagstudio.previews.renderers.code import CodePreview
|
||||
from tagstudio.previews.renderers.ebook import epub_thumb
|
||||
from tagstudio.previews.renderers.font import font_full_preview, font_small_thumb
|
||||
from tagstudio.previews.renderers.medibang_paint import medibang_paint_thumb
|
||||
@@ -47,7 +50,10 @@ from tagstudio.previews.renderers.raster_image import (
|
||||
raw_image_thumb,
|
||||
)
|
||||
from tagstudio.previews.renderers.source_engine import vtf_thumb
|
||||
from tagstudio.previews.renderers.text import code_thumb, text_thumb
|
||||
from tagstudio.previews.renderers.text import (
|
||||
TextPreview,
|
||||
text_thumb,
|
||||
)
|
||||
from tagstudio.previews.renderers.vector_image import vector_image_thumb
|
||||
from tagstudio.previews.renderers.video import video_thumb
|
||||
from tagstudio.qt.app_settings import (
|
||||
@@ -55,7 +61,6 @@ from tagstudio.qt.app_settings import (
|
||||
MAX_CACHED_THUMB_RES,
|
||||
MIN_CACHED_THUMB_RES,
|
||||
AppSettings,
|
||||
Theme,
|
||||
)
|
||||
from tagstudio.qt.cache_manager import CacheManager
|
||||
from tagstudio.qt.resource_manager import ResourceManager
|
||||
@@ -99,13 +104,13 @@ class FileRenderer:
|
||||
ext = url.suffix.lower()
|
||||
types: set[MediaTypeOld] = MediaCategories.get_types(ext, mime_fallback=True)
|
||||
|
||||
# Manual icon overrides.
|
||||
if ext in {".gif", ".vtf"}:
|
||||
return MediaTypeOld.IMAGE
|
||||
elif ext in {".dll", ".pyc", ".o", ".dylib"}:
|
||||
return MediaTypeOld.PROGRAM
|
||||
elif ext in {".mscz"}: # noqa: SIM114
|
||||
return MediaTypeOld.TEXT
|
||||
# # Manual icon overrides.
|
||||
# if ext in {".gif", ".vtf"}:
|
||||
# return MediaTypeOld.IMAGE
|
||||
# elif ext in {".dll", ".pyc", ".o", ".dylib"}:
|
||||
# return MediaTypeOld.PROGRAM
|
||||
# elif ext in {".mscz"}: # noqa: SIM114
|
||||
# return MediaTypeOld.TEXT
|
||||
|
||||
# Loop though the specific (non-IANA) categories and return the string
|
||||
# name of the first matching category found.
|
||||
@@ -766,17 +771,82 @@ class FileRenderer:
|
||||
|
||||
# Ordered groups of file renderers.
|
||||
# A file extension is rendered with the first group it's found in.
|
||||
|
||||
# TODO: Dynamically import these from the renderers/ directory at runtime,
|
||||
# And allow user-created ones from an external directory.
|
||||
previews: list[type[BasePreview]] = [
|
||||
# ArchivePreview,
|
||||
# AudioPreview,
|
||||
BlenderPreview,
|
||||
# ClipStudioPaintPreview,
|
||||
CodePreview,
|
||||
# EbookPreview,
|
||||
# FontPreview,
|
||||
# IWorkPreview,
|
||||
# KritaPreview,
|
||||
# MediBangPaintPreview,
|
||||
# PaintDotNetPreview,
|
||||
# RasterImagePreview,
|
||||
# RawImagePreview,
|
||||
TextPreview,
|
||||
# VectorImagePreview,
|
||||
]
|
||||
|
||||
if filepath and filepath.is_file():
|
||||
try:
|
||||
ext = filepath.suffix.lower() if filepath.suffix else filepath.stem.lower()
|
||||
for preview in previews:
|
||||
media_type: MediaTypeGroup = getattr(MediaTypes, preview.media_type_name)
|
||||
if media_type.contains(ext, Context.RENDER):
|
||||
logger.info(f"{ext} in: {media_type.renderable}")
|
||||
image = preview.render(
|
||||
filepath=filepath,
|
||||
theme=theme,
|
||||
size=(scaled_size, scaled_size),
|
||||
dpi_scale=dpi_scale,
|
||||
)
|
||||
break
|
||||
|
||||
if image:
|
||||
image = self._resize_image(image, (scaled_size, scaled_size))
|
||||
if cache_filename and is_savable_type and image and cache:
|
||||
cache.save_image(image, cache_filename, mode="RGBA")
|
||||
except Exception as e:
|
||||
logger.error("[FileRenderer] Couldn't render thumbnail", filepath=filepath, error=e)
|
||||
image = None
|
||||
|
||||
return image
|
||||
|
||||
# -------------------------------- new old
|
||||
|
||||
render_groups: list[tuple[MediaTypeGroup, Callable[..., Image.Image | None]]] = [
|
||||
(MediaTypes.raw_image, partial(raw_image_thumb, filepath)),
|
||||
(MediaTypes.binary, partial(raster_image_thumb, filepath)),
|
||||
(MediaTypes.python, partial(text_thumb, filepath)),
|
||||
(MediaTypes.pdf, partial(pdf_thumb, filepath, scaled_size)),
|
||||
(MediaTypes.raster_image, partial(raster_image_thumb, filepath)),
|
||||
(MediaTypes.vector, partial(vector_image_thumb, filepath, scaled_size)),
|
||||
(MediaTypes.code_types, partial(code_thumb, filepath)),
|
||||
(MediaTypes.plaintext, partial(text_thumb, filepath)),
|
||||
(MediaTypes.vector_image, partial(vector_image_thumb, filepath, scaled_size)),
|
||||
(
|
||||
getattr(MediaTypes, CodePreview.media_type_name),
|
||||
partial(CodePreview.render, filepath),
|
||||
),
|
||||
(
|
||||
getattr(MediaTypes, TextPreview.media_type_name),
|
||||
partial(TextPreview.render, filepath),
|
||||
),
|
||||
(MediaTypes.audio, partial(audio_thumb, filepath, scaled_size, dpi_scale)),
|
||||
(MediaTypes.video, partial(video_thumb, filepath)),
|
||||
(
|
||||
MediaTypes.font,
|
||||
partial(font_small_thumb if is_thumb else font_full_preview, filepath, scaled_size),
|
||||
),
|
||||
(MediaTypes.archive, partial(archive_thumb, filepath)),
|
||||
(MediaTypes.pdf, partial(pdf_thumb, filepath, scaled_size)),
|
||||
(MediaTypes.ebook, partial(epub_thumb, filepath)),
|
||||
(MediaTypes.iwork, partial(apple_embedded_thumb, filepath)),
|
||||
(MediaTypes.blender, partial(_blender_thumb, filepath)),
|
||||
(MediaTypes.krita, partial(krita_thumb, filepath)),
|
||||
(MediaTypes.clip_studio_paint, partial(clip_studio_thumb, filepath)),
|
||||
(MediaTypes.paint_dot_net, partial(paint_dot_net_thumb, filepath)),
|
||||
(MediaTypes.medibang_paint, partial(medibang_paint_thumb, filepath)),
|
||||
(MediaTypes.binary, partial(raster_image_thumb, filepath)),
|
||||
]
|
||||
if filepath and filepath.is_file():
|
||||
try:
|
||||
@@ -791,6 +861,9 @@ class FileRenderer:
|
||||
# TODO: Differentiate between album art and waveform
|
||||
image = self._apply_overlay_color(image, UiColor.GREEN, theme)
|
||||
is_savable_type = False
|
||||
elif image and ext in MediaTypes.font.renderable:
|
||||
# TODO: Differentiate between ful preview and small preview
|
||||
image = self._apply_overlay_color(image, UiColor.BLUE, theme)
|
||||
|
||||
break
|
||||
|
||||
@@ -821,6 +894,8 @@ class FileRenderer:
|
||||
|
||||
return image
|
||||
|
||||
# ---------- old old
|
||||
|
||||
if filepath and filepath.is_file():
|
||||
try:
|
||||
ext: str = filepath.suffix.lower() if filepath.suffix else filepath.stem.lower()
|
||||
@@ -854,7 +929,7 @@ class FileRenderer:
|
||||
):
|
||||
image = raw_image_thumb(filepath)
|
||||
# Vector Images ----------------------------------------------------------------
|
||||
elif ext in MediaTypes.vector.renderable:
|
||||
elif ext in MediaTypes.vector_image.renderable:
|
||||
image = vector_image_thumb(filepath, scaled_size)
|
||||
# EXR Images -------------------------------------------------------------------
|
||||
elif ext in [".exr"]:
|
||||
@@ -912,7 +987,7 @@ class FileRenderer:
|
||||
elif MediaCategories.is_ext_in_category(
|
||||
ext, MediaCategories.BLENDER_TYPES, mime_fallback=True
|
||||
):
|
||||
image = blender_thumb(filepath)
|
||||
image = _blender_thumb(filepath)
|
||||
# PDF ==========================================================
|
||||
elif MediaCategories.is_ext_in_category(
|
||||
ext, MediaCategories.PDF_TYPES, mime_fallback=True
|
||||
|
||||
@@ -60,7 +60,7 @@ class TarFile:
|
||||
self.tar.__exit__(*args)
|
||||
|
||||
|
||||
def open_archive(filepath: Path, ext: str = "") -> Archive:
|
||||
def open_archive(filepath: Path) -> Archive:
|
||||
"""Open an archive with its corresponding archiver.
|
||||
|
||||
Args:
|
||||
@@ -70,6 +70,7 @@ def open_archive(filepath: Path, ext: str = "") -> Archive:
|
||||
Returns:
|
||||
Archive: The opened archive.
|
||||
"""
|
||||
ext = filepath.suffix.lower()
|
||||
archiver: type[Archive] = zipfile.ZipFile
|
||||
if ext in {".7z", ".cb7", ".s7z"}:
|
||||
archiver = SevenZipFile
|
||||
@@ -101,7 +102,6 @@ def first_image_in_archive(archive: Archive) -> Image.Image | None:
|
||||
def archive_thumb(
|
||||
filepath: Path,
|
||||
image_names: list[Path] | list[str] | None = None,
|
||||
ext: str = "",
|
||||
) -> Image.Image | None:
|
||||
"""Extract an embedded preview image from an archive.
|
||||
|
||||
@@ -114,7 +114,7 @@ def archive_thumb(
|
||||
Image: The first image found in the archive.
|
||||
"""
|
||||
try:
|
||||
with open_archive(filepath, ext) as archive:
|
||||
with open_archive(filepath) as archive:
|
||||
# If no list of image names to search for was provided, default to the first image.
|
||||
if not image_names:
|
||||
return first_image_in_archive(archive)
|
||||
|
||||
@@ -3,32 +3,45 @@
|
||||
|
||||
|
||||
from pathlib import Path
|
||||
from typing import override
|
||||
|
||||
import structlog
|
||||
from PIL import Image
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtGui import QGuiApplication
|
||||
from PIL.Image import Image, new
|
||||
|
||||
from tagstudio.core.enums import Theme
|
||||
from tagstudio.previews.base_preview import BasePreview
|
||||
from tagstudio.previews.vendored.blender_thumbnailer import blend_thumb
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
def blender_thumb(filepath: Path) -> Image.Image | None:
|
||||
class BlenderPreview(BasePreview):
|
||||
media_type_name = "blender"
|
||||
|
||||
@override
|
||||
@classmethod
|
||||
def render(
|
||||
cls,
|
||||
filepath: Path,
|
||||
theme: Theme,
|
||||
size: tuple[int, int],
|
||||
dpi_scale: float,
|
||||
) -> Image | None:
|
||||
return _blender_thumb(filepath, theme)
|
||||
|
||||
|
||||
def _blender_thumb(filepath: Path, theme: Theme) -> Image | None:
|
||||
"""Get an emended thumbnail from a Blender file, if a thumbnail is present.
|
||||
|
||||
Args:
|
||||
filepath (Path): The path of the file.
|
||||
theme (Theme): The system color theme.
|
||||
"""
|
||||
bg_color: str = (
|
||||
"#1e1e1e"
|
||||
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
|
||||
else "#FFFFFF"
|
||||
)
|
||||
im: Image.Image | None = None
|
||||
bg_color: str = "#1e1e1e" if theme == Theme.DARK else "#FFFFFF"
|
||||
im: Image | None = None
|
||||
try:
|
||||
if (blend_image := blend_thumb(str(filepath))) is not None:
|
||||
bg = Image.new("RGB", blend_image.size, color=bg_color)
|
||||
bg = new("RGB", blend_image.size, color=bg_color)
|
||||
bg.paste(blend_image, mask=blend_image.getchannel(3))
|
||||
im = bg
|
||||
else:
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
|
||||
from pathlib import Path
|
||||
from typing import override
|
||||
|
||||
import structlog
|
||||
from PIL.Image import (
|
||||
Image,
|
||||
)
|
||||
from pygments.style import Style
|
||||
from pygments.token import (
|
||||
Comment,
|
||||
Error,
|
||||
Generic,
|
||||
Keyword,
|
||||
Literal,
|
||||
Name,
|
||||
Number,
|
||||
Operator,
|
||||
Other,
|
||||
Punctuation,
|
||||
String,
|
||||
Text,
|
||||
Whitespace,
|
||||
)
|
||||
|
||||
from tagstudio.core.enums import Theme
|
||||
from tagstudio.previews.base_preview import BasePreview
|
||||
from tagstudio.previews.renderers.text import text_thumb
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
class CodeStyle(Style):
|
||||
background = "#111111"
|
||||
foreground = "#f8f8f2"
|
||||
selection = "#44475a"
|
||||
comment = "#6272a4"
|
||||
cyan = "#8be9fd"
|
||||
green = "#50fa7b"
|
||||
orange = "#ffb86c"
|
||||
pink = "#ff79c6"
|
||||
purple = "#bd93f9"
|
||||
red = "#ff5555"
|
||||
yellow = "#f1fa8c"
|
||||
deletion = "#8b080b"
|
||||
|
||||
background_color = background
|
||||
highlight_color = selection
|
||||
line_number_color = yellow
|
||||
line_number_background_color = selection
|
||||
line_number_special_color = green
|
||||
line_number_special_background_color = comment
|
||||
|
||||
styles = {
|
||||
Whitespace: foreground,
|
||||
Comment: comment,
|
||||
Comment.Preproc: pink,
|
||||
Generic: foreground,
|
||||
Generic.Deleted: deletion,
|
||||
Generic.Emph: "underline",
|
||||
Generic.Heading: "bold",
|
||||
Generic.Inserted: "bold",
|
||||
Generic.Output: selection,
|
||||
Generic.EmphStrong: "underline",
|
||||
Generic.Subheading: "bold",
|
||||
Error: foreground,
|
||||
Keyword: pink,
|
||||
Keyword.Constant: pink,
|
||||
Keyword.Declaration: cyan + " italic",
|
||||
Keyword.Type: cyan,
|
||||
Literal: foreground,
|
||||
Name: foreground,
|
||||
Name.Attribute: green,
|
||||
Name.Builtin: cyan + " italic",
|
||||
Name.Builtin.Pseudo: foreground,
|
||||
Name.Class: green,
|
||||
Name.Function: green,
|
||||
Name.Label: cyan + " italic",
|
||||
Name.Tag: pink,
|
||||
Name.Variable: cyan + " italic",
|
||||
Number: orange,
|
||||
Operator: pink,
|
||||
Other: foreground,
|
||||
Punctuation: foreground,
|
||||
String: purple,
|
||||
Text: foreground,
|
||||
}
|
||||
|
||||
|
||||
class CodePreview(BasePreview):
|
||||
media_type_name = "code"
|
||||
|
||||
@override
|
||||
@classmethod
|
||||
def render(
|
||||
cls,
|
||||
filepath: Path,
|
||||
theme: Theme,
|
||||
size: tuple[int, int],
|
||||
dpi_scale: float,
|
||||
) -> Image | None:
|
||||
return text_thumb(filepath, size, CodeStyle)
|
||||
@@ -18,7 +18,7 @@ from tagstudio.previews.renderers.raster_image import image_from_bytes
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
def epub_thumb(filepath: Path, ext: str) -> Image.Image | None:
|
||||
def epub_thumb(filepath: Path) -> Image.Image | None:
|
||||
"""Extracts the cover specified by ComicInfo.xml or first image found in the ePub file.
|
||||
|
||||
Args:
|
||||
@@ -31,7 +31,7 @@ def epub_thumb(filepath: Path, ext: str) -> Image.Image | None:
|
||||
"""
|
||||
im: Image.Image | None = None
|
||||
try:
|
||||
with open_archive(filepath, ext) as archive:
|
||||
with open_archive(filepath) as archive:
|
||||
if "ComicInfo.xml" in archive.namelist():
|
||||
comic_info = ET.fromstring(archive.read("ComicInfo.xml"))
|
||||
im = _cover_from_comic_info(archive, comic_info, "FrontCover")
|
||||
|
||||
@@ -1,86 +1,172 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
|
||||
import io
|
||||
import textwrap
|
||||
from math import ceil
|
||||
from pathlib import Path
|
||||
from typing import override
|
||||
|
||||
import cv2
|
||||
import structlog
|
||||
from PIL import Image, ImageDraw, UnidentifiedImageError
|
||||
from PIL.Image import DecompressionBombError
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtGui import QGuiApplication
|
||||
from PIL import ImageFont, UnidentifiedImageError
|
||||
from PIL.Image import (
|
||||
DecompressionBombError,
|
||||
Image,
|
||||
Resampling,
|
||||
)
|
||||
from PIL.Image import (
|
||||
new as new_image,
|
||||
)
|
||||
from PIL.Image import (
|
||||
open as open_image,
|
||||
)
|
||||
from pygments import highlight
|
||||
from pygments.formatters import ImageFormatter
|
||||
from pygments.lexers import PythonLexer
|
||||
from pygments.style import Style
|
||||
from pygments.token import (
|
||||
Comment,
|
||||
Error,
|
||||
Generic,
|
||||
Keyword,
|
||||
Literal,
|
||||
Name,
|
||||
Number,
|
||||
Operator,
|
||||
Other,
|
||||
Punctuation,
|
||||
String,
|
||||
Text,
|
||||
)
|
||||
|
||||
from tagstudio.core.enums import Theme
|
||||
from tagstudio.core.utils.encoding import detect_char_encoding
|
||||
from tagstudio.previews.base_preview import BasePreview
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
def text_thumb(filepath: Path) -> Image.Image | None:
|
||||
class TextLightStyle(Style):
|
||||
background = "#FFFFFF"
|
||||
foreground = "#111111"
|
||||
|
||||
background_color = background
|
||||
styles = {
|
||||
Generic: foreground,
|
||||
Text: foreground,
|
||||
Literal: foreground,
|
||||
String: foreground,
|
||||
}
|
||||
|
||||
|
||||
class TextDarkStyle(Style):
|
||||
background = "#1e1e1e"
|
||||
foreground = "#FFFFFF"
|
||||
|
||||
background_color = background
|
||||
styles = {
|
||||
Generic: foreground,
|
||||
Text: foreground,
|
||||
Literal: foreground,
|
||||
String: foreground,
|
||||
Comment: foreground,
|
||||
Error: foreground,
|
||||
Keyword: foreground,
|
||||
Name: foreground,
|
||||
Number: foreground,
|
||||
Operator: foreground,
|
||||
Other: foreground,
|
||||
Punctuation: foreground,
|
||||
}
|
||||
|
||||
|
||||
# text_light_style = TextLightStyle()
|
||||
# text_dark_style = TextDarkStyle()
|
||||
|
||||
|
||||
class TextPreview(BasePreview):
|
||||
media_type_name = "plaintext"
|
||||
font = ImageFont.load_default(20)
|
||||
|
||||
@override
|
||||
@classmethod
|
||||
def render(
|
||||
cls,
|
||||
filepath: Path,
|
||||
theme: Theme,
|
||||
size: tuple[int, int],
|
||||
dpi_scale: float,
|
||||
) -> Image | None:
|
||||
return text_thumb(
|
||||
filepath=filepath,
|
||||
size=size,
|
||||
style=TextDarkStyle if theme == Theme.DARK else TextLightStyle,
|
||||
)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def text_thumb(
|
||||
filepath: Path,
|
||||
size: tuple[int, int],
|
||||
style: type[Style],
|
||||
) -> Image | None:
|
||||
"""Render a thumbnail for a plaintext file.
|
||||
|
||||
Args:
|
||||
filepath (Path): The path of the file.
|
||||
size (str): The final size for the image.
|
||||
style (str): The pygments style class to use.
|
||||
"""
|
||||
im: Image.Image | None = None
|
||||
|
||||
bg_color: str = (
|
||||
"#1e1e1e"
|
||||
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
|
||||
else "#FFFFFF"
|
||||
)
|
||||
fg_color: str = (
|
||||
"#FFFFFF"
|
||||
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
|
||||
else "#111111"
|
||||
)
|
||||
im: Image | None = None
|
||||
|
||||
try:
|
||||
encoding = detect_char_encoding(filepath)
|
||||
encoding: str = detect_char_encoding(filepath) or "UTF-8"
|
||||
with open(filepath, encoding=encoding) as text_file:
|
||||
text = text_file.read(256)
|
||||
bg = Image.new("RGB", (256, 256), color=bg_color)
|
||||
draw = ImageDraw.Draw(bg)
|
||||
draw.text((16, 16), text, fill=fg_color)
|
||||
im = bg
|
||||
text = text_file.read(1024)
|
||||
|
||||
wrapped_text = "\n".join(
|
||||
"\n".join(textwrap.wrap(line, width=40)) for line in text.splitlines()
|
||||
)
|
||||
|
||||
# TODO: Get this path from the ResourceManager, when that can handle fonts.
|
||||
font_path = str(
|
||||
Path(__file__).parents[2] / "resources/fonts/JetBrainsMono/JetBrainsMono.ttf"
|
||||
)
|
||||
# logger.info(font_path)
|
||||
|
||||
image_bytes = highlight(
|
||||
wrapped_text,
|
||||
PythonLexer(),
|
||||
ImageFormatter(
|
||||
encoding=encoding,
|
||||
font_name=font_path,
|
||||
font_size=32,
|
||||
line_numbers=False,
|
||||
style=style,
|
||||
image_pad=48,
|
||||
),
|
||||
)
|
||||
im_text = open_image(io.BytesIO(image_bytes))
|
||||
|
||||
ratio_w = size[0] / im_text.width
|
||||
im_text = Image.resize(
|
||||
im_text,
|
||||
(ceil(im_text.width * ratio_w), ceil(im_text.height * ratio_w)),
|
||||
Resampling.BILINEAR,
|
||||
)
|
||||
|
||||
bg = new_image("RGB", size, color=style.background_color)
|
||||
Image.paste(bg, im_text, (0, 0))
|
||||
|
||||
return bg
|
||||
|
||||
except (
|
||||
UnidentifiedImageError,
|
||||
cv2.error,
|
||||
DecompressionBombError,
|
||||
UnicodeDecodeError,
|
||||
OSError,
|
||||
FileNotFoundError,
|
||||
) as e:
|
||||
logger.error("Couldn't render thumbnail", filepath=filepath, error=type(e).__name__)
|
||||
return im
|
||||
|
||||
|
||||
def code_thumb(filepath: Path) -> Image.Image | None:
|
||||
"""Render a thumbnail for a plaintext file.
|
||||
|
||||
Args:
|
||||
filepath (Path): The path of the file.
|
||||
"""
|
||||
im: Image.Image | None = None
|
||||
|
||||
bg_color: str = "#000000"
|
||||
fg_color: str = "#00FF00"
|
||||
|
||||
try:
|
||||
encoding = detect_char_encoding(filepath)
|
||||
with open(filepath, encoding=encoding) as text_file:
|
||||
text = text_file.read(256)
|
||||
bg = Image.new("RGB", (256, 256), color=bg_color)
|
||||
draw = ImageDraw.Draw(bg)
|
||||
draw.text((16, 16), text, fill=fg_color)
|
||||
im = bg
|
||||
except (
|
||||
UnidentifiedImageError,
|
||||
cv2.error,
|
||||
DecompressionBombError,
|
||||
UnicodeDecodeError,
|
||||
OSError,
|
||||
FileNotFoundError,
|
||||
) as e:
|
||||
logger.error("Couldn't render thumbnail", filepath=filepath, error=type(e).__name__)
|
||||
logger.error("Couldn't render thumbnail", filepath=filepath, error=e)
|
||||
return im
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import platform
|
||||
from datetime import datetime
|
||||
from enum import Enum, IntEnum, StrEnum
|
||||
from enum import Enum, StrEnum
|
||||
from pathlib import Path
|
||||
from typing import override
|
||||
|
||||
@@ -12,7 +12,7 @@ import structlog
|
||||
import toml
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from tagstudio.core.enums import ShowFilepathOption, TagClickActionOption
|
||||
from tagstudio.core.enums import ShowFilepathOption, TagClickActionOption, Theme
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
@@ -32,13 +32,6 @@ MAX_CACHED_THUMB_RES = 1024 # Pixels
|
||||
DEFAULT_CACHED_THUMB_RES = 256 # Pixels
|
||||
|
||||
|
||||
class Theme(IntEnum):
|
||||
DARK = 0
|
||||
LIGHT = 1
|
||||
SYSTEM = 2
|
||||
DEFAULT = SYSTEM
|
||||
|
||||
|
||||
class Splash(StrEnum):
|
||||
DEFAULT = "default"
|
||||
RANDOM = "random"
|
||||
|
||||
@@ -64,8 +64,8 @@ class ProgressWidget(QWidget):
|
||||
|
||||
self.show()
|
||||
|
||||
r = CustomRunnable(lambda: iterator.run())
|
||||
r.done.connect(
|
||||
runnable = CustomRunnable(lambda: iterator.run())
|
||||
runnable.done.connect(
|
||||
lambda: (self.hide(), self.deleteLater(), [callback() for callback in done_callbacks])
|
||||
)
|
||||
QThreadPool.globalInstance().start(r)
|
||||
QThreadPool.globalInstance().start(runnable)
|
||||
|
||||
@@ -22,7 +22,7 @@ from PySide6.QtWidgets import (
|
||||
)
|
||||
|
||||
from tagstudio.core.constants import RESERVED_NAMESPACE_PREFIX
|
||||
from tagstudio.core.enums import Theme
|
||||
from tagstudio.core.enums import ThemePalette
|
||||
from tagstudio.core.utils.types import unwrap
|
||||
from tagstudio.i18n.translations import Translations
|
||||
from tagstudio.qt.controllers.modal import Modal
|
||||
@@ -56,9 +56,9 @@ class TagColorManager(QWidget):
|
||||
self.root_layout.setContentsMargins(6, 6, 6, 6)
|
||||
|
||||
panel_bg_color = (
|
||||
Theme.COLOR_BG_DARK.value
|
||||
ThemePalette.COLOR_BG_DARK.value
|
||||
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
|
||||
else Theme.COLOR_BG_LIGHT.value
|
||||
else ThemePalette.COLOR_BG_LIGHT.value
|
||||
)
|
||||
|
||||
self.title_label = QLabel()
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtGui import QColor, QGuiApplication
|
||||
|
||||
from tagstudio.core.enums import Theme
|
||||
from tagstudio.core.enums import ThemePalette
|
||||
from tagstudio.core.library.alchemy.enums import TagColorEnum
|
||||
from tagstudio.core.library.alchemy.models import Tag
|
||||
from tagstudio.qt.views.styles.palette import (
|
||||
@@ -55,14 +55,14 @@ def button_style() -> str:
|
||||
"""Style used for common QPushButtons."""
|
||||
return f"""
|
||||
QPushButton{{
|
||||
background-color: {Theme.COLOR_BG.value};
|
||||
background-color: {ThemePalette.COLOR_BG.value};
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
padding: 0px 12px;
|
||||
}}
|
||||
QPushButton::hover{{
|
||||
background-color: {Theme.COLOR_HOVER.value};
|
||||
background-color: {ThemePalette.COLOR_HOVER.value};
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
border-color: {get_ui_color(ColorType.BORDER, UiColor.THEME_DARK)};
|
||||
@@ -84,7 +84,7 @@ def button_style() -> str:
|
||||
padding: 0px 8px;
|
||||
}}
|
||||
QPushButton::disabled{{
|
||||
background-color: {Theme.COLOR_DISABLED_BG.value};
|
||||
background-color: {ThemePalette.COLOR_DISABLED_BG.value};
|
||||
}}
|
||||
"""
|
||||
|
||||
@@ -92,9 +92,9 @@ def button_style() -> str:
|
||||
def line_edit_style_main() -> str:
|
||||
"""Style used for common QLineEdits."""
|
||||
bg_color = (
|
||||
Theme.COLOR_BG_DARK.value
|
||||
ThemePalette.COLOR_BG_DARK.value
|
||||
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
|
||||
else Theme.COLOR_BG_LIGHT.value
|
||||
else ThemePalette.COLOR_BG_LIGHT.value
|
||||
)
|
||||
|
||||
return f"""
|
||||
@@ -118,7 +118,7 @@ def line_edit_style_main() -> str:
|
||||
padding: 0px 2px;
|
||||
}}
|
||||
QLineEdit::disabled{{
|
||||
background-color: {Theme.COLOR_DISABLED_BG.value};
|
||||
background-color: {ThemePalette.COLOR_DISABLED_BG.value};
|
||||
}}
|
||||
"""
|
||||
|
||||
@@ -259,10 +259,10 @@ def container_style() -> str:
|
||||
border-radius: 4px;
|
||||
}}
|
||||
QWidget#fieldContainer::hover{{
|
||||
background-color: {Theme.COLOR_HOVER.value};
|
||||
background-color: {ThemePalette.COLOR_HOVER.value};
|
||||
}}
|
||||
QWidget#fieldContainer::pressed{{
|
||||
background-color: {Theme.COLOR_PRESSED.value};
|
||||
background-color: {ThemePalette.COLOR_PRESSED.value};
|
||||
}}
|
||||
"""
|
||||
|
||||
@@ -271,9 +271,9 @@ def form_content_style() -> str:
|
||||
return f"""
|
||||
QLabel{{
|
||||
background-color: {
|
||||
Theme.COLOR_BG.value
|
||||
ThemePalette.COLOR_BG.value
|
||||
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
|
||||
else Theme.COLOR_BG_LIGHT.value
|
||||
else ThemePalette.COLOR_BG_LIGHT.value
|
||||
};
|
||||
border-radius: 3px;
|
||||
font-weight: 500;
|
||||
@@ -340,9 +340,9 @@ def list_button_style(
|
||||
def properties_style() -> str:
|
||||
"""Style used for small labels such as file properties."""
|
||||
label_bg_color = (
|
||||
Theme.COLOR_BG_DARK.value
|
||||
ThemePalette.COLOR_BG_DARK.value
|
||||
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
|
||||
else Theme.COLOR_DARK_LABEL.value
|
||||
else ThemePalette.COLOR_DARK_LABEL.value
|
||||
)
|
||||
|
||||
return f"""
|
||||
@@ -447,9 +447,9 @@ def title_line_edit_style() -> str:
|
||||
def inset_container_style(object_name: str = "") -> str:
|
||||
"""Used for darkened inset areas."""
|
||||
bg_color = (
|
||||
Theme.COLOR_BG_DARK.value
|
||||
ThemePalette.COLOR_BG_DARK.value
|
||||
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
|
||||
else Theme.COLOR_BG_LIGHT.value
|
||||
else ThemePalette.COLOR_BG_LIGHT.value
|
||||
)
|
||||
|
||||
return f"""
|
||||
@@ -464,9 +464,9 @@ def inset_container_style(object_name: str = "") -> str:
|
||||
def autofill_scroll_top_style(object_name: str = "") -> str:
|
||||
"""Used autofill lists positioned on top of line edits."""
|
||||
bg_color = (
|
||||
Theme.COLOR_BG_DARK.value
|
||||
ThemePalette.COLOR_BG_DARK.value
|
||||
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
|
||||
else Theme.COLOR_BG_LIGHT.value
|
||||
else ThemePalette.COLOR_BG_LIGHT.value
|
||||
)
|
||||
|
||||
return f"""
|
||||
@@ -482,9 +482,9 @@ def autofill_scroll_top_style(object_name: str = "") -> str:
|
||||
def autofill_scroll_top_focus_style(object_name: str = "") -> str:
|
||||
"""Used autofill lists positioned on top of line edits."""
|
||||
bg_color = (
|
||||
Theme.COLOR_BG_DARK.value
|
||||
ThemePalette.COLOR_BG_DARK.value
|
||||
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
|
||||
else Theme.COLOR_BG_LIGHT.value
|
||||
else ThemePalette.COLOR_BG_LIGHT.value
|
||||
)
|
||||
|
||||
return f"""
|
||||
@@ -502,9 +502,9 @@ def autofill_scroll_top_focus_style(object_name: str = "") -> str:
|
||||
def autofill_line_edit_style() -> str:
|
||||
"""Used for QLineEdits."""
|
||||
bg_color = (
|
||||
Theme.COLOR_BG_DARK.value
|
||||
ThemePalette.COLOR_BG_DARK.value
|
||||
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
|
||||
else Theme.COLOR_BG_LIGHT.value
|
||||
else ThemePalette.COLOR_BG_LIGHT.value
|
||||
)
|
||||
|
||||
return f"""
|
||||
@@ -525,9 +525,9 @@ def autofill_line_edit_style() -> str:
|
||||
def autofill_line_edit_top_style() -> str:
|
||||
"""Used for QLineEdits when there's a top autofill section present."""
|
||||
bg_color = (
|
||||
Theme.COLOR_BG_DARK.value
|
||||
ThemePalette.COLOR_BG_DARK.value
|
||||
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
|
||||
else Theme.COLOR_BG_LIGHT.value
|
||||
else ThemePalette.COLOR_BG_LIGHT.value
|
||||
)
|
||||
|
||||
return f"""
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,2 +0,0 @@
|
||||
SPDX-FileCopyrightText: 2019 The Oxanium Project Authors (https://github.com/sevmeyer/oxanium)
|
||||
SPDX-License-Identifier: OFL-1.1
|
||||
@@ -9,7 +9,7 @@ from unittest.mock import Mock
|
||||
import pytest
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
from tagstudio.qt.app_settings import Theme
|
||||
from tagstudio.core.enums import Theme
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
Reference in New Issue
Block a user