Compare commits

...

1 Commits

Author SHA1 Message Date
Agus Zubiaga
2acd48f439 Checkpoint: Refactoring blink manager 2025-11-25 17:24:27 -03:00
3 changed files with 97 additions and 113 deletions

View File

@@ -1,8 +1,8 @@
use gpui::Context;
use gpui::{Context, FocusHandle};
use settings::SettingsStore;
use smol::Timer;
use std::time::Duration;
use ui::App;
use ui::{App, Window};
pub struct BlinkManager {
blink_interval: Duration,
@@ -11,21 +11,34 @@ pub struct BlinkManager {
blinking_paused: bool,
/// Whether the cursor should be visibly rendered or not.
visible: bool,
/// Whether the blinking currently enabled.
enabled: bool,
/// The focus handle to use to determine if the cursor should be blinking.
focus_handle: FocusHandle,
/// Whether the blinking is enabled in the settings.
blink_enabled_in_settings: fn(&App) -> bool,
is_enabled: Box<dyn Fn(&App) -> bool>,
}
impl BlinkManager {
pub fn new(
blink_interval: Duration,
blink_enabled_in_settings: fn(&App) -> bool,
focus_handle: FocusHandle,
is_enabled: impl Fn(&App) -> bool + 'static,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
// Make sure we blink the cursors if the setting is re-enabled
cx.observe_global::<SettingsStore>(move |this, cx| {
this.blink_cursors(this.blink_epoch, cx)
cx.observe_global_in::<SettingsStore>(window, move |this, window, cx| {
this.refresh(window, cx);
})
.detach();
cx.on_focus(&focus_handle, window, move |this, window, cx| {
this.visible = false;
this.refresh(window, cx);
})
.detach();
cx.on_blur(&focus_handle, window, move |this, _window, _cx| {
this.visible = false;
})
.detach();
@@ -34,48 +47,64 @@ impl BlinkManager {
blink_epoch: 0,
blinking_paused: false,
visible: true,
enabled: false,
blink_enabled_in_settings,
focus_handle,
is_enabled: Box::new(is_enabled),
}
}
pub fn refresh(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.blink_cursors(self.blink_epoch, window, cx)
}
fn next_blink_epoch(&mut self) -> usize {
self.blink_epoch += 1;
self.blink_epoch
}
pub fn pause_blinking(&mut self, cx: &mut Context<Self>) {
pub fn pause_blinking(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.show_cursor(cx);
let epoch = self.next_blink_epoch();
let interval = self.blink_interval;
cx.spawn(async move |this, cx| {
cx.spawn_in(window, async move |this, cx| {
Timer::after(interval).await;
this.update(cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
this.update_in(cx, |this, window, cx| {
this.resume_cursor_blinking(epoch, window, cx)
})
})
.detach();
}
fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut Context<Self>) {
fn resume_cursor_blinking(
&mut self,
epoch: usize,
window: &mut Window,
cx: &mut Context<Self>,
) {
if epoch == self.blink_epoch {
self.blinking_paused = false;
self.blink_cursors(epoch, cx);
self.blink_cursors(epoch, window, cx);
}
}
fn blink_cursors(&mut self, epoch: usize, cx: &mut Context<Self>) {
if (self.blink_enabled_in_settings)(cx) {
if epoch == self.blink_epoch && self.enabled && !self.blinking_paused {
fn blink_cursors(&mut self, epoch: usize, window: &mut Window, cx: &mut Context<Self>) {
if (self.is_enabled)(cx) {
if epoch == self.blink_epoch
&& self.focus_handle.is_focused(window)
&& !self.blinking_paused
{
self.visible = !self.visible;
cx.notify();
let epoch = self.next_blink_epoch();
let interval = self.blink_interval;
cx.spawn(async move |this, cx| {
cx.spawn_in(window, async move |this, cx| {
Timer::after(interval).await;
if let Some(this) = this.upgrade() {
this.update(cx, |this, cx| this.blink_cursors(epoch, cx))
.ok();
this.update_in(cx, |this, window, cx| {
this.blink_cursors(epoch, window, cx)
})
.ok();
}
})
.detach();
@@ -92,25 +121,6 @@ impl BlinkManager {
}
}
/// Enable the blinking of the cursor.
pub fn enable(&mut self, cx: &mut Context<Self>) {
if self.enabled {
return;
}
self.enabled = true;
// Set cursors as invisible and start blinking: this causes cursors
// to be visible during the next render.
self.visible = false;
self.blink_cursors(self.blink_epoch, cx);
}
/// Disable the blinking of the cursor.
pub fn disable(&mut self, _cx: &mut Context<Self>) {
self.visible = false;
self.enabled = false;
}
pub fn visible(&self) -> bool {
self.visible
}

View File

@@ -1886,16 +1886,26 @@ impl Editor {
let selections = SelectionsCollection::new();
let focus_handle = cx.focus_handle();
let blink_manager = cx.new(|cx| {
let mut blink_manager = BlinkManager::new(
CURSOR_BLINK_INTERVAL,
|cx| EditorSettings::get_global(cx).cursor_blink,
cx,
);
if is_minimap {
blink_manager.disable(cx);
BlinkManager::new(
CURSOR_BLINK_INTERVAL,
focus_handle.clone(),
|_cx| false,
window,
cx,
)
} else {
BlinkManager::new(
CURSOR_BLINK_INTERVAL,
focus_handle.clone(),
|cx| EditorSettings::get_global(cx).cursor_blink,
window,
cx,
)
}
blink_manager
});
let soft_wrap_mode_override =
@@ -2095,7 +2105,6 @@ impl Editor {
let inlay_hint_settings =
inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
let focus_handle = cx.focus_handle();
if !is_minimap {
cx.on_focus(&focus_handle, window, Self::handle_focus)
.detach();
@@ -2292,15 +2301,7 @@ impl Editor {
cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
cx.observe_window_activation(window, |editor, window, cx| {
let active = window.is_window_active();
editor.blink_manager.update(cx, |blink_manager, cx| {
if active {
blink_manager.enable(cx);
} else {
blink_manager.disable(cx);
}
});
if active {
if window.is_window_active() {
editor.show_mouse_cursor(cx);
}
}),
@@ -3321,7 +3322,9 @@ impl Editor {
}
}
self.blink_manager.update(cx, BlinkManager::pause_blinking);
self.blink_manager.update(cx, |blink_manager, cx| {
blink_manager.pause_blinking(window, cx)
});
cx.emit(EditorEvent::SelectionsChanged { local });
let selections = &self.selections.disjoint_anchors_arc();
@@ -22161,7 +22164,6 @@ impl Editor {
blame.update(cx, GitBlame::focus)
}
self.blink_manager.update(cx, BlinkManager::enable);
self.show_cursor_names(window, cx);
self.buffer.update(cx, |buffer, cx| {
buffer.finalize_last_transaction(cx);
@@ -22209,7 +22211,6 @@ impl Editor {
}
pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.blink_manager.update(cx, BlinkManager::disable);
self.buffer
.update(cx, |buffer, cx| buffer.remove_active_selections(cx));

View File

@@ -234,17 +234,25 @@ impl TerminalView {
let scroll_handle = TerminalScrollHandle::new(terminal.read(cx));
let blink_manager = cx.new(|cx| {
BlinkManager::new(
CURSOR_BLINK_INTERVAL,
|cx| {
!matches!(
TerminalSettings::get_global(cx).blinking,
TerminalBlink::Off
)
},
cx,
)
let blink_manager = cx.new({
let weak_this = cx.weak_entity();
let focus_handle = focus_handle.clone();
move |cx| {
BlinkManager::new(
CURSOR_BLINK_INTERVAL,
focus_handle,
move |cx| match TerminalSettings::get_global(cx).blinking {
TerminalBlink::Off => false,
TerminalBlink::On => true,
TerminalBlink::TerminalControlled => weak_this
.read_with(cx, |this, _cx| this.blinking_terminal_enabled)
.unwrap_or(false),
},
window,
cx,
)
}
});
let _subscriptions = vec![
@@ -434,11 +442,6 @@ impl TerminalView {
let breadcrumb_visibility_changed = self.show_breadcrumbs != settings.toolbar.breadcrumbs;
self.show_breadcrumbs = settings.toolbar.breadcrumbs;
let should_blink = match settings.blinking {
TerminalBlink::Off => false,
TerminalBlink::On => true,
TerminalBlink::TerminalControlled => self.blinking_terminal_enabled,
};
let new_cursor_shape = settings.cursor_shape;
let old_cursor_shape = self.cursor_shape;
if old_cursor_shape != new_cursor_shape {
@@ -448,15 +451,6 @@ impl TerminalView {
});
}
self.blink_manager.update(
cx,
if should_blink {
BlinkManager::enable
} else {
BlinkManager::disable
},
);
if breadcrumb_visibility_changed {
cx.emit(ItemEvent::UpdateBreadcrumbs);
}
@@ -656,8 +650,10 @@ impl TerminalView {
}
}
pub fn pause_cursor_blinking(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
self.blink_manager.update(cx, BlinkManager::pause_blinking);
pub fn pause_cursor_blinking(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.blink_manager.update(cx, |blink_manager, cx| {
blink_manager.pause_blinking(window, cx)
});
}
pub fn terminal(&self) -> &Entity<Terminal> {
@@ -873,21 +869,9 @@ fn subscribe_for_terminal_events(
Event::BlinkChanged(blinking) => {
terminal_view.blinking_terminal_enabled = *blinking;
// If in terminal-controlled mode and focused, update blink manager
if matches!(
TerminalSettings::get_global(cx).blinking,
TerminalBlink::TerminalControlled
) && terminal_view.focus_handle.is_focused(window)
{
terminal_view.blink_manager.update(cx, |manager, cx| {
if *blinking {
manager.enable(cx);
} else {
manager.disable(cx);
}
});
}
terminal_view
.blink_manager
.update(cx, |this, cx| this.refresh(window, cx));
}
Event::TitleChanged => {
@@ -1013,22 +997,11 @@ impl TerminalView {
terminal.focus_in();
});
let should_blink = match TerminalSettings::get_global(cx).blinking {
TerminalBlink::Off => false,
TerminalBlink::On => true,
TerminalBlink::TerminalControlled => self.blinking_terminal_enabled,
};
if should_blink {
self.blink_manager.update(cx, BlinkManager::enable);
}
window.invalidate_character_coordinates();
cx.notify();
}
fn focus_out(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
self.blink_manager.update(cx, BlinkManager::disable);
self.terminal.update(cx, |terminal, _| {
terminal.focus_out();
terminal.set_cursor_shape(CursorShape::Hollow);