"""Tk renderer for the status-bar layout playground.""" from __future__ import annotations import tkinter as tk from tkinter import font as tkfont from typing import Iterable from statusbar_lab import CameraCutout, Container, IconContainer, StatusBarLab, rectangles_overlap TYPE_COLOURS = { "clock": "#9fd7ff", "notification": "#a7efaa", "status": "#ffd596", "chip": "#e2b0ff", "carrier": "#ffb4c4", } def render( lab: StatusBarLab, statusbar_width: float, statusbar_height: float, render_scale: float = 1.0, vertical_scale: float = 2.0, margin: int = 40, ) -> None: root = tk.Tk() root.title("StatusBarTweak layout playground") width = int(statusbar_width * render_scale + margin * 2) height = int(statusbar_height * vertical_scale + margin * 2) canvas = tk.Canvas(root, width=width, height=height, bg="#242424", highlightthickness=0) canvas.pack() def point(x: float, y: float) -> tuple[float, float]: return margin + x * render_scale, margin + (statusbar_height - y) * vertical_scale left, bottom = point(0, 0) right, top = point(statusbar_width, statusbar_height) canvas.create_rectangle(left, top, right, bottom, fill="white", outline="#4a4a4a", width=5) containers = lab.render_containers() for container in containers: _draw_container(canvas, point, container, render_scale, vertical_scale) _draw_overlaps(canvas, point, containers) for camera in lab.cameras: _draw_camera(canvas, point, camera) root.mainloop() def _draw_container(canvas: tk.Canvas, point, container: Container, render_scale: float, vertical_scale: float) -> None: if isinstance(container, IconContainer): _draw_icons(canvas, point, container) return x1, y1 = point(container.left, container.bottom) x2, y2 = point(container.right, container.top) canvas.create_rectangle(x1, y2, x2, y1, fill=TYPE_COLOURS[container.kind], outline="black", width=1) _draw_clipped_text(canvas, container.text, x1 + 3, (y1 + y2) / 2, max(0, x2 - x1 - 6), container.height * vertical_scale) def _draw_icons(canvas: tk.Canvas, point, container: IconContainer) -> None: colour = TYPE_COLOURS[container.kind] if not container.segments(): x, y1 = point(container.left, container.bottom) _, y2 = point(container.left, container.top) canvas.create_line(x, y1, x, y2, fill="black", width=1) return cursor = container.left for segment_kind, icon_id, segment_width in container.segments(): x1, y1 = point(cursor, container.bottom) x2, y2 = point(cursor + segment_width, container.top) if segment_kind == "gap": cursor += segment_width continue if segment_kind == "dot": canvas.create_rectangle(x1, y2, x2, y1, fill=colour, outline="black", width=1) radius = min(abs(x2 - x1), abs(y1 - y2)) * 0.24 cx, cy = (x1 + x2) / 2, (y1 + y2) / 2 canvas.create_oval(cx - radius, cy - radius, cx + radius, cy + radius, fill="black", outline="black") else: canvas.create_rectangle(x1, y2, x2, y1, fill=colour, outline="black", width=1) _draw_clipped_text(canvas, str(icon_id), (x1 + x2) / 2, (y1 + y2) / 2, abs(x2 - x1) - 2, abs(y1 - y2), anchor="center") cursor += segment_width def _draw_camera(canvas: tk.Canvas, point, camera: CameraCutout) -> None: x1, y1 = point(camera.left, camera.bottom) x2, y2 = point(camera.right, camera.top) canvas.create_rectangle(x1, y2, x2, y1, fill="black", outline="black") def _draw_overlaps(canvas: tk.Canvas, point, containers: Iterable[Container]) -> None: items = list(containers) for index, first in enumerate(items): for second in items[index + 1:]: if first.kind == "clock" and second.kind == "clock": continue if not rectangles_overlap(first.bounds, second.bounds): continue left = max(first.left, second.left) right = min(first.right, second.right) bottom = max(first.bottom, second.bottom) top = min(first.top, second.top) x1, y1 = point(left, bottom) x2, y2 = point(right, top) canvas.create_rectangle(x1, y2, x2, y1, fill="red", outline="red") radius = max(abs(x2 - x1), abs(y1 - y2)) / 2 + 12 cx, cy = (x1 + x2) / 2, (y1 + y2) / 2 canvas.create_oval(cx - radius, cy - radius, cx + radius, cy + radius, outline="red", width=3) def _draw_clipped_text( canvas: tk.Canvas, text: str, x: float, y: float, width: float, height: float, anchor: str = "w", ) -> None: if width <= 0: return size = max(7, int(height * 0.42)) text_font = tkfont.Font(family="TkDefaultFont", size=size) clipped = _clip_text(text_font, text, width) canvas.create_text(x, y, text=clipped, font=text_font, fill="black", anchor=anchor) def _clip_text(text_font: tkfont.Font, text: str, width: float) -> str: if text_font.measure(text) <= width: return text ellipsis = "..." while text and text_font.measure(text + ellipsis) > width: text = text[:-1] return text + ellipsis if text else ""