egui_demo_lib/easy_mark/
easy_mark_viewer.rs1use super::easy_mark_parser as easy_mark;
2use egui::{
3 Align, Align2, Hyperlink, Layout, Response, RichText, Sense, Separator, Shape, TextStyle, Ui,
4 vec2,
5};
6
7pub fn easy_mark(ui: &mut Ui, easy_mark: &str) {
9 easy_mark_it(ui, easy_mark::Parser::new(easy_mark));
10}
11
12pub fn easy_mark_it<'em>(ui: &mut Ui, items: impl Iterator<Item = easy_mark::Item<'em>>) {
13 let initial_size = vec2(
14 ui.available_width(),
15 ui.spacing().interact_size.y, );
17
18 let layout = Layout::left_to_right(Align::BOTTOM).with_main_wrap(true);
19
20 ui.allocate_ui_with_layout(initial_size, layout, |ui| {
21 ui.spacing_mut().item_spacing.x = 0.0;
22 let row_height = ui.text_style_height(&TextStyle::Body);
23 ui.set_row_height(row_height);
24
25 for item in items {
26 item_ui(ui, item);
27 }
28 });
29}
30
31pub fn item_ui(ui: &mut Ui, item: easy_mark::Item<'_>) {
32 let row_height = ui.text_style_height(&TextStyle::Body);
33 let one_indent = row_height / 2.0;
34
35 match item {
36 easy_mark::Item::Newline => {
37 ui.allocate_exact_size(vec2(0.0, row_height), Sense::hover()); ui.end_row();
40 ui.set_row_height(row_height);
41 }
42
43 easy_mark::Item::Text(style, text) => {
44 let label = rich_text_from_style(text, &style);
45 if style.small && !style.raised {
46 ui.with_layout(Layout::left_to_right(Align::BOTTOM), |ui| {
47 ui.set_min_height(row_height);
48 ui.label(label);
49 });
50 } else {
51 ui.label(label);
52 }
53 }
54 easy_mark::Item::Hyperlink(style, text, url) => {
55 let label = rich_text_from_style(text, &style);
56 if style.small && !style.raised {
57 ui.with_layout(Layout::left_to_right(Align::BOTTOM), |ui| {
58 ui.set_height(row_height);
59 ui.add(Hyperlink::from_label_and_url(label, url));
60 });
61 } else {
62 ui.add(Hyperlink::from_label_and_url(label, url));
63 }
64 }
65
66 easy_mark::Item::Separator => {
67 ui.add(Separator::default().horizontal());
68 }
69 easy_mark::Item::Indentation(indent) => {
70 let indent = indent as f32 * one_indent;
71 ui.allocate_exact_size(vec2(indent, row_height), Sense::hover());
72 }
73 easy_mark::Item::QuoteIndent => {
74 let rect = ui
75 .allocate_exact_size(vec2(2.0 * one_indent, row_height), Sense::hover())
76 .0;
77 let rect = rect.expand2(ui.style().spacing.item_spacing * 0.5);
78 ui.painter().line_segment(
79 [rect.center_top(), rect.center_bottom()],
80 (1.0, ui.visuals().weak_text_color()),
81 );
82 }
83 easy_mark::Item::BulletPoint => {
84 ui.allocate_exact_size(vec2(one_indent, row_height), Sense::hover());
85 bullet_point(ui, one_indent);
86 ui.allocate_exact_size(vec2(one_indent, row_height), Sense::hover());
87 }
88 easy_mark::Item::NumberedPoint(number) => {
89 let width = 3.0 * one_indent;
90 numbered_point(ui, width, number);
91 ui.allocate_exact_size(vec2(one_indent, row_height), Sense::hover());
92 }
93 easy_mark::Item::CodeBlock(_language, code) => {
94 let where_to_put_background = ui.painter().add(Shape::Noop);
95 let mut rect = ui.monospace(code).rect;
96 rect = rect.expand(1.0); rect.max.x = ui.max_rect().max.x;
98 let code_bg_color = ui.visuals().code_bg_color;
99 ui.painter().set(
100 where_to_put_background,
101 Shape::rect_filled(rect, 1.0, code_bg_color),
102 );
103 }
104 }
105}
106
107fn rich_text_from_style(text: &str, style: &easy_mark::Style) -> RichText {
108 let easy_mark::Style {
109 heading,
110 quoted,
111 code,
112 strong,
113 underline,
114 strikethrough,
115 italics,
116 small,
117 raised,
118 } = *style;
119
120 let small = small || raised; let mut rich_text = RichText::new(text);
123 if heading && !small {
124 rich_text = rich_text.heading().strong();
125 }
126 if small && !heading {
127 rich_text = rich_text.small();
128 }
129 if code {
130 rich_text = rich_text.code();
131 }
132 if strong {
133 rich_text = rich_text.strong();
134 } else if quoted {
135 rich_text = rich_text.weak();
136 }
137 if underline {
138 rich_text = rich_text.underline();
139 }
140 if strikethrough {
141 rich_text = rich_text.strikethrough();
142 }
143 if italics {
144 rich_text = rich_text.italics();
145 }
146 if raised {
147 rich_text = rich_text.raised();
148 }
149 rich_text
150}
151
152fn bullet_point(ui: &mut Ui, width: f32) -> Response {
153 let row_height = ui.text_style_height(&TextStyle::Body);
154 let (rect, response) = ui.allocate_exact_size(vec2(width, row_height), Sense::hover());
155 ui.painter().circle_filled(
156 rect.center(),
157 rect.height() / 8.0,
158 ui.visuals().strong_text_color(),
159 );
160 response
161}
162
163fn numbered_point(ui: &mut Ui, width: f32, number: &str) -> Response {
164 let font_id = TextStyle::Body.resolve(ui.style());
165 let row_height = ui.fonts_mut(|f| f.row_height(&font_id));
166 let (rect, response) = ui.allocate_exact_size(vec2(width, row_height), Sense::hover());
167 let text = format!("{number}.");
168 let text_color = ui.visuals().strong_text_color();
169 ui.painter().text(
170 rect.right_center(),
171 Align2::RIGHT_CENTER,
172 text,
173 font_id,
174 text_color,
175 );
176 response
177}