From 372946c79fb5f4a58574fbb670e389d383f41c65 Mon Sep 17 00:00:00 2001 From: trav Date: Thu, 24 Sep 2026 23:52:09 -0700 Subject: [PATCH] v0.24.0: click the blank cover Clicking the gray sidebar art square (playing track has no embedded art) now opens Download Album Art for that track, same as the right-click menu. Once art is embedded the square refreshes and a click opens the big view again. Co-Authored-By: Claude Opus 5.5 --- lintunes/__init__.py | 2 +- lintunes/gui/main_window.py | 8 ++++- lintunes/gui/sidebar.py | 22 +++++++++---- tests/test_round61.py | 63 +++++++++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 tests/test_round61.py diff --git a/lintunes/__init__.py b/lintunes/__init__.py index 73ad5dc..8fa5eb4 100644 --- a/lintunes/__init__.py +++ b/lintunes/__init__.py @@ -1,3 +1,3 @@ """LinTunes — iTunes-style music library manager and player for Linux.""" -__version__ = "0.23.0" +__version__ = "0.24.0" diff --git a/lintunes/gui/main_window.py b/lintunes/gui/main_window.py index 9af45f3..29ace94 100644 --- a/lintunes/gui/main_window.py +++ b/lintunes/gui/main_window.py @@ -206,6 +206,8 @@ class MainWindow(QMainWindow): self._sidebar.library_selected.connect(self._show_library) self._sidebar.playlist_selected.connect(self._show_playlist) self._sidebar.tree.export_requested.connect(self._export_playlist) + self._sidebar.art.download_art_requested.connect( + lambda tid: self._download_album_art([tid])) self._content.currentChanged.connect(self._update_totals) for view in (self._library_view, self._playlist_view): view.play_requested.connect( @@ -1399,7 +1401,11 @@ class MainWindow(QMainWindow): self, "Download Album Art", "Couldn't embed artwork in some files:\n\n" + "\n".join(failures[:10])) - elif done: + if done: + # Normally track_updated already did this, but only when the + # file's size changed. + self._sidebar.art.refresh() + if done and not failures: noun = "track" if done == 1 else "tracks" self.statusBar().showMessage( f"Artwork embedded in {done} {noun}", 5000) diff --git a/lintunes/gui/sidebar.py b/lintunes/gui/sidebar.py index f390aba..44fd241 100644 --- a/lintunes/gui/sidebar.py +++ b/lintunes/gui/sidebar.py @@ -88,7 +88,10 @@ class SidebarPanel(QWidget): class SidebarArt(QWidget): """Square album art pinned to the sidebar bottom; gray square when the - current track has no embedded art. A click opens the big art window.""" + current track has no embedded art. A click opens the big art window, or, + on the gray square, asks for the track's art to be downloaded.""" + + download_art_requested = pyqtSignal(int) # track id def __init__(self, player, parent=None): super().__init__(parent) @@ -96,7 +99,6 @@ class SidebarArt(QWidget): self._pixmap: QPixmap | None = None self._track = None self._art_window = ArtWindow() - self.setToolTip("Click to view full size") player.track_changed.connect(self.set_track) def set_track(self, track): @@ -108,6 +110,12 @@ class SidebarArt(QWidget): pixmap = QPixmap() if pixmap.loadFromData(data): self._pixmap = pixmap + if self._pixmap is not None: + self.setToolTip("Click to view full size") + elif track is not None: + self.setToolTip("Click to download album art") + else: + self.setToolTip("") if self._art_window.isVisible(): self._art_window.set_artwork(self._pixmap, self._title()) self.update() @@ -132,10 +140,12 @@ class SidebarArt(QWidget): # A click is a release that lands on the art. Opening on press would # also fire when a drag merely starts here. if (event.button() == Qt.MouseButton.LeftButton - and self.rect().contains(event.position().toPoint()) - and self._pixmap is not None): - self._art_window.show_artwork(self._pixmap, self._title(), - self.screen()) + and self.rect().contains(event.position().toPoint())): + if self._pixmap is not None: + self._art_window.show_artwork(self._pixmap, self._title(), + self.screen()) + elif self._track is not None: + self.download_art_requested.emit(self._track.track_id) super().mouseReleaseEvent(event) def paintEvent(self, event): diff --git a/tests/test_round61.py b/tests/test_round61.py new file mode 100644 index 0000000..e9c7413 --- /dev/null +++ b/tests/test_round61.py @@ -0,0 +1,63 @@ +"""Round 61: clicking the gray (art-less) sidebar square asks for the +playing track's art to be downloaded; with art it still opens the big view.""" + +from PyQt6.QtCore import Qt +from PyQt6.QtGui import QPixmap + +from lintunes.models import Track +from tests.test_round49 import _FakePlayer, _release + + +def _art(): + from lintunes.gui.sidebar import SidebarArt + art = SidebarArt(_FakePlayer()) + art.resize(100, 100) + requested = [] + art.download_art_requested.connect(requested.append) + return art, requested + + +def _track(tid=7): + # No location: set_track reads no art, which is the gray-square case + return Track(track_id=tid, name="Song", artist="Artist", album="Album") + + +class TestGraySquareClick: + def test_click_without_art_requests_a_download(self, qapp): + art, requested = _art() + art.set_track(_track(42)) + art.mouseReleaseEvent(_release(Qt.MouseButton.LeftButton)) + assert requested == [42] + assert not art._art_window.isVisible() + + def test_no_track_requests_nothing(self, qapp): + art, requested = _art() + art.mouseReleaseEvent(_release(Qt.MouseButton.LeftButton)) + assert requested == [] + + def test_with_art_opens_the_big_view_instead(self, qapp): + art, requested = _art() + art.set_track(_track()) + pixmap = QPixmap(32, 32) + pixmap.fill(Qt.GlobalColor.blue) + art._pixmap = pixmap + art.mouseReleaseEvent(_release(Qt.MouseButton.LeftButton)) + try: + assert requested == [] + assert art._art_window.isVisible() + finally: + art._art_window.hide() + + def test_right_click_or_release_outside_requests_nothing(self, qapp): + art, requested = _art() + art.set_track(_track()) + art.mouseReleaseEvent(_release(Qt.MouseButton.RightButton)) + art.mouseReleaseEvent(_release(Qt.MouseButton.LeftButton, 500, 500)) + assert requested == [] + + def test_tooltip_says_what_a_click_will_do(self, qapp): + art, _ = _art() + art.set_track(None) + assert art.toolTip() == "" + art.set_track(_track()) + assert art.toolTip() == "Click to download album art"