From 89824f7859a2280e3db22cbe0394cc1ae684bae1 Mon Sep 17 00:00:00 2001 From: Stu Leak Date: Wed, 31 Dec 2025 12:25:24 -0500 Subject: [PATCH] feat(ui): Add lock icon to disabled modules for better visibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhanced disabled module visual indicators: - Added lock icon (🔒) in top-right corner of disabled tiles - Lock icon shows/hides dynamically based on module availability - Improved Refresh() to handle dynamic enable/disable state changes - Updated renderer to include lock icon in layout and objects list This makes it immediately clear which modules are available and which require missing dependencies, addressing the issue where adaptive text colors made disabled modules less distinguishable. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- internal/ui/components.go | 54 +++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/internal/ui/components.go b/internal/ui/components.go index 5006c77..a1f09fd 100644 --- a/internal/ui/components.go +++ b/internal/ui/components.go @@ -168,10 +168,19 @@ 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() + } + return &moduleTileRenderer{ - tile: m, - bg: bg, - label: txt, + tile: m, + bg: bg, + label: txt, + lockIcon: lockIcon, } } @@ -182,9 +191,10 @@ func (m *ModuleTile) Tapped(*fyne.PointEvent) { } type moduleTileRenderer struct { - tile *ModuleTile - bg *canvas.Rectangle - label *canvas.Text + tile *ModuleTile + bg *canvas.Rectangle + label *canvas.Text + lockIcon *canvas.Text } func (r *moduleTileRenderer) Layout(size fyne.Size) { @@ -195,6 +205,15 @@ func (r *moduleTileRenderer) Layout(size fyne.Size) { 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 { @@ -202,7 +221,23 @@ func (r *moduleTileRenderer) MinSize() fyne.Size { } func (r *moduleTileRenderer) Refresh() { - r.bg.FillColor = r.tile.color + // 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() + } + } // Apply visual feedback based on state if r.tile.flashing { @@ -222,12 +257,15 @@ func (r *moduleTileRenderer) Refresh() { r.bg.Refresh() r.label.Text = r.tile.label r.label.Refresh() + if r.lockIcon != nil { + r.lockIcon.Refresh() + } } func (r *moduleTileRenderer) Destroy() {} func (r *moduleTileRenderer) Objects() []fyne.CanvasObject { - return []fyne.CanvasObject{r.bg, r.label} + return []fyne.CanvasObject{r.bg, r.label, r.lockIcon} } // TintedBar creates a colored bar container