diff --git a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc index f6d0796b44..764df0c430 100644 --- a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc +++ b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc @@ -17,8 +17,10 @@ #include "EntityContextMenuPlugin.hh" +#include #include #include +#include #include @@ -159,9 +161,10 @@ void EntityContextMenuItem::SetEntityContextMenuHandler( } /////////////////////////////////////////////////// -void EntityContextMenuItem::OnContextMenuRequested(QString _entity) +void EntityContextMenuItem::OnContextMenuRequested( + QString _entity, uint64_t _eid) { - emit openContextMenu(std::move(_entity)); + emit openContextMenu(std::move(_entity), _eid); } ///////////////////////////////////////////////// @@ -197,7 +200,13 @@ void EntityContextMenuHandler::HandleMouseContextMenu( visual = std::dynamic_pointer_cast(visual->Parent()); } - emit ContextMenuRequested(visual->Name().c_str()); + uint64_t entityId = kNullEntity; + if (std::holds_alternative(visual->UserData("gazebo-entity"))) + { + entityId = std::get(visual->UserData("gazebo-entity")); + } + + emit ContextMenuRequested(visual->Name().c_str(), entityId); } } diff --git a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.hh b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.hh index eb849d41dd..a279bf798f 100644 --- a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.hh +++ b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.hh @@ -26,6 +26,8 @@ #include +#include + namespace gz { namespace sim @@ -70,7 +72,7 @@ namespace sim /// \brief Signal fired when context menu event is triggered /// \param[in] _entity Scoped name of entity. - signals: void ContextMenuRequested(QString _entity); + signals: void ContextMenuRequested(QString _entity, uint64_t _entityId); }; /// \brief A QQUickItem that manages the render window @@ -91,11 +93,12 @@ namespace sim /// Note that the function name needs to start with lowercase in order for /// the connection to work on the QML side /// \param[in] _entity Scoped name of entity. - signals: void openContextMenu(QString _entity); // NOLINT + signals: void openContextMenu(QString _entity, uint64_t _entiyI); // NOLINT /// \brief Qt callback when context menu request is received /// \param[in] _entity Scoped name of entity. - public slots: void OnContextMenuRequested(QString _entity); + public slots: void OnContextMenuRequested(QString _entity, + uint64_t _entityId); }; } } diff --git a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qml b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qml index 5f14a00a7d..53017f9b0f 100644 --- a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qml +++ b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qml @@ -55,9 +55,9 @@ ColumnLayout { Connections { target: renderWindowOverlay - function onOpenContextMenu(_entity) + function onOpenContextMenu(_entity, _eid) { - entityContextMenu.open(_entity, "model"); + entityContextMenu.open(_entity, "model", _eid); } } } diff --git a/src/gui/plugins/modules/EntityContextMenu.cc b/src/gui/plugins/modules/EntityContextMenu.cc index 6af4435972..0d5b722113 100644 --- a/src/gui/plugins/modules/EntityContextMenu.cc +++ b/src/gui/plugins/modules/EntityContextMenu.cc @@ -21,8 +21,10 @@ #include #include #include +#include #include +#include #include #include #include @@ -89,6 +91,9 @@ namespace gz::sim /// \brief Storing last follow target for look at. public: std::string followTargetLookAt; + /// \brief Add plugin service + public: std::string addPluginService; + /// \brief Flag used to disable look at when not following target. public: bool followingTarget{false}; @@ -325,7 +330,7 @@ void EntityContextMenu::OnRequest(const QString &_request, const QString &_data) } else if (request == "view_frames") { - gz::msgs::StringMsg req; + msgs::StringMsg req; req.set_data(_data.toStdString()); this->dataPtr->node.Request(this->dataPtr->viewFramesService, req, cb); } @@ -345,3 +350,52 @@ void EntityContextMenu::OnRequest(const QString &_request, const QString &_data) gzwarn << "Unknown request [" << request << "]" << std::endl; } } + +///////////////////////////////////////////////// +void EntityContextMenu::OnAddPlugin(const QString &_name, + const QString &_filename, const QString &_innerXml, + const uint64_t _entity) { + + if (this->dataPtr->worldName.empty()) + { + auto runners = gui::App()->findChildren(); + if (runners.empty() || runners[0] == nullptr) + { + gzerr << "Internal error: no GuiRunner found." << std::endl; + return; + } + + this->dataPtr->worldName = "default"; + auto worldNameVariant = runners[0]->property("worldName"); + if (!worldNameVariant.isValid()) + { + gzwarn << "GuiRunner's worldName not set, using[" + << this->dataPtr->worldName << "]" << std::endl; + } + else + { + this->dataPtr->worldName = worldNameVariant.toString().toStdString(); + } + this->dataPtr->removeService = + "/world/" + this->dataPtr->worldName + "/remove"; + } + this->dataPtr->addPluginService = + "/world/" + this->dataPtr->worldName + "/entity/system/add"; + std::function cb = + [](const msgs::Boolean &/*_rep*/, const bool _result) + { + if (!_result) + gzerr << "Error creating new plugin" << std::endl; + }; + + msgs::EntityPlugin_V entity_plugin_message; + msgs::Entity* entity = entity_plugin_message.mutable_entity(); + entity->set_id(_entity); + msgs::Plugin* new_plugin = entity_plugin_message.add_plugins(); + new_plugin->set_name(_name.toStdString()); + new_plugin->set_filename(_filename.toStdString()); + new_plugin->set_innerxml(_innerXml.toStdString()); + + this->dataPtr->node.Request( + this->dataPtr->addPluginService, entity_plugin_message, cb); +} diff --git a/src/gui/plugins/modules/EntityContextMenu.hh b/src/gui/plugins/modules/EntityContextMenu.hh index 8fa07cc9d4..d3870e26c7 100644 --- a/src/gui/plugins/modules/EntityContextMenu.hh +++ b/src/gui/plugins/modules/EntityContextMenu.hh @@ -88,6 +88,15 @@ namespace sim public: Q_INVOKABLE void OnRequest(const QString &_request, const QString &_data); + /// \brief Callback when a new plugin window finishes configuring + /// \param[in] _name Plugin name + /// \param[in] _filename Plugin filename + /// \param[in] _innerXml Inner XML of the plugin + /// \param[in] _entity Entity to attach plugin to + public: Q_INVOKABLE void OnAddPlugin(const QString &_name, + const QString &_filename, const QString &_innerXml, + const uint64_t _entity); + /// \internal /// \brief Pointer to private data. private: std::unique_ptr dataPtr; diff --git a/src/gui/plugins/modules/EntityContextMenu.qml b/src/gui/plugins/modules/EntityContextMenu.qml index 9085f7df9f..6695bda335 100644 --- a/src/gui/plugins/modules/EntityContextMenu.qml +++ b/src/gui/plugins/modules/EntityContextMenu.qml @@ -14,8 +14,12 @@ * limitations under the License. * */ -import QtQuick 2.0 -import QtQuick.Controls 2.0 +import QtCore +import QtQuick 2.9 +import QtQuick.Controls +import QtQuick.Controls.Material 2.1 +import QtQuick.Dialogs +import QtQuick.Layouts 1.3 import GzSim 1.0 as GzSim Item { @@ -80,6 +84,14 @@ Item { onEntered: thirdMenu.open() } } + MenuItem { + id: attachPlugin + text: "Attach Plugin" + onTriggered: { + menu.close() + pluginDialog.open() + } + } } Menu { id: secondMenu @@ -172,9 +184,10 @@ Item { } } - function open(_entity, _type) { + function open(_entity, _type, _eid) { context.entity = _entity context.type = _type + context.eid = _eid moveToMenu.enabled = false followMenu.enabled = false followFreeLookMenu.enabled = false @@ -233,9 +246,91 @@ Item { menu.popup() } + Dialog { + id: pluginDialog + title: "Add Plugin Dialog" + modal: true + standardButtons: Dialog.Ok | Dialog.Cancel // Adds standard buttons + parent: Overlay.overlay + //parent: customPlugin.Window.window ? customPlugin.Window.window.contentItem : customPlugin + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + width: parent.width * 0.3 + height: parent.height * 0.6 + + contentItem: Item { + // Your dialog content goes here + width: parent.width + height: parent.height + Column { + anchors.centerIn: parent + spacing: 10 + width: parent.width * 0.8 // Make inputs a bit narrower than parent + height: parent.height + + Text { + id: pluginNameLbl + text: "Plugin Name:" + } + + TextInput { + id: pluginNames + width: parent.width + height: 20 + + } + + Text { + id: pluginFilesLbl + text: "Plugin Filename:" + } + + TextInput { + id: pluginFilename + width: parent.width + height: 20 + } + + Text { + id: pluginSdfLbl + text: "Enter the inner-xml of your plugin" + } + + TextArea { + id: pluginParams + width: parent.width + height: parent.y + parent.height - (pluginSdfLbl.y + pluginFilesLbl.height) // Set a reasonable height + + font.family: "Monospace" + background: Rectangle { + color: "transparent" // No fill + border.width: 2 // No border + border.color: "lightblue" + } + leftPadding: 5 + topPadding: 5 + rightPadding: 5 + bottomPadding: 5 + } + } + } + + onAccepted: { + console.log("Dialog Accepted!"); + context.OnAddPlugin(pluginNames.text, pluginFilename.text, pluginParams.text, context.eid) + // Perform actions when OK is clicked + } + + onRejected: { + console.log("Dialog Rejected!"); + // Perform actions when Cancel is clicked or dialog is closed + } + } + GzSim.EntityContextMenuItem { id: context property string entity property string type + property int eid } }