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::{Background, Border, Color, Padding, Pixels, Point, Rectangle, Size};
12
13use std::borrow::Cow;
14use std::hash::{Hash, Hasher};
15
16#[derive(Debug, Clone, Copy)]
18pub struct Text<Content = String, Font = crate::Font> {
19 pub content: Content,
21
22 pub bounds: Size,
24
25 pub size: Pixels,
27
28 pub line_height: LineHeight,
30
31 pub font: Font,
33
34 pub align_x: Alignment,
36
37 pub align_y: alignment::Vertical,
39
40 pub shaping: Shaping,
42
43 pub wrapping: Wrapping,
45
46 pub hint_factor: Option<f32>,
55}
56
57impl<Content, Font> Text<Content, Font>
58where
59 Font: Copy,
60{
61 pub fn with_content<T>(&self, content: T) -> Text<T, Font> {
64 Text {
65 content,
66 bounds: self.bounds,
67 size: self.size,
68 line_height: self.line_height,
69 font: self.font,
70 align_x: self.align_x,
71 align_y: self.align_y,
72 shaping: self.shaping,
73 wrapping: self.wrapping,
74 hint_factor: self.hint_factor,
75 }
76 }
77}
78
79impl<Content, Font> Text<Content, Font>
80where
81 Content: AsRef<str>,
82 Font: Copy,
83{
84 pub fn as_ref(&self) -> Text<&str, Font> {
86 self.with_content(self.content.as_ref())
87 }
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
92pub enum Alignment {
93 #[default]
98 Default,
99 Left,
101 Center,
103 Right,
105 Justified,
107}
108
109impl From<alignment::Horizontal> for Alignment {
110 fn from(alignment: alignment::Horizontal) -> Self {
111 match alignment {
112 alignment::Horizontal::Left => Self::Left,
113 alignment::Horizontal::Center => Self::Center,
114 alignment::Horizontal::Right => Self::Right,
115 }
116 }
117}
118
119impl From<crate::Alignment> for Alignment {
120 fn from(alignment: crate::Alignment) -> Self {
121 match alignment {
122 crate::Alignment::Start => Self::Left,
123 crate::Alignment::Center => Self::Center,
124 crate::Alignment::End => Self::Right,
125 }
126 }
127}
128
129impl From<Alignment> for alignment::Horizontal {
130 fn from(alignment: Alignment) -> Self {
131 match alignment {
132 Alignment::Default | Alignment::Left | Alignment::Justified => {
133 alignment::Horizontal::Left
134 }
135 Alignment::Center => alignment::Horizontal::Center,
136 Alignment::Right => alignment::Horizontal::Right,
137 }
138 }
139}
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
143pub enum Shaping {
144 Auto,
153 Basic,
164 Advanced,
174}
175
176impl Default for Shaping {
177 fn default() -> Self {
178 if cfg!(feature = "advanced-shaping") {
179 Self::Advanced
180 } else if cfg!(feature = "basic-shaping") {
181 Self::Basic
182 } else {
183 Self::Auto
184 }
185 }
186}
187
188#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
190pub enum Wrapping {
191 None,
193 #[default]
197 Word,
198 Glyph,
200 WordOrGlyph,
202}
203
204#[derive(Debug, Clone, Copy, PartialEq)]
206pub enum LineHeight {
207 Relative(f32),
209
210 Absolute(Pixels),
212}
213
214impl LineHeight {
215 pub fn to_absolute(self, text_size: Pixels) -> Pixels {
217 match self {
218 Self::Relative(factor) => Pixels(factor * text_size.0),
219 Self::Absolute(pixels) => pixels,
220 }
221 }
222}
223
224impl Default for LineHeight {
225 fn default() -> Self {
226 Self::Relative(1.3)
227 }
228}
229
230impl From<f32> for LineHeight {
231 fn from(factor: f32) -> Self {
232 Self::Relative(factor)
233 }
234}
235
236impl From<Pixels> for LineHeight {
237 fn from(pixels: Pixels) -> Self {
238 Self::Absolute(pixels)
239 }
240}
241
242impl Hash for LineHeight {
243 fn hash<H: Hasher>(&self, state: &mut H) {
244 match self {
245 Self::Relative(factor) => {
246 state.write_u8(0);
247 factor.to_bits().hash(state);
248 }
249 Self::Absolute(pixels) => {
250 state.write_u8(1);
251 f32::from(*pixels).to_bits().hash(state);
252 }
253 }
254 }
255}
256
257#[derive(Debug, Clone, Copy, PartialEq)]
259pub enum Hit {
260 CharOffset(usize),
262}
263
264impl Hit {
265 pub fn cursor(self) -> usize {
267 match self {
268 Self::CharOffset(i) => i,
269 }
270 }
271}
272
273#[derive(Debug, Clone, Copy, PartialEq, Eq)]
280pub enum Difference {
281 None,
285
286 Bounds,
291
292 Shape,
298}
299
300pub trait Renderer: crate::Renderer {
302 type Font: Copy + PartialEq;
304
305 type Paragraph: Paragraph<Font = Self::Font> + 'static;
307
308 type Editor: Editor<Font = Self::Font> + 'static;
310
311 const ICON_FONT: Self::Font;
313
314 const CHECKMARK_ICON: char;
318
319 const ARROW_DOWN_ICON: char;
323
324 const SCROLL_UP_ICON: char;
328
329 const SCROLL_DOWN_ICON: char;
333
334 const SCROLL_LEFT_ICON: char;
338
339 const SCROLL_RIGHT_ICON: char;
343
344 const ICED_LOGO: char;
348
349 fn default_font(&self) -> Self::Font;
351
352 fn default_size(&self) -> Pixels;
354
355 fn fill_paragraph(
358 &mut self,
359 text: &Self::Paragraph,
360 position: Point,
361 color: Color,
362 clip_bounds: Rectangle,
363 );
364
365 fn fill_editor(
368 &mut self,
369 editor: &Self::Editor,
370 position: Point,
371 color: Color,
372 clip_bounds: Rectangle,
373 );
374
375 fn fill_text(
378 &mut self,
379 text: Text<String, Self::Font>,
380 position: Point,
381 color: Color,
382 clip_bounds: Rectangle,
383 );
384}
385
386#[derive(Debug, Clone)]
388pub struct Span<'a, Link = (), Font = crate::Font> {
389 pub text: Fragment<'a>,
391 pub size: Option<Pixels>,
393 pub line_height: Option<LineHeight>,
395 pub font: Option<Font>,
397 pub color: Option<Color>,
399 pub link: Option<Link>,
401 pub highlight: Option<Highlight>,
403 pub padding: Padding,
407 pub underline: bool,
409 pub strikethrough: bool,
411}
412
413#[derive(Debug, Clone, Copy, PartialEq)]
415pub struct Highlight {
416 pub background: Background,
418 pub border: Border,
420}
421
422impl<'a, Link, Font> Span<'a, Link, Font> {
423 pub fn new(fragment: impl IntoFragment<'a>) -> Self {
425 Self {
426 text: fragment.into_fragment(),
427 ..Self::default()
428 }
429 }
430
431 pub fn size(mut self, size: impl Into<Pixels>) -> Self {
433 self.size = Some(size.into());
434 self
435 }
436
437 pub fn line_height(mut self, line_height: impl Into<LineHeight>) -> Self {
439 self.line_height = Some(line_height.into());
440 self
441 }
442
443 pub fn font(mut self, font: impl Into<Font>) -> Self {
445 self.font = Some(font.into());
446 self
447 }
448
449 pub fn font_maybe(mut self, font: Option<impl Into<Font>>) -> Self {
451 self.font = font.map(Into::into);
452 self
453 }
454
455 pub fn color(mut self, color: impl Into<Color>) -> Self {
457 self.color = Some(color.into());
458 self
459 }
460
461 pub fn color_maybe(mut self, color: Option<impl Into<Color>>) -> Self {
463 self.color = color.map(Into::into);
464 self
465 }
466
467 pub fn link(mut self, link: impl Into<Link>) -> Self {
469 self.link = Some(link.into());
470 self
471 }
472
473 pub fn link_maybe(mut self, link: Option<impl Into<Link>>) -> Self {
475 self.link = link.map(Into::into);
476 self
477 }
478
479 pub fn background(self, background: impl Into<Background>) -> Self {
481 self.background_maybe(Some(background))
482 }
483
484 pub fn background_maybe(mut self, background: Option<impl Into<Background>>) -> Self {
486 let Some(background) = background else {
487 return self;
488 };
489
490 match &mut self.highlight {
491 Some(highlight) => {
492 highlight.background = background.into();
493 }
494 None => {
495 self.highlight = Some(Highlight {
496 background: background.into(),
497 border: Border::default(),
498 });
499 }
500 }
501
502 self
503 }
504
505 pub fn border(self, border: impl Into<Border>) -> Self {
507 self.border_maybe(Some(border))
508 }
509
510 pub fn border_maybe(mut self, border: Option<impl Into<Border>>) -> Self {
512 let Some(border) = border else {
513 return self;
514 };
515
516 match &mut self.highlight {
517 Some(highlight) => {
518 highlight.border = border.into();
519 }
520 None => {
521 self.highlight = Some(Highlight {
522 border: border.into(),
523 background: Background::Color(Color::TRANSPARENT),
524 });
525 }
526 }
527
528 self
529 }
530
531 pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
539 self.padding = padding.into();
540 self
541 }
542
543 pub fn underline(mut self, underline: bool) -> Self {
545 self.underline = underline;
546 self
547 }
548
549 pub fn strikethrough(mut self, strikethrough: bool) -> Self {
551 self.strikethrough = strikethrough;
552 self
553 }
554
555 pub fn to_static(self) -> Span<'static, Link, Font> {
557 Span {
558 text: Cow::Owned(self.text.into_owned()),
559 size: self.size,
560 line_height: self.line_height,
561 font: self.font,
562 color: self.color,
563 link: self.link,
564 highlight: self.highlight,
565 padding: self.padding,
566 underline: self.underline,
567 strikethrough: self.strikethrough,
568 }
569 }
570}
571
572impl<Link, Font> Default for Span<'_, Link, Font> {
573 fn default() -> Self {
574 Self {
575 text: Cow::default(),
576 size: None,
577 line_height: None,
578 font: None,
579 color: None,
580 link: None,
581 highlight: None,
582 padding: Padding::default(),
583 underline: false,
584 strikethrough: false,
585 }
586 }
587}
588
589impl<'a, Link, Font> From<&'a str> for Span<'a, Link, Font> {
590 fn from(value: &'a str) -> Self {
591 Span::new(value)
592 }
593}
594
595impl<Link, Font: PartialEq> PartialEq for Span<'_, Link, Font> {
596 fn eq(&self, other: &Self) -> bool {
597 self.text == other.text
598 && self.size == other.size
599 && self.line_height == other.line_height
600 && self.font == other.font
601 && self.color == other.color
602 }
603}
604
605pub type Fragment<'a> = Cow<'a, str>;
610
611pub trait IntoFragment<'a> {
613 fn into_fragment(self) -> Fragment<'a>;
615}
616
617impl<'a> IntoFragment<'a> for Fragment<'a> {
618 fn into_fragment(self) -> Fragment<'a> {
619 self
620 }
621}
622
623impl<'a> IntoFragment<'a> for &'a Fragment<'_> {
624 fn into_fragment(self) -> Fragment<'a> {
625 Fragment::Borrowed(self)
626 }
627}
628
629impl<'a> IntoFragment<'a> for &'a str {
630 fn into_fragment(self) -> Fragment<'a> {
631 Fragment::Borrowed(self)
632 }
633}
634
635impl<'a> IntoFragment<'a> for &'a String {
636 fn into_fragment(self) -> Fragment<'a> {
637 Fragment::Borrowed(self.as_str())
638 }
639}
640
641impl<'a> IntoFragment<'a> for String {
642 fn into_fragment(self) -> Fragment<'a> {
643 Fragment::Owned(self)
644 }
645}
646
647macro_rules! into_fragment {
648 ($type:ty) => {
649 impl<'a> IntoFragment<'a> for $type {
650 fn into_fragment(self) -> Fragment<'a> {
651 Fragment::Owned(self.to_string())
652 }
653 }
654
655 impl<'a> IntoFragment<'a> for &$type {
656 fn into_fragment(self) -> Fragment<'a> {
657 Fragment::Owned(self.to_string())
658 }
659 }
660 };
661}
662
663into_fragment!(char);
664into_fragment!(bool);
665
666into_fragment!(u8);
667into_fragment!(u16);
668into_fragment!(u32);
669into_fragment!(u64);
670into_fragment!(u128);
671into_fragment!(usize);
672
673into_fragment!(i8);
674into_fragment!(i16);
675into_fragment!(i32);
676into_fragment!(i64);
677into_fragment!(i128);
678into_fragment!(isize);
679
680into_fragment!(f32);
681into_fragment!(f64);