From 750d8f3cb747103cca7a2a4afe8ff41ff1318b6f Mon Sep 17 00:00:00 2001
From: Clement Tsang <34804052+ClementTsang@users.noreply.github.com>
Date: Sat, 26 Sep 2020 20:04:34 -0400
Subject: [PATCH] refactor: tui-rs 0.11.0 refactor (#253)

Refactors tui-rs usage to the new 0.11.0 release. This release also fixes the highlighting bug from #249, and now, expanding a widget no longer overrides the widget title colour.

This commit also introduces #255, but that seems to be easy to bandaid so hopefully it will get fixed soon?
---
 CHANGELOG.md                             |   6 +-
 Cargo.lock                               |   6 +-
 Cargo.toml                               |   3 +-
 src/app.rs                               |   1 +
 src/canvas.rs                            |  15 +-
 src/canvas/dialogs/dd_dialog.rs          | 106 +++++-------
 src/canvas/dialogs/help_dialog.rs        |  36 ++---
 src/canvas/screens/config_screen.rs      |   6 +-
 src/canvas/widgets/basic_table_arrows.rs |  22 ++-
 src/canvas/widgets/battery_display.rs    | 154 ++++++------------
 src/canvas/widgets/cpu_basic.rs          |  20 +--
 src/canvas/widgets/cpu_graph.rs          |  64 ++++----
 src/canvas/widgets/disk_table.rs         |  67 +++-----
 src/canvas/widgets/mem_basic.rs          |  12 +-
 src/canvas/widgets/mem_graph.rs          |  70 ++++----
 src/canvas/widgets/network_basic.rs      |  26 ++-
 src/canvas/widgets/network_graph.rs      |  65 ++++----
 src/canvas/widgets/process_table.rs      | 120 +++++++-------
 src/canvas/widgets/temp_table.rs         |  39 ++---
 src/constants.rs                         | 198 +++++++++++------------
 20 files changed, 488 insertions(+), 548 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6ebdd407..0d1bb1df 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,8 +19,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 - [#223](https://github.com/ClementTsang/bottom/pull/223): Add tree mode for processes.
 
-- [](): Add in-app configuration.
-
 ### Changes
 
 - [#213](https://github.com/ClementTsang/bottom/pull/213), [#214](https://github.com/ClementTsang/bottom/pull/214): Updated help descriptions, added auto-complete generation.
@@ -33,6 +31,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 - [#224](https://github.com/ClementTsang/bottom/pull/224): Implements sorting by count. It previously did absolutely nothing.
 
+- [#253](https://github.com/ClementTsang/bottom/pull/253): Fix highlighted entries being stuck in another colour when the widget is not selected.
+
+- [#253](https://github.com/ClementTsang/bottom/pull/253): Expanding a widget no longer overrides the widget title colour.
+
 ## [0.4.7] - 2020-08-26
 
 ### Bug Fixes
diff --git a/Cargo.lock b/Cargo.lock
index 7893cdbb..ebef0f0a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1363,15 +1363,13 @@ checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
 
 [[package]]
 name = "tui"
-version = "0.9.5"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9533d39bef0ae8f510e8a99d78702e68d1bbf0b98a78ec9740509d287010ae1e"
+checksum = "36626dee5ede9fd34015e9fb4fd7eedf3f3d05bdf1436aaef15b7d0a24233778"
 dependencies = [
  "bitflags",
  "cassowary",
  "crossterm",
- "either",
- "itertools",
  "unicode-segmentation",
  "unicode-width",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index d019a7ba..90a877a5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -42,7 +42,8 @@ serde = {version = "1.0", features = ["derive"] }
 sysinfo = "0.15.1"
 thiserror = "1.0.20"
 toml = "0.5.6"
-tui = {version = "0.9.5", features = ["crossterm"], default-features = false }
+tui = {version = "0.11.0", features = ["crossterm"], default-features = false }
+# tui = {version = "0.11.0", features = ["crossterm"], default-features = false, path="../tui-rs" }
 typed-builder = "0.7.0"
 unicode-segmentation = "1.6.0"
 unicode-width = "0.1"
diff --git a/src/app.rs b/src/app.rs
index 33c11ebd..3eda03ad 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1276,6 +1276,7 @@ impl App {
     }
 
     /// Call this whenever the config value is updated!
+    #[allow(dead_code)] //FIXME: Remove this
     fn update_config_file(&mut self) -> anyhow::Result<()> {
         if self.app_config_fields.no_write {
             // Don't write!
diff --git a/src/canvas.rs b/src/canvas.rs
index f4707507..bc142194 100644
--- a/src/canvas.rs
+++ b/src/canvas.rs
@@ -5,7 +5,7 @@ use std::collections::HashMap;
 use tui::{
     backend::Backend,
     layout::{Constraint, Direction, Layout, Rect},
-    widgets::Text,
+    text::{Span, Spans},
     Frame, Terminal,
 };
 
@@ -63,7 +63,7 @@ pub struct Painter {
     pub colours: CanvasColours,
     height: u16,
     width: u16,
-    styled_help_text: Vec<Text<'static>>,
+    styled_help_text: Vec<Spans<'static>>,
     is_mac_os: bool,
     row_constraints: Vec<Constraint>,
     col_constraints: Vec<Vec<Constraint>>,
@@ -289,28 +289,27 @@ impl Painter {
                 styled_help_spans.extend(
                     section
                         .iter()
-                        .map(|&text| Text::styled(text, self.colours.text_style))
+                        .map(|&text| Span::styled(text, self.colours.text_style))
                         .collect::<Vec<_>>(),
                 );
             } else {
                 // Not required check but it runs only a few times... so whatever ig, prevents me from
                 // being dumb and leaving a help text section only one line long.
                 if section.len() > 1 {
-                    styled_help_spans.push(Text::raw("\n\n"));
+                    styled_help_spans.push(Span::raw(""));
                     styled_help_spans
-                        .push(Text::styled(section[0], self.colours.table_header_style));
+                        .push(Span::styled(section[0], self.colours.table_header_style));
                     styled_help_spans.extend(
                         section[1..]
                             .iter()
-                            .map(|&text| Text::styled(text, self.colours.text_style))
+                            .map(|&text| Span::styled(text, self.colours.text_style))
                             .collect::<Vec<_>>(),
                     );
                 }
             }
         });
 
-        // self.styled_help_text = styled_help_spans.into_iter().map(Spans::from).collect();
-        self.styled_help_text = styled_help_spans;
+        self.styled_help_text = styled_help_spans.into_iter().map(Spans::from).collect();
     }
 
     // FIXME: [CONFIG] write this, should call painter init and any changed colour functions...
diff --git a/src/canvas/dialogs/dd_dialog.rs b/src/canvas/dialogs/dd_dialog.rs
index 11d0df5f..50dbc7ef 100644
--- a/src/canvas/dialogs/dd_dialog.rs
+++ b/src/canvas/dialogs/dd_dialog.rs
@@ -2,7 +2,8 @@ use tui::{
     backend::Backend,
     layout::{Alignment, Constraint, Direction, Layout, Rect},
     terminal::Frame,
-    widgets::{Block, Borders, Paragraph, Text},
+    text::{Span, Spans, Text},
+    widgets::{Block, Borders, Paragraph, Wrap},
 };
 
 use crate::{app::App, canvas::Painter};
@@ -11,46 +12,46 @@ const DD_BASE: &str = " Confirm Kill Process ── Esc to close ";
 const DD_ERROR_BASE: &str = " Error ── Esc to close ";
 
 pub trait KillDialog {
-    fn get_dd_spans(&self, app_state: &App) -> Option<Vec<Text<'_>>>;
+    fn get_dd_spans(&self, app_state: &App) -> Option<Text<'_>>;
 
     fn draw_dd_dialog<B: Backend>(
-        &self, f: &mut Frame<'_, B>, dd_text: Option<Vec<Text<'_>>>, app_state: &mut App,
-        draw_loc: Rect,
+        &self, f: &mut Frame<'_, B>, dd_text: Option<Text<'_>>, app_state: &mut App, draw_loc: Rect,
     ) -> bool;
 }
 
 impl KillDialog for Painter {
-    fn get_dd_spans(&self, app_state: &App) -> Option<Vec<Text<'_>>> {
+    fn get_dd_spans(&self, app_state: &App) -> Option<Text<'_>> {
         if let Some(dd_err) = &app_state.dd_err {
-            return Some(vec![
-                Text::raw("\n"),
-                Text::raw(format!("Failed to kill process.\n{}\n", dd_err)),
-                Text::raw("Please press ENTER or ESC to close this dialog."),
-            ]);
+            return Some(Text::from(vec![
+                Spans::default(),
+                Spans::from("Failed to kill process."),
+                Spans::from(dd_err.clone()),
+                Spans::from("Please press ENTER or ESC to close this dialog."),
+            ]));
         } else if let Some(to_kill_processes) = app_state.get_to_delete_processes() {
             if let Some(first_pid) = to_kill_processes.1.first() {
-                return Some(vec![
-                    Text::raw("\n"),
+                return Some(Text::from(vec![
+                    Spans::from(""),
                     if app_state.is_grouped(app_state.current_widget.widget_id) {
                         if to_kill_processes.1.len() != 1 {
-                            Text::raw(format!(
+                            Spans::from(format!(
                                 "Kill {} processes with the name \"{}\"?  Press ENTER to confirm.",
                                 to_kill_processes.1.len(),
                                 to_kill_processes.0
                             ))
                         } else {
-                            Text::raw(format!(
+                            Spans::from(format!(
                                 "Kill 1 process with the name \"{}\"?  Press ENTER to confirm.",
                                 to_kill_processes.0
                             ))
                         }
                     } else {
-                        Text::raw(format!(
+                        Spans::from(format!(
                             "Kill process \"{}\" with PID {}?  Press ENTER to confirm.",
                             to_kill_processes.0, first_pid
                         ))
                     },
-                ]);
+                ]));
             }
         }
 
@@ -58,63 +59,44 @@ impl KillDialog for Painter {
     }
 
     fn draw_dd_dialog<B: Backend>(
-        &self, f: &mut Frame<'_, B>, dd_text: Option<Vec<Text<'_>>>, app_state: &mut App,
-        draw_loc: Rect,
+        &self, f: &mut Frame<'_, B>, dd_text: Option<Text<'_>>, app_state: &mut App, draw_loc: Rect,
     ) -> bool {
         if let Some(dd_text) = dd_text {
-            // let dd_title = if app_state.dd_err.is_some() {
-            //     Text::styled(
-            //         format!(
-            //             " Error ─{}─ Esc to close ",
-            //             "─".repeat(
-            //                 usize::from(draw_loc.width)
-            //                     .saturating_sub(DD_ERROR_BASE.chars().count() + 2)
-            //             )
-            //         ),
-            //         self.colours.border_style,
-            //     )
-            // } else {
-            //     Text::styled(
-            //         format!(
-            //             " Confirm Kill Process ─{}─ Esc to close ",
-            //             "─".repeat(
-            //                 usize::from(draw_loc.width).saturating_sub(DD_BASE.chars().count() + 2)
-            //             )
-            //         ),
-            //         self.colours.border_style,
-            //     )
-            // };
-
             let dd_title = if app_state.dd_err.is_some() {
-                format!(
-                    " Error ─{}─ Esc to close ",
-                    "─".repeat(
-                        usize::from(draw_loc.width)
-                            .saturating_sub(DD_ERROR_BASE.chars().count() + 2)
-                    )
+                Span::styled(
+                    format!(
+                        " Error ─{}─ Esc to close ",
+                        "─".repeat(
+                            usize::from(draw_loc.width)
+                                .saturating_sub(DD_ERROR_BASE.chars().count() + 2)
+                        )
+                    ),
+                    self.colours.border_style,
                 )
             } else {
-                format!(
-                    " Confirm Kill Process ─{}─ Esc to close ",
-                    "─".repeat(
-                        usize::from(draw_loc.width).saturating_sub(DD_BASE.chars().count() + 2)
-                    )
+                Span::styled(
+                    format!(
+                        " Confirm Kill Process ─{}─ Esc to close ",
+                        "─".repeat(
+                            usize::from(draw_loc.width).saturating_sub(DD_BASE.chars().count() + 2)
+                        )
+                    ),
+                    self.colours.border_style,
                 )
             };
 
             f.render_widget(
-                Paragraph::new(dd_text.iter())
+                Paragraph::new(dd_text)
                     .block(
                         Block::default()
-                            .title(&dd_title)
-                            .title_style(self.colours.border_style)
+                            .title(dd_title)
                             .style(self.colours.border_style)
                             .borders(Borders::ALL)
                             .border_style(self.colours.border_style),
                     )
                     .style(self.colours.text_style)
                     .alignment(Alignment::Center)
-                    .wrap(true),
+                    .wrap(Wrap { trim: true }),
                 draw_loc,
             );
 
@@ -135,13 +117,13 @@ impl KillDialog for Painter {
             if let Some(button_draw_loc) = split_draw_loc.get(1) {
                 let (yes_button, no_button) = if app_state.delete_dialog_state.is_on_yes {
                     (
-                        Text::styled("Yes", self.colours.currently_selected_text_style),
-                        Text::raw("No"),
+                        Span::styled("Yes", self.colours.currently_selected_text_style),
+                        Span::raw("No"),
                     )
                 } else {
                     (
-                        Text::raw("Yes"),
-                        Text::styled("No", self.colours.currently_selected_text_style),
+                        Span::raw("Yes"),
+                        Span::styled("No", self.colours.currently_selected_text_style),
                     )
                 };
 
@@ -158,13 +140,13 @@ impl KillDialog for Painter {
                     .split(*button_draw_loc);
 
                 f.render_widget(
-                    Paragraph::new([yes_button].iter())
+                    Paragraph::new(yes_button)
                         .block(Block::default())
                         .alignment(Alignment::Right),
                     button_layout[0],
                 );
                 f.render_widget(
-                    Paragraph::new([no_button].iter())
+                    Paragraph::new(no_button)
                         .block(Block::default())
                         .alignment(Alignment::Left),
                     button_layout[2],
diff --git a/src/canvas/dialogs/help_dialog.rs b/src/canvas/dialogs/help_dialog.rs
index fe0f1c52..752dc858 100644
--- a/src/canvas/dialogs/help_dialog.rs
+++ b/src/canvas/dialogs/help_dialog.rs
@@ -5,7 +5,8 @@ use tui::{
     backend::Backend,
     layout::{Alignment, Rect},
     terminal::Frame,
-    widgets::{Block, Borders, Paragraph},
+    text::Span,
+    widgets::{Block, Borders, Paragraph, Wrap},
 };
 
 const HELP_BASE: &str = " Help ── Esc to close ";
@@ -21,19 +22,14 @@ impl HelpDialog for Painter {
     fn draw_help_dialog<B: Backend>(
         &self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
     ) {
-        // let help_title = Text::styled(
-        //     format!(
-        //         " Help ─{}─ Esc to close ",
-        //         "─".repeat(
-        //             usize::from(draw_loc.width).saturating_sub(HELP_BASE.chars().count() + 2)
-        //         )
-        //     ),
-        //     self.colours.border_style,
-        // );
-
-        let help_title = format!(
-            " Help ─{}─ Esc to close ",
-            "─".repeat(usize::from(draw_loc.width).saturating_sub(HELP_BASE.chars().count() + 2))
+        let help_title = Span::styled(
+            format!(
+                " Help ─{}─ Esc to close ",
+                "─".repeat(
+                    usize::from(draw_loc.width).saturating_sub(HELP_BASE.chars().count() + 2)
+                )
+            ),
+            self.colours.border_style,
         );
 
         if app_state.should_get_widget_bounds() {
@@ -99,24 +95,24 @@ impl HelpDialog for Painter {
         }
 
         f.render_widget(
-            Paragraph::new(self.styled_help_text.iter())
+            Paragraph::new(self.styled_help_text.clone())
                 .block(
                     Block::default()
-                        .title(&help_title)
-                        .title_style(self.colours.border_style)
+                        .title(help_title)
                         .style(self.colours.border_style)
                         .borders(Borders::ALL)
                         .border_style(self.colours.border_style),
                 )
                 .style(self.colours.text_style)
                 .alignment(Alignment::Left)
-                .wrap(true)
-                .scroll(
+                .wrap(Wrap { trim: true })
+                .scroll((
                     app_state
                         .help_dialog_state
                         .scroll_state
                         .current_scroll_index,
-                ),
+                    0,
+                )),
             draw_loc,
         );
     }
diff --git a/src/canvas/screens/config_screen.rs b/src/canvas/screens/config_screen.rs
index 6aa22493..161e6ca6 100644
--- a/src/canvas/screens/config_screen.rs
+++ b/src/canvas/screens/config_screen.rs
@@ -1,3 +1,5 @@
+#![allow(unused_variables)] //FIXME: Remove this
+#![allow(unused_imports)] //FIXME: Remove this
 use crate::{app::App, canvas::Painter, constants};
 use tui::{
     backend::Backend,
@@ -6,6 +8,7 @@ use tui::{
     layout::Layout,
     layout::{Alignment, Rect},
     terminal::Frame,
+    text::Span,
     widgets::{Block, Borders, Paragraph},
 };
 
@@ -20,8 +23,7 @@ impl ConfigScreen for Painter {
         &self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
     ) {
         let config_block = Block::default()
-            .title(&" Config ")
-            .title_style(self.colours.border_style)
+            .title(" Config ") // FIXME: [Config] missing title styling
             .style(self.colours.border_style)
             .borders(Borders::ALL)
             .border_style(self.colours.border_style);
diff --git a/src/canvas/widgets/basic_table_arrows.rs b/src/canvas/widgets/basic_table_arrows.rs
index fd64e8dc..bce169e1 100644
--- a/src/canvas/widgets/basic_table_arrows.rs
+++ b/src/canvas/widgets/basic_table_arrows.rs
@@ -7,7 +7,9 @@ use tui::{
     backend::Backend,
     layout::{Alignment, Constraint, Direction, Layout, Rect},
     terminal::Frame,
-    widgets::{Block, Paragraph, Text},
+    text::Span,
+    text::Spans,
+    widgets::{Block, Paragraph},
 };
 
 pub trait BasicTableArrows {
@@ -97,13 +99,19 @@ impl BasicTableArrows for Painter {
                 usize::from(draw_loc.width).saturating_sub(6 + left_name.len() + right_name.len());
 
             let left_arrow_text = vec![
-                Text::raw("\n"),
-                Text::styled(format!("◄ {}", left_name), self.colours.text_style),
+                Spans::default(),
+                Spans::from(Span::styled(
+                    format!("◄ {}", left_name),
+                    self.colours.text_style,
+                )),
             ];
 
             let right_arrow_text = vec![
-                Text::raw("\n"),
-                Text::styled(format!("{} ►", right_name), self.colours.text_style),
+                Spans::default(),
+                Spans::from(Span::styled(
+                    format!("{} ►", right_name),
+                    self.colours.text_style,
+                )),
             ];
 
             let margined_draw_loc = Layout::default()
@@ -120,11 +128,11 @@ impl BasicTableArrows for Painter {
                 .split(draw_loc);
 
             f.render_widget(
-                Paragraph::new(left_arrow_text.iter()).block(Block::default()),
+                Paragraph::new(left_arrow_text).block(Block::default()),
                 margined_draw_loc[0],
             );
             f.render_widget(
-                Paragraph::new(right_arrow_text.iter())
+                Paragraph::new(right_arrow_text)
                     .block(Block::default())
                     .alignment(Alignment::Right),
                 margined_draw_loc[2],
diff --git a/src/canvas/widgets/battery_display.rs b/src/canvas/widgets/battery_display.rs
index e50ed515..7364044b 100644
--- a/src/canvas/widgets/battery_display.rs
+++ b/src/canvas/widgets/battery_display.rs
@@ -8,8 +8,10 @@ use tui::{
     backend::Backend,
     layout::{Constraint, Direction, Layout, Rect},
     terminal::Frame,
-    widgets::{Block, Borders, Paragraph, Row, Table, Tabs, Text},
+    text::{Span, Spans},
+    widgets::{Block, Borders, Paragraph, Row, Table, Tabs},
 };
+use unicode_segmentation::UnicodeSegmentation;
 
 pub trait BatteryDisplayWidget {
     fn draw_battery_display<B: Backend>(
@@ -28,53 +30,38 @@ impl BatteryDisplayWidget for Painter {
             app_state.battery_state.widget_states.get_mut(&widget_id)
         {
             let is_on_widget = widget_id == app_state.current_widget.widget_id;
-            let border_and_title_style = if is_on_widget {
+            let border_style = if is_on_widget {
                 self.colours.highlighted_border_style
             } else {
                 self.colours.border_style
             };
 
-            // let title = if app_state.is_expanded {
-            //     const TITLE_BASE: &str = " Battery ── Esc to go back ";
-            //     Span::styled(
-            //         format!(
-            //             " Battery ─{}─ Esc to go back ",
-            //             "─".repeat(
-            //                 usize::from(draw_loc.width)
-            //                     .saturating_sub(TITLE_BASE.chars().count() + 2)
-            //             )
-            //         ),
-            //         border_and_title_style,
-            //     )
-            // } else {
-            //     Span::styled(" Battery ".to_string(), self.colours.widget_title_style)
-            // };
-
             let title = if app_state.is_expanded {
                 const TITLE_BASE: &str = " Battery ── Esc to go back ";
-
-                format!(
-                    " Battery ─{}─ Esc to go back ",
-                    "─".repeat(
-                        usize::from(draw_loc.width).saturating_sub(TITLE_BASE.chars().count() + 2)
-                    )
-                )
+                Spans::from(vec![
+                    Span::styled(" Battery ".to_string(), self.colours.widget_title_style),
+                    Span::styled(
+                        format!(
+                            "─{}─ Esc to go back ",
+                            "─".repeat(usize::from(draw_loc.width).saturating_sub(
+                                UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2
+                            ))
+                        ),
+                        border_style,
+                    ),
+                ])
             } else {
-                " Battery ".to_string()
-            };
-
-            let title_style = if app_state.is_expanded {
-                border_and_title_style
-            } else {
-                self.colours.widget_title_style
+                Spans::from(Span::styled(
+                    " Battery ".to_string(),
+                    self.colours.widget_title_style,
+                ))
             };
 
             let battery_block = if draw_border {
                 Block::default()
-                    .title(&title)
-                    .title_style(title_style)
+                    .title(title)
                     .borders(Borders::ALL)
-                    .border_style(border_and_title_style)
+                    .border_style(border_style)
             } else if is_on_widget {
                 Block::default()
                     .borders(*SIDE_BORDERS)
@@ -83,38 +70,12 @@ impl BatteryDisplayWidget for Painter {
                 Block::default().borders(Borders::NONE)
             };
 
-            // f.render_widget(
-            //     // Tabs::new(
-            //     //     (app_state
-            //     //         .canvas_data
-            //     //         .battery_data
-            //     //         .iter()
-            //     //         .map(|battery| Spans::from(battery.battery_name.clone())))
-            //     //     .collect::<Vec<_>>(),
-            //     // )
-            //     Tabs::default()
-            //         .titles(
-            //             app_state
-            //                 .canvas_data
-            //                 .battery_data
-            //                 .iter()
-            //                 .map(|battery| &battery.battery_name)
-            //                 .collect::<Vec<_>>()
-            //                 .as_ref(),
-            //         )
-            //         .block(battery_block)
-            //         .divider(tui::symbols::line::VERTICAL)
-            //         .style(self.colours.text_style)
-            //         .highlight_style(self.colours.currently_selected_text_style)
-            //         .select(battery_widget_state.currently_selected_battery_index),
-            //     draw_loc,
-            // );
-
-            let margined_draw_loc = Layout::default()
-                .constraints([Constraint::Percentage(100)].as_ref())
-                .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 })
-                .direction(Direction::Horizontal)
-                .split(draw_loc)[0];
+            let battery_names = app_state
+                .canvas_data
+                .battery_data
+                .iter()
+                .map(|battery| &battery.battery_name)
+                .collect::<Vec<_>>();
 
             let tab_draw_loc = Layout::default()
                 .constraints(
@@ -128,6 +89,27 @@ impl BatteryDisplayWidget for Painter {
                 .direction(Direction::Vertical)
                 .split(draw_loc)[1];
 
+            f.render_widget(
+                Tabs::new(
+                    battery_names
+                        .iter()
+                        .map(|name| Spans::from((*name).clone()))
+                        .collect::<Vec<_>>(),
+                )
+                .block(Block::default())
+                .divider(tui::symbols::line::VERTICAL)
+                .style(self.colours.text_style)
+                .highlight_style(self.colours.currently_selected_text_style)
+                .select(battery_widget_state.currently_selected_battery_index),
+                tab_draw_loc,
+            );
+
+            let margined_draw_loc = Layout::default()
+                .constraints([Constraint::Percentage(100)].as_ref())
+                .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 })
+                .direction(Direction::Horizontal)
+                .split(draw_loc)[0];
+
             if let Some(battery_details) = app_state
                 .canvas_data
                 .battery_data
@@ -189,45 +171,15 @@ impl BatteryDisplayWidget for Painter {
                 );
             } else {
                 f.render_widget(
-                    Paragraph::new(
-                        [Text::styled(
-                            "No data found for this battery",
-                            self.colours.text_style,
-                        )]
-                        .iter(),
-                    )
+                    Paragraph::new(Span::styled(
+                        "No data found for this battery",
+                        self.colours.text_style,
+                    ))
                     .block(battery_block),
                     margined_draw_loc,
                 );
             }
 
-            let battery_names = app_state
-                .canvas_data
-                .battery_data
-                .iter()
-                .map(|battery| &battery.battery_name)
-                .collect::<Vec<_>>();
-
-            // Has to be placed AFTER for tui 0.9, place BEFORE for 0.10.
-            f.render_widget(
-                // Tabs::new(
-                //     (app_state
-                //         .canvas_data
-                //         .battery_data
-                //         .iter()
-                //         .map(|battery| Spans::from(battery.battery_name.clone())))
-                //     .collect::<Vec<_>>(),
-                // )
-                Tabs::default()
-                    .titles(battery_names.as_ref())
-                    .block(Block::default())
-                    .divider(tui::symbols::line::VERTICAL)
-                    .style(self.colours.text_style)
-                    .highlight_style(self.colours.currently_selected_text_style) //FIXME: [HIGHLIGHT] THIS IS BROKEN ON TUI's SIDE, override this with your own style...
-                    .select(battery_widget_state.currently_selected_battery_index),
-                tab_draw_loc,
-            );
-
             if should_get_widget_bounds {
                 // Tab wizardry
                 if !battery_names.is_empty() {
diff --git a/src/canvas/widgets/cpu_basic.rs b/src/canvas/widgets/cpu_basic.rs
index 7f9381f9..2f7574b6 100644
--- a/src/canvas/widgets/cpu_basic.rs
+++ b/src/canvas/widgets/cpu_basic.rs
@@ -11,7 +11,8 @@ use tui::{
     backend::Backend,
     layout::{Constraint, Direction, Layout, Rect},
     terminal::Frame,
-    widgets::{Block, Paragraph, Text},
+    text::{Span, Spans},
+    widgets::{Block, Paragraph},
 };
 
 pub trait CpuBasicWidget {
@@ -76,7 +77,7 @@ impl CpuBasicWidget for Painter {
 
                     let num_bars = calculate_basic_use_bars(use_percentage, bar_length);
                     format!(
-                        "{:3}[{}{}{:3.0}%]\n",
+                        "{:3}[{}{}{:3.0}%]",
                         if app_state.app_config_fields.show_average_cpu {
                             if cpu_index == 0 {
                                 "AVG".to_string()
@@ -108,16 +109,11 @@ impl CpuBasicWidget for Painter {
                     let end_index = min(start_index + how_many_cpus, num_cpus);
                     let cpu_column = (start_index..end_index)
                         .map(|cpu_index| {
-                            // Spans::from(Span {
-                            //     content: (&cpu_bars[cpu_index]).into(),
-                            //     style: self.colours.cpu_colour_styles
-                            //         [cpu_index % self.colours.cpu_colour_styles.len()],
-                            // })
-                            Text::styled(
-                                &cpu_bars[cpu_index],
-                                self.colours.cpu_colour_styles
+                            Spans::from(Span {
+                                content: (&cpu_bars[cpu_index]).into(),
+                                style: self.colours.cpu_colour_styles
                                     [cpu_index % self.colours.cpu_colour_styles.len()],
-                            )
+                            })
                         })
                         .collect::<Vec<_>>();
 
@@ -130,7 +126,7 @@ impl CpuBasicWidget for Painter {
                         .split(*chunk)[0];
 
                     f.render_widget(
-                        Paragraph::new(cpu_column.iter()).block(Block::default()),
+                        Paragraph::new(cpu_column).block(Block::default()),
                         margined_loc,
                     );
                 }
diff --git a/src/canvas/widgets/cpu_graph.rs b/src/canvas/widgets/cpu_graph.rs
index 97ba064d..07fdfaf0 100644
--- a/src/canvas/widgets/cpu_graph.rs
+++ b/src/canvas/widgets/cpu_graph.rs
@@ -1,5 +1,6 @@
 use lazy_static::lazy_static;
 use std::borrow::Cow;
+use unicode_segmentation::UnicodeSegmentation;
 
 use crate::{
     app::{layout_manager::WidgetDirection, App},
@@ -16,6 +17,8 @@ use tui::{
     layout::{Constraint, Direction, Layout, Rect},
     symbols::Marker,
     terminal::Frame,
+    text::Span,
+    text::Spans,
     widgets::{Axis, Block, Borders, Chart, Dataset, Row, Table},
 };
 
@@ -135,17 +138,17 @@ impl CpuGraphWidget for Painter {
         if let Some(cpu_widget_state) = app_state.cpu_state.widget_states.get_mut(&widget_id) {
             let cpu_data: &mut [ConvertedCpuData] = &mut app_state.canvas_data.cpu_data;
 
-            // let display_time_labels = vec![
-            //     Text::styled(
-            //         format!("{}s", cpu_widget_state.current_display_time / 1000),
-            //         self.colours.graph_style,
-            //     ),
-            //     Text::styled("0s".to_string(), self.colours.graph_style),
-            // ];
+            let display_time_labels = vec![
+                Span::styled(
+                    format!("{}s", cpu_widget_state.current_display_time / 1000),
+                    self.colours.graph_style,
+                ),
+                Span::styled("0s".to_string(), self.colours.graph_style),
+            ];
 
-            let display_time_labels = [
-                format!("{}s", cpu_widget_state.current_display_time / 1000),
-                "0s".to_string(),
+            let y_axis_labels = vec![
+                Span::styled("0%", self.colours.graph_style),
+                Span::styled("100%", self.colours.graph_style),
             ];
 
             let x_axis = if app_state.app_config_fields.hide_time
@@ -160,8 +163,7 @@ impl CpuGraphWidget for Painter {
                     Axis::default()
                         .bounds([-(cpu_widget_state.current_display_time as f64), 0.0])
                         .style(self.colours.graph_style)
-                        .labels(&display_time_labels)
-                        .labels_style(self.colours.graph_style)
+                        .labels(display_time_labels)
                 } else {
                     cpu_widget_state.autohide_timer = None;
                     Axis::default().bounds([-(cpu_widget_state.current_display_time as f64), 0.0])
@@ -172,15 +174,13 @@ impl CpuGraphWidget for Painter {
                 Axis::default()
                     .bounds([-(cpu_widget_state.current_display_time as f64), 0.0])
                     .style(self.colours.graph_style)
-                    .labels(&display_time_labels)
-                    .labels_style(self.colours.graph_style)
+                    .labels(display_time_labels)
             };
 
             let y_axis = Axis::default()
                 .style(self.colours.graph_style)
                 .bounds([0.0, 100.5])
-                .labels_style(self.colours.graph_style)
-                .labels(&["0%", "100%"]);
+                .labels(y_axis_labels);
 
             let use_dot = app_state.app_config_fields.use_dot;
             let show_avg_cpu = app_state.app_config_fields.show_average_cpu;
@@ -228,32 +228,36 @@ impl CpuGraphWidget for Painter {
                 vec![]
             };
 
-            let border_style = if app_state.current_widget.widget_id == widget_id {
+            let is_on_widget = widget_id == app_state.current_widget.widget_id;
+            let border_style = if is_on_widget {
                 self.colours.highlighted_border_style
             } else {
                 self.colours.border_style
             };
 
-            // let title = if app_state.is_expanded {
-            //     Span::styled(" CPU ".to_string(), border_style)
-            // } else {
-            //     Span::styled(" CPU ".to_string(), self.colours.widget_title_style)
-            // };
-            let title = " CPU ";
-            let title_style = if app_state.is_expanded {
-                border_style
+            let title = if app_state.is_expanded {
+                const TITLE_BASE: &str = " CPU ── Esc to go back ";
+                Spans::from(vec![
+                    Span::styled(" CPU ", self.colours.widget_title_style),
+                    Span::styled(
+                        format!(
+                            "─{}─ Esc to go back ",
+                            "─".repeat(usize::from(draw_loc.width).saturating_sub(
+                                UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2
+                            ))
+                        ),
+                        border_style,
+                    ),
+                ])
             } else {
-                self.colours.widget_title_style
+                Spans::from(Span::styled(" CPU ", self.colours.widget_title_style))
             };
 
             f.render_widget(
-                // Chart::new(dataset_vector)
-                Chart::default()
-                    .datasets(&dataset_vector)
+                Chart::new(dataset_vector)
                     .block(
                         Block::default()
                             .title(title)
-                            .title_style(title_style)
                             .borders(Borders::ALL)
                             .border_style(border_style),
                     )
diff --git a/src/canvas/widgets/disk_table.rs b/src/canvas/widgets/disk_table.rs
index 440687df..091b58c9 100644
--- a/src/canvas/widgets/disk_table.rs
+++ b/src/canvas/widgets/disk_table.rs
@@ -3,6 +3,8 @@ use tui::{
     backend::Backend,
     layout::{Constraint, Direction, Layout, Rect},
     terminal::Frame,
+    text::Span,
+    text::Spans,
     widgets::{Block, Borders, Row, Table},
 };
 
@@ -139,19 +141,13 @@ impl DiskTableWidget for Painter {
                                             let first_n = graphemes
                                                 [..(*calculated_col_width as usize - 1)]
                                                 .concat();
-                                            Cow::Owned(format!("{}…", first_n))
-                                        } else {
-                                            Cow::Borrowed(entry)
+                                            return Cow::Owned(format!("{}…", first_n));
                                         }
-                                    } else {
-                                        Cow::Borrowed(entry)
                                     }
-                                } else {
-                                    Cow::Borrowed(entry)
                                 }
-                            } else {
-                                Cow::Borrowed(entry)
                             }
+
+                            Cow::Borrowed(entry)
                         },
                     );
 
@@ -159,7 +155,7 @@ impl DiskTableWidget for Painter {
                 });
 
             // TODO: This seems to be bugged?  The selected text style gets "stuck"?  I think this gets fixed with tui 0.10?
-            let (border_and_title_style, highlight_style) = if is_on_widget {
+            let (border_style, highlight_style) = if is_on_widget {
                 (
                     self.colours.highlighted_border_style,
                     self.colours.currently_selected_text_style,
@@ -168,50 +164,29 @@ impl DiskTableWidget for Painter {
                 (self.colours.border_style, self.colours.text_style)
             };
 
-            // let title = if app_state.is_expanded {
-            //     const TITLE_BASE: &str = " Disk ── Esc to go back ";
-            //     Span::styled(
-            //         format!(
-            //             " Disk ─{}─ Esc to go back ",
-            //             "─".repeat(
-            //                 usize::from(draw_loc.width)
-            //                     .saturating_sub(TITLE_BASE.chars().count() + 2)
-            //             )
-            //         ),
-            //         border_and_title_style,
-            //     )
-            // } else if app_state.app_config_fields.use_basic_mode {
-            //     Span::from(String::new())
-            // } else {
-            //     Span::styled(" Disk ".to_string(), self.colours.widget_title_style)
-            // };
-
             let title = if app_state.is_expanded {
                 const TITLE_BASE: &str = " Disk ── Esc to go back ";
-                format!(
-                    " Disk ─{}─ Esc to go back ",
-                    "─".repeat(
-                        usize::from(draw_loc.width).saturating_sub(TITLE_BASE.chars().count() + 2)
-                    )
-                )
-            } else if app_state.app_config_fields.use_basic_mode {
-                String::new()
+                Spans::from(vec![
+                    Span::styled(" Disk ", self.colours.widget_title_style),
+                    Span::styled(
+                        format!(
+                            "─{}─ Esc to go back, ",
+                            "─".repeat(usize::from(draw_loc.width).saturating_sub(
+                                UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2
+                            ))
+                        ),
+                        border_style,
+                    ),
+                ])
             } else {
-                " Disk ".to_string()
-            };
-
-            let title_style = if app_state.is_expanded {
-                border_and_title_style
-            } else {
-                self.colours.widget_title_style
+                Spans::from(Span::styled(" Disk ", self.colours.widget_title_style))
             };
 
             let disk_block = if draw_border {
                 Block::default()
-                    .title(&title)
-                    .title_style(title_style)
+                    .title(title)
                     .borders(Borders::ALL)
-                    .border_style(border_and_title_style)
+                    .border_style(border_style)
             } else if is_on_widget {
                 Block::default()
                     .borders(*SIDE_BORDERS)
diff --git a/src/canvas/widgets/mem_basic.rs b/src/canvas/widgets/mem_basic.rs
index 89f96dd5..38ce9dfc 100644
--- a/src/canvas/widgets/mem_basic.rs
+++ b/src/canvas/widgets/mem_basic.rs
@@ -8,7 +8,9 @@ use tui::{
     backend::Backend,
     layout::{Constraint, Layout, Rect},
     terminal::Frame,
-    widgets::{Block, Paragraph, Text},
+    text::Span,
+    text::Spans,
+    widgets::{Block, Paragraph},
 };
 
 pub trait MemBasicWidget {
@@ -106,13 +108,13 @@ impl MemBasicWidget for Painter {
             )
         };
 
-        let mem_text = [
-            Text::styled(mem_label, self.colours.ram_style),
-            Text::styled(swap_label, self.colours.swap_style),
+        let mem_text = vec![
+            Spans::from(Span::styled(mem_label, self.colours.ram_style)),
+            Spans::from(Span::styled(swap_label, self.colours.swap_style)),
         ];
 
         f.render_widget(
-            Paragraph::new(mem_text.iter()).block(Block::default()),
+            Paragraph::new(mem_text).block(Block::default()),
             margined_loc[0],
         );
 
diff --git a/src/canvas/widgets/mem_graph.rs b/src/canvas/widgets/mem_graph.rs
index bdc0b0b7..c3f62197 100644
--- a/src/canvas/widgets/mem_graph.rs
+++ b/src/canvas/widgets/mem_graph.rs
@@ -5,8 +5,11 @@ use tui::{
     layout::{Constraint, Rect},
     symbols::Marker,
     terminal::Frame,
+    text::Span,
+    text::Spans,
     widgets::{Axis, Block, Borders, Chart, Dataset},
 };
+use unicode_segmentation::UnicodeSegmentation;
 
 pub trait MemGraphWidget {
     fn draw_memory_graph<B: Backend>(
@@ -22,10 +25,18 @@ impl MemGraphWidget for Painter {
             let mem_data: &[(f64, f64)] = &app_state.canvas_data.mem_data;
             let swap_data: &[(f64, f64)] = &app_state.canvas_data.swap_data;
 
-            let display_time_labels = [
-                format!("{}s", mem_widget_state.current_display_time / 1000),
-                "0s".to_string(),
+            let display_time_labels = vec![
+                Span::styled(
+                    format!("{}s", mem_widget_state.current_display_time / 1000),
+                    self.colours.graph_style,
+                ),
+                Span::styled("0s".to_string(), self.colours.graph_style),
             ];
+            let y_axis_label = vec![
+                Span::styled("0%", self.colours.graph_style),
+                Span::styled("100%", self.colours.graph_style),
+            ];
+
             let x_axis = if app_state.app_config_fields.hide_time
                 || (app_state.app_config_fields.autohide_time
                     && mem_widget_state.autohide_timer.is_none())
@@ -38,8 +49,7 @@ impl MemGraphWidget for Painter {
                     Axis::default()
                         .bounds([-(mem_widget_state.current_display_time as f64), 0.0])
                         .style(self.colours.graph_style)
-                        .labels(&display_time_labels)
-                        .labels_style(self.colours.graph_style)
+                        .labels(display_time_labels)
                 } else {
                     mem_widget_state.autohide_timer = None;
                     Axis::default().bounds([-(mem_widget_state.current_display_time as f64), 0.0])
@@ -50,15 +60,13 @@ impl MemGraphWidget for Painter {
                 Axis::default()
                     .bounds([-(mem_widget_state.current_display_time as f64), 0.0])
                     .style(self.colours.graph_style)
-                    .labels(&display_time_labels)
-                    .labels_style(self.colours.graph_style)
+                    .labels(display_time_labels)
             };
 
             let y_axis = Axis::default()
                 .style(self.colours.graph_style)
                 .bounds([0.0, 100.5])
-                .labels(&["0%", "100%"])
-                .labels_style(self.colours.graph_style);
+                .labels(y_axis_label);
 
             let mut mem_canvas_vec: Vec<Dataset<'_>> = vec![];
             let mem_label = format!(
@@ -96,31 +104,39 @@ impl MemGraphWidget for Painter {
                     .graph_type(tui::widgets::GraphType::Line),
             );
 
-            let title = if app_state.is_expanded {
-                const TITLE_BASE: &str = " Memory ── Esc to go back ";
-                format!(
-                    " Memory ─{}─ Esc to go back ",
-                    "─".repeat(
-                        usize::from(draw_loc.width).saturating_sub(TITLE_BASE.chars().count() + 2)
-                    )
-                )
-            } else {
-                " Memory ".to_string()
-            };
-            let title_style = if app_state.is_expanded {
+            let is_on_widget = widget_id == app_state.current_widget.widget_id;
+            let border_style = if is_on_widget {
                 self.colours.highlighted_border_style
             } else {
-                self.colours.widget_title_style
+                self.colours.border_style
+            };
+
+            let title = if app_state.is_expanded {
+                const TITLE_BASE: &str = " Memory ── Esc to go back ";
+                Spans::from(vec![
+                    Span::styled(" Memory ", self.colours.widget_title_style),
+                    Span::styled(
+                        format!(
+                            "─{}─ Esc to go back ",
+                            "─".repeat(usize::from(draw_loc.width).saturating_sub(
+                                UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2
+                            ))
+                        ),
+                        border_style,
+                    ),
+                ])
+            } else {
+                Spans::from(Span::styled(
+                    " Memory ".to_string(),
+                    self.colours.widget_title_style,
+                ))
             };
 
             f.render_widget(
-                // Chart::new(mem_canvas_vec)
-                Chart::default()
-                    .datasets(&mem_canvas_vec)
+                Chart::new(mem_canvas_vec)
                     .block(
                         Block::default()
-                            .title(&title)
-                            .title_style(title_style)
+                            .title(title)
                             .borders(Borders::ALL)
                             .border_style(if app_state.current_widget.widget_id == widget_id {
                                 self.colours.highlighted_border_style
diff --git a/src/canvas/widgets/network_basic.rs b/src/canvas/widgets/network_basic.rs
index 126c8ae0..e96d2cf6 100644
--- a/src/canvas/widgets/network_basic.rs
+++ b/src/canvas/widgets/network_basic.rs
@@ -4,7 +4,8 @@ use tui::{
     backend::Backend,
     layout::{Constraint, Direction, Layout, Rect},
     terminal::Frame,
-    widgets::{Block, Paragraph, Text},
+    text::{Span, Spans, },
+    widgets::{Block, Paragraph},
 };
 
 pub trait NetworkBasicWidget {
@@ -43,28 +44,25 @@ impl NetworkBasicWidget for Painter {
             );
         }
 
-        let rx_label = format!("RX: {}\n", &app_state.canvas_data.rx_display);
+        let rx_label = format!("RX: {}", &app_state.canvas_data.rx_display);
         let tx_label = format!("TX: {}", &app_state.canvas_data.tx_display);
-        let total_rx_label = format!("Total RX: {}\n", &app_state.canvas_data.total_rx_display);
+        let total_rx_label = format!("Total RX: {}", &app_state.canvas_data.total_rx_display);
         let total_tx_label = format!("Total TX: {}", &app_state.canvas_data.total_tx_display);
 
-        let net_text = [
-            Text::styled(rx_label, self.colours.rx_style),
-            Text::styled(tx_label, self.colours.tx_style),
+        let net_text = vec![
+            Spans::from(Span::styled(rx_label, self.colours.rx_style)),
+            Spans::from(Span::styled(tx_label, self.colours.tx_style)),
         ];
 
-        let total_net_text = [
-            Text::styled(total_rx_label, self.colours.total_rx_style),
-            Text::styled(total_tx_label, self.colours.total_tx_style),
+        let total_net_text = vec![
+            Spans::from(Span::styled(total_rx_label, self.colours.total_rx_style)),
+            Spans::from(Span::styled(total_tx_label, self.colours.total_tx_style)),
         ];
 
-        f.render_widget(
-            Paragraph::new(net_text.iter()).block(Block::default()),
-            net_loc[0],
-        );
+        f.render_widget(Paragraph::new(net_text).block(Block::default()), net_loc[0]);
 
         f.render_widget(
-            Paragraph::new(total_net_text.iter()).block(Block::default()),
+            Paragraph::new(total_net_text).block(Block::default()),
             total_loc[0],
         );
 
diff --git a/src/canvas/widgets/network_graph.rs b/src/canvas/widgets/network_graph.rs
index 8050046b..95dfe304 100644
--- a/src/canvas/widgets/network_graph.rs
+++ b/src/canvas/widgets/network_graph.rs
@@ -1,5 +1,6 @@
 use lazy_static::lazy_static;
 use std::cmp::max;
+use unicode_segmentation::UnicodeSegmentation;
 
 use crate::{
     app::App,
@@ -13,6 +14,8 @@ use tui::{
     layout::{Constraint, Direction, Layout, Rect},
     symbols::Marker,
     terminal::Frame,
+    text::Span,
+    text::Spans,
     widgets::{Axis, Block, Borders, Chart, Dataset, Row, Table},
 };
 
@@ -179,9 +182,12 @@ impl NetworkGraphWidget for Painter {
                 -(network_widget_state.current_display_time as f64),
                 0.0,
             );
-            let display_time_labels = [
-                format!("{}s", network_widget_state.current_display_time / 1000),
-                "0s".to_string(),
+            let display_time_labels = vec![
+                Span::styled(
+                    format!("{}s", network_widget_state.current_display_time / 1000),
+                    self.colours.graph_style,
+                ),
+                Span::styled("0s".to_string(), self.colours.graph_style),
             ];
             let x_axis = if app_state.app_config_fields.hide_time
                 || (app_state.app_config_fields.autohide_time
@@ -195,8 +201,7 @@ impl NetworkGraphWidget for Painter {
                     Axis::default()
                         .bounds([-(network_widget_state.current_display_time as f64), 0.0])
                         .style(self.colours.graph_style)
-                        .labels(&display_time_labels)
-                        .labels_style(self.colours.graph_style)
+                        .labels(display_time_labels)
                 } else {
                     network_widget_state.autohide_timer = None;
                     Axis::default()
@@ -208,32 +213,41 @@ impl NetworkGraphWidget for Painter {
                 Axis::default()
                     .bounds([-(network_widget_state.current_display_time as f64), 0.0])
                     .style(self.colours.graph_style)
-                    .labels(&display_time_labels)
-                    .labels_style(self.colours.graph_style)
+                    .labels(display_time_labels)
             };
 
-            let y_axis_labels = labels;
+            let y_axis_labels = labels
+                .iter()
+                .map(|label| Span::styled(label, self.colours.graph_style))
+                .collect::<Vec<_>>();
             let y_axis = Axis::default()
                 .style(self.colours.graph_style)
                 .bounds([0.0, max_range])
-                .labels(&y_axis_labels)
-                .labels_style(self.colours.graph_style);
+                .labels(y_axis_labels);
+
+            let is_on_widget = widget_id == app_state.current_widget.widget_id;
+            let border_style = if is_on_widget {
+                self.colours.highlighted_border_style
+            } else {
+                self.colours.border_style
+            };
 
             let title = if app_state.is_expanded {
                 const TITLE_BASE: &str = " Network ── Esc to go back ";
-                format!(
-                    " Network ─{}─ Esc to go back ",
-                    "─".repeat(
-                        usize::from(draw_loc.width).saturating_sub(TITLE_BASE.chars().count() + 2)
-                    )
-                )
+                Spans::from(vec![
+                    Span::styled(" Network ", self.colours.widget_title_style),
+                    Span::styled(
+                        format!(
+                            "─{}─ Esc to go back ",
+                            "─".repeat(usize::from(draw_loc.width).saturating_sub(
+                                UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2
+                            ))
+                        ),
+                        border_style,
+                    ),
+                ])
             } else {
-                " Network ".to_string()
-            };
-            let title_style = if app_state.is_expanded {
-                self.colours.highlighted_border_style
-            } else {
-                self.colours.widget_title_style
+                Spans::from(Span::styled(" Network ", self.colours.widget_title_style))
             };
 
             let legend_constraints = if hide_legend {
@@ -321,13 +335,10 @@ impl NetworkGraphWidget for Painter {
             };
 
             f.render_widget(
-                // Chart::new(dataset)
-                Chart::default()
-                    .datasets(&dataset)
+                Chart::new(dataset)
                     .block(
                         Block::default()
-                            .title(&title)
-                            .title_style(title_style)
+                            .title(title)
                             .borders(Borders::ALL)
                             .border_style(if app_state.current_widget.widget_id == widget_id {
                                 self.colours.highlighted_border_style
diff --git a/src/canvas/widgets/process_table.rs b/src/canvas/widgets/process_table.rs
index 89a27ba9..970dcda1 100644
--- a/src/canvas/widgets/process_table.rs
+++ b/src/canvas/widgets/process_table.rs
@@ -11,7 +11,8 @@ use tui::{
     backend::Backend,
     layout::{Alignment, Constraint, Direction, Layout, Rect},
     terminal::Frame,
-    widgets::{Block, Borders, Paragraph, Row, Table, Text},
+    text::{Span, Spans},
+    widgets::{Block, Borders, Paragraph, Row, Table},
 };
 
 use std::borrow::Cow;
@@ -122,7 +123,7 @@ impl ProcessTableWidget for Painter {
                 .direction(Direction::Horizontal)
                 .split(draw_loc)[0];
 
-            let (border_and_title_style, highlight_style) = if is_on_widget {
+            let (border_style, highlight_style) = if is_on_widget {
                 (
                     self.colours.highlighted_border_style,
                     self.colours.currently_selected_text_style,
@@ -131,41 +132,35 @@ impl ProcessTableWidget for Painter {
                 (self.colours.border_style, self.colours.text_style)
             };
 
-            let title = if draw_border {
-                if app_state.is_expanded
-                    && !proc_widget_state
-                        .process_search_state
-                        .search_state
-                        .is_enabled
-                    && !proc_widget_state.is_sort_open
-                {
-                    const TITLE_BASE: &str = " Processes ── Esc to go back ";
-                    format!(
-                        " Processes ─{}─ Esc to go back ",
-                        "─".repeat(
-                            usize::from(draw_loc.width)
-                                .saturating_sub(TITLE_BASE.chars().count() + 2)
-                        )
-                    )
-                } else {
-                    " Processes ".to_string()
-                }
+            let title = if app_state.is_expanded
+                && !proc_widget_state
+                    .process_search_state
+                    .search_state
+                    .is_enabled
+                && !proc_widget_state.is_sort_open
+            {
+                const TITLE_BASE: &str = " Processes ── Esc to go back ";
+                Spans::from(vec![
+                    Span::styled(" Processes ", self.colours.widget_title_style),
+                    Span::styled(
+                        format!(
+                            "─{}─ Esc to go back ",
+                            "─".repeat(usize::from(draw_loc.width).saturating_sub(
+                                UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2
+                            ))
+                        ),
+                        border_style,
+                    ),
+                ])
             } else {
-                String::default()
-            };
-
-            let title_style = if app_state.is_expanded {
-                border_and_title_style
-            } else {
-                self.colours.widget_title_style
+                Spans::from(Span::styled(" Processes ", self.colours.widget_title_style))
             };
 
             let process_block = if draw_border {
                 Block::default()
-                    .title(&title)
-                    .title_style(title_style)
+                    .title(title)
                     .borders(Borders::ALL)
-                    .border_style(border_and_title_style)
+                    .border_style(border_style)
             } else if is_on_widget {
                 Block::default()
                     .borders(*SIDE_BORDERS)
@@ -459,7 +454,7 @@ impl ProcessTableWidget for Painter {
             is_on_widget: bool, grapheme_indices: GraphemeIndices<'a>, start_position: usize,
             cursor_position: usize, query: &str, currently_selected_text_style: tui::style::Style,
             text_style: tui::style::Style,
-        ) -> Vec<Text<'a>> {
+        ) -> Vec<Span<'a>> {
             let mut current_grapheme_posn = 0;
 
             if is_on_widget {
@@ -471,9 +466,9 @@ impl ProcessTableWidget for Painter {
                             None
                         } else {
                             let styled = if grapheme.0 == cursor_position {
-                                Text::styled(grapheme.1, currently_selected_text_style)
+                                Span::styled(grapheme.1, currently_selected_text_style)
                             } else {
-                                Text::styled(grapheme.1, text_style)
+                                Span::styled(grapheme.1, text_style)
                             };
                             Some(styled)
                         }
@@ -481,7 +476,7 @@ impl ProcessTableWidget for Painter {
                     .collect::<Vec<_>>();
 
                 if cursor_position >= query.len() {
-                    res.push(Text::styled(" ", currently_selected_text_style))
+                    res.push(Span::styled(" ", currently_selected_text_style))
                 }
 
                 res
@@ -495,7 +490,7 @@ impl ProcessTableWidget for Painter {
                         if current_grapheme_posn <= start_position {
                             None
                         } else {
-                            let styled = Text::styled(grapheme.1, text_style);
+                            let styled = Span::styled(grapheme.1, text_style);
                             Some(styled)
                         }
                     })
@@ -543,8 +538,8 @@ impl ProcessTableWidget for Painter {
                 self.colours.text_style,
             );
 
-            let mut search_text = {
-                let mut search_vec = vec![Text::styled(
+            let mut search_text = vec![Spans::from({
+                let mut search_vec = vec![Span::styled(
                     search_title,
                     if is_on_widget {
                         self.colours.table_header_style
@@ -554,7 +549,7 @@ impl ProcessTableWidget for Painter {
                 )];
                 search_vec.extend(query_with_cursor);
                 search_vec
-            };
+            })];
 
             // Text options shamelessly stolen from VS Code.
             let case_style = if !proc_widget_state.process_search_state.is_ignoring_case {
@@ -583,26 +578,24 @@ impl ProcessTableWidget for Painter {
 
             // FIXME: [MOUSE] Mouse support for these in search
             // FIXME: [MOVEMENT] Movement support for these in search
-            let option_text = vec![
-                Text::raw("\n"),
-                Text::styled(
+            let option_text = Spans::from(vec![
+                Span::styled(
                     format!("Case({})", if self.is_mac_os { "F1" } else { "Alt+C" }),
                     case_style,
                 ),
-                Text::raw("  "),
-                Text::styled(
+                Span::raw("  "),
+                Span::styled(
                     format!("Whole({})", if self.is_mac_os { "F2" } else { "Alt+W" }),
                     whole_word_style,
                 ),
-                Text::raw("  "),
-                Text::styled(
+                Span::raw("  "),
+                Span::styled(
                     format!("Regex({})", if self.is_mac_os { "F3" } else { "Alt+R" }),
                     regex_style,
                 ),
-            ];
+            ]);
 
-            search_text.push(Text::raw("\n"));
-            search_text.push(Text::styled(
+            search_text.push(Spans::from(Span::styled(
                 if let Some(err) = &proc_widget_state
                     .process_search_state
                     .search_state
@@ -613,8 +606,8 @@ impl ProcessTableWidget for Painter {
                     ""
                 },
                 self.colours.invalid_query_style,
-            ));
-            search_text.extend(option_text);
+            )));
+            search_text.push(option_text);
 
             let current_border_style = if proc_widget_state
                 .process_search_state
@@ -628,20 +621,21 @@ impl ProcessTableWidget for Painter {
                 self.colours.border_style
             };
 
-            let title = if draw_border {
-                const TITLE_BASE: &str = " Esc to close ";
-
-                let repeat_num =
-                    usize::from(draw_loc.width).saturating_sub(TITLE_BASE.chars().count() + 2);
-                format!("{} Esc to close ", "─".repeat(repeat_num))
-            } else {
-                String::new()
-            };
+            let title = Span::styled(
+                if draw_border {
+                    const TITLE_BASE: &str = " Esc to close ";
+                    let repeat_num =
+                        usize::from(draw_loc.width).saturating_sub(TITLE_BASE.chars().count() + 2);
+                    format!("{} Esc to close ", "─".repeat(repeat_num))
+                } else {
+                    String::new()
+                },
+                current_border_style,
+            );
 
             let process_search_block = if draw_border {
                 Block::default()
-                    .title(&title)
-                    .title_style(current_border_style)
+                    .title(title)
                     .borders(Borders::ALL)
                     .border_style(current_border_style)
             } else if is_on_widget {
@@ -659,7 +653,7 @@ impl ProcessTableWidget for Painter {
                 .split(draw_loc)[0];
 
             f.render_widget(
-                Paragraph::new(search_text.iter())
+                Paragraph::new(search_text)
                     .block(process_search_block)
                     .style(self.colours.text_style)
                     .alignment(Alignment::Left),
diff --git a/src/canvas/widgets/temp_table.rs b/src/canvas/widgets/temp_table.rs
index b17bb0e0..960efac6 100644
--- a/src/canvas/widgets/temp_table.rs
+++ b/src/canvas/widgets/temp_table.rs
@@ -3,6 +3,8 @@ use tui::{
     backend::Backend,
     layout::{Constraint, Direction, Layout, Rect},
     terminal::Frame,
+    text::Span,
+    text::Spans,
     widgets::{Block, Borders, Row, Table},
 };
 
@@ -141,7 +143,7 @@ impl TempTableWidget for Painter {
                     Row::Data(truncated_data)
                 });
 
-            let (border_and_title_style, highlight_style) = if is_on_widget {
+            let (border_style, highlight_style) = if is_on_widget {
                 (
                     self.colours.highlighted_border_style,
                     self.colours.currently_selected_text_style,
@@ -152,29 +154,30 @@ impl TempTableWidget for Painter {
 
             let title = if app_state.is_expanded {
                 const TITLE_BASE: &str = " Temperatures ── Esc to go back ";
-                format!(
-                    " Temperatures ─{}─ Esc to go back ",
-                    "─".repeat(
-                        usize::from(draw_loc.width).saturating_sub(TITLE_BASE.chars().count() + 2)
-                    )
-                )
-            } else if app_state.app_config_fields.use_basic_mode {
-                String::new()
+                Spans::from(vec![
+                    Span::styled(" Temperatures ", self.colours.widget_title_style),
+                    Span::styled(
+                        format!(
+                            "─{}─ Esc to go back ",
+                            "─".repeat(usize::from(draw_loc.width).saturating_sub(
+                                UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2
+                            ))
+                        ),
+                        border_style,
+                    ),
+                ])
             } else {
-                " Temperatures ".to_string()
-            };
-            let title_style = if app_state.is_expanded {
-                border_and_title_style
-            } else {
-                self.colours.widget_title_style
+                Spans::from(Span::styled(
+                    " Temperatures ",
+                    self.colours.widget_title_style,
+                ))
             };
 
             let temp_block = if draw_border {
                 Block::default()
-                    .title(&title)
-                    .title_style(title_style)
+                    .title(title)
                     .borders(Borders::ALL)
-                    .border_style(border_and_title_style)
+                    .border_style(border_style)
             } else if is_on_widget {
                 Block::default()
                     .borders(*SIDE_BORDERS)
diff --git a/src/constants.rs b/src/constants.rs
index 3044d023..40f8e646 100644
--- a/src/constants.rs
+++ b/src/constants.rs
@@ -40,45 +40,45 @@ lazy_static! {
 // FIXME: [HELP] I wanna update this before release... it's missing mouse too.
 // Help text
 pub const HELP_CONTENTS_TEXT: [&str; 8] = [
-    "Press the corresponding numbers to jump to the section, or scroll:\n",
-    "1 - General\n",
-    "2 - CPU widget\n",
-    "3 - Process widget\n",
-    "4 - Process search widget\n",
-    "5 - Process sort widget\n",
-    "6 - Battery widget\n",
+    "Press the corresponding numbers to jump to the section, or scroll:",
+    "1 - General",
+    "2 - CPU widget",
+    "3 - Process widget",
+    "4 - Process search widget",
+    "5 - Process sort widget",
+    "6 - Battery widget",
     "7 - Basic memory widget",
 ];
 
 pub const GENERAL_HELP_TEXT: [&str; 29] = [
-    "1 - General\n",
-    "q, Ctrl-c        Quit\n",
-    "Esc              Close dialog windows, search, widgets, or exit expanded mode\n",
-    "Ctrl-r           Reset display and any collected data\n",
-    "f                Freeze/unfreeze updating with new data\n",
-    "Ctrl-Left,       \n",
-    "Shift-Left,      Move widget selection left\n",
-    "H, A             \n",
-    "Ctrl-Right,      \n",
-    "Shift-Right,     Move widget selection right\n",
-    "L, D             \n",
-    "Ctrl-Up,         \n",
-    "Shift-Up,        Move widget selection up\n",
-    "K, W             \n",
-    "Ctrl-Down,       \n",
-    "Shift-Down,      Move widget selection down\n",
-    "J, S             \n",
-    "Left, h          Move left within widget\n",
-    "Down, j          Move down within widget\n",
-    "Up, k            Move up within widget\n",
-    "Right, l         Move right within widget\n",
-    "?                Open help menu\n",
-    "gg               Jump to the first entry\n",
-    "G                Jump to the last entry\n",
-    "e                Toggle expanding the currently selected widget\n",
-    "+                Zoom in on chart (decrease time range)\n",
-    "-                Zoom out on chart (increase time range)\n",
-    "=                Reset zoom\n",
+    "1 - General",
+    "q, Ctrl-c        Quit",
+    "Esc              Close dialog windows, search, widgets, or exit expanded mode",
+    "Ctrl-r           Reset display and any collected data",
+    "f                Freeze/unfreeze updating with new data",
+    "Ctrl-Left,       ",
+    "Shift-Left,      Move widget selection left",
+    "H, A             ",
+    "Ctrl-Right,      ",
+    "Shift-Right,     Move widget selection right",
+    "L, D             ",
+    "Ctrl-Up,         ",
+    "Shift-Up,        Move widget selection up",
+    "K, W             ",
+    "Ctrl-Down,       ",
+    "Shift-Down,      Move widget selection down",
+    "J, S             ",
+    "Left, h          Move left within widget",
+    "Down, j          Move down within widget",
+    "Up, k            Move up within widget",
+    "Right, l         Move right within widget",
+    "?                Open help menu",
+    "gg               Jump to the first entry",
+    "G                Jump to the last entry",
+    "e                Toggle expanding the currently selected widget",
+    "+                Zoom in on chart (decrease time range)",
+    "-                Zoom out on chart (increase time range)",
+    "=                Reset zoom",
     "Mouse scroll     Scroll through the tables or zoom in/out of charts by scrolling up/down",
 ];
 
@@ -90,87 +90,87 @@ pub const CPU_HELP_TEXT: [&str; 2] = [
 // TODO [Help]: Search in help?
 // TODO [Help]: Move to using tables for easier formatting?
 pub const PROCESS_HELP_TEXT: [&str; 13] = [
-    "3 - Process widget\n",
-    "dd               Kill the selected process\n",
-    "c                Sort by CPU usage, press again to reverse sorting order\n",
-    "m                Sort by memory usage, press again to reverse sorting order\n",
-    "p                Sort by PID name, press again to reverse sorting order\n",
-    "n                Sort by process name, press again to reverse sorting order\n",
-    "Tab              Group/un-group processes with the same name\n",
-    "Ctrl-f, /        Open process search widget\n",
-    "P                Toggle between showing the full command or just the process name\n",
-    "s, F6            Open process sort widget\n",
-    "I                Invert current sort\n",
-    "%                Toggle between values and percentages for memory usage\n",
+    "3 - Process widget",
+    "dd               Kill the selected process",
+    "c                Sort by CPU usage, press again to reverse sorting order",
+    "m                Sort by memory usage, press again to reverse sorting order",
+    "p                Sort by PID name, press again to reverse sorting order",
+    "n                Sort by process name, press again to reverse sorting order",
+    "Tab              Group/un-group processes with the same name",
+    "Ctrl-f, /        Open process search widget",
+    "P                Toggle between showing the full command or just the process name",
+    "s, F6            Open process sort widget",
+    "I                Invert current sort",
+    "%                Toggle between values and percentages for memory usage",
     "t, F5            Toggle tree mode",
 ];
 
 pub const SEARCH_HELP_TEXT: [&str; 46] = [
-    "4 - Process search widget\n",
-    "Tab              Toggle between searching for PID and name\n",
-    "Esc              Close the search widget (retains the filter)\n",
-    "Ctrl-a           Skip to the start of the search query\n",
-    "Ctrl-e           Skip to the end of the search query\n",
-    "Ctrl-u           Clear the current search query\n",
-    "Backspace        Delete the character behind the cursor\n",
-    "Delete           Delete the character at the cursor\n",
-    "Alt-c, F1        Toggle matching case\n",
-    "Alt-w, F2        Toggle matching the entire word\n",
-    "Alt-r, F3        Toggle using regex\n",
-    "Left, Alt-h      Move cursor left\n",
-    "Right, Alt-l     Move cursor right\n",
-    "\n",
-    "Supported search types:\n",
-    "<by name/cmd>    ex: btm\n",
-    "pid              ex: pid 825\n",
-    "cpu, cpu%        ex: cpu > 4.2\n",
-    "mem, mem%        ex: mem < 4.2\n",
-    "memb             ex: memb < 100 kb\n",
-    "read, r/s        ex: read >= 1 b\n",
-    "write, w/s       ex: write <= 1 tb\n",
-    "tread, t.read    ex: tread = 1\n",
-    "twrite, t.write  ex: twrite = 1\n",
-    "state            ex: state = running\n",
-    "\n",
-    "Comparison operators:\n",
-    "=                ex: cpu = 1\n",
-    ">                ex: cpu > 1\n",
-    "<                ex: cpu < 1\n",
-    ">=               ex: cpu >= 1\n",
-    "<=               ex: cpu <= 1\n",
-    "\n",
-    "Logical operators:\n",
-    "and, &&, <Space> ex: btm and cpu > 1 and mem > 1\n",
-    "or, ||           ex: btm or firefox\n",
-    "\n",
-    "Supported units:\n",
-    "B                ex: read > 1 b\n",
-    "KB               ex: read > 1 kb\n",
-    "MB               ex: read > 1 mb\n",
-    "TB               ex: read > 1 tb\n",
-    "KiB              ex: read > 1 kib\n",
-    "MiB              ex: read > 1 mib\n",
-    "GiB              ex: read > 1 gib\n",
+    "4 - Process search widget",
+    "Tab              Toggle between searching for PID and name",
+    "Esc              Close the search widget (retains the filter)",
+    "Ctrl-a           Skip to the start of the search query",
+    "Ctrl-e           Skip to the end of the search query",
+    "Ctrl-u           Clear the current search query",
+    "Backspace        Delete the character behind the cursor",
+    "Delete           Delete the character at the cursor",
+    "Alt-c, F1        Toggle matching case",
+    "Alt-w, F2        Toggle matching the entire word",
+    "Alt-r, F3        Toggle using regex",
+    "Left, Alt-h      Move cursor left",
+    "Right, Alt-l     Move cursor right",
+    "",
+    "Supported search types:",
+    "<by name/cmd>    ex: btm",
+    "pid              ex: pid 825",
+    "cpu, cpu%        ex: cpu > 4.2",
+    "mem, mem%        ex: mem < 4.2",
+    "memb             ex: memb < 100 kb",
+    "read, r/s        ex: read >= 1 b",
+    "write, w/s       ex: write <= 1 tb",
+    "tread, t.read    ex: tread = 1",
+    "twrite, t.write  ex: twrite = 1",
+    "state            ex: state = running",
+    "",
+    "Comparison operators:",
+    "=                ex: cpu = 1",
+    ">                ex: cpu > 1",
+    "<                ex: cpu < 1",
+    ">=               ex: cpu >= 1",
+    "<=               ex: cpu <= 1",
+    "",
+    "Logical operators:",
+    "and, &&, <Space> ex: btm and cpu > 1 and mem > 1",
+    "or, ||           ex: btm or firefox",
+    "",
+    "Supported units:",
+    "B                ex: read > 1 b",
+    "KB               ex: read > 1 kb",
+    "MB               ex: read > 1 mb",
+    "TB               ex: read > 1 tb",
+    "KiB              ex: read > 1 kib",
+    "MiB              ex: read > 1 mib",
+    "GiB              ex: read > 1 gib",
     "TiB              ex: read > 1 tib",
 ];
 
 pub const SORT_HELP_TEXT: [&str; 6] = [
     "5 - Sort widget\n",
-    "Down, 'j'        Scroll down in list\n",
-    "Up, 'k'          Scroll up in list\n",
-    "Mouse scroll     Scroll through sort widget\n",
-    "Esc              Close the sort widget\n",
+    "Down, 'j'        Scroll down in list",
+    "Up, 'k'          Scroll up in list",
+    "Mouse scroll     Scroll through sort widget",
+    "Esc              Close the sort widget",
     "Enter            Sort by current selected column",
 ];
 
 pub const BATTERY_HELP_TEXT: [&str; 3] = [
-    "6 - Battery widget\n",
-    "Left             Go to previous battery\n",
+    "6 - Battery widget",
+    "Left             Go to previous battery",
     "Right            Go to next battery",
 ];
 
 pub const BASIC_MEM_HELP_TEXT: [&str; 2] = [
-    "7 - Basic memory widget\n",
+    "7 - Basic memory widget",
     "%                Toggle between values and percentages for memory usage",
 ];