ui(console): center menu in a bordered box with better spacing
Both renderers now draw the menu inside a centered box with: - Rounded corners (modern) / box-drawing (VT220) - Title centered inside the box top - Section separators as horizontal lines within the box - Selected item highlighted across the full box width - Vertical and horizontal centering on screen Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -167,17 +167,42 @@ class CursesRenderer(BaseRenderer):
|
||||
|
||||
def draw_menu(self, items, selected_index=0, title=''):
|
||||
h, w = self.get_size()
|
||||
start_row = 3
|
||||
|
||||
# Calculate menu dimensions for centering
|
||||
item_count = len(items)
|
||||
# Find widest label for box sizing
|
||||
max_label = 0
|
||||
for num, label in items:
|
||||
if num != "---" and num != "\u2500":
|
||||
max_label = max(max_label, len(f" {num}. {label} "))
|
||||
box_w = max(max_label + 8, 44)
|
||||
box_w = min(box_w, w - 4)
|
||||
box_h = item_count + 4 # top/bottom border + title + blank line
|
||||
if title:
|
||||
self._safe_addstr(start_row, 2, title, self._attr("title"))
|
||||
start_row += 2
|
||||
box_h += 2
|
||||
|
||||
visible = h - start_row - 3 # leave room for footer
|
||||
if visible < 1:
|
||||
return
|
||||
# Center the box vertically and horizontally
|
||||
start_row = max((h - box_h) // 2 - 1, 2)
|
||||
start_col = max((w - box_w) // 2, 1)
|
||||
|
||||
# Scrolling offset
|
||||
# Draw box
|
||||
self.draw_box(start_row, start_col, box_h, box_w, "")
|
||||
|
||||
row = start_row + 1
|
||||
if title:
|
||||
# Title centered inside the box
|
||||
title_col = start_col + max((box_w - len(title)) // 2, 2)
|
||||
self._safe_addstr(row, title_col, title,
|
||||
self._attr("title"))
|
||||
row += 1
|
||||
self._hline(row, start_col + 1, box_w - 2)
|
||||
row += 1
|
||||
|
||||
# Menu items inside the box
|
||||
inner_left = start_col + 3
|
||||
inner_w = box_w - 6
|
||||
|
||||
visible = box_h - (row - start_row) - 1
|
||||
offset = 0
|
||||
if selected_index >= visible:
|
||||
offset = selected_index - visible + 1
|
||||
@@ -188,19 +213,20 @@ class CursesRenderer(BaseRenderer):
|
||||
break
|
||||
if idx < offset:
|
||||
continue
|
||||
row = start_row + drawn
|
||||
|
||||
# Separator
|
||||
if num == "\u2500" or num == "---":
|
||||
self._hline(row, 2, w - 4)
|
||||
self._hline(row, start_col + 1, box_w - 2)
|
||||
row += 1
|
||||
drawn += 1
|
||||
continue
|
||||
|
||||
marker = "\u25b8 " if idx == selected_index else " "
|
||||
text = f"{marker}{num}. {label}"
|
||||
style = "highlight" if idx == selected_index else "normal"
|
||||
self._safe_addstr(row, 2, pad_right(text, w - 4),
|
||||
self._safe_addstr(row, inner_left, pad_right(text, inner_w),
|
||||
self._attr(style))
|
||||
row += 1
|
||||
drawn += 1
|
||||
|
||||
def draw_table(self, headers, rows, widths, page_info=None,
|
||||
|
||||
Reference in New Issue
Block a user