v0.1.3 — Centered Teletext frame, added full footer with color band, and finalized header alignment.

This commit is contained in:
Stu Leak 2025-11-03 11:17:44 -05:00
parent 64f0e74105
commit 211ebfcd01
9 changed files with 67 additions and 63 deletions

22
main.py
View File

@ -1,13 +1,12 @@
# ============================================================ # ============================================================
# File: /home/stu/Projects/Local REPO/telefact/main.py # File: /telefact/main.py
# Description: # Description:
# Entry point for the Telefact Broadcaster mode. # Entry point for the Telefact Broadcaster mode.
# Initializes the Tkinter window, loads configuration, # Initializes the Tkinter window, loads configuration,
# and renders the Teletext grid with dynamic header. # and renders the Teletext grid with dynamic header/footer.
# ============================================================ # ============================================================
import tkinter as tk import tkinter as tk
from src.config_manager import ConfigManager from src.config_manager import ConfigManager
from src.telefact_renderer import TelefactRenderer from src.telefact_renderer import TelefactRenderer
from src.core.telefact_frame import TelefactFrame from src.core.telefact_frame import TelefactFrame
@ -31,34 +30,33 @@ def main():
height=config["ScreenHeight"], height=config["ScreenHeight"],
show_grid=config.get("ShowGrid", False), show_grid=config.get("ShowGrid", False),
font_path=config["Font"]["Path"], font_path=config["Font"]["Path"],
font_size=config["Font"]["Size"],
) )
# --- Prepare frame --- # --- Prepare frame ---
frame = TelefactFrame() frame = TelefactFrame()
# --- Initialize header --- # --- Header ---
header = TelefactHeader(frame, config) header = TelefactHeader(frame, config)
header.render() header.render()
header.update_time(root, renderer) header.update_time(root, renderer)
# --- Initialize footer --- # --- Body content ---
footer = TelefactFooter(frame, config)
footer.render_test_footer()
# --- Body & footer test content ---
formatter = TelefactFormatter(frame) formatter = TelefactFormatter(frame)
formatter.add_body_line(1, "Welcome to the Telefact Broadcaster base.", align="center", color="white") formatter.add_body_line(1, "Welcome to the Telefact Broadcaster base.", align="center", color="white")
formatter.add_body_line(3, "Press Q or ESC to exit.", align="center", color="cyan") formatter.add_body_line(3, "Press Q or ESC to exit.", align="center", color="cyan")
formatter.set_footer("PAGE 100 TELEFACT", align="center", color="green")
# --- Render frame --- # --- Footer (replaces old PAGE 100 TELEFACT) ---
footer = TelefactFooter(frame, config)
footer.set_footer("Telefact: The world at your fingertips", align="left", fg="yellow", bg="blue")
# --- Render everything ---
renderer.render(frame) renderer.render(frame)
# --- Bind exit keys --- # --- Bind exit keys ---
root.bind("<Escape>", lambda e: root.quit()) root.bind("<Escape>", lambda e: root.quit())
root.bind("q", lambda e: root.quit()) root.bind("q", lambda e: root.quit())
# --- Start loop ---
root.mainloop() root.mainloop()

Binary file not shown.

View File

@ -2,8 +2,7 @@
# File: /home/stu/Projects/Local REPO/telefact/src/core/telefact_footer.py # File: /home/stu/Projects/Local REPO/telefact/src/core/telefact_footer.py
# Description: # Description:
# Dynamic Telefact footer renderer for broadcaster mode. # Dynamic Telefact footer renderer for broadcaster mode.
# Draws a single bottom-row text band with background color # Draws a single bottom-row text band with full background fill.
# and per-page contextual support.
# ============================================================ # ============================================================
from src.core.telefact_frame import TelefactFrame from src.core.telefact_frame import TelefactFrame
@ -22,24 +21,24 @@ class TelefactFooter:
self.frame = frame self.frame = frame
self.config = config self.config = config
# default colors from global config # Default colors
colours = config.get("Colours", {}) colours = config.get("Colours", {})
self.default_bg = colours.get("Footer", "blue") self.default_bg = colours.get("Footer", "blue")
self.default_fg = colours.get("TextPrimary", "white") self.default_fg = colours.get("TextPrimary", "white")
# reserved footer row (last visible line) # Reserved row (bottom row = line 24)
self.row = frame.rows - 1 self.row = frame.rows - 1
# -------------------------------------------------------- # --------------------------------------------------------
def clear_footer(self, bg: str | None = None): def clear_footer(self, bg: str | None = None):
"""Clears the footer line with background fill.""" """Clears the footer row with solid background color."""
bg_color = bg or self.default_bg bg_color = bg or self.default_bg
for c in range(self.frame.cols): for c in range(self.frame.cols):
self.frame.set_cell(c, self.row, " ", self.default_fg, bg_color) self.frame.set_cell(c, self.row, " ", self.default_fg, bg_color)
# -------------------------------------------------------- # --------------------------------------------------------
def set_footer(self, text: str, align: str = "left", fg: str | None = None, bg: str | None = None): def set_footer(self, text: str, align: str = "left", fg: str | None = None, bg: str | None = None):
"""Draw a footer message with color and alignment control.""" """Draw footer message with proper background fill."""
fg_color = fg or self.default_fg fg_color = fg or self.default_fg
bg_color = bg or self.default_bg bg_color = bg or self.default_bg
self.clear_footer(bg_color) self.clear_footer(bg_color)
@ -47,25 +46,26 @@ class TelefactFooter:
text = text.strip() text = text.strip()
text_len = len(text) text_len = len(text)
# --- Alignment handling --- # Alignment
if align == "center": if align == "center":
start_col = (self.frame.cols - text_len) // 2 start_col = (self.frame.cols - text_len) // 2
elif align == "right": elif align == "right":
start_col = max(self.frame.cols - text_len - 1, 0) start_col = max(self.frame.cols - text_len - 1, 0)
else: else:
start_col = 1 start_col = 1 # left padding
# --- Background fill --- # Draw each character with full background
for c in range(self.frame.cols):
self.frame.set_cell(c, self.row, " ", fg_color, bg_color)
# --- Text overlay ---
for i, ch in enumerate(text): for i, ch in enumerate(text):
pos = start_col + i pos = start_col + i
if 0 <= pos < self.frame.cols: if 0 <= pos < self.frame.cols:
self.frame.set_cell(pos, self.row, ch, fg_color) self.frame.set_cell(pos, self.row, ch, fg_color, bg_color)
# -------------------------------------------------------- # --------------------------------------------------------
def render_test_footer(self): def render_test_footer(self):
"""Temporary demo for testing footer display.""" """Temporary demo for testing footer display."""
self.set_footer("Telefact: The world at your fingertips", align="left", fg="yellow", bg="blue") self.set_footer(
"Telefact: The world at your fingertips",
align="left",
fg="yellow",
bg="blue",
)

View File

@ -1,5 +1,5 @@
# ============================================================ # ============================================================
# File: /home/stu/Projects/Local REPO/telefact/src/core/telefact_frame.py # File: /telefact/src/core/telefact_frame.py
# Description: # Description:
# Telefact Frame (model) # Telefact Frame (model)
# Defines a 40×24 grid and logical regions for Teletext layout. # Defines a 40×24 grid and logical regions for Teletext layout.

View File

@ -1,5 +1,5 @@
# ============================================================ # ============================================================
# File: /home/stu/Projects/Local REPO/telefact/src/core/telefact_header.py # File: /telefact/src/core/telefact_header.py
# Description: # Description:
# Dynamic Telefact header renderer for broadcaster mode. # Dynamic Telefact header renderer for broadcaster mode.
# Displays page numbers, centered service name block with # Displays page numbers, centered service name block with

View File

@ -1,10 +1,9 @@
# ============================================================ # ============================================================
# File: /home/stu/Projects/Local REPO/telefact/src/telefact_renderer.py # File: /telefact/src/telefact_renderer.py
# Description: # Description:
# Telefact Renderer (Tkinter) # Telefact Renderer (Tkinter)
# Renders a 40×24 Telefact grid with full foreground/background # Renders a 40×24 Telefact grid with full foreground/background
# colour support, aligned inside a CRT-style safe area margin. # colour support, perfectly centered inside the window.
# No header/footer bars — layout calibration only.
# ============================================================ # ============================================================
import os import os
@ -44,20 +43,33 @@ class TelefactRenderer:
self.height = height self.height = height
self.show_grid = show_grid self.show_grid = show_grid
# Teletext dimensions # Teletext logical dimensions
self.cols, self.rows = 40, 24 self.cols, self.rows = 40, 24
# Safe area margins (simulate CRT overscan) # --- Dynamic centered layout ---
self.margin_x = 24 # Define safe area target margins
self.margin_y = 24 safe_margin_x = 24
self.inner_w = width - self.margin_x * 2 safe_margin_y = 24
self.inner_h = height - self.margin_y * 2
# Per-cell dimensions # Compute total drawable region
self.cell_w = self.inner_w // self.cols self.inner_w = width - safe_margin_x * 2
self.cell_h = self.inner_h // self.rows self.inner_h = height - safe_margin_y * 2
self.w = self.inner_w
self.h = self.inner_h # Compute exact per-cell dimensions (float precision)
self.cell_w = self.inner_w / self.cols
self.cell_h = self.inner_h / self.rows
# Compute corrected total grid size (may differ slightly due to float math)
total_grid_w = self.cell_w * self.cols
total_grid_h = self.cell_h * self.rows
# --- Center grid on screen ---
self.margin_x = (width - total_grid_w) / 2
self.margin_y = (height - total_grid_h) / 2
# Cache total grid extents
self.w = total_grid_w
self.h = total_grid_h
# Base colours # Base colours
self.colors = { self.colors = {
@ -101,12 +113,12 @@ class TelefactRenderer:
return ("Courier New", font_size, "bold") return ("Courier New", font_size, "bold")
# ------------------------------------------------------------------ # ------------------------------------------------------------------
def _gx(self, col: int) -> int: def _gx(self, col: int) -> float:
"""Grid X coordinate.""" """Grid X coordinate (precise, centered)."""
return self.margin_x + col * self.cell_w return self.margin_x + col * self.cell_w
def _gy(self, row: int) -> int: def _gy(self, row: int) -> float:
"""Grid Y coordinate.""" """Grid Y coordinate (precise, centered)."""
return self.margin_y + row * self.cell_h return self.margin_y + row * self.cell_h
# ------------------------------------------------------------------ # ------------------------------------------------------------------
@ -115,21 +127,18 @@ class TelefactRenderer:
x = self._gx(col) x = self._gx(col)
y = self._gy(row) y = self._gy(row)
# Draw background block # Background rectangle
self.canvas.create_rectangle( self.canvas.create_rectangle(
x, x, y, x + self.cell_w, y + self.cell_h,
y,
x + self.cell_w,
y + self.cell_h,
fill=PALETTE.get(bg, bg), fill=PALETTE.get(bg, bg),
outline=PALETTE.get(bg, bg), outline=PALETTE.get(bg, bg)
) )
# Draw text glyph # Foreground glyph
if char.strip(): if char.strip():
self.canvas.create_text( self.canvas.create_text(
x + 2, x + 2,
y + self.cell_h // 2, y + self.cell_h / 2,
anchor="w", anchor="w",
text=char, text=char,
font=self.font, font=self.font,
@ -138,7 +147,7 @@ class TelefactRenderer:
# ------------------------------------------------------------------ # ------------------------------------------------------------------
def _draw_grid(self) -> None: def _draw_grid(self) -> None:
"""Optional debug overlay for cell alignment.""" """Optional grid overlay for visual debugging."""
for c in range(self.cols + 1): for c in range(self.cols + 1):
x = self._gx(c) x = self._gx(c)
self.canvas.create_line( self.canvas.create_line(
@ -152,20 +161,17 @@ class TelefactRenderer:
# ------------------------------------------------------------------ # ------------------------------------------------------------------
def render(self, frame: TelefactFrame) -> None: def render(self, frame: TelefactFrame) -> None:
"""Renders the full Telefact grid (foreground + background).""" """Renders the entire 40×24 Telefact grid."""
self.canvas.delete("all") self.canvas.delete("all")
# Fill background # Fill full canvas background
self.canvas.create_rectangle( self.canvas.create_rectangle(
0, 0, 0, self.width, self.height,
0,
self.width,
self.height,
fill=PALETTE.get(self.colors["Background"], self.colors["Background"]), fill=PALETTE.get(self.colors["Background"], self.colors["Background"]),
width=0, width=0,
) )
# Draw all cells (bg first, then text) # Draw text grid (each cell: bg + fg)
for row in range(frame.rows): for row in range(frame.rows):
for col in range(frame.cols): for col in range(frame.cols):
ch, fg, bg = frame.get_cell(col, row) ch, fg, bg = frame.get_cell(col, row)