-use ui_rs::{ui, UiModel};
+use ui_rs::{action, ui, ui_actions, UiModel};
use ui_rs::ui::*;
fn main() {
struct TestData {
i: i32,
#[bind("list")]
- list: UiList<i32>
+ list: UiList<usize>
}
+#[ui_actions]
+impl TestData {
+ #[action]
+ pub fn my_action(&mut self, _event: &ActionEvent) {
+ println!("my action {}", self.i);
+ self.i += 1;
+ }
+}
fn create_window() {
let testdata = TestData::default();
println!("Button clicked: {}", e.data.i);
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::<i32>().fill(true).varname("list").model(&model).getvalue(|elm, _row, _col| {
proc-macro = true
[dependencies]
-syn = "2"
+syn = { version = "2", features = ["full"] }
quote = "1"
proc-macro2 = "1"
\ No newline at end of file
use proc_macro::TokenStream;
use quote::quote;
-use syn::{parse_macro_input, Data, DeriveInput, Expr, ExprLit, Lit, Meta};
+use syn::{parse_macro_input, Attribute, Data, DeriveInput, Expr, ExprLit, ImplItem, ItemImpl, Lit, Meta};
#[proc_macro_derive(UiModel, attributes(bind))]
pub fn derive_ui_model(input: TokenStream) -> TokenStream {
//eprintln!("{}", expanded);
TokenStream::from(expanded)
+}
+
+/// Derives a no-op implementation of [`UiActions`].
+///
+/// This macro generates an empty implementation of the [`UiActions`] trait
+/// for the annotated type. It is intended for use with UI models that do not
+/// define any actions via [`ui_actions`], but still need to satisfy trait
+/// bounds such as `T: UiModel + UiActions`.
+///
+/// If you define actions using the [`ui_actions`] attribute macro, you should
+/// **not** also derive `UiActions`, as both macros generate an implementation
+/// of the same trait and will conflict.
+///
+/// # Generated code
+///
+/// This derive expands to:
+///
+/// ```ignore
+/// impl UiActions for YourType {}
+/// ```
+///
+/// # When to use
+///
+/// Use `#[derive(UiActions)]` when:
+///
+/// - Your type implements [`UiModel`]
+/// - You do **not** define any actions using `#[ui_actions]`
+/// - You still need to satisfy a `UiActions` trait bound
+///
+/// # See also
+///
+/// - [`ui_actions`] — Defines UI actions for a type and generates a non-empty
+/// implementation of [`UiActions`].
+#[proc_macro_derive(UiActions)]
+pub fn derive_ui_actions(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+ let name = input.ident;
+
+ let expanded = quote! {
+ impl UiActions for #name {
+
+ }
+ };
+
+ TokenStream::from(expanded)
+}
+
+/// Generates an implementation of [`UiActions`] for an `impl` block containing
+/// methods marked with the `#[action]` attribute.
+///
+/// This macro scans the provided `impl` block, collects all methods annotated
+/// with `#[action]`, and generates a corresponding implementation of
+/// [`UiActions`] that registers these methods as UI actions in the provided
+/// [`UiContext`].
+///
+/// The original `impl` block is preserved and emitted unchanged.
+///
+/// # Requirements
+///
+/// - Must be applied to an `impl` block
+/// - Methods intended as actions must be annotated with `#[action]`
+/// - The type must be compatible with [`UiActions`]
+///
+/// # Example
+///
+/// ```
+/// #[ui_actions]
+/// impl TestData {
+/// #[action]
+/// pub fn my_action(&mut self, event: &ActionEvent) {
+/// println!("action triggered");
+/// }
+/// }
+/// ```
+///
+/// # Notes
+///
+/// - Only methods marked with `#[action]` are registered
+/// - Methods without `#[action]` are ignored
+/// - Action names are derived from method names via `stringify!`
+///
+/// # See also
+///
+/// - [`UiActions`]
+/// - [`UiContext`]
+/// - `#[action]` attribute macro
+#[proc_macro_attribute]
+pub fn ui_actions(_attr: TokenStream, item: TokenStream) -> TokenStream {
+ let impl_block = parse_macro_input!(item as ItemImpl);
+ let self_ty = &impl_block.self_ty;
+
+ let mut 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());
+ }
+ }
+ }
+
+ let expanded = quote! {
+ #impl_block
+ impl UiActions for #self_ty {
+ fn init_actions(&mut self, ctx: &mut UiContext) {
+ unsafe {
+ #(
+ ctx.add_action(
+ stringify!(#handlers),
+ |target: &mut Self, event| {
+ target.#handlers(event);
+ }
+ );
+ )*
+ }
+ }
+ }
+ };
+ //eprintln!("{}", expanded);
+ TokenStream::from(expanded)
+}
+
+fn has_handler_attr(attrs: &[Attribute]) -> bool {
+ attrs.iter().any(|attr| attr.path().is_ident("action"))
+}
+
+#[proc_macro_attribute]
+pub fn action(_attr: TokenStream, item: TokenStream) -> TokenStream {
+ item
}
\ No newline at end of file
#![allow(dead_code)]
use std::ffi::c_void;
-use crate::ui::{ffi, UiDouble, UiInteger, UiString, UiText};
+use crate::ui::{event, ffi, UiDouble, UiInteger, UiString, UiText};
use crate::ui::ffi::{UiEvent, UiRange};
pub struct Event<'a, T> {
pub set: bool
}
+pub struct ActionEvent<'a> {
+ pub event_type: EventType<'a>,
+ pub intval: i32,
+ pub set: bool
+}
+
pub enum EventType<'a> {
Null,
Pointer,
pub callback: Box<dyn FnMut(&mut Event<T>)>
}
+impl<T> EventWrapper<T> {
+ pub fn new<F>(f: F) -> EventWrapper<T>
+ where F: FnMut(&mut event::Event<T>) + 'static {
+ EventWrapper { callback: Box::new(f) }
+ }
+}
+
pub extern "C" fn event_wrapper<T>(e: *const ffi::UiEvent, data: *mut c_void) {
unsafe {
let wrapper = &mut *(data as *mut EventWrapper<T>);
}
}
+pub struct ActionEventWrapper<T> {
+ pub callback: Box<dyn FnMut(&mut T, &ActionEvent)>,
+ pub target: *mut T
+}
+
+pub extern "C" fn action_event_wrapper<T>(e: *const ffi::UiEvent, data: *mut c_void) {
+ unsafe {
+ let wrapper = &mut *(data as *mut ActionEventWrapper<T>);
+ let target = &mut *(wrapper.target as *mut T);
+
+ let ev_int = ui_event_get_int(e);
+ let ev_set = ui_event_get_set(e) != 0;
+
+ let event = ActionEvent {
+ event_type: EventType::Null,
+ intval: ev_int,
+ set: ev_set
+ };
+
+ (wrapper.callback)(target, &event);
+ }
+}
extern "C" {
fn ui_event_get_windowdata(event: *const UiEvent) -> *const c_void;
#![allow(dead_code)]
use std::ffi::{c_char, c_int, c_void, CStr, CString};
-use crate::ui::{ffi, ui_object_get_context, ui_reg_destructor};
+use crate::ui::{action_event_wrapper, event, ffi, ui_object_get_context, ui_object_get_windowdata, ui_reg_destructor, ActionEventWrapper, EventWrapper};
use std::marker::PhantomData;
use std::mem;
+use crate::ui::ffi::UiCallback;
pub struct UiContext {
pub ptr: *mut ffi::UiContext
ptr
}
- pub fn attach(&mut self, doc: &UiDoc) {
+ pub fn attach<T: UiModel + UiActions>(&mut self, doc: &UiDoc<T>) {
unsafe {
ui_attach_document(self.ptr, doc.ptr);
}
}
- pub fn detach(&mut self, doc: &UiDoc) {
+ pub fn detach<T: UiModel + UiActions>(&mut self, doc: &UiDoc<T>) {
unsafe {
ui_attach_document(self.ptr, doc.ptr);
}
ls.init(self, None);
ls
}
+
+ pub unsafe fn add_action<T, F>(&mut self, name: &str, f: F)
+ where F: FnMut(&mut T, &event::ActionEvent) + 'static {
+ // if this is a document context, use the document pointer as target
+ // otherwise it is a UiObject context, in that case we use the window_data as target
+ let doc_ptr = ui_context_document(self.ptr) as *mut T;
+ let target_ptr = if !doc_ptr.is_null() {
+ doc_ptr
+ } else {
+ let obj_ptr = ui_context_obj(self.ptr);
+ assert!(!obj_ptr.is_null());
+ let obj_wdata_ptr = ui_object_get_windowdata(obj_ptr);
+ assert!(!obj_wdata_ptr.is_null());
+ obj_wdata_ptr as *mut T
+ };
+ let wrapper = Box::new(ActionEventWrapper { callback: Box::new(f), target: target_ptr });
+ let ptr = self.reg_box(wrapper);
+ let cstr = CString::new(name).unwrap();
+ ui_add_action(self.ptr, cstr.as_ptr(), Some(action_event_wrapper::<T>), ptr as *mut c_void);
+ }
}
extern "C" fn destroy_boxed<T>(data: *mut c_void) {
}
}
-pub struct UiDoc {
+pub struct UiDoc<T: UiModel + UiActions> {
pub ctx: UiContext,
- ptr: *mut c_void
+ ptr: *mut c_void,
+ _marker: PhantomData<T>
}
-impl Clone for UiDoc {
+impl<T: UiModel + UiActions> Clone for UiDoc<T> {
fn clone(&self) -> Self {
unsafe {
ui_document_ref(self.ptr);
}
- UiDoc {
+ UiDoc::<T> {
ctx: self.ctx.clone(),
- ptr: self.ptr
+ ptr: self.ptr,
+ _marker: PhantomData
}
}
}
-impl Drop for UiDoc {
+impl<T: UiModel + UiActions> Drop for UiDoc<T> {
fn drop(&mut self) {
unsafe {
if !self.ptr.is_null() {
}
}
-impl UiDoc {
- pub fn new<T: UiModel>(mut data: T) -> UiDoc {
+impl<T: UiModel + UiActions> UiDoc<T> {
+ pub fn new(mut data: T) -> UiDoc<T> {
unsafe {
let doc = ui_document_new(mem::size_of::<*mut T>());
let mut ctx = UiContext { ptr: ui_document_context(doc) };
let data_ptr = ctx.reg_box(Box::new(data)); // returns *mut T
let doc_storage: *mut *mut T = doc.cast();
*doc_storage = data_ptr;
- UiDoc { ctx: ctx, ptr: doc }
+ UiDoc::<T> { ctx: ctx, ptr: doc, _marker: PhantomData }
+ }
+ }
+
+ pub unsafe fn get_data_ptr(&self) -> *mut T {
+ let ptr: *mut *mut T = self.ptr.cast();
+ *ptr
+ }
+
+ pub fn add_action<F>(&mut self, name: &str, f: F)
+ where F: FnMut(&mut T, &event::ActionEvent) + 'static {
+ unsafe {
+ let wrapper = Box::new(ActionEventWrapper { callback: Box::new(f), target: self.get_data_ptr() });
+ let ptr = self.ctx.reg_box(wrapper);
+ let cstr = CString::new(name).unwrap();
+ ui_add_action(self.ctx.ptr, cstr.as_ptr(), Some(action_event_wrapper::<T>), ptr as *mut c_void);
}
}
}
fn init(&mut self, ctx: &UiContext);
}
+pub trait UiActions {
+ fn init_actions(&mut self, _ctx: &mut UiContext) {}
+}
+
+
pub struct UiObject<T> {
pub ptr: *mut ffi::UiObject,
pub ctx: UiContext,
fn ui_document_context(doc: *mut c_void) -> *mut ffi::UiContext;
fn ui_attach_document(ctx: *mut ffi::UiContext, doc: *mut c_void);
fn ui_detach_document(ctx: *mut ffi::UiContext, doc: *mut c_void);
+ fn ui_context_document(ctx: *mut ffi::UiContext) -> *mut c_void;
+ fn ui_context_obj(ctx: *mut ffi::UiContext) -> *mut ffi::UiObject;
+
+ fn ui_add_action(ctx: *mut ffi::UiContext, name: *const c_char, callback: UiCallback, data: *mut c_void);
fn ui_int_new(ctx: *mut ffi::UiContext, name: *const c_char) -> *mut ffi::UiInteger;
fn ui_double_new(ctx: *mut ffi::UiContext, name: *const c_char) -> *mut ffi::UiDouble;
use std::ffi::{c_char, c_int, c_void};
use std::ffi::CString;
use std::marker::PhantomData;
-use crate::ui::{toolkit, UiModel};
+use crate::ui::{toolkit, UiActions, UiModel};
use crate::ui::ffi::{UiContext, UiDestructor, UiObject};
extern "C" {
Simple
}
-fn window_create<T: UiModel, F>(title: &str, kind: WindowType, mut data: T, create_ui: F) -> toolkit::UiObject<T>
+fn window_create<T: UiModel + UiActions, F>(title: &str, kind: WindowType, data: T, create_ui: F) -> toolkit::UiObject<T>
where F: FnOnce(&mut toolkit::UiObject<T>, &mut T) {
// create the window
let objptr = unsafe {
_data: PhantomData
};
- // init view model
- data.init(&obj.ctx);
-
- // call ui building closure
- create_ui(&mut obj, &mut data);
-
// store windowdata object in the UiObject
let window_data = Box::new(data);
let wdata_ptr = obj.ctx.reg_box(window_data);
unsafe {
ui_object_set_windowdata(objptr, wdata_ptr as *mut c_void);
}
+
+ let wdata = unsafe {&mut *(wdata_ptr) };
+
+ // init view model
+ wdata.init(&obj.ctx);
+ wdata.init_actions(&mut obj.ctx);
+
+ // call ui building closure
+ create_ui(&mut obj, wdata);
+
obj
}
-pub fn window<T: UiModel, F>(title: &str, data: T, create_ui: F) -> toolkit::UiObject<T>
+pub fn window<T: UiModel + UiActions, F>(title: &str, data: T, create_ui: F) -> toolkit::UiObject<T>
where F: FnOnce(&mut toolkit::UiObject<T>, &mut T)
{
window_create(title, WindowType::Standard, data, create_ui)
}
-pub fn sidebar_window<T: UiModel, F>(title: &str, data: T, create_ui: F) -> toolkit::UiObject<T>
+pub fn sidebar_window<T: UiModel + UiActions, F>(title: &str, data: T, create_ui: F) -> toolkit::UiObject<T>
where F: FnOnce(&mut toolkit::UiObject<T>, &mut T)
{
window_create(title, WindowType::Sidebar, data, create_ui)
}
-pub fn splitview_window<T: UiModel, F>(title: &str, sidebar: bool, data: T, create_ui: F) -> toolkit::UiObject<T>
+pub fn splitview_window<T: UiModel + UiActions, F>(title: &str, sidebar: bool, data: T, create_ui: F) -> toolkit::UiObject<T>
where F: FnOnce(&mut toolkit::UiObject<T>, &mut T)
{
window_create(title, WindowType::SplitView(sidebar), data, create_ui)
}
-pub fn simple_window<T: UiModel, F>(title: &str, data: T, create_ui: F) -> toolkit::UiObject<T>
+pub fn simple_window<T: UiModel + UiActions, F>(title: &str, data: T, create_ui: F) -> toolkit::UiObject<T>
where F: FnOnce(&mut toolkit::UiObject<T>, &mut T)
{
window_create(title, WindowType::Simple, data, create_ui)
UiContext *doc_ctx = ui_document_context(document);
doc_ctx->parent = ctx;
+ doc_ctx->ref++;
// if a document variable has the same name as a parent variable,
// move the bindings to the document
var_ctx = var_ctx->parent;
}
-
+
ui_update_action_bindings(ctx);
}
UiContext *docctx = ui_document_context(document);
uic_context_unbind_vars(docctx); // unbind all doc/subdoc vars from the parent
docctx->parent = NULL;
+ ui_document_unref(document);
ui_update_action_bindings(ctx);
}
return NULL;
}
}
+
+void* ui_context_document(UiContext *ctx) {
+ return ctx->self_doc;
+}
+
+UiObject* ui_context_obj(UiContext *ctx) {
+ return ctx->obj;
+}
newargs.tooltip = nl_strdup(args->tooltip);\r
newargs.onclick = args->onclick;\r
newargs.onclickdata = args->onclickdata;\r
+ newargs.action = nl_strdup(args->action);\r
newargs.states = uic_copy_states(args->states, ngroups);\r
newargs.visibility_states = uic_copy_states(args->visibility_states, nvstates);\r
return newargs;\r
newargs.varname = nl_strdup(args->varname);\r
newargs.onchange = args->onchange;\r
newargs.onchangedata = args->onchangedata;\r
+ newargs.action = nl_strdup(args->action);\r
newargs.states = uic_copy_states(args->states, ngroups);\r
newargs.visibility_states = uic_copy_states(args->visibility_states, nvstates);\r
return newargs;\r
#include "button.h"
#include "container.h"
+#include "widget.h"
#include <cx/allocator.h>
#include <cx/buffer.h>
#include <cx/json.h>
#include "../common/context.h"
#include "../common/object.h"
+#include "../common/action.h"
void ui_button_set_icon_name(GtkWidget *button, const char *icon) {
if(!icon) {
const char *tooltip,
ui_callback onclick,
void *userdata,
+ const char *action,
int event_value,
bool activate_event)
{
gtk_widget_set_tooltip_text(button, tooltip);
}
- if(onclick) {
+ if(onclick || action) {
UiEventData *event = malloc(sizeof(UiEventData));
event->obj = obj;
event->userdata = userdata;
event->callback = onclick;
event->value = event_value;
+ event->action = action ? strdup(action) : NULL;
event->customdata = NULL;
event->customint = 0;
g_signal_connect(
button,
"destroy",
- G_CALLBACK(ui_destroy_userdata),
+ G_CALLBACK(ui_destroy_eventdata),
event);
if(activate_event) {
g_signal_connect(
G_CALLBACK(ui_button_clicked),
event);
}
+
+ if(action) {
+ uic_bind_action(obj->ctx, action, button, (ui_enablefunc)ui_set_enabled);
+ UiAction *ui_action = uic_resolve_action(obj->ctx, action);
+ if(!ui_action) {
+ ui_set_enabled(button, FALSE);
+ }
+ }
}
return button;
}
UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args) {
- GtkWidget *button = ui_create_button(obj, args->label, args->icon, args->tooltip, args->onclick, args->onclickdata, 0, FALSE);
+ GtkWidget *button = ui_create_button(obj, args->label, args->icon, args->tooltip, args->onclick, args->onclickdata, args->action, 0, FALSE);
ui_set_name_and_style(button, args->name, args->style_class);
ui_set_widget_states(obj->ctx, button, args->states);
UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
e.eventdatatype = 0;
e.intval = event->value;
e.set = ui_get_setop();
- event->callback(&e, event->userdata);
+ if(event->callback) {
+ event->callback(&e, event->userdata);
+ }
+
+ if(event->action) {
+ uic_action_callback(&e, event->action);
+ }
}
void ui_button_set_label(UIWIDGET button, const char *label) {
const char *tooltip,
ui_callback onclick,
void *userdata,
+ const char *action,
int event_value,
bool activate_event);
UiObject *obj,
enum UiToolbarPos pos)
{
- GtkWidget *button = ui_create_button(obj, item->args.label, item->args.icon, item->args.tooltip, item->args.onclick, item->args.onclickdata, 0, FALSE);
+ GtkWidget *button = ui_create_button(obj, item->args.label, item->args.icon, item->args.tooltip, item->args.onclick, item->args.onclickdata, item->args.action, 0, FALSE);
ui_set_widget_states(obj->ctx, button, item->args.states);
ui_set_widget_visibility_states(obj->ctx, button, item->args.visibility_states);
WIDGET_ADD_CSS_CLASS(button, "flat");
}
if(args->lbutton1) {
- GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 1, args->default_button == 1);
+ GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, NULL, 1, args->default_button == 1);
gtk_header_bar_pack_start(GTK_HEADER_BAR(headerbar), button);
if(args->default_button == 1) {
WIDGET_ADD_CSS_CLASS(button, "suggested-action");
}
}
if(args->lbutton2) {
- GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 2, args->default_button == 2);
+ GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, NULL, 2, args->default_button == 2);
gtk_header_bar_pack_start(GTK_HEADER_BAR(headerbar), button);
if(args->default_button == 2) {
WIDGET_ADD_CSS_CLASS(button, "suggested-action");
}
if(args->rbutton4) {
- GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 4, args->default_button == 4);
+ GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, NULL, 4, args->default_button == 4);
gtk_header_bar_pack_end(GTK_HEADER_BAR(headerbar), button);
if(args->default_button == 4) {
WIDGET_ADD_CSS_CLASS(button, "suggested-action");
}
}
if(args->rbutton3) {
- GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 3, args->default_button == 3);
+ GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, NULL, 3, args->default_button == 3);
gtk_header_bar_pack_end(GTK_HEADER_BAR(headerbar), button);
if(args->default_button == 3) {
WIDGET_ADD_CSS_CLASS(button, "suggested-action");
gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE);
if(args->lbutton1) {
- GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 1, args->default_button == 1);
+ GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, NULL, 1, args->default_button == 1);
gtk_grid_attach(GTK_GRID(grid), button, 0, 0, 1, 1);
if(args->default_button == 1) {
WIDGET_ADD_CSS_CLASS(button, "suggested-action");
}
}
if(args->lbutton2) {
- GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 2, args->default_button == 2);
+ GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, NULL, 2, args->default_button == 2);
gtk_grid_attach(GTK_GRID(grid), button, 1, 0, 1, 1);
if(args->default_button == 2) {
WIDGET_ADD_CSS_CLASS(button, "suggested-action");
gtk_widget_set_hexpand(space, TRUE);
gtk_grid_attach(GTK_GRID(grid), space, 2, 0, 1, 1);
if(args->rbutton3) {
- GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 3, args->default_button == 3);
+ GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, NULL, 3, args->default_button == 3);
gtk_grid_attach(GTK_GRID(grid), button, 3, 0, 1, 1);
if(args->default_button == 3) {
WIDGET_ADD_CSS_CLASS(button, "suggested-action");
}
}
if(args->rbutton4) {
- GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 4, args->default_button == 4);
+ GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, NULL, 4, args->default_button == 4);
gtk_grid_attach(GTK_GRID(grid), button, 4, 0, 1, 1);
if(args->default_button == 4) {
WIDGET_ADD_CSS_CLASS(button, "suggested-action");
UIEXPORT void* ui_get_subdocument(void *document); // deprecated
UIEXPORT UiContext* ui_document_context(void *doc);
+UIEXPORT void* ui_context_document(UiContext *ctx);
+UIEXPORT UiObject* ui_context_obj(UiContext *ctx);
UIEXPORT void* ui_context_get_document(UiContext *ctx);
UIEXPORT void ui_context_single_attachment_mode(UiContext *ctx, UiBool enable);