v0.0.44 - Move tab to new window on 'Open in New Window' (close original tab); keep app alive when main window closes if aux windows have tabs (delete-event handler)

This commit is contained in:
Laan Tungir
2026-07-17 19:14:35 -04:00
parent 6a8e51c812
commit 7d91a186d4
6 changed files with 123 additions and 8 deletions

View File

@@ -1 +1 @@
0.0.43
0.0.44

Binary file not shown.

View File

@@ -505,7 +505,83 @@ static void agent_login_callback(void) {
g_print("[login] Agent login detected.\n");
}
/* ---- Window destroy ------------------------------------------------- */
/* ---- Window close / destroy ----------------------------------------- *
* The main window uses a delete-event handler to intercept the window
* manager's close request BEFORE the window is destroyed. This lets us
* close the main window's tabs cleanly (updating g_tab_count) and keep
* the app running if auxiliary windows still have tabs. The app only
* quits when the last window is closed (no tabs remain anywhere).
*
* delete-event: runs first. Closes all main-window tabs. If aux windows
* still have tabs, returns TRUE to suppress destroy and hides the
* main window. If no tabs remain, returns FALSE to let destroy
* proceed → on_window_destroy → gtk_main_quit().
* destroy: runs only when the app is truly shutting down. Performs
* session save, net_services_shutdown, agent_server_stop, etc.
*/
/* Guard flag: set while on_window_delete_event is closing the main
* window's tabs. Prevents re-entrancy when tab_manager_close_tab()
* calls gtk_window_close(g_window) after the last tab is closed, which
* would re-emit delete-event. */
static gboolean g_main_window_closing = FALSE;
static gboolean on_window_delete_event(GtkWidget *widget,
GdkEvent *event,
gpointer data) {
(void)event;
(void)data;
/* If we're already in the process of closing (re-entrant call from
* tab_manager_close_tab → gtk_window_close), let the destroy
* proceed. */
if (g_main_window_closing) {
return FALSE;
}
g_main_window_closing = TRUE;
/* Close all tabs that belong to the main window's notebook. We
* identify main-window tabs by checking gtk_notebook_page_num on
* the main notebook. tab_manager_close_tab handles removing from
* the notebook and the g_tabs array. Re-scan each iteration because
* closing shifts indices. */
for (;;) {
gboolean found = FALSE;
GtkWidget *main_nb = tab_manager_get_main_notebook();
if (main_nb == NULL) break;
/* Close from the highest index down so removal doesn't shift
* unprocessed indices. Find the highest-index tab in the main
* notebook and close it. */
for (int i = tab_manager_count() - 1; i >= 0; i--) {
tab_info_t *tab = tab_manager_get(i);
if (tab == NULL || tab->page == NULL) continue;
if (gtk_notebook_page_num(GTK_NOTEBOOK(main_nb),
tab->page) >= 0) {
tab_manager_close_tab(i);
found = TRUE;
break;
}
}
if (!found) break;
}
/* If auxiliary windows still have tabs, keep the app running.
* Suppress the default destroy by returning TRUE, and hide the
* main window. The aux windows continue independently. */
if (tab_manager_count() > 0) {
g_print("[windows] Main window closed but %d tab(s) remain in "
"other window(s) — keeping app alive.\n",
tab_manager_count());
gtk_widget_hide(widget);
g_main_window_closing = FALSE; /* allow re-opening later */
return TRUE; /* suppress destroy */
}
/* No tabs left anywhere — let the destroy proceed, which triggers
* on_window_destroy and quits the app. Keep the guard set so any
* re-entrant delete-event from the destroy path doesn't re-enter. */
return FALSE;
}
static void on_window_destroy(GtkWidget *widget, gpointer data) {
(void)widget;
@@ -761,6 +837,8 @@ int main(int argc, char **argv) {
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "sovereign browser " SB_VERSION);
gtk_window_set_default_size(GTK_WINDOW(window), 1024, 768);
g_signal_connect(window, "delete-event",
G_CALLBACK(on_window_delete_event), NULL);
g_signal_connect(window, "destroy", G_CALLBACK(on_window_destroy), NULL);
g_signal_connect(window, "key-press-event", G_CALLBACK(on_key_press), NULL);
g_window = GTK_WINDOW(window);

View File

@@ -3274,6 +3274,14 @@ int tab_manager_count(void) {
return g_tab_count;
}
/* Return the main window's notebook widget. Used by main.c's
* delete-event handler to identify which tabs belong to the main window
* (vs auxiliary windows) when closing the main window but keeping aux
* windows alive. */
GtkWidget *tab_manager_get_main_notebook(void) {
return g_notebook;
}
void tab_manager_set_title(int index, const char *title) {
if (index < 0 || index >= g_tab_count) return;
tab_info_t *tab = g_tabs[index];
@@ -3344,10 +3352,31 @@ void tab_manager_open_in_new_window(int index) {
if (index < 0 || index >= g_tab_count) return;
tab_info_t *tab = g_tabs[index];
if (tab == NULL) return;
/* Open the tab's current URL in a new window. Pass the active
* webview as the related view so the new window shares the
* WebProcess. */
tab_manager_new_window(tab->current_url, tab->webview);
/* Capture the URL before opening the new window — the original tab
* will be closed below, which frees the tab_info_t. */
char url_buf[TAB_URL_MAX];
snprintf(url_buf, sizeof(url_buf), "%s", tab->current_url);
/* Open the URL in a new window. Pass the original tab's webview as
* the related view so the new window shares the WebProcess. The new
* window takes its own ref on the related view, so it's safe to
* close the original tab afterwards. */
GtkWidget *new_wv = tab_manager_new_window(url_buf, tab->webview);
if (new_wv == NULL) {
/* New window creation failed — keep the original tab open so the
* user doesn't lose the page. */
return;
}
/* Close the original tab — "Open in New Window" moves the tab to a
* new window rather than duplicating it. The index may have shifted
* if tab_manager_new_window added tabs to the array, so re-resolve
* the tab's current index by pointer. */
int cur_index = tab_array_find(tab);
if (cur_index >= 0) {
tab_manager_close_tab(cur_index);
}
}
void tab_manager_new_window_blank(void) {

View File

@@ -208,6 +208,14 @@ WebKitWebView *tab_manager_get_main_webview(void);
*/
gboolean tab_manager_sidebar_visible(void);
/*
* Returns the main window's GtkNotebook widget. Used by main.c's
* delete-event handler to identify which tabs belong to the main window
* (vs auxiliary windows) when closing the main window but keeping aux
* windows alive.
*/
GtkWidget *tab_manager_get_main_notebook(void);
/*
* Re-hide the sidebar container after gtk_widget_show_all(window).
* Call this in main.c after show_all so the sidebar (which is packed

View File

@@ -11,9 +11,9 @@
#ifndef SOVEREIGN_BROWSER_VERSION_H
#define SOVEREIGN_BROWSER_VERSION_H
#define SB_VERSION "v0.0.43"
#define SB_VERSION "v0.0.44"
#define SB_VERSION_MAJOR 0
#define SB_VERSION_MINOR 0
#define SB_VERSION_PATCH 43
#define SB_VERSION_PATCH 44
#endif /* SOVEREIGN_BROWSER_VERSION_H */