git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commitf8a216c3ce4d1da0bc0ce5e371a4e66be87d0e8a
parent8350e02cb8
authorLucas Galante <[email protected]>
date2026-06-21 09:52
Remove Paginator widget and tab margin spinboxes

 src/layout.rs                     |  105 ---
 src/main.rs                       |    2 +-
 src/widget/container/menu.rs      |    2 +-
 src/widget/container/mod.rs       |    2 -
 src/widget/container/paginator.rs | 1283 -------------------------------------
 src/widget/json_layout.rs         |   69 +-
 src/widget/mod.rs                 |    2 +-
 7 files changed, 14 insertions(+), 1451 deletions(-)

diff --git a/src/layout.rs b/src/layout.rs
index 7fc1b97..0b654b5 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -87,8 +87,6 @@ static SECTION_LABEL_FONT: RwLock<String> = RwLock::new(String::new());
 static NESTED_SECTION_LABEL_FONT: RwLock<String> = RwLock::new(String::new());
 static BREADCRUMB_FONT: RwLock<String> = RwLock::new(String::new());
 
-static PAGINATOR_TAB_MARGIN_X: RwLock<f32> = RwLock::new(5.0);
-static PAGINATOR_TAB_MARGIN_Y: RwLock<f32> = RwLock::new(10.0);
 static PAGINATOR_TAB_PADDING_X: RwLock<f32> = RwLock::new(10.0);
 static BUTTON_PADDING: RwLock<f32> = RwLock::new(14.0);
 
@@ -402,35 +400,6 @@ pub fn reload_config() {
                     }
                 }
             }
-            if let Some(rest) = trimmed.strip_prefix("paginator_tab_margin_x") {
-                let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
-                let val_str = rest.trim_end_matches('"').trim();
-                if let Ok(val) = val_str.parse::<f32>() {
-                    if let Ok(mut lock) = PAGINATOR_TAB_MARGIN_X.write() {
-                        *lock = val;
-                    }
-                }
-            } else if let Some(rest) = trimmed.strip_prefix("paginator_tab_margin") {
-                let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
-                let val_str = rest.trim_end_matches('"').trim();
-                if let Ok(val) = val_str.parse::<f32>() {
-                    if let Ok(mut lock) = PAGINATOR_TAB_MARGIN_X.write() {
-                        *lock = val;
-                    }
-                    if let Ok(mut lock) = PAGINATOR_TAB_MARGIN_Y.write() {
-                        *lock = val;
-                    }
-                }
-            }
-            if let Some(rest) = trimmed.strip_prefix("paginator_tab_margin_y") {
-                let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
-                let val_str = rest.trim_end_matches('"').trim();
-                if let Ok(val) = val_str.parse::<f32>() {
-                    if let Ok(mut lock) = PAGINATOR_TAB_MARGIN_Y.write() {
-                        *lock = val;
-                    }
-                }
-            }
             if let Some(rest) = trimmed.strip_prefix("paginator_tab_padding_x") {
                 let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
                 let val_str = rest.trim_end_matches('"').trim();
@@ -1403,81 +1372,7 @@ pub fn set_color_selector_preview_margin(margin: f32) {
     }
 }
 
-pub fn paginator_tab_margin_x() -> f32 {
-    use std::sync::Once;
-    static INIT: Once = Once::new();
-    INIT.call_once(|| {
-        if let Some(content) = read_config() {
-            let mut general_margin = None;
-            let mut x_margin = None;
-            for line in content.lines() {
-                let trimmed = line.trim();
-                if let Some(rest) = trimmed.strip_prefix("paginator_tab_margin_x") {
-                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
-                    let val_str = rest.trim_end_matches('"').trim();
-                    if let Ok(val) = val_str.parse::<f32>() {
-                        x_margin = Some(val);
-                    }
-                } else if let Some(rest) = trimmed.strip_prefix("paginator_tab_margin") {
-                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
-                    let val_str = rest.trim_end_matches('"').trim();
-                    if let Ok(val) = val_str.parse::<f32>() {
-                        general_margin = Some(val);
-                    }
-                }
-            }
-            let val = x_margin.or(general_margin).unwrap_or(5.0);
-            if let Ok(mut lock) = PAGINATOR_TAB_MARGIN_X.write() {
-                *lock = val;
-            }
-        }
-    });
-    *PAGINATOR_TAB_MARGIN_X.read().unwrap()
-}
-
-pub fn set_paginator_tab_margin_x(margin: f32) {
-    if let Ok(mut lock) = PAGINATOR_TAB_MARGIN_X.write() {
-        *lock = margin;
-    }
-}
-
-pub fn paginator_tab_margin_y() -> f32 {
-    use std::sync::Once;
-    static INIT: Once = Once::new();
-    INIT.call_once(|| {
-        if let Some(content) = read_config() {
-            let mut general_margin = None;
-            let mut y_margin = None;
-            for line in content.lines() {
-                let trimmed = line.trim();
-                if let Some(rest) = trimmed.strip_prefix("paginator_tab_margin_y") {
-                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
-                    let val_str = rest.trim_end_matches('"').trim();
-                    if let Ok(val) = val_str.parse::<f32>() {
-                        y_margin = Some(val);
-                    }
-                } else if let Some(rest) = trimmed.strip_prefix("paginator_tab_margin") {
-                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
-                    let val_str = rest.trim_end_matches('"').trim();
-                    if let Ok(val) = val_str.parse::<f32>() {
-                        general_margin = Some(val);
-                    }
-                }
-            }
-            let val = y_margin.or(general_margin).unwrap_or(10.0);
-            if let Ok(mut lock) = PAGINATOR_TAB_MARGIN_Y.write() {
-                *lock = val;
-            }
-        }
-    });
-    *PAGINATOR_TAB_MARGIN_Y.read().unwrap()
-}
 
-pub fn set_paginator_tab_margin_y(margin: f32) {
-    if let Ok(mut lock) = PAGINATOR_TAB_MARGIN_Y.write() {
-        *lock = margin;
-    }
-}
 
 pub fn paginator_tab_padding_x() -> f32 {
     use std::sync::Once;
diff --git a/src/main.rs b/src/main.rs
index 1ebefaa..c594014 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1240,7 +1240,7 @@ impl PointerHandler for AppState {
                                 }
                                 if btn == cce_ui::widget::MouseButton::Left {
                                     for w in &mut jl.widgets {
-                                        let active_page = jl.paginator.as_ref().map(|p| p.selected_page()).unwrap_or(0);
+                                        let active_page = 0;
                                         if w.page_idx != active_page {
                                             continue;
                                         }
diff --git a/src/widget/container/menu.rs b/src/widget/container/menu.rs
index 0c5a465..4dffcbf 100644
--- a/src/widget/container/menu.rs
+++ b/src/widget/container/menu.rs
@@ -1511,7 +1511,7 @@ impl PageSelector for MenuBar {
 
     fn sidebar_w(&self) -> f32 {
         let padding_x = crate::layout::paginator_tab_padding_x();
-        let margin_x = crate::layout::paginator_tab_margin_x();
+        let margin_x = 5.0;
         if self.vertical {
             (12.0 + 2.0 * padding_x).max(24.0) + 2.0 * margin_x
         } else {
diff --git a/src/widget/container/mod.rs b/src/widget/container/mod.rs
index 277cbcc..dda794f 100644
--- a/src/widget/container/mod.rs
+++ b/src/widget/container/mod.rs
@@ -9,7 +9,6 @@ pub mod spreadsheet;
 pub mod scroll_box;
 pub mod scrolling_list;
 pub mod plate;
-pub mod paginator;
 pub mod switcher;
 pub mod layer;
 pub mod page;
@@ -26,7 +25,6 @@ pub use spreadsheet::Spreadsheet;
 pub use scroll_box::ScrollBox;
 pub use scrolling_list::ScrollingList;
 pub use plate::Plate;
-pub use paginator::Paginator;
 pub use switcher::Switcher;
 pub use layer::Layer;
 pub use page::Page;
diff --git a/src/widget/container/paginator.rs b/src/widget/container/paginator.rs
deleted file mode 100644
index 134ddcf..0000000
--- a/src/widget/container/paginator.rs
+++ /dev/null
@@ -1,1283 +0,0 @@
-use crate::colors;
-use crate::widget::*;
-use crate::widget::input::get_font_db;
-use crate::widget::display::TextLabel;
-use super::plate::Plate;
-use super::menu::MenuBar;
-
-pub struct Paginator {
-    x: f32,
-    y: f32,
-    w: f32,
-    h: f32,
-    hovered: bool,
-    pub sidebar_menu: MenuBar,
-    pub plates: Vec<Plate>,
-    pub selected_page: usize,
-    pub page_changed: bool,
-    pub sidebar_label: Option<String>,
-    pub tab_text_quads: Vec<Vec<(f32, f32, f32, f32, [f32; 4])>>,
-    pub sidebar_scroll_y: f32,
-    pub scale_factor: f32,
-    pub sidebar_mode: bool,
-    pub page_hidden: bool,
-    pub sidebar_w: f32,
-    pub pages: Vec<String>,
-    pub tabs_at_top: bool,
-    pub tab_y_offset: f32,
-    pub tabs_rotated: bool,
-    pub hovered_tab: Option<usize>,
-    pub pressed_tab: Option<usize>,
-    pub target_y: f32,
-    pub target_x: f32,
-    pub current_y: Option<f32>,
-    pub current_x: Option<f32>,
-    parent: Option<*mut (dyn Element + 'static)>,
-    pub context_options: Vec<String>,
-    pub context_selected: usize,
-    pub context_just_changed: bool,
-    pub tab_quads_cache: std::collections::HashMap<String, Vec<(f32, f32, f32, f32, [f32; 4])>>,
-    pub on_page_changed_cb: Option<Box<dyn Fn(usize) + Send + Sync>>,
-}
-
-impl Paginator {
-    pub fn sidebar_w(&self) -> f32 {
-        let padding_x = crate::layout::paginator_tab_padding_x();
-        let margin_x = crate::layout::paginator_tab_margin_x();
-        if self.tabs_rotated {
-            (12.0 + 2.0 * padding_x).max(24.0) + 2.0 * margin_x
-        } else {
-            let max_tab_req_w = self.pages.iter()
-                .map(|p| p.len() as f32 * 7.5 + 2.0 * padding_x)
-                .max_by(|a, b| a.partial_cmp(b).unwrap())
-                .unwrap_or(0.0);
-            max_tab_req_w.max(24.0) + 2.0 * margin_x
-        }
-    }
-
-    pub fn update_sidebar_w(&mut self) {
-        self.sidebar_w = self.sidebar_w();
-    }
-
-    pub fn new(sidebar_w: f32, pages: Vec<String>) -> Self {
-        let num_pages = pages.len();
-        
-        let mut pag = Self {
-            x: 0.0,
-            y: 0.0,
-            w: 0.0,
-            h: 0.0,
-            hovered: false,
-            sidebar_menu: MenuBar::new(0.0, 0.0, sidebar_w, 0.0).with_vertical(true),
-            plates: Vec::new(),
-            selected_page: 0,
-            page_changed: false,
-            sidebar_label: None,
-            tab_text_quads: Vec::new(),
-            sidebar_scroll_y: 0.0,
-            scale_factor: 1.0,
-            sidebar_mode: true,
-            page_hidden: false,
-            sidebar_w,
-            pages: pages.clone(),
-            tabs_at_top: false,
-            tab_y_offset: 10.0,
-            tabs_rotated: true,
-            hovered_tab: None,
-            pressed_tab: None,
-            target_y: 10.0,
-            target_x: 0.0,
-            current_y: Some(10.0),
-            current_x: Some(0.0),
-            parent: None,
-            context_options: Vec::new(),
-            context_selected: 0,
-            context_just_changed: false,
-            tab_quads_cache: std::collections::HashMap::new(),
-            on_page_changed_cb: None,
-        };
-
-        for page in &pages {
-            pag.sidebar_menu = pag.sidebar_menu.with_item(page, &[]);
-        }
-
-        let mut plates = Vec::new();
-        for _ in 0..num_pages {
-            let mut plate = Plate::new(0.0, 0.0, 0.0, 0.0).with_draggable(false);
-            plate.visible = false;
-            plates.push(plate);
-        }
-        pag.plates = plates;
-
-        if num_pages > 0 {
-            pag.plates[0].visible = true;
-            pag.sidebar_menu.menus.set_selected(Some(0));
-        }
-
-        pag.update_sidebar_w();
-        pag.update_target_pos();
-        pag
-    }
-
-    pub fn tab_rect(&self, idx: usize) -> (f32, f32, f32, f32) {
-        if idx >= self.pages.len() {
-            return (0.0, 0.0, 0.0, 0.0);
-        }
-        let margin_x = crate::layout::paginator_tab_margin_x();
-        let margin_y = crate::layout::paginator_tab_margin_y();
-        let padding_y = crate::layout::paginator_tab_padding_y();
-        if self.tabs_at_top {
-            let tab_w = if self.pages.is_empty() { 0.0 } else { self.w / self.pages.len() as f32 };
-            let tab_h = 2.0 * padding_y + 12.0;
-            (self.x + idx as f32 * tab_w, self.y, tab_w, tab_h)
-        } else {
-            let (tab_w, tab_h) = self.vertical_tab_size();
-            let spacing = margin_y;
-            (
-                self.x + margin_x,
-                self.y + self.tab_y_offset + self.sidebar_label_height() + idx as f32 * (tab_h + spacing) - self.sidebar_scroll_y,
-                tab_w,
-                tab_h,
-            )
-        }
-    }
-
-    pub fn tab_size(&self, idx: usize) -> (f32, f32) {
-        let r = self.tab_rect(idx);
-        (r.2, r.3)
-    }
-
-
-
-    pub fn with_sidebar_mode(mut self, enabled: bool) -> Self {
-        self.sidebar_mode = enabled;
-        self
-    }
-
-    pub fn with_sidebar_label(mut self, label: &str) -> Self {
-        self.sidebar_label = Some(label.to_string());
-        if !self.context_options.is_empty() {
-            self.sidebar_menu.title = label.to_string();
-            self.sidebar_menu.label = None;
-        } else {
-            self.sidebar_menu.label = Some(label.to_string());
-            self.sidebar_menu.title = String::new();
-        }
-        self
-    }
-
-    pub fn with_context_options(mut self, options: Vec<String>, selected: usize) -> Self {
-        self.context_options = options.clone();
-        self.context_selected = selected;
-        self.sidebar_menu = self.sidebar_menu.with_context_options(options, selected);
-        self
-    }
-
-    pub fn set_context_selected(&mut self, selected: usize) {
-        self.context_selected = selected;
-        self.sidebar_menu.set_context_selected(selected);
-    }
-
-    pub fn sidebar_label_height(&self) -> f32 {
-        if let Some(ref label) = self.sidebar_label {
-            if self.tabs_rotated {
-                let font_size = 11.0;
-                let line_height = font_size * 1.2;
-                label.chars().count() as f32 * line_height + 20.0
-            } else {
-                24.0
-            }
-        } else {
-            0.0
-        }
-    }
-
-    pub fn is_page_hidden(&self) -> bool {
-        self.page_hidden
-    }
-
-    pub fn set_page_hidden(&mut self, hidden: bool) {
-        self.page_hidden = hidden;
-    }
-
-    pub fn set_sidebar_mode(&mut self, enabled: bool) {
-        self.sidebar_mode = enabled;
-    }
-
-    pub fn add_widget_to_page(&mut self, page_idx: usize, widget: *mut (dyn Element + 'static), ctx: &mut UiContext) {
-        if page_idx < self.plates.len() {
-            self.plates[page_idx].add_child(widget, ctx);
-            unsafe {
-                (*widget).set_parent(Some(&mut self.plates[page_idx] as *mut _), ctx);
-            }
-        }
-    }
-
-    pub fn clear_page_widgets(&mut self, page_idx: usize, ctx: &mut UiContext) {
-        if page_idx < self.plates.len() {
-            self.plates[page_idx].clear_children(ctx);
-        }
-    }
-
-    pub fn with_tabs_at_top(mut self, top: bool) -> Self {
-        self.tabs_at_top = top;
-        self.update_target_pos();
-        self
-    }
-
-    pub fn with_tab_y_offset(mut self, offset: f32) -> Self {
-        self.tab_y_offset = offset;
-        if self.current_y == Some(10.0) {
-            self.current_y = Some(offset);
-        }
-        self.update_target_pos();
-        self
-    }
-
-    pub fn with_tabs_rotated(mut self, rotated: bool) -> Self {
-        self.tabs_rotated = rotated;
-        self.update_target_pos();
-        self
-    }
-
-    pub fn on_page_changed<F: Fn(usize) + Send + Sync + 'static>(mut self, cb: F) -> Self {
-        self.on_page_changed_cb = Some(Box::new(cb));
-        self
-    }
-
-    pub fn selected_page(&self) -> usize {
-        self.selected_page
-    }
-
-    pub fn set_selected_page(&mut self, page: usize) {
-        if page < self.plates.len() {
-            if self.selected_page != page {
-                self.plates[self.selected_page].visible = false;
-                self.selected_page = page;
-                self.plates[self.selected_page].visible = true;
-                self.update_target_pos();
-                
-                // Update MenuBar focus/selection
-                self.sidebar_menu.menus.set_selected(Some(page));
-
-                if let Some(ref cb) = self.on_page_changed_cb {
-                    cb(page);
-                }
-            }
-        }
-    }
-
-    pub fn set_pages(&mut self, pages: Vec<String>) {
-        if self.pages == pages {
-            return;
-        }
-        self.pages = pages.clone();
-        let num_pages = pages.len();
-        
-        self.update_sidebar_w();
-        let mut sidebar_menu = MenuBar::new(0.0, 0.0, self.sidebar_w, 0.0)
-            .with_vertical(true);
-        if let Some(ref l) = self.sidebar_label {
-            if !self.context_options.is_empty() {
-                sidebar_menu = sidebar_menu.with_title(l);
-            } else {
-                sidebar_menu = sidebar_menu.with_label(l);
-            }
-        }
-        if !self.context_options.is_empty() {
-            sidebar_menu = sidebar_menu.with_context_options(self.context_options.clone(), self.context_selected);
-        }
-        for page in &pages {
-            sidebar_menu = sidebar_menu.with_item(page, &[]);
-        }
-        self.sidebar_menu = sidebar_menu;
-
-        let mut plates = Vec::new();
-        for _ in 0..num_pages {
-            let mut plate = Plate::new(0.0, 0.0, 0.0, 0.0).with_draggable(false);
-            plate.visible = false;
-            plates.push(plate);
-        }
-        self.plates = plates;
-        if self.selected_page >= num_pages {
-            self.selected_page = 0;
-        }
-        if !self.plates.is_empty() {
-            self.plates[self.selected_page].visible = true;
-            self.sidebar_menu.menus.set_selected(Some(self.selected_page));
-        }
-        self.update_sidebar_w();
-        self.update_target_pos();
-    }
-
-    pub fn set_pages_with_items(&mut self, pages: Vec<String>, _items: Vec<Vec<String>>) {
-        self.set_pages(pages);
-    }
-
-    pub fn set_scale_factor(&mut self, scale: f32) {
-        self.scale_factor = scale;
-    }
-
-    pub fn vertical_tab_size(&self) -> (f32, f32) {
-        let margin_x = crate::layout::paginator_tab_margin_x();
-        let padding_y = crate::layout::paginator_tab_padding_y();
-        let current_sidebar_w = self.sidebar_w();
-        let (_, font_size) = crate::layout::menubar_font_parsed();
-
-        if self.tabs_rotated {
-            let tab_w = (current_sidebar_w - 2.0 * margin_x).max(24.0);
-            let text_h = 64.0 * (font_size / 12.0);
-            let tab_h = 4.0 * padding_y + text_h;
-            (tab_w, tab_h)
-        } else {
-            let tab_w = current_sidebar_w - 2.0 * margin_x;
-            let tab_h = 2.0 * padding_y + font_size;
-            (tab_w, tab_h)
-        }
-    }
-
-    fn total_sidebar_height(&self) -> f32 {
-        let (_, tab_h) = self.vertical_tab_size();
-        let spacing = crate::layout::paginator_tab_margin_y();
-        let step = tab_h + spacing;
-        self.tab_y_offset + self.sidebar_label_height() + self.pages.len() as f32 * step - spacing
-    }
-
-    fn update_target_pos(&mut self) {
-        self.update_sidebar_w();
-        if self.tabs_at_top {
-            let tab_w = if self.pages.is_empty() { 0.0 } else { self.w / self.pages.len() as f32 };
-            self.target_x = self.selected_page as f32 * tab_w;
-            if self.current_x.is_none() {
-                self.current_x = Some(self.target_x);
-            }
-        } else {
-            let (_, tab_h) = self.vertical_tab_size();
-            let spacing = crate::layout::paginator_tab_margin_y();
-            let target = self.tab_y_offset + self.sidebar_label_height() + self.selected_page as f32 * (tab_h + spacing);
-            self.target_y = target;
-            if self.current_y.is_none() {
-                self.current_y = Some(target);
-            }
-        }
-        self.generate_tab_quads();
-    }
-
-    fn generate_tab_quads(&mut self) {
-        self.tab_text_quads.clear();
-        let (tab_w, _) = self.vertical_tab_size();
-        let active_color = colors::menubar_tab_label_color();
-        let active_srgb = colors::to_srgb(active_color);
-        let active_r = (active_srgb[0] * 255.0) as u8;
-        let active_g = (active_srgb[1] * 255.0) as u8;
-        let active_b = (active_srgb[2] * 255.0) as u8;
-        let inactive_r = (active_r as f32 * 0.78) as u8;
-        let inactive_g = (active_g as f32 * 0.78) as u8;
-        let inactive_b = (active_b as f32 * 0.78) as u8;
-
-        let (font_fam, font_size) = crate::layout::menubar_font_parsed();
-        let scale = crate::scale::scale_factor().max(1.0);
-
-        for (i, page_name) in self.pages.iter().enumerate() {
-            let color = if self.selected_page == i {
-                [active_r, active_g, active_b]
-            } else {
-                [inactive_r, inactive_g, inactive_b]
-            };
-            let hex_color = format!("#{:02X}{:02X}{:02X}", color[0], color[1], color[2]);
-
-            let trimmed = page_name.trim();
-            let has_icon = trimmed.find(' ').is_some();
-            let label_text = if let Some(space_idx) = trimmed.find(' ') {
-                trimmed.split_at(space_idx).1.trim()
-            } else {
-                trimmed
-            };
-
-            let padding_y = crate::layout::paginator_tab_padding_y();
-            let text_h = if has_icon { 52.0 } else { 92.0 };
-            let logical_h = (2.0 * padding_y + text_h * (font_size / 12.0)).max(1.0);
-
-            let w_px = (tab_w * scale) as u32;
-            let h_px = (logical_h * scale) as u32;
-
-            if w_px == 0 || h_px == 0 {
-                self.tab_text_quads.push(Vec::new());
-                continue;
-            }
-
-            let cache_key = format!("{}:{}:{}:{:?}:{}:{}", trimmed, w_px, h_px, color, font_fam, scale);
-            if let Some(cached_quads) = self.tab_quads_cache.get(&cache_key) {
-                self.tab_text_quads.push(cached_quads.clone());
-                continue;
-            }
-
-            let svg_data = format!(
-                r##"<svg width="{}" height="{}" viewBox="0 0 {} {}" xmlns="http://www.w3.org/2000/svg">
-  <text x="{}" y="{}" font-family="{}" font-size="{}" fill="{}" text-anchor="middle" dominant-baseline="middle" transform="rotate(-90 {} {})">{}</text>
-</svg>"##,
-                w_px, h_px,
-                tab_w, logical_h,
-                tab_w / 2.0, logical_h / 2.0,
-                font_fam,
-                font_size,
-                hex_color,
-                tab_w / 2.0, logical_h / 2.0,
-                label_text
-            );
-
-            let opt = resvg::usvg::Options::default();
-            let fontdb = get_font_db();
-            
-            let mut page_quads = Vec::new();
-            if let Ok(tree) = resvg::usvg::Tree::from_data(svg_data.as_bytes(), &opt, fontdb) {
-                if let Some(mut pixmap) = resvg::tiny_skia::Pixmap::new(w_px, h_px) {
-                    resvg::render(&tree, resvg::tiny_skia::Transform::default(), &mut pixmap.as_mut());
-                    let pixels = pixmap.data();
-                    for row in 0..h_px {
-                        for col in 0..w_px {
-                            let idx = ((row * w_px + col) * 4) as usize;
-                            if idx + 3 < pixels.len() {
-                                let a = pixels[idx + 3] as f32 / 255.0;
-                                if a > 0.0 {
-                                    let r = ((pixels[idx] as f32 / 255.0) / a).min(1.0);
-                                    let g = ((pixels[idx + 1] as f32 / 255.0) / a).min(1.0);
-                                    let b = ((pixels[idx + 2] as f32 / 255.0) / a).min(1.0);
-                                    page_quads.push((
-                                        col as f32 / scale,
-                                        row as f32 / scale,
-                                        1.2 / scale,
-                                        1.2 / scale,
-                                        [r, g, b, a],
-                                    ));
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-            eprintln!("DEBUG_PAGINATOR: page='{}', w_px={}, h_px={}, quads_len={}", trimmed, w_px, h_px, page_quads.len());
-            if !page_quads.is_empty() {
-                eprintln!("DEBUG_PAGINATOR_EXAMPLES: {:?}", &page_quads[..5.min(page_quads.len())]);
-            }
-            self.tab_quads_cache.insert(cache_key, page_quads.clone());
-            self.tab_text_quads.push(page_quads);
-        }
-    }
-}
-
-
-impl Element for Paginator {
-    fn as_any(&self) -> &dyn std::any::Any { self }
-    fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
-    fn as_ptr(&self) -> *mut (dyn Element + 'static) {
-        self as *const Self as *mut Self as *mut (dyn Element + 'static)
-    }
-    fn as_ptr_mut(&mut self) -> *mut (dyn Element + 'static) {
-        self as *mut Self as *mut (dyn Element + 'static)
-    }
-
-    fn rect(&self) -> (f32, f32, f32, f32) {
-        (self.x, self.y, self.w, self.h)
-    }
-
-    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
-        self.x = x;
-        self.y = y;
-        self.w = w;
-        self.h = h;
-        self.update_target_pos();
-
-        let self_ptr = self as *mut Paginator;
-        let mut dummy = crate::context::UiContext::new();
-        self.sidebar_menu.set_parent(Some(self_ptr), &mut dummy);
-        for plate in &mut self.plates {
-            plate.set_parent(Some(self_ptr), &mut dummy);
-        }
-
-        let tabs_at_top = self.tabs_at_top;
-        if tabs_at_top {
-            let padding_y = crate::layout::paginator_tab_padding_y();
-            let (_, font_size) = crate::layout::menubar_font_parsed();
-            let tab_h = 2.0 * padding_y + font_size;
-            self.sidebar_menu.set_rect(self.x, self.y, self.w, tab_h);
-            for plate in &mut self.plates {
-                plate.set_rect(self.x, self.y + tab_h, self.w, (self.h - tab_h).max(0.0));
-            }
-        } else {
-            let sidebar_w = self.sidebar_w;
-            let label_h = self.sidebar_label_height();
-            self.sidebar_menu.set_rect(self.x, self.y + label_h, sidebar_w, (self.h - label_h).max(0.0));
-            for plate in &mut self.plates {
-                plate.set_rect(self.x + sidebar_w, self.y, (self.w - sidebar_w).max(0.0), self.h);
-            }
-        }
-    }
-
-    fn set_modifiers(&mut self, ctrl: bool, shift: bool, alt: bool) {
-        self.sidebar_menu.set_modifiers(ctrl, shift, alt);
-        if self.selected_page < self.plates.len() {
-            self.plates[self.selected_page].set_modifiers(ctrl, shift, alt);
-        }
-    }
-
-    fn color(&self) -> [f32; 4] {
-        [0.0, 0.0, 0.0, 0.0]
-    }
-
-    fn set_hovered(&mut self, v: bool) {
-        self.hovered = v;
-    }
-
-    fn hovered(&self) -> bool {
-        self.hovered
-    }
-
-    fn hit_test(&self, px: f32, py: f32, ctx: &UiContext) -> bool {
-        if ctx.is_coordinate_covered(self as *const Self as *const () as usize, px, py) {
-            return false;
-        }
-        if self.sidebar_menu.hit_test(px, py, ctx) {
-            return true;
-        }
-        if self.selected_page < self.plates.len() {
-            if self.plates[self.selected_page].hit_test(px, py, ctx) {
-                return true;
-            }
-        }
-        let (x, y, w, h) = self.rect();
-        px >= x && px <= x + w && py >= y && py <= y + h
-    }
-
-    fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, f32, [f32; 4])>{
-        if let Some(i) = self.hovered_tab {
-            if self.tabs_at_top {
-                let (bx, by, bw, bh) = self.tab_rect(i);
-                let bx = bx + 2.0;
-                let by = by + 2.0;
-                let bw = bw - 4.0;
-                let bh = bh - 4.0;
-                let scroll_offset = ctx.get_scroll_offset();
-                Some((bx, by + scroll_offset, bw, bh, colors::HIGHLIGHT_SECONDARY))
-            } else {
-                let (bx, by, bw, bh) = self.tab_rect(i);
-                let scroll_offset = ctx.get_scroll_offset();
-                let qy = by + scroll_offset;
-                let qh = bh;
-
-                let min_y = self.y;
-                let max_y = self.y + self.h;
-                let ry1 = qy.max(min_y);
-                let ry2 = (qy + qh).min(max_y);
-                let rh = ry2 - ry1;
-                if rh > 0.0 {
-                    Some((bx, ry1, bw, rh, colors::HIGHLIGHT_SECONDARY))
-                } else {
-                    None
-                }
-            }
-        } else {
-            None
-        }
-    }
-
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        let mut quads = Vec::new();
-        if self.tabs_rotated {
-            let current_sidebar_w = self.sidebar_w();
-            quads.push((self.x, self.y, current_sidebar_w, self.h, colors::sidebar_bg_color()));
-            
-            let (tab_w, tab_h) = self.vertical_tab_size();
-            let margin_x = crate::layout::paginator_tab_margin_x();
-            let min_y = self.y;
-            let max_y = self.y + self.h;
-
-            // Draw primary highlight for the selected active tab
-            if let Some(cy) = self.current_y {
-                let bx = self.x + margin_x;
-                let by = self.y + cy - self.sidebar_scroll_y;
-                let ry1 = by.max(min_y);
-                let ry2 = (by + tab_h).min(max_y);
-                let rh = ry2 - ry1;
-                if rh > 0.0 {
-                    quads.push((bx, ry1, tab_w, rh, colors::highlight_primary_color()));
-                }
-            }
-
-            for (i, page_name) in self.pages.iter().enumerate() {
-                let (bx, by, _, _) = self.tab_rect(i);
-
-                let trimmed = page_name.trim();
-                let has_icon = trimmed.find(' ').is_some();
-                let padding_y = crate::layout::paginator_tab_padding_y();
-                let y_offset = if has_icon { 2.0 * padding_y + 12.0 } else { 0.0 };
-
-                if i < self.tab_text_quads.len() {
-                    for &(qx, qy, qw, qh, qc) in &self.tab_text_quads[i] {
-                        let absolute_x = bx + qx;
-                        let absolute_y = by + y_offset + qy;
-                        
-                        let ry1 = absolute_y.max(min_y);
-                        let ry2 = (absolute_y + qh).min(max_y);
-                        let rh = ry2 - ry1;
-                        if rh > 0.0 {
-                            quads.push((absolute_x, ry1, qw, rh, qc));
-                        }
-                    }
-                }
-            }
-        } else {
-            quads.extend(self.sidebar_menu.extra_quads());
-        }
-        quads
-    }
-
-    fn all_quads(&self, ctx: &UiContext) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        let mut quads = self.extra_quads();
-        if !self.tabs_rotated {
-            quads.extend(self.sidebar_menu.all_quads(ctx));
-        }
-
-        if self.selected_page < self.plates.len() {
-            quads.extend(self.plates[self.selected_page].all_quads(ctx));
-        }
-        quads
-    }
-
-    fn text_labels(&self) -> Vec<TextLabel> {
-        let mut labels = Vec::new();
-        if self.tabs_rotated {
-            if let Some(ref label) = self.sidebar_label {
-                let font_size = 11.0;
-                let line_height = font_size * 1.2;
-                let sidebar_w = self.sidebar_w();
-                let start_y = self.y + 10.0;
-                let char_w = TextLabel::estimate_width("o", font_size);
-                let x_pos = self.x + (sidebar_w - char_w) / 2.0;
-                for (i, c) in label.chars().enumerate() {
-                    let char_str = c.to_string();
-                    let y_pos = start_y + i as f32 * line_height;
-                    labels.push(TextLabel {
-                        text: char_str,
-                        x: x_pos,
-                        y: y_pos,
-                        font_size,
-                        color: [0x83, 0x83, 0x8a],
-                    });
-                }
-            }
-
-            let (tab_w, _) = self.vertical_tab_size();
-            let padding_y = crate::layout::paginator_tab_padding_y();
-            let active_color = colors::menubar_tab_label_color();
-            let active_srgb = colors::to_srgb(active_color);
-            let active_r = (active_srgb[0] * 255.0) as u8;
-            let active_g = (active_srgb[1] * 255.0) as u8;
-            let active_b = (active_srgb[2] * 255.0) as u8;
-            let inactive_r = (active_r as f32 * 0.78) as u8;
-            let inactive_g = (active_g as f32 * 0.78) as u8;
-            let inactive_b = (active_b as f32 * 0.78) as u8;
-
-            for (i, page_name) in self.pages.iter().enumerate() {
-                let color = if self.selected_page == i {
-                    [active_r, active_g, active_b]
-                } else {
-                    [inactive_r, inactive_g, inactive_b]
-                };
-                let (bx, by, _, _) = self.tab_rect(i);
-                let bw = tab_w;
-
-                let trimmed = page_name.trim();
-                let has_icon = trimmed.find(' ').is_some();
-                if has_icon {
-                    if let Some(space_idx) = trimmed.find(' ') {
-                        let (icon, _) = trimmed.split_at(space_idx);
-                        let icon = icon.trim();
-                        if !icon.is_empty() {
-                            let icon_font_size = 14.0;
-                            let est_icon_w = TextLabel::estimate_width(icon, icon_font_size);
-                            let icon_y = by + (padding_y - 2.0).max(0.0);
-                            if icon_y >= self.y && icon_y + icon_font_size <= self.y + self.h {
-                                labels.push(TextLabel {
-                                    text: icon.to_string(),
-                                    x: bx + (bw - est_icon_w) / 2.0,
-                                    y: icon_y,
-                                    font_size: icon_font_size,
-                                    color,
-                                });
-                            }
-                        }
-                    }
-                }
-            }
-        } else {
-            labels.extend(self.sidebar_menu.text_labels());
-        }
-
-        if self.selected_page < self.plates.len() {
-            labels.extend(self.plates[self.selected_page].text_labels());
-        }
-        labels
-    }
-
-    fn text_labels_with_bounds(&self, _ctx: &UiContext) -> Vec<(TextLabel, Option<[f32; 4]>)> {
-        let mut labels = Vec::new();
-        for l in self.text_labels() {
-            labels.push((l, None));
-        }
-        labels
-    }
-
-    fn on_cursor_moved(&mut self, px: f32, py: f32, ctx: &mut UiContext) -> bool {
-        let mut changed = false;
-        let was_hovered_tab = self.hovered_tab;
-        self.hovered_tab = None;
-        if self.tabs_rotated {
-            for i in 0..self.pages.len() {
-                let (bx, by, bw, bh) = self.tab_rect(i);
-                if px >= bx && px <= bx + bw && py >= by && py <= by + bh && py >= self.y && py <= self.y + self.h {
-                    self.hovered_tab = Some(i);
-                    break;
-                }
-            }
-        }
-        if was_hovered_tab != self.hovered_tab {
-            changed = true;
-        }
-
-        if self.sidebar_menu.cursor_moved(px, py, ctx) {
-            changed = true;
-        }
-        if self.selected_page < self.plates.len() {
-            if self.plates[self.selected_page].cursor_moved(px, py, ctx) {
-                changed = true;
-            }
-        }
-
-        changed
-    }
-
-    fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32, ctx: &mut UiContext) -> bool {
-        if self.selected_page < self.plates.len() {
-            if self.plates[self.selected_page].popover_rect().is_some() {
-                if self.plates[self.selected_page].mouse_input(button, state, px, py, ctx) {
-                    return true;
-                }
-            }
-        }
-
-        let mut clicked_tab = false;
-        if button == MouseButton::Left {
-            if self.tabs_rotated {
-                match state {
-                    ElementState::Pressed => {
-                        self.pressed_tab = None;
-                        for i in 0..self.pages.len() {
-                            let (bx, by, bw, bh) = self.tab_rect(i);
-                            if px >= bx && px <= bx + bw && py >= by && py <= by + bh && py >= self.y && py <= self.y + self.h {
-                                self.pressed_tab = Some(i);
-                                clicked_tab = true;
-                                break;
-                            }
-                        }
-                        if !clicked_tab {
-                            if self.sidebar_menu.mouse_input(button, state, px, py, ctx) {
-                                if let Some(new_sel) = self.sidebar_menu.take_context_change() {
-                                    self.context_selected = new_sel;
-                                    self.context_just_changed = true;
-                                }
-                                clicked_tab = true;
-                            }
-                        }
-                    }
-                    ElementState::Released => {
-                        if let Some(i) = self.pressed_tab.take() {
-                            let (bx, by, bw, bh) = self.tab_rect(i);
-                            if px >= bx && px <= bx + bw && py >= by && py <= by + bh && py >= self.y && py <= self.y + self.h {
-                                if self.selected_page != i {
-                                    self.set_selected_page(i);
-                                    self.page_changed = true;
-                                }
-                                clicked_tab = true;
-                            }
-                        } else {
-                            if self.sidebar_menu.mouse_input(button, state, px, py, ctx) {
-                                if let Some(new_sel) = self.sidebar_menu.take_context_change() {
-                                    self.context_selected = new_sel;
-                                    self.context_just_changed = true;
-                                }
-                                clicked_tab = true;
-                            }
-                        }
-                    }
-                }
-            } else {
-                if self.sidebar_menu.mouse_input(button, state, px, py, ctx) {
-                    if let Some(new_sel) = self.sidebar_menu.take_context_change() {
-                        self.context_selected = new_sel;
-                        self.context_just_changed = true;
-                    }
-                    if let Some((idx, _)) = self.sidebar_menu.menu_click() {
-                        if self.selected_page != idx {
-                            self.set_selected_page(idx);
-                            self.page_changed = true;
-                        }
-                    }
-                    clicked_tab = true;
-                }
-            }
-        }
-
-        if clicked_tab {
-            return true;
-        }
-
-        if self.selected_page < self.plates.len() {
-            if self.plates[self.selected_page].mouse_input(button, state, px, py, ctx) {
-                return true;
-            }
-        }
-
-        false
-    }
-
-    fn keyboard_input(&mut self, event: &KeyEvent, ctx: &mut UiContext) -> bool {
-        if self.sidebar_menu.keyboard_input(event, ctx) {
-            if let Some(new_sel) = self.sidebar_menu.take_context_change() {
-                self.context_selected = new_sel;
-                self.context_just_changed = true;
-            }
-            return true;
-        }
-        if self.selected_page < self.plates.len() {
-            if self.plates[self.selected_page].keyboard_input(event, ctx) {
-                return true;
-            }
-        }
-        false
-    }
-
-    fn mouse_wheel(&mut self, delta: &MouseScrollDelta, px: f32, py: f32, ctx: &mut UiContext) -> bool {
-        self.update_sidebar_w();
-        if !self.tabs_at_top {
-            let bx = self.x;
-            let by = self.y;
-            let bw = self.sidebar_w;
-            let bh = self.h;
-            if px >= bx && px <= bx + bw && py >= by && py <= by + bh {
-                let scroll_speed = 24.0;
-                let dy = match delta {
-                    MouseScrollDelta::LineDelta(_, y) => -y * scroll_speed,
-                    MouseScrollDelta::PixelDelta(pos) => -pos.y as f32,
-                };
-                let old_scroll = self.sidebar_scroll_y;
-                let max_scroll = (self.total_sidebar_height() - self.h).max(0.0);
-                self.sidebar_scroll_y = (self.sidebar_scroll_y + dy).clamp(0.0, max_scroll);
-                if (self.sidebar_scroll_y - old_scroll).abs() > 0.01 {
-                    self.update_target_pos();
-                    return true;
-                }
-            }
-        }
-
-        if self.sidebar_menu.mouse_wheel(delta, px, py, ctx) {
-            return true;
-        }
-
-        if self.selected_page < self.plates.len() {
-            if self.plates[self.selected_page].mouse_wheel(delta, px, py, ctx) {
-                return true;
-            }
-        }
-        false
-    }
-
-    fn popover_rect(&self) -> Option<(f32, f32, f32, f32)> {
-        if let Some(r) = self.sidebar_menu.popover_rect() {
-            return Some(r);
-        }
-        if self.selected_page < self.plates.len() {
-            if let Some(r) = self.plates[self.selected_page].popover_rect() {
-                return Some(r);
-            }
-        }
-        None
-    }
-
-    fn render_popover(&self, pc: &mut dyn crate::layout::RenderTarget) {
-        self.sidebar_menu.render_popover(pc);
-        if self.selected_page < self.plates.len() {
-            self.plates[self.selected_page].render_popover(pc);
-        }
-    }
-
-    fn take_click(&mut self) -> bool {
-        if self.page_changed {
-            self.page_changed = false;
-            true
-        } else {
-            false
-        }
-    }
-
-    fn value(&self) -> i32 {
-        self.selected_page as i32
-    }
-
-    fn tick(&mut self, dt: f32, ctx: &mut UiContext) -> bool {
-        let mut changed = false;
-        if let Some(current) = self.current_y {
-            let diff = self.target_y - current;
-            if diff.abs() > 0.1 {
-                let decay = 15.0;
-                let next = current + diff * (1.0 - (-decay * dt).exp());
-                self.current_y = Some(next);
-                changed = true;
-            } else {
-                self.current_y = Some(self.target_y);
-            }
-        }
-        if let Some(current) = self.current_x {
-            let diff = self.target_x - current;
-            if diff.abs() > 0.1 {
-                let decay = 15.0;
-                let next = current + diff * (1.0 - (-decay * dt).exp());
-                self.current_x = Some(next);
-                changed = true;
-            } else {
-                self.current_x = Some(self.target_x);
-            }
-        }
-
-        let self_ptr = self as *mut Paginator;
-        self.sidebar_menu.set_parent(Some(self_ptr), ctx);
-        for plate in &mut self.plates {
-            plate.set_parent(Some(self_ptr), ctx);
-        }
-
-        if self.sidebar_menu.tick(dt, ctx) {
-            changed = true;
-        }
-
-        for plate in &mut self.plates {
-            if plate.tick(dt, ctx) {
-                changed = true;
-            }
-        }
-
-        changed
-    }
-
-    fn parent(&self, _ctx: &UiContext) -> Option<*mut (dyn Element + 'static)> {
-        self.parent
-    }
-
-    fn set_parent(&mut self, parent: Option<*mut (dyn Element + 'static)>, _ctx: &mut UiContext) {
-        self.parent = parent;
-    }
-
-    fn children(&self, _ctx: &UiContext) -> Vec<*mut (dyn Element + 'static)> {
-        let mut list = Vec::new();
-        list.push(&self.sidebar_menu as *const dyn Element as *mut dyn Element);
-        for plate in &self.plates {
-            list.push(plate as *const dyn Element as *mut dyn Element);
-        }
-        list
-    }
-
-    fn add_child(&mut self, _child: *mut (dyn Element + 'static), _ctx: &mut UiContext) {}
-    fn clear_children(&mut self, _ctx: &mut UiContext) {}
-
-    fn as_page_selector(&self) -> Option<&dyn PageSelector> { Some(self) }
-    fn as_page_selector_mut(&mut self) -> Option<&mut dyn PageSelector> { Some(self) }
-    fn as_menu_controller(&self) -> Option<&dyn MenuController> { Some(self) }
-    fn as_menu_controller_mut(&mut self) -> Option<&mut dyn MenuController> { Some(self) }
-
-    fn prepare_text(&mut self, fs: &mut glyphon::FontSystem) {
-        self.sidebar_menu.prepare_text(fs);
-        for plate in &mut self.plates {
-            plate.prepare_text(fs);
-        }
-    }
-
-    fn text_labels_with_font_and_bounds(&self, ctx: &UiContext) -> Vec<(TextLabel, Option<String>, Option<[f32; 4]>)> {
-        let mut result = Vec::new();
-        if self.tabs_rotated {
-            let font = self.widget_font();
-            for l in self.text_labels() {
-                result.push((l, font.clone(), None));
-            }
-        } else {
-            result.extend(self.sidebar_menu.text_labels_with_font_and_bounds(ctx));
-        }
-
-        if self.selected_page < self.plates.len() {
-            result.extend(self.plates[self.selected_page].text_labels_with_font_and_bounds(ctx));
-        }
-        result
-    }
-
-    fn widget_font(&self) -> Option<String> {
-        Some(crate::layout::menubar_font())
-    }
-
-    fn focus(&mut self) {
-        self.sidebar_menu.focus();
-    }
-
-    fn unfocus(&mut self) {
-        self.sidebar_menu.unfocus();
-    }
-
-    fn focused(&self, ctx: &UiContext) -> bool {
-        self.sidebar_menu.focused(ctx)
-    }
-}
-
-unsafe impl Send for Paginator {}
-unsafe impl Sync for Paginator {}
-
-impl PageSelector for Paginator {
-    fn selected_page(&self) -> usize {
-        self.selected_page()
-    }
-    fn set_selected_page(&mut self, page: usize) {
-        self.set_selected_page(page);
-    }
-    fn is_page_hidden(&self) -> bool {
-        self.is_page_hidden()
-    }
-    fn set_page_hidden(&mut self, hidden: bool) {
-        self.set_page_hidden(hidden);
-    }
-    fn set_pages(&mut self, pages: Vec<String>) {
-        self.set_pages(pages);
-    }
-    fn set_pages_with_items(&mut self, pages: Vec<String>, items: Vec<Vec<String>>) {
-        self.set_pages_with_items(pages, items);
-    }
-    fn sidebar_w(&self) -> f32 {
-        self.sidebar_w()
-    }
-    fn set_sidebar_mode(&mut self, enabled: bool) {
-        self.set_sidebar_mode(enabled);
-    }
-    fn set_sidebar_label(&mut self, label: Option<String>) {
-        self.sidebar_label = label.clone();
-        if !self.context_options.is_empty() {
-            self.sidebar_menu.title = label.unwrap_or_default();
-            self.sidebar_menu.label = None;
-        } else {
-            self.sidebar_menu.label = label;
-            self.sidebar_menu.title = String::new();
-        }
-        self.update_target_pos();
-    }
-    fn add_widget_to_page(&mut self, page_idx: usize, widget: *mut (dyn Element + 'static), ctx: &mut UiContext) {
-        self.add_widget_to_page(page_idx, widget, ctx);
-    }
-    fn clear_page_widgets(&mut self, page_idx: usize, ctx: &mut UiContext) {
-        self.clear_page_widgets(page_idx, ctx);
-    }
-}
-
-impl MenuController for Paginator {
-    fn menu_click(&mut self) -> Option<(usize, usize)> {
-        if self.sidebar_mode {
-            self.sidebar_menu.menu_click()
-        } else {
-            None
-        }
-    }
-
-    fn trigger_menu_click(&mut self, menu_idx: usize, item_idx: usize) {
-        self.sidebar_menu.trigger_menu_click(menu_idx, item_idx);
-    }
-
-    fn set_item_checked(&mut self, menu_idx: usize, item_idx: usize, checked: bool) {
-        self.sidebar_menu.set_item_checked(menu_idx, item_idx, checked);
-    }
-
-    fn set_menu_items(&mut self, menu_idx: usize, items: &[String]) {
-        self.sidebar_menu.set_menu_items(menu_idx, items);
-    }
-
-    fn is_menu_bar(&self) -> bool {
-        true
-    }
-
-    fn is_menu_open(&self) -> bool {
-        self.sidebar_menu.is_menu_open()
-    }
-
-    fn menu_items(&self) -> Vec<String> {
-        self.sidebar_menu.menu_items()
-    }
-
-    fn menu_item_checked(&self) -> Vec<Option<bool>> {
-        self.sidebar_menu.menu_item_checked()
-    }
-
-    fn is_vertical(&self) -> bool {
-        self.sidebar_menu.is_vertical()
-    }
-
-    fn menu_names(&self) -> Vec<String> {
-        self.pages.clone()
-    }
-
-    fn menu_items_list(&self) -> Vec<Vec<String>> {
-        if let Some(c) = self.sidebar_menu.as_menu_controller() {
-            c.menu_items_list()
-        } else {
-            vec![]
-        }
-    }
-
-    fn menu_checked_list(&self) -> Vec<Vec<Option<bool>>> {
-        if let Some(c) = self.sidebar_menu.as_menu_controller() {
-            c.menu_checked_list()
-        } else {
-            vec![]
-        }
-    }
-
-    fn take_context_change(&mut self) -> Option<usize> {
-        if self.context_just_changed {
-            self.context_just_changed = false;
-            Some(self.context_selected)
-        } else {
-            None
-        }
-    }
-
-    fn set_context_selected(&mut self, selected: usize) {
-        self.set_context_selected(selected);
-    }
-
-    fn set_center_items(&mut self, center: bool) {
-        if let Some(c) = self.sidebar_menu.as_menu_controller_mut() {
-            c.set_center_items(center);
-        }
-    }
-
-    fn get_menu_items_at(&self, px: f32, py: f32) -> Option<(usize, String, Vec<String>, f32, f32, f32, f32)> {
-        if let Some(c) = self.sidebar_menu.as_menu_controller() {
-            c.get_menu_items_at(px, py)
-        } else {
-            None
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_paginator_rotated_tabs() {
-        // Force initialization from cce/config.json so Once won't overwrite our test values
-        let orig_margin_x = crate::layout::paginator_tab_margin_x();
-        let orig_margin_y = crate::layout::paginator_tab_margin_y();
-        let orig_padding_x = crate::layout::paginator_tab_padding_x();
-        let orig_padding_y = crate::layout::paginator_tab_padding_y();
-        let orig_font = crate::layout::menubar_font();
-
-        crate::layout::set_paginator_tab_margin_x(8.0);
-        crate::layout::set_paginator_tab_margin_y(10.0);
-        crate::layout::set_paginator_tab_padding_x(10.0);
-        crate::layout::set_paginator_tab_padding_y(14.0);
-        crate::layout::set_menubar_font("Berkeley Mono 12");
-
-        let pages = vec!["📁 Browse".to_string(), "🌐 Network".to_string()];
-        let mut paginator = Paginator::new(56.0, pages)
-            .with_tab_y_offset(100.0)
-            .with_tabs_rotated(true);
-        
-        paginator.set_rect(0.0, 0.0, 1000.0, 600.0);
-
-        // Verify target_y is updated correctly
-        // selected_page = 0: tab_y_offset = 100.0
-        assert_eq!(paginator.target_y, 100.0);
-
-        paginator.set_selected_page(1);
-        // selected_page = 1: tab_y_offset + 1 * (120.0 + 10.0) = 230.0
-        assert_eq!(paginator.target_y, 230.0);
-
-        // Verify hover coordinates
-        // Tab 0 bx/by bounds:
-        // tab_w = 32.0, tab_h = 120.0, spacing = 10.0, sidebar_w = 48.0
-        // bx = self.x + (sidebar_w - tab_w) / 2 = 8.0
-        // by = self.y + tab_y_offset + i * 130.0 = 100.0
-        // bx range: [8.0, 40.0], by range: [100.0, 220.0]
-        
-        let mut dummy = crate::context::UiContext::new();
-
-        // Hover at (24.0, 150.0) should hit Tab 0
-        let hover_tab0 = paginator.on_cursor_moved(24.0, 150.0, &mut dummy);
-        assert!(hover_tab0);
-        assert_eq!(paginator.hovered_tab, Some(0));
-
-        // Hover at (24.0, 280.0) should hit Tab 1 (by range: [230.0, 350.0])
-        let hover_tab1 = paginator.on_cursor_moved(24.0, 280.0, &mut dummy);
-        assert!(hover_tab1);
-        assert_eq!(paginator.hovered_tab, Some(1));
-
-        // Clicking Tab 0 selects it
-        let click_tab0 = paginator.mouse_input(MouseButton::Left, ElementState::Pressed, 24.0, 150.0, &mut dummy);
-        assert!(click_tab0);
-        assert_eq!(paginator.pressed_tab, Some(0));
-
-        let release_tab0 = paginator.mouse_input(MouseButton::Left, ElementState::Released, 24.0, 150.0, &mut dummy);
-        assert!(release_tab0);
-        assert_eq!(paginator.selected_page, 0);
-
-        // Check vertical text formatting
-        let labels = paginator.text_labels();
-        assert_eq!(labels.len(), 2);
-        assert_eq!(labels[0].text, "📁");
-        assert_eq!(labels[1].text, "🌐");
-        
-        // Check that rotated text quads were generated
-        assert!(!paginator.tab_text_quads[0].is_empty());
-        assert!(!paginator.tab_text_quads[1].is_empty());
-
-        // Restore original values
-        crate::layout::set_paginator_tab_margin_x(orig_margin_x);
-        crate::layout::set_paginator_tab_margin_y(orig_margin_y);
-        crate::layout::set_paginator_tab_padding_x(orig_padding_x);
-        crate::layout::set_paginator_tab_padding_y(orig_padding_y);
-        crate::layout::set_menubar_font(&orig_font);
-    }
-
-    #[test]
-    fn test_paginator_vertical_tabs() {
-        let pages = vec!["File".to_string(), "Edit".to_string()];
-        let mut paginator = Paginator::new(56.0, pages)
-            .with_tab_y_offset(10.0)
-            .with_tabs_rotated(false);
-        
-        paginator.set_rect(0.0, 0.0, 1000.0, 600.0);
-
-        let mut dummy = crate::context::UiContext::new();
-
-        // Check rects of menus
-        let (bx, by, bw, bh) = paginator.sidebar_menu.menus.item_rect(1);
-        assert!(by > 0.0);
-
-        // Click Edit menu (index 1)
-        let px = bx + bw / 2.0;
-        let py = by + bh / 2.0;
-
-        // Press
-        let click_press = paginator.mouse_input(MouseButton::Left, ElementState::Pressed, px, py, &mut dummy);
-        assert!(click_press);
-        assert_eq!(paginator.selected_page, 0);
-
-        // Release
-        let click_release = paginator.mouse_input(MouseButton::Left, ElementState::Released, px, py, &mut dummy);
-        assert!(click_release);
-        assert_eq!(paginator.selected_page, 1);
-        assert!(paginator.page_changed);
-    }
-}
diff --git a/src/widget/json_layout.rs b/src/widget/json_layout.rs
index e4e5ff1..a624cb5 100644
--- a/src/widget/json_layout.rs
+++ b/src/widget/json_layout.rs
@@ -1,5 +1,5 @@
 use crate::widget::{
-    Element, Widget, Checkbox, Button, Label, Spinbox, ColorSelector, TextLabel, Paginator, MouseButton, ElementState, focus, Slider, Event, UiContext,
+    Element, Widget, Checkbox, Button, Label, Spinbox, ColorSelector, TextLabel, MouseButton, ElementState, Slider, Event, UiContext,
 };
 use serde::Deserialize;
 
@@ -51,7 +51,6 @@ pub struct JsonWidget {
 pub struct JsonLayoutWidget {
     base: Widget,
     pub widgets: Vec<JsonWidget>,
-    pub paginator: Option<Paginator>,
     pub dragging_slider_idx: Option<usize>,
     pub page_scroll_y: Vec<f32>,
     pub page_total_heights: Vec<f32>,
@@ -61,7 +60,6 @@ impl JsonLayoutWidget {
     pub fn new(config: &JsonLayoutConfig) -> Self {
         let mut widgets = Vec::new();
         let mut page_titles = Vec::new();
-        let mut paginator = None;
 
         if let Some(ref pages_conf) = config.pages {
             for (page_idx, page) in pages_conf.iter().enumerate() {
@@ -131,7 +129,6 @@ impl JsonLayoutWidget {
                     });
                 }
             }
-            paginator = Some(Paginator::new(56.0, page_titles));
         } else if let Some(ref widgets_conf) = config.widgets {
             for (idx, w_conf) in widgets_conf.iter().enumerate() {
                 let id = w_conf.id.clone().unwrap_or_else(|| format!("widget_{}", idx));
@@ -202,7 +199,6 @@ impl JsonLayoutWidget {
         Self {
             base: Widget::new(),
             widgets,
-            paginator,
             dragging_slider_idx: None,
             page_scroll_y: vec![0.0; 16],
             page_total_heights: vec![0.0; 16],
@@ -210,14 +206,10 @@ impl JsonLayoutWidget {
     }
 
     pub fn layout_children(&mut self) {
-        let (bx, by, bw, bh) = self.rect();
-        if let Some(paginator) = &mut self.paginator {
-            paginator.set_rect(bx, by, bw, bh);
-        }
+        let (bx, by, bw, _) = self.rect();
 
-        let has_paginator = self.paginator.is_some();
-        let pad_x = if has_paginator { 76.0 } else { 16.0 };
-        let usable_w = if has_paginator { bw - pad_x - 16.0 } else { bw - 2.0 * 16.0 };
+        let pad_x = 16.0;
+        let usable_w = bw - 2.0 * 16.0;
         
         let mut page_current_y = vec![16.0; 16]; // support up to 16 pages
         let spacing = 12.0;
@@ -299,12 +291,7 @@ impl Element for JsonLayoutWidget {
 
     fn tick(&mut self, dt: f32, ctx: &mut UiContext) -> bool {
         let mut changed = false;
-        if let Some(paginator) = &mut self.paginator {
-            if paginator.tick(dt, ctx) {
-                changed = true;
-            }
-        }
-        let active_page = self.paginator.as_ref().map(|p| p.selected_page()).unwrap_or(0);
+        let active_page = 0;
         for w in &mut self.widgets {
             if w.page_idx != active_page {
                 continue;
@@ -318,19 +305,10 @@ impl Element for JsonLayoutWidget {
 
     fn all_quads(&self, ctx: &UiContext) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
         let mut quads = Vec::new();
-        if let Some(paginator) = &self.paginator {
-            quads.extend(paginator.all_quads(ctx));
-            if let Some(hq) = paginator.highlight_quad(ctx) {
-                if !quads.contains(&hq) {
-                    quads.push(hq);
-                }
-            }
-        }
 
-        let active_page = self.paginator.as_ref().map(|p| p.selected_page()).unwrap_or(0);
+        let active_page = 0;
         let (bx, by, bw, bh) = self.rect();
-        let has_paginator = self.paginator.is_some();
-        let pad_x = if has_paginator { 76.0 } else { 16.0 };
+        let pad_x = 16.0;
         let min_x = bx + pad_x - 4.0;
         let max_x = bx + bw;
         let min_y = by;
@@ -365,11 +343,7 @@ impl Element for JsonLayoutWidget {
 
     fn text_labels(&self) -> Vec<TextLabel> {
         let mut labels = Vec::new();
-        if let Some(paginator) = &self.paginator {
-            labels.extend(paginator.text_labels());
-        }
-
-        let active_page = self.paginator.as_ref().map(|p| p.selected_page()).unwrap_or(0);
+        let active_page = 0;
 
         for w in &self.widgets {
             if w.page_idx != active_page {
@@ -389,15 +363,9 @@ impl Element for JsonLayoutWidget {
     fn text_labels_with_bounds(&self, _ctx: &UiContext) -> Vec<(TextLabel, Option<[f32; 4]>)> {
         let mut labels = Vec::new();
         let (bx, by, bw, bh) = self.rect();
-        if let Some(paginator) = &self.paginator {
-            for l in paginator.text_labels() {
-                labels.push((l, None));
-            }
-        }
 
-        let active_page = self.paginator.as_ref().map(|p| p.selected_page()).unwrap_or(0);
-        let has_paginator = self.paginator.is_some();
-        let pad_x = if has_paginator { 76.0 } else { 16.0 };
+        let active_page = 0;
+        let pad_x = 16.0;
         let content_bounds = Some([bx + pad_x - 4.0, by, bx + bw, by + bh]);
 
         for w in &self.widgets {
@@ -423,16 +391,6 @@ impl Element for JsonLayoutWidget {
     fn handle_event(&mut self, event: &Event, ctx: &mut UiContext) -> bool {
         let mut changed = false;
 
-        if let Some(paginator) = &mut self.paginator {
-            if paginator.handle_event(event, ctx) {
-                if paginator.take_click() {
-                    focus::clear_focus();
-                    self.layout_children();
-                }
-                changed = true;
-            }
-        }
-
         match event {
             Event::PointerMove { x, y } => {
                 if let Some(idx) = self.dragging_slider_idx {
@@ -459,7 +417,7 @@ impl Element for JsonLayoutWidget {
             _ => {}
         }
 
-        let active_page = self.paginator.as_ref().map(|p| p.selected_page()).unwrap_or(0);
+        let active_page = 0;
         for (idx, w) in self.widgets.iter_mut().enumerate() {
             if w.page_idx != active_page {
                 continue;
@@ -535,11 +493,6 @@ impl Element for JsonLayoutWidget {
         }
 
         if let Event::Tick(dt) = event {
-            if let Some(paginator) = &mut self.paginator {
-                if paginator.tick(*dt, ctx) {
-                    changed = true;
-                }
-            }
             for w in &mut self.widgets {
                 if w.page_idx != active_page {
                     continue;
diff --git a/src/widget/mod.rs b/src/widget/mod.rs
index 9cb83a3..e05e40e 100644
--- a/src/widget/mod.rs
+++ b/src/widget/mod.rs
@@ -552,7 +552,7 @@ pub use self::input::{
 pub use self::container::{
     Container, Header, ContentBg, ViewportBg, ParametersBg, ScrollingList,
     ScrollBox, Menu, MenuBar, Spreadsheet, Breadcrumb, Plate,
-    Paginator, Switcher, Layer, Page, Window
+    Switcher, Layer, Page, Window
 };
 pub use self::display::{
     TextLabel, Label, SectionHeader, StyledLabel, TextItem, Svg, UsageBar,