fix: center progress thumb on pointer while seeking
This commit is contained in:
@@ -201,9 +201,9 @@ struct CastPlaybackCheck {
|
||||
_ = NSApplication.shared
|
||||
NSApp.setActivationPolicy(.prohibited)
|
||||
require(castVolumeSliderFeedbackCheck(), "Native slider keeps user position during tracking and applies receiver volume when idle")
|
||||
require(playbackSliderHitCheck(), "Progress slider seeks beside the thumb and preserves cursor alignment throughout dragging")
|
||||
require(playbackSliderHitCheck(), "Progress thumb centers on the cursor for clicks and drags, clamped to track endpoints")
|
||||
if CommandLine.arguments.count == 2, CommandLine.arguments[1] == "--slider-regression" {
|
||||
print("PASS: adjacent seeking, thumb and track drag alignment, and volume tracking feedback")
|
||||
print("PASS: centered progress clicks and drags, endpoint clamping, and volume tracking feedback")
|
||||
return
|
||||
}
|
||||
let source = URL(fileURLWithPath: CommandLine.arguments[1])
|
||||
|
||||
@@ -23,7 +23,7 @@ To run without installing, use `make run`. The built `.build/terebi.app` is self
|
||||
|
||||
- **Local files:** use **⌘O**, drag files onto the player, or choose Finder's **Open With**. Multiple files form a queue in natural filename order, with automatic advance. Use **View → Show Queue** to play, reorder or remove items; **+** or dropping files onto the sidebar appends them. The queue lasts only for the session; opening other media replaces it.
|
||||
- **Streams:** use **Open Stream…** (**⇧⌘O**) or drop a supported link. YouTube supports individual videos, not playlists or channels. Miruro links must use `https://barelystarted.miruro.tv/watch/<opaque-id>/<slug>?ep=<episode>` with an explicit positive episode number; arbitrary streaming sites are not supported.
|
||||
- **Seeking:** click the progress track to seek, including immediately beside the visible thumb. Grabbing the thumb preserves the cursor's position within it throughout the drag; dragging from the track first seeks to the cursor.
|
||||
- **Seeking:** click or drag the progress track or thumb to seek. The thumb centers on the cursor, clamped to the track endpoints.
|
||||
- **Miruro metadata:** titles and opening/ending intervals load independently of playback when available. Small rounded gaps mark interval boundaries in the slider, without visible labels; accessibility descriptions retain the interval names and times. Missing/invalid metadata leaves playback usable. Timings may differ between cuts. No automatic skipping is performed.
|
||||
- **Resume:** videos resume their saved position; finished videos restart from the beginning. Live and audio-only media do not save progress.
|
||||
- **Compact mode:** press **P** for a floating player; double-click it to restore. Drag noninteractive areas to move the window. Use the titlebar pin to keep the normal window on top.
|
||||
|
||||
@@ -2008,17 +2008,14 @@ private struct PlaybackSlider: NSViewRepresentable {
|
||||
let width = bar.width - cell.knobThickness
|
||||
guard width > 0, maxValue > minValue else { return }
|
||||
let point = convert(event.locationInWindow, from: nil)
|
||||
let knob = cell.visibleKnobRect(cell.knobRect(flipped: isFlipped))
|
||||
let grabbed = NSBezierPath(roundedRect: knob, xRadius: 1, yRadius: 1).contains(point)
|
||||
let offset = grabbed ? point.x - knob.midX : 0
|
||||
func seek(_ x: CGFloat) {
|
||||
let fraction = min(1, max(0, (x - offset - start) / width))
|
||||
let fraction = min(1, max(0, (x - start) / width))
|
||||
let value = minValue + fraction * (maxValue - minValue)
|
||||
guard value != doubleValue else { return }
|
||||
doubleValue = value
|
||||
if let action { sendAction(action, to: target) }
|
||||
}
|
||||
if !grabbed { seek(point.x) }
|
||||
seek(point.x)
|
||||
while let next = window?.nextEvent(matching: [.leftMouseDragged, .leftMouseUp]) {
|
||||
let nextPoint = convert(next.locationInWindow, from: nil)
|
||||
if next.type == .leftMouseDragged || nextPoint.x != point.x { seek(nextPoint.x) }
|
||||
@@ -2130,12 +2127,9 @@ func playbackSliderHitCheck() -> Bool {
|
||||
}
|
||||
slider.mouseDown(with: down)
|
||||
let movedKnob = cell.knobRect(flipped: slider.isFlipped)
|
||||
let expectedMovement = movement + (abs(offset) <= 1 ? 0 : offset)
|
||||
let expectedMovement = movement + offset
|
||||
let expectedX = min(right, max(left, knob.midX + expectedMovement))
|
||||
guard abs(movedKnob.midX - expectedX) < 0.5 else { return false }
|
||||
if abs(offset) <= 1 && movement == 0 {
|
||||
guard slider.doubleValue == initial else { return false }
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user