From 62cbaf73368bcdb4a1ec1a85dbc207e7eda3110a Mon Sep 17 00:00:00 2001 From: paddle Date: Thu, 22 Jan 2026 14:21:28 +0100 Subject: [PATCH] Assembly: Fix "deactivated by activating a App::Part, assembly stay in edit" Fix "Activating a body in a part in an assembly deactivates the assembly and activate the part" Fix "A manually deactivated assembly is still restoring later" --- src/Gui/ActiveObjectList.cpp | 8 ++++ src/Gui/ActiveObjectList.h | 1 + src/Gui/Document.h | 4 +- src/Gui/ViewProviderPart.cpp | 4 +- src/Gui/ViewProviderPart.h | 3 +- src/Mod/Assembly/Gui/ViewProviderAssembly.cpp | 45 ++++++++++++++----- src/Mod/Assembly/Gui/ViewProviderAssembly.h | 2 + src/Mod/PartDesign/Gui/Utils.cpp | 6 ++- 8 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/Gui/ActiveObjectList.cpp b/src/Gui/ActiveObjectList.cpp index 6d27c0f22c..49e64b32fa 100644 --- a/src/Gui/ActiveObjectList.cpp +++ b/src/Gui/ActiveObjectList.cpp @@ -187,6 +187,9 @@ void Gui::ActiveObjectList::setObject( } if (!obj) { + if (_Doc) { + _Doc->signalActivatedVP(nullptr, name); + } return; } @@ -202,6 +205,11 @@ void Gui::ActiveObjectList::setObject( _ObjectMap[name] = info; setHighlight(info, mode, true); + + auto vp = freecad_cast(Application::Instance->getViewProvider(obj)); + if (vp) { + vp->getDocument()->signalActivatedVP(vp, name); + } } bool Gui::ActiveObjectList::hasObject(const char* name) const diff --git a/src/Gui/ActiveObjectList.h b/src/Gui/ActiveObjectList.h index fee647cae4..b34b0d6f05 100644 --- a/src/Gui/ActiveObjectList.h +++ b/src/Gui/ActiveObjectList.h @@ -106,5 +106,6 @@ private: static const char PDBODYKEY[] = "pdbody"; static const char PARTKEY[] = "part"; +static const char ASSEMBLYKEY[] = "assembly"; #endif diff --git a/src/Gui/Document.h b/src/Gui/Document.h index b8a1213561..ea95612a4a 100644 --- a/src/Gui/Document.h +++ b/src/Gui/Document.h @@ -121,8 +121,10 @@ public: signalChangedObject; /// signal on renamed Object mutable fastsignals::signal signalRelabelObject; - /// signal on activated Object + /// signal on activated Object (relay of App activation signal) mutable fastsignals::signal signalActivatedObject; + /// signal on activated Object in the tree (bold item) + mutable fastsignals::signal signalActivatedVP; /// signal on entering in edit mode mutable fastsignals::signal signalInEdit; /// signal on leaving edit mode diff --git a/src/Gui/ViewProviderPart.cpp b/src/Gui/ViewProviderPart.cpp index 7c576959a6..a0db89d0c2 100644 --- a/src/Gui/ViewProviderPart.cpp +++ b/src/Gui/ViewProviderPart.cpp @@ -77,7 +77,7 @@ void ViewProviderPart::setupContextMenu(QMenu* menu, QObject* receiver, const ch ViewProviderGeometryObject::setupContextMenu(menu, receiver, member); } -bool ViewProviderPart::isActivePart() +bool ViewProviderPart::isActivePart(const char* key) { App::DocumentObject* activePart = nullptr; auto activeDoc = Gui::Application::Instance->activeDocument(); @@ -89,7 +89,7 @@ bool ViewProviderPart::isActivePart() return false; } - activePart = activeView->getActiveObject(PARTKEY); + activePart = activeView->getActiveObject(key); if (activePart == this->getObject()) { return true; diff --git a/src/Gui/ViewProviderPart.h b/src/Gui/ViewProviderPart.h index 5d9ec8db4a..336d2fd105 100644 --- a/src/Gui/ViewProviderPart.h +++ b/src/Gui/ViewProviderPart.h @@ -23,6 +23,7 @@ #ifndef GUI_VIEWPROVIDER_ViewProviderPart_H #define GUI_VIEWPROVIDER_ViewProviderPart_H +#include "ActiveObjectList.h" #include "ViewProviderGeometryObject.h" #include "ViewProviderOriginGroup.h" #include "ViewProviderFeaturePython.h" @@ -44,7 +45,7 @@ public: bool doubleClicked() override; void setupContextMenu(QMenu* menu, QObject* receiver, const char* member) override; - bool isActivePart(); + bool isActivePart(const char* key = PARTKEY); void toggleActivePart(); /// deliver the icon shown in the tree view diff --git a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp index 5056ad9201..5892bc4e98 100644 --- a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp +++ b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp @@ -143,7 +143,7 @@ void ViewProviderAssembly::setupContextMenu(QMenu* menu, QObject* receiver, cons QAction* act = menu->addAction(QObject::tr("Active object")); act->setCheckable(true); - act->setChecked(isActivePart()); + act->setChecked(isActivePart(ASSEMBLYKEY)); func->trigger(act, [this]() { this->doubleClicked(); }); ViewProviderDragger::setupContextMenu(menu, receiver, member); // NOLINT @@ -153,6 +153,7 @@ bool ViewProviderAssembly::doubleClicked() { if (isInEditMode()) { autoCollapseOnDeactivation = true; + getDocument()->setEditRestore(false); getDocument()->resetEdit(); } else { @@ -280,6 +281,7 @@ bool ViewProviderAssembly::setEdit(int mode) { if (mode == ViewProvider::Default) { // Ask that this edit mode be restored. For example if it is quit to edit a sketch. + Base::Console().warning("setEdit\n"); getDocument()->setEditRestore(true); autoCollapseOnDeactivation = false; @@ -290,7 +292,7 @@ bool ViewProviderAssembly::setEdit(int mode) "Gui.getDocument(appDoc).ActiveView.setActiveObject('%s', " "appDoc.getObject('%s'))", this->getObject()->getDocument()->getName(), - PARTKEY, + ASSEMBLYKEY, this->getObject()->getNameInDocument() ); @@ -310,6 +312,13 @@ bool ViewProviderAssembly::setEdit(int mode) UpdateSolverInformation(); }); + connectActivatedVP = getDocument()->signalActivatedVP.connect(std::bind( + &ViewProviderAssembly::slotActivatedVP, + this, + std::placeholders::_1, + std::placeholders::_2 + )); + assembly->solve(); return true; @@ -334,13 +343,15 @@ void ViewProviderAssembly::unsetEdit(int mode) } // Set the part as not 'Activated' ie not bold in the tree. - Gui::Command::doCommand( - Gui::Command::Gui, - "appDoc = App.getDocument('%s')\n" - "Gui.getDocument(appDoc).ActiveView.setActiveObject('%s', None)", - this->getObject()->getDocument()->getName(), - PARTKEY - ); + if (isActivePart(ASSEMBLYKEY)) { + Gui::Command::doCommand( + Gui::Command::Gui, + "appDoc = App.getDocument('%s')\n" + "Gui.getDocument(appDoc).ActiveView.setActiveObject('%s', None)", + this->getObject()->getDocument()->getName(), + ASSEMBLYKEY + ); + } Gui::TaskView::TaskView* taskView = Gui::Control().taskPanel(); if (taskView) { @@ -349,12 +360,26 @@ void ViewProviderAssembly::unsetEdit(int mode) } connectSolverUpdate.disconnect(); + connectActivatedVP.disconnect(); return; } ViewProviderPart::unsetEdit(mode); } +void ViewProviderAssembly::slotActivatedVP(const Gui::ViewProviderDocumentObject* vp, const char* name) +{ + if (name && strcmp(name, ASSEMBLYKEY) == 0) { + + // If the new active VP is NOT this assembly (meaning we lost activation or it was cleared) + if (vp != this && isInEditMode()) { + autoCollapseOnDeactivation = true; + getDocument()->setEditRestore(false); + getDocument()->resetEdit(); + } + } +} + void ViewProviderAssembly::setDragger() { // Create the dragger coin object @@ -404,7 +429,7 @@ App::DocumentObject* ViewProviderAssembly::getActivePart() const if (!activeView) { return nullptr; } - return activeView->getActiveObject(PARTKEY); + return activeView->getActiveObject(ASSEMBLYKEY); } bool ViewProviderAssembly::keyPressed(bool pressed, int key) diff --git a/src/Mod/Assembly/Gui/ViewProviderAssembly.h b/src/Mod/Assembly/Gui/ViewProviderAssembly.h index 9ac9499f7e..a6b37aa697 100644 --- a/src/Mod/Assembly/Gui/ViewProviderAssembly.h +++ b/src/Mod/Assembly/Gui/ViewProviderAssembly.h @@ -263,6 +263,7 @@ private: ); void slotAboutToOpenTransaction(const std::string& cmdName); + void slotActivatedVP(const Gui::ViewProviderDocumentObject* vp, const char* name); struct ComponentState { @@ -289,6 +290,7 @@ private: TaskAssemblyMessages* taskSolver; + fastsignals::connection connectActivatedVP; fastsignals::connection connectSolverUpdate; fastsignals::scoped_connection m_preTransactionConn; }; diff --git a/src/Mod/PartDesign/Gui/Utils.cpp b/src/Mod/PartDesign/Gui/Utils.cpp index 4a3e7ed1d8..47179c40bb 100644 --- a/src/Mod/PartDesign/Gui/Utils.cpp +++ b/src/Mod/PartDesign/Gui/Utils.cpp @@ -284,7 +284,11 @@ App::Part* getActivePart() { Gui::MDIView* activeView = Gui::Application::Instance->activeView(); if (activeView) { - return activeView->getActiveObject(PARTKEY); + auto* obj = activeView->getActiveObject(PARTKEY); + if (!obj) { + obj = activeView->getActiveObject(ASSEMBLYKEY); + } + return obj; } else { return nullptr;