]> uap-core.de Git - note.git/commitdiff
finish SourceList bindings
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Thu, 23 Apr 2026 15:22:31 +0000 (17:22 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Thu, 23 Apr 2026 15:22:31 +0000 (17:22 +0200)
12 files changed:
application/src/main.rs
ui-rs/src/ui/list.rs
ui-rs/src/ui/toolkit.rs
ui/cocoa/EventData.h
ui/cocoa/EventData.m
ui/cocoa/button.m
ui/cocoa/text.h
ui/cocoa/text.m
ui/common/types.c
ui/gtk/text.c
ui/gtk/text.h
ui/ui/toolkit.h

index 45fdf7c237981a96c15cf70cf5d98a415197d4cb..db83377af50d3247389cee9557d789fe29c5b8bf 100644 (file)
@@ -15,8 +15,8 @@ struct TestData {
     i: i32,
     #[bind("list")]
     list: UiList<i32>,
-    #[bind]
-    sublists: UiList<SubList<String>>
+    #[bind("source")]
+    sublists: UiSourceList<String>
 }
 
 #[ui_actions]
@@ -37,6 +37,14 @@ fn create_window() {
         list.push(11);
         list.push(12);
 
+        let mut sl = SubList::new();
+        sl.set_header("Header");
+        sl.data().push(String::from("Elm 1"));
+        sl.data().push(String::from("Elm 2"));
+
+        data.sublists.push(sl);
+
+
         obj.button_builder().label("Hello").onclick(|e| {
             println!("Button clicked: {}", e.data.i);
             e.data.i += 1;
@@ -47,7 +55,12 @@ fn create_window() {
         //obj.tableview_builder::<i32>().fill(true).varname("list").model(&model).getvalue(|elm, _row, _col| {
         //    ListValue::String(elm.to_string())
         //}).create();
-        obj.sourcelist_builder::<String>().fill(true).value(&data.sublists).create();
+        obj.sourcelist_builder::<String>().fill(true).value(&data.sublists).getvalue(|elm, index, item| {
+            item.label = Some(elm.to_string());
+            item.badge = Some(index.to_string());
+        }).create();
+
+        println!("test");
     });
 
     window.show();
index a667220ffc18c855f01fdb99d7ebbb05712e1eed..a1fca13e78482ba9ff74328e10eab2fbe7e484df 100644 (file)
@@ -43,10 +43,20 @@ pub struct TableModel {
 }
 
 pub struct SubList<T> {
-    handle: *mut ffi::UiSubList,
+    pub handle: *mut ffi::UiSubList,
     list: toolkit::UiList<T>
 }
 
+#[derive(Default)]
+pub struct SourceListItem {
+    pub icon: Option<String>,
+    pub label: Option<String>,
+    pub button_icon: Option<String>,
+    pub button_label: Option<String>,
+    pub badge: Option<String>,
+    // TODO: button_menu
+}
+
 pub enum ColumnType {
     String = 0,
     // skip UI_STRING_FREE
@@ -473,6 +483,7 @@ impl<'a, T, E> TableViewBuilder<'a, T, E> {
 impl<'a, T, E> SourceListBuilder<'a, T, E> {
     pub fn create(&mut self) {
         unsafe {
+            ui_sourcelist_args_set_getvalue_func(self.args, sourcelist_getvalue_wrapper::<E>);
             ui_sourcelist_create(self.obj.ptr, self.args);
         }
     }
@@ -591,7 +602,7 @@ impl<'a, T, E> SourceListBuilder<'a, T, E> {
         }
         self
     }
-    pub fn value(&mut self, value: &toolkit::UiList<SubList<E>>) -> &mut Self {
+    pub fn value(&mut self, value: &toolkit::UiSourceList<E>) -> &mut Self {
         unsafe {
             ui_sourcelist_args_set_dynamic_sublists(self.args, value.ptr);
         }
@@ -599,11 +610,10 @@ impl<'a, T, E> SourceListBuilder<'a, T, E> {
     }
 
     pub fn getvalue<F>(&mut self, f: F) -> &mut Self
-    where F: Fn(&E, i32, i32) -> ListValue<'a> + 'static {
+    where F: Fn(&E, i32, &mut SourceListItem) + 'static {
         unsafe {
-            let wrapper = Box::new(GetValueWrapper { callback: Box::new(f), is_table: false });
+            let wrapper = Box::new(SLGetValueWrapper { callback: Box::new(f)});
             let ptr = self.obj.ctx.reg_box(wrapper);
-            ui_sourcelist_args_set_getvalue_func(self.args, sourcelist_getvalue_wrapper::<E>);
             ui_sourcelist_args_set_getvalue_userdata(self.args, ptr as *mut c_void);
         }
         self
@@ -685,10 +695,12 @@ impl Drop for TableModel {
 impl<E> SubList<E> {
     pub fn new() -> Self {
         unsafe {
-            SubList {
+            let s = SubList {
                 handle: ui_sublist_new(),
                 list: toolkit::UiList::new()
-            }
+            };
+            ui_sublist_set_value(s.handle, s.list.ptr);
+            s
         }
     }
 
@@ -698,6 +710,14 @@ impl<E> SubList<E> {
             ui_sublist_set_header(self.handle, cstr.as_ptr());
         }
     }
+
+    pub fn list(&mut self) -> &mut toolkit::UiList<E> {
+        &mut self.list
+    }
+
+    pub fn data(&mut self) -> &mut Vec<E> {
+        self.list.data()
+    }
 }
 
 impl<E> Drop for SubList<E> {
@@ -718,6 +738,10 @@ struct GetValueWrapper<'a, T> {
     is_table: bool
 }
 
+struct SLGetValueWrapper<T> {
+    callback: Box<dyn Fn(&T, i32, &mut SourceListItem)>
+}
+
 
 fn str_dup(str: &str) -> *mut c_void {
     let cstr = CString::new(str).unwrap();
@@ -765,12 +789,51 @@ pub extern "C" fn null_getvalue_wrapper(_list: *const ffi::UiList, _elm_ptr: *co
 pub extern "C" fn sourcelist_getvalue_wrapper<T>(
     _list: *const ffi::UiList,
     _sublist_userdata: *mut c_void,
-    _row_data: *mut c_void,
-    _index: c_int,
-    _item: *mut ffi::UiSubListItem,
-    _userdata: *mut c_void)
+    row_data: *mut c_void,
+    index: c_int,
+    item: *mut ffi::UiSubListItem,
+    userdata: *mut c_void)
 {
-    // TODO
+    if userdata.is_null() {
+        return;
+    }
+
+    let wrapper = unsafe { &*(userdata as *const SLGetValueWrapper<T> ) };
+    let elm = unsafe { &*(row_data as *const T) };
+
+    let mut it: SourceListItem = Default::default();
+    (wrapper.callback)(elm, index, &mut it);
+
+    if let Some(icon) = it.icon {
+        let cstr = CString::new(icon).unwrap();
+        unsafe {
+            ui_sublist_item_set_icon(item, cstr.as_ptr());
+        }
+    }
+    if let Some(label) = it.label {
+        let cstr = CString::new(label).unwrap();
+        unsafe {
+            ui_sublist_item_set_label(item, cstr.as_ptr());
+        }
+    }
+    if let Some(icon) = it.button_icon {
+        let cstr = CString::new(icon).unwrap();
+        unsafe {
+            ui_sublist_item_set_button_icon(item, cstr.as_ptr());
+        }
+    }
+    if let Some(label) = it.button_label {
+        let cstr = CString::new(label).unwrap();
+        unsafe {
+            ui_sublist_item_set_button_label(item, cstr.as_ptr());
+        }
+    }
+    if let Some(badge) = it.badge {
+        let cstr = CString::new(badge).unwrap();
+        unsafe {
+            ui_sublist_item_set_badge(item, cstr.as_ptr());
+        }
+    }
 }
 
 type SLGetValueFunc = extern "C" fn(
@@ -856,4 +919,10 @@ extern "C" {
     fn ui_sublist_set_value(list: *mut ffi::UiSubList, value: *mut ffi::UiList);
     fn ui_sublist_set_header(list: *mut ffi::UiSubList, header: *const c_char);
     fn ui_sublist_free(list: *mut ffi::UiSubList);
+
+    fn ui_sublist_item_set_icon(item: *mut ffi::UiSubListItem, icon: *const c_char);
+    fn ui_sublist_item_set_label(item: *mut ffi::UiSubListItem, label: *const c_char);
+    fn ui_sublist_item_set_button_icon(item: *mut ffi::UiSubListItem, icon: *const c_char);
+    fn ui_sublist_item_set_button_label(item: *mut ffi::UiSubListItem, label: *const c_char);
+    fn ui_sublist_item_set_badge(item: *mut ffi::UiSubListItem, badge: *const c_char);
 }
\ No newline at end of file
index 4a5b8e8d1c48c0cf7f234ad4fe460a29b56e51d3..a1765a764297954e2d86f888aeb0ad259d4be0d0 100644 (file)
@@ -1,7 +1,7 @@
 #![allow(dead_code)]
 
 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, ActionEventWrapper, EventWrapper};
+use crate::ui::{action_event_wrapper, event, ffi, ui_object_get_context, ui_object_get_windowdata, ui_reg_destructor, ActionEventWrapper, EventWrapper, SubList};
 
 use std::marker::PhantomData;
 use std::mem;
@@ -186,6 +186,11 @@ pub struct UiList<T> {
     data: Box<Vec<T>>
 }
 
+pub struct UiSourceList<T> {
+    pub ptr: *mut ffi::UiList,
+    sublists: Vec<SubList<T>>
+}
+
 /* -------------------------------- Default implementation -------------------------------- */
 
 macro_rules! value_default_impl {
@@ -213,6 +218,17 @@ impl<T> Default for UiList<T> {
     }
 }
 
+impl<T> Default for UiSourceList<T> {
+    fn default() -> Self {
+        Self {
+            ptr: std::ptr::null_mut(),
+            sublists: Default::default()
+        }
+    }
+
+
+}
+
 pub fn app_init(appname: &str) {
     let s;
     let c_str = if appname.len() > 0 {
@@ -372,6 +388,47 @@ impl<T> Drop for UiList<T> {
 }
 
 
+/* -------------------------------- SourceList -------------------------------- */
+
+impl<T> UiSourceList<T> {
+    pub fn init(&mut self, ctx: &UiContext, name: Option<&str>) {
+        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());
+        unsafe {
+            self.ptr = ui_list_new(ctx.ptr, c_str);
+        }
+    }
+
+    pub fn push(&mut self, sublist: SubList<T>) {
+        unsafe {
+            ui_list_append(self.ptr, sublist.handle as *mut c_void);
+        }
+        self.sublists.push(sublist);
+    }
+
+    pub fn insert(&mut self, index: usize, sublist: SubList<T>) {
+        unsafe {
+            ui_list_insert(self.ptr, index as c_int, sublist.handle as *mut c_void);
+        }
+        self.sublists.insert(index, sublist);
+    }
+
+    pub fn remove(&mut self, index: usize) {
+        // TODO
+        self.sublists.remove(index);
+    }
+
+    pub fn clear(&mut self) {
+        // TODO
+        self.sublists.clear();
+    }
+
+    pub fn update(&mut self) {
+
+    }
+}
+
+
 /* -------------------------------- List -------------------------------- */
 
 /*
@@ -500,6 +557,7 @@ type UiListGetFunc   = extern "C" fn(*const ffi::UiList, c_int) -> *mut c_void;
 type UiListCountFunc = extern "C" fn(*const ffi::UiList) -> c_int;
 
 extern "C" {
+    fn ui_list_new(ctx: *mut ffi::UiContext, name: *const c_char) -> *mut ffi::UiList;
     fn ui_list_new2(ctx: *mut ffi::UiContext, name: *const c_char, init: UiListInitFunc, userdata: *mut c_void) -> *mut ffi::UiList;
     fn ui_list_class_set_first(list: *mut ffi::UiList, func: UiListFirstFunc);
     fn ui_list_class_set_next(list: *mut ffi::UiList, func: UiListNextFunc);
@@ -521,5 +579,8 @@ extern "C" {
     fn ui_list_selection_get_rows(list: *mut ffi::UiListSelection) -> *const c_int;
     fn ui_list_selection_free(list: *mut ffi::UiListSelection);
 
+    pub fn ui_list_append(list: *mut ffi::UiList, data: *mut c_void);
+    pub fn ui_list_insert(list: *mut ffi::UiList, index: c_int, data: *mut c_void);
+
     fn ui_list_free(ctx: *mut ffi::UiContext, list: *mut ffi::UiList);
 }
index 603b87c64e5a81033307e8fc060fbaf13cc5658f..f82f1d4cfc9eaeadca6ed3353d1bf3e94e579335 100644 (file)
@@ -29,7 +29,7 @@
 #import "../ui/toolkit.h"
 #import "../common/context.h"
 
-typedef void(*get_eventdata_func)(id sender, UiVar *var, void **eventdata, int *value);
+typedef void(*get_eventdata_func)(id sender, UiVar *var, void **eventdata, int *eventdatatype, int *value);
 
 @interface EventData : NSObject
 @property UiObject           *obj;
index c7e95ecbfaf0d4b456012a31bd8b1828a578344e..aa8c74b6e11a463eef16fcc1376ef235c0c013b8 100644 (file)
@@ -70,7 +70,7 @@
     event.intval = 0;
     event.set = ui_get_setop();
     if(_get_eventdata) {
-        _get_eventdata(sender, _var, &event.eventdata, &event.intval);
+        _get_eventdata(sender, _var, &event.eventdata, &event.eventdatatype, &event.intval);
     }
     if(self.callback) {
         self.callback(&event, self.userdata);
index 70ee28ce2f7ea2993e1a0854c1d4bd5016a5e1b5..9a85451c572fd211695cfc511a2cffb47257c2fb 100644 (file)
@@ -47,12 +47,14 @@ UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) {
         button.image = ui_cocoa_named_icon(args->icon);;
     }
     
-    if(args->onclick) {
+    if(args->onclick || args->action) {
         EventData *event = [[EventData alloc] init:args->onclick userdata:args->onclickdata action:args->action];
         event.obj = obj;
         button.target = event;
         button.action = @selector(handleEvent:);
         objc_setAssociatedObject(button, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
+        
+        ui_cocoa_view_bind_action(obj->ctx, button, args->action);
     }
     
     UiLayout layout = UI_INIT_LAYOUT(args);
@@ -62,7 +64,7 @@ UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) {
 }
 
 
-static void togglebutton_eventdata(id button, UiVar *var, void **eventdata, int *value) {
+static void togglebutton_eventdata(id button, UiVar *var, void **eventdata, int *eventdatatype, int *value) {
     NSButton *btn = (NSButton*)button;
     NSControlStateValue state = btn.state;
     *value = (int)state;
@@ -135,7 +137,7 @@ UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) {
     return togglebutton_create(obj, args, NSButtonTypeSwitch);
 }
 
-static void switch_eventdata(id button, UiVar *var, void **eventdata, int *value) {
+static void switch_eventdata(id button, UiVar *var, void **eventdata, int *eventdatatype, int *value) {
     NSSwitch *btn = (NSSwitch*)button;
     NSControlStateValue state = btn.state;
     *value = (int)state;
@@ -205,10 +207,10 @@ void ui_switch_set(UiInteger *i, int64_t value) {
 
 @end
 
-static void radiobutton_eventdata(id button, UiVar *var, void **eventdata, int *value) {
+static void radiobutton_eventdata(id button, UiVar *var, void **eventdata, int *eventdatatype, int *value) {
     if(var) {
-        UiInteger *value = var->value;
-        NSMutableArray *buttons = (__bridge NSMutableArray*)value->obj;
+        UiInteger *i = var->value;
+        NSMutableArray *buttons = (__bridge NSMutableArray*)i->obj;
         for(UiRadioButton *b in buttons) {
             if(b != button) {
                 b.direct_state = YES;
index 415717c58babe86b57cfd3e1713a0660423afd2e..aa4d410eaf6050dbd292e2c3be271823b8a16d64 100644 (file)
@@ -47,3 +47,15 @@ void  ui_textarea_remove(UiText *text, int begin, int end);
 
 char* ui_textfield_get(UiString *s);
 void ui_textfield_set(UiString *s, const char *value);
+
+@interface TextFieldDelegate : NSObject<NSTextFieldDelegate>
+
+@property UiObject *obj;
+@property UiVar *var;
+@property ui_callback onchange;
+@property void *onchangedata;
+@property NSString *onchange_action;
+
+- (id)init:(UiObject*)obj var:(UiVar*)var;
+
+@end
index b01a07bc0e3daab5c1226d1a2e4ca3fc6746cb40..00a0877ded92a6ac3a1f79cc1db9037bdc784284 100644 (file)
@@ -29,6 +29,7 @@
 #import "text.h"
 #import "EventData.h"
 #import "container.h"
+#import "action.h"
 #import <objc/runtime.h>
 
 UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) {
@@ -186,6 +187,10 @@ void ui_textarea_remove(UiText *text, int begin, int end) {
 
 /* -------------------------- TextField -------------------------- */
 
+static void textfield_geteventdata(id sender, UiVar *var, void **eventdata, int *eventdatatype, int *value) {
+    
+}
+
 static UIWIDGET textfield_create(UiObject *obj, UiTextFieldArgs *args, BOOL password, BOOL frameless) {
     NSTextField *textfield;
     if(password) {
@@ -222,6 +227,27 @@ static UIWIDGET textfield_create(UiObject *obj, UiTextFieldArgs *args, BOOL pass
         s->set = ui_textfield_set;
     }
     
+    if(args->onactivate || args->onactivate_action) {
+        EventData *event = [[EventData alloc] init:args->onactivate userdata:args->onactivatedata action:args->onactivate_action];
+        event.get_eventdata = textfield_geteventdata;
+        event.obj = obj;
+        textfield.target = event;
+        textfield.action = @selector(handleEventWithEventData:);
+        objc_setAssociatedObject(textfield, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
+        ui_cocoa_view_bind_action(obj->ctx, textfield, args->onactivate_action);
+    }
+    
+    if(args->onchange || args->onchange_action) {
+        TextFieldDelegate *tfd = [[TextFieldDelegate alloc]init:obj var:var];
+        tfd.onchange = args->onchange;
+        tfd.onchangedata = args->onchangedata;
+        if(args->onchange_action) {
+            tfd.onchange_action = [[NSString alloc]initWithUTF8String:args->onchange_action];
+        }
+        objc_setAssociatedObject(textfield, "delegate", tfd, OBJC_ASSOCIATION_RETAIN);
+        textfield.delegate = tfd;
+    }
+    
     return (__bridge void*)textfield;
 }
 
@@ -306,3 +332,41 @@ int ui_textfield_get_position(UIWIDGET textfield) {
     NSRange selectedRange = [editor selectedRange];
     return (int)selectedRange.location;
 }
+
+
+/* -------------------- textfield delegate -------------------- */
+
+@implementation TextFieldDelegate
+
+- (id)init:(UiObject*)obj var:(UiVar*)var {
+    self.obj = obj;
+    self.var = var;
+    return self;
+}
+
+- (void)controlTextDidChange:(NSNotification *)obj {
+    UiString *value = _var ? _var->value : NULL;
+    
+    UiEvent e;
+    e.obj = _obj;
+    e.window = e.obj->window;
+    e.document = e.obj->ctx->document;
+    e.eventdata = value;
+    e.eventdatatype = value ? UI_EVENT_DATA_STRING_VALUE : 0;
+    e.intval = 0;
+    e.set = ui_get_setop();
+    
+    if(_onchange) {
+        _onchange(&e, _onchangedata);
+    }
+    
+    if(_var) {
+        ui_notify_evt(value->observers, &e);
+    }
+    
+    if(_onchange_action) {
+        uic_action_callback(&e, _onchange_action.UTF8String);
+    }
+}
+
+@end
index c4b51cefbaa2170b7737360eb0516138fcb8028e..aeb8586c7309035064297976ab0fc70198f811a8 100644 (file)
@@ -100,8 +100,9 @@ void ui_notify_evt(UiObserver *observer, UiEvent *event) {
 /* --------------------------- UiList --------------------------- */
 
 void uic_ucx_list_init(UiContext *ctx, UiList *list, void *unused) {
+    const CxAllocator *a = ctx ? ctx->allocator : cxDefaultAllocator;
     list->destroy = uic_ucx_list_destroy;
-    list->data = cxArrayListCreate(ctx->mp->allocator, CX_STORE_POINTERS, 32);
+    list->data = cxArrayListCreate(a, CX_STORE_POINTERS, 32);
     list->first = ui_list_first;
     list->next = ui_list_next;
     list->get = ui_list_get;
@@ -170,6 +171,10 @@ void ui_list_prepend(UiList *list, void *data) {
     cxListInsert(list->data, 0, data);
 }
 
+void ui_list_insert(UiList *list, int index, void *data) {
+    cxListInsert(list->data, (size_t)index, data);
+}
+
 void ui_list_remove(UiList *list, int i) {
     if(i >= 0) {
         cxListRemove(list->data, i);
@@ -394,7 +399,7 @@ void ui_model_free(UiModel *mi) {
 UiInteger* ui_int_new(UiContext *ctx, const char *name) {
     UiInteger *i = ui_malloc(ctx, sizeof(UiInteger));
     memset(i, 0, sizeof(UiInteger));
-    if(name) {
+    if(name && ctx) {
         uic_reg_var(ctx, name, UI_VAR_INTEGER, i);
     }
     return i;
@@ -403,7 +408,7 @@ UiInteger* ui_int_new(UiContext *ctx, const char *name) {
 UiDouble* ui_double_new(UiContext *ctx, const char *name) {
     UiDouble *d = ui_malloc(ctx, sizeof(UiDouble));
     memset(d, 0, sizeof(UiDouble));
-    if(name) {
+    if(name && ctx) {
         uic_reg_var(ctx, name, UI_VAR_DOUBLE, d);
     }
     return d;
@@ -419,7 +424,7 @@ UiString* ui_string_new(UiContext *ctx, const char *name) {
     UiString *s = ui_malloc(ctx, sizeof(UiString));
     memset(s, 0, sizeof(UiString));
     ui_set_destructor(s, (ui_destructor_func)string_destroy);
-    if(name) {
+    if(name && ctx) {
         uic_reg_var(ctx, name, UI_VAR_STRING, s);
     }
     return s;
@@ -435,7 +440,7 @@ UiText* ui_text_new(UiContext *ctx, const char *name) {
     UiText *t = ui_malloc(ctx, sizeof(UiText));
     memset(t, 0, sizeof(UiText));
     ui_set_destructor(t, (ui_destructor_func)text_destroy);
-    if(name) {
+    if(name && ctx) {
         uic_reg_var(ctx, name, UI_VAR_TEXT, t);
     }
     return t;
@@ -444,7 +449,7 @@ UiText* ui_text_new(UiContext *ctx, const char *name) {
 UiRange* ui_range_new(UiContext *ctx, const char *name) {
     UiRange *r = ui_malloc(ctx, sizeof(UiRange));
     memset(r, 0, sizeof(UiRange));
-    if(name) {
+    if(name && ctx) {
         uic_reg_var(ctx, name, UI_VAR_RANGE, r);
     }
     return r;
@@ -459,8 +464,10 @@ static void generic_destroy(UiGeneric *g) {
 UiGeneric* ui_generic_new(UiContext *ctx, const char *name) {
     UiGeneric *g = ui_malloc(ctx, sizeof(UiGeneric));
     memset(g, 0, sizeof(UiGeneric));
-    ui_set_destructor(g, (ui_destructor_func)generic_destroy);
-    if(name) {
+    if(ctx) {
+        ui_set_destructor(g, (ui_destructor_func)generic_destroy);
+    }
+    if(name && ctx) {
         uic_reg_var(ctx, name, UI_VAR_GENERIC, g);
     }
     return g;
index e9dc703e529318c422f9fdec6cad82ce916e3130..e3929da123e38d0982611f6234900b28d4fd62bb 100644 (file)
@@ -129,6 +129,7 @@ UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) {
     uitext->last_selection_state = 0;
     uitext->onchange = args->onchange;
     uitext->onchangedata = args->onchangedata;
+    uitext->action = args->action ? strdup(args->action) : NULL;
     
     g_object_set_data(G_OBJECT(text_area), "ui_textarea", uitext);
     g_object_set_data(G_OBJECT(text_area), "ui_textarea_widget", text_area);
@@ -212,6 +213,7 @@ void ui_textarea_destroy(GtkWidget *object, UiTextArea *textarea) {
     if(textarea->var) {
         ui_destroy_boundvar(textarea->ctx, textarea->var);
     }
+    free(textarea->action);
     free(textarea);
 }
 
@@ -394,6 +396,10 @@ void ui_textbuf_changed(GtkTextBuffer *textbuffer, UiTextArea *textarea) {
     if(value->observers) {
         ui_notify_evt(value->observers, &e);
     }
+    
+    if(textarea->action) {
+        uic_action_callback(&e, textarea->action);
+    }
 }
 
 // undo manager functions
@@ -660,6 +666,8 @@ static UIWIDGET create_textfield(UiObject *obj, UiBool frameless, UiBool passwor
     uitext->onchangedata = args->onchangedata;
     uitext->onactivate = args->onactivate;
     uitext->onactivatedata = args->onactivatedata;
+    uitext->onactivate_action = args->onactivate_action ? strdup(args->onactivate_action) : NULL;
+    uitext->onchange_action = args->onchange_action ? strdup(args->onchange_action) : NULL;
     
     g_signal_connect(
                 textfield,
@@ -703,7 +711,7 @@ static UIWIDGET create_textfield(UiObject *obj, UiBool frameless, UiBool passwor
         value->obj = GTK_ENTRY(textfield);
     }
     
-    if(args->onchange || var) {
+    if(args->onchange || args->onchange_action || var) {
         g_signal_connect(
                 textfield,
                 "changed",
@@ -711,7 +719,7 @@ static UIWIDGET create_textfield(UiObject *obj, UiBool frameless, UiBool passwor
                 uitext);
     }
     
-    if(args->onactivate) {
+    if(args->onactivate || args->onactivate_action) {
         g_signal_connect(
                 textfield,
                 "activate",
@@ -771,6 +779,8 @@ int ui_textfield_get_position(UIWIDGET textfield) {
 }
 
 void ui_textfield_destroy(GtkWidget *object, UiTextField *textfield) {
+    free(textfield->onactivate_action);
+    free(textfield->onchange_action);
     free(textfield);
 }
 
@@ -786,7 +796,7 @@ void ui_textfield_changed(GtkEditable *editable, UiTextField *textfield) {
     e.window = e.obj->window;
     e.document = textfield->obj->ctx->document;
     e.eventdata = value;
-    e.eventdatatype = value ? UI_EVENT_DATA_TEXT_VALUE : 0;
+    e.eventdatatype = value ? UI_EVENT_DATA_STRING_VALUE : 0;
     e.intval = 0;
     e.set = ui_get_setop();
     
@@ -797,22 +807,31 @@ void ui_textfield_changed(GtkEditable *editable, UiTextField *textfield) {
     if(textfield->var) {
         ui_notify_evt(value->observers, &e);
     }
+    
+    if(textfield->onchange_action) {
+        uic_action_callback(&e, textfield->onchange_action);
+    }
 }
 
 void ui_textfield_activate(GtkEntry* self, UiTextField *textfield) {
-    if(textfield->onactivate) {
-        UiString *value = textfield->var ? textfield->var->value : NULL;
+    UiString *value = textfield->var ? textfield->var->value : NULL;
         
-        UiEvent e;
-        e.obj = textfield->obj;
-        e.window = e.obj->window;
-        e.document = textfield->obj->ctx->document;
-        e.eventdata = value;
-        e.eventdatatype = value ? UI_EVENT_DATA_TEXT_VALUE : 0;
-        e.intval = 0;
-        e.set = ui_get_setop();
+    UiEvent e;
+    e.obj = textfield->obj;
+    e.window = e.obj->window;
+    e.document = textfield->obj->ctx->document;
+    e.eventdata = value;
+    e.eventdatatype = value ? UI_EVENT_DATA_TEXT_VALUE : 0;
+    e.intval = 0;
+    e.set = ui_get_setop();
+    
+    if(textfield->onactivate) {
         textfield->onactivate(&e, textfield->onactivatedata);
     }
+    
+    if(textfield->onactivate_action) {
+        uic_action_callback(&e, textfield->onactivate_action);
+    }
 }
 
 char* ui_textfield_get(UiString *str) {
index 4d306aa5026cadf9ffb9c5d3675a5974f9742e61..057aef825759fca6a23c130b8a95a8045d9911ab 100644 (file)
@@ -67,6 +67,7 @@ typedef struct UiTextArea {
     int         last_selection_state;
     ui_callback onchange;
     void        *onchangedata;
+    char        *action;
 } UiTextArea;
 
 typedef struct UiTextField {
@@ -76,6 +77,8 @@ typedef struct UiTextField {
     void        *onchangedata;
     ui_callback onactivate;
     void        *onactivatedata;
+    char        *onactivate_action;
+    char        *onchange_action;
 } UiTextField;
 
 typedef struct UiPathTextField {
index 77234b92881864f9cf8b63ba53a69fc043b14fce..d76f7a55b1af7a0f6f120153a28fbb4f795fd93f 100644 (file)
@@ -694,6 +694,7 @@ UIEXPORT void* ui_list_get(UiList *list, int i);
 UIEXPORT int ui_list_count(UiList *list);
 UIEXPORT void ui_list_append(UiList *list, void *data);
 UIEXPORT void ui_list_prepend(UiList *list, void *data);
+UIEXPORT void ui_list_insert(UiList *list, int index, void *data);
 UIEXPORT void ui_list_remove(UiList *list, int i);
 UIEXPORT void ui_list_clear(UiList *list);
 UIEXPORT void ui_list_update(UiList *list);