Detailed changes
@@ -102,27 +102,37 @@
//
//
"working_directory": "current_project_directory",
- //Set the cursor blinking behavior in the terminal.
- //May take 4 values:
- // 1. Never blink the cursor, ignoring the terminal mode
- // "blinking": "off",
- // 2. Default the cursor blink to off, but allow the terminal to
- // set blinking
- // "blinking": "terminal_controlled",
- // 3. Always blink the cursor, ignoring the terminal mode
- // "blinking": "on",
+ // Set the cursor blinking behavior in the terminal.
+ // May take 4 values:
+ // 1. Never blink the cursor, ignoring the terminal mode
+ // "blinking": "off",
+ // 2. Default the cursor blink to off, but allow the terminal to
+ // set blinking
+ // "blinking": "terminal_controlled",
+ // 3. Always blink the cursor, ignoring the terminal mode
+ // "blinking": "on",
"blinking": "terminal_controlled",
- //Any key-value pairs added to this list will be added to the terminal's
- //enviroment. Use `:` to seperate multiple values.
+ // Set whether Alternate Scroll mode (code: ?1007) is active by default.
+ // Alternate Scroll mode converts mouse scroll events into up / down key
+ // presses when in the alternate screen (e.g. when running applications
+ // like vim or less). The terminal can still set and unset this mode.
+ // May take 2 values:
+ // 1. Default alternate scroll mode to on
+ // "alternate_scroll": "on",
+ // 2. Default alternate scroll mode to off
+ // "alternate_scroll": "off",
+ "alternate_scroll": "off",
+ // Any key-value pairs added to this list will be added to the terminal's
+ // enviroment. Use `:` to seperate multiple values.
"env": {
- //"KEY": "value1:value2"
+ // "KEY": "value1:value2"
}
- //Set the terminal's font size. If this option is not included,
- //the terminal will default to matching the buffer's font size.
- //"font_size": "15"
- //Set the terminal's font family. If this option is not included,
- //the terminal will default to matching the buffer's font family.
- //"font_family": "Zed Mono"
+ // Set the terminal's font size. If this option is not included,
+ // the terminal will default to matching the buffer's font size.
+ // "font_size": "15"
+ // Set the terminal's font family. If this option is not included,
+ // the terminal will default to matching the buffer's font family.
+ // "font_family": "Zed Mono"
},
// Different settings for specific languages.
"languages": {
@@ -155,15 +165,15 @@
"tab_size": 2
}
},
- //LSP Specific settings.
+ // LSP Specific settings.
"lsp": {
- //Specify the LSP name as a key here.
- //As of 8/10/22, supported LSPs are:
- //pyright
- //gopls
- //rust-analyzer
- //typescript-language-server
- //vscode-json-languageserver
+ // Specify the LSP name as a key here.
+ // As of 8/10/22, supported LSPs are:
+ // pyright
+ // gopls
+ // rust-analyzer
+ // typescript-language-server
+ // vscode-json-languageserver
// "rust_analyzer": {
// //These initialization options are merged into Zed's defaults
// "initialization_options": {
@@ -1610,6 +1610,7 @@ impl Element for EditorElement {
position,
delta,
precise,
+ ..
}) => self.scroll(*position, *delta, *precise, layout, paint, cx),
&Event::ModifiersChanged(event) => self.modifiers_changed(event, cx),
@@ -293,6 +293,7 @@ impl Element for Flex {
position,
delta,
precise,
+ ..
}) = event
{
if *remaining_space < 0. && bounds.contains_point(position) {
@@ -316,6 +316,7 @@ impl Element for List {
position,
delta,
precise,
+ ..
}) = event
{
if bounds.contains_point(*position)
@@ -315,6 +315,7 @@ impl Element for UniformList {
position,
delta,
precise,
+ ..
}) = event
{
if bounds.contains_point(*position)
@@ -24,6 +24,10 @@ pub struct ScrollWheelEvent {
pub position: Vector2F,
pub delta: Vector2F,
pub precise: bool,
+ pub ctrl: bool,
+ pub alt: bool,
+ pub shift: bool,
+ pub cmd: bool,
}
#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
@@ -148,6 +148,8 @@ impl Event {
})
}
NSEventType::NSScrollWheel => window_height.map(|window_height| {
+ let modifiers = native_event.modifierFlags();
+
Self::ScrollWheel(ScrollWheelEvent {
position: vec2f(
native_event.locationInWindow().x as f32,
@@ -158,6 +160,10 @@ impl Event {
native_event.scrollingDeltaY() as f32,
),
precise: native_event.hasPreciseScrollingDeltas() == YES,
+ ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
+ alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
+ shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
+ cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
})
}),
NSEventType::NSLeftMouseDragged
@@ -235,7 +235,9 @@ impl Presenter {
if let Some(root_view_id) = cx.root_view_id(self.window_id) {
let mut invalidated_views = Vec::new();
let mut mouse_down_out_handlers = Vec::new();
+ let mut mouse_moved_region = None;
let mut mouse_down_region = None;
+ let mut mouse_up_region = None;
let mut clicked_region = None;
let mut dragged_region = None;
@@ -282,6 +284,15 @@ impl Presenter {
}
}
+ for (region, _) in self.mouse_regions.iter().rev() {
+ if region.bounds.contains_point(position) {
+ invalidated_views.push(region.view_id);
+ mouse_up_region =
+ Some((region.clone(), MouseRegionEvent::Up(e.clone())));
+ break;
+ }
+ }
+
if let Some(moved) = &mut self.last_mouse_moved_event {
if moved.pressed_button == Some(button) {
moved.pressed_button = None;
@@ -302,6 +313,15 @@ impl Presenter {
*prev_drag_position = *position;
}
+ for (region, _) in self.mouse_regions.iter().rev() {
+ if region.bounds.contains_point(*position) {
+ invalidated_views.push(region.view_id);
+ mouse_moved_region =
+ Some((region.clone(), MouseRegionEvent::Move(e.clone())));
+ break;
+ }
+ }
+
self.last_mouse_moved_event = Some(e.clone());
}
@@ -329,6 +349,28 @@ impl Presenter {
}
}
+ if let Some((move_moved_region, region_event)) = mouse_moved_region {
+ handled = true;
+ if let Some(mouse_moved_callback) =
+ move_moved_region.handlers.get(®ion_event.handler_key())
+ {
+ event_cx.with_current_view(move_moved_region.view_id, |event_cx| {
+ mouse_moved_callback(region_event, event_cx);
+ })
+ }
+ }
+
+ if let Some((mouse_up_region, region_event)) = mouse_up_region {
+ handled = true;
+ if let Some(mouse_up_callback) =
+ mouse_up_region.handlers.get(®ion_event.handler_key())
+ {
+ event_cx.with_current_view(mouse_up_region.view_id, |event_cx| {
+ mouse_up_callback(region_event, event_cx);
+ })
+ }
+ }
+
if let Some((clicked_region, region_event)) = clicked_region {
handled = true;
if let Some(click_callback) =
@@ -1,6 +1,7 @@
use std::{any::TypeId, mem::Discriminant, rc::Rc};
use collections::HashMap;
+
use pathfinder_geometry::{rect::RectF, vector::Vector2F};
use crate::{EventContext, MouseButton, MouseButtonEvent, MouseMovedEvent, ScrollWheelEvent};
@@ -97,6 +98,14 @@ impl MouseRegion {
self.handlers = self.handlers.on_hover(handler);
self
}
+
+ pub fn on_move(
+ mut self,
+ handler: impl Fn(MouseMovedEvent, &mut EventContext) + 'static,
+ ) -> Self {
+ self.handlers = self.handlers.on_move(handler);
+ self
+ }
}
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
@@ -267,6 +276,23 @@ impl HandlerSet {
}));
self
}
+
+ pub fn on_move(
+ mut self,
+ handler: impl Fn(MouseMovedEvent, &mut EventContext) + 'static,
+ ) -> Self {
+ self.set.insert((MouseRegionEvent::move_disc(), None),
+ Rc::new(move |region_event, cx| {
+ if let MouseRegionEvent::Move(move_event)= region_event {
+ handler(move_event, cx);
+ } else {
+ panic!(
+ "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Move, found {:?}",
+ region_event);
+ }
+ }));
+ self
+ }
}
#[derive(Debug)]
@@ -84,6 +84,7 @@ pub struct TerminalSettings {
pub font_family: Option<String>,
pub env: Option<HashMap<String, String>>,
pub blinking: Option<TerminalBlink>,
+ pub alternate_scroll: Option<AlternateScroll>,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)]
@@ -114,6 +115,19 @@ impl Default for Shell {
}
}
+#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum AlternateScroll {
+ On,
+ Off,
+}
+
+impl Default for AlternateScroll {
+ fn default() -> Self {
+ AlternateScroll::On
+ }
+}
+
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum WorkingDirectory {
@@ -10,7 +10,7 @@ The TerminalView struct abstracts over failed and successful terminals, passing
#Input
-There are currently 3 distinct paths for getting keystrokes to the terminal:
+There are currently many distinct paths for getting keystrokes to the terminal:
1. Terminal specific characters and bindings. Things like ctrl-a mapping to ASCII control character 1, ANSI escape codes associated with the function keys, etc. These are caught with a raw key-down handler in the element and are processed immediately. This is done with the `try_keystroke()` method on Terminal
@@ -18,3 +18,6 @@ There are currently 3 distinct paths for getting keystrokes to the terminal:
3. IME text. When the special character mappings fail, we pass the keystroke back to GPUI to hand it to the IME system. This comes back to us in the `View::replace_text_in_range()` method, and we then send that to the terminal directly, bypassing `try_keystroke()`.
+4. Pasted text has a seperate pathway.
+
+Generally, there's a distinction between 'keystrokes that need to be mapped' and 'strings which need to be written'. I've attempted to unify these under the '.try_keystroke()' API and the `.input()` API (which try_keystroke uses) so we have consistent input handling across the terminal
@@ -1,23 +1,26 @@
use alacritty_terminal::{
ansi::{Color as AnsiColor, Color::Named, CursorShape as AlacCursorShape, NamedColor},
- grid::{Dimensions, Scroll},
- index::{Column as GridCol, Line as GridLine, Point, Side},
+ grid::Dimensions,
+ index::Point,
selection::SelectionRange,
- term::cell::{Cell, Flags},
+ term::{
+ cell::{Cell, Flags},
+ TermMode,
+ },
};
use editor::{Cursor, CursorShape, HighlightedRange, HighlightedRangeLine};
use gpui::{
color::Color,
- elements::*,
fonts::{Properties, Style::Italic, TextStyle, Underline, Weight},
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
},
- json::json,
+ serde_json::json,
text_layout::{Line, RunStyle},
- Event, FontCache, KeyDownEvent, MouseButton, MouseButtonEvent, MouseMovedEvent, MouseRegion,
- PaintContext, Quad, ScrollWheelEvent, TextLayoutCache, WeakModelHandle, WeakViewHandle,
+ Element, Event, EventContext, FontCache, KeyDownEvent, ModelContext, MouseButton,
+ MouseButtonEvent, MouseRegion, PaintContext, Quad, TextLayoutCache, WeakModelHandle,
+ WeakViewHandle,
};
use itertools::Itertools;
use ordered_float::OrderedFloat;
@@ -25,12 +28,11 @@ use settings::Settings;
use theme::TerminalStyle;
use util::ResultExt;
+use std::fmt::Debug;
use std::{
- cmp::min,
mem,
ops::{Deref, Range},
};
-use std::{fmt::Debug, ops::Sub};
use crate::{
connected_view::{ConnectedView, DeployContextMenu},
@@ -38,11 +40,6 @@ use crate::{
Terminal, TerminalSize,
};
-///Scrolling is unbearably sluggish by default. Alacritty supports a configurable
-///Scroll multiplier that is set to 3 by default. This will be removed when I
-///Implement scroll bars.
-pub const ALACRITTY_SCROLL_MULTIPLIER: f32 = 3.;
-
///The information generated during layout that is nescessary for painting
pub struct LayoutState {
cells: Vec<LayoutCell>,
@@ -52,7 +49,7 @@ pub struct LayoutState {
background_color: Color,
selection_color: Color,
size: TerminalSize,
- display_offset: usize,
+ mode: TermMode,
}
#[derive(Debug)]
@@ -413,90 +410,159 @@ impl TerminalEl {
}
}
+ fn generic_button_handler(
+ connection: WeakModelHandle<Terminal>,
+ origin: Vector2F,
+ f: impl Fn(&mut Terminal, Vector2F, MouseButtonEvent, &mut ModelContext<Terminal>),
+ ) -> impl Fn(MouseButtonEvent, &mut EventContext) {
+ move |event, cx| {
+ cx.focus_parent_view();
+ if let Some(conn_handle) = connection.upgrade(cx.app) {
+ conn_handle.update(cx.app, |terminal, cx| {
+ f(terminal, origin, event, cx);
+
+ cx.notify();
+ })
+ }
+ }
+ }
+
fn attach_mouse_handlers(
&self,
origin: Vector2F,
view_id: usize,
visible_bounds: RectF,
- cur_size: TerminalSize,
- display_offset: usize,
+ mode: TermMode,
cx: &mut PaintContext,
) {
- let mouse_down_connection = self.terminal;
- let click_connection = self.terminal;
- let drag_connection = self.terminal;
- cx.scene.push_mouse_region(
- MouseRegion::new(view_id, None, visible_bounds)
- .on_down(
- MouseButton::Left,
- move |MouseButtonEvent { position, .. }, cx| {
- if let Some(conn_handle) = mouse_down_connection.upgrade(cx.app) {
- conn_handle.update(cx.app, |terminal, cx| {
- let (point, side) = TerminalEl::mouse_to_cell_data(
- position,
- origin,
- cur_size,
- display_offset,
- );
-
- terminal.mouse_down(point, side);
-
- cx.notify();
- })
- }
+ let connection = self.terminal;
+
+ let mut region = MouseRegion::new(view_id, None, visible_bounds);
+
+ //Terminal Emulator controlled behavior:
+ region = region
+ //Start selections
+ .on_down(
+ MouseButton::Left,
+ TerminalEl::generic_button_handler(
+ connection,
+ origin,
+ move |terminal, origin, e, _cx| {
+ terminal.mouse_down(&e, origin);
},
- )
- .on_click(
- MouseButton::Left,
- move |MouseButtonEvent {
- position,
- click_count,
- ..
- },
- cx| {
- cx.focus_parent_view();
- if let Some(conn_handle) = click_connection.upgrade(cx.app) {
- conn_handle.update(cx.app, |terminal, cx| {
- let (point, side) = TerminalEl::mouse_to_cell_data(
- position,
- origin,
- cur_size,
- display_offset,
- );
-
- terminal.click(point, side, click_count);
-
- cx.notify();
- });
- }
+ ),
+ )
+ //Update drag selections
+ .on_drag(MouseButton::Left, move |_prev, event, cx| {
+ 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);
+ cx.notify();
+ })
+ }
+ }
+ })
+ //Copy on up behavior
+ .on_up(
+ MouseButton::Left,
+ TerminalEl::generic_button_handler(
+ connection,
+ origin,
+ move |terminal, origin, e, _cx| {
+ terminal.mouse_up(&e, origin);
},
+ ),
+ )
+ //Handle click based selections
+ .on_click(
+ MouseButton::Left,
+ TerminalEl::generic_button_handler(
+ connection,
+ origin,
+ move |terminal, origin, e, _cx| {
+ terminal.left_click(&e, origin);
+ },
+ ),
+ )
+ //Context menu
+ .on_click(
+ MouseButton::Right,
+ move |e @ MouseButtonEvent { position, .. }, cx| {
+ let mouse_mode = if let Some(conn_handle) = connection.upgrade(cx.app) {
+ conn_handle.update(cx.app, |terminal, _cx| terminal.mouse_mode(e.shift))
+ } else {
+ //If we can't get the model handle, probably can't deploy the context menu
+ true
+ };
+ if !mouse_mode {
+ cx.dispatch_action(DeployContextMenu { position });
+ }
+ },
+ )
+ //This handles both drag mode and mouse motion mode
+ //Mouse Move TODO
+ //This cannot be done conditionally for unknown reasons. Pending drag and drop rework.
+ //This also does not fire on right-mouse-down-move events wild.
+ .on_move(move |event, cx| {
+ dbg!(event);
+ if cx.is_parent_view_focused() {
+ if let Some(conn_handle) = connection.upgrade(cx.app) {
+ conn_handle.update(cx.app, |terminal, cx| {
+ terminal.mouse_move(&event, origin);
+ cx.notify();
+ })
+ }
+ }
+ });
+
+ if mode.contains(TermMode::MOUSE_MODE) {
+ region = region
+ .on_down(
+ MouseButton::Right,
+ TerminalEl::generic_button_handler(
+ connection,
+ origin,
+ move |terminal, origin, e, _cx| {
+ terminal.mouse_down(&e, origin);
+ },
+ ),
)
- .on_click(
+ .on_down(
+ MouseButton::Middle,
+ TerminalEl::generic_button_handler(
+ connection,
+ origin,
+ move |terminal, origin, e, _cx| {
+ terminal.mouse_down(&e, origin);
+ },
+ ),
+ )
+ .on_up(
MouseButton::Right,
- move |MouseButtonEvent { position, .. }, cx| {
- cx.dispatch_action(DeployContextMenu { position });
- },
+ TerminalEl::generic_button_handler(
+ connection,
+ origin,
+ move |terminal, origin, e, _cx| {
+ terminal.mouse_up(&e, origin);
+ },
+ ),
)
- .on_drag(
- MouseButton::Left,
- move |_, MouseMovedEvent { position, .. }, cx| {
- if let Some(conn_handle) = drag_connection.upgrade(cx.app) {
- conn_handle.update(cx.app, |terminal, cx| {
- let (point, side) = TerminalEl::mouse_to_cell_data(
- position,
- origin,
- cur_size,
- display_offset,
- );
-
- terminal.drag(point, side);
-
- cx.notify()
- });
- }
- },
- ),
- );
+ .on_up(
+ MouseButton::Middle,
+ TerminalEl::generic_button_handler(
+ connection,
+ origin,
+ move |terminal, origin, e, _cx| {
+ terminal.mouse_up(&e, origin);
+ },
+ ),
+ )
+ }
+
+ //TODO: Mouse drag isn't correct
+ //TODO: Nor is mouse motion. Move events aren't happening??
+ cx.scene.push_mouse_region(region);
}
///Configures a text style from the current settings.
@@ -530,47 +596,6 @@ impl TerminalEl {
underline: Default::default(),
}
}
-
- pub fn mouse_to_cell_data(
- pos: Vector2F,
- origin: Vector2F,
- cur_size: TerminalSize,
- display_offset: usize,
- ) -> (Point, alacritty_terminal::index::Direction) {
- let pos = pos.sub(origin);
- let point = {
- let col = pos.x() / cur_size.cell_width; //TODO: underflow...
- let col = min(GridCol(col as usize), cur_size.last_column());
-
- let line = pos.y() / cur_size.line_height;
- let line = min(line as i32, cur_size.bottommost_line().0);
-
- Point::new(GridLine(line - display_offset as i32), col)
- };
-
- //Copied (with modifications) from alacritty/src/input.rs > Processor::cell_side()
- let side = {
- let x = pos.0.x() as usize;
- let cell_x =
- x.saturating_sub(cur_size.cell_width as usize) % cur_size.cell_width as usize;
- let half_cell_width = (cur_size.cell_width / 2.0) as usize;
-
- let additional_padding =
- (cur_size.width() - cur_size.cell_width * 2.) % cur_size.cell_width;
- let end_of_grid = cur_size.width() - cur_size.cell_width - additional_padding;
- //Width: Pixels or columns?
- if cell_x > half_cell_width
- // Edge case when mouse leaves the window.
- || x as f32 >= end_of_grid
- {
- Side::Right
- } else {
- Side::Left
- }
- };
-
- (point, side)
- }
}
impl Element for TerminalEl {
@@ -601,7 +626,7 @@ impl Element for TerminalEl {
terminal_theme.colors.background
};
- let (cells, selection, cursor, display_offset, cursor_text) = self
+ let (cells, selection, cursor, display_offset, cursor_text, mode) = self
.terminal
.upgrade(cx)
.unwrap()
@@ -624,13 +649,13 @@ impl Element for TerminalEl {
cell: ic.cell.clone(),
}),
);
-
(
cells,
content.selection,
content.cursor,
content.display_offset,
cursor_text,
+ content.mode,
)
})
});
@@ -709,7 +734,7 @@ impl Element for TerminalEl {
size: dimensions,
rects,
highlights,
- display_offset,
+ mode,
},
)
}
@@ -728,14 +753,7 @@ impl Element for TerminalEl {
let origin = bounds.origin() + vec2f(layout.size.cell_width, 0.);
//Elements are ephemeral, only at paint time do we know what could be clicked by a mouse
- self.attach_mouse_handlers(
- origin,
- self.view.id(),
- visible_bounds,
- layout.size,
- layout.display_offset,
- cx,
- );
+ self.attach_mouse_handlers(origin, self.view.id(), visible_bounds, layout.mode, cx);
cx.paint_layer(clip_bounds, |cx| {
//Start with a background color
@@ -799,28 +817,22 @@ impl Element for TerminalEl {
fn dispatch_event(
&mut self,
event: &gpui::Event,
- _bounds: gpui::geometry::rect::RectF,
+ bounds: gpui::geometry::rect::RectF,
visible_bounds: gpui::geometry::rect::RectF,
layout: &mut Self::LayoutState,
_paint: &mut Self::PaintState,
cx: &mut gpui::EventContext,
) -> bool {
match event {
- Event::ScrollWheel(ScrollWheelEvent {
- delta, position, ..
- }) => visible_bounds
- .contains_point(*position)
+ Event::ScrollWheel(e) => visible_bounds
+ .contains_point(e.position)
.then(|| {
- let vertical_scroll =
- (delta.y() / layout.size.line_height) * ALACRITTY_SCROLL_MULTIPLIER;
+ 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(Scroll::Delta(vertical_scroll.round() as i32))
- });
+ terminal.update(cx.app, |term, _| term.scroll(e, origin));
+ cx.notify();
}
-
- cx.notify();
})
.is_some(),
Event::KeyDown(KeyDownEvent { keystroke, .. }) => {
@@ -828,7 +840,6 @@ impl Element for TerminalEl {
return false;
}
- //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);
@@ -884,36 +895,3 @@ impl Element for TerminalEl {
Some(layout.cursor.as_ref()?.bounding_rect(origin))
}
}
-
-mod test {
-
- #[test]
- fn test_mouse_to_selection() {
- let term_width = 100.;
- let term_height = 200.;
- let cell_width = 10.;
- let line_height = 20.;
- let mouse_pos_x = 100.; //Window relative
- let mouse_pos_y = 100.; //Window relative
- let origin_x = 10.;
- let origin_y = 20.;
-
- let cur_size = crate::connected_el::TerminalSize::new(
- line_height,
- cell_width,
- gpui::geometry::vector::vec2f(term_width, term_height),
- );
-
- let mouse_pos = gpui::geometry::vector::vec2f(mouse_pos_x, mouse_pos_y);
- let origin = gpui::geometry::vector::vec2f(origin_x, origin_y); //Position of terminal window, 1 'cell' in
- let (point, _) =
- crate::connected_el::TerminalEl::mouse_to_cell_data(mouse_pos, origin, cur_size, 0);
- assert_eq!(
- point,
- alacritty_terminal::index::Point::new(
- alacritty_terminal::index::Line(((mouse_pos_y - origin_y) / line_height) as i32),
- alacritty_terminal::index::Column(((mouse_pos_x - origin_x) / cell_width) as usize),
- )
- );
- }
-}
@@ -251,7 +251,8 @@ impl ConnectedView {
///Attempt to paste the clipboard into the terminal
fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
if let Some(item) = cx.read_from_clipboard() {
- self.terminal.read(cx).paste(item.text());
+ self.terminal
+ .update(cx, |terminal, _cx| terminal.paste(item.text()));
}
}
@@ -359,8 +360,7 @@ impl View for ConnectedView {
cx: &mut ViewContext<Self>,
) {
self.terminal.update(cx, |terminal, _| {
- terminal.write_to_pty(text.into());
- terminal.scroll(alacritty_terminal::grid::Scroll::Bottom);
+ terminal.input(text.into());
});
}
@@ -1,3 +1,4 @@
+/// The mappings defined in this file where created from reading the alacritty source
use alacritty_terminal::term::TermMode;
use gpui::keymap::Keystroke;
@@ -1,2 +1,3 @@
pub mod colors;
pub mod keys;
+pub mod mouse;
@@ -0,0 +1,330 @@
+use std::cmp::{max, min};
+use std::iter::repeat;
+
+use alacritty_terminal::grid::Dimensions;
+/// Most of the code, and specifically the constants, in this are copied from Alacritty,
+/// with modifications for our circumstances
+use alacritty_terminal::index::{Column as GridCol, Line as GridLine, Point, Side};
+use alacritty_terminal::term::TermMode;
+use gpui::{geometry::vector::Vector2F, MouseButtonEvent, MouseMovedEvent, ScrollWheelEvent};
+
+use crate::TerminalSize;
+
+struct Modifiers {
+ ctrl: bool,
+ shift: bool,
+ alt: bool,
+}
+
+impl Modifiers {
+ fn from_moved(e: &MouseMovedEvent) -> Self {
+ Modifiers {
+ ctrl: e.ctrl,
+ shift: e.shift,
+ alt: e.alt,
+ }
+ }
+
+ fn from_button(e: &MouseButtonEvent) -> Self {
+ Modifiers {
+ ctrl: e.ctrl,
+ shift: e.shift,
+ alt: e.alt,
+ }
+ }
+
+ //TODO: Determine if I should add modifiers into the ScrollWheelEvent type
+ fn from_scroll() -> Self {
+ Modifiers {
+ ctrl: false,
+ shift: false,
+ alt: false,
+ }
+ }
+}
+
+enum MouseFormat {
+ SGR,
+ Normal(bool),
+}
+
+impl MouseFormat {
+ fn from_mode(mode: TermMode) -> Self {
+ if mode.contains(TermMode::SGR_MOUSE) {
+ MouseFormat::SGR
+ } else if mode.contains(TermMode::UTF8_MOUSE) {
+ MouseFormat::Normal(true)
+ } else {
+ MouseFormat::Normal(false)
+ }
+ }
+}
+
+#[derive(Debug)]
+enum MouseButton {
+ LeftButton = 0,
+ MiddleButton = 1,
+ RightButton = 2,
+ LeftMove = 32,
+ MiddleMove = 33,
+ RightMove = 34,
+ NoneMove = 35,
+ ScrollUp = 64,
+ ScrollDown = 65,
+ Other = 99,
+}
+
+impl MouseButton {
+ fn from_move(e: &MouseMovedEvent) -> Self {
+ match e.pressed_button {
+ Some(b) => match b {
+ gpui::MouseButton::Left => MouseButton::LeftMove,
+ gpui::MouseButton::Middle => MouseButton::MiddleMove,
+ gpui::MouseButton::Right => MouseButton::RightMove,
+ gpui::MouseButton::Navigate(_) => MouseButton::Other,
+ },
+ None => MouseButton::NoneMove,
+ }
+ }
+
+ fn from_button(e: &MouseButtonEvent) -> Self {
+ match e.button {
+ gpui::MouseButton::Left => MouseButton::LeftButton,
+ gpui::MouseButton::Right => MouseButton::MiddleButton,
+ gpui::MouseButton::Middle => MouseButton::RightButton,
+ gpui::MouseButton::Navigate(_) => MouseButton::Other,
+ }
+ }
+
+ fn from_scroll(e: &ScrollWheelEvent) -> Self {
+ if e.delta.y() > 0. {
+ MouseButton::ScrollUp
+ } else {
+ MouseButton::ScrollDown
+ }
+ }
+
+ fn is_other(&self) -> bool {
+ match self {
+ MouseButton::Other => true,
+ _ => false,
+ }
+ }
+}
+
+pub fn scroll_report(
+ point: Point,
+ scroll_lines: i32,
+ e: &ScrollWheelEvent,
+ mode: TermMode,
+) -> Option<impl Iterator<Item = Vec<u8>>> {
+ if mode.intersects(TermMode::MOUSE_MODE) {
+ mouse_report(
+ point,
+ MouseButton::from_scroll(e),
+ true,
+ Modifiers::from_scroll(),
+ MouseFormat::from_mode(mode),
+ )
+ .map(|report| repeat(report).take(max(scroll_lines, 1) as usize))
+ } else {
+ None
+ }
+}
+
+pub fn alt_scroll(scroll_lines: i32) -> Vec<u8> {
+ let cmd = if scroll_lines > 0 { b'A' } else { b'B' };
+
+ let mut content = Vec::with_capacity(scroll_lines.abs() as usize * 3);
+ for _ in 0..scroll_lines.abs() {
+ content.push(0x1b);
+ content.push(b'O');
+ content.push(cmd);
+ }
+ content
+}
+
+pub fn mouse_button_report(
+ point: Point,
+ e: &MouseButtonEvent,
+ pressed: bool,
+ mode: TermMode,
+) -> Option<Vec<u8>> {
+ let button = MouseButton::from_button(e);
+ if !button.is_other() && mode.intersects(TermMode::MOUSE_MODE) {
+ mouse_report(
+ point,
+ button,
+ pressed,
+ Modifiers::from_button(e),
+ MouseFormat::from_mode(mode),
+ )
+ } else {
+ None
+ }
+}
+
+pub fn mouse_moved_report(point: Point, e: &MouseMovedEvent, mode: TermMode) -> Option<Vec<u8>> {
+ let button = MouseButton::from_move(e);
+ dbg!(&button);
+
+ if !button.is_other() && mode.intersects(TermMode::MOUSE_MOTION | TermMode::MOUSE_DRAG) {
+ //Only drags are reported in drag mode, so block NoneMove.
+ if mode.contains(TermMode::MOUSE_DRAG) && matches!(button, MouseButton::NoneMove) {
+ None
+ } else {
+ mouse_report(
+ point,
+ button,
+ true,
+ Modifiers::from_moved(e),
+ MouseFormat::from_mode(mode),
+ )
+ }
+ } else {
+ None
+ }
+}
+
+pub fn mouse_side(pos: Vector2F, cur_size: TerminalSize) -> alacritty_terminal::index::Direction {
+ let x = pos.0.x() as usize;
+ let cell_x = x.saturating_sub(cur_size.cell_width as usize) % cur_size.cell_width as usize;
+ let half_cell_width = (cur_size.cell_width / 2.0) as usize;
+ let additional_padding = (cur_size.width() - cur_size.cell_width * 2.) % cur_size.cell_width;
+ let end_of_grid = cur_size.width() - cur_size.cell_width - additional_padding;
+ //Width: Pixels or columns?
+ if cell_x > half_cell_width
+ // Edge case when mouse leaves the window.
+ || x as f32 >= end_of_grid
+ {
+ Side::Right
+ } else {
+ Side::Left
+ }
+}
+
+pub fn mouse_point(pos: Vector2F, cur_size: TerminalSize, display_offset: usize) -> Point {
+ let col = pos.x() / cur_size.cell_width;
+ let col = min(GridCol(col as usize), cur_size.last_column());
+ let line = pos.y() / cur_size.line_height;
+ let line = min(line as i32, cur_size.bottommost_line().0);
+ Point::new(GridLine(line - display_offset as i32), col)
+}
+
+///Generate the bytes to send to the terminal, from the cell location, a mouse event, and the terminal mode
+fn mouse_report(
+ point: Point,
+ button: MouseButton,
+ pressed: bool,
+ modifiers: Modifiers,
+ format: MouseFormat,
+) -> Option<Vec<u8>> {
+ if point.line < 0 {
+ return None;
+ }
+
+ let mut mods = 0;
+ if modifiers.shift {
+ mods += 4;
+ }
+ if modifiers.alt {
+ mods += 8;
+ }
+ if modifiers.ctrl {
+ mods += 16;
+ }
+
+ match format {
+ MouseFormat::SGR => {
+ Some(sgr_mouse_report(point, button as u8 + mods, pressed).into_bytes())
+ }
+ MouseFormat::Normal(utf8) => {
+ if pressed {
+ normal_mouse_report(point, button as u8 + mods, utf8)
+ } else {
+ normal_mouse_report(point, 3 + mods, utf8)
+ }
+ }
+ }
+}
+
+fn normal_mouse_report(point: Point, button: u8, utf8: bool) -> Option<Vec<u8>> {
+ let Point { line, column } = point;
+ let max_point = if utf8 { 2015 } else { 223 };
+
+ if line >= max_point || column >= max_point {
+ return None;
+ }
+
+ let mut msg = vec![b'\x1b', b'[', b'M', 32 + button];
+
+ let mouse_pos_encode = |pos: usize| -> Vec<u8> {
+ let pos = 32 + 1 + pos;
+ let first = 0xC0 + pos / 64;
+ let second = 0x80 + (pos & 63);
+ vec![first as u8, second as u8]
+ };
+
+ if utf8 && column >= 95 {
+ msg.append(&mut mouse_pos_encode(column.0));
+ } else {
+ msg.push(32 + 1 + column.0 as u8);
+ }
+
+ if utf8 && line >= 95 {
+ msg.append(&mut mouse_pos_encode(line.0 as usize));
+ } else {
+ msg.push(32 + 1 + line.0 as u8);
+ }
+
+ Some(msg)
+}
+
+fn sgr_mouse_report(point: Point, button: u8, pressed: bool) -> String {
+ let c = if pressed { 'M' } else { 'm' };
+
+ let msg = format!(
+ "\x1b[<{};{};{}{}",
+ button,
+ point.column + 1,
+ point.line + 1,
+ c
+ );
+
+ msg
+}
+
+#[cfg(test)]
+mod test {
+ use crate::mappings::mouse::mouse_point;
+
+ #[test]
+ fn test_mouse_to_selection() {
+ let term_width = 100.;
+ let term_height = 200.;
+ let cell_width = 10.;
+ let line_height = 20.;
+ let mouse_pos_x = 100.; //Window relative
+ let mouse_pos_y = 100.; //Window relative
+ let origin_x = 10.;
+ let origin_y = 20.;
+
+ let cur_size = crate::TerminalSize::new(
+ line_height,
+ cell_width,
+ gpui::geometry::vector::vec2f(term_width, term_height),
+ );
+
+ let mouse_pos = gpui::geometry::vector::vec2f(mouse_pos_x, mouse_pos_y);
+ let origin = gpui::geometry::vector::vec2f(origin_x, origin_y); //Position of terminal window, 1 'cell' in
+ let mouse_pos = mouse_pos - origin;
+ let point = mouse_point(mouse_pos, cur_size, 0);
+ assert_eq!(
+ point,
+ alacritty_terminal::index::Point::new(
+ alacritty_terminal::index::Line(((mouse_pos_y - origin_y) / line_height) as i32),
+ alacritty_terminal::index::Column(((mouse_pos_x - origin_x) / cell_width) as usize),
+ )
+ );
+ }
+}
@@ -24,15 +24,19 @@ use futures::{
FutureExt,
};
+use mappings::mouse::{
+ alt_scroll, mouse_button_report, mouse_moved_report, mouse_point, mouse_side, scroll_report,
+};
use modal::deploy_modal;
-use settings::{Settings, Shell, TerminalBlink};
-use std::{collections::HashMap, fmt::Display, path::PathBuf, sync::Arc, time::Duration};
+use settings::{AlternateScroll, Settings, Shell, TerminalBlink};
+use std::{collections::HashMap, fmt::Display, ops::Sub, path::PathBuf, sync::Arc, time::Duration};
use thiserror::Error;
use gpui::{
geometry::vector::{vec2f, Vector2F},
keymap::Keystroke,
- ClipboardItem, Entity, ModelContext, MutableAppContext,
+ ClipboardItem, Entity, ModelContext, MouseButton, MouseButtonEvent, MouseMovedEvent,
+ MutableAppContext, ScrollWheelEvent,
};
use crate::mappings::{
@@ -48,12 +52,15 @@ pub fn init(cx: &mut MutableAppContext) {
connected_view::init(cx);
}
+///Scrolling is unbearably sluggish by default. Alacritty supports a configurable
+///Scroll multiplier that is set to 3 by default. This will be removed when I
+///Implement scroll bars.
+pub const ALACRITTY_SCROLL_MULTIPLIER: f32 = 3.;
+
const DEBUG_TERMINAL_WIDTH: f32 = 500.;
-const DEBUG_TERMINAL_HEIGHT: f32 = 30.; //This needs to be wide enough that the CI & a local dev's prompt can fill the whole space.
+const DEBUG_TERMINAL_HEIGHT: f32 = 30.;
const DEBUG_CELL_WIDTH: f32 = 5.;
const DEBUG_LINE_HEIGHT: f32 = 5.;
-// const MAX_FRAME_RATE: f32 = 60.;
-// const BACK_BUFFER_SIZE: usize = 5000;
///Upward flowing events, for changing the title and such
#[derive(Clone, Copy, Debug)]
@@ -256,6 +263,7 @@ impl TerminalBuilder {
env: Option<HashMap<String, String>>,
initial_size: TerminalSize,
blink_settings: Option<TerminalBlink>,
+ alternate_scroll: &AlternateScroll,
) -> Result<TerminalBuilder> {
let pty_config = {
let alac_shell = shell.clone().and_then(|shell| match shell {
@@ -299,6 +307,14 @@ 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.
+ term.unset_mode(alacritty_terminal::ansi::Mode::AlternateScroll)
+ }
+
let term = Arc::new(FairMutex::new(term));
//Setup the pty...
@@ -348,7 +364,8 @@ impl TerminalBuilder {
default_title: shell_txt,
last_mode: TermMode::NONE,
cur_size: initial_size,
- // utilization: 0.,
+ last_mouse: None,
+ last_offset: 0,
};
Ok(TerminalBuilder {
@@ -406,27 +423,6 @@ impl TerminalBuilder {
})
.detach();
- // //Render loop
- // cx.spawn_weak(|this, mut cx| async move {
- // loop {
- // let utilization = match this.upgrade(&cx) {
- // Some(this) => this.update(&mut cx, |this, cx| {
- // cx.notify();
- // this.utilization()
- // }),
- // None => break,
- // };
-
- // let utilization = (1. - utilization).clamp(0.1, 1.);
- // let delay = cx.background().timer(Duration::from_secs_f32(
- // 1.0 / (Terminal::default_fps() * utilization),
- // ));
-
- // delay.await;
- // }
- // })
- // .detach();
-
self.terminal
}
}
@@ -439,19 +435,11 @@ pub struct Terminal {
title: String,
cur_size: TerminalSize,
last_mode: TermMode,
- //Percentage, between 0 and 1
- // utilization: f32,
+ last_offset: usize,
+ last_mouse: Option<(Point, Direction)>,
}
impl Terminal {
- // fn default_fps() -> f32 {
- // MAX_FRAME_RATE
- // }
-
- // fn utilization(&self) -> f32 {
- // self.utilization
- // }
-
fn process_event(&mut self, event: &AlacTermEvent, cx: &mut ModelContext<Self>) {
match event {
AlacTermEvent::Title(title) => {
@@ -494,12 +482,6 @@ impl Terminal {
}
}
- // fn process_events(&mut self, events: Vec<AlacTermEvent>, cx: &mut ModelContext<Self>) {
- // for event in events.into_iter() {
- // self.process_event(&event, cx);
- // }
- // }
-
///Takes events from Alacritty and translates them to behavior on this view
fn process_terminal_event(
&mut self,
@@ -507,7 +489,6 @@ impl Terminal {
term: &mut Term<ZedListener>,
cx: &mut ModelContext<Self>,
) {
- // TODO: Handle is_self_focused in subscription on terminal view
match event {
InternalEvent::TermEvent(term_event) => {
if let AlacTermEvent::ColorRequest(index, format) = term_event {
@@ -546,8 +527,14 @@ 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);
+ }
+
///Write the Input payload to the tty.
- pub fn write_to_pty(&self, input: String) {
+ fn write_to_pty(&self, input: String) {
self.pty_tx.notify(input.into_bytes());
}
@@ -563,8 +550,7 @@ impl Terminal {
pub fn try_keystroke(&mut self, keystroke: &Keystroke) -> bool {
let esc = to_esc_str(keystroke, &self.last_mode);
if let Some(esc) = esc {
- self.write_to_pty(esc);
- self.scroll(Scroll::Bottom);
+ self.input(esc);
true
} else {
false
@@ -572,14 +558,13 @@ impl Terminal {
}
///Paste text into the terminal
- pub fn paste(&self, text: &str) {
- if self.last_mode.contains(TermMode::BRACKETED_PASTE) {
- self.write_to_pty("\x1b[200~".to_string());
- self.write_to_pty(text.replace('\x1b', ""));
- self.write_to_pty("\x1b[201~".to_string());
+ pub fn paste(&mut self, text: &str) {
+ let paste_text = if self.last_mode.contains(TermMode::BRACKETED_PASTE) {
+ format!("{}{}{}", "\x1b[200~", text.replace('\x1b', ""), "\x1b[201~")
} else {
- self.write_to_pty(text.replace("\r\n", "\r").replace('\n', "\r"));
- }
+ text.replace("\r\n", "\r").replace('\n', "\r")
+ };
+ self.input(paste_text)
}
pub fn copy(&mut self) {
@@ -597,21 +582,17 @@ impl Terminal {
self.process_terminal_event(&e, &mut term, cx)
}
- // self.utilization = Self::estimate_utilization(term.take_last_processed_bytes());
self.last_mode = *term.mode();
let content = term.renderable_content();
+ self.last_offset = content.display_offset;
+
let cursor_text = term.grid()[content.cursor.point].c;
f(content, cursor_text)
}
- ///Scroll the terminal
- pub fn scroll(&mut self, scroll: Scroll) {
- self.events.push(InternalEvent::Scroll(scroll));
- }
-
pub fn focus_in(&self) {
if self.last_mode.contains(TermMode::FOCUS_IN_OUT) {
self.write_to_pty("\x1b[I".to_string());
@@ -624,34 +605,143 @@ impl Terminal {
}
}
- pub fn click(&mut self, point: Point, side: Direction, clicks: usize) {
- let selection_type = match clicks {
- 0 => return, //This is a release
- 1 => Some(SelectionType::Simple),
- 2 => Some(SelectionType::Semantic),
- 3 => Some(SelectionType::Lines),
- _ => None,
- };
+ pub fn mouse_changed(&mut self, point: Point, side: Direction) -> bool {
+ match self.last_mouse {
+ Some((old_point, old_side)) => {
+ if old_point == point && old_side == side {
+ false
+ } else {
+ self.last_mouse = Some((point, side));
+ true
+ }
+ }
+ None => {
+ self.last_mouse = Some((point, side));
+ true
+ }
+ }
+ }
+
+ pub fn mouse_mode(&self, shift: bool) -> bool {
+ self.last_mode.intersects(TermMode::MOUSE_MODE) && !shift
+ }
+
+ pub fn mouse_move(&mut self, e: &MouseMovedEvent, origin: Vector2F) {
+ dbg!("term mouse_move");
+ let position = e.position.sub(origin);
- let selection =
- selection_type.map(|selection_type| Selection::new(selection_type, point, side));
+ let point = mouse_point(position, self.cur_size, self.last_offset);
+ let side = mouse_side(position, self.cur_size);
- self.events.push(InternalEvent::SetSelection(selection));
+ if self.mouse_changed(point, side) && self.mouse_mode(e.shift) {
+ if let Some(bytes) = mouse_moved_report(point, e, self.last_mode) {
+ self.pty_tx.notify(bytes);
+ }
+ }
+ }
+
+ pub fn mouse_drag(&mut self, e: MouseMovedEvent, origin: Vector2F) {
+ 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);
+
+ self.events
+ .push(InternalEvent::UpdateSelection((point, side)));
+ }
+ }
+
+ pub fn mouse_down(&mut self, e: &MouseButtonEvent, origin: Vector2F) {
+ let position = e.position.sub(origin);
+ let point = mouse_point(position, self.cur_size, self.last_offset);
+ let side = mouse_side(position, self.cur_size);
+
+ if self.mouse_mode(e.shift) {
+ if let Some(bytes) = mouse_button_report(point, e, true, self.last_mode) {
+ self.pty_tx.notify(bytes);
+ }
+ } else if e.button == MouseButton::Left {
+ self.events
+ .push(InternalEvent::SetSelection(Some(Selection::new(
+ SelectionType::Simple,
+ point,
+ side,
+ ))));
+ }
+ }
+
+ pub fn left_click(&mut self, e: &MouseButtonEvent, origin: Vector2F) {
+ 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);
+
+ let selection_type = match e.click_count {
+ 0 => return, //This is a release
+ 1 => Some(SelectionType::Simple),
+ 2 => Some(SelectionType::Semantic),
+ 3 => Some(SelectionType::Lines),
+ _ => None,
+ };
+
+ let selection =
+ selection_type.map(|selection_type| Selection::new(selection_type, point, side));
+
+ self.events.push(InternalEvent::SetSelection(selection));
+ }
}
- pub fn drag(&mut self, point: Point, side: Direction) {
- self.events
- .push(InternalEvent::UpdateSelection((point, side)));
+ pub fn mouse_up(&mut self, e: &MouseButtonEvent, origin: Vector2F) {
+ let position = e.position.sub(origin);
+ if self.mouse_mode(e.shift) {
+ let point = mouse_point(position, self.cur_size, self.last_offset);
+
+ if let Some(bytes) = mouse_button_report(point, e, false, self.last_mode) {
+ self.pty_tx.notify(bytes);
+ }
+ } else if e.button == MouseButton::Left {
+ // Seems pretty standard to automatically copy on mouse_up for terminals,
+ // so let's do that here
+ self.copy();
+ }
}
- ///TODO: Check if the mouse_down-then-click assumption holds, so this code works as expected
- pub fn mouse_down(&mut self, point: Point, side: Direction) {
- self.events
- .push(InternalEvent::SetSelection(Some(Selection::new(
- SelectionType::Simple,
- point,
- side,
- ))));
+ ///Scroll the terminal
+ pub fn scroll(&mut self, scroll: &ScrollWheelEvent, origin: Vector2F) {
+ if self.mouse_mode(scroll.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);
+
+ if let Some(scrolls) = scroll_report(point, scroll_lines as i32, scroll, self.last_mode)
+ {
+ for scroll in scrolls {
+ self.pty_tx.notify(scroll);
+ }
+ };
+ } else if self
+ .last_mode
+ .contains(TermMode::ALT_SCREEN | TermMode::ALTERNATE_SCROLL)
+ && !scroll.shift
+ {
+ //TODO: See above TODO, also applies here.
+ let scroll_lines = ((scroll.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;
+ if scroll_lines != 0 {
+ let scroll = Scroll::Delta(scroll_lines);
+ self.events.push(InternalEvent::Scroll(scroll));
+ }
+ }
}
}
@@ -10,7 +10,7 @@ use workspace::{Item, Workspace};
use crate::TerminalSize;
use project::{LocalWorktree, Project, ProjectPath};
-use settings::{Settings, WorkingDirectory};
+use settings::{AlternateScroll, Settings, WorkingDirectory};
use smallvec::SmallVec;
use std::path::{Path, PathBuf};
@@ -94,12 +94,26 @@ impl TerminalView {
let shell = settings.terminal_overrides.shell.clone();
let envs = settings.terminal_overrides.env.clone(); //Should be short and cheap.
+ //TODO: move this pattern to settings
+ let scroll = settings
+ .terminal_overrides
+ .alternate_scroll
+ .as_ref()
+ .unwrap_or(
+ settings
+ .terminal_defaults
+ .alternate_scroll
+ .as_ref()
+ .unwrap_or_else(|| &AlternateScroll::On),
+ );
+
let content = match TerminalBuilder::new(
working_directory.clone(),
shell,
envs,
size_info,
settings.terminal_overrides.blinking.clone(),
+ scroll,
) {
Ok(terminal) => {
let terminal = cx.add_model(|cx| terminal.subscribe(cx));
@@ -5,7 +5,6 @@
"requires": true,
"packages": {
"": {
- "name": "styles",
"version": "1.0.0",
"license": "ISC",
"dependencies": {