mirror of
https://github.com/dredx/prole.git
synced 2026-09-24 17:44:33 +00:00
183 lines
6.6 KiB
Python
183 lines
6.6 KiB
Python
"""
|
|
Shared UI helpers for the Prole installer screens.
|
|
|
|
These routines are intentionally lightweight wrappers around the existing
|
|
Tk Canvas used by the installer, to reduce clutter in install.py.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import tkinter as tk
|
|
from tkinter import ttk
|
|
|
|
|
|
def render_title(app, text: str, y: int = 40):
|
|
"""Render a section title on the main background canvas."""
|
|
if getattr(app, 'bg_canvas', None) is None:
|
|
return
|
|
item = app.bg_canvas.create_text(40, y, anchor='nw', text=text, fill='#111', font=('Helvetica Neue', 22, 'bold'))
|
|
app._canvas_items.append(item)
|
|
|
|
|
|
def render_paragraph(app, text: str, y: int, wrap: int = 860):
|
|
"""Render a paragraph on the main background canvas."""
|
|
if getattr(app, 'bg_canvas', None) is None:
|
|
return
|
|
item = app.bg_canvas.create_text(40, y, anchor='nw', text=text, fill='#1d1d1f', font=('Helvetica', 12), width=wrap)
|
|
app._canvas_items.append(item)
|
|
|
|
|
|
def canvas_text(app, x: int, y: int, text: str, *, fill: str = '#1d1d1f',
|
|
font: tuple = ('Helvetica', 12), anchor: str = 'nw', width: int | None = None,
|
|
justify: str | None = None) -> int:
|
|
"""Create a text item on the app's main canvas and track it.
|
|
|
|
Returns the created canvas item id.
|
|
"""
|
|
if getattr(app, 'bg_canvas', None) is None:
|
|
return -1
|
|
kwargs = dict(anchor=anchor, text=text, fill=fill, font=font)
|
|
if width is not None:
|
|
kwargs['width'] = width
|
|
if justify is not None:
|
|
kwargs['justify'] = justify
|
|
item = app.bg_canvas.create_text(x, y, **kwargs)
|
|
app._canvas_items.append(item)
|
|
return item
|
|
|
|
|
|
def canvas_oval(app, x1: int, y1: int, x2: int, y2: int, *, fill: str | None = None,
|
|
outline: str | None = None, width: int = 1) -> int:
|
|
"""Create an oval on the app's main canvas and track it."""
|
|
if getattr(app, 'bg_canvas', None) is None:
|
|
return -1
|
|
item = app.bg_canvas.create_oval(x1, y1, x2, y2, fill=fill or '', outline=outline or '', width=width)
|
|
app._canvas_items.append(item)
|
|
return item
|
|
|
|
|
|
def canvas_rectangle(app, x1: int, y1: int, x2: int, y2: int, *, outline: str = '#6e6e73',
|
|
width: int = 1, fill: str | None = None) -> int:
|
|
if getattr(app, 'bg_canvas', None) is None:
|
|
return -1
|
|
item = app.bg_canvas.create_rectangle(x1, y1, x2, y2, outline=outline, width=width, fill=fill or '')
|
|
app._canvas_items.append(item)
|
|
return item
|
|
|
|
|
|
def canvas_line(app, x1: int, y1: int, x2: int, y2: int, *, fill: str = '#1d1d1f', width: int = 2) -> int:
|
|
if getattr(app, 'bg_canvas', None) is None:
|
|
return -1
|
|
item = app.bg_canvas.create_line(x1, y1, x2, y2, fill=fill, width=width)
|
|
app._canvas_items.append(item)
|
|
return item
|
|
|
|
|
|
def render_link(app, x: int, y: int, text: str, *, color: str = '#0a84ff', font: tuple = ('Helvetica', 12, 'underline')) -> int:
|
|
"""Render a link-styled text on canvas and return its item id."""
|
|
return canvas_text(app, x, y, text, fill=color, font=font)
|
|
|
|
|
|
def canvas_image(app, x: int, y: int, image, *, anchor: str = 'center') -> int:
|
|
"""Create an image on the app's main canvas. Not tracked in _canvas_items by default."""
|
|
if getattr(app, 'bg_canvas', None) is None:
|
|
return -1
|
|
try:
|
|
item = app.bg_canvas.create_image(x, y, anchor=anchor, image=image)
|
|
return item
|
|
except Exception:
|
|
return -1
|
|
|
|
|
|
# Explicit-canvas helpers for non-background canvases
|
|
def canvas_delete(app, item_id: int):
|
|
try:
|
|
app.bg_canvas.delete(item_id)
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def canvas_clear_all(app):
|
|
try:
|
|
app.bg_canvas.delete('all')
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def canvas_coords(app, item_id: int, *coords):
|
|
try:
|
|
app.bg_canvas.coords(item_id, *coords)
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def canvas_clear(cnv: tk.Canvas):
|
|
try:
|
|
cnv.delete('all')
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def canvas_text_on(cnv: tk.Canvas, x: int, y: int, text: str, *, fill: str = '#1d1d1f',
|
|
font: tuple = ('Helvetica', 12), anchor: str = 'center') -> int:
|
|
return cnv.create_text(x, y, text=text, fill=fill, font=font, anchor=anchor)
|
|
|
|
|
|
def canvas_oval_on(cnv: tk.Canvas, x1: int, y1: int, x2: int, y2: int, *, fill: str | None = None,
|
|
outline: str | None = None, width: int = 1) -> int:
|
|
return cnv.create_oval(x1, y1, x2, y2, fill=fill or '', outline=outline or '', width=width)
|
|
|
|
|
|
def canvas_line_on(cnv: tk.Canvas, x1: int, y1: int, x2: int, y2: int, *, fill: str = 'white', width: int = 2) -> int:
|
|
return cnv.create_line(x1, y1, x2, y2, fill=fill, width=width)
|
|
|
|
|
|
def create_nav_footer(parent, buttons: list[tuple[int, str]], commands: dict[int, callable] | None = None,
|
|
style_name: str = 'Nav.TButton') -> dict[int, ttk.Button]:
|
|
"""Create a right-aligned navigation footer with uniform button styling.
|
|
|
|
Parameters:
|
|
- parent: the container (typically the root installer container)
|
|
- buttons: list of (button_id, title) in the order to display
|
|
- commands: optional mapping of button_id -> callback function
|
|
- style_name: ttk style to apply to all buttons
|
|
|
|
Returns: {button_id: ttk.Button}
|
|
|
|
Examples:
|
|
create_nav_footer(footer, [(1, 'Next')])
|
|
create_nav_footer(footer, [(1, 'Prev'), (2, 'Next')])
|
|
create_nav_footer(footer, [(1, 'Exit')])
|
|
"""
|
|
# Ensure a consistent style for navigation buttons
|
|
try:
|
|
style = ttk.Style()
|
|
# Keep padding modest to allow Aqua to size buttons naturally.
|
|
# Avoid forcing a font so the native Aqua metrics are used.
|
|
style.configure(style_name, padding=(10, 6))
|
|
except Exception:
|
|
pass
|
|
|
|
footer = ttk.Frame(parent)
|
|
footer.pack(fill='x', side='bottom')
|
|
# Let the footer naturally size to its contents to avoid clipping text on macOS
|
|
|
|
# Flexible spacer to push buttons to the right
|
|
spacer = ttk.Frame(footer)
|
|
spacer.pack(side='left', expand=True, fill='x')
|
|
|
|
cmds = commands or {}
|
|
btn_map: dict[int, ttk.Button] = {}
|
|
for btn_id, title in buttons:
|
|
cmd = cmds.get(btn_id)
|
|
b = ttk.Button(footer, text=title, style=style_name, command=cmd)
|
|
# Right-aligned order (pack to the right in the declared order)
|
|
# Small extra right padding on the last (right-most) button
|
|
pad = (0, 20) if title.lower() in ('finish', 'exit', 'done') else (0, 8)
|
|
# Use modest vertical padding; let Aqua compute proper height.
|
|
b.pack(side='right', padx=pad, pady=10)
|
|
btn_map[btn_id] = b
|
|
|
|
# Return both the frame and button map if needed later by callers
|
|
btn_map['_footer'] = footer # type: ignore[index]
|
|
return btn_map
|