1pub mod editor;
3pub mod highlighter;
4pub mod paragraph;
5
6pub use editor::Editor;
7pub use highlighter::Highlighter;
8pub use paragraph::Paragraph;
9
10use crate::alignment;
11use crate::{
12 Background, Border, Color, Padding, Pixels, Point, Rectangle, Size,
13};
14
15use std::borrow::Cow;
16use std::hash::{Hash, Hasher};
17
18#[derive(Debug, Clone, Copy)]
20pub struct Text<Content = String, Font = crate::Font> {
21 pub content: Content,
23
24 pub bounds: Size,
26
27 pub size: Pixels,
29
30 pub line_height: LineHeight,
32
33 pub font: Font,
35
36 pub horizontal_alignment: alignment::Horizontal,
38
39 pub vertical_alignment: alignment::Vertical,
41
42 pub shaping: Shaping,
44
45 pub wrapping: Wrapping,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
51pub enum Shaping {
52 Basic,
61 #[default]
70 Advanced,
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
75pub enum Wrapping {
76 None,
78 #[default]
82 Word,
83 Glyph,
85 WordOrGlyph,
87}
88
89#[derive(Debug, Clone, Copy, PartialEq)]
91pub enum LineHeight {
92 Relative(f32),
94
95 Absolute(Pixels),
97}
98
99impl LineHeight {
100 pub fn to_absolute(self, text_size: Pixels) -> Pixels {
102 match self {
103 Self::Relative(factor) => Pixels(factor * text_size.0),
104 Self::Absolute(pixels) => pixels,
105 }
106 }
107}
108
109impl Default for LineHeight {
110 fn default() -> Self {
111 Self::Relative(1.4)
112 }
113}
114
115impl From<f32> for LineHeight {
116 fn from(factor: f32) -> Self {
117 Self::Relative(factor)
118 }
119}
120
121impl From<Pixels> for LineHeight {
122 fn from(pixels: Pixels) -> Self {
123 Self::Absolute(pixels)
124 }
125}
126
127impl Hash for LineHeight {
128 fn hash<H: Hasher>(&self, state: &mut H) {
129 match self {
130 Self::Relative(factor) => {
131 state.write_u8(0);
132 factor.to_bits().hash(state);
133 }
134 Self::Absolute(pixels) => {
135 state.write_u8(1);
136 f32::from(*pixels).to_bits().hash(state);
137 }
138 }
139 }
140}
141
142#[derive(Debug, Clone, Copy, PartialEq)]
144pub enum Hit {
145 CharOffset(usize),
147}
148
149impl Hit {
150 pub fn cursor(self) -> usize {
152 match self {
153 Self::CharOffset(i) => i,
154 }
155 }
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
165pub enum Difference {
166 None,
170
171 Bounds,
176
177 Shape,
183}
184
185pub trait Renderer: crate::Renderer {
187 type Font: Copy + PartialEq;
189
190 type Paragraph: Paragraph<Font = Self::Font> + 'static;
192
193 type Editor: Editor<Font = Self::Font> + 'static;
195
196 type Raw;
198
199 const ICON_FONT: Self::Font;
201
202 const CHECKMARK_ICON: char;
206
207 const ARROW_DOWN_ICON: char;
211
212 fn default_font(&self) -> Self::Font;
214
215 fn default_size(&self) -> Pixels;
217
218 fn fill_paragraph(
221 &mut self,
222 text: &Self::Paragraph,
223 position: Point,
224 color: Color,
225 clip_bounds: Rectangle,
226 );
227
228 fn fill_editor(
231 &mut self,
232 editor: &Self::Editor,
233 position: Point,
234 color: Color,
235 clip_bounds: Rectangle,
236 );
237
238 fn fill_raw(&mut self, raw: Self::Raw);
240
241 fn fill_text(
244 &mut self,
245 text: Text<String, Self::Font>,
246 position: Point,
247 color: Color,
248 clip_bounds: Rectangle,
249 );
250}
251
252#[derive(Debug, Clone)]
254pub struct Span<'a, Link = (), Font = crate::Font> {
255 pub text: Fragment<'a>,
257 pub size: Option<Pixels>,
259 pub line_height: Option<LineHeight>,
261 pub font: Option<Font>,
263 pub color: Option<Color>,
265 pub link: Option<Link>,
267 pub highlight: Option<Highlight>,
269 pub padding: Padding,
273 pub underline: bool,
275 pub strikethrough: bool,
277}
278
279#[derive(Debug, Clone, Copy, PartialEq)]
281pub struct Highlight {
282 pub background: Background,
284 pub border: Border,
286}
287
288impl<'a, Link, Font> Span<'a, Link, Font> {
289 pub fn new(fragment: impl IntoFragment<'a>) -> Self {
291 Self {
292 text: fragment.into_fragment(),
293 size: None,
294 line_height: None,
295 font: None,
296 color: None,
297 highlight: None,
298 link: None,
299 padding: Padding::ZERO,
300 underline: false,
301 strikethrough: false,
302 }
303 }
304
305 pub fn size(mut self, size: impl Into<Pixels>) -> Self {
307 self.size = Some(size.into());
308 self
309 }
310
311 pub fn line_height(mut self, line_height: impl Into<LineHeight>) -> Self {
313 self.line_height = Some(line_height.into());
314 self
315 }
316
317 pub fn font(mut self, font: impl Into<Font>) -> Self {
319 self.font = Some(font.into());
320 self
321 }
322
323 pub fn font_maybe(mut self, font: Option<impl Into<Font>>) -> Self {
325 self.font = font.map(Into::into);
326 self
327 }
328
329 pub fn color(mut self, color: impl Into<Color>) -> Self {
331 self.color = Some(color.into());
332 self
333 }
334
335 pub fn color_maybe(mut self, color: Option<impl Into<Color>>) -> Self {
337 self.color = color.map(Into::into);
338 self
339 }
340
341 pub fn link(mut self, link: impl Into<Link>) -> Self {
343 self.link = Some(link.into());
344 self
345 }
346
347 pub fn link_maybe(mut self, link: Option<impl Into<Link>>) -> Self {
349 self.link = link.map(Into::into);
350 self
351 }
352
353 pub fn background(self, background: impl Into<Background>) -> Self {
355 self.background_maybe(Some(background))
356 }
357
358 pub fn background_maybe(
360 mut self,
361 background: Option<impl Into<Background>>,
362 ) -> Self {
363 let Some(background) = background else {
364 return self;
365 };
366
367 match &mut self.highlight {
368 Some(highlight) => {
369 highlight.background = background.into();
370 }
371 None => {
372 self.highlight = Some(Highlight {
373 background: background.into(),
374 border: Border::default(),
375 });
376 }
377 }
378
379 self
380 }
381
382 pub fn border(self, border: impl Into<Border>) -> Self {
384 self.border_maybe(Some(border))
385 }
386
387 pub fn border_maybe(mut self, border: Option<impl Into<Border>>) -> Self {
389 let Some(border) = border else {
390 return self;
391 };
392
393 match &mut self.highlight {
394 Some(highlight) => {
395 highlight.border = border.into();
396 }
397 None => {
398 self.highlight = Some(Highlight {
399 border: border.into(),
400 background: Background::Color(Color::TRANSPARENT),
401 });
402 }
403 }
404
405 self
406 }
407
408 pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
416 self.padding = padding.into();
417 self
418 }
419
420 pub fn underline(mut self, underline: bool) -> Self {
422 self.underline = underline;
423 self
424 }
425
426 pub fn strikethrough(mut self, strikethrough: bool) -> Self {
428 self.strikethrough = strikethrough;
429 self
430 }
431
432 pub fn to_static(self) -> Span<'static, Link, Font> {
434 Span {
435 text: Cow::Owned(self.text.into_owned()),
436 size: self.size,
437 line_height: self.line_height,
438 font: self.font,
439 color: self.color,
440 link: self.link,
441 highlight: self.highlight,
442 padding: self.padding,
443 underline: self.underline,
444 strikethrough: self.strikethrough,
445 }
446 }
447}
448
449impl<'a, Link, Font> From<&'a str> for Span<'a, Link, Font> {
450 fn from(value: &'a str) -> Self {
451 Span::new(value)
452 }
453}
454
455impl<'a, Link, Font: PartialEq> PartialEq for Span<'a, Link, Font> {
456 fn eq(&self, other: &Self) -> bool {
457 self.text == other.text
458 && self.size == other.size
459 && self.line_height == other.line_height
460 && self.font == other.font
461 && self.color == other.color
462 }
463}
464
465pub type Fragment<'a> = Cow<'a, str>;
470
471pub trait IntoFragment<'a> {
473 fn into_fragment(self) -> Fragment<'a>;
475}
476
477impl<'a> IntoFragment<'a> for Fragment<'a> {
478 fn into_fragment(self) -> Fragment<'a> {
479 self
480 }
481}
482
483impl<'a, 'b> IntoFragment<'a> for &'a Fragment<'b> {
484 fn into_fragment(self) -> Fragment<'a> {
485 Fragment::Borrowed(self)
486 }
487}
488
489impl<'a> IntoFragment<'a> for &'a str {
490 fn into_fragment(self) -> Fragment<'a> {
491 Fragment::Borrowed(self)
492 }
493}
494
495impl<'a> IntoFragment<'a> for &'a String {
496 fn into_fragment(self) -> Fragment<'a> {
497 Fragment::Borrowed(self.as_str())
498 }
499}
500
501impl<'a> IntoFragment<'a> for String {
502 fn into_fragment(self) -> Fragment<'a> {
503 Fragment::Owned(self)
504 }
505}
506
507macro_rules! into_fragment {
508 ($type:ty) => {
509 impl<'a> IntoFragment<'a> for $type {
510 fn into_fragment(self) -> Fragment<'a> {
511 Fragment::Owned(self.to_string())
512 }
513 }
514
515 impl<'a> IntoFragment<'a> for &$type {
516 fn into_fragment(self) -> Fragment<'a> {
517 Fragment::Owned(self.to_string())
518 }
519 }
520 };
521}
522
523into_fragment!(char);
524into_fragment!(bool);
525
526into_fragment!(u8);
527into_fragment!(u16);
528into_fragment!(u32);
529into_fragment!(u64);
530into_fragment!(u128);
531into_fragment!(usize);
532
533into_fragment!(i8);
534into_fragment!(i16);
535into_fragment!(i32);
536into_fragment!(i64);
537into_fragment!(i128);
538into_fragment!(isize);
539
540into_fragment!(f32);
541into_fragment!(f64);