Skip to content

Commit 231799a

Browse files
committed
fix: unify bottom bar behavior and redraw flow
1 parent f07c125 commit 231799a

12 files changed

Lines changed: 401 additions & 53 deletions

retrotui/constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@
119119

120120
# Layout constants
121121
MENU_BAR_HEIGHT = 1 # Row 0 is the global menu bar
122-
BOTTOM_BARS_HEIGHT = 2 # Taskbar + status bar at bottom
122+
BOTTOM_BARS_HEIGHT = 1 # Unified bottom bar (taskbar + status info)
123123
TASKBAR_TITLE_MAX_LEN = 15 # Max chars shown in taskbar buttons
124124
WIN_MIN_WIDTH = 20 # Minimum window width on resize
125125
WIN_MIN_HEIGHT = 8 # Minimum window height on resize

retrotui/core/event_loop.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,34 @@ def _emit_runtime_metrics(metrics, final=False):
259259
)
260260

261261

262+
def _refresh_idle_clock(app):
263+
"""Refresh only the menu clock while idle, avoiding a full frame redraw."""
264+
if getattr(app, "_dirty", False):
265+
return False
266+
267+
frame_size = getattr(app, "_frame_size", None)
268+
width = None
269+
if isinstance(frame_size, tuple) and len(frame_size) == 2:
270+
width = frame_size[1]
271+
272+
menu = getattr(app, "menu", None)
273+
refresh_clock = getattr(menu, "refresh_clock", None)
274+
if callable(refresh_clock):
275+
try:
276+
updated = bool(refresh_clock(app.stdscr, width=width))
277+
except _INPUT_TIMEOUT_APPLY_ERRORS:
278+
return False
279+
280+
if not updated:
281+
return False
282+
try:
283+
app.stdscr.noutrefresh()
284+
curses.doupdate()
285+
except _INPUT_TIMEOUT_APPLY_ERRORS:
286+
return False
287+
return True
288+
289+
262290
def run_app_loop(app):
263291
"""Run main draw/input loop with terminal cleanup on exit."""
264292
metrics = _ensure_runtime_metrics(app)
@@ -292,6 +320,8 @@ def run_app_loop(app):
292320
consume_sigint = getattr(app, "_consume_pending_sigint", None)
293321
if callable(consume_sigint):
294322
key = consume_sigint()
323+
if key is None and _refresh_idle_clock(app):
324+
metrics["redraws"] += 1
295325
_record_input_stats(metrics, key)
296326
dispatch_start = time.perf_counter()
297327
if dispatch_input(app, key):

retrotui/core/mouse_router.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -660,15 +660,15 @@ def handle_mouse_event(app, event):
660660
if app._handle_global_menu_mouse(mx, my, bstate):
661661
return True
662662

663+
if norm.get("is_click_like") and app.handle_taskbar_click(mx, my):
664+
return True
665+
663666
h, w = app.stdscr.getmaxyx()
664667
if my == h - 1 and mx >= w - CLOCK_CLICK_REGION_WIDTH:
665668
if norm.get("button1_clicked") or norm.get("button1_double"):
666669
app.execute_action(AppAction.CLOCK_CALENDAR)
667670
return True
668671

669-
if norm.get("is_click_like") and app.handle_taskbar_click(mx, my):
670-
return True
671-
672672
if app._handle_window_mouse(mx, my, bstate):
673673
return True
674674

retrotui/core/rendering.py

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,26 @@ def _window_stats(app):
7171
return stats
7272

7373

74+
def _taskbar_buttons(app, width, stats=None):
75+
"""Return taskbar button layout as `(start_x, end_x, label, win)` tuples."""
76+
window_mgr = getattr(app, "window_mgr", None)
77+
if window_mgr is not None and hasattr(window_mgr, "taskbar_buttons"):
78+
return tuple(window_mgr.taskbar_buttons(width))
79+
80+
if stats is None:
81+
stats = _window_stats(app)
82+
83+
x = 1
84+
buttons = []
85+
for label in stats["minimized_labels"]:
86+
btn_w = len(label) + 2 # [label]
87+
if x + btn_w > width:
88+
break
89+
buttons.append((x, x + btn_w, label, None))
90+
x += btn_w + 1
91+
return tuple(buttons)
92+
93+
7494
def draw_desktop(app, frame_size=None):
7595
"""Draw the desktop background pattern."""
7696
h, w = _resolve_frame_size(app, frame_size)
@@ -119,39 +139,27 @@ def draw_taskbar(app, frame_size=None):
119139
# Always clear the taskbar line
120140
safe_addstr(app.stdscr, taskbar_y, 0, ' ' * w, attr)
121141

122-
window_mgr = getattr(app, "window_mgr", None)
123-
if window_mgr is not None and hasattr(window_mgr, "taskbar_buttons"):
124-
buttons = window_mgr.taskbar_buttons(w)
125-
for start_x, _end_x, label, _win in buttons:
126-
safe_addstr(app.stdscr, taskbar_y, start_x, f'[{label}]', attr | curses.A_BOLD)
127-
return
128-
129142
stats = _window_stats(app)
130-
minimized_labels = stats["minimized_labels"]
131-
if not minimized_labels:
132-
return
133-
x = 1
134-
for label in minimized_labels:
135-
btn = f'[{label}]'
136-
if x + len(btn) > w:
137-
break
138-
safe_addstr(app.stdscr, taskbar_y, x, btn, attr | curses.A_BOLD)
139-
x += len(btn) + 1
143+
buttons = _taskbar_buttons(app, w, stats=stats)
144+
for start_x, _end_x, label, _win in buttons:
145+
safe_addstr(app.stdscr, taskbar_y, start_x, f'[{label}]', attr | curses.A_BOLD)
140146

141147

142148
def draw_statusbar(app, version, frame_size=None):
143-
"""Draw the bottom status bar."""
149+
"""Draw status text on the unified bottom bar without clearing taskbar buttons."""
144150
h, w = _resolve_frame_size(app, frame_size)
145151
attr = theme_attr("status")
146152
stats = _window_stats(app)
147153
visible = stats["visible"]
148154
total = stats["total"]
149-
150-
left_status = f' RetroTUI v{version} | Windows: {visible}/{total} | Mouse: Enabled'
151-
152-
statusbar_y = h - BOTTOM_BARS_HEIGHT + 1 # Last row: below taskbar
153-
# Draw background
154-
safe_addstr(app.stdscr, statusbar_y, 0, ' ' * w, attr)
155+
status_text = f' RetroTUI v{version} | Windows: {visible}/{total} | Mouse: Enabled '
155156

156-
# Draw left text
157-
safe_addstr(app.stdscr, statusbar_y, 0, left_status, attr)
157+
statusbar_y = h - BOTTOM_BARS_HEIGHT
158+
buttons = _taskbar_buttons(app, w, stats=stats)
159+
left_reserved = buttons[-1][1] + 1 if buttons else 0
160+
161+
status_x = left_reserved + 1
162+
max_status_len = w - status_x
163+
if max_status_len <= 0:
164+
return
165+
safe_addstr(app.stdscr, statusbar_y, status_x, status_text[:max_status_len], attr)

0 commit comments

Comments
 (0)