From: Olaf Wintermann Date: Sun, 7 Jun 2026 11:53:07 +0000 (+0200) Subject: add support for multiple app windows X-Git-Url: https://uap-core.de/gitweb/?a=commitdiff_plain;h=115ee0741429962c2677cc33d56ca9e38d5e0ec1;p=note.git add support for multiple app windows --- diff --git a/application/src/main.rs b/application/src/main.rs index 97aa3dd..ef714dd 100644 --- a/application/src/main.rs +++ b/application/src/main.rs @@ -116,7 +116,10 @@ impl Application for App { //}); create_toolbar(app); + self.on_new_window(app); + } + fn on_new_window(&mut self, app: &AppContext) { create_window(self, app); } } @@ -127,10 +130,14 @@ fn create_toolbar(app: &AppContext) { app.toolbar_item("go_forward").icon(UiIconSet::GoForward.as_str()).action("go_forward").create(); app.toolbar_item("new_note").icon(UiIconSet::Add.as_str()).action("new_note").create(); - app.toolbar_add_default("new_notebook", ToolbarItemPosition::SidebarRight); + app.toolbar_add_default("new_notebook", ToolbarItemPosition::SidebarLeft); app.toolbar_add_default("go_back", ToolbarItemPosition::Left); app.toolbar_add_default("go_forward", ToolbarItemPosition::Left); app.toolbar_add_default("new_note", ToolbarItemPosition::Left); + + app.toolbar_appmenu(|menu|{ + menu.item("New Window").onclick(|f| new_app_window() ).create(); + }); } diff --git a/ui-rs/src/ui/application.rs b/ui-rs/src/ui/application.rs index df3aee0..8da78c9 100644 --- a/ui-rs/src/ui/application.rs +++ b/ui-rs/src/ui/application.rs @@ -32,11 +32,13 @@ use std::ffi::{c_char, c_int, c_void, CString}; use std::marker::PhantomData; use ui_rs_derive::UiModel; use crate::ui::ffi::{UiCallback, UiEvent}; -use crate::ui::{dialog, toolkit, ui_global_context, UiActions, UiContext, UiModel}; +use crate::ui::{call_mainthread, dialog, toolkit, ui_call_mainthread, ui_global_context, UiActions, UiContext, UiModel}; pub trait Application { fn on_startup(&mut self, app: &AppContext); + fn on_new_window(&mut self, app: &AppContext) {} + fn on_exit(&mut self, _app: &AppContext) { } } @@ -60,7 +62,10 @@ extern "C" { fn ui_main(); fn ui_onstartup(callback: UiCallback, userdata: *mut c_void); + fn ui_onnewwindow(callback: UiCallback, userdata: *mut c_void); fn ui_onexit(callback: UiCallback, userdata: *mut c_void); + + fn ui_newwindow(); } extern "C" fn app_startup(_event: *const UiEvent, data: *mut c_void) { @@ -76,6 +81,19 @@ extern "C" fn app_startup(_event: *const UiEvent, data: } } +extern "C" fn app_new_window(_event: *const UiEvent, data: *mut c_void) { + unsafe { + let app_ptr = data as *mut AppWrapper; + let app_ref: &mut AppWrapper = &mut *app_ptr; + let global_ctx = toolkit::UiContext::from_ptr(ui_global_context()); + let ctx = AppContext:: { + ctx: global_ctx, + _marker: PhantomData, + }; + app_ref.app.on_new_window(&ctx); + } +} + extern "C" fn app_exit(_event: *const UiEvent, data: *mut c_void) { unsafe { let app_ptr = data as *mut AppWrapper; @@ -95,12 +113,21 @@ pub fn app_run(app: &mut dyn Application) { let ptr: *mut AppWrapper = &mut wrapper; let c_ptr: *mut c_void = ptr as *mut c_void; ui_onstartup(Some(app_startup::), c_ptr); + ui_onnewwindow(Some(app_new_window::), c_ptr); ui_onexit(Some(app_exit::), c_ptr); ui_main(); } } +pub fn new_app_window() { + unsafe { + call_mainthread(||{ + ui_newwindow(); + }); + } +} + struct ErrApp<'a> { diff --git a/ui-rs/src/ui/toolbar.rs b/ui-rs/src/ui/toolbar.rs index defdbab..57e8f97 100644 --- a/ui-rs/src/ui/toolbar.rs +++ b/ui-rs/src/ui/toolbar.rs @@ -68,7 +68,7 @@ impl AppContext { } } - pub fn toolbar_menuitem(&self, name: &str) -> ToolbarMenuItemBuilder { + pub fn toolbar_menu(&self, name: &str) -> ToolbarMenuItemBuilder { unsafe { ToolbarMenuItemBuilder { args: ui_toolbar_menu_args_new(), @@ -85,6 +85,22 @@ impl AppContext { ui_toolbar_add_default(cstr.as_ptr(), pos as c_int); } } + + pub fn toolbar_appmenu(&self, f: F) + where F: FnOnce(&mut Menu) { + unsafe { + let args = ui_toolbar_menu_args_new(); + ui_toolbar_menu_create(std::ptr::null(), args); + ui_toolbar_menu_args_free(args); + + let mut menu = Menu:: { + ctx: toolkit::ui_global_context(), + _marker: PhantomData + }; + f(&mut menu); + ui_menu_end(); + } + } } pub struct ToolbarItemBuilder { @@ -243,9 +259,16 @@ impl Drop for ToolbarMenuItemBuilder { } impl ToolbarMenuItemBuilder { - pub fn create(&mut self) { + pub fn create(&mut self, f: F) + where F: FnOnce(&mut Menu) { unsafe { ui_toolbar_menu_create(self.name.as_ptr(), self.args); + let mut menu = Menu:: { + ctx: toolkit::ui_global_context(), + _marker: PhantomData + }; + f(&mut menu); + ui_menu_end(); } } pub fn label(&mut self, label: &str) -> &mut Self { diff --git a/ui/cocoa/list.m b/ui/cocoa/list.m index bf6deba..132d84a 100644 --- a/ui/cocoa/list.m +++ b/ui/cocoa/list.m @@ -215,6 +215,7 @@ UIWIDGET ui_table_create(UiObject* obj, UiListArgs *args) { void ui_tableview_update(UiList *list, int i) { NSTableView *tableview = (__bridge NSTableView*)list->obj; if(i < 0) { + [tableview deselectAll: tableview]; [tableview reloadData]; } else { [tableview reloadData]; // TODO: optimize diff --git a/ui/common/app.c b/ui/common/app.c index 9674599..e3bfd94 100644 --- a/ui/common/app.c +++ b/ui/common/app.c @@ -30,6 +30,8 @@ static ui_callback startup_func; static void *startup_data; +static ui_callback newwindow_func; +static void *newwindow_data; static ui_callback open_func; void *open_data; static ui_callback exit_func; @@ -41,6 +43,11 @@ void ui_onstartup(ui_callback f, void *userdata) { startup_data = userdata; } +void ui_onnewwindow(ui_callback f, void *userdata) { + newwindow_func = f; + newwindow_data = userdata; +} + void ui_onopen(ui_callback f, void *userdata) { open_func = f; open_data = userdata; @@ -58,6 +65,12 @@ void uic_application_startup(UiEvent *event) { } } +void uic_application_newwindow(UiEvent *event) { + if(newwindow_func) { + newwindow_func(event, newwindow_data); + } +} + void uic_application_open(UiEvent *event) { if(open_func) { open_func(event, open_data); @@ -69,3 +82,7 @@ void uic_application_exit(UiEvent *event) { exit_func(event, exit_data); } } + +void ui_newwindow(void) { + uic_application_newwindow(NULL); +} diff --git a/ui/common/app.h b/ui/common/app.h index 02356a9..9176e85 100644 --- a/ui/common/app.h +++ b/ui/common/app.h @@ -36,6 +36,7 @@ extern "C" { #endif void uic_application_startup(UiEvent *event); +void uic_application_newwindow(UiEvent *event); void uic_application_open(UiEvent *event); void uic_application_exit(UiEvent *event); diff --git a/ui/gtk/list.c b/ui/gtk/list.c index 7214687..70ecc3b 100644 --- a/ui/gtk/list.c +++ b/ui/gtk/list.c @@ -1259,6 +1259,11 @@ static GtkListStore* create_list_store(UiListView *listview, UiList *list) { return store; } +static void ui_destroy_tree_eventdata(GtkWidget *object, UiTreeEventData *data) { + free(data->activate_action); + free(data->selection_action); + free(data); +} UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { // create treeview @@ -1316,22 +1321,23 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { event->obj = obj; event->activate = args->onactivate; event->activatedata = args->onactivatedata; + event->activate_action = args->onactivate_action ? strdup(args->onactivate_action) : NULL; event->selection = args->onselection; event->selectiondata = args->onselectiondata; + event->selection_action = args->onselection_action ? strdup(args->onselection_action) : NULL; g_signal_connect( view, "destroy", - G_CALLBACK(ui_destroy_userdata), + G_CALLBACK(ui_destroy_tree_eventdata), event); - - if(args->onactivate) { + if(args->onactivate || args->onactivate_action) { g_signal_connect( view, "row-activated", G_CALLBACK(ui_listview_activate_event), event); } - if(args->onselection) { + if(args->onselection || args->onselection_action) { GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(view)); g_signal_connect( @@ -1543,20 +1549,27 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { list->obj = tableview; // add callback - UiTreeEventData *event = ui_malloc(obj->ctx, sizeof(UiTreeEventData)); + UiTreeEventData *event = malloc(sizeof(UiTreeEventData)); event->obj = obj; event->activate = args->onactivate; - event->selection = args->onselection; event->activatedata = args->onactivatedata; + event->activate_action = args->onactivate_action ? strdup(args->onactivate_action) : NULL; + event->selection = args->onselection; event->selectiondata = args->onselectiondata; - if(args->onactivate) { + event->selection_action = args->onselection_action ? strdup(args->onselection_action) : NULL; + g_signal_connect( + view, + "destroy", + G_CALLBACK(ui_destroy_tree_eventdata), + event); + if(args->onactivate || args->onactivate_action) { g_signal_connect( view, "row-activated", G_CALLBACK(ui_listview_activate_event), event); } - if(args->onselection) { + if(args->onselection || args->onselection_action) { GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(view)); g_signal_connect( @@ -1565,8 +1578,6 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { G_CALLBACK(ui_listview_selection_event), event); } - // TODO: destroy callback - if(args->ondragstart) { ui_listview_add_dnd(tableview, args); @@ -1798,13 +1809,18 @@ void ui_listview_activate_event( e.window = event->obj->window; e.document = event->obj->ctx->document; e.eventdata = &selection; + e.eventdatatype = UI_EVENT_DATA_LIST_SELECTION; e.intval = selection.count > 0 ? selection.rows[0] : -1; e.set = ui_get_setop(); - event->activate(&e, event->activatedata); - if(selection.count > 0) { - free(selection.rows); + if(event->activate) { + event->activate(&e, event->activatedata); + } + if(event->activate_action) { + uic_action_callback(&e, event->activate_action); } + + free(selection.rows); } void ui_listview_selection_event( @@ -1822,13 +1838,17 @@ void ui_listview_selection_event( e.window = event->obj->window; e.document = event->obj->ctx->document; e.eventdata = &selection; + e.eventdatatype = UI_EVENT_DATA_LIST_SELECTION; e.intval = selection.count > 0 ? selection.rows[0] : -1; e.set = ui_get_setop(); - event->selection(&e, event->selectiondata); - - if(selection.count > 0) { - free(selection.rows); + if(event->selection) { + event->selection(&e, event->selectiondata); } + if(event->selection_action) { + uic_action_callback(&e, event->selection_action); + } + + free(selection.rows); } UiListSelection ui_listview_get_selection( diff --git a/ui/gtk/list.h b/ui/gtk/list.h index d73ae56..69f30e4 100644 --- a/ui/gtk/list.h +++ b/ui/gtk/list.h @@ -106,6 +106,8 @@ typedef struct UiTreeEventData { ui_callback selection; void *activatedata; void *selectiondata; + char *activate_action; + char *selection_action; } UiTreeEventData; typedef struct UiListBox UiListBox; diff --git a/ui/ui/toolkit.h b/ui/ui/toolkit.h index 901d303..0dd606b 100644 --- a/ui/ui/toolkit.h +++ b/ui/ui/toolkit.h @@ -545,9 +545,12 @@ UIEXPORT void ui_object_ref(UiObject *obj); UIEXPORT int ui_object_unref(UiObject *obj); UIEXPORT void ui_onstartup(ui_callback f, void *userdata); +UIEXPORT void ui_onnewwindow(ui_callback f, void *userdata); UIEXPORT void ui_onopen(ui_callback f, void *userdata); UIEXPORT void ui_onexit(ui_callback f, void *userdata); +UIEXPORT void ui_newwindow(void); + UIEXPORT int ui_app_save_settings(void); UIEXPORT void ui_app_exit_on_shutdown(UiBool exitapp);