use std::env;
use entity::collection::{create_notebook_hierarchy, Node};
-use ui_rs::{ui};
+use ui_rs::{action, application, ui};
use ui_rs::ui::*;
use crate::backend::{Backend, BackendHandle};
use crate::window::*;
backend: backend_handle,
notebooks: notebooks,
};
- ui::app_run::<MainWindow>(&mut app);
+ ui::app_run(&mut app);
drop(app);
let _ = join_handle.join();
}
}
+#[application(MainWindow)]
+impl App {
+ #[action]
+ pub fn message(&mut self, _action: &ActionEvent) {
+ println!("app message handler");
+ }
+}
+
fn create_toolbar(app: &AppContext<MainWindow>) {
app.toolbar_item("new_notebook").icon(UiIconSet::NewFolder.as_str()).action("new_notebook").create();
app.toolbar_item("go_back").icon(UiIconSet::GoBack.as_str()).action("go_back").create();
use proc_macro::TokenStream;
use quote::quote;
-use syn::{parse_macro_input, Attribute, Data, DeriveInput, Expr, ExprLit, ImplItem, ItemImpl, Lit, Meta, Type};
+use syn::{parse_macro_input, Attribute, Data, DeriveInput, Expr, ExprLit, GenericArgument, ImplItem, ItemImpl, Lit, Meta, PathArguments, Type};
#[proc_macro_derive(UiModel, attributes(bind))]
pub fn derive_ui_model(input: TokenStream) -> TokenStream {
TokenStream::from(expanded)
}
+/// Generates an implementation of [`Application`] for an `impl` block containing
+/// methods marked with the `#[action]` attribute.
+#[proc_macro_attribute]
+pub fn application(attr: TokenStream, item: TokenStream) -> TokenStream {
+ let window_ty = parse_macro_input!(attr as syn::Type);
+ let impl_block = parse_macro_input!(item as ItemImpl);
+ let self_ty = &impl_block.self_ty;
+
+ let mut handlers = vec![];
+ let mut param_handlers = vec![];
+
+ for item in &impl_block.items {
+ if let ImplItem::Fn(method) = item {
+ if let Some(attr) = has_handler_attr(&method.attrs) {
+ match attr {
+ Some(param) => param_handlers.push((param, method.sig.ident.clone())),
+ None => handlers.push(method.sig.ident.clone()),
+ }
+ }
+ }
+ }
+
+ let typed_actions = param_handlers.into_iter().map(|(ty, name)| {
+ quote! {
+ ctx.add_action_for_target(
+ app,
+ stringify!(#name),
+ |target: &mut Self, event| {
+ if let EventType::TypedObject(obj) = &event.event_type {
+ if let Some(arg) = obj.downcast_ref::<#ty>() {
+ target.#name(event, arg);
+ }
+ }
+ }
+ );
+ }
+ });
+
+ let expanded = quote! {
+ #impl_block
+ impl AppActions<#window_ty> for #self_ty {
+ fn init_actions(&mut self, ctx: &UiContext) {
+ let app: *mut Self = self;
+ unsafe {
+ #(
+ ctx.add_action_for_target(
+ app,
+ stringify!(#handlers),
+ |target: &mut Self, event| {
+ target.#handlers(event);
+ }
+ );
+ )*;
+ #(#typed_actions)*
+ }
+ }
+ }
+ };
+
+ TokenStream::from(expanded)
+}
+
+
fn has_handler_attr(attrs: &[Attribute]) -> Option<Option<Type>> {
for attr in attrs {
if !attr.path().is_ident("action") {
use crate::ui::ffi::{UiCallback, UiEvent};
use crate::ui::{call_mainthread, dialog, toolkit, ui_call_mainthread, ui_global_context, UiActions, UiContext, UiModel};
-pub trait Application<T: UiModel + UiActions> {
+pub trait AppActions<T: UiModel + UiActions> {
+ fn init_actions(&mut self, _ctx: &UiContext) {}
+}
+
+pub trait Application<T: UiModel + UiActions>: AppActions<T> {
fn on_startup(&mut self, app: &AppContext<T>);
fn on_new_window(&mut self, app: &AppContext<T>) {}
fn ui_newwindow();
}
-extern "C" fn app_startup<T: UiModel + UiActions>(_event: *const UiEvent, data: *mut c_void) {
+extern "C" fn app_startup<T: UiModel + UiActions, A: Application<T>>(_event: *const UiEvent, data: *mut c_void) {
unsafe {
- let app_ptr = data as *mut AppWrapper<T>;
- let app_ref: &mut AppWrapper<T> = &mut *app_ptr;
+ let app_ptr = data as *mut A;
+ let app_ref: &mut A = &mut *app_ptr;
let global_ctx = toolkit::UiContext::from_ptr(ui_global_context());
let ctx = AppContext::<T> {
ctx: global_ctx,
_marker: PhantomData,
};
- app_ref.app.on_startup(&ctx);
+ app_ref.on_startup(&ctx);
}
}
-extern "C" fn app_new_window<T: UiModel + UiActions>(_event: *const UiEvent, data: *mut c_void) {
+extern "C" fn app_new_window<T: UiModel + UiActions, A: Application<T>>(_event: *const UiEvent, data: *mut c_void) {
unsafe {
- let app_ptr = data as *mut AppWrapper<T>;
- let app_ref: &mut AppWrapper<T> = &mut *app_ptr;
+ let app_ptr = data as *mut A;
+ let app_ref: &mut A = &mut *app_ptr;
let global_ctx = toolkit::UiContext::from_ptr(ui_global_context());
let ctx = AppContext::<T> {
ctx: global_ctx,
_marker: PhantomData,
};
- app_ref.app.on_new_window(&ctx);
+ app_ref.on_new_window(&ctx);
}
}
-extern "C" fn app_exit<T: UiModel + UiActions>(_event: *const UiEvent, data: *mut c_void) {
+extern "C" fn app_exit<T: UiModel + UiActions, A: Application<T>>(_event: *const UiEvent, data: *mut c_void) {
unsafe {
- let app_ptr = data as *mut AppWrapper<T>;
- let app_ref: &mut AppWrapper<T> = &mut *app_ptr;
+ let app_ptr = data as *mut A;
+ let app_ref: &mut A = &mut *app_ptr;
let global_ctx = toolkit::UiContext::from_ptr(ui_global_context());
let ctx = AppContext::<T> {
ctx: global_ctx,
_marker: PhantomData,
};
- app_ref.app.on_exit(&ctx);
+ app_ref.on_exit(&ctx);
}
}
-pub fn app_run<T: UiModel + UiActions>(app: &mut dyn Application<T>) {
- let mut wrapper = AppWrapper { app: app };
+pub fn app_run<T, A>(app: &mut A)
+where
+ T: UiModel + UiActions,
+ A: Application<T>
+{
unsafe {
- let ptr: *mut AppWrapper<T> = &mut wrapper;
+ let ctx = UiContext::from_ptr(ui_global_context());
+ app.init_actions(&ctx);
+ let ptr: *mut A = app;
let c_ptr: *mut c_void = ptr as *mut c_void;
- ui_onstartup(Some(app_startup::<T>), c_ptr);
- ui_onnewwindow(Some(app_new_window::<T>), c_ptr);
- ui_onexit(Some(app_exit::<T>), c_ptr);
+ ui_onstartup(Some(app_startup::<T, A>), c_ptr);
+ ui_onnewwindow(Some(app_new_window::<T, A>), c_ptr);
+ ui_onexit(Some(app_exit::<T, A>), c_ptr);
ui_main();
}
}
}
}
+impl AppActions<NoAppData> for ErrApp<'_> {}
+
pub fn app_run_startup_error(title: &str, message: &str) {
let mut app = ErrApp { title, message };
app_run(&mut app);
}
-
-#[repr(C)]
-struct AppWrapper<'a, T> {
- app: &'a mut dyn Application<T>,
-}
\ No newline at end of file
assert!(!obj_wdata_ptr.is_null());
obj_wdata_ptr as *mut T
};
+ self.add_action_for_target(target_ptr, name, f);
+ }
+
+ pub unsafe fn add_action_for_target<T, F>(&self, target_ptr: *mut T, name: &str, f: F)
+ where F: FnMut(&mut T, &event::ActionEvent) + 'static {
let wrapper = Box::new(ActionEventWrapper { callback: Box::new(f), target: target_ptr });
let ptr = self.reg_box(wrapper);
let cstr = CString::new(name).unwrap();
return ret;
}
-void ui_broadcast_action(const char *action_name) {
- ui_broadcast_action2(action_name, NULL, 0);
+static void call_action_recursive(const char *action_name, UiContext *ctx, void *eventdata, UiEventType eventdatatype, int intval, UiBool call_children) {
+ UiEvent event;
+ memset(&event, 0, sizeof(UiEvent));
+ event.obj = ctx->obj;
+ event.window = event.obj ? event.obj->window : NULL;
+ event.document = ctx->self_doc ? ctx->self_doc : ctx->document;
+ if(eventdata) {
+ event.eventdata = eventdata;
+ event.eventdatatype = eventdatatype;
+ }
+ event.intval = intval;
+
+ UiAction *a = NULL;
+ if(ctx->actions) {
+ a = cxMapGet(ctx->actions, action_name);
+ }
+ if(a && a->callback) {
+ a->callback(&event, a->userdata);
+ }
+
+ if(call_children) {
+ CxIterator i = cxListIterator(ctx->documents);
+ cx_foreach(void *, doc, i) {
+ UiContext *doc_ctx = ui_document_context(doc);
+ call_action_recursive(action_name, doc_ctx, eventdata, eventdatatype, intval, TRUE);
+ }
+ }
}
-void ui_broadcast_action2(const char *action_name, void *eventdata, int intval) {
+static void broadcast_action(const char *action_name, void *eventdata, UiEventType eventdatatype, int intval) {
CxList *objects = uic_object_list();
CxIterator i = cxListIterator(objects);
cx_foreach(UiObject*, obj, i) {
- ui_call_action2(obj->ctx, action_name, eventdata, intval);
+ call_action_recursive(action_name, obj->ctx, eventdata, eventdatatype, intval, TRUE);
}
+
+ call_action_recursive(action_name, ui_global_context(), eventdata, eventdatatype, intval, FALSE);
+}
+
+void ui_broadcast_action(const char *action_name) {
+ broadcast_action(action_name, NULL, UI_EVENT_DATA_NULL, 0);
+}
+
+void ui_broadcast_action2(const char *action_name, void *eventdata, int intval) {
+ broadcast_action(action_name, eventdata, UI_EVENT_DATA_POINTER, intval);
}
typedef struct UiActionBroadcast {
char *action;
void *eventdata;
+ UiEventType eventdatataype;
int intval;
} UiActionBroadcast;
static int mainthread_action_broadcast(void *data) {
UiActionBroadcast *broadcast = data;
- ui_broadcast_action2(broadcast->action, broadcast->eventdata, broadcast->intval);
+ broadcast_action(broadcast->action, broadcast->eventdata, broadcast->eventdatataype, broadcast->intval);
+ if(broadcast->eventdatataype == UI_EVENT_DATA_TYPED_OBJECT) {
+ free(broadcast->eventdata);
+ }
free(broadcast->action);
free(broadcast);
return 0;
UiActionBroadcast *broadcast = malloc(sizeof(UiActionBroadcast));
broadcast->action = strdup(action_name);
broadcast->eventdata = eventdata;
+ broadcast->eventdatataype = UI_EVENT_DATA_POINTER;
broadcast->intval = intval;
ui_call_mainthread(mainthread_action_broadcast, broadcast);
}
+
+void ui_mainthread_broadcast3(const char *action_name, void *ptr, uint64_t type_id) {
+ UiTypedObj *obj = malloc(sizeof(UiTypedObj));
+ obj->ptr = ptr;
+ obj->type = type_id;
+
+ UiActionBroadcast *broadcast = malloc(sizeof(UiActionBroadcast));
+ broadcast->action = strdup(action_name);
+ broadcast->eventdata = obj;
+ broadcast->eventdatataype = UI_EVENT_DATA_TYPED_OBJECT;
+ broadcast->intval = 0;
+ ui_call_mainthread(mainthread_action_broadcast, broadcast);
+}
UIEXPORT int ui_call_action3(UiContext *ctx, const char *action_name, void *ptr, uint64_t type_id);
UIEXPORT void ui_broadcast_action(const char *action_name);
UIEXPORT void ui_broadcast_action2(const char *action_name, void *eventdata, int intval);
+UIEXPORT void ui_broadcast_action3(const char *action_name, void *ptr, uint64_t type_id);
UIEXPORT void ui_mainthread_broadcast(const char *action_name);
UIEXPORT void ui_mainthread_broadcast2(const char *action_name, void *eventdata, int intval);
+UIEXPORT void ui_mainthread_broadcast3(const char *action_name, void *ptr, uint64_t type_id);
UIEXPORT void ui_set_state(UiContext *ctx, int state);
UIEXPORT void ui_unset_state(UiContext *ctx, int state);