Files
lintunes/tests/test_round80.py
T
trav fbb439b7ee v0.47.0: no album art shows a debossed panel, not a flat square
The sidebar's art square, when a track has no art, is now the list's own
background with a rounded panel set into it, debossed like the visualizer
and the now-playing panel. paint_deboss takes its rims in pixels when
asked, so a panel as tall as the sidebar is recessed as much as a short
one rather than fading over 16 px.
2026-10-10 11:26:33 -07:00

144 lines
5.2 KiB
Python

"""Round 80: scrollbars lose their arrow buttons, and the thumb is a button.
- No arrows: the trough runs the bar's full length and the arrow rects are
empty, so nothing paints or hit-tests there.
- The thumb gets the buttons' corner radius and emboss, with rims as tall in
pixels as a button's, however long the thumb.
- The bar's thickness comes from the UI scale through the style, not QSS
(any QSS rule on a scrollbar part makes Qt draw the arrows itself).
"""
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QColor
from PyQt6.QtWidgets import QPushButton, QScrollBar, QStyle, QStyleOptionSlider
from lintunes import theme
from lintunes.gui.track_table import ClickToJumpScrollStyle
SC = QStyle.SubControl
def _option(bar):
opt = QStyleOptionSlider()
bar.initStyleOption(opt)
return opt
def _bar(qapp, orientation=Qt.Orientation.Vertical):
bar = QScrollBar(orientation)
bar.setStyle(ClickToJumpScrollStyle())
bar.setRange(0, 900)
bar.setPageStep(100)
bar.resize(14, 400) if orientation == Qt.Orientation.Vertical \
else bar.resize(400, 14)
return bar
def test_no_arrow_buttons(qapp):
bar = _bar(qapp)
style, opt = bar.style(), _option(bar)
cc = QStyle.ComplexControl.CC_ScrollBar
assert style.subControlRect(cc, opt, SC.SC_ScrollBarAddLine, bar).isEmpty()
assert style.subControlRect(cc, opt, SC.SC_ScrollBarSubLine, bar).isEmpty()
assert style.subControlRect(cc, opt, SC.SC_ScrollBarGroove, bar) == opt.rect
def test_thumb_spans_the_full_trough(qapp):
for orientation in (Qt.Orientation.Vertical, Qt.Orientation.Horizontal):
bar = _bar(qapp, orientation)
style, cc = bar.style(), QStyle.ComplexControl.CC_ScrollBar
bar.setValue(0)
top = style.subControlRect(cc, _option(bar), SC.SC_ScrollBarSlider, bar)
bar.setValue(900)
end = style.subControlRect(cc, _option(bar), SC.SC_ScrollBarSlider, bar)
if orientation == Qt.Orientation.Vertical:
assert top.top() == 0 and end.bottom() == 399
assert top.height() == 40 # 100 / (900 + 100) of 400
else:
assert top.left() == 0 and end.right() == 399
def test_tiny_page_keeps_a_grabbable_thumb(qapp):
bar = _bar(qapp)
bar.setRange(0, 100000)
rect = bar.style().subControlRect(QStyle.ComplexControl.CC_ScrollBar,
_option(bar), SC.SC_ScrollBarSlider, bar)
assert rect.height() == ClickToJumpScrollStyle.extent * 2
def test_thickness_follows_ui_scale(qapp):
class Prefs(dict):
def get(self, key, default=None):
return "large" if key == "ui_scale" else default
before = ClickToJumpScrollStyle.extent
try:
theme.apply_theme(qapp, Prefs())
assert ClickToJumpScrollStyle.extent == theme.UI_SCALES["large"]["scrollbar"]
assert "QScrollBar" not in qapp.styleSheet()
finally:
ClickToJumpScrollStyle.extent = before
def test_thumb_rims_match_a_real_button(qapp):
qapp.setStyleSheet(theme.button_emboss_qss(QColor("#d0d0d0")))
try:
button = QPushButton("OK")
height = button.sizeHint().height()
assert abs(theme.button_rim_px(button.fontMetrics())
- theme.EMBOSS_RIM_WIDTH * height) < 1
finally:
qapp.setStyleSheet("")
def test_thumb_paints(qapp):
bar = _bar(qapp)
bar.setValue(450)
image = bar.grab().toImage()
thumb = bar.style().subControlRect(QStyle.ComplexControl.CC_ScrollBar,
_option(bar), SC.SC_ScrollBarSlider, bar)
trough = image.pixelColor(7, 5)
middle = image.pixelColor(7, thumb.center().y())
assert middle != trough
# --------------------------------------------------------------------------
# no album art: a debossed panel, not a flat square
# --------------------------------------------------------------------------
def test_no_art_is_a_debossed_panel(qapp):
from PyQt6.QtCore import QObject, pyqtSignal
from lintunes.gui.sidebar import NO_ART_MARGIN, SidebarArt
class Player(QObject):
track_changed = pyqtSignal(object)
art = SidebarArt(Player())
art.resize(200, 200)
image = art.grab().toImage()
base = art.palette().base().color()
# the margin around the panel is the list's own background...
assert image.pixelColor(1, 100) == base
# ...the corner is rounded off, so it's background too...
assert image.pixelColor(NO_ART_MARGIN + 1, NO_ART_MARGIN + 1) == base
# ...and the panel is shaded darker at the top than in the middle
top = image.pixelColor(100, NO_ART_MARGIN + 2).lightness()
middle = image.pixelColor(100, 100).lightness()
assert top < middle
def test_deboss_rims_can_be_fixed_in_pixels(qapp):
"""On a 400 px panel the default rims are 32 px deep; 3 px rims leave
row 10 already on the face, a good deal lighter."""
from PyQt6.QtCore import QRectF
from PyQt6.QtGui import QImage, QPainter
color = QColor("#e0e0e0")
def row10(**kw):
image = QImage(100, 400, QImage.Format.Format_ARGB32)
image.fill(0)
painter = QPainter(image)
theme.paint_deboss(painter, QRectF(0.5, 0.5, 99, 399), color, 8, **kw)
painter.end()
return image.pixelColor(50, 10).lightness()
assert row10(rim=3) > row10() + 5