Badge system wiring

Table of Contents

Summary

ORE Studio renders pill badges in Qt list views via a three-layer DB-driven system: ores.dq owns the four domain entities (badge_severity, code_domain, badge_definition, badge_mapping); ores.sql seeds them at install time; and ores.qt.api's BadgeCache loads them at login and resolves (domain, entity_code) pairs to colours at paint time. Adding a new badge requires no C++ changes to the resolution logic — only a SQL population entry and a one-line delegate call. Return to Knowledge.

Detail

The four domain entities

All four live in the dq schema and are managed by ores.dq.

Entity Table Purpose
badge_severity ores_dq_badge_severities_tbl Named severity levels (secondary, info, success, warning, danger, primary). Codes align with Bootstrap 5 classes so Wt needs no translation.
code_domain ores_dq_code_domains_tbl Named namespace that disambiguates codes across entity types (e.g. account_type, login_status).
badge_definition ores_dq_badge_definitions_tbl One row per visual variant: label, background colour, text colour, severity, CSS class hint.
badge_mapping ores_dq_badge_mappings_tbl Junction: maps (code_domain, entity_code)badge_definition.

SQL population

All badge metadata lives in one idempotent script:

projects/ores.sql/populate/dq/dq_badge_system_populate.sql

The script uses upsert functions so it is safe to re-run. The structure per new badge domain is always three blocks:

  1. Code domain — one ores_dq_code_domains_upsert_fn call naming the domain and its display_order.
  2. Badge definitions — one ores_dq_badge_definitions_upsert_fn call per distinct visual variant (label, hex colours, severity code, CSS class, display_order).
  3. Badge mappings — one ores_dq_badge_mappings_upsert_fn call per value, mapping (domain, entity_code)badge_definition_code.

The entity code in the mapping must match exactly what the Qt model returns for that column (see §Qt wiring below).

BadgeCache

BadgeCache (projects/ores.qt/api/include/ores.qt/BadgeCache.hpp) is a session-scoped cache populated at login from the standard service/protocol/client pipeline. It exposes one method:

const badge_definition* resolve(
    const std::string& domain,
    const std::string& entity_code) const;

Returns nullptr on cache miss — callers must fall back to a default colour. The cache is injected into every ItemDelegate at construction via the plugin_context received in on_login.

Qt wiring — item delegate

Each entity list view has an ItemDelegate subclass (e.g. AccountItemDelegate) that overrides paint() and sizeHint().

paint():

if (index.column() == Column::MyField /* || ... */) {
    QStyle* style = QApplication::style();
    style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter);

    static const QColor default_gray(107, 114, 128);
    static const QColor white(255, 255, 255);
    QColor bgColor = default_gray;
    QColor textColor = white;

    QString text = index.data(Qt::DisplayRole).toString();
    QString badgeText = text; // or a translated label

    if (badgeCache_) {
        auto* def = badgeCache_->resolve("my_domain", text.toStdString());
        if (def) {
            bgColor = QColor(QString::fromStdString(def->background_colour));
            textColor = QColor(QString::fromStdString(def->text_colour));
        }
    }

    QFont badgeFont = opt.font;
    badgeFont.setPointSize(qRound(badgeFont.pointSize() * 0.8));
    badgeFont.setBold(true);
    DelegatePaintUtils::draw_centered_badge(
        painter, opt.rect, badgeText, bgColor, textColor, badgeFont);
    return;
}

sizeHint(): add the same column guard and set a minimum height of 24 and a minimum width appropriate for the longest label (typically 50–90 px).

The entity code passed to resolve() must exactly match the string the model returns for Qt::DisplayRole on that column. If the model returns a display name (tr("Online")), the badge mapping key must use that display name. If the model returns the raw DB code ("user"), the mapping key must use the raw code.

Worked example — account_type

account_type badge (added in sprint 19):

  • Code domain: account_type, display_order 17.
  • Four badge definitions: account_type_user (info/#3b82f6), account_type_service (secondary/#6b7280), account_type_algorithm (warning/#eab308), account_type_llm (primary/#7c3aed).
  • Four mappings keyed on the raw DB codes: 'user', 'service', 'algorithm', 'llm' (because ClientAccountModel returns account.account_type verbatim).
  • Delegate: Column::AccountType added to the badge guard in AccountItemDelegate::paint() and sizeHint() (min width 80 px).

See also

Emacs 29.1 (Org mode 9.6.6)