The row of boxes keeps one 16px gap before, between and after its controls at every width; the now-playing panel and the seek row under it take all the remaining width. The track lists get the same 8px of bare window on the right that the playlists have on the left. README updates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
172 lines
6.6 KiB
Python
172 lines
6.6 KiB
Python
"""Round 69: the header row moves up into the seek row's band.
|
|
|
|
The Library button, the view's header (search box or playlist name) and the
|
|
seek row share one row under the transport boxes: the Library button as wide
|
|
as the sidebar, the header where the view starts, the seek row exactly under
|
|
the now-playing panel. The row of boxes keeps one GAP before, between and
|
|
after its controls, and the panel fills the rest.
|
|
"""
|
|
|
|
from PyQt6.QtCore import QPoint
|
|
|
|
from lintunes.gui.fit_label import FitLabel
|
|
from lintunes.gui.main_window import MainWindow
|
|
from lintunes.gui.transport import NOW_PLAYING_MIN
|
|
from lintunes.library_manager import LibraryManager
|
|
from lintunes.models.library import Library
|
|
from lintunes.models.playlist import Playlist
|
|
from lintunes.models.track import Track
|
|
from lintunes.preferences import Preferences
|
|
|
|
|
|
def _window(qapp, tmp_path, width=1280):
|
|
library = Library(tracks={1: Track(track_id=1, name="One")})
|
|
library.playlists["abcd1234"] = Playlist(
|
|
persistent_id="abcd1234", name="Road Trip", track_ids=[1])
|
|
window = MainWindow(LibraryManager(library, tmp_path), Preferences(tmp_path))
|
|
window.resize(width, 500)
|
|
window.show()
|
|
qapp.processEvents()
|
|
return window
|
|
|
|
|
|
def _x(widget, window, x=0):
|
|
return widget.mapTo(window, QPoint(x, 0)).x()
|
|
|
|
|
|
def _gx(widget, x=0):
|
|
return widget.mapToGlobal(QPoint(x, 0)).x()
|
|
|
|
|
|
def test_startup_still_opens_on_the_library(qapp, tmp_path):
|
|
# The moved Library button fell to the end of the focus chain, the tree
|
|
# took the first focus and selected its first playlist.
|
|
window = _window(qapp, tmp_path)
|
|
assert window._content.currentWidget() is window._library_view
|
|
assert window._sidebar.library_button.isChecked()
|
|
|
|
|
|
def test_library_button_spans_the_playlist_list(qapp, tmp_path):
|
|
# Edge to edge with the list below it, scrollbar included, with bare
|
|
# window to the left of both.
|
|
window = _window(qapp, tmp_path)
|
|
header = window.sidebar.header
|
|
lists = window.sidebar.list_area
|
|
# The frame around the playlists takes in the album art too.
|
|
assert window.sidebar.art.parentWidget() is lists
|
|
assert header.parentWidget() is not window.sidebar
|
|
assert _gx(header) == _gx(lists) > _gx(window)
|
|
assert header.width() == lists.width()
|
|
button = window.sidebar.library_button
|
|
assert _gx(button) == _gx(lists)
|
|
assert _gx(button, button.width()) == _gx(lists, lists.width())
|
|
|
|
|
|
def test_header_row_items_share_one_floor(qapp, tmp_path):
|
|
from lintunes.gui.transport import HeaderRow
|
|
window = _window(qapp, tmp_path)
|
|
row = window._transport._header_row
|
|
floor = row.height() - HeaderRow.BOTTOM
|
|
|
|
def bottom(widget):
|
|
return widget.mapTo(row, QPoint(0, widget.height())).y()
|
|
|
|
assert bottom(window.sidebar.library_button) == floor
|
|
assert bottom(window._library_view._search) == floor
|
|
# Text and the playhead sit LIFT px up, on the button's bottom line by eye.
|
|
assert bottom(window._transport._slider) == floor - HeaderRow.LIFT
|
|
window._show_playlist("abcd1234")
|
|
qapp.processEvents()
|
|
label = window._playlist_view.header
|
|
baseline = bottom(label) - label.fontMetrics().descent()
|
|
assert abs(baseline - (floor - HeaderRow.LIFT)) <= 1
|
|
|
|
|
|
def test_search_then_title_sit_where_the_view_starts(qapp, tmp_path):
|
|
window = _window(qapp, tmp_path)
|
|
search_strip = window._library_view.header
|
|
assert search_strip.isVisible()
|
|
assert _gx(search_strip) == _gx(window._content)
|
|
|
|
window._show_playlist("abcd1234")
|
|
qapp.processEvents()
|
|
label = window._playlist_view.header
|
|
assert label.isVisible() and label.full_text() == "Road Trip"
|
|
assert _gx(label) == _gx(window._content)
|
|
# The search strip went home to its view, hidden.
|
|
assert not search_strip.isVisible()
|
|
assert search_strip.parentWidget() is window._library_view
|
|
|
|
|
|
def test_header_row_shares_the_seek_band(qapp, tmp_path):
|
|
window = _window(qapp, tmp_path)
|
|
bar = window._transport
|
|
panel = bar._now_playing
|
|
seek = bar._slider.parentWidget()
|
|
# Under the panel, edge to edge, and level with the Library button.
|
|
assert _gx(seek) == _gx(panel)
|
|
assert seek.width() == panel.width()
|
|
library = window.sidebar.library_button
|
|
row = bar._header_row
|
|
assert row.geometry().top() > panel.geometry().bottom()
|
|
assert library.mapTo(bar, QPoint()).y() >= row.geometry().top()
|
|
|
|
|
|
def test_panel_fills_what_the_gaps_leave(qapp, tmp_path):
|
|
window = _window(qapp, tmp_path, width=1920)
|
|
bar = window._transport
|
|
wide = bar._now_playing.width()
|
|
window.resize(1280, 500)
|
|
qapp.processEvents()
|
|
assert wide - bar._now_playing.width() == 640
|
|
window.resize(800, 500)
|
|
qapp.processEvents()
|
|
assert bar._now_playing.width() >= NOW_PLAYING_MIN
|
|
bpm = bar._bpm_button.parentWidget()
|
|
assert bar._now_playing.geometry().right() < bpm.geometry().left()
|
|
assert bpm.geometry().right() < bar.width()
|
|
|
|
|
|
def test_lists_have_bare_window_on_both_sides(qapp, tmp_path):
|
|
from lintunes.gui.sidebar import SIDEBAR_INSET
|
|
window = _window(qapp, tmp_path)
|
|
lists, view = window.sidebar.list_area, window._library_view
|
|
assert _gx(lists) - _gx(window) == SIDEBAR_INSET
|
|
assert _gx(window, window.width()) - _gx(view, view.width()) == SIDEBAR_INSET
|
|
|
|
|
|
def test_views_seek_row_never_overlaps_the_header(qapp, tmp_path):
|
|
window = _window(qapp, tmp_path, width=800)
|
|
strip = window._library_view.header
|
|
seek = window._transport._slider.parentWidget()
|
|
assert _gx(strip, strip.width()) <= _gx(seek)
|
|
|
|
|
|
def test_long_name_shrinks_then_elides(qapp):
|
|
label = FitLabel()
|
|
base = label.font().pointSize()
|
|
label.resize(120, 30)
|
|
label.setText("A short one")
|
|
assert label.text() == "A short one"
|
|
label.setText("An Extraordinarily Long Playlist Name That Goes On Forever")
|
|
assert label.font().pointSize() >= base - 2
|
|
assert label.text().endswith("…")
|
|
assert label.toolTip() == label.full_text()
|
|
|
|
|
|
def test_row_is_knolled_with_equal_gaps_and_ends(qapp, tmp_path):
|
|
from lintunes.gui.transport import GAP
|
|
window = _window(qapp, tmp_path)
|
|
bar = window._transport
|
|
for width in (900, 1280, 1920):
|
|
window.resize(width, 500)
|
|
qapp.processEvents()
|
|
row = [bar._prev_btn.parentWidget(), bar._shuffle_btn.parentWidget(),
|
|
bar._visualizer, bar._volume_slider, bar._now_playing,
|
|
bar._bpm_button.parentWidget()]
|
|
edges = [(w.geometry().left(), w.geometry().right()) for w in row]
|
|
gaps = [b[0] - a[1] - 1 for a, b in zip(edges, edges[1:])]
|
|
# The same GAP everywhere, ends included, at every width.
|
|
left_end, right_end = edges[0][0], bar.width() - 1 - edges[-1][1]
|
|
assert gaps + [left_end, right_end] == [GAP] * 7
|