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 enum Hit {
278 CharOffset(usize),
280}
281
282impl Hit {
283 pub fn cursor(self) -> usize {
285 match self {
286 Self::CharOffset(i) => i,
287 }
288 }
289}
290
291#[derive(Debug, Clone, Copy, PartialEq, Eq)]
298pub enum Difference {
299 None,
303
304 Bounds,
309
310 Shape,
316}
317
318pub trait Renderer: crate::Renderer {
320 type Paragraph: Paragraph + 'static;
322
323 type Editor: Editor + 'static;
325
326 const ICON_FONT: Font;
328
329 const CHECKMARK_ICON: char;
333
334 const ARROW_DOWN_ICON: char;
338
339 const SCROLL_UP_ICON: char;
343
344 const SCROLL_DOWN_ICON: char;
348
349 const SCROLL_LEFT_ICON: char;
353
354 const SCROLL_RIGHT_ICON: char;
358
359 const ICED_LOGO: char;
363
364 fn font(&self) -> Font {
366 self.settings().font
367 }
368
369 fn text_size(&self) -> Pixels {
371 self.settings().text_size
372 }
373
374 fn line_height(&self) -> LineHeight {
376 self.settings().line_height
377 }
378
379 fn fill_paragraph(
382 &mut self,
383 text: &Self::Paragraph,
384 position: Point,
385 color: Color,
386 clip_bounds: Rectangle,
387 );
388
389 fn fill_editor(
392 &mut self,
393 editor: &Self::Editor,
394 position: Point,
395 color: Color,
396 clip_bounds: Rectangle,
397 );
398
399 fn fill_text(
402 &mut self,
403 text: Text<String>,
404 position: Point,
405 color: Color,
406 clip_bounds: Rectangle,
407 );
408}
409
410#[derive(Debug, Clone)]
412pub struct Span<'a, Link = ()> {
413 pub text: Fragment<'a>,
415 pub size: Option<Pixels>,
417 pub line_height: Option<LineHeight>,
419 pub font: Option<Font>,
421 pub color: Option<Color>,
423 pub link: Option<Link>,
425 pub highlight: Option<Highlight>,
427 pub padding: Padding,
431 pub underline: bool,
433 pub strikethrough: bool,
435}
436
437#[derive(Debug, Clone, Copy, PartialEq)]
439pub struct Highlight {
440 pub background: Background,
442 pub border: Border,
444}
445
446impl<'a, Link> Span<'a, Link> {
447 pub fn new(fragment: impl IntoFragment<'a>) -> Self {
449 Self {
450 text: fragment.into_fragment(),
451 ..Self::default()
452 }
453 }
454
455 pub fn size(mut self, size: impl Into<Pixels>) -> Self {
457 self.size = Some(size.into());
458 self
459 }
460
461 pub fn line_height(mut self, line_height: impl Into<LineHeight>) -> Self {
463 self.line_height = Some(line_height.into());
464 self
465 }
466
467 pub fn font(mut self, font: impl Into<Font>) -> Self {
469 self.font = Some(font.into());
470 self
471 }
472
473 pub fn font_maybe(mut self, font: Option<impl Into<Font>>) -> Self {
475 self.font = font.map(Into::into);
476 self
477 }
478
479 pub fn color(mut self, color: impl Into<Color>) -> Self {
481 self.color = Some(color.into());
482 self
483 }
484
485 pub fn color_maybe(mut self, color: Option<impl Into<Color>>) -> Self {
487 self.color = color.map(Into::into);
488 self
489 }
490
491 pub fn link(mut self, link: impl Into<Link>) -> Self {
493 self.link = Some(link.into());
494 self
495 }
496
497 pub fn link_maybe(mut self, link: Option<impl Into<Link>>) -> Self {
499 self.link = link.map(Into::into);
500 self
501 }
502
503 pub fn background(self, background: impl Into<Background>) -> Self {
505 self.background_maybe(Some(background))
506 }
507
508 pub fn background_maybe(mut self, background: Option<impl Into<Background>>) -> Self {
510 let Some(background) = background else {
511 return self;
512 };
513
514 match &mut self.highlight {
515 Some(highlight) => {
516 highlight.background = background.into();
517 }
518 None => {
519 self.highlight = Some(Highlight {
520 background: background.into(),
521 border: Border::default(),
522 });
523 }
524 }
525
526 self
527 }
528
529 pub fn border(self, border: impl Into<Border>) -> Self {
531 self.border_maybe(Some(border))
532 }
533
534 pub fn border_maybe(mut self, border: Option<impl Into<Border>>) -> Self {
536 let Some(border) = border else {
537 return self;
538 };
539
540 match &mut self.highlight {
541 Some(highlight) => {
542 highlight.border = border.into();
543 }
544 None => {
545 self.highlight = Some(Highlight {
546 border: border.into(),
547 background: Background::Color(Color::TRANSPARENT),
548 });
549 }
550 }
551
552 self
553 }
554
555 pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
563 self.padding = padding.into();
564 self
565 }
566
567 pub fn underline(mut self, underline: bool) -> Self {
569 self.underline = underline;
570 self
571 }
572
573 pub fn strikethrough(mut self, strikethrough: bool) -> Self {
575 self.strikethrough = strikethrough;
576 self
577 }
578
579 pub fn to_static(self) -> Span<'static, Link> {
581 Span {
582 text: Cow::Owned(self.text.into_owned()),
583 size: self.size,
584 line_height: self.line_height,
585 font: self.font,
586 color: self.color,
587 link: self.link,
588 highlight: self.highlight,
589 padding: self.padding,
590 underline: self.underline,
591 strikethrough: self.strikethrough,
592 }
593 }
594}
595
596impl<Link> Default for Span<'_, Link> {
597 fn default() -> Self {
598 Self {
599 text: Cow::default(),
600 size: None,
601 line_height: None,
602 font: None,
603 color: None,
604 link: None,
605 highlight: None,
606 padding: Padding::default(),
607 underline: false,
608 strikethrough: false,
609 }
610 }
611}
612
613impl<'a, Link> From<&'a str> for Span<'a, Link> {
614 fn from(value: &'a str) -> Self {
615 Span::new(value)
616 }
617}
618
619impl<Link> PartialEq for Span<'_, Link> {
620 fn eq(&self, other: &Self) -> bool {
621 self.text == other.text
622 && self.size == other.size
623 && self.line_height == other.line_height
624 && self.font == other.font
625 && self.color == other.color
626 }
627}
628
629#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
631pub struct Position {
632 pub line: usize,
634
635 pub index: usize,
637}
638
639pub type Fragment<'a> = Cow<'a, str>;
644
645pub trait IntoFragment<'a> {
647 fn into_fragment(self) -> Fragment<'a>;
649}
650
651impl<'a> IntoFragment<'a> for Fragment<'a> {
652 fn into_fragment(self) -> Fragment<'a> {
653 self
654 }
655}
656
657impl<'a> IntoFragment<'a> for &'a Fragment<'_> {
658 fn into_fragment(self) -> Fragment<'a> {
659 Fragment::Borrowed(self)
660 }
661}
662
663impl<'a> IntoFragment<'a> for &'a str {
664 fn into_fragment(self) -> Fragment<'a> {
665 Fragment::Borrowed(self)
666 }
667}
668
669impl<'a> IntoFragment<'a> for &'a String {
670 fn into_fragment(self) -> Fragment<'a> {
671 Fragment::Borrowed(self.as_str())
672 }
673}
674
675impl<'a> IntoFragment<'a> for String {
676 fn into_fragment(self) -> Fragment<'a> {
677 Fragment::Owned(self)
678 }
679}
680
681macro_rules! into_fragment {
682 ($type:ty) => {
683 impl<'a> IntoFragment<'a> for $type {
684 fn into_fragment(self) -> Fragment<'a> {
685 Fragment::Owned(self.to_string())
686 }
687 }
688
689 impl<'a> IntoFragment<'a> for &$type {
690 fn into_fragment(self) -> Fragment<'a> {
691 Fragment::Owned(self.to_string())
692 }
693 }
694 };
695}
696
697into_fragment!(char);
698into_fragment!(bool);
699
700into_fragment!(u8);
701into_fragment!(u16);
702into_fragment!(u32);
703into_fragment!(u64);
704into_fragment!(u128);
705into_fragment!(usize);
706
707into_fragment!(i8);
708into_fragment!(i16);
709into_fragment!(i32);
710into_fragment!(i64);
711into_fragment!(i128);
712into_fragment!(isize);
713
714into_fragment!(f32);
715into_fragment!(f64);