1
0
Fork 0
forked from wry/wry
wry/src/tree/tab_bar.rs
entailz f056727621 Add support for experimental xx_foreign_toplevel_geometry_tracker_v1 (#1)
Co-authored-by: entailz <entail-wraps0r@icloud.com>
Reviewed-on: wry/wry#1
Co-authored-by: entailz <entailzwrapped@proton.me>
Co-committed-by: entailz <entailzwrapped@proton.me>
2026-04-30 17:08:19 -04:00

110 lines
3.5 KiB
Rust

use {
crate::{scale::Scale, state::State, text::TextTexture, theme::Color, tree::NodeId},
std::{
cell::{Cell, RefCell},
rc::Rc,
},
};
/// A single entry (tab) within a tab bar.
pub struct TabBarEntry {
/// The node ID of the child this tab represents.
pub child_id: NodeId,
/// The display title of the tab.
pub title: String,
/// Pre-rendered text texture for the tab title.
pub title_texture: Rc<RefCell<Option<TextTexture>>>,
/// Whether this is the active (visible) tab.
pub active: bool,
/// Whether this tab's window has requested attention.
pub attention_requested: bool,
/// X offset of this tab within the tab bar (relative to tab bar start).
pub x: Cell<i32>,
/// Width of this tab in pixels.
pub width: Cell<i32>,
}
/// A tab bar rendered above a container in mono (tabbed) mode.
pub struct TabBar {
/// The individual tab entries.
pub entries: Vec<TabBarEntry>,
/// Height of the tab bar in pixels (from theme).
pub height: i32,
/// The output scale at which text textures were rendered.
pub render_scale: Scale,
}
impl TabBar {
/// Create a new empty tab bar.
pub fn new(height: i32, render_scale: Scale) -> Self {
Self {
entries: Vec::new(),
height,
render_scale,
}
}
/// Recompute the positions and widths of all tab entries.
///
/// `total_width` is the available width for the entire tab bar.
pub fn layout_entries(&self, total_width: i32, padding: i32) {
let n = self.entries.len() as i32;
if n == 0 {
return;
}
let total_padding = padding * (n + 1);
let available = (total_width - total_padding).max(0);
let per_tab = available / n;
let mut remainder = available - per_tab * n;
let mut x = padding;
for entry in &self.entries {
let w = if remainder > 0 {
remainder -= 1;
per_tab + 1
} else {
per_tab
};
entry.x.set(x);
entry.width.set(w);
x += w + padding;
}
}
/// Find the tab entry index at the given x coordinate (relative to tab bar).
///
/// Returns `None` if the coordinate is in padding between tabs or out of bounds.
pub fn entry_at_x(&self, x: i32) -> Option<usize> {
for (i, entry) in self.entries.iter().enumerate() {
let ex = entry.x.get();
let ew = entry.width.get();
if x >= ex && x < ex + ew {
return Some(i);
}
}
None
}
/// Get the colors for a tab entry based on its state.
pub fn entry_colors(state: &State, entry: &TabBarEntry) -> (Color, Color, Color) {
let theme = &state.theme;
if entry.attention_requested {
(
theme.colors.tab_attention_background.get(),
theme.colors.tab_inactive_border.get(),
theme.colors.tab_active_text.get(),
)
} else if entry.active {
(
theme.colors.tab_active_background.get(),
theme.colors.tab_active_border.get(),
theme.colors.tab_active_text.get(),
)
} else {
(
theme.colors.tab_inactive_background.get(),
theme.colors.tab_inactive_border.get(),
theme.colors.tab_inactive_text.get(),
)
}
}
}