Compare commits

...
1 Commits
8 changed files with 238 additions and 3 deletions
+1 -1
View File
@@ -1 +1 @@
0.0.62
0.0.63
+177
View File
@@ -0,0 +1,177 @@
# Plan: Keyboard Shortcut to Toggle All Menu Bars (per-window)
## Goal
Add a configurable keyboard shortcut that shows/hides all the browser's
"menu bars" — the per-tab toolbars, bookmark bars, and the tab strip —
**independently per window**. Pressing the shortcut in one window hides
that window's chrome only; other windows are unaffected. This mirrors how
the agent sidebar already works (per-window `sidebar_visible` on
[`window_state_t`](src/tab_manager.c:87)).
In sovereign_browser there is no traditional `GtkMenuBar`; each tab's chrome
is two widgets packed at the top of the tab page
([`tab->page`](src/tab_manager.c:2966)):
1. **`#main-toolbar`** — the horizontal GtkBox holding the hamburger menu
button, refresh/stop, back, forward, URL entry, and bookmark button
(built in [`tab_create()`](src/tab_manager.c:2973)).
2. **`#bookmark-bar`** — the bookmarks toolbar below the URL toolbar
([`tab->bookmark_bar`](src/tab_manager.c:3089)).
Plus the tab strip itself, which is the `GtkNotebook` header, controlled
via `gtk_notebook_set_show_tabs()`.
Hiding all three gives the webview the entire window (a focus / kiosk
mode). Pressing the shortcut again restores them.
## Design
### 1. New shortcut action
Add `SHORTCUT_TOGGLE_TOOLBARS` to the enum in [`shortcuts.h`](src/shortcuts.h:29)
(before `SHORTCUT_COUNT`) and a matching entry in the `g_registry` table in
[`shortcuts.c`](src/shortcuts.c:24):
| Field | Value |
|---------|----------------------------------------------------|
| id | `toggle_toolbars` |
| label | `Toggle menu bars` |
| desc | `Show or hide the tab strip, toolbars, and bookmark bars in the active window` |
| dflt | `<Control><Shift>m` |
Default `<Control><Shift>m` ("m" for menu) is free and mnemonic. It is a
browser-level shortcut (not consumed by WebKit for web content), so it
reaches the window-level `on_key_press` handler reliably.
Because the settings page enumerates actions via
[`sovereign://settings/config`](src/nostr_bridge.c:2994) (which loops over
`SHORTCUT_COUNT` calling [`shortcuts_meta()`](src/shortcuts.c:232) /
[`shortcuts_get()`](src/shortcuts.c:202)), the new action appears
automatically in the Keyboard Shortcuts section of
[`www/settings.html`](www/settings.html:74) with no JS changes. NIP-78 sync
also picks it up for free via [`shortcuts_serialize()`](src/shortcuts.c:247).
### 2. Track toolbar widgets on each tab
The `#main-toolbar` GtkBox is currently a local variable in
[`tab_create()`](src/tab_manager.c:2973) and is not stored on
[`tab_info_t`](src/tab_manager.h:23). Add a field:
```c
GtkWidget *toolbar; /* #main-toolbar (hamburger + nav + URL) */
```
to `tab_info_t` in [`tab_manager.h`](src/tab_manager.h:23), and assign
`tab->toolbar = toolbar;` in [`tab_create()`](src/tab_manager.c:2973).
`tab->bookmark_bar` already exists.
### 3. Per-window visibility flag
Add a field to [`window_state_t`](src/tab_manager.c:87) in
[`tab_manager.c`](src/tab_manager.c):
```c
gboolean toolbars_visible; /* per-window menu-bar visibility */
```
Initialize it to `TRUE` for `g_main_window` (in
[`tab_manager_init`](src/tab_manager.c:3598), near where
`sidebar_visible = FALSE` is set at line 3643) and for each aux window
created in [`tab_manager_new_window`](src/tab_manager.c:1262) (near line
1376 where `sidebar_visible = FALSE` is set).
### 4. Toggle function (active window only)
Add a public function in [`tab_manager.c`](src/tab_manager.c):
```c
void tab_manager_toggle_toolbars(void);
```
Implementation — mirrors [`tab_manager_toggle_sidebar()`](src/tab_manager.c:4143):
1. `window_state_t *ws = get_active_window_state();` — resolves the focused
window (main or aux), exactly like the sidebar toggle does.
2. Flip `ws->toolbars_visible`.
3. `gtk_notebook_set_show_tabs(GTK_NOTEBOOK(ws->notebook), ws->toolbars_visible);`
— hides/shows the tab strip for this window's notebook only.
4. Iterate `g_tabs[0..g_tab_count)` and, for each tab whose **page belongs
to this window's notebook** (use [`tab_find_notebook(tab->page)`](src/tab_manager.c:2660)
and compare to `ws->notebook`), call
`gtk_widget_set_visible(tab->toolbar, ws->toolbars_visible)` and
`gtk_widget_set_visible(tab->bookmark_bar, ws->toolbars_visible)`.
This affects only the active window's tabs and tab strip; other windows
keep their own `toolbars_visible` state.
### 5. Respect hidden state for new tabs
In [`tab_create()`](src/tab_manager.c:2973), after building `toolbar` and
`tab->bookmark_bar`, look up the notebook the tab is being added to (the
target notebook — `g_active_notebook` / `get_effective_notebook()` at tab
creation time, or `tab_find_notebook(tab->page)` after packing) and its
`window_state_t` via [`window_state_for_notebook()`](src/tab_manager.c:4051),
then apply that window's flag:
```c
window_state_t *ws = window_state_for_notebook(<target notebook>);
gboolean vis = ws ? ws->toolbars_visible : TRUE;
gtk_widget_set_visible(toolbar, vis);
gtk_widget_set_visible(tab->bookmark_bar, vis);
```
So a tab opened in a hidden-chrome window is also hidden, while a tab
opened in a visible-chrome window stays visible. The tab strip visibility
is per-notebook via `gtk_notebook_set_show_tabs()`, so no extra work is
needed there.
### 6. Dispatch the shortcut
Add a case to the switch in [`on_key_press`](src/main.c:611) in
[`main.c`](src/main.c):
```c
case SHORTCUT_TOGGLE_TOOLBARS:
tab_manager_toggle_toolbars();
return TRUE;
```
The key-press handler is window-level, so `get_active_window_state()`
inside the toggle resolves to the window that had focus when the key was
pressed — giving the per-window behavior.
### 7. Expose in tab_manager.h
Add the prototype for `tab_manager_toggle_toolbars(void)` near the other
toggle prototypes ([`tab_manager_toggle_inspector`](src/tab_manager.h),
[`tab_manager_toggle_sidebar`](src/tab_manager.h)).
## Files touched
| File | Change |
|---------------------|---------------------------------------------------------------|
| [`src/shortcuts.h`](src/shortcuts.h:29) | Add `SHORTCUT_TOGGLE_TOOLBARS` to enum |
| [`src/shortcuts.c`](src/shortcuts.c:24) | Add registry entry (id/label/desc/dflt) |
| [`src/tab_manager.h`](src/tab_manager.h:23) | Add `GtkWidget *toolbar` field + `tab_manager_toggle_toolbars` prototype |
| [`src/tab_manager.c`](src/tab_manager.c:87) | Add `toolbars_visible` to `window_state_t`; init to TRUE for main + aux windows |
| [`src/tab_manager.c`](src/tab_manager.c:2973) | Store `tab->toolbar`; apply window's `toolbars_visible` on creation; implement `tab_manager_toggle_toolbars()` (active window only: tab strip + that window's tabs' toolbars/bookmark bars) |
| [`src/main.c`](src/main.c:611) | Add `case SHORTCUT_TOGGLE_TOOLBARS` dispatch |
No changes needed in `www/` (settings page auto-discovers the action) or in
`settings_sync.c` (NIP-78 sync already serializes all actions).
## Verification
1. `make` — builds clean.
2. `./browser.sh restart --login-method generate --url https://example.com`
3. Press `Ctrl+Shift+M` → in the focused window, toolbars + bookmark bar +
tab strip disappear; the webview fills the entire window. Press again →
they reappear.
4. Open a second window (`Ctrl+N`). Hide chrome in window A with
`Ctrl+Shift+M`; window B's chrome stays visible. Hide B independently.
5. With chrome hidden in a window, `Ctrl+T` opens a new tab in that window
whose toolbars are also hidden; the tab strip stays hidden too.
6. Open `sovereign://settings` → Keyboard Shortcuts section lists
"Toggle menu bars" with `Ctrl+Shift+M`; rebind it and confirm the new
binding works.
+4
View File
@@ -627,6 +627,10 @@ gboolean on_key_press(GtkWidget *widget, GdkEventKey *event,
tab_manager_toggle_sidebar();
return TRUE;
case SHORTCUT_TOGGLE_TOOLBARS:
tab_manager_toggle_toolbars();
return TRUE;
case SHORTCUT_ZOOM_IN:
tab_manager_zoom_in();
return TRUE;
+5
View File
@@ -100,6 +100,11 @@ static const shortcut_meta_t g_registry[SHORTCUT_COUNT] = {
"toggle_sidebar", "Toggle agent sidebar",
"Show or hide the agent chat sidebar", "<Control><Shift>a"
},
[SHORTCUT_TOGGLE_TOOLBARS] = {
"toggle_toolbars", "Toggle menu bars",
"Show or hide the tab strip, toolbars, and bookmark bars in the active window",
"<Control><Shift>m"
},
[SHORTCUT_ZOOM_IN] = {
"zoom_in", "Zoom in",
"Increase the zoom level of the active tab", "<Control>plus"
+1
View File
@@ -46,6 +46,7 @@ typedef enum {
SHORTCUT_TOGGLE_FULLSCREEN,
SHORTCUT_TOGGLE_INSPECTOR,
SHORTCUT_TOGGLE_SIDEBAR,
SHORTCUT_TOGGLE_TOOLBARS,
SHORTCUT_ZOOM_IN,
SHORTCUT_ZOOM_OUT,
SHORTCUT_ZOOM_RESET,
+38
View File
@@ -91,6 +91,8 @@ typedef struct {
GtkWidget *sidebar_container; /* GtkBox for sidebar webview */
WebKitWebView *sidebar_webview; /* lazily created on first toggle */
gboolean sidebar_visible;
gboolean toolbars_visible; /* per-window menu-bar (tab strip +
* toolbars + bookmark bars) visibility */
} window_state_t;
/* ── Static state ─────────────────────────────────────────────────── */
@@ -1374,6 +1376,7 @@ static GtkWidget *tab_manager_new_window(const char *url,
ws.sidebar_container = sidebar_container;
ws.sidebar_webview = NULL;
ws.sidebar_visible = FALSE;
ws.toolbars_visible = TRUE;
g_array_append_val(g_aux_windows, ws);
/* This new window is now the active window. */
@@ -2976,6 +2979,7 @@ static tab_info_t *tab_create(const char *url) {
gtk_widget_set_margin_bottom(toolbar, 4);
gtk_widget_set_margin_start(toolbar, 4);
gtk_widget_set_margin_end(toolbar, 4);
tab->toolbar = toolbar;
gtk_box_pack_start(GTK_BOX(tab->page), toolbar, FALSE, FALSE, 0);
tab->hamburger = build_hamburger_menu(tab);
@@ -3095,6 +3099,17 @@ static tab_info_t *tab_create(const char *url) {
gtk_box_pack_start(GTK_BOX(tab->page), tab->bookmark_bar, FALSE, FALSE, 0);
bookmark_bar_refresh(tab);
/* Apply the active window's menu-bar visibility so a tab opened in a
* hidden-chrome window is also hidden. The tab strip itself is
* per-notebook (gtk_notebook_set_show_tabs), already set by the
* toggle; here we only sync the per-tab toolbar + bookmark bar. */
{
window_state_t *ws = get_active_window_state();
gboolean vis = ws ? ws->toolbars_visible : TRUE;
gtk_widget_set_visible(tab->toolbar, vis);
gtk_widget_set_visible(tab->bookmark_bar, vis);
}
/* Ensure the webview expands vertically to fill the available space.
* Without this, WebKitGTK may only allocate 1px of height on some
* display servers, resulting in a blank page. */
@@ -3641,6 +3656,7 @@ void tab_manager_init(GtkContainer *parent,
g_main_window.notebook = g_notebook;
g_main_window.sidebar_webview = NULL;
g_main_window.sidebar_visible = FALSE;
g_main_window.toolbars_visible = TRUE;
/* The main window/notebook are the default active window/notebook
* for MCP get_active_webview() and keyboard shortcuts (zoom, etc.).
@@ -4164,6 +4180,28 @@ void tab_manager_toggle_sidebar(void) {
}
}
void tab_manager_toggle_toolbars(void) {
window_state_t *ws = get_active_window_state();
if (ws == NULL || ws->notebook == NULL) return;
ws->toolbars_visible = !ws->toolbars_visible;
gboolean vis = ws->toolbars_visible;
/* Tab strip (GtkNotebook header) — per-notebook. */
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(ws->notebook), vis);
/* Per-tab toolbar + bookmark bar, but only for tabs that belong to
* this window's notebook (aux windows have their own notebook). */
for (int i = 0; i < g_tab_count; i++) {
tab_info_t *tab = g_tabs[i];
if (tab == NULL || tab->page == NULL) continue;
GtkWidget *nb = tab_find_notebook(tab->page);
if (nb != ws->notebook) continue;
if (tab->toolbar) gtk_widget_set_visible(tab->toolbar, vis);
if (tab->bookmark_bar) gtk_widget_set_visible(tab->bookmark_bar, vis);
}
}
WebKitWebView *tab_manager_get_main_webview(void) {
tab_info_t *tab = tab_manager_get_active();
if (tab == NULL) return NULL;
+10
View File
@@ -29,6 +29,7 @@ typedef struct {
GtkWidget *title_label; /* child of tab_label */
GtkWidget *favicon; /* favicon image in tab_label */
GtkWidget *refresh_btn; /* toolbar reload/stop button (per-tab) */
GtkWidget *toolbar; /* #main-toolbar (hamburger + nav + URL) */
GtkWidget *bookmark_bar; /* bookmarks toolbar (below URL toolbar) */
gboolean refresh_shows_stop; /* cached visual state, avoids redundant updates */
char current_url[TAB_URL_MAX];
@@ -228,6 +229,15 @@ void tab_manager_zoom_reset(void);
*/
void tab_manager_toggle_sidebar(void);
/*
* Toggle visibility of all "menu bars" in the active window: the tab strip
* (GtkNotebook header), each tab's #main-toolbar, and each tab's bookmark
* bar. Per-window — only the focused window is affected, mirroring
* tab_manager_toggle_sidebar(). New tabs opened in a hidden-chrome window
* inherit the hidden state.
*/
void tab_manager_toggle_toolbars(void);
/*
* Returns the main webview (the web page) of the active tab, never the
* sidebar webview. This is used by agent tools so they always operate
+2 -2
View File
@@ -11,9 +11,9 @@
#ifndef SOVEREIGN_BROWSER_VERSION_H
#define SOVEREIGN_BROWSER_VERSION_H
#define SB_VERSION "v0.0.62"
#define SB_VERSION "v0.0.63"
#define SB_VERSION_MAJOR 0
#define SB_VERSION_MINOR 0
#define SB_VERSION_PATCH 62
#define SB_VERSION_PATCH 63
#endif /* SOVEREIGN_BROWSER_VERSION_H */