2 Commits
Author SHA1 Message Date
ThePendulum 8c34a0f0c2 Give each tab its own full timeline and transport (PlayerPanel refactor)
Extracts a new PlayerPanel widget (video + its own seek slider/marker
strip + its own centered frame-step/play/mute transport row) into
player_panel.py. Both Source and Preview tabs now use one, so
switching tabs switches a complete, independent monitor -- its own
scrubber, its own play state, its own frame-stepping -- not just the
video frame with a single shared play button underneath.

main.py's global keyboard shortcuts (,/./space/left/right) now
dispatch to whichever panel is active via a small _active_panel()
helper, so they follow the same 'whichever monitor you're looking at'
rule as the on-screen controls.

MarkerStrip (clap detection) and the Mark Audio/Video/offset/export
row stay outside the tabs, at the MainWindow level -- marking and
exporting are inherently about the source file, not the preview clip,
so only the timeline+transport moved, not the whole marking workflow.

Updated every existing smoke test for the new win.source_panel.* /
win.preview_panel.* structure (previously flat win.player,
win.seek_slider, etc.), and added a new check to
smoke_test_preview.py confirming the Preview tab's slider is a
genuinely separate, functional widget -- dragging it seeks the preview
player without moving the source player, which is the actual thing
this change was for.

Verified the full suite on the virtual Xvfb display, plus a live
click-through (mark both events, build preview, confirm the Preview
tab shows its own populated timeline/transport, not the source's).
2026-07-26 03:58:23 +02:00
ThePendulum 6f76b3f15a Arrow-key skip preserves play/pause state instead of always pausing
_seek_relative() was unconditionally calling player.pause() before
seeking. mpv's seek command doesn't itself change pause state, so
simply not forcing it is enough: paused stays paused, playing keeps
playing through the skip. (Frame-step still always pauses, correctly
-- that's for precisely inspecting a single frame, a different
use case from a coarse 5s nudge.)

Verified by calling _seek_relative() directly rather than simulating
the QShortcut keypress: a debug script isolated that PlayerWidget.seek()
itself works fine while playing, and QTest.keyClick against this
shortcut turned out to be unreliable in this headless setup even with
Qt's active-window state forced -- consistent with the flakiness
already noted in smoke_test_arrow_seek.py. The real key binding itself
was already confirmed with live xdotool keypresses in an earlier
session; this only needed to test the new state-preservation logic.
2026-07-26 02:44:37 +02:00