1pub mod editor;
4pub mod highlighter;
5pub mod input;
6pub mod paragraph;
7pub mod parser;
8
9pub use editor::Editor;
10pub use highlighter::Highlighter;
11pub use input::Input;
12pub use paragraph::Paragraph;
13pub use parser::Parser;
14
15use crate::alignment;
16use crate::{Background, Border, Color, Font, Padding, Pixels, Point, Rectangle, Size};
17
18use std::borrow::Cow;
19use std::hash::{Hash, Hasher};
20
21#[derive(Debug, Clone, Copy)]
23pub struct Text<Content = String> {
24 pub content: Content,
26
27 pub bounds: Size,
29
30 pub size: Pixels,
32
33 pub line_height: LineHeight,
35
36 pub font: Font,
38
39 pub align_x: Alignment,
41
42 pub align_y: alignment::Vertical,
44
45 pub shaping: Shaping,
47
48 pub wrapping: Wrapping,
50
51 pub ellipsis: Ellipsis,
53
54 pub hint_factor: Option<f32>,
63}
64
65impl<Content> Text<Content> {
66 pub fn with_content<T>(&self, content: T) -> Text<T> {
69 Text {
70 content,
71 bounds: self.bounds,
72 size: self.size,
73 line_height: self.line_height,
74 font: self.font,
75 align_x: self.align_x,
76 align_y: self.align_y,
77 shaping: self.shaping,
78 wrapping: self.wrapping,
79 ellipsis: self.ellipsis,
80 hint_factor: self.hint_factor,
81 }
82 }
83}
84
85impl<Content: AsRef<str>> Text<Content> {
86 pub fn as_ref(&self) -> Text<&str> {
88 self.with_content(self.content.as_ref())
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
94pub enum Alignment {
95 #[default]
100 Default,
101 Left,
103 Center,
105 Right,
107 Justified,
109}
110
111impl From<alignment::Horizontal> for Alignment {
112 fn from(alignment: alignment::Horizontal) -> Self {
113 match alignment {
114 alignment::Horizontal::Left => Self::Left,
115 alignment::Horizontal::Center => Self::Center,
116 alignment::Horizontal::Right => Self::Right,
117 }
118 }
119}
120
121impl From<crate::Alignment> for Alignment {
122 fn from(alignment: crate::Alignment) -> Self {
123 match alignment {
124 crate::Alignment::Start => Self::Left,
125 crate::Alignment::Center => Self::Center,
126 crate::Alignment::End => Self::Right,
127 }
128 }
129}
130
131impl From<Alignment> for alignment::Horizontal {
132 fn from(alignment: Alignment) -> Self {
133 match alignment {
134 Alignment::Default | Alignment::Left | Alignment::Justified => {
135 alignment::Horizontal::Left
136 }
137 Alignment::Center => alignment::Horizontal::Center,
138 Alignment::Right => alignment::Horizontal::Right,
139 }
140 }
141}
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
145pub enum Shaping {
146 Auto,
155 Basic,
166 Advanced,
176}
177
178impl Default for Shaping {
179 fn default() -> Self {
180 if cfg!(feature = "advanced-shaping") {
181 Self::Advanced
182 } else if cfg!(feature = "basic-shaping") {
183 Self::Basic
184 } else {
185 Self::Auto
186 }
187 }
188}
189
190#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
192pub enum Wrapping {
193 None,
195 #[default]
199 Word,
200 Glyph,
202 WordOrGlyph,
204}
205
206#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
207pub enum Ellipsis {
209 #[default]
213 None,
214 Start,
216 Middle,
218 End,
220}
221
222#[derive(Debug, Clone, Copy, PartialEq)]
224pub enum LineHeight {
225 Relative(f32),
227
228 Absolute(Pixels),
230}
231
232impl LineHeight {
233 pub fn to_absolute(self, text_size: impl Into<Pixels>) -> Pixels {
235 match self {
236 Self::Relative(factor) => Pixels(factor * text_size.into().0),
237 Self::Absolute(pixels) => pixels,
238 }
239 }
240}
241
242impl Default for LineHeight {
243 fn default() -> Self {
244 Self::Relative(1.375)
245 }
246}
247
248impl From<f32> for LineHeight {
249 fn from(factor: f32) -> Self {
250 Self::Relative(factor)
251 }
252}
253
254impl From<Pixels> for LineHeight {
255 fn from(pixels: Pixels) -> Self {
256 Self::Absolute(pixels)
257 }
258}
259
260impl Hash for LineHeight {
261 fn hash<H: Hasher>(&self, state: &mut H) {
262 match self {
263 Self::Relative(factor) => {
264 state.write_u8(0);
265 factor.to_bits().hash(state);
266 }
267 Self::Absolute(pixels) => {
268 state.write_u8(1);
269 f32::from(*pixels).to_bits().hash(state);
270 }
271 }
272 }
273}
274
275#[derive(Debug, Clone, Copy, PartialEq)]
277pub struct Hit {
278 pub line: usize,
280 pub index: usize,
282}
283
284#[derive(Debug, Clone, Copy, PartialEq, Eq)]
291pub enum Difference {
292 None,
296
297 Bounds,
302
303 Shape,
309}
310
311pub trait Renderer: crate::Renderer {
313 type Paragraph: Paragraph + 'static;
315
316 type Editor: Editor + 'static;
318
319 const ICON_FONT: Font;
321
322 const CHECKMARK_ICON: char;
326
327 const ARROW_DOWN_ICON: char;
331
332 const SCROLL_UP_ICON: char;
336
337 const SCROLL_DOWN_ICON: char;
341
342 const SCROLL_LEFT_ICON: char;
346
347 const SCROLL_RIGHT_ICON: char;
351
352 const ICED_LOGO: char;
356
357 fn font(&self) -> Font {
359 self.settings().font
360 }
361
362 fn text_size(&self) -> Pixels {
364 self.settings().text_size
365 }
366
367 fn line_height(&self) -> LineHeight {
369 self.settings().line_height
370 }
371
372 fn fill_paragraph(
375 &mut self,
376 text: &Self::Paragraph,
377 position: Point,
378 color: Color,
379 clip_bounds: Rectangle,
380 );
381
382 fn fill_editor(
385 &mut self,
386 editor: &Self::Editor,
387 position: Point,
388 color: Color,
389 clip_bounds: Rectangle,
390 );
391
392 fn fill_text(
395 &mut self,
396 text: Text<String>,
397 position: Point,
398 color: Color,
399 clip_bounds: Rectangle,
400 );
401}
402
403#[derive(Debug, Clone)]
405pub struct Span<'a, Link = ()> {
406 pub text: Fragment<'a>,
408 pub size: Option<Pixels>,
410 pub line_height: Option<LineHeight>,
412 pub font: Option<Font>,
414 pub color: Option<Color>,
416 pub link: Option<Link>,
418 pub highlight: Option<Highlight>,
420 pub padding: Padding,
424 pub underline: bool,
426 pub strikethrough: bool,
428}
429
430#[derive(Debug, Clone, Copy, PartialEq)]
432pub struct Highlight {
433 pub background: Background,
435 pub border: Border,
437}
438
439impl<'a, Link> Span<'a, Link> {
440 pub fn new(fragment: impl IntoFragment<'a>) -> Self {
442 Self {
443 text: fragment.into_fragment(),
444 ..Self::default()
445 }
446 }
447
448 pub fn size(mut self, size: impl Into<Pixels>) -> Self {
450 self.size = Some(size.into());
451 self
452 }
453
454 pub fn line_height(mut self, line_height: impl Into<LineHeight>) -> Self {
456 self.line_height = Some(line_height.into());
457 self
458 }
459
460 pub fn font(mut self, font: impl Into<Font>) -> Self {
462 self.font = Some(font.into());
463 self
464 }
465
466 pub fn font_maybe(mut self, font: Option<impl Into<Font>>) -> Self {
468 self.font = font.map(Into::into);
469 self
470 }
471
472 pub fn color(mut self, color: impl Into<Color>) -> Self {
474 self.color = Some(color.into());
475 self
476 }
477
478 pub fn color_maybe(mut self, color: Option<impl Into<Color>>) -> Self {
480 self.color = color.map(Into::into);
481 self
482 }
483
484 pub fn link(mut self, link: impl Into<Link>) -> Self {
486 self.link = Some(link.into());
487 self
488 }
489
490 pub fn link_maybe(mut self, link: Option<impl Into<Link>>) -> Self {
492 self.link = link.map(Into::into);
493 self
494 }
495
496 pub fn background(self, background: impl Into<Background>) -> Self {
498 self.background_maybe(Some(background))
499 }
500
501 pub fn background_maybe(mut self, background: Option<impl Into<Background>>) -> Self {
503 let Some(background) = background else {
504 return self;
505 };
506
507 match &mut self.highlight {
508 Some(highlight) => {
509 highlight.background = background.into();
510 }
511 None => {
512 self.highlight = Some(Highlight {
513 background: background.into(),
514 border: Border::default(),
515 });
516 }
517 }
518
519 self
520 }
521
522 pub fn border(self, border: impl Into<Border>) -> Self {
524 self.border_maybe(Some(border))
525 }
526
527 pub fn border_maybe(mut self, border: Option<impl Into<Border>>) -> Self {
529 let Some(border) = border else {
530 return self;
531 };
532
533 match &mut self.highlight {
534 Some(highlight) => {
535 highlight.border = border.into();
536 }
537 None => {
538 self.highlight = Some(Highlight {
539 border: border.into(),
540 background: Background::Color(Color::TRANSPARENT),
541 });
542 }
543 }
544
545 self
546 }
547
548 pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
556 self.padding = padding.into();
557 self
558 }
559
560 pub fn underline(mut self, underline: bool) -> Self {
562 self.underline = underline;
563 self
564 }
565
566 pub fn strikethrough(mut self, strikethrough: bool) -> Self {
568 self.strikethrough = strikethrough;
569 self
570 }
571
572 pub fn to_static(self) -> Span<'static, Link> {
574 Span {
575 text: Cow::Owned(self.text.into_owned()),
576 size: self.size,
577 line_height: self.line_height,
578 font: self.font,
579 color: self.color,
580 link: self.link,
581 highlight: self.highlight,
582 padding: self.padding,
583 underline: self.underline,
584 strikethrough: self.strikethrough,
585 }
586 }
587}
588
589impl<Link> Default for Span<'_, Link> {
590 fn default() -> Self {
591 Self {
592 text: Cow::default(),
593 size: None,
594 line_height: None,
595 font: None,
596 color: None,
597 link: None,
598 highlight: None,
599 padding: Padding::default(),
600 underline: false,
601 strikethrough: false,
602 }
603 }
604}
605
606impl<'a, Link> From<&'a str> for Span<'a, Link> {
607 fn from(value: &'a str) -> Self {
608 Span::new(value)
609 }
610}
611
612impl<Link> PartialEq for Span<'_, Link> {
613 fn eq(&self, other: &Self) -> bool {
614 self.text == other.text
615 && self.size == other.size
616 && self.line_height == other.line_height
617 && self.font == other.font
618 && self.color == other.color
619 }
620}
621
622#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
624pub struct Position {
625 pub line: usize,
627
628 pub index: usize,
630}
631
632#[derive(Debug, Clone, Copy, PartialEq, Eq)]
642pub enum Target {
643 Character,
645 Word,
647 Line,
649}
650
651pub type Fragment<'a> = Cow<'a, str>;
656
657pub trait IntoFragment<'a> {
659 fn into_fragment(self) -> Fragment<'a>;
661}
662
663impl<'a> IntoFragment<'a> for Fragment<'a> {
664 fn into_fragment(self) -> Fragment<'a> {
665 self
666 }
667}
668
669impl<'a> IntoFragment<'a> for &'a Fragment<'_> {
670 fn into_fragment(self) -> Fragment<'a> {
671 Fragment::Borrowed(self)
672 }
673}
674
675impl<'a> IntoFragment<'a> for &'a str {
676 fn into_fragment(self) -> Fragment<'a> {
677 Fragment::Borrowed(self)
678 }
679}
680
681impl<'a> IntoFragment<'a> for &'a String {
682 fn into_fragment(self) -> Fragment<'a> {
683 Fragment::Borrowed(self.as_str())
684 }
685}
686
687impl<'a> IntoFragment<'a> for String {
688 fn into_fragment(self) -> Fragment<'a> {
689 Fragment::Owned(self)
690 }
691}
692
693macro_rules! into_fragment {
694 ($type:ty) => {
695 impl<'a> IntoFragment<'a> for $type {
696 fn into_fragment(self) -> Fragment<'a> {
697 Fragment::Owned(self.to_string())
698 }
699 }
700
701 impl<'a> IntoFragment<'a> for &$type {
702 fn into_fragment(self) -> Fragment<'a> {
703 Fragment::Owned(self.to_string())
704 }
705 }
706 };
707}
708
709into_fragment!(char);
710into_fragment!(bool);
711
712into_fragment!(u8);
713into_fragment!(u16);
714into_fragment!(u32);
715into_fragment!(u64);
716into_fragment!(u128);
717into_fragment!(usize);
718
719into_fragment!(i8);
720into_fragment!(i16);
721into_fragment!(i32);
722into_fragment!(i64);
723into_fragment!(i128);
724into_fragment!(isize);
725
726into_fragment!(f32);
727into_fragment!(f64);