]> uap-core.de Git - note.git/commitdiff
add support for actions with typed arguments main
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Fri, 5 Jun 2026 18:10:27 +0000 (20:10 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Fri, 5 Jun 2026 18:10:27 +0000 (20:10 +0200)
application/src/notebook.rs
application/src/window.rs
ui-rs-derive/src/lib.rs
ui-rs/src/ui/event.rs
ui-rs/src/ui/ffi.rs
ui-rs/src/ui/toolkit.rs
ui/common/action.c
ui/common/wrapper.c
ui/common/wrapper.h
ui/ui/toolkit.h

index d8030c97692d34e090f9aff7904020f4bd77d409..a8465ade2c0a709e529878daa9ed80859f3a7da4 100644 (file)
@@ -1,3 +1,4 @@
+use std::any::Any;
 use std::rc::Rc;
 use ui_rs::{action, ui_actions, UiModel};
 use ui_rs::ui::*;
@@ -96,6 +97,9 @@ impl Notebook {
                 if let Some(mut nb) = self.doc_ref.get_doc() {
                     nb.ctx.attach(&doc);
                     self.selected_note = Some(doc);
+
+                    let param: Box<dyn Any> = Box::new("hello world".to_string());
+                    nb.ctx.call_action_with_parameter("navigation", param);
                 }
             }
         }
index 69d6c84673c284f96d3495cb6c7ebdc7ae4f0844..ddf5ca6f5809df7c558d512b42de191f592353f2 100644 (file)
@@ -137,6 +137,11 @@ impl MainWindow {
 
     }
 
+    #[action(String)]
+    pub fn navigation(&mut self, _event: &ActionEvent, arg: &String) {
+        println!("navigation: {}", arg);
+    }
+
     #[action]
     pub fn message(&mut self, _event: &ActionEvent) {
         while let Ok(msg) = self.broadcast_rx.try_recv() {
index 03fc8f283f53607ca1fb63562d13050d34c3c232..5c6da1f5629fbb6cac386a4db323abdba7a1e6bc 100644 (file)
@@ -1,6 +1,6 @@
 use proc_macro::TokenStream;
 use quote::quote;
-use syn::{parse_macro_input, Attribute, Data, DeriveInput, Expr, ExprLit, ImplItem, ItemImpl, Lit, Meta};
+use syn::{parse_macro_input, Attribute, Data, DeriveInput, Expr, ExprLit, ImplItem, ItemImpl, Lit, Meta, Type};
 
 #[proc_macro_derive(UiModel, attributes(bind))]
 pub fn derive_ui_model(input: TokenStream) -> TokenStream {
@@ -158,15 +158,34 @@ pub fn ui_actions(_attr: TokenStream, item: TokenStream) -> TokenStream {
     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 has_handler_attr(&method.attrs) {
-                handlers.push(method.sig.ident.clone());
+            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(
+                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 UiActions for #self_ty {
@@ -179,7 +198,8 @@ pub fn ui_actions(_attr: TokenStream, item: TokenStream) -> TokenStream {
                                 target.#handlers(event);
                             }
                         );
-                    )*
+                    )*;
+                    #(#typed_actions)*
                 }
             }
         }
@@ -188,8 +208,21 @@ pub fn ui_actions(_attr: TokenStream, item: TokenStream) -> TokenStream {
     TokenStream::from(expanded)
 }
 
-fn has_handler_attr(attrs: &[Attribute]) -> bool {
-    attrs.iter().any(|attr| attr.path().is_ident("action"))
+fn has_handler_attr(attrs: &[Attribute]) -> Option<Option<Type>> {
+    for attr in attrs {
+        if !attr.path().is_ident("action") {
+            continue;
+        }
+        match &attr.meta {
+            Meta::Path(_) => return Some(None),
+            Meta::List(list) => {
+                let ty: Type = syn::parse2(list.tokens.clone()).ok()?;
+                return Some(Some(ty));
+            },
+            _ => panic!("Unexpected Attribute")
+        }
+    }
+    None
 }
 
 #[proc_macro_attribute]
index 984a8fa48e5f216c8fd1903be502c55e457e6217..87d84bc858d838ebf22708b245694c6d1298326c 100644 (file)
 
 #![allow(dead_code)]
 
+use std::any::Any;
 use std::ffi::{c_char, c_void, CStr, CString};
 use std::slice;
 use crate::ui::{event, ffi, UiDouble, UiInteger, UiString, UiText, UiRange, ListSelection, UiObject, NoAppData, UiSourceList};
-use crate::ui::ffi::{UiEvent, UiSubListEventData, UiTextChangeEventData};
+use crate::ui::ffi::{UiEvent, UiSubListEventData, UiTextChangeEventData, UiTypedObj};
 use crate::ui::text::{TextInsert, TextDelete};
 
+pub const RUST_TYPED_OBJECT_BOX_ANY: u64 = 0x4416222c10162501;
+
 pub struct Event<'a, T> {
     pub obj: &'a mut UiObject<T>,
     pub data: &'a mut T,
@@ -64,7 +67,8 @@ enum UiEventType {
     ListElm,
     DND,
     SubList,
-    FileList
+    FileList,
+    TypedObject
 }
 
 pub enum EventType<'a> {
@@ -82,7 +86,8 @@ pub enum EventType<'a> {
     ListElement,
     Dnd,
     SubList(&'a mut SubListEvent),
-    FileList
+    FileList,
+    TypedObject(&'a mut dyn Any)
 }
 
 enum EventTypeData {
@@ -100,7 +105,8 @@ enum EventTypeData {
     ListElement,
     Dnd,
     SubList(SubListEvent),
-    FileList
+    FileList,
+    TypedObject(*mut Box<dyn Any>)
 }
 
 fn get_event_data(e: *const ffi::UiEvent) -> EventTypeData {
@@ -179,7 +185,18 @@ fn get_event_data(e: *const ffi::UiEvent) -> EventTypeData {
         UiEventType::FileList => {
             // filelist
             EventTypeData::Null
-        }
+        },
+        UiEventType::TypedObject => {
+            unsafe {
+                if ui_typed_obj_get_type(ptr.cast()) == RUST_TYPED_OBJECT_BOX_ANY && !ptr.is_null() {
+                    let box_ptr: *mut Box<dyn Any> = ui_typed_obj_get_ptr(ptr.cast()).cast();
+                    EventTypeData::TypedObject(box_ptr)
+                } else {
+                    EventTypeData::Null
+
+                }
+            }
+        },
         _ => EventTypeData::Null
     }
 }
@@ -201,6 +218,11 @@ fn get_event_type<'a>(data: &'a mut EventTypeData) -> EventType<'a> {
         EventTypeData::Dnd => EventType::Dnd,
         EventTypeData::SubList(s) => EventType::SubList( s ),
         EventTypeData::FileList => EventType::FileList,
+        EventTypeData::TypedObject(ptr) => unsafe {
+            let boxed: &mut Box<dyn Any> = ptr.as_mut().unwrap();
+            let any: &mut dyn Any = boxed.as_mut();
+            EventType::TypedObject(any)
+        }
     }
 }
 
@@ -333,6 +355,9 @@ extern "C" {
     fn ui_event_get_int(event: *const UiEvent) -> i32;
     fn ui_event_get_set(event: *const UiEvent) -> i32;
 
+    fn ui_typed_obj_get_ptr(obj: *const UiTypedObj) -> *mut c_void;
+    fn ui_typed_obj_get_type(obj: *const UiTypedObj) -> u64;
+
     fn ui_text_change_event_get_type(data: *const UiTextChangeEventData) -> i32;
     fn ui_text_change_event_get_begin(data: *const UiTextChangeEventData) -> i32;
     fn ui_text_change_event_get_end(data: *const UiTextChangeEventData) -> i32;
index 1908c509b1d855b57e5ee3d530ff2f28f39d34c4..21daf48e205859786f6ab85434170821fc185290 100644 (file)
@@ -51,6 +51,11 @@ pub struct UiSubListEventData {
     _private: [u8; 0],
 }
 
+#[repr(C)]
+pub struct UiTypedObj {
+    _private: [u8; 0],
+}
+
 #[repr(C)]
 pub struct UiTextChangeEventData {
     _private: [u8; 0],
index 394626b56af7ba197d2edd088eb54315746f7e5f..d0666a8ebeb04235d6f31695db853cc964e3aa06 100644 (file)
@@ -28,8 +28,9 @@
 
 #![allow(dead_code)]
 
+use std::any::Any;
 use std::ffi::{c_char, c_int, c_void, CStr, CString};
-use crate::ui::{action_event_wrapper, event, ffi, ui_object_get_context, ui_object_get_windowdata, ui_reg_destructor, ui_remove_destructor, ActionEventWrapper, Event, EventWrapper, SubList};
+use crate::ui::{action_event_wrapper, event, ffi, ui_object_get_context, ui_object_get_windowdata, ui_reg_destructor, ui_remove_destructor, ActionEventWrapper, Event, EventWrapper, SubList, RUST_TYPED_OBJECT_BOX_ANY};
 
 use std::marker::PhantomData;
 use std::mem;
@@ -117,6 +118,14 @@ impl UiContext {
             ui_call_action(self.ptr, cstr.as_ptr());
         }
     }
+
+    pub fn call_action_with_parameter(&self, action: &str, mut param: Box<dyn Any>) {
+        let cstr = CString::new(action).unwrap();
+        unsafe {
+            let param_ptr = &mut param as *mut Box<dyn Any>;
+            ui_call_action3(self.ptr, cstr.as_ptr(), param_ptr.cast(), RUST_TYPED_OBJECT_BOX_ANY);
+        }
+    }
 }
 
 pub extern "C" fn destroy_boxed<T>(data: *mut c_void) {
@@ -1275,6 +1284,7 @@ extern "C" {
     fn ui_text_length(text: *const ffi::UiText) -> c_int;
     fn ui_text_remove(text: *mut ffi::UiText, begin: c_int, end: c_int);
 
-    fn ui_call_action(ctx: *mut ffi::UiContext, action: *const c_char);
+    fn ui_call_action(ctx: *mut ffi::UiContext, action: *const c_char) -> c_int;
+    fn ui_call_action3(ctx: *mut ffi::UiContext, action: *const c_char, data: *mut c_void, typeid: u64) -> c_int;
     fn ui_mainthread_broadcast(action: *const c_char);
 }
index 7c99361451d7423604cc7df6c9e0a1ca3a5c249a..5624c34384cc84328c9accaf1ee8d04f050d9d9c 100644 (file)
@@ -143,11 +143,7 @@ void uic_action_callback(UiEvent *event, const char *action_name) {
     }
 }
 
-void ui_call_action(UiContext *ctx, const char *action_name) {
-    ui_call_action2(ctx, action_name, NULL, 0);
-}
-
-void ui_call_action2(UiContext *ctx, const char *action_name, void *eventdata, int intval) {
+static int call_action(UiContext *ctx, const char *action_name, void *eventdata, UiEventType eventdatatype, int intval) {
     UiAction *action = uic_resolve_action(ctx, action_name);
     if(action && action->callback) {
         UiEvent event;
@@ -157,13 +153,33 @@ void ui_call_action2(UiContext *ctx, const char *action_name, void *eventdata, i
         event.document = ctx->self_doc ? ctx->self_doc : ctx->document;
         if(eventdata) {
             event.eventdata = eventdata;
-            event.eventdatatype = UI_EVENT_DATA_POINTER;
+            event.eventdatatype = eventdatatype;
         }
         event.intval = intval;
         action->callback(&event, action->userdata);
+        return 1;
+    } else {
+        return 0;
     }
 }
 
+int ui_call_action(UiContext *ctx, const char *action_name) {
+    return ui_call_action2(ctx, action_name, NULL, 0);
+}
+
+int ui_call_action2(UiContext *ctx, const char *action_name, void *eventdata, int intval) {
+    return call_action(ctx, action_name, eventdata, UI_EVENT_DATA_POINTER, intval);
+}
+
+int ui_call_action3(UiContext *ctx, const char *action_name, void *ptr, uint64_t type_id) {
+    UiTypedObj *obj = malloc(sizeof(UiTypedObj));
+    obj->ptr = ptr;
+    obj->type = type_id;
+    int ret = call_action(ctx, action_name, obj, UI_EVENT_DATA_TYPED_OBJECT, 0);
+    free(obj);
+    return ret;
+}
+
 void ui_broadcast_action(const char *action_name) {
     ui_broadcast_action2(action_name, NULL, 0);
 }
index 56194e89a469c5baabd5fb7ac232eb46ac80f4c7..29af77edfbb9296a7f98c536ee9529f6f99b9987 100644 (file)
@@ -250,6 +250,17 @@ int ui_event_get_set(UiEvent *event) {
 }
 
 
+/* ---------------------------- UiTypedObj ---------------------------- */
+
+void* ui_typed_obj_get_ptr(UiTypedObj *obj) {
+    return obj->ptr;
+}
+
+uint64_t ui_typed_obj_get_type(UiTypedObj *obj) {
+    return obj->type;
+}
+
+
 /* ------------------------- SubListItem (public) ------------------------- */
 
 void ui_sublist_item_set_icon(UiSubListItem *item, const char *icon) {
index 6f7507cc28208a16bfa18b38bcab01e9b49af8da..2883e56d56ce234b2931be09b74687867f1b0ec1 100644 (file)
@@ -84,6 +84,9 @@ UIEXPORT int ui_event_get_eventdatatype(UiEvent *event);
 UIEXPORT int ui_event_get_int(UiEvent *event);
 UIEXPORT int ui_event_get_set(UiEvent *event);
 
+UIEXPORT void* ui_typed_obj_get_ptr(UiTypedObj *obj);
+UIEXPORT uint64_t ui_typed_obj_get_type(UiTypedObj *obj);
+
 UIEXPORT int ui_list_selection_get_count(UiListSelection *sel);
 UIEXPORT int* ui_list_selection_get_rows(UiListSelection *sel);
 UIEXPORT void ui_list_set_selected_indices(UiList *list, int *indices, int num);
index 0e89071046fbe56047a80870f6d26030391ec1f1..901d30305f807b55ad34694558c1262ec5fadd2b 100644 (file)
@@ -213,6 +213,8 @@ typedef struct UiListSelection UiListSelection;
 typedef struct UiTextStyle  UiTextStyle;
 typedef struct UiColor      UiColor;
 
+typedef enum UiEventType UiEventType;
+
 /* begin opaque types */
 typedef struct UiContext     UiContext;
 typedef struct UiContainer   UiContainer;
@@ -317,14 +319,32 @@ struct UiObject {
     unsigned int ref;
 };
 
+enum UiEventType {
+    UI_EVENT_DATA_NULL = 0,
+    UI_EVENT_DATA_POINTER,
+    UI_EVENT_DATA_STRING,
+    UI_EVENT_DATA_INTEGER_VALUE,
+    UI_EVENT_DATA_STRING_VALUE,
+    UI_EVENT_DATA_TEXT_VALUE,
+    UI_EVENT_DATA_DOUBLE_VALUE,
+    UI_EVENT_DATA_RANGE_VALUE,
+    UI_EVENT_DATA_TEXT_CHANGED,
+    UI_EVENT_DATA_LIST_SELECTION,
+    UI_EVENT_DATA_LIST_ELM,
+    UI_EVENT_DATA_DND,
+    UI_EVENT_DATA_SUBLIST,
+    UI_EVENT_DATA_FILE_LIST,
+    UI_EVENT_DATA_TYPED_OBJECT
+};
+
 struct UiEvent {
-    UiObject *obj;
-    void     *document;
-    void     *window;
-    void     *eventdata;
-    int      eventdatatype;
-    int      intval;
-    int      set;
+    UiObject     *obj;
+    void         *document;
+    void         *window;
+    void         *eventdata;
+    UiEventType  eventdatatype;
+    int          intval;
+    int          set;
 };
 
 struct UiMouseEvent {
@@ -486,22 +506,11 @@ typedef struct UiCondVar {
     int intdata;
 } UiCondVar;
 
-enum UiEventType {
-    UI_EVENT_DATA_NULL = 0,
-    UI_EVENT_DATA_POINTER,
-    UI_EVENT_DATA_STRING,
-    UI_EVENT_DATA_INTEGER_VALUE,
-    UI_EVENT_DATA_STRING_VALUE,
-    UI_EVENT_DATA_TEXT_VALUE,
-    UI_EVENT_DATA_DOUBLE_VALUE,
-    UI_EVENT_DATA_RANGE_VALUE,
-    UI_EVENT_DATA_TEXT_CHANGED,
-    UI_EVENT_DATA_LIST_SELECTION,
-    UI_EVENT_DATA_LIST_ELM,
-    UI_EVENT_DATA_DND,
-    UI_EVENT_DATA_SUBLIST,
-    UI_EVENT_DATA_FILE_LIST
-};
+// eventdata in case eventdatatype is UI_EVENT_DATA_TYPED_OBJECT
+typedef struct UiTypedObj {
+    void *ptr;
+    uint64_t type;
+} UiTypedObj;
 
 #define UI_COLOR(r, g, b) (UiColor){r, g, b}
 struct UiColor {
@@ -587,8 +596,9 @@ UIEXPORT void ui_add_action_with_accelerator(
         const char *accelerator,
         const char *accelerator_text);
 UIEXPORT void ui_update_action_bindings(UiContext *ctx);
-UIEXPORT void ui_call_action(UiContext *ctx, const char *action_name);
-UIEXPORT void ui_call_action2(UiContext *ctx, const char *action_name, void *eventdata, int intval);
+UIEXPORT int ui_call_action(UiContext *ctx, const char *action_name);
+UIEXPORT int ui_call_action2(UiContext *ctx, const char *action_name, void *eventdata, int intval);
+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_mainthread_broadcast(const char *action_name);