diff --git a/uidev/assets/gui/various_widgets.xml b/uidev/assets/gui/various_widgets.xml
index 7e4e8cf..5efb8e8 100644
--- a/uidev/assets/gui/various_widgets.xml
+++ b/uidev/assets/gui/various_widgets.xml
@@ -36,7 +36,7 @@
-
+
diff --git a/uidev/src/main.rs b/uidev/src/main.rs
index ad94189..be392f4 100644
--- a/uidev/src/main.rs
+++ b/uidev/src/main.rs
@@ -359,7 +359,11 @@ fn main() -> Result<(), Box> {
.unwrap();
if debug_draw_enabled {
- log::debug!("pass count: {}", draw_result.pass_count);
+ log::debug!(
+ "pass count: {}, primitive commands count: {}",
+ draw_result.pass_count,
+ draw_result.primitive_commands_count
+ );
}
cmd_buf.end_rendering().unwrap();
diff --git a/wgui/src/components/tooltip.rs b/wgui/src/components/tooltip.rs
index 41dfcfa..6eef47c 100644
--- a/wgui/src/components/tooltip.rs
+++ b/wgui/src/components/tooltip.rs
@@ -129,27 +129,34 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
let transform = Mat4::from_translation(Vec3::new(-0.5, 0.0, 0.0));
+ // this value needs to be bigger than rectangle padding sizes due to the
+ // transform stack & scissoring design. Needs investigation, zero-size objects
+ // would result in PushScissorStackResult::OutOfBounds otherwise preventing us
+ // to render the label. Didn't find the best solution for this edge-case yet,
+ // so here it is.
+ let pin_size = 32.0;
+
let (pin_left, pin_top, pin_align_items, pin_justify_content) = match params.info.side {
TooltipSide::Left => (
- absolute_boundary.left() - spacing,
- absolute_boundary.top() + absolute_boundary.size.y / 2.0,
+ absolute_boundary.left() - spacing - pin_size,
+ absolute_boundary.top() + absolute_boundary.size.y / 2.0 - pin_size / 2.0,
taffy::AlignItems::Center,
taffy::JustifyContent::End,
),
TooltipSide::Right => (
absolute_boundary.left() + absolute_boundary.size.x + spacing,
- absolute_boundary.top() + absolute_boundary.size.y / 2.0,
+ absolute_boundary.top() + absolute_boundary.size.y / 2.0 - pin_size / 2.0,
taffy::AlignItems::Center,
taffy::JustifyContent::Start,
),
TooltipSide::Top => (
- absolute_boundary.left() + absolute_boundary.size.x / 2.0,
- absolute_boundary.top() - spacing,
+ absolute_boundary.left() + absolute_boundary.size.x / 2.0 - pin_size / 2.0,
+ absolute_boundary.top() - spacing - pin_size,
taffy::AlignItems::End,
taffy::JustifyContent::Center,
),
TooltipSide::Bottom => (
- absolute_boundary.left() + absolute_boundary.size.x / 2.0,
+ absolute_boundary.left() + absolute_boundary.size.x / 2.0 - pin_size / 2.0,
absolute_boundary.top() + absolute_boundary.size.y + spacing,
taffy::AlignItems::Baseline,
taffy::JustifyContent::Center,
@@ -173,8 +180,8 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
},
/* important, to make it centered! */
size: taffy::Size {
- width: length(0.0),
- height: length(0.0),
+ width: length(pin_size),
+ height: length(pin_size),
},
..Default::default()
},
diff --git a/wgui/src/drawing.rs b/wgui/src/drawing.rs
index 0a2c337..eacabf1 100644
--- a/wgui/src/drawing.rs
+++ b/wgui/src/drawing.rs
@@ -288,24 +288,48 @@ pub fn push_transform_stack(
});
}
-/// returns true if scissor has been pushed
+#[derive(Eq, PartialEq)]
+pub enum PushScissorStackResult {
+ VisibleDontClip, // scissor calculated, but don't clip anything
+ VisibleAndClip, // scissor should be applied at this stage (ScissorSet primitive needs to be called)
+ OutOfBounds, // scissor rectangle is out of bounds (negative boundary dimensions)
+}
+
+impl PushScissorStackResult {
+ pub fn should_display(&self) -> bool {
+ *self != Self::OutOfBounds
+ }
+}
+
+/// Returns true if scissor has been pushed.
pub fn push_scissor_stack(
transform_stack: &mut TransformStack,
scissor_stack: &mut ScissorStack,
scroll_shift: Vec2,
info: &Option,
style: &taffy::Style,
-) -> bool {
- let scissor_pushed = info.is_some() && has_overflow_clip(style);
- if !scissor_pushed {
- return false;
- }
-
+) -> PushScissorStackResult {
let mut boundary_absolute = drawing::Boundary::construct_absolute(transform_stack);
boundary_absolute.pos += scroll_shift;
+
+ let do_clip = info.is_some() && has_overflow_clip(style);
+
scissor_stack.push(ScissorBoundary(boundary_absolute));
- true
+ if scissor_stack.is_out_of_bounds() {
+ return PushScissorStackResult::OutOfBounds;
+ }
+
+ if do_clip {
+ PushScissorStackResult::VisibleAndClip
+ } else {
+ PushScissorStackResult::VisibleDontClip
+ }
+}
+
+struct DrawWidgetInternal {
+ // how many times ScissorSet render primitives has been called?
+ scissor_set_count: u32,
}
fn draw_widget(
@@ -313,6 +337,7 @@ fn draw_widget(
state: &mut DrawState,
node_id: taffy::NodeId,
style: &taffy::Style,
+ internal: &mut DrawWidgetInternal,
widget: &Widget,
) {
let Ok(l) = params.layout.state.tree.layout(node_id) else {
@@ -346,9 +371,11 @@ fn draw_widget(
));
}
- let scissor_pushed = push_scissor_stack(state.transform_stack, state.scissor_stack, scroll_shift, &info, style);
+ let starting_scissor_set_count = internal.scissor_set_count;
- if scissor_pushed {
+ let scissor_result = push_scissor_stack(state.transform_stack, state.scissor_stack, scroll_shift, &info, style);
+
+ if scissor_result == PushScissorStackResult::VisibleAndClip {
if params.debug_draw {
let mut boundary_relative = drawing::Boundary::construct_relative(state.transform_stack);
boundary_relative.pos += scroll_shift;
@@ -358,10 +385,10 @@ fn draw_widget(
Color::new(1.0, 0.0, 1.0, 1.0),
));
}
-
state
.primitives
.push(drawing::RenderPrimitive::ScissorSet(*state.scissor_stack.get()));
+ internal.scissor_set_count += 1;
}
let draw_params = widget::DrawParams {
@@ -370,12 +397,16 @@ fn draw_widget(
style,
};
- widget_state.draw_all(state, &draw_params);
+ if scissor_result.should_display() {
+ widget_state.draw_all(state, &draw_params);
+ draw_children(params, state, node_id, internal, false);
+ }
- draw_children(params, state, node_id, false);
+ state.scissor_stack.pop();
- if scissor_pushed {
- state.scissor_stack.pop();
+ let current_scissor_set_count = internal.scissor_set_count;
+
+ if current_scissor_set_count > starting_scissor_set_count {
state
.primitives
.push(drawing::RenderPrimitive::ScissorSet(*state.scissor_stack.get()));
@@ -392,7 +423,13 @@ fn draw_widget(
}
}
-fn draw_children(params: &DrawParams, state: &mut DrawState, parent_node_id: taffy::NodeId, is_topmost: bool) {
+fn draw_children(
+ params: &DrawParams,
+ state: &mut DrawState,
+ parent_node_id: taffy::NodeId,
+ internal: &mut DrawWidgetInternal,
+ is_topmost: bool,
+) {
let layout = ¶ms.layout;
for node_id in layout.state.tree.child_ids(parent_node_id) {
@@ -415,7 +452,7 @@ fn draw_children(params: &DrawParams, state: &mut DrawState, parent_node_id: taf
continue;
};
- draw_widget(params, state, node_id, style, widget);
+ draw_widget(params, state, node_id, style, internal, widget);
if is_topmost {
state.primitives.push(RenderPrimitive::NewPass);
@@ -439,7 +476,9 @@ pub fn draw(params: &mut DrawParams) -> anyhow::Result> {
alterables: &mut alterables,
};
- draw_children(params, &mut state, params.layout.tree_root_node, true);
+ let mut internal = DrawWidgetInternal { scissor_set_count: 0 };
+
+ draw_children(params, &mut state, params.layout.tree_root_node, &mut internal, true);
params.layout.process_alterables(alterables)?;
diff --git a/wgui/src/layout.rs b/wgui/src/layout.rs
index f57031c..823e448 100644
--- a/wgui/src/layout.rs
+++ b/wgui/src/layout.rs
@@ -442,7 +442,7 @@ impl Layout {
widget.data.cached_absolute_boundary = drawing::Boundary::construct_absolute(&alterables.transform_stack);
- let scissor_pushed = push_scissor_stack(
+ let scissor_result = push_scissor_stack(
&mut alterables.transform_stack,
&mut alterables.scissor_stack,
scroll_shift,
@@ -450,24 +450,24 @@ impl Layout {
style,
);
- // check children first
- self.push_event_children(node_id, event, event_result, alterables, user_data)?;
+ if scissor_result.should_display() {
+ // check children first
+ self.push_event_children(node_id, event, event_result, alterables, user_data)?;
- if event_result.can_propagate() {
- let mut params = EventParams {
- state: &self.state,
- layout: l,
- alterables,
- node_id,
- style,
- };
+ if event_result.can_propagate() {
+ let mut params = EventParams {
+ state: &self.state,
+ layout: l,
+ alterables,
+ node_id,
+ style,
+ };
- widget.process_event(widget_id, node_id, event, event_result, user_data, &mut params)?;
+ widget.process_event(widget_id, node_id, event, event_result, user_data, &mut params)?;
+ }
}
- if scissor_pushed {
- alterables.scissor_stack.pop();
- }
+ alterables.scissor_stack.pop();
alterables.transform_stack.pop();
Ok(())
diff --git a/wgui/src/renderer_vk/context.rs b/wgui/src/renderer_vk/context.rs
index 66a9d1a..4dd552d 100644
--- a/wgui/src/renderer_vk/context.rs
+++ b/wgui/src/renderer_vk/context.rs
@@ -173,6 +173,7 @@ pub struct Context {
pub struct ContextDrawResult {
pub pass_count: u32,
+ pub primitive_commands_count: u32,
}
impl Context {
@@ -239,7 +240,7 @@ impl Context {
let mut passes = Vec::::new();
let mut needs_new_pass = true;
- let mut next_scissor: Option = None;
+ let mut cur_scissor: Option = None;
for primitive in primitives {
if needs_new_pass {
@@ -247,7 +248,7 @@ impl Context {
&mut atlas.text_atlas,
shared.rect_pipeline.clone(),
shared.image_pipeline.clone(),
- next_scissor,
+ cur_scissor,
self.pixel_scale,
)?);
needs_new_pass = false;
@@ -309,14 +310,26 @@ impl Context {
.add_image(extent.boundary, image.clone(), &extent.transform);
}
drawing::RenderPrimitive::ScissorSet(boundary) => {
- next_scissor = Some(boundary.0);
- needs_new_pass = true;
+ let skip = if let Some(cur_scissor) = cur_scissor {
+ // do not create a new pass if it's not needed (same scissor values)
+ cur_scissor == boundary.0
+ } else {
+ false
+ };
+
+ cur_scissor = Some(boundary.0);
+ if skip {
+ //log::debug!("same scissor boundary, re-using the same pass");
+ } else {
+ needs_new_pass = true;
+ }
}
}
}
let res = ContextDrawResult {
pass_count: passes.len() as u32,
+ primitive_commands_count: primitives.len() as u32,
};
for mut pass in passes {
diff --git a/wgui/src/stack.rs b/wgui/src/stack.rs
index c41bec2..21ba9a9 100644
--- a/wgui/src/stack.rs
+++ b/wgui/src/stack.rs
@@ -6,6 +6,7 @@ pub trait Pushable {
fn push(&mut self, item: &T);
}
+#[derive(Debug)]
pub struct GenericStack {
pub stack: [T; STACK_MAX],
top: u8,
@@ -50,7 +51,7 @@ impl, const STACK_MAX: usize> Default for GenericStack;
// Scissor stack
// ########################################
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Debug, PartialEq)]
pub struct ScissorBoundary(pub drawing::Boundary);
impl Default for ScissorBoundary {
@@ -144,3 +145,10 @@ impl Pushable for ScissorBoundary {
}
pub type ScissorStack = GenericStack;
+
+impl ScissorStack {
+ pub const fn is_out_of_bounds(&self) -> bool {
+ let boundary = &self.get().0;
+ boundary.width() < 0.0 || boundary.height() < 0.0
+ }
+}