Fix convert layout stacking and cgo build

This commit is contained in:
Stu Leak 2025-11-30 00:01:06 -05:00
parent a56113b8cc
commit 97781b625f
6 changed files with 69 additions and 41 deletions

1
.gitignore vendored
View File

@ -1,4 +1,3 @@
videotools.log videotools.log
.gocache/ .gocache/
.gomodcache/ .gomodcache/
VideoTools

BIN
VideoTools Executable file

Binary file not shown.

View File

@ -424,7 +424,8 @@ func (r *conversionStatsRenderer) Layout(size fyne.Size) {
} }
func (r *conversionStatsRenderer) MinSize() fyne.Size { func (r *conversionStatsRenderer) MinSize() fyne.Size {
return fyne.NewSize(400, 32) // Only constrain height, allow width to flex
return fyne.NewSize(0, 32)
} }
func (r *conversionStatsRenderer) Refresh() { func (r *conversionStatsRenderer) Refresh() {

102
main.go
View File

@ -1159,8 +1159,10 @@ func runGUI() {
} else { } else {
logging.Debug(logging.CatUI, "app icon not found; continuing without custom icon") logging.Debug(logging.CatUI, "app icon not found; continuing without custom icon")
} }
w.Resize(fyne.NewSize(1120, 640)) // Use a generous default window size that fits typical desktops without overflowing.
logging.Debug(logging.CatUI, "window initialized at 1120x640") w.Resize(fyne.NewSize(1280, 800))
w.SetFixedSize(false) // Allow manual resizing
logging.Debug(logging.CatUI, "window initialized with manual resizing enabled")
state := &appState{ state := &appState{
window: w, window: w,
@ -1420,8 +1422,9 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
} }
} }
videoPanel := buildVideoPane(state, fyne.NewSize(360, 230), src, updateCover) // Make panel sizes responsive with modest minimums to avoid forcing the window beyond the screen
metaPanel, metaCoverUpdate := buildMetadataPanel(state, src, fyne.NewSize(360, 150)) videoPanel := buildVideoPane(state, fyne.NewSize(460, 260), src, updateCover)
metaPanel, metaCoverUpdate := buildMetadataPanel(state, src, fyne.NewSize(0, 200))
updateMetaCover = metaCoverUpdate updateMetaCover = metaCoverUpdate
var formatLabels []string var formatLabels []string
@ -1537,7 +1540,8 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
// Settings management for batch operations // Settings management for batch operations
settingsInfoLabel := widget.NewLabel("Settings persist across videos. Change them anytime to affect all subsequent videos.") settingsInfoLabel := widget.NewLabel("Settings persist across videos. Change them anytime to affect all subsequent videos.")
settingsInfoLabel.Wrapping = fyne.TextWrapWord // Don't wrap - let text scroll or truncate if needed
settingsInfoLabel.Alignment = fyne.TextAlignCenter
resetSettingsBtn := widget.NewButton("Reset to Defaults", func() { resetSettingsBtn := widget.NewButton("Reset to Defaults", func() {
// Reset to default settings // Reset to default settings
@ -1569,10 +1573,33 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
}) })
resetSettingsBtn.Importance = widget.LowImportance resetSettingsBtn.Importance = widget.LowImportance
settingsBox := container.NewVBox( // Create collapsible batch settings section
widget.NewLabelWithStyle("═══ BATCH SETTINGS ═══", fyne.TextAlignCenter, fyne.TextStyle{Bold: true}), settingsContent := container.NewVBox(
settingsInfoLabel, settingsInfoLabel,
resetSettingsBtn, resetSettingsBtn,
)
settingsContent.Hide() // Hidden by default
// Use a pointer to track visibility state
settingsVisible := false
var toggleSettingsBtn *widget.Button
toggleSettingsBtn = widget.NewButton("Show Batch Settings", func() {
if settingsVisible {
settingsContent.Hide()
toggleSettingsBtn.SetText("Show Batch Settings")
settingsVisible = false
} else {
settingsContent.Show()
toggleSettingsBtn.SetText("Hide Batch Settings")
settingsVisible = true
}
})
toggleSettingsBtn.Importance = widget.LowImportance
settingsBox := container.NewVBox(
toggleSettingsBtn,
settingsContent,
widget.NewSeparator(), widget.NewSeparator(),
) )
@ -1780,9 +1807,13 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
simpleOptions, simpleOptions,
) )
// Avoid nested scrolls capturing wheel events; let the outer page scroll handle overflow.
simpleScrollBox := simpleWithSettings
advancedScrollBox := advancedOptions
tabs := container.NewAppTabs( tabs := container.NewAppTabs(
container.NewTabItem("Simple", container.NewVScroll(simpleWithSettings)), container.NewTabItem("Simple", simpleScrollBox),
container.NewTabItem("Advanced", container.NewVScroll(advancedOptions)), container.NewTabItem("Advanced", advancedScrollBox),
) )
tabs.SetTabLocation(container.TabLocationTop) tabs.SetTabLocation(container.TabLocationTop)
@ -1831,15 +1862,16 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
} }
snippetHint := widget.NewLabel("Creates a 20s clip centred on the timeline midpoint.") snippetHint := widget.NewLabel("Creates a 20s clip centred on the timeline midpoint.")
snippetRow := container.NewHBox(snippetBtn, layout.NewSpacer(), snippetHint) snippetRow := container.NewHBox(snippetBtn, layout.NewSpacer(), snippetHint)
// Use VSplit to make panels expand vertically and fill available space
leftColumn := container.NewVSplit(videoPanel, metaPanel) // Stack video and metadata directly so metadata sits immediately under the player.
leftColumn.Offset = 0.65 // Video pane gets 65% of space, metadata gets 35% leftColumn := container.NewVBox(videoPanel, metaPanel)
// Split: left side (video + metadata VSplit) takes 55% | right side (options) takes 45%
mainSplit := container.NewHSplit(leftColumn, optionsPanel) mainSplit := container.NewHSplit(leftColumn, optionsPanel)
mainSplit.Offset = 0.45 // Give the options panel extra horizontal room mainSplit.Offset = 0.55 // Video/metadata column gets 55%, options gets 45%
mainArea := container.NewPadded(container.NewVBox(
mainSplit, // Core content now just the split; ancillary controls stack in bottomSection.
snippetRow, mainContent := container.NewMax(mainSplit)
))
resetBtn := widget.NewButton("Reset", func() { resetBtn := widget.NewButton("Reset", func() {
tabs.SelectIndex(0) // Select Simple tab tabs.SelectIndex(0) // Select Simple tab
@ -1922,12 +1954,12 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
addQueueBtn.Enable() addQueueBtn.Enable()
} }
actionInner := container.NewHBox(resetBtn, activity, statusLabel, layout.NewSpacer(), cancelBtn, addQueueBtn, convertBtn) leftControls := container.NewHBox(resetBtn)
centerStatus := container.NewHBox(activity, statusLabel)
rightControls := container.NewHBox(cancelBtn, addQueueBtn, convertBtn)
actionInner := container.NewBorder(nil, nil, leftControls, rightControls, centerStatus)
actionBar := ui.TintedBar(convertColor, actionInner) actionBar := ui.TintedBar(convertColor, actionInner)
// Wrap mainArea in a scroll container to prevent content from forcing window resize
scrollableMain := container.NewScroll(mainArea)
// Start a UI refresh ticker to update widgets from state while conversion is active // Start a UI refresh ticker to update widgets from state while conversion is active
// This ensures progress updates even when navigating between modules // This ensures progress updates even when navigating between modules
go func() { go func() {
@ -1993,20 +2025,12 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
// Update stats bar // Update stats bar
state.updateStatsBar() state.updateStatsBar()
// Add stats bar above the action bar at the bottom // Stack status + snippet + actions tightly to avoid dead air, outside the scroll area.
bottomSection := container.NewVBox( bottomSection := container.NewVBox(state.statsBar, snippetRow, widget.NewSeparator(), actionBar)
state.statsBar,
widget.NewSeparator(),
actionBar,
)
return container.NewBorder( scrollableMain := container.NewVScroll(mainContent)
backBar,
bottomSection, return container.NewBorder(backBar, bottomSection, nil, nil, container.NewMax(scrollableMain))
nil,
nil,
scrollableMain,
)
} }
func makeLabeledPanel(title, body string, min fyne.Size) *fyne.Container { func makeLabeledPanel(title, body string, min fyne.Size) *fyne.Container {
@ -2249,11 +2273,13 @@ func buildVideoPane(state *appState, min fyne.Size, src *videoSource, onCover fu
img = canvas.NewImageFromResource(nil) img = canvas.NewImageFromResource(nil)
} }
img.FillMode = canvas.ImageFillContain img.FillMode = canvas.ImageFillContain
img.SetMinSize(fyne.NewSize(targetWidth-28, targetHeight-40)) // Let the image grow with the available stage size
img.SetMinSize(fyne.NewSize(targetWidth, targetHeight))
stage := canvas.NewRectangle(utils.MustHex("#0F1529")) stage := canvas.NewRectangle(utils.MustHex("#0F1529"))
stage.CornerRadius = 6 stage.CornerRadius = 6
stage.SetMinSize(fyne.NewSize(targetWidth-12, targetHeight-12)) stage.SetMinSize(fyne.NewSize(targetWidth, targetHeight))
videoStage := container.NewMax(stage, container.NewPadded(container.NewCenter(img))) // Overlay the image directly so it fills the stage while preserving aspect.
videoStage := container.NewMax(stage, img)
coverBtn := utils.MakeIconButton("⌾", "Set current frame as cover art", func() { coverBtn := utils.MakeIconButton("⌾", "Set current frame as cover art", func() {
path, err := state.captureCoverFromCurrent() path, err := state.captureCoverFromCurrent()
@ -2454,7 +2480,7 @@ func buildVideoPane(state *appState, min fyne.Size, src *videoSource, onCover fu
stack := container.NewVBox( stack := container.NewVBox(
container.NewPadded(videoWithOverlay), container.NewPadded(videoWithOverlay),
) )
return container.NewMax(outer, container.NewCenter(container.NewPadded(stack))) return container.NewMax(outer, container.NewPadded(stack))
} }
type playSession struct { type playSession struct {

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 KiB

View File

@ -38,7 +38,9 @@ echo "✓ Dependencies verified"
echo "" echo ""
echo "🔨 Building VideoTools..." echo "🔨 Building VideoTools..."
if CGO_ENABLED=0 go build -o "$BUILD_OUTPUT" .; then # Fyne needs cgo for GLFW/OpenGL bindings; build with CGO enabled.
export CGO_ENABLED=1
if go build -o "$BUILD_OUTPUT" .; then
echo "✓ Build successful!" echo "✓ Build successful!"
echo "" echo ""
echo "════════════════════════════════════════════════════════════════" echo "════════════════════════════════════════════════════════════════"