Compare commits
No commits in common. "62dd39347a6516ab486222b9c5936ef048ee1ed8" and "3b99cad32bfebb6befc012b78ed8fe0b73aa171e" have entirely different histories.
62dd39347a
...
3b99cad32b
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
67
main.go
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user