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