#![allow(non_upper_case_globals)]
#[macro_use]
mod prelude {
pub use std::{ops::DerefMut, os::raw::{c_int, c_uint, c_void}};
pub use boing_internals::*;
pub use libui_ng_sys::*;
pub use crate::{Container, Control, NonNegativeInt, Ui, Widget};
pub trait Sealed {}
pub fn bool_from_libui(value: c_int) -> bool {
debug_assert!((value == 0) || (value == 1));
value != 0
}
macro_rules! call_fallible_libui_fn {
($fn:ident( $($arg:expr),* $(,)? )) => {
$fn( $($arg),* )
.as_mut()
.ok_or($crate::Error::LibuiFn { name: stringify!($fn), cause: None })
};
}
macro_rules! call_libui_new_fn {
(
ui: $ui:expr,
fn: $fn:ident( $($arg:expr),* $(,)? ) -> $out_ty:ident $(,)?
) => {
call_fallible_libui_fn!( $fn($($arg),*) ).map(|ptr| $ui.alloc_object({
// SAFETY: `ptr` is ostensibly a valid control returned by *libui-ng*, and it will
// live until either we destroy it or *libui-ng* is de-initialized.
// Note: this does not necessarily invoke [`Control::new`], in particular if this is
// a non-control widget.
$out_ty::from_ptr($ui, ptr)
}))
};
}
}
pub mod reexports {
macro_rules! reexport {
($krate:ident) => {
pub mod $krate {
#,
").",
)]
pub use ::$krate::*;
}
};
}
#[cfg(feature = "image")]
reexport!(image);
reexport!(libui_ng_sys);
#[cfg(feature = "raw-window-handle")]
reexport!(raw_window_handle);
}
pub mod area;
mod axis;
mod checkbox;
pub mod color;
mod combobox;
mod control;
pub mod font;
pub mod form;
mod grid;
mod group;
pub mod image;
mod label;
pub mod menu;
mod multiline_text_entry;
mod path;
mod progress_bar;
mod pushbutton;
mod radio_buttons;
mod separator;
mod slider;
mod spinbox;
mod tab;
mod text_entry;
mod ui;
mod window;
use std::fmt;
pub use area::Area;
pub use axis::Axis;
pub use checkbox::Checkbox;
pub use color::{Color, Picker as ColorPicker};
pub use combobox::Combobox;
pub use control::Control;
pub use font::{Font, Picker as FontPicker};
pub use form::Form;
pub use grid::Grid;
pub use group::Group;
pub use crate::image::Image;
pub use label::Label;
pub use menu::{Item as MenuItem, Menu};
pub use multiline_text_entry::MultilineTextEntry;
pub use non_negative_int::NonNegativeInt;
pub use path::Path;
pub use progress_bar::ProgressBar;
pub use pushbutton::Pushbutton;
pub use radio_buttons::RadioButtons;
pub use separator::Separator;
pub use slider::Slider;
pub use spinbox::Spinbox;
pub use tab::Tab;
pub use text_entry::TextEntry;
pub use ui::Ui;
pub use window::Window;
#[derive(Debug)]
pub enum Error {
AlreadyInitedLibui,
ConvertCString(std::str::Utf8Error),
ConvertRustString(std::ffi::NulError),
LibuiFn {
name: &'static str,
cause: Option<String>,
},
}
impl std::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::AlreadyInitedLibui => {
f.write_str("libui-ng is already initialized")
}
Self::ConvertCString(e) => {
write!(f, "failed to convert C string to Rust string: {e}")
}
Self::ConvertRustString(e) => {
write!(f, "failed to convert Rust string to C string: {e}")
}
Self::LibuiFn { name, cause } => {
write!(f, "libui-ng function {name}() failed")?;
if let Some(cause) = cause {
write!(f, ": {cause}")?;
}
Ok(())
}
}
}
}
pub trait Widget: prelude::Sealed {
type Handle;
#[must_use]
fn as_ptr(&self) -> *mut Self::Handle;
}
pub trait Container: prelude::Sealed {
#[must_use]
fn is_empty(&self) -> bool {
self.child_count().to_libui() == 0
}
#[must_use]
fn child_count(&self) -> NonNegativeInt;
fn contains_child(&self, index: impl Into<NonNegativeInt>) -> bool {
index.into() < self.child_count()
}
fn pop_child(&self) {
let child_count = self.child_count().to_libui();
let index = child_count - 1;
if index >= 0 {
self.remove_child(unsafe { NonNegativeInt::from_libui(index) });
}
}
fn remove_child(&self, index: impl Into<NonNegativeInt>);
fn clear(&self) {
for i in (0..self.child_count().to_libui()).into_iter().rev() {
self.remove_child(unsafe { NonNegativeInt::from_libui(i) });
}
}
}
mod non_negative_int {
use std::os::raw::c_int;
macro_rules! impl_from_uints {
($( $ty:ty )*) => {
$(
impl From<$ty> for NonNegativeInt {
#[inline]
fn from(value: $ty) -> Self {
Self(value.into())
}
}
)*
};
}
macro_rules! impl_try_from_uints {
($( $ty:ty )*) => {
$(
impl TryFrom<$ty> for NonNegativeInt {
type Error = std::num::TryFromIntError;
#[inline]
fn try_from(value: $ty) -> Result<Self, Self::Error> {
value.try_into().map(Self)
}
}
)*
};
}
impl_from_uints!(u8 u16);
impl_try_from_uints!(u32 u128 usize);
impl NonNegativeInt {
pub(crate) unsafe fn from_libui(value: c_int) -> Self {
Self(value)
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct NonNegativeInt(c_int);
impl NonNegativeInt {
pub(crate) fn to_libui(self) -> c_int {
self.0
}
pub fn as_u32(self) -> u32 {
match u32::try_from(self.0) {
Ok(it) => it,
Err(_) => {
panic!(
"Found a negative integer ({}) where a non-negative integer was expected. \
\
I would greatly appreciate if you submitted an issue to the *boing* \
repository. Thanks! ~norepi",
self.0,
);
}
}
}
}
}