How to Add New Terminal Screens to the Fincept Qt6 Application

To add new terminal screens to the Fincept Qt6 application, create a QWidget-derived class, register it with the DockScreenRouter using either eager or lazy factory registration in MainWindow::setup_app_screens(), and optionally implement IStatefulScreen for UI state persistence.

The Fincept Terminal is a Qt6-based financial terminal that uses an Advanced Docking System (ADS) to manage multiple screens. When you need to add new terminal screens to the Fincept Qt6 application, you will work with the DockScreenRouter class to handle registration, navigation, and state management across the docking grid.

Step 1: Create the Screen Class

Every terminal screen must derive from QWidget. If the screen needs to persist UI state across sessions, it should also inherit from fincept::screens::IStatefulScreen and implement the required virtual methods.

Deriving from QWidget

Create your screen in the fincept-qt/src/screens/ directory structure:

// fincept-qt/src/screens/my_screen/MyScreen.h
#pragma once
#include <QWidget>

namespace fincept::screens {

class MyScreen : public QWidget {
    Q_OBJECT
public:
    explicit MyScreen(QWidget* parent = nullptr);
    ~MyScreen() override;
};

} // namespace fincept::screens
// fincept-qt/src/screens/my_screen/MyScreen.cpp
#include "screens/my_screen/MyScreen.h"
#include <QLabel>
#include <QVBoxLayout>

namespace fincept::screens {

MyScreen::MyScreen(QWidget* parent) : QWidget(parent) {
    auto* layout = new QVBoxLayout(this);
    layout->addWidget(new QLabel("Hello Fincept!"));
}

MyScreen::~MyScreen() = default;

} // namespace fincept::screens

Implementing IStatefulScreen for Persistence

If your screen needs to remember user preferences or UI configuration, implement the IStatefulScreen interface located in fincept-qt/src/screens/IStatefulScreen.h:

// fincept-qt/src/screens/my_screen/MyScreen.h
#pragma once
#include <QWidget>
#include "screens/IStatefulScreen.h"

namespace fincept::screens {

class MyScreen : public QWidget, public IStatefulScreen {
    Q_OBJECT
public:
    explicit MyScreen(QWidget* parent = nullptr);
    ~MyScreen() override = default;

    // IStatefulScreen implementation
    QVariantMap save_state() const override;
    void restore_state(const QVariantMap& state) override;
    QString state_key() const override { return "my_screen"; }
};

} // namespace fincept::screens
// fincept-qt/src/screens/my_screen/MyScreen.cpp
#include "screens/my_screen/MyScreen.h"
#include <QLabel>
#include <QVBoxLayout>

namespace fincept::screens {

MyScreen::MyScreen(QWidget* parent) : QWidget(parent) {
    auto* layout = new QVBoxLayout(this);
    layout->addWidget(new QLabel("Hello Fincept!"));
}

QVariantMap MyScreen::save_state() const {
    return { {"exampleFlag", true} };
}

void MyScreen::restore_state(const QVariantMap& state) {
    // Read back persisted values
    Q_UNUSED(state);
}

} // namespace fincept::screens

Step 2: Register the Screen with DockScreenRouter

All screen registration happens in MainWindow::setup_app_screens() in fincept-qt/src/app/MainWindow.cpp (around line 801). The DockScreenRouter supports two registration patterns.

Lazy Factory Registration (Preferred)

Use register_factory() for heavy screens that should only instantiate when first navigated to. The factory lambda is stored in DockScreenRouter::factories_ until the first navigation triggers it (see lines 86-88 in DockScreenRouter.cpp):

// In MainWindow::setup_app_screens()
dock_router_->register_factory(
    "my_screen",
    []() { return new screens::MyScreen; });

Eager Registration

For ultra-lightweight utility screens that must exist immediately, use register_screen():

dock_router_->register_screen(
    "about",
    new screens::AboutScreen);

Step 3: Add a Human-Readable Title

To display a friendly name in the tab header instead of the raw ID, add an entry to DockScreenRouter::title_for_id() in fincept-qt/src/app/DockScreenRouter.cpp (around lines 71-74):

QString DockScreenRouter::title_for_id(const QString& id) {
    static const QHash<QString, QString> titles{
        // Existing entries...
        {"my_screen", "My Custom Screen"},
        {"about", "About"},
        // ...
    };
    return titles.value(id, id);
}

Step 4: Navigate to the Screen

Any UI element (menu, button, or shortcut) can trigger navigation using the screen ID:

connect(ui->actionMyScreen, &QAction::triggered,
        this, [this]() { dock_router_->navigate("my_screen"); });

The DockScreenRouter::navigate() method (starting at line 90 in DockScreenRouter.cpp) performs four operations:

  1. Locates or creates the ads::CDockWidget wrapper
  2. Materializes the screen widget if it was lazily registered
  3. Places the widget in the 2×2 docking grid or tabs it into an existing area
  4. Persists the current screen ID via SessionManager (see lines 68-70 in DockScreenRouter.cpp)

Step 5: Handle State Persistence

If your screen implements IStatefulScreen, DockScreenRouter automatically manages state persistence:

  • When the screen is hidden or the application shuts down, DockScreenRouter::save_screen_state() calls your save_state() implementation
  • On application startup or when the screen is first shown, DockScreenRouter::restore_screen_state() invokes your restore_state() method after the dock widget materializes

These methods are located near the bottom of DockScreenRouter.cpp (search for save_screen_state and restore_screen_state). No additional wiring is required beyond implementing the interface.

Summary

  • Create your screen by deriving from QWidget in fincept-qt/src/screens/<your_screen>/ and optionally implementing IStatefulScreen for persistence.
  • Register the screen in MainWindow::setup_app_screens() using register_factory() for lazy loading or register_screen() for eager instantiation.
  • Title the screen by adding an entry to DockScreenRouter::title_for_id() in DockScreenRouter.cpp.
  • Navigate using dock_router_->navigate("screen_id") from menus, buttons, or shortcuts.
  • Persist UI state automatically by implementing the IStatefulScreen interface methods.

Frequently Asked Questions

Where do I place the new screen files in the Fincept Qt6 codebase?

Place your header and implementation files in fincept-qt/src/screens/<your_screen>/ (for example, fincept-qt/src/screens/my_screen/MyScreen.h and MyScreen.cpp). This follows the existing project structure where each screen resides in its own directory under the screens folder.

What is the difference between eager and lazy registration in DockScreenRouter?

Eager registration using register_screen() creates the widget immediately and keeps it in memory, which is suitable for lightweight utility screens like an About dialog. Lazy registration using register_factory() stores a factory lambda that only instantiates the widget on first navigation, which is preferred for heavy screens to improve startup performance and reduce memory usage.

How does Fincept Qt6 save and restore screen state across sessions?

If your screen implements the IStatefulScreen interface and provides save_state(), restore_state(), and state_key() methods, the DockScreenRouter automatically calls save_state() when the screen closes or the app shuts down, and restore_state() when the screen is recreated. The data is persisted through the SessionManager to maintain UI configuration across application restarts.

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 →