replace multiline with InputLayout enum
This commit is contained in:
@@ -3,6 +3,7 @@ mod colors;
|
||||
mod cursor;
|
||||
mod element;
|
||||
mod history;
|
||||
mod layout;
|
||||
mod paint;
|
||||
mod state;
|
||||
mod state_input_handler;
|
||||
@@ -12,4 +13,5 @@ pub use colors::*;
|
||||
pub(self) use cursor::*;
|
||||
pub use element::*;
|
||||
pub(self) use history::*;
|
||||
pub use layout::*;
|
||||
pub use state::*;
|
||||
|
||||
@@ -15,7 +15,6 @@ pub struct Input {
|
||||
pub(super) interactivity: Interactivity,
|
||||
pub(super) placeholder: Option<SharedString>,
|
||||
pub(super) colors: PaintColors,
|
||||
pub(super) multiline: bool,
|
||||
}
|
||||
|
||||
impl Input {
|
||||
@@ -27,7 +26,6 @@ impl Input {
|
||||
interactivity: Interactivity::new(),
|
||||
placeholder: None,
|
||||
colors: PaintColors::default(),
|
||||
multiline: false,
|
||||
};
|
||||
input.register_actions();
|
||||
input
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum InputLayout {
|
||||
SingleLine,
|
||||
MultiLine,
|
||||
}
|
||||
|
||||
impl InputLayout {
|
||||
pub(super) fn sanitize_content<'s>(&self, content: &'s str) -> std::borrow::Cow<'s, str> {
|
||||
match self {
|
||||
// Strip newlines for single-line input
|
||||
Self::SingleLine => {
|
||||
std::borrow::Cow::Owned(content.replace('\n', " ").replace('\r', ""))
|
||||
}
|
||||
Self::MultiLine => std::borrow::Cow::Borrowed(content),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn axis(&self) -> gpui::Axis {
|
||||
match self {
|
||||
Self::SingleLine => gpui::Axis::Horizontal,
|
||||
Self::MultiLine => gpui::Axis::Vertical,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::input::{Input, InputLineLayout, InputState, PaintColors};
|
||||
use gpui::{
|
||||
App, Bounds, ContentMask, CursorStyle, DispatchPhase, Element, ElementId, ElementInputHandler,
|
||||
Entity, FocusHandle, Focusable, GlobalElementId, Hitbox, HitboxBehavior, Hsla,
|
||||
InspectorElementId, LayoutId, Length, MouseButton, MouseDownEvent, MouseMoveEvent,
|
||||
Along, App, Bounds, ContentMask, CursorStyle, DispatchPhase, Element, ElementId,
|
||||
ElementInputHandler, Entity, FocusHandle, Focusable, GlobalElementId, Hitbox, HitboxBehavior,
|
||||
Hsla, InspectorElementId, LayoutId, Length, MouseButton, MouseDownEvent, MouseMoveEvent,
|
||||
MouseUpEvent, Pixels, Point, ScrollWheelEvent, SharedString, TextAlign, TextRun, TextStyle,
|
||||
Window, WrappedLine, fill, point, px, relative, size,
|
||||
};
|
||||
@@ -39,7 +39,6 @@ impl Element for Input {
|
||||
cx: &mut App,
|
||||
) -> (LayoutId, Self::RequestLayoutState) {
|
||||
let mut resolved_text_style = None;
|
||||
let multiline = self.multiline;
|
||||
|
||||
let layout_id = self.interactivity.request_layout(
|
||||
global_id,
|
||||
@@ -47,11 +46,12 @@ impl Element for Input {
|
||||
window,
|
||||
cx,
|
||||
|element_style, window, cx| {
|
||||
let layout = self.input.read(cx).get_layout();
|
||||
window.with_text_style(element_style.text_style().cloned(), |window| {
|
||||
resolved_text_style = Some(window.text_style());
|
||||
|
||||
let mut layout_style = element_style.clone();
|
||||
if multiline {
|
||||
if matches!(layout, super::InputLayout::MultiLine) {
|
||||
if let Length::Auto = layout_style.size.width {
|
||||
layout_style.size.width = relative(1.).into();
|
||||
}
|
||||
@@ -85,10 +85,9 @@ impl Element for Input {
|
||||
.text_style
|
||||
.line_height_in_pixels(window.rem_size());
|
||||
|
||||
let wrap_width = if self.multiline {
|
||||
bounds.size.width
|
||||
} else {
|
||||
px(100000.)
|
||||
let wrap_width = match self.input.read(cx).get_layout() {
|
||||
super::InputLayout::SingleLine => px(100000.),
|
||||
super::InputLayout::MultiLine => bounds.size.width,
|
||||
};
|
||||
|
||||
self.input.update(cx, |input, _cx| {
|
||||
@@ -137,7 +136,7 @@ impl Element for Input {
|
||||
let input = self.input.clone();
|
||||
let placeholder = self.placeholder.clone();
|
||||
let text_style = layout_state.text_style.clone();
|
||||
let multiline = self.multiline;
|
||||
let layout = input.read(cx).get_layout();
|
||||
let is_focused = focus_handle.is_focused(window);
|
||||
let cursor_visible = self
|
||||
.input
|
||||
@@ -152,34 +151,31 @@ impl Element for Input {
|
||||
window,
|
||||
cx,
|
||||
|_style, window, cx| {
|
||||
handle_mouse(&input, bounds, multiline, window, cx);
|
||||
handle_mouse(&input, bounds, layout.axis(), window, cx);
|
||||
|
||||
window.with_content_mask(Some(ContentMask { bounds }), |window| {
|
||||
if multiline {
|
||||
paint_multiline(
|
||||
&input,
|
||||
&focus_handle,
|
||||
bounds,
|
||||
&text_style,
|
||||
placeholder.as_ref(),
|
||||
&colors,
|
||||
cursor_visible,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
} else {
|
||||
paint_singleline(
|
||||
&input,
|
||||
&focus_handle,
|
||||
bounds,
|
||||
&text_style,
|
||||
placeholder.as_ref(),
|
||||
&colors,
|
||||
cursor_visible,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
window.with_content_mask(Some(ContentMask { bounds }), |window| match layout {
|
||||
super::InputLayout::SingleLine => paint_singleline(
|
||||
&input,
|
||||
&focus_handle,
|
||||
bounds,
|
||||
&text_style,
|
||||
placeholder.as_ref(),
|
||||
&colors,
|
||||
cursor_visible,
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
super::InputLayout::MultiLine => paint_multiline(
|
||||
&input,
|
||||
&focus_handle,
|
||||
bounds,
|
||||
&text_style,
|
||||
placeholder.as_ref(),
|
||||
&colors,
|
||||
cursor_visible,
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -190,20 +186,20 @@ impl Element for Input {
|
||||
fn handle_mouse(
|
||||
input: &Entity<InputState>,
|
||||
bounds: Bounds<Pixels>,
|
||||
multiline: bool,
|
||||
axis: gpui::Axis,
|
||||
window: &mut Window,
|
||||
cx: &App,
|
||||
) {
|
||||
mouse_down(input.clone(), bounds, multiline, window);
|
||||
mouse_down(input.clone(), bounds, axis, window);
|
||||
mouse_up(input.clone(), window);
|
||||
mouse_move(input.clone(), bounds, multiline, window);
|
||||
handle_scroll(input.clone(), bounds, multiline, window, cx);
|
||||
mouse_move(input.clone(), bounds, axis, window);
|
||||
handle_scroll(input.clone(), bounds, axis, window, cx);
|
||||
}
|
||||
|
||||
fn mouse_down(
|
||||
input: Entity<InputState>,
|
||||
bounds: Bounds<Pixels>,
|
||||
multiline: bool,
|
||||
axis: gpui::Axis,
|
||||
window: &mut Window,
|
||||
) {
|
||||
window.on_mouse_event(move |event: &MouseDownEvent, phase, window, cx| {
|
||||
@@ -219,7 +215,7 @@ fn mouse_down(
|
||||
|
||||
input.update(cx, |input, cx| {
|
||||
let text_position =
|
||||
screen_to_text_position(event.position, bounds, input.scroll_offset, multiline);
|
||||
screen_to_text_position(event.position, bounds, input.scroll_offset, axis);
|
||||
input.on_mouse_down(
|
||||
text_position,
|
||||
event.click_count,
|
||||
@@ -249,7 +245,7 @@ fn mouse_up(input: Entity<InputState>, window: &mut Window) {
|
||||
fn mouse_move(
|
||||
input: Entity<InputState>,
|
||||
bounds: Bounds<Pixels>,
|
||||
multiline: bool,
|
||||
axis: gpui::Axis,
|
||||
window: &mut Window,
|
||||
) {
|
||||
window.on_mouse_event(move |event: &MouseMoveEvent, phase, _window, cx| {
|
||||
@@ -259,7 +255,7 @@ fn mouse_move(
|
||||
|
||||
input.update(cx, |input, cx| {
|
||||
let text_position =
|
||||
screen_to_text_position(event.position, bounds, input.scroll_offset, multiline);
|
||||
screen_to_text_position(event.position, bounds, input.scroll_offset, axis);
|
||||
input.on_mouse_move(text_position, cx);
|
||||
});
|
||||
});
|
||||
@@ -268,23 +264,20 @@ fn mouse_move(
|
||||
fn handle_scroll(
|
||||
input: Entity<InputState>,
|
||||
bounds: Bounds<Pixels>,
|
||||
multiline: bool,
|
||||
axis: gpui::Axis,
|
||||
window: &mut Window,
|
||||
cx: &App,
|
||||
) {
|
||||
let max_scroll = if multiline {
|
||||
let total_height = input.read(cx).total_content_height();
|
||||
(total_height - bounds.size.height).max(px(0.))
|
||||
} else {
|
||||
let text_width = input
|
||||
.read(cx)
|
||||
.line_layouts
|
||||
.first()
|
||||
.and_then(|l| l.wrapped_line.as_ref())
|
||||
.map(|w| w.width())
|
||||
.unwrap_or(px(0.));
|
||||
(text_width - bounds.size.width).max(px(0.))
|
||||
let content_size = match axis {
|
||||
gpui::Axis::Horizontal => {
|
||||
let state = input.read(cx);
|
||||
let line = state.line_layouts.first();
|
||||
let line = line.and_then(|l| l.wrapped_line.as_ref());
|
||||
line.map(|w| w.width()).unwrap_or(px(0.))
|
||||
}
|
||||
gpui::Axis::Vertical => input.read(cx).total_content_height(),
|
||||
};
|
||||
let max_scroll = (content_size - bounds.size.along(axis)).max(px(0.));
|
||||
|
||||
window.on_mouse_event(move |event: &ScrollWheelEvent, phase, _window, cx| {
|
||||
if phase != DispatchPhase::Bubble {
|
||||
@@ -296,17 +289,17 @@ fn handle_scroll(
|
||||
|
||||
let pixel_delta = event.delta.pixel_delta(px(20.));
|
||||
input.update(cx, |input, cx| {
|
||||
if multiline {
|
||||
input.scroll_offset =
|
||||
(input.scroll_offset - pixel_delta.y).clamp(px(0.), max_scroll);
|
||||
} else {
|
||||
let delta = if pixel_delta.x.abs() > pixel_delta.y.abs() {
|
||||
pixel_delta.x
|
||||
} else {
|
||||
pixel_delta.y
|
||||
};
|
||||
input.scroll_offset = (input.scroll_offset - delta).clamp(px(0.), max_scroll);
|
||||
}
|
||||
let delta = match axis {
|
||||
gpui::Axis::Horizontal => pixel_delta.y,
|
||||
gpui::Axis::Vertical => {
|
||||
if pixel_delta.x.abs() > pixel_delta.y.abs() {
|
||||
pixel_delta.x
|
||||
} else {
|
||||
pixel_delta.y
|
||||
}
|
||||
}
|
||||
};
|
||||
input.scroll_offset = (input.scroll_offset - delta).clamp(px(0.), max_scroll);
|
||||
cx.notify();
|
||||
});
|
||||
});
|
||||
@@ -318,19 +311,10 @@ fn screen_to_text_position(
|
||||
screen_position: Point<Pixels>,
|
||||
bounds: Bounds<Pixels>,
|
||||
scroll_offset: Pixels,
|
||||
multiline: bool,
|
||||
axis: gpui::Axis,
|
||||
) -> Point<Pixels> {
|
||||
if multiline {
|
||||
point(
|
||||
screen_position.x - bounds.origin.x,
|
||||
screen_position.y - bounds.origin.y + scroll_offset,
|
||||
)
|
||||
} else {
|
||||
point(
|
||||
screen_position.x - bounds.origin.x + scroll_offset,
|
||||
screen_position.y - bounds.origin.y,
|
||||
)
|
||||
}
|
||||
let point = screen_position - bounds.origin;
|
||||
point.apply_along(axis, |pos| pos + scroll_offset)
|
||||
}
|
||||
|
||||
fn paint_multiline(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::actions::*;
|
||||
use crate::input::unicode::UnicodeString;
|
||||
use crate::input::{InputLayout, unicode::UnicodeString};
|
||||
use gpui::{
|
||||
App, AppContext, ClipboardItem, Context, Entity, EntityId, EntityInputHandler, EventEmitter,
|
||||
FocusHandle, Focusable, Pixels, Point, SharedString, Subscription, TextRun, TextStyle, Window,
|
||||
@@ -54,7 +54,7 @@ pub struct InputState {
|
||||
pub(super) scroll_offset: Pixels,
|
||||
pub(super) available_height: Pixels,
|
||||
pub(super) available_width: Pixels,
|
||||
pub(super) multiline: bool,
|
||||
pub(super) layout: InputLayout,
|
||||
/// Stack of previous states for undo.
|
||||
undo_stack: Vec<super::HistoryEntry>,
|
||||
/// Stack of undone states for redo.
|
||||
@@ -121,7 +121,7 @@ impl InputState {
|
||||
scroll_offset: px(0.),
|
||||
available_height: px(0.),
|
||||
available_width: px(0.),
|
||||
multiline: false,
|
||||
layout: InputLayout::SingleLine,
|
||||
undo_stack: Vec::new(),
|
||||
cached_utf16_len: None,
|
||||
redo_stack: Vec::new(),
|
||||
@@ -201,16 +201,14 @@ impl InputState {
|
||||
&mut self.content
|
||||
}
|
||||
|
||||
pub fn get_layout(&self) -> InputLayout {
|
||||
self.layout
|
||||
}
|
||||
|
||||
/// Sets the text content, resetting selection to the beginning.
|
||||
/// This clears the undo/redo history.
|
||||
pub fn set_content(&mut self, content: impl Into<String>, cx: &mut Context<Self>) {
|
||||
let content = content.into();
|
||||
self.content = if self.multiline {
|
||||
content
|
||||
} else {
|
||||
// Strip newlines for single-line input
|
||||
content.replace('\n', " ").replace('\r', "")
|
||||
};
|
||||
pub fn set_content(&mut self, content: impl AsRef<str>, cx: &mut Context<Self>) {
|
||||
self.content = self.layout.sanitize_content(content.as_ref()).to_string();
|
||||
self.selected_range = 0..0;
|
||||
self.selection_reversed = false;
|
||||
self.marked_range = None;
|
||||
@@ -375,13 +373,7 @@ impl InputState {
|
||||
.unwrap_or(self.selected_range.clone());
|
||||
let range = range.start.min(self.content.len())..range.end.min(self.content.len());
|
||||
|
||||
let sanitized_text;
|
||||
let text_to_insert = if self.multiline {
|
||||
text
|
||||
} else {
|
||||
sanitized_text = text.replace('\n', " ").replace('\r', "");
|
||||
&sanitized_text
|
||||
};
|
||||
let text_to_insert = self.layout.sanitize_content(text);
|
||||
|
||||
// Record patch for undo before modifying content
|
||||
self.push_undo_patch(range.clone(), text_to_insert.len());
|
||||
@@ -396,7 +388,7 @@ impl InputState {
|
||||
self.cached_utf16_len = Some(cached_len - removed_utf16_len + added_utf16_len);
|
||||
}
|
||||
|
||||
self.content.replace_range(range.clone(), text_to_insert);
|
||||
self.content.replace_range(range.clone(), &text_to_insert);
|
||||
self.selected_range =
|
||||
range.start + text_to_insert.len()..range.start + text_to_insert.len();
|
||||
self.marked_range.take();
|
||||
@@ -478,38 +470,44 @@ impl InputState {
|
||||
|
||||
pub(crate) fn up(&mut self, _: &Up, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.pause_cursor_blink(cx);
|
||||
if !self.multiline {
|
||||
// In single-line mode, up moves to start
|
||||
self.selected_range = 0..0;
|
||||
self.selection_reversed = false;
|
||||
self.scroll_to_cursor();
|
||||
cx.notify();
|
||||
return;
|
||||
}
|
||||
if let Some(new_offset) = self.move_vertically(self.cursor_offset(), -1) {
|
||||
self.selected_range = new_offset..new_offset;
|
||||
self.selection_reversed = false;
|
||||
self.scroll_to_cursor();
|
||||
cx.notify();
|
||||
match self.layout {
|
||||
InputLayout::SingleLine => {
|
||||
// In single-line mode, up moves to start
|
||||
self.selected_range = 0..0;
|
||||
self.selection_reversed = false;
|
||||
self.scroll_to_cursor();
|
||||
cx.notify();
|
||||
}
|
||||
InputLayout::MultiLine => {
|
||||
if let Some(new_offset) = self.move_vertically(self.cursor_offset(), -1) {
|
||||
self.selected_range = new_offset..new_offset;
|
||||
self.selection_reversed = false;
|
||||
self.scroll_to_cursor();
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn down(&mut self, _: &Down, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.pause_cursor_blink(cx);
|
||||
if !self.multiline {
|
||||
// In single-line mode, down moves to end
|
||||
let end = self.content.len();
|
||||
self.selected_range = end..end;
|
||||
self.selection_reversed = false;
|
||||
self.scroll_to_cursor();
|
||||
cx.notify();
|
||||
return;
|
||||
}
|
||||
if let Some(new_offset) = self.move_vertically(self.cursor_offset(), 1) {
|
||||
self.selected_range = new_offset..new_offset;
|
||||
self.selection_reversed = false;
|
||||
self.scroll_to_cursor();
|
||||
cx.notify();
|
||||
match self.layout {
|
||||
InputLayout::SingleLine => {
|
||||
// In single-line mode, down moves to end
|
||||
let end = self.content.len();
|
||||
self.selected_range = end..end;
|
||||
self.selection_reversed = false;
|
||||
self.scroll_to_cursor();
|
||||
cx.notify();
|
||||
}
|
||||
InputLayout::MultiLine => {
|
||||
if let Some(new_offset) = self.move_vertically(self.cursor_offset(), 1) {
|
||||
self.selected_range = new_offset..new_offset;
|
||||
self.selection_reversed = false;
|
||||
self.scroll_to_cursor();
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,23 +521,26 @@ impl InputState {
|
||||
|
||||
pub(crate) fn select_up(&mut self, _: &SelectUp, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.pause_cursor_blink(cx);
|
||||
if !self.multiline {
|
||||
// In single-line mode, select_up selects to start
|
||||
self.select_to(0, cx);
|
||||
return;
|
||||
}
|
||||
if let Some(new_offset) = self.move_vertically(self.cursor_offset(), -1) {
|
||||
if self.selection_reversed {
|
||||
self.selected_range.start = new_offset;
|
||||
} else {
|
||||
self.selected_range.end = new_offset;
|
||||
match self.layout {
|
||||
InputLayout::SingleLine => {
|
||||
// In single-line mode, select_up selects to start
|
||||
self.select_to(0, cx);
|
||||
}
|
||||
if self.selected_range.end < self.selected_range.start {
|
||||
self.selection_reversed = !self.selection_reversed;
|
||||
self.selected_range = self.selected_range.end..self.selected_range.start;
|
||||
InputLayout::MultiLine => {
|
||||
if let Some(new_offset) = self.move_vertically(self.cursor_offset(), -1) {
|
||||
if self.selection_reversed {
|
||||
self.selected_range.start = new_offset;
|
||||
} else {
|
||||
self.selected_range.end = new_offset;
|
||||
}
|
||||
if self.selected_range.end < self.selected_range.start {
|
||||
self.selection_reversed = !self.selection_reversed;
|
||||
self.selected_range = self.selected_range.end..self.selected_range.start;
|
||||
}
|
||||
self.scroll_to_cursor();
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
self.scroll_to_cursor();
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,23 +551,26 @@ impl InputState {
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.pause_cursor_blink(cx);
|
||||
if !self.multiline {
|
||||
// In single-line mode, select_down selects to end
|
||||
self.select_to(self.content.len(), cx);
|
||||
return;
|
||||
}
|
||||
if let Some(new_offset) = self.move_vertically(self.cursor_offset(), 1) {
|
||||
if self.selection_reversed {
|
||||
self.selected_range.start = new_offset;
|
||||
} else {
|
||||
self.selected_range.end = new_offset;
|
||||
match self.layout {
|
||||
InputLayout::SingleLine => {
|
||||
// In single-line mode, select_down selects to end
|
||||
self.select_to(self.content.len(), cx);
|
||||
}
|
||||
if self.selected_range.end < self.selected_range.start {
|
||||
self.selection_reversed = !self.selection_reversed;
|
||||
self.selected_range = self.selected_range.end..self.selected_range.start;
|
||||
InputLayout::MultiLine => {
|
||||
if let Some(new_offset) = self.move_vertically(self.cursor_offset(), 1) {
|
||||
if self.selection_reversed {
|
||||
self.selected_range.start = new_offset;
|
||||
} else {
|
||||
self.selected_range.end = new_offset;
|
||||
}
|
||||
if self.selected_range.end < self.selected_range.start {
|
||||
self.selection_reversed = !self.selection_reversed;
|
||||
self.selected_range = self.selected_range.end..self.selected_range.start;
|
||||
}
|
||||
self.scroll_to_cursor();
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
self.scroll_to_cursor();
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -642,7 +646,7 @@ impl InputState {
|
||||
}
|
||||
|
||||
pub(crate) fn enter(&mut self, _: &Enter, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if self.multiline {
|
||||
if matches!(&self.layout, InputLayout::MultiLine) {
|
||||
self.replace_text_in_range(None, "\n", window, cx);
|
||||
}
|
||||
}
|
||||
@@ -714,15 +718,11 @@ impl InputState {
|
||||
}
|
||||
|
||||
pub(crate) fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if let Some(text) = cx.read_from_clipboard().and_then(|item| item.text()) {
|
||||
if self.multiline {
|
||||
self.replace_text_in_range(None, &text, window, cx);
|
||||
} else {
|
||||
// Strip newlines for single-line input
|
||||
let text = text.replace('\n', " ").replace('\r', "");
|
||||
self.replace_text_in_range(None, &text, window, cx);
|
||||
}
|
||||
}
|
||||
let Some(text) = cx.read_from_clipboard().and_then(|item| item.text()) else {
|
||||
return;
|
||||
};
|
||||
let text = self.layout.sanitize_content(&text);
|
||||
self.replace_text_in_range(None, &text, window, cx);
|
||||
}
|
||||
|
||||
pub(crate) fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
|
||||
@@ -986,11 +986,9 @@ impl InputState {
|
||||
}
|
||||
|
||||
let cursor_offset = self.cursor_offset();
|
||||
|
||||
if self.multiline {
|
||||
self.scroll_to_cursor_vertical(cursor_offset);
|
||||
} else {
|
||||
self.scroll_to_cursor_horizontal(cursor_offset);
|
||||
match self.layout {
|
||||
InputLayout::SingleLine => self.scroll_to_cursor_horizontal(cursor_offset),
|
||||
InputLayout::MultiLine => self.scroll_to_cursor_vertical(cursor_offset),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,14 +59,7 @@ impl EntityInputHandler for super::InputState {
|
||||
|
||||
let range = range.start.min(self.content().len())..range.end.min(self.content().len());
|
||||
|
||||
// Strip newlines for single-line input
|
||||
let sanitized_text;
|
||||
let text_to_insert = if self.multiline {
|
||||
new_text
|
||||
} else {
|
||||
sanitized_text = new_text.replace('\n', " ").replace('\r', "");
|
||||
&sanitized_text
|
||||
};
|
||||
let text_to_insert = self.layout.sanitize_content(new_text);
|
||||
|
||||
// Record patch for undo before modifying content
|
||||
self.push_undo_patch(range.clone(), text_to_insert.len());
|
||||
@@ -82,7 +75,7 @@ impl EntityInputHandler for super::InputState {
|
||||
}
|
||||
|
||||
self.content_mut()
|
||||
.replace_range(range.clone(), text_to_insert);
|
||||
.replace_range(range.clone(), &text_to_insert);
|
||||
self.selected_range =
|
||||
range.start + text_to_insert.len()..range.start + text_to_insert.len();
|
||||
self.marked_range.take();
|
||||
@@ -108,14 +101,7 @@ impl EntityInputHandler for super::InputState {
|
||||
|
||||
let range = range.start.min(self.content().len())..range.end.min(self.content().len());
|
||||
|
||||
// Strip newlines for single-line input
|
||||
let sanitized_text;
|
||||
let text_to_insert = if self.multiline {
|
||||
new_text
|
||||
} else {
|
||||
sanitized_text = new_text.replace('\n', " ").replace('\r', "");
|
||||
&sanitized_text
|
||||
};
|
||||
let text_to_insert = self.layout.sanitize_content(new_text);
|
||||
|
||||
// Update cached UTF-16 length incrementally if available
|
||||
if let Some(cached_len) = self.cached_utf16_len {
|
||||
@@ -128,7 +114,7 @@ impl EntityInputHandler for super::InputState {
|
||||
}
|
||||
|
||||
self.content_mut()
|
||||
.replace_range(range.clone(), text_to_insert);
|
||||
.replace_range(range.clone(), &text_to_insert);
|
||||
|
||||
if !text_to_insert.is_empty() {
|
||||
self.marked_range = Some(range.start..range.start + text_to_insert.len());
|
||||
|
||||
Reference in New Issue
Block a user