Write documentation for the new text APIs

This commit is contained in:
Héctor Ramón Jiménez 2023-10-27 05:04:14 +02:00
parent 6582387579
commit 625cd745f3
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
16 changed files with 185 additions and 22 deletions

View file

@ -12,7 +12,7 @@
#![forbid(unsafe_code, rust_2018_idioms)] #![forbid(unsafe_code, rust_2018_idioms)]
#![deny( #![deny(
missing_debug_implementations, missing_debug_implementations,
// missing_docs, missing_docs,
unused_results, unused_results,
rustdoc::broken_intra_doc_links rustdoc::broken_intra_doc_links
)] )]

View file

@ -61,6 +61,7 @@ impl Click {
self.kind self.kind
} }
/// Returns the position of the [`Click`].
pub fn position(&self) -> Point { pub fn position(&self) -> Point {
self.position self.position
} }

View file

@ -204,6 +204,8 @@ pub trait Renderer: crate::Renderer {
color: Color, color: Color,
); );
/// Draws the given [`Editor`] at the given position and with the given
/// [`Color`].
fn fill_editor( fn fill_editor(
&mut self, &mut self,
editor: &Self::Editor, editor: &Self::Editor,

View file

@ -1,25 +1,36 @@
//! Edit text.
use crate::text::highlighter::{self, Highlighter}; use crate::text::highlighter::{self, Highlighter};
use crate::text::LineHeight; use crate::text::LineHeight;
use crate::{Pixels, Point, Rectangle, Size}; use crate::{Pixels, Point, Rectangle, Size};
use std::sync::Arc; use std::sync::Arc;
/// A component that can be used by widgets to edit multi-line text.
pub trait Editor: Sized + Default { pub trait Editor: Sized + Default {
/// The [`Font`] of the [`Editor`].
type Font: Copy + PartialEq + Default; type Font: Copy + PartialEq + Default;
/// Creates a new [`Editor`] laid out with the given text. /// Creates a new [`Editor`] laid out with the given text.
fn with_text(text: &str) -> Self; fn with_text(text: &str) -> Self;
/// Returns the current [`Cursor`] of the [`Editor`].
fn cursor(&self) -> Cursor; fn cursor(&self) -> Cursor;
/// Returns the current cursor position of the [`Editor`].
///
/// Line and column, respectively.
fn cursor_position(&self) -> (usize, usize); fn cursor_position(&self) -> (usize, usize);
/// Returns the current selected text of the [`Editor`].
fn selection(&self) -> Option<String>; fn selection(&self) -> Option<String>;
/// Returns the text of the given line in the [`Editor`], if it exists.
fn line(&self, index: usize) -> Option<&str>; fn line(&self, index: usize) -> Option<&str>;
/// Returns the amount of lines in the [`Editor`].
fn line_count(&self) -> usize; fn line_count(&self) -> usize;
/// Performs an [`Action`] on the [`Editor`].
fn perform(&mut self, action: Action); fn perform(&mut self, action: Action);
/// Returns the current boundaries of the [`Editor`]. /// Returns the current boundaries of the [`Editor`].
@ -35,6 +46,7 @@ pub trait Editor: Sized + Default {
new_highlighter: &mut impl Highlighter, new_highlighter: &mut impl Highlighter,
); );
/// Runs a text [`Highlighter`] in the [`Editor`].
fn highlight<H: Highlighter>( fn highlight<H: Highlighter>(
&mut self, &mut self,
font: Self::Font, font: Self::Font,
@ -43,50 +55,83 @@ pub trait Editor: Sized + Default {
); );
} }
/// An interaction with an [`Editor`].
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Action { pub enum Action {
/// Apply a [`Motion`].
Move(Motion), Move(Motion),
/// Select text with a given [`Motion`].
Select(Motion), Select(Motion),
/// Select the word at the current cursor.
SelectWord, SelectWord,
/// Select the line at the current cursor.
SelectLine, SelectLine,
/// Perform an [`Edit`].
Edit(Edit), Edit(Edit),
/// Click the [`Editor`] at the given [`Point`].
Click(Point), Click(Point),
/// Drag the mouse on the [`Editor`] to the given [`Point`].
Drag(Point), Drag(Point),
Scroll { lines: i32 }, /// Scroll the [`Editor`] a certain amount of lines.
Scroll {
/// The amount of lines to scroll.
lines: i32,
},
} }
impl Action { impl Action {
/// Returns whether the [`Action`] is an editing action.
pub fn is_edit(&self) -> bool { pub fn is_edit(&self) -> bool {
matches!(self, Self::Edit(_)) matches!(self, Self::Edit(_))
} }
} }
/// An action that edits text.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Edit { pub enum Edit {
/// Insert the given character.
Insert(char), Insert(char),
/// Paste the given text.
Paste(Arc<String>), Paste(Arc<String>),
/// Break the current line.
Enter, Enter,
/// Delete the previous character.
Backspace, Backspace,
/// Delete the next character.
Delete, Delete,
} }
/// A cursor movement.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum Motion { pub enum Motion {
/// Move left.
Left, Left,
/// Move right.
Right, Right,
/// Move up.
Up, Up,
/// Move down.
Down, Down,
/// Move to the left boundary of a word.
WordLeft, WordLeft,
/// Move to the right boundary of a word.
WordRight, WordRight,
/// Move to the start of the line.
Home, Home,
/// Move to the end of the line.
End, End,
/// Move to the start of the previous window.
PageUp, PageUp,
/// Move to the start of the next window.
PageDown, PageDown,
/// Move to the start of the text.
DocumentStart, DocumentStart,
/// Move to the end of the text.
DocumentEnd, DocumentEnd,
} }
impl Motion { impl Motion {
/// Widens the [`Motion`], if possible.
pub fn widen(self) -> Self { pub fn widen(self) -> Self {
match self { match self {
Self::Left => Self::WordLeft, Self::Left => Self::WordLeft,
@ -97,6 +142,7 @@ impl Motion {
} }
} }
/// Returns the [`Direction`] of the [`Motion`].
pub fn direction(&self) -> Direction { pub fn direction(&self) -> Direction {
match self { match self {
Self::Left Self::Left
@ -115,9 +161,12 @@ impl Motion {
} }
} }
/// A direction in some text.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction { pub enum Direction {
/// <-
Left, Left,
/// ->
Right, Right,
} }

View file

@ -1,31 +1,48 @@
//! Highlight text.
use crate::Color; use crate::Color;
use std::ops::Range; use std::ops::Range;
/// A type capable of highlighting text.
///
/// A [`Highlighter`] highlights lines in sequence. When a line changes,
/// it must be notified and the lines after the changed one must be fed
/// again to the [`Highlighter`].
pub trait Highlighter: 'static { pub trait Highlighter: 'static {
/// The settings to configure the [`Highlighter`].
type Settings: PartialEq + Clone; type Settings: PartialEq + Clone;
/// The output of the [`Highlighter`].
type Highlight; type Highlight;
/// The highlight iterator type.
type Iterator<'a>: Iterator<Item = (Range<usize>, Self::Highlight)> type Iterator<'a>: Iterator<Item = (Range<usize>, Self::Highlight)>
where where
Self: 'a; Self: 'a;
/// Creates a new [`Highlighter`] from its [`Self::Settings`].
fn new(settings: &Self::Settings) -> Self; fn new(settings: &Self::Settings) -> Self;
/// Updates the [`Highlighter`] with some new [`Self::Settings`].
fn update(&mut self, new_settings: &Self::Settings); fn update(&mut self, new_settings: &Self::Settings);
/// Notifies the [`Highlighter`] that the line at the given index has changed.
fn change_line(&mut self, line: usize); fn change_line(&mut self, line: usize);
/// Highlights the given line.
///
/// If a line changed prior to this, the first line provided here will be the
/// line that changed.
fn highlight_line(&mut self, line: &str) -> Self::Iterator<'_>; fn highlight_line(&mut self, line: &str) -> Self::Iterator<'_>;
/// Returns the current line of the [`Highlighter`].
///
/// If `change_line` has been called, this will normally be the least index
/// that changed.
fn current_line(&self) -> usize; fn current_line(&self) -> usize;
} }
#[derive(Debug, Clone, Copy)] /// A highlighter that highlights nothing.
pub struct Style {
pub color: Color,
}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct PlainText; pub struct PlainText;
@ -52,9 +69,12 @@ impl Highlighter for PlainText {
} }
} }
/// The format of some text.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct Format<Font> { pub struct Format<Font> {
/// The [`Color`] of the text.
pub color: Option<Color>, pub color: Option<Color>,
/// The `Font` of the text.
pub font: Option<Font>, pub font: Option<Font>,
} }

View file

@ -34,7 +34,7 @@ struct Editor {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum Message { enum Message {
Edit(text_editor::Action), ActionPerformed(text_editor::Action),
ThemeSelected(highlighter::Theme), ThemeSelected(highlighter::Theme),
NewFile, NewFile,
OpenFile, OpenFile,
@ -68,10 +68,10 @@ impl Application for Editor {
fn update(&mut self, message: Message) -> Command<Message> { fn update(&mut self, message: Message) -> Command<Message> {
match message { match message {
Message::Edit(action) => { Message::ActionPerformed(action) => {
self.is_dirty = self.is_dirty || action.is_edit(); self.is_dirty = self.is_dirty || action.is_edit();
self.content.edit(action); self.content.perform(action);
Command::none() Command::none()
} }
@ -103,7 +103,7 @@ impl Application for Editor {
if let Ok((path, contents)) = result { if let Ok((path, contents)) = result {
self.file = Some(path); self.file = Some(path);
self.content = text_editor::Content::with(&contents); self.content = text_editor::Content::with_text(&contents);
} }
Command::none() Command::none()
@ -191,7 +191,7 @@ impl Application for Editor {
column![ column![
controls, controls,
text_editor(&self.content) text_editor(&self.content)
.on_edit(Message::Edit) .on_action(Message::ActionPerformed)
.highlight::<Highlighter>( .highlight::<Highlighter>(
highlighter::Settings { highlighter::Settings {
theme: self.theme, theme: self.theme,

View file

@ -10,7 +10,7 @@
#![forbid(rust_2018_idioms)] #![forbid(rust_2018_idioms)]
#![deny( #![deny(
missing_debug_implementations, missing_debug_implementations,
//missing_docs, missing_docs,
unsafe_code, unsafe_code,
unused_results, unused_results,
rustdoc::broken_intra_doc_links rustdoc::broken_intra_doc_links

View file

@ -1,3 +1,4 @@
//! Draw text.
pub mod cache; pub mod cache;
pub mod editor; pub mod editor;
pub mod paragraph; pub mod paragraph;
@ -17,6 +18,7 @@ use once_cell::sync::OnceCell;
use std::borrow::Cow; use std::borrow::Cow;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
/// Returns the global [`FontSystem`].
pub fn font_system() -> &'static RwLock<FontSystem> { pub fn font_system() -> &'static RwLock<FontSystem> {
static FONT_SYSTEM: OnceCell<RwLock<FontSystem>> = OnceCell::new(); static FONT_SYSTEM: OnceCell<RwLock<FontSystem>> = OnceCell::new();
@ -32,6 +34,7 @@ pub fn font_system() -> &'static RwLock<FontSystem> {
}) })
} }
/// A set of system fonts.
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct FontSystem { pub struct FontSystem {
raw: cosmic_text::FontSystem, raw: cosmic_text::FontSystem,
@ -39,10 +42,12 @@ pub struct FontSystem {
} }
impl FontSystem { impl FontSystem {
/// Returns the raw [`cosmic_text::FontSystem`].
pub fn raw(&mut self) -> &mut cosmic_text::FontSystem { pub fn raw(&mut self) -> &mut cosmic_text::FontSystem {
&mut self.raw &mut self.raw
} }
/// Loads a font from its bytes.
pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) { pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
let _ = self.raw.db_mut().load_font_source( let _ = self.raw.db_mut().load_font_source(
cosmic_text::fontdb::Source::Binary(Arc::new(bytes.into_owned())), cosmic_text::fontdb::Source::Binary(Arc::new(bytes.into_owned())),
@ -51,14 +56,19 @@ impl FontSystem {
self.version = Version(self.version.0 + 1); self.version = Version(self.version.0 + 1);
} }
/// Returns the current [`Version`] of the [`FontSystem`].
///
/// Loading a font will increase the version of a [`FontSystem`].
pub fn version(&self) -> Version { pub fn version(&self) -> Version {
self.version self.version
} }
} }
/// A version number.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Version(u32); pub struct Version(u32);
/// Measures the dimensions of the given [`cosmic_text::Buffer`].
pub fn measure(buffer: &cosmic_text::Buffer) -> Size { pub fn measure(buffer: &cosmic_text::Buffer) -> Size {
let (width, total_lines) = buffer let (width, total_lines) = buffer
.layout_runs() .layout_runs()
@ -69,6 +79,7 @@ pub fn measure(buffer: &cosmic_text::Buffer) -> Size {
Size::new(width, total_lines as f32 * buffer.metrics().line_height) Size::new(width, total_lines as f32 * buffer.metrics().line_height)
} }
/// Returns the attributes of the given [`Font`].
pub fn to_attributes(font: Font) -> cosmic_text::Attrs<'static> { pub fn to_attributes(font: Font) -> cosmic_text::Attrs<'static> {
cosmic_text::Attrs::new() cosmic_text::Attrs::new()
.family(to_family(font.family)) .family(to_family(font.family))
@ -124,6 +135,7 @@ fn to_style(style: font::Style) -> cosmic_text::Style {
} }
} }
/// Converts some [`Shaping`] strategy to a [`cosmic_text::Shaping`] strategy.
pub fn to_shaping(shaping: Shaping) -> cosmic_text::Shaping { pub fn to_shaping(shaping: Shaping) -> cosmic_text::Shaping {
match shaping { match shaping {
Shaping::Basic => cosmic_text::Shaping::Basic, Shaping::Basic => cosmic_text::Shaping::Basic,
@ -131,6 +143,7 @@ pub fn to_shaping(shaping: Shaping) -> cosmic_text::Shaping {
} }
} }
/// Converts some [`Color`] to a [`cosmic_text::Color`].
pub fn to_color(color: Color) -> cosmic_text::Color { pub fn to_color(color: Color) -> cosmic_text::Color {
let [r, g, b, a] = color::pack(color).components(); let [r, g, b, a] = color::pack(color).components();

View file

@ -1,3 +1,4 @@
//! Cache text.
use crate::core::{Font, Size}; use crate::core::{Font, Size};
use crate::text; use crate::text;
@ -5,6 +6,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
use std::collections::hash_map; use std::collections::hash_map;
use std::hash::{BuildHasher, Hash, Hasher}; use std::hash::{BuildHasher, Hash, Hasher};
/// A store of recently used sections of text.
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
#[derive(Default)] #[derive(Default)]
pub struct Cache { pub struct Cache {
@ -21,14 +23,17 @@ type HashBuilder = twox_hash::RandomXxHashBuilder64;
type HashBuilder = std::hash::BuildHasherDefault<twox_hash::XxHash64>; type HashBuilder = std::hash::BuildHasherDefault<twox_hash::XxHash64>;
impl Cache { impl Cache {
/// Creates a new empty [`Cache`].
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self::default()
} }
/// Gets the text [`Entry`] with the given [`KeyHash`].
pub fn get(&self, key: &KeyHash) -> Option<&Entry> { pub fn get(&self, key: &KeyHash) -> Option<&Entry> {
self.entries.get(key) self.entries.get(key)
} }
/// Allocates a text [`Entry`] if it is not already present in the [`Cache`].
pub fn allocate( pub fn allocate(
&mut self, &mut self,
font_system: &mut cosmic_text::FontSystem, font_system: &mut cosmic_text::FontSystem,
@ -88,6 +93,9 @@ impl Cache {
(hash, self.entries.get_mut(&hash).unwrap()) (hash, self.entries.get_mut(&hash).unwrap())
} }
/// Trims the [`Cache`].
///
/// This will clear the sections of text that have not been used since the last `trim`.
pub fn trim(&mut self) { pub fn trim(&mut self) {
self.entries self.entries
.retain(|key, _| self.recently_used.contains(key)); .retain(|key, _| self.recently_used.contains(key));
@ -99,13 +107,20 @@ impl Cache {
} }
} }
/// A cache key representing a section of text.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Key<'a> { pub struct Key<'a> {
/// The content of the text.
pub content: &'a str, pub content: &'a str,
/// The size of the text.
pub size: f32, pub size: f32,
/// The line height of the text.
pub line_height: f32, pub line_height: f32,
/// The [`Font`] of the text.
pub font: Font, pub font: Font,
/// The bounds of the text.
pub bounds: Size, pub bounds: Size,
/// The shaping strategy of the text.
pub shaping: text::Shaping, pub shaping: text::Shaping,
} }
@ -123,10 +138,14 @@ impl Key<'_> {
} }
} }
/// The hash of a [`Key`].
pub type KeyHash = u64; pub type KeyHash = u64;
/// A cache entry.
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct Entry { pub struct Entry {
/// The buffer of text, ready for drawing.
pub buffer: cosmic_text::Buffer, pub buffer: cosmic_text::Buffer,
/// The minimum bounds of the text.
pub min_bounds: Size, pub min_bounds: Size,
} }

View file

@ -1,3 +1,4 @@
//! Draw and edit text.
use crate::core::text::editor::{ use crate::core::text::editor::{
self, Action, Cursor, Direction, Edit, Motion, self, Action, Cursor, Direction, Edit, Motion,
}; };
@ -11,6 +12,7 @@ use cosmic_text::Edit as _;
use std::fmt; use std::fmt;
use std::sync::{self, Arc}; use std::sync::{self, Arc};
/// A multi-line text editor.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Editor(Option<Arc<Internal>>); pub struct Editor(Option<Arc<Internal>>);
@ -23,14 +25,21 @@ struct Internal {
} }
impl Editor { impl Editor {
/// Creates a new empty [`Editor`].
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self::default()
} }
/// Returns the buffer of the [`Editor`].
pub fn buffer(&self) -> &cosmic_text::Buffer { pub fn buffer(&self) -> &cosmic_text::Buffer {
self.internal().editor.buffer() self.internal().editor.buffer()
} }
/// Creates a [`Weak`] reference to the [`Editor`].
///
/// This is useful to avoid cloning the [`Editor`] when
/// referential guarantees are unnecessary. For instance,
/// when creating a rendering tree.
pub fn downgrade(&self) -> Weak { pub fn downgrade(&self) -> Weak {
let editor = self.internal(); let editor = self.internal();
@ -662,13 +671,16 @@ impl fmt::Debug for Internal {
} }
} }
/// A weak reference to an [`Editor`].
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Weak { pub struct Weak {
raw: sync::Weak<Internal>, raw: sync::Weak<Internal>,
/// The bounds of the [`Editor`].
pub bounds: Size, pub bounds: Size,
} }
impl Weak { impl Weak {
/// Tries to update the reference into an [`Editor`].
pub fn upgrade(&self) -> Option<Editor> { pub fn upgrade(&self) -> Option<Editor> {
self.raw.upgrade().map(Some).map(Editor) self.raw.upgrade().map(Some).map(Editor)
} }

View file

@ -1,3 +1,4 @@
//! Draw paragraphs.
use crate::core; use crate::core;
use crate::core::alignment; use crate::core::alignment;
use crate::core::text::{Hit, LineHeight, Shaping, Text}; use crate::core::text::{Hit, LineHeight, Shaping, Text};
@ -7,6 +8,7 @@ use crate::text;
use std::fmt; use std::fmt;
use std::sync::{self, Arc}; use std::sync::{self, Arc};
/// A bunch of text.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct Paragraph(Option<Arc<Internal>>); pub struct Paragraph(Option<Arc<Internal>>);
@ -23,14 +25,21 @@ struct Internal {
} }
impl Paragraph { impl Paragraph {
/// Creates a new empty [`Paragraph`].
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self::default()
} }
/// Returns the buffer of the [`Paragraph`].
pub fn buffer(&self) -> &cosmic_text::Buffer { pub fn buffer(&self) -> &cosmic_text::Buffer {
&self.internal().buffer &self.internal().buffer
} }
/// Creates a [`Weak`] reference to the [`Paragraph`].
///
/// This is useful to avoid cloning the [`Editor`] when
/// referential guarantees are unnecessary. For instance,
/// when creating a rendering tree.
pub fn downgrade(&self) -> Weak { pub fn downgrade(&self) -> Weak {
let paragraph = self.internal(); let paragraph = self.internal();
@ -269,15 +278,20 @@ impl Default for Internal {
} }
} }
/// A weak reference to a [`Paragraph`].
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Weak { pub struct Weak {
raw: sync::Weak<Internal>, raw: sync::Weak<Internal>,
/// The minimum bounds of the [`Paragraph`].
pub min_bounds: Size, pub min_bounds: Size,
/// The horizontal alignment of the [`Paragraph`].
pub horizontal_alignment: alignment::Horizontal, pub horizontal_alignment: alignment::Horizontal,
/// The vertical alignment of the [`Paragraph`].
pub vertical_alignment: alignment::Vertical, pub vertical_alignment: alignment::Vertical,
} }
impl Weak { impl Weak {
/// Tries to update the reference into a [`Paragraph`].
pub fn upgrade(&self) -> Option<Paragraph> { pub fn upgrade(&self) -> Option<Paragraph> {
self.raw.upgrade().map(Some).map(Paragraph) self.raw.upgrade().map(Some).map(Paragraph)
} }

View file

@ -10,7 +10,7 @@
#![forbid(unsafe_code, rust_2018_idioms)] #![forbid(unsafe_code, rust_2018_idioms)]
#![deny( #![deny(
unused_results, unused_results,
// missing_docs, missing_docs,
unused_results, unused_results,
rustdoc::broken_intra_doc_links rustdoc::broken_intra_doc_links
)] )]

View file

@ -4,19 +4,24 @@ use crate::core::{Color, Font, Pixels, Point, Rectangle};
use crate::graphics::text::editor; use crate::graphics::text::editor;
use crate::graphics::text::paragraph; use crate::graphics::text::paragraph;
/// A paragraph of text. /// A text primitive.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Text<'a> { pub enum Text<'a> {
/// A paragraph.
#[allow(missing_docs)]
Paragraph { Paragraph {
paragraph: paragraph::Weak, paragraph: paragraph::Weak,
position: Point, position: Point,
color: Color, color: Color,
}, },
/// An editor.
#[allow(missing_docs)]
Editor { Editor {
editor: editor::Weak, editor: editor::Weak,
position: Point, position: Point,
color: Color, color: Color,
}, },
/// A cached text.
Cached(Cached<'a>), Cached(Cached<'a>),
} }

View file

@ -23,7 +23,7 @@
#![forbid(rust_2018_idioms)] #![forbid(rust_2018_idioms)]
#![deny( #![deny(
missing_debug_implementations, missing_debug_implementations,
//missing_docs, missing_docs,
unsafe_code, unsafe_code,
unused_results, unused_results,
rustdoc::broken_intra_doc_links rustdoc::broken_intra_doc_links

View file

@ -4,8 +4,8 @@
)] )]
#![forbid(unsafe_code, rust_2018_idioms)] #![forbid(unsafe_code, rust_2018_idioms)]
#![deny( #![deny(
// missing_debug_implementations, //missing_debug_implementations,
// missing_docs, missing_docs,
unused_results, unused_results,
rustdoc::broken_intra_doc_links rustdoc::broken_intra_doc_links
)] )]

View file

@ -1,3 +1,4 @@
//! Display a multi-line text input for text editing.
use crate::core::event::{self, Event}; use crate::core::event::{self, Event};
use crate::core::keyboard; use crate::core::keyboard;
use crate::core::layout::{self, Layout}; use crate::core::layout::{self, Layout};
@ -19,6 +20,7 @@ use std::sync::Arc;
pub use crate::style::text_editor::{Appearance, StyleSheet}; pub use crate::style::text_editor::{Appearance, StyleSheet};
pub use text::editor::{Action, Edit, Motion}; pub use text::editor::{Action, Edit, Motion};
/// A multi-line text input.
pub struct TextEditor<'a, Highlighter, Message, Renderer = crate::Renderer> pub struct TextEditor<'a, Highlighter, Message, Renderer = crate::Renderer>
where where
Highlighter: text::Highlighter, Highlighter: text::Highlighter,
@ -47,6 +49,7 @@ where
Renderer: text::Renderer, Renderer: text::Renderer,
Renderer::Theme: StyleSheet, Renderer::Theme: StyleSheet,
{ {
/// Creates new [`TextEditor`] with the given [`Content`].
pub fn new(content: &'a Content<Renderer>) -> Self { pub fn new(content: &'a Content<Renderer>) -> Self {
Self { Self {
content, content,
@ -73,21 +76,34 @@ where
Renderer: text::Renderer, Renderer: text::Renderer,
Renderer::Theme: StyleSheet, Renderer::Theme: StyleSheet,
{ {
pub fn on_edit(mut self, on_edit: impl Fn(Action) -> Message + 'a) -> Self { /// Sets the message that should be produced when some action is performed in
/// the [`TextEditor`].
///
/// If this method is not called, the [`TextEditor`] will be disabled.
pub fn on_action(
mut self,
on_edit: impl Fn(Action) -> Message + 'a,
) -> Self {
self.on_edit = Some(Box::new(on_edit)); self.on_edit = Some(Box::new(on_edit));
self self
} }
/// Sets the [`Font`] of the [`TextEditor`].
///
/// [`Font`]: text::Renderer::Font
pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self { pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
self.font = Some(font.into()); self.font = Some(font.into());
self self
} }
/// Sets the [`Padding`] of the [`TextEditor`].
pub fn padding(mut self, padding: impl Into<Padding>) -> Self { pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
self.padding = padding.into(); self.padding = padding.into();
self self
} }
/// Highlights the [`TextEditor`] with the given [`Highlighter`] and
/// a strategy to turn its highlights into some text format.
pub fn highlight<H: text::Highlighter>( pub fn highlight<H: text::Highlighter>(
self, self,
settings: H::Settings, settings: H::Settings,
@ -112,6 +128,7 @@ where
} }
} }
/// The content of a [`TextEditor`].
pub struct Content<R = crate::Renderer>(RefCell<Internal<R>>) pub struct Content<R = crate::Renderer>(RefCell<Internal<R>>)
where where
R: text::Renderer; R: text::Renderer;
@ -128,28 +145,33 @@ impl<R> Content<R>
where where
R: text::Renderer, R: text::Renderer,
{ {
/// Creates an empty [`Content`].
pub fn new() -> Self { pub fn new() -> Self {
Self::with("") Self::with_text("")
} }
pub fn with(text: &str) -> Self { /// Creates a [`Content`] with the given text.
pub fn with_text(text: &str) -> Self {
Self(RefCell::new(Internal { Self(RefCell::new(Internal {
editor: R::Editor::with_text(text), editor: R::Editor::with_text(text),
is_dirty: true, is_dirty: true,
})) }))
} }
pub fn edit(&mut self, action: Action) { /// Performs an [`Action`] on the [`Content`].
pub fn perform(&mut self, action: Action) {
let internal = self.0.get_mut(); let internal = self.0.get_mut();
internal.editor.perform(action); internal.editor.perform(action);
internal.is_dirty = true; internal.is_dirty = true;
} }
/// Returns the amount of lines of the [`Content`].
pub fn line_count(&self) -> usize { pub fn line_count(&self) -> usize {
self.0.borrow().editor.line_count() self.0.borrow().editor.line_count()
} }
/// Returns the text of the line at the given index, if it exists.
pub fn line( pub fn line(
&self, &self,
index: usize, index: usize,
@ -160,6 +182,7 @@ where
.ok() .ok()
} }
/// Returns an iterator of the text of the lines in the [`Content`].
pub fn lines( pub fn lines(
&self, &self,
) -> impl Iterator<Item = impl std::ops::Deref<Target = str> + '_> { ) -> impl Iterator<Item = impl std::ops::Deref<Target = str> + '_> {
@ -190,6 +213,9 @@ where
} }
} }
/// Returns the text of the [`Content`].
///
/// Lines are joined with `'\n'`.
pub fn text(&self) -> String { pub fn text(&self) -> String {
let mut text = self.lines().enumerate().fold( let mut text = self.lines().enumerate().fold(
String::new(), String::new(),
@ -211,10 +237,12 @@ where
text text
} }
/// Returns the selected text of the [`Content`].
pub fn selection(&self) -> Option<String> { pub fn selection(&self) -> Option<String> {
self.0.borrow().editor.selection() self.0.borrow().editor.selection()
} }
/// Returns the current cursor position of the [`Content`].
pub fn cursor_position(&self) -> (usize, usize) { pub fn cursor_position(&self) -> (usize, usize) {
self.0.borrow().editor.cursor_position() self.0.borrow().editor.cursor_position()
} }