Architecture

Lexicon follows a deliberately simple architecture: Qt Widgets UI classes on top, one static data-access class at the bottom, plain C++ record structs in between. No frameworks, no layers you have to guess about.

Source modules

File(s)Responsibility
main.cpp Entry point. Initializes the database via DatabaseManager::initialize() (creating lexicon.db next to the executable and applying migrations), then constructs and shows MainWindow.
MainWindow.{h,cpp} The largest UI class. Owns the filter row (map/tag/flag/status/understanding/pinned/search), the paginated and sortable term table, the pagination bar, the rendered content pane with links/backlinks summary, the menu bar (maps manager, global overviews, theme switch), and the Add/Edit/Delete actions.
TermEditDialog.{h,cpp} The five-tab term editor: General (identity and state), Content (Markdown editor with toolbar and live preview), Metadata (tags/flags/aliases lists), Links, and Backlinks (typed relationships).
MapManagerDialog.{h,cpp} CRUD dialog for maps (add, rename/describe, delete).
ValueListDialog.{h,cpp} Read-only overview dialog used for the All tags / All flags / All aliases views, showing values with usage counts.
DatabaseManager.{h,cpp} The entire persistence layer: connection setup, pragma configuration, schema migrations, and every query the app runs (term listing with filters/paging/sorting, CRUD for maps/terms/links, metadata handling, usage statistics, operation logging).
MarkdownConverter.{h,cpp} Thin wrapper around the vendored md4qt parser: converts Markdown term content to HTML for the preview panes.
3rdparty/md4qt/ Vendored Markdown parser (CommonMark + GFM extensions such as tables, strikethrough, autolinks, footnotes). Compiled directly into the executable — no external dependency to install.

Core data structures

UI and data layer communicate through plain structs defined in DatabaseManager.h:

struct MapRecord {
    int id = -1;
    QString name;
    QString description;
};

struct TermRecord {
    int id = -1;
    int mapId = -1;
    QString mapName;
    QString title;
    QString disambiguation;
    QStringList aliases;
    QStringList tags;
    QStringList flags;
    TermStatus status = TermStatus::None;
    UnderstandingLevel understanding = UnderstandingLevel::Unknown;
    bool pinned = false;
    QString content;          // Markdown source
};

struct LinkRecord {
    int id = -1;
    int fromTermId = -1;
    int toTermId = -1;
    LinkType linkType = LinkType::None;
    int position = 0;
    QString fromTermTitle;    // optional, filled for UI display
    QString toTermTitle;
};

struct UsageValueRecord {     // rows of the tag/flag/alias overviews
    QString value;
    int usageCount = 0;
};

Enums

Three enums encode the domain vocabulary. Their integer values are stored directly in SQLite, so they must never be renumbered — only appended to.

enum class TermStatus {
    None = 0, Draft = 1, Completed = 2
};

enum class UnderstandingLevel {
    Unknown = 0,      // Never encountered
    Recognized = 1,   // Seen before, can identify
    Understood = 2,   // Conceptually grasped
    Practiced = 3,    // Can apply in real situations
    Mastered = 4      // Fully internalized, can teach or innovate
};

enum class LinkType {
    None = 0, IsA = 1, PartOf = 2, Uses = 3, DependsOn = 4,
    Implements = 5, Related = 6, Contrasts = 7,
    AlternativeTo = 8, ParentOf = 9
};

Design decisions

Static data-access API

DatabaseManager exposes only static methods over Qt's default database connection. There is no ORM and no repository hierarchy — every operation is an explicit, reviewable SQL statement. Errors are reported through an optional QString* errorMessage out-parameter combined with a bool return value:

QString error;
if (!DatabaseManager::saveTerm(term, &error)) {
    QMessageBox::warning(this, "Save failed", error);
}

Filtering and paging in SQL

loadTerms() takes the whole filter state (map, search text, tag, flag, understanding, status, pinned), plus limit/offset and the sort column/order, and translates them into one SQL query. The UI never filters in memory, which is what keeps large lexicons fast. A matching countTerms() powers the pagination bar.

Operation log

Every create, update, delete — and term read — is recorded in the log table with a type code (1=created, 2=updated, 3=deleted, 4=read) and a timestamp. Nothing consumes the log in the UI yet; it is groundwork for future features such as recent-activity views or spaced-repetition hints.

Referential integrity in the database

Foreign keys are switched on with PRAGMA foreign_keys = ON at connection time, and dependent rows (aliases, tags, flags, links) are declared ON DELETE CASCADE. Deletion consistency is the database's job, not scattered application code — see the schema reference.

Vendored Markdown parser

md4qt is compiled into the binary from 3rdparty/ rather than linked as an external package. That keeps the dependency list at exactly "Qt", which makes the project easy to build on any platform.