mirror of
https://github.com/phranck/TUIkit.git
synced 2026-06-20 09:54:37 +00:00
feat(Menu): Improve selection bar styling
- Selection bar now uses full width (padded to content width) - Selection background uses dimmed theme color (selectionBackground) - Selection text is bold and highlighted with accent color - Add selectionBackground to Theme protocol and all themes - Remove arrow indicator, use pure background highlight for selection
This commit is contained in:
@@ -85,8 +85,11 @@ public protocol Theme: Sendable {
|
||||
/// Separator/divider color.
|
||||
var separator: Color { get }
|
||||
|
||||
/// Selection highlight color.
|
||||
/// Selection highlight color (foreground).
|
||||
var selection: Color { get }
|
||||
|
||||
/// Selection background color (dimmed accent).
|
||||
var selectionBackground: Color { get }
|
||||
|
||||
/// Color for disabled elements.
|
||||
var disabled: Color { get }
|
||||
@@ -116,6 +119,7 @@ extension Theme {
|
||||
public var borderFocused: Color { accent }
|
||||
public var separator: Color { border }
|
||||
public var selection: Color { accent }
|
||||
public var selectionBackground: Color { backgroundSecondary }
|
||||
public var disabled: Color { foregroundTertiary }
|
||||
public var statusBarBackground: Color { backgroundSecondary }
|
||||
public var statusBarForeground: Color { foreground }
|
||||
@@ -233,6 +237,9 @@ public enum ThemeColors {
|
||||
|
||||
/// Selection color.
|
||||
public static var selection: Color { current.selection }
|
||||
|
||||
/// Selection background color.
|
||||
public static var selectionBackground: Color { current.selectionBackground }
|
||||
|
||||
/// Disabled color.
|
||||
public static var disabled: Color { current.disabled }
|
||||
@@ -299,7 +306,8 @@ public struct GreenPhosphorTheme: Theme {
|
||||
// UI elements
|
||||
public let border = Color.hex(0x2D5A2D) // Subtle green border
|
||||
public let borderFocused = Color.hex(0x33FF33) // Bright when focused
|
||||
public let selection = Color.hex(0x1F4D1F) // Dark green for selection bg
|
||||
public let selection = Color.hex(0x66FF66) // Bright green for selection text
|
||||
public let selectionBackground = Color.hex(0x1A4D1A) // Dark green for selection bar bg
|
||||
|
||||
// Status bar
|
||||
public let statusBarBackground = Color.hex(0x162016)
|
||||
@@ -340,7 +348,8 @@ public struct AmberPhosphorTheme: Theme {
|
||||
// UI elements
|
||||
public let border = Color.hex(0x5A4A2D) // Subtle amber border
|
||||
public let borderFocused = Color.hex(0xFFAA00) // Bright when focused
|
||||
public let selection = Color.hex(0x4D3A1F) // Dark amber for selection bg
|
||||
public let selection = Color.hex(0xFFCC33) // Bright amber for selection text
|
||||
public let selectionBackground = Color.hex(0x4D3A1F) // Dark amber for selection bar bg
|
||||
|
||||
// Status bar
|
||||
public let statusBarBackground = Color.hex(0x201A14)
|
||||
@@ -381,7 +390,8 @@ public struct WhitePhosphorTheme: Theme {
|
||||
// UI elements
|
||||
public let border = Color.hex(0x484848) // Subtle gray border
|
||||
public let borderFocused = Color.hex(0xE8E8E8) // Bright when focused
|
||||
public let selection = Color.hex(0x3A3A3A) // Dark gray for selection bg
|
||||
public let selection = Color.hex(0xFFFFFF) // White for selection text
|
||||
public let selectionBackground = Color.hex(0x3A3A3A) // Dark gray for selection bar bg
|
||||
|
||||
// Status bar
|
||||
public let statusBarBackground = Color.hex(0x181C20)
|
||||
@@ -422,7 +432,8 @@ public struct RedPhosphorTheme: Theme {
|
||||
// UI elements
|
||||
public let border = Color.hex(0x5A2D2D) // Subtle red border
|
||||
public let borderFocused = Color.hex(0xFF4444) // Bright when focused
|
||||
public let selection = Color.hex(0x4D1F1F) // Dark red for selection bg
|
||||
public let selection = Color.hex(0xFF6666) // Bright red for selection text
|
||||
public let selectionBackground = Color.hex(0x4D1F1F) // Dark red for selection bar bg
|
||||
|
||||
// Status bar
|
||||
public let statusBarBackground = Color.hex(0x201414)
|
||||
@@ -455,7 +466,8 @@ public struct NCursesTheme: Theme {
|
||||
public let info = Color.cyan
|
||||
public let border = Color.white
|
||||
public let borderFocused = Color.brightCyan
|
||||
public let selection = Color.blue
|
||||
public let selection = Color.brightCyan
|
||||
public let selectionBackground = Color.blue
|
||||
public let disabled = Color.brightBlack
|
||||
public let statusBarBackground = Color.blue
|
||||
public let statusBarForeground = Color.white
|
||||
|
||||
@@ -200,8 +200,9 @@ extension Menu: Renderable {
|
||||
|
||||
// Menu items
|
||||
let currentSelection = selectionBinding?.wrappedValue ?? selectedIndex
|
||||
let appearance = context.environment.appearance
|
||||
let isBlockAppearance = appearance.id == .block
|
||||
|
||||
// Calculate the content width for full-width selection bar
|
||||
let contentWidth = maxItemWidth + 2 // +2 for padding
|
||||
|
||||
for (index, item) in items.enumerated() {
|
||||
let isSelected = index == currentSelection
|
||||
@@ -214,34 +215,28 @@ extension Menu: Renderable {
|
||||
labelText = " \(item.label)"
|
||||
}
|
||||
|
||||
// For block appearance: no indicator, use background highlight
|
||||
// For other appearances: use indicator prefix
|
||||
let fullText: String
|
||||
if isBlockAppearance {
|
||||
fullText = " " + labelText
|
||||
} else {
|
||||
let prefix = isSelected ? selectionIndicator : String(repeating: " ", count: selectionIndicator.count)
|
||||
fullText = " " + prefix + labelText
|
||||
}
|
||||
// Build the full text with padding
|
||||
let fullText = " " + labelText
|
||||
|
||||
// Pad to full width for selection bar
|
||||
let visibleLength = fullText.count
|
||||
let padding = max(0, contentWidth - visibleLength)
|
||||
let paddedText = fullText + String(repeating: " ", count: padding)
|
||||
|
||||
// Apply styling
|
||||
var style = TextStyle()
|
||||
if isSelected {
|
||||
// Selected: bold text with dimmed background, highlighted foreground
|
||||
style.isBold = true
|
||||
if isBlockAppearance {
|
||||
// Block appearance: use background highlight
|
||||
style.foregroundColor = Color.theme.background
|
||||
style.backgroundColor = selectedColor ?? Color.theme.accent
|
||||
} else {
|
||||
// Other appearances: just change foreground color
|
||||
style.foregroundColor = selectedColor ?? Color.theme.accent
|
||||
}
|
||||
style.foregroundColor = selectedColor ?? Color.theme.accent
|
||||
// Use a dimmed version of the accent color for background
|
||||
style.backgroundColor = Color.theme.selectionBackground
|
||||
} else {
|
||||
// Use theme foreground color if no custom itemColor is set
|
||||
style.foregroundColor = itemColor ?? Color.theme.foreground
|
||||
}
|
||||
|
||||
let styledLine = ANSIRenderer.render(fullText, with: style)
|
||||
let styledLine = ANSIRenderer.render(paddedText, with: style)
|
||||
lines.append(styledLine)
|
||||
}
|
||||
|
||||
@@ -310,7 +305,7 @@ extension Menu: Renderable {
|
||||
private var maxItemWidth: Int {
|
||||
items.map { item -> Int in
|
||||
let shortcutPart = item.shortcut != nil ? 4 : 4 // "[x] " or " "
|
||||
return selectionIndicator.count + shortcutPart + item.label.count
|
||||
return shortcutPart + item.label.count
|
||||
}.max() ?? 0
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user