Skip to content

Commit f680ecb

Browse files
HermanKasslerRubenSochaErzaDuNordZarko SestobitGatito
authored
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>
1 parent 234ddec commit f680ecb

File tree

6 files changed

+232
-10
lines changed

6 files changed

+232
-10
lines changed

src/tagstudio/core/enums.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,21 @@ class SettingItems(str, enum.Enum):
1515
LIBS_LIST = "libs_list"
1616
WINDOW_SHOW_LIBS = "window_show_libs"
1717
SHOW_FILENAMES = "show_filenames"
18+
SHOW_FILEPATH = "show_filepath"
1819
AUTOPLAY = "autoplay_videos"
1920
THUMB_CACHE_SIZE_LIMIT = "thumb_cache_size_limit"
2021
LANGUAGE = "language"
2122

2223

24+
class ShowFilepathOption(int, enum.Enum):
25+
"""Values representing the options for the "show_filenames" setting."""
26+
27+
SHOW_FULL_PATHS = 0
28+
SHOW_RELATIVE_PATHS = 1
29+
SHOW_FILENAMES_ONLY = 2
30+
DEFAULT = SHOW_RELATIVE_PATHS
31+
32+
2333
class Theme(str, enum.Enum):
2434
COLOR_BG_DARK = "#65000000"
2535
COLOR_BG_LIGHT = "#22000000"

src/tagstudio/qt/modals/settings_panel.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from PySide6.QtCore import Qt
77
from PySide6.QtWidgets import QComboBox, QFormLayout, QLabel, QVBoxLayout, QWidget
88

9-
from tagstudio.core.enums import SettingItems
9+
from tagstudio.core.enums import SettingItems, ShowFilepathOption
1010
from tagstudio.qt.translations import Translations
1111
from tagstudio.qt.widgets.panel import PanelWidget
1212

@@ -63,10 +63,45 @@ def __init__(self, driver):
6363
)
6464
self.form_layout.addRow(language_label, self.language_combobox)
6565

66+
filepath_option_map: dict[int, str] = {
67+
ShowFilepathOption.SHOW_FULL_PATHS: Translations["settings.filepath.option.full"],
68+
ShowFilepathOption.SHOW_RELATIVE_PATHS: Translations[
69+
"settings.filepath.option.relative"
70+
],
71+
ShowFilepathOption.SHOW_FILENAMES_ONLY: Translations["settings.filepath.option.name"],
72+
}
73+
self.filepath_combobox = QComboBox()
74+
self.filepath_combobox.addItems(list(filepath_option_map.values()))
75+
filepath_option: int = int(
76+
driver.settings.value(
77+
SettingItems.SHOW_FILEPATH, defaultValue=ShowFilepathOption.DEFAULT.value, type=int
78+
)
79+
)
80+
filepath_option = 0 if filepath_option not in filepath_option_map else filepath_option
81+
self.filepath_combobox.setCurrentIndex(filepath_option)
82+
self.filepath_combobox.currentIndexChanged.connect(self.apply_filepath_setting)
83+
self.form_layout.addRow(Translations["settings.filepath.label"], self.filepath_combobox)
84+
6685
self.root_layout.addWidget(self.form_container)
6786
self.root_layout.addStretch(1)
6887
self.root_layout.addWidget(self.restart_label)
6988

7089
def get_language(self) -> str:
7190
values: list[str] = list(self.languages.values())
7291
return values[self.language_combobox.currentIndex()]
92+
93+
def apply_filepath_setting(self):
94+
selected_value = self.filepath_combobox.currentIndex()
95+
self.driver.settings.setValue(SettingItems.SHOW_FILEPATH, selected_value)
96+
self.driver.update_recent_lib_menu()
97+
self.driver.preview_panel.update_widgets()
98+
library_directory = self.driver.lib.library_dir
99+
if selected_value == ShowFilepathOption.SHOW_FULL_PATHS:
100+
display_path = library_directory
101+
else:
102+
display_path = library_directory.name
103+
self.driver.main_window.setWindowTitle(
104+
Translations.format(
105+
"app.title", base_title=self.driver.base_title, library_dir=display_path
106+
)
107+
)

src/tagstudio/qt/ts_qt.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
import tagstudio.qt.resources_rc # noqa: F401
5555
from tagstudio.core.constants import TAG_ARCHIVED, TAG_FAVORITE, VERSION, VERSION_BRANCH
5656
from tagstudio.core.driver import DriverMixin
57-
from tagstudio.core.enums import LibraryPrefs, MacroID, SettingItems
57+
from tagstudio.core.enums import LibraryPrefs, MacroID, SettingItems, ShowFilepathOption
5858
from tagstudio.core.library.alchemy.enums import (
5959
FieldTypeEnum,
6060
FilterState,
@@ -1749,6 +1749,11 @@ def update_recent_lib_menu(self):
17491749
"""Updates the recent library menu from the latest values from the settings file."""
17501750
actions: list[QAction] = []
17511751
lib_items: dict[str, tuple[str, str]] = {}
1752+
filepath_option: int = int(
1753+
self.settings.value(
1754+
SettingItems.SHOW_FILEPATH, defaultValue=ShowFilepathOption.DEFAULT.value, type=int
1755+
)
1756+
)
17521757

17531758
settings = self.settings
17541759
settings.beginGroup(SettingItems.LIBS_LIST)
@@ -1767,7 +1772,10 @@ def update_recent_lib_menu(self):
17671772
for library_key in libs_sorted:
17681773
path = Path(library_key[1][0])
17691774
action = QAction(self.open_recent_library_menu)
1770-
action.setText(str(path))
1775+
if filepath_option == ShowFilepathOption.SHOW_FULL_PATHS:
1776+
action.setText(str(path))
1777+
else:
1778+
action.setText(str(Path(path).name))
17711779
action.triggered.connect(lambda checked=False, p=path: self.open_library(p))
17721780
actions.append(action)
17731781

@@ -1822,7 +1830,15 @@ def update_language_settings(self, language: str):
18221830

18231831
def open_library(self, path: Path) -> None:
18241832
"""Open a TagStudio library."""
1825-
message = Translations.format("splash.opening_library", library_path=str(path))
1833+
filepath_option: int = int(
1834+
self.settings.value(
1835+
SettingItems.SHOW_FILEPATH, defaultValue=ShowFilepathOption.DEFAULT.value, type=int
1836+
)
1837+
)
1838+
library_dir_display = (
1839+
path if filepath_option == ShowFilepathOption.SHOW_FULL_PATHS else path.name
1840+
)
1841+
message = Translations.format("splash.opening_library", library_path=library_dir_display)
18261842
self.main_window.landing_widget.set_status_label(message)
18271843
self.main_window.statusbar.showMessage(message, 3)
18281844
self.main_window.repaint()
@@ -1867,12 +1883,23 @@ def init_library(self, path: Path, open_status: LibraryStatus):
18671883
if self.lib.entries_count < 10000:
18681884
self.add_new_files_callback()
18691885

1886+
library_dir_display = self.lib.library_dir
1887+
filepath_option: int = int(
1888+
self.settings.value(
1889+
SettingItems.SHOW_FILEPATH, defaultValue=ShowFilepathOption.DEFAULT.value, type=int
1890+
)
1891+
)
1892+
if filepath_option == ShowFilepathOption.SHOW_FULL_PATHS:
1893+
library_dir_display = self.lib.library_dir
1894+
else:
1895+
library_dir_display = self.lib.library_dir.name
1896+
18701897
self.update_libs_list(path)
18711898
self.main_window.setWindowTitle(
18721899
Translations.format(
18731900
"app.title",
18741901
base_title=self.base_title,
1875-
library_dir=self.lib.library_dir,
1902+
library_dir=library_dir_display,
18761903
)
18771904
)
18781905
self.main_window.setAcceptDrops(True)

src/tagstudio/qt/widgets/preview/file_attributes.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from PySide6.QtGui import QGuiApplication
1818
from PySide6.QtWidgets import QLabel, QVBoxLayout, QWidget
1919

20-
from tagstudio.core.enums import Theme
20+
from tagstudio.core.enums import SettingItems, ShowFilepathOption, Theme
2121
from tagstudio.core.library.alchemy.library import Library
2222
from tagstudio.core.media_types import MediaCategories
2323
from tagstudio.qt.helpers.file_opener import FileOpenerHelper, FileOpenerLabel
@@ -96,6 +96,8 @@ def __init__(self, library: Library, driver: "QtDriver"):
9696
root_layout.addWidget(self.file_label)
9797
root_layout.addWidget(self.date_container)
9898
root_layout.addWidget(self.dimensions_label)
99+
self.library = library
100+
self.driver = driver
99101

100102
def update_date_label(self, filepath: Path | None = None) -> None:
101103
"""Update the "Date Created" and "Date Modified" file property labels."""
@@ -142,19 +144,33 @@ def update_stats(self, filepath: Path | None = None, ext: str = ".", stats: dict
142144
self.dimensions_label.setText("")
143145
self.dimensions_label.setHidden(True)
144146
else:
147+
filepath_option = self.driver.settings.value(
148+
SettingItems.SHOW_FILEPATH, defaultValue=ShowFilepathOption.DEFAULT.value, type=int
149+
)
150+
self.library_path = self.library.library_dir
151+
display_path = filepath
152+
if filepath_option == ShowFilepathOption.SHOW_FULL_PATHS:
153+
display_path = filepath
154+
elif filepath_option == ShowFilepathOption.SHOW_RELATIVE_PATHS:
155+
display_path = Path(filepath).relative_to(self.library_path)
156+
elif filepath_option == ShowFilepathOption.SHOW_FILENAMES_ONLY:
157+
display_path = Path(filepath.name)
158+
145159
self.layout().setSpacing(6)
146160
self.file_label.setAlignment(Qt.AlignmentFlag.AlignLeft)
147161
self.file_label.set_file_path(filepath)
148162
self.dimensions_label.setHidden(False)
149163

150164
file_str: str = ""
151165
separator: str = f"<a style='color: #777777'><b>{os.path.sep}</a>" # Gray
152-
for i, part in enumerate(filepath.parts):
166+
for i, part in enumerate(display_path.parts):
153167
part_ = part.strip(os.path.sep)
154-
if i != len(filepath.parts) - 1:
155-
file_str += f"{'\u200b'.join(part_)}{separator}</b>"
168+
if i != len(display_path.parts) - 1:
169+
file_str += f"{"\u200b".join(part_)}{separator}</b>"
156170
else:
157-
file_str += f"<br><b>{'\u200b'.join(part_)}</b>"
171+
if file_str != "":
172+
file_str += "<br>"
173+
file_str += f"<b>{"\u200b".join(part_)}</b>"
158174
self.file_label.setText(file_str)
159175
self.file_label.setCursor(Qt.CursorShape.PointingHandCursor)
160176
self.opener = FileOpenerHelper(filepath)

src/tagstudio/resources/translations/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@
223223
"select.all": "Select All",
224224
"select.clear": "Clear Selection",
225225
"settings.clear_thumb_cache.title": "Clear Thumbnail Cache",
226+
"settings.filepath.label": "Filepath Visibility",
227+
"settings.filepath.option.full": "Show Full Paths",
228+
"settings.filepath.option.name": "Show Filenames Only",
229+
"settings.filepath.option.relative": "Show Relative Paths",
226230
"settings.language": "Language",
227231
"settings.open_library_on_start": "Open Library on Start",
228232
"settings.restart_required": "Please restart TagStudio for changes to take effect.",

tests/qt/test_file_path_options.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import os
2+
import pathlib
3+
from unittest.mock import patch
4+
5+
import pytest
6+
from PySide6.QtGui import (
7+
QAction,
8+
)
9+
from PySide6.QtWidgets import QMenu, QMenuBar
10+
11+
from tagstudio.core.enums import SettingItems, ShowFilepathOption
12+
from tagstudio.core.library.alchemy.library import LibraryStatus
13+
from tagstudio.qt.modals.settings_panel import SettingsPanel
14+
from tagstudio.qt.widgets.preview_panel import PreviewPanel
15+
16+
17+
# Tests to see if the file path setting is applied correctly
18+
@pytest.mark.parametrize(
19+
"filepath_option",
20+
[
21+
ShowFilepathOption.SHOW_FULL_PATHS.value,
22+
ShowFilepathOption.SHOW_RELATIVE_PATHS.value,
23+
ShowFilepathOption.SHOW_FILENAMES_ONLY.value,
24+
],
25+
)
26+
def test_filepath_setting(qtbot, qt_driver, filepath_option):
27+
settings_panel = SettingsPanel(qt_driver)
28+
qtbot.addWidget(settings_panel)
29+
30+
# Mock the update_recent_lib_menu method
31+
with patch.object(qt_driver, "update_recent_lib_menu", return_value=None):
32+
# Set the file path option
33+
settings_panel.filepath_combobox.setCurrentIndex(filepath_option)
34+
settings_panel.apply_filepath_setting()
35+
36+
# Assert the setting is applied
37+
assert qt_driver.settings.value(SettingItems.SHOW_FILEPATH) == filepath_option
38+
39+
40+
# Tests to see if the file paths are being displayed correctly
41+
@pytest.mark.parametrize(
42+
"filepath_option, expected_path",
43+
[
44+
(
45+
ShowFilepathOption.SHOW_FULL_PATHS,
46+
lambda library: pathlib.Path(library.library_dir / "one/two/bar.md"),
47+
),
48+
(ShowFilepathOption.SHOW_RELATIVE_PATHS, lambda library: pathlib.Path("one/two/bar.md")),
49+
(ShowFilepathOption.SHOW_FILENAMES_ONLY, lambda library: pathlib.Path("bar.md")),
50+
],
51+
)
52+
def test_file_path_display(qt_driver, library, filepath_option, expected_path):
53+
panel = PreviewPanel(library, qt_driver)
54+
55+
# Select 2
56+
qt_driver.toggle_item_selection(2, append=False, bridge=False)
57+
panel.update_widgets()
58+
59+
with patch.object(qt_driver.settings, "value", return_value=filepath_option):
60+
# Apply the mock value
61+
filename = library.get_entry(2).path
62+
panel.file_attrs.update_stats(filepath=pathlib.Path(library.library_dir / filename))
63+
64+
# Generate the expected file string.
65+
# This is copied directly from the file_attributes.py file
66+
# can be imported as a function in the future
67+
display_path = expected_path(library)
68+
file_str: str = ""
69+
separator: str = f"<a style='color: #777777'><b>{os.path.sep}</a>" # Gray
70+
for i, part in enumerate(display_path.parts):
71+
part_ = part.strip(os.path.sep)
72+
if i != len(display_path.parts) - 1:
73+
file_str += f"{"\u200b".join(part_)}{separator}</b>"
74+
else:
75+
if file_str != "":
76+
file_str += "<br>"
77+
file_str += f"<b>{"\u200b".join(part_)}</b>"
78+
79+
# Assert the file path is displayed correctly
80+
assert panel.file_attrs.file_label.text() == file_str
81+
82+
83+
@pytest.mark.parametrize(
84+
"filepath_option, expected_title",
85+
[
86+
(
87+
ShowFilepathOption.SHOW_FULL_PATHS.value,
88+
lambda path, base_title: f"{base_title} - Library '{path}'",
89+
),
90+
(
91+
ShowFilepathOption.SHOW_RELATIVE_PATHS.value,
92+
lambda path, base_title: f"{base_title} - Library '{path.name}'",
93+
),
94+
(
95+
ShowFilepathOption.SHOW_FILENAMES_ONLY.value,
96+
lambda path, base_title: f"{base_title} - Library '{path.name}'",
97+
),
98+
],
99+
)
100+
def test_title_update(qtbot, qt_driver, filepath_option, expected_title):
101+
base_title = qt_driver.base_title
102+
test_path = pathlib.Path("/dev/null")
103+
open_status = LibraryStatus(
104+
success=True,
105+
library_path=test_path,
106+
message="",
107+
msg_description="",
108+
)
109+
# Set the file path option
110+
qt_driver.settings.setValue(SettingItems.SHOW_FILEPATH, filepath_option)
111+
menu_bar = QMenuBar()
112+
113+
qt_driver.open_recent_library_menu = QMenu(menu_bar)
114+
qt_driver.manage_file_ext_action = QAction(menu_bar)
115+
qt_driver.save_library_backup_action = QAction(menu_bar)
116+
qt_driver.close_library_action = QAction(menu_bar)
117+
qt_driver.refresh_dir_action = QAction(menu_bar)
118+
qt_driver.tag_manager_action = QAction(menu_bar)
119+
qt_driver.color_manager_action = QAction(menu_bar)
120+
qt_driver.new_tag_action = QAction(menu_bar)
121+
qt_driver.fix_dupe_files_action = QAction(menu_bar)
122+
qt_driver.fix_unlinked_entries_action = QAction(menu_bar)
123+
qt_driver.clear_thumb_cache_action = QAction(menu_bar)
124+
qt_driver.folders_to_tags_action = QAction(menu_bar)
125+
126+
# Trigger the update
127+
qt_driver.init_library(pathlib.Path(test_path), open_status)
128+
129+
# Assert the title is updated correctly
130+
qt_driver.main_window.setWindowTitle.assert_called_with(expected_title(test_path, base_title))

0 commit comments

Comments
 (0)