From 5f52717a7ddbe54b400b10a0fe2a60db958e02bb Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Tue, 14 Apr 2026 19:38:41 +0200 Subject: [PATCH] add listview --- application/src/main.rs | 9 +- ui-rs/src/ui/ffi.rs | 5 + ui-rs/src/ui/list.rs | 275 ++++++++++++++++++++++++++++++++++++++++ ui-rs/src/ui/mod.rs | 3 +- ui-rs/src/ui/widget.rs | 16 ++- 5 files changed, 304 insertions(+), 4 deletions(-) diff --git a/application/src/main.rs b/application/src/main.rs index 9cca73e..fabbe19 100644 --- a/application/src/main.rs +++ b/application/src/main.rs @@ -1,5 +1,5 @@ use ui_rs::ui; -use ui_rs::ui::{UiContext, UiList}; +use ui_rs::ui::{ListValue, UiContext, UiList}; fn main() { ui::app_init("note"); @@ -25,12 +25,17 @@ fn create_window() { v.push(11); v.push(12); - data.list = Some(list); + obj.button_builder().label("Hello").onclick(|e| { println!("Button clicked: {}", e.data.i); e.data.i += 1; }).create(); + obj.listview_builder::().fill(true).value(&list).getvalue(|elm, _row, _col| { + ListValue::String(elm.to_string()) + }).create(); + + data.list = Some(list); }); diff --git a/ui-rs/src/ui/ffi.rs b/ui-rs/src/ui/ffi.rs index 8e2da06..99eba46 100644 --- a/ui-rs/src/ui/ffi.rs +++ b/ui-rs/src/ui/ffi.rs @@ -111,3 +111,8 @@ pub struct UiTextFieldArgs { _private: [u8; 0], } +#[repr(C)] +pub struct UiListArgs { + _private: [u8; 0], +} + diff --git a/ui-rs/src/ui/list.rs b/ui-rs/src/ui/list.rs index 3b768e4..045fef9 100644 --- a/ui-rs/src/ui/list.rs +++ b/ui-rs/src/ui/list.rs @@ -1,2 +1,277 @@ #![allow(dead_code)] +use crate::ui::ffi::{UiObject, UiListArgs, UiCallback, UiList}; +use crate::ui::{ffi, toolkit}; +use std::ffi::{c_char, c_int, c_void}; +use std::ffi::CString; +use std::marker::PhantomData; +use crate::ui::widget::{widget_typed_fn}; + +pub enum ListValue<'a> { + None, + Str(&'a str), + String(String) +} + +pub struct ListBuilder<'a, T, E> { + args: *mut UiListArgs, + obj: &'a mut toolkit::UiObject, + create: ListBuilderCreate, + has_getvalue_func: bool, + _marker: PhantomData +} + +pub type ListBuilderCreate = fn(*const UiObject, *const UiListArgs); + +impl toolkit::UiObject { + widget_typed_fn!(listview, listview_builder, ListBuilder); + pub fn listview_builder<'a, E>(&'a mut self) -> ListBuilder<'a, T, E> { + unsafe { + let args = ui_list_args_new(); + ListBuilder { args: args, obj: self, create: list_create, has_getvalue_func: false, _marker: PhantomData } + } + } +} + +fn list_create(obj: *const UiObject, args: *const UiListArgs) { + unsafe { + ui_listview_create(obj, args); + } +} + +impl<'a, T, E> Drop for ListBuilder<'a, T, E> { + fn drop(&mut self) { + unsafe { + ui_list_args_free(self.args); + } + } +} + + +impl<'a, T, E> ListBuilder<'a, T, E> { + pub fn create(&mut self) { + if !self.has_getvalue_func { + unsafe { + ui_list_args_set_getvalue_func2(self.args, null_getvalue_wrapper); + } + } + (self.create)(self.obj.ptr, self.args); + } + + pub fn fill(&mut self, fill: bool) -> &mut Self { + unsafe { + ui_list_args_set_fill(self.args, if fill { 1 } else { 0 }); + } + self + } + + pub fn hexpand(&mut self, value: bool) -> &mut Self { + unsafe { + ui_list_args_set_hexpand(self.args, if value { 1 } else { 0 }); + } + self + } + + pub fn vexpand(&mut self, value: bool) -> &mut Self { + unsafe { + ui_list_args_set_vexpand(self.args, if value { 1 } else { 0 }); + } + self + } + + pub fn hfill(&mut self, value: bool) -> &mut Self { + unsafe { + ui_list_args_set_hfill(self.args, if value { 1 } else { 0 }); + } + self + } + + pub fn vfill(&mut self, value: bool) -> &mut Self { + unsafe { + ui_list_args_set_vfill(self.args, if value { 1 } else { 0 }); + } + self + } + + pub fn override_defaults(&mut self, value: bool) -> &mut Self { + unsafe { + ui_list_args_set_override_defaults(self.args, if value { 1 } else { 0 }); + } + self + } + + pub fn margin(&mut self, value: i32) -> &mut Self { + unsafe { + ui_list_args_set_margin(self.args, value); + } + self + } + + pub fn margin_left(&mut self, value: i32) -> &mut Self { + unsafe { + ui_list_args_set_margin_left(self.args, value); + } + self + } + + pub fn margin_right(&mut self, value: i32) -> &mut Self { + unsafe { + ui_list_args_set_margin_right(self.args, value); + } + self + } + + pub fn margin_top(&mut self, value: i32) -> &mut Self { + unsafe { + ui_list_args_set_margin_top(self.args, value); + } + self + } + + pub fn margin_bottom(&mut self, value: i32) -> &mut Self { + unsafe { + ui_list_args_set_margin_bottom(self.args, value); + } + self + } + + pub fn colspan(&mut self, value: i32) -> &mut Self { + unsafe { + ui_list_args_set_colspan(self.args, value); + } + self + } + + pub fn rowspan(&mut self, value: i32) -> &mut Self { + unsafe { + ui_list_args_set_rowspan(self.args, value); + } + self + } + + pub fn name(&mut self, value: &str) -> &mut Self { + let cstr = CString::new(value).unwrap(); + unsafe { + ui_list_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_list_args_set_style_class(self.args, cstr.as_ptr()); + } + self + } + + pub fn visibility_states(&mut self, states: &[i32]) -> &mut Self { + unsafe { + ui_list_args_set_visibility_states(self.args, states.as_ptr(), states.len() as c_int); + } + self + } + + pub fn value(&mut self, value: &toolkit::UiList) -> &mut Self { + unsafe { + ui_list_args_set_value(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) }); + let ptr = self.obj.reg_box(wrapper); + ui_list_args_set_getvalue_func2(self.args, getvalue_wrapper::); + ui_list_args_set_getvalue_data(self.args, ptr as *mut c_void); + } + self.has_getvalue_func = true; + self + } + + pub fn states(&mut self, states: &[i32]) -> &mut Self { + unsafe { + ui_list_args_set_states(self.args, states.as_ptr(), states.len() as c_int); + } + self + } +} + +type GetValueFunc = extern "C" fn(list: *const ffi::UiList, elm_ptr: *const c_void, row: i32, col: i32, wrapper: *const c_void, free: *mut bool) -> *mut c_void; + +pub struct GetValueWrapper<'a, T> { + pub callback: Box ListValue<'a>>, +} + + +fn str_dup(str: &str) -> *mut c_void { + let cstr = CString::new(str).unwrap(); + let bytes = cstr.as_bytes_with_nul(); + + unsafe { + let ptr = malloc(bytes.len()) as *mut c_char; + if ptr.is_null() { + return std::ptr::null_mut(); + } + + std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr as *mut u8, bytes.len()); + ptr as *mut c_void + } +} + +pub extern "C" fn getvalue_wrapper(_list: *const ffi::UiList, elm_ptr: *const c_void, row: i32, col: i32, wrapper_ptr: *const c_void, free: *mut bool) -> *mut c_void { + let wrapper = unsafe { &*(wrapper_ptr as *const GetValueWrapper ) }; + let elm = unsafe { &*(elm_ptr as *const T) }; + + unsafe { + *free = true; + } + + let result = (wrapper.callback)(elm, col, row); + match result { + ListValue::None => std::ptr::null_mut(), + ListValue::Str(s) => str_dup(s), + ListValue::String(s) => str_dup(s.as_str()) + } +} + +pub extern "C" fn null_getvalue_wrapper(_list: *const ffi::UiList, _elm_ptr: *const c_void, _row: i32, _col: i32, _wrapper: *const c_void, _free: *mut bool) -> *mut c_void { + std::ptr::null_mut() +} + + +extern "C" { + fn malloc(size: usize) -> *mut c_void; + + fn ui_listview_create(obj: *const UiObject, args: *const UiListArgs); + + fn ui_list_args_new() -> *mut UiListArgs; + fn ui_list_args_set_fill(args: *mut UiListArgs, fill: c_int); + fn ui_list_args_set_hexpand(args: *mut UiListArgs, value: c_int); + fn ui_list_args_set_vexpand(args: *mut UiListArgs, value: c_int); + fn ui_list_args_set_hfill(args: *mut UiListArgs, value: c_int); + fn ui_list_args_set_vfill(args: *mut UiListArgs, value: c_int); + fn ui_list_args_set_override_defaults(args: *mut UiListArgs, value: c_int); + fn ui_list_args_set_margin(args: *mut UiListArgs, value: c_int); + fn ui_list_args_set_margin_left(args: *mut UiListArgs, value: c_int); + fn ui_list_args_set_margin_right(args: *mut UiListArgs, value: c_int); + fn ui_list_args_set_margin_top(args: *mut UiListArgs, value: c_int); + fn ui_list_args_set_margin_bottom(args: *mut UiListArgs, value: c_int); + fn ui_list_args_set_colspan(args: *mut UiListArgs, value: c_int); + fn ui_list_args_set_rowspan(args: *mut UiListArgs, value: c_int); + fn ui_list_args_set_name(args: *mut UiListArgs, name: *const c_char); + fn ui_list_args_set_style_class(args: *mut UiListArgs, classname: *const c_char); + fn ui_list_args_set_onchange(args: *mut UiListArgs, callback: UiCallback); + fn ui_list_args_set_onchangedata(args: *mut UiListArgs, data: *mut c_void); + fn ui_list_args_set_varname(args: *mut UiListArgs, varname: *const c_char); + fn ui_list_args_set_value(args: *mut UiListArgs, ivalue: *mut UiList); + 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); + +} \ No newline at end of file diff --git a/ui-rs/src/ui/mod.rs b/ui-rs/src/ui/mod.rs index 8e0ce22..5451bac 100644 --- a/ui-rs/src/ui/mod.rs +++ b/ui-rs/src/ui/mod.rs @@ -7,7 +7,7 @@ mod container; mod widget; mod text; pub mod event; -mod list; +pub mod list; pub use toolkit::*; pub use event::*; @@ -15,3 +15,4 @@ pub use application::*; pub use window::*; pub use button::*; pub use text::*; +pub use list::*; diff --git a/ui-rs/src/ui/widget.rs b/ui-rs/src/ui/widget.rs index a35bd3d..4ae67dc 100644 --- a/ui-rs/src/ui/widget.rs +++ b/ui-rs/src/ui/widget.rs @@ -26,4 +26,18 @@ macro_rules! widget_fn { }; } -pub(super) use widget_fn; \ No newline at end of file +macro_rules! widget_typed_fn { + ($fn_name:ident, $builder_fn:ident, $builder_ty:ident) => { + pub fn $fn_name(&mut self, build: F) + where + F: Fn(&mut $builder_ty), + { + let mut builder = self.$builder_fn(); + build(&mut builder); + builder.create(); + } + }; +} + +pub(super) use widget_fn; +pub(super) use widget_typed_fn; \ No newline at end of file -- 2.47.3