slider and some input stuff

This commit is contained in:
griffi-gh 2024-03-21 18:41:28 +01:00
parent 4476307482
commit c60775ff4d
6 changed files with 91 additions and 25 deletions

View file

@ -15,8 +15,7 @@ use hui::{
}; };
enum CounterSignal { enum CounterSignal {
Increment, ChangeValue(f32)
Decrement,
} }
impl UiSignal for CounterSignal {} impl UiSignal for CounterSignal {}
@ -46,8 +45,9 @@ ui_main!(
.with_text_size(24) .with_text_size(24)
.add_child(ui); .add_child(ui);
Br.add_child(ui); Br.add_child(ui);
Slider::new(0.5) Slider::new(*counter as f32 / 100.)
.with_size(size!(66%, 20)) .with_size(size!(66%, 20))
.on_change(CounterSignal::ChangeValue)
.add_child(ui); .add_child(ui);
Br.add_child(ui); Br.add_child(ui);
for _ in 0..*counter { for _ in 0..*counter {
@ -59,8 +59,9 @@ ui_main!(
.add_root(ui, size); .add_root(ui, size);
ui.process_signals(|sig| match sig { ui.process_signals(|sig| match sig {
CounterSignal::Increment => *counter += 1, CounterSignal::ChangeValue(v) => {
CounterSignal::Decrement => *counter -= 1, *counter = (v * 100.).round() as usize;
}
}); });
} }
); );

View file

@ -1,22 +1,26 @@
//! work in progress //! work in progress
use derive_setters::Setters; use derive_setters::Setters;
use glam::{vec2, Vec2};
use crate::{ use crate::{
draw::UiDrawCommand, draw::{RoundedCorners, UiDrawCommand},
element::{MeasureContext, ProcessContext, UiElement}, element::{MeasureContext, ProcessContext, UiElement},
layout::{compute_size, Size2d}, layout::{compute_size, Size2d},
measure::Response, measure::Response,
rectangle::Corners, rectangle::Corners,
signal::UiSignal, signal::{SignalStore, UiSignal},
}; };
/// work in progress /// work in progress
#[derive(Default, Debug, Clone, Copy, Setters)] #[derive(Default, Setters)]
#[setters(prefix = "with_")] #[setters(prefix = "with_")]
pub struct Slider { pub struct Slider {
pub value: f32, pub value: f32,
pub size: Size2d, pub size: Size2d,
#[setters(skip)]
fire_on_shit: Option<Box<dyn Fn(&mut SignalStore, f32)>>,
} }
impl Slider { impl Slider {
@ -28,6 +32,15 @@ impl Slider {
..Default::default() ..Default::default()
} }
} }
pub fn on_change<S: UiSignal + 'static, T: Fn(f32) -> S + 'static>(self, f: T) -> Self {
Self {
fire_on_shit: Some(Box::new(move |s: &mut SignalStore, x| {
s.add::<S>(f(x));
})),
..self
}
}
} }
impl UiElement for Slider { impl UiElement for Slider {
@ -43,26 +56,33 @@ impl UiElement for Slider {
} }
fn process(&self, ctx: ProcessContext) { fn process(&self, ctx: ProcessContext) {
let bgrect_height_ratio = 0.25;
ctx.draw.add(UiDrawCommand::Rectangle { ctx.draw.add(UiDrawCommand::Rectangle {
position: ctx.layout.position, position: ctx.layout.position + ctx.measure.size * vec2(0., 0.5 - bgrect_height_ratio / 2.),
size: ctx.measure.size, size: ctx.measure.size * vec2(1., bgrect_height_ratio),
color: Corners::all((1., 0., 0., 1.).into()), color: Corners::all((1., 1., 1., 0.7).into()),
texture: None, texture: None,
rounded_corners: None, rounded_corners: None,
//Some(RoundedCorners::from_radius(Corners::all(bgrect_height_ratio * ctx.measure.size.y * 0.4))),
}); });
let value = self.value.clamp(0., 1.); let value = self.value.clamp(0., 1.);
let handle_size = vec2(15., ctx.measure.size.y);
ctx.draw.add(UiDrawCommand::Rectangle { ctx.draw.add(UiDrawCommand::Rectangle {
position: ctx.layout.position, position: ctx.layout.position + (ctx.measure.size.x * value - handle_size.x / 2.) * Vec2::X,
size: (ctx.measure.size.x * value, ctx.measure.size.y).into(), size: handle_size,
color: Corners::all((0., 1., 0., 1.).into()), color: Corners::all((1., 1., 1., 1.).into()),
texture: None, texture: None,
rounded_corners: None, rounded_corners: None,
//Some(RoundedCorners::from_radius(Corners::all(handle_size.x / 3.))),
}); });
//handle click etc //handle click etc
if let Some(res) = ctx.input.check_click(ctx.measure.rect(ctx.layout.position)) { if let Some(res) = ctx.input.check_active(ctx.measure.rect(ctx.layout.position)) {
let new_value = res.position_in_rect.x / ctx.measure.size.x; let new_value = (res.position_in_rect.x / ctx.measure.size.x).clamp(0., 1.);
if let Some(fire) = &self.fire_on_shit {
fire(ctx.signal, new_value);
}
//TODO call signal with new value //TODO call signal with new value
} }
} }

View file

@ -286,15 +286,7 @@ impl<'a> InputCtx<'a> {
rect.contains_point(self.0.mouse_pointer.current_position) rect.contains_point(self.0.mouse_pointer.current_position)
} }
/// Check if a rect can be considered "active" (i.e. held down) /// Check if a rect can be considered "clicked" in the current frame
///
/// WIP: Not implemented yet, always returns `false`
pub fn check_active(&self, _rect: Rect) -> bool {
//TODO `check_active`
false
}
/// Check if a rect can be considered "clicked"
/// ///
/// This can be triggered by multiple input sources, such as mouse, touch, etc.\ /// This can be triggered by multiple input sources, such as mouse, touch, etc.\
/// In case of a mouse, these conditions must be met: /// In case of a mouse, these conditions must be met:
@ -313,4 +305,15 @@ impl<'a> InputCtx<'a> {
position_in_rect: pos - rect.position, position_in_rect: pos - rect.position,
}) })
} }
// TODO: write better docs
/// Check if a rect is being actively being interacted with (e.g. dragged)
pub fn check_active(&self, rect: Rect) -> Option<ClickCheckResponse> {
self.0.mouse_pointer.buttons.get(&MouseButton::Primary).filter(|mi| {
rect.contains_point(mi.start_position)
}).map(|_| ClickCheckResponse {
position_in_rect: self.0.mouse_pointer.current_position - rect.position,
})
}
} }

View file

@ -248,6 +248,8 @@ impl UiInstance {
self.signal.add(signal); self.signal.add(signal);
} }
//TODO: offer a non-consuming version of this function for T: Clone
/// Process all signals of a given type /// Process all signals of a given type
/// ///
/// This clears the signal queue for the given type and iterates over all signals /// This clears the signal queue for the given type and iterates over all signals

View file

@ -2,18 +2,23 @@
use glam::Vec2; use glam::Vec2;
/// Represents a rectangle/AABB with specified position and size
#[derive(Clone, Copy, Debug, PartialEq, Default)] #[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct Rect { pub struct Rect {
/// Position of the top-left corner of the rect.
pub position: Vec2, pub position: Vec2,
/// Size of the rect, should not be negative.
pub size: Vec2, pub size: Vec2,
} }
impl Rect { impl Rect {
/// Check if the rect contains a point.
pub fn contains_point(&self, point: Vec2) -> bool { pub fn contains_point(&self, point: Vec2) -> bool {
point.cmpge(self.position).all() && point.cmple(self.position + self.size).all() point.cmpge(self.position).all() && point.cmple(self.position + self.size).all()
} }
//TODO: return intersect rect //TODO: return intersect rect
/// Check if the rect intersects with another rect.
pub fn intersects_rect(&self, other: Rect) -> bool { pub fn intersects_rect(&self, other: Rect) -> bool {
self.position.x < other.position.x + other.size.x self.position.x < other.position.x + other.size.x
&& self.position.x + self.size.x > other.position.x && self.position.x + self.size.x > other.position.x
@ -21,22 +26,35 @@ impl Rect {
&& self.position.y + self.size.y > other.position.y && self.position.y + self.size.y > other.position.y
} }
/// Get width of the rectangle.
///
/// To get both width and height, use the `size` property instead.
pub fn width(&self) -> f32 { pub fn width(&self) -> f32 {
self.size.x self.size.x
} }
/// Get height of the rectangle.
///
/// To get both width and height, use the `size` property instead.
pub fn height(&self) -> f32 { pub fn height(&self) -> f32 {
self.size.y self.size.y
} }
/// Get position of the top-left corner of the rectangle on the x-axis.
///
/// To get both x and y, use the `position` property instead.
pub fn x(&self) -> f32 { pub fn x(&self) -> f32 {
self.position.x self.position.x
} }
/// Get position of the top-left corner of the rectangle on the y-axis.
///
/// To get both x and y, use the `position` property instead.
pub fn y(&self) -> f32 { pub fn y(&self) -> f32 {
self.position.y self.position.y
} }
/// Get positions of all 4 corners of the rectangle.
pub fn corners(&self) -> Corners<Vec2> { pub fn corners(&self) -> Corners<Vec2> {
Corners { Corners {
top_left: self.position, top_left: self.position,

View file

@ -56,6 +56,28 @@ impl SignalStore {
//TODO this, simplifies handling signals //TODO this, simplifies handling signals
// pub trait Signal {
// type Arg;
// type Output;
// fn call(&self, arg: Self::Arg) -> Self::Output;
// }
// impl<F: Fn() -> T, T> Signal for F {
// type Arg = ();
// type Output = T;
// fn call(&self, _: Self::Arg) -> Self::Output {
// self()
// }
// }
// // impl<F: Fn(A) -> T, A, T> Signal for F {
// // type Arg = A;
// // type Output = T;
// // fn call(&self, a: Self::Arg) -> Self::Output {
// // self(a)
// // }
// // }
// pub struct SignalTrigger<R: UiSignal + 'static, A = ()>(pub(crate) Box<dyn Fn(A) -> R + 'static>); // pub struct SignalTrigger<R: UiSignal + 'static, A = ()>(pub(crate) Box<dyn Fn(A) -> R + 'static>);
// impl<R: UiSignal + 'static, A> SignalTrigger<R, A> { // impl<R: UiSignal + 'static, A> SignalTrigger<R, A> {