Repository: osa1/tiny (language: rust)

Issue:
Tab bar should scroll left when right-most tabs are closed
Suppose the tab bar looks like this: (`|` marks screen bounds)

```
| < some_server #chan1 #chan2 |
```

Because there are tabs to the left of what's seen on the screen, if I close `some_server` we should scroll left. Currently it looks like this:

```

| <                           |
```


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f1af6eb1..e80eabe9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+# Unreleased
+
+- TUI: Tab bar scrolls to left after closing tabs to fit more tabs into the
+  visible part of the tab bar (#164). See #164 for an example of previous
+  behavior.
+
 # 2020/01/08: 0.5.1
 
 - When a domain name resolves to multiple IP addresses tiny now tries connecting
diff --git a/libtiny_tui/src/tui.rs b/libtiny_tui/src/tui.rs
index 5c1f34c4..76108045 100644
--- a/libtiny_tui/src/tui.rs
+++ b/libtiny_tui/src/tui.rs
@@ -301,6 +301,7 @@ impl TUI {
                 self.select_tab(if tab_idx == 0 { 0 } else { tab_idx - 1 });
             }
         }
+        self.fix_scroll_after_close();
     }
 
     /// Returns index of the new tab if a new tab is created.
@@ -354,6 +355,7 @@ impl TUI {
                 self.select_tab(if tab_idx == 0 { 0 } else { tab_idx - 1 });
             }
         }
+        self.fix_scroll_after_close();
     }
 
     /// Returns index of the new tab if a new tab is created.
@@ -392,6 +394,7 @@ impl TUI {
                 self.select_tab(if tab_idx == 0 { 0 } else { tab_idx - 1 });
             }
         }
+        self.fix_scroll_after_close();
     }
 
     pub(crate) fn handle_input_event(&mut self, ev: Event) -> TUIRet {
@@ -789,6 +792,48 @@ impl TUI {
         self.tabs[self.active_idx].set_style(TabStyle::Normal);
     }
 
+    /// After closing a tab scroll left if there is space on the right and we can fit more tabs
+    /// from the left into the visible part of the tab bar.
+    fn fix_scroll_after_close(&mut self) {
+        let (tab_left, tab_right) = self.rendered_tabs();
+
+        if tab_left == 0 {
+            return;
+        }
+
+        // Size of shown part of the tab bar. DOES NOT include LEFT_ARROW.
+        let mut shown_width = 0;
+        for (tab_idx, tab) in self.tabs[tab_left..tab_right].iter().enumerate() {
+            shown_width += tab.width();
+            if tab_idx != tab_right - 1 {
+                shown_width += 1; // space between tabs
+            }
+        }
+
+        // How much space left in tab bar. Not accounting for LEFT_ARROW here!
+        let mut space_left = self.width - shown_width;
+
+        // How much to scroll left
+        let mut scroll_left = 0;
+
+        // Start iterating tabs on the left, add the tab size to `scroll_left` as long as scrolling
+        // doesn't make the right-most tab go out of bounds
+        for left_tab_idx in (0..=tab_left - 1).rev() {
+            let tab_width = self.tabs[left_tab_idx].width() + 1; // 1 for space
+            let draw_arrow = left_tab_idx != 0;
+            let tab_with_arrow_w = tab_width + if draw_arrow { 2 } else { 0 };
+
+            if tab_with_arrow_w <= space_left {
+                scroll_left += tab_width;
+                space_left -= tab_width;
+            } else {
+                break;
+            }
+        }
+
+        self.h_scroll -= scroll_left;
+    }
+
     pub(crate) fn switch(&mut self, string: &str) {
         let mut next_idx = self.active_idx;
         for (tab_idx, tab) in self.tabs.iter().enumerate() {


Test patch: