1. Overview
The SettingsWindow module implements BastionGuard’s top-level settings dialog using GTKmm (GTK4). It provides a custom application-styled window composed of a branded header bar, a left sidebar navigation with expandable groups, and a main content area backed by a Gtk::Stack hosting a SettingsPage instance.
The window acts as the main settings container for the application. It is modal, resizable, visually styled through CSS classes and style providers, and delegates all functional settings logic to SettingsPage while retaining responsibility for layout, navigation, and visual framing.
The module integrates with:
- GTKmm / GTK4 – windowing, layout containers, widgets, signals, and stack-based content routing
- SettingsPage – the actual settings content, internally navigated by numeric page indices
- Resource – runtime path resolution for icons, images, and sidebar assets
- StyleProvider – application CSS loading and injection through the global CSS provider
- Gdk::Pixbuf – icon and image loading plus scaling for header and sidebar visuals
- glibmm i18n – localized labels and messages through
_() - ClamdConfig – forwarded to
SettingsPageduring construction
2. Window Initialization and Top-Level Layout
2.1 Window Properties
The constructor configures the window as a dialog-like settings container with the following properties:
- Empty title via
set_title(""), because the visual identity is provided by a custom header bar - Default size:
600 × 400 - Modal behavior via
set_modal(true) - Resizable behavior via
set_resizable(true)
2.2 Main Structure
The content hierarchy is assembled through:
Gtk::HeaderBarcreated bybuild_headerbar()Gtk::Box mainBox_as the primary horizontal containerGtk::Stack stack_as the content host- A custom sidebar created by
build_sidebar(config)
The SettingsPage widget is instantiated through:
settingsPage_ = Gtk::make_managed<SettingsPage>(config);
It is then configured to expand in both directions and added to the stack under the name:
settings_root
The internal notebook tabs of SettingsPage are explicitly hidden through:
settingsPage_->set_notebook_no_tabs();
This ensures that all navigation is driven exclusively by the custom sidebar instead of the embedded notebook UI.
3. Styling and CSS Provider Injection
3.1 Widget Style Classes
The window and its major containers are tagged with CSS classes to support BastionGuard’s custom visual theme.
The following classes are applied directly by the implementation:
settings-window– applied to the window itselfsettings-main– applied tomainBox_content– applied to the stack content areasidebar– applied to the left navigation containersidebar-header– applied to the sidebar header sectionsidebar-title– applied to the localized sidebar titlesidebar-separator– applied to separator widgetssidebar-button– base class for sidebar buttonssidebar-leaf– applied to leaf navigation itemssidebar-leaf-indent– applied to nested leaf itemssidebar-submenu– applied to collapsible submenu containersactive-sidebar-button– applied dynamically to the active leaf buttoncustom-headerbar– applied to the custom title barheader-left– applied to the left header regionheader-right– applied to the right-side button areaheader-accent– applied to the narrow branding accent barheader-button– applied to the custom close button
3.2 CSS Loading and Deferred Provider Injection
After constructing the window hierarchy, the module invokes:
load_style();
Styling is then applied when the window is mapped, ensuring that the widget hierarchy is already realized before provider attachment:
signal_map().connect([this]() {
if (global_css)
get_style_context()->add_provider(global_css, GTK_STYLE_PROVIDER_PRIORITY_USER);
});
This deferred provider injection avoids timing issues that can occur when attaching CSS providers too early in the widget lifecycle.
4. Sidebar Navigation Model
4.1 Sidebar Composition
The sidebar is created by build_sidebar() as a vertical box with fixed width and expanding height:
sidebar_box_->set_size_request(220, -1);
Its structure contains:
- A header section with logo and localized title
- A separator line
- A menu container with leaf buttons and collapsible groups
- A footer spacer used to push content upward and stabilize layout
- A final separator at the bottom
The header attempts to load:
resource("logo-sidebar.png")
and displays the localized title:
_("Impostazioni")
4.2 Leaf Buttons vs Group Buttons
The navigation model distinguishes between two button constructors:
- Leaf buttons, created by
make_leaf_button() - Group buttons, created by
make_group_button()
Leaf buttons:
- Display only a label
- Represent a direct page target
- May be optionally indented using
sidebar-leaf-indent - Are tracked in
all_leaf_buttons_for active state management
Group buttons:
- Display a left icon, label, and right chevron
- Can create a hidden submenu container returned through
out_submenu - Toggle submenu visibility on click
- Update the chevron from
▸to▾depending on expanded state
Sidebar group icons are loaded through Gdk::Pixbuf, scaled to 16×16, and wrapped in Gtk::Picture.
4.3 Active Selection Handling
Active leaf highlighting is handled by activate_button(btn). The function removes active-sidebar-button from all tracked leaf buttons and applies it only to the selected one.
This guarantees that only one leaf entry is visually active at a time, even when leaf items belong to different expandable groups.
5. Page Routing and Stack Integration
5.1 Routing Strategy
Sidebar entries do not switch stack pages directly. Instead, they forward navigation to the embedded SettingsPage through:
settingsPage_->goto_page(index)
A local lambda named go is defined in build_sidebar() to map sidebar interactions to page indices.
5.2 Current Page Index Map
According to the current implementation, the sidebar routes menu items to the following SettingsPage indices:
- Base → page
0 - Avanzate → page
1 - Anti-Ransomware group:
- Base → page
2 - Avanzate → page
3
- Base → page
- Anti-Phishing group:
- Protezione → page
4 - Whitelist / Blacklist → page
5
- Protezione → page
- Pagamenti Sicuri → page
6 - Servizi group:
- Servizi Utente → page
7 - Email → page
8 - Servizi Sistema → page
9
- Servizi Utente → page
- DB ClamAV → page
10 - Opzioni → page
11
Compared to the earlier layout, the current implementation includes a dedicated top-level Email entry and shifts the later indices accordingly.
5.3 Initial State
After sidebar construction, the first leaf button is activated if available, and the settings page is explicitly initialized to page 0:
if (!all_leaf_buttons_.empty())
activate_button(all_leaf_buttons_.front());
settingsPage_->goto_page(0);
This ensures a deterministic initial selection and a synchronized visual state between sidebar and content.
6. Header Bar and Close Control
6.1 Custom Header Bar
The standard title bar is replaced with a custom Gtk::HeaderBar created by build_headerbar(). It suppresses default title buttons and window decoration layout through:
header->set_show_title_buttons(false);
header->set_decoration_layout("");
This gives the application full control over header appearance and window actions.
6.2 Left Accent Region
The header contains a left-aligned box sized to match the sidebar width:
header_left->set_size_request(220, -1);
Inside this area, a narrow accent widget is created with fixed width 6px and styled through the header-accent class. This acts as a visual brand marker aligned with the sidebar region.
6.3 Right Control Area and Close Button
The right side of the header contains a custom close button area styled through header-right.
The close button attempts to load:
resource("icon-close.png")
The image is loaded at 32×32 and used inside a GTK button with the header-button class. On click, the handler executes:
this->close();
If the icon cannot be loaded, the implementation writes a localized warning to standard error instead of failing hard.
7. Resource Handling and Internationalization
7.1 Resource Loading
The window resolves visual assets through the resource(...) helper and loads them via Gdk::Pixbuf::create_from_file().
Current assets include:
logo-sidebar.png– used in the sidebar header, requested at48×48icons/ransomware.svg– used for the Anti-Ransomware groupicons/scan.svg– used for the Anti-Phishing groupicons/settings.svg– used for the Services groupicon-close.png– used for the custom close button at32×32
Group icons are scaled to 16×16 using scale_simple(..., Gdk::InterpType::BILINEAR) before being converted to a Gdk::Texture and displayed in a Gtk::Picture.
7.2 Translated UI Strings
All visible labels are localized through _() from glibmm/i18n.h. This includes sidebar items, group titles, the settings title label, and error/warning output messages.
Examples include:
_("Impostazioni")_("Base")_("Avanzate")_("Anti-Ransomware")_("Anti-Phishing")_("Pagamenti Sicuri")_("Servizi Utente")_("Servizi Sistema")_("Email")_("DB ClamAV")_("Opzioni")
8. Runtime and UX Considerations
Deterministic startup state: the first leaf entry is activated automatically and the initial page is forced to index 0
Separation of concerns: SettingsWindow handles framing, navigation, and visual structure, while SettingsPage owns the actual functional settings UI
Consistent active navigation: active leaf selection is explicitly controlled through a dedicated CSS class
Expandable groups: nested sections reduce clutter while preserving discoverability of secondary pages
Responsive layout: the stack and main containers are configured with horizontal and vertical expansion for flexible resizing
Stable visual geometry: sidebar width and header-left width are intentionally aligned at 220px to maintain layout consistency
Robust resource handling: icon loading is wrapped in try/catch blocks to avoid crashes when resources are missing
Deferred CSS application: style provider injection is performed on map to avoid premature styling issues