Files
kiln/src/render.rs
T
randrews 12ebe77932 Simplify: extract paint_glyph, drop unused param and redundant comments
- Add paint_glyph(painter, rect, glyph) to render.rs; draw_glyph now
  delegates to it, eliminating the duplicated rect_filled+text pattern
  that appeared in four places across editor.rs and glyph_picker.rs
- Remove unused _board param from show_editor_panel and its call site
- Drop redundant section-header comments in glyph_picker.rs (the
  ui.label() calls immediately below already name each section)
- Remove extracted fg/bg/cur_ch locals in the char grid loop now that
  paint_glyph accepts a Glyph directly
- Fix CLAUDE.md update phase description (stale from pre-refactor)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 22:01:21 -05:00

127 lines
6.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Layout constants — which value controls which part of the window:
//
// One board cell:
//
// ◄── CELL_W (14 px) ──►
// ╔════════════════════╗ ▲
// ║ @ ║ │ CELL_H (20 px)
// ╚════════════════════╝ ▼
//
// Full window (Edit mode shown; side panel absent in Play mode):
//
// ◄────────────────── DEFAULT_WINDOW_W (840 px) ───────────────────►
// ┌────────────────────────────────────────────────────────────────┐ ▲
// │ File │ Play Edit │ │ MENU_H (24 px)
// ├────────────────────────────────────────────────┬───────────────┤ ▼
// │ PANEL_MARGIN (8 px) on all sides │ PALETTE_ │ ▲
// │ ┌──────────────────────────────────────────┐ │ PANEL_W │ │
// │ │ │ │ (200 px) │ │ DEFAULT_
// │ │ board cells (CELL_W × CELL_H each) │ │ Edit mode │ │ WINDOW_H
// │ │ │ │ only │ │ (524 px)
// │ └──────────────────────────────────────────┘ │ │ │
// │ │ │ ▼
// └────────────────────────────────────────────────┴───────────────┘
//
// MIN_WINDOW_W and MIN_WINDOW_H are derived: 10 cells wide / 5 cells tall plus margins.
use eframe::egui;
use egui::{Align2, FontId, Pos2, Rect, vec2};
use crate::game::{Board, Glyph, Player};
/// Width of one board cell in pixels.
pub(crate) const CELL_W: f32 = 14.0;
/// Height of one board cell in pixels.
pub(crate) const CELL_H: f32 = 20.0;
/// Approximate height of the egui menu bar in pixels, used for window sizing.
pub(crate) const MENU_H: f32 = 24.0;
/// Default inner margin that egui's CentralPanel adds on all sides, in pixels.
pub(crate) const PANEL_MARGIN: f32 = 8.0;
/// Default width of the editor side panel in pixels. The panel is resizable.
pub(crate) const PALETTE_PANEL_W: f32 = 200.0;
/// Default window width in pixels. Independent of map size.
pub(crate) const DEFAULT_WINDOW_W: f32 = 840.0;
/// Default window height in pixels. Independent of map size.
pub(crate) const DEFAULT_WINDOW_H: f32 = 524.0;
/// Minimum window width: enough room for 10 cells plus margins.
pub(crate) const MIN_WINDOW_W: f32 = CELL_W * 10.0 + 2.0 * PANEL_MARGIN;
/// Minimum window height: enough room for 5 cells plus menu and margins.
pub(crate) const MIN_WINDOW_H: f32 = CELL_H * 5.0 + MENU_H + 2.0 * PANEL_MARGIN;
/// Paints a glyph into an already-allocated `rect`.
///
/// Unlike [`draw_glyph`], this does not compute the rect from grid coordinates —
/// the caller owns the rect (typically from [`egui::Ui::allocate_exact_size`])
/// and handles interaction. Use this when you need the click [`egui::Response`].
pub(crate) fn paint_glyph(painter: &egui::Painter, rect: Rect, glyph: &Glyph) {
painter.rect_filled(rect, 0.0, glyph.bg);
painter.text(rect.center(), Align2::CENTER_CENTER, glyph.ch, FontId::monospace(CELL_H), glyph.fg);
}
/// Draws a single cell at grid position `(x, y)` using the given glyph.
///
/// `origin` is the pixel position of cell `(0, 0)` — the top-left corner of
/// the board within the panel. Cell positions are computed from `origin` using
/// [`CELL_W`] and [`CELL_H`].
pub(crate) fn draw_glyph(painter: &egui::Painter, origin: Pos2, x: usize, y: usize, glyph: &Glyph) {
let tl = origin + vec2(x as f32 * CELL_W, y as f32 * CELL_H);
let rect = Rect::from_min_size(tl, vec2(CELL_W, CELL_H));
paint_glyph(painter, rect, glyph);
}
/// Draws all board cells then the player overlay at `origin`.
pub(crate) fn draw_board(painter: &egui::Painter, origin: Pos2, board: &Board) {
for y in 0..board.height {
for x in 0..board.width {
let (glyph, _) = board.get(x, y);
draw_glyph(painter, origin, x, y, glyph);
}
}
// The player is not a board cell; it is rendered on top as an overlay.
// This separate draw will be removed once the player becomes a scripted object.
let player_glyph = Glyph::player();
draw_glyph(painter, origin, board.player.x as usize, board.player.y as usize, &player_glyph);
}
/// Converts a pixel position to cell coordinates relative to `origin`.
///
/// Uses floor so positions above or left of `origin` produce negative values —
/// callers must guard `>= 0` before treating the result as a valid cell index.
pub(crate) fn pos_to_cell(origin: Pos2, pos: Pos2) -> (i32, i32) {
let rel = pos - origin;
((rel.x / CELL_W).floor() as i32, (rel.y / CELL_H).floor() as i32)
}
/// Computes the pixel position of board cell (0, 0) within `available`.
///
/// If the board fits along an axis, it is centered. If it overflows, the
/// viewport is centered on the player and clamped so no empty space appears
/// at the board edges.
pub(crate) fn board_origin(available: Rect, board_w: usize, board_h: usize, player: Player) -> Pos2 {
let board_px_w = board_w as f32 * CELL_W;
let board_px_h = board_h as f32 * CELL_H;
let origin_x = if board_px_w <= available.width() {
// Board fits: center it horizontally.
available.min.x + (available.width() - board_px_w) / 2.0
} else {
// Board overflows: keep player at viewport center, clamp to board edges.
let player_px_x = player.x as f32 * CELL_W + CELL_W / 2.0;
(available.center().x - player_px_x)
.max(available.max.x - board_px_w) // right edge must reach viewport right
.min(available.min.x) // left edge must reach viewport left
};
let origin_y = if board_px_h <= available.height() {
available.min.y + (available.height() - board_px_h) / 2.0
} else {
let player_px_y = player.y as f32 * CELL_H + CELL_H / 2.0;
(available.center().y - player_px_y)
.max(available.max.y - board_px_h)
.min(available.min.y)
};
Pos2::new(origin_x, origin_y)
}