1use crate::EditorSettings;
  2use gpui::Context;
  3use settings::Settings;
  4use settings::SettingsStore;
  5use smol::Timer;
  6use std::time::Duration;
  7
  8pub struct BlinkManager {
  9    blink_interval: Duration,
 10    blink_epoch: usize,
 11    blinking_paused: bool,
 12    visible: bool,
 13    enabled: bool,
 14}
 15
 16impl BlinkManager {
 17    pub fn new(blink_interval: Duration, cx: &mut Context<Self>) -> Self {
 18        // Make sure we blink the cursors if the setting is re-enabled
 19        cx.observe_global::<SettingsStore>(move |this, cx| {
 20            this.blink_cursors(this.blink_epoch, cx)
 21        })
 22        .detach();
 23
 24        Self {
 25            blink_interval,
 26            blink_epoch: 0,
 27            blinking_paused: false,
 28            visible: true,
 29            enabled: false,
 30        }
 31    }
 32
 33    const fn next_blink_epoch(&mut self) -> usize {
 34        self.blink_epoch += 1;
 35        self.blink_epoch
 36    }
 37
 38    pub fn pause_blinking(&mut self, cx: &mut Context<Self>) {
 39        self.show_cursor(cx);
 40
 41        let epoch = self.next_blink_epoch();
 42        let interval = self.blink_interval;
 43        cx.spawn(async move |this, cx| {
 44            Timer::after(interval).await;
 45            this.update(cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
 46        })
 47        .detach();
 48    }
 49
 50    fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut Context<Self>) {
 51        if epoch == self.blink_epoch {
 52            self.blinking_paused = false;
 53            self.blink_cursors(epoch, cx);
 54        }
 55    }
 56
 57    fn blink_cursors(&mut self, epoch: usize, cx: &mut Context<Self>) {
 58        if EditorSettings::get_global(cx).cursor_blink {
 59            if epoch == self.blink_epoch && self.enabled && !self.blinking_paused {
 60                self.visible = !self.visible;
 61                cx.notify();
 62
 63                let epoch = self.next_blink_epoch();
 64                let interval = self.blink_interval;
 65                cx.spawn(async move |this, cx| {
 66                    Timer::after(interval).await;
 67                    if let Some(this) = this.upgrade() {
 68                        this.update(cx, |this, cx| this.blink_cursors(epoch, cx))
 69                            .ok();
 70                    }
 71                })
 72                .detach();
 73            }
 74        } else {
 75            self.show_cursor(cx);
 76        }
 77    }
 78
 79    pub fn show_cursor(&mut self, cx: &mut Context<BlinkManager>) {
 80        if !self.visible {
 81            self.visible = true;
 82            cx.notify();
 83        }
 84    }
 85
 86    pub fn enable(&mut self, cx: &mut Context<Self>) {
 87        if self.enabled {
 88            return;
 89        }
 90
 91        self.enabled = true;
 92        // Set cursors as invisible and start blinking: this causes cursors
 93        // to be visible during the next render.
 94        self.visible = false;
 95        self.blink_cursors(self.blink_epoch, cx);
 96    }
 97
 98    pub const fn disable(&mut self, _cx: &mut Context<Self>) {
 99        self.visible = false;
100        self.enabled = false;
101    }
102
103    pub const fn visible(&self) -> bool {
104        self.visible
105    }
106}