refactor: Restructure status bar rendering with improved layout management
This commit is contained in:
parent
f2a07737df
commit
1c5588470e
171
gtm
171
gtm
|
|
@ -126,6 +126,30 @@ def get_diff_info(current_commit, prev_commit, filename):
|
||||||
i += 1
|
i += 1
|
||||||
return added_lines, deleted_lines
|
return added_lines, deleted_lines
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class StatusBarLayout:
|
||||||
|
"""Encapsulates all status bar layout calculations"""
|
||||||
|
total_height: int
|
||||||
|
start_y: int
|
||||||
|
main_status_y: int
|
||||||
|
commit_detail_start_y: int
|
||||||
|
commit_detail_end_y: int
|
||||||
|
available_commit_lines: int
|
||||||
|
screen_width: int
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls, screen_height: int, screen_width: int, status_bar_height: int):
|
||||||
|
start_y = screen_height - status_bar_height
|
||||||
|
return cls(
|
||||||
|
total_height=status_bar_height,
|
||||||
|
start_y=start_y,
|
||||||
|
main_status_y=start_y, # Top line is main status
|
||||||
|
commit_detail_start_y=start_y + 1, # Commit details start below
|
||||||
|
commit_detail_end_y=screen_height - 1, # Bottom line
|
||||||
|
available_commit_lines=status_bar_height - 1,
|
||||||
|
screen_width=screen_width
|
||||||
|
)
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class AppState:
|
class AppState:
|
||||||
filename: str
|
filename: str
|
||||||
|
|
@ -810,52 +834,19 @@ def draw_help_popup(stdscr, state):
|
||||||
except curses.error:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def draw_status_bars(stdscr, state):
|
def draw_status_bar_background(stdscr, layout: StatusBarLayout):
|
||||||
# Calculate the position of the status bars
|
"""Fill the entire status bar area with reverse video background"""
|
||||||
status_bar_start = state.height - state.status_bar_height
|
for y in range(layout.start_y, layout.start_y + layout.total_height):
|
||||||
visible_height = status_bar_start
|
for x in range(layout.screen_width - 1):
|
||||||
|
|
||||||
# If in search mode, draw the search input field instead of the normal status bars
|
|
||||||
if state.search_mode:
|
|
||||||
# Fill the top status bar with spaces
|
|
||||||
for x in range(state.width - 1):
|
|
||||||
try:
|
|
||||||
stdscr.addch(status_bar_start, x, ' ')
|
|
||||||
except curses.error:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Draw search prompt
|
|
||||||
search_prompt = "Search: "
|
|
||||||
try:
|
|
||||||
stdscr.addstr(status_bar_start, 0, search_prompt)
|
|
||||||
|
|
||||||
# Draw the search query
|
|
||||||
stdscr.addstr(status_bar_start, len(search_prompt), state.search_query)
|
|
||||||
|
|
||||||
# Draw cursor at the end of the query
|
|
||||||
try:
|
|
||||||
stdscr.move(status_bar_start, len(search_prompt) + len(state.search_query))
|
|
||||||
except curses.error:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# If we have search results, show count
|
|
||||||
if state.search_matches:
|
|
||||||
match_info = f" [{state.current_match_idx + 1}/{len(state.search_matches)}]"
|
|
||||||
stdscr.addstr(status_bar_start, state.width - len(match_info) - 1, match_info)
|
|
||||||
except curses.error:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Fill all remaining status bar lines with spaces
|
|
||||||
for y in range(status_bar_start + 1, state.height):
|
|
||||||
for x in range(state.width - 1):
|
|
||||||
try:
|
try:
|
||||||
stdscr.addch(y, x, ' ', curses.A_REVERSE)
|
stdscr.addch(y, x, ' ', curses.A_REVERSE)
|
||||||
except curses.error:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return
|
def draw_main_status_line(stdscr, state: AppState, layout: StatusBarLayout):
|
||||||
|
"""Draw the main status line (percentages, indicators, etc.)"""
|
||||||
|
visible_height = layout.start_y
|
||||||
|
|
||||||
# --- First status bar (top) ---
|
|
||||||
# Add indicators to status bar
|
# Add indicators to status bar
|
||||||
wrap_indicator = "" if state.wrap_lines else "[NW] "
|
wrap_indicator = "" if state.wrap_lines else "[NW] "
|
||||||
change_indicator = ""
|
change_indicator = ""
|
||||||
|
|
@ -886,15 +877,7 @@ def draw_status_bars(stdscr, state):
|
||||||
left_status = f"{left_percent}%"
|
left_status = f"{left_percent}%"
|
||||||
|
|
||||||
# Use terminal's default colors for the status bar
|
# Use terminal's default colors for the status bar
|
||||||
status_attr = curses.A_NORMAL # Use terminal's default colors
|
status_attr = curses.A_NORMAL
|
||||||
|
|
||||||
# Fill all status bar lines with spaces using reverse video
|
|
||||||
for y in range(status_bar_start, state.height):
|
|
||||||
for x in range(state.width - 1):
|
|
||||||
try:
|
|
||||||
stdscr.addch(y, x, ' ', curses.A_REVERSE)
|
|
||||||
except curses.error:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Add left percentage indicator (only if sidebar is visible)
|
# Add left percentage indicator (only if sidebar is visible)
|
||||||
if state.show_sidebar:
|
if state.show_sidebar:
|
||||||
|
|
@ -902,7 +885,7 @@ def draw_status_bars(stdscr, state):
|
||||||
# Use normal video if left pane is active (since status bar is already reverse)
|
# Use normal video if left pane is active (since status bar is already reverse)
|
||||||
left_attr = status_attr if state.focus == "left" else curses.A_REVERSE
|
left_attr = status_attr if state.focus == "left" else curses.A_REVERSE
|
||||||
# Add a space before and after the percentage with the same highlighting
|
# Add a space before and after the percentage with the same highlighting
|
||||||
stdscr.addstr(status_bar_start, 0, f" {left_status} ", left_attr)
|
stdscr.addstr(layout.main_status_y, 0, f" {left_status} ", left_attr)
|
||||||
except curses.error:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -911,14 +894,14 @@ def draw_status_bars(stdscr, state):
|
||||||
# Calculate available space
|
# Calculate available space
|
||||||
left_margin = len(left_status) + 3 if state.show_sidebar else 1 # +3 for the spaces around percentage
|
left_margin = len(left_status) + 3 if state.show_sidebar else 1 # +3 for the spaces around percentage
|
||||||
right_margin = len(right_status) + 3 # +3 for the spaces around percentage
|
right_margin = len(right_status) + 3 # +3 for the spaces around percentage
|
||||||
available_width = state.width - left_margin - right_margin
|
available_width = layout.screen_width - left_margin - right_margin
|
||||||
|
|
||||||
# Truncate if needed
|
# Truncate if needed
|
||||||
if len(status_indicators) > available_width:
|
if len(status_indicators) > available_width:
|
||||||
status_indicators = status_indicators[:available_width-3] + "..."
|
status_indicators = status_indicators[:available_width-3] + "..."
|
||||||
|
|
||||||
try:
|
try:
|
||||||
stdscr.addstr(status_bar_start, left_margin, status_indicators, curses.A_REVERSE)
|
stdscr.addstr(layout.main_status_y, left_margin, status_indicators, curses.A_REVERSE)
|
||||||
except curses.error:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -926,45 +909,37 @@ def draw_status_bars(stdscr, state):
|
||||||
try:
|
try:
|
||||||
# Include spaces in the highlighted area
|
# Include spaces in the highlighted area
|
||||||
padded_right_status = f" {right_status} "
|
padded_right_status = f" {right_status} "
|
||||||
right_x = state.width - len(padded_right_status)
|
right_x = layout.screen_width - len(padded_right_status)
|
||||||
if right_x >= 0:
|
if right_x >= 0:
|
||||||
# Use normal video if right pane is active (since status bar is already reverse)
|
# Use normal video if right pane is active (since status bar is already reverse)
|
||||||
right_attr = status_attr if state.focus == "right" else curses.A_REVERSE
|
right_attr = status_attr if state.focus == "right" else curses.A_REVERSE
|
||||||
stdscr.addstr(status_bar_start, right_x, padded_right_status, right_attr)
|
stdscr.addstr(layout.main_status_y, right_x, padded_right_status, right_attr)
|
||||||
except curses.error:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# --- Second status bar and additional lines for commit details ---
|
def draw_commit_details(stdscr, state: AppState, layout: StatusBarLayout):
|
||||||
# Draw the commit details in the status bar area
|
"""Draw commit hash, message, author in the available space"""
|
||||||
|
if not state.commit_hash:
|
||||||
|
return
|
||||||
|
|
||||||
# Draw the commit hash and message
|
|
||||||
if state.commit_hash:
|
|
||||||
commit_info = f" {state.commit_hash} "
|
commit_info = f" {state.commit_hash} "
|
||||||
|
|
||||||
# Calculate available space for the commit message
|
|
||||||
author_branch_info = f" {state.commit_author} [{state.commit_branch}] "
|
author_branch_info = f" {state.commit_author} [{state.commit_branch}] "
|
||||||
available_width = state.width - len(commit_info) - len(author_branch_info) - 1
|
available_width = layout.screen_width - len(commit_info) - len(author_branch_info) - 1
|
||||||
|
|
||||||
# Get the commit message (potentially multi-line)
|
# Get the commit message (potentially multi-line)
|
||||||
commit_message = state.commit_message
|
message_lines = state.commit_message.splitlines()
|
||||||
|
|
||||||
# For multi-line messages, we'll show as many lines as we can fit in the status bar height
|
|
||||||
message_lines = commit_message.splitlines()
|
|
||||||
if not message_lines:
|
if not message_lines:
|
||||||
message_lines = [""]
|
message_lines = [""]
|
||||||
|
|
||||||
# Calculate how many message lines we can display (reserve 1 line for the main status bar)
|
|
||||||
available_message_lines = state.status_bar_height - 1
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Always draw the commit hash and first line of message on the bottom line
|
# Always draw the commit hash and first line of message on the bottom line
|
||||||
bottom_line = state.height - 1
|
bottom_line = layout.commit_detail_end_y
|
||||||
|
|
||||||
# Draw the commit hash with bold
|
# Draw the commit hash with bold
|
||||||
stdscr.addstr(bottom_line, 0, commit_info, curses.A_REVERSE | curses.A_BOLD)
|
stdscr.addstr(bottom_line, 0, commit_info, curses.A_REVERSE | curses.A_BOLD)
|
||||||
|
|
||||||
# Draw the author and branch on the right of the bottom line
|
# Draw the author and branch on the right of the bottom line
|
||||||
right_x = state.width - len(author_branch_info)
|
right_x = layout.screen_width - len(author_branch_info)
|
||||||
if right_x >= 0:
|
if right_x >= 0:
|
||||||
stdscr.addstr(bottom_line, right_x, author_branch_info, curses.A_REVERSE)
|
stdscr.addstr(bottom_line, right_x, author_branch_info, curses.A_REVERSE)
|
||||||
|
|
||||||
|
|
@ -975,31 +950,67 @@ def draw_status_bars(stdscr, state):
|
||||||
stdscr.addstr(bottom_line, len(commit_info), first_line, curses.A_REVERSE)
|
stdscr.addstr(bottom_line, len(commit_info), first_line, curses.A_REVERSE)
|
||||||
|
|
||||||
# Draw additional lines of the commit message if we have space and more lines
|
# Draw additional lines of the commit message if we have space and more lines
|
||||||
for i in range(1, min(len(message_lines), available_message_lines + 1)):
|
for i, line in enumerate(message_lines[1:], 1):
|
||||||
if i >= len(message_lines):
|
line_y = layout.commit_detail_end_y - i
|
||||||
break
|
if line_y < layout.commit_detail_start_y:
|
||||||
line = message_lines[i]
|
break # No more space
|
||||||
|
|
||||||
# For continuation lines, we have more space since we don't need to show the hash
|
# For continuation lines, we have more space since we don't need to show the hash
|
||||||
line_available_width = state.width - 4 # Leave some margin
|
line_available_width = layout.screen_width - 4 # Leave some margin
|
||||||
if len(line) > line_available_width:
|
if len(line) > line_available_width:
|
||||||
line = line[:line_available_width-3] + "..."
|
line = line[:line_available_width-3] + "..."
|
||||||
# Draw the line with some indentation (going upward from the bottom line)
|
|
||||||
line_y = bottom_line - i
|
|
||||||
if line_y >= status_bar_start: # Make sure we don't draw above the status bar area
|
|
||||||
stdscr.addstr(line_y, 4, line, curses.A_REVERSE)
|
stdscr.addstr(line_y, 4, line, curses.A_REVERSE)
|
||||||
except curses.error:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Draw a handle for resizing the status bar
|
def draw_search_mode(stdscr, state: AppState, layout: StatusBarLayout):
|
||||||
|
"""Draw search input interface"""
|
||||||
|
# Draw search prompt on the main status line
|
||||||
|
search_prompt = "Search: "
|
||||||
|
try:
|
||||||
|
stdscr.addstr(layout.main_status_y, 0, search_prompt)
|
||||||
|
|
||||||
|
# Draw the search query
|
||||||
|
stdscr.addstr(layout.main_status_y, len(search_prompt), state.search_query)
|
||||||
|
|
||||||
|
# Draw cursor at the end of the query
|
||||||
|
try:
|
||||||
|
stdscr.move(layout.main_status_y, len(search_prompt) + len(state.search_query))
|
||||||
|
except curses.error:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# If we have search results, show count
|
||||||
|
if state.search_matches:
|
||||||
|
match_info = f" [{state.current_match_idx + 1}/{len(state.search_matches)}]"
|
||||||
|
stdscr.addstr(layout.main_status_y, layout.screen_width - len(match_info) - 1, match_info)
|
||||||
|
except curses.error:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def draw_resize_handle(stdscr, state: AppState, layout: StatusBarLayout):
|
||||||
|
"""Draw the resize handle"""
|
||||||
try:
|
try:
|
||||||
handle_char = "≡" if state.dragging_status_bar else "="
|
handle_char = "≡" if state.dragging_status_bar else "="
|
||||||
handle_text = handle_char * 5 # Make handle wider
|
handle_text = handle_char * 5 # Make handle wider
|
||||||
if state.status_bar_height > 2:
|
if state.status_bar_height > 2:
|
||||||
handle_text += f" ({state.status_bar_height} lines)"
|
handle_text += f" ({state.status_bar_height} lines)"
|
||||||
stdscr.addstr(status_bar_start, state.width // 2 - len(handle_text) // 2, handle_text, curses.A_REVERSE | curses.A_BOLD)
|
handle_x = layout.screen_width // 2 - len(handle_text) // 2
|
||||||
|
stdscr.addstr(layout.main_status_y, handle_x, handle_text, curses.A_REVERSE | curses.A_BOLD)
|
||||||
except curses.error:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def draw_status_bars(stdscr, state):
|
||||||
|
layout = StatusBarLayout.create(state.height, state.width, state.status_bar_height)
|
||||||
|
|
||||||
|
draw_status_bar_background(stdscr, layout)
|
||||||
|
|
||||||
|
if state.search_mode:
|
||||||
|
draw_search_mode(stdscr, state, layout)
|
||||||
|
else:
|
||||||
|
draw_main_status_line(stdscr, state, layout)
|
||||||
|
draw_commit_details(stdscr, state, layout)
|
||||||
|
draw_resize_handle(stdscr, state, layout)
|
||||||
|
|
||||||
def draw_ui(stdscr, state):
|
def draw_ui(stdscr, state):
|
||||||
stdscr.erase()
|
stdscr.erase()
|
||||||
draw_left_pane(stdscr, state)
|
draw_left_pane(stdscr, state)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue