Milestone 3
This commit is contained in:
parent
2edd7a366a
commit
282136632e
3 changed files with 62 additions and 19 deletions
38
GUI_PLAN.md
38
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] Keep connections visible with faded style
|
||||||
- [x] Verify: Application nodes appear vibrant when active, fade when inactive, never disappear
|
- [x] Verify: Application nodes appear vibrant when active, fade when inactive, never disappear
|
||||||
|
|
||||||
- [ ] Milestone 3 - Link Visualization and Drag-Connect
|
- [x] Milestone 3 - Link Visualization and Drag-Connect
|
||||||
- [ ] Implement connection mapping:
|
- [x] Implement connection mapping:
|
||||||
- [ ] Call `Client::ListLinks()` to get existing PipeWire links
|
- [x] Call `Client::ListLinks()` to get existing PipeWire links
|
||||||
- [ ] For each Link, find corresponding NodeId and PortIndex for output/input
|
- [x] For each Link, find corresponding NodeId and PortIndex for output/input
|
||||||
- [ ] Create QtNodes::ConnectionId from (outNodeId, outPortType, outPortIndex, inNodeId, inPortType, inPortIndex)
|
- [x] Create QtNodes::ConnectionId from (outNodeId, outPortType, outPortIndex, inNodeId, inPortType, inPortIndex)
|
||||||
- [ ] Store in model's m_connections set
|
- [x] Store in model's m_connections set
|
||||||
- [ ] Implement `addConnection(ConnectionId)`:
|
- [x] Implement `addConnection(ConnectionId)`:
|
||||||
- [ ] Extract output port and input port from ConnectionId
|
- [x] Extract output port and input port from ConnectionId
|
||||||
- [ ] Call `Client::CreateLink(outputPortId, inputPortId, LinkOptions{})`
|
- [x] Call `Client::CreateLink(outputPortId, inputPortId, LinkOptions{})`
|
||||||
- [ ] If successful, add connection to m_connections
|
- [x] If successful, add connection to m_connections
|
||||||
- [ ] If failed, emit error and do NOT add to graph
|
- [x] If failed, emit error and do NOT add to graph
|
||||||
- [ ] Implement `deleteConnection(ConnectionId)`:
|
- [x] Implement `deleteConnection(ConnectionId)`:
|
||||||
- [ ] Find corresponding warppipe LinkId from connection
|
- [x] Find corresponding warppipe LinkId from connection
|
||||||
- [ ] Call `Client::RemoveLink(linkId)`
|
- [x] Call `Client::RemoveLink(linkId)`
|
||||||
- [ ] Remove from m_connections
|
- [x] Remove from m_connections
|
||||||
- [ ] Verify: Drag connection from output port to input port creates PipeWire link; delete removes it
|
- [x] Verify: Drag connection from output port to input port creates PipeWire link; delete removes it
|
||||||
|
|
||||||
- [ ] Milestone 4 - Context Menu and Virtual Node Creation
|
- [ ] Milestone 4 - Context Menu and Virtual Node Creation
|
||||||
- [ ] Add context menu to GraphEditorWidget:
|
- [ ] Add context menu to GraphEditorWidget:
|
||||||
|
|
@ -203,10 +203,10 @@ warppipe/
|
||||||
## Design Notes
|
## Design Notes
|
||||||
|
|
||||||
### Node Title Synthesis
|
### 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")
|
- **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 both are empty
|
||||||
- Fallback to `name` if `application_name` is empty
|
|
||||||
|
|
||||||
### Port Orientation
|
### Port Orientation
|
||||||
- **Input ports** (is_input=true): LEFT side of node (QtNodes::PortType::In)
|
- **Input ports** (is_input=true): LEFT side of node (QtNodes::PortType::In)
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,32 @@ void WarpGraphModel::addConnection(
|
||||||
if (!connectionPossible(connectionId)) {
|
if (!connectionPossible(connectionId)) {
|
||||||
return;
|
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<size_t>(connectionId.outPortIndex);
|
||||||
|
auto inIdx = static_cast<size_t>(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);
|
m_connections.insert(connectionId);
|
||||||
Q_EMIT connectionCreated(connectionId);
|
Q_EMIT connectionCreated(connectionId);
|
||||||
}
|
}
|
||||||
|
|
@ -224,6 +250,18 @@ bool WarpGraphModel::deleteConnection(
|
||||||
if (it == m_connections.end()) {
|
if (it == m_connections.end()) {
|
||||||
return false;
|
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);
|
m_connections.erase(it);
|
||||||
Q_EMIT connectionDeleted(connectionId);
|
Q_EMIT connectionDeleted(connectionId);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -269,6 +307,7 @@ void WarpGraphModel::refreshFromClient() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_refreshing = true;
|
||||||
auto nodesResult = m_client->ListNodes();
|
auto nodesResult = m_client->ListNodes();
|
||||||
if (!nodesResult.ok()) {
|
if (!nodesResult.ok()) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -450,6 +489,8 @@ void WarpGraphModel::refreshFromClient() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_refreshing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const WarpNodeData *
|
const WarpNodeData *
|
||||||
|
|
|
||||||
|
|
@ -91,4 +91,6 @@ private:
|
||||||
double m_nextX = 0.0;
|
double m_nextX = 0.0;
|
||||||
double m_nextY = 0.0;
|
double m_nextY = 0.0;
|
||||||
double m_rowMaxHeight = 0.0;
|
double m_rowMaxHeight = 0.0;
|
||||||
|
|
||||||
|
bool m_refreshing = false;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue