"""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