-use ui_rs::ui;
-use ui_rs::ui::{ListValue, UiList};
+use ui_rs::{ui, UiModel};
+use ui_rs::ui::{ListValue, UiList, UiModel};
fn main() {
ui::app_init("note");
struct App;
+#[derive(UiModel, Default)]
struct TestData {
i: i32,
- list: Option<UiList<i32>>
+ #[bind("list")]
+ list: UiList<i32>
}
+
fn create_window() {
- let testdata = TestData { i: 0, list: None } ;
+ let testdata = TestData::default();
let window = ui::window("note", testdata, |obj, data| {
- let mut list = obj.ctx.list::<i32>();
- let v = list.data();
- v.push(10);
- v.push(11);
- v.push(12);
-
-
+ let list = data.list.data();
+ list.push(10);
+ list.push(11);
+ list.push(12);
obj.button_builder().label("Hello").onclick(|e| {
println!("Button clicked: {}", e.data.i);
e.data.i += 1;
}).create();
- obj.listview_builder::<i32>().fill(true).value(&list).getvalue(|elm, _row, _col| {
+ obj.listview_builder::<i32>().fill(true).varname("list").getvalue(|elm, _row, _col| {
ListValue::String(elm.to_string())
}).create();
-
- data.list = Some(list);
});
window.show();
use proc_macro::TokenStream;
use quote::quote;
-use syn::{parse_macro_input, DeriveInput};
+use syn::{parse_macro_input, Data, DeriveInput, Expr, ExprLit, Lit, Meta};
-#[proc_macro_derive(ViewModel)]
-pub fn derive_view_model(input: TokenStream) -> TokenStream {
+#[proc_macro_derive(UiModel, attributes(bind))]
+pub fn derive_ui_model(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = input.ident;
+ let fields = match &input.data {
+ Data::Struct(data) => &data.fields,
+ _ => panic!("UiModel can only be derived for structs"),
+ };
+
+ let mut inits = Vec::new();
+
+ // Add initialization code for all fields that have the [bind] attribute
+ for field in fields {
+ let field_name = match &field.ident {
+ Some(ident) => ident,
+ None => panic!("UiModel fields must be named") // tupels are not supported
+ };
+
+ // find bind attribute
+ for attr in &field.attrs {
+ if !attr.path().is_ident("bind") {
+ continue;
+ }
+
+ match &attr.meta {
+ Meta::Path(_) => {
+ // unnamed bind
+ inits.push(quote! {
+ self.#field_name.init(_ctx, None);
+ });
+ },
+ Meta::List(list) => {
+ // named bind
+ let expr: Expr = list.parse_args().unwrap();
+ match &expr {
+ Expr::Lit(ExprLit { lit: Lit::Str(lit_str), .. }) => {
+ let name = lit_str.value();
+ inits.push(quote! {
+ self.#field_name.init(_ctx, Some(#name));
+ });
+ },
+ _ => {
+ panic!("UiModel bind attribute must be a string literal")
+ }
+ }
+ }
+ _ => panic!("Unexpected Attribute"),
+ }
+ }
+ }
+
let expanded = quote! {
- impl ViewModel for #name {
+ impl UiModel for #name {
fn init(&mut self, _ctx: &ui_rs::ui::UiContext) {
- // empty for now
+ #(#inits)*
}
}
};
+ //eprintln!("{}", expanded);
+
TokenStream::from(expanded)
}
\ No newline at end of file
self
}
+ pub fn varname(&mut self, varname: &str) -> &mut Self {
+ let cstr = CString::new(varname).unwrap();
+ unsafe {
+ ui_list_args_set_varname(self.args, cstr.as_ptr());
+ }
+ self
+ }
pub fn value<C>(&mut self, value: &toolkit::UiList<C>) -> &mut Self {
unsafe {
ui_list_args_set_value(self.args, value.ptr);
}
}
+impl Default for UiContext {
+ fn default() -> Self {
+ UiContext {
+ ptr: std::ptr::null_mut(),
+ doc: std::ptr::null_mut()
+ }
+ }
+}
+
impl Drop for UiContext {
fn drop(&mut self) {
if !self.doc.is_null() {
doc: std::ptr::null_mut()
}
}
-
- pub fn list<T>(&self) -> UiList<T> {
- let v: Vec<T> = Vec::new();
- let b = Box::new(v);
- let data = Box::into_raw(b);
- unsafe {
- UiList {
- ptr: ui_list_new2(self.ptr, std::ptr::null_mut(), list_init::<T>, data as *mut c_void),
- data: Box::from_raw(data as *mut Vec<T>)
- }
- }
+ pub fn list<T>(&self) -> UiList<T> {
+ let mut ls = UiList::<T>::default();
+ ls.init(self, None);
+ ls
}
}
-pub trait WindowModel {
- fn init(&mut self, ctx: *const ffi::UiContext);
+pub trait UiModel {
+ fn init(&mut self, ctx: &UiContext);
}
pub struct UiObject<T> {
pub fn data(&mut self) -> &mut Vec<T> {
self.data.as_mut()
}
+
+ pub fn init(&mut self, ctx: &UiContext, name: Option<&str>) {
+ unsafe {
+ let c_string = name.map(|n| CString::new(n).unwrap());
+ let c_str = c_string.as_ref().map_or(std::ptr::null(), |s| s.as_ptr());
+ let data: *mut Vec<T> = &mut *self.data;
+ self.ptr = ui_list_new2(ctx.ptr, c_str, list_init::<T>, data as *mut c_void);
+ }
+ }
+}
+
+
+/* -------------------------------- Default implementation -------------------------------- */
+
+macro_rules! value_default_impl {
+ ($type_name:ident) => {
+ impl Default for $type_name {
+ fn default() -> Self {
+ Self { ptr: std::ptr::null_mut() }
+ }
+ }
+ };
+}
+
+value_default_impl!(UiInteger);
+value_default_impl!(UiDouble);
+value_default_impl!(UiString);
+value_default_impl!(UiText);
+
+impl<T> Default for UiList<T> {
+ fn default() -> Self {
+ Self {
+ ptr: std::ptr::null_mut(),
+ data: Box::new(Vec::new())
+ }
+ }
}
+/* -------------------------------- C functions -------------------------------- */
extern "C" {
fn ui_init(appname: *const c_char, argc: c_int, argv: *const *const c_char);
-
+
fn ui_document_new(size: usize) -> *mut c_void;
fn ui_document_ref(doc: *mut c_void);
fn ui_document_unref(doc: *mut c_void);
use std::ffi::{c_char, c_int, c_void};
use std::ffi::CString;
use std::marker::PhantomData;
-use crate::ui::{toolkit};
+use crate::ui::{toolkit, UiModel};
use crate::ui::ffi::{UiContext, UiDestructor, UiObject};
extern "C" {
Simple
}
-fn window_create<T, F>(title: &str, kind: WindowType, mut data: T, create_ui: F) -> toolkit::UiObject<T>
+fn window_create<T: UiModel, F>(title: &str, kind: WindowType, mut data: T, create_ui: F) -> toolkit::UiObject<T>
where F: FnOnce(&mut toolkit::UiObject<T>, &mut T) {
// create the window
let objptr = unsafe {
_data: PhantomData
};
+ // init view model
+ data.init(&obj.ctx);
+
// call ui building closure
create_ui(&mut obj, &mut data);
obj
}
-pub fn window<T, F>(title: &str, data: T, create_ui: F) -> toolkit::UiObject<T>
+pub fn window<T: UiModel, F>(title: &str, data: T, create_ui: F) -> toolkit::UiObject<T>
where F: FnOnce(&mut toolkit::UiObject<T>, &mut T)
{
window_create(title, WindowType::Standard, data, create_ui)
}
-pub fn sidebar_window<T, F>(title: &str, data: T, create_ui: F) -> toolkit::UiObject<T>
+pub fn sidebar_window<T: UiModel, F>(title: &str, data: T, create_ui: F) -> toolkit::UiObject<T>
where F: FnOnce(&mut toolkit::UiObject<T>, &mut T)
{
window_create(title, WindowType::Sidebar, data, create_ui)
}
-pub fn splitview_window<T, F>(title: &str, sidebar: bool, data: T, create_ui: F) -> toolkit::UiObject<T>
+pub fn splitview_window<T: UiModel, F>(title: &str, sidebar: bool, data: T, create_ui: F) -> toolkit::UiObject<T>
where F: FnOnce(&mut toolkit::UiObject<T>, &mut T)
{
window_create(title, WindowType::SplitView(sidebar), data, create_ui)
}
-pub fn simple_window<T, F>(title: &str, data: T, create_ui: F) -> toolkit::UiObject<T>
+pub fn simple_window<T: UiModel, F>(title: &str, data: T, create_ui: F) -> toolkit::UiObject<T>
where F: FnOnce(&mut toolkit::UiObject<T>, &mut T)
{
window_create(title, WindowType::Simple, data, create_ui)