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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/tagstudio/qt/ts_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
from tagstudio.qt.utils.function_iterator import FunctionIterator
from tagstudio.qt.views.main_window import MainWindow
from tagstudio.qt.views.panel_modal import PanelModal
from tagstudio.qt.views.search_syntax_modal import SearchSyntaxModal
from tagstudio.qt.views.splash import SplashScreen

BADGE_TAGS = {
Expand Down Expand Up @@ -186,6 +187,7 @@ class QtDriver(DriverMixin, QObject):
add_tag_modal: PanelModal | None = None
folders_modal: FoldersToTagsModal
about_modal: AboutModal
search_syntax_modal: SearchSyntaxModal
unlinked_modal: FixUnlinkedEntriesModal
ignored_modal: FixIgnoredEntriesModal
dupe_modal: FixDupeFilesModal
Expand Down Expand Up @@ -557,6 +559,15 @@ def create_about_modal():

self.main_window.menu_bar.about_action.triggered.connect(create_about_modal)

def create_search_syntax_cheatsheet_modal():
if not hasattr(self, "search_syntax_modal"):
self.search_syntax_modal = SearchSyntaxModal(self)
self.search_syntax_modal.show()

self.main_window.menu_bar.search_syntax_action.triggered.connect(
create_search_syntax_cheatsheet_modal
)

# endregion

# endregion
Expand Down
4 changes: 4 additions & 0 deletions src/tagstudio/qt/views/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class MainMenuBar(QMenuBar):

help_menu: QMenu
about_action: QAction
search_syntax_action: QAction

def __init__(self, parent: QWidget | None = None):
super().__init__(parent)
Expand Down Expand Up @@ -398,6 +399,9 @@ def setup_help_menu(self):
self.about_action = QAction(Translations["menu.help.about"], self)
self.help_menu.addAction(self.about_action)

self.search_syntax_action = QAction(Translations["menu.help.search_syntax"], self)
self.help_menu.addAction(self.search_syntax_action)

assign_mnemonics(self.help_menu)
self.addMenu(self.help_menu)

Expand Down
32 changes: 32 additions & 0 deletions src/tagstudio/qt/views/markdown_widget_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from pathlib import Path
from typing import TYPE_CHECKING

from PySide6.QtCore import Qt
from PySide6.QtWidgets import QLabel

if TYPE_CHECKING:
from tagstudio.qt.ts_qt import QtDriver


MARKDOWN_RESOURCE_PATH = Path(Path(__file__).parents[2] / "resources" / "markdown")
FALLBACK_LANGUAGE: str = "en"


class MarkdownWidgetView(QLabel):
def __init__(self, driver: "QtDriver"):
super().__init__()
self.__driver = driver

self.setTextFormat(Qt.TextFormat.MarkdownText)

def set_file(self, file_path: Path):
current_language: str = self.__driver.settings.language

try:
with open(Path(MARKDOWN_RESOURCE_PATH / current_language / file_path)) as markdown_file:
self.setText(markdown_file.read())
except FileNotFoundError:
with open(
Path(MARKDOWN_RESOURCE_PATH / FALLBACK_LANGUAGE / file_path)
) as markdown_file:
self.setText(markdown_file.read())
50 changes: 50 additions & 0 deletions src/tagstudio/qt/views/search_syntax_modal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from pathlib import Path
from typing import TYPE_CHECKING

from PySide6.QtCore import Qt
from PySide6.QtWidgets import QFrame, QHBoxLayout, QPushButton, QScrollArea, QVBoxLayout, QWidget

from tagstudio.qt.translations import Translations
from tagstudio.qt.views.markdown_widget_view import MarkdownWidgetView

if TYPE_CHECKING:
from tagstudio.qt.ts_qt import QtDriver


class SearchSyntaxModal(QWidget):
def __init__(self, driver: "QtDriver"):
super().__init__()

# Modal
self.setWindowTitle(Translations["search_syntax.title"])

self.setWindowModality(Qt.WindowModality.ApplicationModal)
self.setMinimumSize(320, 720)
self.__root_layout = QVBoxLayout(self)
self.__root_layout.setSpacing(0)
self.__root_layout.setAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignCenter)

# Scroll
self.__scroll_area = QScrollArea()
self.__scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
self.__scroll_area.setWidgetResizable(True)
self.__scroll_area.setFrameShadow(QFrame.Shadow.Plain)
self.__scroll_area.setFrameShape(QFrame.Shape.NoFrame)
self.__root_layout.addWidget(self.__scroll_area)

# Content
self.__content: MarkdownWidgetView = MarkdownWidgetView(driver)
self.__content.set_file(Path("search_syntax_cheatsheet.md"))
self.__content.setWordWrap(True)
self.__scroll_area.setWidget(self.__content)

# Close button
self.__buttons_row = QWidget()
self.__buttons_row_layout = QHBoxLayout(self.__buttons_row)
self.__buttons_row_layout.setContentsMargins(12, 12, 12, 12)
self.__buttons_row_layout.addStretch(1)
self.__root_layout.addWidget(self.__buttons_row)

self.__close_button = QPushButton(Translations["generic.close"])
self.__close_button.clicked.connect(lambda: self.close())
self.__buttons_row_layout.addWidget(self.__close_button)
62 changes: 62 additions & 0 deletions src/tagstudio/resources/markdown/en/search_syntax_cheatsheet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Search Syntax
For a more detailed explanation of search syntax, check [the documentation](https://docs.tagstud.io/search/).

## Boolean Operators

Search for entries that have Tag1 **AND** Tag2
- Tag1 Tag2
- Tag1 `AND` Tag2

Search for entries that have Tag1 **OR** Tag2
- Tag1 `OR` Tag2

Search for entries that **don't** have Tag1
- `NOT` Tag1

Searches can be grouped and nested by using parentheses to surround parts of your search query.
- (Tag1 `OR` Tag2) `AND` Tag3

## Escaping Characters

To escape most search terms, surround the section of your search in plain quotes or replace spaces in tag names with underscores.
- "Tag Name With Spaces"
- Tag_Name_With_Spaces

## Tags

Search for entries that have the tag Tag1
- Tag1
- tag: Tag1

Search for entries that have the tag with ID 1
- tag_id: 1

## Path

Search for a file named `file.jpg`
- path: file.jpg

Search for any `.jpg` file
- path: *.jpg

Search for any file that ends in a number
- path: \*2.\*

Search for a file located at `folder/file.jpg`
- path: folder/file.jpg

Search for any file inside `folder/`
- path: folder/*

## File/Media Type

Search for videos
- mediatype: video

Search for `.mp4` files
- filetype: mp4

## Special Searches

Search for entries that don't contain any tags
- special: untagged
2 changes: 2 additions & 0 deletions src/tagstudio/resources/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@
"menu.file.save_library": "Save Library",
"menu.file": "&File",
"menu.help.about": "About",
"menu.help.search_syntax": "Search Syntax Cheatsheet",
"menu.help": "&Help",
"menu.macros.folders_to_tags": "Folders to Tags",
"menu.macros": "&Macros",
Expand All @@ -251,6 +252,7 @@
"preview.multiple_selection": "<b>{count}</b> Items Selected",
"preview.no_selection": "No Items Selected",
"preview.unlinked": "Unlinked",
"search_syntax.title": "Search Syntax Cheatsheet",
"select.add_tag_to_selected": "Add Tag to Selected",
"select.all": "Select All",
"select.clear": "Clear Selection",
Expand Down