Files
create/docs/src/reference/cpp-origin-selector-widget.md
forbes 87a0af0b0f phase 1: copy Kindred-only files onto upstream/main (FreeCAD 1.2.0-dev)
Wholesale copy of all Kindred Create additions that don't conflict with
upstream FreeCAD code:

- kindred-icons/ (1444 Catppuccin Mocha SVG icon overrides)
- src/Mod/Create/ (Kindred Create workbench)
- src/Gui/ Kindred source files (FileOrigin, OriginManager,
  OriginSelectorWidget, CommandOrigin, BreadcrumbToolBar, EditingContext)
- src/Gui/Icons/ (Kindred branding and silo icons)
- src/Gui/PreferencePacks/KindredCreate/
- src/Gui/Stylesheets/ (KindredCreate.qss, images_dark-light/)
- package/ (rattler-build recipe)
- docs/ (architecture, guides, specifications)
- .gitea/ (CI workflows, issue templates)
- mods/silo, mods/ztools submodules
- .gitmodules (Kindred submodule URLs)
- resources/ (kindred-create.desktop, kindred-create.xml)
- banner-logo-light.png, CONTRIBUTING.md
2026-02-13 14:03:58 -06:00

9.1 KiB

OriginSelectorWidget

OriginSelectorWidget is a toolbar dropdown that lets users switch between registered file origins (Local Files, Silo, etc.). It appears as the first item in the File toolbar across all workbenches.

  • Header: src/Gui/OriginSelectorWidget.h
  • Source: src/Gui/OriginSelectorWidget.cpp
  • Base class: QToolButton
  • Command ID: Std_Origin

Widget appearance

The button shows the current origin's nickname and icon. Clicking opens a dropdown menu listing all registered origins with a checkmark on the active one.

┌────────────────┐
│  Local    ▼  │     ← nickname + icon, InstantPopup mode
└────────────────┘

Dropdown:
  ✓  Local           ← checked = current origin
     Kindred Silo       ← disconnected origins show status overlay
  ──────────────────
     Manage Origins...  ← opens OriginManagerDialog

Size constraints

Property Value
Popup mode QToolButton::InstantPopup
Button style Qt::ToolButtonTextBesideIcon
Minimum width 80 px
Maximum width 160 px
Size policy Preferred, Fixed

Lifecycle

Construction

The constructor calls four setup methods in order:

  1. setupUi() — Configures the button, creates m_menu (QMenu) and m_originActions (exclusive QActionGroup). Connects the action group's triggered signal to onOriginActionTriggered.
  2. connectSignals() — Subscribes to three OriginManager fastsignals via scoped_connection objects.
  3. rebuildMenu() — Populates the menu from the current OriginManager state.
  4. updateDisplay() — Sets the button text, icon, and tooltip to match the current origin.

Destruction

The destructor calls disconnectSignals(), which explicitly disconnects the three fastsignals::scoped_connection members. The scoped connections would auto-disconnect on destruction regardless, but explicit disconnection prevents any signal delivery during teardown.

Signal connections

The widget subscribes to three OriginManager fastsignals:

OriginManager signal Widget handler Response
signalOriginRegistered onOriginRegistered(id) Rebuild menu
signalOriginUnregistered onOriginUnregistered(id) Rebuild menu
signalCurrentOriginChanged onCurrentOriginChanged(id) Update display + menu checkmarks

Connections are stored as fastsignals::scoped_connection members for RAII lifetime management:

fastsignals::scoped_connection m_connRegistered;
fastsignals::scoped_connection m_connUnregistered;
fastsignals::scoped_connection m_connChanged;

Menu population

rebuildMenu() rebuilds the entire dropdown from scratch each time an origin is registered or unregistered:

  1. Clear m_menu and remove all actions from m_originActions.
  2. Iterate OriginManager::originIds() (returns std::vector<std::string> in registration order).
  3. For each origin ID, create a QAction with:
    • Icon from iconForOrigin(origin) (with connection-state overlay)
    • Text from origin->nickname()
    • Tooltip from origin->name()
    • setCheckable(true), checked if this is the current origin
    • Data set to the origin ID string
  4. Add a separator.
  5. Add a "Manage Origins..." action with the preferences-system theme icon, connected to onManageOriginsClicked.

Origins appear in the menu in the order returned by OriginManager::originIds(). The local origin is always first (registered at startup), followed by Python-registered origins in registration order.

Origin selection

When the user clicks an origin in the dropdown, onOriginActionTriggered(QAction*) runs:

  1. Extract the origin ID from action->data().
  2. Look up the FileOrigin from OriginManager.
  3. Authentication gate: If origin->requiresAuthentication() is true and the connection state is Disconnected or Error:
    • Call origin->connect().
    • If connection fails, revert the menu checkmark to the previous origin and return without changing.
  4. On success, call OriginManager::setCurrentOrigin(originId).

This means selecting a disconnected PLM origin triggers an automatic reconnection attempt. The user sees no change if the connection fails.

Icon overlays

iconForOrigin(FileOrigin*) generates a display icon with optional connection-state indicators:

Connection state Overlay Position
Connected None
Connecting None (TODO: animated)
Disconnected dagViewFail (8x8 px, red) Bottom-right
Error Warning (8x8 px, yellow) Bottom-right

Overlays are only applied to origins where requiresAuthentication() returns true. Local origins never get overlays. The merge uses BitmapFactoryInst::mergePixmap with BottomRight placement.

Display updates

updateDisplay() sets the button face to reflect the current origin:

  • Text: origin->nickname() (e.g. "Local", "Silo")
  • Icon: Result of iconForOrigin(origin) (with possible overlay)
  • Tooltip: origin->name() (e.g. "Local Files", "Kindred Silo")
  • No origin: Text becomes "No Origin", icon and tooltip are cleared

This method runs on construction, on signalCurrentOriginChanged, and after the Manage Origins dialog closes.

Command wrapper

The widget is exposed to the command/toolbar system through two classes.

StdCmdOrigin

Defined in src/Gui/CommandStd.cpp using DEF_STD_CMD_AC(StdCmdOrigin):

StdCmdOrigin::StdCmdOrigin()
    : Command("Std_Origin")
{
    sGroup      = "File";
    sMenuText   = QT_TR_NOOP("&Origin");
    sToolTipText = QT_TR_NOOP("Select file origin (Local Files, Silo, etc.)");
    sWhatsThis  = "Std_Origin";
    sStatusTip  = sToolTipText;
    sPixmap     = "folder";
    eType       = 0;
}
Property Value
Command ID Std_Origin
Menu group File
Icon folder
isActive() Always true
activated() No-op (widget handles interaction)

createAction() returns an OriginSelectorAction instance.

OriginSelectorAction

Defined in src/Gui/Action.h / Action.cpp. Bridges the command system and the widget:

void OriginSelectorAction::addTo(QWidget* widget)
{
    if (widget->inherits("QToolBar")) {
        auto* selector = new OriginSelectorWidget(widget);
        static_cast<QToolBar*>(widget)->addWidget(selector);
    } else {
        widget->addAction(action());
    }
}

When added to a QToolBar, it instantiates an OriginSelectorWidget. When added to a menu or other container, it falls back to adding a plain QAction.

Toolbar integration

StdWorkbench::setupToolBars() in src/Gui/Workbench.cpp places the widget:

auto file = new ToolBarItem(root);
file->setCommand("File");
*file << "Std_Origin"     // ← origin selector (first)
      << "Std_New"
      << "Std_Open"
      << "Std_Save";

The widget appears as the first item in the File toolbar. Because StdWorkbench is the base workbench, this placement is inherited by all workbenches (Part Design, Assembly, Silo, etc.).

A separate "Origin Tools" toolbar follows with PLM-specific commands:

auto originTools = new ToolBarItem(root);
originTools->setCommand("Origin Tools");
*originTools << "Origin_Commit" << "Origin_Pull" << "Origin_Push"
             << "Separator"
             << "Origin_Info" << "Origin_BOM";

These commands auto-disable based on the current origin's capability flags (supportsRevisions, supportsBOM, etc.).

Member variables

QMenu*                          m_menu;              // dropdown menu
QActionGroup*                   m_originActions;     // exclusive checkmark group
QAction*                        m_manageAction;      // "Manage Origins..." action

fastsignals::scoped_connection  m_connRegistered;    // signalOriginRegistered
fastsignals::scoped_connection  m_connUnregistered;  // signalOriginUnregistered
fastsignals::scoped_connection  m_connChanged;       // signalCurrentOriginChanged

Behavioural notes

  • Origin scope is global, not per-document. Switching origin affects all subsequent New/Open/Save operations. Existing open documents retain their origin association via OriginManager::_documentOrigins.
  • Menu rebuilds are full rebuilds. On any registration/unregistration event, the entire menu is cleared and rebuilt. This is simple and correct — origin counts are small (typically 2-3).
  • No document-switch tracking. The widget does not respond to active document changes. The current origin is a user preference, not derived from the active document.
  • Thread safety. All operations assume the Qt main thread. Signal emissions from OriginManager are synchronous.

See also