diff --git a/hui-examples/boilerplate.rs b/hui-examples/boilerplate.rs new file mode 100644 index 0000000..060a62c --- /dev/null +++ b/hui-examples/boilerplate.rs @@ -0,0 +1,54 @@ +use glam::{UVec2, Vec2}; +use glium::{backend::glutin::SimpleWindowBuilder, Surface}; +use winit::{ + event::{Event, WindowEvent}, + event_loop::{EventLoopBuilder, ControlFlow} +}; +use hui::UiInstance; +use hui_glium::GliumUiRenderer; + +/// Generates a `main` function that initializes glium renderer, `UiInstance`, and runs the event loop. +macro_rules! ui_main { + ($closure: expr) => { + fn main() { + $crate::boilerplate::ui($closure); + } + }; +} + +/// Initializes glium renderer, `UiInstance`, and runs the event loop. +pub fn ui(mut x: impl FnMut(&mut UiInstance, Vec2)) { + kubi_logging::init(); + + let event_loop = EventLoopBuilder::new().build().unwrap(); + let (_window, display) = SimpleWindowBuilder::new().build(&event_loop); + + let mut hui = UiInstance::new(); + let mut backend = GliumUiRenderer::new(&display); + + event_loop.run(|event, window_target| { + window_target.set_control_flow(ControlFlow::Poll); + match event { + Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { + window_target.exit(); + }, + Event::AboutToWait => { + let mut frame = display.draw(); + frame.clear_color_srgb(0.5, 0.5, 0.5, 0.); + + hui.begin(); + + let size = UVec2::from(display.get_framebuffer_dimensions()).as_vec2(); + x(&mut hui, size); + + hui.end(); + + backend.update(&hui); + backend.draw(&mut frame, size); + + frame.finish().unwrap(); + } + _ => (), + } + }).unwrap(); +} diff --git a/hui-examples/examples/align_test.rs b/hui-examples/examples/align_test.rs index 72612f8..a8d4aa9 100644 --- a/hui-examples/examples/align_test.rs +++ b/hui-examples/examples/align_test.rs @@ -1,3 +1,5 @@ +//WARNING: THIS EXAMPLE IS EXTREMELY OUTDATED AND USES DEPRECATED API + use std::time::Instant; use glam::{UVec2, vec4}; use glium::{backend::glutin::SimpleWindowBuilder, Surface}; @@ -7,7 +9,7 @@ use winit::{ }; use hui::{ element::{ - container::Container, progress_bar::ProgressBar, rect::Rect, ElementList, UiElement + container::Container, progress_bar::ProgressBar, fill_rect::FillRect, ElementList, UiElement }, layout::{Alignment, UiDirection, Size}, rectangle::{Corners, Sides}, UiInstance }; use hui_glium::GliumUiRenderer; @@ -69,23 +71,26 @@ fn main() { padding: Sides::all(5.), gap: 10., children: ElementList(vec![ - Box::new(Rect { - size: (Size::Fraction(0.5), Size::Static(30.)), - color: vec4(0.75, 0., 0., 1.).into() + Box::new(FillRect { + size: (Size::Fraction(0.5), Size::Static(30.)).into(), + background: vec4(0.75, 0., 0., 1.).into(), + ..Default::default() }), - Box::new(Rect { - size: (Size::Fraction(z / 2. + 0.5), Size::Static(30.)), - color: Corners::left_right( + Box::new(FillRect { + size: (Size::Fraction(z / 2. + 0.5), Size::Static(30.)).into(), + background: Corners::left_right( vec4(1., 0., 0., 1.), vec4(0., 1., 0., 1.) ).into(), + ..Default::default() }), ]), ..Default::default() }), - Box::new(Rect { - size: (Size::Fraction(z / 2. + 0.5), Size::Static(30.)), - color: vec4(0., 0.75, 0., 1.).into() + Box::new(FillRect { + size: (Size::Fraction(z / 2. + 0.5), Size::Static(30.)).into(), + background: vec4(0., 0.75, 0., 1.).into(), + ..Default::default() }), Box::new(Container { gap: 5., @@ -95,13 +100,14 @@ fn main() { children: { let mut x: Vec> = vec![]; for i in 0..10 { - x.push(Box::new(Rect { - size: (Size::Static(50.), Size::Static(50.)), - color: if i == 1 { + x.push(Box::new(FillRect { + size: (Size::Static(50.), Size::Static(50.)).into(), + background: if i == 1 { vec4(0.75, 0.75, 0.75, 0.75).into() } else { vec4(0.5, 0.5, 0.5, 0.75).into() - } + }, + ..Default::default() })); } ElementList(x) @@ -123,9 +129,10 @@ fn main() { bottom_right: 0., }, children: ElementList(vec![ - Box::new(Rect { - size: (Size::Static(50.), Size::Static(50.)), - color: vec4(1., 1., 1., 0.75).into() + Box::new(FillRect { + size: (Size::Static(50.), Size::Static(50.)).into(), + background: vec4(1., 1., 1., 0.75).into(), + ..Default::default() }), ]), ..Default::default() diff --git a/hui-examples/examples/modern_testbed.rs b/hui-examples/examples/modern_testbed.rs deleted file mode 100644 index 22e3e2d..0000000 --- a/hui-examples/examples/modern_testbed.rs +++ /dev/null @@ -1,76 +0,0 @@ -use glam::UVec2; -use glium::{backend::glutin::SimpleWindowBuilder, Surface}; -use winit::{ - event::{Event, WindowEvent}, - event_loop::{EventLoopBuilder, ControlFlow} -}; -use hui::{ - UiInstance, color, size, - layout::Alignment, - element::{ - container::Container, text::Text, UiElementExt - }, -}; -use hui_glium::GliumUiRenderer; - -fn main() { - kubi_logging::init(); - - let event_loop = EventLoopBuilder::new().build().unwrap(); - let (_window, display) = SimpleWindowBuilder::new().build(&event_loop); - - let mut hui = UiInstance::new(); - let mut backend = GliumUiRenderer::new(&display); - - event_loop.run(|event, window_target| { - window_target.set_control_flow(ControlFlow::Poll); - match event { - Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { - window_target.exit(); - }, - Event::AboutToWait => { - let mut frame = display.draw(); - frame.clear_color_srgb(0.5, 0.5, 0.5, 0.); - - let resolution = UVec2::from(display.get_framebuffer_dimensions()).as_vec2(); - - hui.begin(); - - Container::default() - .with_size(size!(100%, 50%)) - .with_align(Alignment::Center) - .with_padding(5.) - .with_gap(10.) - .with_corner_radius(10.) - .with_background(color::WHITE) - .with_children(|ui| { - Text::default() - .with_text("Hello, world") - .with_text_size(100) - .with_color(color::BLACK) - .add_child(ui); - Container::default() - .with_padding((10., 20.)) - .with_corner_radius((2.5, 30., 2.5, 2.5)) - .with_background(color::DARK_RED) - .with_children(|ui| { - Text::default() - .with_text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.") - .with_text_size(24) - .add_child(ui); - }) - .add_child(ui); - }) - .add_root(&mut hui, resolution); - - hui.end(); - - backend.update(&hui); - backend.draw(&mut frame, resolution); - - frame.finish().unwrap(); - } - _ => (), - } - }).unwrap(); -} diff --git a/hui-examples/examples/mom_downloader.rs b/hui-examples/examples/mom_downloader.rs index fdbea3a..1b15018 100644 --- a/hui-examples/examples/mom_downloader.rs +++ b/hui-examples/examples/mom_downloader.rs @@ -1,3 +1,5 @@ +//WARNING: THIS EXAMPLE IS EXTREMELY OUTDATED AND USES DEPRECATED API + use std::time::Instant; use glam::{UVec2, vec4}; use glium::{backend::glutin::SimpleWindowBuilder, Surface}; diff --git a/hui-examples/examples/rounded_rect.rs b/hui-examples/examples/rounded_rect.rs index 97cc236..3d47137 100644 --- a/hui-examples/examples/rounded_rect.rs +++ b/hui-examples/examples/rounded_rect.rs @@ -1,3 +1,5 @@ +//WARNING: THIS EXAMPLE IS EXTREMELY OUTDATED AND USES DEPRECATED API + use glam::{vec4, UVec2}; use glium::{backend::glutin::SimpleWindowBuilder, Surface}; use winit::{ diff --git a/hui-examples/examples/text_weird.rs b/hui-examples/examples/text_weird.rs index 4dee33d..24eca1a 100644 --- a/hui-examples/examples/text_weird.rs +++ b/hui-examples/examples/text_weird.rs @@ -1,3 +1,5 @@ +//WARNING: THIS EXAMPLE IS EXTREMELY OUTDATED AND USES DEPRECATED API + use std::time::Instant; use glam::{UVec2, vec4}; use glium::{backend::glutin::SimpleWindowBuilder, Surface}; @@ -7,7 +9,7 @@ use winit::{ }; use hui::{ element::{ - container::Container, rect::Rect, spacer::Spacer, text::Text, ElementList + container::Container, fill_rect::FillRect, spacer::Spacer, text::Text, ElementList }, layout::Size, UiInstance }; use hui_glium::GliumUiRenderer; @@ -69,13 +71,15 @@ fn main() { ..Default::default() })); } - elem.push(Box::new(Rect { - size: (Size::Fraction(1.), Size::Static(10.)), - color: vec4(0., 0., 1., 1.).into(), + elem.push(Box::new(FillRect { + size: (Size::Fraction(1.), Size::Static(10.)).into(), + background: vec4(0., 0., 1., 1.).into(), + ..Default::default() })); - elem.push(Box::new(Rect { - size: (Size::Fraction(1.), Size::Static(10.)), - color: vec4(1., 1., 0., 1.).into(), + elem.push(Box::new(FillRect { + size: (Size::Fraction(1.), Size::Static(10.)).into(), + background: vec4(1., 1., 0., 1.).into(), + ..Default::default() })); elem.push(Box::new(Text { text: "Hello, world!\nżółty liść. życie nie ma sensu i wszyscy zginemy;\nтест кирилиці їїїїїїїїїїї\njapanese text: テスト".into(), @@ -84,13 +88,15 @@ fn main() { ..Default::default() })); if instant.elapsed().as_secs() & 1 != 0 { - elem.push(Box::new(Rect { - size: (Size::Fraction(1.), Size::Static(10.)), - color: vec4(1., 0., 0., 1.).into(), + elem.push(Box::new(FillRect { + size: (Size::Fraction(1.), Size::Static(10.)).into(), + background: vec4(1., 0., 0., 1.).into(), + ..Default::default() })); - elem.push(Box::new(Rect { - size: (Size::Fraction(1.), Size::Static(10.)), - color: vec4(0., 0., 0., 1.).into(), + elem.push(Box::new(FillRect { + size: (Size::Fraction(1.), Size::Static(10.)).into(), + background: vec4(0., 0., 0., 1.).into(), + ..Default::default() })); elem.push(Box::new(Spacer(100.))); elem.push(Box::new(Text { diff --git a/hui-examples/examples/ui_test.rs b/hui-examples/examples/ui_test.rs new file mode 100644 index 0000000..f6ed78a --- /dev/null +++ b/hui-examples/examples/ui_test.rs @@ -0,0 +1,38 @@ +use hui::{ + color, size, + layout::Alignment, + element::{UiElementExt, container::Container, text::Text}, +}; + +#[path = "../boilerplate.rs"] +#[macro_use] +mod boilerplate; + +ui_main!(|ui, size| { + Container::default() + .with_size(size!(100%, 50%)) + .with_align(Alignment::Center) + .with_padding(5.) + .with_gap(10.) + .with_corner_radius(10.) + .with_background(color::WHITE) + .with_children(|ui| { + Text::default() + .with_text("Hello, world") + .with_text_size(100) + .with_color(color::BLACK) + .add_child(ui); + Container::default() + .with_padding((10., 20.)) + .with_corner_radius((2.5, 30., 2.5, 2.5)) + .with_background(color::DARK_RED) + .with_children(|ui| { + Text::default() + .with_text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.") + .with_text_size(24) + .add_child(ui); + }) + .add_child(ui); + }) + .add_root(ui, size); +}); diff --git a/hui/Cargo.toml b/hui/Cargo.toml index 350f699..886a3ae 100644 --- a/hui/Cargo.toml +++ b/hui/Cargo.toml @@ -26,7 +26,7 @@ nz = "0.3" document-features = "0.2" derive_setters = "0.1" #smallvec = "1.13" -#tinyset = "0.4" +tinyset = "0.4" [features] default = ["builtin_elements", "builtin_font", "pixel_perfect_text"] diff --git a/hui/src/background.rs b/hui/src/background.rs index ed40133..70226a6 100644 --- a/hui/src/background.rs +++ b/hui/src/background.rs @@ -9,27 +9,28 @@ use crate::rectangle::Corners; // pub texture: Option // } +//TODO: move this into the color module? #[derive(Clone, Copy, Default, Debug, PartialEq)] -pub enum BackgroundColor { +pub enum RectBackground { #[default] Transparent, Solid(Vec4), Gradient(Corners), } -impl From<(f32, f32, f32, f32)> for BackgroundColor { +impl From<(f32, f32, f32, f32)> for RectBackground { fn from(color: (f32, f32, f32, f32)) -> Self { Self::Solid(vec4(color.0, color.1, color.2, color.3)) } } -impl From> for BackgroundColor { +impl From> for RectBackground { fn from(corners: Corners) -> Self { Self::Gradient(corners) } } -impl From> for BackgroundColor { +impl From> for RectBackground { fn from(color: Option) -> Self { match color { Some(color) => Self::Solid(color), @@ -38,19 +39,19 @@ impl From> for BackgroundColor { } } -impl From for BackgroundColor { +impl From for RectBackground { fn from(color: Vec4) -> Self { Self::Solid(color) } } -impl From<(f32, f32, f32)> for BackgroundColor { +impl From<(f32, f32, f32)> for RectBackground { fn from(color: (f32, f32, f32)) -> Self { Self::Solid(vec4(color.0, color.1, color.2, 1.)) } } -impl From> for BackgroundColor { +impl From> for RectBackground { fn from(corners: Corners) -> Self { Self::Gradient(Corners { top_left: corners.top_left.extend(1.), @@ -61,7 +62,7 @@ impl From> for BackgroundColor { } } -impl From> for BackgroundColor { +impl From> for RectBackground { fn from(color: Option) -> Self { match color { Some(color) => Self::Solid(color.extend(1.)), @@ -70,13 +71,13 @@ impl From> for BackgroundColor { } } -impl From for BackgroundColor { +impl From for RectBackground { fn from(color: Vec3) -> Self { Self::Solid(color.extend(1.)) } } -impl BackgroundColor { +impl RectBackground { /// Currently, never returns None.\ /// `Option` has been added in preparation for future changes.\ /// (`Background::Texture` etc) diff --git a/hui/src/element/builtin.rs b/hui/src/element/builtin.rs index 6f8fe6f..e3edfe8 100644 --- a/hui/src/element/builtin.rs +++ b/hui/src/element/builtin.rs @@ -2,7 +2,7 @@ pub mod container; #[cfg(feature = "builtin_elements")] -pub mod rect; +pub mod fill_rect; #[cfg(feature = "builtin_elements")] pub mod spacer; diff --git a/hui/src/element/builtin/container.rs b/hui/src/element/builtin/container.rs index 4e164e1..d1e9f52 100644 --- a/hui/src/element/builtin/container.rs +++ b/hui/src/element/builtin/container.rs @@ -1,7 +1,7 @@ use derive_setters::Setters; use glam::{Vec2, vec2}; use crate::{ - background::BackgroundColor, + background::RectBackground, draw::{RoundedCorners, UiDrawCommand}, element::{ElementList, MeasureContext, ProcessContext, UiElement}, layout::{Alignment, Alignment2d, LayoutInfo, UiDirection, Size, Size2d}, @@ -31,7 +31,7 @@ pub struct Container { #[setters(into)] pub align: Alignment2d, #[setters(into)] - pub background: BackgroundColor, + pub background: RectBackground, #[setters(into)] pub corner_radius: Corners, #[setters(skip)] diff --git a/hui/src/element/builtin/fill_rect.rs b/hui/src/element/builtin/fill_rect.rs new file mode 100644 index 0000000..623727b --- /dev/null +++ b/hui/src/element/builtin/fill_rect.rs @@ -0,0 +1,75 @@ +//! Simple filled rectangle with the specified size, background and corner radius + +use derive_setters::Setters; +use glam::{vec2, Vec4}; +use crate::{ + background::RectBackground, + draw::{UiDrawCommand, RoundedCorners}, + element::{UiElement, MeasureContext, ProcessContext}, + layout::{Size, Size2d}, + measure::Response, + rectangle::Corners, + size, +}; + +/// Simple filled rectangle with the specified size, background, and corner radius +#[derive(Debug, Clone, Copy, Setters)] +#[setters(prefix = "with_")] +pub struct FillRect { + /// Size of the rectangle + #[setters(into)] + pub size: Size2d, + + /// Background color of the rectangle + #[setters(into)] + pub background: RectBackground, + + /// Corner radius of the rectangle + #[setters(into)] + pub corner_radius: Corners, +} + +impl Default for FillRect { + fn default() -> Self { + Self { + size: size!(10, 10), + background: Vec4::new(0., 0., 0., 0.5).into(), + corner_radius: Corners::all(0.), + } + } +} + +impl UiElement for FillRect { + fn measure(&self, ctx: MeasureContext) -> Response { + Response { + size: vec2( + match self.size.width { + Size::Auto => ctx.layout.max_size.x, + Size::Fraction(percentage) => ctx.layout.max_size.x * percentage, + Size::Static(pixels) => pixels, + }, + match self.size.height { + Size::Auto => ctx.layout.max_size.y, + Size::Fraction(percentage) => ctx.layout.max_size.y * percentage, + Size::Static(pixels) => pixels, + }, + ), + hints: Default::default(), + user_data: None + } + } + + fn process(&self, ctx: ProcessContext) { + if !self.background.is_transparent() { + ctx.draw.add(UiDrawCommand::Rectangle { + position: ctx.layout.position, + size: ctx.measure.size, + color: self.background.corners().unwrap(), + texture: None, + rounded_corners: (self.corner_radius.max_f32() > 0.).then_some({ + RoundedCorners::from_radius(self.corner_radius) + }), + }); + } + } +} diff --git a/hui/src/element/builtin/progress_bar.rs b/hui/src/element/builtin/progress_bar.rs index 9578ea1..7d391b2 100644 --- a/hui/src/element/builtin/progress_bar.rs +++ b/hui/src/element/builtin/progress_bar.rs @@ -1,14 +1,25 @@ -use glam::{vec2, Vec4, vec4}; +use derive_setters::Setters; +use glam::{vec2, vec4}; use crate::{ - draw::{RoundedCorners, UiDrawCommand}, element::{MeasureContext, ProcessContext, UiElement}, layout::Size, measure::Response, rectangle::Corners + background::RectBackground, + draw::{RoundedCorners, UiDrawCommand}, + element::{MeasureContext, ProcessContext, UiElement}, + layout::{Size, Size2d}, + measure::Response, + rectangle::Corners }; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Setters)] +#[setters(prefix = "with_")] pub struct ProgressBar { - pub size: (Size, Size), pub value: f32, - pub color_foreground: Vec4, - pub color_background: Vec4, + #[setters(into)] + pub size: Size2d, + #[setters(into)] + pub foreground: RectBackground, + #[setters(into)] + pub background: RectBackground, + #[setters(into)] pub corner_radius: Corners, } @@ -19,10 +30,10 @@ impl ProgressBar { impl Default for ProgressBar { fn default() -> Self { Self { - size: (Size::Auto, Size::Auto), value: 0., - color_foreground: vec4(0.0, 0.0, 1.0, 1.0), - color_background: vec4(0.0, 0.0, 0.0, 1.0), + size: Size::Auto.into(), + foreground: vec4(0.0, 0.0, 1.0, 1.0).into(), + background: vec4(0.0, 0.0, 0.0, 1.0).into(), corner_radius: Corners::all(0.), } } @@ -34,12 +45,12 @@ impl UiElement for ProgressBar { fn measure(&self, ctx: MeasureContext) -> Response { Response { size: vec2( - match self.size.0 { + match self.size.width { Size::Auto => ctx.layout.max_size.x.max(300.), Size::Fraction(p) => ctx.layout.max_size.x * p, Size::Static(p) => p, }, - match self.size.1 { + match self.size.height { Size::Auto => Self::DEFAULT_HEIGHT, Size::Fraction(p) => ctx.layout.max_size.y * p, Size::Static(p) => p, @@ -71,7 +82,7 @@ impl UiElement for ProgressBar { ctx.draw.add(UiDrawCommand::Rectangle { position: ctx.layout.position, size: ctx.measure.size, - color: Corners::all(self.color_background), + color: self.background.corners().unwrap(), texture: None, rounded_corners }); @@ -80,7 +91,7 @@ impl UiElement for ProgressBar { ctx.draw.add(UiDrawCommand::Rectangle { position: ctx.layout.position, size: ctx.measure.size * vec2(value, 1.0), - color: Corners::all(self.color_foreground), + color: self.foreground.corners().unwrap(), texture: None, rounded_corners, }); diff --git a/hui/src/element/builtin/rect.rs b/hui/src/element/builtin/rect.rs deleted file mode 100644 index 85698d0..0000000 --- a/hui/src/element/builtin/rect.rs +++ /dev/null @@ -1,55 +0,0 @@ -use glam::{vec2, Vec4}; -use crate::{ - background::BackgroundColor, - draw::UiDrawCommand, - element::{MeasureContext, ProcessContext, UiElement}, - layout::Size, - measure::Response -}; - -pub struct Rect { - pub size: (Size, Size), - pub color: BackgroundColor, -} - -impl Default for Rect { - fn default() -> Self { - Self { - size: (Size::Static(10.), Size::Static(10.)), - color: Vec4::new(0., 0., 0., 0.5).into(), - } - } -} - -impl UiElement for Rect { - fn measure(&self, ctx: MeasureContext) -> Response { - Response { - size: vec2( - match self.size.0 { - Size::Auto => ctx.layout.max_size.x, - Size::Fraction(percentage) => ctx.layout.max_size.x * percentage, - Size::Static(pixels) => pixels, - }, - match self.size.1 { - Size::Auto => ctx.layout.max_size.y, - Size::Fraction(percentage) => ctx.layout.max_size.y * percentage, - Size::Static(pixels) => pixels, - }, - ), - hints: Default::default(), - user_data: None - } - } - - fn process(&self, ctx: ProcessContext) { - if !self.color.is_transparent() { - ctx.draw.add(UiDrawCommand::Rectangle { - position: ctx.layout.position, - size: ctx.measure.size, - color: self.color.corners().unwrap(), - texture: None, - rounded_corners: None, - }); - } - } -} diff --git a/hui/src/element/builtin/spacer.rs b/hui/src/element/builtin/spacer.rs index 98b9e05..4fb4b74 100644 --- a/hui/src/element/builtin/spacer.rs +++ b/hui/src/element/builtin/spacer.rs @@ -1,3 +1,5 @@ +//! Adds spacing between elements in a layout + use glam::vec2; use crate::{ element::{MeasureContext, ProcessContext, UiElement}, @@ -5,6 +7,8 @@ use crate::{ layout::UiDirection }; +/// Adds spacing between elements in a layout\ +/// (depending on the current layout direction) pub struct Spacer(pub f32); impl Default for Spacer { diff --git a/hui/src/input.rs b/hui/src/input.rs index afbcfb3..93e2d75 100644 --- a/hui/src/input.rs +++ b/hui/src/input.rs @@ -4,6 +4,7 @@ use std::hash::{Hash, Hasher}; use glam::Vec2; use hashbrown::HashMap; use nohash_hasher::BuildNoHashHasher; +use tinyset::{SetU32, SetUsize}; use crate::rectangle::Rect; /// Represents a mouse button. @@ -110,6 +111,15 @@ pub(crate) enum Pointer { TouchFinger(TouchFinger), } +impl Pointer { + pub fn current_position(&self) -> Vec2 { + match self { + Pointer::MousePointer(mouse) => mouse.current_position, + Pointer::TouchFinger(touch) => touch.current_position, + } + } +} + impl ActiveMouseButton { /// Check if the pointer (mouse or touch) was just pressed\ /// (i.e. it was not pressed in the previous frame, but is pressed now) @@ -128,8 +138,38 @@ impl ActiveMouseButton { } } +pub struct PointerQuery<'a> { + pointers: &'a [Pointer], + /// Set of pointer IDs to filter **out** + filter_out: SetUsize, +} + +impl<'a> PointerQuery<'a> { + fn new(pointers: &'a [Pointer]) -> Self { + Self { + pointers, + filter_out: SetUsize::new(), + } + } + + /// Filter pointers that are *currently* located within the specified rectangle + pub fn within_rect(&mut self, rect: Rect) -> &mut Self { + for (idx, pointer) in self.pointers.iter().enumerate() { + if !rect.contains_point(pointer.current_position()) { + self.filter_out.insert(idx); + } + } + self + } + + /// Check if any pointers matched the filter + pub fn any_matched(&self) -> bool { + self.filter_out.len() != self.pointers.len() + } +} + pub(crate) struct UiInputState { - pointers: Vec, + pointers: Vec, } impl UiInputState { @@ -139,7 +179,7 @@ impl UiInputState { } } - pub fn query_pointer(&self, area: Rect) -> bool { - todo!() + pub fn query_pointer(&self) -> PointerQuery { + PointerQuery::new(&self.pointers) } } diff --git a/hui/src/rectangle.rs b/hui/src/rectangle.rs index 09848fa..099d196 100644 --- a/hui/src/rectangle.rs +++ b/hui/src/rectangle.rs @@ -8,6 +8,11 @@ pub struct Rect { pub size: Vec2, } +impl Rect { + pub fn contains_point(&self, point: Vec2) -> bool { + point.cmpge(self.position).all() && point.cmple(self.position + self.size).all() + } +} /// Represents 4 sides of a rectangular shape. #[derive(Default, Clone, Copy, PartialEq, Eq, Debug)] pub struct Sides {