Terminal mouse improvements (#25104)
Closes #24911 Closes #17983 Closes #7073 Release Notes: - Terminal: Fix cmd-click on links/files when terminal is not focused - Terminal: Remove hover treatment after Zed hides/re-opens --------- Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
This commit is contained in:
parent
b1872e3afd
commit
60a44359e4
6 changed files with 229 additions and 230 deletions
|
@ -22,7 +22,7 @@ use alacritty_terminal::{
|
||||||
term::Config,
|
term::Config,
|
||||||
vte::ansi::Processor,
|
vte::ansi::Processor,
|
||||||
};
|
};
|
||||||
use gpui::{canvas, size, ClipboardItem, Entity, FontStyle, TextStyle, WhiteSpace};
|
use gpui::{canvas, size, Bounds, ClipboardItem, Entity, FontStyle, TextStyle, WhiteSpace};
|
||||||
use language::Buffer;
|
use language::Buffer;
|
||||||
use settings::Settings as _;
|
use settings::Settings as _;
|
||||||
use terminal_view::terminal_element::TerminalElement;
|
use terminal_view::terminal_element::TerminalElement;
|
||||||
|
@ -85,7 +85,7 @@ pub fn text_style(window: &mut Window, cx: &mut App) -> TextStyle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the default terminal size for the terminal output.
|
/// Returns the default terminal size for the terminal output.
|
||||||
pub fn terminal_size(window: &mut Window, cx: &mut App) -> terminal::TerminalSize {
|
pub fn terminal_size(window: &mut Window, cx: &mut App) -> terminal::TerminalBounds {
|
||||||
let text_style = text_style(window, cx);
|
let text_style = text_style(window, cx);
|
||||||
let text_system = window.text_system();
|
let text_system = window.text_system();
|
||||||
|
|
||||||
|
@ -106,10 +106,13 @@ pub fn terminal_size(window: &mut Window, cx: &mut App) -> terminal::TerminalSiz
|
||||||
let width = columns as f32 * cell_width;
|
let width = columns as f32 * cell_width;
|
||||||
let height = num_lines as f32 * window.line_height();
|
let height = num_lines as f32 * window.line_height();
|
||||||
|
|
||||||
terminal::TerminalSize {
|
terminal::TerminalBounds {
|
||||||
cell_width,
|
cell_width,
|
||||||
line_height,
|
line_height,
|
||||||
size: size(width, height),
|
bounds: Bounds {
|
||||||
|
origin: gpui::Point::default(),
|
||||||
|
size: size(width, height),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,10 +280,10 @@ impl Render for TerminalOutput {
|
||||||
for rect in rects {
|
for rect in rects {
|
||||||
rect.paint(
|
rect.paint(
|
||||||
bounds.origin,
|
bounds.origin,
|
||||||
&terminal::TerminalSize {
|
&terminal::TerminalBounds {
|
||||||
cell_width,
|
cell_width,
|
||||||
line_height: text_line_height,
|
line_height: text_line_height,
|
||||||
size: bounds.size,
|
bounds,
|
||||||
},
|
},
|
||||||
window,
|
window,
|
||||||
);
|
);
|
||||||
|
@ -289,10 +292,10 @@ impl Render for TerminalOutput {
|
||||||
for cell in cells {
|
for cell in cells {
|
||||||
cell.paint(
|
cell.paint(
|
||||||
bounds.origin,
|
bounds.origin,
|
||||||
&terminal::TerminalSize {
|
&terminal::TerminalBounds {
|
||||||
cell_width,
|
cell_width,
|
||||||
line_height: text_line_height,
|
line_height: text_line_height,
|
||||||
size: bounds.size,
|
bounds,
|
||||||
},
|
},
|
||||||
bounds,
|
bounds,
|
||||||
window,
|
window,
|
||||||
|
|
|
@ -6,9 +6,9 @@ use alacritty_terminal::grid::Dimensions;
|
||||||
/// with modifications for our circumstances
|
/// with modifications for our circumstances
|
||||||
use alacritty_terminal::index::{Column as GridCol, Line as GridLine, Point as AlacPoint, Side};
|
use alacritty_terminal::index::{Column as GridCol, Line as GridLine, Point as AlacPoint, Side};
|
||||||
use alacritty_terminal::term::TermMode;
|
use alacritty_terminal::term::TermMode;
|
||||||
use gpui::{px, Modifiers, MouseButton, MouseMoveEvent, Pixels, Point, ScrollWheelEvent};
|
use gpui::{px, Modifiers, MouseButton, Pixels, Point, ScrollWheelEvent};
|
||||||
|
|
||||||
use crate::TerminalSize;
|
use crate::TerminalBounds;
|
||||||
|
|
||||||
enum MouseFormat {
|
enum MouseFormat {
|
||||||
Sgr,
|
Sgr,
|
||||||
|
@ -42,14 +42,12 @@ enum AlacMouseButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AlacMouseButton {
|
impl AlacMouseButton {
|
||||||
fn from_move(e: &MouseMoveEvent) -> Self {
|
fn from_move_button(e: Option<MouseButton>) -> Self {
|
||||||
match e.pressed_button {
|
match e {
|
||||||
Some(b) => match b {
|
Some(gpui::MouseButton::Left) => AlacMouseButton::LeftMove,
|
||||||
gpui::MouseButton::Left => AlacMouseButton::LeftMove,
|
Some(gpui::MouseButton::Middle) => AlacMouseButton::MiddleMove,
|
||||||
gpui::MouseButton::Middle => AlacMouseButton::MiddleMove,
|
Some(gpui::MouseButton::Right) => AlacMouseButton::RightMove,
|
||||||
gpui::MouseButton::Right => AlacMouseButton::RightMove,
|
Some(gpui::MouseButton::Navigate(_)) => AlacMouseButton::Other,
|
||||||
gpui::MouseButton::Navigate(_) => AlacMouseButton::Other,
|
|
||||||
},
|
|
||||||
None => AlacMouseButton::NoneMove,
|
None => AlacMouseButton::NoneMove,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,34 +132,37 @@ pub fn mouse_button_report(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mouse_moved_report(point: AlacPoint, e: &MouseMoveEvent, mode: TermMode) -> Option<Vec<u8>> {
|
pub fn mouse_moved_report(
|
||||||
let button = AlacMouseButton::from_move(e);
|
point: AlacPoint,
|
||||||
|
button: Option<MouseButton>,
|
||||||
|
modifiers: Modifiers,
|
||||||
|
mode: TermMode,
|
||||||
|
) -> Option<Vec<u8>> {
|
||||||
|
let button = AlacMouseButton::from_move_button(button);
|
||||||
|
|
||||||
if !button.is_other() && mode.intersects(TermMode::MOUSE_MOTION | TermMode::MOUSE_DRAG) {
|
if !button.is_other() && mode.intersects(TermMode::MOUSE_MOTION | TermMode::MOUSE_DRAG) {
|
||||||
//Only drags are reported in drag mode, so block NoneMove.
|
//Only drags are reported in drag mode, so block NoneMove.
|
||||||
if mode.contains(TermMode::MOUSE_DRAG) && matches!(button, AlacMouseButton::NoneMove) {
|
if mode.contains(TermMode::MOUSE_DRAG) && matches!(button, AlacMouseButton::NoneMove) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
mouse_report(
|
mouse_report(point, button, true, modifiers, MouseFormat::from_mode(mode))
|
||||||
point,
|
|
||||||
button,
|
|
||||||
true,
|
|
||||||
e.modifiers,
|
|
||||||
MouseFormat::from_mode(mode),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grid_point(pos: Point<Pixels>, cur_size: TerminalSize, display_offset: usize) -> AlacPoint {
|
pub fn grid_point(
|
||||||
|
pos: Point<Pixels>,
|
||||||
|
cur_size: TerminalBounds,
|
||||||
|
display_offset: usize,
|
||||||
|
) -> AlacPoint {
|
||||||
grid_point_and_side(pos, cur_size, display_offset).0
|
grid_point_and_side(pos, cur_size, display_offset).0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grid_point_and_side(
|
pub fn grid_point_and_side(
|
||||||
pos: Point<Pixels>,
|
pos: Point<Pixels>,
|
||||||
cur_size: TerminalSize,
|
cur_size: TerminalBounds,
|
||||||
display_offset: usize,
|
display_offset: usize,
|
||||||
) -> (AlacPoint, Side) {
|
) -> (AlacPoint, Side) {
|
||||||
let mut col = GridCol((pos.x / cur_size.cell_width) as usize);
|
let mut col = GridCol((pos.x / cur_size.cell_width) as usize);
|
||||||
|
|
|
@ -61,6 +61,7 @@ use gpui::{
|
||||||
actions, black, px, AnyWindowHandle, App, AppContext as _, Bounds, ClipboardItem, Context,
|
actions, black, px, AnyWindowHandle, App, AppContext as _, Bounds, ClipboardItem, Context,
|
||||||
EventEmitter, Hsla, Keystroke, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent,
|
EventEmitter, Hsla, Keystroke, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent,
|
||||||
MouseUpEvent, Pixels, Point, Rgba, ScrollWheelEvent, SharedString, Size, Task, TouchPhase,
|
MouseUpEvent, Pixels, Point, Rgba, ScrollWheelEvent, SharedString, Size, Task, TouchPhase,
|
||||||
|
Window,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::mappings::{colors::to_alac_rgb, keys::to_esc_str};
|
use crate::mappings::{colors::to_alac_rgb, keys::to_esc_str};
|
||||||
|
@ -131,7 +132,7 @@ pub enum MaybeNavigationTarget {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum InternalEvent {
|
enum InternalEvent {
|
||||||
Resize(TerminalSize),
|
Resize(TerminalBounds),
|
||||||
Clear,
|
Clear,
|
||||||
// FocusNextMatch,
|
// FocusNextMatch,
|
||||||
Scroll(AlacScroll),
|
Scroll(AlacScroll),
|
||||||
|
@ -161,35 +162,35 @@ pub fn init(cx: &mut App) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct TerminalSize {
|
pub struct TerminalBounds {
|
||||||
pub cell_width: Pixels,
|
pub cell_width: Pixels,
|
||||||
pub line_height: Pixels,
|
pub line_height: Pixels,
|
||||||
pub size: Size<Pixels>,
|
pub bounds: Bounds<Pixels>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TerminalSize {
|
impl TerminalBounds {
|
||||||
pub fn new(line_height: Pixels, cell_width: Pixels, size: Size<Pixels>) -> Self {
|
pub fn new(line_height: Pixels, cell_width: Pixels, bounds: Bounds<Pixels>) -> Self {
|
||||||
TerminalSize {
|
TerminalBounds {
|
||||||
cell_width,
|
cell_width,
|
||||||
line_height,
|
line_height,
|
||||||
size,
|
bounds,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn num_lines(&self) -> usize {
|
pub fn num_lines(&self) -> usize {
|
||||||
(self.size.height / self.line_height).floor() as usize
|
(self.bounds.size.height / self.line_height).floor() as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn num_columns(&self) -> usize {
|
pub fn num_columns(&self) -> usize {
|
||||||
(self.size.width / self.cell_width).floor() as usize
|
(self.bounds.size.width / self.cell_width).floor() as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn height(&self) -> Pixels {
|
pub fn height(&self) -> Pixels {
|
||||||
self.size.height
|
self.bounds.size.height
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn width(&self) -> Pixels {
|
pub fn width(&self) -> Pixels {
|
||||||
self.size.width
|
self.bounds.size.width
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cell_width(&self) -> Pixels {
|
pub fn cell_width(&self) -> Pixels {
|
||||||
|
@ -201,21 +202,24 @@ impl TerminalSize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TerminalSize {
|
impl Default for TerminalBounds {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
TerminalSize::new(
|
TerminalBounds::new(
|
||||||
DEBUG_LINE_HEIGHT,
|
DEBUG_LINE_HEIGHT,
|
||||||
DEBUG_CELL_WIDTH,
|
DEBUG_CELL_WIDTH,
|
||||||
Size {
|
Bounds {
|
||||||
width: DEBUG_TERMINAL_WIDTH,
|
origin: Point::default(),
|
||||||
height: DEBUG_TERMINAL_HEIGHT,
|
size: Size {
|
||||||
|
width: DEBUG_TERMINAL_WIDTH,
|
||||||
|
height: DEBUG_TERMINAL_HEIGHT,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TerminalSize> for WindowSize {
|
impl From<TerminalBounds> for WindowSize {
|
||||||
fn from(val: TerminalSize) -> Self {
|
fn from(val: TerminalBounds) -> Self {
|
||||||
WindowSize {
|
WindowSize {
|
||||||
num_lines: val.num_lines() as u16,
|
num_lines: val.num_lines() as u16,
|
||||||
num_cols: val.num_columns() as u16,
|
num_cols: val.num_columns() as u16,
|
||||||
|
@ -225,7 +229,7 @@ impl From<TerminalSize> for WindowSize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dimensions for TerminalSize {
|
impl Dimensions for TerminalBounds {
|
||||||
/// Note: this is supposed to be for the back buffer's length,
|
/// Note: this is supposed to be for the back buffer's length,
|
||||||
/// but we exclusively use it to resize the terminal, which does not
|
/// but we exclusively use it to resize the terminal, which does not
|
||||||
/// use this method. We still have to implement it for the trait though,
|
/// use this method. We still have to implement it for the trait though,
|
||||||
|
@ -406,7 +410,7 @@ impl TerminalBuilder {
|
||||||
//Set up the terminal...
|
//Set up the terminal...
|
||||||
let mut term = Term::new(
|
let mut term = Term::new(
|
||||||
config.clone(),
|
config.clone(),
|
||||||
&TerminalSize::default(),
|
&TerminalBounds::default(),
|
||||||
ZedListener(events_tx.clone()),
|
ZedListener(events_tx.clone()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -420,7 +424,7 @@ impl TerminalBuilder {
|
||||||
//Setup the pty...
|
//Setup the pty...
|
||||||
let pty = match tty::new(
|
let pty = match tty::new(
|
||||||
&pty_options,
|
&pty_options,
|
||||||
TerminalSize::default().into(),
|
TerminalBounds::default().into(),
|
||||||
window.window_id().as_u64(),
|
window.window_id().as_u64(),
|
||||||
) {
|
) {
|
||||||
Ok(pty) => pty,
|
Ok(pty) => pty,
|
||||||
|
@ -463,11 +467,9 @@ impl TerminalBuilder {
|
||||||
pty_info,
|
pty_info,
|
||||||
breadcrumb_text: String::new(),
|
breadcrumb_text: String::new(),
|
||||||
scroll_px: px(0.),
|
scroll_px: px(0.),
|
||||||
last_mouse_position: None,
|
|
||||||
next_link_id: 0,
|
next_link_id: 0,
|
||||||
selection_phase: SelectionPhase::Ended,
|
selection_phase: SelectionPhase::Ended,
|
||||||
secondary_pressed: false,
|
// hovered_word: false,
|
||||||
hovered_word: false,
|
|
||||||
url_regex: RegexSearch::new(URL_REGEX).unwrap(),
|
url_regex: RegexSearch::new(URL_REGEX).unwrap(),
|
||||||
word_regex: RegexSearch::new(WORD_REGEX).unwrap(),
|
word_regex: RegexSearch::new(WORD_REGEX).unwrap(),
|
||||||
vi_mode_enabled: false,
|
vi_mode_enabled: false,
|
||||||
|
@ -569,7 +571,7 @@ pub struct TerminalContent {
|
||||||
pub selection: Option<SelectionRange>,
|
pub selection: Option<SelectionRange>,
|
||||||
pub cursor: RenderableCursor,
|
pub cursor: RenderableCursor,
|
||||||
pub cursor_char: char,
|
pub cursor_char: char,
|
||||||
pub size: TerminalSize,
|
pub terminal_bounds: TerminalBounds,
|
||||||
pub last_hovered_word: Option<HoveredWord>,
|
pub last_hovered_word: Option<HoveredWord>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,7 +595,7 @@ impl Default for TerminalContent {
|
||||||
point: AlacPoint::new(Line(0), Column(0)),
|
point: AlacPoint::new(Line(0), Column(0)),
|
||||||
},
|
},
|
||||||
cursor_char: Default::default(),
|
cursor_char: Default::default(),
|
||||||
size: Default::default(),
|
terminal_bounds: Default::default(),
|
||||||
last_hovered_word: None,
|
last_hovered_word: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -613,8 +615,6 @@ pub struct Terminal {
|
||||||
events: VecDeque<InternalEvent>,
|
events: VecDeque<InternalEvent>,
|
||||||
/// This is only used for mouse mode cell change detection
|
/// This is only used for mouse mode cell change detection
|
||||||
last_mouse: Option<(AlacPoint, AlacDirection)>,
|
last_mouse: Option<(AlacPoint, AlacDirection)>,
|
||||||
/// This is only used for terminal hovered word checking
|
|
||||||
last_mouse_position: Option<Point<Pixels>>,
|
|
||||||
pub matches: Vec<RangeInclusive<AlacPoint>>,
|
pub matches: Vec<RangeInclusive<AlacPoint>>,
|
||||||
pub last_content: TerminalContent,
|
pub last_content: TerminalContent,
|
||||||
pub selection_head: Option<AlacPoint>,
|
pub selection_head: Option<AlacPoint>,
|
||||||
|
@ -625,8 +625,6 @@ pub struct Terminal {
|
||||||
scroll_px: Pixels,
|
scroll_px: Pixels,
|
||||||
next_link_id: usize,
|
next_link_id: usize,
|
||||||
selection_phase: SelectionPhase,
|
selection_phase: SelectionPhase,
|
||||||
secondary_pressed: bool,
|
|
||||||
hovered_word: bool,
|
|
||||||
url_regex: RegexSearch,
|
url_regex: RegexSearch,
|
||||||
word_regex: RegexSearch,
|
word_regex: RegexSearch,
|
||||||
task: Option<TaskState>,
|
task: Option<TaskState>,
|
||||||
|
@ -697,7 +695,7 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
AlacTermEvent::PtyWrite(out) => self.write_to_pty(out.clone()),
|
AlacTermEvent::PtyWrite(out) => self.write_to_pty(out.clone()),
|
||||||
AlacTermEvent::TextAreaSizeRequest(format) => {
|
AlacTermEvent::TextAreaSizeRequest(format) => {
|
||||||
self.write_to_pty(format(self.last_content.size.into()))
|
self.write_to_pty(format(self.last_content.terminal_bounds.into()))
|
||||||
}
|
}
|
||||||
AlacTermEvent::CursorBlinkingChange => {
|
AlacTermEvent::CursorBlinkingChange => {
|
||||||
let terminal = self.term.lock();
|
let terminal = self.term.lock();
|
||||||
|
@ -746,18 +744,20 @@ impl Terminal {
|
||||||
&mut self,
|
&mut self,
|
||||||
event: &InternalEvent,
|
event: &InternalEvent,
|
||||||
term: &mut Term<ZedListener>,
|
term: &mut Term<ZedListener>,
|
||||||
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
match event {
|
match event {
|
||||||
InternalEvent::Resize(mut new_size) => {
|
InternalEvent::Resize(mut new_bounds) => {
|
||||||
new_size.size.height = cmp::max(new_size.line_height, new_size.height());
|
new_bounds.bounds.size.height =
|
||||||
new_size.size.width = cmp::max(new_size.cell_width, new_size.width());
|
cmp::max(new_bounds.line_height, new_bounds.height());
|
||||||
|
new_bounds.bounds.size.width = cmp::max(new_bounds.cell_width, new_bounds.width());
|
||||||
|
|
||||||
self.last_content.size = new_size;
|
self.last_content.terminal_bounds = new_bounds;
|
||||||
|
|
||||||
self.pty_tx.0.send(Msg::Resize(new_size.into())).ok();
|
self.pty_tx.0.send(Msg::Resize(new_bounds.into())).ok();
|
||||||
|
|
||||||
term.resize(new_size);
|
term.resize(new_bounds);
|
||||||
}
|
}
|
||||||
InternalEvent::Clear => {
|
InternalEvent::Clear => {
|
||||||
// Clear back buffer
|
// Clear back buffer
|
||||||
|
@ -793,7 +793,7 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
InternalEvent::Scroll(scroll) => {
|
InternalEvent::Scroll(scroll) => {
|
||||||
term.scroll_display(*scroll);
|
term.scroll_display(*scroll);
|
||||||
self.refresh_hovered_word();
|
self.refresh_hovered_word(window);
|
||||||
|
|
||||||
if self.vi_mode_enabled {
|
if self.vi_mode_enabled {
|
||||||
match *scroll {
|
match *scroll {
|
||||||
|
@ -849,7 +849,7 @@ impl Terminal {
|
||||||
if let Some(mut selection) = term.selection.take() {
|
if let Some(mut selection) = term.selection.take() {
|
||||||
let (point, side) = grid_point_and_side(
|
let (point, side) = grid_point_and_side(
|
||||||
*position,
|
*position,
|
||||||
self.last_content.size,
|
self.last_content.terminal_bounds,
|
||||||
term.grid().display_offset(),
|
term.grid().display_offset(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -873,7 +873,7 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
InternalEvent::ScrollToAlacPoint(point) => {
|
InternalEvent::ScrollToAlacPoint(point) => {
|
||||||
term.scroll_to_point(*point);
|
term.scroll_to_point(*point);
|
||||||
self.refresh_hovered_word();
|
self.refresh_hovered_word(window);
|
||||||
}
|
}
|
||||||
InternalEvent::ToggleViMode => {
|
InternalEvent::ToggleViMode => {
|
||||||
self.vi_mode_enabled = !self.vi_mode_enabled;
|
self.vi_mode_enabled = !self.vi_mode_enabled;
|
||||||
|
@ -887,7 +887,7 @@ impl Terminal {
|
||||||
|
|
||||||
let point = grid_point(
|
let point = grid_point(
|
||||||
*position,
|
*position,
|
||||||
self.last_content.size,
|
self.last_content.terminal_bounds,
|
||||||
term.grid().display_offset(),
|
term.grid().display_offset(),
|
||||||
)
|
)
|
||||||
.grid_clamp(term, Boundary::Grid);
|
.grid_clamp(term, Boundary::Grid);
|
||||||
|
@ -977,13 +977,9 @@ impl Terminal {
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
self.hovered_word = true;
|
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if self.hovered_word {
|
cx.emit(Event::NewNavigationTarget(None));
|
||||||
cx.emit(Event::NewNavigationTarget(None));
|
|
||||||
}
|
|
||||||
self.hovered_word = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1015,6 +1011,7 @@ impl Terminal {
|
||||||
id: self.next_link_id(),
|
id: self.next_link_id(),
|
||||||
});
|
});
|
||||||
cx.emit(Event::NewNavigationTarget(Some(navigation_target)));
|
cx.emit(Event::NewNavigationTarget(Some(navigation_target)));
|
||||||
|
cx.notify()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_link_id(&mut self) -> usize {
|
fn next_link_id(&mut self) -> usize {
|
||||||
|
@ -1134,9 +1131,9 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
|
|
||||||
///Resize the terminal and the PTY.
|
///Resize the terminal and the PTY.
|
||||||
pub fn set_size(&mut self, new_size: TerminalSize) {
|
pub fn set_size(&mut self, new_bounds: TerminalBounds) {
|
||||||
if self.last_content.size != new_size {
|
if self.last_content.terminal_bounds != new_bounds {
|
||||||
self.events.push_back(InternalEvent::Resize(new_size))
|
self.events.push_back(InternalEvent::Resize(new_bounds))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1200,8 +1197,8 @@ impl Terminal {
|
||||||
if let Some(motion) = motion {
|
if let Some(motion) = motion {
|
||||||
let cursor = self.last_content.cursor.point;
|
let cursor = self.last_content.cursor.point;
|
||||||
let cursor_pos = Point {
|
let cursor_pos = Point {
|
||||||
x: cursor.column.0 as f32 * self.last_content.size.cell_width,
|
x: cursor.column.0 as f32 * self.last_content.terminal_bounds.cell_width,
|
||||||
y: cursor.line.0 as f32 * self.last_content.size.line_height,
|
y: cursor.line.0 as f32 * self.last_content.terminal_bounds.line_height,
|
||||||
};
|
};
|
||||||
self.events
|
self.events
|
||||||
.push_back(InternalEvent::UpdateSelection(cursor_pos));
|
.push_back(InternalEvent::UpdateSelection(cursor_pos));
|
||||||
|
@ -1215,11 +1212,11 @@ impl Terminal {
|
||||||
"b" if keystroke.modifiers.control => Some(AlacScroll::PageUp),
|
"b" if keystroke.modifiers.control => Some(AlacScroll::PageUp),
|
||||||
"f" if keystroke.modifiers.control => Some(AlacScroll::PageDown),
|
"f" if keystroke.modifiers.control => Some(AlacScroll::PageDown),
|
||||||
"d" if keystroke.modifiers.control => {
|
"d" if keystroke.modifiers.control => {
|
||||||
let amount = self.last_content.size.line_height().to_f64() as i32 / 2;
|
let amount = self.last_content.terminal_bounds.line_height().to_f64() as i32 / 2;
|
||||||
Some(AlacScroll::Delta(-amount))
|
Some(AlacScroll::Delta(-amount))
|
||||||
}
|
}
|
||||||
"u" if keystroke.modifiers.control => {
|
"u" if keystroke.modifiers.control => {
|
||||||
let amount = self.last_content.size.line_height().to_f64() as i32 / 2;
|
let amount = self.last_content.terminal_bounds.line_height().to_f64() as i32 / 2;
|
||||||
Some(AlacScroll::Delta(amount))
|
Some(AlacScroll::Delta(amount))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -1277,13 +1274,22 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_modifiers_change(&mut self, modifiers: &Modifiers) -> bool {
|
pub fn try_modifiers_change(
|
||||||
let changed = self.secondary_pressed != modifiers.secondary();
|
&mut self,
|
||||||
if !self.secondary_pressed && modifiers.secondary() {
|
modifiers: &Modifiers,
|
||||||
self.refresh_hovered_word();
|
window: &Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
if self
|
||||||
|
.last_content
|
||||||
|
.terminal_bounds
|
||||||
|
.bounds
|
||||||
|
.contains(&window.mouse_position())
|
||||||
|
&& modifiers.secondary()
|
||||||
|
{
|
||||||
|
self.refresh_hovered_word(window);
|
||||||
}
|
}
|
||||||
self.secondary_pressed = modifiers.secondary();
|
cx.notify();
|
||||||
changed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///Paste text into the terminal
|
///Paste text into the terminal
|
||||||
|
@ -1297,12 +1303,12 @@ impl Terminal {
|
||||||
self.input(paste_text);
|
self.input(paste_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sync(&mut self, cx: &mut Context<Self>) {
|
pub fn sync(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let term = self.term.clone();
|
let term = self.term.clone();
|
||||||
let mut terminal = term.lock_unfair();
|
let mut terminal = term.lock_unfair();
|
||||||
//Note that the ordering of events matters for event processing
|
//Note that the ordering of events matters for event processing
|
||||||
while let Some(e) = self.events.pop_front() {
|
while let Some(e) = self.events.pop_front() {
|
||||||
self.process_terminal_event(&e, &mut terminal, cx)
|
self.process_terminal_event(&e, &mut terminal, window, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.last_content = Self::make_content(&terminal, &self.last_content);
|
self.last_content = Self::make_content(&terminal, &self.last_content);
|
||||||
|
@ -1331,7 +1337,7 @@ impl Terminal {
|
||||||
selection: content.selection,
|
selection: content.selection,
|
||||||
cursor: content.cursor,
|
cursor: content.cursor,
|
||||||
cursor_char: term.grid()[content.cursor.point].c,
|
cursor_char: term.grid()[content.cursor.point].c,
|
||||||
size: last_content.size,
|
terminal_bounds: last_content.terminal_bounds,
|
||||||
last_hovered_word: last_content.last_hovered_word.clone(),
|
last_hovered_word: last_content.last_hovered_word.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1368,7 +1374,6 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn focus_out(&mut self) {
|
pub fn focus_out(&mut self) {
|
||||||
self.last_mouse_position = None;
|
|
||||||
if self.last_content.mode.contains(TermMode::FOCUS_IN_OUT) {
|
if self.last_content.mode.contains(TermMode::FOCUS_IN_OUT) {
|
||||||
self.write_to_pty("\x1b[O".to_string());
|
self.write_to_pty("\x1b[O".to_string());
|
||||||
}
|
}
|
||||||
|
@ -1395,44 +1400,48 @@ impl Terminal {
|
||||||
self.last_content.mode.intersects(TermMode::MOUSE_MODE) && !shift
|
self.last_content.mode.intersects(TermMode::MOUSE_MODE) && !shift
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mouse_move(&mut self, e: &MouseMoveEvent, origin: Point<Pixels>) {
|
pub fn mouse_move(&mut self, e: &MouseMoveEvent, cx: &mut Context<Self>) {
|
||||||
let position = e.position - origin;
|
let position = e.position - self.last_content.terminal_bounds.bounds.origin;
|
||||||
self.last_mouse_position = Some(position);
|
|
||||||
if self.mouse_mode(e.modifiers.shift) {
|
if self.mouse_mode(e.modifiers.shift) {
|
||||||
let (point, side) = grid_point_and_side(
|
let (point, side) = grid_point_and_side(
|
||||||
position,
|
position,
|
||||||
self.last_content.size,
|
self.last_content.terminal_bounds,
|
||||||
self.last_content.display_offset,
|
self.last_content.display_offset,
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.mouse_changed(point, side) {
|
if self.mouse_changed(point, side) {
|
||||||
if let Some(bytes) = mouse_moved_report(point, e, self.last_content.mode) {
|
if let Some(bytes) =
|
||||||
|
mouse_moved_report(point, e.pressed_button, e.modifiers, self.last_content.mode)
|
||||||
|
{
|
||||||
self.pty_tx.notify(bytes);
|
self.pty_tx.notify(bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if self.secondary_pressed {
|
} else if e.modifiers.secondary() {
|
||||||
self.word_from_position(Some(position));
|
self.word_from_position(e.position);
|
||||||
}
|
}
|
||||||
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn word_from_position(&mut self, position: Option<Point<Pixels>>) {
|
fn word_from_position(&mut self, position: Point<Pixels>) {
|
||||||
if self.selection_phase == SelectionPhase::Selecting {
|
if self.selection_phase == SelectionPhase::Selecting {
|
||||||
self.last_content.last_hovered_word = None;
|
self.last_content.last_hovered_word = None;
|
||||||
} else if let Some(position) = position {
|
} else if self.last_content.terminal_bounds.bounds.contains(&position) {
|
||||||
self.events
|
self.events.push_back(InternalEvent::FindHyperlink(
|
||||||
.push_back(InternalEvent::FindHyperlink(position, false));
|
position - self.last_content.terminal_bounds.bounds.origin,
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
self.last_content.last_hovered_word = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mouse_drag(
|
pub fn mouse_drag(
|
||||||
&mut self,
|
&mut self,
|
||||||
e: &MouseMoveEvent,
|
e: &MouseMoveEvent,
|
||||||
origin: Point<Pixels>,
|
|
||||||
region: Bounds<Pixels>,
|
region: Bounds<Pixels>,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
let position = e.position - origin;
|
let position = e.position - self.last_content.terminal_bounds.bounds.origin;
|
||||||
self.last_mouse_position = Some(position);
|
|
||||||
|
|
||||||
if !self.mouse_mode(e.modifiers.shift) {
|
if !self.mouse_mode(e.modifiers.shift) {
|
||||||
self.selection_phase = SelectionPhase::Selecting;
|
self.selection_phase = SelectionPhase::Selecting;
|
||||||
// Alacritty has the same ordering, of first updating the selection
|
// Alacritty has the same ordering, of first updating the selection
|
||||||
|
@ -1447,18 +1456,21 @@ impl Terminal {
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let scroll_lines = (scroll_delta / self.last_content.size.line_height) as i32;
|
let scroll_lines =
|
||||||
|
(scroll_delta / self.last_content.terminal_bounds.line_height) as i32;
|
||||||
|
|
||||||
self.events
|
self.events
|
||||||
.push_back(InternalEvent::Scroll(AlacScroll::Delta(scroll_lines)));
|
.push_back(InternalEvent::Scroll(AlacScroll::Delta(scroll_lines)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cx.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drag_line_delta(&self, e: &MouseMoveEvent, region: Bounds<Pixels>) -> Option<Pixels> {
|
fn drag_line_delta(&self, e: &MouseMoveEvent, region: Bounds<Pixels>) -> Option<Pixels> {
|
||||||
//TODO: Why do these need to be doubled? Probably the same problem that the IME has
|
//TODO: Why do these need to be doubled? Probably the same problem that the IME has
|
||||||
let top = region.origin.y + (self.last_content.size.line_height * 2.);
|
let top = region.origin.y + (self.last_content.terminal_bounds.line_height * 2.);
|
||||||
let bottom = region.bottom_left().y - (self.last_content.size.line_height * 2.);
|
let bottom = region.bottom_left().y - (self.last_content.terminal_bounds.line_height * 2.);
|
||||||
let scroll_delta = if e.position.y < top {
|
let scroll_delta = if e.position.y < top {
|
||||||
(top - e.position.y).pow(1.1)
|
(top - e.position.y).pow(1.1)
|
||||||
} else if e.position.y > bottom {
|
} else if e.position.y > bottom {
|
||||||
|
@ -1469,16 +1481,11 @@ impl Terminal {
|
||||||
Some(scroll_delta)
|
Some(scroll_delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mouse_down(
|
pub fn mouse_down(&mut self, e: &MouseDownEvent, _cx: &mut Context<Self>) {
|
||||||
&mut self,
|
let position = e.position - self.last_content.terminal_bounds.bounds.origin;
|
||||||
e: &MouseDownEvent,
|
|
||||||
origin: Point<Pixels>,
|
|
||||||
_cx: &mut Context<Self>,
|
|
||||||
) {
|
|
||||||
let position = e.position - origin;
|
|
||||||
let point = grid_point(
|
let point = grid_point(
|
||||||
position,
|
position,
|
||||||
self.last_content.size,
|
self.last_content.terminal_bounds,
|
||||||
self.last_content.display_offset,
|
self.last_content.display_offset,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1491,10 +1498,9 @@ impl Terminal {
|
||||||
} else {
|
} else {
|
||||||
match e.button {
|
match e.button {
|
||||||
MouseButton::Left => {
|
MouseButton::Left => {
|
||||||
let position = e.position - origin;
|
|
||||||
let (point, side) = grid_point_and_side(
|
let (point, side) = grid_point_and_side(
|
||||||
position,
|
position,
|
||||||
self.last_content.size,
|
self.last_content.terminal_bounds,
|
||||||
self.last_content.display_offset,
|
self.last_content.display_offset,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1526,14 +1532,14 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mouse_up(&mut self, e: &MouseUpEvent, origin: Point<Pixels>, cx: &Context<Self>) {
|
pub fn mouse_up(&mut self, e: &MouseUpEvent, cx: &Context<Self>) {
|
||||||
let setting = TerminalSettings::get_global(cx);
|
let setting = TerminalSettings::get_global(cx);
|
||||||
|
|
||||||
let position = e.position - origin;
|
let position = e.position - self.last_content.terminal_bounds.bounds.origin;
|
||||||
if self.mouse_mode(e.modifiers.shift) {
|
if self.mouse_mode(e.modifiers.shift) {
|
||||||
let point = grid_point(
|
let point = grid_point(
|
||||||
position,
|
position,
|
||||||
self.last_content.size,
|
self.last_content.terminal_bounds,
|
||||||
self.last_content.display_offset,
|
self.last_content.display_offset,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1549,10 +1555,11 @@ impl Terminal {
|
||||||
|
|
||||||
//Hyperlinks
|
//Hyperlinks
|
||||||
if self.selection_phase == SelectionPhase::Ended {
|
if self.selection_phase == SelectionPhase::Ended {
|
||||||
let mouse_cell_index = content_index_for_mouse(position, &self.last_content.size);
|
let mouse_cell_index =
|
||||||
|
content_index_for_mouse(position, &self.last_content.terminal_bounds);
|
||||||
if let Some(link) = self.last_content.cells[mouse_cell_index].hyperlink() {
|
if let Some(link) = self.last_content.cells[mouse_cell_index].hyperlink() {
|
||||||
cx.open_url(link.uri());
|
cx.open_url(link.uri());
|
||||||
} else if self.secondary_pressed {
|
} else if e.modifiers.secondary() {
|
||||||
self.events
|
self.events
|
||||||
.push_back(InternalEvent::FindHyperlink(position, true));
|
.push_back(InternalEvent::FindHyperlink(position, true));
|
||||||
}
|
}
|
||||||
|
@ -1564,14 +1571,14 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
|
|
||||||
///Scroll the terminal
|
///Scroll the terminal
|
||||||
pub fn scroll_wheel(&mut self, e: &ScrollWheelEvent, origin: Point<Pixels>) {
|
pub fn scroll_wheel(&mut self, e: &ScrollWheelEvent) {
|
||||||
let mouse_mode = self.mouse_mode(e.shift);
|
let mouse_mode = self.mouse_mode(e.shift);
|
||||||
|
|
||||||
if let Some(scroll_lines) = self.determine_scroll_lines(e, mouse_mode) {
|
if let Some(scroll_lines) = self.determine_scroll_lines(e, mouse_mode) {
|
||||||
if mouse_mode {
|
if mouse_mode {
|
||||||
let point = grid_point(
|
let point = grid_point(
|
||||||
e.position - origin,
|
e.position - self.last_content.terminal_bounds.bounds.origin,
|
||||||
self.last_content.size,
|
self.last_content.terminal_bounds,
|
||||||
self.last_content.display_offset,
|
self.last_content.display_offset,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1596,13 +1603,13 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh_hovered_word(&mut self) {
|
fn refresh_hovered_word(&mut self, window: &Window) {
|
||||||
self.word_from_position(self.last_mouse_position);
|
self.word_from_position(window.mouse_position());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn determine_scroll_lines(&mut self, e: &ScrollWheelEvent, mouse_mode: bool) -> Option<i32> {
|
fn determine_scroll_lines(&mut self, e: &ScrollWheelEvent, mouse_mode: bool) -> Option<i32> {
|
||||||
let scroll_multiplier = if mouse_mode { 1. } else { SCROLL_MULTIPLIER };
|
let scroll_multiplier = if mouse_mode { 1. } else { SCROLL_MULTIPLIER };
|
||||||
let line_height = self.last_content.size.line_height;
|
let line_height = self.last_content.terminal_bounds.line_height;
|
||||||
match e.touch_phase {
|
match e.touch_phase {
|
||||||
/* Reset scroll state on started */
|
/* Reset scroll state on started */
|
||||||
TouchPhase::Started => {
|
TouchPhase::Started => {
|
||||||
|
@ -1619,7 +1626,7 @@ impl Terminal {
|
||||||
|
|
||||||
// Whenever we hit the edges, reset our stored scroll to 0
|
// Whenever we hit the edges, reset our stored scroll to 0
|
||||||
// so we can respond to changes in direction quickly
|
// so we can respond to changes in direction quickly
|
||||||
self.scroll_px %= self.last_content.size.height();
|
self.scroll_px %= self.last_content.terminal_bounds.height();
|
||||||
|
|
||||||
Some(new_offset - old_offset)
|
Some(new_offset - old_offset)
|
||||||
}
|
}
|
||||||
|
@ -1714,10 +1721,6 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_navigate_to_selected_word(&self) -> bool {
|
|
||||||
self.secondary_pressed && self.hovered_word
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn task(&self) -> Option<&TaskState> {
|
pub fn task(&self) -> Option<&TaskState> {
|
||||||
self.task.as_ref()
|
self.task.as_ref()
|
||||||
}
|
}
|
||||||
|
@ -1899,12 +1902,12 @@ fn all_search_matches<'a, T>(
|
||||||
RegexIter::new(start, end, AlacDirection::Right, term, regex)
|
RegexIter::new(start, end, AlacDirection::Right, term, regex)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content_index_for_mouse(pos: Point<Pixels>, size: &TerminalSize) -> usize {
|
fn content_index_for_mouse(pos: Point<Pixels>, terminal_bounds: &TerminalBounds) -> usize {
|
||||||
let col = (pos.x / size.cell_width()).round() as usize;
|
let col = (pos.x / terminal_bounds.cell_width()).round() as usize;
|
||||||
let clamped_col = min(col, size.columns() - 1);
|
let clamped_col = min(col, terminal_bounds.columns() - 1);
|
||||||
let row = (pos.y / size.line_height()).round() as usize;
|
let row = (pos.y / terminal_bounds.line_height()).round() as usize;
|
||||||
let clamped_row = min(row, size.screen_lines() - 1);
|
let clamped_row = min(row, terminal_bounds.screen_lines() - 1);
|
||||||
clamped_row * size.columns() + clamped_col
|
clamped_row * terminal_bounds.columns() + clamped_col
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts an 8 bit ANSI color to its GPUI equivalent.
|
/// Converts an 8 bit ANSI color to its GPUI equivalent.
|
||||||
|
@ -1997,11 +2000,11 @@ mod tests {
|
||||||
index::{Column, Line, Point as AlacPoint},
|
index::{Column, Line, Point as AlacPoint},
|
||||||
term::cell::Cell,
|
term::cell::Cell,
|
||||||
};
|
};
|
||||||
use gpui::{point, size, Pixels};
|
use gpui::{bounds, point, size, Pixels, Point};
|
||||||
use rand::{distributions::Alphanumeric, rngs::ThreadRng, thread_rng, Rng};
|
use rand::{distributions::Alphanumeric, rngs::ThreadRng, thread_rng, Rng};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
content_index_for_mouse, rgb_for_index, IndexedCell, TerminalContent, TerminalSize,
|
content_index_for_mouse, rgb_for_index, IndexedCell, TerminalBounds, TerminalContent,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2023,12 +2026,15 @@ mod tests {
|
||||||
let viewport_cells = rng.gen_range(15..20);
|
let viewport_cells = rng.gen_range(15..20);
|
||||||
let cell_size = rng.gen_range(5 * PRECISION..20 * PRECISION) as f32 / PRECISION as f32;
|
let cell_size = rng.gen_range(5 * PRECISION..20 * PRECISION) as f32 / PRECISION as f32;
|
||||||
|
|
||||||
let size = crate::TerminalSize {
|
let size = crate::TerminalBounds {
|
||||||
cell_width: Pixels::from(cell_size),
|
cell_width: Pixels::from(cell_size),
|
||||||
line_height: Pixels::from(cell_size),
|
line_height: Pixels::from(cell_size),
|
||||||
size: size(
|
bounds: bounds(
|
||||||
Pixels::from(cell_size * (viewport_cells as f32)),
|
Point::default(),
|
||||||
Pixels::from(cell_size * (viewport_cells as f32)),
|
size(
|
||||||
|
Pixels::from(cell_size * (viewport_cells as f32)),
|
||||||
|
Pixels::from(cell_size * (viewport_cells as f32)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2048,7 +2054,8 @@ mod tests {
|
||||||
Pixels::from(row as f32 * cell_size + row_offset),
|
Pixels::from(row as f32 * cell_size + row_offset),
|
||||||
);
|
);
|
||||||
|
|
||||||
let content_index = content_index_for_mouse(mouse_pos, &content.size);
|
let content_index =
|
||||||
|
content_index_for_mouse(mouse_pos, &content.terminal_bounds);
|
||||||
let mouse_cell = content.cells[content_index].c;
|
let mouse_cell = content.cells[content_index].c;
|
||||||
let real_cell = cells[row][col];
|
let real_cell = cells[row][col];
|
||||||
|
|
||||||
|
@ -2062,10 +2069,13 @@ mod tests {
|
||||||
fn test_mouse_to_cell_clamp() {
|
fn test_mouse_to_cell_clamp() {
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
let size = crate::TerminalSize {
|
let size = crate::TerminalBounds {
|
||||||
cell_width: Pixels::from(10.),
|
cell_width: Pixels::from(10.),
|
||||||
line_height: Pixels::from(10.),
|
line_height: Pixels::from(10.),
|
||||||
size: size(Pixels::from(100.), Pixels::from(100.)),
|
bounds: bounds(
|
||||||
|
Point::default(),
|
||||||
|
size(Pixels::from(100.), Pixels::from(100.)),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
let cells = get_cells(size, &mut rng);
|
let cells = get_cells(size, &mut rng);
|
||||||
|
@ -2074,7 +2084,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
content.cells[content_index_for_mouse(
|
content.cells[content_index_for_mouse(
|
||||||
point(Pixels::from(-10.), Pixels::from(-10.)),
|
point(Pixels::from(-10.), Pixels::from(-10.)),
|
||||||
&content.size,
|
&content.terminal_bounds,
|
||||||
)]
|
)]
|
||||||
.c,
|
.c,
|
||||||
cells[0][0]
|
cells[0][0]
|
||||||
|
@ -2082,14 +2092,14 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
content.cells[content_index_for_mouse(
|
content.cells[content_index_for_mouse(
|
||||||
point(Pixels::from(1000.), Pixels::from(1000.)),
|
point(Pixels::from(1000.), Pixels::from(1000.)),
|
||||||
&content.size,
|
&content.terminal_bounds,
|
||||||
)]
|
)]
|
||||||
.c,
|
.c,
|
||||||
cells[9][9]
|
cells[9][9]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_cells(size: TerminalSize, rng: &mut ThreadRng) -> Vec<Vec<char>> {
|
fn get_cells(size: TerminalBounds, rng: &mut ThreadRng) -> Vec<Vec<char>> {
|
||||||
let mut cells = Vec::new();
|
let mut cells = Vec::new();
|
||||||
|
|
||||||
for _ in 0..((size.height() / size.line_height()) as usize) {
|
for _ in 0..((size.height() / size.line_height()) as usize) {
|
||||||
|
@ -2104,7 +2114,10 @@ mod tests {
|
||||||
cells
|
cells
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_cells_to_content(size: TerminalSize, cells: &[Vec<char>]) -> TerminalContent {
|
fn convert_cells_to_content(
|
||||||
|
terminal_bounds: TerminalBounds,
|
||||||
|
cells: &[Vec<char>],
|
||||||
|
) -> TerminalContent {
|
||||||
let mut ic = Vec::new();
|
let mut ic = Vec::new();
|
||||||
|
|
||||||
for (index, row) in cells.iter().enumerate() {
|
for (index, row) in cells.iter().enumerate() {
|
||||||
|
@ -2121,7 +2134,7 @@ mod tests {
|
||||||
|
|
||||||
TerminalContent {
|
TerminalContent {
|
||||||
cells: ic,
|
cells: ic,
|
||||||
size,
|
terminal_bounds,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ use terminal::{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
terminal_settings::TerminalSettings,
|
terminal_settings::TerminalSettings,
|
||||||
HoveredWord, IndexedCell, Terminal, TerminalContent, TerminalSize,
|
HoveredWord, IndexedCell, Terminal, TerminalBounds, TerminalContent,
|
||||||
};
|
};
|
||||||
use theme::{ActiveTheme, Theme, ThemeSettings};
|
use theme::{ActiveTheme, Theme, ThemeSettings};
|
||||||
use ui::{ParentElement, Tooltip};
|
use ui::{ParentElement, Tooltip};
|
||||||
|
@ -40,7 +40,7 @@ pub struct LayoutState {
|
||||||
relative_highlighted_ranges: Vec<(RangeInclusive<AlacPoint>, Hsla)>,
|
relative_highlighted_ranges: Vec<(RangeInclusive<AlacPoint>, Hsla)>,
|
||||||
cursor: Option<CursorLayout>,
|
cursor: Option<CursorLayout>,
|
||||||
background_color: Hsla,
|
background_color: Hsla,
|
||||||
dimensions: TerminalSize,
|
dimensions: TerminalBounds,
|
||||||
mode: TermMode,
|
mode: TermMode,
|
||||||
display_offset: usize,
|
display_offset: usize,
|
||||||
hyperlink_tooltip: Option<AnyElement>,
|
hyperlink_tooltip: Option<AnyElement>,
|
||||||
|
@ -86,7 +86,7 @@ impl LayoutCell {
|
||||||
pub fn paint(
|
pub fn paint(
|
||||||
&self,
|
&self,
|
||||||
origin: Point<Pixels>,
|
origin: Point<Pixels>,
|
||||||
dimensions: &TerminalSize,
|
dimensions: &TerminalBounds,
|
||||||
_visible_bounds: Bounds<Pixels>,
|
_visible_bounds: Bounds<Pixels>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
|
@ -130,7 +130,7 @@ impl LayoutRect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paint(&self, origin: Point<Pixels>, dimensions: &TerminalSize, window: &mut Window) {
|
pub fn paint(&self, origin: Point<Pixels>, dimensions: &TerminalBounds, window: &mut Window) {
|
||||||
let position = {
|
let position = {
|
||||||
let alac_point = self.point;
|
let alac_point = self.point;
|
||||||
point(
|
point(
|
||||||
|
@ -313,7 +313,7 @@ impl TerminalElement {
|
||||||
/// the same position for sequential indexes. Use em_width instead
|
/// the same position for sequential indexes. Use em_width instead
|
||||||
fn shape_cursor(
|
fn shape_cursor(
|
||||||
cursor_point: DisplayCursor,
|
cursor_point: DisplayCursor,
|
||||||
size: TerminalSize,
|
size: TerminalBounds,
|
||||||
text_fragment: &ShapedLine,
|
text_fragment: &ShapedLine,
|
||||||
) -> Option<(Point<Pixels>, Pixels)> {
|
) -> Option<(Point<Pixels>, Pixels)> {
|
||||||
if cursor_point.line() < size.total_lines() as i32 {
|
if cursor_point.line() < size.total_lines() as i32 {
|
||||||
|
@ -412,27 +412,20 @@ impl TerminalElement {
|
||||||
|
|
||||||
fn generic_button_handler<E>(
|
fn generic_button_handler<E>(
|
||||||
connection: Entity<Terminal>,
|
connection: Entity<Terminal>,
|
||||||
origin: Point<Pixels>,
|
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
f: impl Fn(&mut Terminal, Point<Pixels>, &E, &mut Context<Terminal>),
|
f: impl Fn(&mut Terminal, &E, &mut Context<Terminal>),
|
||||||
) -> impl Fn(&E, &mut Window, &mut App) {
|
) -> impl Fn(&E, &mut Window, &mut App) {
|
||||||
move |event, window, cx| {
|
move |event, window, cx| {
|
||||||
window.focus(&focus_handle);
|
window.focus(&focus_handle);
|
||||||
connection.update(cx, |terminal, cx| {
|
connection.update(cx, |terminal, cx| {
|
||||||
f(terminal, origin, event, cx);
|
f(terminal, event, cx);
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_mouse_listeners(
|
fn register_mouse_listeners(&mut self, mode: TermMode, hitbox: &Hitbox, window: &mut Window) {
|
||||||
&mut self,
|
|
||||||
origin: Point<Pixels>,
|
|
||||||
mode: TermMode,
|
|
||||||
hitbox: &Hitbox,
|
|
||||||
window: &mut Window,
|
|
||||||
) {
|
|
||||||
let focus = self.focus.clone();
|
let focus = self.focus.clone();
|
||||||
let terminal = self.terminal.clone();
|
let terminal = self.terminal.clone();
|
||||||
|
|
||||||
|
@ -442,29 +435,26 @@ impl TerminalElement {
|
||||||
move |e, window, cx| {
|
move |e, window, cx| {
|
||||||
window.focus(&focus);
|
window.focus(&focus);
|
||||||
terminal.update(cx, |terminal, cx| {
|
terminal.update(cx, |terminal, cx| {
|
||||||
terminal.mouse_down(e, origin, cx);
|
terminal.mouse_down(e, cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
window.on_mouse_event({
|
window.on_mouse_event({
|
||||||
let focus = self.focus.clone();
|
|
||||||
let terminal = self.terminal.clone();
|
let terminal = self.terminal.clone();
|
||||||
let hitbox = hitbox.clone();
|
let hitbox = hitbox.clone();
|
||||||
|
let focus = focus.clone();
|
||||||
move |e: &MouseMoveEvent, phase, window, cx| {
|
move |e: &MouseMoveEvent, phase, window, cx| {
|
||||||
if phase != DispatchPhase::Bubble || !focus.is_focused(window) {
|
if phase != DispatchPhase::Bubble {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.pressed_button.is_some() && !cx.has_active_drag() {
|
if e.pressed_button.is_some() && !cx.has_active_drag() && focus.is_focused(window) {
|
||||||
let hovered = hitbox.is_hovered(window);
|
let hovered = hitbox.is_hovered(window);
|
||||||
terminal.update(cx, |terminal, cx| {
|
terminal.update(cx, |terminal, cx| {
|
||||||
if terminal.selection_started() {
|
if terminal.selection_started() || hovered {
|
||||||
terminal.mouse_drag(e, origin, hitbox.bounds);
|
terminal.mouse_drag(e, hitbox.bounds, cx);
|
||||||
cx.notify();
|
|
||||||
} else if hovered {
|
|
||||||
terminal.mouse_drag(e, origin, hitbox.bounds);
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -472,8 +462,7 @@ impl TerminalElement {
|
||||||
|
|
||||||
if hitbox.is_hovered(window) {
|
if hitbox.is_hovered(window) {
|
||||||
terminal.update(cx, |terminal, cx| {
|
terminal.update(cx, |terminal, cx| {
|
||||||
terminal.mouse_move(e, origin);
|
terminal.mouse_move(e, cx);
|
||||||
cx.notify();
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -483,10 +472,9 @@ impl TerminalElement {
|
||||||
MouseButton::Left,
|
MouseButton::Left,
|
||||||
TerminalElement::generic_button_handler(
|
TerminalElement::generic_button_handler(
|
||||||
terminal.clone(),
|
terminal.clone(),
|
||||||
origin,
|
|
||||||
focus.clone(),
|
focus.clone(),
|
||||||
move |terminal, origin, e, cx| {
|
move |terminal, e, cx| {
|
||||||
terminal.mouse_up(e, origin, cx);
|
terminal.mouse_up(e, cx);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -494,10 +482,9 @@ impl TerminalElement {
|
||||||
MouseButton::Middle,
|
MouseButton::Middle,
|
||||||
TerminalElement::generic_button_handler(
|
TerminalElement::generic_button_handler(
|
||||||
terminal.clone(),
|
terminal.clone(),
|
||||||
origin,
|
|
||||||
focus.clone(),
|
focus.clone(),
|
||||||
move |terminal, origin, e, cx| {
|
move |terminal, e, cx| {
|
||||||
terminal.mouse_down(e, origin, cx);
|
terminal.mouse_down(e, cx);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -506,7 +493,7 @@ impl TerminalElement {
|
||||||
move |e, _, cx| {
|
move |e, _, cx| {
|
||||||
terminal_view
|
terminal_view
|
||||||
.update(cx, |terminal_view, cx| {
|
.update(cx, |terminal_view, cx| {
|
||||||
terminal_view.scroll_wheel(e, origin, cx);
|
terminal_view.scroll_wheel(e, cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
|
@ -520,10 +507,9 @@ impl TerminalElement {
|
||||||
MouseButton::Right,
|
MouseButton::Right,
|
||||||
TerminalElement::generic_button_handler(
|
TerminalElement::generic_button_handler(
|
||||||
terminal.clone(),
|
terminal.clone(),
|
||||||
origin,
|
|
||||||
focus.clone(),
|
focus.clone(),
|
||||||
move |terminal, origin, e, cx| {
|
move |terminal, e, cx| {
|
||||||
terminal.mouse_down(e, origin, cx);
|
terminal.mouse_down(e, cx);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -531,23 +517,17 @@ impl TerminalElement {
|
||||||
MouseButton::Right,
|
MouseButton::Right,
|
||||||
TerminalElement::generic_button_handler(
|
TerminalElement::generic_button_handler(
|
||||||
terminal.clone(),
|
terminal.clone(),
|
||||||
origin,
|
|
||||||
focus.clone(),
|
focus.clone(),
|
||||||
move |terminal, origin, e, cx| {
|
move |terminal, e, cx| {
|
||||||
terminal.mouse_up(e, origin, cx);
|
terminal.mouse_up(e, cx);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
self.interactivity.on_mouse_up(
|
self.interactivity.on_mouse_up(
|
||||||
MouseButton::Middle,
|
MouseButton::Middle,
|
||||||
TerminalElement::generic_button_handler(
|
TerminalElement::generic_button_handler(terminal, focus, move |terminal, e, cx| {
|
||||||
terminal,
|
terminal.mouse_up(e, cx);
|
||||||
origin,
|
}),
|
||||||
focus,
|
|
||||||
move |terminal, origin, e, cx| {
|
|
||||||
terminal.mouse_up(e, origin, cx);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -705,7 +685,10 @@ impl Element for TerminalElement {
|
||||||
size.width = cell_width * 2.0;
|
size.width = cell_width * 2.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TerminalSize::new(line_height, cell_width, size)
|
let mut origin = bounds.origin;
|
||||||
|
origin.x += gutter;
|
||||||
|
|
||||||
|
TerminalBounds::new(line_height, cell_width, Bounds { origin, size })
|
||||||
};
|
};
|
||||||
|
|
||||||
let search_matches = self.terminal.read(cx).matches.clone();
|
let search_matches = self.terminal.read(cx).matches.clone();
|
||||||
|
@ -714,9 +697,11 @@ impl Element for TerminalElement {
|
||||||
|
|
||||||
let last_hovered_word = self.terminal.update(cx, |terminal, cx| {
|
let last_hovered_word = self.terminal.update(cx, |terminal, cx| {
|
||||||
terminal.set_size(dimensions);
|
terminal.set_size(dimensions);
|
||||||
terminal.sync(cx);
|
terminal.sync(window, cx);
|
||||||
|
|
||||||
if self.can_navigate_to_selected_word
|
if self.can_navigate_to_selected_word
|
||||||
&& terminal.can_navigate_to_selected_word()
|
&& window.modifiers().secondary()
|
||||||
|
&& bounds.contains(&window.mouse_position())
|
||||||
{
|
{
|
||||||
terminal.last_content.last_hovered_word.clone()
|
terminal.last_content.last_hovered_word.clone()
|
||||||
} else {
|
} else {
|
||||||
|
@ -898,7 +883,7 @@ impl Element for TerminalElement {
|
||||||
workspace: self.workspace.clone(),
|
workspace: self.workspace.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.register_mouse_listeners(origin, layout.mode, &layout.hitbox, window);
|
self.register_mouse_listeners(layout.mode, &layout.hitbox, window);
|
||||||
if self.can_navigate_to_selected_word && layout.last_hovered_word.is_some() {
|
if self.can_navigate_to_selected_word && layout.last_hovered_word.is_some() {
|
||||||
window.set_cursor_style(gpui::CursorStyle::PointingHand, &layout.hitbox);
|
window.set_cursor_style(gpui::CursorStyle::PointingHand, &layout.hitbox);
|
||||||
} else {
|
} else {
|
||||||
|
@ -924,12 +909,9 @@ impl Element for TerminalElement {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let handled = this
|
this.update(cx, |term, cx| {
|
||||||
.update(cx, |term, _| term.try_modifiers_change(&event.modifiers));
|
term.try_modifiers_change(&event.modifiers, window, cx)
|
||||||
|
});
|
||||||
if handled {
|
|
||||||
window.refresh();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ struct ScrollHandleState {
|
||||||
impl ScrollHandleState {
|
impl ScrollHandleState {
|
||||||
fn new(terminal: &Terminal) -> Self {
|
fn new(terminal: &Terminal) -> Self {
|
||||||
Self {
|
Self {
|
||||||
line_height: terminal.last_content().size.line_height,
|
line_height: terminal.last_content().terminal_bounds.line_height,
|
||||||
total_lines: terminal.total_lines(),
|
total_lines: terminal.total_lines(),
|
||||||
viewport_lines: terminal.viewport_lines(),
|
viewport_lines: terminal.viewport_lines(),
|
||||||
display_offset: terminal.last_content().display_offset,
|
display_offset: terminal.last_content().display_offset,
|
||||||
|
|
|
@ -23,7 +23,7 @@ use terminal::{
|
||||||
terminal_settings::{self, CursorShape, TerminalBlink, TerminalSettings, WorkingDirectory},
|
terminal_settings::{self, CursorShape, TerminalBlink, TerminalSettings, WorkingDirectory},
|
||||||
Clear, Copy, Event, MaybeNavigationTarget, Paste, ScrollLineDown, ScrollLineUp, ScrollPageDown,
|
Clear, Copy, Event, MaybeNavigationTarget, Paste, ScrollLineDown, ScrollLineUp, ScrollPageDown,
|
||||||
ScrollPageUp, ScrollToBottom, ScrollToTop, ShowCharacterPalette, TaskStatus, Terminal,
|
ScrollPageUp, ScrollToBottom, ScrollToTop, ShowCharacterPalette, TaskStatus, Terminal,
|
||||||
TerminalSize, ToggleViMode,
|
TerminalBounds, ToggleViMode,
|
||||||
};
|
};
|
||||||
use terminal_element::{is_blank, TerminalElement};
|
use terminal_element::{is_blank, TerminalElement};
|
||||||
use terminal_panel::TerminalPanel;
|
use terminal_panel::TerminalPanel;
|
||||||
|
@ -101,7 +101,7 @@ pub struct BlockProperties {
|
||||||
pub struct BlockContext<'a, 'b> {
|
pub struct BlockContext<'a, 'b> {
|
||||||
pub window: &'a mut Window,
|
pub window: &'a mut Window,
|
||||||
pub context: &'b mut App,
|
pub context: &'b mut App,
|
||||||
pub dimensions: TerminalSize,
|
pub dimensions: TerminalBounds,
|
||||||
}
|
}
|
||||||
|
|
||||||
///A terminal view, maintains the PTY's file handles and communicates with the terminal
|
///A terminal view, maintains the PTY's file handles and communicates with the terminal
|
||||||
|
@ -342,7 +342,7 @@ impl TerminalView {
|
||||||
return Pixels::ZERO;
|
return Pixels::ZERO;
|
||||||
};
|
};
|
||||||
|
|
||||||
let line_height = terminal.last_content().size.line_height;
|
let line_height = terminal.last_content().terminal_bounds.line_height;
|
||||||
let mut terminal_lines = terminal.total_lines();
|
let mut terminal_lines = terminal.total_lines();
|
||||||
let viewport_lines = terminal.viewport_lines();
|
let viewport_lines = terminal.viewport_lines();
|
||||||
if terminal.total_lines() == terminal.viewport_lines() {
|
if terminal.total_lines() == terminal.viewport_lines() {
|
||||||
|
@ -366,16 +366,11 @@ impl TerminalView {
|
||||||
max_scroll_top_in_lines as f32 * line_height
|
max_scroll_top_in_lines as f32 * line_height
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_wheel(
|
fn scroll_wheel(&mut self, event: &ScrollWheelEvent, cx: &mut Context<Self>) {
|
||||||
&mut self,
|
|
||||||
event: &ScrollWheelEvent,
|
|
||||||
origin: gpui::Point<Pixels>,
|
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) {
|
|
||||||
let terminal_content = self.terminal.read(cx).last_content();
|
let terminal_content = self.terminal.read(cx).last_content();
|
||||||
|
|
||||||
if self.block_below_cursor.is_some() && terminal_content.display_offset == 0 {
|
if self.block_below_cursor.is_some() && terminal_content.display_offset == 0 {
|
||||||
let line_height = terminal_content.size.line_height;
|
let line_height = terminal_content.terminal_bounds.line_height;
|
||||||
let y_delta = event.delta.pixel_delta(line_height).y;
|
let y_delta = event.delta.pixel_delta(line_height).y;
|
||||||
if y_delta < Pixels::ZERO || self.scroll_top > Pixels::ZERO {
|
if y_delta < Pixels::ZERO || self.scroll_top > Pixels::ZERO {
|
||||||
self.scroll_top = cmp::max(
|
self.scroll_top = cmp::max(
|
||||||
|
@ -387,8 +382,7 @@ impl TerminalView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.terminal
|
self.terminal.update(cx, |term, _| term.scroll_wheel(event));
|
||||||
.update(cx, |term, _| term.scroll_wheel(event, origin));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_line_up(&mut self, _: &ScrollLineUp, _: &mut Window, cx: &mut Context<Self>) {
|
fn scroll_line_up(&mut self, _: &ScrollLineUp, _: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
@ -397,7 +391,7 @@ impl TerminalView {
|
||||||
&& terminal_content.display_offset == 0
|
&& terminal_content.display_offset == 0
|
||||||
&& self.scroll_top > Pixels::ZERO
|
&& self.scroll_top > Pixels::ZERO
|
||||||
{
|
{
|
||||||
let line_height = terminal_content.size.line_height;
|
let line_height = terminal_content.terminal_bounds.line_height;
|
||||||
self.scroll_top = cmp::max(self.scroll_top - line_height, Pixels::ZERO);
|
self.scroll_top = cmp::max(self.scroll_top - line_height, Pixels::ZERO);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -411,7 +405,7 @@ impl TerminalView {
|
||||||
if self.block_below_cursor.is_some() && terminal_content.display_offset == 0 {
|
if self.block_below_cursor.is_some() && terminal_content.display_offset == 0 {
|
||||||
let max_scroll_top = self.max_scroll_top(cx);
|
let max_scroll_top = self.max_scroll_top(cx);
|
||||||
if self.scroll_top < max_scroll_top {
|
if self.scroll_top < max_scroll_top {
|
||||||
let line_height = terminal_content.size.line_height;
|
let line_height = terminal_content.terminal_bounds.line_height;
|
||||||
self.scroll_top = cmp::min(self.scroll_top + line_height, max_scroll_top);
|
self.scroll_top = cmp::min(self.scroll_top + line_height, max_scroll_top);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -425,7 +419,12 @@ impl TerminalView {
|
||||||
if self.scroll_top == Pixels::ZERO {
|
if self.scroll_top == Pixels::ZERO {
|
||||||
self.terminal.update(cx, |term, _| term.scroll_page_up());
|
self.terminal.update(cx, |term, _| term.scroll_page_up());
|
||||||
} else {
|
} else {
|
||||||
let line_height = self.terminal.read(cx).last_content.size.line_height();
|
let line_height = self
|
||||||
|
.terminal
|
||||||
|
.read(cx)
|
||||||
|
.last_content
|
||||||
|
.terminal_bounds
|
||||||
|
.line_height();
|
||||||
let visible_block_lines = (self.scroll_top / line_height) as usize;
|
let visible_block_lines = (self.scroll_top / line_height) as usize;
|
||||||
let viewport_lines = self.terminal.read(cx).viewport_lines();
|
let viewport_lines = self.terminal.read(cx).viewport_lines();
|
||||||
let visible_content_lines = viewport_lines - visible_block_lines;
|
let visible_content_lines = viewport_lines - visible_block_lines;
|
||||||
|
@ -866,7 +865,8 @@ fn subscribe_for_terminal_events(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => false,
|
None => false,
|
||||||
}
|
};
|
||||||
|
cx.notify()
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::Open(maybe_navigation_target) => match maybe_navigation_target {
|
Event::Open(maybe_navigation_target) => match maybe_navigation_target {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue