use core::ffi::c_void;
use core::marker::PhantomData;
use std::sync::Mutex;
use crate::abi::{Str, UiFrame, UiWidget};
use crate::locks::Relock;
use crate::{Host, Load, Off};
use super::{Label, client_only};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Anchor {
TopLeft,
TopCenter,
#[default]
TopRight,
BottomLeft,
BottomCenter,
BottomRight,
}
impl Anchor {
fn to_raw(self) -> u32 {
match self {
Self::TopLeft => UiWidget::TOP_LEFT,
Self::TopCenter => UiWidget::TOP_CENTER,
Self::TopRight => UiWidget::TOP_RIGHT,
Self::BottomLeft => UiWidget::BOTTOM_LEFT,
Self::BottomCenter => UiWidget::BOTTOM_CENTER,
Self::BottomRight => UiWidget::BOTTOM_RIGHT,
}
}
}
#[derive(Debug)]
pub struct Hud<'f> {
host: Host,
token: u64,
_life: PhantomData<&'f mut ()>,
_thread: PhantomData<*const ()>,
}
impl Hud<'_> {
fn draw(&mut self, mut widget: UiWidget) {
let _drawn = (self.host.api().ui_widget)(self.host.handle(), self.token, &raw mut widget);
}
pub fn text<'t>(&mut self, anchor: Anchor, label: impl Into<Label<'t>>) {
let label = label.into();
self.draw(UiWidget {
kind: UiWidget::LABEL,
flags: label.flags,
style: label.style.to_raw(),
text: Str::new(label.text),
anchor: anchor.to_raw(),
..UiWidget::EMPTY
});
}
pub fn progress(&mut self, anchor: Anchor, fraction: f32) {
self.draw(UiWidget {
kind: UiWidget::PROGRESS,
value: if fraction.is_finite() {
fraction.clamp(0.0, 1.0)
} else {
0.0
},
min: 0.0,
max: 1.0,
anchor: anchor.to_raw(),
..UiWidget::EMPTY
});
}
}
struct Drawer<F> {
draw: Mutex<F>,
}
unsafe extern "system" fn draw_hud<F>(frame: *const UiFrame, user: *mut c_void)
where
F: FnMut(&mut Hud<'_>),
{
let Some(raw) = (unsafe { frame.as_ref() }) else {
return;
};
let Some(host) = crate::host() else {
return;
};
let drawer = unsafe { &*user.cast::<Drawer<F>>() };
crate::host::guard(
"panic in a HUD callback",
|| (),
|| {
let mut draw = drawer.draw.relock();
(draw)(&mut Hud {
host,
token: raw.token,
_life: PhantomData,
_thread: PhantomData,
});
},
);
}
impl Load<'_> {
pub fn on_hud<F>(&mut self, draw: F) -> Result<(), Off>
where
F: FnMut(&mut Hud<'_>) + Send + 'static,
{
let host = self.host();
client_only(host, "HUD callbacks")?;
crate::load::hand_over(
Drawer {
draw: Mutex::new(draw),
},
|user| (host.api().ui_hud)(host.handle(), draw_hud::<F>, user),
"HUD callback",
)
}
}