Skip to main content

blockworx_editor/render/
text.rs

1use crate::{
2    grid::{GRID_SIZE, TYPE_SHIM_Y},
3    shape::pin::PinSide,
4    theme::{Role, Style},
5};
6use blockworx_geom::{Align, Align2, Pos2};
7use blockworx_paint::Renderer;
8
9use super::pin_tag_location;
10
11/// A pin/port label's first line. Name and type are single-line, but a
12/// document may carry a trailing `\n`-separated line in `name`; this drops it.
13pub fn first_line(s: &str) -> &str {
14    s.split('\n').next().unwrap_or(s)
15}
16
17/// The two draw anchors for a pin's labels, both vertically centered: the
18/// **name** on `pos` (the pin's stub), and the **type** one [`GRID_SIZE`] below —
19/// nudged up by [`TYPE_SHIM_Y`]. Content-independent, so the render, placeholder,
20/// and hit-test paths place both lines identically. Returns `(name_pos, type_pos)`.
21pub fn pin_label_anchors(pos: Pos2) -> (Pos2, Pos2) {
22    (pos, Pos2::new(pos.x, pos.y + GRID_SIZE - TYPE_SHIM_Y))
23}
24
25/// Draw a pin/port's `name` (centered on `pos`, in `pin_font`) and `type_label`
26/// (centered one grid below, in the smaller `pin_subtitle_font`). `h_align` is the
27/// horizontal alignment (RIGHT for East, LEFT for West, CENTER for ports). Empty
28/// labels draw nothing.
29pub fn draw_pin_labels<R: Renderer>(
30    pos: Pos2,
31    h_align: Align,
32    name: &str,
33    type_label: &str,
34    painter: &Style<'_, R>,
35) {
36    let color = Role::PinText;
37    let name = first_line(name);
38    let type_label = first_line(type_label);
39    let (name_pos, type_pos) = pin_label_anchors(pos);
40    let theme = painter.theme();
41    if !name.is_empty() {
42        painter.text(
43            name_pos,
44            Align2([h_align, Align::Center]),
45            name,
46            &theme.pin_font,
47            color,
48        );
49    }
50    if !type_label.is_empty() {
51        let font = &theme.pin_subtitle_font;
52        painter.text(
53            type_pos,
54            Align2([h_align, Align::Center]),
55            type_label,
56            font,
57            color,
58        );
59    }
60}
61
62/// Placeholder text shown faint at an empty editor or label slot to prompt input.
63/// The pin name/type prompts also appear as a faint static overlay when the pin's
64/// shape is selected (see [`draw_label_placeholders`]).
65pub const ADD_NAME_PLACEHOLDER: &str = "Add name";
66pub const ADD_TYPE_PLACEHOLDER: &str = "Add type";
67/// Short prompt for the compact tag editors (pin tag, block tag).
68pub const ADD_TAG_PLACEHOLDER: &str = "+tag";
69pub const ADD_ROUTE_LABEL_PLACEHOLDER: &str = "Add route label";
70pub const ADD_TITLE_PLACEHOLDER: &str = "Add title";
71pub const ADD_TEXT_PLACEHOLDER: &str = "Add text";
72
73/// Draw faint "Add Name"/"Add Type" prompts at a pin's empty label slots, in the
74/// same positions [`draw_pin_labels`] would use. Called only while the pin's
75/// shape is selected, so the user sees where to double-click to add a label.
76pub fn draw_label_placeholders<R: Renderer>(
77    pos: Pos2,
78    h_align: Align,
79    name: &str,
80    type_label: &str,
81    painter: &Style<'_, R>,
82) {
83    let name = first_line(name);
84    let type_label = first_line(type_label);
85    let (name_pos, type_pos) = pin_label_anchors(pos);
86    let color = Role::PinLabelPlaceholder;
87    let theme = painter.theme();
88    if name.is_empty() {
89        let font = &theme.pin_font;
90        painter.text(
91            name_pos,
92            Align2([h_align, Align::Center]),
93            ADD_NAME_PLACEHOLDER,
94            font,
95            color,
96        );
97    }
98    if type_label.is_empty() {
99        let font = &theme.pin_subtitle_font;
100        painter.text(
101            type_pos,
102            Align2([h_align, Align::Center]),
103            ADD_TYPE_PLACEHOLDER,
104            font,
105            color,
106        );
107    }
108}
109
110/// Draw the faint "+tag" prompt where an empty (but shown) pin/port tag would
111/// sit — outboard over the stub, placed by [`pin_tag_location`]. `left`/`right`
112/// are the shape's edge x-coordinates, `side` the side the label hangs off, and
113/// `line_y` the stub's y. Only drawn when the tag is shown; an empty hidden tag
114/// stays invisible.
115pub fn draw_tag_placeholder<R: Renderer>(
116    left: f32,
117    right: f32,
118    side: PinSide,
119    line_y: f32,
120    painter: &Style<'_, R>,
121) {
122    let (pos, align) = pin_tag_location(left, right, side, line_y);
123    painter.text(
124        pos,
125        align,
126        ADD_TAG_PLACEHOLDER,
127        &painter.theme().tag_font,
128        Role::PinLabelPlaceholder,
129    );
130}