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