Added keyboard shortcut help.
This commit is contained in:
1 parent
33bba7282e
commit
bd6cfb83eb
3 files changed
+87
-2
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
# Maintainer: Storm Dragon <storm_dragon@stormux.org>
|
# Maintainer: Storm Dragon <storm_dragon@stormux.org>
|
||||||
pkgname=navipy-git
|
pkgname=navipy-git
|
||||||
pkgver=r11.gd9f4ac4
|
pkgver=r12.g33bba72
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc='Accessible Subsonic desktop client tested with Navidrome'
|
pkgdesc='Accessible Subsonic desktop client tested with Navidrome'
|
||||||
arch=('any')
|
arch=('any')
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import time
|
|||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from PySide6.QtWidgets import (
|
from PySide6.QtWidgets import (
|
||||||
QHBoxLayout,
|
QHBoxLayout,
|
||||||
|
QDialog,
|
||||||
|
QDialogButtonBox,
|
||||||
|
QHeaderView,
|
||||||
QLabel,
|
QLabel,
|
||||||
QMenu,
|
QMenu,
|
||||||
QListWidget,
|
QListWidget,
|
||||||
@@ -20,6 +23,8 @@ from PySide6.QtWidgets import (
|
|||||||
QSlider,
|
QSlider,
|
||||||
QSplitter,
|
QSplitter,
|
||||||
QStatusBar,
|
QStatusBar,
|
||||||
|
QTableWidget,
|
||||||
|
QTableWidgetItem,
|
||||||
QVBoxLayout,
|
QVBoxLayout,
|
||||||
QWidget,
|
QWidget,
|
||||||
)
|
)
|
||||||
@@ -222,6 +227,10 @@ class MainWindow(QMainWindow):
|
|||||||
# Help menu
|
# Help menu
|
||||||
helpMenu = menuBar.addMenu("&Help")
|
helpMenu = menuBar.addMenu("&Help")
|
||||||
|
|
||||||
|
self.keyboardShortcutsAction = QAction("&Keyboard Shortcuts", self)
|
||||||
|
self.keyboardShortcutsAction.triggered.connect(self.showKeyboardShortcuts)
|
||||||
|
helpMenu.addAction(self.keyboardShortcutsAction)
|
||||||
|
|
||||||
self.aboutAction = QAction("&About NaviPy", self)
|
self.aboutAction = QAction("&About NaviPy", self)
|
||||||
self.aboutAction.triggered.connect(self.showAbout)
|
self.aboutAction.triggered.connect(self.showAbout)
|
||||||
helpMenu.addAction(self.aboutAction)
|
helpMenu.addAction(self.aboutAction)
|
||||||
@@ -1830,6 +1839,64 @@ class MainWindow(QMainWindow):
|
|||||||
parent=self
|
parent=self
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def showKeyboardShortcuts(self):
|
||||||
|
"""Show the application's keyboard shortcuts in an accessible table."""
|
||||||
|
shortcuts = (
|
||||||
|
("Ctrl+O", "Connect to server"),
|
||||||
|
("F5", "Refresh library"),
|
||||||
|
("Shift+F5", "Full library sync"),
|
||||||
|
("Ctrl+Q", "Quit"),
|
||||||
|
("Space", "Play or pause"),
|
||||||
|
("X", "Play or resume"),
|
||||||
|
("C", "Toggle pause"),
|
||||||
|
("V", "Stop playback"),
|
||||||
|
("Ctrl+Left or Z", "Previous track"),
|
||||||
|
("Ctrl+Right or B", "Next track"),
|
||||||
|
("Alt+S", "Toggle shuffle"),
|
||||||
|
("Alt+R", "Change repeat mode"),
|
||||||
|
("+", "Favorite selected item"),
|
||||||
|
("_", "Unfavorite selected item"),
|
||||||
|
("Ctrl+Up or 0", "Increase volume"),
|
||||||
|
("Ctrl+Down or 9", "Decrease volume"),
|
||||||
|
("Ctrl+F", "Search"),
|
||||||
|
("Ctrl+T", "Announce current track in a dialog"),
|
||||||
|
("Alt+C", "Announce current track"),
|
||||||
|
("Ctrl+P", "Announce playback position"),
|
||||||
|
("Alt+I", "Open actions for the current item"),
|
||||||
|
("Ctrl+Tab", "Next library tab"),
|
||||||
|
("Ctrl+Shift+Tab", "Previous library tab"),
|
||||||
|
("Enter", "Open selected collection or play selected song"),
|
||||||
|
("Backspace or Alt+Left", "Go back in the library"),
|
||||||
|
("Shift+F10 or Menu", "Open actions for selected library item"),
|
||||||
|
("Ctrl+C", "Copy selected library row"),
|
||||||
|
)
|
||||||
|
dialog = QDialog(self)
|
||||||
|
dialog.setWindowTitle("Keyboard Shortcuts")
|
||||||
|
dialog.setModal(True)
|
||||||
|
dialog.setMinimumSize(620, 500)
|
||||||
|
|
||||||
|
layout = QVBoxLayout(dialog)
|
||||||
|
table = QTableWidget(len(shortcuts), 2, dialog)
|
||||||
|
table.setHorizontalHeaderLabels(["Shortcut", "Action"])
|
||||||
|
table.setAccessibleName("Keyboard shortcuts")
|
||||||
|
table.setAccessibleDescription("A table of NaviPy keyboard shortcuts and their actions.")
|
||||||
|
table.setEditTriggers(QTableWidget.NoEditTriggers)
|
||||||
|
table.setSelectionBehavior(QTableWidget.SelectRows)
|
||||||
|
table.setSelectionMode(QTableWidget.SingleSelection)
|
||||||
|
table.verticalHeader().setVisible(False)
|
||||||
|
table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
|
||||||
|
table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
|
||||||
|
for row, (shortcut, action) in enumerate(shortcuts):
|
||||||
|
table.setItem(row, 0, QTableWidgetItem(shortcut))
|
||||||
|
table.setItem(row, 1, QTableWidgetItem(action))
|
||||||
|
layout.addWidget(table)
|
||||||
|
|
||||||
|
buttonBox = QDialogButtonBox(QDialogButtonBox.Close, dialog)
|
||||||
|
buttonBox.rejected.connect(dialog.reject)
|
||||||
|
layout.addWidget(buttonBox)
|
||||||
|
table.setFocus()
|
||||||
|
dialog.exec()
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
"""Handle window close"""
|
"""Handle window close"""
|
||||||
# Signal background tasks to stop
|
# Signal background tasks to stop
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from unittest.mock import Mock
|
|||||||
import pytest
|
import pytest
|
||||||
from PySide6.QtCore import Qt
|
from PySide6.QtCore import Qt
|
||||||
from PySide6.QtMultimedia import QMediaPlayer
|
from PySide6.QtMultimedia import QMediaPlayer
|
||||||
from PySide6.QtWidgets import QApplication, QWidget, QTreeWidgetItem
|
from PySide6.QtWidgets import QApplication, QDialog, QTableWidget, QTreeWidgetItem, QWidget
|
||||||
|
|
||||||
from src.api.client import SubsonicClient
|
from src.api.client import SubsonicClient
|
||||||
from src.api.models import Song, Playlist
|
from src.api.models import Song, Playlist
|
||||||
@@ -49,6 +49,24 @@ def test_background_error_survives_until_ui_delivery(app):
|
|||||||
assert str(errors[0]) == "synthetic failure"
|
assert str(errors[0]) == "synthetic failure"
|
||||||
|
|
||||||
|
|
||||||
|
def test_keyboard_shortcuts_dialog_has_accessible_shortcut_table(app, monkeypatch):
|
||||||
|
parent = QWidget()
|
||||||
|
|
||||||
|
def inspect_dialog(dialog):
|
||||||
|
table = dialog.findChild(QTableWidget)
|
||||||
|
assert dialog.windowTitle() == "Keyboard Shortcuts"
|
||||||
|
assert table.accessibleName() == "Keyboard shortcuts"
|
||||||
|
assert table.horizontalHeaderItem(0).text() == "Shortcut"
|
||||||
|
assert table.horizontalHeaderItem(1).text() == "Action"
|
||||||
|
assert table.rowCount() >= 20
|
||||||
|
assert table.item(15, 0).text() == "Ctrl+Down or 9"
|
||||||
|
assert table.item(15, 1).text() == "Decrease volume"
|
||||||
|
return QDialog.Accepted
|
||||||
|
|
||||||
|
monkeypatch.setattr(QDialog, "exec", inspect_dialog)
|
||||||
|
MainWindow.showKeyboardShortcuts(parent)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("refresh", ["forced", "full", "incremental"])
|
@pytest.mark.parametrize("refresh", ["forced", "full", "incremental"])
|
||||||
def test_refresh_bypasses_api_memory_cache(tmp_path, refresh):
|
def test_refresh_bypasses_api_memory_cache(tmp_path, refresh):
|
||||||
raw = SubsonicClient("https://fake.example", "user", "fake")
|
raw = SubsonicClient("https://fake.example", "user", "fake")
|
||||||
|
|||||||
Reference in new issue
Block a user