docs(c++): OriginSelectorWidget toolbar integration
Covers widget lifecycle, signal connections (fastsignals), menu population, origin selection with authentication gate, icon overlays for connection state, StdCmdOrigin/OriginSelectorAction command wrappers, and StdWorkbench toolbar placement. Closes #133
This commit is contained in:
@@ -53,4 +53,5 @@
|
||||
- [LocalFileOrigin](./reference/cpp-local-file-origin.md)
|
||||
- [OriginManager](./reference/cpp-origin-manager.md)
|
||||
- [CommandOrigin](./reference/cpp-command-origin.md)
|
||||
- [OriginSelectorWidget](./reference/cpp-origin-selector-widget.md)
|
||||
- [FileOriginPython Bridge](./reference/cpp-file-origin-python.md)
|
||||
|
||||
224
docs/src/reference/cpp-origin-selector-widget.md
Normal file
224
docs/src/reference/cpp-origin-selector-widget.md
Normal file
@@ -0,0 +1,224 @@
|
||||
# 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:
|
||||
|
||||
```cpp
|
||||
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)`:
|
||||
|
||||
```cpp
|
||||
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:
|
||||
|
||||
```cpp
|
||||
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:
|
||||
|
||||
```cpp
|
||||
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:
|
||||
|
||||
```cpp
|
||||
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
|
||||
|
||||
```cpp
|
||||
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
|
||||
|
||||
- [FileOrigin Interface](./cpp-file-origin.md) — abstract base class
|
||||
- [LocalFileOrigin](./cpp-local-file-origin.md) — built-in default origin
|
||||
- [OriginManager](./cpp-origin-manager.md) — singleton registry and signal source
|
||||
- [CommandOrigin](./cpp-command-origin.md) — PLM commands in the Origin Tools toolbar
|
||||
Reference in New Issue
Block a user