densa_paragraph/lib.rs
1//! Paragraph layout for densa: shaping, line breaking, bidi and alignment.
2//!
3//! [`TextSystem`] (fonts plus layout scratch space, one per application) turns text and a
4//! [`ParagraphStyle`], with optional [`Span`]s, into a [`Paragraph`]: shaped by parley,
5//! broken into lines at a maximum width and aligned. A paragraph hands its glyphs out as
6//! `densa_text::GlyphRun`s, which a renderer draws (densa-draw's `Canvas::draw_run`), so
7//! layout and rendering stay independent of each other.
8//!
9//! [`TextSelection`] adds what editing needs on top: hit-testing a point to a text position,
10//! caret and selection rectangles, and keyboard motions (by character, word and line, in
11//! visual order for mixed-direction text).
12//!
13//! [`TextEditor`] makes text editable: typing, deletion by character, word or line,
14//! input-method (IME) composition, and undo and redo, keeping a selection and a laid-out
15//! paragraph in step.
16//!
17//! Laying out allocates and happens when text, style or width change; drawing a laid-out
18//! paragraph, hit-testing and moving a selection or a caret do not allocate.
19//!
20//! # Fonts and scripts
21//!
22//! [`TextSystem::new`] draws the default `monospace` family with the bundled JetBrains Mono
23//! and every character it lacks (Korean, Japanese, Chinese, symbols, ...) with the operating
24//! system's fonts; [`TextSystem::bundled`] uses the bundled font alone, the same on every
25//! machine. Features:
26//!
27//! - `system-fonts` (default): find the operating system's fonts. On Linux, fontconfig is
28//! loaded at run time; without it, text falls back to the bundled font.
29//! - `complex-scripts`: dictionaries for word breaks in Chinese and Japanese and line breaks
30//! in Thai, Lao, Khmer and Burmese (several MB). Without it, a double-click in Japanese
31//! selects one character.
32//!
33//! ```
34//! use densa_paragraph::{ParagraphStyle, TextSystem};
35//!
36//! // `TextSystem::new()` also finds the system's fonts; `bundled()` is the same everywhere.
37//! let mut text = TextSystem::bundled()?;
38//! let style = ParagraphStyle::new(16.0).with_max_width(Some(120.0));
39//! let mut paragraph = text.paragraph("A paragraph wrapped at a width.", &style, &[])?;
40//! let wrapped = paragraph.lines().len();
41//! assert!(wrapped > 1);
42//!
43//! // A new width re-breaks the lines without shaping the text again.
44//! paragraph.reflow(None)?;
45//! assert_eq!(paragraph.lines().len(), 1);
46//! # Ok::<(), Box<dyn std::error::Error>>(())
47//! ```
48
49mod editor;
50mod history;
51mod paragraph;
52mod selection;
53mod style;
54mod system;
55
56pub use densa_core::{Color, Point, Rect};
57pub use editor::TextEditor;
58pub use paragraph::{LineBox, Paragraph};
59pub use selection::{Affinity, Motion, TextSelection};
60pub use style::{ParagraphError, ParagraphStyle, Span, TextAlign, MAX_FONT_PX, MAX_LINE_HEIGHT};
61pub use system::TextSystem;