diff --git a/crates/hooks/src/rope_editor.rs b/crates/hooks/src/rope_editor.rs index 1215c5af0..51a876368 100644 --- a/crates/hooks/src/rope_editor.rs +++ b/crates/hooks/src/rope_editor.rs @@ -10,6 +10,7 @@ use crate::{text_editor::*, EditableMode, EditorHistory, HistoryChange}; pub struct RopeEditor { pub(crate) rope: Rope, pub(crate) cursor: TextCursor, + pub(crate) identation: u8, pub(crate) mode: EditableMode, pub(crate) selected: Option<(usize, usize)>, pub(crate) clipboard: UseClipboard, @@ -27,6 +28,7 @@ impl RopeEditor { pub fn new( text: String, cursor: TextCursor, + identation: u8, mode: EditableMode, clipboard: UseClipboard, history: EditorHistory, @@ -34,6 +36,7 @@ impl RopeEditor { Self { rope: Rope::from_str(&text), cursor, + identation, selected: None, mode, clipboard, @@ -271,6 +274,10 @@ impl TextEditor for RopeEditor { fn redo(&mut self) -> Option { self.history.redo(&mut self.rope) } + + fn get_identation(&self) -> u8 { + self.identation + } } /// Iterator over text lines. diff --git a/crates/hooks/src/text_editor.rs b/crates/hooks/src/text_editor.rs index 39397fef1..d29456801 100644 --- a/crates/hooks/src/text_editor.rs +++ b/crates/hooks/src/text_editor.rs @@ -362,6 +362,15 @@ pub trait TextEditor { event.insert(TextEvent::TEXT_CHANGED); } + Key::Tab => { + // Inserts a tab + let text = " ".repeat(self.get_identation().into()); + let cursor_pos = self.cursor_pos(); + self.insert(&text, cursor_pos); + self.set_cursor_pos(cursor_pos + text.chars().count()); + + event.insert(TextEvent::TEXT_CHANGED); + } Key::Character(character) => { let meta_or_ctrl = if cfg!(target_os = "macos") { modifiers.meta() @@ -476,4 +485,6 @@ pub trait TextEditor { fn redo(&mut self) -> Option; fn get_selection_range(&self) -> Option<(usize, usize)>; + + fn get_identation(&self) -> u8; } diff --git a/crates/hooks/src/use_editable.rs b/crates/hooks/src/use_editable.rs index 8a305392d..f30c23c46 100644 --- a/crates/hooks/src/use_editable.rs +++ b/crates/hooks/src/use_editable.rs @@ -98,6 +98,7 @@ pub struct UseEditable { pub(crate) cursor_reference: Signal, pub(crate) dragging: Signal, pub(crate) platform: UsePlatform, + pub(crate) allow_tabs: bool, } impl UseEditable { @@ -162,32 +163,40 @@ impl UseEditable { None } EditableEvent::KeyDown(e) => { - if e.code == Code::ShiftLeft { - let dragging = &mut *self.dragging.write(); - match dragging { - TextDragging::FromCursorToPoint { - shift: shift_pressed, - .. - } => { - *shift_pressed = true; - } - TextDragging::None => { - *dragging = TextDragging::FromCursorToPoint { - shift: true, - clicked: false, - cursor: self.editor.peek().cursor_pos(), - dist: None, + match e.code { + // Handle dragging + Code::ShiftLeft => { + let dragging = &mut *self.dragging.write(); + match dragging { + TextDragging::FromCursorToPoint { + shift: shift_pressed, + .. + } => { + *shift_pressed = true; } + TextDragging::None => { + *dragging = TextDragging::FromCursorToPoint { + shift: true, + clicked: false, + cursor: self.editor.peek().cursor_pos(), + dist: None, + } + } + _ => {} + } + } + // Do not write Tabs + Code::Tab if !self.allow_tabs => {} + // Handle editing + _ => { + let event = self + .editor + .write() + .process_key(&e.key, &e.code, &e.modifiers); + if event.contains(TextEvent::TEXT_CHANGED) { + *self.dragging.write() = TextDragging::None; } - _ => {} } - } - let event = self - .editor - .write() - .process_key(&e.key, &e.code, &e.modifiers); - if event.contains(TextEvent::TEXT_CHANGED) { - *self.dragging.write() = TextDragging::None; } None @@ -226,6 +235,8 @@ impl UseEditable { pub struct EditableConfig { pub(crate) content: String, pub(crate) cursor: TextCursor, + pub(crate) identation: u8, + pub(crate) allow_tabs: bool, } impl EditableConfig { @@ -234,6 +245,8 @@ impl EditableConfig { Self { content, cursor: TextCursor::default(), + identation: 4, + allow_tabs: false, } } @@ -242,6 +255,18 @@ impl EditableConfig { self.cursor = TextCursor::new(pos); self } + + /// Specify a custom identation + pub fn with_identation(mut self, identation: u8) -> Self { + self.identation = identation; + self + } + + /// Specify whether you want to allow tabs to be inserted + pub fn with_allow_tabs(mut self, allow_tabs: bool) -> Self { + self.allow_tabs = allow_tabs; + self + } } /// Create a virtual text editor with it's own cursor and rope. @@ -255,6 +280,7 @@ pub fn use_editable(initializer: impl Fn() -> EditableConfig, mode: EditableMode let mut editor = Signal::new(RopeEditor::new( config.content, config.cursor, + config.identation, mode, clipboard, EditorHistory::new(), @@ -325,6 +351,7 @@ pub fn use_editable(initializer: impl Fn() -> EditableConfig, mode: EditableMode cursor_reference: Signal::new(cursor_reference.clone()), dragging, platform, + allow_tabs: config.allow_tabs, } }) } diff --git a/examples/simple_editor.rs b/examples/simple_editor.rs index 20c4b6163..10d886016 100644 --- a/examples/simple_editor.rs +++ b/examples/simple_editor.rs @@ -18,6 +18,7 @@ fn app() -> Element { .trim() .to_string(), ) + .with_allow_tabs(true) }, EditableMode::MultipleLinesSingleEditor, );