feat(ui): add the ability to synchronize input sent to panes

This commit is contained in:
Brooks Rady 2021-04-28 16:27:06 +01:00 committed by GitHub
commit 3c431ee625
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 63 additions and 8 deletions

View file

@ -97,6 +97,8 @@ keybinds:
key: [Char: 'r',] key: [Char: 'r',]
- action: [CloseFocus,] - action: [CloseFocus,]
key: [Char: 'x',] key: [Char: 'x',]
- action: [ToggleActiveSyncPanes]
key: [Char: 's']
- action: [ToggleFocusFullscreen,] - action: [ToggleFocusFullscreen,]
key: [Char: 'f',] key: [Char: 'f',]
- action: [FocusPreviousPane,] - action: [FocusPreviousPane,]

View file

@ -65,7 +65,7 @@ impl ZellijPlugin for State {
} else if t.active { } else if t.active {
active_tab_index = t.position; active_tab_index = t.position;
} }
let tab = tab_style(tabname, t.active, t.position); let tab = tab_style(tabname, t.active, t.position, t.is_sync_panes_active);
all_tabs.push(tab); all_tabs.push(tab);
} }
let tab_line = tab_line(all_tabs, active_tab_index, cols); let tab_line = tab_line(all_tabs, active_tab_index, cols);

View file

@ -40,9 +40,18 @@ pub fn non_active_tab(text: String) -> LinePart {
} }
} }
pub fn tab_style(text: String, is_active_tab: bool, position: usize) -> LinePart { pub fn tab_style(
text: String,
is_active_tab: bool,
position: usize,
is_sync_panes_active: bool,
) -> LinePart {
let sync_text = match is_sync_panes_active {
true => " (Sync)".to_string(),
false => "".to_string(),
};
let tab_text = if text.is_empty() { let tab_text = if text.is_empty() {
format!("Tab #{}", position + 1) format!("Tab #{}{}", position + 1, sync_text)
} else { } else {
text text
}; };

View file

@ -68,6 +68,7 @@ pub struct Tab {
max_panes: Option<usize>, max_panes: Option<usize>,
full_screen_ws: PositionAndSize, full_screen_ws: PositionAndSize,
fullscreen_is_active: bool, fullscreen_is_active: bool,
synchronize_is_active: bool,
os_api: Box<dyn OsApi>, os_api: Box<dyn OsApi>,
pub send_pty_instructions: SenderWithContext<PtyInstruction>, pub send_pty_instructions: SenderWithContext<PtyInstruction>,
pub send_plugin_instructions: SenderWithContext<PluginInstruction>, pub send_plugin_instructions: SenderWithContext<PluginInstruction>,
@ -249,6 +250,7 @@ impl Tab {
active_terminal: pane_id, active_terminal: pane_id,
full_screen_ws: *full_screen_ws, full_screen_ws: *full_screen_ws,
fullscreen_is_active: false, fullscreen_is_active: false,
synchronize_is_active: false,
os_api, os_api,
send_app_instructions, send_app_instructions,
send_pty_instructions, send_pty_instructions,
@ -592,6 +594,21 @@ impl Tab {
terminal_output.handle_pty_bytes(bytes); terminal_output.handle_pty_bytes(bytes);
} }
} }
pub fn write_to_terminals_on_current_tab(&mut self, input_bytes: Vec<u8>) {
let pane_ids = self.get_pane_ids();
pane_ids.iter().for_each(|pane_id| match pane_id {
PaneId::Terminal(pid) => {
self.write_to_pane_id(input_bytes.clone(), *pid);
}
PaneId::Plugin(_) => {}
});
}
pub fn write_to_pane_id(&mut self, mut input_bytes: Vec<u8>, pid: RawFd) {
self.os_api
.write_to_tty_stdin(pid, &mut input_bytes)
.expect("failed to write to terminal");
self.os_api.tcdrain(pid).expect("failed to drain terminal");
}
pub fn write_to_active_terminal(&mut self, input_bytes: Vec<u8>) { pub fn write_to_active_terminal(&mut self, input_bytes: Vec<u8>) {
match self.get_active_pane_id() { match self.get_active_pane_id() {
Some(PaneId::Terminal(active_terminal_id)) => { Some(PaneId::Terminal(active_terminal_id)) => {
@ -677,6 +694,12 @@ impl Tab {
pub fn toggle_fullscreen_is_active(&mut self) { pub fn toggle_fullscreen_is_active(&mut self) {
self.fullscreen_is_active = !self.fullscreen_is_active; self.fullscreen_is_active = !self.fullscreen_is_active;
} }
pub fn is_sync_panes_active(&self) -> bool {
self.synchronize_is_active
}
pub fn toggle_sync_panes_is_active(&mut self) {
self.synchronize_is_active = !self.synchronize_is_active;
}
pub fn render(&mut self) { pub fn render(&mut self) {
if self.active_terminal.is_none() { if self.active_terminal.is_none() {
// we might not have an active terminal if we closed the last pane // we might not have an active terminal if we closed the last pane

View file

@ -200,6 +200,7 @@ pub enum ScreenContext {
PageScrollDown, PageScrollDown,
ClearScroll, ClearScroll,
CloseFocusedPane, CloseFocusedPane,
ToggleActiveSyncPanes,
ToggleActiveTerminalFullscreen, ToggleActiveTerminalFullscreen,
SetSelectable, SetSelectable,
SetInvisibleBorders, SetInvisibleBorders,
@ -260,6 +261,7 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::UpdateTabName(_) => ScreenContext::UpdateTabName, ScreenInstruction::UpdateTabName(_) => ScreenContext::UpdateTabName,
ScreenInstruction::TerminalResize => ScreenContext::TerminalResize, ScreenInstruction::TerminalResize => ScreenContext::TerminalResize,
ScreenInstruction::ChangeMode(_) => ScreenContext::ChangeMode, ScreenInstruction::ChangeMode(_) => ScreenContext::ChangeMode,
ScreenInstruction::ToggleActiveSyncPanes => ScreenContext::ToggleActiveSyncPanes,
} }
} }
} }

View file

@ -39,6 +39,8 @@ pub enum Action {
PageScrollDown, PageScrollDown,
/// Toggle between fullscreen focus pane and normal layout. /// Toggle between fullscreen focus pane and normal layout.
ToggleFocusFullscreen, ToggleFocusFullscreen,
/// Toggle between sending text commands to all panes and normal mode.
ToggleActiveSyncPanes,
/// Open a new pane in the specified direction (relative to focus). /// Open a new pane in the specified direction (relative to focus).
/// If no direction is specified, will try to use the biggest available space. /// If no direction is specified, will try to use the biggest available space.
NewPane(Option<Direction>), NewPane(Option<Direction>),

View file

@ -244,6 +244,11 @@ impl InputHandler {
.send(ScreenInstruction::SwitchTabPrev) .send(ScreenInstruction::SwitchTabPrev)
.unwrap(); .unwrap();
} }
Action::ToggleActiveSyncPanes => {
self.send_screen_instructions
.send(ScreenInstruction::ToggleActiveSyncPanes)
.unwrap();
}
Action::CloseTab => { Action::CloseTab => {
self.command_is_executing.closing_pane(); self.command_is_executing.closing_pane();
self.send_screen_instructions self.send_screen_instructions
@ -293,6 +298,7 @@ pub fn get_mode_info(mode: InputMode) -> ModeInfo {
keybinds.push(("d".to_string(), "Down split".to_string())); keybinds.push(("d".to_string(), "Down split".to_string()));
keybinds.push(("r".to_string(), "Right split".to_string())); keybinds.push(("r".to_string(), "Right split".to_string()));
keybinds.push(("x".to_string(), "Close".to_string())); keybinds.push(("x".to_string(), "Close".to_string()));
keybinds.push(("s".to_string(), "Sync".to_string()));
keybinds.push(("f".to_string(), "Fullscreen".to_string())); keybinds.push(("f".to_string(), "Fullscreen".to_string()));
} }
InputMode::Tab => { InputMode::Tab => {

View file

@ -320,10 +320,11 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
command_is_executing.done_opening_new_pane(); command_is_executing.done_opening_new_pane();
} }
ScreenInstruction::WriteCharacter(bytes) => { ScreenInstruction::WriteCharacter(bytes) => {
screen let active_tab = screen.get_active_tab_mut().unwrap();
.get_active_tab_mut() match active_tab.is_sync_panes_active() {
.unwrap() true => active_tab.write_to_terminals_on_current_tab(bytes),
.write_to_active_terminal(bytes); false => active_tab.write_to_active_terminal(bytes),
}
} }
ScreenInstruction::ResizeLeft => { ScreenInstruction::ResizeLeft => {
screen.get_active_tab_mut().unwrap().resize_left(); screen.get_active_tab_mut().unwrap().resize_left();
@ -444,6 +445,13 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
ScreenInstruction::ChangeMode(mode_info) => { ScreenInstruction::ChangeMode(mode_info) => {
screen.change_mode(mode_info); screen.change_mode(mode_info);
} }
ScreenInstruction::ToggleActiveSyncPanes => {
screen
.get_active_tab_mut()
.unwrap()
.toggle_sync_panes_is_active();
screen.update_tabs();
}
ScreenInstruction::Quit => { ScreenInstruction::Quit => {
break; break;
} }

View file

@ -51,6 +51,7 @@ pub enum ScreenInstruction {
NewTab(RawFd), NewTab(RawFd),
SwitchTabNext, SwitchTabNext,
SwitchTabPrev, SwitchTabPrev,
ToggleActiveSyncPanes,
CloseTab, CloseTab,
GoToTab(u32), GoToTab(u32),
UpdateTabName(Vec<u8>), UpdateTabName(Vec<u8>),
@ -285,7 +286,7 @@ impl Screen {
self.update_tabs(); self.update_tabs();
} }
fn update_tabs(&self) { pub fn update_tabs(&self) {
let mut tab_data = vec![]; let mut tab_data = vec![];
let active_tab_index = self.active_tab_index.unwrap(); let active_tab_index = self.active_tab_index.unwrap();
for tab in self.tabs.values() { for tab in self.tabs.values() {
@ -293,6 +294,7 @@ impl Screen {
position: tab.position, position: tab.position,
name: tab.name.clone(), name: tab.name.clone(),
active: active_tab_index == tab.index, active: active_tab_index == tab.index,
is_sync_panes_active: tab.is_sync_panes_active(),
}); });
} }
self.send_plugin_instructions self.send_plugin_instructions

View file

@ -83,6 +83,7 @@ pub struct TabInfo {
pub position: usize, pub position: usize,
pub name: String, pub name: String,
pub active: bool, pub active: bool,
pub is_sync_panes_active: bool,
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]