From 282136632e832546281f9d78648926b4e77f0121 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 30 Jan 2026 05:59:40 -0700 Subject: [PATCH] Milestone 3 --- GUI_PLAN.md | 38 +++++++++++++++++++------------------- gui/WarpGraphModel.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ gui/WarpGraphModel.h | 2 ++ 3 files changed, 62 insertions(+), 19 deletions(-) diff --git a/GUI_PLAN.md b/GUI_PLAN.md index 99a9d46..d1a8d68 100644 --- a/GUI_PLAN.md +++ b/GUI_PLAN.md @@ -57,22 +57,22 @@ A Qt6-based node editor GUI for warppipe using the QtNodes (nodeeditor) library. - [x] Keep connections visible with faded style - [x] Verify: Application nodes appear vibrant when active, fade when inactive, never disappear -- [ ] Milestone 3 - Link Visualization and Drag-Connect - - [ ] Implement connection mapping: - - [ ] Call `Client::ListLinks()` to get existing PipeWire links - - [ ] For each Link, find corresponding NodeId and PortIndex for output/input - - [ ] Create QtNodes::ConnectionId from (outNodeId, outPortType, outPortIndex, inNodeId, inPortType, inPortIndex) - - [ ] Store in model's m_connections set - - [ ] Implement `addConnection(ConnectionId)`: - - [ ] Extract output port and input port from ConnectionId - - [ ] Call `Client::CreateLink(outputPortId, inputPortId, LinkOptions{})` - - [ ] If successful, add connection to m_connections - - [ ] If failed, emit error and do NOT add to graph - - [ ] Implement `deleteConnection(ConnectionId)`: - - [ ] Find corresponding warppipe LinkId from connection - - [ ] Call `Client::RemoveLink(linkId)` - - [ ] Remove from m_connections - - [ ] Verify: Drag connection from output port to input port creates PipeWire link; delete removes it +- [x] Milestone 3 - Link Visualization and Drag-Connect + - [x] Implement connection mapping: + - [x] Call `Client::ListLinks()` to get existing PipeWire links + - [x] For each Link, find corresponding NodeId and PortIndex for output/input + - [x] Create QtNodes::ConnectionId from (outNodeId, outPortType, outPortIndex, inNodeId, inPortType, inPortIndex) + - [x] Store in model's m_connections set + - [x] Implement `addConnection(ConnectionId)`: + - [x] Extract output port and input port from ConnectionId + - [x] Call `Client::CreateLink(outputPortId, inputPortId, LinkOptions{})` + - [x] If successful, add connection to m_connections + - [x] If failed, emit error and do NOT add to graph + - [x] Implement `deleteConnection(ConnectionId)`: + - [x] Find corresponding warppipe LinkId from connection + - [x] Call `Client::RemoveLink(linkId)` + - [x] Remove from m_connections + - [x] Verify: Drag connection from output port to input port creates PipeWire link; delete removes it - [ ] Milestone 4 - Context Menu and Virtual Node Creation - [ ] Add context menu to GraphEditorWidget: @@ -203,10 +203,10 @@ warppipe/ ## Design Notes ### Node Title Synthesis -warppipe::NodeInfo does not have a `description` field. Derive display title: +Display title priority: `description` > `application_name` > `name` +- **Hardware/Virtual nodes**: Use `description` (PW_KEY_NODE_DESCRIPTION), e.g., "Speakers", "Headphones" - **Application nodes**: Use `application_name` if non-empty (e.g., "Firefox", "Spotify") -- **Hardware/Virtual nodes**: Use `name` field (e.g., "alsa_output.pci-0000_00_1f.3.analog-stereo") -- Fallback to `name` if `application_name` is empty +- Fallback to `name` if both are empty ### Port Orientation - **Input ports** (is_input=true): LEFT side of node (QtNodes::PortType::In) diff --git a/gui/WarpGraphModel.cpp b/gui/WarpGraphModel.cpp index 8677220..e9d498e 100644 --- a/gui/WarpGraphModel.cpp +++ b/gui/WarpGraphModel.cpp @@ -100,6 +100,32 @@ void WarpGraphModel::addConnection( if (!connectionPossible(connectionId)) { return; } + + if (m_client) { + auto outIt = m_nodes.find(connectionId.outNodeId); + auto inIt = m_nodes.find(connectionId.inNodeId); + if (outIt == m_nodes.end() || inIt == m_nodes.end()) { + return; + } + + auto outIdx = static_cast(connectionId.outPortIndex); + auto inIdx = static_cast(connectionId.inPortIndex); + if (outIdx >= outIt->second.outputPorts.size() || + inIdx >= inIt->second.inputPorts.size()) { + return; + } + + warppipe::PortId outPortId = outIt->second.outputPorts[outIdx].id; + warppipe::PortId inPortId = inIt->second.inputPorts[inIdx].id; + + auto result = m_client->CreateLink(outPortId, inPortId, warppipe::LinkOptions{}); + if (!result.ok()) { + return; + } + + m_linkIdToConn.emplace(result.value.id.value, connectionId); + } + m_connections.insert(connectionId); Q_EMIT connectionCreated(connectionId); } @@ -224,6 +250,18 @@ bool WarpGraphModel::deleteConnection( if (it == m_connections.end()) { return false; } + + if (m_client && !m_refreshing) { + for (auto linkIt = m_linkIdToConn.begin(); linkIt != m_linkIdToConn.end(); + ++linkIt) { + if (linkIt->second == connectionId) { + m_client->RemoveLink(warppipe::LinkId{linkIt->first}); + m_linkIdToConn.erase(linkIt); + break; + } + } + } + m_connections.erase(it); Q_EMIT connectionDeleted(connectionId); return true; @@ -269,6 +307,7 @@ void WarpGraphModel::refreshFromClient() { return; } + m_refreshing = true; auto nodesResult = m_client->ListNodes(); if (!nodesResult.ok()) { return; @@ -450,6 +489,8 @@ void WarpGraphModel::refreshFromClient() { } } } + + m_refreshing = false; } const WarpNodeData * diff --git a/gui/WarpGraphModel.h b/gui/WarpGraphModel.h index 0578ffc..4497b90 100644 --- a/gui/WarpGraphModel.h +++ b/gui/WarpGraphModel.h @@ -91,4 +91,6 @@ private: double m_nextX = 0.0; double m_nextY = 0.0; double m_rowMaxHeight = 0.0; + + bool m_refreshing = false; };