Feature Request: Expose Block Title Rects

Summary

Block computes the position of each title during rendering but does not expose this information. Interactive applications need title rects for mouse click hit-testing.

The Problem

Building clickable TUI interfaces requires knowing where widgets render. When a block has multiple titles (e.g., a left-aligned title and a right-aligned toggle label), each title occupies a specific screen region. The application cannot query these regions.

Currently, the only options are:

  1. Recompute the layout manually. Duplicate the logic from render_left_titles, render_right_titles, and render_center_titles. This is fragileβ€”any upstream change breaks the user’s code.

  2. Use coarse hit-testing. Check if a click is anywhere in the block’s top border row. This cannot distinguish between multiple titles.

Neither approach is satisfactory.

Use Case

Consider a TUI with a tabbed interface where the block’s title bar contains:

β”ŒAnnounce v0.7.3───────────────────────────────emate┐
β”‚ Preview Email  β–Έ  Preview Commit  β–Έ  Announce   β”‚
β”‚                                                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The application wants to:

  1. Detect clicks on "emate" and toggle the email client

  2. Detect clicks on tab names and switch tabs

  3. Ignore clicks on the border characters

Without title rects, the application must manually compute where "emate" renders based on block width, borders, alignment, and title content. This duplicates private logic from Block::render_right_titles.

Current State (v0.30.0)

Block has private methods that compute title areas:

// Private - not accessible to users
fn titles_area(&self, area: Rect, position: Position) -> Rect { ... }
fn render_left_titles(&self, position: Position, area: Rect, buf: &mut Buffer) { ... }
fn render_center_titles(&self, position: Position, area: Rect, buf: &mut Buffer) { ... }
fn render_right_titles(&self, position: Position, area: Rect, buf: &mut Buffer) { ... }

The titles_area method computes the general title region. The render_*_titles methods compute individual title rects during rendering but do not expose them.

Proposed API

Following the pattern established by Block::inner(area), add a pure computation method that takes an area and returns computed sub-rects without rendering:

impl Block {
    /// Returns the bounding rect for each title given an area.
    ///
    /// The rects are returned in the same order as titles were added.
    /// Useful for hit-testing mouse clicks against specific titles.
    ///
    /// # Example
    ///
    ///

rust /// let block = Block::bordered() /// .title_top(β€œLeft Title”) /// .title_top(Line::from(β€œRight”).right_aligned()); /// /// let rects = block.title_rects(area); /// if rects.contains(mouse_position) { /// // Clicked on β€œRight” /// } /// pub fn title_rects(&self, area: Rect) -> Vec<Rect> { ... } }

Alternatively, expose the individual areas by alignment:

/// Returns the rect for the title at the given index.
pub fn title_rect(&self, area: Rect, index: usize) -> Option<Rect> { ... }

/// Returns the titles area for a given position (top or bottom).
pub fn titles_area(&self, area: Rect, position: Position) -> Rect { ... }

Workaround

Without this API, users must replicate the title layout algorithm. Here is the current approach used in RatatuiRuby:

// Manually compute right-aligned title position
let email_label_width = email_client.len() as u16;
let email_label_x = area.x + area.width - email_label_width - 2; // border + padding
let email_label_rect = Rect::new(email_label_x, area.y, email_label_width, 1);

if email_label_rect.contains(click_position) {
    toggle_email_client();
}

This works for simple cases but breaks when:

Impact

This feature benefits any application with interactive block titles:

Related discussion: ratatui#738 (title positioning behavior)


This issue includes creative contributions from Claude (Anthropic) via Antigravity (Google). declare-ai.org/1.0.0/creative.html

Discovered while implementing click handling for block title toggles in RatatuiRuby.