v0.51.1: album art sits under the list frame's closing line, like the empty panel

This commit is contained in:
2026-10-13 10:11:55 -07:00
parent 976d338574
commit f48165080b
4 changed files with 58 additions and 8 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
"""LinTunes — iTunes-style music library manager and player for Linux.""" """LinTunes — iTunes-style music library manager and player for Linux."""
__version__ = "0.51.0" __version__ = "0.51.1"
+28 -6
View File
@@ -2,9 +2,9 @@ from PyQt6.QtWidgets import (
QWidget, QVBoxLayout, QPushButton, QTreeWidget, QTreeWidgetItem, QMenu, QWidget, QVBoxLayout, QPushButton, QTreeWidget, QTreeWidgetItem, QMenu,
QInputDialog, QMessageBox, QAbstractItemView, QLineEdit, QApplication, QInputDialog, QMessageBox, QAbstractItemView, QLineEdit, QApplication,
QHBoxLayout, QListWidget, QListWidgetItem, QStackedWidget, QSizePolicy, QHBoxLayout, QListWidget, QListWidgetItem, QStackedWidget, QSizePolicy,
QFrame, QFrame, QStyle, QStyleOptionFrame,
) )
from PyQt6.QtCore import Qt, QTimer, QPoint, QRectF, pyqtSignal from PyQt6.QtCore import Qt, QTimer, QPoint, QRect, QRectF, pyqtSignal
from PyQt6.QtGui import QBrush, QPainter, QPalette, QPixmap from PyQt6.QtGui import QBrush, QPainter, QPalette, QPixmap
from lintunes.models import PlaylistType from lintunes.models import PlaylistType
@@ -267,6 +267,10 @@ class FriendPlaylistList(QListWidget):
# it, with the visualizer's corners. # it, with the visualizer's corners.
NO_ART_GAP = 6 NO_ART_GAP = 6
NO_ART_RADIUS = 8 NO_ART_RADIUS = 8
# Art sits inside the list's frame, so the frame never closes between them;
# the art draws that closing line itself, this tall, so a cover with a white
# border doesn't run straight on from the list.
ART_RULE = 1
class SidebarArt(QWidget): class SidebarArt(QWidget):
@@ -326,7 +330,7 @@ class SidebarArt(QWidget):
def _fit_height(self): def _fit_height(self):
# Keep it square: height follows the sidebar's width (plus the gap # Keep it square: height follows the sidebar's width (plus the gap
# above the empty panel) # above the empty panel)
height = self.width() + (0 if self.has_art() else NO_ART_GAP) height = self.width() + (ART_RULE if self.has_art() else NO_ART_GAP)
if self.height() != height: if self.height() != height:
self.setFixedHeight(height) self.setFixedHeight(height)
@@ -358,11 +362,29 @@ class SidebarArt(QWidget):
self.palette().alternateBase().color(), self.palette().alternateBase().color(),
NO_ART_RADIUS, like=CONTROL_HEIGHT) NO_ART_RADIUS, like=CONTROL_HEIGHT)
return return
# The list frame's own bottom edge, drawn by the style: a frame
# whose bottom row is this widget's top row, its sides pushed out of
# view, so the line is exactly the one under the list with no art.
option = QStyleOptionFrame()
option.initFrom(self)
option.frameShape = QFrame.Shape.StyledPanel
option.state |= QStyle.StateFlag.State_Sunken
option.lineWidth = option.midLineWidth = 0
frame = self.style().pixelMetric(QStyle.PixelMetric.PM_DefaultFrameWidth)
option.rect = QRect(-frame - 4, ART_RULE - 4 * frame - 8,
self.width() + 2 * frame + 8, 4 * frame + 8)
painter.save()
painter.setClipRect(0, 0, self.width(), ART_RULE)
self.style().drawPrimitive(QStyle.PrimitiveElement.PE_Frame,
option, painter, self)
painter.restore()
area = self.rect().adjusted(0, ART_RULE, 0, 0)
scaled = self._pixmap.scaled( scaled = self._pixmap.scaled(
self.size(), Qt.AspectRatioMode.KeepAspectRatioByExpanding, area.size(), Qt.AspectRatioMode.KeepAspectRatioByExpanding,
Qt.TransformationMode.SmoothTransformation) Qt.TransformationMode.SmoothTransformation)
x = (self.width() - scaled.width()) // 2 x = (area.width() - scaled.width()) // 2
y = (self.height() - scaled.height()) // 2 y = area.top() + (area.height() - scaled.height()) // 2
painter.setClipRect(area)
painter.drawPixmap(x, y, scaled) painter.drawPixmap(x, y, scaled)
+2 -1
View File
@@ -138,6 +138,7 @@ def test_no_art_panel_sits_below_the_frame(qapp, tmp_path):
def test_real_art_goes_inside_the_frame(qapp, tmp_path): def test_real_art_goes_inside_the_frame(qapp, tmp_path):
from PyQt6.QtGui import QPixmap from PyQt6.QtGui import QPixmap
from lintunes.gui.sidebar import ART_RULE
window, sidebar = _sidebar(qapp, tmp_path) window, sidebar = _sidebar(qapp, tmp_path)
art = sidebar.art art = sidebar.art
pixmap = QPixmap(10, 10) pixmap = QPixmap(10, 10)
@@ -146,7 +147,7 @@ def test_real_art_goes_inside_the_frame(qapp, tmp_path):
art.has_art_changed.emit(True) art.has_art_changed.emit(True)
art._fit_height() art._fit_height()
assert art.parentWidget() is sidebar.list_area assert art.parentWidget() is sidebar.list_area
assert art.height() == art.width() assert art.height() == art.width() + ART_RULE
art._pixmap = None art._pixmap = None
art.set_track(None) art.set_track(None)
assert art.parentWidget() is sidebar assert art.parentWidget() is sidebar
+27
View File
@@ -0,0 +1,27 @@
"""Round 82: the list frame closes above album art too."""
from PyQt6.QtGui import QColor, QPixmap
from tests.test_round80 import _sidebar
def test_art_draws_the_frames_closing_line(qapp, tmp_path):
"""A white cover inside the list's frame starts under a line in the
frame's own color, not straight on from the list."""
from lintunes.gui.sidebar import ART_RULE
window, sidebar = _sidebar(qapp, tmp_path)
art = sidebar.art
pixmap = QPixmap(10, 10)
pixmap.fill(QColor("white"))
art._pixmap = pixmap
art.has_art_changed.emit(True)
art._fit_height()
qapp.processEvents()
group = sidebar.list_area.grab().toImage()
# the frame's own left edge, halfway down the list
frame = group.pixelColor(0, group.height() // 4)
image = art.grab().toImage()
middle = image.width() // 2
for y in range(ART_RULE):
assert image.pixelColor(middle, y) == frame
assert image.pixelColor(middle, ART_RULE) == QColor("white")
window.close()