SettingsWindow (Core UI Shell)

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 SettingsPage during 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::HeaderBar created by build_headerbar()
  • Gtk::Box mainBox_ as the primary horizontal container
  • Gtk::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 itself
  • settings-main – applied to mainBox_
  • content – applied to the stack content area
  • sidebar – applied to the left navigation container
  • sidebar-header – applied to the sidebar header section
  • sidebar-title – applied to the localized sidebar title
  • sidebar-separator – applied to separator widgets
  • sidebar-button – base class for sidebar buttons
  • sidebar-leaf – applied to leaf navigation items
  • sidebar-leaf-indent – applied to nested leaf items
  • sidebar-submenu – applied to collapsible submenu containers
  • active-sidebar-button – applied dynamically to the active leaf button
  • custom-headerbar – applied to the custom title bar
  • header-left – applied to the left header region
  • header-right – applied to the right-side button area
  • header-accent – applied to the narrow branding accent bar
  • header-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
  • Anti-Phishing group:
    • Protezione → page 4
    • Whitelist / Blacklist → page 5
  • Pagamenti Sicuri → page 6
  • Servizi group:
    • Servizi Utente → page 7
    • Email → page 8
    • Servizi Sistema → page 9
  • 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 at 48×48
  • icons/ransomware.svg – used for the Anti-Ransomware group
  • icons/scan.svg – used for the Anti-Phishing group
  • icons/settings.svg – used for the Services group
  • icon-close.png – used for the custom close button at 32×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