use super::*;
impl TextField {
pub(super) fn left(&mut self, _: &Left, _: &mut Window, cx: &mut Context<Self>) {
if self.selected_range.is_empty() {
self.move_to(self.previous_boundary(self.cursor_offset()), cx);
} else {
self.move_to(self.selected_range.start, cx)
}
}
pub(super) fn right(&mut self, _: &Right, _: &mut Window, cx: &mut Context<Self>) {
if self.selected_range.is_empty() {
self.move_to(self.next_boundary(self.selected_range.end), cx);
} else {
self.move_to(self.selected_range.end, cx)
}
}
pub(super) fn select_left(&mut self, _: &SelectLeft, _: &mut Window, cx: &mut Context<Self>) {
self.select_to(self.previous_boundary(self.cursor_offset()), cx);
}
pub(super) fn select_right(&mut self, _: &SelectRight, _: &mut Window, cx: &mut Context<Self>) {
self.select_to(self.next_boundary(self.cursor_offset()), cx);
}
pub(super) fn select_all(&mut self, _: &SelectAll, _: &mut Window, cx: &mut Context<Self>) {
self.move_to(0, cx);
self.select_to(self.content.len(), cx)
}
pub(super) fn home(&mut self, _: &Home, _: &mut Window, cx: &mut Context<Self>) {
self.move_to(line_start(&self.content, self.cursor_offset()), cx);
}
pub(super) fn end(&mut self, _: &End, _: &mut Window, cx: &mut Context<Self>) {
self.move_to(line_end(&self.content, self.cursor_offset()), cx);
}
pub(super) fn select_home(&mut self, _: &SelectHome, _: &mut Window, cx: &mut Context<Self>) {
self.select_to(line_start(&self.content, self.cursor_offset()), cx);
}
pub(super) fn select_end(&mut self, _: &SelectEnd, _: &mut Window, cx: &mut Context<Self>) {
self.select_to(line_end(&self.content, self.cursor_offset()), cx);
}
pub(super) fn up(&mut self, _: &Up, _: &mut Window, cx: &mut Context<Self>) {
self.vertical(-1, false, cx);
}
pub(super) fn down(&mut self, _: &Down, _: &mut Window, cx: &mut Context<Self>) {
self.vertical(1, false, cx);
}
pub(super) fn select_up(&mut self, _: &SelectUp, _: &mut Window, cx: &mut Context<Self>) {
self.vertical(-1, true, cx);
}
pub(super) fn select_down(&mut self, _: &SelectDown, _: &mut Window, cx: &mut Context<Self>) {
self.vertical(1, true, cx);
}
pub(super) fn vertical(&mut self, rows: i32, extend: bool, cx: &mut Context<Self>) {
if self.last_layout.is_empty() {
return;
}
let line_height = self.line_height();
let Some(at) = self.position_of(self.cursor_offset(), line_height) else {
return;
};
let goal = self.goal_x.unwrap_or(at.x);
let target = at.y + line_height * rows as f32;
let offset = if target < px(0.) {
0
} else {
self.offset_at(gpui::point(goal, target), line_height)
};
if extend {
self.select_to(offset, cx);
} else {
self.move_to(offset, cx);
}
self.goal_x = Some(goal);
}
pub(super) fn insert_newline(
&mut self,
_: &InsertNewline,
window: &mut Window,
cx: &mut Context<Self>,
) {
if self.shape.is_multiline() {
self.replace_text_in_range(None, "\n", window, cx);
}
}
pub(super) fn word_left(&mut self, _: &WordLeft, _: &mut Window, cx: &mut Context<Self>) {
self.move_to(
previous_word_boundary(&self.content, self.cursor_offset()),
cx,
);
}
pub(super) fn word_right(&mut self, _: &WordRight, _: &mut Window, cx: &mut Context<Self>) {
self.move_to(next_word_boundary(&self.content, self.cursor_offset()), cx);
}
pub(super) fn select_word_left(
&mut self,
_: &SelectWordLeft,
_: &mut Window,
cx: &mut Context<Self>,
) {
self.select_to(
previous_word_boundary(&self.content, self.cursor_offset()),
cx,
);
}
pub(super) fn select_word_right(
&mut self,
_: &SelectWordRight,
_: &mut Window,
cx: &mut Context<Self>,
) {
self.select_to(next_word_boundary(&self.content, self.cursor_offset()), cx);
}
pub(super) fn delete_word_left(
&mut self,
_: &DeleteWordLeft,
window: &mut Window,
cx: &mut Context<Self>,
) {
if self.selected_range.is_empty() {
self.select_to(
previous_word_boundary(&self.content, self.cursor_offset()),
cx,
);
}
self.replace_text_in_range(None, "", window, cx)
}
pub(super) fn delete_word_right(
&mut self,
_: &DeleteWordRight,
window: &mut Window,
cx: &mut Context<Self>,
) {
if self.selected_range.is_empty() {
self.select_to(next_word_boundary(&self.content, self.cursor_offset()), cx);
}
self.replace_text_in_range(None, "", window, cx)
}
pub(super) fn delete_to_line_start(
&mut self,
_: &DeleteToLineStart,
window: &mut Window,
cx: &mut Context<Self>,
) {
if self.selected_range.is_empty() {
self.select_to(line_start(&self.content, self.cursor_offset()), cx);
}
self.replace_text_in_range(None, "", window, cx)
}
pub(super) fn delete_to_line_end(
&mut self,
_: &DeleteToLineEnd,
window: &mut Window,
cx: &mut Context<Self>,
) {
if self.selected_range.is_empty() {
self.select_to(line_end(&self.content, self.cursor_offset()), cx);
}
self.replace_text_in_range(None, "", window, cx)
}
pub(super) fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
if self.selected_range.is_empty() {
let prev = self.previous_boundary(self.cursor_offset());
if self.cursor_offset() == prev {
return;
}
self.select_to(prev, cx)
}
self.replace_text_in_range(None, "", window, cx)
}
pub(super) fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
if self.selected_range.is_empty() {
let next = self.next_boundary(self.cursor_offset());
if self.cursor_offset() == next {
return;
}
self.select_to(next, cx)
}
self.replace_text_in_range(None, "", window, cx)
}
pub(super) fn on_mouse_down(
&mut self,
event: &MouseDownEvent,
_window: &mut Window,
cx: &mut Context<Self>,
) {
let line_height = self.line_height();
let offset = self.index_for_mouse_position(event.position, line_height);
if event.modifiers.shift {
self.selecting = Some((
Granularity::Char,
self.anchor_offset()..self.anchor_offset(),
));
return self.select_to(offset, cx);
}
let unit = Granularity::of_clicks(event.click_count);
let span = self.unit_at(unit, event.position, offset, line_height);
self.selecting = Some((unit, span.clone()));
self.select_span(span.start, span.end, cx);
}
pub(super) fn on_right_mouse_down(
&mut self,
event: &MouseDownEvent,
window: &mut Window,
cx: &mut Context<Self>,
) {
let offset = self.index_for_mouse_position(event.position, self.line_height());
let range = &self.selected_range;
if range.is_empty() || offset < range.start || offset > range.end {
self.move_to(offset, cx);
}
let items = crate::menu::edit_items(
!self.selected_range.is_empty(),
cx.read_from_clipboard()
.and_then(|item| item.text())
.is_some(),
[&Cut, &Copy, &Paste, &SelectAll],
window,
);
self.menu.open(event.position, items, window, cx);
window.prevent_default();
cx.stop_propagation();
}
pub(super) fn edit_menu(
&self,
window: &mut Window,
cx: &mut Context<Self>,
) -> Option<gpui::AnyElement> {
self.menu.render(
"text-field-menu",
|this| &mut this.menu,
|this, path, window, cx| match crate::menu::Edit::at(path) {
Some(crate::menu::Edit::Cut) => this.cut(&Cut, window, cx),
Some(crate::menu::Edit::Copy) => this.copy(&Copy, window, cx),
Some(crate::menu::Edit::Paste) => this.paste(&Paste, window, cx),
Some(crate::menu::Edit::SelectAll) => this.select_all(&SelectAll, window, cx),
None => {}
},
window,
cx,
)
}
fn unit_at(
&self,
unit: Granularity,
position: Point<Pixels>,
offset: usize,
line_height: Pixels,
) -> Range<usize> {
match self.chip_at(position, line_height) {
Some(chip) if unit != Granularity::Char => chip,
_ => unit.around(&self.content, offset),
}
}
pub(super) fn on_mouse_up(&mut self, _: &MouseUpEvent, _: &mut Window, _: &mut Context<Self>) {
self.selecting = None;
}
pub(super) fn on_scroll_wheel(
&mut self,
event: &gpui::ScrollWheelEvent,
_window: &mut Window,
cx: &mut Context<Self>,
) {
let delta = event.delta.pixel_delta(self.line_height());
self.scroll.x = (self.scroll.x - delta.x).max(px(0.));
self.scroll.y = (self.scroll.y - delta.y).max(px(0.));
cx.notify();
}
pub(super) fn drag_to(
&mut self,
position: Point<Pixels>,
line_height: Pixels,
cx: &mut Context<Self>,
) {
let Some((unit, pressed)) = self.selecting.clone() else {
return;
};
let offset = self.index_for_mouse_position(position, line_height);
let span = self.unit_at(unit, position, offset, line_height);
let (anchor, head) = drag_selection(pressed, span);
if (anchor, head) != (self.anchor_offset(), self.cursor_offset()) {
self.select_span(anchor, head, cx);
}
}
pub(super) fn show_character_palette(
&mut self,
_: &ShowCharacterPalette,
window: &mut Window,
_: &mut Context<Self>,
) {
window.show_character_palette();
}
pub(super) 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()) {
self.replace_text_in_range(None, &normalize(&text, self.shape), window, cx);
}
}
pub(super) fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
if !self.selected_range.is_empty() {
cx.write_to_clipboard(ClipboardItem::new_string(
self.content[self.selected_range.clone()].to_string(),
));
}
}
pub(super) fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
if !self.selected_range.is_empty() {
cx.write_to_clipboard(ClipboardItem::new_string(
self.content[self.selected_range.clone()].to_string(),
));
self.replace_text_in_range(None, "", window, cx)
}
}
pub(super) fn snapshot(&self) -> Snapshot {
Snapshot {
content: self.content.clone(),
selection: self.selected_range.clone(),
reversed: self.selection_reversed,
chips: self.chips.clone(),
}
}
pub(super) fn restore(&mut self, point: Snapshot, cx: &mut Context<Self>) {
let edit = Edit::between(&self.content, &point.content);
self.content = point.content;
self.selected_range = point.selection;
self.selection_reversed = point.reversed;
self.marked_range = None;
self.last_edit = None;
self.caret_moved();
self.edited(edit, cx);
self.chips = point.chips;
cx.notify();
}
pub(super) fn push_undo(&mut self, kind: EditKind, at: usize) {
let before = (!joins_group(self.last_edit, kind, at)).then(|| self.snapshot());
self.history.record(before);
}
pub(super) fn undo(&mut self, _: &Undo, _: &mut Window, cx: &mut Context<Self>) {
let current = self.snapshot();
if let Some(point) = self.history.undo(|| current) {
self.restore(point, cx);
}
}
pub(super) fn redo(&mut self, _: &Redo, _: &mut Window, cx: &mut Context<Self>) {
let current = self.snapshot();
if let Some(point) = self.history.redo(|| current) {
self.restore(point, cx);
}
}
}