How UI Components Are Managed in the Amnezia VPN Client: Qt/QML Architecture Explained

The Amnezia VPN client manages UI components through a hybrid architecture where the AmneziaApplication C++ class instantiates a QQmlApplicationEngine to load declarative QML pages, while specialized C++ controllers exposed as context properties handle focus management, navigation, and business logic.

The Amnezia VPN client repository (amnezia-vpn/amnezia-client) implements a clean separation between presentation and logic. How UI components are managed in Amnezia VPN client centers on Qt Quick/QML for the visual layer, with C++ controller classes bridging the gap between the declarative frontend and the VPN backend services.

Application Bootstrap and Engine Initialization

The UI lifecycle begins in the AmneziaApplication class, which acts as the central coordinator for the Qt Quick runtime.

AmneziaApplication Class

In client/amneziaApplication.cpp, the AmneziaApplication constructor creates the QQmlApplicationEngine instance that powers the entire interface. This engine manages the component tree, resource loading, and QML context properties.

// client/amneziaApplication.cpp - simplified excerpt
void AmneziaApplication::init()
{
    m_engine = new QQmlApplicationEngine;
    
    // Register controller objects with the root context
    QQmlContext *ctx = m_engine->rootContext();
    ctx->setContextProperty("focusController", new FocusController(m_engine));
    
    // Load the root QML document
    m_engine->load(QUrl(QStringLiteral("qrc:/client/ui/qml/main2.qml")));
}

According to the Amnezia VPN source code, the engine initialization sequence ensures that controller objects are instantiated and exposed to QML before the root component loads, allowing QML files to access these objects immediately upon creation.

Root QML Entry Point

The engine loads client/ui/qml/main2.qml, which serves as the application's root window. This file establishes the fundamental visual structure, typically containing a StackView for page-based navigation and the global synchronization engine.

// client/ui/qml/main2.qml - conceptual structure
import QtQuick 2.15
import QtQuick.Controls 2.15

ApplicationWindow {
    id: root
    visible: true
    
    StackView {
        id: stackView
        anchors.fill: parent
        initialItem: "Pages2/PageHome.qml"
    }
}

Declarative UI Component Organization

The QML source tree follows a strict organizational pattern that separates pages, reusable controls, and styling logic.

Page Components (Pages2)

Individual screens reside in client/ui/qml/Pages2/, with each major feature implemented as a distinct QML file. Key pages include:

  • PageHome.qml – Main dashboard displaying connection status
  • PageSettings.qml – Configuration and preferences interface
  • PageSetupWizardStart.qml – Initial VPN setup flow

These components are standard QML Page types that declare their own visual hierarchy while inheriting shared styling from the global theme module.

Shared Controls and Styling

Reusable UI elements are centralized under client/ui/qml/Controls2/, preventing duplication across pages. The visual theme is defined in client/ui/qml/Modules/Style/AmneziaStyle.qml, which exports color palettes, font metrics, and spacing constants.

// client/ui/qml/Controls2/ButtonTextType.qml
import QtQuick 2.15
import "../Modules/Style"

Text {
    font.family: AmneziaStyle.fontFamily
    color: AmneziaStyle.primaryColor
    font.pixelSize: AmneziaStyle.textSizeMedium
}

This modular approach ensures consistent styling across the application; changes to AmneziaStyle.qml propagate automatically to all controls.

C++ Controllers: Logic Bridge to QML

While the UI is declarative, complex interactions—particularly focus management and backend communication—are handled by C++ controllers instantiated by AmneziaApplication.

FocusController Implementation

The FocusController class in client/ui/controllers/qml/focusController.cpp demonstrates the controller pattern. As implemented in amnezia-vpn/amnezia-client, it connects to the QQmlApplicationEngine::objectCreated signal to locate the defaultFocusItem object by name and implements custom keyboard navigation logic.

// client/ui/controllers/qml/focusController.cpp
FocusController::FocusController(QQmlApplicationEngine *engine, QObject *parent)
    : QObject(parent), m_engine(engine)
{
    connect(m_engine, &QQmlApplicationEngine::objectCreated,
            this, &FocusController::onObjectCreated);
}

void FocusController::onObjectCreated(QObject *object, const QUrl &url)
{
    // Locate default focus item by objectName
    if (QObject *focusItem = object->findChild<QObject*>("defaultFocusItem")) {
        // Initialize focus tracking for this page
    }
}

This controller exposes methods like nextKeyTabItem() and nextKeyUpItem() as QML-callable slots.

Exposing Controllers to QML

Controllers are registered as context properties in AmneziaApplication::init(), making them accessible to any QML file loaded by the engine:

ctx->setContextProperty("focusController", m_focusController);

QML pages invoke these controller methods directly in response to user input:

// client/ui/qml/Pages2/PageHome.qml
Page {
    Keys.onTabPressed: focusController.nextKeyTabItem()
    Keys.onBackPressed: focusController.nextKeyUpItem()
    
    // Component definition...
}

Navigation follows a stack-based pattern managed by the StackView defined in main2.qml. Controllers can trigger navigation changes by emitting signals that QML handlers catch, or by invoking QMetaObject::invokeMethod on QML objects.

StackView Page Management

New pages are pushed onto the stack declaratively:

// Within a page, navigating to settings
stackView.push("PageSettings.qml")

Or dynamically from C++:

// Controller pushing a new page
QMetaObject::invokeMethod(rootObject, "pushPage",
                          Q_ARG(QVariant, QVariant::fromValue(QString("PageSettings.qml"))));

Summary

  • Engine Ownership: The AmneziaApplication class in client/amneziaApplication.cpp creates and owns the QQmlApplicationEngine, loading client/ui/qml/main2.qml as the root component.
  • Component Organization: UI pages reside in client/ui/qml/Pages2/, reusable controls in client/ui/qml/Controls2/, and global styling in client/ui/qml/Modules/Style/AmneziaStyle.qml.
  • Controller Pattern: C++ controllers like FocusController (client/ui/controllers/qml/focusController.cpp) are exposed as context properties to handle complex logic while keeping QML declarative.
  • Navigation: The StackView in main2.qml manages page transitions, with pages pushed declaratively or via C++ to QML invocation.

Frequently Asked Questions

What is the entry point for the Amnezia VPN UI?

The entry point is client/ui/qml/main2.qml, which is loaded by the QQmlApplicationEngine instantiated in the AmneziaApplication class within client/amneziaApplication.cpp. This root QML file sets up the application window and the StackView that manages page navigation.

How does the Amnezia VPN client handle keyboard navigation?

Keyboard navigation is managed by the FocusController class located in client/ui/controllers/qml/focusController.cpp. This C++ controller connects to the QML engine's objectCreated signal to track focus items by object name and exposes methods like nextKeyTabItem() that QML pages call in response to key events such as Keys.onTabPressed.

Where are UI components defined in the Amnezia VPN client source code?

UI components are defined in three primary locations according to the repository structure: page-specific screens under client/ui/qml/Pages2/ (e.g., PageHome.qml), reusable control elements under client/ui/qml/Controls2/, and the visual theme definition in client/ui/qml/Modules/Style/AmneziaStyle.qml.

How is styling managed across the Amnezia VPN client UI?

Styling is centralized in the AmneziaStyle.qml module, which defines color constants, font families, and spacing values. All QML controls import this module, ensuring consistent theming across the application; modifying AmneziaStyle.qml immediately updates the appearance of all dependent components without requiring changes to individual control files.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →