Give video pane dedicated transport bar

This commit is contained in:
Stu Leak 2026-01-07 02:06:20 -05:00
parent d41bdb3557
commit 369d1e3f4f

89
main.go
View File

@ -7305,10 +7305,9 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
} }
} }
// Make panel sizes responsive with modest minimums to avoid forcing the window beyond the screen // Make panel sizes responsive with modest minimums to avoid forcing the window beyond the screen.
// Use a smaller minimum size to allow window to be more flexible // The video pane should be the primary region; keep a stronger minimum.
// The video pane will scale to fit available space videoPanel := buildVideoPane(state, fyne.NewSize(640, 360), src, updateCover)
videoPanel := buildVideoPane(state, fyne.NewSize(320, 180), src, updateCover)
metaPanel, metaCoverUpdate := buildMetadataPanel(state, src, fyne.NewSize(0, 200)) metaPanel, metaCoverUpdate := buildMetadataPanel(state, src, fyne.NewSize(0, 200))
updateMetaCover = metaCoverUpdate updateMetaCover = metaCoverUpdate
@ -9872,24 +9871,24 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
snippetRow = container.NewHBox(snippetBtn, snippetOptionsBtn, layout.NewSpacer(), snippetHint) snippetRow = container.NewHBox(snippetBtn, snippetOptionsBtn, layout.NewSpacer(), snippetHint)
} }
// Stack video and metadata with 10px spacing between them // Stack options and metadata with a small gap on the right.
// Create a 10px spacer using a container with fixed size
spacerRect := canvas.NewRectangle(color.Transparent) spacerRect := canvas.NewRectangle(color.Transparent)
spacerRect.SetMinSize(fyne.NewSize(1, 10)) spacerRect.SetMinSize(fyne.NewSize(1, 10))
spacer := container.NewMax(spacerRect) spacer := container.NewMax(spacerRect)
spacer.Resize(fyne.NewSize(1, 10)) spacer.Resize(fyne.NewSize(1, 10))
leftColumn := container.NewVBox(videoPanel, spacer, metaPanel) leftColumn := container.NewVBox(videoPanel)
rightColumn := container.NewVBox(optionsPanel, spacer, metaPanel)
// Add minimal spacing (10px) between left and right panels // Add minimal spacing (10px) between left and right panels
horizontalSpacer := canvas.NewRectangle(color.Transparent) horizontalSpacer := canvas.NewRectangle(color.Transparent)
horizontalSpacer.SetMinSize(fyne.NewSize(10, 1)) horizontalSpacer.SetMinSize(fyne.NewSize(10, 1))
// Split: left side (video + metadata) takes 50% | right side (options) takes 50% // Split: left side (player) takes priority | right side (controls + metadata).
mainSplit := container.NewHSplit( mainSplit := container.NewHSplit(
leftColumn, leftColumn,
optionsPanel) rightColumn)
mainSplit.SetOffset(0.5) // 50/50 split mainSplit.SetOffset(0.65) // 65/35 split
// Add horizontal padding around the split (10px on each side) // Add horizontal padding around the split (10px on each side)
mainContent := container.NewPadded(mainSplit) mainContent := container.NewPadded(mainSplit)
@ -10642,34 +10641,24 @@ func buildVideoPane(state *appState, min fyne.Size, src *videoSource, onCover fu
outer.CornerRadius = 8 outer.CornerRadius = 8
outer.StrokeColor = gridColor outer.StrokeColor = gridColor
outer.StrokeWidth = 1 outer.StrokeWidth = 1
defaultAspect := 9.0 / 16.0 defaultAspect := 16.0 / 9.0
if src != nil && src.Width > 0 && src.Height > 0 { if src != nil && src.Width > 0 && src.Height > 0 {
defaultAspect = float64(src.Height) / float64(src.Width) defaultAspect = float64(src.Width) / float64(src.Height)
} }
baseWidth := float64(min.Width) targetWidth := float32(min.Width)
targetWidth := float32(baseWidth)
_ = defaultAspect
targetHeight := float32(min.Height) targetHeight := float32(min.Height)
if state != nil && state.window != nil { if targetWidth <= 0 {
winSize := state.window.Canvas().Size() targetWidth = 320
if winSize.Height >= 900 { }
desiredHeight := float32(360) if targetHeight <= 0 {
desiredWidth := desiredHeight / float32(defaultAspect) targetHeight = 180
maxWidth := winSize.Width - 48 }
maxHeight := winSize.Height - 200 aspect := float32(defaultAspect)
if maxWidth > 0 && desiredWidth > maxWidth { stageWidth := targetWidth
desiredWidth = maxWidth stageHeight := stageWidth / aspect
desiredHeight = desiredWidth * float32(defaultAspect) if stageHeight < targetHeight {
} stageHeight = targetHeight
if maxHeight > 0 && desiredHeight > maxHeight { stageWidth = stageHeight * aspect
desiredHeight = maxHeight
desiredWidth = desiredHeight / float32(defaultAspect)
}
if desiredWidth > 0 && desiredHeight > 0 {
targetWidth = desiredWidth
targetHeight = desiredHeight
}
}
} }
// Don't set rigid MinSize - let the outer container be flexible // Don't set rigid MinSize - let the outer container be flexible
// outer.SetMinSize(fyne.NewSize(targetWidth, targetHeight)) // outer.SetMinSize(fyne.NewSize(targetWidth, targetHeight))
@ -10756,13 +10745,7 @@ func buildVideoPane(state *appState, min fyne.Size, src *videoSource, onCover fu
stage := canvas.NewRectangle(utils.MustHex("#0F1529")) stage := canvas.NewRectangle(utils.MustHex("#0F1529"))
stage.CornerRadius = 6 stage.CornerRadius = 6
// Set minimum size based on source aspect ratio // Set minimum size based on source aspect ratio
stageWidth := float32(200) // Set minimum size based on source aspect ratio.
stageHeight := float32(113) // Default 16:9
if src != nil && src.Width > 0 && src.Height > 0 {
// Calculate height based on actual aspect ratio
aspectRatio := float32(src.Width) / float32(src.Height)
stageHeight = stageWidth / aspectRatio
}
stage.SetMinSize(fyne.NewSize(stageWidth, stageHeight)) stage.SetMinSize(fyne.NewSize(stageWidth, stageHeight))
// Overlay the image directly so it fills the stage while preserving aspect. // Overlay the image directly so it fills the stage while preserving aspect.
videoStage := container.NewMax(stage, img) videoStage := container.NewMax(stage, img)
@ -10965,12 +10948,12 @@ func buildVideoPane(state *appState, min fyne.Size, src *videoSource, onCover fu
fullBtn := utils.MakeIconButton("⛶", "Toggle fullscreen", func() { fullBtn := utils.MakeIconButton("⛶", "Toggle fullscreen", func() {
// Placeholder: embed fullscreen toggle into playback surface later. // Placeholder: embed fullscreen toggle into playback surface later.
}) })
volBox := container.NewHBox(volIcon, container.NewMax(volSlider)) volBox := container.NewHBox(volIcon, container.NewMax(volSlider))
progress := container.NewBorder(nil, nil, currentTime, totalTime, container.NewMax(slider)) progress := container.NewBorder(nil, nil, currentTime, totalTime, container.NewMax(slider))
controls = container.NewVBox( controls = container.NewVBox(
container.NewHBox(prevFrameBtn, playBtn, nextFrameBtn, fullBtn, coverBtn, saveFrameBtn, importBtn, layout.NewSpacer(), frameLabel, volBox), container.NewHBox(prevFrameBtn, playBtn, nextFrameBtn, fullBtn, coverBtn, saveFrameBtn, importBtn, layout.NewSpacer(), frameLabel, volBox),
progress, progress,
) )
} else { } else {
slider := widget.NewSlider(0, math.Max(1, float64(len(src.PreviewFrames)-1))) slider := widget.NewSlider(0, math.Max(1, float64(len(src.PreviewFrames)-1)))
slider.Step = 1 slider.Step = 1
@ -11016,17 +10999,17 @@ func buildVideoPane(state *appState, min fyne.Size, src *videoSource, onCover fu
} }
barBg := canvas.NewRectangle(color.NRGBA{R: 12, G: 17, B: 31, A: 180}) barBg := canvas.NewRectangle(color.NRGBA{R: 12, G: 17, B: 31, A: 180})
barBg.SetMinSize(fyne.NewSize(targetWidth-32, 72)) barBg.SetMinSize(fyne.NewSize(0, 72))
overlayBar := container.NewMax(barBg, container.NewPadded(controls)) transportBar := container.NewMax(barBg, container.NewPadded(controls))
overlay := container.NewVBox(layout.NewSpacer(), overlayBar) videoWithOverlay := videoStage
videoWithOverlay := container.NewMax(videoStage, overlay)
if usePlayer { if usePlayer {
state.setPlayerSurface(videoStage, int(targetWidth-12), int(targetHeight-12)) state.setPlayerSurface(videoStage, int(stageWidth), int(stageHeight))
} }
stack := container.NewVBox( stack := container.NewVBox(
container.NewPadded(videoWithOverlay), container.NewPadded(videoWithOverlay),
container.NewPadded(transportBar),
) )
return container.NewMax(outer, container.NewPadded(stack)) return container.NewMax(outer, container.NewPadded(stack))
} }