Prev/play/next (and shuffle) used to hug their ~20px glyphs in the middle of the 62px rounded box, so most of the bubble was dead space. _box() now takes split=True: the buttons tile the bubble interior in equal shares, expanding to the (tightened) margins with the glyph centered in each. Minimum button widths keep the bubbles at their old footprint — no bigger, no smaller. Round 21 tests cover the equal split, vertical fill, edge spans, and the preserved footprint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
76 lines
3.1 KiB
Python
76 lines
3.1 KiB
Python
"""Round 21: transport buttons fill their bubble as equal tap targets.
|
|
|
|
The prev/play/next FlashButtons used to hug their ~20px icons in the middle
|
|
of the 62px-tall rounded box, leaving most of the bubble unclickable. Now
|
|
_box(split=True) tiles the bubble interior with the buttons: each gets an
|
|
equal third, expands vertically to the margins, and keeps its glyph centered.
|
|
The bubble itself keeps its pre-split footprint (no bigger, no smaller).
|
|
"""
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from lintunes.models import Library, Track
|
|
from lintunes.library_manager import LibraryManager
|
|
from lintunes.preferences import Preferences
|
|
from lintunes import player as player_module
|
|
from lintunes.player import Player
|
|
from lintunes.gui.transport import TransportBar, CONTROL_HEIGHT
|
|
|
|
|
|
def _mock_player():
|
|
"""A real Player with the Qt multimedia backend stubbed out (it blocks on
|
|
init headless — see tests/test_player.py)."""
|
|
library = Library(tracks={1: Track(track_id=1, name="One")})
|
|
with patch.multiple(
|
|
player_module,
|
|
QMediaPlayer=MagicMock(),
|
|
QAudioOutput=MagicMock(),
|
|
QAudioBufferOutput=MagicMock(),
|
|
QMediaDevices=MagicMock(),
|
|
):
|
|
return Player(MagicMock(library=library))
|
|
|
|
|
|
def _build_bar(qapp, tmp_path):
|
|
prefs = Preferences(tmp_path)
|
|
manager = LibraryManager(
|
|
Library(tracks={1: Track(track_id=1, name="One")}), tmp_path)
|
|
bar = TransportBar(_mock_player(), manager, prefs)
|
|
bar.resize(1200, 90)
|
|
bar.show()
|
|
qapp.processEvents()
|
|
return bar
|
|
|
|
|
|
class TestTransportTapTargets:
|
|
def test_buttons_split_the_bubble_equally(self, qapp, tmp_path):
|
|
bar = _build_bar(qapp, tmp_path)
|
|
widths = [b.width() for b in
|
|
(bar._prev_btn, bar._play_btn, bar._next_btn)]
|
|
assert max(widths) - min(widths) <= 1
|
|
|
|
def test_buttons_fill_the_bubble_vertically(self, qapp, tmp_path):
|
|
bar = _build_bar(qapp, tmp_path)
|
|
for btn in (bar._prev_btn, bar._play_btn, bar._next_btn,
|
|
bar._shuffle_btn):
|
|
# Bubble height minus margins+border (4px each side).
|
|
assert btn.height() >= CONTROL_HEIGHT - 10
|
|
|
|
def test_buttons_span_the_bubble_horizontally(self, qapp, tmp_path):
|
|
bar = _build_bar(qapp, tmp_path)
|
|
box = bar._prev_btn.parentWidget()
|
|
assert bar._prev_btn.x() <= 5 # flush against the left margin
|
|
right_edge = bar._next_btn.x() + bar._next_btn.width()
|
|
assert right_edge >= box.width() - 5 # ...and the right one
|
|
# No stretch gaps between the buttons.
|
|
assert bar._play_btn.x() == bar._prev_btn.x() + bar._prev_btn.width()
|
|
assert bar._next_btn.x() == bar._play_btn.x() + bar._play_btn.width()
|
|
|
|
def test_bubble_keeps_its_footprint(self, qapp, tmp_path):
|
|
"""The bubble must not grow (trav's constraint) nor shrink (the
|
|
minimum button widths preserve the old stretch-padded size)."""
|
|
bar = _build_bar(qapp, tmp_path)
|
|
box = bar._prev_btn.parentWidget()
|
|
assert box.height() == CONTROL_HEIGHT
|
|
assert 106 <= box.width() <= 126 # was 116 pre-split
|