]> uap-core.de Git - note.git/commitdiff
add UiObject onclose event handler
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Tue, 19 May 2026 16:41:29 +0000 (18:41 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Tue, 19 May 2026 16:41:29 +0000 (18:41 +0200)
application/src/window.rs
ui-rs/src/ui/window.rs
ui/common/context.c
ui/common/context.h
ui/common/object.c
ui/common/wrapper.c
ui/common/wrapper.h
ui/gtk/toolkit.c
ui/gtk/window.c
ui/qt/window.cpp
ui/ui/toolkit.h

index 6fd2ab2d27f3889cd4dd103e75a05b978baa6389..e3a8bf33608d678fa63393c5e585a57a6b2b204b 100644 (file)
@@ -35,6 +35,7 @@ use crate::newnotebook::new_notebook_dialog;
 
 #[derive(UiModel)]
 pub struct MainWindow {
+    pub obj: Option<UiObject<MainWindow>>,
     pub backend: BackendHandle,
 
     #[bind]
@@ -45,6 +46,7 @@ pub struct MainWindow {
 impl MainWindow {
     pub fn new(app: &App) -> MainWindow {
         MainWindow {
+            obj: None,
             backend: app.backend.clone(),
             notebooks: UiSourceList::default()
         }
@@ -83,8 +85,9 @@ impl MainWindow {
 pub fn create_window(app: &App, ctx: &AppContext<MainWindow>) -> UiObject<MainWindow> {
     let windowdata: MainWindow = MainWindow::new(app);
 
-    let window = ctx.splitview_window("note", true, windowdata, |obj, data| {
+    let mut window = ctx.splitview_window("note", true, windowdata, |obj, data| {
         init_window_data(data, app);
+        data.obj = Some(obj.clone());
 
         obj.sidebar_builder().create(|obj|{
             obj.sourcelist(|b|{
@@ -127,6 +130,10 @@ pub fn create_window(app: &App, ctx: &AppContext<MainWindow>) -> UiObject<MainWi
     });
 
     window.show();
+    window.onclose(|event| {
+        println!("window closing");
+        event.data.obj = None;
+    });
     window
 }
 
index 39beac9169f2a8eca82bd8c8b5eee683f25a89ff..6751154499e1d359c782db50fc808ac00c5a6c29 100644 (file)
@@ -49,6 +49,15 @@ impl<T> toolkit::UiObject<T> {
         }
     }
 
+    pub fn onclose<F>(&mut self, f: F)
+    where F: FnMut(&mut event::Event<T>) + 'static {
+        let wrapper = Box::new(EventWrapper { callback: Box::new(f) });
+        let ptr = self.ctx.reg_box(wrapper);
+        unsafe {
+            ui_object_set_onclose(self.ptr, Some(event::event_wrapper::<T>), ptr.cast());
+        }
+    }
+
     pub fn set_size(&mut self, width: u32, height: u32) {
         unsafe {
             ui_window_size(self.ptr, width as c_int, height as c_int);
@@ -487,6 +496,7 @@ extern "C" {
     pub fn ui_object_get_windowdata(obj: *const UiObject) -> *mut c_void;
     pub fn ui_object_set_windowdata(obj: *mut UiObject, data: *mut c_void);
     pub fn ui_object_get_context(obj: *const UiObject) -> *mut UiContext;
+    fn ui_object_set_onclose(obj: *const UiObject, onclose: UiCallback, data: *mut c_void);
 
     pub fn ui_reg_destructor(ctx: *mut UiContext, data: *mut c_void, destructor: UiDestructor);
 
index 12130d0fa6c6c7555cab2764f7bd81807841b855..19de77d920d3e78c4a4d7f7ca8f721b7ad8e1b5e 100644 (file)
@@ -97,6 +97,15 @@ void uic_context_add_destructor(UiContext *ctx, cx_destructor_func func, void *d
     cxListAdd(ctx->destroy_handler, &handler);
 }
 
+void uic_context_remove_destructor(UiContext *ctx, void *data) {
+    CxIterator i = cxListIterator(ctx->destroy_handler);
+    cx_foreach(UiDestroyHandler *, handler, i) {
+        if(handler->data == data) {
+            cxIteratorFlagRemoval(i);
+        }
+    }
+}
+
 void uic_context_prepare_close(UiContext *ctx) {
     cxListClear(ctx->states);
     cxListClear(ctx->state_widgets);
@@ -733,6 +742,10 @@ void  ui_reg_destructor(UiContext *ctx, void *data, ui_destructor_func destr) {
     uic_context_add_destructor(ctx, destr, data);
 }
 
+void  ui_remove_destructor(UiContext *ctx, void *data) {
+    uic_context_remove_destructor(ctx, data);
+}
+
 void  ui_set_destructor(void *mem, ui_destructor_func destr) {
     cxMempoolSetDestructor(mem, (cx_destructor_func)destr);
 }
index c846ba5cfdc7432d22d261719dda399bb4837bc8..d692f16f2f4c5b56a43d35ba818bbc17a81dc612 100644 (file)
@@ -134,6 +134,7 @@ void uic_init_global_context(void);
 UiContext* uic_context(UiObject *toplevel, CxMempool *mp);
 UiContext* uic_root_context(UiContext *ctx);
 void uic_context_add_destructor(UiContext *ctx, cx_destructor_func func, void *data);
+void uic_context_remove_destructor(UiContext *ctx, void *data);
 
 void uic_context_prepare_close(UiContext *ctx);
 void uic_context_destroy(UiContext *ctx, void *document);
index 16f2ed9a1ea8cff41f811cdf248375fcd09d05ac..8ea5b37e6438154a3ecdf73f2b1cabeb283e650b 100644 (file)
@@ -103,6 +103,7 @@ int ui_object_unref(UiObject *obj) {
     // it is possible to have 0 references, in case
     // a window was created but ui_show was never called
     if(obj->ref == 0 || --obj->ref == 0) {
+        uic_context_prepare_close(obj->ctx);
         if(obj->destroy) {
             obj->destroy(obj);
         } else {
index 575dd2989f65b13ddec22503fd89ad0c612f3947..8a891459a5644605ede51969e032a088d8b24ea3 100644 (file)
@@ -45,6 +45,11 @@ void ui_object_set_windowdata(UiObject *obj, void *windowdata) {
     obj->window = windowdata;
 }
 
+void ui_object_set_onclose(UiObject *obj, ui_callback callback, void *userdata) {
+    obj->onclose = callback;
+    obj->onclosedata = userdata;
+}
+
 
 /* ---------------------------- UiList ---------------------------- */
 
index 37f2f38a9f4de88d7dc584e2b178a5bc0bac1b57..1c94d53012a2a27f02eed4db68e9dc36d8092f4f 100644 (file)
@@ -39,6 +39,7 @@ extern "C" {
 UIEXPORT UiContext* ui_object_get_context(UiObject *obj);
 UIEXPORT void* ui_object_get_windowdata(UiObject *obj);
 UIEXPORT void ui_object_set_windowdata(UiObject *obj, void *windowdata);
+UIEXPORT void ui_object_set_onclose(UiObject *obj, ui_callback callback, void *userdata);
 
 UIEXPORT void* ui_list_get_data(UiList *list);
 UIEXPORT void* ui_list_get_iter(UiList *list);
index 9d6cf9150429d56662e63c2c20977e5e4a4af698..b1bb9d10270010143646a3f85ed4731cc58fdf89 100644 (file)
@@ -165,7 +165,6 @@ GtkApplication* ui_get_application() {
 
 void ui_show(UiObject *obj) {
     gboolean visible = FALSE;
-    uic_check_state_widgets(obj->ctx);
     if(obj->widget) {
         visible = gtk_widget_is_visible(obj->widget);
 #if GTK_MAJOR_VERSION >= 4
@@ -174,6 +173,7 @@ void ui_show(UiObject *obj) {
         gtk_widget_show_all(obj->widget);
 #endif
     }
+    uic_check_state_widgets(obj->ctx);
       
     if(!visible) {
         obj->ref++;
@@ -181,18 +181,6 @@ void ui_show(UiObject *obj) {
 }
 
 void ui_close(UiObject *obj) {
-    uic_context_prepare_close(obj->ctx); // TODO: should this be moved to the close event handler? Yes!
-    /*
-    if(obj->widget) {
-#if GTK_CHECK_VERSION(4, 0, 0)
-        gtk_window_close(GTK_WINDOW(obj->widget));
-#else
-        gtk_widget_destroy(obj->widget);
-#endif
-    } else {
-        ui_window_close_request(obj);
-    }
-    */
     ui_window_close_request(obj);
 }
 
index 7952570a712b67456cf28da31c5fd29b76e5252f..5b74b575e34510629c0cfac9f87d9f16fe0d40d2 100644 (file)
@@ -112,7 +112,12 @@ gboolean ui_window_close_request(UiObject *obj) {
         }
     }
     
-    obj->ref--;
+    if(obj->ref > 0) {
+        obj->ref--;
+    } else {
+        // warn about invalid reference counting
+        fprintf(stderr, "Error: UiObject %p ref == 0\n", obj);
+    }
     if(obj->ref > 0) {
 #if GTK_CHECK_VERSION(2, 18, 0)
         gtk_widget_set_visible(obj->widget, FALSE);
@@ -121,29 +126,30 @@ gboolean ui_window_close_request(UiObject *obj) {
 #endif
         return TRUE;
     } else {
-        if(obj->ctx->close_callback) {
-            UiEvent ev;
-            ev.window = obj->window;
-            ev.document = obj->ctx->document;
-            ev.obj = obj;
-            ev.eventdata = NULL;
-            ev.eventdatatype = 0;
-            ev.intval = 0;
-            obj->ctx->close_callback(&ev, obj->ctx->close_data);
-            obj->ctx->close_callback = NULL;
-        }
-        
         uic_context_prepare_close(obj->ctx);
         return FALSE;
     }
 }
 
+static void window_onclose_callback(UiObject *obj) {
+    if(obj->onclose) {
+        UiEvent event;
+        memset(&event, 0, sizeof(UiEvent));
+        event.obj = obj;
+        event.window = obj->window;
+        event.document = obj->ctx->document;
+        obj->onclose(&event, obj->onclosedata);
+    }
+}
+
 #if GTK_MAJOR_VERSION >= 4
 static gboolean close_request(GtkWindow* self, UiObject *obj) {
+    window_onclose_callback(obj);
     return ui_window_close_request(obj);
 }
 #else
 static gboolean close_request(GtkWidget* self, GdkEvent* event, UiObject *obj) {
+    window_onclose_callback(obj);
     return ui_window_close_request(obj);
 }
 #endif
@@ -305,17 +311,23 @@ static UiObject* create_window(const char *title, UiBool sidebar, UiBool splitvi
             adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE);
         } else if(!strcmp(show_title, "sidebar")) {
             adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_main), FALSE);
-            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), TRUE);
+            if(headerbar_sidebar) {
+                adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), TRUE);
+            }
         } else if(!strcmp(show_title, "false")) {
-            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE);
+            if(headerbar_sidebar) {
+                adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE);
+            }
             adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_main), FALSE);
         } else {
             fprintf(stderr, "Unknown value '%s' for property ui.gtk.window.showtitle\n", show_title);
-            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE);
+            if(headerbar_sidebar) {
+                adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE);
+            }
         }
     } else {
         adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_main), FALSE);
-        if(sidebar) {
+        if(headerbar_sidebar) {
             adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), TRUE);
         }
     }
index 6e4c1c83af43cd07b8043d06660183566db659ec..1c696065d1417013da5359c722531d629e7ae9c5 100644 (file)
@@ -43,7 +43,6 @@
 
 static UiObject* create_window(const char *title, bool simple, bool sidebar = false) {
     UiObject *obj = uic_object_new_toplevel();
-    obj->next = NULL;
     
     QMainWindow *window = new QMainWindow();
     window->setWindowTitle(title);
index cd3fb441bfd03e8f1e9c52a7556c7c88b980e402..7f7ede9f291a4ff00283fb625c68d427ee3c67e4 100644 (file)
@@ -295,9 +295,10 @@ struct UiObject {
     UiContainer *container_end;
     
     /*
-     * next container object
+     * called when someone requests to close the window
      */
-    UiObject    *next;
+    void (*onclose)(UiEvent *event, void *userdata);
+    void *onclosedata;
     
     /*
      * obj destroy func