GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
Refactor parameters panel layout to use native widget labels and full-width widgets
src/backend/window_runner.rs | 45 +++++++++++++++----
src/widget/container/paginator.rs | 6 +++
src/widget/container/parameters_bg.rs | 85 +++++++++--------------------------
src/widget/json_layout.rs | 6 +++
src/widget/mod.rs | 16 +++++--
5 files changed, 81 insertions(+), 77 deletions(-)
diff --git a/src/backend/window_runner.rs b/src/backend/window_runner.rs
index c199ded..f691d3f 100644
--- a/src/backend/window_runner.rs
+++ b/src/backend/window_runner.rs
@@ -34,7 +34,7 @@ use glyphon::{
Cache, FontSystem, Resolution, SwashCache, TextArea, TextAtlas,
TextBounds, TextRenderer, Viewport, Buffer, Attrs, Metrics,
};
-use crate::widget::{TextItem, MouseButton, ElementState, MouseScrollDelta, KeyEvent, Key, NamedKey};
+use crate::widget::{Element, TextItem, MouseButton, ElementState, MouseScrollDelta, KeyEvent, Key, NamedKey};
use crate::wayland::{WaylandSurfaceHandle, detect_scale_factor};
use crate::backend::WgpuAdapter;
@@ -570,6 +570,31 @@ pub fn extra_quad_vertices(
verts
}
+fn get_child_widget_for_quad<'a>(
+ w: &'a dyn crate::widget::Element,
+ qx: f32, qy: f32, qw: f32, qh: f32,
+) -> &'a dyn crate::widget::Element {
+ if let Some(pbg) = w.as_any().downcast_ref::<crate::widget::ParametersBg>() {
+ for sb_opt in &pbg.spinboxes {
+ if let Some(sb) = sb_opt {
+ let (sx, sy, sww, shh) = sb.rect();
+ if qx >= sx - 0.1 && qx + qw <= sx + sww + 0.1 && qy >= sy - 0.1 && qy + qh <= sy + shh + 0.1 {
+ return sb;
+ }
+ }
+ }
+ for btn_opt in &pbg.buttons {
+ if let Some(btn) = btn_opt {
+ let (bx, by, bww, bhh) = btn.rect();
+ if qx >= bx - 0.1 && qx + qw <= bx + bww + 0.1 && qy >= by - 0.1 && qy + qh <= by + bhh + 0.1 {
+ return btn;
+ }
+ }
+ }
+ }
+ w
+}
+
pub fn push_extra_quad_vertices(
w: &dyn crate::widget::Element,
qx: f32, qy: f32, qw: f32, qh: f32,
@@ -578,14 +603,15 @@ pub fn push_extra_quad_vertices(
clip_circle: [f32; 3],
out: &mut Vec<Vertex>,
) {
- let corners = w.rounded_corners();
+ let target_w = get_child_widget_for_quad(w, qx, qy, qw, qh);
+ let corners = target_w.rounded_corners();
if corners == (false, false, false, false) {
out.extend_from_slice(&quad_vertices_with_clip(qx, qy, qw, qh, sw, sh, qc, clip_circle));
return;
}
- let (wx, mut wy, ww, mut wh) = w.rect();
- let top_room = crate::widget::label_offset(w);
+ let (wx, mut wy, ww, mut wh) = target_w.rect();
+ let top_room = crate::widget::label_offset(target_w);
wy += top_room;
wh -= top_room;
let extra_corners = (
@@ -595,7 +621,7 @@ pub fn push_extra_quad_vertices(
corners.3 && qx <= wx + 0.1 && qy + qh >= wy + wh - 0.1,
);
- push_rounded_rect_vertices_corners(qx, qy, qw, qh, w.corner_radius(), sw, sh, qc, clip_circle, extra_corners, None, out);
+ push_rounded_rect_vertices_corners(qx, qy, qw, qh, target_w.corner_radius(), sw, sh, qc, clip_circle, extra_corners, None, out);
}
pub fn extra_quad_vertices_clipped(
@@ -620,7 +646,8 @@ pub fn push_extra_quad_vertices_clipped(
clip_circle: [f32; 3],
out: &mut Vec<Vertex>,
) {
- let corners = w.rounded_corners();
+ let target_w = get_child_widget_for_quad(w, qx, qy, qw, qh);
+ let corners = target_w.rounded_corners();
if corners == (false, false, false, false) {
let (cx0, cy0, cx1, cy1) = clip;
let ix0 = qx.max(cx0);
@@ -634,8 +661,8 @@ pub fn push_extra_quad_vertices_clipped(
return;
}
- let (wx, mut wy, ww, mut wh) = w.rect();
- let top_room = crate::widget::label_offset(w);
+ let (wx, mut wy, ww, mut wh) = target_w.rect();
+ let top_room = crate::widget::label_offset(target_w);
wy += top_room;
wh -= top_room;
let extra_corners = (
@@ -645,7 +672,7 @@ pub fn push_extra_quad_vertices_clipped(
corners.3 && qx <= wx + 0.1 && qy + qh >= wy + wh - 0.1,
);
- push_rounded_rect_vertices_corners(qx, qy, qw, qh, w.corner_radius(), sw, sh, qc, clip_circle, extra_corners, Some(clip), out);
+ push_rounded_rect_vertices_corners(qx, qy, qw, qh, target_w.corner_radius(), sw, sh, qc, clip_circle, extra_corners, Some(clip), out);
}
pub fn circle_vertices(
diff --git a/src/widget/container/paginator.rs b/src/widget/container/paginator.rs
index ef7a1b8..f1d48fc 100644
--- a/src/widget/container/paginator.rs
+++ b/src/widget/container/paginator.rs
@@ -473,6 +473,12 @@ impl Paginator {
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 rect(&self) -> (f32, f32, f32, f32) {
(self.x, self.y, self.w, self.h)
}
diff --git a/src/widget/container/parameters_bg.rs b/src/widget/container/parameters_bg.rs
index da05798..a3d3904 100644
--- a/src/widget/container/parameters_bg.rs
+++ b/src/widget/container/parameters_bg.rs
@@ -12,7 +12,7 @@ pub struct ParametersBg {
mouse_pos: Option<(f32, f32)>,
sliders: Vec<Option<Slider>>,
float3s: Vec<Option<Float3>>,
- spinboxes: Vec<Option<Spinbox>>,
+ pub spinboxes: Vec<Option<Spinbox>>,
pub buttons: Vec<Option<Button>>,
pub choices: Vec<Option<Dropdown>>,
pub texts: Vec<Option<TextBox>>,
@@ -65,7 +65,11 @@ impl ParametersBg {
24.0
} else if p.2.starts_with("float3") {
108.0
- } else if p.2 == "text" || p.2.starts_with("spinbox") || p.2.starts_with("choice") || p.2 == "button" || p.2 == "toggle" || p.2 == "checkbox" {
+ } else if p.2.starts_with("slider") {
+ 38.0
+ } else if p.2 == "text" || p.2.starts_with("spinbox") || p.2.starts_with("choice") {
+ 42.0
+ } else if p.2 == "button" || p.2 == "toggle" || p.2 == "checkbox" {
24.0
} else {
20.0
@@ -81,11 +85,7 @@ impl ParametersBg {
for (i, s_opt) in self.sliders.iter_mut().enumerate() {
if let Some(s) = s_opt {
let r = rects[i];
- let track_x = self.base.x + 100.0;
- let track_w = (self.base.w - 100.0 - 20.0).max(10.0);
- let track_y = r.1 + 4.0;
- let track_h = 12.0;
- s.set_rect(track_x, track_y, track_w, track_h);
+ s.set_rect(r.0, r.1, r.2, r.3);
}
}
for (i, f_opt) in self.float3s.iter_mut().enumerate() {
@@ -97,41 +97,31 @@ impl ParametersBg {
for (i, sb_opt) in self.spinboxes.iter_mut().enumerate() {
if let Some(sb) = sb_opt {
let r = rects[i];
- let box_x = self.base.x + 100.0;
- let box_w = (self.base.w - 100.0 - 16.0).max(10.0);
- sb.set_rect(box_x, r.1, box_w, r.3);
+ sb.set_rect(r.0, r.1, r.2, r.3);
}
}
for (i, b_opt) in self.buttons.iter_mut().enumerate() {
if let Some(b) = b_opt {
let r = rects[i];
- let box_x = self.base.x + 100.0;
- let box_w = (self.base.w - 100.0 - 16.0).max(10.0);
- b.set_rect(box_x, r.1, box_w, r.3);
+ b.set_rect(r.0, r.1, r.2, r.3);
}
}
for (i, d_opt) in self.choices.iter_mut().enumerate() {
if let Some(d) = d_opt {
let r = rects[i];
- let box_x = self.base.x + 100.0;
- let box_w = (self.base.w - 100.0 - 16.0).max(10.0);
- d.set_rect(box_x, r.1, box_w, r.3);
+ d.set_rect(r.0, r.1, r.2, r.3);
}
}
for (i, tb_opt) in self.texts.iter_mut().enumerate() {
if let Some(tb) = tb_opt {
let r = rects[i];
- let box_x = self.base.x + 100.0;
- let box_w = (self.base.w - 100.0 - 16.0).max(10.0);
- tb.set_rect(box_x, r.1, box_w, r.3);
+ tb.set_rect(r.0, r.1, r.2, r.3);
}
}
for (i, cb_opt) in self.checkboxes.iter_mut().enumerate() {
if let Some(cb) = cb_opt {
let r = rects[i];
- let box_x = self.base.x + 100.0;
- let box_w = (self.base.w - 100.0 - 16.0).max(10.0);
- cb.set_rect(box_x, r.1, box_w, r.3);
+ cb.set_rect(r.0, r.1, r.2, r.3);
}
}
}
@@ -142,13 +132,6 @@ impl ParametersBg {
for (i, (name, value, ptype)) in self.display_params.iter().enumerate() {
let r = rects[i];
if ptype.starts_with("slider") {
- labels.push(TextLabel {
- text: name.clone(),
- x: self.base.x + 8.0,
- y: r.1,
- font_size: 12.0,
- color: [0xaa, 0xaa, 0xbb],
- });
if let Some(s) = &self.sliders[i] {
labels.extend(s.text_labels());
}
@@ -189,35 +172,14 @@ impl ParametersBg {
color: [0xee, 0xee, 0xf0],
});
} else if ptype.starts_with("spinbox") {
- labels.push(TextLabel {
- text: name.clone(),
- x: self.base.x + 8.0,
- y: r.1 + (r.3 - 12.0) / 2.0 - 2.0,
- font_size: 12.0,
- color: [0xaa, 0xaa, 0xbb],
- });
if let Some(sb) = &self.spinboxes[i] {
labels.extend(sb.text_labels());
}
} else if ptype == "text" {
- labels.push(TextLabel {
- text: name.clone(),
- x: self.base.x + 8.0,
- y: r.1 + (r.3 - 12.0) / 2.0 - 2.0,
- font_size: 12.0,
- color: [0xaa, 0xaa, 0xbb],
- });
if let Some(tb) = &self.texts[i] {
labels.extend(tb.text_labels());
}
} else if ptype.starts_with("choice") {
- labels.push(TextLabel {
- text: name.clone(),
- x: self.base.x + 8.0,
- y: r.1 + (r.3 - 12.0) / 2.0 - 2.0,
- font_size: 12.0,
- color: [0xaa, 0xaa, 0xbb],
- });
if let Some(d) = &self.choices[i] {
labels.extend(d.text_labels());
}
@@ -226,13 +188,6 @@ impl ParametersBg {
labels.extend(b.text_labels());
}
} else if ptype == "toggle" || ptype == "checkbox" {
- labels.push(TextLabel {
- text: name.clone(),
- x: self.base.x + 8.0,
- y: r.1 + (r.3 - 12.0) / 2.0 - 2.0,
- font_size: 12.0,
- color: [0xaa, 0xaa, 0xbb],
- });
if let Some(cb) = &self.checkboxes[i] {
labels.extend(cb.text_labels());
}
@@ -438,9 +393,9 @@ impl Element for ParametersBg {
for (i, p) in self.display_params.iter().enumerate() {
if p.2.starts_with("slider") {
let r = rects[i];
- let row_y = r.1;
- if py >= row_y - 2.0 && py <= row_y + 18.0 {
- if let Some(s) = &mut self.sliders[i] {
+ if let Some(s) = &mut self.sliders[i] {
+ let top = crate::widget::label_offset(s);
+ if py >= r.1 + top && py <= r.1 + r.3 {
s.drag_begin(px, py);
self.dragging_param = Some(i);
break;
@@ -1360,7 +1315,7 @@ impl ParamController for ParametersBg {
} else {
0.0
};
- Some(Slider::new().with_value(t).with_range(min, max).with_readout(true))
+ Some(Slider::new().with_value(t).with_range(min, max).with_readout(true).with_label(&p.0))
} else {
None
}
@@ -1378,7 +1333,7 @@ impl ParamController for ParametersBg {
if p.2.starts_with("spinbox") {
let (min, max, step) = parse_spinbox_range(&p.2);
let val = p.1.parse::<i32>().unwrap_or(min);
- Some(Spinbox::new(val, min, max, step))
+ Some(Spinbox::new(val, min, max, step).with_label(&p.0))
} else {
None
}
@@ -1395,14 +1350,14 @@ impl ParamController for ParametersBg {
let options_str = p.2.strip_prefix("choice:").unwrap_or("");
let options: Vec<String> = options_str.split(',').map(|s| s.to_string()).collect();
let selected = options.iter().position(|o| o == &p.1).unwrap_or(0);
- Some(Dropdown::new(options, selected))
+ Some(Dropdown::new(options, selected).with_label(&p.0))
} else {
None
}
}).collect();
self.texts = self.display_params.iter().map(|p| {
if p.2 == "text" {
- Some(TextBox::new(p.1.clone()))
+ Some(TextBox::new(p.1.clone()).with_label(&p.0))
} else {
None
}
@@ -1410,7 +1365,7 @@ impl ParamController for ParametersBg {
self.checkboxes = self.display_params.iter().map(|p| {
if p.2 == "toggle" || p.2 == "checkbox" {
let checked = p.1.trim().to_lowercase() == "true";
- let mut cb = Checkbox::new();
+ let mut cb = Checkbox::new().with_label(&p.0);
cb.set_checked(checked);
Some(cb)
} else {
diff --git a/src/widget/json_layout.rs b/src/widget/json_layout.rs
index 839ec7a..f30b49f 100644
--- a/src/widget/json_layout.rs
+++ b/src/widget/json_layout.rs
@@ -275,6 +275,12 @@ impl JsonLayoutWidget {
}
impl Element for JsonLayoutWidget {
+ 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 base(&self) -> Option<&Widget> { Some(&self.base) }
fn base_mut(&mut self) -> Option<&mut Widget> { Some(&mut self.base) }
fn color(&self) -> [f32; 4] { [0.0, 0.0, 0.0, 0.0] }
diff --git a/src/widget/mod.rs b/src/widget/mod.rs
index 596a81c..e1ddd06 100644
--- a/src/widget/mod.rs
+++ b/src/widget/mod.rs
@@ -127,13 +127,23 @@ pub trait Element {
fn preferred_height(&self) -> Option<f32> { None }
fn as_any(&self) -> &dyn std::any::Any {
- panic!("as_any not implemented");
+ struct DummyAny;
+ static DUMMY: DummyAny = DummyAny;
+ &DUMMY
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
- panic!("as_any_mut not implemented");
+ struct DummyAny;
+ thread_local! {
+ static DUMMY_MUT: std::cell::UnsafeCell<DummyAny> = std::cell::UnsafeCell::new(DummyAny);
+ }
+ DUMMY_MUT.with(|d| unsafe { &mut *d.get() })
}
fn as_ptr(&self) -> *mut (dyn Element + 'static) {
- panic!("as_ptr not implemented");
+ struct DummyElement;
+ impl Element for DummyElement {
+ fn color(&self) -> [f32; 4] { [0.0, 0.0, 0.0, 0.0] }
+ }
+ std::ptr::null_mut::<DummyElement>() as *mut (dyn Element + 'static)
}
fn handle_event(&mut self, event: &Event, ctx: &mut UiContext) -> bool {