use std::ffi::c_char;
use denise_ui::widgets::{Button, Label, Panel, TextInput};
use denise_ui::{NodeId, Widget};
use crate::types::{DeniseRect, role};
use crate::{
DENISE_ERR_INVALID, DENISE_ERR_NO_NODE, DENISE_ERR_NULL, DENISE_ERR_PANIC,
DENISE_ERR_WRONG_WIDGET, DENISE_OK, DeniseUi, guard, handle, utf8,
};
pub const DENISE_NODE_NONE: u64 = 0;
unsafe fn add(ui: *mut DeniseUi, parent: u64, layout: DeniseRect, widget: impl Widget<u32>) -> u64 {
let Some(handle) = (unsafe { handle(ui) }) else {
return DENISE_NODE_NONE;
};
let parent = NodeId::from_ffi(parent);
handle
.ui
.add(parent, widget, layout.into())
.map_or(DENISE_NODE_NONE, NodeId::as_ffi)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_root(ui: *mut DeniseUi) -> u64 {
guard(DENISE_NODE_NONE, || {
match unsafe { handle(ui) } {
Some(handle) => handle.ui.root().as_ffi(),
None => DENISE_NODE_NONE,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_top_root(ui: *mut DeniseUi) -> u64 {
guard(DENISE_NODE_NONE, || {
match unsafe { handle(ui) } {
Some(handle) => handle.ui.top_root().as_ffi(),
None => DENISE_NODE_NONE,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_add_panel(
ui: *mut DeniseUi,
parent: u64,
layout: DeniseRect,
fill: i32,
border: i32,
border_width: i32,
) -> u64 {
guard(DENISE_NODE_NONE, || {
let panel = Panel {
fill: role(fill),
border: role(border),
border_width,
..Panel::default()
};
unsafe { add(ui, parent, layout, panel) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_add_label(
ui: *mut DeniseUi,
parent: u64,
layout: DeniseRect,
text: *const c_char,
role_value: i32,
) -> u64 {
guard(DENISE_NODE_NONE, || {
let Some(text) = (unsafe { utf8(text) }) else {
return DENISE_NODE_NONE;
};
let Some(role) = role(role_value) else {
return DENISE_NODE_NONE;
};
unsafe { add(ui, parent, layout, Label::new(text).with_role(role)) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_add_button(
ui: *mut DeniseUi,
parent: u64,
layout: DeniseRect,
label: *const c_char,
message: u32,
role_value: i32,
) -> u64 {
guard(DENISE_NODE_NONE, || {
let Some(label) = (unsafe { utf8(label) }) else {
return DENISE_NODE_NONE;
};
let Some(role) = role(role_value) else {
return DENISE_NODE_NONE;
};
let button = if message == 0 {
Button::inert(label)
} else {
Button::new(label, message)
};
unsafe { add(ui, parent, layout, button.with_role(role)) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_add_text_input(
ui: *mut DeniseUi,
parent: u64,
layout: DeniseRect,
placeholder: *const c_char,
submit: u32,
max_chars: u32,
password: bool,
) -> u64 {
guard(DENISE_NODE_NONE, || {
let mut field = TextInput::<u32>::new().with_password(password);
if !placeholder.is_null() {
let Some(text) = (unsafe { utf8(placeholder) }) else {
return DENISE_NODE_NONE;
};
field = field.with_placeholder(text);
}
if submit != 0 {
field = field.with_submit(submit);
}
if max_chars != 0 {
field = field.with_max_chars(max_chars as usize);
}
unsafe { add(ui, parent, layout, field) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_push_scene(ui: *mut DeniseUi, dim: u8) -> u64 {
guard(DENISE_NODE_NONE, || {
match unsafe { handle(ui) } {
Some(handle) => handle.ui.push_scene(dim).as_ffi(),
None => DENISE_NODE_NONE,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_pop_scene(ui: *mut DeniseUi) -> bool {
guard(false, || {
match unsafe { handle(ui) } {
Some(handle) => handle.ui.pop_scene(),
None => false,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_scene_count(ui: *mut DeniseUi) -> u32 {
guard(0, || {
match unsafe { handle(ui) } {
Some(handle) => handle.ui.scene_count() as u32,
None => 0,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_remove(ui: *mut DeniseUi, node: u64) -> i32 {
guard(DENISE_ERR_PANIC, || {
let Some(handle) = (unsafe { handle(ui) }) else {
return DENISE_ERR_NULL;
};
if handle.ui.remove(NodeId::from_ffi(node)) {
DENISE_OK
} else {
DENISE_ERR_NO_NODE
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_set_layout(
ui: *mut DeniseUi,
node: u64,
layout: DeniseRect,
) -> i32 {
unsafe {
with_node(ui, node, |handle, id| {
handle.ui.set_layout(id, layout.into());
DENISE_OK
})
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_bounds(
ui: *mut DeniseUi,
node: u64,
out: *mut DeniseRect,
) -> i32 {
if out.is_null() {
return DENISE_ERR_NULL;
}
unsafe {
with_node(ui, node, |handle, id| match handle.ui.bounds(id) {
Some(bounds) => {
out.write(bounds.into());
DENISE_OK
}
None => DENISE_ERR_NO_NODE,
})
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_set_visible(ui: *mut DeniseUi, node: u64, visible: bool) -> i32 {
unsafe {
with_node(ui, node, |handle, id| {
handle.ui.set_visible(id, visible);
DENISE_OK
})
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_set_enabled(ui: *mut DeniseUi, node: u64, enabled: bool) -> i32 {
unsafe {
with_node(ui, node, |handle, id| {
handle.ui.set_enabled(id, enabled);
DENISE_OK
})
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_set_z(ui: *mut DeniseUi, node: u64, z: i32) -> i32 {
unsafe {
with_node(ui, node, |handle, id| {
handle.ui.set_z(id, z);
DENISE_OK
})
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_focus(ui: *mut DeniseUi, node: u64) -> i32 {
guard(DENISE_ERR_PANIC, || {
let Some(handle) = (unsafe { handle(ui) }) else {
return DENISE_ERR_NULL;
};
if node == DENISE_NODE_NONE {
handle.ui.focus(None);
return DENISE_OK;
}
let id = NodeId::from_ffi(node);
if !handle.ui.contains(id) {
return DENISE_ERR_NO_NODE;
}
handle.ui.focus(Some(id));
DENISE_OK
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_set_text(
ui: *mut DeniseUi,
node: u64,
text: *const c_char,
) -> i32 {
let Some(text) = (unsafe { utf8(text) }) else {
return if text.is_null() {
DENISE_ERR_NULL
} else {
DENISE_ERR_INVALID
};
};
unsafe {
with_node(ui, node, |handle, id| {
if let Some(label) = handle.ui.widget_mut::<Label>(id) {
label.set_text(text);
DENISE_OK
} else if let Some(button) = handle.ui.widget_mut::<Button<u32>>(id) {
button.set_label(text);
DENISE_OK
} else if let Some(field) = handle.ui.widget_mut::<TextInput<u32>>(id) {
field.set_text(text);
DENISE_OK
} else {
DENISE_ERR_WRONG_WIDGET
}
})
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn denise_ui_get_text(
ui: *mut DeniseUi,
node: u64,
out: *mut c_char,
cap: usize,
) -> isize {
guard(DENISE_ERR_PANIC as isize, || {
let Some(handle) = (unsafe { handle(ui) }) else {
return DENISE_ERR_NULL as isize;
};
let id = NodeId::from_ffi(node);
if !handle.ui.contains(id) {
return DENISE_ERR_NO_NODE as isize;
}
let text = if let Some(label) = handle.ui.widget::<Label>(id) {
label.text()
} else if let Some(button) = handle.ui.widget::<Button<u32>>(id) {
button.label()
} else if let Some(field) = handle.ui.widget::<TextInput<u32>>(id) {
field.text()
} else {
return DENISE_ERR_WRONG_WIDGET as isize;
};
let needed = text.len();
if out.is_null() {
return needed as isize;
}
if cap < needed + 1 {
return DENISE_ERR_BUFFER_TOO_SMALL_ISIZE;
}
unsafe {
std::ptr::copy_nonoverlapping(text.as_ptr().cast::<c_char>(), out, needed);
out.add(needed).write(0);
}
needed as isize
})
}
const DENISE_ERR_BUFFER_TOO_SMALL_ISIZE: isize = crate::DENISE_ERR_BUFFER_TOO_SMALL as isize;
unsafe fn with_node(
ui: *mut DeniseUi,
node: u64,
body: impl FnOnce(&mut DeniseUi, NodeId) -> i32,
) -> i32 {
guard(DENISE_ERR_PANIC, || {
let Some(handle) = (unsafe { handle(ui) }) else {
return DENISE_ERR_NULL;
};
let id = NodeId::from_ffi(node);
if !handle.ui.contains(id) {
return DENISE_ERR_NO_NODE;
}
body(handle, id)
})
}