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.