Introduce Motion concept in core::text::editor
This commit is contained in:
parent
52b36a9574
commit
f4c51a96d5
3 changed files with 157 additions and 66 deletions
|
|
@ -40,14 +40,8 @@ pub trait Editor: Sized + Default {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
MoveLeft,
|
Move(Motion),
|
||||||
MoveRight,
|
Select(Motion),
|
||||||
MoveUp,
|
|
||||||
MoveDown,
|
|
||||||
MoveLeftWord,
|
|
||||||
MoveRightWord,
|
|
||||||
MoveHome,
|
|
||||||
MoveEnd,
|
|
||||||
SelectWord,
|
SelectWord,
|
||||||
SelectLine,
|
SelectLine,
|
||||||
Insert(char),
|
Insert(char),
|
||||||
|
|
@ -58,6 +52,34 @@ pub enum Action {
|
||||||
Drag(Point),
|
Drag(Point),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum Motion {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
WordLeft,
|
||||||
|
WordRight,
|
||||||
|
Home,
|
||||||
|
End,
|
||||||
|
PageUp,
|
||||||
|
PageDown,
|
||||||
|
DocumentStart,
|
||||||
|
DocumentEnd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Motion {
|
||||||
|
pub fn widen(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Left => Self::WordLeft,
|
||||||
|
Self::Right => Self::WordRight,
|
||||||
|
Self::Home => Self::DocumentStart,
|
||||||
|
Self::End => Self::DocumentEnd,
|
||||||
|
_ => self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The cursor of an [`Editor`].
|
/// The cursor of an [`Editor`].
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Cursor {
|
pub enum Cursor {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::text::editor::{self, Action, Cursor};
|
use crate::core::text::editor::{self, Action, Cursor, Motion};
|
||||||
use crate::core::text::LineHeight;
|
use crate::core::text::LineHeight;
|
||||||
use crate::core::{Font, Pixels, Point, Rectangle, Size, Vector};
|
use crate::core::{Font, Pixels, Point, Rectangle, Size, Vector};
|
||||||
use crate::text;
|
use crate::text;
|
||||||
|
|
@ -76,10 +76,7 @@ impl editor::Editor for Editor {
|
||||||
let buffer = internal.editor.buffer();
|
let buffer = internal.editor.buffer();
|
||||||
|
|
||||||
match internal.editor.select_opt() {
|
match internal.editor.select_opt() {
|
||||||
Some(selection)
|
Some(selection) => {
|
||||||
if cursor.line != selection.line
|
|
||||||
|| cursor.index != selection.index =>
|
|
||||||
{
|
|
||||||
let line_height = buffer.metrics().line_height;
|
let line_height = buffer.metrics().line_height;
|
||||||
let scroll_offset = buffer.scroll() as f32 * line_height;
|
let scroll_offset = buffer.scroll() as f32 * line_height;
|
||||||
|
|
||||||
|
|
@ -236,26 +233,87 @@ impl editor::Editor for Editor {
|
||||||
|
|
||||||
let editor = &mut internal.editor;
|
let editor = &mut internal.editor;
|
||||||
|
|
||||||
let mut act = |action| editor.action(font_system.raw(), action);
|
|
||||||
|
|
||||||
match action {
|
match action {
|
||||||
Action::MoveLeft => act(cosmic_text::Action::Left),
|
// Motion events
|
||||||
Action::MoveRight => act(cosmic_text::Action::Right),
|
Action::Move(motion) => {
|
||||||
Action::MoveUp => act(cosmic_text::Action::Up),
|
if let Some(_selection) = editor.select_opt() {
|
||||||
Action::MoveDown => act(cosmic_text::Action::Down),
|
editor.set_select_opt(None);
|
||||||
Action::Insert(c) => act(cosmic_text::Action::Insert(c)),
|
} else {
|
||||||
Action::Enter => act(cosmic_text::Action::Enter),
|
editor.action(
|
||||||
Action::Backspace => act(cosmic_text::Action::Backspace),
|
font_system.raw(),
|
||||||
Action::Delete => act(cosmic_text::Action::Delete),
|
match motion {
|
||||||
Action::Click(position) => act(cosmic_text::Action::Click {
|
Motion::Left => cosmic_text::Action::Left,
|
||||||
x: position.x as i32,
|
Motion::Right => cosmic_text::Action::Right,
|
||||||
y: position.y as i32,
|
Motion::Up => cosmic_text::Action::Up,
|
||||||
}),
|
Motion::Down => cosmic_text::Action::Down,
|
||||||
Action::Drag(position) => act(cosmic_text::Action::Drag {
|
Motion::WordLeft => cosmic_text::Action::LeftWord,
|
||||||
x: position.x as i32,
|
Motion::WordRight => cosmic_text::Action::RightWord,
|
||||||
y: position.y as i32,
|
Motion::Home => cosmic_text::Action::Home,
|
||||||
}),
|
Motion::End => cosmic_text::Action::End,
|
||||||
_ => todo!(),
|
Motion::PageUp => cosmic_text::Action::PageUp,
|
||||||
|
Motion::PageDown => cosmic_text::Action::PageDown,
|
||||||
|
Motion::DocumentStart => {
|
||||||
|
cosmic_text::Action::BufferStart
|
||||||
|
}
|
||||||
|
Motion::DocumentEnd => {
|
||||||
|
cosmic_text::Action::BufferEnd
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Selection events
|
||||||
|
Action::Select(_motion) => todo!(),
|
||||||
|
Action::SelectWord => todo!(),
|
||||||
|
Action::SelectLine => todo!(),
|
||||||
|
|
||||||
|
// Editing events
|
||||||
|
Action::Insert(c) => {
|
||||||
|
editor
|
||||||
|
.action(font_system.raw(), cosmic_text::Action::Insert(c));
|
||||||
|
}
|
||||||
|
Action::Enter => {
|
||||||
|
editor.action(font_system.raw(), cosmic_text::Action::Enter);
|
||||||
|
}
|
||||||
|
Action::Backspace => {
|
||||||
|
editor
|
||||||
|
.action(font_system.raw(), cosmic_text::Action::Backspace);
|
||||||
|
}
|
||||||
|
Action::Delete => {
|
||||||
|
editor.action(font_system.raw(), cosmic_text::Action::Delete);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mouse events
|
||||||
|
Action::Click(position) => {
|
||||||
|
editor.action(
|
||||||
|
font_system.raw(),
|
||||||
|
cosmic_text::Action::Click {
|
||||||
|
x: position.x as i32,
|
||||||
|
y: position.y as i32,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Action::Drag(position) => {
|
||||||
|
editor.action(
|
||||||
|
font_system.raw(),
|
||||||
|
cosmic_text::Action::Drag {
|
||||||
|
x: position.x as i32,
|
||||||
|
y: position.y as i32,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Deselect if selection matches cursor position
|
||||||
|
if let Some(selection) = editor.select_opt() {
|
||||||
|
let cursor = editor.cursor();
|
||||||
|
|
||||||
|
if cursor.line == selection.line
|
||||||
|
&& cursor.index == selection.index
|
||||||
|
{
|
||||||
|
editor.set_select_opt(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.shape_as_needed(font_system.raw());
|
editor.shape_as_needed(font_system.raw());
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use crate::core::{
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
pub use crate::style::text_editor::{Appearance, StyleSheet};
|
pub use crate::style::text_editor::{Appearance, StyleSheet};
|
||||||
pub use text::editor::Action;
|
pub use text::editor::{Action, Motion};
|
||||||
|
|
||||||
pub struct TextEditor<'a, Message, Renderer = crate::Renderer>
|
pub struct TextEditor<'a, Message, Renderer = crate::Renderer>
|
||||||
where
|
where
|
||||||
|
|
@ -189,16 +189,16 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
match update {
|
match update {
|
||||||
|
Update::Click { click, action } => {
|
||||||
|
state.is_focused = true;
|
||||||
|
state.is_dragging = true;
|
||||||
|
state.last_click = Some(click);
|
||||||
|
shell.publish(on_edit(action));
|
||||||
|
}
|
||||||
Update::Unfocus => {
|
Update::Unfocus => {
|
||||||
state.is_focused = false;
|
state.is_focused = false;
|
||||||
state.is_dragging = false;
|
state.is_dragging = false;
|
||||||
}
|
}
|
||||||
Update::Click { click, action } => {
|
|
||||||
state.is_focused = true;
|
|
||||||
state.last_click = Some(click);
|
|
||||||
state.is_dragging = true;
|
|
||||||
shell.publish(on_edit(action));
|
|
||||||
}
|
|
||||||
Update::StopDragging => {
|
Update::StopDragging => {
|
||||||
state.is_dragging = false;
|
state.is_dragging = false;
|
||||||
}
|
}
|
||||||
|
|
@ -352,6 +352,9 @@ impl Update {
|
||||||
padding: Padding,
|
padding: Padding,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
|
let edit = |action| Some(Update::Edit(action));
|
||||||
|
let move_ = |motion| Some(Update::Edit(Action::Move(motion)));
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
Event::Mouse(event) => match event {
|
Event::Mouse(event) => match event {
|
||||||
mouse::Event::ButtonPressed(mouse::Button::Left) => {
|
mouse::Event::ButtonPressed(mouse::Button::Left) => {
|
||||||
|
|
@ -386,7 +389,7 @@ impl Update {
|
||||||
let cursor_position = cursor.position_in(bounds)?
|
let cursor_position = cursor.position_in(bounds)?
|
||||||
- Vector::new(padding.top, padding.left);
|
- Vector::new(padding.top, padding.left);
|
||||||
|
|
||||||
Some(Self::Edit(Action::Drag(cursor_position)))
|
edit(Action::Drag(cursor_position))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
|
|
@ -394,37 +397,31 @@ impl Update {
|
||||||
keyboard::Event::KeyPressed {
|
keyboard::Event::KeyPressed {
|
||||||
key_code,
|
key_code,
|
||||||
modifiers,
|
modifiers,
|
||||||
} if state.is_focused => match key_code {
|
} if state.is_focused => {
|
||||||
keyboard::KeyCode::Left => {
|
if let Some(motion) = motion(key_code) {
|
||||||
if platform::is_jump_modifier_pressed(modifiers) {
|
let motion = if modifiers.control() {
|
||||||
Some(Self::Edit(Action::MoveLeftWord))
|
motion.widen()
|
||||||
} else {
|
} else {
|
||||||
Some(Self::Edit(Action::MoveLeft))
|
motion
|
||||||
}
|
};
|
||||||
}
|
|
||||||
keyboard::KeyCode::Right => {
|
return edit(if modifiers.shift() {
|
||||||
if platform::is_jump_modifier_pressed(modifiers) {
|
Action::Select(motion)
|
||||||
Some(Self::Edit(Action::MoveRightWord))
|
|
||||||
} else {
|
} else {
|
||||||
Some(Self::Edit(Action::MoveRight))
|
Action::Move(motion)
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
keyboard::KeyCode::Up => Some(Self::Edit(Action::MoveUp)),
|
|
||||||
keyboard::KeyCode::Down => {
|
match key_code {
|
||||||
Some(Self::Edit(Action::MoveDown))
|
keyboard::KeyCode::Enter => edit(Action::Enter),
|
||||||
|
keyboard::KeyCode::Backspace => edit(Action::Backspace),
|
||||||
|
keyboard::KeyCode::Delete => edit(Action::Delete),
|
||||||
|
keyboard::KeyCode::Escape => Some(Self::Unfocus),
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
keyboard::KeyCode::Enter => Some(Self::Edit(Action::Enter)),
|
}
|
||||||
keyboard::KeyCode::Backspace => {
|
|
||||||
Some(Self::Edit(Action::Backspace))
|
|
||||||
}
|
|
||||||
keyboard::KeyCode::Delete => {
|
|
||||||
Some(Self::Edit(Action::Delete))
|
|
||||||
}
|
|
||||||
keyboard::KeyCode::Escape => Some(Self::Unfocus),
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
keyboard::Event::CharacterReceived(c) if state.is_focused => {
|
keyboard::Event::CharacterReceived(c) if state.is_focused => {
|
||||||
Some(Self::Edit(Action::Insert(c)))
|
edit(Action::Insert(c))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
|
|
@ -433,6 +430,20 @@ impl Update {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn motion(key_code: keyboard::KeyCode) -> Option<Motion> {
|
||||||
|
match key_code {
|
||||||
|
keyboard::KeyCode::Left => Some(Motion::Left),
|
||||||
|
keyboard::KeyCode::Right => Some(Motion::Right),
|
||||||
|
keyboard::KeyCode::Up => Some(Motion::Up),
|
||||||
|
keyboard::KeyCode::Down => Some(Motion::Down),
|
||||||
|
keyboard::KeyCode::Home => Some(Motion::Home),
|
||||||
|
keyboard::KeyCode::End => Some(Motion::End),
|
||||||
|
keyboard::KeyCode::PageUp => Some(Motion::PageUp),
|
||||||
|
keyboard::KeyCode::PageDown => Some(Motion::PageDown),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod platform {
|
mod platform {
|
||||||
use crate::core::keyboard;
|
use crate::core::keyboard;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue