summaryrefslogtreecommitdiff
path: root/internal/gui/app.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/gui/app.go')
-rw-r--r--internal/gui/app.go142
1 files changed, 127 insertions, 15 deletions
diff --git a/internal/gui/app.go b/internal/gui/app.go
index 28c703e..3783eb9 100644
--- a/internal/gui/app.go
+++ b/internal/gui/app.go
@@ -35,6 +35,7 @@ type Application struct {
progressBar *widget.ProgressBar
statusLabel *widget.Label
queueStatusLabel *widget.Label
+ imagePromptEntry *widget.Entry
// Navigation buttons
prevWordBtn *widget.Button
@@ -56,6 +57,7 @@ type Application struct {
savedCards []anki.Card
existingWords []string // Words already in anki_cards folder
currentWordIndex int
+ deleteConfirming bool // Track if we're in delete confirmation mode
// Word processing queue
queue *WordQueue
@@ -155,9 +157,9 @@ func (a *Application) setupUI() {
a.wordInput.SetPlaceHolder("Enter Bulgarian word...")
a.wordInput.OnSubmitted = func(string) { a.onSubmit() }
- a.submitButton = widget.NewButton("Generate", a.onSubmit)
- a.prevWordBtn = widget.NewButton("◀ Prev", a.onPrevWord)
- a.nextWordBtn = widget.NewButton("Next ▶", a.onNextWord)
+ a.submitButton = widget.NewButton("Generate (G)", a.onSubmit)
+ a.prevWordBtn = widget.NewButton("◀ Prev (←)", a.onPrevWord)
+ a.nextWordBtn = widget.NewButton("Next (→) ▶", a.onNextWord)
inputSection := container.NewBorder(
nil, nil,
@@ -172,19 +174,40 @@ func (a *Application) setupUI() {
a.translationText = widget.NewLabel("")
a.translationText.Alignment = fyne.TextAlignCenter
+ // Create image prompt entry
+ a.imagePromptEntry = widget.NewMultiLineEntry()
+ a.imagePromptEntry.SetPlaceHolder("Custom image prompt (optional)...")
+ a.imagePromptEntry.Wrapping = fyne.TextWrapWord // Enable word wrapping
+
+ // Create container for image and prompt with proper sizing
+ promptContainer := container.NewBorder(
+ widget.NewLabel("Image Prompt:"),
+ nil,
+ nil,
+ nil,
+ container.NewScroll(a.imagePromptEntry),
+ )
+
+ // Use a split container to give equal space to image and prompt
+ imageSection := container.NewHSplit(
+ a.imageDisplay,
+ promptContainer,
+ )
+ imageSection.SetOffset(0.5) // Equal 50/50 split
+
displaySection := container.NewBorder(
a.translationText,
a.audioPlayer,
nil, nil,
- a.imageDisplay,
+ imageSection,
)
// Create action buttons
- a.keepButton = widget.NewButton("New Word", a.onKeepAndContinue)
- a.regenerateImageBtn = widget.NewButton("Regenerate Image", a.onRegenerateImage)
- a.regenerateAudioBtn = widget.NewButton("Regenerate Audio", a.onRegenerateAudio)
- a.regenerateAllBtn = widget.NewButton("Regenerate All", a.onRegenerateAll)
- a.deleteButton = widget.NewButton("Delete", a.onDelete)
+ a.keepButton = widget.NewButton("New Word (N)", a.onKeepAndContinue)
+ a.regenerateImageBtn = widget.NewButton("Regenerate Image (I)", a.onRegenerateImage)
+ a.regenerateAudioBtn = widget.NewButton("Regenerate Audio (A)", a.onRegenerateAudio)
+ a.regenerateAllBtn = widget.NewButton("Regenerate All (R)", a.onRegenerateAll)
+ a.deleteButton = widget.NewButton("Delete (D)", a.onDelete)
a.deleteButton.Importance = widget.DangerImportance
// Initially disable action buttons
@@ -248,6 +271,9 @@ func (a *Application) setupUI() {
a.queue.Stop()
a.wg.Wait()
})
+
+ // Set up keyboard shortcuts
+ a.setupKeyboardShortcuts()
}
// Run starts the GUI application
@@ -268,8 +294,11 @@ func (a *Application) onSubmit() {
return
}
- // Add word to processing queue
- job := a.queue.AddWord(word)
+ // Get custom prompt from the UI
+ customPrompt := a.imagePromptEntry.Text
+
+ // Add word to processing queue with custom prompt
+ job := a.queue.AddWordWithPrompt(word, customPrompt)
// Clear the input field for next word
a.wordInput.SetText("")
@@ -323,12 +352,16 @@ func (a *Application) generateMaterials(word string) {
a.audioPlayer.SetAudioFile(audioFile)
})
- // Generate images
+ // Generate images with custom prompt if provided
fyne.Do(func() {
a.updateStatus("Downloading images...")
a.incrementProcessing() // Image processing starts
})
- images, err := a.generateImages(word)
+
+ // Get custom prompt from UI
+ customPrompt := a.imagePromptEntry.Text
+
+ images, err := a.generateImagesWithPrompt(word, customPrompt)
a.decrementProcessing() // Image processing ends
if err != nil {
@@ -414,6 +447,9 @@ func (a *Application) onRegenerateImage() {
// Clear the current image immediately
a.imageDisplay.Clear()
+ // Get custom prompt from UI
+ customPrompt := a.imagePromptEntry.Text
+
a.incrementProcessing() // Image processing starts
a.wg.Add(1)
@@ -421,7 +457,7 @@ func (a *Application) onRegenerateImage() {
defer a.wg.Done()
defer a.decrementProcessing() // Image processing ends
- images, err := a.generateImages(a.currentWord)
+ images, err := a.generateImagesWithPrompt(a.currentWord, customPrompt)
if err != nil {
fyne.Do(func() {
a.showError(fmt.Errorf("Image regeneration failed: %w", err))
@@ -591,6 +627,7 @@ func (a *Application) clearUI() {
a.imageDisplay.Clear()
a.audioPlayer.Clear()
a.translationText.SetText("")
+ a.imagePromptEntry.SetText("")
a.setActionButtonsEnabled(false)
}
@@ -687,7 +724,8 @@ func (a *Application) processWordJob(job *WordJob) {
a.incrementProcessing() // Image processing starts
})
- imageFiles, err := a.generateImages(job.Word)
+ // Use the custom prompt from the job
+ imageFiles, err := a.generateImagesWithPrompt(job.Word, job.CustomPrompt)
a.decrementProcessing() // Image processing ends
if err != nil {
@@ -855,3 +893,77 @@ func (a *Application) decrementProcessing() {
})
}
+// setupKeyboardShortcuts sets up keyboard shortcuts for the application
+func (a *Application) setupKeyboardShortcuts() {
+ // Create a custom shortcut handler
+ a.window.Canvas().SetOnTypedKey(func(ev *fyne.KeyEvent) {
+ // Don't process shortcuts if the word input is focused
+ if a.window.Canvas().Focused() == a.wordInput || a.window.Canvas().Focused() == a.imagePromptEntry {
+ return
+ }
+
+ // Don't process if we're in delete confirmation mode (handled by dialog)
+ if a.deleteConfirming {
+ return
+ }
+
+ switch ev.Name {
+ case fyne.KeyG: // Generate
+ if a.submitButton.Disabled() {
+ return
+ }
+ a.onSubmit()
+
+ case fyne.KeyN: // New Word
+ if a.keepButton.Disabled() {
+ return
+ }
+ a.onKeepAndContinue()
+
+ case fyne.KeyI: // Regenerate Image
+ if a.regenerateImageBtn.Disabled() {
+ return
+ }
+ a.onRegenerateImage()
+
+ case fyne.KeyA: // Regenerate Audio
+ if a.regenerateAudioBtn.Disabled() {
+ return
+ }
+ a.onRegenerateAudio()
+
+ case fyne.KeyR: // Regenerate All
+ if a.regenerateAllBtn.Disabled() {
+ return
+ }
+ a.onRegenerateAll()
+
+ case fyne.KeyD: // Delete
+ if a.deleteButton.Disabled() {
+ return
+ }
+ a.onDelete()
+
+ case fyne.KeyLeft: // Previous word
+ if a.prevWordBtn.Disabled() {
+ return
+ }
+ a.onPrevWord()
+
+ case fyne.KeyRight: // Next word
+ if a.nextWordBtn.Disabled() {
+ return
+ }
+ a.onNextWord()
+
+ case fyne.KeyP: // Play audio
+ if a.currentAudioFile != "" {
+ a.audioPlayer.Play()
+ }
+
+ case fyne.KeyEscape: // Cancel any operation
+ a.deleteConfirming = false
+ }
+ })
+}
+