reorganize paint logic and actions module
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
mod actions;
|
||||
pub mod actions;
|
||||
mod input_element;
|
||||
mod input_state;
|
||||
pub mod notify;
|
||||
@@ -7,7 +7,6 @@ mod storage;
|
||||
mod text_area_element;
|
||||
mod text_area_state;
|
||||
|
||||
pub use actions::*;
|
||||
pub use input_element::*;
|
||||
pub use input_state::*;
|
||||
pub use shared_state::*;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use gpui::{Action, App, AppContext, Context, InteractiveElement, Window};
|
||||
use gpui::{Action, AppContext, Context, InteractiveElement, Window};
|
||||
|
||||
/// The key context used for input element keybindings.
|
||||
pub const DEFAULT_INPUT_CONTEXT: &str = "Input";
|
||||
@@ -234,7 +234,7 @@ pub trait EditableTextActionHandler<'app>: Sized {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EditableInputActionElement: super::StateBackedElement {
|
||||
pub(super) trait EditableInputActionElement: super::StateBackedElement {
|
||||
fn register_action<A: Action>(
|
||||
&mut self,
|
||||
init_props: Self::InitProps,
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
use std::{ops::Range, sync::Arc};
|
||||
|
||||
use crate::editable_text::{
|
||||
EditableInputActionElement, EditableTextActionHandler, InitStorage, StateBackedElement,
|
||||
TextInputLayoutData, TextInputState, TextLayoutWrapping, TextLineSegment,
|
||||
InitStorage, StateBackedElement, TextInputState, TextInputStateBase, TextLayoutWrapping,
|
||||
TextLineSegment,
|
||||
actions::{DEFAULT_INPUT_CONTEXT, EditableInputActionElement},
|
||||
};
|
||||
use gpui::{
|
||||
Along, App, Axis, Bounds, ContentMask, CursorStyle, DispatchPhase, Display, Element, ElementId,
|
||||
ElementInputHandler, Entity, FocusHandle, Focusable, Hitbox, HitboxBehavior, Hsla,
|
||||
InteractiveElement, Interactivity, IntoElement, MouseButton, MouseDownEvent, MouseMoveEvent,
|
||||
MouseUpEvent, PaintQuad, Pixels, Point, ScrollWheelEvent, ShapedLine, SharedString, Style,
|
||||
StyleRefinement, Styled, TextAlign, TextRun, TextStyle, Window, WrappedLine, fill, point, px,
|
||||
size,
|
||||
MouseUpEvent, PaintQuad, Pixels, Point, ScrollWheelEvent, SharedString, Style, StyleRefinement,
|
||||
Styled, TextAlign, TextStyle, Window, WrappedLine, fill, point, px, size,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{ops::Range, sync::Arc};
|
||||
|
||||
#[track_caller]
|
||||
pub fn input(id: impl Into<ElementId>) -> TextInputElement {
|
||||
@@ -22,7 +21,7 @@ pub fn input(id: impl Into<ElementId>) -> TextInputElement {
|
||||
interactivity: Interactivity::new(),
|
||||
init_storage: InitStorage::default(),
|
||||
};
|
||||
this = this.key_context(super::DEFAULT_INPUT_CONTEXT);
|
||||
this = this.key_context(DEFAULT_INPUT_CONTEXT);
|
||||
this.register_actions();
|
||||
this
|
||||
}
|
||||
@@ -174,25 +173,60 @@ impl Element for TextInputElement {
|
||||
window: &mut gpui::Window,
|
||||
cx: &mut gpui::App,
|
||||
) -> Self::PrepaintState {
|
||||
struct InteractivityPrepaint {
|
||||
hitbox: Option<Hitbox>,
|
||||
scroll_offset: Point<Pixels>,
|
||||
}
|
||||
// TODO: how do we enable scrolling? overflow on interactivity?
|
||||
let prepaint = self.interactivity.prepaint(
|
||||
global_id,
|
||||
inspector_id,
|
||||
bounds,
|
||||
bounds.size,
|
||||
window,
|
||||
cx,
|
||||
|_style, scroll_offset, hitbox, window, _cx| {
|
||||
let hitbox =
|
||||
hitbox.or_else(|| Some(window.insert_hitbox(bounds, HitboxBehavior::Normal)));
|
||||
InteractivityPrepaint {
|
||||
hitbox,
|
||||
scroll_offset,
|
||||
}
|
||||
},
|
||||
);
|
||||
let InteractivityPrepaint {
|
||||
hitbox,
|
||||
scroll_offset,
|
||||
} = prepaint;
|
||||
|
||||
let text_color = request_layout.text_style.color;
|
||||
let placeholder_color = Hsla::black().opacity(0.5); // TODO: as an element param
|
||||
let selection_color = Hsla::blue().opacity(0.5); // TODO: as an element param
|
||||
let caret_color = Hsla::white(); // TODO: as an element param
|
||||
|
||||
// TODO: no wrapping in single-line
|
||||
let wrap_width = Some(bounds.size.width);
|
||||
|
||||
let wrapping = TextLayoutWrapping::new(request_layout.text_style.clone(), wrap_width);
|
||||
let showing_placeholder = request_layout.state.update(cx, |state, _cx| {
|
||||
let text_value = state.storage().content_utf8();
|
||||
let is_empty = text_value.is_empty();
|
||||
let display_text = match is_empty {
|
||||
false => text_value,
|
||||
true => self
|
||||
.placeholder
|
||||
.as_ref()
|
||||
.map(SharedString::as_str)
|
||||
.unwrap_or_default(),
|
||||
};
|
||||
|
||||
state.layout_data_mut().bounds = bounds;
|
||||
state.apply_wrapping(wrapping, display_text, window);
|
||||
is_empty
|
||||
let show_placeholder = state.storage().content_utf8().is_empty();
|
||||
state.layout_data.bounds = bounds;
|
||||
if state.layout_wrapping.integrate(wrapping) {
|
||||
let (display_text, color) = match show_placeholder {
|
||||
false => (state.storage().content_utf8(), text_color),
|
||||
true => {
|
||||
let value = self.placeholder.as_ref();
|
||||
let value = value.map(SharedString::as_str).unwrap_or_default();
|
||||
(value, placeholder_color)
|
||||
}
|
||||
};
|
||||
state.layout_data.lines = TextInputStateBase::build_wrapped_lines(
|
||||
display_text,
|
||||
&state.layout_wrapping,
|
||||
window,
|
||||
color,
|
||||
);
|
||||
}
|
||||
show_placeholder
|
||||
});
|
||||
|
||||
let input = request_layout.state.read(cx);
|
||||
@@ -204,28 +238,8 @@ impl Element for TextInputElement {
|
||||
// TODO: Cursor blinking
|
||||
let cursor_visible = true; // input.cursor_visible();
|
||||
|
||||
let text_color = Hsla::white(); // TODO: as an element param
|
||||
let placeholder_color = Hsla::black().opacity(0.5); // TODO: as an element param
|
||||
let selection_color = Hsla::blue().opacity(0.5); // TODO: as an element param
|
||||
let caret_color = Hsla::white(); // TODO: as an element param
|
||||
|
||||
let mut elements = SmallVec::new();
|
||||
|
||||
// TODO: how do we enable scrolling? overflow on interactivity?
|
||||
let (hitbox, scroll_offset) = self.interactivity.prepaint(
|
||||
global_id,
|
||||
inspector_id,
|
||||
bounds,
|
||||
bounds.size,
|
||||
window,
|
||||
cx,
|
||||
|_style, scroll_offset, hitbox, window, _cx| {
|
||||
let hitbox =
|
||||
hitbox.or_else(|| Some(window.insert_hitbox(bounds, HitboxBehavior::Normal)));
|
||||
(hitbox, scroll_offset)
|
||||
},
|
||||
);
|
||||
|
||||
let line_height = window.line_height();
|
||||
let is_range_contained_by_range =
|
||||
|text_range: &Range<usize>, containing_range: &Range<usize>| {
|
||||
@@ -369,6 +383,7 @@ impl Element for TextInputElement {
|
||||
let ime_handler = ElementInputHandler::new(bounds, request_layout.state.clone());
|
||||
window.handle_input(&prepaint.focus_handle, ime_handler, cx);
|
||||
|
||||
use super::actions::EditableTextActionHandler;
|
||||
let get_relative_position = {
|
||||
let bounds = bounds.clone();
|
||||
move |position: Point<Pixels>| {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use super::notify::TextHistoryPushed;
|
||||
use crate::editable_text::{
|
||||
EditableTextActionHandler, TextBoundary, TextInputStateBase, TextStateNotifier,
|
||||
UnicodeTextStorage, notify::TextChanged,
|
||||
TextBoundary, TextInputStateBase, TextStateNotifier, UnicodeTextStorage, notify::TextChanged,
|
||||
};
|
||||
use gpui::{
|
||||
Bounds, Context, EntityInputHandler, EventEmitter, NavigationDirection, Pixels, Point,
|
||||
@@ -142,55 +141,46 @@ impl EntityInputHandler for TextInputState {
|
||||
}
|
||||
}
|
||||
|
||||
use super::actions::*;
|
||||
impl<'app> EditableTextActionHandler<'app> for TextInputState {
|
||||
type Context = gpui::Context<'app, Self>;
|
||||
|
||||
fn escape(&mut self, _: &super::Escape, window: &mut Window, cx: &mut Self::Context) {
|
||||
fn escape(&mut self, _: &Escape, window: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal.set_selected_range(0..0);
|
||||
cx.notify();
|
||||
|
||||
window.blur();
|
||||
}
|
||||
|
||||
fn insert_enter(&mut self, _: &super::Enter, _w: &mut Window, _cx: &mut Self::Context) {}
|
||||
fn insert_enter(&mut self, _: &Enter, _w: &mut Window, _cx: &mut Self::Context) {}
|
||||
|
||||
fn insert_tab(&mut self, _: &super::Tab, window: &mut Window, cx: &mut Self::Context) {
|
||||
fn insert_tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Self::Context) {
|
||||
self.replace_text_in_range(None, "\t", window, cx);
|
||||
}
|
||||
|
||||
fn backspace(&mut self, _: &super::Backspace, _: &mut Window, cx: &mut Self::Context) {
|
||||
fn backspace(&mut self, _: &Backspace, _: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.delete(NavigationDirection::Back, TextBoundary::Graphmeme, cx);
|
||||
}
|
||||
|
||||
fn delete(&mut self, _: &super::Delete, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn delete(&mut self, _: &Delete, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.delete(NavigationDirection::Forward, TextBoundary::Graphmeme, cx);
|
||||
}
|
||||
|
||||
fn delete_word_left(
|
||||
&mut self,
|
||||
_: &super::DeleteWordLeft,
|
||||
_w: &mut Window,
|
||||
cx: &mut Self::Context,
|
||||
) {
|
||||
fn delete_word_left(&mut self, _: &DeleteWordLeft, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.delete(NavigationDirection::Back, TextBoundary::Word, cx);
|
||||
}
|
||||
|
||||
fn delete_word_right(
|
||||
&mut self,
|
||||
_: &super::DeleteWordRight,
|
||||
_w: &mut Window,
|
||||
cx: &mut Self::Context,
|
||||
) {
|
||||
fn delete_word_right(&mut self, _: &DeleteWordRight, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.delete(NavigationDirection::Forward, TextBoundary::Word, cx);
|
||||
}
|
||||
|
||||
fn delete_to_line_start(
|
||||
&mut self,
|
||||
_: &super::DeleteToBeginningOfLine,
|
||||
_: &DeleteToBeginningOfLine,
|
||||
_w: &mut Window,
|
||||
cx: &mut Self::Context,
|
||||
) {
|
||||
@@ -200,7 +190,7 @@ impl<'app> EditableTextActionHandler<'app> for TextInputState {
|
||||
|
||||
fn delete_to_line_end(
|
||||
&mut self,
|
||||
_: &super::DeleteToEndOfLine,
|
||||
_: &DeleteToEndOfLine,
|
||||
_w: &mut Window,
|
||||
cx: &mut Self::Context,
|
||||
) {
|
||||
@@ -208,138 +198,123 @@ impl<'app> EditableTextActionHandler<'app> for TextInputState {
|
||||
.delete(NavigationDirection::Forward, TextBoundary::Line, cx);
|
||||
}
|
||||
|
||||
fn nav_left(&mut self, _: &super::Left, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_left(&mut self, _: &Left, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Back, TextBoundary::Graphmeme, cx);
|
||||
}
|
||||
|
||||
fn nav_right(&mut self, _: &super::Right, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_right(&mut self, _: &Right, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Forward, TextBoundary::Graphmeme, cx);
|
||||
}
|
||||
|
||||
fn nav_up(&mut self, _: &super::Up, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_up(&mut self, _: &Up, _w: &mut Window, cx: &mut Self::Context) {
|
||||
// semantically equivalent to line
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Back, TextBoundary::Line, cx);
|
||||
}
|
||||
|
||||
fn nav_down(&mut self, _: &super::Down, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_down(&mut self, _: &Down, _w: &mut Window, cx: &mut Self::Context) {
|
||||
// semantically equivalent to line
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Forward, TextBoundary::Line, cx);
|
||||
}
|
||||
|
||||
fn nav_line_start(&mut self, _: &super::Home, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_line_start(&mut self, _: &Home, _w: &mut Window, cx: &mut Self::Context) {
|
||||
// semantically equivalent to document
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Back, TextBoundary::Line, cx);
|
||||
}
|
||||
|
||||
fn nav_line_end(&mut self, _: &super::End, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_line_end(&mut self, _: &End, _w: &mut Window, cx: &mut Self::Context) {
|
||||
// semantically equivalent to document
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Forward, TextBoundary::Line, cx);
|
||||
}
|
||||
|
||||
fn nav_start(&mut self, _: &super::MoveToBeginning, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_start(&mut self, _: &MoveToBeginning, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Back, TextBoundary::Document, cx);
|
||||
}
|
||||
|
||||
fn nav_end(&mut self, _: &super::MoveToEnd, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_end(&mut self, _: &MoveToEnd, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Forward, TextBoundary::Document, cx);
|
||||
}
|
||||
|
||||
fn nav_left_word(&mut self, _: &super::WordLeft, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_left_word(&mut self, _: &WordLeft, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Back, TextBoundary::Word, cx);
|
||||
}
|
||||
|
||||
fn nav_right_word(&mut self, _: &super::WordRight, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_right_word(&mut self, _: &WordRight, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Forward, TextBoundary::Word, cx);
|
||||
}
|
||||
|
||||
fn select_all(&mut self, _: &super::SelectAll, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn select_all(&mut self, _: &SelectAll, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal.select_all(cx);
|
||||
}
|
||||
|
||||
fn select_left(&mut self, _: &super::SelectLeft, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn select_left(&mut self, _: &SelectLeft, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.select_linear(NavigationDirection::Back, TextBoundary::Graphmeme, cx);
|
||||
}
|
||||
|
||||
fn select_right(&mut self, _: &super::SelectRight, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn select_right(&mut self, _: &SelectRight, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.select_linear(NavigationDirection::Forward, TextBoundary::Graphmeme, cx);
|
||||
}
|
||||
|
||||
fn select_up(&mut self, _: &super::SelectUp, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn select_up(&mut self, _: &SelectUp, _w: &mut Window, cx: &mut Self::Context) {
|
||||
// semantically equivalent to select document
|
||||
self.internal
|
||||
.select_linear(NavigationDirection::Back, TextBoundary::Document, cx);
|
||||
}
|
||||
|
||||
fn select_down(&mut self, _: &super::SelectDown, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn select_down(&mut self, _: &SelectDown, _w: &mut Window, cx: &mut Self::Context) {
|
||||
// semantically equivalent to select document
|
||||
self.internal
|
||||
.select_linear(NavigationDirection::Forward, TextBoundary::Document, cx);
|
||||
}
|
||||
|
||||
fn select_start(
|
||||
&mut self,
|
||||
_: &super::SelectToBeginning,
|
||||
_w: &mut Window,
|
||||
cx: &mut Self::Context,
|
||||
) {
|
||||
fn select_start(&mut self, _: &SelectToBeginning, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.select_linear(NavigationDirection::Back, TextBoundary::Document, cx);
|
||||
}
|
||||
|
||||
fn select_end(&mut self, _: &super::SelectToEnd, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn select_end(&mut self, _: &SelectToEnd, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.select_linear(NavigationDirection::Forward, TextBoundary::Document, cx);
|
||||
}
|
||||
|
||||
fn select_left_word(
|
||||
&mut self,
|
||||
_: &super::SelectWordLeft,
|
||||
_w: &mut Window,
|
||||
cx: &mut Self::Context,
|
||||
) {
|
||||
fn select_left_word(&mut self, _: &SelectWordLeft, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.select_linear(NavigationDirection::Back, TextBoundary::Word, cx);
|
||||
}
|
||||
|
||||
fn select_right_word(
|
||||
&mut self,
|
||||
_: &super::SelectWordRight,
|
||||
_w: &mut Window,
|
||||
cx: &mut Self::Context,
|
||||
) {
|
||||
fn select_right_word(&mut self, _: &SelectWordRight, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.select_linear(NavigationDirection::Forward, TextBoundary::Word, cx);
|
||||
}
|
||||
|
||||
fn cut(&mut self, _: &super::Cut, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn cut(&mut self, _: &Cut, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal.cut(cx);
|
||||
}
|
||||
|
||||
fn copy(&mut self, _: &super::Copy, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn copy(&mut self, _: &Copy, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal.copy(cx);
|
||||
}
|
||||
|
||||
fn paste(&mut self, _: &super::Paste, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn paste(&mut self, _: &Paste, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal.paste(cx);
|
||||
}
|
||||
|
||||
fn undo(&mut self, _: &super::Undo, _w: &mut Window, _cx: &mut Self::Context) {
|
||||
fn undo(&mut self, _: &Undo, _w: &mut Window, _cx: &mut Self::Context) {
|
||||
// TODO: STUB
|
||||
}
|
||||
|
||||
fn redo(&mut self, _: &super::Redo, _w: &mut Window, _cx: &mut Self::Context) {
|
||||
fn redo(&mut self, _: &Redo, _w: &mut Window, _cx: &mut Self::Context) {
|
||||
// TODO: STUB
|
||||
}
|
||||
|
||||
|
||||
@@ -54,8 +54,8 @@ pub struct TextInputStateBase {
|
||||
|
||||
focus_handle: FocusHandle,
|
||||
|
||||
layout_wrapping: TextLayoutWrapping,
|
||||
layout_data: TextInputLayoutData,
|
||||
pub(super) layout_wrapping: TextLayoutWrapping,
|
||||
pub(super) layout_data: TextInputLayoutData,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -72,6 +72,14 @@ impl TextLayoutWrapping {
|
||||
dirty: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn integrate(&mut self, other: Self) -> bool {
|
||||
let dirty = self.dirty
|
||||
|| self.wrap_width != other.wrap_width
|
||||
|| self.text_style != other.text_style;
|
||||
*self = other;
|
||||
dirty
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -155,38 +163,20 @@ impl TextInputStateBase {
|
||||
pub fn marked_range(&self) -> Option<Range<usize>> {
|
||||
self.marked_range.clone()
|
||||
}
|
||||
|
||||
pub(super) fn layout_data(&self) -> &TextInputLayoutData {
|
||||
&self.layout_data
|
||||
}
|
||||
|
||||
pub(super) fn layout_data_mut(&mut self) -> &mut TextInputLayoutData {
|
||||
&mut self.layout_data
|
||||
}
|
||||
}
|
||||
|
||||
impl TextInputStateBase {
|
||||
pub fn apply_wrapping(
|
||||
&mut self,
|
||||
wrapping: TextLayoutWrapping,
|
||||
display_text: &str,
|
||||
window: &Window,
|
||||
) {
|
||||
let dirty = self.layout_wrapping.dirty
|
||||
|| self.layout_wrapping.wrap_width != wrapping.wrap_width
|
||||
|| self.layout_wrapping.text_style != wrapping.text_style;
|
||||
self.layout_wrapping = wrapping;
|
||||
if dirty {
|
||||
self.layout_data.lines = self.build_wrapped_lines(display_text, window);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn line_segments(&self) -> &Vec<TextLineSegment> {
|
||||
&self.layout_data.lines
|
||||
}
|
||||
|
||||
fn build_wrapped_lines(&self, content: &str, window: &Window) -> Vec<TextLineSegment> {
|
||||
let text_style = &self.layout_wrapping.text_style;
|
||||
pub fn build_wrapped_lines(
|
||||
content: &str,
|
||||
wrapping: &TextLayoutWrapping,
|
||||
window: &Window,
|
||||
color: Hsla,
|
||||
) -> Vec<TextLineSegment> {
|
||||
let text_style = &wrapping.text_style;
|
||||
let font_size = text_style.font_size.to_pixels(window.rem_size());
|
||||
let mut lines = Vec::new();
|
||||
|
||||
@@ -223,9 +213,7 @@ impl TextInputStateBase {
|
||||
let run = TextRun {
|
||||
len: line_slice.len(),
|
||||
font: text_style.font(),
|
||||
// TODO: This is the actual text color that is stored in WrappedLine,
|
||||
// needs to reflect the style color provided by paint
|
||||
color: Hsla::default(),
|
||||
color,
|
||||
background_color: None,
|
||||
underline: None,
|
||||
strikethrough: None,
|
||||
@@ -237,7 +225,7 @@ impl TextInputStateBase {
|
||||
SharedString::from(line_slice.to_string()),
|
||||
font_size,
|
||||
&[run],
|
||||
self.layout_wrapping.wrap_width,
|
||||
wrapping.wrap_width,
|
||||
None,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::editable_text::{
|
||||
EditableInputActionElement, InitStorage, StateBackedElement, TextAreaState,
|
||||
InitStorage, StateBackedElement, TextAreaState, actions::EditableInputActionElement,
|
||||
};
|
||||
use gpui::{
|
||||
App, Element, ElementId, Entity, Hitbox, InteractiveElement, Interactivity, IntoElement,
|
||||
@@ -13,7 +13,7 @@ pub fn text_area(id: impl Into<ElementId>) -> TextAreaElement {
|
||||
interactivity: Interactivity::new(),
|
||||
init_storage: InitStorage::default(),
|
||||
};
|
||||
this = this.key_context(super::DEFAULT_INPUT_CONTEXT);
|
||||
this = this.key_context(super::actions::DEFAULT_INPUT_CONTEXT);
|
||||
this.register_actions();
|
||||
this
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::editable_text::{
|
||||
EditableTextActionHandler, TextBoundary, TextInputStateBase, TextStateNotifier,
|
||||
UnicodeTextStorage,
|
||||
TextBoundary, TextInputStateBase, TextStateNotifier, UnicodeTextStorage,
|
||||
notify::{TextChanged, TextHistoryPushed},
|
||||
};
|
||||
use gpui::{
|
||||
@@ -142,57 +141,48 @@ impl EntityInputHandler for TextAreaState {
|
||||
}
|
||||
}
|
||||
|
||||
use super::actions::*;
|
||||
impl<'app> EditableTextActionHandler<'app> for TextAreaState {
|
||||
type Context = gpui::Context<'app, Self>;
|
||||
|
||||
fn escape(&mut self, _: &super::Escape, window: &mut Window, cx: &mut Self::Context) {
|
||||
fn escape(&mut self, _: &Escape, window: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal.set_selected_range(0..0);
|
||||
cx.notify();
|
||||
|
||||
window.blur();
|
||||
}
|
||||
|
||||
fn insert_enter(&mut self, _: &super::Enter, window: &mut Window, cx: &mut Self::Context) {
|
||||
fn insert_enter(&mut self, _: &Enter, window: &mut Window, cx: &mut Self::Context) {
|
||||
self.replace_text_in_range(None, "\n", window, cx);
|
||||
}
|
||||
|
||||
fn insert_tab(&mut self, _: &super::Tab, window: &mut Window, cx: &mut Self::Context) {
|
||||
fn insert_tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Self::Context) {
|
||||
self.replace_text_in_range(None, "\t", window, cx);
|
||||
}
|
||||
|
||||
fn backspace(&mut self, _: &super::Backspace, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn backspace(&mut self, _: &Backspace, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.delete(NavigationDirection::Back, TextBoundary::Graphmeme, cx);
|
||||
}
|
||||
|
||||
fn delete(&mut self, _: &super::Delete, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn delete(&mut self, _: &Delete, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.delete(NavigationDirection::Forward, TextBoundary::Graphmeme, cx);
|
||||
}
|
||||
|
||||
fn delete_word_left(
|
||||
&mut self,
|
||||
_: &super::DeleteWordLeft,
|
||||
_w: &mut Window,
|
||||
cx: &mut Self::Context,
|
||||
) {
|
||||
fn delete_word_left(&mut self, _: &DeleteWordLeft, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.delete(NavigationDirection::Back, TextBoundary::Word, cx);
|
||||
}
|
||||
|
||||
fn delete_word_right(
|
||||
&mut self,
|
||||
_: &super::DeleteWordRight,
|
||||
_w: &mut Window,
|
||||
cx: &mut Self::Context,
|
||||
) {
|
||||
fn delete_word_right(&mut self, _: &DeleteWordRight, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.delete(NavigationDirection::Forward, TextBoundary::Word, cx);
|
||||
}
|
||||
|
||||
fn delete_to_line_start(
|
||||
&mut self,
|
||||
_: &super::DeleteToBeginningOfLine,
|
||||
_: &DeleteToBeginningOfLine,
|
||||
_w: &mut Window,
|
||||
cx: &mut Self::Context,
|
||||
) {
|
||||
@@ -202,7 +192,7 @@ impl<'app> EditableTextActionHandler<'app> for TextAreaState {
|
||||
|
||||
fn delete_to_line_end(
|
||||
&mut self,
|
||||
_: &super::DeleteToEndOfLine,
|
||||
_: &DeleteToEndOfLine,
|
||||
_w: &mut Window,
|
||||
cx: &mut Self::Context,
|
||||
) {
|
||||
@@ -210,128 +200,113 @@ impl<'app> EditableTextActionHandler<'app> for TextAreaState {
|
||||
.delete(NavigationDirection::Forward, TextBoundary::Line, cx);
|
||||
}
|
||||
|
||||
fn nav_left(&mut self, _: &super::Left, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_left(&mut self, _: &Left, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Back, TextBoundary::Graphmeme, cx);
|
||||
}
|
||||
|
||||
fn nav_right(&mut self, _: &super::Right, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_right(&mut self, _: &Right, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Forward, TextBoundary::Graphmeme, cx);
|
||||
}
|
||||
|
||||
fn nav_up(&mut self, _: &super::Up, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_up(&mut self, _: &Up, _w: &mut Window, cx: &mut Self::Context) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
fn nav_down(&mut self, _: &super::Down, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_down(&mut self, _: &Down, _w: &mut Window, cx: &mut Self::Context) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
fn nav_line_start(&mut self, _: &super::Home, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_line_start(&mut self, _: &Home, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Back, TextBoundary::Line, cx);
|
||||
}
|
||||
|
||||
fn nav_line_end(&mut self, _: &super::End, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_line_end(&mut self, _: &End, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Forward, TextBoundary::Line, cx);
|
||||
}
|
||||
|
||||
fn nav_start(&mut self, _: &super::MoveToBeginning, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_start(&mut self, _: &MoveToBeginning, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Back, TextBoundary::Document, cx);
|
||||
}
|
||||
|
||||
fn nav_end(&mut self, _: &super::MoveToEnd, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_end(&mut self, _: &MoveToEnd, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Forward, TextBoundary::Document, cx);
|
||||
}
|
||||
|
||||
fn nav_left_word(&mut self, _: &super::WordLeft, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_left_word(&mut self, _: &WordLeft, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Back, TextBoundary::Word, cx);
|
||||
}
|
||||
|
||||
fn nav_right_word(&mut self, _: &super::WordRight, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn nav_right_word(&mut self, _: &WordRight, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.nav_linear(NavigationDirection::Forward, TextBoundary::Word, cx);
|
||||
}
|
||||
|
||||
fn select_all(&mut self, _: &super::SelectAll, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn select_all(&mut self, _: &SelectAll, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal.select_all(cx);
|
||||
}
|
||||
|
||||
fn select_left(&mut self, _: &super::SelectLeft, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn select_left(&mut self, _: &SelectLeft, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.select_linear(NavigationDirection::Back, TextBoundary::Graphmeme, cx);
|
||||
}
|
||||
|
||||
fn select_right(&mut self, _: &super::SelectRight, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn select_right(&mut self, _: &SelectRight, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.select_linear(NavigationDirection::Forward, TextBoundary::Graphmeme, cx);
|
||||
}
|
||||
|
||||
fn select_up(&mut self, _: &super::SelectUp, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn select_up(&mut self, _: &SelectUp, _w: &mut Window, cx: &mut Self::Context) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
fn select_down(&mut self, _: &super::SelectDown, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn select_down(&mut self, _: &SelectDown, _w: &mut Window, cx: &mut Self::Context) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
fn select_start(
|
||||
&mut self,
|
||||
_: &super::SelectToBeginning,
|
||||
_w: &mut Window,
|
||||
cx: &mut Self::Context,
|
||||
) {
|
||||
fn select_start(&mut self, _: &SelectToBeginning, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.select_linear(NavigationDirection::Back, TextBoundary::Document, cx);
|
||||
}
|
||||
|
||||
fn select_end(&mut self, _: &super::SelectToEnd, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn select_end(&mut self, _: &SelectToEnd, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.select_linear(NavigationDirection::Forward, TextBoundary::Document, cx);
|
||||
}
|
||||
|
||||
fn select_left_word(
|
||||
&mut self,
|
||||
_: &super::SelectWordLeft,
|
||||
_w: &mut Window,
|
||||
cx: &mut Self::Context,
|
||||
) {
|
||||
fn select_left_word(&mut self, _: &SelectWordLeft, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.select_linear(NavigationDirection::Back, TextBoundary::Word, cx);
|
||||
}
|
||||
|
||||
fn select_right_word(
|
||||
&mut self,
|
||||
_: &super::SelectWordRight,
|
||||
_w: &mut Window,
|
||||
cx: &mut Self::Context,
|
||||
) {
|
||||
fn select_right_word(&mut self, _: &SelectWordRight, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal
|
||||
.select_linear(NavigationDirection::Forward, TextBoundary::Word, cx);
|
||||
}
|
||||
|
||||
fn cut(&mut self, _: &super::Cut, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn cut(&mut self, _: &Cut, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal.cut(cx);
|
||||
}
|
||||
|
||||
fn copy(&mut self, _: &super::Copy, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn copy(&mut self, _: &Copy, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal.copy(cx);
|
||||
}
|
||||
|
||||
fn paste(&mut self, _: &super::Paste, _w: &mut Window, cx: &mut Self::Context) {
|
||||
fn paste(&mut self, _: &Paste, _w: &mut Window, cx: &mut Self::Context) {
|
||||
self.internal.paste(cx);
|
||||
}
|
||||
|
||||
fn undo(&mut self, _: &super::Undo, _w: &mut Window, _cx: &mut Self::Context) {
|
||||
fn undo(&mut self, _: &Undo, _w: &mut Window, _cx: &mut Self::Context) {
|
||||
// TODO: STUB
|
||||
}
|
||||
|
||||
fn redo(&mut self, _: &super::Redo, _w: &mut Window, _cx: &mut Self::Context) {
|
||||
fn redo(&mut self, _: &Redo, _w: &mut Window, _cx: &mut Self::Context) {
|
||||
// TODO: STUB
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
pub mod actions;
|
||||
mod colors;
|
||||
mod cursor;
|
||||
mod element;
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
/// The key context used for input element keybindings.
|
||||
pub const DEFAULT_INPUT_CONTEXT: &str = "Input";
|
||||
|
||||
gpui::actions!(
|
||||
actions,
|
||||
[
|
||||
/// Delete the character before the cursor.
|
||||
Backspace,
|
||||
/// Delete the character after the cursor.
|
||||
Delete,
|
||||
/// Blur focus from the input.
|
||||
Escape,
|
||||
/// Delete the word before the cursor.
|
||||
DeleteWordLeft,
|
||||
/// Delete the word after the cursor.
|
||||
DeleteWordRight,
|
||||
/// Delete from the cursor to the beginning of the line.
|
||||
DeleteToBeginningOfLine,
|
||||
/// Delete from the cursor to the end of the line.
|
||||
DeleteToEndOfLine,
|
||||
/// Insert a tab character at the cursor position.
|
||||
Tab,
|
||||
/// Move the cursor one character to the left.
|
||||
Left,
|
||||
/// Move the cursor one character to the right.
|
||||
Right,
|
||||
/// Move the cursor up one visual line.
|
||||
Up,
|
||||
/// Move the cursor down one visual line.
|
||||
Down,
|
||||
/// Extend selection one character to the left.
|
||||
SelectLeft,
|
||||
/// Extend selection one character to the right.
|
||||
SelectRight,
|
||||
/// Extend selection up one visual line.
|
||||
SelectUp,
|
||||
/// Extend selection down one visual line.
|
||||
SelectDown,
|
||||
/// Select all text content.
|
||||
SelectAll,
|
||||
/// Move cursor to the start of the current line.
|
||||
Home,
|
||||
/// Move cursor to the end of the current line.
|
||||
End,
|
||||
/// Extend selection to the beginning of the content.
|
||||
SelectToBeginning,
|
||||
/// Extend selection to the end of the content.
|
||||
SelectToEnd,
|
||||
/// Move cursor to the beginning of the content.
|
||||
MoveToBeginning,
|
||||
/// Move cursor to the end of the content.
|
||||
MoveToEnd,
|
||||
/// Paste from clipboard at the cursor position.
|
||||
Paste,
|
||||
/// Cut selected text to clipboard.
|
||||
Cut,
|
||||
/// Copy selected text to clipboard.
|
||||
Copy,
|
||||
/// Insert a newline at the cursor position.
|
||||
Enter,
|
||||
/// Move cursor one word to the left.
|
||||
WordLeft,
|
||||
/// Move cursor one word to the right.
|
||||
WordRight,
|
||||
/// Extend selection one word to the left.
|
||||
SelectWordLeft,
|
||||
/// Extend selection one word to the right.
|
||||
SelectWordRight,
|
||||
/// Undo the last edit.
|
||||
Undo,
|
||||
/// Redo the last undone edit.
|
||||
Redo,
|
||||
]
|
||||
);
|
||||
|
||||
pub fn default_bindings() -> gpui::ActionBindingCollection {
|
||||
let mut bindings = gpui::ActionBindingCollection::default();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
bindings = bindings
|
||||
.with::<Backspace>("backspace")
|
||||
.with::<Delete>("delete")
|
||||
.with::<DeleteWordLeft>("alt-backspace")
|
||||
.with::<DeleteWordRight>("alt-delete")
|
||||
.with::<DeleteToBeginningOfLine>("cmd-backspace")
|
||||
.with::<DeleteToEndOfLine>("ctrl-k")
|
||||
.with::<Tab>("tab")
|
||||
.with::<Enter>("enter")
|
||||
.with::<Left>("left")
|
||||
.with::<Right>("right")
|
||||
.with::<Up>("up")
|
||||
.with::<Down>("down")
|
||||
.with::<SelectLeft>("shift-left")
|
||||
.with::<SelectRight>("shift-right")
|
||||
.with::<SelectUp>("shift-up")
|
||||
.with::<SelectDown>("shift-down")
|
||||
.with::<SelectAll>("cmd-a")
|
||||
// Mac keyboards don't have Home/End keys, so cmd-left/right are standard
|
||||
.with::<Home>("cmd-left")
|
||||
.with::<End>("cmd-right")
|
||||
.with::<MoveToBeginning>("cmd-up")
|
||||
.with::<MoveToEnd>("cmd-down")
|
||||
.with::<SelectToBeginning>("cmd-shift-up")
|
||||
.with::<SelectToEnd>("cmd-shift-down")
|
||||
.with::<WordLeft>("alt-left")
|
||||
.with::<WordRight>("alt-right")
|
||||
.with::<SelectWordLeft>("alt-shift-left")
|
||||
.with::<SelectWordRight>("alt-shift-right")
|
||||
.with::<Copy>("cmd-c")
|
||||
.with::<Cut>("cmd-x")
|
||||
.with::<Paste>("cmd-v")
|
||||
.with::<Undo>("cmd-z")
|
||||
.with::<Redo>("cmd-shift-z")
|
||||
.with::<Escape>("escape");
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
{
|
||||
bindings = bindings
|
||||
.with::<Backspace>("backspace")
|
||||
.with::<Delete>("delete")
|
||||
.with::<DeleteWordLeft>("ctrl-backspace")
|
||||
.with::<DeleteWordRight>("ctrl-delete")
|
||||
.with::<DeleteToBeginningOfLine>("ctrl-shift-backspace")
|
||||
.with::<DeleteToEndOfLine>("ctrl-shift-delete")
|
||||
.with::<Tab>("tab")
|
||||
.with::<Enter>("enter")
|
||||
.with::<Left>("left")
|
||||
.with::<Right>("right")
|
||||
.with::<Up>("up")
|
||||
.with::<Down>("down")
|
||||
.with::<SelectLeft>("shift-left")
|
||||
.with::<SelectRight>("shift-right")
|
||||
.with::<SelectUp>("shift-up")
|
||||
.with::<SelectDown>("shift-down")
|
||||
.with::<SelectAll>("ctrl-a")
|
||||
.with::<Home>("home")
|
||||
.with::<End>("end")
|
||||
.with::<MoveToBeginning>("ctrl-home")
|
||||
.with::<MoveToEnd>("ctrl-end")
|
||||
.with::<SelectToBeginning>("ctrl-shift-home")
|
||||
.with::<SelectToEnd>("ctrl-shift-end")
|
||||
.with::<WordLeft>("ctrl-left")
|
||||
.with::<WordRight>("ctrl-right")
|
||||
.with::<SelectWordLeft>("ctrl-shift-left")
|
||||
.with::<SelectWordRight>("ctrl-shift-right")
|
||||
.with::<Copy>("ctrl-c")
|
||||
.with::<Cut>("ctrl-x")
|
||||
.with::<Paste>("ctrl-v")
|
||||
.with::<Undo>("ctrl-z")
|
||||
.with::<Redo>("ctrl-shift-z")
|
||||
.with::<Escape>("escape");
|
||||
}
|
||||
|
||||
bindings
|
||||
}
|
||||
@@ -31,7 +31,7 @@ impl Input {
|
||||
};
|
||||
input.register_actions();
|
||||
input
|
||||
.key_context(super::actions::DEFAULT_INPUT_CONTEXT)
|
||||
.key_context(crate::editable_text::actions::DEFAULT_INPUT_CONTEXT)
|
||||
.track_focus(&focus_handle)
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ impl Input {
|
||||
register_action(&mut self.interactivity, &self.input, InputState::redo);
|
||||
|
||||
self.interactivity
|
||||
.on_action::<super::actions::Escape>(|_action, window, _cx| {
|
||||
.on_action::<crate::editable_text::actions::Escape>(|_action, window, _cx| {
|
||||
window.blur();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::actions::*;
|
||||
use crate::editable_text::actions::*;
|
||||
use crate::input::{InputLayoutStyle, InputStorage};
|
||||
use gpui::{
|
||||
App, ClipboardItem, Context, EntityId, EntityInputHandler, EventEmitter, FocusHandle,
|
||||
|
||||
Reference in New Issue
Block a user