Add video interaction: double-click fullscreen and right-click play/pause

Implements intuitive video player interactions for enhanced user experience.

Video Interaction Features:
- Double-click video area to toggle fullscreen
- Right-click video area to play/pause
- Single-click does nothing (reserved for future use)
- Transparent tappable overlay on video canvas

Implementation:
- Created TappableOverlay widget in internal/ui/tappable.go
- Invisible widget captures tap, double-tap, and secondary-tap events
- Extends widget.BaseWidget for Fyne compatibility
- Added overlay to stage container after video image

User Experience:
- Double-click anywhere on video → instant fullscreen
- Right-click anywhere on video → quick play/pause
- Works alongside existing keyboard shortcuts (F11, Space, ESC)
- Play button icon updates when using right-click

Technical Details:
- TappableOverlay has no visual representation
- Implements Tapped(), DoubleTapped(), TappedSecondary()
- Callbacks are configurable per instance
- Positioned as top layer in container.NewMax() stack

Usage:
1. Load a video
2. Double-click video to enter fullscreen
3. Right-click to pause/play
4. ESC or F11 to exit fullscreen

Next Steps:
- Consider adding single-click functionality
- Add visual feedback for interactions
- Implement mouse cursor auto-hide in fullscreen

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Stu 2025-12-09 11:23:26 -05:00
parent b73beb1fef
commit e25f0c4284
2 changed files with 92 additions and 0 deletions

64
internal/ui/tappable.go Normal file
View File

@ -0,0 +1,64 @@
package ui
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/widget"
)
// TappableOverlay is an invisible widget that captures tap and double-tap events
type TappableOverlay struct {
widget.BaseWidget
OnTapped func()
OnDoubleTapped func()
OnSecondaryTapped func()
}
// NewTappableOverlay creates a new tappable overlay
func NewTappableOverlay(onTapped, onDoubleTapped, onSecondaryTapped func()) *TappableOverlay {
t := &TappableOverlay{
OnTapped: onTapped,
OnDoubleTapped: onDoubleTapped,
OnSecondaryTapped: onSecondaryTapped,
}
t.ExtendBaseWidget(t)
return t
}
// Tapped handles single tap events
func (t *TappableOverlay) Tapped(*fyne.PointEvent) {
if t.OnTapped != nil {
t.OnTapped()
}
}
// TappedSecondary handles right-click events
func (t *TappableOverlay) TappedSecondary(*fyne.PointEvent) {
if t.OnSecondaryTapped != nil {
t.OnSecondaryTapped()
}
}
// DoubleTapped handles double-tap events
func (t *TappableOverlay) DoubleTapped(*fyne.PointEvent) {
if t.OnDoubleTapped != nil {
t.OnDoubleTapped()
}
}
// CreateRenderer implements fyne.Widget
func (t *TappableOverlay) CreateRenderer() fyne.WidgetRenderer {
return &tappableRenderer{}
}
// MinSize returns minimum size (should fill parent)
func (t *TappableOverlay) MinSize() fyne.Size {
return fyne.NewSize(1, 1)
}
type tappableRenderer struct{}
func (r *tappableRenderer) Layout(size fyne.Size) {}
func (r *tappableRenderer) MinSize() fyne.Size { return fyne.NewSize(1, 1) }
func (r *tappableRenderer) Refresh() {}
func (r *tappableRenderer) Objects() []fyne.CanvasObject { return nil }
func (r *tappableRenderer) Destroy() {}

28
main.go
View File

@ -1068,6 +1068,34 @@ func (s *appState) showPlayerView() {
})
}
// Add tappable overlay to stage for video interactions
// Double-click toggles fullscreen, right-click plays/pauses
videoTapper := ui.NewTappableOverlay(
nil, // Single tap does nothing for now
func() {
// Double-tap: toggle fullscreen
s.toggleFullscreen()
},
func() {
// Right-click: toggle play/pause
if !ensureSession() {
return
}
if s.playerPaused {
s.playSess.Play()
s.playerPaused = false
playBtn.SetText(ui.IconPause)
} else {
s.playSess.Pause()
s.playerPaused = true
playBtn.SetText(ui.IconPlayArrow)
}
},
)
// Add tapper to stage
stage.Objects = append(stage.Objects, videoTapper)
playerArea = container.NewBorder(
nil,
container.NewVBox(container.NewPadded(progressBar), container.NewPadded(controlRow)),