v0.44.0: per-track volume, laser rig diagram, bolder Preferences headings

Get Info has a Volume slider (−100%…+100%): iTunes' own per-song volume
adjustment, which the library already imported but never used. It scales
the volume slider while that song plays; a boost can't pass full volume.
Works on one song or several (Mixed when they differ), and undoes.

Get Info's Previous/Next buttons are gone: since the combined multi-song
editor they were always disabled (and blank in some themes).

The laser settings draw the rig — speaker and mirror, laser, beam, and the
screen at its distance and angles — as a small textbook line drawing that
labels distance, angles and dot size in place of the sliders' numbers.

Preferences section headings are bold and 2 px larger than the text.
This commit is contained in:
2026-10-10 01:53:07 -07:00
parent 599972fa1a
commit 8339e0404f
8 changed files with 600 additions and 39 deletions
+11
View File
@@ -139,6 +139,14 @@ persistence) → GUI (Qt widgets that read the manager and connect to its signal
without recursing. Widgets react to its signals (`playlists_changed`,
`playlist_content_changed(pid)`, `track_updated(id)`, `track_fields_edited`).
- **Per-track volume** (Round 79): `Track.volume_adjustment` is iTunes'
"Volume Adjustment", imported as-is (±255 = ±100%) and edited in Get Info.
`player.volume_with_adjustment` scales the slider's *perceptual* level by
it: −100% is silence, and a boost can't pass full. `Player` applies it
through `_output_volume()` everywhere it pushes volume to a sink, before
each load, and on `refresh_volume()` (MainWindow calls that when the
playing track is edited). `Player.volume()` stays the bare slider value.
- **`lintunes/player.py`** — `Player(QObject)` walks a queue through a swappable
**`PlaybackSink`**. Player owns the queue, shuffle walk, per-track start/stop
times and the play-count/scrobble bookkeeping; a sink owns only "make this file
@@ -232,6 +240,9 @@ persistence) → GUI (Qt widgets that read the manager and connect to its signal
"Custom" whenever the settings match no preset. A setting added later
must join `STYLE_KEYS` and register a setter, or presets silently skip it
(`test_every_style_key_has_a_control` pins that).
The laser page draws the rig (`gui/laser_diagram.py`, textbook line art
through the renderer's own `_rotation`); it labels distance, angles and
dot size, so those sliders show no number.
The highlight swatches can be repainted with a color picker:
`highlight_palette` overrides a slot, and `theme.highlight_color` reads
through `theme.highlight_palette`, so nothing else needs to know.