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)]
134pub enum Shaping {
135 Auto,
144 Basic,
155 Advanced,
165}
166
167impl Default for Shaping {
168 fn default() -> Self {
169 if cfg!(feature = "advanced-shaping") {
170 Self::Advanced
171 } else if cfg!(feature = "basic-shaping") {
172 Self::Basic
173 } else {
174 Self::Auto
175 }
176 }
177}
178
179#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
181pub enum Wrapping {
182 None,
184 #[default]
188 Word,
189 Glyph,
191 WordOrGlyph,
193}
194
195#[derive(Debug, Clone, Copy, PartialEq)]
197pub enum LineHeight {
198 Relative(f32),
200
201 Absolute(Pixels),
203}
204
205impl LineHeight {
206 pub fn to_absolute(self, text_size: Pixels) -> Pixels {
208 match self {
209 Self::Relative(factor) => Pixels(factor * text_size.0),
210 Self::Absolute(pixels) => pixels,
211 }
212 }
213}
214
215impl Default for LineHeight {
216 fn default() -> Self {
217 Self::Relative(1.3)
218 }
219}
220
221impl From<f32> for LineHeight {
222 fn from(factor: f32) -> Self {
223 Self::Relative(factor)
224 }
225}
226
227impl From<Pixels> for LineHeight {
228 fn from(pixels: Pixels) -> Self {
229 Self::Absolute(pixels)
230 }
231}
232
233impl Hash for LineHeight {
234 fn hash<H: Hasher>(&self, state: &mut H) {
235 match self {
236 Self::Relative(factor) => {
237 state.write_u8(0);
238 factor.to_bits().hash(state);
239 }
240 Self::Absolute(pixels) => {
241 state.write_u8(1);
242 f32::from(*pixels).to_bits().hash(state);
243 }
244 }
245 }
246}
247
248#[derive(Debug, Clone, Copy, PartialEq)]
250pub enum Hit {
251 CharOffset(usize),
253}
254
255impl Hit {
256 pub fn cursor(self) -> usize {
258 match self {
259 Self::CharOffset(i) => i,
260 }
261 }
262}
263
264#[derive(Debug, Clone, Copy, PartialEq, Eq)]
271pub enum Difference {
272 None,
276
277 Bounds,
282
283 Shape,
289}
290
291pub trait Renderer: crate::Renderer {
293 type Font: Copy + PartialEq;
295
296 type Paragraph: Paragraph<Font = Self::Font> + 'static;
298
299 type Editor: Editor<Font = Self::Font> + 'static;
301
302 const ICON_FONT: Self::Font;
304
305 const CHECKMARK_ICON: char;
309
310 const ARROW_DOWN_ICON: char;
314
315 const SCROLL_UP_ICON: char;
319
320 const SCROLL_DOWN_ICON: char;
324
325 const SCROLL_LEFT_ICON: char;
329
330 const SCROLL_RIGHT_ICON: char;
334
335 const ICED_LOGO: char;
339
340 fn default_font(&self) -> Self::Font;
342
343 fn default_size(&self) -> Pixels;
345
346 fn fill_paragraph(
349 &mut self,
350 text: &Self::Paragraph,
351 position: Point,
352 color: Color,
353 clip_bounds: Rectangle,
354 );
355
356 fn fill_editor(
359 &mut self,
360 editor: &Self::Editor,
361 position: Point,
362 color: Color,
363 clip_bounds: Rectangle,
364 );
365
366 fn fill_text(
369 &mut self,
370 text: Text<String, Self::Font>,
371 position: Point,
372 color: Color,
373 clip_bounds: Rectangle,
374 );
375}
376
377#[derive(Debug, Clone)]
379pub struct Span<'a, Link = (), Font = crate::Font> {
380 pub text: Fragment<'a>,
382 pub size: Option<Pixels>,
384 pub line_height: Option<LineHeight>,
386 pub font: Option<Font>,
388 pub color: Option<Color>,
390 pub link: Option<Link>,
392 pub highlight: Option<Highlight>,
394 pub padding: Padding,
398 pub underline: bool,
400 pub strikethrough: bool,
402}
403
404#[derive(Debug, Clone, Copy, PartialEq)]
406pub struct Highlight {
407 pub background: Background,
409 pub border: Border,
411}
412
413impl<'a, Link, Font> Span<'a, Link, Font> {
414 pub fn new(fragment: impl IntoFragment<'a>) -> Self {
416 Self {
417 text: fragment.into_fragment(),
418 ..Self::default()
419 }
420 }
421
422 pub fn size(mut self, size: impl Into<Pixels>) -> Self {
424 self.size = Some(size.into());
425 self
426 }
427
428 pub fn line_height(mut self, line_height: impl Into<LineHeight>) -> Self {
430 self.line_height = Some(line_height.into());
431 self
432 }
433
434 pub fn font(mut self, font: impl Into<Font>) -> Self {
436 self.font = Some(font.into());
437 self
438 }
439
440 pub fn font_maybe(mut self, font: Option<impl Into<Font>>) -> Self {
442 self.font = font.map(Into::into);
443 self
444 }
445
446 pub fn color(mut self, color: impl Into<Color>) -> Self {
448 self.color = Some(color.into());
449 self
450 }
451
452 pub fn color_maybe(mut self, color: Option<impl Into<Color>>) -> Self {
454 self.color = color.map(Into::into);
455 self
456 }
457
458 pub fn link(mut self, link: impl Into<Link>) -> Self {
460 self.link = Some(link.into());
461 self
462 }
463
464 pub fn link_maybe(mut self, link: Option<impl Into<Link>>) -> Self {
466 self.link = link.map(Into::into);
467 self
468 }
469
470 pub fn background(self, background: impl Into<Background>) -> Self {
472 self.background_maybe(Some(background))
473 }
474
475 pub fn background_maybe(
477 mut self,
478 background: Option<impl Into<Background>>,
479 ) -> Self {
480 let Some(background) = background else {
481 return self;
482 };
483
484 match &mut self.highlight {
485 Some(highlight) => {
486 highlight.background = background.into();
487 }
488 None => {
489 self.highlight = Some(Highlight {
490 background: background.into(),
491 border: Border::default(),
492 });
493 }
494 }
495
496 self
497 }
498
499 pub fn border(self, border: impl Into<Border>) -> Self {
501 self.border_maybe(Some(border))
502 }
503
504 pub fn border_maybe(mut self, border: Option<impl Into<Border>>) -> Self {
506 let Some(border) = border else {
507 return self;
508 };
509
510 match &mut self.highlight {
511 Some(highlight) => {
512 highlight.border = border.into();
513 }
514 None => {
515 self.highlight = Some(Highlight {
516 border: border.into(),
517 background: Background::Color(Color::TRANSPARENT),
518 });
519 }
520 }
521
522 self
523 }
524
525 pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
533 self.padding = padding.into();
534 self
535 }
536
537 pub fn underline(mut self, underline: bool) -> Self {
539 self.underline = underline;
540 self
541 }
542
543 pub fn strikethrough(mut self, strikethrough: bool) -> Self {
545 self.strikethrough = strikethrough;
546 self
547 }
548
549 pub fn to_static(self) -> Span<'static, Link, Font> {
551 Span {
552 text: Cow::Owned(self.text.into_owned()),
553 size: self.size,
554 line_height: self.line_height,
555 font: self.font,
556 color: self.color,
557 link: self.link,
558 highlight: self.highlight,
559 padding: self.padding,
560 underline: self.underline,
561 strikethrough: self.strikethrough,
562 }
563 }
564}
565
566impl<Link, Font> Default for Span<'_, Link, Font> {
567 fn default() -> Self {
568 Self {
569 text: Cow::default(),
570 size: None,
571 line_height: None,
572 font: None,
573 color: None,
574 link: None,
575 highlight: None,
576 padding: Padding::default(),
577 underline: false,
578 strikethrough: false,
579 }
580 }
581}
582
583impl<'a, Link, Font> From<&'a str> for Span<'a, Link, Font> {
584 fn from(value: &'a str) -> Self {
585 Span::new(value)
586 }
587}
588
589impl<Link, Font: PartialEq> PartialEq for Span<'_, Link, Font> {
590 fn eq(&self, other: &Self) -> bool {
591 self.text == other.text
592 && self.size == other.size
593 && self.line_height == other.line_height
594 && self.font == other.font
595 && self.color == other.color
596 }
597}
598
599pub type Fragment<'a> = Cow<'a, str>;
604
605pub trait IntoFragment<'a> {
607 fn into_fragment(self) -> Fragment<'a>;
609}
610
611impl<'a> IntoFragment<'a> for Fragment<'a> {
612 fn into_fragment(self) -> Fragment<'a> {
613 self
614 }
615}
616
617impl<'a> IntoFragment<'a> for &'a Fragment<'_> {
618 fn into_fragment(self) -> Fragment<'a> {
619 Fragment::Borrowed(self)
620 }
621}
622
623impl<'a> IntoFragment<'a> for &'a str {
624 fn into_fragment(self) -> Fragment<'a> {
625 Fragment::Borrowed(self)
626 }
627}
628
629impl<'a> IntoFragment<'a> for &'a String {
630 fn into_fragment(self) -> Fragment<'a> {
631 Fragment::Borrowed(self.as_str())
632 }
633}
634
635impl<'a> IntoFragment<'a> for String {
636 fn into_fragment(self) -> Fragment<'a> {
637 Fragment::Owned(self)
638 }
639}
640
641macro_rules! into_fragment {
642 ($type:ty) => {
643 impl<'a> IntoFragment<'a> for $type {
644 fn into_fragment(self) -> Fragment<'a> {
645 Fragment::Owned(self.to_string())
646 }
647 }
648
649 impl<'a> IntoFragment<'a> for &$type {
650 fn into_fragment(self) -> Fragment<'a> {
651 Fragment::Owned(self.to_string())
652 }
653 }
654 };
655}
656
657into_fragment!(char);
658into_fragment!(bool);
659
660into_fragment!(u8);
661into_fragment!(u16);
662into_fragment!(u32);
663into_fragment!(u64);
664into_fragment!(u128);
665into_fragment!(usize);
666
667into_fragment!(i8);
668into_fragment!(i16);
669into_fragment!(i32);
670into_fragment!(i64);
671into_fragment!(i128);
672into_fragment!(isize);
673
674into_fragment!(f32);
675into_fragment!(f64);