mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-07-13 17:21:35 +02:00
Compare commits
4 Commits
65d6a0405b
...
0bb1615b61
| Author | SHA1 | Date | |
|---|---|---|---|
| 0bb1615b61 | |||
| beb77588bd | |||
| 095ec9a379 | |||
| 8989bedeb5 |
@@ -75,6 +75,7 @@ jobs:
|
||||
run: |
|
||||
pytest
|
||||
|
||||
# TODO: Change the coverage check and generally figure out how we want to use one.
|
||||
coverage:
|
||||
name: Check coverage
|
||||
runs-on: ubuntu-latest
|
||||
@@ -89,9 +90,9 @@ jobs:
|
||||
- name: Check coverage
|
||||
uses: yedpodtrzitko/coverage@main
|
||||
with:
|
||||
thresholdAll: 0.1
|
||||
thresholdNew: 0.1
|
||||
thresholdModified: 0.1
|
||||
thresholdAll: 0
|
||||
thresholdNew: 0
|
||||
thresholdModified: 0
|
||||
coverageFile: coverage.xml
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
sourceDir: tagstudio/src
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
+40
-11
@@ -8,24 +8,53 @@ icon: material/text-box
|
||||
|
||||
# :material-text-box: Fields
|
||||
|
||||
Fields are additional types of metadata that you can attach to [file entries](./entries.md). Like [tags](tags.md), fields are not stored inside files themselves nor in sidecar files, but rather inside the respective TagStudio [library](./index.md) save file.
|
||||
Fields are extra pieces of information you can add to [file entries](./entries.md), similar to how [tags](tags.md) are added to entries. Fields are useful for storing information that doesn't nessisarily need to be a tag, such as titles, comments, notes, specific dates or times, etc.
|
||||
|
||||
## Field Types
|
||||
To add a field to an entry, click the "Add Field" button in the preview panel. From there you can search and/or select a [field template](#field-templates) to choose from, or create a new one from the search bar. Alternatively you can create new field templates from **Edit -> Manage Field Templates**.
|
||||
|
||||
### Text Line
|
||||
<figure markdown="span">
|
||||

|
||||
<figcaption>Example of tags and various fields on a file entry.</figcaption>
|
||||
</figure>
|
||||
|
||||
A string of text, displayed as a single line.
|
||||
## :material-text-box-plus-outline: Field Templates
|
||||
|
||||
- e.g: Title, Author, Artist, URL, etc.
|
||||
Field templates are handy templates to use when adding fields to entries that contain preconfigured options but no actual data. When you add a field to an entry from the "Add Field" button, you choose from a template to add and then fill in the information afterwards. TagStudio includes a handful of field templates to start you off with, but you're free to modify or delete them, or simply create your own.
|
||||
|
||||
### Text Box
|
||||
Field templates can be viewed, created, and deleted from the **Edit -> Manage Field Templates** window. You can also edit field templates from the "Add Field" menu, and create new ones on the fly from the search bar. Note that you can not currently delete field templates from the "Add Field" menu, just like tags.
|
||||
|
||||
A long string of text displayed as a box of text.
|
||||
<figure markdown="span">
|
||||

|
||||
<figcaption>Field Template Manager from <b>Edit -> Manage Field Templates</b>.</figcaption>
|
||||
</figure>
|
||||
|
||||
- e.g: Description, Notes, etc.
|
||||
<figure markdown="span">
|
||||

|
||||
<figcaption>The field template editor, shown creating a new "Citations" field.</figcaption>
|
||||
</figure>
|
||||
|
||||
### Datetime
|
||||
## :material-format-list-bulleted-type: Field Types
|
||||
|
||||
A date and time value.
|
||||
Fields come in a variety of types that are better suited for different types of information, and may provide additional options unique to those types. Single lines are good for fields like titles, while multiline blocks are good for things like comments and notes.
|
||||
|
||||
- e.g: Date Published, Date Taken, etc.
|
||||
### :material-text-box: Text
|
||||
|
||||
Text fields contain a piece of text with the option to display it either a single line or a multiline body of text.
|
||||
|
||||
| Option | Value | Description |
|
||||
| --------- | ---------- | ------------------------------------------------------------------------ |
|
||||
| Multiline | True/False | Indicates if the text should be displayed on multiple lines or just one. |
|
||||
|
||||
<figure markdown="span">
|
||||

|
||||
<figcaption>The text field editor, editing a "Comments" field on an entry.</figcaption>
|
||||
</figure>
|
||||
|
||||
### :material-calendar-month: Datetime
|
||||
|
||||
Datetime fields contain a date and time value. Dates are formatted using the format specified in your application settings.
|
||||
|
||||
<figure markdown="span">
|
||||

|
||||
<figcaption>The datetime field editor, expanded to show the date picker.</figcaption>
|
||||
</figure>
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
|
||||
VERSION: str = "9.5.7" # Major.Minor.Patch
|
||||
VERSION_BRANCH: str = "" # Usually "" or "Pre-Release"
|
||||
GITHUB_REPO_URL = "https://github.com/TagStudioDev/TagStudio"
|
||||
GITHUB_RELEASE_URL = "https://github.com/TagStudioDev/TagStudio/releases/latest"
|
||||
DOCS_URL = "https://docs.tagstud.io"
|
||||
DISCORD_URL = "https://discord.com/invite/hRNnVKhF2G"
|
||||
|
||||
# The folder & file names where TagStudio keeps its data relative to a library.
|
||||
TS_FOLDER_NAME: str = ".TagStudio"
|
||||
@@ -13,9 +16,7 @@ COLLAGE_FOLDER_NAME: str = "collages"
|
||||
IGNORE_NAME: str = ".ts_ignore"
|
||||
THUMB_CACHE_NAME: str = "thumbs"
|
||||
|
||||
FONT_SAMPLE_TEXT: str = (
|
||||
"""ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!?@$%(){}[]"""
|
||||
)
|
||||
FONT_SAMPLE_TEXT: str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!?@$%(){}[]"
|
||||
FONT_SAMPLE_SIZES: list[int] = [10, 15, 20]
|
||||
|
||||
# NOTE: These were the field IDs used for the "Tags", "Content Tags", and "Meta Tags" fields inside
|
||||
@@ -27,5 +28,4 @@ TAG_FAVORITE = 1
|
||||
TAG_META = 2
|
||||
RESERVED_TAG_START = 0
|
||||
RESERVED_TAG_END = 999
|
||||
|
||||
RESERVED_NAMESPACE_PREFIX = "tagstudio"
|
||||
|
||||
@@ -9,9 +9,9 @@ from tagstudio.core.library.alchemy.fields import (
|
||||
DatetimeFieldTemplate,
|
||||
TextFieldTemplate,
|
||||
)
|
||||
from tagstudio.qt.models.palette import ColorType, UiColor, get_ui_color
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.edit_field_template_modal_view import EditFieldTemplateModalView
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import line_edit_style
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
@@ -71,11 +71,7 @@ class EditFieldTemplateModal(EditFieldTemplateModalView):
|
||||
def __on_name_changed(self):
|
||||
is_empty = not self.name_field.text().strip()
|
||||
|
||||
self.name_field.setStyleSheet(
|
||||
f"border: 1px solid {get_ui_color(ColorType.PRIMARY, UiColor.RED)}; border-radius: 2px"
|
||||
if is_empty
|
||||
else ""
|
||||
)
|
||||
self.name_field.setStyleSheet(line_edit_style() if is_empty else "")
|
||||
|
||||
if self.panel_save_button is not None:
|
||||
self.panel_save_button.setDisabled(is_empty)
|
||||
|
||||
@@ -79,9 +79,7 @@ class FieldTemplateSearchPanel(SearchPanel[BaseFieldTemplate]):
|
||||
modal: PanelModal = PanelModal(
|
||||
panel,
|
||||
Translations["field_template.new"],
|
||||
Translations["field_template.add"]
|
||||
if add_to_entry
|
||||
else Translations["field_template.new"],
|
||||
Translations["field_template.new"],
|
||||
is_savable=True,
|
||||
)
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ from tagstudio.qt.mixed.progress_bar import ProgressWidget
|
||||
from tagstudio.qt.mixed.remove_ignored_modal import RemoveIgnoredModal
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.fix_ignored_modal_view import FixIgnoredEntriesModalView
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import header
|
||||
|
||||
# Only import for type checking/autocompletion, will not be imported at runtime.
|
||||
if TYPE_CHECKING:
|
||||
@@ -78,7 +79,7 @@ class FixIgnoredEntriesModal(FixIgnoredEntriesModalView):
|
||||
count_text: str = Translations.format(
|
||||
"entries.ignored.ignored_count", count=count if count >= 0 else "—"
|
||||
)
|
||||
self.ignored_count_label.setText(f"<h3>{count_text}</h3>")
|
||||
self.ignored_count_label.setText(header(count_text, 3))
|
||||
|
||||
def update_driver_widgets(self):
|
||||
if (
|
||||
|
||||
@@ -22,6 +22,7 @@ from tagstudio.core.utils.types import unwrap
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.utils import file_opener
|
||||
from tagstudio.qt.views.library_info_window_view import LibraryInfoWindowView
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import header
|
||||
|
||||
# Only import for type checking/autocompletion, will not be imported at runtime.
|
||||
if TYPE_CHECKING:
|
||||
@@ -61,7 +62,7 @@ class LibraryInfoWindow(LibraryInfoWindowView):
|
||||
title: str = Translations.format(
|
||||
"library_info.title", library_dir=self.lib.library_dir.stem
|
||||
)
|
||||
self.title_label.setText(f"<h2>{title}</h2>")
|
||||
self.title_label.setText(header(title, 2))
|
||||
|
||||
def update_stats(self):
|
||||
self.entry_count_label.setText(f"<b>{self.lib.entries_count}</b>")
|
||||
|
||||
@@ -10,6 +10,7 @@ from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import QHBoxLayout, QLabel, QVBoxLayout, QWidget
|
||||
|
||||
from tagstudio.qt.controllers.paged_panel_state import PagedPanelState
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import header
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
@@ -89,7 +90,7 @@ class PagedPanel(QWidget):
|
||||
|
||||
# Update Title
|
||||
self.setWindowTitle(frame.title)
|
||||
self.title_label.setText(f"<h1>{frame.title}</h1>")
|
||||
self.title_label.setText(header(frame.title, 1))
|
||||
|
||||
# Update Body Widget
|
||||
if self.body_layout.itemAt(0):
|
||||
@@ -107,7 +108,7 @@ class PagedPanel(QWidget):
|
||||
if isinstance(item, QWidget):
|
||||
self.button_nav_layout.addWidget(item)
|
||||
item.setHidden(False)
|
||||
elif isinstance(item, int):
|
||||
elif isinstance(item, int): # pyright: ignore[reportUnnecessaryIsInstance]
|
||||
self.button_nav_layout.addStretch(item)
|
||||
|
||||
@override
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
|
||||
|
||||
import math
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import ImageQt
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtGui import QGuiApplication, QPixmap
|
||||
from PySide6.QtGui import QPixmap
|
||||
from PySide6.QtWidgets import (
|
||||
QFormLayout,
|
||||
QHBoxLayout,
|
||||
@@ -17,35 +18,29 @@ from PySide6.QtWidgets import (
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from tagstudio.core.constants import VERSION, VERSION_BRANCH
|
||||
from tagstudio.core.enums import Theme
|
||||
from tagstudio.core.constants import (
|
||||
DISCORD_URL,
|
||||
DOCS_URL,
|
||||
GITHUB_REPO_URL,
|
||||
VERSION,
|
||||
VERSION_BRANCH,
|
||||
)
|
||||
from tagstudio.core.ts_core import TagStudioCore
|
||||
from tagstudio.core.utils.types import unwrap
|
||||
from tagstudio.qt.models.palette import ColorType, UiColor, get_ui_color
|
||||
from tagstudio.qt.previews.vendored import ffmpeg
|
||||
from tagstudio.qt.resource_manager import ResourceManager
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import form_content_style, header
|
||||
|
||||
|
||||
class AboutModal(QWidget):
|
||||
def __init__(self, config_path):
|
||||
def __init__(self, config_path: Path | str):
|
||||
super().__init__()
|
||||
self.setWindowTitle(Translations["about.title"])
|
||||
|
||||
self.rm: ResourceManager = ResourceManager()
|
||||
|
||||
# TODO: There should be a global button theme somewhere.
|
||||
self.form_content_style = (
|
||||
f"background-color:{
|
||||
Theme.COLOR_BG.value
|
||||
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
|
||||
else Theme.COLOR_BG_LIGHT.value
|
||||
};"
|
||||
"border-radius:3px;"
|
||||
"font-weight: 500;"
|
||||
"padding: 2px;"
|
||||
)
|
||||
|
||||
self.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||
self.setMinimumSize(360, 540)
|
||||
self.setMaximumSize(600, 600)
|
||||
@@ -72,7 +67,8 @@ class AboutModal(QWidget):
|
||||
|
||||
# Title ----------------------------------------------------------------
|
||||
branch: str = (" (" + VERSION_BRANCH + ")") if VERSION_BRANCH else ""
|
||||
self.title_label = QLabel(f"<h2>TagStudio Alpha {VERSION}{branch}</h2>")
|
||||
# NOTE: Do not localize program name.
|
||||
self.title_label = QLabel(header(f"TagStudio Alpha {VERSION}{branch}", 2))
|
||||
self.title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
# Description ----------------------------------------------------------
|
||||
@@ -105,14 +101,18 @@ class AboutModal(QWidget):
|
||||
self.system_info_layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight)
|
||||
|
||||
# Version
|
||||
version_title = QLabel("Version")
|
||||
most_recent_release = unwrap(TagStudioCore.get_most_recent_release_version(), "UNKNOWN")
|
||||
version_content_style = self.form_content_style
|
||||
if most_recent_release == VERSION:
|
||||
version_title = QLabel(Translations["about.version"])
|
||||
latest_version = unwrap(TagStudioCore.get_most_recent_release_version(), "?")
|
||||
version_content_style = form_content_style()
|
||||
if latest_version == VERSION:
|
||||
version_content = QLabel(f"{VERSION}")
|
||||
else:
|
||||
version_content = QLabel(f"{VERSION} (Latest Release: {most_recent_release})")
|
||||
version_content_style += "color: #d9534f;"
|
||||
version_content = QLabel(
|
||||
Translations.format(
|
||||
"about.version.latest", built_version=VERSION, latest_version=latest_version
|
||||
)
|
||||
)
|
||||
version_content_style += f"color: {red};"
|
||||
version_content.setStyleSheet(version_content_style)
|
||||
version_content.setMaximumWidth(version_content.sizeHint().width())
|
||||
self.system_info_layout.addRow(version_title, version_content)
|
||||
@@ -120,40 +120,37 @@ class AboutModal(QWidget):
|
||||
# License
|
||||
license_title = QLabel(f"{Translations['about.license']}")
|
||||
license_content = QLabel("GPLv3")
|
||||
license_content.setStyleSheet(self.form_content_style)
|
||||
license_content.setStyleSheet(form_content_style())
|
||||
license_content.setMaximumWidth(license_content.sizeHint().width())
|
||||
self.system_info_layout.addRow(license_title, license_content)
|
||||
|
||||
# Config Path
|
||||
config_path_title = QLabel(f"{Translations['about.config_path']}")
|
||||
config_path_content = QLabel(f"{config_path}")
|
||||
config_path_content.setStyleSheet(self.form_content_style)
|
||||
config_path_content.setStyleSheet(form_content_style())
|
||||
config_path_content.setWordWrap(True)
|
||||
self.system_info_layout.addRow(config_path_title, config_path_content)
|
||||
|
||||
# FFmpeg Status
|
||||
ffmpeg_path_title = QLabel("FFmpeg")
|
||||
ffmpeg_path_content = QLabel(f"{ffmpeg_status}")
|
||||
ffmpeg_path_content.setStyleSheet(self.form_content_style)
|
||||
ffmpeg_path_content.setStyleSheet(form_content_style())
|
||||
ffmpeg_path_content.setMaximumWidth(ffmpeg_path_content.sizeHint().width())
|
||||
self.system_info_layout.addRow(ffmpeg_path_title, ffmpeg_path_content)
|
||||
|
||||
# FFprobe Status
|
||||
ffprobe_path_title = QLabel("FFprobe")
|
||||
ffprobe_path_content = QLabel(f"{ffprobe_status}")
|
||||
ffprobe_path_content.setStyleSheet(self.form_content_style)
|
||||
ffprobe_path_content.setStyleSheet(form_content_style())
|
||||
ffprobe_path_content.setMaximumWidth(ffprobe_path_content.sizeHint().width())
|
||||
self.system_info_layout.addRow(ffprobe_path_title, ffprobe_path_content)
|
||||
|
||||
# Links ----------------------------------------------------------------
|
||||
repo_link = "https://github.com/TagStudioDev/TagStudio"
|
||||
docs_link = "https://docs.tagstud.io"
|
||||
discord_link = "https://discord.com/invite/hRNnVKhF2G"
|
||||
|
||||
self.links_label = QLabel(
|
||||
f'<p><a href="{repo_link}">GitHub</a> | '
|
||||
f'<a href="{docs_link}">{Translations["about.documentation"]}</a> | '
|
||||
f'<a href="{discord_link}">Discord</a></p>'
|
||||
f'<p><a href="{GITHUB_REPO_URL}">GitHub</a> | '
|
||||
f'<a href="{DOCS_URL}">{Translations["about.documentation"]}</a> | '
|
||||
f'<a href="{DISCORD_URL}">Discord</a></p>'
|
||||
)
|
||||
self.links_label.setWordWrap(True)
|
||||
self.links_label.setOpenExternalLinks(True)
|
||||
|
||||
@@ -19,6 +19,7 @@ from PySide6.QtWidgets import (
|
||||
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.qt.translations import FIELD_TYPE_KEYS, Translations
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import header
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
@@ -39,10 +40,9 @@ class AddFieldModal(QWidget):
|
||||
self.root_layout = QVBoxLayout(self)
|
||||
self.root_layout.setContentsMargins(6, 6, 6, 6)
|
||||
|
||||
self.title_widget = QLabel(Translations["field.add"])
|
||||
self.title_widget = QLabel(header(Translations["field.add"], 3))
|
||||
self.title_widget.setObjectName("fieldTitle")
|
||||
self.title_widget.setWordWrap(True)
|
||||
self.title_widget.setStyleSheet("font-weight:bold;font-size:14px;padding-top: 6px;")
|
||||
self.title_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.list_widget = QListWidget()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
|
||||
import contextlib
|
||||
from typing import override
|
||||
|
||||
import structlog
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
@@ -24,15 +25,14 @@ from tagstudio.core.library.alchemy.library import Library, slugify
|
||||
from tagstudio.core.library.alchemy.models import TagColorGroup
|
||||
from tagstudio.core.utils.types import unwrap
|
||||
from tagstudio.qt.mixed.tag_color_preview import TagColorPreview
|
||||
from tagstudio.qt.mixed.tag_widget import (
|
||||
get_border_color,
|
||||
get_highlight_color,
|
||||
get_text_color,
|
||||
)
|
||||
from tagstudio.qt.models.palette import ColorType, UiColor, get_tag_color, get_ui_color
|
||||
from tagstudio.qt.models.palette import ColorType, get_tag_color
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelWidget
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import checkbox_style
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import (
|
||||
checkbox_style,
|
||||
line_edit_style,
|
||||
list_button_style,
|
||||
)
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
@@ -130,7 +130,6 @@ class BuildColorPanel(PanelWidget):
|
||||
color=QColor(unwrap(self.preview_button.tag_color_group).secondary)
|
||||
if unwrap(self.preview_button.tag_color_group).secondary
|
||||
else None,
|
||||
color_border=checked,
|
||||
)
|
||||
)
|
||||
self.border_layout.addWidget(self.border_checkbox)
|
||||
@@ -193,89 +192,20 @@ class BuildColorPanel(PanelWidget):
|
||||
def update_primary(self, color: QColor):
|
||||
logger.info("[BuildColorPanel] Updating Primary", primary_color=color)
|
||||
|
||||
highlight_color = get_highlight_color(color)
|
||||
text_color = get_text_color(color, highlight_color)
|
||||
border_color = get_border_color(color)
|
||||
|
||||
hex_code = color.name().upper()
|
||||
|
||||
self.primary_button.setText(hex_code)
|
||||
self.primary_button.setStyleSheet(
|
||||
f"QPushButton{{"
|
||||
f"background: rgba{color.toTuple()};"
|
||||
f"color: rgba{text_color.toTuple()};"
|
||||
f"font-weight: 600;"
|
||||
f"border-color: rgba{border_color.toTuple()};"
|
||||
f"border-radius: 6px;"
|
||||
f"border-style:solid;"
|
||||
f"border-width: 2px;"
|
||||
f"padding-right: 4px;"
|
||||
f"padding-bottom: 1px;"
|
||||
f"padding-left: 4px;"
|
||||
f"font-size: 13px"
|
||||
f"}}"
|
||||
f"QPushButton::hover{{"
|
||||
f"border-color: rgba{highlight_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QPushButton::pressed{{"
|
||||
f"background: rgba{highlight_color.toTuple()};"
|
||||
f"color: rgba{color.toTuple()};"
|
||||
f"border-color: rgba{color.toTuple()};"
|
||||
f"}}"
|
||||
f"QPushButton::focus{{"
|
||||
f"padding-right: 0px;"
|
||||
f"padding-left: 0px;"
|
||||
f"outline-style: solid;"
|
||||
f"outline-width: 1px;"
|
||||
f"outline-radius: 4px;"
|
||||
f"outline-color: rgba{text_color.toTuple()};"
|
||||
f"}}"
|
||||
)
|
||||
self.primary_button.setStyleSheet(list_button_style(color))
|
||||
self.preview_button.set_tag_color_group(self.build_color()[1])
|
||||
|
||||
def update_secondary(self, color: QColor | None = None, color_border: bool = False):
|
||||
def update_secondary(self, color: QColor | None = None):
|
||||
logger.info("[BuildColorPanel] Updating Secondary", color=color)
|
||||
|
||||
color_ = color or QColor(get_tag_color(ColorType.PRIMARY, TagColorEnum.DEFAULT))
|
||||
|
||||
highlight_color = get_highlight_color(color_)
|
||||
text_color = get_text_color(color_, highlight_color)
|
||||
border_color = get_border_color(color_)
|
||||
|
||||
hex_code = "" if not color else color.name().upper()
|
||||
self.secondary_button.setText(
|
||||
Translations["color.title.no_color"] if not color else hex_code
|
||||
)
|
||||
self.secondary_button.setStyleSheet(
|
||||
f"QPushButton{{"
|
||||
f"background: rgba{color_.toTuple()};"
|
||||
f"color: rgba{text_color.toTuple()};"
|
||||
f"font-weight: 600;"
|
||||
f"border-color: rgba{border_color.toTuple()};"
|
||||
f"border-radius: 6px;"
|
||||
f"border-style:solid;"
|
||||
f"border-width: 2px;"
|
||||
f"padding-right: 4px;"
|
||||
f"padding-bottom: 1px;"
|
||||
f"padding-left: 4px;"
|
||||
f"font-size: 13px"
|
||||
f"}}"
|
||||
f"QPushButton::hover{{"
|
||||
f"border-color: rgba{highlight_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QPushButton::pressed{{"
|
||||
f"background: rgba{highlight_color.toTuple()};"
|
||||
f"color: rgba{color_.toTuple()};"
|
||||
f"border-color: rgba{color_.toTuple()};"
|
||||
f"}}"
|
||||
f"QPushButton::focus{{"
|
||||
f"padding-right: 0px;"
|
||||
f"padding-left: 0px;"
|
||||
f"outline-style: solid;"
|
||||
f"outline-width: 1px;"
|
||||
f"outline-radius: 4px;"
|
||||
f"outline-color: rgba{text_color.toTuple()};"
|
||||
f"}}"
|
||||
)
|
||||
|
||||
self.secondary_button.setText(hex_code if color else Translations["color.title.no_color"])
|
||||
self.secondary_button.setStyleSheet(list_button_style(color_))
|
||||
self.preview_button.set_tag_color_group(self.build_color()[1])
|
||||
|
||||
def update_known_colors(self):
|
||||
@@ -317,17 +247,9 @@ class BuildColorPanel(PanelWidget):
|
||||
is_slug_empty = not slug
|
||||
is_invalid = False
|
||||
|
||||
self.name_field.setStyleSheet(
|
||||
f"border: 1px solid {get_ui_color(ColorType.PRIMARY, UiColor.RED)}; border-radius: 2px"
|
||||
if is_name_empty
|
||||
else ""
|
||||
)
|
||||
self.name_field.setStyleSheet(line_edit_style() if is_name_empty else "")
|
||||
|
||||
self.slug_field.setStyleSheet(
|
||||
f"border: 1px solid {get_ui_color(ColorType.PRIMARY, UiColor.RED)}; border-radius: 2px"
|
||||
if is_slug_empty or is_invalid
|
||||
else ""
|
||||
)
|
||||
self.slug_field.setStyleSheet(line_edit_style() if is_slug_empty or is_invalid else "")
|
||||
|
||||
self.slug_field.setText(slug)
|
||||
self.update_preview_text()
|
||||
@@ -364,13 +286,7 @@ class BuildColorPanel(PanelWidget):
|
||||
)
|
||||
return (self.color_group, new_color)
|
||||
|
||||
@override
|
||||
def parent_post_init(self):
|
||||
# self.setTabOrder(self.name_field, self.shorthand_field)
|
||||
# self.setTabOrder(self.shorthand_field, self.aliases_add_button)
|
||||
# self.setTabOrder(self.aliases_add_button, self.parent_tags_add_button)
|
||||
# self.setTabOrder(self.parent_tags_add_button, self.color_button)
|
||||
# self.setTabOrder(self.color_button, self.panel_cancel_button)
|
||||
# self.setTabOrder(self.panel_cancel_button, self.panel_save_button)
|
||||
# self.setTabOrder(self.panel_save_button, self.aliases_table.cellWidget(0, 1))
|
||||
self.name_field.selectAll()
|
||||
self.name_field.setFocus()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
|
||||
import contextlib
|
||||
from typing import override
|
||||
from uuid import uuid4
|
||||
|
||||
import structlog
|
||||
@@ -12,9 +13,9 @@ from PySide6.QtWidgets import QLabel, QLineEdit, QVBoxLayout, QWidget
|
||||
from tagstudio.core.constants import RESERVED_NAMESPACE_PREFIX
|
||||
from tagstudio.core.library.alchemy.library import Library, ReservedNamespaceError, slugify
|
||||
from tagstudio.core.library.alchemy.models import Namespace
|
||||
from tagstudio.qt.models.palette import ColorType, UiColor, get_ui_color
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelWidget
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import line_edit_style
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
@@ -111,17 +112,9 @@ class BuildNamespacePanel(PanelWidget):
|
||||
is_slug_empty = not slug
|
||||
is_invalid = False
|
||||
|
||||
self.name_field.setStyleSheet(
|
||||
f"border: 1px solid {get_ui_color(ColorType.PRIMARY, UiColor.RED)}; border-radius: 2px"
|
||||
if is_name_empty
|
||||
else ""
|
||||
)
|
||||
self.name_field.setStyleSheet(line_edit_style() if is_name_empty else "")
|
||||
|
||||
self.slug_field.setStyleSheet(
|
||||
f"border: 1px solid {get_ui_color(ColorType.PRIMARY, UiColor.RED)}; border-radius: 2px"
|
||||
if is_slug_empty or is_invalid
|
||||
else ""
|
||||
)
|
||||
self.slug_field.setStyleSheet(line_edit_style() if is_slug_empty or is_invalid else "")
|
||||
|
||||
self.slug_field.setText(slug)
|
||||
|
||||
@@ -156,6 +149,7 @@ class BuildNamespacePanel(PanelWidget):
|
||||
logger.info("[BuildNamespacePanel] Built Namespace", slug=slug, name=name)
|
||||
return namespace
|
||||
|
||||
@override
|
||||
def parent_post_init(self):
|
||||
self.setTabOrder(self.name_field, self.slug_field)
|
||||
self.name_field.selectAll()
|
||||
|
||||
@@ -33,15 +33,19 @@ from tagstudio.qt.mixed.tag_color_preview import TagColorPreview
|
||||
from tagstudio.qt.mixed.tag_color_selection import TagColorSelection
|
||||
from tagstudio.qt.mixed.tag_widget import (
|
||||
TagWidget,
|
||||
get_border_color,
|
||||
get_highlight_color,
|
||||
get_primary_color,
|
||||
get_text_color,
|
||||
get_tag_border_color,
|
||||
get_tag_highlight_color,
|
||||
get_tag_primary_color,
|
||||
get_tag_text_color,
|
||||
)
|
||||
from tagstudio.qt.models.palette import ColorType, UiColor, get_ui_color
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelModal, PanelWidget
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import checkbox_style
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import (
|
||||
checkbox_style,
|
||||
colored_radio_button_style,
|
||||
header,
|
||||
line_edit_style,
|
||||
)
|
||||
from tagstudio.qt.views.tag_search_panel_view import TagSearchPanelView
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
@@ -244,7 +248,7 @@ class BuildTagPanel(PanelWidget):
|
||||
self.root_layout.addWidget(self.aliases_add_button)
|
||||
self.root_layout.addWidget(self.parent_tags_widget)
|
||||
self.root_layout.addWidget(self.color_widget)
|
||||
self.root_layout.addWidget(QLabel("<h3>Properties</h3>"))
|
||||
self.root_layout.addWidget(QLabel(header(Translations["tag.properties"], 3)))
|
||||
self.root_layout.addWidget(self.cat_widget)
|
||||
self.root_layout.addWidget(self.hidden_widget)
|
||||
|
||||
@@ -357,13 +361,13 @@ class BuildTagPanel(PanelWidget):
|
||||
row.setSpacing(3)
|
||||
|
||||
# Init Colors
|
||||
primary_color = get_primary_color(tag)
|
||||
primary_color = get_tag_primary_color(tag)
|
||||
border_color = (
|
||||
get_border_color(primary_color)
|
||||
get_tag_border_color(primary_color)
|
||||
if not (tag.color and tag.color.secondary and tag.color.color_border)
|
||||
else (QColor(tag.color.secondary))
|
||||
)
|
||||
highlight_color = get_highlight_color(
|
||||
highlight_color = get_tag_highlight_color(
|
||||
primary_color
|
||||
if not (tag.color and tag.color.secondary)
|
||||
else QColor(tag.color.secondary)
|
||||
@@ -372,7 +376,7 @@ class BuildTagPanel(PanelWidget):
|
||||
if tag.color and tag.color.secondary:
|
||||
text_color = QColor(tag.color.secondary)
|
||||
else:
|
||||
text_color = get_text_color(primary_color, highlight_color)
|
||||
text_color = get_tag_text_color(primary_color, highlight_color)
|
||||
|
||||
# Add Tag Widget
|
||||
tag_widget = TagWidget(
|
||||
@@ -395,35 +399,7 @@ class BuildTagPanel(PanelWidget):
|
||||
disam_button.setFixedSize(22, 22)
|
||||
disam_button.setToolTip(Translations["tag.disambiguation.tooltip"])
|
||||
disam_button.setStyleSheet(
|
||||
f"QRadioButton{{"
|
||||
f"background: rgba{primary_color.toTuple()};"
|
||||
f"color: rgba{text_color.toTuple()};"
|
||||
f"border-color: rgba{border_color.toTuple()};"
|
||||
f"border-radius: 6px;"
|
||||
f"border-style:solid;"
|
||||
f"border-width: 2px;"
|
||||
f"}}"
|
||||
f"QRadioButton::indicator{{"
|
||||
f"width: 10px;"
|
||||
f"height: 10px;"
|
||||
f"border-radius: 2px;"
|
||||
f"margin: 4px;"
|
||||
f"}}"
|
||||
f"QRadioButton::indicator:checked{{"
|
||||
f"background: rgba{text_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QRadioButton::hover{{"
|
||||
f"border-color: rgba{highlight_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QRadioButton::pressed{{"
|
||||
f"background: rgba{border_color.toTuple()};"
|
||||
f"color: rgba{primary_color.toTuple()};"
|
||||
f"border-color: rgba{primary_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QRadioButton::focus{{"
|
||||
f"border-color: rgba{highlight_color.toTuple()};"
|
||||
f"outline:none;"
|
||||
f"}}"
|
||||
colored_radio_button_style(primary_color, text_color, border_color, highlight_color)
|
||||
)
|
||||
|
||||
self.disam_button_group.addButton(disam_button)
|
||||
@@ -543,11 +519,7 @@ class BuildTagPanel(PanelWidget):
|
||||
def on_name_changed(self):
|
||||
is_empty = not self.name_field.text().strip()
|
||||
|
||||
self.name_field.setStyleSheet(
|
||||
f"border: 1px solid {get_ui_color(ColorType.PRIMARY, UiColor.RED)}; border-radius: 2px"
|
||||
if is_empty
|
||||
else ""
|
||||
)
|
||||
self.name_field.setStyleSheet(line_edit_style() if is_empty else "")
|
||||
|
||||
if self.panel_save_button is not None:
|
||||
self.panel_save_button.setDisabled(is_empty)
|
||||
|
||||
@@ -10,16 +10,15 @@ from PySide6.QtCore import Signal
|
||||
from PySide6.QtWidgets import QMessageBox, QPushButton
|
||||
|
||||
from tagstudio.core.constants import RESERVED_NAMESPACE_PREFIX
|
||||
from tagstudio.core.library.alchemy.enums import TagColorEnum
|
||||
from tagstudio.core.library.alchemy.models import TagColorGroup
|
||||
from tagstudio.core.utils.types import unwrap
|
||||
from tagstudio.qt.mixed.build_color import BuildColorPanel
|
||||
from tagstudio.qt.mixed.field_widget import FieldWidget
|
||||
from tagstudio.qt.mixed.tag_color_label import TagColorLabel
|
||||
from tagstudio.qt.models.palette import ColorType, get_tag_color
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.layouts.flow_layout import FlowLayout
|
||||
from tagstudio.qt.views.panel_modal import PanelModal
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import add_button_style
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
@@ -43,34 +42,6 @@ class ColorBoxWidget(FieldWidget):
|
||||
title = "" if not self.lib.engine else self.lib.get_namespace_name(group)
|
||||
super().__init__(title)
|
||||
|
||||
self.add_button_stylesheet = (
|
||||
f"QPushButton{{"
|
||||
f"background: {get_tag_color(ColorType.PRIMARY, TagColorEnum.DEFAULT)};"
|
||||
f"color: {get_tag_color(ColorType.TEXT, TagColorEnum.DEFAULT)};"
|
||||
f"font-weight: 600;"
|
||||
f"border-color:{get_tag_color(ColorType.BORDER, TagColorEnum.DEFAULT)};"
|
||||
f"border-radius: 6px;"
|
||||
f"border-style:solid;"
|
||||
f"border-width: 2px;"
|
||||
f"padding-right: 4px;"
|
||||
f"padding-bottom: 2px;"
|
||||
f"padding-left: 4px;"
|
||||
f"font-size: 15px"
|
||||
f"}}"
|
||||
f"QPushButton::hover{{"
|
||||
f"border-color:{get_tag_color(ColorType.LIGHT_ACCENT, TagColorEnum.DEFAULT)};"
|
||||
f"}}"
|
||||
f"QPushButton::pressed{{"
|
||||
f"background: {get_tag_color(ColorType.LIGHT_ACCENT, TagColorEnum.DEFAULT)};"
|
||||
f"color: {get_tag_color(ColorType.PRIMARY, TagColorEnum.DEFAULT)};"
|
||||
f"border-color: {get_tag_color(ColorType.PRIMARY, TagColorEnum.DEFAULT)};"
|
||||
f"}}"
|
||||
f"QPushButton::focus{{"
|
||||
f"border-color: {get_tag_color(ColorType.LIGHT_ACCENT, TagColorEnum.DEFAULT)};"
|
||||
f"outline:none;"
|
||||
f"}}"
|
||||
)
|
||||
|
||||
self.setObjectName("colorBox")
|
||||
self.base_layout = FlowLayout()
|
||||
self.base_layout.enable_grid_optimizations(value=True)
|
||||
@@ -114,7 +85,7 @@ class ColorBoxWidget(FieldWidget):
|
||||
add_button.setText("+")
|
||||
add_button.setFlat(True)
|
||||
add_button.setFixedSize(22, 22)
|
||||
add_button.setStyleSheet(self.add_button_stylesheet)
|
||||
add_button.setStyleSheet(add_button_style())
|
||||
add_button.clicked.connect(
|
||||
lambda: self.edit_color(
|
||||
TagColorGroup(
|
||||
|
||||
@@ -10,6 +10,7 @@ from PySide6.QtCore import QDateTime
|
||||
from PySide6.QtWidgets import QDateTimeEdit, QLineEdit, QVBoxLayout
|
||||
|
||||
from tagstudio.qt.views.panel_modal import PanelWidget
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import title_line_edit_style
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from tagstudio.qt.ts_qt import QtDriver
|
||||
@@ -46,7 +47,7 @@ class DatetimePicker(PanelWidget):
|
||||
self.root_layout.setContentsMargins(6, 0, 6, 0)
|
||||
|
||||
self.name_field = QLineEdit()
|
||||
self.name_field.setStyleSheet("font-weight:bold;font-size:14px;padding-top:6px")
|
||||
self.name_field.setStyleSheet(title_line_edit_style())
|
||||
self.name_field.setText(name)
|
||||
|
||||
if isinstance(datetime, str):
|
||||
|
||||
@@ -434,7 +434,7 @@ class FieldContainers(QWidget):
|
||||
)
|
||||
else:
|
||||
text = f"<i>{Translations['field.mixed_data']}</i>"
|
||||
inner_widget = TextContainerWidget("Mixed Tags", text)
|
||||
inner_widget = TextContainerWidget("Mixed Tags", text) # NOTE: Unlocalized but unused
|
||||
container.set_inner_widget(inner_widget)
|
||||
|
||||
container.set_edit_callback()
|
||||
|
||||
@@ -14,7 +14,7 @@ from PySide6.QtCore import QEvent, 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.views.stylesheets.stylesheets import container_style, header
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
@@ -37,17 +37,6 @@ class FieldContainer(QWidget):
|
||||
trash_icon_128.load()
|
||||
|
||||
# TODO: There should be a global button theme somewhere.
|
||||
container_style = (
|
||||
f"QWidget#fieldContainer{{"
|
||||
"border-radius:4px;"
|
||||
f"}}"
|
||||
f"QWidget#fieldContainer::hover{{"
|
||||
f"background-color:{Theme.COLOR_HOVER.value};"
|
||||
f"}}"
|
||||
f"QWidget#fieldContainer::pressed{{"
|
||||
f"background-color:{Theme.COLOR_PRESSED.value};"
|
||||
f"}}"
|
||||
)
|
||||
|
||||
def __init__(self, title: str = "Field", inline: bool = True) -> None:
|
||||
super().__init__()
|
||||
@@ -126,7 +115,7 @@ class FieldContainer(QWidget):
|
||||
self.inner_layout.addWidget(self.field)
|
||||
|
||||
self.set_title(title)
|
||||
self.setStyleSheet(FieldContainer.container_style)
|
||||
self.setStyleSheet(container_style())
|
||||
|
||||
def set_copy_callback(self, callback: Callable[[], None] | None = None) -> None:
|
||||
with catch_warnings(record=True):
|
||||
@@ -166,7 +155,7 @@ class FieldContainer(QWidget):
|
||||
return None
|
||||
|
||||
def set_title(self, title: str) -> None:
|
||||
self.title = self.title = f"<h4>{title}</h4>"
|
||||
self.title = header(title, 4)
|
||||
self.title_widget.setText(self.title)
|
||||
|
||||
@override
|
||||
|
||||
@@ -11,13 +11,12 @@ from datetime import timedelta
|
||||
from pathlib import Path
|
||||
|
||||
import structlog
|
||||
from humanfriendly import format_size
|
||||
from humanfriendly import format_size # pyright: ignore[reportUnknownVariableType]
|
||||
from PIL import ImageFont
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtGui import QGuiApplication
|
||||
from PySide6.QtWidgets import QLabel, QVBoxLayout, QWidget
|
||||
|
||||
from tagstudio.core.enums import ShowFilepathOption, Theme
|
||||
from tagstudio.core.enums import ShowFilepathOption
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.core.library.ignore import Ignore
|
||||
from tagstudio.core.media_types import MediaCategories
|
||||
@@ -25,6 +24,7 @@ from tagstudio.core.utils.types import unwrap
|
||||
from tagstudio.qt.models.palette import ColorType, UiColor, get_ui_color
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.utils.file_opener import FileOpenerHelper, FileOpenerLabel
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import properties_style
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from tagstudio.qt.ts_qt import QtDriver
|
||||
@@ -48,26 +48,8 @@ class FileAttributes(QWidget):
|
||||
root_layout.setContentsMargins(0, 0, 0, 0)
|
||||
root_layout.setSpacing(0)
|
||||
|
||||
label_bg_color = (
|
||||
Theme.COLOR_BG_DARK.value
|
||||
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
|
||||
else Theme.COLOR_DARK_LABEL.value
|
||||
)
|
||||
|
||||
self.date_style = "font-size:12px;"
|
||||
self.date_style = "font-size: 12px;"
|
||||
self.file_label_style = "font-size: 12px"
|
||||
self.properties_style = (
|
||||
f"background-color:{label_bg_color};"
|
||||
"color:#FFFFFF;"
|
||||
"font-family:Oxanium;"
|
||||
"font-weight:bold;"
|
||||
"font-size:12px;"
|
||||
"border-radius:3px;"
|
||||
"padding-top: 4px;"
|
||||
"padding-right: 1px;"
|
||||
"padding-bottom: 1px;"
|
||||
"padding-left: 1px;"
|
||||
)
|
||||
|
||||
self.file_label = FileOpenerLabel()
|
||||
self.file_label.setObjectName("filenameLabel")
|
||||
@@ -93,7 +75,7 @@ class FileAttributes(QWidget):
|
||||
self.dimensions_label = QLabel()
|
||||
self.dimensions_label.setObjectName("dimensionsLabel")
|
||||
self.dimensions_label.setWordWrap(True)
|
||||
self.dimensions_label.setStyleSheet(self.properties_style)
|
||||
self.dimensions_label.setStyleSheet(properties_style())
|
||||
self.dimensions_label.setHidden(True)
|
||||
|
||||
self.date_container = QWidget()
|
||||
|
||||
@@ -19,6 +19,7 @@ from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.core.library.alchemy.registries.dupe_files_registry import DupeFilesRegistry
|
||||
from tagstudio.qt.mixed.mirror_entries_modal import MirrorEntriesModal
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import header
|
||||
|
||||
# Only import for type checking/autocompletion, will not be imported at runtime.
|
||||
if TYPE_CHECKING:
|
||||
@@ -49,7 +50,6 @@ class FixDupeFilesModal(QWidget):
|
||||
|
||||
self.dupe_count = QLabel()
|
||||
self.dupe_count.setObjectName("dupeCountLabel")
|
||||
self.dupe_count.setStyleSheet("font-weight:bold;font-size:14px;")
|
||||
self.dupe_count.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.file_label = QLabel(Translations["file.duplicates.dupeguru.no_file"])
|
||||
@@ -119,13 +119,19 @@ class FixDupeFilesModal(QWidget):
|
||||
def set_dupe_count(self, count: int):
|
||||
if count < 0:
|
||||
self.mirror_button.setDisabled(True)
|
||||
self.dupe_count.setText(Translations["file.duplicates.matches_uninitialized"])
|
||||
self.dupe_count.setText(
|
||||
header(Translations["file.duplicates.matches_uninitialized"], 4)
|
||||
)
|
||||
elif count == 0:
|
||||
self.mirror_button.setDisabled(True)
|
||||
self.dupe_count.setText(Translations.format("file.duplicates.matches", count=count))
|
||||
self.dupe_count.setText(
|
||||
header(Translations.format("file.duplicates.matches", count=count), 4)
|
||||
)
|
||||
else:
|
||||
self.mirror_button.setDisabled(False)
|
||||
self.dupe_count.setText(Translations.format("file.duplicates.matches", count=count))
|
||||
self.dupe_count.setText(
|
||||
header(Translations.format("file.duplicates.matches", count=count), 4)
|
||||
)
|
||||
|
||||
@override
|
||||
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None: # noqa N802
|
||||
|
||||
@@ -15,6 +15,7 @@ from tagstudio.qt.mixed.progress_bar import ProgressWidget
|
||||
from tagstudio.qt.mixed.relink_entries_modal import RelinkUnlinkedEntries
|
||||
from tagstudio.qt.mixed.remove_unlinked_modal import RemoveUnlinkedEntriesModal
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import header
|
||||
|
||||
# Only import for type checking/autocompletion, will not be imported at runtime.
|
||||
if TYPE_CHECKING:
|
||||
@@ -148,7 +149,7 @@ class FixUnlinkedEntriesModal(QWidget):
|
||||
count_text: str = Translations.format(
|
||||
"entries.unlinked.unlinked_count", count=count if count >= 0 else "—"
|
||||
)
|
||||
self.unlinked_count_label.setText(f"<h3>{count_text}</h3>")
|
||||
self.unlinked_count_label.setText(header(count_text, 3))
|
||||
|
||||
@override
|
||||
def showEvent(self, event: QtGui.QShowEvent) -> None:
|
||||
|
||||
@@ -29,6 +29,7 @@ from tagstudio.core.utils.types import unwrap
|
||||
from tagstudio.qt.models.palette import ColorType, get_tag_color
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.layouts.flow_layout import FlowLayout
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import header
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from tagstudio.qt.ts_qt import QtDriver
|
||||
@@ -56,7 +57,7 @@ def add_folders_to_tree(library: Library, tree: BranchData, items: tuple[str, ..
|
||||
return branch
|
||||
|
||||
|
||||
@deprecated("Will be replaced with upcoming 'Macros' feature before v9.6")
|
||||
@deprecated("Will be replaced with upcoming 'Macros' feature.")
|
||||
def folders_to_tags(library: Library):
|
||||
logger.info("Converting folders to Tags")
|
||||
tree = BranchData()
|
||||
@@ -177,10 +178,9 @@ class FoldersToTagsModal(QWidget):
|
||||
self.root_layout = QVBoxLayout(self)
|
||||
self.root_layout.setContentsMargins(6, 6, 6, 6)
|
||||
|
||||
self.title_widget = QLabel(Translations["folders_to_tags.title"])
|
||||
self.title_widget = QLabel(header(Translations["folders_to_tags.title"], 3))
|
||||
self.title_widget.setObjectName("title")
|
||||
self.title_widget.setWordWrap(True)
|
||||
self.title_widget.setStyleSheet("font-weight:bold;font-size:14px;padding-top: 6px")
|
||||
self.title_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.desc_widget = QLabel()
|
||||
|
||||
@@ -48,6 +48,7 @@ from tagstudio.qt.utils.custom_runnable import CustomRunnable
|
||||
from tagstudio.qt.utils.function_iterator import FunctionIterator
|
||||
from tagstudio.qt.views.paged_body_wrapper import PagedBodyWrapper
|
||||
from tagstudio.qt.views.qbutton_wrapper import QPushButtonWrapper
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import header
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
@@ -364,7 +365,7 @@ class JsonMigrationModal(QObject):
|
||||
iterator = FunctionIterator(self.migration_iterator)
|
||||
iterator.value.connect(
|
||||
lambda x: (
|
||||
pb.setLabelText(f"<h4>{x}</h4>"),
|
||||
pb.setLabelText(header(x, 4)),
|
||||
self.update_sql_value_ui(show_msg_box=False)
|
||||
if x == Translations["json_migration.checking_for_parity"]
|
||||
else (),
|
||||
@@ -386,7 +387,7 @@ class JsonMigrationModal(QObject):
|
||||
QThreadPool.globalInstance().start(r)
|
||||
except Exception as e:
|
||||
logger.error("[MigrationModal][Iterator] Error:", error=e)
|
||||
pb.setLabelText(f"<h4>{type(e).__name__}</h4>")
|
||||
pb.setLabelText(header(type(e).__name__, 4))
|
||||
pb.setMinimum(1)
|
||||
pb.setValue(1)
|
||||
|
||||
@@ -410,7 +411,7 @@ class JsonMigrationModal(QObject):
|
||||
)
|
||||
self.sql_lib.migrate_json_to_sqlite(self.json_lib)
|
||||
yield Translations["json_migration.checking_for_parity"]
|
||||
check_set = set()
|
||||
check_set: set[bool] = set()
|
||||
check_set.add(self.check_field_parity())
|
||||
check_set.add(self.check_path_parity())
|
||||
check_set.add(self.check_name_parity())
|
||||
@@ -522,7 +523,7 @@ class JsonMigrationModal(QObject):
|
||||
def assert_ignore_parity(self) -> None:
|
||||
compiled_pats = fnmatch.compile(
|
||||
ignore_to_glob(
|
||||
Ignore._load_ignore_file(
|
||||
Ignore._load_ignore_file( # pyright: ignore[reportPrivateUsage]
|
||||
unwrap(self.json_lib.library_dir) / TS_FOLDER_NAME / IGNORE_NAME
|
||||
)
|
||||
),
|
||||
|
||||
@@ -11,12 +11,14 @@ from PySide6.QtWidgets import QHBoxLayout, QPushButton, QVBoxLayout, QWidget
|
||||
|
||||
from tagstudio.core.library.alchemy.models import TagColorGroup
|
||||
from tagstudio.qt.helpers.escape_text import escape_text
|
||||
from tagstudio.qt.mixed.tag_widget import (
|
||||
get_border_color,
|
||||
get_highlight_color,
|
||||
get_text_color,
|
||||
)
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import (
|
||||
get_tag_border_color,
|
||||
get_tag_highlight_color,
|
||||
get_tag_text_color,
|
||||
tag_remove_button_style,
|
||||
tag_style,
|
||||
)
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
@@ -101,76 +103,25 @@ class TagColorLabel(QWidget):
|
||||
|
||||
primary_color = self._get_primary_color(color)
|
||||
border_color = (
|
||||
get_border_color(primary_color)
|
||||
get_tag_border_color(primary_color)
|
||||
if not (color and color.secondary and color.color_border)
|
||||
else (QColor(color.secondary))
|
||||
)
|
||||
highlight_color = get_highlight_color(
|
||||
highlight_color = get_tag_highlight_color(
|
||||
primary_color if not (color and color.secondary) else QColor(color.secondary)
|
||||
)
|
||||
text_color: QColor
|
||||
if color and color.secondary:
|
||||
text_color = QColor(color.secondary)
|
||||
else:
|
||||
text_color = get_text_color(primary_color, highlight_color)
|
||||
text_color = get_tag_text_color(primary_color, highlight_color)
|
||||
|
||||
self.bg_button.setStyleSheet(
|
||||
f"QPushButton{{"
|
||||
f"background: rgba{primary_color.toTuple()};"
|
||||
f"color: rgba{text_color.toTuple()};"
|
||||
f"font-weight: 600;"
|
||||
f"border-color: rgba{border_color.toTuple()};"
|
||||
f"border-radius: 6px;"
|
||||
f"border-style:solid;"
|
||||
f"border-width: 2px;"
|
||||
f"padding-right: 4px;"
|
||||
f"padding-left: 4px;"
|
||||
f"font-size: 13px"
|
||||
f"}}"
|
||||
f"QPushButton::hover{{"
|
||||
f"border-color: rgba{highlight_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QPushButton::pressed{{"
|
||||
f"background: rgba{highlight_color.toTuple()};"
|
||||
f"color: rgba{primary_color.toTuple()};"
|
||||
f"border-color: rgba{primary_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QPushButton::focus{{"
|
||||
f"padding-right: 0px;"
|
||||
f"padding-left: 0px;"
|
||||
f"outline-style: solid;"
|
||||
f"outline-width: 1px;"
|
||||
f"outline-radius: 4px;"
|
||||
f"outline-color: rgba{text_color.toTuple()};"
|
||||
f"}}"
|
||||
tag_style(primary_color, text_color, border_color, highlight_color)
|
||||
)
|
||||
|
||||
self.remove_button.setStyleSheet(
|
||||
f"QPushButton{{"
|
||||
f"color: rgba{primary_color.toTuple()};"
|
||||
f"background: rgba{text_color.toTuple()};"
|
||||
f"font-weight: 800;"
|
||||
f"border-radius: 5px;"
|
||||
f"border-width: 4;"
|
||||
f"border-color: rgba(0,0,0,0);"
|
||||
f"padding-bottom: 4px;"
|
||||
f"font-size: 14px"
|
||||
f"}}"
|
||||
f"QPushButton::hover{{"
|
||||
f"background: rgba{primary_color.toTuple()};"
|
||||
f"color: rgba{text_color.toTuple()};"
|
||||
f"border-color: rgba{highlight_color.toTuple()};"
|
||||
f"border-width: 2;"
|
||||
f"border-radius: 6px;"
|
||||
f"}}"
|
||||
f"QPushButton::pressed{{"
|
||||
f"background: rgba{border_color.toTuple()};"
|
||||
f"color: rgba{highlight_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QPushButton::focus{{"
|
||||
f"background: rgba{border_color.toTuple()};"
|
||||
f"outline:none;"
|
||||
f"}}"
|
||||
tag_remove_button_style(primary_color, text_color, border_color, highlight_color)
|
||||
)
|
||||
|
||||
self.bg_button.setText(escape_text(color.name))
|
||||
@@ -183,13 +134,15 @@ class TagColorLabel(QWidget):
|
||||
def set_has_remove(self, has_remove: bool):
|
||||
self.has_remove = has_remove
|
||||
|
||||
def enterEvent(self, event: QEnterEvent) -> None: # noqa: N802
|
||||
@typing.override
|
||||
def enterEvent(self, event: QEnterEvent) -> None:
|
||||
if self.has_remove:
|
||||
self.remove_button.setHidden(False)
|
||||
self.update()
|
||||
return super().enterEvent(event)
|
||||
|
||||
def leaveEvent(self, event: QEvent) -> None: # noqa: N802
|
||||
@typing.override
|
||||
def leaveEvent(self, event: QEvent) -> None:
|
||||
if self.has_remove:
|
||||
self.remove_button.setHidden(True)
|
||||
self.update()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import TYPE_CHECKING, override
|
||||
from typing import TYPE_CHECKING, Any, override
|
||||
|
||||
import structlog
|
||||
from PySide6 import QtCore, QtGui
|
||||
@@ -28,6 +28,7 @@ from tagstudio.qt.mixed.color_box import ColorBoxWidget
|
||||
from tagstudio.qt.mixed.field_widget import FieldContainer
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelModal
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import header
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
@@ -62,7 +63,7 @@ class TagColorManager(QWidget):
|
||||
self.title_label = QLabel()
|
||||
self.title_label.setObjectName("titleLabel")
|
||||
self.title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.title_label.setText(f"<h3>{Translations['color_manager.title']}</h3>")
|
||||
self.title_label.setText(header(Translations["color_manager.title"], 3))
|
||||
|
||||
self.scroll_layout = QVBoxLayout()
|
||||
self.scroll_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
||||
@@ -189,7 +190,7 @@ class TagColorManager(QWidget):
|
||||
|
||||
self.create_namespace_modal.show()
|
||||
|
||||
def delete_namespace_dialog(self, prompt: str, callback: Callable) -> None:
|
||||
def delete_namespace_dialog(self, prompt: str, callback: Callable[..., Any]) -> None: # pyright: ignore[reportExplicitAny]
|
||||
message_box = QMessageBox()
|
||||
message_box.setText(prompt)
|
||||
message_box.setWindowTitle(Translations["color.namespace.delete.title"])
|
||||
@@ -207,13 +208,13 @@ class TagColorManager(QWidget):
|
||||
callback()
|
||||
|
||||
@override
|
||||
def showEvent(self, event: QtGui.QShowEvent) -> None: # noqa N802
|
||||
def showEvent(self, event: QtGui.QShowEvent) -> None:
|
||||
if not self.is_initialized:
|
||||
self.setup_color_groups()
|
||||
return super().showEvent(event)
|
||||
|
||||
@override
|
||||
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None: # noqa N802
|
||||
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None:
|
||||
if event.key() == QtCore.Qt.Key.Key_Escape: # noqa SIM114
|
||||
self.done_button.click()
|
||||
elif event.key() == Qt.Key.Key_Return or event.key() == Qt.Key.Key_Enter:
|
||||
|
||||
@@ -11,9 +11,14 @@ from PySide6.QtWidgets import QPushButton, QVBoxLayout, QWidget
|
||||
|
||||
from tagstudio.core.library.alchemy.enums import TagColorEnum
|
||||
from tagstudio.core.library.alchemy.models import TagColorGroup
|
||||
from tagstudio.qt.mixed.tag_widget import get_border_color, get_highlight_color, get_text_color
|
||||
from tagstudio.qt.models.palette import ColorType, get_tag_color
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import (
|
||||
get_tag_border_color,
|
||||
get_tag_highlight_color,
|
||||
get_tag_text_color,
|
||||
tag_style,
|
||||
)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
@@ -66,11 +71,11 @@ class TagColorPreview(QWidget):
|
||||
|
||||
primary_color = self._get_primary_color(color_group)
|
||||
border_color = (
|
||||
get_border_color(primary_color)
|
||||
get_tag_border_color(primary_color)
|
||||
if not (color_group and color_group.secondary and color_group.color_border)
|
||||
else (QColor(color_group.secondary))
|
||||
)
|
||||
highlight_color = get_highlight_color(
|
||||
highlight_color = get_tag_highlight_color(
|
||||
primary_color
|
||||
if not (color_group and color_group.secondary)
|
||||
else QColor(color_group.secondary)
|
||||
@@ -79,32 +84,10 @@ class TagColorPreview(QWidget):
|
||||
if color_group and color_group.secondary:
|
||||
text_color = QColor(color_group.secondary)
|
||||
else:
|
||||
text_color = get_text_color(primary_color, highlight_color)
|
||||
text_color = get_tag_text_color(primary_color, highlight_color)
|
||||
|
||||
self.button.setStyleSheet(
|
||||
f"QPushButton{{"
|
||||
f"background: rgba{primary_color.toTuple()};"
|
||||
f"color: rgba{text_color.toTuple()};"
|
||||
f"font-weight: 600;"
|
||||
f"border-color: rgba{border_color.toTuple()};"
|
||||
f"border-radius: 6px;"
|
||||
f"border-style:solid;"
|
||||
f"border-width: 2px;"
|
||||
f"padding-right: 8px;"
|
||||
f"padding-left: 8px;"
|
||||
f"font-size: 14px"
|
||||
f"}}"
|
||||
f"QPushButton::hover{{"
|
||||
f"border-color: rgba{highlight_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QPushButton::focus{{"
|
||||
f"padding-right: 0px;"
|
||||
f"padding-left: 0px;"
|
||||
f"outline-style: solid;"
|
||||
f"outline-width: 1px;"
|
||||
f"outline-radius: 4px;"
|
||||
f"outline-color: rgba{text_color.toTuple()};"
|
||||
f"}}"
|
||||
tag_style(primary_color, text_color, border_color, highlight_color)
|
||||
)
|
||||
# Add back the padding if the hint is generated while the button has focus (no padding)
|
||||
self.button.setMinimumWidth(
|
||||
|
||||
@@ -20,11 +20,16 @@ from PySide6.QtWidgets import (
|
||||
from tagstudio.core.library.alchemy.enums import TagColorEnum
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.core.library.alchemy.models import TagColorGroup
|
||||
from tagstudio.qt.mixed.tag_widget import get_border_color, get_highlight_color, get_text_color
|
||||
from tagstudio.qt.mixed.tag_widget import (
|
||||
get_tag_border_color,
|
||||
get_tag_highlight_color,
|
||||
get_tag_text_color,
|
||||
)
|
||||
from tagstudio.qt.models.palette import ColorType, get_tag_color
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.layouts.flow_layout import FlowLayout
|
||||
from tagstudio.qt.views.panel_modal import PanelWidget
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import color_swatch_style, header
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
@@ -67,9 +72,7 @@ class TagColorSelection(PanelWidget):
|
||||
self.scroll_layout.addSpacerItem(QSpacerItem(1, 6))
|
||||
for group, colors in tag_color_groups.items():
|
||||
display_name: str = self.lib.get_namespace_name(group)
|
||||
self.scroll_layout.addWidget(
|
||||
QLabel(f"<h4>{display_name if display_name else group}</h4>")
|
||||
)
|
||||
self.scroll_layout.addWidget(QLabel(header(display_name if display_name else group, 4)))
|
||||
color_box_widget = QWidget()
|
||||
color_group_layout = FlowLayout()
|
||||
color_group_layout.setSpacing(4)
|
||||
@@ -79,54 +82,31 @@ class TagColorSelection(PanelWidget):
|
||||
for color in colors:
|
||||
primary_color = self._get_primary_color(color)
|
||||
border_color = (
|
||||
get_border_color(primary_color)
|
||||
get_tag_border_color(primary_color)
|
||||
if not (color and color.secondary and color.color_border)
|
||||
else (QColor(color.secondary))
|
||||
)
|
||||
highlight_color = get_highlight_color(
|
||||
highlight_color = get_tag_highlight_color(
|
||||
primary_color if not (color and color.secondary) else QColor(color.secondary)
|
||||
)
|
||||
text_color: QColor
|
||||
if color and color.secondary:
|
||||
text_color = QColor(color.secondary)
|
||||
else:
|
||||
text_color = get_text_color(primary_color, highlight_color)
|
||||
text_color = get_tag_text_color(primary_color, highlight_color)
|
||||
|
||||
radio_button = QRadioButton()
|
||||
radio_button.setObjectName(f"{color.namespace}.{color.slug}")
|
||||
radio_button.setToolTip(color.name)
|
||||
radio_button.setFixedSize(24, 24)
|
||||
bottom_color: str = (
|
||||
f"border-bottom-color: rgba{text_color.toTuple()};" if color.secondary else ""
|
||||
)
|
||||
radio_button.setStyleSheet(
|
||||
f"QRadioButton{{"
|
||||
f"background: rgba{primary_color.toTuple()};"
|
||||
f"color: rgba{text_color.toTuple()};"
|
||||
f"border-color: rgba{border_color.toTuple()};"
|
||||
f"{bottom_color}"
|
||||
f"border-radius: 3px;"
|
||||
f"border-style:solid;"
|
||||
f"border-width: 2px;"
|
||||
f"}}"
|
||||
f"QRadioButton::indicator{{"
|
||||
f"width: 12px;"
|
||||
f"height: 12px;"
|
||||
f"border-radius: 1px;"
|
||||
f"margin: 4px;"
|
||||
f"}}"
|
||||
f"QRadioButton::indicator:checked{{"
|
||||
f"background: rgba{text_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QRadioButton::hover{{"
|
||||
f"border-color: rgba{highlight_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QRadioButton::focus{{"
|
||||
f"outline-style: solid;"
|
||||
f"outline-width: 2px;"
|
||||
f"outline-radius: 3px;"
|
||||
f"outline-color: rgba{highlight_color.toTuple()};"
|
||||
f"}}"
|
||||
color_swatch_style(
|
||||
primary_color,
|
||||
text_color,
|
||||
border_color,
|
||||
highlight_color,
|
||||
text_color if color.secondary else None,
|
||||
)
|
||||
)
|
||||
radio_button.clicked.connect(lambda checked=False, x=color: self.select_color(x))
|
||||
color_group_layout.addWidget(radio_button)
|
||||
@@ -136,7 +116,7 @@ class TagColorSelection(PanelWidget):
|
||||
|
||||
def add_no_color_widget(self):
|
||||
no_color_str: str = Translations["color.title.no_color"]
|
||||
self.scroll_layout.addWidget(QLabel(f"<h4>{no_color_str}</h4>"))
|
||||
self.scroll_layout.addWidget(QLabel(header(no_color_str, 4)))
|
||||
color_box_widget = QWidget()
|
||||
color_group_layout = FlowLayout()
|
||||
color_group_layout.setSpacing(4)
|
||||
@@ -145,45 +125,20 @@ class TagColorSelection(PanelWidget):
|
||||
color_box_widget.setLayout(color_group_layout)
|
||||
color = None
|
||||
primary_color = self._get_primary_color(color)
|
||||
border_color = get_border_color(primary_color)
|
||||
highlight_color = get_highlight_color(primary_color)
|
||||
border_color = get_tag_border_color(primary_color)
|
||||
highlight_color = get_tag_highlight_color(primary_color)
|
||||
text_color: QColor
|
||||
if color and color.secondary and color.color_border:
|
||||
text_color = QColor(color.secondary)
|
||||
else:
|
||||
text_color = get_text_color(primary_color, highlight_color)
|
||||
text_color = get_tag_text_color(primary_color, highlight_color)
|
||||
|
||||
radio_button = QRadioButton()
|
||||
radio_button.setObjectName("None") # NOTE: Internal use, no translation needed.
|
||||
radio_button.setObjectName("None")
|
||||
radio_button.setToolTip(no_color_str)
|
||||
radio_button.setFixedSize(24, 24)
|
||||
radio_button.setStyleSheet(
|
||||
f"QRadioButton{{"
|
||||
f"background: rgba{primary_color.toTuple()};"
|
||||
f"color: rgba{text_color.toTuple()};"
|
||||
f"border-color: rgba{border_color.toTuple()};"
|
||||
f"border-radius: 3px;"
|
||||
f"border-style:solid;"
|
||||
f"border-width: 2px;"
|
||||
f"}}"
|
||||
f"QRadioButton::indicator{{"
|
||||
f"width: 12px;"
|
||||
f"height: 12px;"
|
||||
f"border-radius: 1px;"
|
||||
f"margin: 4px;"
|
||||
f"}}"
|
||||
f"QRadioButton::indicator:checked{{"
|
||||
f"background: rgba{text_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QRadioButton::hover{{"
|
||||
f"border-color: rgba{highlight_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QRadioButton::focus{{"
|
||||
f"outline-style: solid;"
|
||||
f"outline-width: 2px;"
|
||||
f"outline-radius: 3px;"
|
||||
f"outline-color: rgba{highlight_color.toTuple()};"
|
||||
f"}}"
|
||||
color_swatch_style(primary_color, text_color, border_color, highlight_color)
|
||||
)
|
||||
radio_button.clicked.connect(lambda checked=False, x=color: self.select_color(x))
|
||||
color_group_layout.addWidget(radio_button)
|
||||
|
||||
@@ -15,6 +15,14 @@ from tagstudio.core.library.alchemy.models import Tag
|
||||
from tagstudio.qt.helpers.escape_text import escape_text
|
||||
from tagstudio.qt.models.palette import ColorType, get_tag_color
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import (
|
||||
get_tag_border_color,
|
||||
get_tag_highlight_color,
|
||||
get_tag_primary_color,
|
||||
get_tag_text_color,
|
||||
tag_remove_button_style,
|
||||
tag_style,
|
||||
)
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
@@ -188,13 +196,13 @@ class TagWidget(QWidget):
|
||||
if not tag:
|
||||
return
|
||||
|
||||
primary_color = get_primary_color(tag)
|
||||
primary_color = get_tag_primary_color(tag)
|
||||
border_color = (
|
||||
get_border_color(primary_color)
|
||||
get_tag_border_color(primary_color)
|
||||
if not (tag.color and tag.color.secondary and tag.color.color_border)
|
||||
else (QColor(tag.color.secondary))
|
||||
)
|
||||
highlight_color = get_highlight_color(
|
||||
highlight_color = get_tag_highlight_color(
|
||||
primary_color
|
||||
if not (tag.color and tag.color.secondary)
|
||||
else QColor(tag.color.secondary)
|
||||
@@ -203,65 +211,14 @@ class TagWidget(QWidget):
|
||||
if tag.color and tag.color.secondary:
|
||||
text_color = QColor(tag.color.secondary)
|
||||
else:
|
||||
text_color = get_text_color(primary_color, highlight_color)
|
||||
text_color = get_tag_text_color(primary_color, highlight_color)
|
||||
|
||||
self.bg_button.setStyleSheet(
|
||||
f"QPushButton{{"
|
||||
f"background: rgba{primary_color.toTuple()};"
|
||||
f"color: rgba{text_color.toTuple()};"
|
||||
f"font-weight: 600;"
|
||||
f"border-color: rgba{border_color.toTuple()};"
|
||||
f"border-radius: 6px;"
|
||||
f"border-style:solid;"
|
||||
f"border-width: 2px;"
|
||||
f"padding-right: 4px;"
|
||||
f"padding-left: 4px;"
|
||||
f"font-size: 13px"
|
||||
f"}}"
|
||||
f"QPushButton::hover{{"
|
||||
f"border-color: rgba{highlight_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QPushButton::pressed{{"
|
||||
f"background: rgba{highlight_color.toTuple()};"
|
||||
f"color: rgba{primary_color.toTuple()};"
|
||||
f"border-color: rgba{primary_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QPushButton::focus{{"
|
||||
f"padding-right: 0px;"
|
||||
f"padding-left: 0px;"
|
||||
f"outline-style: solid;"
|
||||
f"outline-width: 1px;"
|
||||
f"outline-radius: 4px;"
|
||||
f"outline-color: rgba{text_color.toTuple()};"
|
||||
f"}}"
|
||||
tag_style(primary_color, text_color, border_color, highlight_color)
|
||||
)
|
||||
|
||||
self._delete_button.setStyleSheet(
|
||||
f"QPushButton{{"
|
||||
f"color: rgba{primary_color.toTuple()};"
|
||||
f"background: rgba{text_color.toTuple()};"
|
||||
f"font-weight: 800;"
|
||||
f"border-radius: 5px;"
|
||||
f"border-width: 4;"
|
||||
f"border-color: rgba(0,0,0,0);"
|
||||
f"padding-bottom: 4px;"
|
||||
f"font-size: 14px"
|
||||
f"}}"
|
||||
f"QPushButton::hover{{"
|
||||
f"background: rgba{primary_color.toTuple()};"
|
||||
f"color: rgba{text_color.toTuple()};"
|
||||
f"border-color: rgba{highlight_color.toTuple()};"
|
||||
f"border-width: 2;"
|
||||
f"border-radius: 6px;"
|
||||
f"}}"
|
||||
f"QPushButton::pressed{{"
|
||||
f"background: rgba{border_color.toTuple()};"
|
||||
f"color: rgba{highlight_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QPushButton::focus{{"
|
||||
f"background: rgba{border_color.toTuple()};"
|
||||
f"outline:none;"
|
||||
f"}}"
|
||||
tag_remove_button_style(primary_color, text_color, border_color, highlight_color)
|
||||
)
|
||||
|
||||
if self.lib:
|
||||
@@ -285,42 +242,3 @@ class TagWidget(QWidget):
|
||||
self._delete_button.setHidden(True)
|
||||
self.update()
|
||||
return super().leaveEvent(event)
|
||||
|
||||
|
||||
def get_primary_color(tag: Tag) -> QColor:
|
||||
primary_color = QColor(
|
||||
get_tag_color(ColorType.PRIMARY, TagColorEnum.DEFAULT)
|
||||
if not tag.color
|
||||
else tag.color.primary
|
||||
)
|
||||
|
||||
return primary_color
|
||||
|
||||
|
||||
def get_border_color(primary_color: QColor) -> QColor:
|
||||
border_color: QColor = QColor(primary_color)
|
||||
border_color.setRed(min(border_color.red() + 20, 255))
|
||||
border_color.setGreen(min(border_color.green() + 20, 255))
|
||||
border_color.setBlue(min(border_color.blue() + 20, 255))
|
||||
|
||||
return border_color
|
||||
|
||||
|
||||
def get_highlight_color(primary_color: QColor) -> QColor:
|
||||
highlight_color: QColor = QColor(primary_color)
|
||||
highlight_color = highlight_color.toHsl()
|
||||
highlight_color.setHsl(highlight_color.hue(), min(highlight_color.saturation(), 200), 225, 255)
|
||||
highlight_color = highlight_color.toRgb()
|
||||
|
||||
return highlight_color
|
||||
|
||||
|
||||
def get_text_color(primary_color: QColor, highlight_color: QColor) -> QColor:
|
||||
# logger.info("[TagWidget] Evaluating tag text color", lightness=primary_color.lightness())
|
||||
if primary_color.lightness() > 120:
|
||||
text_color = QColor(primary_color)
|
||||
text_color = text_color.toHsl()
|
||||
text_color.setHsl(text_color.hue(), text_color.saturation(), 50, 255)
|
||||
return text_color.toRgb()
|
||||
else:
|
||||
return highlight_color
|
||||
|
||||
@@ -99,6 +99,7 @@ from tagstudio.qt.views.field_template_search_panel_view import FieldTemplateSea
|
||||
from tagstudio.qt.views.main_window import MainWindow
|
||||
from tagstudio.qt.views.panel_modal import PanelModal
|
||||
from tagstudio.qt.views.splash import SplashScreen
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import header
|
||||
from tagstudio.qt.views.tag_search_panel_view import TagSearchPanelView
|
||||
|
||||
BADGE_TAGS = {
|
||||
@@ -1014,9 +1015,8 @@ class QtDriver(DriverMixin, QObject):
|
||||
perm_warning_msg = Translations.format(
|
||||
"trash.dialog.permanent_delete_warning", trash_term=trash_term()
|
||||
)
|
||||
perm_warning: str = (
|
||||
f"<h4 style='color: {get_ui_color(ColorType.PRIMARY, UiColor.RED)}'>"
|
||||
f"{perm_warning_msg}</h4>"
|
||||
perm_warning: str = header(
|
||||
perm_warning_msg, 4, get_ui_color(ColorType.PRIMARY, UiColor.RED)
|
||||
)
|
||||
|
||||
msg = QMessageBox()
|
||||
@@ -1033,8 +1033,8 @@ class QtDriver(DriverMixin, QObject):
|
||||
"trash.dialog.move.confirmation.singular", trash_term=trash_term()
|
||||
)
|
||||
msg.setText(
|
||||
f"<h3>{msg_text}</h3>"
|
||||
f"<h4>{Translations['trash.dialog.disambiguation_warning.singular']}</h4>"
|
||||
f"{header(msg_text, 3)}"
|
||||
f"{header(Translations['trash.dialog.disambiguation_warning.singular'], 4)}"
|
||||
f"{filename if filename else ''}"
|
||||
f"{perm_warning}<br>"
|
||||
)
|
||||
@@ -1045,8 +1045,8 @@ class QtDriver(DriverMixin, QObject):
|
||||
trash_term=trash_term(),
|
||||
)
|
||||
msg.setText(
|
||||
f"<h3>{msg_text}</h3>"
|
||||
f"<h4>{Translations['trash.dialog.disambiguation_warning.plural']}</h4>"
|
||||
f"{header(msg_text, 3)}"
|
||||
f"{header(Translations['trash.dialog.disambiguation_warning.plural'], 4)}"
|
||||
f"{perm_warning}<br>"
|
||||
)
|
||||
|
||||
@@ -1069,9 +1069,7 @@ class QtDriver(DriverMixin, QObject):
|
||||
pw.update_label(Translations["library.refresh.scanning_preparing"])
|
||||
pw.show()
|
||||
|
||||
iterator = FunctionIterator(
|
||||
lambda lib=unwrap(self.lib.library_dir): tracker.refresh_dir(lib) # noqa: B008
|
||||
)
|
||||
iterator = FunctionIterator(lambda lib=self.lib.library_dir: tracker.refresh_dir(lib))
|
||||
iterator.value.connect(
|
||||
lambda x: (
|
||||
pw.update_progress(x + 1),
|
||||
|
||||
@@ -16,7 +16,7 @@ from PySide6.QtWidgets import (
|
||||
from tagstudio.qt.controllers.clickable_label import ClickableLabel
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelWidget
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import checkbox_style
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import checkbox_style, title_line_edit_style
|
||||
|
||||
|
||||
class EditTextView(PanelWidget):
|
||||
@@ -27,7 +27,7 @@ class EditTextView(PanelWidget):
|
||||
self.root_layout.setContentsMargins(6, 0, 6, 0)
|
||||
|
||||
self.name_field = QLineEdit()
|
||||
self.name_field.setStyleSheet("font-weight:bold;font-size:14px;padding-top:6px")
|
||||
self.name_field.setStyleSheet(title_line_edit_style())
|
||||
|
||||
self.text_box = QPlainTextEdit()
|
||||
self.text_line = QLineEdit()
|
||||
|
||||
@@ -5,41 +5,19 @@ from PySide6.QtCore import Signal
|
||||
from PySide6.QtGui import QColor
|
||||
from PySide6.QtWidgets import QHBoxLayout, QPushButton, QVBoxLayout, QWidget
|
||||
|
||||
from tagstudio.core.enums import Theme
|
||||
from tagstudio.core.library.alchemy.enums import TagColorEnum
|
||||
from tagstudio.qt.mixed.tag_widget import get_border_color, get_highlight_color, get_text_color
|
||||
from tagstudio.qt.models.palette import ColorType, UiColor, get_tag_color, get_ui_color
|
||||
from tagstudio.qt.models.palette import ColorType, get_tag_color
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import (
|
||||
get_tag_border_color,
|
||||
get_tag_highlight_color,
|
||||
get_tag_text_color,
|
||||
list_button_style,
|
||||
)
|
||||
|
||||
primary_color: QColor = QColor(get_tag_color(ColorType.PRIMARY, TagColorEnum.DEFAULT))
|
||||
border_color: QColor = get_border_color(primary_color)
|
||||
highlight_color: QColor = get_highlight_color(primary_color)
|
||||
text_color: QColor = get_text_color(primary_color, highlight_color)
|
||||
|
||||
FIELD_TEMPLATE_BUTTON_STYLESHEET = f"""
|
||||
QPushButton{{
|
||||
background-color: {Theme.COLOR_BG.value};
|
||||
font-weight: 600;
|
||||
border-radius: 6px;
|
||||
padding-right: 4px;
|
||||
padding-left: 4px;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
}}
|
||||
|
||||
QPushButton::hover{{
|
||||
background-color: {Theme.COLOR_HOVER.value};
|
||||
border-color: {get_ui_color(ColorType.BORDER, UiColor.THEME_DARK)};
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
}}
|
||||
|
||||
QPushButton::pressed{{
|
||||
background-color: {Theme.COLOR_PRESSED.value};
|
||||
border-color: {get_ui_color(ColorType.LIGHT_ACCENT, UiColor.THEME_DARK)};
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
}}
|
||||
"""
|
||||
border_color: QColor = get_tag_border_color(primary_color)
|
||||
highlight_color: QColor = get_tag_highlight_color(primary_color)
|
||||
text_color: QColor = get_tag_text_color(primary_color, highlight_color)
|
||||
|
||||
|
||||
class FieldTemplateWidgetView(QWidget):
|
||||
@@ -63,7 +41,7 @@ class FieldTemplateWidgetView(QWidget):
|
||||
self._bg_button.setMinimumSize(44, 22)
|
||||
self._bg_button.setMinimumHeight(22)
|
||||
self._bg_button.setMaximumHeight(22)
|
||||
self._bg_button.setStyleSheet(FIELD_TEMPLATE_BUTTON_STYLESHEET)
|
||||
self._bg_button.setStyleSheet(list_button_style())
|
||||
|
||||
self.__inner_layout = QHBoxLayout()
|
||||
self.__inner_layout.setObjectName("inner_layout")
|
||||
|
||||
@@ -24,6 +24,7 @@ from PySide6.QtWidgets import (
|
||||
from tagstudio.qt.helpers.color_overlay import theme_fg_overlay
|
||||
from tagstudio.qt.platform_strings import open_file_str
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import header
|
||||
|
||||
# Only import for type checking/autocompletion, will not be imported at runtime.
|
||||
if TYPE_CHECKING:
|
||||
@@ -65,7 +66,7 @@ class LibraryInfoWindowView(QWidget):
|
||||
self.stats_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.stats_layout.setSpacing(12)
|
||||
|
||||
self.stats_label = QLabel(f"<h3>{Translations['library_info.stats']}</h3>")
|
||||
self.stats_label = QLabel(header(Translations["library_info.stats"], 3))
|
||||
self.stats_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.stats_grid: QWidget = QWidget()
|
||||
@@ -223,7 +224,7 @@ class LibraryInfoWindowView(QWidget):
|
||||
self.cleanup_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.cleanup_layout.setSpacing(12)
|
||||
|
||||
self.cleanup_label = QLabel(f"<h3>{Translations['library_info.cleanup']}</h3>")
|
||||
self.cleanup_label = QLabel(header(Translations["library_info.cleanup"], 3))
|
||||
self.cleanup_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.cleanup_grid: QWidget = QWidget()
|
||||
|
||||
@@ -16,45 +16,20 @@ from PySide6.QtWidgets import (
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from tagstudio.core.enums import Theme
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.core.library.alchemy.models import Entry
|
||||
from tagstudio.core.utils.types import unwrap
|
||||
from tagstudio.qt.controllers.preview_thumb_controller import PreviewThumb
|
||||
from tagstudio.qt.mixed.field_containers import FieldContainers
|
||||
from tagstudio.qt.mixed.file_attributes import FileAttributeData, FileAttributes
|
||||
from tagstudio.qt.models.palette import ColorType, UiColor, get_ui_color
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import button_style
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from tagstudio.qt.ts_qt import QtDriver
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
BUTTON_STYLE: str = f"""
|
||||
QPushButton{{
|
||||
background-color: {Theme.COLOR_BG.value};
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
}}
|
||||
QPushButton::hover{{
|
||||
background-color: {Theme.COLOR_HOVER.value};
|
||||
border-color: {get_ui_color(ColorType.BORDER, UiColor.THEME_DARK)};
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
}}
|
||||
QPushButton::pressed{{
|
||||
background-color: {Theme.COLOR_PRESSED.value};
|
||||
border-color: {get_ui_color(ColorType.LIGHT_ACCENT, UiColor.THEME_DARK)};
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
}}
|
||||
QPushButton::disabled{{
|
||||
background-color: {Theme.COLOR_DISABLED_BG.value};
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
class PreviewPanelView(QWidget):
|
||||
lib: Library
|
||||
@@ -94,13 +69,13 @@ class PreviewPanelView(QWidget):
|
||||
self.__add_tag_button.setEnabled(False)
|
||||
self.__add_tag_button.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
self.__add_tag_button.setMinimumHeight(28)
|
||||
self.__add_tag_button.setStyleSheet(BUTTON_STYLE)
|
||||
self.__add_tag_button.setStyleSheet(button_style())
|
||||
|
||||
self.__add_field_button = QPushButton(Translations["field.add"])
|
||||
self.__add_field_button.setEnabled(False)
|
||||
self.__add_field_button.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
self.__add_field_button.setMinimumHeight(28)
|
||||
self.__add_field_button.setStyleSheet(BUTTON_STYLE)
|
||||
self.__add_field_button.setStyleSheet(button_style())
|
||||
|
||||
add_buttons_layout.addWidget(self.__add_tag_button)
|
||||
add_buttons_layout.addWidget(self.__add_field_button)
|
||||
|
||||
@@ -16,46 +16,14 @@ from PySide6.QtWidgets import (
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from tagstudio.core.library.alchemy.enums import TagColorEnum
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.qt.models.palette import ColorType, get_tag_color
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelWidget
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import list_button_style
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from tagstudio.qt.controllers.search_panel_controller import SearchPanel
|
||||
|
||||
CREATE_BUTTON_STYLESHEET: str = f"""
|
||||
QPushButton{{
|
||||
background: {get_tag_color(ColorType.PRIMARY, TagColorEnum.DEFAULT)};
|
||||
color: {get_tag_color(ColorType.TEXT, TagColorEnum.DEFAULT)};
|
||||
font-weight: 600;
|
||||
border-color:{get_tag_color(ColorType.BORDER, TagColorEnum.DEFAULT)};
|
||||
border-radius: 6px;
|
||||
border-style: dashed;
|
||||
border-width: 2px;
|
||||
padding-right: 4px;
|
||||
padding-bottom: 1px;
|
||||
padding-left: 4px;
|
||||
font-size: 13px
|
||||
}}
|
||||
|
||||
QPushButton::hover{{
|
||||
border-color: {get_tag_color(ColorType.LIGHT_ACCENT, TagColorEnum.DEFAULT)};
|
||||
}}
|
||||
|
||||
QPushButton::pressed{{
|
||||
background: {get_tag_color(ColorType.LIGHT_ACCENT, TagColorEnum.DEFAULT)};
|
||||
color: {get_tag_color(ColorType.PRIMARY, TagColorEnum.DEFAULT)};
|
||||
border-color: {get_tag_color(ColorType.PRIMARY, TagColorEnum.DEFAULT)};
|
||||
}}
|
||||
|
||||
QPushButton::focus{{
|
||||
border-color: {get_tag_color(ColorType.LIGHT_ACCENT, TagColorEnum.DEFAULT)};
|
||||
outline: none;
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
class SearchPanelView(PanelWidget):
|
||||
def __init__(self, is_chooser: bool) -> None:
|
||||
@@ -120,7 +88,7 @@ class SearchPanelView(PanelWidget):
|
||||
self.create_and_add_button = QPushButton()
|
||||
self.create_and_add_button.setFlat(True)
|
||||
self.create_and_add_button.setMinimumSize(22, 22)
|
||||
self.create_and_add_button.setStyleSheet(CREATE_BUTTON_STYLESHEET)
|
||||
self.create_and_add_button.setStyleSheet(list_button_style(border_style="dashed"))
|
||||
|
||||
@property
|
||||
def scroll_layout(self) -> QVBoxLayout:
|
||||
|
||||
@@ -1,41 +1,431 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
from PySide6.QtGui import QColor
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtGui import QColor, QGuiApplication
|
||||
|
||||
from tagstudio.core.enums import Theme
|
||||
from tagstudio.core.library.alchemy.enums import TagColorEnum
|
||||
from tagstudio.qt.mixed.tag_widget import get_border_color, get_highlight_color, get_text_color
|
||||
from tagstudio.qt.models.palette import ColorType, get_tag_color
|
||||
from tagstudio.core.library.alchemy.models import Tag
|
||||
from tagstudio.qt.models.palette import ColorType, UiColor, get_tag_color, get_ui_color
|
||||
|
||||
# TODO: There's plenty of good opportunities here to consolidate similar styles.
|
||||
# Work should be done to more closely use Qt's theming systems rather than override them.
|
||||
|
||||
|
||||
def add_button_style() -> str:
|
||||
"""Style used for tag-like "Add" buttons [+]."""
|
||||
return f"""
|
||||
QPushButton{{
|
||||
background: {get_tag_color(ColorType.PRIMARY, TagColorEnum.DEFAULT)};
|
||||
color: {get_tag_color(ColorType.TEXT, TagColorEnum.DEFAULT)};
|
||||
font-weight: 600;
|
||||
border-color: {get_tag_color(ColorType.BORDER, TagColorEnum.DEFAULT)};
|
||||
border-radius: 6px;
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
padding-right: 4px;
|
||||
padding-bottom: 2px;
|
||||
padding-left: 4px;
|
||||
font-size: 15px
|
||||
}}
|
||||
QPushButton::hover{{
|
||||
border-color: {get_tag_color(ColorType.LIGHT_ACCENT, TagColorEnum.DEFAULT)};
|
||||
}}
|
||||
QPushButton::pressed{{
|
||||
background: {get_tag_color(ColorType.LIGHT_ACCENT, TagColorEnum.DEFAULT)};
|
||||
color: {get_tag_color(ColorType.PRIMARY, TagColorEnum.DEFAULT)};
|
||||
border-color: {get_tag_color(ColorType.PRIMARY, TagColorEnum.DEFAULT)};
|
||||
}}
|
||||
QPushButton::focus{{
|
||||
border-color: {get_tag_color(ColorType.LIGHT_ACCENT, TagColorEnum.DEFAULT)};
|
||||
outline: none;
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def button_style() -> str:
|
||||
"""Style used for common QPushButtons."""
|
||||
return f"""
|
||||
QPushButton{{
|
||||
background-color: {Theme.COLOR_BG.value};
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
}}
|
||||
QPushButton::hover{{
|
||||
background-color: {Theme.COLOR_HOVER.value};
|
||||
border-color: {get_ui_color(ColorType.BORDER, UiColor.THEME_DARK)};
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
}}
|
||||
QPushButton::pressed{{
|
||||
background-color: {Theme.COLOR_PRESSED.value};
|
||||
border-color: {get_ui_color(ColorType.LIGHT_ACCENT, UiColor.THEME_DARK)};
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
}}
|
||||
QPushButton::disabled{{
|
||||
background-color: {Theme.COLOR_DISABLED_BG.value};
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def checkbox_style() -> str:
|
||||
"""Style used for QCheckBoxes."""
|
||||
primary_color = QColor(get_tag_color(ColorType.PRIMARY, TagColorEnum.DEFAULT))
|
||||
border_color = get_border_color(primary_color)
|
||||
highlight_color = get_highlight_color(primary_color)
|
||||
text_color: QColor = get_text_color(primary_color, highlight_color)
|
||||
return (
|
||||
f"QCheckBox{{"
|
||||
f"background: rgba{primary_color.toTuple()};"
|
||||
f"color: rgba{text_color.toTuple()};"
|
||||
f"border-color: rgba{border_color.toTuple()};"
|
||||
f"border-radius: 6px;"
|
||||
f"border-style:solid;"
|
||||
f"border-width: 2px;"
|
||||
f"}}"
|
||||
f"QCheckBox::indicator{{"
|
||||
f"width: 10px;"
|
||||
f"height: 10px;"
|
||||
f"border-radius: 2px;"
|
||||
f"margin: 4px;"
|
||||
f"}}"
|
||||
f"QCheckBox::indicator:checked{{"
|
||||
f"background: rgba{text_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QCheckBox::hover{{"
|
||||
f"border-color: rgba{highlight_color.toTuple()};"
|
||||
f"}}"
|
||||
f"QCheckBox::focus{{"
|
||||
f"border-color: rgba{highlight_color.toTuple()};"
|
||||
f"outline:none;"
|
||||
f"}}"
|
||||
border_color = get_tag_border_color(primary_color)
|
||||
highlight_color = get_tag_highlight_color(primary_color)
|
||||
text_color: QColor = get_tag_text_color(primary_color, highlight_color)
|
||||
return f"""
|
||||
QCheckBox{{
|
||||
background: rgba{primary_color.toTuple()};
|
||||
color: rgba{text_color.toTuple()};
|
||||
border-color: rgba{border_color.toTuple()};
|
||||
border-radius: 6px;
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
}}
|
||||
QCheckBox::indicator{{
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 2px;
|
||||
margin: 4px;
|
||||
}}
|
||||
QCheckBox::indicator:checked{{
|
||||
background: rgba{text_color.toTuple()};
|
||||
}}
|
||||
QCheckBox::hover{{
|
||||
border-color: rgba{highlight_color.toTuple()};
|
||||
}}
|
||||
QCheckBox::focus{{
|
||||
border-color: rgba{highlight_color.toTuple()};
|
||||
outline: none;
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def colored_radio_button_style(
|
||||
primary_color: QColor,
|
||||
text_color: QColor,
|
||||
border_color: QColor,
|
||||
highlight_color: QColor,
|
||||
) -> str:
|
||||
return f"""
|
||||
QRadioButton{{
|
||||
background: rgba{primary_color.toTuple()};
|
||||
color: rgba{text_color.toTuple()};
|
||||
border-color: rgba{border_color.toTuple()};
|
||||
border-radius: 6px;
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
}}
|
||||
QRadioButton::indicator{{
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 2px;
|
||||
margin: 4px;
|
||||
}}
|
||||
QRadioButton::indicator:checked{{
|
||||
background: rgba{text_color.toTuple()};
|
||||
}}
|
||||
QRadioButton::hover{{
|
||||
border-color: rgba{highlight_color.toTuple()};
|
||||
}}
|
||||
QRadioButton::pressed{{
|
||||
background: rgba{border_color.toTuple()};
|
||||
color: rgba{primary_color.toTuple()};
|
||||
border-color: rgba{primary_color.toTuple()};
|
||||
}}
|
||||
QRadioButton::focus{{
|
||||
border-color: rgba{highlight_color.toTuple()};
|
||||
outline: none;
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def color_swatch_style(
|
||||
primary_color: QColor,
|
||||
text_color: QColor,
|
||||
border_color: QColor,
|
||||
highlight_color: QColor,
|
||||
bottom_color: QColor | None = None,
|
||||
) -> str:
|
||||
"""A style used for color swatches (aka special QRadioButtons)."""
|
||||
bottom_color_str: str = (
|
||||
f"border-bottom-color: rgba{bottom_color.toTuple()};" if bottom_color else ""
|
||||
)
|
||||
|
||||
return f"""
|
||||
QRadioButton{{
|
||||
background: rgba{primary_color.toTuple()};
|
||||
color: rgba{text_color.toTuple()};
|
||||
border-color: rgba{border_color.toTuple()};
|
||||
{bottom_color_str}
|
||||
border-radius: 3px;
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
}}
|
||||
QRadioButton::indicator{{
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 1px;
|
||||
margin: 4px;
|
||||
}}
|
||||
QRadioButton::indicator:checked{{
|
||||
background: rgba{text_color.toTuple()};
|
||||
}}
|
||||
QRadioButton::hover{{
|
||||
border-color: rgba{highlight_color.toTuple()};
|
||||
}}
|
||||
QRadioButton::focus{{
|
||||
outline-style: solid;
|
||||
outline-width: 2px;
|
||||
outline-radius: 3px;
|
||||
outline-color: rgba{highlight_color.toTuple()};
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def container_style() -> str:
|
||||
"""Style used for field containers."""
|
||||
return f"""
|
||||
QWidget#fieldContainer{{
|
||||
border-radius: 4px;
|
||||
}}
|
||||
QWidget#fieldContainer::hover{{
|
||||
background-color: {Theme.COLOR_HOVER.value};
|
||||
}}
|
||||
QWidget#fieldContainer::pressed{{
|
||||
background-color: {Theme.COLOR_PRESSED.value};
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def form_content_style() -> str:
|
||||
return f"""
|
||||
background-color: {
|
||||
Theme.COLOR_BG.value
|
||||
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
|
||||
else Theme.COLOR_BG_LIGHT.value
|
||||
};
|
||||
border-radius: 3px;
|
||||
font-weight: 500;
|
||||
padding: 1px;
|
||||
"""
|
||||
|
||||
|
||||
def line_edit_style() -> str:
|
||||
"""Style used for QLineEdits."""
|
||||
return f"""
|
||||
border: 1px solid {get_ui_color(ColorType.PRIMARY, UiColor.RED)};
|
||||
border-radius: 2px
|
||||
"""
|
||||
|
||||
|
||||
def list_button_style(
|
||||
color: QColor | None = None,
|
||||
border_style: str = "solid",
|
||||
) -> str:
|
||||
"""Style used for special QPushButtons found in lists."""
|
||||
if color is None:
|
||||
color = QColor(get_tag_color(ColorType.PRIMARY, TagColorEnum.DEFAULT))
|
||||
|
||||
highlight_color = get_tag_highlight_color(color)
|
||||
text_color = get_tag_text_color(color, highlight_color)
|
||||
border_color = get_tag_border_color(color)
|
||||
|
||||
return f"""
|
||||
QPushButton{{
|
||||
background: rgba{color.toTuple()};
|
||||
color: rgba{text_color.toTuple()};
|
||||
font-weight: 600;
|
||||
border-color: rgba{border_color.toTuple()};
|
||||
border-radius: 6px;
|
||||
border-style: {border_style};
|
||||
border-width: 2px;
|
||||
padding-right: 4px;
|
||||
padding-bottom: 1px;
|
||||
padding-left: 4px;
|
||||
font-size: 13px
|
||||
}}
|
||||
QPushButton::hover{{
|
||||
border-color: rgba{highlight_color.toTuple()};
|
||||
}}
|
||||
QPushButton::pressed{{
|
||||
background: rgba{highlight_color.toTuple()};
|
||||
color: rgba{color.toTuple()};
|
||||
border-color: rgba{color.toTuple()};
|
||||
}}
|
||||
QPushButton::focus{{
|
||||
padding-right: 0px;
|
||||
padding-left: 0px;
|
||||
outline-style: solid;
|
||||
outline-width: 1px;
|
||||
outline-radius: 4px;
|
||||
outline-color: rgba{text_color.toTuple()};
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def properties_style() -> str:
|
||||
"""Style used for small labels such as file properties."""
|
||||
label_bg_color = (
|
||||
Theme.COLOR_BG_DARK.value
|
||||
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
|
||||
else Theme.COLOR_DARK_LABEL.value
|
||||
)
|
||||
|
||||
return f"""
|
||||
background-color: {label_bg_color};
|
||||
color: #FFFFFF;
|
||||
font-family: Oxanium;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
border-radius: 3px;
|
||||
padding-top: 4px;
|
||||
padding-right: 1px;
|
||||
padding-bottom: 1px;
|
||||
padding-left: 1px;
|
||||
"""
|
||||
|
||||
|
||||
def tag_style(
|
||||
primary_color: QColor,
|
||||
text_color: QColor,
|
||||
border_color: QColor,
|
||||
highlight_color: QColor,
|
||||
border_style: str = "solid",
|
||||
) -> str:
|
||||
"""Style used for TagWidgets."""
|
||||
return f"""
|
||||
QPushButton{{
|
||||
background: rgba{primary_color.toTuple()};
|
||||
color: rgba{text_color.toTuple()};
|
||||
font-weight: 600;
|
||||
border-color: rgba{border_color.toTuple()};
|
||||
border-radius: 6px;
|
||||
border-style: {border_style};
|
||||
border-width: 2px;
|
||||
padding-right: 4px;
|
||||
padding-left: 4px;
|
||||
font-size: 13px
|
||||
}}
|
||||
QPushButton::hover{{
|
||||
border-color: rgba{highlight_color.toTuple()};
|
||||
}}
|
||||
QPushButton::pressed{{
|
||||
background: rgba{highlight_color.toTuple()};
|
||||
color: rgba{primary_color.toTuple()};
|
||||
border-color: rgba{primary_color.toTuple()};
|
||||
}}
|
||||
QPushButton::focus{{
|
||||
padding-right: 0px;
|
||||
padding-left: 0px;
|
||||
outline-style: solid;
|
||||
outline-width: 1px;
|
||||
outline-radius: 4px;
|
||||
outline-color: rgba{text_color.toTuple()};
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def tag_remove_button_style(
|
||||
primary_color: QColor, text_color: QColor, border_color: QColor, highlight_color: QColor
|
||||
) -> str:
|
||||
"""Style used for "Remove" buttons on TagWidgets [-]."""
|
||||
return f"""
|
||||
QPushButton{{
|
||||
color: rgba{primary_color.toTuple()};
|
||||
background: rgba{text_color.toTuple()};
|
||||
font-weight: 800;
|
||||
border-radius: 5px;
|
||||
border-width: 4;
|
||||
border-color: rgba(0,0,0,0);
|
||||
padding-bottom: 4px;
|
||||
font-size: 14px
|
||||
}}
|
||||
QPushButton::hover{{
|
||||
background: rgba{primary_color.toTuple()};
|
||||
color: rgba{text_color.toTuple()};
|
||||
border-color: rgba{highlight_color.toTuple()};
|
||||
border-width: 2;
|
||||
border-radius: 6px;
|
||||
}}
|
||||
QPushButton::pressed{{
|
||||
background: rgba{border_color.toTuple()};
|
||||
color: rgba{highlight_color.toTuple()};
|
||||
}}
|
||||
QPushButton::focus{{
|
||||
background: rgba{border_color.toTuple()};
|
||||
outline: none;
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def title_line_edit_style() -> str:
|
||||
"""Used to mimic an H3-like header style inside a QLineEdit."""
|
||||
return """
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
"""
|
||||
|
||||
|
||||
def header(string: str, level: int, color: str | None = None) -> str:
|
||||
"""Wrap a string in HTML header tags.
|
||||
|
||||
Args:
|
||||
string (str): The string to format.
|
||||
level (int): A value between 1 and 6 denoting the header level.
|
||||
For example, "1" will create <h1> tags, "6" will create <h6> tags, etc.
|
||||
color: Optional color string to pass as an inline HTML style.
|
||||
"""
|
||||
if level < 1:
|
||||
level = 1
|
||||
elif level > 6:
|
||||
level = 6
|
||||
|
||||
style_tag: str = ""
|
||||
if color is not None:
|
||||
style_tag = f" style='color: {color}'"
|
||||
|
||||
return f"<h{level}{style_tag}>{string}</h{level}>"
|
||||
|
||||
|
||||
def get_tag_primary_color(tag: Tag) -> QColor:
|
||||
primary_color = QColor(
|
||||
get_tag_color(ColorType.PRIMARY, TagColorEnum.DEFAULT)
|
||||
if not tag.color
|
||||
else tag.color.primary
|
||||
)
|
||||
|
||||
return primary_color
|
||||
|
||||
|
||||
def get_tag_border_color(primary_color: QColor) -> QColor:
|
||||
border_color: QColor = QColor(primary_color)
|
||||
border_color.setRed(min(border_color.red() + 20, 255))
|
||||
border_color.setGreen(min(border_color.green() + 20, 255))
|
||||
border_color.setBlue(min(border_color.blue() + 20, 255))
|
||||
|
||||
return border_color
|
||||
|
||||
|
||||
def get_tag_highlight_color(primary_color: QColor) -> QColor:
|
||||
highlight_color: QColor = QColor(primary_color)
|
||||
highlight_color = highlight_color.toHsl()
|
||||
highlight_color.setHsl(highlight_color.hue(), min(highlight_color.saturation(), 200), 225, 255)
|
||||
highlight_color = highlight_color.toRgb()
|
||||
|
||||
return highlight_color
|
||||
|
||||
|
||||
def get_tag_text_color(primary_color: QColor, highlight_color: QColor) -> QColor:
|
||||
if primary_color.lightness() > 120:
|
||||
text_color = QColor(primary_color)
|
||||
text_color = text_color.toHsl()
|
||||
text_color.setHsl(text_color.hue(), text_color.saturation(), 50, 255)
|
||||
return text_color.toRgb()
|
||||
else:
|
||||
return highlight_color
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
"about.license": "License",
|
||||
"about.module.found": "Found",
|
||||
"about.title": "About TagStudio",
|
||||
"about.version": "Version",
|
||||
"about.version.latest": "{built_version} (Latest Release: {latest_version})",
|
||||
"about.website": "Website",
|
||||
"app.git": "Git Commit",
|
||||
"app.pre_release": "Pre-Release",
|
||||
@@ -352,6 +354,7 @@
|
||||
"tag.parent_tags": "Parent Tags",
|
||||
"tag.parent_tags.add": "Add Parent Tag(s)",
|
||||
"tag.parent_tags.description": "This tag can be treated as a substitute for any of these Parent Tags in searches.",
|
||||
"tag.properties": "Properties",
|
||||
"tag.remove": "Remove Tag",
|
||||
"tag.search_for_tag": "Search for Tag",
|
||||
"tag.shorthand": "Shorthand",
|
||||
|
||||
Reference in New Issue
Block a user