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