feat: show horizontal scroll title
Show a horizontal scroll title, with arrows, if available
This commit is contained in:
@@ -201,6 +201,7 @@ impl<T> StatefulList<T> {
|
||||
}
|
||||
|
||||
/// Return the current status of the select list, e.g. 2/5,
|
||||
/// MAYBE add up down arrows, check if at start or end etc
|
||||
pub fn get_state_title(&self) -> String {
|
||||
if self.items.is_empty() {
|
||||
String::new()
|
||||
@@ -597,13 +598,12 @@ impl LogsTz {
|
||||
/// stateful list dependent on whether the timestamp is in the HashSet or not
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Logs {
|
||||
// should just be list of spans?
|
||||
lines: StatefulList<Text<'static>>,
|
||||
tz: HashSet<LogsTz>,
|
||||
// could probably be a u16
|
||||
offset: u16,
|
||||
max_log_len: usize,
|
||||
adjusted_max_width: usize,
|
||||
adjust_max_width_text_len: usize,
|
||||
}
|
||||
|
||||
impl Default for Logs {
|
||||
@@ -615,6 +615,7 @@ impl Default for Logs {
|
||||
tz: HashSet::new(),
|
||||
offset: 0,
|
||||
adjusted_max_width: 0,
|
||||
adjust_max_width_text_len: 0,
|
||||
max_log_len: 0,
|
||||
}
|
||||
}
|
||||
@@ -629,10 +630,24 @@ impl Logs {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO test me!
|
||||
/// If scrolling horiztonally along the logs, display a counter of the position in the in the scroll, `x/y`
|
||||
pub fn get_scroll_title(&self) -> Option<String> {
|
||||
if self.offset > 0 {
|
||||
Some(format!(" {}/{} ", self.offset, self.adjusted_max_width))
|
||||
pub fn get_scroll_title(&mut self, width: u16) -> Option<String> {
|
||||
if self.horizontal_scroll_able(width) {
|
||||
let text_width = self.adjust_max_width_text_len;
|
||||
let arrow_left = if self.offset > 0 { " ←" } else { " " };
|
||||
let arrow_right = if usize::from(self.offset) < self.adjusted_max_width {
|
||||
"→ "
|
||||
} else {
|
||||
" "
|
||||
};
|
||||
Some(format!(
|
||||
"{left} {offset:>text_width$}/{adjusted_max_width} {right}",
|
||||
offset = self.offset,
|
||||
adjusted_max_width = self.adjusted_max_width,
|
||||
left = arrow_left,
|
||||
right = arrow_right,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -709,15 +724,25 @@ impl Logs {
|
||||
self.lines.get_state_title()
|
||||
}
|
||||
|
||||
/// Return true it currently selected cotnainer logs are wide enough to horizontally scroll
|
||||
pub fn horizontal_scroll_able(&mut self, width: u16) -> bool {
|
||||
if self.lines.items.is_empty() {
|
||||
return false;
|
||||
}
|
||||
self.adjusted_max_width = self.max_log_len.saturating_sub(width.into()) + 4;
|
||||
self.adjust_max_width_text_len = self.adjusted_max_width.to_string().chars().count();
|
||||
self.max_log_len + 4 > usize::from(width)
|
||||
}
|
||||
|
||||
/// Add a padding so one char will always be visilbe?
|
||||
/// +6 is to account for borders & the selection triangle and a little bit of padding
|
||||
pub fn forward(&mut self, width: u16) {
|
||||
let offset = usize::from(self.offset);
|
||||
self.adjusted_max_width = self.max_log_len.saturating_sub(width.into()) + 6;
|
||||
if self.horizontal_scroll_able(width) {
|
||||
if self.adjusted_max_width > 0 && offset < self.adjusted_max_width {
|
||||
self.offset = self.offset.saturating_add(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reduce the char offset
|
||||
pub const fn back(&mut self) {
|
||||
@@ -1153,4 +1178,38 @@ mod tests {
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Test the get_scroll_title methods
|
||||
fn test_scroll_title() {
|
||||
let mut logs = Logs::default();
|
||||
|
||||
let result = logs.get_scroll_title(10);
|
||||
assert!(result.is_none());
|
||||
|
||||
let input = "short".to_owned();
|
||||
let (tz, _) = LogsTz::splitter(&input);
|
||||
logs.insert(Text::from(input), tz);
|
||||
|
||||
let result = logs.get_scroll_title(10);
|
||||
assert!(result.is_none());
|
||||
|
||||
let input = "2023-01-14T19:13:30.783138328Z Hello world some long line".to_owned();
|
||||
let (tz, _) = LogsTz::splitter(&input);
|
||||
logs.insert(Text::from(input), tz);
|
||||
|
||||
let result = logs.get_scroll_title(10);
|
||||
assert_eq!(result, Some(" 0/51 → ".to_owned()));
|
||||
|
||||
logs.forward(10);
|
||||
|
||||
let result = logs.get_scroll_title(10);
|
||||
assert_eq!(result, Some(" ← 1/51 → ".to_owned()));
|
||||
|
||||
for _ in 0..=49 {
|
||||
logs.forward(10);
|
||||
}
|
||||
let result = logs.get_scroll_title(10);
|
||||
assert_eq!(result, Some(" ← 51/51 ".to_owned()));
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -638,9 +638,9 @@ impl AppData {
|
||||
}
|
||||
|
||||
/// If scrolling horiztonally along the logs, display a counter of the position in the in the scroll, `x/y`
|
||||
pub fn get_scroll_title(&self) -> Option<String> {
|
||||
self.get_selected_container()
|
||||
.and_then(|i| i.logs.get_scroll_title())
|
||||
pub fn get_scroll_title(&mut self, width: u16) -> Option<String> {
|
||||
self.get_mut_selected_container()
|
||||
.and_then(|i| i.logs.get_scroll_title(width))
|
||||
}
|
||||
|
||||
/// Increase the logs offset, basically moving an invisible cursor back
|
||||
|
||||
@@ -168,7 +168,7 @@ impl DockerData {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
// TODO is hardcoded eth0 a good idea here? - Could use first() instead?
|
||||
// TODO is hardcoded eth0 a good idea here?
|
||||
let (rx, tx) = stats.networks.as_ref().map_or((0, 0), |i| {
|
||||
i.get("eth0").map_or((0, 0), |x| {
|
||||
(
|
||||
|
||||
@@ -356,7 +356,6 @@ mod tests {
|
||||
insert_logs(&setup);
|
||||
|
||||
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
|
||||
|
||||
setup
|
||||
.terminal
|
||||
.draw(|f| {
|
||||
|
||||
@@ -158,7 +158,7 @@ pub mod tests {
|
||||
/// Create a FrameData struct from two Arc<mutex>'s, instead of from UI
|
||||
impl From<(&Arc<Mutex<AppData>>, &Arc<Mutex<GuiState>>)> for FrameData {
|
||||
fn from(data: (&Arc<Mutex<AppData>>, &Arc<Mutex<GuiState>>)) -> Self {
|
||||
let (app_data, gui_data) = (data.0.lock(), data.1.lock());
|
||||
let (mut app_data, gui_data) = (data.0.lock(), data.1.lock());
|
||||
|
||||
// let container_section_height = app_data.get_container_len();
|
||||
// let container_section_height = if container_section_height < 12 {
|
||||
@@ -185,7 +185,7 @@ pub mod tests {
|
||||
loading_icon: gui_data.get_loading().to_string(),
|
||||
log_height: gui_data.get_log_height(),
|
||||
log_title: app_data.get_log_title(),
|
||||
scroll_title: app_data.get_scroll_title(),
|
||||
scroll_title: app_data.get_scroll_title(gui_data.get_screen_width()),
|
||||
port_max_lens: app_data.get_longest_port(),
|
||||
ports: app_data.get_selected_ports(),
|
||||
selected_panel: gui_data.get_selected_panel(),
|
||||
@@ -216,6 +216,7 @@ pub mod tests {
|
||||
let gui_state = Arc::new(Mutex::new(gui_state));
|
||||
let fd = FrameData::from((&app_data, &gui_state));
|
||||
let area = Rect::new(0, 0, w, h);
|
||||
gui_state.lock().set_screen_width(w);
|
||||
TuiTestSetup {
|
||||
app_data,
|
||||
gui_state,
|
||||
|
||||
+2
-2
@@ -312,7 +312,7 @@ pub struct FrameData {
|
||||
|
||||
impl From<&Ui> for FrameData {
|
||||
fn from(ui: &Ui) -> Self {
|
||||
let (app_data, gui_data) = (ui.app_data.lock(), ui.gui_state.lock());
|
||||
let (mut app_data, gui_data) = (ui.app_data.lock(), ui.gui_state.lock());
|
||||
|
||||
let (filter_by, filter_term) = app_data.get_filter();
|
||||
Self {
|
||||
@@ -333,7 +333,7 @@ impl From<&Ui> for FrameData {
|
||||
log_title: app_data.get_log_title(),
|
||||
port_max_lens: app_data.get_longest_port(),
|
||||
ports: app_data.get_selected_ports(),
|
||||
scroll_title: app_data.get_scroll_title(),
|
||||
scroll_title: app_data.get_scroll_title(gui_data.get_screen_width()),
|
||||
selected_panel: gui_data.get_selected_panel(),
|
||||
sorted_by: app_data.get_sorted(),
|
||||
status: gui_data.get_status(),
|
||||
|
||||
Reference in New Issue
Block a user