From 4954dc021f7d9c7ea842b6bac321939ceec81937 Mon Sep 17 00:00:00 2001 From: Stu Leak Date: Sun, 4 Jan 2026 04:11:27 -0500 Subject: [PATCH] Use ratio layout for codec and preset row --- internal/ui/components.go | 46 +++++++++++++++++++++ main.go | 86 +++++++++++++++++++++++++++++++-------- 2 files changed, 114 insertions(+), 18 deletions(-) diff --git a/internal/ui/components.go b/internal/ui/components.go index 64ba6f0..4110cc4 100644 --- a/internal/ui/components.go +++ b/internal/ui/components.go @@ -356,6 +356,52 @@ func TintedBar(col color.Color, body fyne.CanvasObject) fyne.CanvasObject { return container.NewMax(rect, padded) } +// NewRatioRow lays out two objects with a fixed width ratio for the left item. +func NewRatioRow(left, right fyne.CanvasObject, leftRatio float32) *fyne.Container { + return container.New(&ratioRowLayout{leftRatio: leftRatio}, left, right) +} + +type ratioRowLayout struct { + leftRatio float32 +} + +func (r *ratioRowLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) { + if len(objects) < 2 { + return + } + ratio := clampRatio(r.leftRatio) + leftWidth := size.Width * ratio + rightWidth := size.Width - leftWidth + + objects[0].Move(fyne.NewPos(0, 0)) + objects[0].Resize(fyne.NewSize(leftWidth, size.Height)) + objects[1].Move(fyne.NewPos(leftWidth, 0)) + objects[1].Resize(fyne.NewSize(rightWidth, size.Height)) +} + +func (r *ratioRowLayout) MinSize(objects []fyne.CanvasObject) fyne.Size { + if len(objects) < 2 { + return fyne.NewSize(0, 0) + } + leftMin := objects[0].MinSize() + rightMin := objects[1].MinSize() + height := leftMin.Height + if rightMin.Height > height { + height = rightMin.Height + } + return fyne.NewSize(leftMin.Width+rightMin.Width, height) +} + +func clampRatio(ratio float32) float32 { + if ratio < 0.1 { + return 0.1 + } + if ratio > 0.9 { + return 0.9 + } + return ratio +} + // Tappable wraps any canvas object and makes it tappable type Tappable struct { widget.BaseWidget diff --git a/main.go b/main.go index bd5028c..8f1f9d7 100644 --- a/main.go +++ b/main.go @@ -1,52 +1,58 @@ package main import ( - "bufio" "bytes" "context" - "encoding/json" - "errors" "flag" "fmt" - "image" "image/color" - "image/png" - "io" - "math" + "log" + "net/url" "os" "os/exec" "path/filepath" - "regexp" "runtime" "slices" - "sort" - "strconv" "strings" "sync" - "sync/atomic" "time" "fyne.io/fyne/v2" "fyne.io/fyne/v2/app" "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/data/binding" "fyne.io/fyne/v2/dialog" - "fyne.io/fyne/v2/driver/desktop" "fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/storage" + "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" + + "git.leaktechnologies.dev/stu/VideoTools/audio_module" + "git.leaktechnologies.dev/stu/VideoTools/author_module" + "git.leaktechnologies.dev/stu/VideoTools/filters_module" + "git.leaktechnologies.dev/stu/VideoTools/inspect_module" + "git.leaktechnologies.dev/stu/VideoTools/internal/app" "git.leaktechnologies.dev/stu/VideoTools/internal/benchmark" "git.leaktechnologies.dev/stu/VideoTools/internal/convert" - - "git.leaktechnologies.dev/stu/VideoTools/internal/interlace" + "git.leaktechnologies.dev/stu/VideoTools/internal/convert/presets" + "git.leaktechnologies.dev/stu/VideoTools/internal/enhancement" "git.leaktechnologies.dev/stu/VideoTools/internal/logging" + "git.leaktechnologies.dev/stu/VideoTools/internal/metadata" "git.leaktechnologies.dev/stu/VideoTools/internal/modules" "git.leaktechnologies.dev/stu/VideoTools/internal/player" "git.leaktechnologies.dev/stu/VideoTools/internal/queue" - "git.leaktechnologies.dev/stu/VideoTools/internal/sysinfo" + "git.leaktechnologies.dev/stu/VideoTools/internal/thumb" "git.leaktechnologies.dev/stu/VideoTools/internal/ui" "git.leaktechnologies.dev/stu/VideoTools/internal/utils" - "github.com/hajimehoshi/oto" + "git.leaktechnologies.dev/stu/VideoTools/rip_module" + "git.leaktechnologies.dev/stu/VideoTools/settings_module" + "git.leaktechnologies.dev/stu/VideoTools/subtitles_module" + "git.leaktechnologies.dev/stu/VideoTools/thumb_module" + "git.leaktechnologies.dev/stu/VideoTools/upscale_module" + + "github.com/yeqown/go-qrcode/v2" + "github.com/yeqown/go-qrcode/writer/standard" ) // Module describes a high level tool surface that gets a tile on the menu. @@ -500,6 +506,34 @@ func openFile(path string) error { return cmd.Start() } +func generatePixelatedQRCode() (fyne.CanvasObject, error) { + docURL := "https://docs.leaktechnologies.dev/VideoTools" + + // Create chunky QR code with large pixel blocks + qrc, err := qrcode.New(docURL, + qrcode.WithQRWidth(6), // Large pixel blocks for chunky look + qrcode.WithBorderWidth(2), // Small border + qrcode.WithErrorCorrectionLevel(qrcode.ErrorCorrectionMedium), + ) + if err != nil { + return nil, err + } + + // Generate to memory + var buf bytes.Buffer + w := standard.NewWithWriter(&buf, standard.WithQRWidth(6)) + if err := qrc.Save(w); err != nil { + return nil, err + } + + // Convert to Fyne image with pixelated look + img := canvas.NewImageFromReader(&buf, "qrcode.png") + img.FillMode = canvas.ImageFillOriginal // Keep pixelated look + img.SetMinSize(fyne.NewSize(160, 160)) + + return img, nil +} + func (s *appState) showAbout() { version := fmt.Sprintf("VideoTools %s", appVersion) dev := "Leak Technologies" @@ -557,6 +591,22 @@ func (s *appState) showAbout() { if ltLogo != nil { logoColumn.Add(ltLogo) } + + // Add QR code for documentation + qrCode, err := generatePixelatedQRCode() + if err != nil { + // Fallback to hyperlink if QR generation fails + docURL, _ := url.Parse("https://docs.leaktechnologies.dev/VideoTools") + fallbackLink := widget.NewHyperlink("View Documentation", docURL) + logoColumn.Add(fallbackLink) + } else { + // Add QR code with label + qrLabel := widget.NewLabel("Scan for docs") + qrLabel.Alignment = fyne.TextAlignCenter + logoColumn.Add(qrCode) + logoColumn.Add(qrLabel) + } + logoColumn.Add(layout.NewSpacer()) logoColumn.Add(logsLink) @@ -8737,8 +8787,8 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject { // Advanced mode options - full controls with organized sections videoCodecLabel := widget.NewLabelWithStyle("Video Codec", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}) presetLabel := widget.NewLabelWithStyle("Encoder Preset (speed vs quality)", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}) - videoCodecRow := container.NewGridWithColumns(2, videoCodecLabel, presetLabel) - videoCodecControls := container.NewGridWithColumns(2, videoCodecContainer, encoderPresetSelect) + videoCodecRow := ui.NewRatioRow(videoCodecLabel, presetLabel, 0.3) + videoCodecControls := ui.NewRatioRow(videoCodecContainer, encoderPresetSelect, 0.3) advancedVideoEncodingBlock = container.NewVBox( widget.NewLabelWithStyle("═══ VIDEO ENCODING ═══", fyne.TextAlignCenter, fyne.TextStyle{Bold: true}),