diff --git a/crates/ui/src/input.rs b/crates/ui/src/input.rs index 377168fa..262b50fc 100644 --- a/crates/ui/src/input.rs +++ b/crates/ui/src/input.rs @@ -118,9 +118,12 @@ impl TextInput { // Blink the cursor when the window is active, pause when it's not. cx.observe_window_activation(|input, cx| { if cx.is_window_active() { - input.blink_cursor.update(cx, |blink_cursor, cx| { - blink_cursor.start(cx); - }); + let focus_handle = input.focus_handle.clone(); + if focus_handle.is_focused(cx) { + input.blink_cursor.update(cx, |blink_cursor, cx| { + blink_cursor.start(cx); + }); + } } }) .detach(); @@ -230,7 +233,8 @@ impl TextInput { if self.selected_range.is_empty() { self.select_to(self.previous_boundary(self.cursor_offset()), cx) } - self.replace_text_in_range(None, "", cx) + self.replace_text_in_range(None, "", cx); + self.pause_blink_cursor(cx); } fn delete(&mut self, _: &Delete, cx: &mut ViewContext) { @@ -267,7 +271,7 @@ impl TextInput { self.replace_text_in_range(Some(self.selected_range.clone()), "", cx); } - pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext) { + fn paste(&mut self, _: &Paste, cx: &mut ViewContext) { if let Some(clipboard) = cx.read_from_clipboard() { let new_text = clipboard.text().replace('\n', ""); self.replace_text_in_range(Some(self.selected_range.clone()), &new_text, cx); @@ -364,22 +368,32 @@ impl TextInput { } fn on_focus(&mut self, cx: &mut ViewContext) { - self.blink_cursor.update(cx, |blink_cursor, cx| { - blink_cursor.start(cx); + self.blink_cursor.update(cx, |cursor, cx| { + cursor.start(cx); }); } fn on_blur(&mut self, cx: &mut ViewContext) { self.unselect(cx); - self.blink_cursor.update(cx, |blink_cursor, cx| { - blink_cursor.pause(cx); + self.blink_cursor.update(cx, |cursor, cx| { + cursor.stop(cx); }); } + fn pause_blink_cursor(&mut self, cx: &mut ViewContext) { + self.blink_cursor.update(cx, |cursor, cx| { + cursor.pause(cx); + }); + } + + fn on_key_down_for_blink_cursor(&mut self, _: &KeyDownEvent, cx: &mut ViewContext) { + self.pause_blink_cursor(cx) + } + fn on_mouse_left_down( &mut self, event: &MouseDownEvent, - text_hitbox: Hitbox, + hitbox: Hitbox, cx: &mut ViewContext, ) { // Ignore if text is empty @@ -387,11 +401,11 @@ impl TextInput { return; } - if !text_hitbox.contains(&event.position) { + if !hitbox.contains(&event.position) { return; } - let offset = self.offset_of_position(event.position, &text_hitbox); + let offset = self.offset_of_position(event.position, &hitbox); if event.modifiers.shift { self.select_to(offset, cx); } else { @@ -399,12 +413,7 @@ impl TextInput { } } - fn on_drag_move( - &mut self, - event: &MouseMoveEvent, - text_hitbox: Hitbox, - cx: &mut ViewContext, - ) { + fn on_drag_move(&mut self, event: &MouseMoveEvent, hitbox: Hitbox, cx: &mut ViewContext) { // Ignore if text is empty if self.text.is_empty() { return; @@ -414,11 +423,11 @@ impl TextInput { return; } - if !text_hitbox.contains(&event.position) { + if !self.focus_handle.is_focused(cx) { return; } - let offset = self.offset_of_position(event.position, &text_hitbox); + let offset = self.offset_of_position(event.position, &hitbox); if offset == self.cursor_offset() { return; } @@ -696,7 +705,7 @@ impl Element for TextElement { point(bounds.left() + cursor_pos, bounds.top()), size(px(1.5), bounds.bottom() - bounds.top()), ), - gpui::blue(), + crate::blue_500(), )), ) } else { @@ -789,6 +798,7 @@ impl Render for TextInput { .on_double_click(cx.listener(|view, _, cx| { view.select_all(&SelectAll, cx); })) + .on_key_down(cx.listener(Self::on_key_down_for_blink_cursor)) .size_full() .line_height(rems(1.25)) .text_size(rems(0.875)) diff --git a/crates/ui/src/input/blink_cursor.rs b/crates/ui/src/input/blink_cursor.rs index 0c9cd54f..db37271a 100644 --- a/crates/ui/src/input/blink_cursor.rs +++ b/crates/ui/src/input/blink_cursor.rs @@ -1,6 +1,8 @@ use std::time::Duration; -use gpui::{ModelContext, Timer}; +use gpui::{ModelContext, Timer, WeakView}; + +use super::TextInput; /// To manage the Input cursor blinking. /// @@ -34,17 +36,21 @@ impl BlinkCursor { } self.started = true; - self.paused = false; self.blink(self.blink_epoch, cx); } + pub fn stop(&mut self, cx: &mut ModelContext) { + self.started = false; + cx.notify(); + } + fn next_epoch(&mut self) -> usize { self.blink_epoch += 1; self.blink_epoch } fn blink(&mut self, epoch: usize, cx: &mut ModelContext) { - if self.paused { + if self.paused || !self.started { return; } @@ -69,12 +75,35 @@ impl BlinkCursor { } pub fn visible(&self) -> bool { + // Keep showing the cursor if paused + if self.paused { + return true; + } self.visible } + /// Pause the blinking, and delay 500ms to resume the blinking. pub fn pause(&mut self, cx: &mut ModelContext) { self.paused = true; - self.started = false; + self.next_epoch(); cx.notify(); + + let epoch = self.next_epoch(); + // delay 500ms to start the blinking + cx.spawn(|this, mut cx| async move { + Timer::after(Duration::from_secs_f64(0.5)).await; + + if let Some(this) = this.upgrade() { + this.update(&mut cx, |this, cx| { + if epoch != this.blink_epoch { + return; + } + this.paused = false; + this.blink(epoch, cx); + }) + .ok(); + } + }) + .detach(); } } diff --git a/crates/ui/src/theme.rs b/crates/ui/src/theme.rs index dac9e76f..8a13c8bb 100644 --- a/crates/ui/src/theme.rs +++ b/crates/ui/src/theme.rs @@ -250,7 +250,7 @@ impl Colors { border: hsl(240.0, 3.7, 15.9), input: hsl(240.0, 3.7, 15.9), ring: hsl(240.0, 4.9, 83.9), - selection: hsl(211.0, 97.0, 85.0), + selection: hsl(211.0, 97.0, 22.0), scrollbar: Hsla::transparent_black(), scrollbar_thumb: hsl(240.0, 3.7, 15.9).opacity(0.7), panel: hsl(299.0, 2., 9.),