GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
Fix grid layout spacing vertical alignment, scroll bar crash, and duplicate headers
src/layout.rs | 15 ++++--
src/widget/container/scroll_bar.rs | 85 ++++++++-----------------------
src/widget/container/scroll_box.rs | 50 ++++++++----------
src/widget/container/section_container.rs | 21 ++------
4 files changed, 56 insertions(+), 115 deletions(-)
diff --git a/src/layout.rs b/src/layout.rs
index 228d7d8..23d9779 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -2945,9 +2945,16 @@ impl<'a, P: RenderTarget> SectionContext<'a, P> {
}
pub fn spacing(&mut self, dy: f32) {
- self.content_y += dy;
- for h in &mut self.grid.col_heights {
- *h += dy;
+ if self.grid.col_heights.len() >= 2 {
+ if self.last_col < self.grid.col_heights.len() {
+ self.grid.col_heights[self.last_col] += dy;
+ }
+ self.content_y = self.grid.max_height();
+ } else {
+ self.content_y += dy;
+ for h in &mut self.grid.col_heights {
+ *h += dy;
+ }
}
}
@@ -3493,7 +3500,7 @@ mod tests {
let height_col_0_after = ctx.grid.col_heights[0];
let height_col_1_after = ctx.grid.col_heights[1];
assert_eq!(height_col_0_after, height_col_0_before + 12.0);
- assert_eq!(height_col_1_after, height_col_1_before + 12.0);
+ assert_eq!(height_col_1_after, height_col_1_before);
assert_ne!(height_col_0_after, height_col_1_after);
}
}
diff --git a/src/widget/container/scroll_bar.rs b/src/widget/container/scroll_bar.rs
index 8cf9262..2295a55 100644
--- a/src/widget/container/scroll_bar.rs
+++ b/src/widget/container/scroll_bar.rs
@@ -2,14 +2,10 @@ use crate::widget::*;
use crate::context::UiContext;
pub struct ScrollBar {
- x: f32,
- y: f32,
- w: f32,
- h: f32,
+ pub base: Widget,
pub scroll_y: f32,
pub content_h: f32,
pub viewport_h: f32,
- pub hovered: bool,
pub dragging: bool,
pub parent: Option<*mut (dyn Element + 'static)>,
pub children: Vec<*mut (dyn Element + 'static)>,
@@ -18,14 +14,10 @@ pub struct ScrollBar {
impl std::fmt::Debug for ScrollBar {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ScrollBar")
- .field("x", &self.x)
- .field("y", &self.y)
- .field("w", &self.w)
- .field("h", &self.h)
+ .field("base", &self.base)
.field("scroll_y", &self.scroll_y)
.field("content_h", &self.content_h)
.field("viewport_h", &self.viewport_h)
- .field("hovered", &self.hovered)
.field("dragging", &self.dragging)
.finish()
}
@@ -34,14 +26,10 @@ impl std::fmt::Debug for ScrollBar {
impl Clone for ScrollBar {
fn clone(&self) -> Self {
Self {
- x: self.x,
- y: self.y,
- w: self.w,
- h: self.h,
+ base: self.base.clone(),
scroll_y: self.scroll_y,
content_h: self.content_h,
viewport_h: self.viewport_h,
- hovered: self.hovered,
dragging: self.dragging,
parent: self.parent,
children: self.children.clone(),
@@ -51,15 +39,13 @@ impl Clone for ScrollBar {
impl ScrollBar {
pub fn new() -> Self {
+ let mut base = Widget::new();
+ base.w = 6.0;
Self {
- x: 0.0,
- y: 0.0,
- w: 6.0,
- h: 0.0,
+ base,
scroll_y: 0.0,
content_h: 0.0,
viewport_h: 0.0,
- hovered: false,
dragging: false,
parent: None,
children: Vec::new(),
@@ -73,13 +59,13 @@ impl ScrollBar {
}
pub fn get_thumb_rect(&self) -> Option<(f32, f32, f32, f32)> {
- if self.content_h <= self.viewport_h || self.viewport_h <= 0.0 || self.h <= 0.0 {
+ if self.content_h <= self.viewport_h || self.viewport_h <= 0.0 || self.base.h <= 0.0 {
return None;
}
- let sb_x = self.x;
- let sb_w = self.w;
- let sb_track_h = self.h;
- let sb_track_y = self.y;
+ let sb_x = self.base.x;
+ let sb_w = self.base.w;
+ let sb_track_h = self.base.h;
+ let sb_track_y = self.base.y;
let visible_ratio = self.viewport_h / self.content_h;
let thumb_h = if sb_track_h <= 20.0 {
@@ -96,63 +82,32 @@ impl ScrollBar {
}
impl Element for ScrollBar {
- fn base(&self) -> Option<&Widget> { None }
- fn base_mut(&mut self) -> Option<&mut Widget> { None }
-
- 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;
- }
-
- 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)
- }
+ crate::impl_widget_base!(ScrollBar);
fn color(&self) -> [f32; 4] {
[0.0, 0.0, 0.0, 0.0]
}
- fn set_hovered(&mut self, hovered: bool) {
- self.hovered = hovered;
- }
-
- 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;
}
let hit_margin = 6.0;
- px >= self.x - hit_margin && px <= self.x + self.w + hit_margin && py >= self.y && py <= self.y + self.h
+ px >= self.base.x - hit_margin && px <= self.base.x + self.base.w + hit_margin && py >= self.base.y && py <= self.base.y + self.base.h
}
fn on_cursor_moved(&mut self, px: f32, py: f32, ctx: &mut UiContext) -> bool {
let mut changed = false;
let is_hit = self.hit_test(px, py, ctx);
- if self.hovered != is_hit {
- self.hovered = is_hit;
+ if self.base.hovered != is_hit {
+ self.base.hovered = is_hit;
changed = true;
}
if self.dragging {
if let Some((_, _, _, thumb_h)) = self.get_thumb_rect() {
- let sb_track_y = self.y;
- let sb_track_h = self.h;
+ let sb_track_y = self.base.y;
+ let sb_track_h = self.base.h;
let track_scroll_range = sb_track_h - thumb_h;
if track_scroll_range > 0.0 {
let mouse_y_in_track = (py - sb_track_y).clamp(0.0, sb_track_h);
@@ -202,13 +157,13 @@ impl Element for ScrollBar {
fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
let mut quads = Vec::new();
- if self.content_h > self.viewport_h && self.h > 0.0 {
- quads.push((self.x, self.y, self.w, self.h, [0.15, 0.15, 0.20, 0.3]));
+ if self.content_h > self.viewport_h && self.base.h > 0.0 {
+ quads.push((self.base.x, self.base.y, self.base.w, self.base.h, [0.15, 0.15, 0.20, 0.3]));
if let Some((sb_x, thumb_y, sb_w, thumb_h)) = self.get_thumb_rect() {
let thumb_color = if self.dragging {
[0.70, 0.70, 0.75, 0.6]
- } else if self.hovered {
+ } else if self.base.hovered {
[0.65, 0.65, 0.70, 0.5]
} else {
[0.60, 0.60, 0.65, 0.4]
diff --git a/src/widget/container/scroll_box.rs b/src/widget/container/scroll_box.rs
index 6636325..a948fc1 100644
--- a/src/widget/container/scroll_box.rs
+++ b/src/widget/container/scroll_box.rs
@@ -2,14 +2,13 @@ use crate::widget::*;
#[derive(Debug, Clone)]
pub struct ScrollBox {
- x: f32, y: f32, w: f32, h: f32,
+ pub base: Widget,
pub scroll_y: f32,
pub content_h: f32,
pub viewport_y: f32,
pub viewport_h: f32,
viewport_offset_y: f32,
viewport_offset_h: f32,
- hovered: bool,
pub show_border: bool,
pub parent: Option<*mut (dyn Element + 'static)>,
pub children: Vec<*mut (dyn Element + 'static)>,
@@ -18,14 +17,13 @@ pub struct ScrollBox {
impl ScrollBox {
pub fn new() -> Self {
Self {
- x: 0.0, y: 0.0, w: 0.0, h: 0.0,
+ base: Widget::new(),
scroll_y: 0.0,
content_h: 0.0,
viewport_y: 0.0,
viewport_h: 0.0,
viewport_offset_y: 0.0,
viewport_offset_h: 0.0,
- hovered: false,
show_border: true,
parent: None,
children: Vec::new(),
@@ -36,8 +34,8 @@ impl ScrollBox {
self.content_h = content_h;
self.viewport_y = viewport_y;
self.viewport_h = viewport_h;
- self.viewport_offset_y = viewport_y - self.y;
- self.viewport_offset_h = viewport_h - self.h;
+ self.viewport_offset_y = viewport_y - self.base.y;
+ self.viewport_offset_h = viewport_h - self.base.h;
let max_scroll = (content_h - viewport_h).max(0.0);
self.scroll_y = self.scroll_y.clamp(0.0, max_scroll);
}
@@ -53,25 +51,17 @@ impl ScrollBox {
}
impl Element for ScrollBox {
- fn rect(&self) -> (f32, f32, f32, f32) { (self.x, self.y, self.w, self.h) }
+ crate::impl_widget_base!(ScrollBox);
+
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.base.x = x;
+ self.base.y = y;
+ self.base.w = w;
+ self.base.h = h;
self.viewport_y = y + self.viewport_offset_y;
self.viewport_h = h + self.viewport_offset_h;
}
fn color(&self) -> [f32; 4] { crate::color::scrollinglist_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 set_hovered(&mut self, v: bool) { self.hovered = v; }
- fn hovered(&self) -> bool { self.hovered }
- fn highlight_color(&self, _ctx: &UiContext) -> Option<[f32; 4]> { None }
fn focus(&mut self) {
focus::set_focused(self);
@@ -89,9 +79,9 @@ impl Element for ScrollBox {
}
fn on_cursor_moved(&mut self, px: f32, py: f32, ctx: &mut UiContext) -> bool {
- let was = self.hovered;
- self.hovered = self.hit_test(px, py, ctx);
- was != self.hovered
+ let was = self.base.hovered;
+ self.base.hovered = self.hit_test(px, py, ctx);
+ was != self.base.hovered
}
fn mouse_wheel(&mut self, delta: &MouseScrollDelta, px: f32, py: f32, ctx: &mut UiContext) -> bool {
@@ -114,24 +104,24 @@ impl Element for ScrollBox {
let mut quads = Vec::new();
// Background
- quads.push((self.x, self.y, self.w, self.h, crate::color::scrollinglist_bg_color()));
+ quads.push((self.base.x, self.base.y, self.base.w, self.base.h, crate::color::scrollinglist_bg_color()));
// Border lines
let box_border_color = if focus::is_focused(self) {
[0.30, 0.50, 0.32, 1.0] // Focused green
- } else if self.hovered {
+ } else if self.base.hovered {
[0.25, 0.25, 0.35, 1.0] // Hovered
} else {
[0.18, 0.18, 0.24, 1.0] // Default
};
- quads.push((self.x, self.y, self.w, 1.0, box_border_color)); // Top
- quads.push((self.x, self.y + self.h - 1.0, self.w, 1.0, box_border_color)); // Bottom
- quads.push((self.x, self.y, 1.0, self.h, box_border_color)); // Left
- quads.push((self.x + self.w - 1.0, self.y, 1.0, self.h, box_border_color)); // Right
+ quads.push((self.base.x, self.base.y, self.base.w, 1.0, box_border_color)); // Top
+ quads.push((self.base.x, self.base.y + self.base.h - 1.0, self.base.w, 1.0, box_border_color)); // Bottom
+ quads.push((self.base.x, self.base.y, 1.0, self.base.h, box_border_color)); // Left
+ quads.push((self.base.x + self.base.w - 1.0, self.base.y, 1.0, self.base.h, box_border_color)); // Right
// Scrollbar
if self.content_h > self.viewport_h {
- let sb_x = self.x + self.w - 8.0;
+ let sb_x = self.base.x + self.base.w - 8.0;
let sb_w = 4.0;
let sb_track_h = self.viewport_h - 8.0;
let sb_track_y = self.viewport_y + 4.0;
diff --git a/src/widget/container/section_container.rs b/src/widget/container/section_container.rs
index 7f2df81..3b451de 100644
--- a/src/widget/container/section_container.rs
+++ b/src/widget/container/section_container.rs
@@ -114,34 +114,23 @@ impl Element for SectionContainer {
}
fn all_quads(&self, ctx: &UiContext) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
- let mut quads = self.header.all_quads(ctx);
- quads.extend(self.header.extra_quads());
- quads.extend(self.container.all_quads(ctx));
- quads
+ self.container.all_quads(ctx)
}
fn text_labels(&self) -> Vec<TextLabel> {
- let mut labels = self.header.text_labels();
- labels.extend(self.container.text_labels());
- labels
+ self.container.text_labels()
}
fn text_labels_with_bounds(&self, ctx: &UiContext) -> Vec<(TextLabel, Option<[f32; 4]>)> {
- let mut result = self.header.text_labels_with_bounds(ctx);
- result.extend(self.container.text_labels_with_bounds(ctx));
- result
+ self.container.text_labels_with_bounds(ctx)
}
fn text_labels_with_font_and_bounds(&self, ctx: &UiContext) -> Vec<(TextLabel, Option<String>, Option<[f32; 4]>)> {
- let mut result = self.header.text_labels_with_font_and_bounds(ctx);
- result.extend(self.container.text_labels_with_font_and_bounds(ctx));
- result
+ self.container.text_labels_with_font_and_bounds(ctx)
}
fn get_text_items(&self) -> Vec<(&glyphon::Buffer, f32, f32, glyphon::Color)> {
- let mut result = self.header.get_text_items();
- result.extend(self.container.get_text_items());
- result
+ self.container.get_text_items()
}
fn hit_test(&self, px: f32, py: f32, ctx: &UiContext) -> bool {