feat: add setting to not display full filepath (#841)

* feat: add show file path option to settings menu (#4)

* feat: implement file path option for file attributes (#10)

* feat: implement file path option for file attributes

---------

Co-authored-by: Zarko Sesto <sesto@kth.se>

* feat: update ui after changing file path setting (#9)

* feat: implement file path options for main window (#11)

Co-authored-by: Zarko Sesto <sesto@kth.se>

* feat: add realtime feedback for setting changes (#13)

Co-authored-by: Zarko Sesto <sesto@kth.se>

style: formatted code changes with ruff  (#16)

* tests: Added tests to test file path display and settings menu

enhancement: formated using RUFF

test: addeded test to check title bar update

* fix: move check for file option to correct spot in open_library (#17)

* fix: change translate_with_setter to new method (#18)

* fix: update call to translator to be inline with upstream (#19)

* refactor: update some imports to reflect refactor on upstream (#20)

* refactor: update import paths to reflect recent reformat

* format: run ruff format

* translation: add translations for filepath setting

* refactor: store filepath options in integer enum

---------

Co-authored-by: RubenSocha <40490633+RubenSocha@users.noreply.github.com>
Co-authored-by: ErzaDuNord <102242407+ErzaDuNord@users.noreply.github.com>
Co-authored-by: Zarko Sesto <sesto@kth.se>
Co-authored-by: BitGatito <usmanbinmujeeb@gmail.com>
This commit is contained in:
HermanKassler
2025-03-12 23:51:55 +01:00
committed by GitHub
parent 234ddec78b
commit f680ecb648
6 changed files with 232 additions and 10 deletions

View File

@@ -15,11 +15,21 @@ class SettingItems(str, enum.Enum):
LIBS_LIST = "libs_list"
WINDOW_SHOW_LIBS = "window_show_libs"
SHOW_FILENAMES = "show_filenames"
SHOW_FILEPATH = "show_filepath"
AUTOPLAY = "autoplay_videos"
THUMB_CACHE_SIZE_LIMIT = "thumb_cache_size_limit"
LANGUAGE = "language"
class ShowFilepathOption(int, enum.Enum):
"""Values representing the options for the "show_filenames" setting."""
SHOW_FULL_PATHS = 0
SHOW_RELATIVE_PATHS = 1
SHOW_FILENAMES_ONLY = 2
DEFAULT = SHOW_RELATIVE_PATHS
class Theme(str, enum.Enum):
COLOR_BG_DARK = "#65000000"
COLOR_BG_LIGHT = "#22000000"

View File

@@ -6,7 +6,7 @@
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QComboBox, QFormLayout, QLabel, QVBoxLayout, QWidget
from tagstudio.core.enums import SettingItems
from tagstudio.core.enums import SettingItems, ShowFilepathOption
from tagstudio.qt.translations import Translations
from tagstudio.qt.widgets.panel import PanelWidget
@@ -63,6 +63,25 @@ class SettingsPanel(PanelWidget):
)
self.form_layout.addRow(language_label, self.language_combobox)
filepath_option_map: dict[int, str] = {
ShowFilepathOption.SHOW_FULL_PATHS: Translations["settings.filepath.option.full"],
ShowFilepathOption.SHOW_RELATIVE_PATHS: Translations[
"settings.filepath.option.relative"
],
ShowFilepathOption.SHOW_FILENAMES_ONLY: Translations["settings.filepath.option.name"],
}
self.filepath_combobox = QComboBox()
self.filepath_combobox.addItems(list(filepath_option_map.values()))
filepath_option: int = int(
driver.settings.value(
SettingItems.SHOW_FILEPATH, defaultValue=ShowFilepathOption.DEFAULT.value, type=int
)
)
filepath_option = 0 if filepath_option not in filepath_option_map else filepath_option
self.filepath_combobox.setCurrentIndex(filepath_option)
self.filepath_combobox.currentIndexChanged.connect(self.apply_filepath_setting)
self.form_layout.addRow(Translations["settings.filepath.label"], self.filepath_combobox)
self.root_layout.addWidget(self.form_container)
self.root_layout.addStretch(1)
self.root_layout.addWidget(self.restart_label)
@@ -70,3 +89,19 @@ class SettingsPanel(PanelWidget):
def get_language(self) -> str:
values: list[str] = list(self.languages.values())
return values[self.language_combobox.currentIndex()]
def apply_filepath_setting(self):
selected_value = self.filepath_combobox.currentIndex()
self.driver.settings.setValue(SettingItems.SHOW_FILEPATH, selected_value)
self.driver.update_recent_lib_menu()
self.driver.preview_panel.update_widgets()
library_directory = self.driver.lib.library_dir
if selected_value == ShowFilepathOption.SHOW_FULL_PATHS:
display_path = library_directory
else:
display_path = library_directory.name
self.driver.main_window.setWindowTitle(
Translations.format(
"app.title", base_title=self.driver.base_title, library_dir=display_path
)
)

View File

@@ -54,7 +54,7 @@ from PySide6.QtWidgets import (
import tagstudio.qt.resources_rc # noqa: F401
from tagstudio.core.constants import TAG_ARCHIVED, TAG_FAVORITE, VERSION, VERSION_BRANCH
from tagstudio.core.driver import DriverMixin
from tagstudio.core.enums import LibraryPrefs, MacroID, SettingItems
from tagstudio.core.enums import LibraryPrefs, MacroID, SettingItems, ShowFilepathOption
from tagstudio.core.library.alchemy.enums import (
FieldTypeEnum,
FilterState,
@@ -1749,6 +1749,11 @@ class QtDriver(DriverMixin, QObject):
"""Updates the recent library menu from the latest values from the settings file."""
actions: list[QAction] = []
lib_items: dict[str, tuple[str, str]] = {}
filepath_option: int = int(
self.settings.value(
SettingItems.SHOW_FILEPATH, defaultValue=ShowFilepathOption.DEFAULT.value, type=int
)
)
settings = self.settings
settings.beginGroup(SettingItems.LIBS_LIST)
@@ -1767,7 +1772,10 @@ class QtDriver(DriverMixin, QObject):
for library_key in libs_sorted:
path = Path(library_key[1][0])
action = QAction(self.open_recent_library_menu)
action.setText(str(path))
if filepath_option == ShowFilepathOption.SHOW_FULL_PATHS:
action.setText(str(path))
else:
action.setText(str(Path(path).name))
action.triggered.connect(lambda checked=False, p=path: self.open_library(p))
actions.append(action)
@@ -1822,7 +1830,15 @@ class QtDriver(DriverMixin, QObject):
def open_library(self, path: Path) -> None:
"""Open a TagStudio library."""
message = Translations.format("splash.opening_library", library_path=str(path))
filepath_option: int = int(
self.settings.value(
SettingItems.SHOW_FILEPATH, defaultValue=ShowFilepathOption.DEFAULT.value, type=int
)
)
library_dir_display = (
path if filepath_option == ShowFilepathOption.SHOW_FULL_PATHS else path.name
)
message = Translations.format("splash.opening_library", library_path=library_dir_display)
self.main_window.landing_widget.set_status_label(message)
self.main_window.statusbar.showMessage(message, 3)
self.main_window.repaint()
@@ -1867,12 +1883,23 @@ class QtDriver(DriverMixin, QObject):
if self.lib.entries_count < 10000:
self.add_new_files_callback()
library_dir_display = self.lib.library_dir
filepath_option: int = int(
self.settings.value(
SettingItems.SHOW_FILEPATH, defaultValue=ShowFilepathOption.DEFAULT.value, type=int
)
)
if filepath_option == ShowFilepathOption.SHOW_FULL_PATHS:
library_dir_display = self.lib.library_dir
else:
library_dir_display = self.lib.library_dir.name
self.update_libs_list(path)
self.main_window.setWindowTitle(
Translations.format(
"app.title",
base_title=self.base_title,
library_dir=self.lib.library_dir,
library_dir=library_dir_display,
)
)
self.main_window.setAcceptDrops(True)

View File

@@ -17,7 +17,7 @@ from PySide6.QtCore import Qt
from PySide6.QtGui import QGuiApplication
from PySide6.QtWidgets import QLabel, QVBoxLayout, QWidget
from tagstudio.core.enums import Theme
from tagstudio.core.enums import SettingItems, ShowFilepathOption, Theme
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.media_types import MediaCategories
from tagstudio.qt.helpers.file_opener import FileOpenerHelper, FileOpenerLabel
@@ -96,6 +96,8 @@ class FileAttributes(QWidget):
root_layout.addWidget(self.file_label)
root_layout.addWidget(self.date_container)
root_layout.addWidget(self.dimensions_label)
self.library = library
self.driver = driver
def update_date_label(self, filepath: Path | None = None) -> None:
"""Update the "Date Created" and "Date Modified" file property labels."""
@@ -142,6 +144,18 @@ class FileAttributes(QWidget):
self.dimensions_label.setText("")
self.dimensions_label.setHidden(True)
else:
filepath_option = self.driver.settings.value(
SettingItems.SHOW_FILEPATH, defaultValue=ShowFilepathOption.DEFAULT.value, type=int
)
self.library_path = self.library.library_dir
display_path = filepath
if filepath_option == ShowFilepathOption.SHOW_FULL_PATHS:
display_path = filepath
elif filepath_option == ShowFilepathOption.SHOW_RELATIVE_PATHS:
display_path = Path(filepath).relative_to(self.library_path)
elif filepath_option == ShowFilepathOption.SHOW_FILENAMES_ONLY:
display_path = Path(filepath.name)
self.layout().setSpacing(6)
self.file_label.setAlignment(Qt.AlignmentFlag.AlignLeft)
self.file_label.set_file_path(filepath)
@@ -149,12 +163,14 @@ class FileAttributes(QWidget):
file_str: str = ""
separator: str = f"<a style='color: #777777'><b>{os.path.sep}</a>" # Gray
for i, part in enumerate(filepath.parts):
for i, part in enumerate(display_path.parts):
part_ = part.strip(os.path.sep)
if i != len(filepath.parts) - 1:
file_str += f"{'\u200b'.join(part_)}{separator}</b>"
if i != len(display_path.parts) - 1:
file_str += f"{"\u200b".join(part_)}{separator}</b>"
else:
file_str += f"<br><b>{'\u200b'.join(part_)}</b>"
if file_str != "":
file_str += "<br>"
file_str += f"<b>{"\u200b".join(part_)}</b>"
self.file_label.setText(file_str)
self.file_label.setCursor(Qt.CursorShape.PointingHandCursor)
self.opener = FileOpenerHelper(filepath)

View File

@@ -223,6 +223,10 @@
"select.all": "Select All",
"select.clear": "Clear Selection",
"settings.clear_thumb_cache.title": "Clear Thumbnail Cache",
"settings.filepath.label": "Filepath Visibility",
"settings.filepath.option.full": "Show Full Paths",
"settings.filepath.option.name": "Show Filenames Only",
"settings.filepath.option.relative": "Show Relative Paths",
"settings.language": "Language",
"settings.open_library_on_start": "Open Library on Start",
"settings.restart_required": "Please restart TagStudio for changes to take effect.",

View File

@@ -0,0 +1,130 @@
import os
import pathlib
from unittest.mock import patch
import pytest
from PySide6.QtGui import (
QAction,
)
from PySide6.QtWidgets import QMenu, QMenuBar
from tagstudio.core.enums import SettingItems, ShowFilepathOption
from tagstudio.core.library.alchemy.library import LibraryStatus
from tagstudio.qt.modals.settings_panel import SettingsPanel
from tagstudio.qt.widgets.preview_panel import PreviewPanel
# Tests to see if the file path setting is applied correctly
@pytest.mark.parametrize(
"filepath_option",
[
ShowFilepathOption.SHOW_FULL_PATHS.value,
ShowFilepathOption.SHOW_RELATIVE_PATHS.value,
ShowFilepathOption.SHOW_FILENAMES_ONLY.value,
],
)
def test_filepath_setting(qtbot, qt_driver, filepath_option):
settings_panel = SettingsPanel(qt_driver)
qtbot.addWidget(settings_panel)
# Mock the update_recent_lib_menu method
with patch.object(qt_driver, "update_recent_lib_menu", return_value=None):
# Set the file path option
settings_panel.filepath_combobox.setCurrentIndex(filepath_option)
settings_panel.apply_filepath_setting()
# Assert the setting is applied
assert qt_driver.settings.value(SettingItems.SHOW_FILEPATH) == filepath_option
# Tests to see if the file paths are being displayed correctly
@pytest.mark.parametrize(
"filepath_option, expected_path",
[
(
ShowFilepathOption.SHOW_FULL_PATHS,
lambda library: pathlib.Path(library.library_dir / "one/two/bar.md"),
),
(ShowFilepathOption.SHOW_RELATIVE_PATHS, lambda library: pathlib.Path("one/two/bar.md")),
(ShowFilepathOption.SHOW_FILENAMES_ONLY, lambda library: pathlib.Path("bar.md")),
],
)
def test_file_path_display(qt_driver, library, filepath_option, expected_path):
panel = PreviewPanel(library, qt_driver)
# Select 2
qt_driver.toggle_item_selection(2, append=False, bridge=False)
panel.update_widgets()
with patch.object(qt_driver.settings, "value", return_value=filepath_option):
# Apply the mock value
filename = library.get_entry(2).path
panel.file_attrs.update_stats(filepath=pathlib.Path(library.library_dir / filename))
# Generate the expected file string.
# This is copied directly from the file_attributes.py file
# can be imported as a function in the future
display_path = expected_path(library)
file_str: str = ""
separator: str = f"<a style='color: #777777'><b>{os.path.sep}</a>" # Gray
for i, part in enumerate(display_path.parts):
part_ = part.strip(os.path.sep)
if i != len(display_path.parts) - 1:
file_str += f"{"\u200b".join(part_)}{separator}</b>"
else:
if file_str != "":
file_str += "<br>"
file_str += f"<b>{"\u200b".join(part_)}</b>"
# Assert the file path is displayed correctly
assert panel.file_attrs.file_label.text() == file_str
@pytest.mark.parametrize(
"filepath_option, expected_title",
[
(
ShowFilepathOption.SHOW_FULL_PATHS.value,
lambda path, base_title: f"{base_title} - Library '{path}'",
),
(
ShowFilepathOption.SHOW_RELATIVE_PATHS.value,
lambda path, base_title: f"{base_title} - Library '{path.name}'",
),
(
ShowFilepathOption.SHOW_FILENAMES_ONLY.value,
lambda path, base_title: f"{base_title} - Library '{path.name}'",
),
],
)
def test_title_update(qtbot, qt_driver, filepath_option, expected_title):
base_title = qt_driver.base_title
test_path = pathlib.Path("/dev/null")
open_status = LibraryStatus(
success=True,
library_path=test_path,
message="",
msg_description="",
)
# Set the file path option
qt_driver.settings.setValue(SettingItems.SHOW_FILEPATH, filepath_option)
menu_bar = QMenuBar()
qt_driver.open_recent_library_menu = QMenu(menu_bar)
qt_driver.manage_file_ext_action = QAction(menu_bar)
qt_driver.save_library_backup_action = QAction(menu_bar)
qt_driver.close_library_action = QAction(menu_bar)
qt_driver.refresh_dir_action = QAction(menu_bar)
qt_driver.tag_manager_action = QAction(menu_bar)
qt_driver.color_manager_action = QAction(menu_bar)
qt_driver.new_tag_action = QAction(menu_bar)
qt_driver.fix_dupe_files_action = QAction(menu_bar)
qt_driver.fix_unlinked_entries_action = QAction(menu_bar)
qt_driver.clear_thumb_cache_action = QAction(menu_bar)
qt_driver.folders_to_tags_action = QAction(menu_bar)
# Trigger the update
qt_driver.init_library(pathlib.Path(test_path), open_status)
# Assert the title is updated correctly
qt_driver.main_window.setWindowTitle.assert_called_with(expected_title(test_path, base_title))