src/UI/StatusBar.ps1
|
# StatusBar — Barra inferior. SIEMPRE devuelve 2+ líneas: # - Línea(s) 1+: hints/keybindings, con auto-wrap si no entran en el ancho. # - Línea final: counts/right (siempre su propia línea). # # Esto deja crecer el set de bindings sin que se "coman" el lado derecho # (Tino reportó: 'son mucho y se comen la parte de la derecha'). class StatusBar { [object] $Theme [object] $Renderer [object] $Primitives StatusBar($theme, $renderer, $primitives) { $this.Theme = $theme $this.Renderer = $renderer $this.Primitives = $primitives } # $items = array de hashtables @{ k='↑↓'; label='Move' } # $right = string ya renderizado (counts, etc.). Vacío = sin línea de right. # Devuelve array de strings, una entrada por línea visible. # # Diseño: NO aplicamos bg propio — respetamos el fondo del terminal. # Razón: los ${reset} interiores en cada hint/cell quiebran cualquier ${bg} # que apliquemos al inicio de la línea, causando bandas inconsistentes (Tino # lo reportó como "el primer espacio queda con otro color"). Las HRules # encima del statusbar ya marcan visualmente el bloque. [string[]] Render([hashtable[]]$items, [string]$right) { $reset = [AnsiService]::Reset $sepFg = $this.Theme.Fg('fg3') + ' │ ' + $reset $width = $this.Renderer.Width() # Chars utiles dentro del padding lateral ' ... ' $usable = [Math]::Max(10, $width - 2) # Renderiza cada hint como cell. $cells = @() foreach ($it in $items) { $k = $this.Primitives.K($it.k) $label = $this.Theme.Fg('fg1') + $it.label + $reset $cells += "$k $label" } $lines = @() # Agrupar cells en líneas de hints. Si una cell sola no entra (caso # extremo de width chico), igual la metemos en su línea — algo es # mejor que nada. $current = '' foreach ($cell in $cells) { $sep = if ($current) { $sepFg } else { '' } $candidate = $current + $sep + $cell if ([Renderer]::VisibleLength($candidate) -gt $usable) { # No entra — push current y arrancamos línea nueva con esta cell if ($current) { $lines += " $current" } $current = $cell } else { $current = $candidate } } if ($current) { $lines += " $current" } # Línea final: right (counts). Si hay right, alineado a la derecha. if ($right) { $padCount = [Math]::Max(0, $width - [Renderer]::VisibleLength($right) - 2) $lines += "$(' ' * $padCount)${right}" } return $lines } # Conveniencia para llamadas legacy de 1 línea con right vacío. [string[]] Render([hashtable[]]$items) { return $this.Render($items, '') } # Devuelve [padding empty lines] + [statusbar lines] para que el statusbar # quede anclado al fondo de la consola. El caller pasa cuántas líneas de # contenido ya tiene; calculamos el gap contra WindowHeight y rellenamos. # Si el contenido excede el alto disponible, no se padea (degrade gracefully). [string[]] RenderAnchored([hashtable[]]$items, [string]$right, [int]$contentLineCount) { $statusLines = $this.Render($items, $right) $padding = [Console]::WindowHeight - $contentLineCount - $statusLines.Count if ($padding -le 0) { return $statusLines } $padArray = @() for ($p = 0; $p -lt $padding; $p++) { $padArray += '' } return $padArray + $statusLines } # Overload con right vacío para screens que no usan la línea de counts. [string[]] RenderAnchored([hashtable[]]$items, [int]$contentLineCount) { return $this.RenderAnchored($items, '', $contentLineCount) } } |