diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index a2fe64f7a4..f6cc2796b1 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -2006,7 +2006,7 @@ impl HighlightedRange { } } -fn scale_vertical_mouse_autoscroll_delta(delta: f32) -> f32 { +pub fn scale_vertical_mouse_autoscroll_delta(delta: f32) -> f32 { delta.powf(1.5) / 100.0 } diff --git a/crates/terminal/src/mappings/mouse.rs b/crates/terminal/src/mappings/mouse.rs index 83d408e75f..c90cbb5cd3 100644 --- a/crates/terminal/src/mappings/mouse.rs +++ b/crates/terminal/src/mappings/mouse.rs @@ -33,12 +33,11 @@ impl Modifiers { } } - //TODO: Determine if I should add modifiers into the ScrollWheelEvent type - fn from_scroll() -> Self { + fn from_scroll(scroll: &ScrollWheelEvent) -> Self { Modifiers { - ctrl: false, - shift: false, - alt: false, + ctrl: scroll.ctrl, + shift: scroll.shift, + alt: scroll.alt, } } } @@ -123,7 +122,7 @@ pub fn scroll_report( point, MouseButton::from_scroll(e), true, - Modifiers::from_scroll(), + Modifiers::from_scroll(e), MouseFormat::from_mode(mode), ) .map(|report| repeat(report).take(max(scroll_lines, 1) as usize)) diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 3adaf7fa7c..17078826b9 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -18,7 +18,6 @@ use alacritty_terminal::{ Term, }; use anyhow::{bail, Result}; - use futures::{ channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}, FutureExt, @@ -29,11 +28,21 @@ use mappings::mouse::{ }; use modal::deploy_modal; use settings::{AlternateScroll, Settings, Shell, TerminalBlink}; -use std::{collections::HashMap, fmt::Display, ops::Sub, path::PathBuf, sync::Arc, time::Duration}; +use std::{ + collections::{HashMap, VecDeque}, + fmt::Display, + ops::Sub, + path::PathBuf, + sync::Arc, + time::Duration, +}; use thiserror::Error; use gpui::{ - geometry::vector::{vec2f, Vector2F}, + geometry::{ + rect::RectF, + vector::{vec2f, Vector2F}, + }, keymap::Keystroke, ClipboardItem, Entity, ModelContext, MouseButton, MouseButtonEvent, MouseMovedEvent, MutableAppContext, ScrollWheelEvent, @@ -82,7 +91,7 @@ enum InternalEvent { Clear, Scroll(Scroll), SetSelection(Option), - UpdateSelection((Point, Direction)), + UpdateSelection(Vector2F), Copy, } @@ -310,11 +319,8 @@ impl TerminalBuilder { term.set_mode(alacritty_terminal::ansi::Mode::BlinkingCursor) } - //Start alternate_scroll if we need to - if let AlternateScroll::On = alternate_scroll { - term.set_mode(alacritty_terminal::ansi::Mode::AlternateScroll) - } else { - //Alacritty turns it on by default, so we need to turn it off. + //Alacritty defaults to alternate scrolling being on, so we just need to turn it off. + if let AlternateScroll::Off = alternate_scroll { term.unset_mode(alacritty_terminal::ansi::Mode::AlternateScroll) } @@ -362,13 +368,14 @@ impl TerminalBuilder { let terminal = Terminal { pty_tx: Notifier(pty_tx), term, - events: vec![], + events: VecDeque::with_capacity(10), //Should never get this high. title: shell_txt.clone(), default_title: shell_txt, last_mode: TermMode::NONE, cur_size: initial_size, last_mouse: None, last_offset: 0, + current_selection: false, }; Ok(TerminalBuilder { @@ -433,13 +440,14 @@ impl TerminalBuilder { pub struct Terminal { pty_tx: Notifier, term: Arc>>, - events: Vec, + events: VecDeque, default_title: String, title: String, cur_size: TerminalSize, last_mode: TermMode, last_offset: usize, last_mouse: Option<(Point, Direction)>, + current_selection: bool, } impl Terminal { @@ -479,9 +487,9 @@ impl Terminal { cx.emit(Event::Wakeup); cx.notify(); } - AlacTermEvent::ColorRequest(_, _) => { - self.events.push(InternalEvent::TermEvent(event.clone())) - } + AlacTermEvent::ColorRequest(_, _) => self + .events + .push_back(InternalEvent::TermEvent(event.clone())), } } @@ -513,11 +521,16 @@ impl Terminal { self.write_to_pty("\x0c".to_string()); term.clear_screen(ClearMode::Saved); } - InternalEvent::Scroll(scroll) => term.scroll_display(*scroll), + InternalEvent::Scroll(scroll) => { + term.scroll_display(*scroll); + } InternalEvent::SetSelection(sel) => term.selection = sel.clone(), - InternalEvent::UpdateSelection((point, side)) => { + InternalEvent::UpdateSelection(position) => { if let Some(mut selection) = term.selection.take() { - selection.update(*point, *side); + let point = mouse_point(*position, self.cur_size, term.grid().display_offset()); + let side = mouse_side(*position, self.cur_size); + + selection.update(point, side); term.selection = Some(selection); } } @@ -530,10 +543,37 @@ impl Terminal { } } - pub fn input(&mut self, input: String) { - self.events.push(InternalEvent::Scroll(Scroll::Bottom)); - self.events.push(InternalEvent::SetSelection(None)); - self.write_to_pty(input); + fn begin_select(&mut self, sel: Selection) { + self.current_selection = true; + self.events + .push_back(InternalEvent::SetSelection(Some(sel))); + } + + fn continue_selection(&mut self, location: Vector2F) { + self.events + .push_back(InternalEvent::UpdateSelection(location)) + } + + fn end_select(&mut self) { + self.current_selection = false; + self.events.push_back(InternalEvent::SetSelection(None)); + } + + fn scroll(&mut self, scroll: Scroll) { + self.events.push_back(InternalEvent::Scroll(scroll)); + } + + pub fn copy(&mut self) { + self.events.push_back(InternalEvent::Copy); + } + + pub fn clear(&mut self) { + self.events.push_back(InternalEvent::Clear) + } + + ///Resize the terminal and the PTY. + pub fn set_size(&mut self, new_size: TerminalSize) { + self.events.push_back(InternalEvent::Resize(new_size)) } ///Write the Input payload to the tty. @@ -541,13 +581,10 @@ impl Terminal { self.pty_tx.notify(input.into_bytes()); } - ///Resize the terminal and the PTY. - pub fn set_size(&mut self, new_size: TerminalSize) { - self.events.push(InternalEvent::Resize(new_size)) - } - - pub fn clear(&mut self) { - self.events.push(InternalEvent::Clear) + pub fn input(&mut self, input: String) { + self.scroll(Scroll::Bottom); + self.end_select(); + self.write_to_pty(input); } pub fn try_keystroke(&mut self, keystroke: &Keystroke) -> bool { @@ -570,10 +607,6 @@ impl Terminal { self.input(paste_text) } - pub fn copy(&mut self) { - self.events.push(InternalEvent::Copy); - } - pub fn render_lock(&mut self, cx: &mut ModelContext, f: F) -> T where F: FnOnce(RenderableContent, char) -> T, @@ -581,7 +614,8 @@ impl Terminal { let m = self.term.clone(); //Arc clone let mut term = m.lock(); - while let Some(e) = self.events.pop() { + //Note that this ordering matters for + while let Some(e) = self.events.pop_front() { self.process_terminal_event(&e, &mut term, cx) } @@ -642,15 +676,32 @@ impl Terminal { } } - pub fn mouse_drag(&mut self, e: MouseMovedEvent, origin: Vector2F) { + pub fn mouse_drag(&mut self, e: MouseMovedEvent, origin: Vector2F, bounds: RectF) { let position = e.position.sub(origin); if !self.mouse_mode(e.shift) { - let point = mouse_point(position, self.cur_size, self.last_offset); - let side = mouse_side(position, self.cur_size); + // Alacritty has the same ordering, of first updating the selection + // then scrolling 15ms later + self.continue_selection(position); - self.events - .push(InternalEvent::UpdateSelection((point, side))); + // Doesn't make sense to scroll the alt screen + if !self.last_mode.contains(TermMode::ALT_SCREEN) { + //TODO: Why do these need to be doubled? + let top = bounds.origin_y() + (self.cur_size.line_height * 2.); + let bottom = bounds.lower_left().y() - (self.cur_size.line_height * 2.); + + let scroll_delta = if e.position.y() < top { + (top - e.position.y()).powf(1.1) + } else if e.position.y() > bottom { + -((e.position.y() - bottom).powf(1.1)) + } else { + return; //Nothing to do + }; + + let scroll_lines = (scroll_delta / self.cur_size.line_height) as i32; + self.scroll(Scroll::Delta(scroll_lines)); + self.continue_selection(position) + } } } @@ -664,12 +715,7 @@ impl Terminal { self.pty_tx.notify(bytes); } } else if e.button == MouseButton::Left { - self.events - .push(InternalEvent::SetSelection(Some(Selection::new( - SelectionType::Simple, - point, - side, - )))); + self.begin_select(Selection::new(SelectionType::Simple, point, side)); } } @@ -691,7 +737,9 @@ impl Terminal { let selection = selection_type.map(|selection_type| Selection::new(selection_type, point, side)); - self.events.push(InternalEvent::SetSelection(selection)); + if let Some(sel) = selection { + self.begin_select(sel); + } } } @@ -711,17 +759,16 @@ impl Terminal { } ///Scroll the terminal - pub fn scroll(&mut self, scroll: &ScrollWheelEvent, origin: Vector2F) { - if self.mouse_mode(scroll.shift) { + pub fn scroll_wheel(&mut self, e: &ScrollWheelEvent, origin: Vector2F) { + if self.mouse_mode(e.shift) { //TODO: Currently this only sends the current scroll reports as they come in. Alacritty //Sends the *entire* scroll delta on *every* scroll event, only resetting it when //The scroll enters 'TouchPhase::Started'. Do I need to replicate this? //This would be consistent with a scroll model based on 'distance from origin'... - let scroll_lines = (scroll.delta.y() / self.cur_size.line_height) as i32; - let point = mouse_point(scroll.position.sub(origin), self.cur_size, self.last_offset); + let scroll_lines = (e.delta.y() / self.cur_size.line_height) as i32; + let point = mouse_point(e.position.sub(origin), self.cur_size, self.last_offset); - if let Some(scrolls) = scroll_report(point, scroll_lines as i32, scroll, self.last_mode) - { + if let Some(scrolls) = scroll_report(point, scroll_lines as i32, e, self.last_mode) { for scroll in scrolls { self.pty_tx.notify(scroll); } @@ -729,19 +776,19 @@ impl Terminal { } else if self .last_mode .contains(TermMode::ALT_SCREEN | TermMode::ALTERNATE_SCROLL) - && !scroll.shift + && !e.shift { //TODO: See above TODO, also applies here. - let scroll_lines = ((scroll.delta.y() * ALACRITTY_SCROLL_MULTIPLIER) - / self.cur_size.line_height) as i32; + let scroll_lines = + ((e.delta.y() * ALACRITTY_SCROLL_MULTIPLIER) / self.cur_size.line_height) as i32; self.pty_tx.notify(alt_scroll(scroll_lines)) } else { - let scroll_lines = ((scroll.delta.y() * ALACRITTY_SCROLL_MULTIPLIER) - / self.cur_size.line_height) as i32; + let scroll_lines = + ((e.delta.y() * ALACRITTY_SCROLL_MULTIPLIER) / self.cur_size.line_height) as i32; if scroll_lines != 0 { let scroll = Scroll::Delta(scroll_lines); - self.events.push(InternalEvent::Scroll(scroll)); + self.scroll(scroll); } } } diff --git a/crates/terminal/src/terminal_element.rs b/crates/terminal/src/terminal_element.rs index 66820c727f..a2f2cc7d4c 100644 --- a/crates/terminal/src/terminal_element.rs +++ b/crates/terminal/src/terminal_element.rs @@ -457,7 +457,7 @@ impl TerminalElement { if cx.is_parent_view_focused() { if let Some(conn_handle) = connection.upgrade(cx.app) { conn_handle.update(cx.app, |terminal, cx| { - terminal.mouse_drag(event, origin); + terminal.mouse_drag(event, origin, visible_bounds); cx.notify(); }) } @@ -830,7 +830,7 @@ impl Element for TerminalElement { let origin = bounds.origin() + vec2f(layout.size.cell_width, 0.); if let Some(terminal) = self.terminal.upgrade(cx.app) { - terminal.update(cx.app, |term, _| term.scroll(e, origin)); + terminal.update(cx.app, |term, _| term.scroll_wheel(e, origin)); cx.notify(); } })