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 align_x: Alignment,
38
39 pub align_y: alignment::Vertical,
41
42 pub shaping: Shaping,
44
45 pub wrapping: Wrapping,
47}
48
49impl<Content, Font> Text<Content, Font>
50where
51 Font: Copy,
52{
53 pub fn with_content<T>(&self, content: T) -> Text<T, Font> {
56 Text {
57 content,
58 bounds: self.bounds,
59 size: self.size,
60 line_height: self.line_height,
61 font: self.font,
62 align_x: self.align_x,
63 align_y: self.align_y,
64 shaping: self.shaping,
65 wrapping: self.wrapping,
66 }
67 }
68}
69
70impl<Content, Font> Text<Content, Font>
71where
72 Content: AsRef<str>,
73 Font: Copy,
74{
75 pub fn as_ref(&self) -> Text<&str, Font> {
77 self.with_content(self.content.as_ref())
78 }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
83pub enum Alignment {
84 #[default]
89 Default,
90 Left,
92 Center,
94 Right,
96 Justified,
98}
99
100impl From<alignment::Horizontal> for Alignment {
101 fn from(alignment: alignment::Horizontal) -> Self {
102 match alignment {
103 alignment::Horizontal::Left => Self::Left,
104 alignment::Horizontal::Center => Self::Center,
105 alignment::Horizontal::Right => Self::Right,
106 }
107 }
108}
109
110impl From<crate::Alignment> for Alignment {
111 fn from(alignment: crate::Alignment) -> Self {
112 match alignment {
113 crate::Alignment::Start => Self::Left,
114 crate::Alignment::Center => Self::Center,
115 crate::Alignment::End => Self::Right,
116 }
117 }
118}
119
120impl From<Alignment> for alignment::Horizontal {
121 fn from(alignment: Alignment) -> Self {
122 match alignment {
123 Alignment::Default | Alignment::Left | Alignment::Justified => {
124 alignment::Horizontal::Left
125 }
126 Alignment::Center => alignment::Horizontal::Center,
127 Alignment::Right => alignment::Horizontal::Right,
128 }
129 }
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
134pub enum Shaping {
135 #[default]
145 Basic,
146 Advanced,
154}
155
156#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
158pub enum Wrapping {
159 None,
161 #[default]
165 Word,
166 Glyph,
168 WordOrGlyph,
170}
171
172#[derive(Debug, Clone, Copy, PartialEq)]
174pub enum LineHeight {
175 Relative(f32),
177
178 Absolute(Pixels),
180}
181
182impl LineHeight {
183 pub fn to_absolute(self, text_size: Pixels) -> Pixels {
185 match self {
186 Self::Relative(factor) => Pixels(factor * text_size.0),
187 Self::Absolute(pixels) => pixels,
188 }
189 }
190}
191
192impl Default for LineHeight {
193 fn default() -> Self {
194 Self::Relative(1.3)
195 }
196}
197
198impl From<f32> for LineHeight {
199 fn from(factor: f32) -> Self {
200 Self::Relative(factor)
201 }
202}
203
204impl From<Pixels> for LineHeight {
205 fn from(pixels: Pixels) -> Self {
206 Self::Absolute(pixels)
207 }
208}
209
210impl Hash for LineHeight {
211 fn hash<H: Hasher>(&self, state: &mut H) {
212 match self {
213 Self::Relative(factor) => {
214 state.write_u8(0);
215 factor.to_bits().hash(state);
216 }
217 Self::Absolute(pixels) => {
218 state.write_u8(1);
219 f32::from(*pixels).to_bits().hash(state);
220 }
221 }
222 }
223}
224
225#[derive(Debug, Clone, Copy, PartialEq)]
227pub enum Hit {
228 CharOffset(usize),
230}
231
232impl Hit {
233 pub fn cursor(self) -> usize {
235 match self {
236 Self::CharOffset(i) => i,
237 }
238 }
239}
240
241#[derive(Debug, Clone, Copy, PartialEq, Eq)]
248pub enum Difference {
249 None,
253
254 Bounds,
259
260 Shape,
266}
267
268pub trait Renderer: crate::Renderer {
270 type Font: Copy + PartialEq;
272
273 type Paragraph: Paragraph<Font = Self::Font> + 'static;
275
276 type Editor: Editor<Font = Self::Font> + 'static;
278
279 const MONOSPACE_FONT: Self::Font;
283
284 const ICON_FONT: Self::Font;
286
287 const CHECKMARK_ICON: char;
291
292 const ARROW_DOWN_ICON: char;
296
297 fn default_font(&self) -> Self::Font;
299
300 fn default_size(&self) -> Pixels;
302
303 fn fill_paragraph(
306 &mut self,
307 text: &Self::Paragraph,
308 position: Point,
309 color: Color,
310 clip_bounds: Rectangle,
311 );
312
313 fn fill_editor(
316 &mut self,
317 editor: &Self::Editor,
318 position: Point,
319 color: Color,
320 clip_bounds: Rectangle,
321 );
322
323 fn fill_text(
326 &mut self,
327 text: Text<String, Self::Font>,
328 position: Point,
329 color: Color,
330 clip_bounds: Rectangle,
331 );
332}
333
334#[derive(Debug, Clone)]
336pub struct Span<'a, Link = (), Font = crate::Font> {
337 pub text: Fragment<'a>,
339 pub size: Option<Pixels>,
341 pub line_height: Option<LineHeight>,
343 pub font: Option<Font>,
345 pub color: Option<Color>,
347 pub link: Option<Link>,
349 pub highlight: Option<Highlight>,
351 pub padding: Padding,
355 pub underline: bool,
357 pub strikethrough: bool,
359}
360
361#[derive(Debug, Clone, Copy, PartialEq)]
363pub struct Highlight {
364 pub background: Background,
366 pub border: Border,
368}
369
370impl<'a, Link, Font> Span<'a, Link, Font> {
371 pub fn new(fragment: impl IntoFragment<'a>) -> Self {
373 Self {
374 text: fragment.into_fragment(),
375 ..Self::default()
376 }
377 }
378
379 pub fn size(mut self, size: impl Into<Pixels>) -> Self {
381 self.size = Some(size.into());
382 self
383 }
384
385 pub fn line_height(mut self, line_height: impl Into<LineHeight>) -> Self {
387 self.line_height = Some(line_height.into());
388 self
389 }
390
391 pub fn font(mut self, font: impl Into<Font>) -> Self {
393 self.font = Some(font.into());
394 self
395 }
396
397 pub fn font_maybe(mut self, font: Option<impl Into<Font>>) -> Self {
399 self.font = font.map(Into::into);
400 self
401 }
402
403 pub fn color(mut self, color: impl Into<Color>) -> Self {
405 self.color = Some(color.into());
406 self
407 }
408
409 pub fn color_maybe(mut self, color: Option<impl Into<Color>>) -> Self {
411 self.color = color.map(Into::into);
412 self
413 }
414
415 pub fn link(mut self, link: impl Into<Link>) -> Self {
417 self.link = Some(link.into());
418 self
419 }
420
421 pub fn link_maybe(mut self, link: Option<impl Into<Link>>) -> Self {
423 self.link = link.map(Into::into);
424 self
425 }
426
427 pub fn background(self, background: impl Into<Background>) -> Self {
429 self.background_maybe(Some(background))
430 }
431
432 pub fn background_maybe(
434 mut self,
435 background: Option<impl Into<Background>>,
436 ) -> Self {
437 let Some(background) = background else {
438 return self;
439 };
440
441 match &mut self.highlight {
442 Some(highlight) => {
443 highlight.background = background.into();
444 }
445 None => {
446 self.highlight = Some(Highlight {
447 background: background.into(),
448 border: Border::default(),
449 });
450 }
451 }
452
453 self
454 }
455
456 pub fn border(self, border: impl Into<Border>) -> Self {
458 self.border_maybe(Some(border))
459 }
460
461 pub fn border_maybe(mut self, border: Option<impl Into<Border>>) -> Self {
463 let Some(border) = border else {
464 return self;
465 };
466
467 match &mut self.highlight {
468 Some(highlight) => {
469 highlight.border = border.into();
470 }
471 None => {
472 self.highlight = Some(Highlight {
473 border: border.into(),
474 background: Background::Color(Color::TRANSPARENT),
475 });
476 }
477 }
478
479 self
480 }
481
482 pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
490 self.padding = padding.into();
491 self
492 }
493
494 pub fn underline(mut self, underline: bool) -> Self {
496 self.underline = underline;
497 self
498 }
499
500 pub fn strikethrough(mut self, strikethrough: bool) -> Self {
502 self.strikethrough = strikethrough;
503 self
504 }
505
506 pub fn to_static(self) -> Span<'static, Link, Font> {
508 Span {
509 text: Cow::Owned(self.text.into_owned()),
510 size: self.size,
511 line_height: self.line_height,
512 font: self.font,
513 color: self.color,
514 link: self.link,
515 highlight: self.highlight,
516 padding: self.padding,
517 underline: self.underline,
518 strikethrough: self.strikethrough,
519 }
520 }
521}
522
523impl<Link, Font> Default for Span<'_, Link, Font> {
524 fn default() -> Self {
525 Self {
526 text: Cow::default(),
527 size: None,
528 line_height: None,
529 font: None,
530 color: None,
531 link: None,
532 highlight: None,
533 padding: Padding::default(),
534 underline: false,
535 strikethrough: false,
536 }
537 }
538}
539
540impl<'a, Link, Font> From<&'a str> for Span<'a, Link, Font> {
541 fn from(value: &'a str) -> Self {
542 Span::new(value)
543 }
544}
545
546impl<Link, Font: PartialEq> PartialEq for Span<'_, Link, Font> {
547 fn eq(&self, other: &Self) -> bool {
548 self.text == other.text
549 && self.size == other.size
550 && self.line_height == other.line_height
551 && self.font == other.font
552 && self.color == other.color
553 }
554}
555
556pub type Fragment<'a> = Cow<'a, str>;
561
562pub trait IntoFragment<'a> {
564 fn into_fragment(self) -> Fragment<'a>;
566}
567
568impl<'a> IntoFragment<'a> for Fragment<'a> {
569 fn into_fragment(self) -> Fragment<'a> {
570 self
571 }
572}
573
574impl<'a> IntoFragment<'a> for &'a Fragment<'_> {
575 fn into_fragment(self) -> Fragment<'a> {
576 Fragment::Borrowed(self)
577 }
578}
579
580impl<'a> IntoFragment<'a> for &'a str {
581 fn into_fragment(self) -> Fragment<'a> {
582 Fragment::Borrowed(self)
583 }
584}
585
586impl<'a> IntoFragment<'a> for &'a String {
587 fn into_fragment(self) -> Fragment<'a> {
588 Fragment::Borrowed(self.as_str())
589 }
590}
591
592impl<'a> IntoFragment<'a> for String {
593 fn into_fragment(self) -> Fragment<'a> {
594 Fragment::Owned(self)
595 }
596}
597
598macro_rules! into_fragment {
599 ($type:ty) => {
600 impl<'a> IntoFragment<'a> for $type {
601 fn into_fragment(self) -> Fragment<'a> {
602 Fragment::Owned(self.to_string())
603 }
604 }
605
606 impl<'a> IntoFragment<'a> for &$type {
607 fn into_fragment(self) -> Fragment<'a> {
608 Fragment::Owned(self.to_string())
609 }
610 }
611 };
612}
613
614into_fragment!(char);
615into_fragment!(bool);
616
617into_fragment!(u8);
618into_fragment!(u16);
619into_fragment!(u32);
620into_fragment!(u64);
621into_fragment!(u128);
622into_fragment!(usize);
623
624into_fragment!(i8);
625into_fragment!(i16);
626into_fragment!(i32);
627into_fragment!(i64);
628into_fragment!(i128);
629into_fragment!(isize);
630
631into_fragment!(f32);
632into_fragment!(f64);