From 7b3a7727c64797fa449ad4bd3667953812d9761e Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 15 Aug 2022 17:00:44 -0700 Subject: [PATCH] Basic cursor blinking :) --- crates/terminal/src/connected_el.rs | 94 ++++++++++++++++----------- crates/terminal/src/connected_view.rs | 66 +++++++++++++++++++ crates/terminal/src/terminal.rs | 1 + 3 files changed, 123 insertions(+), 38 deletions(-) diff --git a/crates/terminal/src/connected_el.rs b/crates/terminal/src/connected_el.rs index 69fc5f581fff656348f55bdf8f1e42f4e5d87cdf..392783f02dd9e613e0cad545c12602c825915f6c 100644 --- a/crates/terminal/src/connected_el.rs +++ b/crates/terminal/src/connected_el.rs @@ -643,49 +643,64 @@ impl Element for TerminalEl { ); //Layout cursor - let cursor = { - let cursor_point = DisplayCursor::from(cursor.point, display_offset); - let cursor_text = { - let str_trxt = cursor_text.to_string(); - - let color = if self.focused { - terminal_theme.colors.background - } else { - terminal_theme.colors.foreground - }; + //TODO: This logic can be a lot better + let show_cursor = if let Some(view_handle) = self.view.upgrade(cx) { + if view_handle.read(cx).show_cursor() { + false + } else { + true + } + } else { + true + }; - cx.text_layout_cache.layout_str( - &str_trxt, - text_style.font_size, - &[( - str_trxt.len(), - RunStyle { - font_id: text_style.font_id, - color, - underline: Default::default(), - }, - )], - ) - }; + let cursor = { + if show_cursor { + None + } else { + let cursor_point = DisplayCursor::from(cursor.point, display_offset); + let cursor_text = { + let str_trxt = cursor_text.to_string(); - TerminalEl::shape_cursor(cursor_point, dimensions, &cursor_text).map( - move |(cursor_position, block_width)| { - let (shape, color) = if self.focused { - (CursorShape::Block, terminal_theme.colors.cursor) + let color = if self.focused { + terminal_theme.colors.background } else { - (CursorShape::Underscore, terminal_theme.colors.foreground) + terminal_theme.colors.foreground }; - Cursor::new( - cursor_position, - block_width, - dimensions.line_height, - color, - shape, - Some(cursor_text), + cx.text_layout_cache.layout_str( + &str_trxt, + text_style.font_size, + &[( + str_trxt.len(), + RunStyle { + font_id: text_style.font_id, + color, + underline: Default::default(), + }, + )], ) - }, - ) + }; + + TerminalEl::shape_cursor(cursor_point, dimensions, &cursor_text).map( + move |(cursor_position, block_width)| { + let (shape, color) = if self.focused { + (CursorShape::Block, terminal_theme.colors.cursor) + } else { + (CursorShape::Underscore, terminal_theme.colors.foreground) + }; + + Cursor::new( + cursor_position, + block_width, + dimensions.line_height, + color, + shape, + Some(cursor_text), + ) + }, + ) + } }; //Done! @@ -818,7 +833,10 @@ impl Element for TerminalEl { //TODO Talk to keith about how to catch events emitted from an element. if let Some(view) = self.view.upgrade(cx.app) { - view.update(cx.app, |view, cx| view.clear_bel(cx)) + view.update(cx.app, |view, cx| { + view.clear_bel(cx); + view.pause_cursor_blinking(cx); + }) } self.terminal diff --git a/crates/terminal/src/connected_view.rs b/crates/terminal/src/connected_view.rs index 9e909d5bccfb7ca6f5f4bd801f1808232374e250..924dece2c22e4f013b193c53e9c8e7a76ab30864 100644 --- a/crates/terminal/src/connected_view.rs +++ b/crates/terminal/src/connected_view.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use alacritty_terminal::term::TermMode; use context_menu::{ContextMenu, ContextMenuItem}; use gpui::{ @@ -9,10 +11,13 @@ use gpui::{ AnyViewHandle, AppContext, Element, ElementBox, ModelHandle, MutableAppContext, View, ViewContext, ViewHandle, }; +use smol::Timer; use workspace::pane; use crate::{connected_el::TerminalEl, Event, Terminal}; +const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500); + ///Event to transmit the scroll from the element to the view #[derive(Clone, Debug, PartialEq)] pub struct ScrollTerminal(pub i32); @@ -51,6 +56,9 @@ pub struct ConnectedView { // Only for styling purposes. Doesn't effect behavior modal: bool, context_menu: ViewHandle, + show_cursor: bool, + blinking_paused: bool, + blink_epoch: usize, } impl ConnectedView { @@ -83,6 +91,9 @@ impl ConnectedView { has_bell: false, modal, context_menu: cx.add_view(ContextMenu::new), + show_cursor: true, + blinking_paused: false, + blink_epoch: 0, } } @@ -120,6 +131,59 @@ impl ConnectedView { cx.notify(); } + //Following code copied from editor cursor + pub fn show_cursor(&self) -> bool { + self.blinking_paused || self.show_cursor + } + + fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext) { + if epoch == self.blink_epoch && !self.blinking_paused { + self.show_cursor = !self.show_cursor; + 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 pause_cursor_blinking(&mut self, cx: &mut ViewContext) { + self.show_cursor = 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 next_blink_epoch(&mut self) -> usize { + self.blink_epoch += 1; + self.blink_epoch + } + + fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext) { + if epoch == self.blink_epoch { + self.blinking_paused = false; + self.blink_cursors(epoch, cx); + } + } + ///Attempt to paste the clipboard into the terminal fn copy(&mut self, _: &Copy, cx: &mut ViewContext) { self.terminal.update(cx, |term, _| term.copy()) @@ -200,6 +264,7 @@ impl View for ConnectedView { fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { self.has_new_content = false; self.terminal.read(cx).focus_in(); + self.blink_cursors(self.blink_epoch, cx); cx.notify(); } @@ -208,6 +273,7 @@ impl View for ConnectedView { cx.notify(); } + //IME stuff fn selected_text_range(&self, cx: &AppContext) -> Option> { if self .terminal diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index af1c763f5230b3ec75a4f1c92a09ad2b81ba222f..0c1d29a248662a14e22fbee84fdf5ab2941dd431 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -287,6 +287,7 @@ impl TerminalBuilder { setup_env(&config); //Spawn a task so the Alacritty EventLoop can communicate with us in a view context + //TODO: Remove with a bounded sender which can be dispatched on &self let (events_tx, events_rx) = unbounded(); //Set up the terminal... let term = Term::new(&config, &initial_size, ZedListener(events_tx.clone()));