From c7d821e03af033f48d940f6c8232e078abf6a238 Mon Sep 17 00:00:00 2001 From: Stu Leak Date: Fri, 5 Dec 2025 10:03:31 -0500 Subject: [PATCH] Add menu bar and center playback controls UI improvements: - Add menu bar at top with File, View, and Tools menus - Move File operations (Open, Add Folder, Clear) to File menu - Add Frame-Accurate Mode toggle in Tools menu - Center playback controls (Prev, Play, Next) at bottom - Move volume controls to left, playlist toggle to right - Remove redundant top control bar for cleaner interface - Add keyframingMode state to appState for feature toggle Layout changes: - Menu bar provides access to advanced features - Main player area takes full space below menu - Controls centered bottom like modern video players (Haruna/VLC) - Cleaner interface suitable for basic playback or advanced editing Prepares for: - Frame-accurate navigation features (when keyframing enabled) - Timeline with keyframe markers - In/out point cutting tools - Integration with VideoTools chapter support Co-Authored-By: Claude --- main.go | 119 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 67 insertions(+), 52 deletions(-) diff --git a/main.go b/main.go index bc898b0..caa1990 100644 --- a/main.go +++ b/main.go @@ -197,6 +197,7 @@ type appState struct { queueOffset fyne.Position compareFile1 *videoSource compareFile2 *videoSource + keyframingMode bool // Toggle for frame-accurate editing features } func (s *appState) stopPreview() { @@ -558,60 +559,70 @@ func (s *appState) showPlayerView() { s.stopCompareSessions() s.active = "player" - header := widget.NewLabelWithStyle("VT Player", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}) - // Helper to refresh the view after selection/loads. refresh := func() { s.showPlayerView() } - openFile := widget.NewButton("Open File…", func() { - dlg := dialog.NewFileOpen(func(r fyne.URIReadCloser, err error) { - if err != nil || r == nil { - return - } - path := r.URI().Path() - r.Close() - go s.loadVideo(path) - }, s.window) - dlg.Resize(fyne.NewSize(700, 480)) - dlg.Show() - }) + // Create menu bar + fileMenu := fyne.NewMenu("File", + fyne.NewMenuItem("Open File…", func() { + dlg := dialog.NewFileOpen(func(r fyne.URIReadCloser, err error) { + if err != nil || r == nil { + return + } + path := r.URI().Path() + r.Close() + go s.loadVideo(path) + }, s.window) + dlg.Resize(fyne.NewSize(700, 480)) + dlg.Show() + }), + fyne.NewMenuItem("Open Folder…", func() { + dlg := dialog.NewFolderOpen(func(l fyne.ListableURI, err error) { + if err != nil || l == nil { + return + } + paths := s.findVideoFiles(l.Path()) + if len(paths) == 0 { + return + } + go s.loadVideos(paths) + }, s.window) + dlg.Resize(fyne.NewSize(700, 480)) + dlg.Show() + }), + fyne.NewMenuItemSeparator(), + fyne.NewMenuItem("Clear Playlist", func() { + s.clearVideo() + refresh() + }), + ) - addFolder := widget.NewButton("Add Folder…", func() { - dlg := dialog.NewFolderOpen(func(l fyne.ListableURI, err error) { - if err != nil || l == nil { - return - } - paths := s.findVideoFiles(l.Path()) - if len(paths) == 0 { - return - } - go s.loadVideos(paths) - }, s.window) - dlg.Resize(fyne.NewSize(700, 480)) - dlg.Show() - }) + viewMenu := fyne.NewMenu("View", + fyne.NewMenuItem("Playlist", func() { + // Will be implemented with playlist toggle + }), + ) - clearList := widget.NewButton("Clear Playlist", func() { - s.clearVideo() + if len(s.loadedVideos) >= 2 { + viewMenu.Items = append(viewMenu.Items, fyne.NewMenuItem("Compare Videos", func() { + s.showCompareView() + })) + } + + toolsMenu := fyne.NewMenu("Tools") + + // Keyframing mode toggle + keyframeModeItem := fyne.NewMenuItem("Frame-Accurate Mode", func() { + s.keyframingMode = !s.keyframingMode refresh() }) - clearList.Importance = widget.LowImportance + keyframeModeItem.Checked = s.keyframingMode + toolsMenu.Items = append(toolsMenu.Items, keyframeModeItem) - var compareBtn *widget.Button - if len(s.loadedVideos) >= 2 { - compareBtn = widget.NewButton("Compare View", func() { - s.showCompareView() - }) - } - - barItems := []fyne.CanvasObject{openFile, addFolder, clearList} - if compareBtn != nil { - barItems = append(barItems, compareBtn) - } - barItems = append(barItems, layout.NewSpacer()) - controlsBar := container.NewHBox(barItems...) + mainMenu := fyne.NewMainMenu(fileMenu, viewMenu, toolsMenu) + s.window.SetMainMenu(mainMenu) // Player area var playerArea fyne.CanvasObject @@ -836,7 +847,17 @@ func (s *appState) showPlayerView() { progressBar := container.NewBorder(nil, nil, currentTime, totalTime, container.NewMax(slider)) volContainer := container.NewHBox(volIcon, container.NewMax(volSlider)) volContainer.Resize(fyne.NewSize(150, 32)) - controlRow := container.NewHBox(prevBtn, playBtn, nextBtn, layout.NewSpacer(), playlistToggleBtn, volContainer) + + // Center the playback controls + playbackControls := container.NewHBox(prevBtn, playBtn, nextBtn) + + // Create control row with centered playback controls + controlRow := container.NewBorder( + nil, nil, + volContainer, // Volume on left + container.NewHBox(playlistToggleBtn), // Playlist toggle on right + container.NewCenter(playbackControls), // Playback controls centered + ) playerArea = container.NewBorder( nil, @@ -847,13 +868,7 @@ func (s *appState) showPlayerView() { ) } - mainPanel := container.NewBorder( - container.NewVBox(header, controlsBar, widget.NewSeparator()), - nil, - nil, - nil, - playerArea, - ) + mainPanel := playerArea s.setContent(mainPanel) }