1use crate::{
8 grid::{GRID_SIZE, pin_offset_y},
9 shape::pin::PinSide,
10 theme::Style,
11};
12use blockworx_geom::{Align, Align2, Pos2, Rect, pos2};
13use blockworx_paint::Renderer;
14
15use super::{
16 ADD_NAME_PLACEHOLDER, ADD_TYPE_PLACEHOLDER, first_line, pin_label_anchors, pin_tag_location,
17};
18
19pub fn pin_name_rect_at(
24 pos: Pos2,
25 h_align: Align,
26 name: &str,
27 painter: &Style<'_, impl Renderer>,
28) -> Rect {
29 let name = first_line(name);
30 let text = if name.is_empty() {
31 ADD_NAME_PLACEHOLDER
32 } else {
33 name
34 };
35 let size = painter.text_size(text, &painter.theme().pin_font);
36 let (name_pos, _) = pin_label_anchors(pos);
37 Align2([h_align, Align::Center]).anchor_size(name_pos, size)
38}
39
40pub fn pin_type_rect_at(
43 pos: Pos2,
44 h_align: Align,
45 type_label: &str,
46 painter: &Style<'_, impl Renderer>,
47) -> Rect {
48 let type_label = first_line(type_label);
49 let text = if type_label.is_empty() {
50 ADD_TYPE_PLACEHOLDER
51 } else {
52 type_label
53 };
54 let size = painter.text_size(text, &painter.theme().pin_subtitle_font);
55 let (_, type_pos) = pin_label_anchors(pos);
56 Align2([h_align, Align::Center]).anchor_size(type_pos, size)
57}
58
59fn block_pin_label_anchor(bbox: Rect, side: PinSide, offset: u32) -> (Pos2, Align) {
62 let y = pin_offset_y(bbox.top(), offset);
63 match side {
64 PinSide::East => (pos2(bbox.right() - GRID_SIZE / 2.0, y), Align::Max),
65 PinSide::West => (pos2(bbox.left() + GRID_SIZE / 2.0, y), Align::Min),
66 }
67}
68
69pub fn estimate_bbox_for_pin_name(
71 bbox: Rect,
72 side: PinSide,
73 offset: u32,
74 name: &str,
75 painter: &Style<'_, impl Renderer>,
76) -> Rect {
77 let (pos, h) = block_pin_label_anchor(bbox, side, offset);
78 pin_name_rect_at(pos, h, name, painter)
79}
80
81pub fn estimate_bbox_for_pin_type(
83 bbox: Rect,
84 side: PinSide,
85 offset: u32,
86 type_label: &str,
87 painter: &Style<'_, impl Renderer>,
88) -> Rect {
89 let (pos, h) = block_pin_label_anchor(bbox, side, offset);
90 pin_type_rect_at(pos, h, type_label, painter)
91}
92
93pub struct PinExtent<'a> {
98 pub bbox: Rect,
100 pub side: PinSide,
101 pub offset: u32,
102 pub name: &'a str,
103 pub type_label: &'a str,
104 pub tag: &'a str,
105}
106
107impl PinExtent<'_> {
108 pub fn bbox(&self, painter: &Style<'_, impl Renderer>) -> Rect {
109 let Self {
110 bbox,
111 side,
112 offset,
113 name,
114 type_label,
115 tag,
116 } = *self;
117 let line_y = pin_offset_y(bbox.top(), offset);
118 let mut rect = estimate_bbox_for_pin_stub(bbox.left(), bbox.right(), side, line_y)
119 .union(estimate_bbox_for_pin_name(
120 bbox, side, offset, name, painter,
121 ))
122 .union(estimate_bbox_for_pin_type(
123 bbox, side, offset, type_label, painter,
124 ));
125 if !tag.is_empty() {
126 rect = rect.union(
127 TagSlot {
128 left: bbox.left(),
129 right: bbox.right(),
130 side,
131 line_y,
132 }
133 .bbox(tag, painter),
134 );
135 }
136 rect
137 }
138}
139
140pub fn estimate_bbox_for_pin_stub(left: f32, right: f32, side: PinSide, line_y: f32) -> Rect {
144 let half = GRID_SIZE / 3.0;
145 let (x0, x1) = match side {
146 PinSide::East => (right, right + GRID_SIZE),
147 PinSide::West => (left - GRID_SIZE, left),
148 };
149 Rect::from_min_max(pos2(x0, line_y - half), pos2(x1, line_y + half))
150}
151
152pub struct TagSlot {
155 pub left: f32,
157 pub right: f32,
158 pub side: PinSide,
159 pub line_y: f32,
161}
162
163impl TagSlot {
164 pub fn bbox(&self, tag: &str, painter: &Style<'_, impl Renderer>) -> Rect {
165 let size = painter.text_size(tag, &painter.theme().tag_font);
166 let (pos, align) = pin_tag_location(self.left, self.right, self.side, self.line_y);
167 align.anchor_size(pos, size)
168 }
169}