Skip to main content

blockworx/shape/
comment.rs

1use crate::theme::Style;
2use egui::{Pos2, Rect};
3
4use blockworx_doc::block_model::Comment;
5
6use crate::render::block_title_position;
7use crate::{
8    canvas::Renderer,
9    edit::{
10        lower::{accent_from_role, px_rect, shape_label},
11        naming::InterfaceLock,
12    },
13    grid::grid_rect,
14    shape::{BaseShape, ShapeLabel, block::resize_rect},
15    state::RenderMode,
16    theme::Role,
17};
18
19/// The [`Role`] the outline strokes with, chosen by the comment's accent
20/// `role` register: `Accent1..=Accent8` → [`Role::Accent0`]..[`Role::Accent7`],
21/// the plain `Accent0` → [`Role::CommentStroke`].
22fn accent(comment: &Comment) -> Role {
23    crate::theme::accent_role(accent_from_role(*comment.role.as_ref()))
24        .unwrap_or(Role::CommentStroke)
25}
26
27/// Draw the comment's outline (no fill) plus its title, at `bbox`.
28fn draw<R: Renderer>(
29    comment: &Comment,
30    bbox: Rect,
31    title: &ShapeLabel<'_>,
32    painter: &mut Style<'_, R>,
33) {
34    crate::render::draw_box_outline(bbox, Role::Transparent, (1.0, accent(comment)), painter);
35    crate::render::draw_block_title(bbox, title, InterfaceLock::Unlocked, painter);
36}
37
38impl BaseShape for Comment {
39    fn title(&self) -> Option<ShapeLabel<'_>> {
40        Some(shape_label(&self.title))
41    }
42    fn gui_rect(&self) -> Rect {
43        px_rect(*self.rect.as_ref())
44    }
45    fn title_anchor(&self) -> Option<Pos2> {
46        let (pos, _) = block_title_position(self.gui_rect(), &shape_label(&self.title));
47        Some(pos)
48    }
49    fn resizable(&self) -> bool {
50        true
51    }
52    fn render_ng<R: Renderer>(&self, mode: RenderMode, painter: &mut Style<'_, R>) {
53        let bbox = self.gui_rect();
54        let title = shape_label(&self.title);
55        match mode {
56            RenderMode::Moving { delta } => {
57                let shifted = bbox.translate(delta);
58                draw(self, shifted, &title, painter);
59            }
60            RenderMode::Selected { .. } => {
61                draw(self, bbox, &title, painter);
62                crate::render::draw_selection_frame(bbox, None, painter);
63            }
64            RenderMode::Resizing { mode, delta } => {
65                let resized = grid_rect(resize_rect(&bbox, mode, delta));
66                draw(self, resized, &title, painter);
67                crate::render::draw_selection_frame(resized, Some(mode), painter);
68            }
69            RenderMode::TitleDragged { delta } => {
70                // Mirror `Block`'s title drag: snap the side to the nearest third
71                // of the box and slide the horizontal offset, previewing where the
72                // label will land before the drop commits.
73                let (anchor_pos, _) = block_title_position(bbox, &title);
74                let text_w = painter.text_size(title.name, &painter.theme().title_font).x;
75                let shifted = ShapeLabel {
76                    side: crate::render::label_side_for_y(bbox, anchor_pos.y + delta.y),
77                    offset: crate::render::clamp_label_offset(
78                        bbox,
79                        text_w,
80                        title.offset + delta.x,
81                        egui::Align::Center,
82                    ),
83                    ..title
84                };
85                draw(self, bbox, &shifted, painter);
86                painter.line_segment(
87                    [bbox.left_center(), bbox.right_center()],
88                    (2.0, Role::PinDragIndicator),
89                );
90            }
91            // Comments have no pins, image, or tag, so every other mode renders
92            // the same as `Normal`: the outline and title.
93            _ => draw(self, bbox, &title, painter),
94        }
95    }
96}