Skip to content

Commit

Permalink
feat(ui): add floating panes (#1066)
Browse files Browse the repository at this point in the history
* basic functionality

* close and reopen scratch terminal working

* embed/float and resize whole tab for floating and static floating panes

* move focus working

* fix focus change in floating panes

* move pane with mouse

* floating z indices

* tests and better resize algorithm

* starting to work on performance

* some performance experimentations

* new render engine

* reverse painters algorithm for floating panes

* fix frame buffering

* improve ux situation

* handle multiple new panes on screen without overlap

* adjust keybindings

* adjust key hints

* fix multiuser frame ui

* fix various floating/multiuser bugs

* remove stuff

* wide characters under floating panes

* fix wide character frame override

* fix non-frame boundaries interactions with floating panes

* fix selection character width

* fix title frame wide char overflow

* fix existing tests

* add tests

* refactor output out of tab

* refactor floating panes out of tab

* refactor tab

* moar refactoring

* refactorings and bring back terminal window title setting

* add frame vte output

* remove more unused stuff

* remove even more unused stuff

* you know the drill

* refactor floating panes and remove more stuffs

* refactor pane grids

* remove unused output caching

* refactor output

* remove unused stuff

* rustfmt

* some formatting

* rustfmt

* reduce clippy to normal

* remove comment

* remove unused

* fix closign pane

* fix tests
  • Loading branch information
imsnif authored Feb 18, 2022
1 parent 10a22c4 commit 821e7cb
Show file tree
Hide file tree
Showing 61 changed files with 6,808 additions and 1,250 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,4 @@ jobs:
- name: Install cargo-make
run: test -x "${HOME}/.cargo/bin/cargo-make" || cargo install --debug cargo-make
- name: Check Lints
run: cargo make clippy -D clippy::all
run: cargo make clippy
48 changes: 48 additions & 0 deletions src/tests/e2e/cases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub const MOVE_FOCUS_IN_PANE_MODE: [u8; 1] = [112]; // p
pub const SPLIT_DOWN_IN_PANE_MODE: [u8; 1] = [100]; // d
pub const SPLIT_RIGHT_IN_PANE_MODE: [u8; 1] = [114]; // r
pub const TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE: [u8; 1] = [102]; // f
pub const TOGGLE_FLOATING_PANES: [u8; 1] = [119]; // w
pub const CLOSE_PANE_IN_PANE_MODE: [u8; 1] = [120]; // x
pub const MOVE_FOCUS_DOWN_IN_PANE_MODE: [u8; 1] = [106]; // j
pub const MOVE_FOCUS_UP_IN_PANE_MODE: [u8; 1] = [107]; // k
Expand Down Expand Up @@ -1664,6 +1665,53 @@ pub fn bracketed_paste() {
assert_snapshot!(last_snapshot);
}

#[test]
#[ignore]
pub fn toggle_floating_panes() {
let fake_win_size = Size {
cols: 120,
rows: 24,
};

let mut test_attempts = 10;
let last_snapshot = loop {
RemoteRunner::kill_running_sessions(fake_win_size);
let mut runner = RemoteRunner::new(fake_win_size).add_step(Step {
name: "Toggle floating panes",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
{
remote_terminal.send_key(&PANE_MODE);
remote_terminal.send_key(&TOGGLE_FLOATING_PANES);
// back to normal mode after split
step_is_complete = true;
}
step_is_complete
},
});
runner.run_all_steps();
let last_snapshot = runner.take_snapshot_after(Step {
name: "Wait for new pane to appear",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.cursor_position_is(33, 7) && remote_terminal.tip_appears() {
// cursor is in the newly opened second pane
step_is_complete = true;
}
step_is_complete
},
});
if runner.test_timed_out && test_attempts > 0 {
test_attempts -= 1;
continue;
} else {
break last_snapshot;
}
};
assert_snapshot!(last_snapshot);
}

#[test]
#[ignore]
pub fn focus_tab_with_layout() {
Expand Down
20 changes: 15 additions & 5 deletions src/tests/e2e/remote_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use zellij_tile::data::Palette;

use zellij_server::panes::TerminalPane;
use zellij_server::panes::{LinkHandler, TerminalPane};
use zellij_utils::pane_size::{Dimension, PaneGeom, Size};
use zellij_utils::{vte, zellij_tile};

Expand All @@ -12,6 +12,9 @@ use std::net::TcpStream;

use std::path::Path;

use std::cell::RefCell;
use std::rc::Rc;

const ZELLIJ_EXECUTABLE_LOCATION: &str = "/usr/src/zellij/x86_64-unknown-linux-musl/release/zellij";
const ZELLIJ_LAYOUT_PATH: &str = "/usr/src/zellij/fixtures/layouts";
const CONNECTION_STRING: &str = "127.0.0.1:2222";
Expand Down Expand Up @@ -141,16 +144,23 @@ fn read_from_channel(
let thread = std::thread::Builder::new()
.name("read_thread".into())
.spawn({
let pane_geom = *pane_geom;
let should_keep_running = should_keep_running.clone();
let channel = channel.clone();
let last_snapshot = last_snapshot.clone();
let cursor_coordinates = cursor_coordinates.clone();
let mut vte_parser = vte::Parser::new();
let mut terminal_output =
TerminalPane::new(0, *pane_geom, Palette::default(), 0, String::new()); // 0 is the pane index
let mut retries_left = 3;
move || {
let mut retries_left = 3;
let mut should_sleep = false;
let mut vte_parser = vte::Parser::new();
let mut terminal_output = TerminalPane::new(
0,
pane_geom,
Palette::default(),
0,
String::new(),
Rc::new(RefCell::new(LinkHandler::new())),
); // 0 is the pane index
loop {
if !should_keep_running.load(Ordering::SeqCst) {
break;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
source: src/tests/e2e/cases.rs
expression: last_snapshot

---
Zellij (e2e-test)  Tab #1
Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
$
│ │
│ │
│ │
│ ┌ Pane #2 ─────────────────────────────────────────────────┐ │
│ │$ █ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ │
│ │
│ │
│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SCROLL  <o> SESSION  <q> QUIT 
Tip: Alt + <n> => new pane. Alt + <[] or hjkl> => navigate. Alt + <+-> => resize pane.
2 changes: 2 additions & 0 deletions zellij-client/src/input_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ impl InputHandler {
}
Action::CloseFocus
| Action::NewPane(_)
| Action::ToggleFloatingPanes
| Action::TogglePaneEmbedOrFloating
| Action::NewTab(_)
| Action::GoToNextTab
| Action::GoToPreviousTab
Expand Down
12 changes: 5 additions & 7 deletions zellij-server/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod os_input_output;
pub mod output;
pub mod panes;
pub mod tab;

Expand Down Expand Up @@ -29,7 +30,6 @@ use crate::{
os_input_output::ServerOsApi,
pty::{pty_thread_main, Pty, PtyInstruction},
screen::{screen_thread_main, ScreenInstruction},
tab::Output,
thread_bus::{Bus, ThreadSenders},
wasm_vm::{wasm_thread_main, PluginInstruction},
};
Expand Down Expand Up @@ -63,7 +63,7 @@ pub enum ServerInstruction {
ClientId,
Option<PluginsConfig>,
),
Render(Option<Output>),
Render(Option<HashMap<ClientId, String>>),
UnblockInputThread,
ClientExit(ClientId),
RemoveClient(ClientId),
Expand Down Expand Up @@ -492,14 +492,12 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
.send_to_plugin(PluginInstruction::RemoveClient(client_id))
.unwrap();
}
ServerInstruction::Render(mut output) => {
ServerInstruction::Render(serialized_output) => {
let client_ids = session_state.read().unwrap().client_ids();
// Here the output is of the type Option<String> sent by screen thread.
// If `Some(_)`- unwrap it and forward it to the clients to render.
// If `None`- Send an exit instruction. This is the case when a user closes the last Tab/Pane.
if let Some(op) = &mut output {
for (client_id, client_render_instruction) in &mut op.client_render_instructions
{
if let Some(output) = &serialized_output {
for (client_id, client_render_instruction) in output.iter() {
os_input.send_to_client(
*client_id,
ServerToClientMsg::Render(client_render_instruction.clone()),
Expand Down
Loading

0 comments on commit 821e7cb

Please sign in to comment.