From 73f279b61b90d487cbf5d4d3a0bd57e34025d8ba Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Thu, 9 Jul 2020 16:59:48 +0800 Subject: [PATCH] Gui: improve Std_SelUp command --- src/Gui/Action.cpp | 10 +- src/Gui/Action.h | 5 +- src/Gui/CommandView.cpp | 5 +- src/Gui/SelectionView.cpp | 60 ++++++++++-- src/Gui/SelectionView.h | 15 +++ src/Gui/Tree.cpp | 192 ++++++++++++++++++++++++++++++++++---- src/Gui/Tree.h | 3 +- src/Gui/Workbench.cpp | 2 - 8 files changed, 256 insertions(+), 36 deletions(-) diff --git a/src/Gui/Action.cpp b/src/Gui/Action.cpp index 69c0726ff4637..477e12940494c 100644 --- a/src/Gui/Action.cpp +++ b/src/Gui/Action.cpp @@ -38,6 +38,7 @@ #endif #include +#include #include "Action.h" #include "Application.h" #include "Command.h" @@ -51,6 +52,7 @@ #include "WorkbenchManager.h" #include "View3DInventor.h" #include "Document.h" +#include "SelectionView.h" #include #include @@ -1108,15 +1110,15 @@ SelUpAction::SelUpAction ( Command* pcCmd, QObject * parent ) SelUpAction::~SelUpAction() { + delete _menu; } void SelUpAction::addTo ( QWidget * w ) { if (!_menu) { - _menu = new QMenu(); + _menu = new SelUpMenu(nullptr); _action->setMenu(_menu); connect(_menu, SIGNAL(aboutToShow()), this, SLOT(onShowMenu())); - connect(_menu, SIGNAL(triggered(QAction*)), this, SLOT(onTriggered(QAction*))); } w->addAction(_action); } @@ -1127,9 +1129,9 @@ void SelUpAction::onShowMenu() TreeWidget::populateSelUpMenu(_menu); } -void SelUpAction::onTriggered(QAction *action) +void SelUpAction::popup(const QPoint &pt) { - TreeWidget::selectUp(action); + _menu->exec(pt); } #include "moc_Action.cpp" diff --git a/src/Gui/Action.h b/src/Gui/Action.h index 57ca9c4e08c03..d532be6b4cad4 100644 --- a/src/Gui/Action.h +++ b/src/Gui/Action.h @@ -25,6 +25,7 @@ #define GUI_ACTION_H #include +#include #include #include @@ -338,8 +339,6 @@ protected Q_SLOTS: QMenu* _menu; }; -// -------------------------------------------------------------------- - /** * Special action for Std_SelUp command. */ @@ -351,10 +350,10 @@ class GuiExport SelUpAction : public Action SelUpAction (Command* pcCmd, QObject * parent = 0); virtual ~SelUpAction(); void addTo (QWidget * w); + void popup(const QPoint &pt); protected Q_SLOTS: void onShowMenu(); - void onTriggered(QAction *); private: QMenu* _menu; diff --git a/src/Gui/CommandView.cpp b/src/Gui/CommandView.cpp index 140cf8ca2dfcd..19b8d976a272f 100644 --- a/src/Gui/CommandView.cpp +++ b/src/Gui/CommandView.cpp @@ -3343,13 +3343,14 @@ StdCmdSelUp::StdCmdSelUp() bool StdCmdSelUp::isActive(void) { - return Selection().size() == 1; + return App::GetApplication().getActiveDocument() && Selection().size() <= 1; } void StdCmdSelUp::activated(int iMsg) { Q_UNUSED(iMsg); - TreeWidget::selectUp(); + if (_pcAction) + static_cast(_pcAction)->popup(QCursor::pos()); } Action * StdCmdSelUp::createAction(void) diff --git a/src/Gui/SelectionView.cpp b/src/Gui/SelectionView.cpp index 2da8ccf698438..48cb3e8fc2a93 100644 --- a/src/Gui/SelectionView.cpp +++ b/src/Gui/SelectionView.cpp @@ -538,10 +538,8 @@ static QLatin1String _DefaultStyle( static QLatin1String _DefaultStyle("QMenu {menu-scrollable:1}"); #endif -SelectionMenu::SelectionMenu(QWidget *parent) - :QMenu(parent),pSelList(0) +static void setupMenuStyle(QMenu *menu) { - connect(this, SIGNAL(aboutToShow()), this, SLOT(beforeShow())); #if QT_VERSION >= 0x050000 auto hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/MainWindow"); @@ -569,15 +567,22 @@ SelectionMenu::SelectionMenu(QWidget *parent) } if(_Stylesheet.isEmpty()) _Stylesheet = _DefaultStyle; - setStyleSheet(_Stylesheet); - setWindowFlags(windowFlags() | Qt::FramelessWindowHint); - setAttribute(Qt::WA_NoSystemBackground, true); - setAttribute(Qt::WA_TranslucentBackground, true); + menu->setStyleSheet(_Stylesheet); + menu->setWindowFlags(menu->windowFlags() | Qt::FramelessWindowHint); + menu->setAttribute(Qt::WA_NoSystemBackground, true); + menu->setAttribute(Qt::WA_TranslucentBackground, true); #else - setStyleSheet(QLatin1String(_DefaultStyle)); + menu->setStyleSheet(QLatin1String(_DefaultStyle)); #endif } +SelectionMenu::SelectionMenu(QWidget *parent) + :QMenu(parent),pSelList(0) +{ + connect(this, SIGNAL(aboutToShow()), this, SLOT(beforeShow())); + setupMenuStyle(this); +} + void SelectionMenu::beforeShow() { #if QT_VERSION >= 0x050000 @@ -803,4 +808,43 @@ void SelectionMenu::onSubMenu() { tooltipIndex = -idx; } +// -------------------------------------------------------------------- + +SelUpMenu::SelUpMenu(QWidget *parent) + :QMenu(parent) +{ + connect(this, SIGNAL(triggered(QAction*)), this, SLOT(onTriggered(QAction *))); + connect(this, SIGNAL(hovered(QAction*)), this, SLOT(onHovered(QAction *))); + setupMenuStyle(this); +#if QT_VERSION >= 0x050100 + setToolTipsVisible(true); +#endif +} + +void SelUpMenu::mouseReleaseEvent(QMouseEvent *e) +{ + if (e->button() == Qt::RightButton) { + QAction *action = actionAt(e->pos()); + if (action) { + TreeWidget::selectUp(action, this); + return; + } + } + QMenu::mouseReleaseEvent(e); +} + +void SelUpMenu::onTriggered(QAction *action) +{ + TreeWidget::selectUp(action); +} + +void SelUpMenu::onHovered(QAction *action) +{ + App::SubObjectT objT = qvariant_cast(action->data()); + if (!objT.getSubObject()) + return; + Selection().setPreselect(objT.getDocumentName().c_str(), + objT.getObjectName().c_str(), objT.getSubName().c_str(), 0,0,0,2); +} + #include "moc_SelectionView.cpp" diff --git a/src/Gui/SelectionView.h b/src/Gui/SelectionView.h index 9e738eb4ba179..1e386a06d6ce4 100644 --- a/src/Gui/SelectionView.h +++ b/src/Gui/SelectionView.h @@ -131,6 +131,21 @@ public Q_SLOTS: int tooltipIndex; }; + +class GuiExport SelUpMenu : public QMenu +{ + Q_OBJECT +public: + SelUpMenu(QWidget *parent); + +protected: + void mouseReleaseEvent(QMouseEvent *e); + +protected Q_SLOTS: + void onTriggered(QAction *action); + void onHovered(QAction *action); +}; + } // namespace Gui #endif // GUI_DOCKWND_SELECTIONVIEW_H diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index ad241149f9104..02d917915ff2e 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -68,6 +68,8 @@ #include "Widgets.h" #include "ExpressionCompleter.h" #include "MetaTypes.h" +#include "Action.h" +#include "SelectionView.h" FC_LOG_LEVEL_INIT("Tree",false,true,true) @@ -3425,37 +3427,150 @@ void TreeWidget::populateSelUpMenu(QMenu *menu) return; auto sels = tree->selectedItems(); - if (sels.size() != 1) + if (sels.size() > 1) return; - QList items; - for (auto item=sels.front()->parent(); item; item=item->parent()) { - if (item->type() == ObjectType) { - items.prepend(static_cast(item)); - } else if (item->type() == DocumentType) { - QAction *action = menu->addAction(tree->documentPixmap, item->text(0)); - auto docItem = static_cast(item); - action->setData(QByteArray(docItem->document()->getDocument()->getName())); - break; + // Static variable that remembers the last hierarhcy + static std::vector lastItemNames; + + QList items; + QTreeWidgetItem *currentItem = nullptr; + + if (sels.size() == 0) { + // If no current selection, then populate using the last hierarhcy + if (lastItemNames.empty()) + return; + + auto docItem = tree->getDocumentItem( + Application::Instance->getDocument(lastItemNames.back().c_str())); + if (!docItem) { + lastItemNames.clear(); + return; + } + QTreeWidgetItem *lastItem = docItem; + items.push_back(lastItem); + for(std::size_t i=1; iforcePopulateItem(lastItem); + for (int j=0, c=lastItem->childCount(); jchild(j)->type() != ObjectType) + continue; + auto child = static_cast(lastItem->child(j)); + if (*itName == child->object()->getObject()->getNameInDocument()) { + lastItem = child; + items.push_back(child); + found = true; + break; + } + } + if (!found) { + lastItemNames.erase(lastItemNames.begin(), itName+1); + break; + } + } + if (items.empty()) { + lastItemNames.clear(); + return; + } + } else { + // Populate using the current hierarhcy + std::vector itemNames; + DocumentItem *docItem = nullptr; + for (auto item=sels.front(); item; item=item->parent()) { + if (item->type() == ObjectType) { + auto objItem = static_cast(item); + items.prepend(objItem); + itemNames.push_back(objItem->object()->getObject()->getNameInDocument()); + } else if (item->type() == DocumentType) { + docItem = static_cast(item); + items.prepend(docItem); + itemNames.push_back(docItem->document()->getDocument()->getName()); + break; + } + } + + if (!docItem || items.empty()) { + lastItemNames.clear(); + return; + } + + currentItem = items.back(); + if (lastItemNames.size() <= itemNames.size()) + lastItemNames = std::move(itemNames); + else { + // Check if the current hierarhcy is a sub-path of the last + // hierarhcy. If so, populate the tail path. + std::size_t i=0; + for (auto rit=lastItemNames.rbegin(), rit2=itemNames.rbegin(); + rit2!=itemNames.rend(); ++rit2, ++rit) + { + if (*rit != *rit2) + break; + ++i; + } + if (i != itemNames.size()) + lastItemNames = std::move(itemNames); + else { + QTreeWidgetItem *lastItem = currentItem; + for (; iforcePopulateItem(lastItem); + for (int j=0, c=lastItem->childCount(); jchild(j)->type() != ObjectType) + continue; + auto child = static_cast(lastItem->child(j)); + if (*itName == child->object()->getObject()->getNameInDocument()) { + lastItem = child; + items.push_back(child); + found = true; + break; + } + } + if (!found) { + lastItemNames.erase(lastItemNames.begin(), itName+1); + break; + } + } + } } } + // The first item is a document item. + QAction *action = menu->addAction(tree->documentPixmap, items.front()->text(0)); + action->setData(QByteArray(lastItemNames.back().c_str())); + items.pop_front(); if (items.empty()) return; - App::SubObjectT objT(items.front()->object()->getObject(), ""); + App::SubObjectT objT(static_cast(items.front())->object()->getObject(), ""); bool first = true; - for (auto item : items) { + for (auto _item : items) { + auto item = static_cast(_item); if (first) first = false; else objT.setSubName(objT.getSubName() + item->object()->getObject()->getNameInDocument() + "."); - QAction *action = menu->addAction(item->object()->getIcon(), item->text(0)); + QString text = item->text(0); + if (item == currentItem) { + // It would be ideal if Qt checkable menu item always show the tick + // instead of a embossed icon. But that's not the case on some + // system. So we add a unicode triangle here to indicate the + // current item. + text = QString::fromUtf8("\xe2\x96\xB6 ") + text; + } + QAction *action = menu->addAction(item->object()->getIcon(), text); + auto &txt = item->object()->getObject()->Label2.getStrValue(); + if (txt.size()) + action->setToolTip(QString::fromUtf8(txt.c_str())); action->setData(QVariant::fromValue(objT)); } + QString tooltip(tr("Left click to select. Right click to show children.")); + menu->setToolTip(tooltip); } -void TreeWidget::selectUp(QAction *action) +void TreeWidget::selectUp(QAction *action, QMenu *parentMenu) { auto tree = instance(); if (!tree) @@ -3492,13 +3607,17 @@ void TreeWidget::selectUp(QAction *action) } App::SubObjectT objT = qvariant_cast(action->data()); + if (!objT.getSubObject()) + return; auto it = tree->DocumentMap.find(Application::Instance->getDocument(objT.getDocument())); if (it == tree->DocumentMap.end()) return; - QTreeWidgetItem *item = it->second; + DocumentItem *docItem = it->second; + QTreeWidgetItem *item = docItem; for (auto obj : objT.getSubObjectList()) { bool found = false; + docItem->forcePopulateItem(item); for (int i=0, c=item->childCount(); ichild(i); if (child->type() != ObjectType) @@ -3514,12 +3633,42 @@ void TreeWidget::selectUp(QAction *action) return; } - if (item->type() == ObjectType) { + if (item->type() != ObjectType) + return; + + if (!parentMenu) { Gui::Selection().selStackPush(); Gui::Selection().clearCompleteSelection(); tree->onSelectTimer(); item->setSelected(true); tree->scrollToItem(item); + return; + } + + docItem->forcePopulateItem(item); + if (!item->childCount()) + return; + SelUpMenu menu(parentMenu); + for(int i=0, c=item->childCount(); ichild(i); + if (child->type() != ObjectType) + continue; + auto citem = static_cast(child); + App::SubObjectT sobjT = objT; + sobjT.setSubName(sobjT.getSubName() + + citem->object()->getObject()->getNameInDocument() + "."); + QAction *action = menu.addAction(citem->object()->getIcon(), citem->text(0)); + auto &txt = citem->object()->getObject()->Label2.getStrValue(); + if (txt.size()) + action->setToolTip(QString::fromUtf8(txt.c_str())); + action->setData(QVariant::fromValue(sobjT)); + } + if(menu.exec(QCursor::pos())) { + for(QWidget *w=parentMenu; w; w=w->parentWidget()) { + if(!qobject_cast(w)) + break; + w->hide(); + } } } @@ -3882,6 +4031,17 @@ bool DocumentItem::populateObject(App::DocumentObject *obj, bool delay) { return true; } +void DocumentItem::forcePopulateItem(QTreeWidgetItem *item) +{ + if(item->type() != TreeWidget::ObjectType) + return; + auto objItem = static_cast(item); + if (!objItem->populated) { + objItem->populated = true; + populateItem(objItem, true, false); + } +} + void DocumentItem::populateItem(DocumentObjectItem *item, bool refresh, bool delay) { (void)delay; diff --git a/src/Gui/Tree.h b/src/Gui/Tree.h index e6402d4ac75ce..c44c5d72f21e7 100644 --- a/src/Gui/Tree.h +++ b/src/Gui/Tree.h @@ -95,7 +95,7 @@ class TreeWidget : public QTreeWidget, public SelectionObserver static void selectLinkedObject(App::DocumentObject *linked); static void selectAllLinks(App::DocumentObject *obj); static void populateSelUpMenu(QMenu *menu); - static void selectUp(QAction *action=nullptr); + static void selectUp(QAction *action=nullptr, QMenu *parentMenu=nullptr); static void expandSelectedItems(TreeItemMode mode); static bool setupObjectMenu(QMenu &menu, const App::SubObjectT *sobj=nullptr); static bool isDragging(); @@ -319,6 +319,7 @@ class DocumentItem : public QTreeWidgetItem, public Base::Persistence void testStatus(void); void setData(int column, int role, const QVariant & value) override; void populateItem(DocumentObjectItem *item, bool refresh=false, bool delayUpdate=true); + void forcePopulateItem(QTreeWidgetItem *item); bool populateObject(App::DocumentObject *obj, bool delay=false); void selectAllInstances(const ViewProviderDocumentObject &vpd); bool showItem(DocumentObjectItem *item, bool select, bool force=false); diff --git a/src/Gui/Workbench.cpp b/src/Gui/Workbench.cpp index 89b80e3b1a17e..19b22caf2e7b9 100644 --- a/src/Gui/Workbench.cpp +++ b/src/Gui/Workbench.cpp @@ -557,8 +557,6 @@ void StdWorkbench::setupContextMenu(const char* recipient, MenuItem* item) const visu->setCommand("Visibility"); *visu << "Std_ToggleVisibility" << "Std_ToggleGroupVisibility" << "Std_ToggleShowOnTop" << "Std_ShowSelection" << "Std_HideSelection"; - if (Gui::Selection().size() == 1) - *item << "Std_SelUp"; *item << visu << "Std_ToggleSelectability" << "Std_TreeSelectAllInstances" << "Separator" << "Std_SetAppearance" << "Std_RandomColor" << "Separator"