diff --git a/src/app/helpers/block_title.rs b/src/app/helpers/block_title.rs
new file mode 100644
index 0000000..5bf484f
--- /dev/null
+++ b/src/app/helpers/block_title.rs
@@ -0,0 +1,99 @@
+use ratatui::{
+ style::{Color, Style, Stylize},
+ text::{Line, Span},
+};
+
+/// Creates a styled title `Line` for a UI block.
+///
+/// This helper builds a `Line` that looks like:
+/// ```text
+/// [
... ]
+/// ```
+/// where each title fragment is rendered in the color supplied by the caller,
+/// and the surrounding brackets and any separator are rendered in gray.
+///
+/// # Arguments
+///
+/// * `texts` – A slice of tuples. Each tuple contains a title fragment (`&str`)
+/// and a `Color` that should be applied to that fragment. The order of the
+/// tuples determines the order of the fragments in the final line.
+///
+/// * `separator` – An optional string that will be inserted (in gray) between
+/// successive title fragments. If `None` the fragments are concatenated
+/// directly without any separator.
+///
+/// # Returns
+///
+/// A `Line<'a>` that starts with a gray “\[ ”, contains the colored title
+/// fragments separated (if requested) by a gray separator, and ends with a
+/// gray “ \]”. The line can be passed directly to widgets such as `Block::title`.
+///
+/// # Example
+///
+/// ```rust
+/// use ratatui::style::Color;
+///
+/// let title: Line<'_> = block_title_helper(
+/// &[
+/// ("Skirmish", Color::Magenta),
+/// ("Map", Color::Green),
+/// ],
+/// Some(" - ")
+/// );
+/// // `title` now represents: [ Skirmish - Map ] with the appropriate colors.
+/// ```
+pub fn block_title_helper<'a>(texts: &[(&'a str, Color)], separator: Option<&'a str>) -> Line<'a> {
+ let mut line: Line<'a> = Line::default();
+
+ line.spans.push(Span::from("[ ").gray());
+
+ for (i, (text, color)) in texts.iter().enumerate() {
+ let span: Span<'a> = Span::styled(*text, Style::new().fg(*color));
+
+ line.spans.push(span);
+
+ if let Some(sep) = separator {
+ if i + 1 < texts.len() {
+ line.spans.push(Span::from(sep).gray());
+ }
+ }
+ }
+
+ line.spans.push(Span::from(" ]").gray());
+
+ line
+}
+
+/// Convenience wrapper for `block_title_helper` that creates a title line for a
+/// single piece of text.
+///
+/// This function builds a `Line` that looks like:
+/// ```text
+/// [ ]
+/// ```
+/// where `` is rendered with the supplied color, and the surrounding
+/// brackets are rendered in gray. It simply forwards the arguments to
+/// `block_title_helper` with `separator` set to `None`.
+///
+/// # Arguments
+///
+/// * `text` – The title string to display.
+///
+/// * `color` – The `Color` that should be applied to the title text.
+///
+/// # Returns
+///
+/// A `Line<'a>` containing the grey brackets and the coloured title text,
+/// ready to be passed to widgets such as `Block::title`.
+///
+/// # Example
+///
+/// ```rust
+/// use ratatui::style::Color;
+///
+/// let title: Line<'_> = block_single_title_helper("Keybindings", Color::Magenta);
+/// // `title` now represents: [ Keybindings ] with “Keybindings” coloured magenta.
+/// ```
+pub fn block_single_title_helper<'a>(text: &'a str, color: Color) -> Line<'a> {
+ block_title_helper(&[(text, color)], None)
+}
diff --git a/src/app/helpers/mod.rs b/src/app/helpers/mod.rs
new file mode 100644
index 0000000..5ccb37f
--- /dev/null
+++ b/src/app/helpers/mod.rs
@@ -0,0 +1,3 @@
+pub mod block_title;
+
+pub use block_title::{block_single_title_helper, block_title_helper};
diff --git a/src/app/mod.rs b/src/app/mod.rs
index 1f32052..a6924d3 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -1,4 +1,5 @@
pub mod app;
+pub mod helpers;
pub mod keybind;
pub mod keybindings;
pub mod state;
diff --git a/src/app/views/skirmish.rs b/src/app/views/skirmish.rs
index fe4543a..f3cfdc9 100644
--- a/src/app/views/skirmish.rs
+++ b/src/app/views/skirmish.rs
@@ -1,12 +1,13 @@
use crate::app::{
App,
+ helpers::block_title_helper,
keybindings::{Action, count_largest_group},
widgets::{BoardWidget, KeybindingsWidget},
};
use ratatui::{
buffer::Buffer,
layout::{Alignment, Constraint, Layout, Margin, Rect},
- style::Stylize,
+ style::{Color, Stylize},
text::Line,
widgets::{Block, Borders, Padding, Paragraph, Widget},
};
@@ -69,13 +70,10 @@ pub fn skirmish_view(app: &mut App, area: Rect, buf: &mut Buffer) {
{
let board_block: Block = Block::new()
.gray()
- .title(Line::from_iter([
- "[ ".gray(),
- "Skirmish".magenta(),
- " - ".gray(),
- "Map".green(),
- " ]".gray(),
- ]))
+ .title(block_title_helper(
+ &[("Skirmish", Color::Magenta), ("Map", Color::Green)],
+ Some(" - "),
+ ))
.borders(Borders::LEFT | Borders::TOP | Borders::RIGHT);
let board_area: Rect = board_block.inner(main_area);
diff --git a/src/app/widgets/keybindings.rs b/src/app/widgets/keybindings.rs
index 4ced29d..ddc9814 100644
--- a/src/app/widgets/keybindings.rs
+++ b/src/app/widgets/keybindings.rs
@@ -1,9 +1,12 @@
-use crate::app::keybindings::{Action, Group, KeyBinding, binding_for};
+use crate::app::{
+ helpers::block_single_title_helper,
+ keybindings::{Action, Group, KeyBinding, binding_for},
+};
use clap::ValueEnum;
use ratatui::{
buffer::Buffer,
layout::{Alignment, Constraint, Layout, Rect},
- style::Stylize,
+ style::{Color, Stylize},
text::Line,
widgets::{Block, Borders, Padding, Paragraph, Widget},
};
@@ -72,11 +75,7 @@ impl Widget for KeybindingsWidget {
let block: Block<'_> = Block::default()
.borders(Borders::ALL)
- .title(Line::from_iter([
- "[ ".gray(),
- "Keybindings".magenta(),
- " ]".gray(),
- ]));
+ .title(block_single_title_helper("Keybindings", Color::Magenta));
let inner: Rect = block.inner(area);