From ba15d6fcebb2424b81111b2cfd49e13df173a0a5 Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Mon, 20 Apr 2026 21:01:07 +0200 Subject: [PATCH] add SourceListBuilder --- application/src/main.rs | 17 ++- ui-rs/src/ui/application.rs | 4 +- ui-rs/src/ui/ffi.rs | 20 +++ ui-rs/src/ui/list.rs | 258 +++++++++++++++++++++++++++++++++++- 4 files changed, 286 insertions(+), 13 deletions(-) diff --git a/application/src/main.rs b/application/src/main.rs index 77dc750..45fdf7c 100644 --- a/application/src/main.rs +++ b/application/src/main.rs @@ -14,7 +14,9 @@ struct App; struct TestData { i: i32, #[bind("list")] - list: UiList + list: UiList, + #[bind] + sublists: UiList> } #[ui_actions] @@ -40,18 +42,19 @@ fn create_window() { e.data.i += 1; }).create(); obj.button_builder().label("Action").action("my_action").create(); - let mut model = TableModel::new(); - model.add_column("Name", ColumnType::String, -1); - obj.tableview_builder::().fill(true).varname("list").model(&model).getvalue(|elm, _row, _col| { - ListValue::String(elm.to_string()) - }).create(); + //let mut model = TableModel::new(); + //model.add_column("Name", ColumnType::String, -1); + //obj.tableview_builder::().fill(true).varname("list").model(&model).getvalue(|elm, _row, _col| { + // ListValue::String(elm.to_string()) + //}).create(); + obj.sourcelist_builder::().fill(true).value(&data.sublists).create(); }); window.show(); } impl ui::Application for App { - fn on_startup(&self) { + fn on_startup(&mut self) { create_window(); } } diff --git a/ui-rs/src/ui/application.rs b/ui-rs/src/ui/application.rs index 275b3b8..a339842 100644 --- a/ui-rs/src/ui/application.rs +++ b/ui-rs/src/ui/application.rs @@ -4,9 +4,9 @@ use std::ffi::{c_char, c_int, c_void, CString}; use crate::ui::ffi::{UiCallback, UiEvent}; pub trait Application { - fn on_startup(&self); + fn on_startup(&mut self); - fn on_exit(&self) { } + fn on_exit(&mut self) { } } diff --git a/ui-rs/src/ui/ffi.rs b/ui-rs/src/ui/ffi.rs index 0bb02f2..5a19f5b 100644 --- a/ui-rs/src/ui/ffi.rs +++ b/ui-rs/src/ui/ffi.rs @@ -57,6 +57,16 @@ pub struct UiListSelection { _private: [u8; 0], } +#[repr(C)] +pub struct UiSubListItem { + _private: [u8; 0], +} + +#[repr(C)] +pub struct UiMenuBuilder { + _private: [u8; 0], +} + pub type UiCallback = Option; pub type UiDestructor = extern "C" fn(object: *mut c_void); @@ -126,7 +136,17 @@ pub struct UiListArgs { _private: [u8; 0], } +#[repr(C)] +pub struct UiSourceListArgs { + _private: [u8; 0], +} + #[repr(C)] pub struct UiModel { _private: [u8; 0], +} + +#[repr(C)] +pub struct UiSubList { + _private: [u8; 0], } \ No newline at end of file diff --git a/ui-rs/src/ui/list.rs b/ui-rs/src/ui/list.rs index b878bce..eee2c4c 100644 --- a/ui-rs/src/ui/list.rs +++ b/ui-rs/src/ui/list.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] -use crate::ui::ffi::{UiObject, UiListArgs, UiCallback, UiList}; -use crate::ui::{ffi, toolkit, ui_list_get, ui_list_get_data}; +use crate::ui::ffi::{UiObject, UiListArgs, UiCallback, UiList, UiSourceListArgs}; +use crate::ui::{event, ffi, toolkit, ui_list_get, ui_list_get_data, EventWrapper}; use std::ffi::{c_char, c_int, c_void}; use std::ffi::CString; use std::marker::PhantomData; @@ -32,8 +32,19 @@ pub struct TableViewBuilder<'a, T, E> { _marker: PhantomData } +pub struct SourceListBuilder<'a, T, E> { + args: *mut UiSourceListArgs, + obj: &'a mut toolkit::UiObject, + _marker: PhantomData +} + pub struct TableModel { - pub handle: *mut ffi::UiModel, + handle: *mut ffi::UiModel, +} + +pub struct SubList { + handle: *mut ffi::UiSubList, + list: toolkit::UiList } pub enum ColumnType { @@ -52,6 +63,7 @@ pub type ListBuilderCreate = fn(*const UiObject, *const UiListArgs); impl toolkit::UiObject { widget_typed_fn!(listview, listview_builder, ListViewBuilder); widget_typed_fn!(tableview, tableview_builder, TableViewBuilder); + widget_typed_fn!(sourcelist, sourcelist_builder, SourceListBuilder); pub fn listview_builder<'a, E>(&'a mut self) -> ListViewBuilder<'a, T, E> { unsafe { @@ -73,6 +85,13 @@ impl toolkit::UiObject { TableViewBuilder { args: args, obj: self, create: tableview_create, model: std::ptr::null_mut(), has_getvalue_func: false, _marker: PhantomData } } } + + pub fn sourcelist_builder<'a, E>(&'a mut self) -> SourceListBuilder<'a, T, E> { + unsafe { + let args = ui_sourcelist_args_new(); + SourceListBuilder { args: args, obj: self, _marker: PhantomData } + } + } } fn list_create(obj: *const UiObject, args: *const UiListArgs) { @@ -451,6 +470,189 @@ impl<'a, T, E> TableViewBuilder<'a, T, E> { } } +impl<'a, T, E> SourceListBuilder<'a, T, E> { + pub fn create(&mut self) { + unsafe { + ui_sourcelist_create(self.obj.ptr, self.args); + } + } + + pub fn fill(&mut self, fill: bool) -> &mut Self { + unsafe { + ui_sourcelist_args_set_fill(self.args, if fill { 1 } else { 0 }); + } + self + } + + pub fn hexpand(&mut self, value: bool) -> &mut Self { + unsafe { + ui_sourcelist_args_set_hexpand(self.args, if value { 1 } else { 0 }); + } + self + } + + pub fn vexpand(&mut self, value: bool) -> &mut Self { + unsafe { + ui_sourcelist_args_set_vexpand(self.args, if value { 1 } else { 0 }); + } + self + } + + pub fn hfill(&mut self, value: bool) -> &mut Self { + unsafe { + ui_sourcelist_args_set_hfill(self.args, if value { 1 } else { 0 }); + } + self + } + + pub fn vfill(&mut self, value: bool) -> &mut Self { + unsafe { + ui_sourcelist_args_set_vfill(self.args, if value { 1 } else { 0 }); + } + self + } + + pub fn override_defaults(&mut self, value: bool) -> &mut Self { + unsafe { + ui_sourcelist_args_set_override_defaults(self.args, if value { 1 } else { 0 }); + } + self + } + + pub fn margin(&mut self, value: i32) -> &mut Self { + unsafe { + ui_sourcelist_args_set_margin(self.args, value); + } + self + } + + pub fn margin_left(&mut self, value: i32) -> &mut Self { + unsafe { + ui_sourcelist_args_set_margin_left(self.args, value); + } + self + } + + pub fn margin_right(&mut self, value: i32) -> &mut Self { + unsafe { + ui_sourcelist_args_set_margin_right(self.args, value); + } + self + } + + pub fn margin_top(&mut self, value: i32) -> &mut Self { + unsafe { + ui_sourcelist_args_set_margin_top(self.args, value); + } + self + } + + pub fn margin_bottom(&mut self, value: i32) -> &mut Self { + unsafe { + ui_sourcelist_args_set_margin_bottom(self.args, value); + } + self + } + + pub fn colspan(&mut self, value: i32) -> &mut Self { + unsafe { + ui_sourcelist_args_set_colspan(self.args, value); + } + self + } + + pub fn rowspan(&mut self, value: i32) -> &mut Self { + unsafe { + ui_sourcelist_args_set_rowspan(self.args, value); + } + self + } + + pub fn name(&mut self, value: &str) -> &mut Self { + let cstr = CString::new(value).unwrap(); + unsafe { + ui_sourcelist_args_set_name(self.args, cstr.as_ptr()); + } + self + } + + pub fn style_class(&mut self, value: &str) -> &mut Self { + let cstr = CString::new(value).unwrap(); + unsafe { + ui_sourcelist_args_set_style_class(self.args, cstr.as_ptr()); + } + self + } + + pub fn varname(&mut self, varname: &str) -> &mut Self { + let cstr = CString::new(varname).unwrap(); + unsafe { + ui_sourcelist_args_set_varname(self.args, cstr.as_ptr()); + } + self + } + pub fn value(&mut self, value: &toolkit::UiList>) -> &mut Self { + unsafe { + ui_sourcelist_args_set_dynamic_sublists(self.args, value.ptr); + } + self + } + + pub fn getvalue(&mut self, f: F) -> &mut Self + where F: Fn(&E, i32, i32) -> ListValue<'a> + 'static { + unsafe { + let wrapper = Box::new(GetValueWrapper { callback: Box::new(f), is_table: false }); + let ptr = self.obj.ctx.reg_box(wrapper); + ui_sourcelist_args_set_getvalue_func(self.args, sourcelist_getvalue_wrapper::); + ui_sourcelist_args_set_getvalue_userdata(self.args, ptr as *mut c_void); + } + self + } + + pub fn header_is_item(&mut self, value: bool) -> &mut Self { + unsafe { + ui_sourcelist_args_set_header_is_item(self.args, if value { 1 } else { 0 }); + } + self + } + + pub fn onactivate(&mut self, f: F) -> &mut Self + where F: FnMut(&mut event::Event) + 'static { + let wrapper = Box::new(EventWrapper { callback: Box::new(f) }); + let ptr = self.obj.ctx.reg_box(wrapper); + unsafe { + ui_sourcelist_args_set_onactivate(self.args, Some(event::event_wrapper::)); + ui_sourcelist_args_set_onactivatedata(self.args, ptr as *mut c_void); + } + self + } + + pub fn onbuttonclick(&mut self, f: F) -> &mut Self + where F: FnMut(&mut event::Event) + 'static { + let wrapper = Box::new(EventWrapper { callback: Box::new(f) }); + let ptr = self.obj.ctx.reg_box(wrapper); + unsafe { + ui_sourcelist_args_set_onbuttonclick(self.args, Some(event::event_wrapper::)); + ui_sourcelist_args_set_onbuttonclickdata(self.args, ptr as *mut c_void); + } + self + } + + pub fn visibility_states(&mut self, states: &[i32]) -> &mut Self { + unsafe { + ui_sourcelist_args_set_visibility_states(self.args, states.as_ptr(), states.len() as c_int); + } + self + } + + pub fn states(&mut self, states: &[i32]) -> &mut Self { + unsafe { + ui_sourcelist_args_set_states(self.args, states.as_ptr(), states.len() as c_int); + } + self + } +} + // TableModel implementation @@ -531,6 +733,25 @@ pub extern "C" fn null_getvalue_wrapper(_list: *const ffi::UiList, _elm_ptr: *co std::ptr::null_mut() } +pub extern "C" fn sourcelist_getvalue_wrapper( + _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) +{ + // TODO +} + +type SLGetValueFunc = extern "C" fn( + 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); + extern "C" { fn malloc(size: usize) -> *mut c_void; @@ -538,6 +759,7 @@ extern "C" { fn ui_listview_create(obj: *const UiObject, args: *const UiListArgs); fn ui_dropdown_create(obj: *const UiObject, args: *const UiListArgs); fn ui_table_create(obj: *const UiObject, args: *const UiListArgs); + fn ui_sourcelist_create(obj: *const UiObject, args: *const UiSourceListArgs); fn ui_list_args_new() -> *mut UiListArgs; fn ui_list_args_set_fill(args: *mut UiListArgs, fill: c_int); @@ -562,7 +784,6 @@ extern "C" { fn ui_list_args_set_model(args: *mut UiListArgs, model: *mut ffi::UiModel); fn ui_list_args_set_getvalue_func2(args: *mut UiListArgs, func: GetValueFunc); fn ui_list_args_set_getvalue_data(args: *mut UiListArgs, data: *mut c_void); - fn ui_list_args_set_enablestate(args: *mut UiListArgs, state: c_int); fn ui_list_args_set_states(args: *mut UiListArgs, states: *const c_int, numstates: c_int); fn ui_list_args_set_visibility_states(args: *mut UiListArgs, states: *const c_int, numstates: c_int); fn ui_list_args_free(args: *mut UiListArgs); @@ -572,4 +793,33 @@ extern "C" { fn ui_model_ref(model: *mut ffi::UiModel); fn ui_model_unref(model: *mut ffi::UiModel); + fn ui_sourcelist_args_new() -> *mut UiSourceListArgs; + fn ui_sourcelist_args_set_fill(args: *mut UiSourceListArgs, fill: c_int); + fn ui_sourcelist_args_set_hexpand(args: *mut UiSourceListArgs, value: c_int); + fn ui_sourcelist_args_set_vexpand(args: *mut UiSourceListArgs, value: c_int); + fn ui_sourcelist_args_set_hfill(args: *mut UiSourceListArgs, value: c_int); + fn ui_sourcelist_args_set_vfill(args: *mut UiSourceListArgs, value: c_int); + fn ui_sourcelist_args_set_override_defaults(args: *mut UiSourceListArgs, value: c_int); + fn ui_sourcelist_args_set_margin(args: *mut UiSourceListArgs, value: c_int); + fn ui_sourcelist_args_set_margin_left(args: *mut UiSourceListArgs, value: c_int); + fn ui_sourcelist_args_set_margin_right(args: *mut UiSourceListArgs, value: c_int); + fn ui_sourcelist_args_set_margin_top(args: *mut UiSourceListArgs, value: c_int); + fn ui_sourcelist_args_set_margin_bottom(args: *mut UiSourceListArgs, value: c_int); + fn ui_sourcelist_args_set_colspan(args: *mut UiSourceListArgs, value: c_int); + fn ui_sourcelist_args_set_rowspan(args: *mut UiSourceListArgs, value: c_int); + fn ui_sourcelist_args_set_name(args: *mut UiSourceListArgs, name: *const c_char); + fn ui_sourcelist_args_set_style_class(args: *mut UiSourceListArgs, classname: *const c_char); + fn ui_sourcelist_args_set_varname(args: *mut UiSourceListArgs, varname: *const c_char); + fn ui_sourcelist_args_set_dynamic_sublists(args: *mut UiSourceListArgs, ivalue: *mut UiList); + fn ui_sourcelist_args_set_getvalue_func(args: *mut UiSourceListArgs, func: SLGetValueFunc); + fn ui_sourcelist_args_set_getvalue_userdata(args: *mut UiSourceListArgs, data: *mut c_void); + fn ui_sourcelist_args_set_onactivate(args: *mut UiSourceListArgs, callback: UiCallback); + fn ui_sourcelist_args_set_onactivatedata(args: *mut UiSourceListArgs, data: *mut c_void); + fn ui_sourcelist_args_set_onbuttonclick(args: *mut UiSourceListArgs, allback: UiCallback); + fn ui_sourcelist_args_set_onbuttonclickdata(args: *mut UiSourceListArgs, data: *mut c_void); + fn ui_sourcelist_args_set_contextmenu(args: *mut UiSourceListArgs, menu: *mut ffi::UiMenuBuilder); + fn ui_sourcelist_args_set_header_is_item(args: *mut UiSourceListArgs, value: c_int); + fn ui_sourcelist_args_set_states(args: *mut UiSourceListArgs, states: *const c_int, numstates: c_int); + fn ui_sourcelist_args_set_visibility_states(args: *mut UiSourceListArgs, states: *const c_int, numstates: c_int); + fn ui_sourcelist_args_free(args: *mut UiSourceListArgs); } \ No newline at end of file -- 2.47.3