use super::*;
#[unsafe(no_mangle)]
pub extern "C" fn kui_spec_float_preset(spec: *mut KuiSpec, name: KuiStr) -> bool {
guard(false, || {
let (Some(s), Some(cfg)) = (unsafe { spec.as_mut() }, FloatConfig::preset(&kstr(name)))
else {
return false;
};
s.float_mode = match cfg.anchor {
kui_core::FloatAnchor::Parent => KUI_FLOAT_PARENT,
kui_core::FloatAnchor::Viewport => KUI_FLOAT_VIEWPORT,
kui_core::FloatAnchor::Node(_) => KUI_FLOAT_PARENT,
};
s.float_anchor_x = align_code(cfg.anchor_point.0);
s.float_anchor_y = align_code(cfg.anchor_point.1);
s.float_self_x = align_code(cfg.self_point.0);
s.float_self_y = align_code(cfg.self_point.1);
s.float_dx = cfg.offset.x;
s.float_dy = cfg.offset.y;
s.float_fit = cfg.fit as u32;
s.float_clip = cfg.clip as u32;
true
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_frame_begin(ptr: *mut KuiCtx, w: f32, h: f32, scale: f32) {
guard((), || {
if let Some(c) = unsafe { ctx(ptr) } {
c.draws_current = false;
c.core()
.begin_frame(Size::new(w, h), if scale > 0.0 { scale } else { 1.0 });
}
});
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_root(ptr: *mut KuiCtx, spec: *const KuiSpec) {
guard((), || {
if let (Some(c), Some(s)) = (unsafe { ctx(ptr) }, unsafe { spec.as_ref() }) {
c.core().configure_root(spec_of(s, NONE, NONE, NONE, NONE));
}
});
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_open(ptr: *mut KuiCtx, spec: *const KuiSpec, on_click: *mut KuiValue) -> u64 {
guard(0, || {
let (Some(c), Some(s)) = (unsafe { ctx(ptr) }, unsafe { spec.as_ref() }) else {
return 0;
};
let key = c.core().open(spec_of(s, on_click, NONE, NONE, NONE));
c.push_tooltip(key, s.tooltip);
key.0
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_open_keyed(
ptr: *mut KuiCtx,
label: KuiStr,
spec: *const KuiSpec,
on_click: *mut KuiValue,
) -> u64 {
guard(0, || {
let (Some(c), Some(s)) = (unsafe { ctx(ptr) }, unsafe { spec.as_ref() }) else {
return 0;
};
let key = c
.core()
.open_keyed(&kstr(label), spec_of(s, on_click, NONE, NONE, NONE));
c.push_tooltip(key, s.tooltip);
key.0
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_row_count(ptr: *mut KuiCtx, rows: u64) {
guard((), || {
if let Some(c) = unsafe { ctx(ptr) } {
c.core().row_count(rows);
}
});
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_open_indexed(
ptr: *mut KuiCtx,
index: u64,
spec: *const KuiSpec,
on_click: *mut KuiValue,
) -> u64 {
guard(0, || {
let (Some(c), Some(s)) = (unsafe { ctx(ptr) }, unsafe { spec.as_ref() }) else {
return 0;
};
let key = c
.core()
.open_indexed(index, spec_of(s, on_click, NONE, NONE, NONE));
c.push_tooltip(key, s.tooltip);
key.0
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_image(ptr: *mut KuiCtx, id: u64, spec: *const KuiSpec) {
kui_image_with(ptr, id, 0, 0, spec);
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_image_with(
ptr: *mut KuiCtx,
id: u64,
sampling: u32,
fit: u32,
spec: *const KuiSpec,
) {
guard((), || {
if let (Some(c), Some(s)) = (unsafe { ctx(ptr) }, unsafe { spec.as_ref() }) {
let spec = leaf_spec_of(s, NONE, NONE, NONE, NONE);
let opts = kui_core::ImageOpts {
sampling: kui_core::Sampling::ALL
.get(sampling as usize)
.copied()
.unwrap_or_default(),
fit: kui_core::ImageFit::ALL
.get(fit as usize)
.copied()
.unwrap_or_default(),
};
c.core()
.image_node_with(kui_core::ImageId::from_ffi(id), opts, spec);
}
});
}
fn leaf_spec(
spec: *const KuiSpec,
on_click: *mut KuiValue,
on_drag: *mut KuiValue,
on_hover: *mut KuiValue,
) -> kui_core::NodeSpec {
match unsafe { spec.as_ref() } {
Some(s) => leaf_spec_of(s, on_click, on_drag, NONE, on_hover),
None => {
let mut spec = kui_core::NodeSpec::column();
if let Some(v) = take_msg(on_click) {
spec = spec.on_click(v);
}
if let Some(v) = take_msg(on_drag) {
spec = spec.on_drag(v);
}
if let Some(v) = take_msg(on_hover) {
spec = spec.on_hover(v);
}
spec
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_polygon(
ptr: *mut KuiCtx,
label: KuiStr,
xy: *const f32,
count: usize,
spec: *const KuiSpec,
on_click: *mut KuiValue,
on_drag: *mut KuiValue,
on_hover: *mut KuiValue,
) {
guard((), || {
let spec = leaf_spec(spec, on_click, on_drag, on_hover);
let Some(c) = (unsafe { ctx(ptr) }) else {
return;
};
if xy.is_null() || count < 3 {
return;
}
let floats = unsafe { std::slice::from_raw_parts(xy, count * 2) };
let points: Vec<kui_core::Vec2> = floats
.as_chunks::<2>()
.0
.iter()
.map(|p| kui_core::Vec2::new(p[0], p[1]))
.collect();
match opt_str(label) {
Some(label) => c.core().polygon_node_keyed(&label, &points, spec),
None => c.core().polygon_node(&points, spec),
}
});
}
#[unsafe(no_mangle)]
#[allow(clippy::too_many_arguments)]
pub extern "C" fn kui_path(
ptr: *mut KuiCtx,
label: KuiStr,
ops: *const f32,
count: usize,
fill_rule: u32,
width: f32,
color: u32,
rotate: f32,
pivot: *const f32,
dash: *const f32,
spec: *const KuiSpec,
on_click: *mut KuiValue,
on_drag: *mut KuiValue,
on_hover: *mut KuiValue,
) {
guard((), || {
let spec = leaf_spec(spec, on_click, on_drag, on_hover);
let Some(c) = (unsafe { ctx(ptr) }) else {
return;
};
if ops.is_null() || count == 0 {
return;
}
let floats = unsafe { std::slice::from_raw_parts(ops, count) };
let rule = kui_core::FillRule::from_index(fill_rule as usize);
let stroke = (width > 0.0).then(|| {
let color = if color == 0 {
c.core().theme().fg
} else {
color_of(color)
};
kui_core::Stroke::new(width, color).dashed(unsafe { dash_of(dash) })
});
let turn = unsafe { turn_of(rotate, pivot) };
match opt_str(label) {
Some(label) => c
.core()
.path_flat_node_keyed(&label, floats, rule, stroke, turn, spec),
None => c.core().path_flat_node(floats, rule, stroke, turn, spec),
}
});
}
unsafe fn dash_of(dash: *const f32) -> kui_core::Dash {
if dash.is_null() {
return kui_core::Dash::SOLID;
}
let d = unsafe { std::slice::from_raw_parts(dash, 5) };
kui_core::Dash {
pattern: [d[0], d[1], d[2], d[3]],
offset: d[4],
}
}
unsafe fn turn_of(rotate: f32, pivot: *const f32) -> Option<kui_core::Turn> {
let pivot = (!pivot.is_null()).then(|| {
let p = unsafe { std::slice::from_raw_parts(pivot, 2) };
kui_core::Vec2::new(p[0], p[1])
});
(rotate != 0.0 || pivot.is_some()).then_some(kui_core::Turn {
turns: rotate,
pivot,
})
}
#[unsafe(no_mangle)]
#[allow(clippy::too_many_arguments)]
pub extern "C" fn kui_path_d(
ptr: *mut KuiCtx,
label: KuiStr,
d: KuiStr,
fill_rule: u32,
width: f32,
color: u32,
rotate: f32,
pivot: *const f32,
dash: *const f32,
spec: *const KuiSpec,
on_click: *mut KuiValue,
on_drag: *mut KuiValue,
on_hover: *mut KuiValue,
) {
guard((), || {
let spec = leaf_spec(spec, on_click, on_drag, on_hover);
let Some(c) = (unsafe { ctx(ptr) }) else {
return;
};
let d = kstr(d);
let turn = unsafe { turn_of(rotate, pivot) };
let rule = kui_core::FillRule::from_index(fill_rule as usize);
let stroke = (width > 0.0).then(|| {
let color = if color == 0 {
c.core().theme().fg
} else {
color_of(color)
};
kui_core::Stroke::new(width, color).dashed(unsafe { dash_of(dash) })
});
match opt_str(label) {
Some(label) => c
.core()
.path_d_node_keyed(&label, &d, rule, stroke, turn, spec),
None => c.core().path_d_node(&d, rule, stroke, turn, spec),
}
});
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_path_parse(d: KuiStr, out: *mut f32, cap: usize) -> usize {
guard(0, || {
let Some(d) = opt_str(d) else {
return 0;
};
let Ok(path) = kui_core::Path::parse(&d) else {
return 0;
};
let floats = path.to_floats();
if !out.is_null() && cap >= floats.len() {
let dst = unsafe { std::slice::from_raw_parts_mut(out, floats.len()) };
dst.copy_from_slice(&floats);
}
floats.len()
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_fragment(
ptr: *mut KuiCtx,
id: u64,
params: *const f32,
count: usize,
spec: *const KuiSpec,
) {
guard((), || {
let Some(c) = (unsafe { ctx(ptr) }) else {
return;
};
let p = fragment_params(params, count);
let spec = fragment_spec(spec);
c.core()
.fragment_node(kui_core::FragmentId::from_ffi(id), p, spec);
});
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_fragment_open(
ptr: *mut KuiCtx,
label: KuiStr,
id: u64,
params: *const f32,
count: usize,
spec: *const KuiSpec,
) {
guard((), || {
let Some(c) = (unsafe { ctx(ptr) }) else {
return;
};
let p = fragment_params(params, count);
let spec = fragment_spec(spec);
let id = kui_core::FragmentId::from_ffi(id);
match opt_str(label) {
Some(label) => c.core().open_fragment_keyed(&label, id, p, spec),
None => c.core().open_fragment(id, p, spec),
};
});
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_fragment_with(
ptr: *mut KuiCtx,
label: KuiStr,
id: u64,
image: u64,
params: *const f32,
count: usize,
spec: *const KuiSpec,
) {
guard((), || {
let Some(c) = (unsafe { ctx(ptr) }) else {
return;
};
let p = fragment_params(params, count);
let spec = fragment_spec(spec);
let frag = fragment_ref(id, image);
match opt_str(label) {
Some(label) => c.core().fragment_node_keyed(&label, frag, p, spec),
None => c.core().fragment_node(frag, p, spec),
};
});
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_fragment_open_with(
ptr: *mut KuiCtx,
label: KuiStr,
id: u64,
image: u64,
params: *const f32,
count: usize,
spec: *const KuiSpec,
) {
guard((), || {
let Some(c) = (unsafe { ctx(ptr) }) else {
return;
};
let p = fragment_params(params, count);
let spec = fragment_spec(spec);
let frag = fragment_ref(id, image);
match opt_str(label) {
Some(label) => c.core().open_fragment_keyed(&label, frag, p, spec),
None => c.core().open_fragment(frag, p, spec),
};
});
}
fn fragment_ref(id: u64, image: u64) -> kui_core::FragmentRef {
kui_core::FragmentRef {
id: kui_core::FragmentId::from_ffi(id),
image: (image != 0).then(|| kui_core::ImageId::from_ffi(image)),
}
}
fn fragment_params<'a>(params: *const f32, count: usize) -> &'a [f32] {
if params.is_null() || count == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(params, count) }
}
}
fn fragment_spec(spec: *const KuiSpec) -> kui_core::NodeSpec {
match unsafe { spec.as_ref() } {
Some(s) => leaf_spec_of(s, NONE, NONE, NONE, NONE),
None => kui_core::NodeSpec::column(),
}
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_line(
ptr: *mut KuiCtx,
x0: f32,
y0: f32,
x1: f32,
y1: f32,
width: f32,
color: u32,
spec: *const KuiSpec,
) {
let xy = [x0, y0, x1, y1];
let none = KuiStr {
ptr: std::ptr::null(),
len: 0,
};
kui_polyline(
ptr,
none,
xy.as_ptr(),
2,
width,
color,
false,
std::ptr::null(),
spec,
NONE,
NONE,
NONE,
);
}
#[unsafe(no_mangle)]
#[allow(clippy::too_many_arguments)]
pub extern "C" fn kui_polyline(
ptr: *mut KuiCtx,
label: KuiStr,
xy: *const f32,
count: usize,
width: f32,
color: u32,
curve: bool,
dash: *const f32,
spec: *const KuiSpec,
on_click: *mut KuiValue,
on_drag: *mut KuiValue,
on_hover: *mut KuiValue,
) {
guard((), || {
let spec = leaf_spec(spec, on_click, on_drag, on_hover);
let Some(c) = (unsafe { ctx(ptr) }) else {
return;
};
if xy.is_null() || count < 2 {
return;
}
let floats = unsafe { std::slice::from_raw_parts(xy, count * 2) };
let points: Vec<kui_core::Vec2> = floats
.as_chunks::<2>()
.0
.iter()
.map(|p| kui_core::Vec2::new(p[0], p[1]))
.collect();
let color = if color == 0 {
c.core().theme().fg
} else {
color_of(color)
};
let mut stroke = kui_core::Stroke::new(if width > 0.0 { width } else { 1.0 }, color);
stroke.curve = curve;
stroke.dash = unsafe { dash_of(dash) };
match opt_str(label) {
Some(label) => c.core().line_node_keyed(&label, &points, stroke, spec),
None => c.core().line_node(&points, stroke, spec),
}
});
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_open_draggable(
ptr: *mut KuiCtx,
label: KuiStr,
spec: *const KuiSpec,
on_click: *mut KuiValue,
on_drag: *mut KuiValue,
) -> u64 {
guard(0, || {
let (Some(c), Some(s)) = (unsafe { ctx(ptr) }, unsafe { spec.as_ref() }) else {
return 0;
};
let spec = spec_of(s, on_click, NONE, NONE, NONE)
.on_drag(take_msg(on_drag).unwrap_or(Value::Null));
let key = c.core().open_keyed(&kstr(label), spec);
c.push_tooltip(key, s.tooltip);
key.0
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_open_with(
ptr: *mut KuiCtx,
label: KuiStr,
spec: *const KuiSpec,
on_click: *mut KuiValue,
on_drag: *mut KuiValue,
on_key: *mut KuiValue,
on_hover: *mut KuiValue,
) -> u64 {
guard(0, || {
let (Some(c), Some(s)) = (unsafe { ctx(ptr) }, unsafe { spec.as_ref() }) else {
for p in [on_click, on_drag, on_key, on_hover] {
drop(take_msg(p));
}
return 0;
};
let key = c.core().open_keyed(
&kstr(label),
spec_of(s, on_click, on_drag, on_key, on_hover),
);
c.push_tooltip(key, s.tooltip);
key.0
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_close(ptr: *mut KuiCtx) {
guard((), || {
if let Some(c) = unsafe { ctx(ptr) } {
c.core().close();
}
});
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_text(ptr: *mut KuiCtx, text: KuiStr, style: *const KuiTextStyle) {
guard((), || {
if let Some(c) = unsafe { ctx(ptr) } {
let style = unsafe { style.as_ref() }
.map(text_style_of)
.unwrap_or_default();
c.core().text_node(&kstr(text), style);
}
});
}
fn with_spans<R>(
spans: *const KuiSpan,
span_count: usize,
f: impl FnOnce(&[Span<'_>]) -> R,
) -> Option<R> {
if spans.is_null() || span_count == 0 {
return None;
}
let raw = unsafe { std::slice::from_raw_parts(spans, span_count) };
let texts: Vec<std::borrow::Cow<'_, str>> = raw.iter().map(|s| kstr(s.text)).collect();
let spans: Vec<Span<'_>> = raw
.iter()
.zip(texts.iter())
.map(|(s, t)| {
let mut span = Span::new(t.as_ref());
if s.flags & KUI_SPAN_BOLD != 0 {
span = span.bold();
}
if s.flags & KUI_SPAN_ITALIC != 0 {
span = span.italic();
}
if s.flags & KUI_SPAN_UNDERLINE != 0 {
span = span.underline();
}
if s.flags & KUI_SPAN_STRIKETHROUGH != 0 {
span = span.strikethrough();
}
if s.underline_color != 0 {
span = span.underline_color(Color::hex(s.underline_color));
}
if s.underline_style != 0 {
span =
span.underline_style(kui_core::UnderlineStyle::from_index(s.underline_style));
}
if s.color != 0 {
span = span.color(Color::hex(s.color));
}
if s.bg != 0 {
span = span.bg(Color::hex(s.bg));
}
if s.bg_radius > 0.0 {
span = span.bg_radius(s.bg_radius);
}
if s.font != 0 {
span = span.family(kui_core::FontFamily::Custom(kui_core::FontId::from_ffi(
s.font,
)));
} else if s.flags & KUI_SPAN_FAMILY != 0 {
span = span.family(match s.family {
1 => kui_core::FontFamily::Serif,
2 => kui_core::FontFamily::Mono,
_ => kui_core::FontFamily::Sans,
});
}
if s.size > 0.0 {
span = span.size(s.size);
}
span
})
.collect();
Some(f(&spans))
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_rich_text(
ptr: *mut KuiCtx,
spans: *const KuiSpan,
span_count: usize,
base: *const KuiTextStyle,
) {
guard((), || {
let Some(c) = (unsafe { ctx(ptr) }) else {
return;
};
let base = unsafe { base.as_ref() }
.map(text_style_of)
.unwrap_or_default();
with_spans(spans, span_count, |spans| {
c.core().rich_text_node(spans, base)
});
});
}
fn metrics_of(m: kui_core::TextMetrics) -> KuiTextMetrics {
KuiTextMetrics {
width: m.width,
height: m.height,
lines: m.lines,
..Default::default()
}
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_measure_text(
ptr: *mut KuiCtx,
text: KuiStr,
style: *const KuiTextStyle,
max_w: f32,
out: *mut KuiTextMetrics,
) -> bool {
guard(false, || {
let Some(c) = (unsafe { ctx(ptr) }) else {
return false;
};
let style = unsafe { style.as_ref() }
.map(text_style_of)
.unwrap_or_default();
let max_w = (max_w > 0.0).then_some(max_w);
write_out(
out,
metrics_of(c.core().measure_text(&kstr(text), &style, max_w)),
)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_text_hit(
ptr: *mut KuiCtx,
key: u64,
x: f32,
y: f32,
out: *mut KuiTextHit,
) -> bool {
guard(false, || {
let Some(c) = (unsafe { ctx(ptr) }) else {
return false;
};
let Some(h) = c.core().text_hit(Key(key), Vec2::new(x, y)) else {
return false;
};
write_out(
out,
KuiTextHit {
line: h.line,
byte: h.byte as u64,
..Default::default()
},
)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_caret_rect(
ptr: *mut KuiCtx,
key: u64,
byte: usize,
out: *mut KuiCaretRect,
) -> bool {
guard(false, || {
let Some(c) = (unsafe { ctx(ptr) }) else {
return false;
};
let Some(r) = c.core().caret_rect(Key(key), byte) else {
return false;
};
write_out(
out,
KuiCaretRect {
x: r.x,
y: r.y,
w: r.w,
h: r.h,
..Default::default()
},
)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_ime_rect(ptr: *mut KuiCtx, out: *mut KuiCaretRect) -> bool {
guard(false, || {
let Some(c) = (unsafe { ctx(ptr) }) else {
return false;
};
let Some(r) = c.core().ime_rect() else {
return false;
};
write_out(
out,
KuiCaretRect {
x: r.x,
y: r.y,
w: r.w,
h: r.h,
..Default::default()
},
)
})
}
#[unsafe(no_mangle)]
#[allow(clippy::too_many_arguments)]
pub extern "C" fn kui_cells(
ptr: *mut KuiCtx,
label: KuiStr,
rows: u32,
cols: u32,
cells: *const KuiCell,
count: usize,
style: *const KuiTextStyle,
spec: *const KuiSpec,
on_click: *mut KuiValue,
on_drag: *mut KuiValue,
on_key: *mut KuiValue,
cursor_row: u32,
cursor_col: u32,
cursor_shape: u32,
cursor_color: u32,
origin_line: u64,
) {
guard((), || {
let Some(c) = (unsafe { ctx(ptr) }) else {
for p in [on_click, on_drag, on_key] {
drop(take_msg(p));
}
return;
};
if rows == 0 || cols == 0 {
for p in [on_click, on_drag, on_key] {
drop(take_msg(p));
}
return;
}
let raw: &[KuiCell] = if cells.is_null() || count == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(cells, count) }
};
let cells: Vec<kui_core::Cell> = raw
.iter()
.map(|k| kui_core::Cell {
ch: char::from_u32(k.ch).unwrap_or(' '),
fg: k.fg,
bg: k.bg,
flags: k.flags as u8,
ul: k.ul,
})
.collect();
let style = unsafe { style.as_ref() }
.map(text_style_of)
.unwrap_or_default();
let spec = match unsafe { spec.as_ref() } {
Some(s) => leaf_spec_of(s, on_click, on_drag, on_key, NONE),
None => {
for p in [on_click, on_drag, on_key] {
drop(take_msg(p));
}
kui_core::NodeSpec::default()
}
};
let cursor =
kui_core::CellCursor::from_index(cursor_shape.wrapping_sub(1) as usize).map(|shape| {
(
cursor_row as usize,
cursor_col as usize,
shape,
color_of(cursor_color),
)
});
let grid = kui_core::CellGrid {
rows: rows as usize,
cols: cols as usize,
cells: &cells,
style,
cursor,
origin_line,
};
let label = kstr(label);
if label.is_empty() {
c.core().cells(&grid, spec);
} else {
c.core().cells_keyed(&label, &grid, spec);
}
});
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_measure_rich_text(
ptr: *mut KuiCtx,
spans: *const KuiSpan,
span_count: usize,
base: *const KuiTextStyle,
max_w: f32,
out: *mut KuiTextMetrics,
) -> bool {
guard(false, || {
let Some(c) = (unsafe { ctx(ptr) }) else {
return false;
};
let base = unsafe { base.as_ref() }
.map(text_style_of)
.unwrap_or_default();
let max_w = (max_w > 0.0).then_some(max_w);
let m = with_spans(spans, span_count, |spans| {
c.core().measure_rich_text(spans, &base, max_w)
});
write_out(out, m.map(metrics_of).unwrap_or_default())
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_child_key(ptr: *mut KuiCtx, label: KuiStr) -> u64 {
guard(0, || {
let Some(c) = (unsafe { ctx(ptr) }) else {
return 0;
};
c.core().child_key(&kstr(label)).0
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_is_hovered(ptr: *mut KuiCtx, key: u64) -> bool {
guard(false, || {
unsafe { ctx(ptr) }.is_some_and(|c| c.core().is_hovered(Key(key)))
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_is_drop_target(ptr: *mut KuiCtx, key: u64) -> bool {
guard(false, || {
unsafe { ctx(ptr) }.is_some_and(|c| c.core().is_drop_target(Key(key)))
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_drop_target(ptr: *mut KuiCtx) -> u64 {
guard(0, || {
unsafe { ctx(ptr) }
.and_then(|c| c.core().drop_target())
.map_or(0, |k| k.0)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_is_pressed(ptr: *mut KuiCtx, key: u64) -> bool {
guard(false, || {
unsafe { ctx(ptr) }.is_some_and(|c| c.core().is_pressed(Key(key)))
})
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_frame_finish(ptr: *mut KuiCtx) {
guard((), || {
if let Some(c) = unsafe { ctx(ptr) } {
let core: &mut Core = unsafe { &mut *c.core };
kui_core::Ui::with_filler(core, &mut c.extensions).finish();
c.draws_current = false;
let pending = c.core().take_pending_events();
c.absorb(pending);
}
});
}
#[unsafe(no_mangle)]
pub extern "C" fn kui_draw_data(ptr: *mut KuiCtx, out: *mut KuiDrawData) -> bool {
guard(false, || {
let Some(c) = (unsafe { ctx(ptr) }) else {
return false;
};
if !out_accepts(out) {
return false;
}
if !c.draws_current {
let fragments: Vec<KuiFragmentDraw> = {
let (dl, _) = c.core().output();
dl.fragments
.iter()
.map(|f| {
let (image_source, image_texture) = match f.image {
kui_core::FragmentImage::None => (KUI_FRAGMENT_IMAGE_NONE, 0),
kui_core::FragmentImage::Atlas(_) => (KUI_FRAGMENT_IMAGE_ATLAS, 0),
kui_core::FragmentImage::Texture { index, .. } => {
(KUI_FRAGMENT_IMAGE_TEXTURE, index)
}
};
KuiFragmentDraw {
fragment: f.id.to_ffi(),
params: f.params,
image_source,
image_texture,
image_uv: f.image.uv(),
}
})
.collect()
};
let textures: Vec<KuiTextureDraw> = {
let (dl, _) = c.core().output();
dl.textures
.iter()
.zip(&dl.texture_pixels)
.map(|(t, px)| KuiTextureDraw {
image: t.id.to_ffi(),
rev: px.rev,
width: px.width,
height: px.height,
uv: t.uv,
})
.collect()
};
c.fragment_draws = fragments;
c.texture_draws = textures;
c.draws_current = true;
}
let (fragment_draws, fragment_count) = match c.fragment_draws.len() {
0 => (std::ptr::null(), 0),
n => (c.fragment_draws.as_ptr(), n),
};
let (texture_draws, texture_count) = match c.texture_draws.len() {
0 => (std::ptr::null(), 0),
n => (c.texture_draws.as_ptr(), n),
};
let (dl, atlas) = c.core().output();
let data = KuiDrawData {
quads: dl.quads.as_ptr().cast(),
quad_count: dl.quads.len(),
clips: dl.clips.as_ptr().cast(),
clip_count: dl.clips.len(),
viewport_w: dl.viewport.w,
viewport_h: dl.viewport.h,
scale: dl.scale,
atlas_pixels: atlas.pixels.as_ptr(),
atlas_size: atlas.size,
atlas_dirty: atlas.dirty,
atlas_epoch: atlas.epoch,
fragments: fragment_draws,
fragment_count,
time: dl.time,
textures: texture_draws,
texture_count,
..Default::default()
};
atlas.dirty = false;
write_out(out, data)
})
}
const _: () = {
assert!(std::mem::size_of::<KuiQuad>() == std::mem::size_of::<kui_core::Quad>());
assert!(std::mem::size_of::<KuiClip>() == std::mem::size_of::<kui_core::Clip>());
};