diff --git a/main.go b/main.go index a2c2c25..c475a21 100644 --- a/main.go +++ b/main.go @@ -899,6 +899,7 @@ type appState struct { // Thumbnail module state thumbFile *videoSource + thumbFiles []*videoSource thumbCount int thumbWidth int thumbContactSheet bool @@ -11120,24 +11121,32 @@ func (s *appState) handleDrop(pos fyne.Position, items []fyne.URI) { return } - // Multi-drop: add jobs for each file and set first as current preview + // Multi-drop: load list and add jobs for each file go func() { - src, err := probeVideo(videoPaths[0]) - if err != nil { - logging.Debug(logging.CatModule, "failed to load video for thumb: %v", err) + var sources []*videoSource + for _, path := range videoPaths { + src, err := probeVideo(path) + if err != nil { + logging.Debug(logging.CatModule, "failed to load video for thumb: %v", err) + continue + } + sources = append(sources, src) + } + if len(sources) == 0 { fyne.CurrentApp().Driver().DoFromGoroutine(func() { - dialog.ShowError(fmt.Errorf("failed to load video: %w", err), s.window) + dialog.ShowError(fmt.Errorf("failed to load video files for thumbnails"), s.window) }, false) return } fyne.CurrentApp().Driver().DoFromGoroutine(func() { - s.thumbFile = src + s.thumbFiles = sources + s.thumbFile = sources[0] s.showThumbView() - logging.Debug(logging.CatModule, "loaded video into thumb module") + logging.Debug(logging.CatModule, "loaded %d videos into thumb module", len(sources)) }, false) - if len(videoPaths) > 1 && s.jobQueue != nil { + if s.jobQueue != nil { for _, path := range videoPaths { s.jobQueue.Add(s.createThumbJobForPath(path)) } diff --git a/thumb_module.go b/thumb_module.go index 2085eb9..6d79d56 100644 --- a/thumb_module.go +++ b/thumb_module.go @@ -27,6 +27,18 @@ func (s *appState) showThumbView() { s.setContent(buildThumbView(s)) } +func (s *appState) addThumbSource(src *videoSource) { + if src == nil { + return + } + for _, existing := range s.thumbFiles { + if existing != nil && existing.Path == src.Path { + return + } + } + s.thumbFiles = append(s.thumbFiles, src) +} + func buildThumbView(state *appState) fyne.CanvasObject { thumbColor := moduleColor("thumb") @@ -100,6 +112,7 @@ func buildThumbView(state *appState) fyne.CanvasObject { } state.thumbFile = src + state.addThumbSource(src) state.showThumbView() logging.Debug(logging.CatModule, "loaded thumbnail file: %s", path) }, state.window) @@ -108,6 +121,7 @@ func buildThumbView(state *appState) fyne.CanvasObject { // Clear button clearBtn := widget.NewButton("Clear", func() { state.thumbFile = nil + state.thumbFiles = nil state.showThumbView() }) clearBtn.Importance = widget.LowImportance @@ -360,9 +374,44 @@ func buildThumbView(state *appState) fyne.CanvasObject { ) // Main content - split layout with preview on left, settings on right - leftColumn := container.NewVBox( - videoContainer, - ) + leftColumn := container.NewVBox(videoContainer) + if len(state.thumbFiles) > 1 { + list := widget.NewList( + func() int { return len(state.thumbFiles) }, + func() fyne.CanvasObject { return widget.NewLabel("") }, + func(i widget.ListItemID, o fyne.CanvasObject) { + if i < 0 || i >= len(state.thumbFiles) { + return + } + label := o.(*widget.Label) + src := state.thumbFiles[i] + if src == nil { + label.SetText("") + return + } + label.SetText(utils.ShortenMiddle(filepath.Base(src.Path), 60)) + }, + ) + list.OnSelected = func(id widget.ListItemID) { + if id < 0 || id >= len(state.thumbFiles) { + return + } + state.thumbFile = state.thumbFiles[id] + state.showThumbView() + } + if state.thumbFile != nil { + for i, src := range state.thumbFiles { + if src != nil && src.Path == state.thumbFile.Path { + list.Select(i) + break + } + } + } + listScroll := container.NewVScroll(list) + listScroll.SetMinSize(fyne.NewSize(0, 140)) + leftColumn.Add(widget.NewLabel("Loaded Videos:")) + leftColumn.Add(listScroll) + } rightColumn := container.NewVBox( settingsPanel,