diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 467a45b93e..5f438057ee 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -362,12 +362,7 @@ mod tests { }); let palette = workspace.read_with(cx, |workspace, _| { - workspace - .modal() - .unwrap() - .clone() - .downcast::() - .unwrap() + workspace.modal::().unwrap() }); palette @@ -398,12 +393,7 @@ mod tests { // Assert editor command not present let palette = workspace.read_with(cx, |workspace, _| { - workspace - .modal() - .unwrap() - .clone() - .downcast::() - .unwrap() + workspace.modal::().unwrap() }); palette diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index 720e0142be..c8172e0de3 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -317,15 +317,7 @@ mod tests { let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); cx.dispatch_action(window_id, Toggle); - let finder = cx.read(|cx| { - workspace - .read(cx) - .modal() - .cloned() - .unwrap() - .downcast::() - .unwrap() - }); + let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); cx.dispatch_action(window_id, Input("b".into())); cx.dispatch_action(window_id, Input("n".into())); cx.dispatch_action(window_id, Input("a".into())); diff --git a/crates/terminal/src/connection.rs b/crates/terminal/src/connection.rs index f7407959f6..d41f4e8fad 100644 --- a/crates/terminal/src/connection.rs +++ b/crates/terminal/src/connection.rs @@ -122,18 +122,18 @@ impl Display for TerminalError { } } -pub struct DisconnectedPTY { +pub struct TerminalBuilder { terminal: Terminal, events_rx: UnboundedReceiver, } -impl DisconnectedPTY { +impl TerminalBuilder { pub fn new( working_directory: Option, shell: Option, env: Option>, initial_size: TerminalDimensions, - ) -> Result { + ) -> Result { let pty_config = { let alac_shell = shell.clone().and_then(|shell| match shell { Shell::System => None, @@ -214,13 +214,13 @@ impl DisconnectedPTY { associated_directory: working_directory, }; - Ok(DisconnectedPTY { + Ok(TerminalBuilder { terminal, events_rx, }) } - pub fn connect(mut self, cx: &mut ModelContext) -> Terminal { + pub fn subscribe(mut self, cx: &mut ModelContext) -> Terminal { cx.spawn_weak(|this, mut cx| async move { //Listen for terminal events while let Some(event) = self.events_rx.next().await { @@ -435,7 +435,7 @@ impl Entity for Terminal { mod alacritty_unix { use alacritty_terminal::config::Program; use gpui::anyhow::{bail, Result}; - use libc::{self}; + use libc; use std::ffi::CStr; use std::mem::MaybeUninit; use std::ptr; diff --git a/crates/terminal/src/modal.rs b/crates/terminal/src/modal.rs index 0cd0febe78..c53f2a0469 100644 --- a/crates/terminal/src/modal.rs +++ b/crates/terminal/src/modal.rs @@ -1,60 +1,60 @@ use gpui::{ModelHandle, ViewContext}; use workspace::Workspace; -use crate::{connection::Terminal, get_wd_for_workspace, DeployModal, Event, TerminalView}; +use crate::{ + connection::Terminal, get_working_directory, DeployModal, Event, TerminalContent, TerminalView, +}; #[derive(Debug)] -struct StoredConnection(ModelHandle); +struct StoredTerminal(ModelHandle); pub fn deploy_modal(workspace: &mut Workspace, _: &DeployModal, cx: &mut ViewContext) { // Pull the terminal connection out of the global if it has been stored - let possible_connection = - cx.update_default_global::, _, _>(|possible_connection, _| { + let possible_terminal = + cx.update_default_global::, _, _>(|possible_connection, _| { possible_connection.take() }); - if let Some(StoredConnection(stored_connection)) = possible_connection { - // Create a view from the stored connection + if let Some(StoredTerminal(stored_terminal)) = possible_terminal { workspace.toggle_modal(cx, |_, cx| { - cx.add_view(|cx| { - TerminalView::from_connection( - crate::TerminalConnection(Ok(stored_connection.clone())), - true, - cx, - ) - }) + // Create a view from the stored connection if the terminal modal is not already shown + cx.add_view(|cx| TerminalView::from_terminal(stored_terminal.clone(), true, cx)) }); - cx.set_global::>(Some(StoredConnection( - stored_connection.clone(), - ))); + // Toggle Modal will dismiss the terminal modal if it is currently shown, so we must + // store the terminal back in the global + cx.set_global::>(Some(StoredTerminal(stored_terminal.clone()))); } else { // No connection was stored, create a new terminal if let Some(closed_terminal_handle) = workspace.toggle_modal(cx, |workspace, cx| { - let wd = get_wd_for_workspace(workspace, cx); + // No terminal modal visible, construct a new one. + let working_directory = get_working_directory(workspace, cx); - //TODO fix this crash - let this = cx.add_view(|cx| TerminalView::new(wd, true, cx).unwrap()); + let this = cx.add_view(|cx| TerminalView::new(working_directory, true, cx)); + + if let TerminalContent::Connected(connected) = &this.read(cx).content { + let terminal_handle = connected.read(cx).terminal.clone(); + cx.subscribe(&terminal_handle, on_event).detach(); + // Set the global immediately if terminal construction was successful, + // in case the user opens the command palette + cx.set_global::>(Some(StoredTerminal( + terminal_handle.clone(), + ))); + } - let connection_handle = this.read(cx).connection.0.as_ref().unwrap().clone(); - cx.subscribe(&connection_handle, on_event).detach(); - //Set the global immediately, in case the user opens the command palette - cx.set_global::>(Some(StoredConnection( - connection_handle.clone(), - ))); this }) { - let connection = closed_terminal_handle - .read(cx) - .connection - .0 - .as_ref() - .unwrap() - .clone(); - cx.set_global(Some(StoredConnection(connection))); + // Terminal modal was dismissed. Store terminal if the terminal view is connected + if let TerminalContent::Connected(connected) = &closed_terminal_handle.read(cx).content + { + let terminal_handle = connected.read(cx).terminal.clone(); + // Set the global immediately if terminal construction was successful, + // in case the user opens the command palette + cx.set_global::>(Some(StoredTerminal( + terminal_handle.clone(), + ))); + } } } - - //The problem is that the terminal modal is never re-stored. } pub fn on_event( @@ -65,13 +65,8 @@ pub fn on_event( ) { // Dismiss the modal if the terminal quit if let Event::CloseTerminal = event { - cx.set_global::>(None); - if workspace - .modal() - .cloned() - .and_then(|modal| modal.downcast::()) - .is_some() - { + cx.set_global::>(None); + if workspace.modal::().is_some() { workspace.dismiss_modal(cx) } } diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 0bbad588c5..9850ff716b 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -3,11 +3,11 @@ pub mod connection; mod modal; pub mod terminal_element; -use connection::{DisconnectedPTY, Event, Terminal, TerminalError}; +use connection::{Event, Terminal, TerminalBuilder, TerminalError}; use dirs::home_dir; use gpui::{ - actions, elements::*, geometry::vector::vec2f, keymap::Keystroke, AppContext, ClipboardItem, - Entity, ModelHandle, MutableAppContext, View, ViewContext, + actions, elements::*, geometry::vector::vec2f, keymap::Keystroke, AnyViewHandle, AppContext, + ClipboardItem, Entity, ModelHandle, MutableAppContext, View, ViewContext, ViewHandle, }; use modal::deploy_modal; @@ -16,7 +16,6 @@ use settings::{Settings, WorkingDirectory}; use smallvec::SmallVec; use std::path::{Path, PathBuf}; use terminal_element::{terminal_layout_context::TerminalLayoutData, TerminalDimensions}; -use util::ResultExt; use workspace::{Item, Workspace}; use crate::terminal_element::TerminalEl; @@ -49,25 +48,49 @@ actions!( ///Initialize and register all of our action handlers pub fn init(cx: &mut MutableAppContext) { //Global binding overrrides - cx.add_action(TerminalView::ctrl_c); - cx.add_action(TerminalView::up); - cx.add_action(TerminalView::down); - cx.add_action(TerminalView::escape); - cx.add_action(TerminalView::enter); + cx.add_action(ConnectedView::ctrl_c); + cx.add_action(ConnectedView::up); + cx.add_action(ConnectedView::down); + cx.add_action(ConnectedView::escape); + cx.add_action(ConnectedView::enter); //Useful terminal actions - cx.add_action(TerminalView::deploy); + cx.add_action(ConnectedView::deploy); + cx.add_action(ConnectedView::copy); + cx.add_action(ConnectedView::paste); + cx.add_action(ConnectedView::clear); cx.add_action(deploy_modal); - cx.add_action(TerminalView::copy); - cx.add_action(TerminalView::paste); - cx.add_action(TerminalView::clear); } -//New Type to make terminal connection's easier -struct TerminalConnection(Result, TerminalError>); +//Make terminal view an enum, that can give you views for the error and non-error states +//Take away all the result unwrapping in the current TerminalView by making it 'infallible' +//Bubble up to deploy(_modal)() calls + +enum TerminalContent { + Connected(ViewHandle), + Error(ViewHandle), +} + +impl TerminalContent { + fn handle(&self) -> AnyViewHandle { + match self { + Self::Connected(handle) => handle.into(), + Self::Error(handle) => handle.into(), + } + } +} + +pub struct TerminalView { + modal: bool, + content: TerminalContent, +} + +pub struct ErrorView { + error: TerminalError, +} ///A terminal view, maintains the PTY's file handles and communicates with the terminal -pub struct TerminalView { - connection: TerminalConnection, +pub struct ConnectedView { + terminal: ModelHandle, has_new_content: bool, //Currently using iTerm bell, show bell emoji in tab until input is received has_bell: bool, @@ -79,14 +102,18 @@ impl Entity for TerminalView { type Event = Event; } +impl Entity for ConnectedView { + type Event = Event; +} + +impl Entity for ErrorView { + type Event = Event; +} + impl TerminalView { ///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()` - fn new( - working_directory: Option, - modal: bool, - cx: &mut ViewContext, - ) -> Option { + fn new(working_directory: Option, modal: bool, cx: &mut ViewContext) -> Self { //The details here don't matter, the terminal will be resized on the first layout let size_info = TerminalDimensions::new( DEBUG_LINE_HEIGHT, @@ -98,156 +125,37 @@ impl TerminalView { let shell = settings.terminal_overrides.shell.clone(); let envs = settings.terminal_overrides.env.clone(); //Should be short and cheap. - let connection = DisconnectedPTY::new(working_directory, shell, envs, size_info) - .map(|pty| cx.add_model(|cx| pty.connect(cx))) - .map_err(|err| { - match err.downcast::() { - Ok(err) => err, - Err(_) => unreachable!(), //This should never happen - } - }); + let content = match TerminalBuilder::new(working_directory, shell, envs, size_info) { + Ok(terminal) => { + let terminal = cx.add_model(|cx| terminal.subscribe(cx)); + let view = cx.add_view(|cx| ConnectedView::from_terminal(terminal, modal, cx)); + cx.subscribe(&view, |_this, _content, event, cx| cx.emit(event.clone())) + .detach(); + TerminalContent::Connected(view) + } + Err(error) => { + let view = cx.add_view(|_| ErrorView { + error: error.downcast::().unwrap(), + }); + TerminalContent::Error(view) + } + }; + cx.focus(content.handle()); - if let Ok(_) = connection { - Some(TerminalView::from_connection( - TerminalConnection(connection), - modal, - cx, - )) - } else { - connection.log_err(); - None - } + TerminalView { modal, content } } - fn from_connection( - connection: TerminalConnection, + fn from_terminal( + terminal: ModelHandle, modal: bool, cx: &mut ViewContext, - ) -> TerminalView { - match connection.0.as_ref() { - Ok(conn) => { - cx.observe(conn, |_, _, cx| cx.notify()).detach(); - cx.subscribe(conn, |this, _, event, cx| match event { - Event::Wakeup => { - if cx.is_self_focused() { - cx.notify() - } else { - this.has_new_content = true; - cx.emit(Event::TitleChanged); - } - } - Event::Bell => { - this.has_bell = true; - cx.emit(Event::TitleChanged); - } - _ => cx.emit(*event), - }) - .detach(); - } - Err(_) => { /* Leave it as is */ } - } - + ) -> Self { + let connected_view = cx.add_view(|cx| ConnectedView::from_terminal(terminal, modal, cx)); TerminalView { - connection, - has_new_content: true, - has_bell: false, modal, + content: TerminalContent::Connected(connected_view), } } - - fn clear_bel(&mut self, cx: &mut ViewContext) { - self.has_bell = false; - cx.emit(Event::TitleChanged); - } - - ///Create a new Terminal in the current working directory or the user's home directory - fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext) { - let wd = get_wd_for_workspace(workspace, cx); - if let Some(view) = cx.add_option_view(|cx| TerminalView::new(wd, false, cx)) { - workspace.add_item(Box::new(view), cx); - } - } - - fn clear(&mut self, _: &Clear, cx: &mut ViewContext) { - self.connection - .0 - .as_ref() - .map(|term_handle| term_handle.read(cx).clear()) - .ok(); - } - - ///Attempt to paste the clipboard into the terminal - fn copy(&mut self, _: &Copy, cx: &mut ViewContext) { - self.connection - .0 - .as_ref() - .map(|handle| handle.read(cx)) - .map(|term| term.copy()) - .map(|text| text.map(|text| cx.write_to_clipboard(ClipboardItem::new(text)))) - .ok(); - } - - ///Attempt to paste the clipboard into the terminal - fn paste(&mut self, _: &Paste, cx: &mut ViewContext) { - cx.read_from_clipboard().map(|item| { - self.connection - .0 - .as_ref() - .map(|handle| handle.read(cx)) - .map(|term| term.paste(item.text())) - .ok(); - }); - } - - ///Synthesize the keyboard event corresponding to 'up' - fn up(&mut self, _: &Up, cx: &mut ViewContext) { - self.connection - .0 - .as_ref() - .map(|handle| handle.read(cx)) - .map(|term| term.try_keystroke(&Keystroke::parse("up").unwrap())) - .ok(); - } - - ///Synthesize the keyboard event corresponding to 'down' - fn down(&mut self, _: &Down, cx: &mut ViewContext) { - self.connection - .0 - .as_ref() - .map(|handle| handle.read(cx)) - .map(|term| term.try_keystroke(&Keystroke::parse("down").unwrap())) - .ok(); - } - - ///Synthesize the keyboard event corresponding to 'ctrl-c' - fn ctrl_c(&mut self, _: &CtrlC, cx: &mut ViewContext) { - self.connection - .0 - .as_ref() - .map(|handle| handle.read(cx)) - .map(|term| term.try_keystroke(&Keystroke::parse("ctrl-c").unwrap())) - .ok(); - } - - ///Synthesize the keyboard event corresponding to 'escape' - fn escape(&mut self, _: &Escape, cx: &mut ViewContext) { - self.connection - .0 - .as_ref() - .map(|handle| handle.read(cx)) - .map(|term| term.try_keystroke(&Keystroke::parse("escape").unwrap())) - .ok(); - } - - ///Synthesize the keyboard event corresponding to 'enter' - fn enter(&mut self, _: &Enter, cx: &mut ViewContext) { - self.connection - .0 - .as_ref() - .map(|handle| handle.read(cx)) - .map(|term| term.try_keystroke(&Keystroke::parse("enter").unwrap())) - .ok(); - } } impl View for TerminalView { @@ -256,48 +164,25 @@ impl View for TerminalView { } fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox { - let element = match self.connection.0.as_ref() { - Ok(handle) => { - let connection_handle = handle.clone().downgrade(); - TerminalEl::new(cx.handle(), connection_handle, self.modal).contained() - } - Err(e) => { - let settings = cx.global::(); - let style = TerminalLayoutData::make_text_style(cx.font_cache(), settings); - - Flex::column() - .with_child( - Flex::row() - .with_child( - Label::new( - format!( - "Failed to open the terminal. Info: \n{}", - e.to_string() - ), - style, - ) - .boxed(), - ) - .aligned() - .boxed(), - ) - .aligned() - .contained() - } + let child_view = match &self.content { + TerminalContent::Connected(connected) => ChildView::new(connected), + TerminalContent::Error(error) => ChildView::new(error), }; if self.modal { let settings = cx.global::(); let container_style = settings.theme.terminal.modal_container; - element.with_style(container_style).boxed() + child_view.contained().with_style(container_style).boxed() } else { - element.boxed() + child_view.boxed() } } fn on_focus(&mut self, cx: &mut ViewContext) { cx.emit(Event::Activate); - self.has_new_content = false; + cx.defer(|view, cx| { + cx.focus(view.content.handle()); + }); } fn keymap_context(&self, _: &gpui::AppContext) -> gpui::keymap::Context { @@ -309,6 +194,144 @@ impl View for TerminalView { } } +impl ConnectedView { + fn from_terminal( + terminal: ModelHandle, + modal: bool, + cx: &mut ViewContext, + ) -> Self { + cx.observe(&terminal, |_, _, cx| cx.notify()).detach(); + cx.subscribe(&terminal, |this, _, event, cx| match event { + Event::Wakeup => { + if cx.is_self_focused() { + cx.notify() + } else { + this.has_new_content = true; + cx.emit(Event::TitleChanged); + } + } + Event::Bell => { + this.has_bell = true; + cx.emit(Event::TitleChanged); + } + _ => cx.emit(*event), + }) + .detach(); + + Self { + terminal, + has_new_content: true, + has_bell: false, + modal, + } + } + + fn clear_bel(&mut self, cx: &mut ViewContext) { + self.has_bell = false; + cx.emit(Event::TitleChanged); + } + + ///Create a new Terminal in the current working directory or the user's home directory + fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext) { + let working_directory = get_working_directory(workspace, cx); + let view = cx.add_view(|cx| TerminalView::new(working_directory, false, cx)); + workspace.add_item(Box::new(view), cx); + } + + fn clear(&mut self, _: &Clear, cx: &mut ViewContext) { + self.terminal.read(cx).clear(); + } + + ///Attempt to paste the clipboard into the terminal + fn copy(&mut self, _: &Copy, cx: &mut ViewContext) { + self.terminal + .read(cx) + .copy() + .map(|text| cx.write_to_clipboard(ClipboardItem::new(text))); + } + + ///Attempt to paste the clipboard into the terminal + fn paste(&mut self, _: &Paste, cx: &mut ViewContext) { + cx.read_from_clipboard().map(|item| { + self.terminal.read(cx).paste(item.text()); + }); + } + + ///Synthesize the keyboard event corresponding to 'up' + fn up(&mut self, _: &Up, cx: &mut ViewContext) { + self.terminal + .read(cx) + .try_keystroke(&Keystroke::parse("up").unwrap()); + } + + ///Synthesize the keyboard event corresponding to 'down' + fn down(&mut self, _: &Down, cx: &mut ViewContext) { + self.terminal + .read(cx) + .try_keystroke(&Keystroke::parse("down").unwrap()); + } + + ///Synthesize the keyboard event corresponding to 'ctrl-c' + fn ctrl_c(&mut self, _: &CtrlC, cx: &mut ViewContext) { + self.terminal + .read(cx) + .try_keystroke(&Keystroke::parse("ctrl-c").unwrap()); + } + + ///Synthesize the keyboard event corresponding to 'escape' + fn escape(&mut self, _: &Escape, cx: &mut ViewContext) { + self.terminal + .read(cx) + .try_keystroke(&Keystroke::parse("escape").unwrap()); + } + + ///Synthesize the keyboard event corresponding to 'enter' + fn enter(&mut self, _: &Enter, cx: &mut ViewContext) { + self.terminal + .read(cx) + .try_keystroke(&Keystroke::parse("enter").unwrap()); + } +} + +impl View for ConnectedView { + fn ui_name() -> &'static str { + "Connected Terminal View" + } + + fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox { + let terminal_handle = self.terminal.clone().downgrade(); + TerminalEl::new(cx.handle(), terminal_handle, self.modal) + .contained() + .boxed() + } + + fn on_focus(&mut self, _cx: &mut ViewContext) { + self.has_new_content = false; + } +} + +impl View for ErrorView { + fn ui_name() -> &'static str { + "Terminal Error" + } + + fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox { + let settings = cx.global::(); + let style = TerminalLayoutData::make_text_style(cx.font_cache(), settings); + + Label::new( + format!( + "Failed to open the terminal. Info: \n{}", + self.error.to_string() + ), + style, + ) + .aligned() + .contained() + .boxed() + } +} + impl Item for TerminalView { fn tab_content( &self, @@ -316,9 +339,11 @@ impl Item for TerminalView { tab_theme: &theme::Tab, cx: &gpui::AppContext, ) -> ElementBox { - let title = match self.connection.0.as_ref() { - Ok(handle) => handle.read(cx).title.clone(), - Err(_) => "Terminal".to_string(), + let title = match &self.content { + TerminalContent::Connected(connected) => { + connected.read(cx).terminal.read(cx).title.clone() + } + TerminalContent::Error(_) => "Terminal".to_string(), }; Flex::row() @@ -335,13 +360,17 @@ impl Item for TerminalView { //From what I can tell, there's no way to tell the current working //Directory of the terminal from outside the shell. There might be //solutions to this, but they are non-trivial and require more IPC - - let wd = match self.connection.0.as_ref() { - Ok(term_handle) => term_handle.read(cx).associated_directory.clone(), - Err(e) => e.directory.clone(), - }; - - TerminalView::new(wd, false, cx) + if let TerminalContent::Connected(connected) = &self.content { + let associated_directory = connected + .read(cx) + .terminal + .read(cx) + .associated_directory + .clone(); + Some(TerminalView::new(associated_directory, false, cx)) + } else { + None + } } fn project_path(&self, _cx: &gpui::AppContext) -> Option { @@ -387,12 +416,20 @@ impl Item for TerminalView { gpui::Task::ready(Ok(())) } - fn is_dirty(&self, _: &gpui::AppContext) -> bool { - self.has_new_content + fn is_dirty(&self, cx: &gpui::AppContext) -> bool { + if let TerminalContent::Connected(connected) = &self.content { + connected.read(cx).has_new_content + } else { + false + } } - fn has_conflict(&self, _: &AppContext) -> bool { - self.has_bell + fn has_conflict(&self, cx: &AppContext) -> bool { + if let TerminalContent::Connected(connected) = &self.content { + connected.read(cx).has_bell + } else { + false + } } fn should_update_tab_on_event(event: &Self::Event) -> bool { @@ -409,7 +446,7 @@ impl Item for TerminalView { } ///Get's the working directory for the given workspace, respecting the user's settings. -fn get_wd_for_workspace(workspace: &Workspace, cx: &AppContext) -> Option { +fn get_working_directory(workspace: &Workspace, cx: &AppContext) -> Option { let wd_setting = cx .global::() .terminal_overrides diff --git a/crates/terminal/src/terminal_element.rs b/crates/terminal/src/terminal_element.rs index ea547a404f..89618a9f3d 100644 --- a/crates/terminal/src/terminal_element.rs +++ b/crates/terminal/src/terminal_element.rs @@ -32,7 +32,7 @@ use util::ResultExt; use std::{cmp::min, ops::Range}; use std::{fmt::Debug, ops::Sub}; -use crate::{color_translation::convert_color, connection::Terminal, TerminalView}; +use crate::{color_translation::convert_color, connection::Terminal, ConnectedView}; use self::terminal_layout_context::TerminalLayoutData; @@ -44,8 +44,8 @@ const ALACRITTY_SCROLL_MULTIPLIER: f32 = 3.; ///The GPUI element that paints the terminal. ///We need to keep a reference to the view for mouse events, do we need it for any other terminal stuff, or can we move that to connection? pub struct TerminalEl { - connection: WeakModelHandle, - view: WeakViewHandle, + terminal: WeakModelHandle, + view: WeakViewHandle, modal: bool, } @@ -227,13 +227,13 @@ pub struct LayoutState { impl TerminalEl { pub fn new( - view: WeakViewHandle, - connection: WeakModelHandle, + view: WeakViewHandle, + terminal: WeakModelHandle, modal: bool, ) -> TerminalEl { TerminalEl { view, - connection, + terminal, modal, } } @@ -246,9 +246,9 @@ impl TerminalEl { cur_size: TerminalDimensions, cx: &mut PaintContext, ) { - let mouse_down_connection = self.connection.clone(); - let click_connection = self.connection.clone(); - let drag_connection = self.connection.clone(); + let mouse_down_connection = self.terminal.clone(); + let click_connection = self.terminal.clone(); + let drag_connection = self.terminal.clone(); cx.scene.push_mouse_region( MouseRegion::new(view_id, None, visible_bounds) .on_down( @@ -330,7 +330,7 @@ impl Element for TerminalEl { let layout = TerminalLayoutData::new(cx.global::(), &cx.font_cache(), constraint.max); - let terminal = self.connection.upgrade(cx).unwrap().read(cx); + let terminal = self.terminal.upgrade(cx).unwrap().read(cx); let (cursor, cells, rects, highlights) = terminal.render_lock(Some(layout.size.clone()), |content, cursor_text| { @@ -476,7 +476,7 @@ impl Element for TerminalEl { let vertical_scroll = (delta.y() / layout.size.line_height) * ALACRITTY_SCROLL_MULTIPLIER; - self.connection.upgrade(cx.app).map(|terminal| { + self.terminal.upgrade(cx.app).map(|terminal| { terminal .read(cx.app) .scroll(Scroll::Delta(vertical_scroll.round() as i32)); @@ -493,7 +493,7 @@ impl Element for TerminalEl { view.update(cx.app, |view, cx| view.clear_bel(cx)) } - self.connection + self.terminal .upgrade(cx.app) .map(|model_handle| model_handle.read(cx.app)) .map(|term| term.try_keystroke(keystroke)) diff --git a/crates/terminal/src/tests/terminal_test_context.rs b/crates/terminal/src/tests/terminal_test_context.rs index f4678045d4..4066882acf 100644 --- a/crates/terminal/src/tests/terminal_test_context.rs +++ b/crates/terminal/src/tests/terminal_test_context.rs @@ -4,7 +4,7 @@ use gpui::{geometry::vector::vec2f, AppContext, ModelHandle, ReadModelWith, Test use itertools::Itertools; use crate::{ - connection::{DisconnectedPTY, Terminal}, + connection::{Terminal, TerminalBuilder}, terminal_element::TerminalDimensions, DEBUG_CELL_WIDTH, DEBUG_LINE_HEIGHT, DEBUG_TERMINAL_HEIGHT, DEBUG_TERMINAL_WIDTH, }; @@ -25,9 +25,9 @@ impl<'a> TerminalTestContext<'a> { ); let connection = cx.add_model(|cx| { - DisconnectedPTY::new(None, None, None, size_info) + TerminalBuilder::new(None, None, None, size_info) .unwrap() - .connect(cx) + .subscribe(cx) }); TerminalTestContext { cx, connection } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 8462967da5..9bda057e39 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1223,8 +1223,10 @@ impl Workspace { } } - pub fn modal(&self) -> Option<&AnyViewHandle> { - self.modal.as_ref() + pub fn modal(&self) -> Option> { + self.modal + .as_ref() + .and_then(|modal| modal.clone().downcast::()) } pub fn dismiss_modal(&mut self, cx: &mut ViewContext) {