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 horizontal_alignment: alignment::Horizontal,
38
39 pub vertical_alignment: alignment::Vertical,
41
42 pub shaping: Shaping,
44
45 pub wrapping: Wrapping,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
51pub enum Shaping {
52 #[default]
62 Basic,
63 Advanced,
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
75pub enum Wrapping {
76 None,
78 #[default]
82 Word,
83 Glyph,
85 WordOrGlyph,
87}
88
89#[derive(Debug, Clone, Copy, PartialEq)]
91pub enum LineHeight {
92 Relative(f32),
94
95 Absolute(Pixels),
97}
98
99impl LineHeight {
100 pub fn to_absolute(self, text_size: Pixels) -> Pixels {
102 match self {
103 Self::Relative(factor) => Pixels(factor * text_size.0),
104 Self::Absolute(pixels) => pixels,
105 }
106 }
107}
108
109impl Default for LineHeight {
110 fn default() -> Self {
111 Self::Relative(1.3)
112 }
113}
114
115impl From<f32> for LineHeight {
116 fn from(factor: f32) -> Self {
117 Self::Relative(factor)
118 }
119}
120
121impl From<Pixels> for LineHeight {
122 fn from(pixels: Pixels) -> Self {
123 Self::Absolute(pixels)
124 }
125}
126
127impl Hash for LineHeight {
128 fn hash<H: Hasher>(&self, state: &mut H) {
129 match self {
130 Self::Relative(factor) => {
131 state.write_u8(0);
132 factor.to_bits().hash(state);
133 }
134 Self::Absolute(pixels) => {
135 state.write_u8(1);
136 f32::from(*pixels).to_bits().hash(state);
137 }
138 }
139 }
140}
141
142#[derive(Debug, Clone, Copy, PartialEq)]
144pub enum Hit {
145 CharOffset(usize),
147}
148
149impl Hit {
150 pub fn cursor(self) -> usize {
152 match self {
153 Self::CharOffset(i) => i,
154 }
155 }
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
165pub enum Difference {
166 None,
170
171 Bounds,
176
177 Shape,
183}
184
185pub trait Renderer: crate::Renderer {
187 type Font: Copy + PartialEq;
189
190 type Paragraph: Paragraph<Font = Self::Font> + 'static;
192
193 type Editor: Editor<Font = Self::Font> + 'static;
195
196 const ICON_FONT: Self::Font;
198
199 const CHECKMARK_ICON: char;
203
204 const ARROW_DOWN_ICON: char;
208
209 fn default_font(&self) -> Self::Font;
211
212 fn default_size(&self) -> Pixels;
214
215 fn fill_paragraph(
218 &mut self,
219 text: &Self::Paragraph,
220 position: Point,
221 color: Color,
222 clip_bounds: Rectangle,
223 );
224
225 fn fill_editor(
228 &mut self,
229 editor: &Self::Editor,
230 position: Point,
231 color: Color,
232 clip_bounds: Rectangle,
233 );
234
235 fn fill_text(
238 &mut self,
239 text: Text<String, Self::Font>,
240 position: Point,
241 color: Color,
242 clip_bounds: Rectangle,
243 );
244}
245
246#[derive(Debug, Clone)]
248pub struct Span<'a, Link = (), Font = crate::Font> {
249 pub text: Fragment<'a>,
251 pub size: Option<Pixels>,
253 pub line_height: Option<LineHeight>,
255 pub font: Option<Font>,
257 pub color: Option<Color>,
259 pub link: Option<Link>,
261 pub highlight: Option<Highlight>,
263 pub padding: Padding,
267 pub underline: bool,
269 pub strikethrough: bool,
271}
272
273#[derive(Debug, Clone, Copy, PartialEq)]
275pub struct Highlight {
276 pub background: Background,
278 pub border: Border,
280}
281
282impl<'a, Link, Font> Span<'a, Link, Font> {
283 pub fn new(fragment: impl IntoFragment<'a>) -> Self {
285 Self {
286 text: fragment.into_fragment(),
287 ..Self::default()
288 }
289 }
290
291 pub fn size(mut self, size: impl Into<Pixels>) -> Self {
293 self.size = Some(size.into());
294 self
295 }
296
297 pub fn line_height(mut self, line_height: impl Into<LineHeight>) -> Self {
299 self.line_height = Some(line_height.into());
300 self
301 }
302
303 pub fn font(mut self, font: impl Into<Font>) -> Self {
305 self.font = Some(font.into());
306 self
307 }
308
309 pub fn font_maybe(mut self, font: Option<impl Into<Font>>) -> Self {
311 self.font = font.map(Into::into);
312 self
313 }
314
315 pub fn color(mut self, color: impl Into<Color>) -> Self {
317 self.color = Some(color.into());
318 self
319 }
320
321 pub fn color_maybe(mut self, color: Option<impl Into<Color>>) -> Self {
323 self.color = color.map(Into::into);
324 self
325 }
326
327 pub fn link(mut self, link: impl Into<Link>) -> Self {
329 self.link = Some(link.into());
330 self
331 }
332
333 pub fn link_maybe(mut self, link: Option<impl Into<Link>>) -> Self {
335 self.link = link.map(Into::into);
336 self
337 }
338
339 pub fn background(self, background: impl Into<Background>) -> Self {
341 self.background_maybe(Some(background))
342 }
343
344 pub fn background_maybe(
346 mut self,
347 background: Option<impl Into<Background>>,
348 ) -> Self {
349 let Some(background) = background else {
350 return self;
351 };
352
353 match &mut self.highlight {
354 Some(highlight) => {
355 highlight.background = background.into();
356 }
357 None => {
358 self.highlight = Some(Highlight {
359 background: background.into(),
360 border: Border::default(),
361 });
362 }
363 }
364
365 self
366 }
367
368 pub fn border(self, border: impl Into<Border>) -> Self {
370 self.border_maybe(Some(border))
371 }
372
373 pub fn border_maybe(mut self, border: Option<impl Into<Border>>) -> Self {
375 let Some(border) = border else {
376 return self;
377 };
378
379 match &mut self.highlight {
380 Some(highlight) => {
381 highlight.border = border.into();
382 }
383 None => {
384 self.highlight = Some(Highlight {
385 border: border.into(),
386 background: Background::Color(Color::TRANSPARENT),
387 });
388 }
389 }
390
391 self
392 }
393
394 pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
402 self.padding = padding.into();
403 self
404 }
405
406 pub fn underline(mut self, underline: bool) -> Self {
408 self.underline = underline;
409 self
410 }
411
412 pub fn strikethrough(mut self, strikethrough: bool) -> Self {
414 self.strikethrough = strikethrough;
415 self
416 }
417
418 pub fn to_static(self) -> Span<'static, Link, Font> {
420 Span {
421 text: Cow::Owned(self.text.into_owned()),
422 size: self.size,
423 line_height: self.line_height,
424 font: self.font,
425 color: self.color,
426 link: self.link,
427 highlight: self.highlight,
428 padding: self.padding,
429 underline: self.underline,
430 strikethrough: self.strikethrough,
431 }
432 }
433}
434
435impl<Link, Font> Default for Span<'_, Link, Font> {
436 fn default() -> Self {
437 Self {
438 text: Cow::default(),
439 size: None,
440 line_height: None,
441 font: None,
442 color: None,
443 link: None,
444 highlight: None,
445 padding: Padding::default(),
446 underline: false,
447 strikethrough: false,
448 }
449 }
450}
451
452impl<'a, Link, Font> From<&'a str> for Span<'a, Link, Font> {
453 fn from(value: &'a str) -> Self {
454 Span::new(value)
455 }
456}
457
458impl<Link, Font: PartialEq> PartialEq for Span<'_, Link, Font> {
459 fn eq(&self, other: &Self) -> bool {
460 self.text == other.text
461 && self.size == other.size
462 && self.line_height == other.line_height
463 && self.font == other.font
464 && self.color == other.color
465 }
466}
467
468pub type Fragment<'a> = Cow<'a, str>;
473
474pub trait IntoFragment<'a> {
476 fn into_fragment(self) -> Fragment<'a>;
478}
479
480impl<'a> IntoFragment<'a> for Fragment<'a> {
481 fn into_fragment(self) -> Fragment<'a> {
482 self
483 }
484}
485
486impl<'a> IntoFragment<'a> for &'a Fragment<'_> {
487 fn into_fragment(self) -> Fragment<'a> {
488 Fragment::Borrowed(self)
489 }
490}
491
492impl<'a> IntoFragment<'a> for &'a str {
493 fn into_fragment(self) -> Fragment<'a> {
494 Fragment::Borrowed(self)
495 }
496}
497
498impl<'a> IntoFragment<'a> for &'a String {
499 fn into_fragment(self) -> Fragment<'a> {
500 Fragment::Borrowed(self.as_str())
501 }
502}
503
504impl<'a> IntoFragment<'a> for String {
505 fn into_fragment(self) -> Fragment<'a> {
506 Fragment::Owned(self)
507 }
508}
509
510macro_rules! into_fragment {
511 ($type:ty) => {
512 impl<'a> IntoFragment<'a> for $type {
513 fn into_fragment(self) -> Fragment<'a> {
514 Fragment::Owned(self.to_string())
515 }
516 }
517
518 impl<'a> IntoFragment<'a> for &$type {
519 fn into_fragment(self) -> Fragment<'a> {
520 Fragment::Owned(self.to_string())
521 }
522 }
523 };
524}
525
526into_fragment!(char);
527into_fragment!(bool);
528
529into_fragment!(u8);
530into_fragment!(u16);
531into_fragment!(u32);
532into_fragment!(u64);
533into_fragment!(u128);
534into_fragment!(usize);
535
536into_fragment!(i8);
537into_fragment!(i16);
538into_fragment!(i32);
539into_fragment!(i64);
540into_fragment!(i128);
541into_fragment!(isize);
542
543into_fragment!(f32);
544into_fragment!(f64);