Added responsiveness to board layout
This commit is contained in:
parent
d7f7b84905
commit
4b4ab92d04
1 changed files with 90 additions and 11 deletions
|
|
@ -817,6 +817,64 @@ TOP_Y = 1
|
|||
COL_Y = 6
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BoardLayout:
|
||||
"""Computed horizontal positions for the current terminal width."""
|
||||
|
||||
freecell_xs: Tuple[int, ...]
|
||||
foundation_xs: Tuple[int, ...]
|
||||
tableau_xs: Tuple[int, ...]
|
||||
freecells_label_x: int
|
||||
foundations_label_x: int
|
||||
tableau_label_x: int
|
||||
|
||||
|
||||
def spread_positions(count: int, start_x: int, end_x: int) -> Tuple[int, ...]:
|
||||
"""Evenly spread fixed-width slots between two inclusive bounds."""
|
||||
if count <= 1:
|
||||
return (start_x,)
|
||||
|
||||
span = max(0, end_x - start_x)
|
||||
step = span / (count - 1)
|
||||
positions = []
|
||||
for index in range(count):
|
||||
positions.append(int(round(start_x + step * index)))
|
||||
return tuple(positions)
|
||||
|
||||
|
||||
def compute_board_layout(max_x: int) -> BoardLayout:
|
||||
"""Compute a centered, width-aware board layout."""
|
||||
left_margin = 2
|
||||
right_margin = max(left_margin, max_x - CARD_W - 2)
|
||||
|
||||
if max_x < 72:
|
||||
top_start = left_margin
|
||||
top_end = max(top_start, max_x - CARD_W - 2)
|
||||
top_positions = spread_positions(8, top_start, top_end)
|
||||
freecell_xs = top_positions[:4]
|
||||
foundation_xs = top_positions[4:]
|
||||
else:
|
||||
center_gap = max(10, min(20, max_x // 6))
|
||||
left_end = max(left_margin, (max_x // 2) - (center_gap // 2) - CARD_W)
|
||||
right_start = min(right_margin, (max_x // 2) + (center_gap // 2))
|
||||
freecell_xs = spread_positions(4, left_margin, left_end)
|
||||
foundation_xs = spread_positions(4, right_start, right_margin)
|
||||
|
||||
tableau_xs = spread_positions(8, left_margin, right_margin)
|
||||
freecells_label_x = freecell_xs[0]
|
||||
foundations_label_x = foundation_xs[0]
|
||||
tableau_label_x = tableau_xs[0]
|
||||
|
||||
return BoardLayout(
|
||||
freecell_xs=freecell_xs,
|
||||
foundation_xs=foundation_xs,
|
||||
tableau_xs=tableau_xs,
|
||||
freecells_label_x=freecells_label_x,
|
||||
foundations_label_x=foundations_label_x,
|
||||
tableau_label_x=tableau_label_x,
|
||||
)
|
||||
|
||||
|
||||
def init_colors() -> None:
|
||||
"""Initialize curses color pairs."""
|
||||
curses.start_color()
|
||||
|
|
@ -859,14 +917,24 @@ def draw_card_label(card: Optional[Card]) -> str:
|
|||
return card.short_name()
|
||||
|
||||
|
||||
def draw_top_row(stdscr, game: FreeCellGame) -> None:
|
||||
def draw_top_row(stdscr, game: FreeCellGame, layout: BoardLayout) -> None:
|
||||
"""Draw freecells and foundations."""
|
||||
stdscr.addstr(TOP_Y, 2, "Free Cells", curses.color_pair(1) | curses.A_BOLD)
|
||||
stdscr.addstr(TOP_Y, 40, "Foundations", curses.color_pair(1) | curses.A_BOLD)
|
||||
stdscr.addstr(
|
||||
TOP_Y,
|
||||
layout.freecells_label_x,
|
||||
"Free Cells",
|
||||
curses.color_pair(1) | curses.A_BOLD,
|
||||
)
|
||||
stdscr.addstr(
|
||||
TOP_Y,
|
||||
layout.foundations_label_x,
|
||||
"Foundations",
|
||||
curses.color_pair(1) | curses.A_BOLD,
|
||||
)
|
||||
|
||||
# Freecells at indexes 0..3
|
||||
for i in range(4):
|
||||
x = 2 + i * (CARD_W + 1)
|
||||
x = layout.freecell_xs[i]
|
||||
selected = game.cursor_zone == "top" and game.cursor_index == i
|
||||
hinted = game.hinted_freecell(i)
|
||||
attr = (
|
||||
|
|
@ -891,7 +959,7 @@ def draw_top_row(stdscr, game: FreeCellGame) -> None:
|
|||
|
||||
# Foundations at indexes 4..7 in cursor space
|
||||
for i in range(4):
|
||||
x = 40 + i * (CARD_W + 1)
|
||||
x = layout.foundation_xs[i]
|
||||
selected = game.cursor_zone == "top" and game.cursor_index == i + 4
|
||||
hinted = game.hinted_foundation(i)
|
||||
attr = (
|
||||
|
|
@ -923,14 +991,19 @@ def draw_top_row(stdscr, game: FreeCellGame) -> None:
|
|||
)
|
||||
|
||||
|
||||
def draw_columns(stdscr, game: FreeCellGame) -> None:
|
||||
def draw_columns(stdscr, game: FreeCellGame, layout: BoardLayout) -> None:
|
||||
"""Draw the tableau columns."""
|
||||
stdscr.addstr(COL_Y - 1, 2, "Tableau", curses.color_pair(1) | curses.A_BOLD)
|
||||
stdscr.addstr(
|
||||
COL_Y - 1,
|
||||
layout.tableau_label_x,
|
||||
"Tableau",
|
||||
curses.color_pair(1) | curses.A_BOLD,
|
||||
)
|
||||
|
||||
max_height = max((len(col) for col in game.columns), default=0)
|
||||
|
||||
for col_idx in range(8):
|
||||
x = 2 + col_idx * 9
|
||||
x = layout.tableau_xs[col_idx]
|
||||
selected = game.cursor_zone == "bottom" and game.cursor_index == col_idx
|
||||
hinted_dest = game.hinted_column_destination(col_idx)
|
||||
header_attr = (
|
||||
|
|
@ -1042,6 +1115,7 @@ def render(stdscr, game: FreeCellGame) -> None:
|
|||
"""Redraw the full screen."""
|
||||
stdscr.erase()
|
||||
max_y, max_x = stdscr.getmaxyx()
|
||||
layout = compute_board_layout(max_x)
|
||||
|
||||
title = "mcfreecell 0.1"
|
||||
if len(title) > max_x - 4:
|
||||
|
|
@ -1056,10 +1130,15 @@ def render(stdscr, game: FreeCellGame) -> None:
|
|||
)
|
||||
|
||||
if game.visual_mode:
|
||||
stdscr.addstr(0, 30, "-- VISUAL --", curses.color_pair(5) | curses.A_BOLD)
|
||||
visual_text = "-- VISUAL --"
|
||||
visual_x = min(max_x - len(visual_text) - 2, title_x + len(title) + 3)
|
||||
if visual_x > title_x + len(title):
|
||||
stdscr.addstr(
|
||||
0, visual_x, visual_text, curses.color_pair(5) | curses.A_BOLD
|
||||
)
|
||||
|
||||
draw_top_row(stdscr, game)
|
||||
draw_columns(stdscr, game)
|
||||
draw_top_row(stdscr, game, layout)
|
||||
draw_columns(stdscr, game, layout)
|
||||
draw_held(stdscr, game, max_y)
|
||||
draw_status(stdscr, game, max_y)
|
||||
draw_help(stdscr, max_y)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue