7ed834bd79
Closes #23006 This PR should have been split into two, but since the changes are related, I merged them into one. 1. On load, the title bar actions and bottom bar toggles are unresponsive until the center pane is clicked. This happens because the terminal captures focus (even if it's closed) long after the workspace sets focus to itself during loading. The issue was in the `focus_view` call used in the `new` method of `TerminalPanel`. Since new terminal views can be created behind the scenes (i.e., without the terminal being visible to the user), we shouldn't handle focus for the terminal in this case. Removing `focus_view` from the `new` method has no impact on the existing terminal focusing logic. I've tested scenarios such as creating new terminals, splitting terminals, zooming, etc., and everything works as expected. 2. Currently, on load, docked terminals do not automatically focus when they are only visible item to the user. This PR implements it. Before/After: 1. When only the dock terminal is visible on load. Terminal is focused. <img src="https://github.com/user-attachments/assets/af8848aa-ccb5-4a3b-b2c6-486e8d588f09" alt="image" height="280px" /> <img src="https://github.com/user-attachments/assets/8f76ca2e-de29-4cc0-979b-749b50a00bbd" alt="image" height="280px" /> 2. When other items are visible along with the dock terminal on load. Editor is focused. <img src="https://github.com/user-attachments/assets/d3248272-a75d-4763-9e99-defb8a369b68" alt="image" height="280px" /> <img src="https://github.com/user-attachments/assets/fba5184e-1ab2-406c-9669-b141aaf1c32f" alt="image" height="280px" /> 3. Multiple tabs along with split panes. Last terminal is focused. <img src="https://github.com/user-attachments/assets/7a10c3cf-8bb3-4b88-aacc-732b678bee19" alt="image" height="270px" /> <img src="https://github.com/user-attachments/assets/4d16e98f-9d7a-45f6-8701-d6652e411d3b" alt="image" height="270px" /> Future: When a docked terminal is in a zoomed state and Zed is loaded, we should prioritize focusing on the terminal over the active item (e.g., an editor) behind it. This hasn't been implemented in this PR because the zoomed state during the load function is stale. The correct state is received later via the workspace. I'm still investigating where exactly this should be handled, so this will be a separate PR. cc: @SomeoneToIgnore Release Notes: - Fixed unresponsive buttons on load until the center pane is clicked. - Added auto-focus for the docked terminal on load when no other item is focused. |
||
---|---|---|
.. | ||
scripts | ||
src | ||
Cargo.toml | ||
LICENSE-GPL | ||
README.md |
Design notes:
This crate is split into two conceptual halves:
- The terminal.rs file and the src/mappings/ folder, these contain the code for interacting with Alacritty and maintaining the pty event loop. Some behavior in this file is constrained by terminal protocols and standards. The Zed init function is also placed here.
- Everything else. These other files integrate the
Terminal
struct created in terminal.rs into the rest of GPUI. The main entry point for GPUI is the terminal_view.rs file and the modal.rs file.
ttys are created externally, and so can fail in unexpected ways. However, GPUI currently does not have an API for models than can fail to instantiate. TerminalBuilder
solves this by using Rust's type system to split tty instantiation into a 2 step process: first attempt to create the file handles with TerminalBuilder::new()
, check the result, then call TerminalBuilder::subscribe(cx)
from within a model context.
The TerminalView struct abstracts over failed and successful terminals, passing focus through to the associated view and allowing clients to build a terminal without worrying about errors.
#Input
There are currently many distinct paths for getting keystrokes to the terminal:
-
Terminal specific characters and bindings. Things like ctrl-a mapping to ASCII control character 1, ANSI escape codes associated with the function keys, etc. These are caught with a raw key-down handler in the element and are processed immediately. This is done with the
try_keystroke()
method on Terminal -
GPU Action handlers. GPUI clobbers a few vital keys by adding bindings to them in the global context. These keys are synthesized and then dispatched through the same
try_keystroke()
API as the above mappings -
IME text. When the special character mappings fail, we pass the keystroke back to GPUI to hand it to the IME system. This comes back to us in the
View::replace_text_in_range()
method, and we then send that to the terminal directly, bypassingtry_keystroke()
. -
Pasted text has a separate pathway.
Generally, there's a distinction between 'keystrokes that need to be mapped' and 'strings which need to be written'. I've attempted to unify these under the '.try_keystroke()' API and the .input()
API (which try_keystroke uses) so we have consistent input handling across the terminal