diff options
| author | Paul Buetow <paul@buetow.org> | 2025-07-21 14:46:25 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-07-21 14:46:25 +0300 |
| commit | b562f3b57f8e4f106aad8856e55b51f1d9d9a367 (patch) | |
| tree | b8730261a43c94d5225d95fea19d4151d73d4ca7 /internal/gui/audio_player.go | |
| parent | 6e8f1ffb85cddd841614a156b291b881a1f6a875 (diff) | |
Release v0.7.0: Add auto-play feature and improve keyboard navigationv0.7.0
New Features:
- Added auto-play feature for audio with 'u'/'у' hotkey toggle
- Auto-play is enabled by default on app startup
- Automatically plays audio when new files are generated
- Automatically plays audio when navigating between cards
- Added Escape key support to unfocus all input fields
- Works for Bulgarian word input
- Works for English translation input
- Works for image prompt multi-line input
Improvements:
- Increased default window size by 10% (800x700 → 880x770)
- Fixed duplicate hotkey handling for 'u', 'h', and 'l' keys
- Fixed tooltip initialization error by deferring audio player tooltips
Bug Fixes:
- Fixed Fyne threading errors in audio playback
- Fixed tooltip layer initialization order issue
- Removed duplicate keyboard shortcut handlers
The auto-play feature enhances the learning experience by automatically
playing pronunciation audio when cards are generated or when navigating
through existing cards. Users can toggle this feature on/off at any time
using the 'u' or 'у' hotkey.
Diffstat (limited to 'internal/gui/audio_player.go')
| -rw-r--r-- | internal/gui/audio_player.go | 70 |
1 files changed, 52 insertions, 18 deletions
diff --git a/internal/gui/audio_player.go b/internal/gui/audio_player.go index 38494c5..8b2637b 100644 --- a/internal/gui/audio_player.go +++ b/internal/gui/audio_player.go @@ -7,6 +7,7 @@ import ( "path/filepath" "runtime" "strings" + "time" "fyne.io/fyne/v2" "fyne.io/fyne/v2/container" @@ -20,40 +21,48 @@ import ( type AudioPlayer struct { widget.BaseWidget - container *fyne.Container - playButton *ttwidget.Button - stopButton *ttwidget.Button - statusLabel *widget.Label - - audioFile string - isPlaying bool - playCmd *exec.Cmd - voiceInfo string // Stores voice and speed info + container *fyne.Container + playButton *ttwidget.Button + stopButton *ttwidget.Button + statusLabel *widget.Label + phoneticLabel *widget.Label + + audioFile string + isPlaying bool + playCmd *exec.Cmd + voiceInfo string // Stores voice and speed info + autoPlayEnabled *bool // Pointer to parent's auto-play state } // NewAudioPlayer creates a new audio player widget func NewAudioPlayer() *AudioPlayer { p := &AudioPlayer{} - // Create controls with tooltips + // Create controls (tooltips will be set later after tooltip layer is created) p.playButton = ttwidget.NewButton("", p.onPlay) p.playButton.Icon = theme.MediaPlayIcon() - p.playButton.SetToolTip("Play audio (P)") p.stopButton = ttwidget.NewButton("", p.onStop) p.stopButton.Icon = theme.MediaStopIcon() - p.stopButton.SetToolTip("Stop audio") p.statusLabel = widget.NewLabel("No audio loaded") + // Create phonetic label + p.phoneticLabel = widget.NewLabel("") + p.phoneticLabel.TextStyle = fyne.TextStyle{ + Bold: true, + Italic: true, + } + // Initially disable controls p.playButton.Disable() p.stopButton.Disable() - // Create main container + // Create main container with phonetic display p.container = container.NewHBox( p.playButton, p.stopButton, + p.phoneticLabel, layout.NewSpacer(), p.statusLabel, ) @@ -74,13 +83,13 @@ func (p *AudioPlayer) SetAudioFile(audioFile string) { if audioFile != "" { p.playButton.Enable() - + // Try to load voice metadata wordDir := filepath.Dir(audioFile) metadataFile := filepath.Join(wordDir, "audio_metadata.txt") voice := "" speed := "" - + if data, err := os.ReadFile(metadataFile); err == nil { lines := strings.Split(string(data), "\n") for _, line := range lines { @@ -91,17 +100,29 @@ func (p *AudioPlayer) SetAudioFile(audioFile string) { } } } - + // Store voice info if voice != "" && speed != "" { p.voiceInfo = fmt.Sprintf(" (voice: %s, speed: %s)", voice, speed) } else { p.voiceInfo = "" } - + // Format status text with voice and speed info statusText := fmt.Sprintf("Audio: %s%s", filepath.Base(audioFile), p.voiceInfo) p.statusLabel.SetText(statusText) + + // Auto-play if enabled + if p.autoPlayEnabled != nil && *p.autoPlayEnabled { + // Small delay to ensure UI is ready + go func() { + // Wait a tiny bit for UI to be ready + time.Sleep(100 * time.Millisecond) + fyne.Do(func() { + p.onPlay() + }) + }() + } } else { p.Clear() } @@ -116,6 +137,17 @@ func (p *AudioPlayer) Clear() { p.playButton.Disable() p.stopButton.Disable() p.statusLabel.SetText("No audio loaded") + p.phoneticLabel.SetText("") +} + +// SetPhonetic sets the phonetic transcription text +func (p *AudioPlayer) SetPhonetic(phonetic string) { + p.phoneticLabel.SetText(phonetic) +} + +// SetAutoPlayEnabled sets the reference to the auto-play state +func (p *AudioPlayer) SetAutoPlayEnabled(autoPlayEnabled *bool) { + p.autoPlayEnabled = autoPlayEnabled } // onPlay handles play button click @@ -158,7 +190,9 @@ func (p *AudioPlayer) onStop() { // Play triggers audio playback func (p *AudioPlayer) Play() { if !p.playButton.Disabled() { - p.onPlay() + fyne.Do(func() { + p.onPlay() + }) } } |
