Group main menu by category and add logs access

This commit is contained in:
Stu Leak 2025-12-08 12:07:58 -05:00
parent c7690f7f61
commit 2d0a865bc1
2 changed files with 92 additions and 35 deletions

View File

@ -13,30 +13,33 @@ import (
// ModuleInfo contains information about a module for display // ModuleInfo contains information about a module for display
type ModuleInfo struct { type ModuleInfo struct {
ID string ID string
Label string Label string
Color color.Color Color color.Color
Enabled bool Enabled bool
Category string
} }
// BuildMainMenu creates the main menu view with module tiles // BuildMainMenu creates the main menu view with module tiles grouped by category
func BuildMainMenu(modules []ModuleInfo, onModuleClick func(string), onModuleDrop func(string, []fyne.URI), onQueueClick func(), titleColor, queueColor, textColor color.Color, queueCompleted, queueTotal int) fyne.CanvasObject { func BuildMainMenu(modules []ModuleInfo, onModuleClick func(string), onModuleDrop func(string, []fyne.URI), onQueueClick func(), onLogsClick func(), titleColor, queueColor, textColor color.Color, queueCompleted, queueTotal int) fyne.CanvasObject {
title := canvas.NewText("VIDEOTOOLS", titleColor) title := canvas.NewText("VIDEOTOOLS", titleColor)
title.TextStyle = fyne.TextStyle{Monospace: true, Bold: true} title.TextStyle = fyne.TextStyle{Monospace: true, Bold: true}
title.TextSize = 28 title.TextSize = 28
queueTile := buildQueueTile(queueCompleted, queueTotal, queueColor, textColor, onQueueClick) queueTile := buildQueueTile(queueCompleted, queueTotal, queueColor, textColor, onQueueClick)
logsBtn := widget.NewButton("Logs", onLogsClick)
logsBtn.Importance = widget.LowImportance
header := container.New(layout.NewHBoxLayout(), header := container.New(layout.NewHBoxLayout(), title, layout.NewSpacer(), logsBtn, queueTile)
title,
layout.NewSpacer(),
queueTile,
)
var tileObjects []fyne.CanvasObject categorized := map[string][]fyne.CanvasObject{}
for i := range modules { for i := range modules {
mod := modules[i] // Create new variable for this iteration mod := modules[i] // Create new variable for this iteration
modID := mod.ID // Capture for closure modID := mod.ID // Capture for closure
cat := mod.Category
if cat == "" {
cat = "General"
}
var tapFunc func() var tapFunc func()
var dropFunc func([]fyne.URI) var dropFunc func([]fyne.URI)
if mod.Enabled { if mod.Enabled {
@ -53,10 +56,16 @@ func BuildMainMenu(modules []ModuleInfo, onModuleClick func(string), onModuleDro
} }
fmt.Printf("[MAINMENU] Creating tile for module=%s enabled=%v hasDropFunc=%v\n", modID, mod.Enabled, dropFunc != nil) fmt.Printf("[MAINMENU] Creating tile for module=%s enabled=%v hasDropFunc=%v\n", modID, mod.Enabled, dropFunc != nil)
logging.Debug(logging.CatUI, "Creating tile for module=%s enabled=%v hasDropFunc=%v", modID, mod.Enabled, dropFunc != nil) logging.Debug(logging.CatUI, "Creating tile for module=%s enabled=%v hasDropFunc=%v", modID, mod.Enabled, dropFunc != nil)
tileObjects = append(tileObjects, buildModuleTile(mod, tapFunc, dropFunc)) categorized[cat] = append(categorized[cat], buildModuleTile(mod, tapFunc, dropFunc))
} }
grid := container.NewGridWithColumns(3, tileObjects...) var sections []fyne.CanvasObject
for _, cat := range sortedKeys(categorized) {
sections = append(sections,
canvas.NewText(cat, textColor),
container.NewGridWithColumns(3, categorized[cat]...),
)
}
padding := canvas.NewRectangle(color.Transparent) padding := canvas.NewRectangle(color.Transparent)
padding.SetMinSize(fyne.NewSize(0, 14)) padding.SetMinSize(fyne.NewSize(0, 14))
@ -64,7 +73,7 @@ func BuildMainMenu(modules []ModuleInfo, onModuleClick func(string), onModuleDro
body := container.New(layout.NewVBoxLayout(), body := container.New(layout.NewVBoxLayout(),
header, header,
padding, padding,
grid, container.NewVBox(sections...),
) )
return body return body
@ -93,3 +102,13 @@ func buildQueueTile(completed, total int, queueColor, textColor color.Color, onC
tappable := NewTappable(tile, onClick) tappable := NewTappable(tile, onClick)
return tappable return tappable
} }
// sortedKeys returns sorted keys for stable category ordering
func sortedKeys(m map[string][]fyne.CanvasObject) []string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
return keys
}

74
main.go
View File

@ -46,10 +46,11 @@ import (
// Module describes a high level tool surface that gets a tile on the menu. // Module describes a high level tool surface that gets a tile on the menu.
type Module struct { type Module struct {
ID string ID string
Label string Label string
Color color.Color Color color.Color
Handle func(files []string) Category string
Handle func(files []string)
} }
var ( var (
@ -66,15 +67,15 @@ var (
logsDirPath string logsDirPath string
modulesList = []Module{ modulesList = []Module{
{"convert", "Convert", utils.MustHex("#8B44FF"), modules.HandleConvert}, // Violet {"convert", "Convert", utils.MustHex("#8B44FF"), "Convert", modules.HandleConvert}, // Violet
{"merge", "Merge", utils.MustHex("#4488FF"), modules.HandleMerge}, // Blue {"merge", "Merge", utils.MustHex("#4488FF"), "Convert", modules.HandleMerge}, // Blue
{"trim", "Trim", utils.MustHex("#44DDFF"), modules.HandleTrim}, // Cyan {"trim", "Trim", utils.MustHex("#44DDFF"), "Convert", modules.HandleTrim}, // Cyan
{"filters", "Filters", utils.MustHex("#44FF88"), modules.HandleFilters}, // Green {"filters", "Filters", utils.MustHex("#44FF88"), "Convert", modules.HandleFilters}, // Green
{"upscale", "Upscale", utils.MustHex("#AAFF44"), modules.HandleUpscale}, // Yellow-Green {"upscale", "Upscale", utils.MustHex("#AAFF44"), "Advanced", modules.HandleUpscale}, // Yellow-Green
{"audio", "Audio", utils.MustHex("#FFD744"), modules.HandleAudio}, // Yellow {"audio", "Audio", utils.MustHex("#FFD744"), "Convert", modules.HandleAudio}, // Yellow
{"thumb", "Thumb", utils.MustHex("#FF8844"), modules.HandleThumb}, // Orange {"thumb", "Thumb", utils.MustHex("#FF8844"), "Screenshots", modules.HandleThumb}, // Orange
{"compare", "Compare", utils.MustHex("#FF44AA"), modules.HandleCompare}, // Pink {"compare", "Compare", utils.MustHex("#FF44AA"), "Inspect", modules.HandleCompare}, // Pink
{"inspect", "Inspect", utils.MustHex("#FF4444"), modules.HandleInspect}, // Red {"inspect", "Inspect", utils.MustHex("#FF4444"), "Inspect", modules.HandleInspect}, // Red
} }
// Platform-specific configuration // Platform-specific configuration
@ -196,6 +197,24 @@ func (s *appState) openLogViewer(title, path string, live bool) {
d.Show() d.Show()
} }
// openFolder tries to open a folder in the OS file browser.
func openFolder(path string) error {
if strings.TrimSpace(path) == "" {
return fmt.Errorf("path is empty")
}
var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
cmd = exec.Command("explorer", path)
case "darwin":
cmd = exec.Command("open", path)
default:
cmd = exec.Command("xdg-open", path)
}
utils.ApplyNoWindow(cmd)
return cmd.Start()
}
type formatOption struct { type formatOption struct {
Label string Label string
Ext string Ext string
@ -558,10 +577,11 @@ func (s *appState) showMainMenu() {
var mods []ui.ModuleInfo var mods []ui.ModuleInfo
for _, m := range modulesList { for _, m := range modulesList {
mods = append(mods, ui.ModuleInfo{ mods = append(mods, ui.ModuleInfo{
ID: m.ID, ID: m.ID,
Label: m.Label, Label: m.Label,
Color: m.Color, Color: m.Color,
Enabled: m.ID == "convert" || m.ID == "compare" || m.ID == "inspect", // Convert, compare, and inspect modules are functional Category: m.Category,
Enabled: m.ID == "convert" || m.ID == "compare" || m.ID == "inspect", // Convert, compare, and inspect modules are functional
}) })
} }
@ -575,7 +595,25 @@ func (s *appState) showMainMenu() {
queueTotal = len(s.jobQueue.List()) queueTotal = len(s.jobQueue.List())
} }
menu := ui.BuildMainMenu(mods, s.showModule, s.handleModuleDrop, s.showQueue, titleColor, queueColor, textColor, queueCompleted, queueTotal) menu := ui.BuildMainMenu(mods, s.showModule, s.handleModuleDrop, s.showQueue, func() {
// Logs button: offer to open logs folder or view app log
logOptions := container.NewVBox(
widget.NewButton("Open Logs Folder", func() {
if err := openFolder(getLogsDir()); err != nil {
dialog.ShowError(fmt.Errorf("failed to open logs folder: %w", err), s.window)
}
}),
widget.NewButton("View App Log", func() {
path := logging.FilePath()
if strings.TrimSpace(path) == "" {
dialog.ShowInformation("No Log", "No app log file found.", s.window)
return
}
s.openLogViewer("App Log", path, false)
}),
)
dialog.ShowCustom("Logs", "Close", logOptions, s.window)
}, titleColor, queueColor, textColor, queueCompleted, queueTotal)
// Update stats bar // Update stats bar
s.updateStatsBar() s.updateStatsBar()