Initial commit
This commit is contained in:
108
src/widgets/search_dialog.py
Normal file
108
src/widgets/search_dialog.py
Normal file
@@ -0,0 +1,108 @@
|
||||
"""
|
||||
Accessible search dialog for finding tracks quickly.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List
|
||||
|
||||
from PySide6.QtCore import Signal, Qt
|
||||
from PySide6.QtWidgets import (
|
||||
QDialog,
|
||||
QDialogButtonBox,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QListWidget,
|
||||
QListWidgetItem,
|
||||
QPushButton,
|
||||
QVBoxLayout,
|
||||
)
|
||||
|
||||
from src.api.client import SubsonicClient
|
||||
from src.api.models import Song
|
||||
from src.widgets.accessible_text_dialog import AccessibleTextDialog
|
||||
|
||||
|
||||
class SearchDialog(QDialog):
|
||||
"""Simple dialog for searching the Navidrome library."""
|
||||
|
||||
songActivated = Signal(Song)
|
||||
|
||||
def __init__(self, client: SubsonicClient, parent=None):
|
||||
super().__init__(parent)
|
||||
self.client = client
|
||||
self.setWindowTitle("Search Library")
|
||||
self.setMinimumSize(500, 420)
|
||||
self.setupUi()
|
||||
|
||||
def setupUi(self):
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
self.instructions = QLabel("Enter a song, artist, or album title. Press Enter to play results.")
|
||||
self.instructions.setAccessibleName("Search Instructions")
|
||||
self.instructions.setWordWrap(True)
|
||||
layout.addWidget(self.instructions)
|
||||
|
||||
inputLayout = QHBoxLayout()
|
||||
self.queryEdit = QLineEdit()
|
||||
self.queryEdit.setAccessibleName("Search Query")
|
||||
self.queryEdit.returnPressed.connect(self.runSearch)
|
||||
inputLayout.addWidget(self.queryEdit)
|
||||
|
||||
self.searchButton = QPushButton("&Search")
|
||||
self.searchButton.setAccessibleName("Start Search")
|
||||
self.searchButton.clicked.connect(self.runSearch)
|
||||
inputLayout.addWidget(self.searchButton)
|
||||
|
||||
layout.addLayout(inputLayout)
|
||||
|
||||
self.resultsList = QListWidget()
|
||||
self.resultsList.setAccessibleName("Search Results")
|
||||
self.resultsList.itemActivated.connect(self.activateSelection)
|
||||
layout.addWidget(self.resultsList)
|
||||
|
||||
self.statusLabel = QLabel("")
|
||||
self.statusLabel.setAccessibleName("Search Status")
|
||||
layout.addWidget(self.statusLabel)
|
||||
|
||||
buttonBox = QDialogButtonBox(QDialogButtonBox.Close)
|
||||
buttonBox.rejected.connect(self.reject)
|
||||
layout.addWidget(buttonBox)
|
||||
|
||||
def runSearch(self):
|
||||
"""Run the Subsonic search and populate results."""
|
||||
query = self.queryEdit.text().strip()
|
||||
if not query:
|
||||
self.statusLabel.setText("Enter a search term to begin.")
|
||||
return
|
||||
|
||||
self.statusLabel.setText("Searching...")
|
||||
self.resultsList.clear()
|
||||
try:
|
||||
results = self.client.search(query, artistCount=0, albumCount=0, songCount=50)
|
||||
songs: List[Song] = results.get("songs", [])
|
||||
if not songs:
|
||||
self.statusLabel.setText("No songs found.")
|
||||
return
|
||||
|
||||
for song in songs:
|
||||
text = f"{song.title} — {song.artist} ({song.album}, {song.durationFormatted})"
|
||||
item = QListWidgetItem(text)
|
||||
item.setData(Qt.UserRole, song)
|
||||
item.setData(Qt.AccessibleTextRole, text)
|
||||
self.resultsList.addItem(item)
|
||||
|
||||
self.statusLabel.setText(f"Found {len(songs)} songs. Activate a result to play.")
|
||||
self.resultsList.setCurrentRow(0)
|
||||
self.resultsList.setFocus()
|
||||
except Exception as e:
|
||||
AccessibleTextDialog.showError("Search Error", str(e), parent=self)
|
||||
self.statusLabel.setText("Search failed.")
|
||||
|
||||
def activateSelection(self, item: QListWidgetItem):
|
||||
"""Emit the chosen song and close the dialog."""
|
||||
song = item.data(Qt.UserRole)
|
||||
if isinstance(song, Song):
|
||||
self.songActivated.emit(song)
|
||||
self.accept()
|
||||
Reference in New Issue
Block a user