From c8ce1fd4a7798bbaadc72cd5d8b4632369f350cc Mon Sep 17 00:00:00 2001 From: Stefania Pedrazzi Date: Wed, 5 Jul 2023 15:17:07 +0200 Subject: [PATCH] Enhancement overlays menu (#6297) * Show all robots in Overlays menu * Improve menus text * Add entry in ChangeLog * Remove unused variable --------- Co-authored-by: Olivier Michel --- docs/reference/changelog-r2023.md | 2 + src/webots/gui/WbMainWindow.cpp | 234 +++++++++++------- src/webots/gui/WbMainWindow.hpp | 5 +- src/webots/nodes/utils/WbWorld.cpp | 1 + src/webots/nodes/utils/WbWorld.hpp | 1 + .../user_commands/WbContextMenuGenerator.cpp | 39 +-- .../user_commands/WbContextMenuGenerator.hpp | 4 +- 7 files changed, 174 insertions(+), 112 deletions(-) diff --git a/docs/reference/changelog-r2023.md b/docs/reference/changelog-r2023.md index f12713cc2f6..67dc7a6b230 100644 --- a/docs/reference/changelog-r2023.md +++ b/docs/reference/changelog-r2023.md @@ -4,6 +4,8 @@ Released on XXX XXth, 2023. - New Devices and Objects - Added a model of a silo and a field ditch ([#6289](https://github.com/cyberbotics/webots/pull/6289)). + - Enhancements + - Improved overlays visible in Overlays menu by adding all the robots in the menu list ([#6297](https://github.com/cyberbotics/webots/pull/6297)). - Bug fixes - Fixed errors loading template PROTO if the system user name, the project path, or the temporary directory path contains the `\` character ([#6288](https://github.com/cyberbotics/webots/pull/6288)). diff --git a/src/webots/gui/WbMainWindow.cpp b/src/webots/gui/WbMainWindow.cpp index 38b1b54fc06..4977a97b04a 100644 --- a/src/webots/gui/WbMainWindow.cpp +++ b/src/webots/gui/WbMainWindow.cpp @@ -180,7 +180,7 @@ WbMainWindow::WbMainWindow(bool minimizedOnStart, WbTcpServer *tcpServer, QWidge mFactoryLayout = new QByteArray(saveState()); - updateGui(); + updateWindowTitle(); if (WbPreferences::instance()->value("General/checkWebotsUpdateOnStartup").toBool() && WbMessageBox::enabled()) { WbWebotsUpdateManager *webotsUpdateManager = WbWebotsUpdateManager::instance(); @@ -383,8 +383,6 @@ void WbMainWindow::createMainTools() { setCentralWidget(mSimulationView); addDock(mSimulationView); connect(mSimulationView, &WbSimulationView::requestOpenUrl, this, &WbMainWindow::openUrl); - connect(mSimulationView->selection(), &WbSelection::selectionChangedFromSceneTree, this, &WbMainWindow::updateOverlayMenu); - connect(mSimulationView->selection(), &WbSelection::selectionChangedFromView3D, this, &WbMainWindow::updateOverlayMenu); connect(mSimulationView->sceneTree(), &WbSceneTree::editRequested, this, &WbMainWindow::openFileInTextEditor); if (mTcpServer) { mTcpServer->setMainWindow(this); @@ -708,17 +706,12 @@ QMenu *WbMainWindow::createOverlayMenu() { mOverlayMenu = new QMenu(this); mOverlayMenu->setTitle(tr("&Overlays")); - mRobotCameraMenu = mOverlayMenu->addMenu(tr("Ca&mera Devices")); - mRobotRangeFinderMenu = mOverlayMenu->addMenu(tr("&RangeFinder Devices")); - mRobotDisplayMenu = mOverlayMenu->addMenu(tr("&Display Devices")); - - WbContextMenuGenerator::setRobotCameraMenu(mRobotCameraMenu); - WbContextMenuGenerator::setRobotRangeFinderMenu(mRobotRangeFinderMenu); - WbContextMenuGenerator::setRobotDisplayMenu(mRobotDisplayMenu); - mOverlayMenu->addAction(WbActionManager::instance()->action(WbAction::HIDE_ALL_CAMERA_OVERLAYS)); mOverlayMenu->addAction(WbActionManager::instance()->action(WbAction::HIDE_ALL_RANGE_FINDER_OVERLAYS)); mOverlayMenu->addAction(WbActionManager::instance()->action(WbAction::HIDE_ALL_DISPLAY_OVERLAYS)); + mOverlayMenu->addSeparator(); + + WbContextMenuGenerator::setOverlaysMenu(mOverlayMenu); return mOverlayMenu; } @@ -1023,6 +1016,11 @@ void WbMainWindow::closeEvent(QCloseEvent *event) { logActiveControllersTermination(); + if (WbWorld::instance()) { + disconnect(WbWorld::instance(), &WbWorld::robotAdded, this, &WbMainWindow::addRobotInOverlaysMenu); + disconnect(WbWorld::instance(), &WbWorld::robotRemoved, this, &WbMainWindow::removeRobotInOverlaysMenu); + } + // disconnect from file changed signal before saving the perspective // if the perspective file is open, a segmentation fault is generated // due to double free of the reload QMessageBox on Linux @@ -1259,6 +1257,7 @@ void WbMainWindow::restoreRenderingDevicesPerspective() { device->restorePerspective(devicePerspective); } disconnect(mSimulationView->view3D(), &WbView3D::resized, this, &WbMainWindow::restoreRenderingDevicesPerspective); + updateOverlayMenu(); } void WbMainWindow::loadDifferentWorld(const QString &fileName) { @@ -1311,6 +1310,10 @@ void WbMainWindow::loadWorld(const QString &fileName, bool reloading) { return; } mSimulationView->cancelSupervisorMovieRecording(); + if (WbWorld::instance()) { + disconnect(WbWorld::instance(), &WbWorld::robotAdded, this, &WbMainWindow::addRobotInOverlaysMenu); + disconnect(WbWorld::instance(), &WbWorld::robotRemoved, this, &WbMainWindow::removeRobotInOverlaysMenu); + } logActiveControllersTermination(); WbLog::setConsoleLogsPostponed(true); WbApplication::instance()->loadWorld(fileName, reloading); @@ -1376,8 +1379,11 @@ void WbMainWindow::updateAfterWorldLoading(bool reloading, bool firstLoad) { connect(world, &WbWorld::modificationChanged, this, &WbMainWindow::updateWindowTitle); connect(world, &WbWorld::resetRequested, this, &WbMainWindow::resetGui, Qt::QueuedConnection); + // update 'overlays' menu + connect(WbWorld::instance(), &WbWorld::robotAdded, this, &WbMainWindow::addRobotInOverlaysMenu); + connect(WbWorld::instance(), &WbWorld::robotRemoved, this, &WbMainWindow::removeRobotInOverlaysMenu); - updateGui(); + updateWindowTitle(); if (!reloading) WbActionManager::instance()->resetApplicationActionsState(); @@ -2107,11 +2113,6 @@ void WbMainWindow::updateWindowTitle() { setWindowTitle(title); } -void WbMainWindow::updateGui() { - updateWindowTitle(); - updateOverlayMenu(); -} - void WbMainWindow::simulationEnabledChanged(bool e) { mSimulationMenu->setEnabled(e); } @@ -2226,91 +2227,136 @@ void WbMainWindow::deleteRobotWindow(WbRobot *robot) { mOnSocketOpen = true; } -static bool isRobotNode(WbBaseNode *node) { - return dynamic_cast(node); +void WbMainWindow::clearOverlaysMenu() { + QList actions = mOverlayMenu->actions(); + while (actions.size() > 4) { + QAction *action = actions.last(); + if (action->menu()) + mOverlayMenu->removeAction(action); + actions.removeLast(); + } } -void WbMainWindow::updateOverlayMenu() { - QList actions; - WbRobot *selectedRobot = mSimulationView->selectedRobot(); - - // remove camera and display item list - actions = mRobotCameraMenu->actions(); - for (int i = 0; i < actions.size(); ++i) { - mRobotCameraMenu->removeAction(actions[i]); - delete actions[i]; - } - actions = mRobotRangeFinderMenu->actions(); - for (int i = 0; i < actions.size(); ++i) { - mRobotRangeFinderMenu->removeAction(actions[i]); - delete actions[i]; - } - actions = mRobotDisplayMenu->actions(); - for (int i = 0; i < actions.size(); ++i) { - mRobotDisplayMenu->removeAction(actions[i]); - delete actions[i]; - } - - // add current robot and descendant robot specific camera and display items - if (selectedRobot) { - QAction *action = NULL; - bool hasDisplay = false; - bool hasCamera = false; - bool hasRangeFinder = false; - QList robotList = WbNodeUtilities::findDescendantNodesOfType(selectedRobot, isRobotNode, true); - robotList.prepend(selectedRobot); - foreach (WbNode *robotNode, robotList) { - WbRobot *robot = reinterpret_cast(robotNode); - QList devices = robot->renderingDevices(); - for (int i = 0; i < devices.size(); ++i) { - const WbRenderingDevice *device = devices[i]; - QString deviceName = device->name(); +void WbMainWindow::updateRobotNameInOverlaysMenu() { + const WbRobot *robot = static_cast(sender()); + QListIterator it(mOverlayMenu->actions()); + while (it.hasNext()) { + const QAction *action = it.next(); + QMenu *menu = action->menu(); + if (menu && menu->property("robot").value() == robot) { + QString robotName = robot->name(); #ifdef __linux__ - // fix Unity bug with underscores in menu item text - if (qgetenv("XDG_CURRENT_DESKTOP") == "Unity") - deviceName.replace("_", "__"); + // fix Unity bug with underscores in menu item text + if (qgetenv("XDG_CURRENT_DESKTOP") == "Unity") + robotName.replace("_", "__"); #endif - action = new QAction(this); - if (robot != selectedRobot) - action->setText(tr("Show '%2' overlay of robot '%1'").arg(robot->name()).arg(deviceName)); - else - action->setText(tr("Show '%1' overlay").arg(deviceName)); - if (device->nodeType() == WB_NODE_CAMERA) { - action->setStatusTip(tr("Show overlay of camera device '%1'.").arg(deviceName)); - mRobotCameraMenu->addAction(action); - hasCamera = true; - } else if (device->nodeType() == WB_NODE_RANGE_FINDER) { - action->setStatusTip(tr("Show overlay of range-finder device '%1'.").arg(deviceName)); - mRobotRangeFinderMenu->addAction(action); - hasRangeFinder = true; - } else if (device->nodeType() == WB_NODE_DISPLAY) { - action->setStatusTip(tr("Show overlay of display device '%1'.").arg(deviceName)); - mRobotDisplayMenu->addAction(action); - hasDisplay = true; - } else { - delete action; - continue; - } - action->setToolTip(mToggleFullScreenAction->statusTip()); - action->setCheckable(true); - action->setChecked(device->isOverlayEnabled()); - action->setEnabled(!device->isWindowActive()); - action->setProperty("renderingDevice", - QVariant::fromValue(static_cast(const_cast(device)))); - connect(action, &QAction::toggled, mSimulationView->view3D(), &WbView3D::setShowRenderingDevice); - connect(device, &WbRenderingDevice::overlayVisibilityChanged, action, &QAction::setChecked); - connect(device, &WbRenderingDevice::overlayStatusChanged, action, &QAction::setEnabled); - } + menu->setTitle(tr("'%1' Overlays").arg(robotName)); } + } +} - mRobotDisplayMenu->setEnabled(hasDisplay); - mRobotCameraMenu->setEnabled(hasCamera); - mRobotRangeFinderMenu->setEnabled(hasRangeFinder); +void WbMainWindow::removeRobotInOverlaysMenu(const WbRobot *robot) { + QListIterator it(mOverlayMenu->actions()); + while (it.hasNext()) { + QAction *action = it.next(); + const QMenu *menu = action->menu(); + if (menu && menu->property("robot").value() == robot) { + mOverlayMenu->removeAction(action); + return; + } } +} + +void WbMainWindow::addRobotInOverlaysMenu(WbRobot *robot) { + QAction *action = NULL; + QList cameraActions; + QList rangeFinderActions; + QList displayActions; + + QString robotName = robot->name(); +#ifdef __linux__ + // fix Unity bug with underscores in menu item text + if (qgetenv("XDG_CURRENT_DESKTOP") == "Unity") + robotName.replace("_", "__"); +#endif + QMenu *robotMenu = mOverlayMenu->addMenu(tr("'%1' Overlays").arg(robotName)); + robotMenu->setProperty("robot", QVariant::fromValue(static_cast(const_cast(robot)))); + connect(robot, &WbMatter::matterNameChanged, this, &WbMainWindow::updateRobotNameInOverlaysMenu); + + QListIterator devicesIt(robot->renderingDevices()); + while (devicesIt.hasNext()) { + const WbRenderingDevice *device = devicesIt.next(); + QString deviceName = device->name(); +#ifdef __linux__ + // fix Unity bug with underscores in menu item text + if (qgetenv("XDG_CURRENT_DESKTOP") == "Unity") + deviceName.replace("_", "__"); +#endif + action = new QAction(this); + action->setText(tr("Show '%1' Overlay").arg(deviceName)); + if (device->nodeType() == WB_NODE_CAMERA) { + action->setStatusTip(tr("Show overlay of camera device '%1' for robot '%2'.").arg(deviceName).arg(robotName)); + cameraActions << action; + } else if (device->nodeType() == WB_NODE_RANGE_FINDER) { + action->setStatusTip(tr("Show overlay of range-finder device '%1' for robot '%2'.").arg(deviceName).arg(robotName)); + rangeFinderActions << action; + } else if (device->nodeType() == WB_NODE_DISPLAY) { + action->setStatusTip(tr("Show overlay of display device '%1' for robot '%2'.").arg(deviceName).arg(robotName)); + displayActions << action; + } else { + delete action; + continue; + } + action->setToolTip(mToggleFullScreenAction->statusTip()); + action->setCheckable(true); + action->setChecked(device->isOverlayEnabled()); + action->setEnabled(!device->isWindowActive()); + action->setProperty("renderingDevice", QVariant::fromValue(static_cast(const_cast(device)))); + connect(action, &QAction::toggled, mSimulationView->view3D(), &WbView3D::setShowRenderingDevice); + connect(device, &WbRenderingDevice::overlayVisibilityChanged, action, &QAction::setChecked); + connect(device, &WbRenderingDevice::overlayStatusChanged, action, &QAction::setEnabled); + } + + if (cameraActions.isEmpty() && rangeFinderActions.isEmpty() && displayActions.isEmpty()) { + robotMenu->setEnabled(false); + return; + } + + QMenu *cameraMenu = robotMenu->addMenu(tr("Camera Devices")); + if (cameraActions.isEmpty()) + cameraMenu->setEnabled(false); + else { + QListIterator actionIt(cameraActions); + while (actionIt.hasNext()) + cameraMenu->addAction(actionIt.next()); + } + QMenu *rangeFinderMenu = robotMenu->addMenu(tr("RangeFinder Devices")); + if (rangeFinderActions.isEmpty()) + rangeFinderMenu->setEnabled(false); + else { + QListIterator actionIt(rangeFinderActions); + while (actionIt.hasNext()) + rangeFinderMenu->addAction(actionIt.next()); + } + QMenu *displayMenu = robotMenu->addMenu(tr("Display Devices")); + if (displayActions.isEmpty()) + displayMenu->setEnabled(false); + else { + QListIterator actionIt(displayActions); + while (actionIt.hasNext()) + displayMenu->addAction(actionIt.next()); + } +} + +void WbMainWindow::updateOverlayMenu() { + clearOverlaysMenu(); + + if (!WbWorld::instance()) + return; - actions = mOverlayMenu->actions(); - for (int i = 0; i < actions.size() - 3; ++i) - actions[i]->setVisible(selectedRobot != NULL); + QListIterator robotIt(WbWorld::instance()->robots()); + while (robotIt.hasNext()) + addRobotInOverlaysMenu(robotIt.next()); } void WbMainWindow::updateProjectPath(const QString &oldPath, const QString &newPath) { diff --git a/src/webots/gui/WbMainWindow.hpp b/src/webots/gui/WbMainWindow.hpp index 88607038f7a..fe7f2a88660 100644 --- a/src/webots/gui/WbMainWindow.hpp +++ b/src/webots/gui/WbMainWindow.hpp @@ -135,7 +135,11 @@ private slots: void showStatusBarMessage(WbLog::Level level, const QString &message); void editRobotController(); void showRobotWindow(); + void clearOverlaysMenu(); void updateOverlayMenu(); + void updateRobotNameInOverlaysMenu(); + void addRobotInOverlaysMenu(WbRobot *robot); + void removeRobotInOverlaysMenu(const WbRobot *robot); void createWorldLoadingProgressDialog(); void deleteWorldLoadingProgressDialog(); void setWorldLoadingProgress(const int progress); @@ -198,7 +202,6 @@ private slots: QString findHtmlFileName(const char *title); void enableToolsWidgetItems(bool enabled); void updateWindowTitle(); - void updateGui(); void updateSimulationMenu(); void writePreferences() const; void showDocument(const QString &url); diff --git a/src/webots/nodes/utils/WbWorld.cpp b/src/webots/nodes/utils/WbWorld.cpp index abf7adef930..1a28eb14815 100644 --- a/src/webots/nodes/utils/WbWorld.cpp +++ b/src/webots/nodes/utils/WbWorld.cpp @@ -591,6 +591,7 @@ void WbWorld::removeRobotIfPresent(WbRobot *robot) { return; mRobots.removeAll(robot); + emit robotRemoved(robot); } void WbWorld::addRobotIfNotAlreadyPresent(WbRobot *robot) { diff --git a/src/webots/nodes/utils/WbWorld.hpp b/src/webots/nodes/utils/WbWorld.hpp index 8020af2f0dd..55d9ef94f23 100644 --- a/src/webots/nodes/utils/WbWorld.hpp +++ b/src/webots/nodes/utils/WbWorld.hpp @@ -158,6 +158,7 @@ class WbWorld : public QObject { void worldLoadingHasProgressed(int percent); void viewpointChanged(); void robotAdded(WbRobot *robot); + void robotRemoved(WbRobot *robot); void resetRequested(bool restartControllers); public slots: diff --git a/src/webots/user_commands/WbContextMenuGenerator.cpp b/src/webots/user_commands/WbContextMenuGenerator.cpp index 13afb92bc22..1ef6a45aa7d 100644 --- a/src/webots/user_commands/WbContextMenuGenerator.cpp +++ b/src/webots/user_commands/WbContextMenuGenerator.cpp @@ -29,9 +29,7 @@ namespace WbContextMenuGenerator { static bool gAreRobotActionsEnabled = false; static bool gAreProtoActionsEnabled = false; static bool gAreExternProtoActionsEnabled = false; - static QMenu *gRobotCameraMenu = NULL; - static QMenu *gRobotRangeFinderMenu = NULL; - static QMenu *gRobotDisplayMenu = NULL; + static QMenu *gOverlaysMenu = NULL; void enableNodeActions(bool enabled) { gAreNodeActionsEnabled = enabled; @@ -45,14 +43,8 @@ namespace WbContextMenuGenerator { void enableExternProtoActions(bool enabled) { gAreExternProtoActionsEnabled = enabled; } - void setRobotCameraMenu(QMenu *menu) { - gRobotCameraMenu = menu; - } - void setRobotRangeFinderMenu(QMenu *menu) { - gRobotRangeFinderMenu = menu; - } - void setRobotDisplayMenu(QMenu *menu) { - gRobotDisplayMenu = menu; + void setOverlaysMenu(QMenu *menu) { + gOverlaysMenu = menu; } const QStringList fillTransformToItems(const WbNode *selectedNode) { @@ -105,10 +97,29 @@ namespace WbContextMenuGenerator { if (gAreRobotActionsEnabled) { contextMenu->addAction(WbActionManager::instance()->action(WbAction::EDIT_CONTROLLER)); contextMenu->addAction(WbActionManager::instance()->action(WbAction::SHOW_ROBOT_WINDOW)); + + assert(gOverlaysMenu); QMenu *subMenu = contextMenu->addMenu(QObject::tr("Overlays")); - subMenu->addMenu(gRobotCameraMenu); - subMenu->addMenu(gRobotRangeFinderMenu); - subMenu->addMenu(gRobotDisplayMenu); + subMenu->setEnabled(false); + QListIterator actionIt(gOverlaysMenu->actions()); + while (actionIt.hasNext()) { + const QAction *action = actionIt.next(); + const QMenu *robotMenu = action->menu(); + if (robotMenu && robotMenu->property("robot").value() == selectedNode) { + if (!robotMenu->isEnabled()) + break; + assert(!robotMenu->actions().isEmpty()); + QListIterator menuIt(robotMenu->actions()); + bool enabled = true; + while (menuIt.hasNext()) { + QMenu *deviceMenu = menuIt.next()->menu(); + enabled = enabled || deviceMenu->isEnabled(); + subMenu->addMenu(deviceMenu); + } + subMenu->setEnabled(enabled); + } + } + contextMenu->addSeparator(); } diff --git a/src/webots/user_commands/WbContextMenuGenerator.hpp b/src/webots/user_commands/WbContextMenuGenerator.hpp index c50aaeb7e88..feb7ea8bc38 100644 --- a/src/webots/user_commands/WbContextMenuGenerator.hpp +++ b/src/webots/user_commands/WbContextMenuGenerator.hpp @@ -31,9 +31,7 @@ namespace WbContextMenuGenerator { void enableProtoActions(bool enabled); void enableExternProtoActions(bool enabled); void enableRobotActions(bool enabled); - void setRobotCameraMenu(QMenu *menu); - void setRobotRangeFinderMenu(QMenu *menu); - void setRobotDisplayMenu(QMenu *menu); + void setOverlaysMenu(QMenu *menu); }; // namespace WbContextMenuGenerator #endif