Write documentation for the new text APIs
This commit is contained in:
parent
6582387579
commit
625cd745f3
16 changed files with 185 additions and 22 deletions
|
|
@ -12,7 +12,7 @@
|
|||
#![forbid(unsafe_code, rust_2018_idioms)]
|
||||
#![deny(
|
||||
missing_debug_implementations,
|
||||
// missing_docs,
|
||||
missing_docs,
|
||||
unused_results,
|
||||
rustdoc::broken_intra_doc_links
|
||||
)]
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ impl Click {
|
|||
self.kind
|
||||
}
|
||||
|
||||
/// Returns the position of the [`Click`].
|
||||
pub fn position(&self) -> Point {
|
||||
self.position
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,6 +204,8 @@ pub trait Renderer: crate::Renderer {
|
|||
color: Color,
|
||||
);
|
||||
|
||||
/// Draws the given [`Editor`] at the given position and with the given
|
||||
/// [`Color`].
|
||||
fn fill_editor(
|
||||
&mut self,
|
||||
editor: &Self::Editor,
|
||||
|
|
|
|||
|
|
@ -1,25 +1,36 @@
|
|||
//! Edit text.
|
||||
use crate::text::highlighter::{self, Highlighter};
|
||||
use crate::text::LineHeight;
|
||||
use crate::{Pixels, Point, Rectangle, Size};
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A component that can be used by widgets to edit multi-line text.
|
||||
pub trait Editor: Sized + Default {
|
||||
/// The [`Font`] of the [`Editor`].
|
||||
type Font: Copy + PartialEq + Default;
|
||||
|
||||
/// Creates a new [`Editor`] laid out with the given text.
|
||||
fn with_text(text: &str) -> Self;
|
||||
|
||||
/// Returns the current [`Cursor`] of the [`Editor`].
|
||||
fn cursor(&self) -> Cursor;
|
||||
|
||||
/// Returns the current cursor position of the [`Editor`].
|
||||
///
|
||||
/// Line and column, respectively.
|
||||
fn cursor_position(&self) -> (usize, usize);
|
||||
|
||||
/// Returns the current selected text of the [`Editor`].
|
||||
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>;
|
||||
|
||||
/// Returns the amount of lines in the [`Editor`].
|
||||
fn line_count(&self) -> usize;
|
||||
|
||||
/// Performs an [`Action`] on the [`Editor`].
|
||||
fn perform(&mut self, action: Action);
|
||||
|
||||
/// Returns the current boundaries of the [`Editor`].
|
||||
|
|
@ -35,6 +46,7 @@ pub trait Editor: Sized + Default {
|
|||
new_highlighter: &mut impl Highlighter,
|
||||
);
|
||||
|
||||
/// Runs a text [`Highlighter`] in the [`Editor`].
|
||||
fn highlight<H: Highlighter>(
|
||||
&mut self,
|
||||
font: Self::Font,
|
||||
|
|
@ -43,50 +55,83 @@ pub trait Editor: Sized + Default {
|
|||
);
|
||||
}
|
||||
|
||||
/// An interaction with an [`Editor`].
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Action {
|
||||
/// Apply a [`Motion`].
|
||||
Move(Motion),
|
||||
/// Select text with a given [`Motion`].
|
||||
Select(Motion),
|
||||
/// Select the word at the current cursor.
|
||||
SelectWord,
|
||||
/// Select the line at the current cursor.
|
||||
SelectLine,
|
||||
/// Perform an [`Edit`].
|
||||
Edit(Edit),
|
||||
/// Click the [`Editor`] at the given [`Point`].
|
||||
Click(Point),
|
||||
/// Drag the mouse on the [`Editor`] to the given [`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 {
|
||||
/// Returns whether the [`Action`] is an editing action.
|
||||
pub fn is_edit(&self) -> bool {
|
||||
matches!(self, Self::Edit(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// An action that edits text.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Edit {
|
||||
/// Insert the given character.
|
||||
Insert(char),
|
||||
/// Paste the given text.
|
||||
Paste(Arc<String>),
|
||||
/// Break the current line.
|
||||
Enter,
|
||||
/// Delete the previous character.
|
||||
Backspace,
|
||||
/// Delete the next character.
|
||||
Delete,
|
||||
}
|
||||
|
||||
/// A cursor movement.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Motion {
|
||||
/// Move left.
|
||||
Left,
|
||||
/// Move right.
|
||||
Right,
|
||||
/// Move up.
|
||||
Up,
|
||||
/// Move down.
|
||||
Down,
|
||||
/// Move to the left boundary of a word.
|
||||
WordLeft,
|
||||
/// Move to the right boundary of a word.
|
||||
WordRight,
|
||||
/// Move to the start of the line.
|
||||
Home,
|
||||
/// Move to the end of the line.
|
||||
End,
|
||||
/// Move to the start of the previous window.
|
||||
PageUp,
|
||||
/// Move to the start of the next window.
|
||||
PageDown,
|
||||
/// Move to the start of the text.
|
||||
DocumentStart,
|
||||
/// Move to the end of the text.
|
||||
DocumentEnd,
|
||||
}
|
||||
|
||||
impl Motion {
|
||||
/// Widens the [`Motion`], if possible.
|
||||
pub fn widen(self) -> Self {
|
||||
match self {
|
||||
Self::Left => Self::WordLeft,
|
||||
|
|
@ -97,6 +142,7 @@ impl Motion {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the [`Direction`] of the [`Motion`].
|
||||
pub fn direction(&self) -> Direction {
|
||||
match self {
|
||||
Self::Left
|
||||
|
|
@ -115,9 +161,12 @@ impl Motion {
|
|||
}
|
||||
}
|
||||
|
||||
/// A direction in some text.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Direction {
|
||||
/// <-
|
||||
Left,
|
||||
/// ->
|
||||
Right,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,31 +1,48 @@
|
|||
//! Highlight text.
|
||||
use crate::Color;
|
||||
|
||||
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 {
|
||||
/// The settings to configure the [`Highlighter`].
|
||||
type Settings: PartialEq + Clone;
|
||||
|
||||
/// The output of the [`Highlighter`].
|
||||
type Highlight;
|
||||
|
||||
/// The highlight iterator type.
|
||||
type Iterator<'a>: Iterator<Item = (Range<usize>, Self::Highlight)>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
/// Creates a new [`Highlighter`] from its [`Self::Settings`].
|
||||
fn new(settings: &Self::Settings) -> Self;
|
||||
|
||||
/// Updates the [`Highlighter`] with some new [`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);
|
||||
|
||||
/// 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<'_>;
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Style {
|
||||
pub color: Color,
|
||||
}
|
||||
|
||||
/// A highlighter that highlights nothing.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PlainText;
|
||||
|
||||
|
|
@ -52,9 +69,12 @@ impl Highlighter for PlainText {
|
|||
}
|
||||
}
|
||||
|
||||
/// The format of some text.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Format<Font> {
|
||||
/// The [`Color`] of the text.
|
||||
pub color: Option<Color>,
|
||||
/// The `Font` of the text.
|
||||
pub font: Option<Font>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ struct Editor {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Message {
|
||||
Edit(text_editor::Action),
|
||||
ActionPerformed(text_editor::Action),
|
||||
ThemeSelected(highlighter::Theme),
|
||||
NewFile,
|
||||
OpenFile,
|
||||
|
|
@ -68,10 +68,10 @@ impl Application for Editor {
|
|||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
match message {
|
||||
Message::Edit(action) => {
|
||||
Message::ActionPerformed(action) => {
|
||||
self.is_dirty = self.is_dirty || action.is_edit();
|
||||
|
||||
self.content.edit(action);
|
||||
self.content.perform(action);
|
||||
|
||||
Command::none()
|
||||
}
|
||||
|
|
@ -103,7 +103,7 @@ impl Application for Editor {
|
|||
|
||||
if let Ok((path, contents)) = result {
|
||||
self.file = Some(path);
|
||||
self.content = text_editor::Content::with(&contents);
|
||||
self.content = text_editor::Content::with_text(&contents);
|
||||
}
|
||||
|
||||
Command::none()
|
||||
|
|
@ -191,7 +191,7 @@ impl Application for Editor {
|
|||
column![
|
||||
controls,
|
||||
text_editor(&self.content)
|
||||
.on_edit(Message::Edit)
|
||||
.on_action(Message::ActionPerformed)
|
||||
.highlight::<Highlighter>(
|
||||
highlighter::Settings {
|
||||
theme: self.theme,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
#![forbid(rust_2018_idioms)]
|
||||
#![deny(
|
||||
missing_debug_implementations,
|
||||
//missing_docs,
|
||||
missing_docs,
|
||||
unsafe_code,
|
||||
unused_results,
|
||||
rustdoc::broken_intra_doc_links
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//! Draw text.
|
||||
pub mod cache;
|
||||
pub mod editor;
|
||||
pub mod paragraph;
|
||||
|
|
@ -17,6 +18,7 @@ use once_cell::sync::OnceCell;
|
|||
use std::borrow::Cow;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
/// Returns the global [`FontSystem`].
|
||||
pub fn font_system() -> &'static RwLock<FontSystem> {
|
||||
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)]
|
||||
pub struct FontSystem {
|
||||
raw: cosmic_text::FontSystem,
|
||||
|
|
@ -39,10 +42,12 @@ pub struct FontSystem {
|
|||
}
|
||||
|
||||
impl FontSystem {
|
||||
/// Returns the raw [`cosmic_text::FontSystem`].
|
||||
pub fn raw(&mut self) -> &mut cosmic_text::FontSystem {
|
||||
&mut self.raw
|
||||
}
|
||||
|
||||
/// Loads a font from its bytes.
|
||||
pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
|
||||
let _ = self.raw.db_mut().load_font_source(
|
||||
cosmic_text::fontdb::Source::Binary(Arc::new(bytes.into_owned())),
|
||||
|
|
@ -51,14 +56,19 @@ impl FontSystem {
|
|||
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 {
|
||||
self.version
|
||||
}
|
||||
}
|
||||
|
||||
/// A version number.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct Version(u32);
|
||||
|
||||
/// Measures the dimensions of the given [`cosmic_text::Buffer`].
|
||||
pub fn measure(buffer: &cosmic_text::Buffer) -> Size {
|
||||
let (width, total_lines) = buffer
|
||||
.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)
|
||||
}
|
||||
|
||||
/// Returns the attributes of the given [`Font`].
|
||||
pub fn to_attributes(font: Font) -> cosmic_text::Attrs<'static> {
|
||||
cosmic_text::Attrs::new()
|
||||
.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 {
|
||||
match shaping {
|
||||
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 {
|
||||
let [r, g, b, a] = color::pack(color).components();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//! Cache text.
|
||||
use crate::core::{Font, Size};
|
||||
use crate::text;
|
||||
|
||||
|
|
@ -5,6 +6,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
|||
use std::collections::hash_map;
|
||||
use std::hash::{BuildHasher, Hash, Hasher};
|
||||
|
||||
/// A store of recently used sections of text.
|
||||
#[allow(missing_debug_implementations)]
|
||||
#[derive(Default)]
|
||||
pub struct Cache {
|
||||
|
|
@ -21,14 +23,17 @@ type HashBuilder = twox_hash::RandomXxHashBuilder64;
|
|||
type HashBuilder = std::hash::BuildHasherDefault<twox_hash::XxHash64>;
|
||||
|
||||
impl Cache {
|
||||
/// Creates a new empty [`Cache`].
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Gets the text [`Entry`] with the given [`KeyHash`].
|
||||
pub fn get(&self, key: &KeyHash) -> Option<&Entry> {
|
||||
self.entries.get(key)
|
||||
}
|
||||
|
||||
/// Allocates a text [`Entry`] if it is not already present in the [`Cache`].
|
||||
pub fn allocate(
|
||||
&mut self,
|
||||
font_system: &mut cosmic_text::FontSystem,
|
||||
|
|
@ -88,6 +93,9 @@ impl Cache {
|
|||
(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) {
|
||||
self.entries
|
||||
.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)]
|
||||
pub struct Key<'a> {
|
||||
/// The content of the text.
|
||||
pub content: &'a str,
|
||||
/// The size of the text.
|
||||
pub size: f32,
|
||||
/// The line height of the text.
|
||||
pub line_height: f32,
|
||||
/// The [`Font`] of the text.
|
||||
pub font: Font,
|
||||
/// The bounds of the text.
|
||||
pub bounds: Size,
|
||||
/// The shaping strategy of the text.
|
||||
pub shaping: text::Shaping,
|
||||
}
|
||||
|
||||
|
|
@ -123,10 +138,14 @@ impl Key<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The hash of a [`Key`].
|
||||
pub type KeyHash = u64;
|
||||
|
||||
/// A cache entry.
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Entry {
|
||||
/// The buffer of text, ready for drawing.
|
||||
pub buffer: cosmic_text::Buffer,
|
||||
/// The minimum bounds of the text.
|
||||
pub min_bounds: Size,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//! Draw and edit text.
|
||||
use crate::core::text::editor::{
|
||||
self, Action, Cursor, Direction, Edit, Motion,
|
||||
};
|
||||
|
|
@ -11,6 +12,7 @@ use cosmic_text::Edit as _;
|
|||
use std::fmt;
|
||||
use std::sync::{self, Arc};
|
||||
|
||||
/// A multi-line text editor.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Editor(Option<Arc<Internal>>);
|
||||
|
||||
|
|
@ -23,14 +25,21 @@ struct Internal {
|
|||
}
|
||||
|
||||
impl Editor {
|
||||
/// Creates a new empty [`Editor`].
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Returns the buffer of the [`Editor`].
|
||||
pub fn buffer(&self) -> &cosmic_text::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 {
|
||||
let editor = self.internal();
|
||||
|
||||
|
|
@ -662,13 +671,16 @@ impl fmt::Debug for Internal {
|
|||
}
|
||||
}
|
||||
|
||||
/// A weak reference to an [`Editor`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Weak {
|
||||
raw: sync::Weak<Internal>,
|
||||
/// The bounds of the [`Editor`].
|
||||
pub bounds: Size,
|
||||
}
|
||||
|
||||
impl Weak {
|
||||
/// Tries to update the reference into an [`Editor`].
|
||||
pub fn upgrade(&self) -> Option<Editor> {
|
||||
self.raw.upgrade().map(Some).map(Editor)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//! Draw paragraphs.
|
||||
use crate::core;
|
||||
use crate::core::alignment;
|
||||
use crate::core::text::{Hit, LineHeight, Shaping, Text};
|
||||
|
|
@ -7,6 +8,7 @@ use crate::text;
|
|||
use std::fmt;
|
||||
use std::sync::{self, Arc};
|
||||
|
||||
/// A bunch of text.
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Paragraph(Option<Arc<Internal>>);
|
||||
|
||||
|
|
@ -23,14 +25,21 @@ struct Internal {
|
|||
}
|
||||
|
||||
impl Paragraph {
|
||||
/// Creates a new empty [`Paragraph`].
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Returns the buffer of the [`Paragraph`].
|
||||
pub fn buffer(&self) -> &cosmic_text::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 {
|
||||
let paragraph = self.internal();
|
||||
|
||||
|
|
@ -269,15 +278,20 @@ impl Default for Internal {
|
|||
}
|
||||
}
|
||||
|
||||
/// A weak reference to a [`Paragraph`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Weak {
|
||||
raw: sync::Weak<Internal>,
|
||||
/// The minimum bounds of the [`Paragraph`].
|
||||
pub min_bounds: Size,
|
||||
/// The horizontal alignment of the [`Paragraph`].
|
||||
pub horizontal_alignment: alignment::Horizontal,
|
||||
/// The vertical alignment of the [`Paragraph`].
|
||||
pub vertical_alignment: alignment::Vertical,
|
||||
}
|
||||
|
||||
impl Weak {
|
||||
/// Tries to update the reference into a [`Paragraph`].
|
||||
pub fn upgrade(&self) -> Option<Paragraph> {
|
||||
self.raw.upgrade().map(Some).map(Paragraph)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
#![forbid(unsafe_code, rust_2018_idioms)]
|
||||
#![deny(
|
||||
unused_results,
|
||||
// missing_docs,
|
||||
missing_docs,
|
||||
unused_results,
|
||||
rustdoc::broken_intra_doc_links
|
||||
)]
|
||||
|
|
|
|||
|
|
@ -4,19 +4,24 @@ use crate::core::{Color, Font, Pixels, Point, Rectangle};
|
|||
use crate::graphics::text::editor;
|
||||
use crate::graphics::text::paragraph;
|
||||
|
||||
/// A paragraph of text.
|
||||
/// A text primitive.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Text<'a> {
|
||||
/// A paragraph.
|
||||
#[allow(missing_docs)]
|
||||
Paragraph {
|
||||
paragraph: paragraph::Weak,
|
||||
position: Point,
|
||||
color: Color,
|
||||
},
|
||||
/// An editor.
|
||||
#[allow(missing_docs)]
|
||||
Editor {
|
||||
editor: editor::Weak,
|
||||
position: Point,
|
||||
color: Color,
|
||||
},
|
||||
/// A cached text.
|
||||
Cached(Cached<'a>),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
#![forbid(rust_2018_idioms)]
|
||||
#![deny(
|
||||
missing_debug_implementations,
|
||||
//missing_docs,
|
||||
missing_docs,
|
||||
unsafe_code,
|
||||
unused_results,
|
||||
rustdoc::broken_intra_doc_links
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
)]
|
||||
#![forbid(unsafe_code, rust_2018_idioms)]
|
||||
#![deny(
|
||||
// missing_debug_implementations,
|
||||
// missing_docs,
|
||||
//missing_debug_implementations,
|
||||
missing_docs,
|
||||
unused_results,
|
||||
rustdoc::broken_intra_doc_links
|
||||
)]
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//! Display a multi-line text input for text editing.
|
||||
use crate::core::event::{self, Event};
|
||||
use crate::core::keyboard;
|
||||
use crate::core::layout::{self, Layout};
|
||||
|
|
@ -19,6 +20,7 @@ use std::sync::Arc;
|
|||
pub use crate::style::text_editor::{Appearance, StyleSheet};
|
||||
pub use text::editor::{Action, Edit, Motion};
|
||||
|
||||
/// A multi-line text input.
|
||||
pub struct TextEditor<'a, Highlighter, Message, Renderer = crate::Renderer>
|
||||
where
|
||||
Highlighter: text::Highlighter,
|
||||
|
|
@ -47,6 +49,7 @@ where
|
|||
Renderer: text::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
/// Creates new [`TextEditor`] with the given [`Content`].
|
||||
pub fn new(content: &'a Content<Renderer>) -> Self {
|
||||
Self {
|
||||
content,
|
||||
|
|
@ -73,21 +76,34 @@ where
|
|||
Renderer: text::Renderer,
|
||||
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
|
||||
}
|
||||
|
||||
/// Sets the [`Font`] of the [`TextEditor`].
|
||||
///
|
||||
/// [`Font`]: text::Renderer::Font
|
||||
pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
|
||||
self.font = Some(font.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the [`Padding`] of the [`TextEditor`].
|
||||
pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
|
||||
self.padding = padding.into();
|
||||
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>(
|
||||
self,
|
||||
settings: H::Settings,
|
||||
|
|
@ -112,6 +128,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// The content of a [`TextEditor`].
|
||||
pub struct Content<R = crate::Renderer>(RefCell<Internal<R>>)
|
||||
where
|
||||
R: text::Renderer;
|
||||
|
|
@ -128,28 +145,33 @@ impl<R> Content<R>
|
|||
where
|
||||
R: text::Renderer,
|
||||
{
|
||||
/// Creates an empty [`Content`].
|
||||
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 {
|
||||
editor: R::Editor::with_text(text),
|
||||
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();
|
||||
|
||||
internal.editor.perform(action);
|
||||
internal.is_dirty = true;
|
||||
}
|
||||
|
||||
/// Returns the amount of lines of the [`Content`].
|
||||
pub fn line_count(&self) -> usize {
|
||||
self.0.borrow().editor.line_count()
|
||||
}
|
||||
|
||||
/// Returns the text of the line at the given index, if it exists.
|
||||
pub fn line(
|
||||
&self,
|
||||
index: usize,
|
||||
|
|
@ -160,6 +182,7 @@ where
|
|||
.ok()
|
||||
}
|
||||
|
||||
/// Returns an iterator of the text of the lines in the [`Content`].
|
||||
pub fn lines(
|
||||
&self,
|
||||
) -> 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 {
|
||||
let mut text = self.lines().enumerate().fold(
|
||||
String::new(),
|
||||
|
|
@ -211,10 +237,12 @@ where
|
|||
text
|
||||
}
|
||||
|
||||
/// Returns the selected text of the [`Content`].
|
||||
pub fn selection(&self) -> Option<String> {
|
||||
self.0.borrow().editor.selection()
|
||||
}
|
||||
|
||||
/// Returns the current cursor position of the [`Content`].
|
||||
pub fn cursor_position(&self) -> (usize, usize) {
|
||||
self.0.borrow().editor.cursor_position()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue