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 f53da0c07f
commit c7a18e89c8
2 changed files with 92 additions and 35 deletions

View File

@ -13,30 +13,33 @@ import (
// ModuleInfo contains information about a module for display
type ModuleInfo struct {
ID string
Label string
Color color.Color
Enabled bool
ID string
Label string
Color color.Color
Enabled bool
Category string
}
// BuildMainMenu creates the main menu view with module tiles
func BuildMainMenu(modules []ModuleInfo, onModuleClick func(string), onModuleDrop func(string, []fyne.URI), onQueueClick func(), titleColor, queueColor, textColor color.Color, queueCompleted, queueTotal int) fyne.CanvasObject {
// 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(), onLogsClick func(), titleColor, queueColor, textColor color.Color, queueCompleted, queueTotal int) fyne.CanvasObject {
title := canvas.NewText("VIDEOTOOLS", titleColor)
title.TextStyle = fyne.TextStyle{Monospace: true, Bold: true}
title.TextSize = 28
queueTile := buildQueueTile(queueCompleted, queueTotal, queueColor, textColor, onQueueClick)
logsBtn := widget.NewButton("Logs", onLogsClick)
logsBtn.Importance = widget.LowImportance
header := container.New(layout.NewHBoxLayout(),
title,
layout.NewSpacer(),
queueTile,
)
header := container.New(layout.NewHBoxLayout(), title, layout.NewSpacer(), logsBtn, queueTile)
var tileObjects []fyne.CanvasObject
categorized := map[string][]fyne.CanvasObject{}
for i := range modules {
mod := modules[i] // Create new variable for this iteration
modID := mod.ID // Capture for closure
mod := modules[i] // Create new variable for this iteration
modID := mod.ID // Capture for closure
cat := mod.Category
if cat == "" {
cat = "General"
}
var tapFunc func()
var dropFunc func([]fyne.URI)
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)
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.SetMinSize(fyne.NewSize(0, 14))
@ -64,7 +73,7 @@ func BuildMainMenu(modules []ModuleInfo, onModuleClick func(string), onModuleDro
body := container.New(layout.NewVBoxLayout(),
header,
padding,
grid,
container.NewVBox(sections...),
)
return body
@ -93,3 +102,13 @@ func buildQueueTile(completed, total int, queueColor, textColor color.Color, onC
tappable := NewTappable(tile, onClick)
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.
type Module struct {
ID string
Label string
Color color.Color
Handle func(files []string)
ID string
Label string
Color color.Color
Category string
Handle func(files []string)
}
var (
@ -66,15 +67,15 @@ var (
logsDirPath string
modulesList = []Module{
{"convert", "Convert", utils.MustHex("#8B44FF"), modules.HandleConvert}, // Violet
{"merge", "Merge", utils.MustHex("#4488FF"), modules.HandleMerge}, // Blue
{"trim", "Trim", utils.MustHex("#44DDFF"), modules.HandleTrim}, // Cyan
{"filters", "Filters", utils.MustHex("#44FF88"), modules.HandleFilters}, // Green
{"upscale", "Upscale", utils.MustHex("#AAFF44"), modules.HandleUpscale}, // Yellow-Green
{"audio", "Audio", utils.MustHex("#FFD744"), modules.HandleAudio}, // Yellow
{"thumb", "Thumb", utils.MustHex("#FF8844"), modules.HandleThumb}, // Orange
{"compare", "Compare", utils.MustHex("#FF44AA"), modules.HandleCompare}, // Pink
{"inspect", "Inspect", utils.MustHex("#FF4444"), modules.HandleInspect}, // Red
{"convert", "Convert", utils.MustHex("#8B44FF"), "Convert", modules.HandleConvert}, // Violet
{"merge", "Merge", utils.MustHex("#4488FF"), "Convert", modules.HandleMerge}, // Blue
{"trim", "Trim", utils.MustHex("#44DDFF"), "Convert", modules.HandleTrim}, // Cyan
{"filters", "Filters", utils.MustHex("#44FF88"), "Convert", modules.HandleFilters}, // Green
{"upscale", "Upscale", utils.MustHex("#AAFF44"), "Advanced", modules.HandleUpscale}, // Yellow-Green
{"audio", "Audio", utils.MustHex("#FFD744"), "Convert", modules.HandleAudio}, // Yellow
{"thumb", "Thumb", utils.MustHex("#FF8844"), "Screenshots", modules.HandleThumb}, // Orange
{"compare", "Compare", utils.MustHex("#FF44AA"), "Inspect", modules.HandleCompare}, // Pink
{"inspect", "Inspect", utils.MustHex("#FF4444"), "Inspect", modules.HandleInspect}, // Red
}
// Platform-specific configuration
@ -196,6 +197,24 @@ func (s *appState) openLogViewer(title, path string, live bool) {
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 {
Label string
Ext string
@ -558,10 +577,11 @@ func (s *appState) showMainMenu() {
var mods []ui.ModuleInfo
for _, m := range modulesList {
mods = append(mods, ui.ModuleInfo{
ID: m.ID,
Label: m.Label,
Color: m.Color,
Enabled: m.ID == "convert" || m.ID == "compare" || m.ID == "inspect", // Convert, compare, and inspect modules are functional
ID: m.ID,
Label: m.Label,
Color: m.Color,
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())
}
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
s.updateStatsBar()