Skip to main content

ratatui_unity/ffi/
layout.rs

1//! Layout splitting, inner-margin areas, and input hit-testing.
2
3use crate::commands::do_split;
4use crate::ffi::util::{slice_from, slice_mut_from, state_mut, state_ref};
5use std::ffi::c_void;
6
7/// Returns the id of the root area, which always covers the whole terminal.
8///
9/// The root id is the constant `0`; this getter exists for symmetry with the
10/// host-side area API.
11#[no_mangle]
12pub extern "C" fn ratatui_root_area(_handle: *const c_void) -> u32 { 0 }
13
14/// Splits an existing area into `count` child areas and writes their ids into
15/// `out_ids`.
16///
17/// # Parameters
18/// - `area_id`: id of the parent area to split.
19/// - `direction`: `0` = horizontal split (left → right), any other value =
20///   vertical split (top → bottom).
21/// - `constraint_types`: array of length `count` describing each child's
22///   constraint kind. Values: `0` = Length, `1` = Min, `2` = Max,
23///   `3` = Percentage, `4` (or any other) = Fill.
24/// - `constraint_values`: array of length `count` with the numeric value
25///   matching the constraint kind (cells for Length/Min/Max, 0..=100 for
26///   Percentage, weight for Fill).
27/// - `count`: number of child areas requested.
28/// - `out_ids`: caller-allocated buffer of length `count` that receives the
29///   ids of the newly registered child areas.
30///
31/// # Returns
32/// The number of child areas actually written. Returns `0` if any required
33/// pointer is null, `count` is zero, or the parent id is unknown.
34#[no_mangle]
35pub extern "C" fn ratatui_split(
36    handle: *mut c_void,
37    area_id: u32,
38    direction: u8,
39    constraint_types: *const u8,
40    constraint_values: *const u16,
41    count: u32,
42    out_ids: *mut u32,
43) -> u32 {
44    if count == 0
45        || constraint_types.is_null()
46        || constraint_values.is_null()
47        || out_ids.is_null()
48    {
49        return 0;
50    }
51    let Some(state) = state_mut(handle) else { return 0; };
52    let n = count as usize;
53    let types = slice_from(constraint_types, n);
54    let values = slice_from(constraint_values, n);
55    let Some(out) = slice_mut_from(out_ids, n) else { return 0; };
56    do_split(state, area_id, direction, types, values, out)
57}
58
59/// Returns a new area id covering the inner rectangle of `area_id` shrunk by
60/// the given margins on each side.
61///
62/// # Parameters
63/// - `area_id`: parent area id.
64/// - `horizontal`: cells to remove from the left and right edges.
65/// - `vertical`: cells to remove from the top and bottom edges.
66///
67/// # Returns
68/// The id of the newly registered inner area, or [`u32::MAX`] if `handle` is
69/// null or `area_id` is unknown.
70#[no_mangle]
71pub extern "C" fn ratatui_inner(
72    handle: *mut c_void,
73    area_id: u32,
74    horizontal: u16,
75    vertical: u16,
76) -> u32 {
77    let Some(state) = state_mut(handle) else { return u32::MAX; };
78    let area = match state.area_map.get(&area_id).copied() {
79        Some(r) => r,
80        None => return u32::MAX,
81    };
82    use ratatui::layout::Margin;
83    let inner = area.inner(Margin { horizontal, vertical });
84    state.register_area(inner)
85}
86
87/// Returns the most specific area id covering the given terminal cell.
88///
89/// When several registered areas contain `(col, row)` the one with the
90/// smallest cell count (the most deeply nested) wins. Returns `0` (root) when
91/// no registered area matches.
92///
93/// Useful for mapping pointer input back into the layout tree.
94#[no_mangle]
95pub extern "C" fn ratatui_hit_test(
96    handle: *mut c_void,
97    col: u16,
98    row: u16,
99) -> u32 {
100    let Some(state) = state_mut(handle) else { return 0; };
101    let mut best_id = 0u32;
102    let mut best_area = u32::MAX;
103
104    for (&id, &rect) in &state.area_map {
105        if col >= rect.x && col < rect.x + rect.width
106            && row >= rect.y && row < rect.y + rect.height
107        {
108            let area = (rect.width as u32) * (rect.height as u32);
109            if area < best_area {
110                best_area = area;
111                best_id = id;
112            }
113        }
114    }
115    best_id
116}
117
118/// Returns the cell-space rectangle of the given area as a packed `u64`.
119///
120/// The four `u16` fields are packed little-endian:
121///
122/// ```text
123/// bits  0..16  -> x
124/// bits 16..32  -> y
125/// bits 32..48  -> width
126/// bits 48..64  -> height
127/// ```
128///
129/// Returns `0` if `handle` is null or the area id is unknown.
130#[no_mangle]
131pub extern "C" fn ratatui_get_area_rect(
132    handle: *const c_void,
133    area_id: u32,
134) -> u64 {
135    let Some(state) = state_ref(handle) else { return 0; };
136    match state.area_map.get(&area_id) {
137        Some(rect) => {
138            (rect.x as u64)
139                | ((rect.y as u64) << 16)
140                | ((rect.width as u64) << 32)
141                | ((rect.height as u64) << 48)
142        }
143        None => 0,
144    }
145}