Origin selector toolbar widget #13

Closed
opened 2026-02-05 18:28:00 +00:00 by forbes · 1 comment
Owner

Overview

Create a compact dropdown widget for the main toolbar that allows users to select the current file origin. This is the primary UI for switching between Local Files and Silo instances.

Parent Issue

Epic: #8 Unified File Origin System

Goals

  1. Create a visually compact, always-visible origin selector
  2. Show current origin with icon and nickname
  3. Dropdown menu listing all available origins
  4. Show connection status for remote origins
  5. Quick access to origin management

Detailed Design

Visual Design

Collapsed (toolbar state):
┌──────────────────┐
│ ☁️ Work      ▼  │   ~70-80px wide
└──────────────────┘

Expanded (dropdown open):
┌──────────────────┐
│ ☁️ Work      ▼  │
├──────────────────┤
│ ✓ ☁️ Work       │  ← Current selection (checkmark)
│   ☁️ Prod       │  
│   ⚠️ Staging    │  ← Disconnected (warning icon)
│   📁 Local      │
├──────────────────┤
│   ⚙️ Manage...  │  ← Opens config dialog
└──────────────────┘

Widget Implementation

// src/Gui/OriginSelectorWidget.h

class OriginSelectorWidget : public QToolButton {
    Q_OBJECT
public:
    explicit OriginSelectorWidget(QWidget* parent = nullptr);
    
private Q_SLOTS:
    void onCurrentOriginChanged(FileOrigin* origin);
    void onOriginsListChanged();
    void onOriginSelected(QAction* action);
    void onManageOriginsClicked();
    void onConnectionStateChanged(FileOrigin* origin);
    
private:
    void updateDisplay();
    void rebuildMenu();
    QIcon statusIcon(FileOrigin* origin) const;
    
    QMenu* m_menu;
    QActionGroup* m_originActions;
    QAction* m_manageAction;
};

Implementation Details

OriginSelectorWidget::OriginSelectorWidget(QWidget* parent)
    : QToolButton(parent)
{
    setPopupMode(QToolButton::InstantPopup);
    setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    setMinimumWidth(70);
    setMaximumWidth(100);
    
    m_menu = new QMenu(this);
    setMenu(m_menu);
    
    m_originActions = new QActionGroup(this);
    m_originActions->setExclusive(true);
    
    // Connect to OriginManager signals
    auto* mgr = OriginManager::instance();
    connect(mgr, &OriginManager::currentOriginChanged, 
            this, &OriginSelectorWidget::onCurrentOriginChanged);
    connect(mgr, &OriginManager::originsListChanged,
            this, &OriginSelectorWidget::rebuildMenu);
    
    rebuildMenu();
    updateDisplay();
}

void OriginSelectorWidget::rebuildMenu() {
    m_menu->clear();
    
    // Clear old actions
    for (QAction* action : m_originActions->actions()) {
        m_originActions->removeAction(action);
        delete action;
    }
    
    auto* mgr = OriginManager::instance();
    FileOrigin* current = mgr->currentOrigin();
    
    // Add origin entries
    for (FileOrigin* origin : mgr->allOrigins()) {
        QAction* action = m_menu->addAction(
            statusIcon(origin),
            origin->nickname()
        );
        action->setCheckable(true);
        action->setChecked(origin == current);
        action->setData(QVariant::fromValue(origin->id()));
        m_originActions->addAction(action);
        
        // Connect to origin's connection state changes
        connect(origin, &FileOrigin::connectionStateChanged,
                this, [this, origin]() { onConnectionStateChanged(origin); });
    }
    
    m_menu->addSeparator();
    
    // Manage origins action
    m_manageAction = m_menu->addAction(
        QIcon::fromTheme("preferences-system"),
        tr("Manage Origins...")
    );
    connect(m_manageAction, &QAction::triggered,
            this, &OriginSelectorWidget::onManageOriginsClicked);
}

void OriginSelectorWidget::updateDisplay() {
    FileOrigin* origin = OriginManager::instance()->currentOrigin();
    if (origin) {
        setIcon(statusIcon(origin));
        setText(origin->nickname());
        setToolTip(origin->name());
    }
}

QIcon OriginSelectorWidget::statusIcon(FileOrigin* origin) const {
    // Overlay connection status on origin icon
    QIcon baseIcon = origin->icon();
    
    switch (origin->connectionState()) {
    case ConnectionState::Connected:
        return baseIcon;  // No overlay needed
    case ConnectionState::Connecting:
        return overlayIcon(baseIcon, ":/icons/status-connecting.svg");
    case ConnectionState::Disconnected:
        return overlayIcon(baseIcon, ":/icons/status-offline.svg");
    case ConnectionState::Error:
        return overlayIcon(baseIcon, ":/icons/status-error.svg");
    }
    return baseIcon;
}

Toolbar Integration

Add the widget to the main toolbar in MainWindow:

// In MainWindow setup or toolbar creation
void MainWindow::setupFileToolbar() {
    QToolBar* fileToolbar = findChild<QToolBar*>("File");
    
    // Insert origin selector at the beginning
    auto* originSelector = new OriginSelectorWidget(this);
    QAction* firstAction = fileToolbar->actions().first();
    fileToolbar->insertWidget(firstAction, originSelector);
    fileToolbar->insertSeparator(firstAction);
}

Icons Needed

Create new icons for connection status overlays:

  • status-connecting.svg - spinning/loading indicator
  • status-offline.svg - gray/disconnected indicator
  • status-error.svg - red warning indicator

Implementation Tasks

  • Create OriginSelectorWidget class
  • Implement dropdown menu population
  • Connect to OriginManager signals
  • Implement connection status icons/overlays
  • Add "Manage Origins..." action
  • Integrate into main toolbar
  • Create status indicator icons
  • Style for Catppuccin theme consistency
  • Test with multiple origins
  • Test connection state changes

Files to Create/Modify

  • src/Gui/OriginSelectorWidget.h (new)
  • src/Gui/OriginSelectorWidget.cpp (new)
  • src/Gui/MainWindow.cpp (integrate widget)
  • src/Gui/CMakeLists.txt
  • kindred-icons/status-*.svg (new icons)

Acceptance Criteria

  • Widget displays current origin nickname and icon
  • Dropdown shows all registered origins
  • Current origin has checkmark indicator
  • Clicking origin switches current origin
  • Connection status is visually indicated
  • "Manage Origins..." opens config dialog
  • Widget fits visually in toolbar (~70-80px)
  • Tooltip shows full origin name
  • Updates when origins list changes

Dependencies

  • #9 Origin abstraction layer
  • #12 Modify Std_* commands (for meaningful switching)

Blocking

  • #14 Dynamic toolbar extension
## Overview Create a compact dropdown widget for the main toolbar that allows users to select the current file origin. This is the primary UI for switching between Local Files and Silo instances. ## Parent Issue Epic: #8 Unified File Origin System ## Goals 1. Create a visually compact, always-visible origin selector 2. Show current origin with icon and nickname 3. Dropdown menu listing all available origins 4. Show connection status for remote origins 5. Quick access to origin management ## Detailed Design ### Visual Design ``` Collapsed (toolbar state): ┌──────────────────┐ │ ☁️ Work ▼ │ ~70-80px wide └──────────────────┘ Expanded (dropdown open): ┌──────────────────┐ │ ☁️ Work ▼ │ ├──────────────────┤ │ ✓ ☁️ Work │ ← Current selection (checkmark) │ ☁️ Prod │ │ ⚠️ Staging │ ← Disconnected (warning icon) │ 📁 Local │ ├──────────────────┤ │ ⚙️ Manage... │ ← Opens config dialog └──────────────────┘ ``` ### Widget Implementation ```cpp // src/Gui/OriginSelectorWidget.h class OriginSelectorWidget : public QToolButton { Q_OBJECT public: explicit OriginSelectorWidget(QWidget* parent = nullptr); private Q_SLOTS: void onCurrentOriginChanged(FileOrigin* origin); void onOriginsListChanged(); void onOriginSelected(QAction* action); void onManageOriginsClicked(); void onConnectionStateChanged(FileOrigin* origin); private: void updateDisplay(); void rebuildMenu(); QIcon statusIcon(FileOrigin* origin) const; QMenu* m_menu; QActionGroup* m_originActions; QAction* m_manageAction; }; ``` ### Implementation Details ```cpp OriginSelectorWidget::OriginSelectorWidget(QWidget* parent) : QToolButton(parent) { setPopupMode(QToolButton::InstantPopup); setToolButtonStyle(Qt::ToolButtonTextBesideIcon); setMinimumWidth(70); setMaximumWidth(100); m_menu = new QMenu(this); setMenu(m_menu); m_originActions = new QActionGroup(this); m_originActions->setExclusive(true); // Connect to OriginManager signals auto* mgr = OriginManager::instance(); connect(mgr, &OriginManager::currentOriginChanged, this, &OriginSelectorWidget::onCurrentOriginChanged); connect(mgr, &OriginManager::originsListChanged, this, &OriginSelectorWidget::rebuildMenu); rebuildMenu(); updateDisplay(); } void OriginSelectorWidget::rebuildMenu() { m_menu->clear(); // Clear old actions for (QAction* action : m_originActions->actions()) { m_originActions->removeAction(action); delete action; } auto* mgr = OriginManager::instance(); FileOrigin* current = mgr->currentOrigin(); // Add origin entries for (FileOrigin* origin : mgr->allOrigins()) { QAction* action = m_menu->addAction( statusIcon(origin), origin->nickname() ); action->setCheckable(true); action->setChecked(origin == current); action->setData(QVariant::fromValue(origin->id())); m_originActions->addAction(action); // Connect to origin's connection state changes connect(origin, &FileOrigin::connectionStateChanged, this, [this, origin]() { onConnectionStateChanged(origin); }); } m_menu->addSeparator(); // Manage origins action m_manageAction = m_menu->addAction( QIcon::fromTheme("preferences-system"), tr("Manage Origins...") ); connect(m_manageAction, &QAction::triggered, this, &OriginSelectorWidget::onManageOriginsClicked); } void OriginSelectorWidget::updateDisplay() { FileOrigin* origin = OriginManager::instance()->currentOrigin(); if (origin) { setIcon(statusIcon(origin)); setText(origin->nickname()); setToolTip(origin->name()); } } QIcon OriginSelectorWidget::statusIcon(FileOrigin* origin) const { // Overlay connection status on origin icon QIcon baseIcon = origin->icon(); switch (origin->connectionState()) { case ConnectionState::Connected: return baseIcon; // No overlay needed case ConnectionState::Connecting: return overlayIcon(baseIcon, ":/icons/status-connecting.svg"); case ConnectionState::Disconnected: return overlayIcon(baseIcon, ":/icons/status-offline.svg"); case ConnectionState::Error: return overlayIcon(baseIcon, ":/icons/status-error.svg"); } return baseIcon; } ``` ### Toolbar Integration Add the widget to the main toolbar in `MainWindow`: ```cpp // In MainWindow setup or toolbar creation void MainWindow::setupFileToolbar() { QToolBar* fileToolbar = findChild<QToolBar*>("File"); // Insert origin selector at the beginning auto* originSelector = new OriginSelectorWidget(this); QAction* firstAction = fileToolbar->actions().first(); fileToolbar->insertWidget(firstAction, originSelector); fileToolbar->insertSeparator(firstAction); } ``` ### Icons Needed Create new icons for connection status overlays: - `status-connecting.svg` - spinning/loading indicator - `status-offline.svg` - gray/disconnected indicator - `status-error.svg` - red warning indicator ## Implementation Tasks - [ ] Create `OriginSelectorWidget` class - [ ] Implement dropdown menu population - [ ] Connect to `OriginManager` signals - [ ] Implement connection status icons/overlays - [ ] Add "Manage Origins..." action - [ ] Integrate into main toolbar - [ ] Create status indicator icons - [ ] Style for Catppuccin theme consistency - [ ] Test with multiple origins - [ ] Test connection state changes ## Files to Create/Modify - `src/Gui/OriginSelectorWidget.h` (new) - `src/Gui/OriginSelectorWidget.cpp` (new) - `src/Gui/MainWindow.cpp` (integrate widget) - `src/Gui/CMakeLists.txt` - `kindred-icons/status-*.svg` (new icons) ## Acceptance Criteria - [ ] Widget displays current origin nickname and icon - [ ] Dropdown shows all registered origins - [ ] Current origin has checkmark indicator - [ ] Clicking origin switches current origin - [ ] Connection status is visually indicated - [ ] "Manage Origins..." opens config dialog - [ ] Widget fits visually in toolbar (~70-80px) - [ ] Tooltip shows full origin name - [ ] Updates when origins list changes ## Dependencies - #9 Origin abstraction layer - #12 Modify Std_* commands (for meaningful switching) ## Blocking - #14 Dynamic toolbar extension
Author
Owner

Complete. Closing with code references:

  • src/Gui/OriginSelectorWidget.h / .cpp (270 lines) — Dropdown widget with origin list, checkmarks, and "Manage..." action
  • src/Gui/Action.cpp:1476-1493OriginSelectorAction creates widget when added to toolbars
  • src/Gui/CommandStd.cpp:141-172StdCmdOrigin command registration
  • Connected to OriginManager signals for real-time updates
Complete. Closing with code references: - `src/Gui/OriginSelectorWidget.h` / `.cpp` (270 lines) — Dropdown widget with origin list, checkmarks, and "Manage..." action - `src/Gui/Action.cpp:1476-1493` — `OriginSelectorAction` creates widget when added to toolbars - `src/Gui/CommandStd.cpp:141-172` — `StdCmdOrigin` command registration - Connected to `OriginManager` signals for real-time updates
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kindred/create#13