Compare commits

..

No commits in common. "62dd39347a6516ab486222b9c5936ef048ee1ed8" and "3b99cad32bfebb6befc012b78ed8fe0b73aa171e" have entirely different histories.

4 changed files with 46 additions and 168 deletions

View File

@ -1,5 +1,5 @@
[Details]
Icon = "assets/logo/VT_Icon.png"
Icon = "assets/logo/VT_Icon.ico"
Name = "VideoTools"
ID = "com.leaktechnologies.videotools"
Version = "0.1.0-dev20"

View File

@ -2,7 +2,6 @@ package ui
import (
"fmt"
"image"
"image/color"
"strings"
"time"
@ -65,22 +64,20 @@ func (m *MonoTheme) Size(name fyne.ThemeSizeName) float32 {
// ModuleTile is a clickable tile widget for module selection
type ModuleTile struct {
widget.BaseWidget
label string
color color.Color
enabled bool
missingDependencies bool
onTapped func()
onDropped func([]fyne.URI)
flashing bool
draggedOver bool
label string
color color.Color
enabled bool
onTapped func()
onDropped func([]fyne.URI)
flashing bool
draggedOver bool
}
// NewModuleTile creates a new module tile
func NewModuleTile(label string, col color.Color, enabled bool, missingDeps bool, tapped func(), dropped func([]fyne.URI)) *ModuleTile {
func NewModuleTile(label string, col color.Color, enabled bool, tapped func(), dropped func([]fyne.URI)) *ModuleTile {
m := &ModuleTile{
label: strings.ToUpper(label),
color: col,
missingDependencies: missingDeps,
label: strings.ToUpper(label),
color: col,
enabled: enabled,
onTapped: tapped,
onDropped: dropped,
@ -150,14 +147,15 @@ func getContrastColor(bgColor color.Color) color.Color {
func (m *ModuleTile) CreateRenderer() fyne.WidgetRenderer {
tileColor := m.color
labelColor := TextColor // White text for all modules
labelColor := getContrastColor(m.color)
// Orange background for modules missing dependencies
if m.missingDependencies {
tileColor = color.NRGBA{R: 255, G: 152, B: 0, A: 255} // Orange
} else if !m.enabled {
// Grey background for not implemented modules
tileColor = color.NRGBA{R: 80, G: 80, B: 80, A: 255}
// Dim disabled tiles
if !m.enabled {
// Reduce opacity by mixing with dark background
if c, ok := m.color.(color.NRGBA); ok {
tileColor = color.NRGBA{R: c.R / 3, G: c.G / 3, B: c.B / 3, A: c.A}
}
labelColor = color.NRGBA{R: 100, G: 100, B: 100, A: 255}
}
bg := canvas.NewRectangle(tileColor)
@ -170,45 +168,10 @@ func (m *ModuleTile) CreateRenderer() fyne.WidgetRenderer {
txt.Alignment = fyne.TextAlignCenter
txt.TextSize = 20
// Lock icon for disabled modules
lockIcon := canvas.NewText("🔒", color.NRGBA{R: 200, G: 200, B: 200, A: 255})
lockIcon.TextSize = 16
lockIcon.Alignment = fyne.TextAlignCenter
if m.enabled {
lockIcon.Hide()
}
// Diagonal stripe overlay for disabled modules
disabledStripe := canvas.NewRaster(func(w, h int) image.Image {
img := image.NewRGBA(image.Rect(0, 0, w, h))
// Only draw stripes if disabled
if !m.enabled {
// Semi-transparent dark stripes
darkStripe := color.NRGBA{R: 0, G: 0, B: 0, A: 100}
lightStripe := color.NRGBA{R: 0, G: 0, B: 0, A: 30}
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
// Thicker diagonal stripes (dividing by 8 instead of 4)
if ((x + y) / 8 % 2) == 0 {
img.Set(x, y, darkStripe)
} else {
img.Set(x, y, lightStripe)
}
}
}
}
// Return transparent image for enabled modules
return img
})
return &moduleTileRenderer{
tile: m,
bg: bg,
label: txt,
lockIcon: lockIcon,
disabledStripe: disabledStripe,
tile: m,
bg: bg,
label: txt,
}
}
@ -219,38 +182,19 @@ func (m *ModuleTile) Tapped(*fyne.PointEvent) {
}
type moduleTileRenderer struct {
tile *ModuleTile
bg *canvas.Rectangle
label *canvas.Text
lockIcon *canvas.Text
disabledStripe *canvas.Raster
tile *ModuleTile
bg *canvas.Rectangle
label *canvas.Text
}
func (r *moduleTileRenderer) Layout(size fyne.Size) {
r.bg.Resize(size)
r.bg.Move(fyne.NewPos(0, 0))
// Stripe overlay covers entire tile
if r.disabledStripe != nil {
r.disabledStripe.Resize(size)
r.disabledStripe.Move(fyne.NewPos(0, 0))
}
// Center the label by positioning it in the middle
labelSize := r.label.MinSize()
r.label.Resize(labelSize)
x := (size.Width - labelSize.Width) / 2
y := (size.Height - labelSize.Height) / 2
r.label.Move(fyne.NewPos(x, y))
// Position lock icon in top-right corner
if r.lockIcon != nil {
lockSize := r.lockIcon.MinSize()
r.lockIcon.Resize(lockSize)
lockX := size.Width - lockSize.Width - 4
lockY := float32(4)
r.lockIcon.Move(fyne.NewPos(lockX, lockY))
}
}
func (r *moduleTileRenderer) MinSize() fyne.Size {
@ -258,23 +202,7 @@ func (r *moduleTileRenderer) MinSize() fyne.Size {
}
func (r *moduleTileRenderer) Refresh() {
// Update tile color and text color based on enabled state
if r.tile.enabled {
r.bg.FillColor = r.tile.color
r.label.Color = getContrastColor(r.tile.color)
if r.lockIcon != nil {
r.lockIcon.Hide()
}
} else {
// Dim disabled tiles
if c, ok := r.tile.color.(color.NRGBA); ok {
r.bg.FillColor = color.NRGBA{R: c.R / 3, G: c.G / 3, B: c.B / 3, A: c.A}
}
r.label.Color = color.NRGBA{R: 100, G: 100, B: 100, A: 255}
if r.lockIcon != nil {
r.lockIcon.Show()
}
}
r.bg.FillColor = r.tile.color
// Apply visual feedback based on state
if r.tile.flashing {
@ -294,18 +222,12 @@ func (r *moduleTileRenderer) Refresh() {
r.bg.Refresh()
r.label.Text = r.tile.label
r.label.Refresh()
if r.lockIcon != nil {
r.lockIcon.Refresh()
}
if r.disabledStripe != nil {
r.disabledStripe.Refresh()
}
}
func (r *moduleTileRenderer) Destroy() {}
func (r *moduleTileRenderer) Objects() []fyne.CanvasObject {
return []fyne.CanvasObject{r.bg, r.disabledStripe, r.label, r.lockIcon}
return []fyne.CanvasObject{r.bg, r.label}
}
// TintedBar creates a colored bar container

View File

@ -18,12 +18,11 @@ import (
// ModuleInfo contains information about a module for display
type ModuleInfo struct {
ID string
Label string
Color color.Color
Enabled bool
Category string
MissingDependencies bool // true if disabled due to missing dependencies
ID string
Label string
Color color.Color
Enabled bool
Category string
}
// HistoryEntry represents a completed job in the history
@ -169,8 +168,8 @@ func BuildMainMenu(modules []ModuleInfo, onModuleClick func(string), onModuleDro
// buildModuleTile creates a single module tile
func buildModuleTile(mod ModuleInfo, tapped func(), dropped func([]fyne.URI)) fyne.CanvasObject {
logging.Debug(logging.CatUI, "building tile %s color=%v enabled=%v missingDeps=%v", mod.ID, mod.Color, mod.Enabled, mod.MissingDependencies)
return NewModuleTile(mod.Label, mod.Color, mod.Enabled, mod.MissingDependencies, tapped, dropped)
logging.Debug(logging.CatUI, "building tile %s color=%v enabled=%v", mod.ID, mod.Color, mod.Enabled)
return NewModuleTile(mod.Label, mod.Color, mod.Enabled, tapped, dropped)
}
// buildQueueTile creates the queue status tile

67
main.go
View File

@ -80,18 +80,18 @@ var (
nvencRuntimeOK bool
// Rainbow color palette: balanced ROYGBIV distribution (2 modules per color)
// Optimized for white text readability
// Bright, vibrant, highly navigable with perfect spectrum balance
modulesList = []Module{
{"convert", "Convert", utils.MustHex("#673AB7"), "Convert", modules.HandleConvert}, // Deep Purple (primary conversion)
{"merge", "Merge", utils.MustHex("#4CAF50"), "Convert", modules.HandleMerge}, // Green (combining)
{"trim", "Trim", utils.MustHex("#F9A825"), "Convert", nil}, // Dark Yellow/Gold (not implemented yet)
{"trim", "Trim", utils.MustHex("#FFEB3B"), "Convert", modules.HandleTrim}, // Yellow (precision cut)
{"filters", "Filters", utils.MustHex("#00BCD4"), "Convert", modules.HandleFilters}, // Cyan (creative filters)
{"upscale", "Upscale", utils.MustHex("#9C27B0"), "Advanced", modules.HandleUpscale}, // Purple (AI/advanced)
{"audio", "Audio", utils.MustHex("#FF8F00"), "Convert", nil}, // Dark Amber (not implemented yet)
{"audio", "Audio", utils.MustHex("#FFC107"), "Convert", modules.HandleAudio}, // Amber (sound waves)
{"author", "Author", utils.MustHex("#FF5722"), "Disc", modules.HandleAuthor}, // Deep Orange (authoring)
{"rip", "Rip", utils.MustHex("#FF9800"), "Disc", modules.HandleRip}, // Orange (extraction)
{"bluray", "Blu-Ray", utils.MustHex("#2196F3"), "Disc", nil}, // Blue (not implemented yet)
{"subtitles", "Subtitles", utils.MustHex("#689F38"), "Convert", modules.HandleSubtitles}, // Dark Green (text)
{"bluray", "Blu-Ray", utils.MustHex("#2196F3"), "Disc", modules.HandleBluRay}, // Blue (Blu-ray brand)
{"subtitles", "Subtitles", utils.MustHex("#8BC34A"), "Convert", modules.HandleSubtitles}, // Light Green (text)
{"thumb", "Thumb", utils.MustHex("#00ACC1"), "Screenshots", modules.HandleThumb}, // Dark Cyan (capture)
{"compare", "Compare", utils.MustHex("#E91E63"), "Inspect", modules.HandleCompare}, // Pink (comparison)
{"inspect", "Inspect", utils.MustHex("#F44336"), "Inspect", modules.HandleInspect}, // Red (analysis)
@ -1617,22 +1617,14 @@ func (s *appState) showMainMenu() {
// Convert Module slice to ui.ModuleInfo slice
var mods []ui.ModuleInfo
for _, m := range modulesList {
hasHandler := m.Handle != nil
depsAvailable := isModuleAvailable(m.ID)
// Module is enabled if: (1) it's Settings (special case) OR (2) it has a handler AND dependencies are available
enabled := m.ID == "settings" || (hasHandler && depsAvailable)
// Missing dependencies = has handler but dependencies not available
missingDeps := hasHandler && !depsAvailable && m.ID != "settings"
// Settings module is always enabled
enabled := m.ID == "settings" || isModuleAvailable(m.ID)
mods = append(mods, ui.ModuleInfo{
ID: m.ID,
Label: m.Label,
Color: m.Color,
Category: m.Category,
Enabled: enabled,
MissingDependencies: missingDeps,
ID: m.ID,
Label: m.Label,
Color: m.Color,
Category: m.Category,
Enabled: enabled,
})
}
@ -2673,45 +2665,10 @@ func (s *appState) showBenchmarkHistory() {
s.setContent(view)
}
func (s *appState) showMissingDependenciesDialog(moduleID string) {
missing, _ := getModuleDependencyStatus(moduleID)
if len(missing) == 0 {
return // No missing dependencies
}
// Build message with missing dependencies and install commands
var message strings.Builder
message.WriteString("This module requires the following dependencies:\n\n")
for _, depName := range missing {
if dep, ok := allDependencies[depName]; ok {
message.WriteString(fmt.Sprintf("• %s\n", dep.Name))
if dep.InstallCmd != "" {
message.WriteString(fmt.Sprintf(" Install: %s\n\n", dep.InstallCmd))
}
}
}
// Create dialog
dialog.ShowInformation(
"Missing Dependencies",
message.String(),
s.window,
)
}
func (s *appState) showModule(id string) {
if id != "queue" {
s.stopQueueAutoRefresh()
}
// Check if module has missing dependencies
if !isModuleAvailable(id) && id != "settings" {
s.showMissingDependenciesDialog(id)
return
}
// Track navigation history
s.pushNavigationHistory(id)