diff --git a/internal/ui/queueview.go b/internal/ui/queueview.go index 194025d..911ac95 100644 --- a/internal/ui/queueview.go +++ b/internal/ui/queueview.go @@ -29,7 +29,7 @@ func BuildQueueView( onClear func(), onClearAll func(), titleColor, bgColor, textColor color.Color, -) fyne.CanvasObject { +) (fyne.CanvasObject, *container.Scroll) { // Header title := canvas.NewText("JOB QUEUE", titleColor) title.TextStyle = fyne.TextStyle{Monospace: true, Bold: true} @@ -87,7 +87,7 @@ func BuildQueueView( scrollable, ) - return container.NewPadded(body) + return container.NewPadded(body), scrollable } // buildJobItem creates a single job item in the queue list diff --git a/main.go b/main.go index 1acca09..fe6773c 100644 --- a/main.go +++ b/main.go @@ -179,6 +179,8 @@ type appState struct { jobQueue *queue.Queue statsBar *ui.ConversionStatsBar queueBtn *widget.Button + queueScroll *container.Scroll + queueOffset fyne.Position } func (s *appState) stopPreview() { @@ -430,6 +432,15 @@ func (s *appState) showQueue() { s.stopPreview() s.stopPlayer() s.active = "queue" + s.refreshQueueView() +} + +// refreshQueueView rebuilds the queue UI while preserving scroll position and inline active conversion. +func (s *appState) refreshQueueView() { + // Preserve current scroll offset if we already have a view + if s.queueScroll != nil { + s.queueOffset = s.queueScroll.Offset + } jobs := s.jobQueue.List() // If a direct conversion is running but not represented in the queue, surface it as a pseudo job. @@ -444,72 +455,79 @@ func (s *appState) showQueue() { }}, jobs...) } - view := ui.BuildQueueView( + view, scroll := ui.BuildQueueView( jobs, s.showMainMenu, // onBack func(id string) { // onPause if err := s.jobQueue.Pause(id); err != nil { logging.Debug(logging.CatSystem, "failed to pause job: %v", err) } - s.showQueue() // Refresh + s.refreshQueueView() // Refresh }, func(id string) { // onResume if err := s.jobQueue.Resume(id); err != nil { logging.Debug(logging.CatSystem, "failed to resume job: %v", err) } - s.showQueue() // Refresh + s.refreshQueueView() // Refresh }, func(id string) { // onCancel if err := s.jobQueue.Cancel(id); err != nil { logging.Debug(logging.CatSystem, "failed to cancel job: %v", err) } - s.showQueue() // Refresh + s.refreshQueueView() // Refresh }, func(id string) { // onRemove if err := s.jobQueue.Remove(id); err != nil { logging.Debug(logging.CatSystem, "failed to remove job: %v", err) } - s.showQueue() // Refresh + s.refreshQueueView() // Refresh }, func(id string) { // onMoveUp if err := s.jobQueue.MoveUp(id); err != nil { logging.Debug(logging.CatSystem, "failed to move job up: %v", err) } - s.showQueue() // Refresh + s.refreshQueueView() // Refresh }, func(id string) { // onMoveDown if err := s.jobQueue.MoveDown(id); err != nil { logging.Debug(logging.CatSystem, "failed to move job down: %v", err) } - s.showQueue() // Refresh + s.refreshQueueView() // Refresh }, func() { // onPauseAll s.jobQueue.PauseAll() - s.showQueue() + s.refreshQueueView() }, func() { // onResumeAll s.jobQueue.ResumeAll() - s.showQueue() + s.refreshQueueView() }, func() { // onStart s.jobQueue.ResumeAll() - s.showQueue() + s.refreshQueueView() }, func() { // onClear s.jobQueue.Clear() s.clearVideo() - s.showQueue() // Refresh + s.refreshQueueView() // Refresh }, func() { // onClearAll s.jobQueue.ClearAll() s.clearVideo() - s.showQueue() // Refresh + s.refreshQueueView() // Refresh }, utils.MustHex("#4CE870"), // titleColor gridColor, // bgColor textColor, // textColor ) + // Restore scroll offset + s.queueScroll = scroll + if s.queueScroll != nil { + s.queueScroll.Offset = s.queueOffset + s.queueScroll.Refresh() + } + s.setContent(container.NewPadded(view)) } @@ -1260,7 +1278,7 @@ func runGUI() { state.updateStatsBar() state.updateQueueButtonLabel() if state.active == "queue" { - state.showQueue() + state.refreshQueueView() } }, false) })