* Implement initial structs from spec * kdl configuration unmarshalling * typo text styling * remove is_selected toggle * incorporate new status bar ui into theming * improve test coverage of config behavior * tab bar correction * correct also compact bar * remove spacing between table columns * refactor table styling * use text_unselected.emphasis_1 for keygroup sep * fix tab bar more text * repair field flattening for theme * remove extra styling KDL node * update tests * updated selected text conversion * padding for header bar * minor corrections for existing themes * background handling * compact bar corrections * properly handle opaque method to activate background * update newer plugins to use styling struct * correct omission of selected state * fix: bold typeface for text elements * fix: fg -> white for list_unselected conversion * fix: emphasis and opacity handling for nested_list * correct stylings in the session-manager * fix emphases translation for table component * correct emphasis for run instructions * correct frame_highlight translation for old themes * provide missing implementation of frame_highlight * fencepost emphasis color names * Set a pseudo-None for frame_unselected in old theme conversion * correct alternating bg for simplified-ui * update snapshots * fix inner text padding and errorneous snapshots * suppress warning about deprecated usage of palette * remove unused import * feat(plugins): API to change floating pane coordinates (#3958) * basic functionality through the cli * added to plugin api * add display area and viewport size to TabInfo * fix tests and add new one * some cleanups * refactor: extract pane_id parsing logic * style(fmt): rustfmt * docs(changelog): floating pane coordinate chagne API * fix(tiled-panes): opening panes from the cli (#3963) * feat(plugins): add `PastedText` Event (#3962) * working with text paste * handle utf8 conversion error * feat(plugins): add PastedText Event * docs(changelog): plugins pasted text event * black for table opaque background * properly apply opacity to table * correct padding for explicit width ribbons * feat(plugins): Allow opening panes near plugin (#3966) * added command + terminal variants * added editor variant * style(fmt): rustfmt * docs(changelog): plugin apis to open panes near plugin * feat(plugins): send info about $EDITOR and $SHELL (#3971) * feat(plugins): send info about $EDITOR and $SHELL * fix(e2e): snapshot update * docs(changelog): plugin editor and shell info * fix(floating-panes): when changing coordinates, if a pane is not floating - make it floating (#3972) * fix(panes): when changing floating pane coordinates, if the pane is not floating, float it * style(fmt): rustfmt * docs(changelog): floating pane coordinate fix * fix(break-pane): strip logical position when inserting pane to new tab (#3973) * docs(changelog): logical position fix * Optional frame_unselected theme * fixture with correct width to account for arrow padding * update snapshot and rustfmt --------- Co-authored-by: Aram Drevekenin <aram@poor.dev>
140 lines
4.4 KiB
Rust
140 lines
4.4 KiB
Rust
use crate::{line::tab_separator, LinePart};
|
|
use ansi_term::{ANSIString, ANSIStrings};
|
|
use unicode_width::UnicodeWidthStr;
|
|
use zellij_tile::prelude::*;
|
|
use zellij_tile_utils::style;
|
|
|
|
fn cursors(
|
|
focused_clients: &[ClientId],
|
|
multiplayer_colors: MultiplayerColors,
|
|
) -> (Vec<ANSIString>, usize) {
|
|
// cursor section, text length
|
|
let mut len = 0;
|
|
let mut cursors = vec![];
|
|
for client_id in focused_clients.iter() {
|
|
if let Some(color) = client_id_to_colors(*client_id, multiplayer_colors) {
|
|
cursors.push(style!(color.1, color.0).paint(" "));
|
|
len += 1;
|
|
}
|
|
}
|
|
(cursors, len)
|
|
}
|
|
|
|
pub fn render_tab(
|
|
text: String,
|
|
tab: &TabInfo,
|
|
is_alternate_tab: bool,
|
|
palette: Styling,
|
|
separator: &str,
|
|
) -> LinePart {
|
|
let focused_clients = tab.other_focused_clients.as_slice();
|
|
let separator_width = separator.width();
|
|
|
|
let alternate_tab_color = if is_alternate_tab {
|
|
palette.ribbon_unselected.emphasis_1
|
|
} else {
|
|
palette.ribbon_unselected.background
|
|
};
|
|
let background_color = if tab.active {
|
|
palette.ribbon_selected.background
|
|
} else if is_alternate_tab {
|
|
alternate_tab_color
|
|
} else {
|
|
palette.ribbon_unselected.background
|
|
};
|
|
let foreground_color = if tab.active {
|
|
palette.ribbon_selected.base
|
|
} else {
|
|
palette.ribbon_unselected.base
|
|
};
|
|
|
|
let separator_fill_color = palette.text_unselected.background;
|
|
let left_separator = style!(separator_fill_color, background_color).paint(separator);
|
|
let mut tab_text_len = text.width() + (separator_width * 2) + 2; // +2 for padding
|
|
let tab_styled_text = style!(foreground_color, background_color)
|
|
.bold()
|
|
.paint(format!(" {} ", text));
|
|
|
|
let right_separator = style!(background_color, separator_fill_color).paint(separator);
|
|
let tab_styled_text = if !focused_clients.is_empty() {
|
|
let (cursor_section, extra_length) =
|
|
cursors(focused_clients, palette.multiplayer_user_colors);
|
|
tab_text_len += extra_length + 2; // 2 for cursor_beginning and cursor_end
|
|
let mut s = String::new();
|
|
let cursor_beginning = style!(foreground_color, background_color)
|
|
.bold()
|
|
.paint("[")
|
|
.to_string();
|
|
let cursor_section = ANSIStrings(&cursor_section).to_string();
|
|
let cursor_end = style!(foreground_color, background_color)
|
|
.bold()
|
|
.paint("]")
|
|
.to_string();
|
|
s.push_str(&left_separator.to_string());
|
|
s.push_str(&tab_styled_text.to_string());
|
|
s.push_str(&cursor_beginning);
|
|
s.push_str(&cursor_section);
|
|
s.push_str(&cursor_end);
|
|
s.push_str(&right_separator.to_string());
|
|
s
|
|
} else {
|
|
ANSIStrings(&[left_separator, tab_styled_text, right_separator]).to_string()
|
|
};
|
|
|
|
LinePart {
|
|
part: tab_styled_text,
|
|
len: tab_text_len,
|
|
tab_index: Some(tab.position),
|
|
}
|
|
}
|
|
|
|
pub fn tab_style(
|
|
mut tabname: String,
|
|
tab: &TabInfo,
|
|
mut is_alternate_tab: bool,
|
|
palette: Styling,
|
|
capabilities: PluginCapabilities,
|
|
) -> LinePart {
|
|
let separator = tab_separator(capabilities);
|
|
|
|
if tab.is_fullscreen_active {
|
|
tabname.push_str(" (FULLSCREEN)");
|
|
} else if tab.is_sync_panes_active {
|
|
tabname.push_str(" (SYNC)");
|
|
}
|
|
// we only color alternate tabs differently if we can't use the arrow fonts to separate them
|
|
if !capabilities.arrow_fonts {
|
|
is_alternate_tab = false;
|
|
}
|
|
|
|
render_tab(tabname, tab, is_alternate_tab, palette, separator)
|
|
}
|
|
|
|
pub(crate) fn get_tab_to_focus(
|
|
tab_line: &[LinePart],
|
|
active_tab_idx: usize,
|
|
mouse_click_col: usize,
|
|
) -> Option<usize> {
|
|
let clicked_line_part = get_clicked_line_part(tab_line, mouse_click_col)?;
|
|
let clicked_tab_idx = clicked_line_part.tab_index?;
|
|
// tabs are indexed starting from 1 so we need to add 1
|
|
let clicked_tab_idx = clicked_tab_idx + 1;
|
|
if clicked_tab_idx != active_tab_idx {
|
|
return Some(clicked_tab_idx);
|
|
}
|
|
None
|
|
}
|
|
|
|
pub(crate) fn get_clicked_line_part(
|
|
tab_line: &[LinePart],
|
|
mouse_click_col: usize,
|
|
) -> Option<&LinePart> {
|
|
let mut len = 0;
|
|
for tab_line_part in tab_line {
|
|
if mouse_click_col >= len && mouse_click_col < len + tab_line_part.len {
|
|
return Some(tab_line_part);
|
|
}
|
|
len += tab_line_part.len;
|
|
}
|
|
None
|
|
}
|