Skip to content

Commit

Permalink
Added Entity context menu plugin
Browse files Browse the repository at this point in the history
Signed-off-by: ahcorde <ahcorde@gmail.com>
  • Loading branch information
ahcorde committed Sep 16, 2021
1 parent fd17a03 commit 85d84a6
Show file tree
Hide file tree
Showing 7 changed files with 386 additions and 0 deletions.
8 changes: 8 additions & 0 deletions examples/worlds/minimal_scene.sdf
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ Missing for parity with GzScene3D:
</plugin>

<!-- Plugins that add functionality to the scene -->
<plugin filename="EntityContextMenuPlugin" name="Entity context menu">
<ignition-gui>
<property key="state" type="string">floating</property>
<property key="width" type="double">5</property>
<property key="height" type="double">5</property>
<property key="showTitleBar" type="bool">false</property>
</ignition-gui>
</plugin>
<plugin filename="GzSceneManager" name="Scene Manager">
<ignition-gui>
<anchors target="3D View">
Expand Down
1 change: 1 addition & 0 deletions src/gui/plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions src/gui/plugins/entity_context_menu/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
gz_add_gui_plugin(EntityContextMenuPlugin
SOURCES
EntityContextMenuPlugin.cc
QT_HEADERS
EntityContextMenuPlugin.hh
PUBLIC_LINK_LIBS
${PROJECT_LIBRARY_TARGET_NAME}-rendering
)
207 changes: 207 additions & 0 deletions src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc
Original file line number Diff line number Diff line change
@@ -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 <iostream>

#include <QtQml>

#include <ignition/common/Console.hh>
#include <ignition/common/MouseEvent.hh>

#include <ignition/gui/Application.hh>
#include <ignition/gui/GuiEvents.hh>
#include <ignition/gui/Helpers.hh>
#include <ignition/gui/MainWindow.hh>

#include <ignition/plugin/Register.hh>

#include <ignition/rendering/Camera.hh>
#include <ignition/rendering/RenderingIface.hh>
#include <ignition/rendering/Visual.hh>
#include <ignition/rendering/Scene.hh>

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<rendering::Camera>(
this->scene->NodeByIndex(i));
if (cam)
{
if (std::get<bool>(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<EntityContextMenuPrivate>())
{
qmlRegisterType<EntityContextMenuItem>(
"RenderWindowOverlay", 1, 0, "RenderWindowOverlay");
}

EntityContextMenu::~EntityContextMenu() = default;

/////////////////////////////////////////////////
void EntityContextMenu::LoadConfig(const tinyxml2::XMLElement *)
{
EntityContextMenuItem *renderWindowOverlay =
this->PluginItem()->findChild<EntityContextMenuItem *>();
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
<ignition::gui::MainWindow *>()->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<ignition::gui::events::RightClickOnScene*>(_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::SetIgnRenderer(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<rendering::Visual>(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)
110 changes: 110 additions & 0 deletions src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.hh
Original file line number Diff line number Diff line change
@@ -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 <ignition/common/MouseEvent.hh>

#include <ignition/gui/Plugin.hh>

#include <ignition/rendering/Camera.hh>

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<EntityContextMenuPrivate> 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
47 changes: 47 additions & 0 deletions src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qml
Original file line number Diff line number Diff line change
@@ -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
}
}
Loading

0 comments on commit 85d84a6

Please sign in to comment.