From fc4bc83ba95cdc45d68edd29d34114ebe9cc34ee Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Sun, 24 Mar 2024 21:35:44 +0100 Subject: [PATCH] add `covers_opaque` --- hui/src/element/builtin/slider.rs | 45 +++++++++++------------------ hui/src/frame.rs | 11 +++++++ hui/src/frame/impls.rs | 48 +++++++++++++++++++++++++++++++ hui/src/frame/rect.rs | 15 ++++++++++ hui/src/frame/stack.rs | 5 ++++ 5 files changed, 96 insertions(+), 28 deletions(-) diff --git a/hui/src/element/builtin/slider.rs b/hui/src/element/builtin/slider.rs index 9b9ac07..2128f5f 100644 --- a/hui/src/element/builtin/slider.rs +++ b/hui/src/element/builtin/slider.rs @@ -122,7 +122,6 @@ impl UiElement for Slider { // } else { // vec2(15., ctx.measure.size.y) // }; - let handle_size = vec2(self.handle_width, ctx.measure.size.y); //Draw the track @@ -130,37 +129,25 @@ impl UiElement for Slider { //However, if the handle is not opaque, we need to draw the background as the active part won't quite reach the end //Of corse, if it's fully transparent, we don't need to draw it either // if !(self.track_color.is_transparent() || (self.track_active_color.is_opaque() && self.handle_color.is_opaque() && self.value >= 1.)) { - // ctx.draw.add(UiDrawCommand::Rectangle { - // position: ctx.layout.position + ctx.measure.size * vec2(0., 0.5 - self.track_height_ratio / 2.), - // size: ctx.measure.size * vec2(1., self.track_height_ratio), - // color: self.track_color.into(), - // texture: None, - // rounded_corners: None, - // }); - // } - self.track.draw( - ctx.draw, - ctx.layout.position + ctx.measure.size * vec2(0., 0.5 - self.track_height_ratio / 2.), - ctx.measure.size * vec2(1., self.track_height_ratio), - ); + if !(self.track_active.covers_opaque() && self.handle.covers_opaque() && self.value >= 1.) { + self.track.draw( + ctx.draw, + ctx.layout.position + ctx.measure.size * vec2(0., 0.5 - self.track_height_ratio / 2.), + ctx.measure.size * vec2(1., self.track_height_ratio), + ); + } //"Active" part of the track //We can skip drawing it if it's fully transparent or value <= 0. //But if the handle is not opaque, it should be visible even if value is zero // if !(self.track_active_color.is_transparent() || (self.value <= 0. && self.handle_color.is_opaque())) { - // ctx.draw.add(UiDrawCommand::Rectangle { - // position: ctx.layout.position + ctx.measure.size * vec2(0., 0.5 - self.track_height_ratio / 2.), - // size: (ctx.measure.size - handle_size * Vec2::X) * vec2(self.value, self.track_height_ratio) + handle_size * Vec2::X / 2., - // color: self.track_active_color.into(), - // texture: None, - // rounded_corners: None, - // }); - // } - self.track_active.draw( - ctx.draw, - ctx.layout.position + ctx.measure.size * vec2(0., 0.5 - self.track_height_ratio / 2.), - (ctx.measure.size - handle_size * Vec2::X) * vec2(self.value, self.track_height_ratio) + handle_size * Vec2::X / 2., - ); + if !(self.handle.covers_opaque() && self.value <= 0.) { + self.track_active.draw( + ctx.draw, + ctx.layout.position + ctx.measure.size * vec2(0., 0.5 - self.track_height_ratio / 2.), + (ctx.measure.size - handle_size * Vec2::X) * vec2(self.value, self.track_height_ratio) + handle_size * Vec2::X / 2., + ); + } // The handle // if handle_size.x != 0. && !self.handle_color.is_transparent() { @@ -182,7 +169,9 @@ impl UiElement for Slider { //handle events if let Some(res) = ctx.input.check_active(ctx.measure.rect(ctx.layout.position)) { let new_value = match self.follow_mode { - SliderFollowMode::Absolute => ((res.position_in_rect.x - handle_size.x / 2.) / (ctx.measure.size.x - handle_size.x)).clamp(0., 1.), + SliderFollowMode::Absolute => { + ((res.position_in_rect.x - handle_size.x / 2.) / (ctx.measure.size.x - handle_size.x)).clamp(0., 1.) + }, SliderFollowMode::Relative => { let delta = res.position_in_rect.x - res.last_position_in_rect.x; let delta_ratio = delta / (ctx.measure.size.x - handle_size.x); diff --git a/hui/src/frame.rs b/hui/src/frame.rs index c01936a..aa866a3 100644 --- a/hui/src/frame.rs +++ b/hui/src/frame.rs @@ -9,5 +9,16 @@ mod impls; pub use rect::FrameRect; pub trait Frame { + /// Draw the frame at the given position and size fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2); + + /// Check if the frame is guaranteed to be fully opaque and fully cover the parent frame regardless of it's size + /// + /// Returns true if the frame: + /// - Is fully opaque (i.e. `alpha >= 1.0`) + /// - Completely covers (or exceeds the size of) the frame + /// + /// False negatives are acceptable, but false positives ***are not***.\ + /// May be used for optimization purposes + fn covers_opaque(&self) -> bool { false } } diff --git a/hui/src/frame/impls.rs b/hui/src/frame/impls.rs index d5db6cd..8d18b28 100644 --- a/hui/src/frame/impls.rs +++ b/hui/src/frame/impls.rs @@ -16,6 +16,9 @@ impl Frame for ImageHandle { rounded_corners: None, }) } + fn covers_opaque(&self) -> bool { + true + } } impl Frame for FillColor { @@ -28,6 +31,9 @@ impl Frame for FillColor { rounded_corners: None, }) } + fn covers_opaque(&self) -> bool { + self.is_opaque() + } } // impl for various types resembling colors @@ -38,24 +44,36 @@ impl Frame for Corners { fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { FillColor::from(*self).draw(draw, position, parent_size) } + fn covers_opaque(&self) -> bool { + FillColor::from(*self).is_opaque() + } } impl Frame for (Vec4, Vec4, Vec4, Vec4) { fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { FillColor::from(*self).draw(draw, position, parent_size) } + fn covers_opaque(&self) -> bool { + FillColor::from(*self).is_opaque() + } } impl Frame for ((f32, f32, f32, f32), (f32, f32, f32, f32), (f32, f32, f32, f32), (f32, f32, f32, f32)) { fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { FillColor::from(*self).draw(draw, position, parent_size) } + fn covers_opaque(&self) -> bool { + FillColor::from(*self).is_opaque() + } } impl Frame for [[f32; 4]; 4] { fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { FillColor::from(*self).draw(draw, position, parent_size) } + fn covers_opaque(&self) -> bool { + FillColor::from(*self).is_opaque() + } } // Corners (RGB): @@ -64,24 +82,36 @@ impl Frame for Corners { fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { FillColor::from(*self).draw(draw, position, parent_size) } + fn covers_opaque(&self) -> bool { + FillColor::from(*self).is_opaque() + } } impl Frame for (Vec3, Vec3, Vec3, Vec3) { fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { FillColor::from(*self).draw(draw, position, parent_size) } + fn covers_opaque(&self) -> bool { + FillColor::from(*self).is_opaque() + } } impl Frame for ((f32, f32, f32), (f32, f32, f32), (f32, f32, f32), (f32, f32, f32)) { fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { FillColor::from(*self).draw(draw, position, parent_size) } + fn covers_opaque(&self) -> bool { + FillColor::from(*self).is_opaque() + } } impl Frame for [[f32; 3]; 4] { fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { FillColor::from(*self).draw(draw, position, parent_size) } + fn covers_opaque(&self) -> bool { + FillColor::from(*self).is_opaque() + } } // RGBA: @@ -90,18 +120,27 @@ impl Frame for Vec4 { fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { FillColor::from(*self).draw(draw, position, parent_size) } + fn covers_opaque(&self) -> bool { + FillColor::from(*self).is_opaque() + } } impl Frame for (f32, f32, f32, f32) { fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { FillColor::from(*self).draw(draw, position, parent_size) } + fn covers_opaque(&self) -> bool { + FillColor::from(*self).is_opaque() + } } impl Frame for [f32; 4] { fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { FillColor::from(*self).draw(draw, position, parent_size) } + fn covers_opaque(&self) -> bool { + FillColor::from(*self).is_opaque() + } } // RGB: @@ -110,16 +149,25 @@ impl Frame for Vec3 { fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { FillColor::from(*self).draw(draw, position, parent_size) } + fn covers_opaque(&self) -> bool { + FillColor::from(*self).is_opaque() + } } impl Frame for (f32, f32, f32) { fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { FillColor::from(*self).draw(draw, position, parent_size) } + fn covers_opaque(&self) -> bool { + FillColor::from(*self).is_opaque() + } } impl Frame for [f32; 3] { fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { FillColor::from(*self).draw(draw, position, parent_size) } + fn covers_opaque(&self) -> bool { + FillColor::from(*self).is_opaque() + } } diff --git a/hui/src/frame/rect.rs b/hui/src/frame/rect.rs index 1ae40ad..3a05833 100644 --- a/hui/src/frame/rect.rs +++ b/hui/src/frame/rect.rs @@ -6,6 +6,9 @@ use crate::{ }; use super::{Frame, point::FramePoint2d}; +/// A rectangular frame +/// +/// Can optionally be tinted, textured, and have rounded corners #[derive(Clone, Copy)] pub struct FrameRect { /// Background color of the frame\ @@ -126,4 +129,16 @@ impl Frame for FrameRect { ), }); } + + fn covers_opaque(&self) -> bool { + self.top_left.x.absolute <= 0. && + self.top_left.x.relative <= 0. && + self.top_left.y.absolute <= 0. && + self.top_left.y.relative <= 0. && + self.bottom_right.x.absolute >= 0. && + self.bottom_right.x.relative >= 1. && + self.bottom_right.y.absolute >= 0. && + self.bottom_right.y.relative >= 1. && + self.color.is_opaque() + } } diff --git a/hui/src/frame/stack.rs b/hui/src/frame/stack.rs index 1d4f45f..f6f6e78 100644 --- a/hui/src/frame/stack.rs +++ b/hui/src/frame/stack.rs @@ -9,6 +9,11 @@ impl Frame for FrameStack { self.0.draw(draw, position, parent_size); self.1.draw(draw, position, parent_size); } + + fn covers_opaque(&self) -> bool { + self.0.covers_opaque() || + self.1.covers_opaque() + } } pub trait FrameStackExt: Frame {