1use alloc::rc::Rc;
7
8use super::camera::SceneCameraView;
9use super::gizmo::{Gizmo, Gizmos};
10use super::view::DebugViewView;
11use super::{DebugMenuView, SamplePanels};
12use crate::ctx::{Ctx, GameData};
13use crate::frame::color::{EngineColor, Palette};
14use crate::frame::console::ConsoleSpec;
15use crate::frame::gizmo::{GizmoItem, GizmoShape, LabelText};
16use crate::frame::glyph_buffer::{BoxStyle, Cell, GlyphBuffer, Rect};
17use crate::prelude::*;
18use crate::screens::{centre_x, print_centred};
19use crate::widgets::menu;
20
21const MENU_TITLE_ROW: i32 = 9;
23
24pub fn paint<G: GameData>(ctx: &Ctx<G>, view: &DebugMenuView, buf: &mut GlyphBuffer) {
27 let c = |e| ctx.palette.engine(e);
28 let black = c(EngineColor::Background);
29 buf.fill_rect(buf.bounds(), Cell::new(' ', c(EngineColor::Text), black));
30 let hi = c(EngineColor::Highlight);
31 print_centred(buf, MENU_TITLE_ROW, &view.title, hi, black);
32 let (w, _) = menu::glyph::size(ctx, &view.menu);
33 let x = centre_x(buf, usize::try_from(w).unwrap_or(0));
34 menu::glyph::paint(ctx, &view.menu, buf, x, MENU_TITLE_ROW + 2);
35 let bottom = i32::from(buf.height()) - 1;
36 print_centred(buf, bottom, &view.help, c(EngineColor::TextDim), black);
37}
38
39pub fn paint_view<G: GameData>(ctx: &Ctx<G>, view: &DebugViewView, buf: &mut GlyphBuffer) {
43 let c = |e| ctx.palette.engine(e);
44 let black = c(EngineColor::Background);
45 buf.fill_rect(buf.bounds(), Cell::new(' ', c(EngineColor::Text), black));
46 let hi = c(EngineColor::Highlight);
47 print_centred(buf, MENU_TITLE_ROW, &view.title, hi, black);
48 if let Some(empty) = &view.empty {
49 print_centred(
50 buf,
51 MENU_TITLE_ROW + 2,
52 empty,
53 c(EngineColor::TextDim),
54 black,
55 );
56 } else {
57 let (w, _) = menu::glyph::size(ctx, &view.menu);
58 let x = centre_x(buf, usize::try_from(w).unwrap_or(0));
59 menu::glyph::paint(ctx, &view.menu, buf, x, MENU_TITLE_ROW + 2);
60 }
61 let bottom = i32::from(buf.height()) - 1;
62 print_centred(buf, bottom, &view.help, c(EngineColor::TextDim), black);
63}
64
65pub fn paint_gizmos(palette: &Palette, gizmos: &Gizmos, buf: &mut GlyphBuffer) {
73 let (cell_w, cell_h) = (
74 i32::from(buf.spec().cell_w).max(1),
75 i32::from(buf.spec().cell_h).max(1),
76 );
77 for drawn in gizmos.drawn() {
78 let color = palette.engine(drawn.role.color());
79 let shape = match &drawn.gizmo {
80 Gizmo::Rect { rect } => GizmoShape::Rect { rect: *rect },
81 Gizmo::Line { from, to } => GizmoShape::Line {
82 from: *from,
83 to: *to,
84 },
85 Gizmo::Cross { at, size } => GizmoShape::Cross {
86 at: *at,
87 size: *size,
88 },
89 Gizmo::Circle { center, radius } => GizmoShape::Circle {
90 center: *center,
91 radius: *radius,
92 },
93 Gizmo::Label { at, text } => GizmoShape::Label {
94 at: *at,
95 text: LabelText::new(text),
96 },
97 };
98 let mut item = GizmoItem::new(drawn.channel.id(), drawn.role, shape, color);
99 if let GizmoShape::Label { at, text } = shape {
100 let (cx, cy) = (at.0.div_euclid(cell_w), at.1.div_euclid(cell_h));
101 let len = buf.text_cells(text.as_str());
102 buf.print_fg(cx, cy, text.as_str(), color);
103 item.clip = Rect::new(
104 cx.saturating_mul(cell_w),
105 cy.saturating_mul(cell_h),
106 len.saturating_mul(cell_w),
107 cell_h,
108 );
109 }
110 buf.add_gizmo(item);
111 }
112}
113
114const CAMERA_LEFT: i32 = 2;
116
117pub fn paint_camera(palette: &Palette, view: &SceneCameraView, buf: &mut GlyphBuffer) {
121 let c = |e| palette.engine(e);
122 let (black, text, hi) = (
123 c(EngineColor::Background),
124 c(EngineColor::Text),
125 c(EngineColor::Highlight),
126 );
127 buf.fill_rect(buf.bounds(), Cell::new(' ', text, black));
128 buf.print(CAMERA_LEFT, 0, &view.title, hi, black);
129 let bottom = i32::from(buf.height()) - 1;
130 let dim = c(EngineColor::TextDim);
131 buf.print(CAMERA_LEFT, bottom, &view.help, dim, black);
132 buf.print(CAMERA_LEFT, 2, &view.info, text, black);
133 let Some((picture, frame)) = &view.shown else {
134 return;
135 };
136
137 let window = view.layout.window;
138 let border = Rect::new(window.x - 1, window.y - 1, window.w + 2, window.h + 2);
139 buf.draw_box(border, BoxStyle::Single, c(EngineColor::Border), black);
140 buf.fill_rect(window, Cell::see_through(' ', text));
141 buf.set_backdrop(Rc::clone(picture), window, frame.origin_px, frame.zoom);
142 buf.print_fg(window.x + 1, window.y, &view.caption, hi);
144
145 let text_box = view.layout.text_box;
146 let panel = c(EngineColor::Panel);
147 buf.fill_rect(text_box, Cell::new(' ', text, panel));
148 buf.draw_box(text_box, BoxStyle::Double, c(EngineColor::Focus), panel);
149 for (row, line) in (text_box.y + 1..).zip(&view.box_text) {
150 buf.print(text_box.x + 2, row, line, text, panel);
151 }
152}
153
154pub fn paint_hint(palette: &Palette, hint: &str, buf: &mut GlyphBuffer, y: i32) {
158 let len = buf.text_cells(hint);
159 let x = i32::from(buf.width()) - 1 - len;
160 let free = (x - 1..x + len).all(|cx| buf.get(cx, y).is_some_and(|c| c.glyph == ' '));
161 if free {
162 buf.print(
163 x,
164 y,
165 hint,
166 palette.engine(EngineColor::TextDim),
167 palette.engine(EngineColor::Background),
168 );
169 }
170}
171
172pub const GLYPHS_PER_ROW: usize = 48;
174pub const SWATCH_W: i32 = 19;
176const SWATCH_NAME_W: usize = SWATCH_W as usize - 5;
178pub const SWATCH_COLUMNS: i32 = 5;
180
181pub fn palette_top(glyphs: &[char]) -> i32 {
186 let glyph_rows: i32 = glyphs
187 .chunks(GLYPHS_PER_ROW)
188 .count()
189 .try_into()
190 .unwrap_or(i32::MAX);
191 1 + glyph_rows
192}
193
194pub fn panels_top(palette: &Palette, glyphs: &[char]) -> i32 {
199 panels_top_for(palette.iter().count(), glyphs)
200}
201
202pub fn panels_top_for(colours: usize, glyphs: &[char]) -> i32 {
204 let swatch_rows: i32 = colours
205 .div_ceil(SWATCH_COLUMNS as usize)
206 .try_into()
207 .unwrap_or(i32::MAX);
208 palette_top(glyphs) + 2 + swatch_rows
209}
210
211pub fn sampler(
215 spec: ConsoleSpec,
216 palette: &Palette,
217 glyphs: &[char],
218 panels: SamplePanels,
219) -> GlyphBuffer {
220 let c = |e| palette.engine(e);
221 let (text, dim, black) = (
222 c(EngineColor::Text),
223 c(EngineColor::TextDim),
224 c(EngineColor::Background),
225 );
226 let mut b = GlyphBuffer::with_spec(spec, Cell::new(' ', text, black));
227
228 let title = format!("Glyphs ({})", glyphs.len());
230 b.print(1, 0, &title, c(EngineColor::Highlight), black);
231 for (row, chunk) in (1..).zip(glyphs.chunks(GLYPHS_PER_ROW)) {
232 for (col, &g) in (0..).zip(chunk) {
233 b.print(2 + col * 2, row, &g.to_string(), text, black);
234 }
235 }
236
237 let bottom = palette_top(glyphs);
238 let colors: Vec<_> = palette.iter().collect();
239 let title = format!("Palette ({})", colors.len());
241 b.print(1, bottom, &title, c(EngineColor::Highlight), black);
242 for (i, &(name, rgb)) in (0..).zip(&colors) {
243 let (x, y) = (
244 1 + i % SWATCH_COLUMNS * SWATCH_W,
245 bottom + 1 + i / SWATCH_COLUMNS,
246 );
247 b.print(x, y, "██", rgb, black);
248 let name: String = name.chars().take(SWATCH_NAME_W).collect();
249 b.print(x + 3, y, &name, dim, black);
250 }
251
252 let panels_top = panels_top(palette, glyphs);
253 b.print(
254 1,
255 panels_top - 1,
256 "The quick brown fox jumps over the lazy dog. 0123456789 ÀÉÎÕÜ àéîõü ß ¿¡ «» ← ↑ → ↓",
258 text,
259 black,
260 );
261 panels(&mut b, palette, panels_top);
262 b
263}