diff --git a/examples/worlds/minimal_scene.sdf b/examples/worlds/minimal_scene.sdf
index 56d9e0c3bd..5b79b16663 100644
--- a/examples/worlds/minimal_scene.sdf
+++ b/examples/worlds/minimal_scene.sdf
@@ -45,6 +45,14 @@ Missing for parity with GzScene3D:
+
+
+ floating
+ 5
+ 5
+ false
+
+
diff --git a/src/gui/plugins/CMakeLists.txt b/src/gui/plugins/CMakeLists.txt
index 2b0489a16c..49e4c5e94f 100644
--- a/src/gui/plugins/CMakeLists.txt
+++ b/src/gui/plugins/CMakeLists.txt
@@ -121,6 +121,7 @@ add_subdirectory(modules)
add_subdirectory(align_tool)
add_subdirectory(banana_for_scale)
add_subdirectory(component_inspector)
+add_subdirectory(entity_context_menu)
add_subdirectory(entity_tree)
add_subdirectory(joint_position_controller)
add_subdirectory(lights)
diff --git a/src/gui/plugins/entity_context_menu/CMakeLists.txt b/src/gui/plugins/entity_context_menu/CMakeLists.txt
new file mode 100644
index 0000000000..85dddd702a
--- /dev/null
+++ b/src/gui/plugins/entity_context_menu/CMakeLists.txt
@@ -0,0 +1,8 @@
+gz_add_gui_plugin(EntityContextMenuPlugin
+ SOURCES
+ EntityContextMenuPlugin.cc
+ QT_HEADERS
+ EntityContextMenuPlugin.hh
+ PUBLIC_LINK_LIBS
+ ${PROJECT_LIBRARY_TARGET_NAME}-rendering
+)
diff --git a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc
new file mode 100644
index 0000000000..df036a0fdf
--- /dev/null
+++ b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2021 Open Source Robotics Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+#include "EntityContextMenuPlugin.hh"
+
+#include
+
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+
+namespace ignition::gazebo
+{
+ class EntityContextMenuPrivate
+ {
+ /// \brief Perform operations in the render thread.
+ public: void OnRender();
+
+ /// \brief Pointer to the rendering scene
+ public: rendering::ScenePtr scene{nullptr};
+
+ /// \brief User camera
+ public: rendering::CameraPtr camera{nullptr};
+
+ /// \brief Entity context menu hanlder
+ public: EntityContextMenuHanlder entityContextMenuHanlder;
+ };
+}
+
+using namespace ignition;
+using namespace gazebo;
+
+/////////////////////////////////////////////////
+void EntityContextMenuPrivate::OnRender()
+{
+ if (nullptr == this->scene)
+ {
+ this->scene = rendering::sceneFromFirstRenderEngine();
+ if (nullptr == this->scene)
+ {
+ return;
+ }
+
+ for (unsigned int i = 0; i < this->scene->NodeCount(); ++i)
+ {
+ auto cam = std::dynamic_pointer_cast(
+ this->scene->NodeByIndex(i));
+ if (cam)
+ {
+ if (std::get(cam->UserData("user-camera")))
+ {
+ this->camera = cam;
+
+ igndbg << "Spawn plugin is using camera ["
+ << this->camera->Name() << "]" << std::endl;
+ break;
+ }
+ }
+ }
+ }
+}
+
+/////////////////////////////////////////////////
+EntityContextMenu::EntityContextMenu()
+ : gui::Plugin(), dataPtr(std::make_unique())
+{
+ qmlRegisterType(
+ "RenderWindowOverlay", 1, 0, "RenderWindowOverlay");
+}
+
+EntityContextMenu::~EntityContextMenu() = default;
+
+/////////////////////////////////////////////////
+void EntityContextMenu::LoadConfig(const tinyxml2::XMLElement *)
+{
+ EntityContextMenuItem *renderWindowOverlay =
+ this->PluginItem()->findChild();
+ if (!renderWindowOverlay)
+ {
+ ignerr << "Unable to find Render Window Overlay item. "
+ << "Render window overlay will not be created" << std::endl;
+ return;
+ }
+
+ renderWindowOverlay->SetEntityContextMenuHanlder(
+ this->dataPtr->entityContextMenuHanlder);
+
+ if (this->title.empty())
+ this->title = "EntityContextMenu";
+
+ ignition::gui::App()->findChild
+ ()->installEventFilter(this);
+}
+
+////////////////////////////////////////////////
+bool EntityContextMenu::eventFilter(QObject *_obj, QEvent *_event)
+{
+ if (_event->type() == ignition::gui::events::Render::kType)
+ {
+ this->dataPtr->OnRender();
+ }
+ else if (_event->type() == ignition::gui::events::RightClickOnScene::kType)
+ {
+ ignition::gui::events::RightClickOnScene *_e =
+ static_cast(_event);
+ if (_e)
+ {
+ this->dataPtr->entityContextMenuHanlder.HandleMouseContextMenu(
+ _e->Mouse(), this->dataPtr->camera);
+ }
+ }
+
+ return QObject::eventFilter(_obj, _event);
+}
+
+
+/////////////////////////////////////////////////
+EntityContextMenuItem::EntityContextMenuItem(QQuickItem *_parent)
+ : QQuickItem(_parent)
+{
+ this->setAcceptedMouseButtons(Qt::AllButtons);
+ this->setFlag(ItemHasContents);
+}
+
+/////////////////////////////////////////////////
+void EntityContextMenuItem::SetEntityContextMenuHanlder(EntityContextMenuHanlder &_entityContextMenuHanlder)
+{
+ entityContextMenuHanlder = &_entityContextMenuHanlder;
+ this->connect(entityContextMenuHanlder,
+ &EntityContextMenuHanlder::ContextMenuRequested,
+ this, &EntityContextMenuItem::OnContextMenuRequested, Qt::QueuedConnection);
+}
+
+///////////////////////////////////////////////////
+void EntityContextMenuItem::OnContextMenuRequested(QString _entity, int _mouseX, int _mouseY)
+{
+ emit openContextMenu(std::move(_entity), _mouseX, _mouseY);
+}
+
+/////////////////////////////////////////////////
+EntityContextMenuHanlder::EntityContextMenuHanlder()
+{
+}
+
+void EntityContextMenuHanlder::HandleMouseContextMenu(const common::MouseEvent &_mouseEvent,
+ const rendering::CameraPtr &_camera)
+{
+ if (!_mouseEvent.Dragging() &&
+ _mouseEvent.Type() == common::MouseEvent::RELEASE &&
+ _mouseEvent.Button() == common::MouseEvent::RIGHT)
+ {
+ math::Vector2i dt =
+ _mouseEvent.PressPos() - _mouseEvent.Pos();
+
+ // check for click with some tol for mouse movement
+ if (dt.Length() > 5.0)
+ return;
+
+ rendering::VisualPtr visual =_camera->Scene()->VisualAt(
+ _camera,
+ _mouseEvent.Pos());
+
+ if (!visual)
+ return;
+
+ // get model visual
+ while (visual->HasParent() && visual->Parent() !=
+ visual->Scene()->RootVisual())
+ {
+ visual = std::dynamic_pointer_cast(visual->Parent());
+ }
+
+ emit ContextMenuRequested(
+ visual->Name().c_str(), _mouseEvent.Pos().X(), _mouseEvent.Pos().Y());
+ }
+}
+
+// Register this plugin
+IGNITION_ADD_PLUGIN(ignition::gazebo::EntityContextMenu,
+ ignition::gui::Plugin)
diff --git a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.hh b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.hh
new file mode 100644
index 0000000000..4e602caad8
--- /dev/null
+++ b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.hh
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2021 Open Source Robotics Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+#ifndef IGNITION_GUI_PLUGINS_ENTITY_CONTEXT_MENU_HH_
+#define IGNITION_GUI_PLUGINS_ENTITY_CONTEXT_MENU_HH_
+
+#include
+
+#include
+
+#include
+
+namespace ignition
+{
+namespace gazebo
+{
+ class EntityContextMenuPrivate;
+
+ /// \brief This plugin is in charge to show the entity context menu when the
+ /// right button is clicked on a visual.
+ class EntityContextMenu : public ignition::gui::Plugin
+ {
+ Q_OBJECT
+
+ /// \brief Constructor
+ public: EntityContextMenu();
+
+ /// \brief Destructor
+ public: ~EntityContextMenu() override;
+
+ // Documentation inherited
+ public: void LoadConfig(const tinyxml2::XMLElement *_pluginElem) override;
+
+ // Documentation inherited
+ protected: bool eventFilter(QObject *_obj, QEvent *_event) override;
+
+ /// \internal
+ /// \brief Pointer to private data.
+ private: std::unique_ptr dataPtr;
+ };
+
+ class EntityContextMenuHanlder : public QObject
+ {
+ Q_OBJECT
+
+ /// \brief Constructor
+ public: EntityContextMenuHanlder();
+
+ /// \brief Handle mouse event for context menu
+ /// \param[in] _mouseEvent Right click mouse event
+ /// \param[in] _camera User camera
+ public: void HandleMouseContextMenu(const common::MouseEvent &_mouseEvent,
+ const rendering::CameraPtr &_camera);
+
+ /// \brief Signal fired when context menu event is triggered
+ /// \param[in] _entity Scoped name of entity.
+ /// \param[in] _mouseX X coordinate of the right click
+ /// \param[in] _mouseY Y coordinate of the right click
+ signals: void ContextMenuRequested(QString _entity, int _mouseX, int _mouseY);
+ };
+
+ /// \brief A QQUickItem that manages the render window
+ class EntityContextMenuItem : public QQuickItem
+ {
+ Q_OBJECT
+
+ /// \brief Constructor
+ /// \param[in] _parent Parent item
+ public: explicit EntityContextMenuItem(QQuickItem *_parent = nullptr);
+
+ /// \brief Set the entity context menu hanlder
+ /// \param[in] _EntityContextMenuHanlder Entity context menu hanlder
+ public: void SetEntityContextMenuHanlder(
+ EntityContextMenuHanlder &_EntityContextMenuHanlder);
+
+ /// \brief Entity context menu hanlder
+ public: EntityContextMenuHanlder *entityContextMenuHanlder;
+
+ /// \brief Signal fired to open context menu
+ /// 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.
+ /// \param[in] _mouseX X coordinate of the right click
+ /// \param[in] _mouseY Y coordinate of the right click
+ signals: void openContextMenu(QString _entity, int _mouseX, int _mouseY); // NOLINT
+
+ /// \brief Qt callback when context menu request is received
+ /// \param[in] _entity Scoped name of entity.
+ /// \param[in] _mouseX X coordinate of the right click
+ /// \param[in] _mouseY Y coordinate of the right click
+ public slots: void OnContextMenuRequested(QString _entity, int _mouseX, int _mouseY);
+ };
+}
+}
+
+#endif
diff --git a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qml b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qml
new file mode 100644
index 0000000000..66e2f56d28
--- /dev/null
+++ b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qml
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 Open Source Robotics Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+import QtQuick 2.0
+import QtQuick.Controls 2.0
+import QtQuick.Layouts 1.3
+import RenderWindowOverlay 1.0
+import IgnGazebo 1.0 as IgnGazebo
+
+Rectangle {
+ visible: false
+ color: "transparent"
+
+ RenderWindowOverlay {
+ id: renderWindowOverlay
+ objectName: "renderWindowOverlay"
+ anchors.fill: parent
+
+ Connections {
+ target: renderWindowOverlay
+ onOpenContextMenu:
+ {
+ entityContextMenu.open(_entity, "model",
+ _mouseX, _mouseY);
+ }
+ }
+ }
+
+ IgnGazebo.EntityContextMenu {
+ id: entityContextMenu
+ anchors.fill: parent
+ }
+}
diff --git a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qrc b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qrc
new file mode 100644
index 0000000000..96bffedd5e
--- /dev/null
+++ b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qrc
@@ -0,0 +1,5 @@
+
+
+ EntityContextMenuPlugin.qml
+
+