GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
Recreate Paginator widget as a simple layout switcher with vertical ButtonStrip
src/widget/container/mod.rs | 2 +
src/widget/container/paginator.rs | 333 ++++++++++++++++++++++++++++++++++++++
src/widget/mod.rs | 2 +-
3 files changed, 336 insertions(+), 1 deletion(-)
diff --git a/src/widget/container/mod.rs b/src/widget/container/mod.rs
index dda794f..9ec6200 100644
--- a/src/widget/container/mod.rs
+++ b/src/widget/container/mod.rs
@@ -13,6 +13,7 @@ pub mod switcher;
pub mod layer;
pub mod page;
pub mod window;
+pub mod paginator;
pub use container::Container;
pub use header::Header;
@@ -29,3 +30,4 @@ pub use switcher::Switcher;
pub use layer::Layer;
pub use page::Page;
pub use window::Window;
+pub use paginator::Paginator;
diff --git a/src/widget/container/paginator.rs b/src/widget/container/paginator.rs
new file mode 100644
index 0000000..4e62eea
--- /dev/null
+++ b/src/widget/container/paginator.rs
@@ -0,0 +1,333 @@
+use crate::colors;
+use crate::widget::*;
+use crate::widget::input::ButtonStrip;
+use crate::widget::display::TextLabel;
+use super::plate::Plate;
+
+pub struct Paginator {
+ x: f32,
+ y: f32,
+ w: f32,
+ h: f32,
+ pub sidebar_menu: ButtonStrip,
+ pub plates: Vec<Plate>,
+ pub selected_page: usize,
+ pub page_hidden: bool,
+ pub sidebar_w: f32,
+ pub pages: Vec<String>,
+ parent: Option<*mut (dyn Element + 'static)>,
+ pub on_page_changed_cb: Option<Box<dyn Fn(usize) + Send + Sync>>,
+}
+
+impl Paginator {
+ pub fn new(sidebar_w: f32, pages: Vec<String>) -> Self {
+ let num_pages = pages.len();
+
+ let mut sidebar_menu = ButtonStrip::new(0.0, 0.0, sidebar_w, 0.0)
+ .with_vertical(true)
+ .with_buttons(pages.clone());
+ if num_pages > 0 {
+ sidebar_menu.set_selected(Some(0));
+ }
+
+ 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);
+ }
+ if num_pages > 0 {
+ plates[0].visible = true;
+ }
+
+ Self {
+ x: 0.0,
+ y: 0.0,
+ w: 0.0,
+ h: 0.0,
+ sidebar_menu,
+ plates,
+ selected_page: 0,
+ page_hidden: false,
+ sidebar_w,
+ pages,
+ parent: None,
+ on_page_changed_cb: None,
+ }
+ }
+
+ pub fn with_sidebar_mode(self, _enabled: bool) -> Self {
+ self
+ }
+
+ pub fn with_tabs_rotated(self, _rotated: bool) -> Self {
+ self
+ }
+
+ pub fn with_tabs_at_top(self, _top: bool) -> Self {
+ self
+ }
+
+ pub fn with_tab_y_offset(self, _offset: f32) -> Self {
+ 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
+ }
+}
+
+impl PageSelector for Paginator {
+ fn selected_page(&self) -> usize {
+ self.selected_page
+ }
+
+ fn set_selected_page(&mut self, page: usize) {
+ if page < self.plates.len() {
+ self.selected_page = page;
+ self.sidebar_menu.set_selected(Some(page));
+ for (i, plate) in self.plates.iter_mut().enumerate() {
+ plate.visible = i == page;
+ }
+ if let Some(ref cb) = self.on_page_changed_cb {
+ cb(page);
+ }
+ }
+ }
+
+ fn is_page_hidden(&self) -> bool {
+ self.page_hidden
+ }
+
+ fn set_page_hidden(&mut self, hidden: bool) {
+ self.page_hidden = hidden;
+ }
+
+ fn set_pages(&mut self, pages: Vec<String>) {
+ self.pages = pages.clone();
+ self.sidebar_menu.buttons = pages.clone();
+ self.sidebar_menu.generate_rotated_labels();
+
+ self.plates.clear();
+ for _ in 0..pages.len() {
+ let mut plate = Plate::new(0.0, 0.0, 0.0, 0.0).with_draggable(false);
+ plate.visible = false;
+ self.plates.push(plate);
+ }
+ if !self.plates.is_empty() {
+ let idx = self.selected_page.min(self.plates.len() - 1);
+ self.plates[idx].visible = true;
+ }
+ }
+
+ fn set_pages_with_items(&mut self, pages: Vec<String>, _items: Vec<Vec<String>>) {
+ self.set_pages(pages);
+ }
+
+ fn sidebar_w(&self) -> f32 {
+ self.sidebar_w
+ }
+
+ fn set_sidebar_mode(&mut self, _enabled: bool) {}
+
+ fn set_sidebar_label(&mut self, _label: Option<String>) {}
+
+ 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);
+ }
+ }
+ }
+
+ 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);
+ }
+ }
+}
+
+impl Element for Paginator {
+ 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.sidebar_menu.set_rect(x, y, self.sidebar_w, h);
+
+ let page_x = x + self.sidebar_w;
+ let page_w = (w - self.sidebar_w).max(0.0);
+ for (i, plate) in self.plates.iter_mut().enumerate() {
+ plate.set_rect(page_x, y, page_w, h);
+ plate.visible = (i == self.selected_page) && !self.page_hidden;
+ }
+ }
+
+ fn color(&self) -> [f32; 4] {
+ colors::sidebar_bg_color()
+ }
+
+ 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 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 childs = Vec::new();
+ childs.push(&self.sidebar_menu as &dyn Element as *const (dyn Element + 'static) as *mut (dyn Element + 'static));
+ for plate in &self.plates {
+ childs.push(plate as &dyn Element as *const (dyn Element + 'static) as *mut (dyn Element + 'static));
+ }
+ childs
+ }
+
+ fn cursor_moved(&mut self, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+ let mut changed = false;
+ if self.sidebar_menu.cursor_moved(px, py, ctx) {
+ changed = true;
+ }
+ for (i, plate) in self.plates.iter_mut().enumerate() {
+ if i == self.selected_page && !self.page_hidden {
+ if plate.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 {
+ let mut changed = false;
+ if self.sidebar_menu.mouse_input(button, state, px, py, ctx) {
+ changed = true;
+ if let Some(idx) = self.sidebar_menu.take_click() {
+ self.set_selected_page(idx);
+ }
+ }
+ for (i, plate) in self.plates.iter_mut().enumerate() {
+ if i == self.selected_page && !self.page_hidden {
+ if plate.mouse_input(button, state, px, py, ctx) {
+ changed = true;
+ }
+ }
+ }
+ changed
+ }
+
+ fn tick(&mut self, dt: f32, ctx: &mut UiContext) -> bool {
+ let mut changed = false;
+ if self.sidebar_menu.tick(dt, ctx) {
+ changed = true;
+ }
+ for (i, plate) in self.plates.iter_mut().enumerate() {
+ if i == self.selected_page && !self.page_hidden {
+ if plate.tick(dt, ctx) {
+ changed = true;
+ }
+ }
+ }
+ changed
+ }
+
+ fn all_quads(&self, ctx: &UiContext) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
+ let mut quads = Vec::new();
+ let c = self.color();
+ if c[3] > 0.0 {
+ quads.push((self.x, self.y, self.w, self.h, c));
+ }
+ quads.extend(self.sidebar_menu.all_quads(ctx));
+ for (i, plate) in self.plates.iter().enumerate() {
+ if i == self.selected_page && !self.page_hidden {
+ quads.extend(plate.all_quads(ctx));
+ }
+ }
+ quads
+ }
+
+ fn text_labels(&self) -> Vec<TextLabel> {
+ let mut labels = Vec::new();
+ labels.extend(self.sidebar_menu.text_labels());
+ for (i, plate) in self.plates.iter().enumerate() {
+ if i == self.selected_page && !self.page_hidden {
+ labels.extend(plate.text_labels());
+ }
+ }
+ labels
+ }
+
+ fn text_labels_with_bounds(&self, ctx: &UiContext) -> Vec<(TextLabel, Option<[f32; 4]>)> {
+ let mut labels = Vec::new();
+ labels.extend(self.sidebar_menu.text_labels_with_bounds(ctx));
+ for (i, plate) in self.plates.iter().enumerate() {
+ if i == self.selected_page && !self.page_hidden {
+ labels.extend(plate.text_labels_with_bounds(ctx));
+ }
+ }
+ labels
+ }
+
+ fn text_labels_with_font_and_bounds(&self, ctx: &UiContext) -> Vec<(TextLabel, Option<String>, Option<[f32; 4]>)> {
+ let mut labels = Vec::new();
+ labels.extend(self.sidebar_menu.text_labels_with_font_and_bounds(ctx));
+ for (i, plate) in self.plates.iter().enumerate() {
+ if i == self.selected_page && !self.page_hidden {
+ labels.extend(plate.text_labels_with_font_and_bounds(ctx));
+ }
+ }
+ labels
+ }
+
+ fn get_text_items(&self) -> Vec<(&glyphon::Buffer, f32, f32, glyphon::Color)> {
+ let mut items = Vec::new();
+ items.extend(self.sidebar_menu.get_text_items());
+ for (i, plate) in self.plates.iter().enumerate() {
+ if i == self.selected_page && !self.page_hidden {
+ items.extend(plate.get_text_items());
+ }
+ }
+ items
+ }
+
+ fn as_page_selector(&self) -> Option<&dyn PageSelector> {
+ Some(self)
+ }
+
+ fn as_page_selector_mut(&mut self) -> Option<&mut dyn PageSelector> {
+ Some(self)
+ }
+
+ fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
+ let mut quads = Vec::new();
+ quads.extend(self.sidebar_menu.extra_quads());
+ for (i, plate) in self.plates.iter().enumerate() {
+ if i == self.selected_page && !self.page_hidden {
+ quads.extend(plate.extra_quads());
+ }
+ }
+ quads
+ }
+}
+
+impl Drop for Paginator {
+ fn drop(&mut self) {
+ clear_widget_references(self);
+ }
+}
diff --git a/src/widget/mod.rs b/src/widget/mod.rs
index e05e40e..5af7410 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,
- Switcher, Layer, Page, Window
+ Switcher, Layer, Page, Window, Paginator
};
pub use self::display::{
TextLabel, Label, SectionHeader, StyledLabel, TextItem, Svg, UsageBar,