wip slider and stuff

This commit is contained in:
griffi-gh 2024-03-14 13:30:44 +01:00
parent 59a704d516
commit 6606119cc4
5 changed files with 156 additions and 16 deletions

View file

@ -0,0 +1,66 @@
use hui::{
color, size,
draw::TextureFormat,
signal::UiSignal,
layout::{Alignment, Direction},
element::{
container::Container,
text::Text,
image::Image,
br::Br,
interactable::ElementInteractableExt,
slider::Slider,
UiElementExt,
},
};
enum CounterSignal {
Increment,
Decrement,
}
impl UiSignal for CounterSignal {}
#[path = "../boilerplate.rs"]
#[macro_use]
mod boilerplate;
const IMAGE_DATA: &[u8] = include_bytes!("../assets/icons/visual-studio-code-icon_32x32.rgba");
ui_main!(
"hUI: Internal input test",
init: |ui| {
let image = ui.add_image(TextureFormat::Rgba, IMAGE_DATA, 32);
(0, image)
},
run: |ui, size, (ref mut counter, image)| {
Container::default()
.with_size(size!(100%))
.with_padding(10.)
.with_align((Alignment::Center, Alignment::Begin))
.with_direction(Direction::Horizontal)
.with_gap(5.)
.with_background((0.1, 0.1, 0.1))
.with_wrap(true)
.with_children(|ui| {
Text::new("Number of images:")
.with_text_size(24)
.add_child(ui);
Br.add_child(ui);
Slider::new(0.5)
.with_size(size!(66%, 20))
.add_child(ui);
Br.add_child(ui);
for _ in 0..*counter {
Image::new(*image)
.with_size(size!(48, 48))
.add_child(ui);
}
})
.add_root(ui, size);
ui.process_signals(|sig| match sig {
CounterSignal::Increment => *counter += 1,
CounterSignal::Decrement => *counter -= 1,
});
}
);

View file

@ -26,8 +26,6 @@ pub struct Interactable<C: UiSignal + 'static> {
pub event: InteractableEvent,
/// Signal that will be called if the element was clicked in the current frame
///
/// Will be consumed after the first time it's called
pub signal: RefCell<Option<C>>,
}
@ -55,7 +53,8 @@ impl<C: UiSignal + 'static> UiElement for Interactable<C> {
//XXX: should we do this AFTER normal process call of wrapped element?
let event_happened = match self.event {
InteractableEvent::Click => ctx.input.check_click(rect),
//TODO: actually pass the response
InteractableEvent::Click => ctx.input.check_click(rect).is_some(),
InteractableEvent::Hover => ctx.input.check_hover(rect),
};

View file

@ -1,7 +1,71 @@
use crate::element::UiElement;
//! work in progress
use derive_setters::Setters;
use crate::{
draw::UiDrawCommand,
element::{MeasureContext, ProcessContext, UiElement},
layout::{compute_size, Size2d},
measure::Response,
rectangle::Corners,
signal::UiSignal,
};
/// work in progress
#[derive(Default, Debug, Clone, Copy, Setters)]
#[setters(prefix = "with_")]
pub struct Slider {
pub value: f32,
pub size: Size2d,
}
impl Slider {
pub const DEFAULT_HEIGHT: f32 = 20.0;
pub fn new(value: f32) -> Self {
Self {
value,
..Default::default()
}
}
}
impl UiElement for Slider {
fn name(&self) -> &'static str {
"Slider"
}
fn measure(&self, ctx: MeasureContext) -> Response {
Response {
size: compute_size(ctx.layout, self.size, (ctx.layout.max_size.x, Self::DEFAULT_HEIGHT).into()),
..Default::default()
}
}
fn process(&self, ctx: ProcessContext) {
ctx.draw.add(UiDrawCommand::Rectangle {
position: ctx.layout.position,
size: ctx.measure.size,
color: Corners::all((1., 0., 0., 1.).into()),
texture: None,
rounded_corners: None,
});
let value = self.value.clamp(0., 1.);
ctx.draw.add(UiDrawCommand::Rectangle {
position: ctx.layout.position,
size: (ctx.measure.size.x * value, ctx.measure.size.y).into(),
color: Corners::all((0., 1., 0., 1.).into()),
texture: None,
rounded_corners: None,
});
//handle click etc
if let Some(res) = ctx.input.check_click(ctx.measure.rect(ctx.layout.position)) {
let new_value = res.position_in_rect.x / ctx.measure.size.x;
//TODO call signal with new value
}
}
}
//TODO

View file

@ -234,6 +234,11 @@ impl UiInputState {
}
}
#[derive(Clone, Copy, Debug)]
pub struct ClickCheckResponse {
pub position_in_rect: Vec2,
}
#[derive(Clone, Copy)]
pub struct InputCtx<'a>(&'a UiInputState);
@ -300,10 +305,12 @@ impl<'a> InputCtx<'a> {
/// By default, this function only checks for the primary mouse button\
/// This is a limitation of the current API and may change in the future\
/// (as the current implementation of this function checks for both mouse and touch input, and the touch input quite obviously only supports one "button")
pub fn check_click(&self, rect: Rect) -> bool {
pub fn check_click(&self, rect: Rect) -> Option<ClickCheckResponse> {
let pos = self.0.mouse_pointer.current_position;
self.0.mouse_pointer.released_buttons.get(&MouseButton::Primary).map_or(false, |meta| {
rect.contains_point(meta.start_position) && rect.contains_point(pos)
}).then_some(ClickCheckResponse {
position_in_rect: pos - rect.position,
})
}
}

View file

@ -56,16 +56,20 @@ impl SignalStore {
//TODO this, simplifies handling signals
pub struct SignalTrigger<R: UiSignal + 'static, A = ()>(pub(crate) Box<dyn Fn(A) -> R>);
// pub struct SignalTrigger<R: UiSignal + 'static, A = ()>(pub(crate) Box<dyn Fn(A) -> R + 'static>);
impl<R: UiSignal + 'static, A> SignalTrigger<R, A> {
pub fn new<F: Fn(A) -> R + 'static>(f: F) -> Self {
Self(Box::new(f))
}
}
// impl<R: UiSignal + 'static, A> SignalTrigger<R, A> {
// pub fn new<F: Fn(A) -> R + 'static>(f: F) -> Self {
// Self(Box::new(f))
// }
impl<R: UiSignal + 'static, A, T: Fn(A) -> R + 'static> From<T> for SignalTrigger<R, A> {
fn from(f: T) -> Self {
Self(Box::new(f))
}
}
// pub fn call(&self, a: A) -> R {
// (self.0)(a)
// }
// }
// impl<R: UiSignal + 'static, A, T: Fn(A) -> R + 'static> From<T> for SignalTrigger<R, A> {
// fn from(f: T) -> Self {
// Self(Box::new(f))
// }
// }