use std::time::Duration; use gpui::{px, Context, Pixels, Timer}; static INTERVAL: Duration = Duration::from_millis(500); static PAUSE_DELAY: Duration = Duration::from_millis(300); pub(super) const CURSOR_WIDTH: Pixels = px(1.5); /// To manage the Input cursor blinking. /// /// It will start blinking with a interval of 500ms. /// Every loop will notify the view to update the `visible`, and Input will observe this update to touch repaint. /// /// The input painter will check if this in visible state, then it will draw the cursor. pub struct BlinkCursor { visible: bool, paused: bool, epoch: usize, } impl BlinkCursor { pub fn new() -> Self { Self { visible: false, paused: false, epoch: 0, } } /// Start the blinking pub fn start(&mut self, cx: &mut Context) { self.blink(self.epoch, cx); } pub fn stop(&mut self, cx: &mut Context) { self.epoch = 0; cx.notify(); } fn next_epoch(&mut self) -> usize { self.epoch += 1; self.epoch } fn blink(&mut self, epoch: usize, cx: &mut Context) { if self.paused || epoch != self.epoch { self.visible = true; return; } self.visible = !self.visible; cx.notify(); // Schedule the next blink let epoch = self.next_epoch(); cx.spawn(async move |this, cx| { Timer::after(INTERVAL).await; if let Some(this) = this.upgrade() { this.update(cx, |this, cx| this.blink(epoch, cx)); } }) .detach(); } pub fn visible(&self) -> bool { // Keep showing the cursor if paused self.paused || self.visible } /// Pause the blinking, and delay 500ms to resume the blinking. pub fn pause(&mut self, cx: &mut Context) { self.paused = true; self.visible = true; cx.notify(); // delay 500ms to start the blinking let epoch = self.next_epoch(); cx.spawn(async move |this, cx| { Timer::after(PAUSE_DELAY).await; if let Some(this) = this.upgrade() { this.update(cx, |this, cx| { this.paused = false; this.blink(epoch, cx); }); } }) .detach(); } } impl Default for BlinkCursor { fn default() -> Self { Self::new() } }