Fix off-center Navidrome layout
Two separate imbalances: - Cover art was top-aligned when width-constrained (landscape, cover_size capped by width*0.5), leaving dead space below it instead of centering in the available height. - The text column always started drawing at the very top and let the source line's bottom-pin create a big empty gap for tracks with short/sparse metadata -- looked top-heavy and unbalanced. Text column is now built as (height, draw_fn) pairs first so the total height is known before committing to a starting y, then centered in the space above the reserved source-line area.
This commit is contained in:
1 parent
a19a7ea49e
commit
5baa45904c
1 file changed
+67
-29
+67
-29
@@ -199,8 +199,10 @@ class NavidromeScreen(Screen):
|
||||
# Cap at a fraction of width too, not just height -- a naive full-height
|
||||
# square cover can eat almost the whole canvas on a wide/short panel and
|
||||
# leave the text column too narrow to wrap sensibly.
|
||||
cover_size = min(height - 2 * pad, int(width * 0.5))
|
||||
cover_x, cover_y = pad, pad
|
||||
available_h = height - 2 * pad
|
||||
cover_size = min(available_h, int(width * 0.5))
|
||||
cover_x = pad
|
||||
cover_y = pad + (available_h - cover_size) // 2 # center vertically when width-constrained
|
||||
|
||||
self._paste_cover(image, cover, cover_x, cover_y, cover_size)
|
||||
|
||||
@@ -244,52 +246,89 @@ class NavidromeScreen(Screen):
|
||||
tw = text_width(draw, text, font)
|
||||
return x + (w - tw) / 2 if center else x
|
||||
|
||||
cursor = y
|
||||
# Reserve space for the bottom-pinned source line up front, so the
|
||||
# block above can be vertically centered in what's actually left
|
||||
# rather than always starting at the very top -- hugging the top
|
||||
# left a large, unbalanced gap above the source line whenever the
|
||||
# track had short/sparse metadata.
|
||||
show_source = settings.get("show_source", True)
|
||||
source_reserved_h = source_font.size * 1.6 if show_source else 0
|
||||
content_h = h - source_reserved_h
|
||||
|
||||
# Build the content block as (height, draw_fn) pairs first so its
|
||||
# total height is known before committing to a starting y.
|
||||
items: list[tuple[float, object]] = []
|
||||
|
||||
status = "ZULETZT GESPIELT" if idle else "● NOW PLAYING"
|
||||
draw.text((centered_x(status, status_font), cursor), status, font=status_font, fill=(100, 100, 100))
|
||||
cursor += status_font.size * 1.8
|
||||
|
||||
def draw_status(yy):
|
||||
draw.text((centered_x(status, status_font), yy), status, font=status_font, fill=(100, 100, 100))
|
||||
|
||||
items.append((status_font.size * 1.8, draw_status))
|
||||
|
||||
title = track["title"] + (" ★" if track["starred"] else "")
|
||||
for line in wrap_to_lines(draw, title, title_font, w, max_lines=2):
|
||||
draw.text((centered_x(line, title_font), cursor), line, font=title_font, fill="black")
|
||||
cursor += title_font.size * 1.15
|
||||
def draw_title_line(yy, line=line):
|
||||
draw.text((centered_x(line, title_font), yy), line, font=title_font, fill="black")
|
||||
items.append((title_font.size * 1.15, draw_title_line))
|
||||
if items:
|
||||
items[-1] = (items[-1][0] + title_font.size * 0.2, items[-1][1])
|
||||
|
||||
cursor += title_font.size * 0.2
|
||||
artist_line = truncate_to_width(draw, track["artist"], artist_font, w)
|
||||
draw.text((centered_x(artist_line, artist_font), cursor), artist_line, font=artist_font, fill=(30, 30, 30))
|
||||
cursor += artist_font.size * 1.4
|
||||
|
||||
def draw_artist(yy):
|
||||
draw.text((centered_x(artist_line, artist_font), yy), artist_line, font=artist_font, fill=(30, 30, 30))
|
||||
|
||||
items.append((artist_font.size * 1.4, draw_artist))
|
||||
|
||||
if track["album"]:
|
||||
album_line = track["album"]
|
||||
if track["year"]:
|
||||
album_line += f" · {track['year']}"
|
||||
album_line = truncate_to_width(draw, album_line, album_font, w)
|
||||
draw.text((centered_x(album_line, album_font), cursor), album_line, font=album_font, fill=(110, 110, 110))
|
||||
cursor += album_font.size * 1.4
|
||||
|
||||
def draw_album(yy, album_line=album_line):
|
||||
draw.text((centered_x(album_line, album_font), yy), album_line, font=album_font, fill=(110, 110, 110))
|
||||
|
||||
items.append((album_font.size * 1.4, draw_album))
|
||||
|
||||
if settings.get("show_genre", True) and track["genre"]:
|
||||
genre_line = truncate_to_width(draw, track["genre"].upper(), album_font, w)
|
||||
draw.text((centered_x(genre_line, album_font), cursor), genre_line, font=album_font, fill=(150, 150, 150))
|
||||
cursor += album_font.size * 1.4
|
||||
|
||||
def draw_genre(yy, genre_line=genre_line):
|
||||
draw.text((centered_x(genre_line, album_font), yy), genre_line, font=album_font, fill=(150, 150, 150))
|
||||
|
||||
items.append((album_font.size * 1.4, draw_genre))
|
||||
|
||||
if settings.get("show_progress", False) and not idle and track["duration"]:
|
||||
bar_y = cursor + status_font.size * 0.3
|
||||
bar_h = max(3, int(scale * 0.012))
|
||||
draw.rectangle((x, bar_y, x + w, bar_y + bar_h), outline=(60, 60, 60))
|
||||
fill_w = int(w * min(1.0, track["progress_pct"]))
|
||||
if fill_w > 0:
|
||||
draw.rectangle((x, bar_y, x + fill_w, bar_y + bar_h), fill=(60, 60, 60))
|
||||
cursor = bar_y + bar_h + status_font.size * 0.4
|
||||
times = f"{track['elapsed_text']} / {track['duration_text']}"
|
||||
draw.text((centered_x(times, source_font), cursor), times, font=source_font, fill=(110, 110, 110))
|
||||
cursor += source_font.size * 1.6
|
||||
bar_gap = status_font.size * 0.3
|
||||
|
||||
def draw_progress(yy, bar_h=bar_h, bar_gap=bar_gap):
|
||||
bar_y = yy + bar_gap
|
||||
draw.rectangle((x, bar_y, x + w, bar_y + bar_h), outline=(60, 60, 60))
|
||||
fill_w = int(w * min(1.0, track["progress_pct"]))
|
||||
if fill_w > 0:
|
||||
draw.rectangle((x, bar_y, x + fill_w, bar_y + bar_h), fill=(60, 60, 60))
|
||||
times = f"{track['elapsed_text']} / {track['duration_text']}"
|
||||
draw.text((centered_x(times, source_font), bar_y + bar_h + status_font.size * 0.4), times, font=source_font, fill=(110, 110, 110))
|
||||
|
||||
items.append((bar_gap + bar_h + status_font.size * 0.4 + source_font.size * 1.6, draw_progress))
|
||||
elif track["duration"]:
|
||||
duration_badge = track["duration_text"]
|
||||
draw.text((centered_x(duration_badge, source_font), cursor), duration_badge, font=source_font, fill=(110, 110, 110))
|
||||
cursor += source_font.size * 1.6
|
||||
|
||||
if settings.get("show_source", True):
|
||||
def draw_duration(yy, duration_badge=duration_badge):
|
||||
draw.text((centered_x(duration_badge, source_font), yy), duration_badge, font=source_font, fill=(110, 110, 110))
|
||||
|
||||
items.append((source_font.size * 1.6, draw_duration))
|
||||
|
||||
total_h = sum(item_h for item_h, _ in items)
|
||||
cursor = y + max(0.0, (content_h - total_h) / 2)
|
||||
for item_h, draw_fn in items:
|
||||
draw_fn(cursor)
|
||||
cursor += item_h
|
||||
|
||||
if show_source:
|
||||
if idle:
|
||||
source_line = f"vor {track['minutes_ago']} min"
|
||||
else:
|
||||
@@ -298,9 +337,8 @@ class NavidromeScreen(Screen):
|
||||
if source_line:
|
||||
source_line = truncate_to_width(draw, source_line, source_font, w)
|
||||
# Prefer pinning to the bottom of the available area, but never
|
||||
# overlap whatever the cursor already drew above it -- long
|
||||
# titles/metadata can push past where a fixed bottom position
|
||||
# would otherwise land.
|
||||
# overlap whatever the block above drew -- long titles/metadata
|
||||
# can still push past where a fixed bottom position would land.
|
||||
bottom_y = max(cursor + status_font.size * 0.4, y + h - source_font.size * 1.3)
|
||||
draw.text((centered_x(source_line, source_font), bottom_y), source_line, font=source_font, fill=(140, 140, 140))
|
||||
|
||||
|
||||
Reference in new issue
Block a user