Nearly done, not scheduling our own re-render yet

This commit is contained in:
Mikayla Maki 2022-07-27 10:11:10 -07:00
parent d1e878f0c6
commit 9dfdaae94d
5 changed files with 104 additions and 108 deletions

View file

@ -46,6 +46,7 @@ pub struct LayoutState {
background_color: Color, background_color: Color,
selection_color: Color, selection_color: Color,
size: TermDimensions, size: TermDimensions,
display_offset: usize,
} }
///Helper struct for converting data between alacritty's cursor points, and displayed cursor points ///Helper struct for converting data between alacritty's cursor points, and displayed cursor points
@ -355,6 +356,7 @@ impl TerminalEl {
view_id: usize, view_id: usize,
visible_bounds: RectF, visible_bounds: RectF,
cur_size: TermDimensions, cur_size: TermDimensions,
display_offset: usize,
cx: &mut PaintContext, cx: &mut PaintContext,
) { ) {
let mouse_down_connection = self.terminal.clone(); let mouse_down_connection = self.terminal.clone();
@ -371,7 +373,7 @@ impl TerminalEl {
position, position,
origin, origin,
cur_size, cur_size,
terminal.get_display_offset(), display_offset,
); );
terminal.mouse_down(point, side); terminal.mouse_down(point, side);
@ -396,7 +398,7 @@ impl TerminalEl {
position, position,
origin, origin,
cur_size, cur_size,
terminal.get_display_offset(), display_offset,
); );
terminal.click(point, side, click_count); terminal.click(point, side, click_count);
@ -415,7 +417,7 @@ impl TerminalEl {
position, position,
origin, origin,
cur_size, cur_size,
terminal.get_display_offset(), display_offset,
); );
terminal.drag(point, side); terminal.drag(point, side);
@ -514,10 +516,6 @@ impl Element for TerminalEl {
let settings = cx.global::<Settings>(); let settings = cx.global::<Settings>();
let font_cache = cx.font_cache(); let font_cache = cx.font_cache();
//First step, make all methods take mut and update internal event queue to take new actions
//Update process terminal event to handle all actions correctly
//And it's done.
//Setup layout information //Setup layout information
let terminal_theme = settings.theme.terminal.clone(); //TODO: Try to minimize this clone. let terminal_theme = settings.theme.terminal.clone(); //TODO: Try to minimize this clone.
let text_style = TerminalEl::make_text_style(font_cache, &settings); let text_style = TerminalEl::make_text_style(font_cache, &settings);
@ -534,58 +532,59 @@ impl Element for TerminalEl {
terminal_theme.colors.background.clone() terminal_theme.colors.background.clone()
}; };
let (cursor, cells, rects, highlights) = let (cursor, cells, rects, highlights, display_offset) = self
self.terminal .terminal
.upgrade(cx) .upgrade(cx)
.unwrap() .unwrap()
.update(cx.app, |terminal, mcx| { .update(cx.app, |terminal, mcx| {
terminal.render_lock(mcx, |content, cursor_text| { terminal.set_size(dimensions);
let (cells, rects, highlights) = TerminalEl::layout_grid( terminal.render_lock(mcx, |content, cursor_text| {
content.display_iter, let (cells, rects, highlights) = TerminalEl::layout_grid(
&text_style, content.display_iter,
&terminal_theme, &text_style,
cx.text_layout_cache, &terminal_theme,
self.modal, cx.text_layout_cache,
content.selection, self.modal,
); content.selection,
);
//Layout cursor //Layout cursor
let cursor = { let cursor = {
let cursor_point = let cursor_point =
DisplayCursor::from(content.cursor.point, content.display_offset); DisplayCursor::from(content.cursor.point, content.display_offset);
let cursor_text = { let cursor_text = {
let str_trxt = cursor_text.to_string(); let str_trxt = cursor_text.to_string();
cx.text_layout_cache.layout_str( cx.text_layout_cache.layout_str(
&str_trxt, &str_trxt,
text_style.font_size, text_style.font_size,
&[( &[(
str_trxt.len(), str_trxt.len(),
RunStyle { RunStyle {
font_id: text_style.font_id, font_id: text_style.font_id,
color: terminal_theme.colors.background, color: terminal_theme.colors.background,
underline: Default::default(), underline: Default::default(),
}, },
)], )],
)
};
TerminalEl::shape_cursor(cursor_point, dimensions, &cursor_text).map(
move |(cursor_position, block_width)| {
Cursor::new(
cursor_position,
block_width,
dimensions.line_height,
terminal_theme.colors.cursor,
CursorShape::Block,
Some(cursor_text.clone()),
)
},
) )
}; };
(cursor, cells, rects, highlights) TerminalEl::shape_cursor(cursor_point, dimensions, &cursor_text).map(
}) move |(cursor_position, block_width)| {
}); Cursor::new(
cursor_position,
block_width,
dimensions.line_height,
terminal_theme.colors.cursor,
CursorShape::Block,
Some(cursor_text.clone()),
)
},
)
};
(cursor, cells, rects, highlights, content.display_offset)
})
});
//Done! //Done!
( (
@ -598,6 +597,7 @@ impl Element for TerminalEl {
size: dimensions, size: dimensions,
rects, rects,
highlights, highlights,
display_offset,
}, },
) )
} }
@ -616,7 +616,14 @@ impl Element for TerminalEl {
let origin = bounds.origin() + vec2f(layout.size.cell_width, 0.); 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 //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, cx); self.attach_mouse_handlers(
origin,
self.view.id(),
visible_bounds,
layout.size,
layout.display_offset,
cx,
);
cx.paint_layer(clip_bounds, |cx| { cx.paint_layer(clip_bounds, |cx| {
//Start with a background color //Start with a background color
@ -694,9 +701,9 @@ impl Element for TerminalEl {
(delta.y() / layout.size.line_height) * ALACRITTY_SCROLL_MULTIPLIER; (delta.y() / layout.size.line_height) * ALACRITTY_SCROLL_MULTIPLIER;
self.terminal.upgrade(cx.app).map(|terminal| { self.terminal.upgrade(cx.app).map(|terminal| {
terminal terminal.update(cx.app, |term, _| {
.read(cx.app) term.scroll(Scroll::Delta(vertical_scroll.round() as i32))
.scroll(Scroll::Delta(vertical_scroll.round() as i32)); });
}); });
}) })
.is_some(), .is_some(),

View file

@ -43,11 +43,12 @@ impl ConnectedView {
modal: bool, modal: bool,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Self { ) -> Self {
// cx.observe(&terminal, |_, _, cx| cx.notify()).detach(); //Terminal notifies for us cx.observe(&terminal, |_, _, cx| cx.notify()).detach();
cx.subscribe(&terminal, |this, _, event, cx| match event { cx.subscribe(&terminal, |this, _, event, cx| match event {
Event::Wakeup => { Event::Wakeup => {
if !cx.is_self_focused() { if !cx.is_self_focused() {
this.has_new_content = true; this.has_new_content = true;
cx.notify();
cx.emit(Event::Wakeup); cx.emit(Event::Wakeup);
} }
} }
@ -91,7 +92,7 @@ impl ConnectedView {
///Attempt to paste the clipboard into the terminal ///Attempt to paste the clipboard into the terminal
fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) { fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
self.terminal.read(cx).copy() self.terminal.update(cx, |term, _| term.copy())
} }
///Attempt to paste the clipboard into the terminal ///Attempt to paste the clipboard into the terminal

View file

@ -40,8 +40,8 @@ impl Modifiers {
///This is unavoidable for our use case. GPUI cannot wait until we acquire the terminal ///This is unavoidable for our use case. GPUI cannot wait until we acquire the terminal
///lock to determine whether we could actually send the keystroke with the current settings. Therefore, ///lock to determine whether we could actually send the keystroke with the current settings. Therefore,
///This conservative guess is used instead. Note that in practice the case where this method ///This conservative guess is used instead. Note that in practice the case where this method
///Returns false when the actual terminal would consume the keystroke never actually happens. All ///Returns false when the actual terminal would consume the keystroke never happens. All keystrokes
///keystrokes that depend on terminal modes also have a mapping that doesn't depend on the terminal mode. ///that depend on terminal modes also have a mapping that doesn't depend on the terminal mode.
///This is fragile, but as these mappings are locked up in legacy compatibility, it's probably good enough ///This is fragile, but as these mappings are locked up in legacy compatibility, it's probably good enough
pub fn might_convert(keystroke: &Keystroke) -> bool { pub fn might_convert(keystroke: &Keystroke) -> bool {
to_esc_str(keystroke, &TermMode::NONE).is_some() to_esc_str(keystroke, &TermMode::NONE).is_some()

View file

@ -24,13 +24,10 @@ use alacritty_terminal::{
}; };
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use itertools::Itertools;
use mappings::keys::might_convert; use mappings::keys::might_convert;
use modal::deploy_modal; use modal::deploy_modal;
use settings::{Settings, Shell}; use settings::{Settings, Shell};
use std::{ use std::{collections::HashMap, fmt::Display, path::PathBuf, sync::Arc, time::Duration};
cmp::Ordering, collections::HashMap, fmt::Display, path::PathBuf, sync::Arc, time::Duration,
};
use terminal_view::TerminalView; use terminal_view::TerminalView;
use thiserror::Error; use thiserror::Error;
@ -53,7 +50,7 @@ pub fn init(cx: &mut MutableAppContext) {
connected_view::init(cx); connected_view::init(cx);
} }
const DEBUG_TERMINAL_WIDTH: f32 = 100.; 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.; //This needs to be wide enough that the CI & a local dev's prompt can fill the whole space.
const DEBUG_CELL_WIDTH: f32 = 5.; const DEBUG_CELL_WIDTH: f32 = 5.;
const DEBUG_LINE_HEIGHT: f32 = 5.; const DEBUG_LINE_HEIGHT: f32 = 5.;
@ -77,7 +74,7 @@ enum InternalEvent {
Paste(String), Paste(String),
Scroll(Scroll), Scroll(Scroll),
SetSelection(Option<Selection>), SetSelection(Option<Selection>),
UpdateSelection(Point), UpdateSelection((Point, Direction)),
Copy, Copy,
} }
@ -337,7 +334,7 @@ impl TerminalBuilder {
pty_tx: Notifier(pty_tx), pty_tx: Notifier(pty_tx),
term, term,
event_stack: vec![], events: vec![],
title: shell_txt.clone(), title: shell_txt.clone(),
default_title: shell_txt, default_title: shell_txt,
}; };
@ -366,12 +363,10 @@ impl TerminalBuilder {
Err(_) => break, Err(_) => break,
} }
} }
match this.upgrade(&cx) { match this.upgrade(&cx) {
Some(this) => { Some(this) => {
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, _cx| {
this.push_events(events); this.push_events(events);
cx.notify();
}); });
} }
None => break 'outer, None => break 'outer,
@ -389,14 +384,14 @@ impl TerminalBuilder {
pub struct Terminal { pub struct Terminal {
pty_tx: Notifier, pty_tx: Notifier,
term: Arc<FairMutex<Term<ZedListener>>>, term: Arc<FairMutex<Term<ZedListener>>>,
event_stack: Vec<InternalEvent>, events: Vec<InternalEvent>,
default_title: String, default_title: String,
title: String, title: String,
} }
impl Terminal { impl Terminal {
fn push_events(&mut self, events: Vec<AlacTermEvent>) { fn push_events(&mut self, events: Vec<AlacTermEvent>) {
self.event_stack self.events
.extend(events.into_iter().map(|e| InternalEvent::TermEvent(e))) .extend(events.into_iter().map(|e| InternalEvent::TermEvent(e)))
} }
@ -407,6 +402,7 @@ impl Terminal {
term: &mut Term<ZedListener>, term: &mut Term<ZedListener>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) { ) {
dbg!(event);
// TODO: Handle is_self_focused in subscription on terminal view // TODO: Handle is_self_focused in subscription on terminal view
match event { match event {
InternalEvent::TermEvent(term_event) => match term_event { InternalEvent::TermEvent(term_event) => match term_event {
@ -467,6 +463,7 @@ impl Terminal {
term.clear_screen(ClearMode::Saved); term.clear_screen(ClearMode::Saved);
} }
InternalEvent::Keystroke(keystroke) => { InternalEvent::Keystroke(keystroke) => {
println!("Trying keystroke: {}", keystroke);
let esc = to_esc_str(keystroke, term.mode()); let esc = to_esc_str(keystroke, term.mode());
if let Some(esc) = esc { if let Some(esc) = esc {
self.notify_pty(esc); self.notify_pty(esc);
@ -482,10 +479,10 @@ impl Terminal {
} }
} }
InternalEvent::Scroll(scroll) => term.scroll_display(*scroll), InternalEvent::Scroll(scroll) => term.scroll_display(*scroll),
InternalEvent::SetSelection(sel) => term.selection = sel, InternalEvent::SetSelection(sel) => term.selection = sel.clone(),
InternalEvent::UpdateSelection(point) => { InternalEvent::UpdateSelection((point, side)) => {
if let Some(mut selection) = term.selection.take() { if let Some(mut selection) = term.selection.take() {
selection.update(*point, side); selection.update(*point, *side);
term.selection = Some(selection); term.selection = Some(selection);
} }
} }
@ -502,30 +499,24 @@ impl Terminal {
self.pty_tx.notify(txt.into_bytes()); self.pty_tx.notify(txt.into_bytes());
} }
//TODO:
// - Continue refactor into event system
// - Fix PTYWrite call to not be circular and messy
// - Change title to be emitted and maintained externally
///Write the Input payload to the tty. ///Write the Input payload to the tty.
pub fn write_to_pty(&mut self, input: String) { pub fn write_to_pty(&mut self, input: String) {
self.event_stack self.events
.push(InternalEvent::TermEvent(AlacTermEvent::PtyWrite(input))) .push(InternalEvent::TermEvent(AlacTermEvent::PtyWrite(input)))
} }
///Resize the terminal and the PTY. ///Resize the terminal and the PTY.
pub fn set_size(&mut self, new_size: TermDimensions) { pub fn set_size(&mut self, new_size: TermDimensions) {
self.event_stack self.events.push(InternalEvent::Resize(new_size.into()))
.push(InternalEvent::Resize(new_size.into()))
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.event_stack.push(InternalEvent::Clear) self.events.push(InternalEvent::Clear)
} }
pub fn try_keystroke(&mut self, keystroke: &Keystroke) -> bool { pub fn try_keystroke(&mut self, keystroke: &Keystroke) -> bool {
if might_convert(keystroke) { if might_convert(keystroke) {
self.event_stack self.events
.push(InternalEvent::Keystroke(keystroke.clone())); .push(InternalEvent::Keystroke(keystroke.clone()));
true true
} else { } else {
@ -535,27 +526,25 @@ impl Terminal {
///Paste text into the terminal ///Paste text into the terminal
pub fn paste(&mut self, text: &str) { pub fn paste(&mut self, text: &str) {
self.event_stack self.events.push(InternalEvent::Paste(text.to_string()));
.push(InternalEvent::Paste(text.to_string()));
} }
pub fn copy(&self) { pub fn copy(&mut self) {
self.event_stack.push(InternalEvent::Copy); self.events.push(InternalEvent::Copy);
} }
pub fn render_lock<F, T>(&mut self, cx: &mut ModelContext<Self>, f: F) -> T pub fn render_lock<F, T>(&mut self, cx: &mut ModelContext<Self>, f: F) -> T
where where
F: FnOnce(RenderableContent, char) -> T, F: FnOnce(RenderableContent, char) -> T,
{ {
println!("RENDER LOCK!");
let m = self.term.clone(); //Arc clone let m = self.term.clone(); //Arc clone
let mut term = m.lock(); let mut term = m.lock();
for event in self.event_stack.clone().into_iter().sorted() { while let Some(e) = self.events.pop() {
self.process_terminal_event(&event, &mut term, cx) self.process_terminal_event(&e, &mut term, cx)
} }
self.event_stack.clear();
let content = term.renderable_content(); let content = term.renderable_content();
let cursor_text = term.grid()[content.cursor.point].c; let cursor_text = term.grid()[content.cursor.point].c;
@ -563,11 +552,11 @@ impl Terminal {
} }
///Scroll the terminal ///Scroll the terminal
pub fn scroll(&self, _scroll: Scroll) { pub fn scroll(&mut self, scroll: Scroll) {
self.event_stack.push(InternalEvent::Scroll(scroll)); self.events.push(InternalEvent::Scroll(scroll));
} }
pub fn click(&self, point: Point, side: Direction, clicks: usize) { pub fn click(&mut self, point: Point, side: Direction, clicks: usize) {
let selection_type = match clicks { let selection_type = match clicks {
0 => return, //This is a release 0 => return, //This is a release
1 => Some(SelectionType::Simple), 1 => Some(SelectionType::Simple),
@ -579,17 +568,17 @@ impl Terminal {
let selection = let selection =
selection_type.map(|selection_type| Selection::new(selection_type, point, side)); selection_type.map(|selection_type| Selection::new(selection_type, point, side));
self.event_stack self.events.push(InternalEvent::SetSelection(selection));
.push(InternalEvent::SetSelection(selection));
} }
pub fn drag(&self, point: Point, side: Direction) { pub fn drag(&mut self, point: Point, side: Direction) {
self.event_stack.push(InternalEvent::UpdateSelection(point)); self.events
.push(InternalEvent::UpdateSelection((point, side)));
} }
///TODO: Check if the mouse_down-then-click assumption holds, so this code works as expected ///TODO: Check if the mouse_down-then-click assumption holds, so this code works as expected
pub fn mouse_down(&self, point: Point, side: Direction) { pub fn mouse_down(&mut self, point: Point, side: Direction) {
self.event_stack self.events
.push(InternalEvent::SetSelection(Some(Selection::new( .push(InternalEvent::SetSelection(Some(Selection::new(
SelectionType::Simple, SelectionType::Simple,
point, point,

View file

@ -66,13 +66,12 @@ impl TerminalView {
} }
///Create a new Terminal view. This spawns a task, a thread, and opens the TTY devices ///Create a new Terminal view. This spawns a task, a thread, and opens the TTY devices
///To get the right working directory from a workspace, use: `get_wd_for_workspace()`
pub fn new( pub fn new(
working_directory: Option<PathBuf>, working_directory: Option<PathBuf>,
modal: bool, modal: bool,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Self { ) -> Self {
//The details here don't matter, the terminal will be resized on the first layout //The exact size here doesn't matter, the terminal will be resized on the first layout
let size_info = TermDimensions::default(); let size_info = TermDimensions::default();
let settings = cx.global::<Settings>(); let settings = cx.global::<Settings>();