mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-24 17:28:40 +00:00
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:
parent
09a0b3eb55
commit
54cf6fa838
5 changed files with 131 additions and 82 deletions
|
@ -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,
|
||||
|
|
110
crates/editor/src/blink_manager.rs
Normal file
110
crates/editor/src/blink_manager.rs
Normal 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 = ();
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in a new issue