Pull blink functionality out of editor and into blink manager. Make blink manager subscribe to settings changes in order to start blinking properly when it is re-enabled.

Co-Authored-By: Mikayla Maki <mikayla@zed.dev>
This commit is contained in:
K Simmons 2022-10-17 16:19:03 -07:00
parent 09a0b3eb55
commit 54cf6fa838
5 changed files with 131 additions and 82 deletions

View file

@ -10,6 +10,8 @@
// Whether to show the informational hover box when moving the mouse
// over symbols in the editor.
"hover_popover_enabled": true,
// Whether the cursor blinks in the editor.
"cursor_blink": true,
// Whether to pop the completions menu while typing in an editor without
// explicitly requesting it.
"show_completions_on_input": true,
@ -69,8 +71,6 @@
// The column at which to soft-wrap lines, for buffers where soft-wrap
// is enabled.
"preferred_line_length": 80,
// Whether the cursor blinks in the editor.
"cursor_blink": true,
// Whether to indent lines using tab characters, as opposed to multiple
// spaces.
"hard_tabs": false,

View file

@ -0,0 +1,110 @@
use std::time::Duration;
use gpui::{Entity, ModelContext};
use settings::Settings;
use smol::Timer;
pub struct BlinkManager {
blink_interval: Duration,
blink_epoch: usize,
blinking_paused: bool,
visible: bool,
enabled: bool,
}
impl BlinkManager {
pub fn new(blink_interval: Duration, cx: &mut ModelContext<Self>) -> Self {
let weak_handle = cx.weak_handle();
cx.observe_global::<Settings, _>(move |_, cx| {
if let Some(this) = weak_handle.upgrade(cx) {
// Make sure we blink the cursors if the setting is re-enabled
this.update(cx, |this, cx| this.blink_cursors(this.blink_epoch, cx));
}
})
.detach();
Self {
blink_interval,
blink_epoch: 0,
blinking_paused: false,
visible: true,
enabled: true,
}
}
fn next_blink_epoch(&mut self) -> usize {
self.blink_epoch += 1;
self.blink_epoch
}
pub fn pause_blinking(&mut self, cx: &mut ModelContext<Self>) {
if !self.visible {
self.visible = true;
cx.notify();
}
let epoch = self.next_blink_epoch();
let interval = self.blink_interval;
cx.spawn(|this, mut cx| {
let this = this.downgrade();
async move {
Timer::after(interval).await;
if let Some(this) = this.upgrade(&cx) {
this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
}
}
})
.detach();
}
fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ModelContext<Self>) {
if epoch == self.blink_epoch {
self.blinking_paused = false;
self.blink_cursors(epoch, cx);
}
}
fn blink_cursors(&mut self, epoch: usize, cx: &mut ModelContext<Self>) {
if cx.global::<Settings>().cursor_blink {
if epoch == self.blink_epoch && self.enabled && !self.blinking_paused {
self.visible = !self.visible;
cx.notify();
let epoch = self.next_blink_epoch();
let interval = self.blink_interval;
cx.spawn(|this, mut cx| {
let this = this.downgrade();
async move {
Timer::after(interval).await;
if let Some(this) = this.upgrade(&cx) {
this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
}
}
})
.detach();
}
} else if !self.visible {
self.visible = true;
cx.notify();
}
}
pub fn enable(&mut self, cx: &mut ModelContext<Self>) {
self.enabled = true;
self.blink_cursors(self.blink_epoch, cx);
}
pub fn disable(&mut self, _: &mut ModelContext<Self>) {
self.enabled = true;
}
pub fn visible(&self) -> bool {
self.visible
}
}
impl Entity for BlinkManager {
type Event = ();
}

View file

@ -1,3 +1,4 @@
mod blink_manager;
pub mod display_map;
mod element;
mod highlight_matching_bracket;
@ -16,6 +17,7 @@ pub mod test;
use aho_corasick::AhoCorasick;
use anyhow::Result;
use blink_manager::BlinkManager;
use clock::ReplicaId;
use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
pub use display_map::DisplayPoint;
@ -447,12 +449,10 @@ pub struct Editor {
override_text_style: Option<Box<OverrideTextStyle>>,
project: Option<ModelHandle<Project>>,
focused: bool,
show_local_cursors: bool,
blink_manager: ModelHandle<BlinkManager>,
show_local_selections: bool,
show_scrollbars: bool,
hide_scrollbar_task: Option<Task<()>>,
blink_epoch: usize,
blinking_paused: bool,
mode: EditorMode,
vertical_scroll_margin: f32,
placeholder_text: Option<Arc<str>>,
@ -1076,6 +1076,8 @@ impl Editor {
let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
let blink_manager = cx.add_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
let mut this = Self {
handle: cx.weak_handle(),
buffer: buffer.clone(),
@ -1097,12 +1099,10 @@ impl Editor {
scroll_top_anchor: Anchor::min(),
autoscroll_request: None,
focused: false,
show_local_cursors: false,
blink_manager: blink_manager.clone(),
show_local_selections: true,
show_scrollbars: true,
hide_scrollbar_task: None,
blink_epoch: 0,
blinking_paused: false,
mode,
vertical_scroll_margin: 3.0,
placeholder_text: None,
@ -1130,6 +1130,7 @@ impl Editor {
cx.observe(&buffer, Self::on_buffer_changed),
cx.subscribe(&buffer, Self::on_buffer_event),
cx.observe(&display_map, Self::on_display_map_changed),
cx.observe(&blink_manager, |_, _, cx| cx.notify()),
],
};
this.end_selection(cx);
@ -1542,7 +1543,7 @@ impl Editor {
refresh_matching_bracket_highlights(self, cx);
}
self.pause_cursor_blinking(cx);
self.blink_manager.update(cx, BlinkManager::pause_blinking);
cx.emit(Event::SelectionsChanged { local });
cx.notify();
}
@ -6111,70 +6112,8 @@ impl Editor {
highlights
}
fn next_blink_epoch(&mut self) -> usize {
self.blink_epoch += 1;
self.blink_epoch
}
fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
if !self.focused {
return;
}
self.show_local_cursors = true;
cx.notify();
let epoch = self.next_blink_epoch();
cx.spawn(|this, mut cx| {
let this = this.downgrade();
async move {
Timer::after(CURSOR_BLINK_INTERVAL).await;
if let Some(this) = this.upgrade(&cx) {
this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
}
}
})
.detach();
}
fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
if epoch == self.blink_epoch {
self.blinking_paused = false;
self.blink_cursors(epoch, cx);
}
}
fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
let newest_head = self.selections.newest::<usize>(cx).head();
let language_name = self
.buffer
.read(cx)
.language_at(newest_head, cx)
.map(|l| l.name());
self.show_local_cursors = !self.show_local_cursors
|| !cx
.global::<Settings>()
.cursor_blink(language_name.as_deref());
cx.notify();
let epoch = self.next_blink_epoch();
cx.spawn(|this, mut cx| {
let this = this.downgrade();
async move {
Timer::after(CURSOR_BLINK_INTERVAL).await;
if let Some(this) = this.upgrade(&cx) {
this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
}
}
})
.detach();
}
}
pub fn show_local_cursors(&self) -> bool {
self.show_local_cursors && self.focused
pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
self.blink_manager.read(cx).visible() && self.focused
}
pub fn show_scrollbars(&self) -> bool {
@ -6493,7 +6432,7 @@ impl View for Editor {
cx.focus(&rename.editor);
} else {
self.focused = true;
self.blink_cursors(self.blink_epoch, cx);
self.blink_manager.update(cx, BlinkManager::enable);
self.buffer.update(cx, |buffer, cx| {
buffer.finalize_last_transaction(cx);
if self.leader_replica_id.is_none() {
@ -6512,6 +6451,7 @@ impl View for Editor {
let blurred_event = EditorBlurred(cx.handle());
cx.emit_global(blurred_event);
self.focused = false;
self.blink_manager.update(cx, BlinkManager::disable);
self.buffer
.update(cx, |buffer, cx| buffer.remove_active_selections(cx));
self.hide_context_menu(cx);

View file

@ -705,7 +705,7 @@ impl EditorElement {
cx,
);
if view.show_local_cursors() || *replica_id != local_replica_id {
if view.show_local_cursors(cx) || *replica_id != local_replica_id {
let cursor_position = selection.head;
if layout
.visible_display_row_range

View file

@ -28,6 +28,7 @@ pub struct Settings {
pub buffer_font_family: FamilyId,
pub default_buffer_font_size: f32,
pub buffer_font_size: f32,
pub cursor_blink: bool,
pub hover_popover_enabled: bool,
pub show_completions_on_input: bool,
pub vim_mode: bool,
@ -79,7 +80,6 @@ pub struct GitGutterConfig {}
pub struct EditorSettings {
pub tab_size: Option<NonZeroU32>,
pub hard_tabs: Option<bool>,
pub cursor_blink: Option<bool>,
pub soft_wrap: Option<SoftWrap>,
pub preferred_line_length: Option<u32>,
pub format_on_save: Option<FormatOnSave>,
@ -235,6 +235,8 @@ pub struct SettingsFileContent {
#[serde(default)]
pub buffer_font_size: Option<f32>,
#[serde(default)]
pub cursor_blink: Option<bool>,
#[serde(default)]
pub hover_popover_enabled: Option<bool>,
#[serde(default)]
pub show_completions_on_input: Option<bool>,
@ -293,6 +295,7 @@ impl Settings {
.unwrap(),
buffer_font_size: defaults.buffer_font_size.unwrap(),
default_buffer_font_size: defaults.buffer_font_size.unwrap(),
cursor_blink: defaults.cursor_blink.unwrap(),
hover_popover_enabled: defaults.hover_popover_enabled.unwrap(),
show_completions_on_input: defaults.show_completions_on_input.unwrap(),
projects_online_by_default: defaults.projects_online_by_default.unwrap(),
@ -302,7 +305,6 @@ impl Settings {
editor_defaults: EditorSettings {
tab_size: required(defaults.editor.tab_size),
hard_tabs: required(defaults.editor.hard_tabs),
cursor_blink: required(defaults.editor.cursor_blink),
soft_wrap: required(defaults.editor.soft_wrap),
preferred_line_length: required(defaults.editor.preferred_line_length),
format_on_save: required(defaults.editor.format_on_save),
@ -348,6 +350,7 @@ impl Settings {
);
merge(&mut self.buffer_font_size, data.buffer_font_size);
merge(&mut self.default_buffer_font_size, data.buffer_font_size);
merge(&mut self.cursor_blink, data.cursor_blink);
merge(&mut self.hover_popover_enabled, data.hover_popover_enabled);
merge(
&mut self.show_completions_on_input,
@ -392,10 +395,6 @@ impl Settings {
self.language_setting(language, |settings| settings.hard_tabs)
}
pub fn cursor_blink(&self, language: Option<&str>) -> bool {
self.language_setting(language, |settings| settings.cursor_blink)
}
pub fn soft_wrap(&self, language: Option<&str>) -> SoftWrap {
self.language_setting(language, |settings| settings.soft_wrap)
}
@ -442,6 +441,7 @@ impl Settings {
buffer_font_family: cx.font_cache().load_family(&["Monaco"]).unwrap(),
buffer_font_size: 14.,
default_buffer_font_size: 14.,
cursor_blink: true,
hover_popover_enabled: true,
show_completions_on_input: true,
vim_mode: false,
@ -450,7 +450,6 @@ impl Settings {
editor_defaults: EditorSettings {
tab_size: Some(4.try_into().unwrap()),
hard_tabs: Some(false),
cursor_blink: Some(true),
soft_wrap: Some(SoftWrap::None),
preferred_line_length: Some(80),
format_on_save: Some(FormatOnSave::On),