aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDenis Maximov <denismaximov98@gmail.com>2021-02-25 09:45:23 +0200
committerGitHub <noreply@github.com>2021-02-25 09:45:23 +0200
commitf9441bce99a80b95204ea2016481c24cee909d51 (patch)
tree0fd91434af3477f9b7c16a61b822537a18abcca2
parentMake fullscreen panes not override the status bar and strider (#209) (diff)
parentadd basic tab bar #166 (diff)
downloadsl0ppij-f9441bce99a80b95204ea2016481c24cee909d51.tar.gz
sl0ppij-f9441bce99a80b95204ea2016481c24cee909d51.tar.xz
sl0ppij-f9441bce99a80b95204ea2016481c24cee909d51.zip
Merge pull request #204 from jcaplan/tab-bar-166
add tab toolbar
-rw-r--r--Cargo.lock8
-rw-r--r--Cargo.toml1
-rw-r--r--assets/layouts/default.yaml4
-rwxr-xr-xbuild-all.sh18
-rw-r--r--build.rs1
-rw-r--r--default-tiles/tab-bar/.cargo/config.toml2
-rw-r--r--default-tiles/tab-bar/Cargo.toml10
l---------default-tiles/tab-bar/LICENSE.md1
-rw-r--r--default-tiles/tab-bar/src/main.rs40
-rw-r--r--src/client/tab.rs3
-rw-r--r--src/common/errors.rs4
-rw-r--r--src/common/input/actions.rs1
-rw-r--r--src/common/input/handler.rs5
-rw-r--r--src/common/input/keybinds.rs3
-rw-r--r--src/common/mod.rs14
-rw-r--r--src/common/screen.rs79
-rw-r--r--src/common/wasm_vm.rs1
-rw-r--r--src/main.rs1
-rw-r--r--zellij-tile/src/lib.rs10
19 files changed, 182 insertions, 24 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1275abf8..fcb87258 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1580,6 +1580,14 @@ dependencies = [
]
[[package]]
+name = "tab-bar"
+version = "0.1.0"
+dependencies = [
+ "colored",
+ "zellij-tile",
+]
+
+[[package]]
name = "tap"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 3782c73f..4dbe24ca 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -53,6 +53,7 @@ members = [
"zellij-tile",
"default-tiles/status-bar",
"default-tiles/strider",
+ "default-tiles/tab-bar",
]
[profile.release]
diff --git a/assets/layouts/default.yaml b/assets/layouts/default.yaml
index b8667b8d..f06bab17 100644
--- a/assets/layouts/default.yaml
+++ b/assets/layouts/default.yaml
@@ -2,6 +2,10 @@
direction: Horizontal
parts:
- direction: Vertical
+ split_size:
+ Fixed: 1
+ plugin: tab-bar
+ - direction: Vertical
expansion_boundary: true
- direction: Vertical
split_size:
diff --git a/build-all.sh b/build-all.sh
index 3243ea45..b3094bf2 100755
--- a/build-all.sh
+++ b/build-all.sh
@@ -1,19 +1,25 @@
#!/bin/sh
+total=6
+
# This is temporary while https://github.com/rust-lang/cargo/issues/7004 is open
-echo "Building zellij-tile (1/5)..."
+echo "Building zellij-tile (1/$total)..."
cd zellij-tile
cargo build --release
-echo "Building status-bar (2/5)..."
+echo "Building status-bar (2/$total)..."
cd ../default-tiles/status-bar
cargo build --release
-echo "Building strider (3/5)..."
+echo "Building strider (3/$total)..."
cd ../strider
cargo build --release
-echo "Optimising WASM executables (4/5)..."
+echo "Building tab-bar (4/$total)..."
+cd ../tab-bar
+cargo build --release
+echo "Optimising WASM executables (5/$total)..."
cd ../..
wasm-opt -O target/wasm32-wasi/release/status-bar.wasm -o target/status-bar.wasm || cp target/wasm32-wasi/release/status-bar.wasm target/status-bar.wasm
wasm-opt -O target/wasm32-wasi/release/strider.wasm -o target/strider.wasm || cp target/wasm32-wasi/release/strider.wasm target/strider.wasm
-echo "Building zellij (5/5)..."
-cargo build $@ \ No newline at end of file
+wasm-opt -O target/wasm32-wasi/release/tab-bar.wasm -o assets/plugins/tab-bar.wasm || cp target/wasm32-wasi/release/tab-bar.wasm target/tab-bar.wasm
+echo "Building zellij (6/$total)..."
+cargo build $@
diff --git a/build.rs b/build.rs
index a9a8c808..e7d2f36a 100644
--- a/build.rs
+++ b/build.rs
@@ -42,6 +42,7 @@ fn main() {
let project_dirs = ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap();
let data_dir = project_dirs.data_dir();
drop(fs::remove_file(data_dir.join("plugins/status-bar.wasm")));
+ drop(fs::remove_file(data_dir.join("plugins/tab-bar.wasm")));
drop(fs::remove_file(data_dir.join("plugins/strider.wasm")));
drop(fs::remove_file(data_dir.join("layouts/default.yaml")));
drop(fs::remove_file(data_dir.join("layouts/strider.yaml")));
diff --git a/default-tiles/tab-bar/.cargo/config.toml b/default-tiles/tab-bar/.cargo/config.toml
new file mode 100644
index 00000000..6b77899c
--- /dev/null
+++ b/default-tiles/tab-bar/.cargo/config.toml
@@ -0,0 +1,2 @@
+[build]
+target = "wasm32-wasi"
diff --git a/default-tiles/tab-bar/Cargo.toml b/default-tiles/tab-bar/Cargo.toml
new file mode 100644
index 00000000..1a0c5f78
--- /dev/null
+++ b/default-tiles/tab-bar/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "tab-bar"
+version = "0.1.0"
+authors = ["Jonah Caplan <jonahcaplan@gmail.com>"]
+edition = "2018"
+license = "MIT"
+
+[dependencies]
+colored = "2"
+zellij-tile = { path = "../../zellij-tile" }
diff --git a/default-tiles/tab-bar/LICENSE.md b/default-tiles/tab-bar/LICENSE.md
new file mode 120000
index 00000000..f0608a63
--- /dev/null
+++ b/default-tiles/tab-bar/LICENSE.md
@@ -0,0 +1 @@
+../../LICENSE.md \ No newline at end of file
diff --git a/default-tiles/tab-bar/src/main.rs b/default-tiles/tab-bar/src/main.rs
new file mode 100644
index 00000000..78a57aca
--- /dev/null
+++ b/default-tiles/tab-bar/src/main.rs
@@ -0,0 +1,40 @@
+use colored::*;
+use zellij_tile::*;
+
+#[derive(Default)]
+struct State {
+ active_tab_index: usize,
+ num_tabs: usize,
+}
+
+register_tile!(State);
+
+impl ZellijTile for State {
+ fn init(&mut self) {
+ set_selectable(false);
+ set_invisible_borders(true);
+ set_max_height(1);
+ self.active_tab_index = 0;
+ self.num_tabs = 0;
+ }
+
+ fn draw(&mut self, _rows: usize, _cols: usize) {
+ let mut s = String::new();
+ let active_tab = self.active_tab_index + 1;
+ for i in 1..=self.num_tabs {
+ let tab;
+ if i == active_tab {
+ tab = format!("*{} ", i).black().bold().on_magenta();
+ } else {
+ tab = format!("-{} ", i).white();
+ }
+ s = format!("{}{}", s, tab);
+ }
+ println!("Tabs: {}\u{1b}[40m\u{1b}[0K", s);
+ }
+
+ fn update_tabs(&mut self, active_tab_index: usize, num_tabs: usize) {
+ self.active_tab_index = active_tab_index;
+ self.num_tabs = num_tabs;
+ }
+}
diff --git a/src/client/tab.rs b/src/client/tab.rs
index bdbf7db1..bf096367 100644
--- a/src/client/tab.rs
+++ b/src/client/tab.rs
@@ -50,6 +50,7 @@ fn split_horizontally_with_gap(rect: &PositionAndSize) -> (PositionAndSize, Posi
pub struct Tab {
pub index: usize,
+ pub position: usize,
panes: BTreeMap<PaneId, Box<dyn Pane>>,
panes_to_hide: HashSet<PaneId>,
active_terminal: Option<PaneId>,
@@ -168,6 +169,7 @@ impl Tab {
// FIXME: Too many arguments here! Maybe bundle all of the senders for the whole program in a struct?
pub fn new(
index: usize,
+ position: usize,
full_screen_ws: &PositionAndSize,
mut os_api: Box<dyn OsApi>,
send_pty_instructions: SenderWithContext<PtyInstruction>,
@@ -191,6 +193,7 @@ impl Tab {
};
Tab {
index,
+ position,
panes,
max_panes,
panes_to_hide: HashSet::new(),
diff --git a/src/common/errors.rs b/src/common/errors.rs
index 3d105dbc..03aec714 100644
--- a/src/common/errors.rs
+++ b/src/common/errors.rs
@@ -197,6 +197,7 @@ pub enum ScreenContext {
SwitchTabNext,
SwitchTabPrev,
CloseTab,
+ GoToTab,
}
impl From<&ScreenInstruction> for ScreenContext {
@@ -234,6 +235,7 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::SwitchTabNext => ScreenContext::SwitchTabNext,
ScreenInstruction::SwitchTabPrev => ScreenContext::SwitchTabPrev,
ScreenInstruction::CloseTab => ScreenContext::CloseTab,
+ ScreenInstruction::GoToTab(_) => ScreenContext::GoToTab,
}
}
}
@@ -277,6 +279,7 @@ pub enum PluginContext {
GlobalInput,
Unload,
Quit,
+ Tabs,
}
impl From<&PluginInstruction> for PluginContext {
@@ -288,6 +291,7 @@ impl From<&PluginInstruction> for PluginContext {
PluginInstruction::GlobalInput(_) => PluginContext::GlobalInput,
PluginInstruction::Unload(_) => PluginContext::Unload,
PluginInstruction::Quit => PluginContext::Quit,
+ PluginInstruction::UpdateTabs(..) => PluginContext::Tabs,
}
}
}
diff --git a/src/common/input/actions.rs b/src/common/input/actions.rs
index eee8b1b6..fe5e2dad 100644
--- a/src/common/input/actions.rs
+++ b/src/common/input/actions.rs
@@ -47,4 +47,5 @@ pub enum Action {
GoToPreviousTab,
/// Close the current tab.
CloseTab,
+ GoToTab(u32),
}
diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs
index 2a8c3bfa..5c4f1971 100644
--- a/src/common/input/handler.rs
+++ b/src/common/input/handler.rs
@@ -227,6 +227,11 @@ impl InputHandler {
.unwrap();
self.command_is_executing.wait_until_pane_is_closed();
}
+ Action::GoToTab(i) => {
+ self.send_screen_instructions
+ .send(ScreenInstruction::GoToTab(i))
+ .unwrap();
+ }
Action::NoOp => {}
}
diff --git a/src/common/input/keybinds.rs b/src/common/input/keybinds.rs
index c573a557..e56488b4 100644
--- a/src/common/input/keybinds.rs
+++ b/src/common/input/keybinds.rs
@@ -133,6 +133,9 @@ fn get_defaults_for_mode(mode: &InputMode) -> Result<ModeKeybinds, String> {
Key::Ctrl('g'),
vec![Action::SwitchToMode(InputMode::Normal)],
);
+ for i in '1'..='9' {
+ defaults.insert(Key::Char(i), vec![Action::GoToTab(i.to_digit(10).unwrap())]);
+ }
defaults.insert(Key::Esc, vec![Action::SwitchToMode(InputMode::Command)]);
}
InputMode::Scroll => {
diff --git a/src/common/mod.rs b/src/common/mod.rs
index 8f37994f..f78303ea 100644
--- a/src/common/mod.rs
+++ b/src/common/mod.rs
@@ -416,6 +416,9 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
screen.apply_layout(layout, new_pane_pids);
command_is_executing.done_opening_new_pane();
}
+ ScreenInstruction::GoToTab(tab_index) => {
+ screen.go_to_tab(tab_index as usize)
+ }
ScreenInstruction::Quit => {
break;
}
@@ -511,6 +514,17 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
buf_tx.send(wasi_stdout(&plugin_env.wasi_env)).unwrap();
}
+ PluginInstruction::UpdateTabs(active_tab_index, num_tabs) => {
+ for (instance, _) in plugin_map.values() {
+ let handler = instance.exports.get_function("update_tabs").unwrap();
+ handler
+ .call(&[
+ Value::I32(active_tab_index as i32),
+ Value::I32(num_tabs as i32),
+ ])
+ .unwrap();
+ }
+ }
// FIXME: Deduplicate this with the callback below!
PluginInstruction::Input(pid, input_bytes) => {
let (instance, plugin_env) = plugin_map.get(&pid).unwrap();
diff --git a/src/common/screen.rs b/src/common/screen.rs
index 1e04e231..7747b43b 100644
--- a/src/common/screen.rs
+++ b/src/common/screen.rs
@@ -45,6 +45,7 @@ pub enum ScreenInstruction {
SwitchTabNext,
SwitchTabPrev,
CloseTab,
+ GoToTab(u32),
}
/// A [`Screen`] holds multiple [`Tab`]s, each one holding multiple [`panes`](crate::client::panes).
@@ -98,8 +99,10 @@ impl Screen {
/// [pane](crate::client::panes) with PTY file descriptor `pane_id`.
pub fn new_tab(&mut self, pane_id: RawFd) {
let tab_index = self.get_new_tab_index();
+ let position = self.tabs.len();
let tab = Tab::new(
tab_index,
+ position,
&self.full_screen_ws,
self.os_api.clone(),
self.send_pty_instructions.clone(),
@@ -110,6 +113,7 @@ impl Screen {
);
self.active_tab_index = Some(tab_index);
self.tabs.insert(tab_index, tab);
+ self.update_tabs();
self.render();
}
@@ -126,34 +130,52 @@ impl Screen {
/// Sets this [`Screen`]'s active [`Tab`] to the next tab.
pub fn switch_tab_next(&mut self) {
- let active_tab_id = self.get_active_tab().unwrap().index;
- let tab_ids: Vec<usize> = self.tabs.keys().copied().collect();
- let first_tab = tab_ids.get(0).unwrap();
- let active_tab_id_position = tab_ids.iter().position(|id| id == &active_tab_id).unwrap();
- if let Some(next_tab) = tab_ids.get(active_tab_id_position + 1) {
- self.active_tab_index = Some(*next_tab);
- } else {
- self.active_tab_index = Some(*first_tab);
+ let active_tab_pos = self.get_active_tab().unwrap().position;
+ let new_tab_pos = (active_tab_pos + 1) % self.tabs.len();
+
+ for tab in self.tabs.values() {
+ if tab.position == new_tab_pos {
+ self.active_tab_index = Some(tab.index);
+ break;
+ }
}
+ self.update_tabs();
self.render();
}
/// Sets this [`Screen`]'s active [`Tab`] to the previous tab.
pub fn switch_tab_prev(&mut self) {
- let active_tab_id = self.get_active_tab().unwrap().index;
- let tab_ids: Vec<usize> = self.tabs.keys().copied().collect();
- let first_tab = tab_ids.get(0).unwrap();
- let last_tab = tab_ids.last().unwrap();
-
- let active_tab_id_position = tab_ids.iter().position(|id| id == &active_tab_id).unwrap();
- if active_tab_id == *first_tab {
- self.active_tab_index = Some(*last_tab)
- } else if let Some(prev_tab) = tab_ids.get(active_tab_id_position - 1) {
- self.active_tab_index = Some(*prev_tab)
+ let active_tab_pos = self.get_active_tab().unwrap().position;
+ let new_tab_pos = if active_tab_pos == 0 {
+ self.tabs.len() - 1
+ } else {
+ active_tab_pos - 1
+ };
+ for tab in self.tabs.values() {
+ if tab.position == new_tab_pos {
+ self.active_tab_index = Some(tab.index);
+ break;
+ }
}
+ self.update_tabs();
self.render();
}
+ pub fn go_to_tab(&mut self, mut tab_index: usize) {
+ tab_index -= 1;
+ let active_tab = self.get_active_tab().unwrap();
+ match self.tabs.values().find(|t| t.position == tab_index) {
+ Some(t) => {
+ if t.index != active_tab.index {
+ self.active_tab_index = Some(t.index);
+ self.update_tabs();
+ self.render();
+ }
+ }
+ None => {}
+ }
+ }
+
/// Closes this [`Screen`]'s active [`Tab`], exiting the application if it happens
/// to be the last tab.
pub fn close_tab(&mut self) {
@@ -174,6 +196,13 @@ impl Screen {
self.send_app_instructions
.send(AppInstruction::Exit)
.unwrap();
+ } else {
+ for t in self.tabs.values_mut() {
+ if t.position > active_tab.position {
+ t.position -= 1;
+ }
+ }
+ self.update_tabs();
}
}
@@ -213,8 +242,10 @@ impl Screen {
/// and switching to it.
pub fn apply_layout(&mut self, layout: Layout, new_pids: Vec<RawFd>) {
let tab_index = self.get_new_tab_index();
+ let position = self.tabs.len();
let mut tab = Tab::new(
tab_index,
+ position,
&self.full_screen_ws,
self.os_api.clone(),
self.send_pty_instructions.clone(),
@@ -226,5 +257,17 @@ impl Screen {
tab.apply_layout(layout, new_pids);
self.active_tab_index = Some(tab_index);
self.tabs.insert(tab_index, tab);
+ self.update_tabs();
+ }
+
+ fn update_tabs(&self) {
+ if let Some(active_tab) = self.get_active_tab() {
+ self.send_plugin_instructions
+ .send(PluginInstruction::UpdateTabs(
+ active_tab.position,
+ self.tabs.len(),
+ ))
+ .unwrap();
+ }
}
}
diff --git a/src/common/wasm_vm.rs b/src/common/wasm_vm.rs
index 70808b5d..5d4b1e30 100644
--- a/src/common/wasm_vm.rs
+++ b/src/common/wasm_vm.rs
@@ -18,6 +18,7 @@ pub enum PluginInstruction {
Input(u32, Vec<u8>), // plugin id, input bytes
GlobalInput(Vec<u8>), // input bytes
Unload(u32),
+ UpdateTabs(usize, usize), // num tabs, active tab
Quit,
}
diff --git a/src/main.rs b/src/main.rs
index 91a7b422..c4123e55 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -33,6 +33,7 @@ pub fn main() {
let data_dir = project_dirs.data_dir();
let assets = asset_map! {
"target/status-bar.wasm" => "plugins/status-bar.wasm",
+ "target/tab-bar.wasm" => "plugins/tab-bar.wasm",
"target/strider.wasm" => "plugins/strider.wasm",
"assets/layouts/default.yaml" => "layouts/default.yaml",
"assets/layouts/strider.yaml" => "layouts/strider.yaml",
diff --git a/zellij-tile/src/lib.rs b/zellij-tile/src/lib.rs
index 1ed56b89..b5ba63fe 100644
--- a/zellij-tile/src/lib.rs
+++ b/zellij-tile/src/lib.rs
@@ -7,6 +7,7 @@ pub trait ZellijTile {
fn draw(&mut self, rows: usize, cols: usize) {}
fn handle_key(&mut self, key: Key) {}
fn handle_global_key(&mut self, key: Key) {}
+ fn update_tabs(&mut self, active_tab_index: usize, num_active_tabs: usize) {}
}
#[macro_export]
@@ -42,5 +43,14 @@ macro_rules! register_tile {
state.borrow_mut().handle_global_key($crate::get_key());
});
}
+
+ #[no_mangle]
+ pub fn update_tabs(active_tab_index: i32, num_active_tabs: i32) {
+ STATE.with(|state| {
+ state
+ .borrow_mut()
+ .update_tabs(active_tab_index as usize, num_active_tabs as usize);
+ })
+ }
};
}