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>>, /// 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, /// Width of this tab in pixels. pub width: Cell, } /// A tab bar rendered above a container in mono (tabbed) mode. pub struct TabBar { /// The individual tab entries. pub entries: Vec, /// 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 { 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(), ) } } }