This commit is contained in:
Ignacio Serantes
2026-03-25 22:02:13 +01:00
parent 56ef674d4a
commit dfddfd17b3
10 changed files with 430 additions and 53 deletions

View File

@@ -12,6 +12,7 @@ including:
import os
import glob
import shutil
import json
import lmdb
from datetime import datetime
from collections import deque
@@ -20,11 +21,11 @@ from PySide6.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton,
QMessageBox, QSizePolicy, QInputDialog, QTableWidget, QTableWidgetItem,
QMenu, QHeaderView, QAbstractItemView, QTreeView, QLabel, QTextEdit,
QComboBox, QCompleter, QToolBar
QComboBox, QCompleter, QToolBar, QDialog
)
from PySide6.QtGui import (
QIcon, QStandardItemModel, QStandardItem, QColor, QPainter, QPen,
QPalette, QAction,
QPalette, QAction, QKeySequence
)
from PySide6.QtCore import (
Signal, QSortFilterProxyModel, Slot, QStringListModel, Qt
@@ -33,7 +34,7 @@ from PySide6.QtCore import (
from metadatamanager import XattrManager
from constants import (
LAYOUTS_DIR, RATING_XATTR_NAME, XATTR_COMMENT_NAME, XATTR_NAME, UITexts,
FACES_MENU_MAX_ITEMS_DEFAULT, APP_CONFIG
FACES_MENU_MAX_ITEMS_DEFAULT, APP_CONFIG, FAVORITES_PATH
)
@@ -513,7 +514,7 @@ class TagEditWidget(QWidget):
self.refresh_ui()
self.tags_updated.emit(updated_files_tags)
except Exception as e:
QMessageBox.critical(self, "Error", str(e))
QMessageBox.critical(self, UITexts.ERROR, str(e))
finally:
QApplication.restoreOverrideCursor()
@@ -898,6 +899,233 @@ class HistoryWidget(QWidget):
self.refresh_list()
class FavoritesWidget(QWidget):
"""A widget for managing favorite search queries."""
favorites_changed = Signal()
def __init__(self, main_win):
super().__init__()
self.main_win = main_win
layout = QVBoxLayout(self)
self.search_bar = QLineEdit()
self.search_bar.setPlaceholderText(UITexts.FAVORITES_SEARCH_PLACEHOLDER)
self.search_bar.setClearButtonEnabled(True)
self.search_bar.textChanged.connect(self.filter_favorites)
layout.addWidget(self.search_bar)
self.table = QTableWidget()
self.table.setColumnCount(3)
self.table.setHorizontalHeaderLabels(UITexts.FAVORITES_TABLE_HEADER)
self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
self.table.horizontalHeader().setStretchLastSection(True)
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
self.table.setSelectionMode(QAbstractItemView.SingleSelection)
self.table.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.table.verticalHeader().setVisible(False)
self.table.doubleClicked.connect(self.load_selected)
layout.addWidget(self.table)
toolbar = QToolBar()
layout.addWidget(toolbar)
load_action = QAction(QIcon.fromTheme("system-run"), UITexts.LOAD, self)
load_action.triggered.connect(self.load_selected)
toolbar.addAction(load_action)
add_action = QAction(QIcon.fromTheme("list-add"), UITexts.CREATE, self)
add_action.setToolTip(UITexts.ADD_FAVORITE_TOOLTIP)
add_action.triggered.connect(self.add_favorite)
toolbar.addAction(add_action)
edit_action = QAction(QIcon.fromTheme("edit-rename"), UITexts.RENAME, self)
edit_action.triggered.connect(self.edit_comment)
toolbar.addAction(edit_action)
shortcut_action = QAction(
QIcon.fromTheme("preferences-desktop-keyboard-shortcuts"),
UITexts.SHORTCUTS_KEY, self)
shortcut_action.triggered.connect(self.edit_shortcut)
toolbar.addAction(shortcut_action)
delete_action = QAction(QIcon.fromTheme("edit-delete"), UITexts.DELETE, self)
delete_action.triggered.connect(self.delete_favorite)
toolbar.addAction(delete_action)
toolbar.addSeparator()
up_action = QAction(QIcon.fromTheme("go-up"), UITexts.MOVE_UP, self)
up_action.triggered.connect(self.move_up)
toolbar.addAction(up_action)
down_action = QAction(QIcon.fromTheme("go-down"), UITexts.MOVE_DOWN, self)
down_action.triggered.connect(self.move_down)
toolbar.addAction(down_action)
self.refresh_list()
def resizeEvent(self, event):
width = self.table.viewport().width()
self.table.setColumnWidth(0, int(width * 0.60))
self.table.setColumnWidth(2, int(width * 0.15))
super().resizeEvent(event)
def refresh_list(self):
self.table.setRowCount(0)
if not os.path.exists(FAVORITES_PATH):
return
try:
with open(FAVORITES_PATH, 'r', encoding='utf-8') as f:
favorites = json.load(f)
except (json.JSONDecodeError, OSError):
favorites = []
self.table.setRowCount(len(favorites))
for i, fav in enumerate(favorites):
query = fav.get('query', '')
comment = fav.get('comment', '')
shortcut = fav.get('shortcut', '')
self.table.setItem(i, 0, QTableWidgetItem(comment))
self.table.setItem(i, 1, QTableWidgetItem(query))
self.table.setItem(i, 2, QTableWidgetItem(shortcut))
def filter_favorites(self, text):
search_text = text.lower()
for row in range(self.table.rowCount()):
item = self.table.item(row, 0)
if item:
self.table.setRowHidden(row, search_text not in item.text().lower())
def save_favorites(self):
favorites = []
for i in range(self.table.rowCount()):
item_comment = self.table.item(i, 0)
item_query = self.table.item(i, 1)
item_shortcut = self.table.item(i, 2)
comment = item_comment.text() if item_comment else ""
query = item_query.text() if item_query else ""
shortcut = item_shortcut.text() if item_shortcut else ""
favorites.append({'query': query, 'comment': comment, 'shortcut': shortcut})
try:
with open(FAVORITES_PATH, 'w', encoding='utf-8') as f:
json.dump(favorites, f, indent=4)
self.favorites_changed.emit()
except OSError:
pass
def load_selected(self):
row = self.table.currentRow()
if row >= 0:
query = self.table.item(row, 1).text()
self.main_win.process_term(query)
def add_favorite(self):
query = self.main_win.search_input.currentText().strip()
if not query:
return
row = self.table.rowCount()
self.table.insertRow(row)
self.table.setItem(row, 0, QTableWidgetItem(""))
self.table.setItem(row, 1, QTableWidgetItem(query))
self.table.setItem(row, 2, QTableWidgetItem(""))
self.table.setCurrentCell(row, 0)
self.save_favorites()
def edit_comment(self):
row = self.table.currentRow()
if row < 0:
return
comment_item = self.table.item(row, 0)
query = self.table.item(row, 1).text()
old_comment = comment_item.text() if comment_item else ""
new_comment, ok = QInputDialog.getText(
self, UITexts.EDIT_COMMENT_TITLE,
UITexts.EDIT_COMMENT_TEXT.format(query),
QLineEdit.Normal, old_comment)
if ok:
self.table.item(row, 0).setText(new_comment)
self.save_favorites()
def edit_shortcut(self):
row = self.table.currentRow()
if row < 0:
return
query = self.table.item(row, 1).text()
current_sc = self.table.item(row, 2).text()
dialog = QDialog(self)
dialog.setWindowTitle(UITexts.EDIT_SHORTCUT_TITLE)
dlg_layout = QVBoxLayout(dialog)
dlg_layout.addWidget(QLabel(UITexts.EDIT_SHORTCUT_TEXT.format(query)))
from PySide6.QtWidgets import QKeySequenceEdit, QDialogButtonBox
key_edit = QKeySequenceEdit(QKeySequence(current_sc))
dlg_layout.addWidget(key_edit)
buttons = QDialogButtonBox(
QDialogButtonBox.Ok | QDialogButtonBox.Cancel | QDialogButtonBox.Reset)
buttons.accepted.connect(dialog.accept)
buttons.rejected.connect(dialog.reject)
buttons.button(QDialogButtonBox.Reset).clicked.connect(
lambda: key_edit.setKeySequence(QKeySequence()))
dlg_layout.addWidget(buttons)
if dialog.exec() == QDialog.Accepted:
new_sequence = key_edit.keySequence()
new_sc_str = new_sequence.toString(QKeySequence.NativeText)
if not new_sequence.isEmpty():
new_key_combo = new_sequence[0]
new_key = new_key_combo.key()
new_mods = new_key_combo.keyboardModifiers()
conflict_desc = self.main_win.shortcut_controller.check_conflict(
new_key, new_mods)
if conflict_desc and new_sc_str != current_sc:
res = QMessageBox.question(self, UITexts.SHORTCUT_CONFLICT_TITLE,
UITexts.SHORTCUT_CONFLICT_TEXT.format(
new_sc_str, conflict_desc) +
"\n\n" +
UITexts.SHORTCUT_OVERRIDE_QUESTION,
QMessageBox.Yes | QMessageBox.No)
if res == QMessageBox.No:
return
self.table.item(row, 2).setText(new_sc_str)
self.save_favorites()
def delete_favorite(self):
row = self.table.currentRow()
if row >= 0:
self.table.removeRow(row)
self.save_favorites()
def move_up(self):
row = self.table.currentRow()
if row > 0:
self._swap_rows(row, row - 1)
self.table.setCurrentCell(row - 1, 0)
self.save_favorites()
def move_down(self):
row = self.table.currentRow()
if row >= 0 and row < self.table.rowCount() - 1:
self._swap_rows(row, row + 1)
self.table.setCurrentCell(row + 1, 0)
self.save_favorites()
def _swap_rows(self, row1, row2):
for col in range(self.table.columnCount()):
item1 = self.table.takeItem(row1, col)
item2 = self.table.takeItem(row2, col)
self.table.setItem(row1, col, item2)
self.table.setItem(row2, col, item1)
class RatingStar(QLabel):
"""An individual star label for the rating widget."""
# Emits the star index (1-5)