1 #include "views/view_data_processor.hpp" 2 3 #include <hex/providers/provider.hpp> 4 5 #include <imnodes.h> 6 7 namespace hex { 8 ViewDataProcessor()9 ViewDataProcessor::ViewDataProcessor() : View("hex.view.data_processor.name"_lang) { 10 imnodes::Initialize(); 11 imnodes::PushAttributeFlag(imnodes::AttributeFlags_EnableLinkDetachWithDragClick); 12 imnodes::PushAttributeFlag(imnodes::AttributeFlags_EnableLinkCreationOnSnap); 13 14 { 15 static bool always = true; 16 imnodes::IO& io = imnodes::GetIO(); 17 io.link_detach_with_modifier_click.modifier = &always; 18 } 19 20 View::subscribeEvent(Events::SettingsChanged, [](auto) { 21 auto theme = ContentRegistry::Settings::getSetting("hex.builtin.setting.interface", "hex.builtin.setting.interface.color"); 22 23 if (theme.has_value()) { 24 switch (static_cast<int>(theme.value())) { 25 default: 26 case 0: /* Dark theme */ 27 imnodes::StyleColorsDark(); 28 break; 29 case 1: /* Light theme */ 30 imnodes::StyleColorsLight(); 31 break; 32 case 2: /* Classic theme */ 33 imnodes::StyleColorsClassic(); 34 break; 35 } 36 37 imnodes::GetStyle().flags = imnodes::StyleFlags(imnodes::StyleFlags_NodeOutline | imnodes::StyleFlags_GridLines); 38 } 39 }); 40 41 View::subscribeEvent(Events::FileLoaded, [this](auto) { 42 for (auto &node : this->m_nodes) { 43 node->setCurrentOverlay(nullptr); 44 } 45 this->m_dataOverlays.clear(); 46 }); 47 } 48 ~ViewDataProcessor()49 ViewDataProcessor::~ViewDataProcessor() { 50 for (auto &node : this->m_nodes) 51 delete node; 52 53 imnodes::PopAttributeFlag(); 54 imnodes::PopAttributeFlag(); 55 imnodes::Shutdown(); 56 } 57 58 eraseLink(u32 id)59 void ViewDataProcessor::eraseLink(u32 id) { 60 auto link = std::find_if(this->m_links.begin(), this->m_links.end(), [&id](auto link){ return link.getID() == id; }); 61 62 if (link == this->m_links.end()) 63 return; 64 65 for (auto &node : this->m_nodes) { 66 for (auto &attribute : node->getAttributes()) { 67 attribute.removeConnectedAttribute(id); 68 } 69 } 70 71 this->m_links.erase(link); 72 } 73 eraseNodes(const std::vector<int> & ids)74 void ViewDataProcessor::eraseNodes(const std::vector<int> &ids) { 75 for (const int id : ids) { 76 auto node = std::find_if(this->m_nodes.begin(), this->m_nodes.end(), [&id](auto node){ return node->getID() == id; }); 77 78 for (auto &attr : (*node)->getAttributes()) { 79 std::vector<u32> linksToRemove; 80 for (auto &[linkId, connectedAttr] : attr.getConnectedAttributes()) 81 linksToRemove.push_back(linkId); 82 83 for (auto linkId : linksToRemove) 84 eraseLink(linkId); 85 } 86 } 87 88 for (const int id : ids) { 89 auto node = std::find_if(this->m_nodes.begin(), this->m_nodes.end(), [&id](auto node){ return node->getID() == id; }); 90 91 std::erase_if(this->m_endNodes, [&id](auto node){ return node->getID() == id; }); 92 93 delete *node; 94 95 this->m_nodes.erase(node); 96 } 97 } 98 processNodes()99 void ViewDataProcessor::processNodes() { 100 if (this->m_dataOverlays.size() != this->m_endNodes.size()) { 101 for (auto overlay : this->m_dataOverlays) 102 SharedData::currentProvider->deleteOverlay(overlay); 103 this->m_dataOverlays.clear(); 104 105 for (u32 i = 0; i < this->m_endNodes.size(); i++) 106 this->m_dataOverlays.push_back(SharedData::currentProvider->newOverlay()); 107 108 u32 overlayIndex = 0; 109 for (auto endNode : this->m_endNodes) { 110 endNode->setCurrentOverlay(this->m_dataOverlays[overlayIndex]); 111 overlayIndex++; 112 } 113 } 114 115 this->m_currNodeError.reset(); 116 117 try { 118 for (auto &endNode : this->m_endNodes) { 119 endNode->resetOutputData(); 120 endNode->process(); 121 } 122 } catch (dp::Node::NodeError &e) { 123 this->m_currNodeError = e; 124 } catch (std::runtime_error &e) { 125 printf("Node implementation bug! %s\n", e.what()); 126 } 127 128 } 129 drawContent()130 void ViewDataProcessor::drawContent() { 131 if (ImGui::Begin("hex.view.data_processor.name"_lang, &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { 132 133 if (ImGui::IsMouseReleased(ImGuiMouseButton_Right) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows)) { 134 imnodes::ClearNodeSelection(); 135 imnodes::ClearLinkSelection(); 136 137 this->m_rightClickedCoords = ImGui::GetMousePos(); 138 139 if (imnodes::IsNodeHovered(&this->m_rightClickedId)) 140 ImGui::OpenPopup("Node Menu"); 141 else if (imnodes::IsLinkHovered(&this->m_rightClickedId)) 142 ImGui::OpenPopup("Link Menu"); 143 else 144 ImGui::OpenPopup("Context Menu"); 145 } 146 147 if (ImGui::BeginPopup("Context Menu")) { 148 dp::Node *node = nullptr; 149 150 if (imnodes::NumSelectedNodes() > 0 || imnodes::NumSelectedLinks() > 0) { 151 if (ImGui::MenuItem("hex.view.data_processor.name"_lang)) { 152 std::vector<int> ids; 153 ids.resize(imnodes::NumSelectedNodes()); 154 imnodes::GetSelectedNodes(ids.data()); 155 156 this->eraseNodes(ids); 157 imnodes::ClearNodeSelection(); 158 159 ids.resize(imnodes::NumSelectedLinks()); 160 imnodes::GetSelectedLinks(ids.data()); 161 162 for (auto id : ids) 163 this->eraseLink(id); 164 imnodes::ClearLinkSelection(); 165 } 166 } 167 168 for (const auto &[category, name, function] : ContentRegistry::DataProcessorNode::getEntries()) { 169 if (category.empty() && name.empty()) { 170 ImGui::Separator(); 171 } else if (category.empty()) { 172 if (ImGui::MenuItem(name.c_str())) { 173 node = function(); 174 } 175 } else { 176 if (ImGui::BeginMenu(category.c_str())) { 177 if (ImGui::MenuItem(name.c_str())) { 178 node = function(); 179 } 180 ImGui::EndMenu(); 181 } 182 } 183 } 184 185 if (node != nullptr) { 186 this->m_nodes.push_back(node); 187 188 bool hasOutput = false; 189 bool hasInput = false; 190 for (auto &attr : node->getAttributes()) { 191 if (attr.getIOType() == dp::Attribute::IOType::Out) 192 hasOutput = true; 193 194 if (attr.getIOType() == dp::Attribute::IOType::In) 195 hasInput = true; 196 } 197 198 if (hasInput && !hasOutput) 199 this->m_endNodes.push_back(node); 200 201 imnodes::SetNodeScreenSpacePos(node->getID(), this->m_rightClickedCoords); 202 } 203 204 ImGui::EndPopup(); 205 } 206 207 if (ImGui::BeginPopup("Node Menu")) { 208 if (ImGui::MenuItem("hex.view.data_processor.menu.remove_node"_lang)) 209 this->eraseNodes({ this->m_rightClickedId }); 210 211 ImGui::EndPopup(); 212 } 213 214 if (ImGui::BeginPopup("Link Menu")) { 215 if (ImGui::MenuItem("hex.view.data_processor.menu.remove_link"_lang)) 216 this->eraseLink(this->m_rightClickedId); 217 218 ImGui::EndPopup(); 219 } 220 221 { 222 int nodeId; 223 if (imnodes::IsNodeHovered(&nodeId) && this->m_currNodeError.has_value() && this->m_currNodeError->first->getID() == nodeId) { 224 ImGui::BeginTooltip(); 225 ImGui::TextUnformatted("hex.common.error"_lang); 226 ImGui::Separator(); 227 ImGui::TextUnformatted(this->m_currNodeError->second.c_str()); 228 ImGui::EndTooltip(); 229 } 230 } 231 232 imnodes::BeginNodeEditor(); 233 234 for (auto& node : this->m_nodes) { 235 const bool hasError = this->m_currNodeError.has_value() && this->m_currNodeError->first == node; 236 237 if (hasError) 238 imnodes::PushColorStyle(imnodes::ColorStyle_NodeOutline, 0xFF0000FF); 239 240 imnodes::BeginNode(node->getID()); 241 242 imnodes::BeginNodeTitleBar(); 243 ImGui::TextUnformatted(LangEntry(node->getUnlocalizedName())); 244 imnodes::EndNodeTitleBar(); 245 246 node->drawNode(); 247 248 for (auto& attribute : node->getAttributes()) { 249 imnodes::PinShape pinShape; 250 251 switch (attribute.getType()) { 252 case dp::Attribute::Type::Integer: pinShape = imnodes::PinShape_Circle; break; 253 case dp::Attribute::Type::Float: pinShape = imnodes::PinShape_Triangle; break; 254 case dp::Attribute::Type::Buffer: pinShape = imnodes::PinShape_Quad; break; 255 } 256 257 if (attribute.getIOType() == dp::Attribute::IOType::In) { 258 imnodes::BeginInputAttribute(attribute.getID(), pinShape); 259 ImGui::TextUnformatted(LangEntry(attribute.getUnlocalizedName())); 260 imnodes::EndInputAttribute(); 261 } else if (attribute.getIOType() == dp::Attribute::IOType::Out) { 262 imnodes::BeginOutputAttribute(attribute.getID(), imnodes::PinShape(pinShape + 1)); 263 ImGui::TextUnformatted(LangEntry(attribute.getUnlocalizedName())); 264 imnodes::EndOutputAttribute(); 265 } 266 } 267 268 imnodes::EndNode(); 269 270 if (hasError) 271 imnodes::PopColorStyle(); 272 } 273 274 for (const auto &link : this->m_links) 275 imnodes::Link(link.getID(), link.getFromID(), link.getToID()); 276 277 imnodes::EndNodeEditor(); 278 279 { 280 int linkId; 281 if (imnodes::IsLinkDestroyed(&linkId)) { 282 this->eraseLink(linkId); 283 } 284 } 285 286 { 287 int from, to; 288 if (imnodes::IsLinkCreated(&from, &to)) { 289 290 do { 291 dp::Attribute *fromAttr, *toAttr; 292 for (auto &node : this->m_nodes) { 293 for (auto &attribute : node->getAttributes()) { 294 if (attribute.getID() == from) 295 fromAttr = &attribute; 296 else if (attribute.getID() == to) 297 toAttr = &attribute; 298 } 299 } 300 301 if (fromAttr == nullptr || toAttr == nullptr) 302 break; 303 304 if (fromAttr->getType() != toAttr->getType()) 305 break; 306 307 if (fromAttr->getIOType() == toAttr->getIOType()) 308 break; 309 310 if (!toAttr->getConnectedAttributes().empty()) 311 break; 312 313 auto newLink = this->m_links.emplace_back(from, to); 314 315 fromAttr->addConnectedAttribute(newLink.getID(), toAttr); 316 toAttr->addConnectedAttribute(newLink.getID(), fromAttr); 317 } while (false); 318 319 } 320 } 321 322 { 323 const int selectedLinkCount = imnodes::NumSelectedLinks(); 324 if (selectedLinkCount > 0 && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) { 325 static std::vector<int> selectedLinks; 326 selectedLinks.resize(static_cast<size_t>(selectedLinkCount)); 327 imnodes::GetSelectedLinks(selectedLinks.data()); 328 329 for (const int id : selectedLinks) { 330 eraseLink(id); 331 } 332 333 } 334 } 335 336 { 337 const int selectedNodeCount = imnodes::NumSelectedNodes(); 338 if (selectedNodeCount > 0 && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) { 339 static std::vector<int> selectedNodes; 340 selectedNodes.resize(static_cast<size_t>(selectedNodeCount)); 341 imnodes::GetSelectedNodes(selectedNodes.data()); 342 343 this->eraseNodes(selectedNodes); 344 345 } 346 } 347 348 this->processNodes(); 349 350 } 351 ImGui::End(); 352 } 353 drawMenu()354 void ViewDataProcessor::drawMenu() { 355 356 } 357 358 }