Merge pull request #22544 from jffmichi/undockfullscreen
Gui: Allow undocking and fullscreen for all MDI windows
This commit is contained in:
@@ -1682,7 +1682,7 @@ void StdViewDock::activated(int iMsg)
|
||||
bool StdViewDock::isActive()
|
||||
{
|
||||
MDIView* view = getMainWindow()->activeWindow();
|
||||
return (qobject_cast<View3DInventor*>(view) ? true : false);
|
||||
return view != nullptr;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
@@ -1711,7 +1711,7 @@ void StdViewUndock::activated(int iMsg)
|
||||
bool StdViewUndock::isActive()
|
||||
{
|
||||
MDIView* view = getMainWindow()->activeWindow();
|
||||
return (qobject_cast<View3DInventor*>(view) ? true : false);
|
||||
return view != nullptr;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
@@ -1773,7 +1773,7 @@ void StdViewFullscreen::activated(int iMsg)
|
||||
bool StdViewFullscreen::isActive()
|
||||
{
|
||||
MDIView* view = getMainWindow()->activeWindow();
|
||||
return (qobject_cast<View3DInventor*>(view) ? true : false);
|
||||
return view != nullptr;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
@@ -1825,67 +1825,61 @@ void StdViewDockUndockFullscreen::activated(int iMsg)
|
||||
if (!view) // no active view
|
||||
return;
|
||||
|
||||
// nothing to do when the view is docked and 'Docked' is pressed
|
||||
if (iMsg == 0 && view->currentViewMode() == MDIView::Child)
|
||||
const auto oldmode = view->currentViewMode();
|
||||
auto mode = (MDIView::ViewMode)iMsg;
|
||||
|
||||
// Pressing the same button again toggles the view back to docked.
|
||||
if (mode == oldmode) {
|
||||
mode = MDIView::Child;
|
||||
}
|
||||
|
||||
if (mode == oldmode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Change the view mode after an mdi view was already visible doesn't
|
||||
// work well with Qt5 any more because of some strange OpenGL behaviour.
|
||||
// A workaround is to clone the mdi view, set its view mode and delete
|
||||
// the original view.
|
||||
Gui::Document* doc = Gui::Application::Instance->activeDocument();
|
||||
if (doc) {
|
||||
Gui::MDIView* clone = doc->cloneView(view);
|
||||
if (!clone)
|
||||
return;
|
||||
|
||||
const char* ppReturn = nullptr;
|
||||
if (view->onMsg("GetCamera", &ppReturn)) {
|
||||
std::string sMsg = "SetCamera ";
|
||||
sMsg += ppReturn;
|
||||
bool needsClone = mode == MDIView::Child || oldmode == MDIView::Child;
|
||||
Gui::MDIView* clone = needsClone ? view->clone() : nullptr;
|
||||
|
||||
const char** pReturnIgnore=nullptr;
|
||||
clone->onMsg(sMsg.c_str(), pReturnIgnore);
|
||||
}
|
||||
|
||||
if (iMsg==0) {
|
||||
if (clone) {
|
||||
if (mode == MDIView::Child) {
|
||||
getMainWindow()->addWindow(clone);
|
||||
}
|
||||
else if (iMsg==1) {
|
||||
if (view->currentViewMode() == MDIView::TopLevel)
|
||||
getMainWindow()->addWindow(clone);
|
||||
else
|
||||
clone->setCurrentViewMode(MDIView::TopLevel);
|
||||
}
|
||||
else if (iMsg==2) {
|
||||
if (view->currentViewMode() == MDIView::FullScreen)
|
||||
getMainWindow()->addWindow(clone);
|
||||
else
|
||||
clone->setCurrentViewMode(MDIView::FullScreen);
|
||||
else {
|
||||
clone->setCurrentViewMode(mode);
|
||||
}
|
||||
|
||||
// destroy the old view
|
||||
view->deleteSelf();
|
||||
}
|
||||
else {
|
||||
// no clone needed, simply change the view mode
|
||||
view->setCurrentViewMode(mode);
|
||||
}
|
||||
}
|
||||
|
||||
bool StdViewDockUndockFullscreen::isActive()
|
||||
{
|
||||
MDIView* view = getMainWindow()->activeWindow();
|
||||
if (qobject_cast<View3DInventor*>(view)) {
|
||||
// update the action group if needed
|
||||
auto pActGrp = qobject_cast<ActionGroup*>(_pcAction);
|
||||
if (pActGrp) {
|
||||
int index = pActGrp->checkedAction();
|
||||
int mode = (int)(view->currentViewMode());
|
||||
if (index != mode) {
|
||||
// active window has changed with another view mode
|
||||
pActGrp->setCheckedAction(mode);
|
||||
}
|
||||
}
|
||||
if (!view)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
// update the action group if needed
|
||||
auto pActGrp = qobject_cast<ActionGroup*>(_pcAction);
|
||||
if (pActGrp) {
|
||||
int index = pActGrp->checkedAction();
|
||||
int mode = (int)(view->currentViewMode());
|
||||
if (index != mode) {
|
||||
// active window has changed with another view mode
|
||||
pActGrp->setCheckedAction(mode);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2016,7 +2016,7 @@ void Document::addRootObjectsToGroup(const std::vector<App::DocumentObject*>& ob
|
||||
}
|
||||
}
|
||||
|
||||
MDIView *Document::createView(const Base::Type& typeId)
|
||||
MDIView* Document::createView(const Base::Type& typeId, CreateViewMode mode)
|
||||
{
|
||||
if (!typeId.isDerivedFrom(MDIView::getClassTypeId()))
|
||||
return nullptr;
|
||||
@@ -2062,11 +2062,16 @@ MDIView *Document::createView(const Base::Type& typeId)
|
||||
for (App::DocumentObject* obj : child_vps)
|
||||
view3D->getViewer()->removeViewProvider(getViewProvider(obj));
|
||||
|
||||
const char* name = getDocument()->Label.getValue();
|
||||
QString title = QStringLiteral("%1 : %2[*]")
|
||||
.arg(QString::fromUtf8(name)).arg(d->_iWinCount++);
|
||||
// When cloning the view, don't increment the window counter as the old view will be deleted
|
||||
// shortly after.
|
||||
if (mode != CreateViewMode::Clone) {
|
||||
const char* name = getDocument()->Label.getValue();
|
||||
QString title =
|
||||
QStringLiteral("%1 : %2[*]").arg(QString::fromUtf8(name)).arg(d->_iWinCount++);
|
||||
|
||||
view3D->setWindowTitle(title);
|
||||
}
|
||||
|
||||
view3D->setWindowTitle(title);
|
||||
view3D->setWindowModified(this->isModified());
|
||||
view3D->resize(400, 300);
|
||||
|
||||
@@ -2075,65 +2080,19 @@ MDIView *Document::createView(const Base::Type& typeId)
|
||||
view3D->onMsg(cameraSettings.c_str(),&ppReturn);
|
||||
}
|
||||
|
||||
getMainWindow()->addWindow(view3D);
|
||||
// When cloning the view, don't add the view to the main window. The whole purpose of the
|
||||
// workaround using cloned views is that the view can be shown in undocked/fullscreen mode
|
||||
// without having been docked before.
|
||||
if (mode != CreateViewMode::Clone) {
|
||||
getMainWindow()->addWindow(view3D);
|
||||
}
|
||||
|
||||
view3D->getViewer()->redraw();
|
||||
return view3D;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Gui::MDIView* Document::cloneView(Gui::MDIView* oldview)
|
||||
{
|
||||
if (!oldview)
|
||||
return nullptr;
|
||||
|
||||
if (oldview->is<View3DInventor>()) {
|
||||
auto view3D = new View3DInventor(this, getMainWindow());
|
||||
|
||||
auto firstView = static_cast<View3DInventor*>(oldview);
|
||||
std::string overrideMode = firstView->getViewer()->getOverrideMode();
|
||||
view3D->getViewer()->setOverrideMode(overrideMode);
|
||||
|
||||
view3D->getViewer()->setAxisCross(firstView->getViewer()->hasAxisCross());
|
||||
|
||||
// attach the viewproviders. we need to make sure that we only attach the toplevel ones
|
||||
// and not viewproviders which are claimed by other providers. To ensure this we first
|
||||
// add all providers and then remove the ones already claimed
|
||||
std::map<const App::DocumentObject*,ViewProviderDocumentObject*>::const_iterator It1;
|
||||
std::vector<App::DocumentObject*> child_vps;
|
||||
for (It1=d->_ViewProviderMap.begin();It1!=d->_ViewProviderMap.end();++It1) {
|
||||
view3D->getViewer()->addViewProvider(It1->second);
|
||||
std::vector<App::DocumentObject*> children = It1->second->claimChildren3D();
|
||||
child_vps.insert(child_vps.end(), children.begin(), children.end());
|
||||
}
|
||||
std::map<std::string,ViewProvider*>::const_iterator It2;
|
||||
for (It2=d->_ViewProviderMapAnnotation.begin();It2!=d->_ViewProviderMapAnnotation.end();++It2) {
|
||||
view3D->getViewer()->addViewProvider(It2->second);
|
||||
std::vector<App::DocumentObject*> children = It2->second->claimChildren3D();
|
||||
child_vps.insert(child_vps.end(), children.begin(), children.end());
|
||||
}
|
||||
|
||||
for (App::DocumentObject* obj : child_vps)
|
||||
view3D->getViewer()->removeViewProvider(getViewProvider(obj));
|
||||
|
||||
view3D->setWindowTitle(oldview->windowTitle());
|
||||
view3D->setWindowModified(oldview->isWindowModified());
|
||||
view3D->setWindowIcon(oldview->windowIcon());
|
||||
view3D->resize(oldview->size());
|
||||
|
||||
// FIXME: Add parameter to define behaviour by the calling instance
|
||||
// View provider editing
|
||||
if (d->_editViewProvider) {
|
||||
firstView->getViewer()->resetEditingViewProvider();
|
||||
view3D->getViewer()->setEditingViewProvider(d->_editViewProvider, d->_editMode);
|
||||
}
|
||||
|
||||
return view3D;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *Document::getCameraSettings() const {
|
||||
return cameraSettings.size()>10?cameraSettings.c_str()+10:cameraSettings.c_str();
|
||||
}
|
||||
|
||||
@@ -58,6 +58,12 @@ class Application;
|
||||
class DocumentPy;
|
||||
class TransactionViewProvider;
|
||||
|
||||
enum class CreateViewMode
|
||||
{
|
||||
Normal,
|
||||
Clone
|
||||
};
|
||||
|
||||
/** The Gui Document
|
||||
* This is the document on GUI level. Its main responsibility is keeping
|
||||
* track off open windows for a document and warning on unsaved closes.
|
||||
@@ -186,9 +192,7 @@ public:
|
||||
Gui::MDIView* getViewOfViewProvider(const Gui::ViewProvider*) const;
|
||||
Gui::MDIView* getViewOfNode(SoNode*) const;
|
||||
/// Create a new view
|
||||
MDIView *createView(const Base::Type& typeId);
|
||||
/// Create a clone of the given view
|
||||
Gui::MDIView* cloneView(Gui::MDIView*);
|
||||
MDIView* createView(const Base::Type& typeId, CreateViewMode mode = CreateViewMode::Normal);
|
||||
/** send messages to the active view
|
||||
* Send a specific massage to the active view and is able to receive a
|
||||
* return message
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#ifndef _PreComp_
|
||||
# include <boost/signals2.hpp>
|
||||
# include <boost/core/ignore_unused.hpp>
|
||||
# include <QAction>
|
||||
# include <QApplication>
|
||||
# include <QEvent>
|
||||
# include <QCloseEvent>
|
||||
@@ -129,6 +130,29 @@ void MDIView::deleteSelf()
|
||||
_pcDocument = nullptr;
|
||||
}
|
||||
|
||||
MDIView* MDIView::clone()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MDIView::cloneFrom(const MDIView& from)
|
||||
{
|
||||
setWindowTitle(from.windowTitle());
|
||||
setWindowIcon(from.windowIcon());
|
||||
resize(from.size());
|
||||
|
||||
// wstate is updated when changing from top-level mode to something else. This hasn't happened
|
||||
// yet if the original widget is currently in top-level mode. In this case we want to use the
|
||||
// actual windowState of the original widget instead of it's wstate.
|
||||
|
||||
if (from.currentViewMode() == TopLevel) {
|
||||
wstate = from.windowState();
|
||||
}
|
||||
else {
|
||||
wstate = from.wstate;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject* MDIView::getPyObject()
|
||||
{
|
||||
if (!pythonObject)
|
||||
@@ -207,7 +231,7 @@ bool MDIView::canClose()
|
||||
return true;
|
||||
}
|
||||
|
||||
void MDIView::closeEvent(QCloseEvent *e)
|
||||
void MDIView::closeEvent(QCloseEvent* e)
|
||||
{
|
||||
if (canClose()) {
|
||||
e->accept();
|
||||
@@ -353,15 +377,15 @@ QSize MDIView::minimumSizeHint () const
|
||||
return {400, 300};
|
||||
}
|
||||
|
||||
void MDIView::changeEvent(QEvent *e)
|
||||
|
||||
void MDIView::changeEvent(QEvent* e)
|
||||
{
|
||||
switch (e->type()) {
|
||||
case QEvent::ActivationChange:
|
||||
{
|
||||
// Forces this top-level window to be the active view of the main window
|
||||
if (isActiveWindow()) {
|
||||
if (getMainWindow()->activeWindow() != this)
|
||||
getMainWindow()->setActiveWindow(this);
|
||||
getMainWindow()->setActiveWindow(this);
|
||||
}
|
||||
} break;
|
||||
case QEvent::WindowTitleChange:
|
||||
@@ -377,6 +401,29 @@ void MDIView::changeEvent(QEvent *e)
|
||||
}
|
||||
}
|
||||
|
||||
bool MDIView::eventFilter(QObject* watched, QEvent* event)
|
||||
{
|
||||
// As long as this widget is a top-level window (either in 'TopLevel' or 'FullScreen' mode) we
|
||||
// need to be notified when an action is added to a widget. This action must also be added to
|
||||
// this window to allow one to make use of its shortcut (if defined).
|
||||
// Note: We don't need to care about removing an action if its parent widget gets destroyed.
|
||||
// This does the action itself for us.
|
||||
|
||||
if (watched != this && event->type() == QEvent::ActionAdded) {
|
||||
auto actionEvent = static_cast<QActionEvent*>(event);
|
||||
QAction* action = actionEvent->action();
|
||||
|
||||
if (!action->isSeparator()) {
|
||||
QList<QAction*> acts = actions();
|
||||
if (!acts.contains(action)) {
|
||||
addAction(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(Q_WS_X11)
|
||||
// To fix bug #0000345 move function declaration to here
|
||||
extern void qt_x11_wait_for_window_manager( QWidget* w ); // defined in qwidget_x11.cpp
|
||||
@@ -384,74 +431,86 @@ extern void qt_x11_wait_for_window_manager( QWidget* w ); // defined in qwidget_
|
||||
|
||||
void MDIView::setCurrentViewMode(ViewMode mode)
|
||||
{
|
||||
const ViewMode oldmode = MDIView::currentViewMode();
|
||||
if (oldmode == mode) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldmode == Child) {
|
||||
// remove window from MDIArea
|
||||
if (qobject_cast<QMdiSubWindow*>(parentWidget())) {
|
||||
getMainWindow()->removeWindow(this, false);
|
||||
setParent(nullptr);
|
||||
}
|
||||
}
|
||||
else if (oldmode == TopLevel) {
|
||||
// backup maximize state for top-level mode
|
||||
wstate = windowState();
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
// go to normal mode
|
||||
case Child:
|
||||
{
|
||||
if (this->currentMode == FullScreen) {
|
||||
showNormal();
|
||||
setWindowFlags(windowFlags() & ~Qt::Window);
|
||||
}
|
||||
else if (this->currentMode == TopLevel) {
|
||||
this->wstate = windowState();
|
||||
setWindowFlags( windowFlags() & ~Qt::Window );
|
||||
}
|
||||
getMainWindow()->addWindow(this);
|
||||
break;
|
||||
|
||||
if (this->currentMode != Child) {
|
||||
this->currentMode = Child;
|
||||
getMainWindow()->addWindow(this);
|
||||
getMainWindow()->activateWindow();
|
||||
update();
|
||||
}
|
||||
} break;
|
||||
// go to top-level mode
|
||||
case TopLevel:
|
||||
{
|
||||
if (this->currentMode == Child) {
|
||||
if (qobject_cast<QMdiSubWindow*>(this->parentWidget()))
|
||||
getMainWindow()->removeWindow(this,false);
|
||||
setWindowFlags(windowFlags() | Qt::Window);
|
||||
setParent(nullptr, Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint |
|
||||
Qt::WindowMinMaxButtonsHint);
|
||||
if (this->wstate & Qt::WindowMaximized)
|
||||
showMaximized();
|
||||
else
|
||||
showNormal();
|
||||
if (wstate & Qt::WindowMaximized) {
|
||||
// Only calling showMaximized doesn't work when the widget is currently in
|
||||
// full-screen mode. We need to exit full-screen mode first or the widget will end
|
||||
// up in normal mode. Same if the window is in child mode but maximized.
|
||||
setWindowState(windowState() & ~(Qt::WindowMaximized | Qt::WindowFullScreen));
|
||||
showMaximized();
|
||||
}
|
||||
else {
|
||||
showNormal();
|
||||
}
|
||||
break;
|
||||
|
||||
// go to full-screen mode
|
||||
case FullScreen:
|
||||
showFullScreen();
|
||||
break;
|
||||
}
|
||||
|
||||
currentMode = mode;
|
||||
|
||||
#if defined(Q_WS_X11)
|
||||
//extern void qt_x11_wait_for_window_manager( QWidget* w ); // defined in qwidget_x11.cpp
|
||||
qt_x11_wait_for_window_manager(this);
|
||||
if (mode == TopLevel && oldmode == Child) {
|
||||
// extern void qt_x11_wait_for_window_manager( QWidget* w ); // defined in
|
||||
// qwidget_x11.cpp
|
||||
qt_x11_wait_for_window_manager(this);
|
||||
}
|
||||
#endif
|
||||
activateWindow();
|
||||
}
|
||||
else if (this->currentMode == FullScreen) {
|
||||
if (this->wstate & Qt::WindowMaximized)
|
||||
showMaximized();
|
||||
else
|
||||
showNormal();
|
||||
}
|
||||
|
||||
this->currentMode = TopLevel;
|
||||
update();
|
||||
} break;
|
||||
// go to fullscreen mode
|
||||
case FullScreen:
|
||||
{
|
||||
if (this->currentMode == Child) {
|
||||
if (qobject_cast<QMdiSubWindow*>(this->parentWidget()))
|
||||
getMainWindow()->removeWindow(this,false);
|
||||
setWindowFlags(windowFlags() | Qt::Window);
|
||||
setParent(nullptr, Qt::Window);
|
||||
showFullScreen();
|
||||
}
|
||||
else if (this->currentMode == TopLevel) {
|
||||
this->wstate = windowState();
|
||||
showFullScreen();
|
||||
}
|
||||
activateWindow();
|
||||
|
||||
this->currentMode = FullScreen;
|
||||
update();
|
||||
} break;
|
||||
if (oldmode == Child) {
|
||||
// To make a global shortcut working from this window we need to add
|
||||
// all existing actions from the mainwindow and its sub-widgets
|
||||
|
||||
QList<QAction*> acts = getMainWindow()->findChildren<QAction*>();
|
||||
addActions(acts);
|
||||
|
||||
// To be notfified for new actions
|
||||
qApp->installEventFilter(this);
|
||||
}
|
||||
else if (mode == Child) {
|
||||
qApp->removeEventFilter(this);
|
||||
QList<QAction*> acts = actions();
|
||||
for (QAction* it : acts) {
|
||||
removeAction(it);
|
||||
}
|
||||
|
||||
// When switching from undocked to docked mode, the widget position is somehow not updated
|
||||
// correctly. In this case mapToGlobal(Point()) returns {0, 0} even though the widget is
|
||||
// clearly not at the top-left corner of the screen. We fix this by briefly changing the
|
||||
// maximum size of the widget.
|
||||
|
||||
const auto oldsize = maximumSize();
|
||||
setMaximumSize({1, 1});
|
||||
setMaximumSize(oldsize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,8 @@ public:
|
||||
*/
|
||||
~MDIView() override;
|
||||
|
||||
virtual MDIView* clone();
|
||||
|
||||
/// get called when the document is updated
|
||||
void onRelabel(Gui::Document *pDoc) override;
|
||||
virtual void viewAll();
|
||||
@@ -177,9 +179,13 @@ protected Q_SLOTS:
|
||||
virtual void windowStateChanged(QWidget*);
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *e) override;
|
||||
void closeEvent(QCloseEvent* e) override;
|
||||
/** \internal */
|
||||
void changeEvent(QEvent *e) override;
|
||||
void changeEvent(QEvent* e) override;
|
||||
|
||||
bool eventFilter(QObject* watched, QEvent* e) override;
|
||||
|
||||
void cloneFrom(const MDIView& from);
|
||||
|
||||
protected:
|
||||
PyObject* pythonObject;
|
||||
|
||||
@@ -447,12 +447,10 @@ MainWindow::MainWindow(QWidget * parent, Qt::WindowFlags f)
|
||||
|
||||
// connection between workspace, window menu and tab bar
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
|
||||
connect(d->windowMapper, &QSignalMapper::mappedWidget,
|
||||
this, &MainWindow::onSetActiveSubWindow);
|
||||
connect(d->windowMapper, &QSignalMapper::mappedWidget, this, &MainWindow::setActiveSubWindow);
|
||||
#else
|
||||
connect(d->windowMapper, &QSignalMapper::mappedObject,
|
||||
this, [=, this](QObject* object) {
|
||||
onSetActiveSubWindow(qobject_cast<QWidget*>(object));
|
||||
connect(d->windowMapper, &QSignalMapper::mappedObject, this, [=, this](QObject* object) {
|
||||
setActiveSubWindow(qobject_cast<QWidget*>(object));
|
||||
});
|
||||
#endif
|
||||
connect(d->mdiArea, &QMdiArea::subWindowActivated,
|
||||
@@ -960,15 +958,16 @@ void MainWindow::activatePreviousWindow ()
|
||||
|
||||
void MainWindow::activateWorkbench(const QString& name)
|
||||
{
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View");
|
||||
bool saveWB = hGrp->GetBool("SaveWBbyTab", false);
|
||||
QMdiSubWindow* subWin = d->mdiArea->activeSubWindow();
|
||||
if (subWin && saveWB) {
|
||||
QString currWb = subWin->property("ownWB").toString();
|
||||
if (currWb.isEmpty() || currWb != name) {
|
||||
subWin->setProperty("ownWB", name);
|
||||
}
|
||||
// remember workbench by tab (if enabled)
|
||||
|
||||
const ParameterGrp::handle hGrp =
|
||||
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View");
|
||||
const bool saveWB = hGrp->GetBool("SaveWBbyTab", false);
|
||||
MDIView* view = activeWindow();
|
||||
if (view && saveWB) {
|
||||
view->setProperty("ownWB", name);
|
||||
}
|
||||
|
||||
// emit this signal
|
||||
Q_EMIT workbenchActivated(name);
|
||||
updateActions(true);
|
||||
@@ -1157,6 +1156,7 @@ bool MainWindow::eventFilter(QObject* o, QEvent* e)
|
||||
void MainWindow::addWindow(MDIView* view)
|
||||
{
|
||||
// make workspace parent of view
|
||||
|
||||
bool isempty = d->mdiArea->subWindowList().isEmpty();
|
||||
auto child = qobject_cast<QMdiSubWindow*>(view->parentWidget());
|
||||
if(!child) {
|
||||
@@ -1186,7 +1186,9 @@ void MainWindow::addWindow(MDIView* view)
|
||||
// listen to the incoming events of the view
|
||||
view->installEventFilter(this);
|
||||
|
||||
// show the very first window in maximized mode
|
||||
// Show the new window. This will also call onWindowActivated. The very first window is shown in
|
||||
// maximized mode.
|
||||
|
||||
if (isempty)
|
||||
view->showMaximized();
|
||||
else
|
||||
@@ -1194,13 +1196,18 @@ void MainWindow::addWindow(MDIView* view)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the instance of Gui::MDiView from the main window and sends am event
|
||||
* Removes the instance of Gui::MDIView from the main window and sends n event
|
||||
* to the parent widget, a QMdiSubWindow to delete itself.
|
||||
* If you want to avoid that the Gui::MDIView instance gets destructed too you
|
||||
* must reparent it afterwards, e.g. set parent to NULL.
|
||||
*/
|
||||
void MainWindow::removeWindow(Gui::MDIView* view, bool close)
|
||||
{
|
||||
if (view->currentViewMode() != MDIView::Child) {
|
||||
FC_WARN("tried to remove an MDIView that is not currently in child mode");
|
||||
return;
|
||||
}
|
||||
|
||||
// free all connections
|
||||
disconnect(view, &MDIView::message, this, &MainWindow::showMessage);
|
||||
disconnect(this, &MainWindow::windowStateChanged, view, &MDIView::windowStateChanged);
|
||||
@@ -1234,6 +1241,7 @@ void MainWindow::removeWindow(Gui::MDIView* view, bool close)
|
||||
auto subwindow = qobject_cast<QMdiSubWindow*>(parent);
|
||||
if(subwindow && d->mdiArea->subWindowList().contains(subwindow)) {
|
||||
subwindow->setParent(nullptr);
|
||||
subwindow->deleteLater();
|
||||
|
||||
assert(!d->mdiArea->subWindowList().contains(subwindow));
|
||||
}
|
||||
@@ -1264,21 +1272,68 @@ void MainWindow::tabCloseRequested(int index)
|
||||
updateActions();
|
||||
}
|
||||
|
||||
void MainWindow::onSetActiveSubWindow(QWidget *window)
|
||||
void MainWindow::setActiveSubWindow(QWidget* window)
|
||||
{
|
||||
if (!window)
|
||||
auto mdi = qobject_cast<QMdiSubWindow*>(window);
|
||||
if (!mdi) {
|
||||
return;
|
||||
d->mdiArea->setActiveSubWindow(qobject_cast<QMdiSubWindow *>(window));
|
||||
updateActions();
|
||||
}
|
||||
|
||||
auto view = qobject_cast<MDIView*>(mdi->widget());
|
||||
setActiveWindow(view);
|
||||
}
|
||||
|
||||
void MainWindow::setActiveWindow(MDIView* view)
|
||||
{
|
||||
if (!view || d->activeView == view)
|
||||
if (!view) {
|
||||
return;
|
||||
onSetActiveSubWindow(view->parentWidget());
|
||||
}
|
||||
|
||||
// always update the focus and active sub window
|
||||
|
||||
// We need the explicit call to setFocus because it seems the focus window and the
|
||||
// activeSubWindow in the QMdiView can diverge when calling setActiveWindow while the MainWindow
|
||||
// is not currently active. In this case Qt will later set the previous focus window as active,
|
||||
// which will call onWindowActivated and activate the wrong window. This e.g. happens when
|
||||
// switching from a 3d view to a spreadsheet using the "Windows..." dialog or when docking a
|
||||
// spreadsheet that was in top-level/fullscreen mode. Why this could only be reproduced with a
|
||||
// spreadsheet remains a mystery.
|
||||
|
||||
view->setFocus();
|
||||
|
||||
auto subwindow = qobject_cast<QMdiSubWindow*>(view->parentWidget());
|
||||
if (subwindow) {
|
||||
d->mdiArea->setActiveSubWindow(subwindow);
|
||||
}
|
||||
|
||||
// if active view changed, notify rest of the application
|
||||
|
||||
if (view == d->activeView) {
|
||||
return;
|
||||
}
|
||||
|
||||
d->activeView = view;
|
||||
Application::Instance->viewActivated(view);
|
||||
|
||||
// activate/remember workbench by tab (if enabled)
|
||||
|
||||
const ParameterGrp::handle hGrp =
|
||||
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View");
|
||||
const bool saveWB = hGrp->GetBool("SaveWBbyTab", false);
|
||||
if (saveWB) {
|
||||
const QString currWb = view->property("ownWB").toString();
|
||||
if (!currWb.isEmpty()) {
|
||||
this->activateWorkbench(currWb);
|
||||
}
|
||||
else {
|
||||
const std::string name = WorkbenchManager::instance()->active()->name();
|
||||
view->setProperty("ownWB", QString::fromStdString(name));
|
||||
}
|
||||
}
|
||||
|
||||
// update actions
|
||||
|
||||
updateActions();
|
||||
}
|
||||
|
||||
void MainWindow::onWindowActivated(QMdiSubWindow* mdi)
|
||||
@@ -1289,41 +1344,11 @@ void MainWindow::onWindowActivated(QMdiSubWindow* mdi)
|
||||
return;
|
||||
}
|
||||
|
||||
// set active the appropriate window (it needs not to be part of mdiIds, e.g. directly after
|
||||
// creation)
|
||||
|
||||
auto view = dynamic_cast<MDIView*>(mdi->widget());
|
||||
|
||||
// set active the appropriate window (it needs not to be part of mdiIds, e.g. directly after creation)
|
||||
if (view)
|
||||
{
|
||||
d->activeView = view;
|
||||
Application::Instance->viewActivated(view);
|
||||
}
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View");
|
||||
bool saveWB = hGrp->GetBool("SaveWBbyTab", false);
|
||||
if (saveWB) {
|
||||
QString currWb = mdi->property("ownWB").toString();
|
||||
if (! currWb.isEmpty()) {
|
||||
this->activateWorkbench(currWb);
|
||||
}
|
||||
else {
|
||||
mdi->setProperty("ownWB", QString::fromStdString(WorkbenchManager::instance()->active()->name()));
|
||||
}
|
||||
}
|
||||
|
||||
// Even if windowActivated() signal is emitted mdi doesn't need to be a top-level window.
|
||||
// This happens e.g. if two windows are top-level and one of them gets docked again.
|
||||
// QWorkspace emits the signal then even though the other window is in front.
|
||||
// The consequence is that the docked window becomes the active window and not the undocked
|
||||
// window on top. This means that all accel events, menu and toolbar actions get redirected
|
||||
// to the (wrong) docked window.
|
||||
// But just testing whether the view is active and ignore it if not leads to other more serious problems -
|
||||
// at least under Linux. It seems to be a problem with the window manager.
|
||||
// Under Windows it seems to work though it's not really sure that it works reliably.
|
||||
// Result: So, we accept the first problem to be sure to avoid the second one.
|
||||
if ( !view /*|| !mdi->isActiveWindow()*/ )
|
||||
return; // either no MDIView or no valid object or no top-level window
|
||||
|
||||
updateActions(true);
|
||||
setActiveWindow(view);
|
||||
}
|
||||
|
||||
void MainWindow::onWindowsMenuAboutToShow()
|
||||
@@ -2179,13 +2204,7 @@ void MainWindow::changeEvent(QEvent *e)
|
||||
else if (e->type() == QEvent::ActivationChange) {
|
||||
if (isActiveWindow()) {
|
||||
QMdiSubWindow* mdi = d->mdiArea->currentSubWindow();
|
||||
if (mdi) {
|
||||
auto view = dynamic_cast<MDIView*>(mdi->widget());
|
||||
if (view && getMainWindow()->activeWindow() != view) {
|
||||
d->activeView = view;
|
||||
Application::Instance->viewActivated(view);
|
||||
}
|
||||
}
|
||||
setActiveSubWindow(mdi);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -318,7 +318,7 @@ private Q_SLOTS:
|
||||
/**
|
||||
* \internal
|
||||
*/
|
||||
void onSetActiveSubWindow(QWidget *window);
|
||||
void setActiveSubWindow(QWidget*);
|
||||
/**
|
||||
* Activates the associated tab to this widget.
|
||||
*/
|
||||
|
||||
@@ -91,7 +91,7 @@ DlgSettingsGeneral::DlgSettingsGeneral( QWidget* parent )
|
||||
ui->themesCombobox->setEnabled(true);
|
||||
Gui::Document* doc = Gui::Application::Instance->activeDocument();
|
||||
if (doc) {
|
||||
Gui::View3DInventor* view = static_cast<Gui::View3DInventor*>(doc->getActiveView());
|
||||
Gui::View3DInventor* view = qobject_cast<Gui::View3DInventor*>(doc->getActiveView());
|
||||
if (view) {
|
||||
Gui::View3DInventorViewer* viewer = view->getViewer();
|
||||
if (viewer->isEditing()) {
|
||||
@@ -639,7 +639,7 @@ void DlgSettingsGeneral::recreatePreferencePackMenu()
|
||||
button->setEnabled(true);
|
||||
Gui::Document* doc = Gui::Application::Instance->activeDocument();
|
||||
if (doc) {
|
||||
Gui::View3DInventor* view = static_cast<Gui::View3DInventor*>(doc->getActiveView());
|
||||
Gui::View3DInventor* view = qobject_cast<Gui::View3DInventor*>(doc->getActiveView());
|
||||
if (view) {
|
||||
Gui::View3DInventorViewer* viewer = view->getViewer();
|
||||
if (viewer->isEditing()) {
|
||||
|
||||
@@ -142,14 +142,13 @@ void TextureMapping::onFileChooserFileNameSelected(const QString& s)
|
||||
if (!this->grp) {
|
||||
Gui::Document* doc = Gui::Application::Instance->activeDocument();
|
||||
if (doc) {
|
||||
Gui::MDIView* mdi = doc->getActiveView();
|
||||
if (mdi && mdi->isDerivedFrom<View3DInventor>()) {
|
||||
Gui::View3DInventorViewer* view = static_cast<View3DInventor*>(mdi)->getViewer();
|
||||
this->grp = static_cast<SoGroup *>(view->getSceneGraph());
|
||||
auto view = qobject_cast<View3DInventor*>(doc->getActiveView());
|
||||
if (view) {
|
||||
this->grp = static_cast<SoGroup*>(view->getViewer()->getSceneGraph());
|
||||
this->grp->ref();
|
||||
this->grp->insertChild(this->tex,1);
|
||||
this->grp->insertChild(this->tex, 1);
|
||||
if (ui->checkEnv->isChecked())
|
||||
this->grp->insertChild(this->env,2);
|
||||
this->grp->insertChild(this->env, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <string>
|
||||
# include <QAction>
|
||||
# include <QApplication>
|
||||
# include <QKeyEvent>
|
||||
# include <QEvent>
|
||||
@@ -39,6 +38,7 @@
|
||||
# include <QPrintDialog>
|
||||
# include <QPrintPreviewDialog>
|
||||
# include <QStackedWidget>
|
||||
# include <QSurfaceFormat>
|
||||
# include <QTimer>
|
||||
# include <QUrl>
|
||||
# include <QWindow>
|
||||
@@ -94,9 +94,12 @@ void GLOverlayWidget::paintEvent(QPaintEvent*)
|
||||
|
||||
TYPESYSTEM_SOURCE_ABSTRACT(Gui::View3DInventor,Gui::MDIView)
|
||||
|
||||
View3DInventor::View3DInventor(Gui::Document* pcDocument, QWidget* parent,
|
||||
const QOpenGLWidget* sharewidget, Qt::WindowFlags wflags)
|
||||
: MDIView(pcDocument, parent, wflags), _viewerPy(nullptr)
|
||||
View3DInventor::View3DInventor(Gui::Document* pcDocument,
|
||||
QWidget* parent,
|
||||
const QOpenGLWidget* sharewidget,
|
||||
Qt::WindowFlags wflags)
|
||||
: MDIView(pcDocument, parent, wflags)
|
||||
, _viewerPy(nullptr)
|
||||
{
|
||||
stack = new QStackedWidget(this);
|
||||
// important for highlighting
|
||||
@@ -189,6 +192,27 @@ void View3DInventor::deleteSelf()
|
||||
MDIView::deleteSelf();
|
||||
}
|
||||
|
||||
View3DInventor* View3DInventor::clone()
|
||||
{
|
||||
auto mdiView = _pcDocument->createView(getClassTypeId(), CreateViewMode::Clone);
|
||||
auto view3D = static_cast<View3DInventor*>(mdiView);
|
||||
|
||||
view3D->cloneFrom(*this);
|
||||
view3D->getViewer()->setAxisCross(getViewer()->hasAxisCross());
|
||||
|
||||
// FIXME: Add parameter to define behaviour by the calling instance
|
||||
// View provider editing
|
||||
|
||||
int editMode;
|
||||
ViewProvider* editViewProvider = _pcDocument->getInEdit(nullptr, nullptr, &editMode);
|
||||
if (editViewProvider) {
|
||||
getViewer()->resetEditingViewProvider();
|
||||
view3D->getViewer()->setEditingViewProvider(editViewProvider, editMode);
|
||||
}
|
||||
|
||||
return view3D;
|
||||
}
|
||||
|
||||
PyObject *View3DInventor::getPyObject()
|
||||
{
|
||||
if (!_viewerPy)
|
||||
@@ -722,33 +746,27 @@ void View3DInventor::dragEnterEvent (QDragEnterEvent * e)
|
||||
e->ignore();
|
||||
}
|
||||
|
||||
void View3DInventor::setCurrentViewMode(ViewMode newmode)
|
||||
void View3DInventor::setCurrentViewMode(ViewMode mode)
|
||||
{
|
||||
ViewMode oldmode = MDIView::currentViewMode();
|
||||
if (oldmode == newmode)
|
||||
ViewMode oldmode = currentViewMode();
|
||||
if (mode == oldmode) {
|
||||
return;
|
||||
|
||||
if (newmode == Child) {
|
||||
// Fix in two steps:
|
||||
// The mdi view got a QWindow when it became a top-level widget and when resetting it to a child widget
|
||||
// the QWindow must be deleted because it has an impact on resize events and may break the layout of
|
||||
// mdi view inside the QMdiSubWindow.
|
||||
// In the second step below the layout must be invalidated after it's again a child widget to make sure
|
||||
// the mdi view fits into the QMdiSubWindow.
|
||||
QWindow* winHandle = this->windowHandle();
|
||||
if (winHandle)
|
||||
winHandle->destroy();
|
||||
}
|
||||
|
||||
MDIView::setCurrentViewMode(newmode);
|
||||
if (mode == Child) {
|
||||
// Fix in two steps:
|
||||
// The mdi view got a QWindow when it became a top-level widget and when resetting it to a
|
||||
// child widget the QWindow must be deleted because it has an impact on resize events and
|
||||
// may break the layout of mdi view inside the QMdiSubWindow. In the second step below the
|
||||
// layout must be invalidated after it's again a child widget to make sure the mdi view fits
|
||||
// into the QMdiSubWindow.
|
||||
QWindow* winHandle = this->windowHandle();
|
||||
if (winHandle) {
|
||||
winHandle->destroy();
|
||||
}
|
||||
}
|
||||
|
||||
// Internally the QOpenGLWidget switches of the multi-sampling and there is no
|
||||
// way to switch it on again. So as a workaround we just re-create a new viewport
|
||||
// The method is private but defined as slot to avoid to call it by accident.
|
||||
//int index = _viewer->metaObject()->indexOfMethod("replaceViewport()");
|
||||
//if (index >= 0) {
|
||||
// _viewer->qt_metacall(QMetaObject::InvokeMetaMethod, index, 0);
|
||||
//}
|
||||
MDIView::setCurrentViewMode(mode);
|
||||
|
||||
// This widget becomes the focus proxy of the embedded GL widget if we leave
|
||||
// the 'Child' mode. If we reenter 'Child' mode the focus proxy is reset to 0.
|
||||
@@ -760,26 +778,18 @@ void View3DInventor::setCurrentViewMode(ViewMode newmode)
|
||||
//
|
||||
// It is important to set the focus proxy to get all key events otherwise we would lose
|
||||
// control after redirecting the first key event to the GL widget.
|
||||
|
||||
if (oldmode == Child) {
|
||||
// To make a global shortcut working from this window we need to add
|
||||
// all existing actions from the mainwindow and its sub-widgets
|
||||
QList<QAction*> acts = getMainWindow()->findChildren<QAction*>();
|
||||
this->addActions(acts);
|
||||
_viewer->getGLWidget()->setFocusProxy(this);
|
||||
// To be notfified for new actions
|
||||
qApp->installEventFilter(this);
|
||||
}
|
||||
else if (newmode == Child) {
|
||||
else if (mode == Child) {
|
||||
_viewer->getGLWidget()->setFocusProxy(nullptr);
|
||||
qApp->removeEventFilter(this);
|
||||
QList<QAction*> acts = this->actions();
|
||||
for (QAction* it : acts)
|
||||
this->removeAction(it);
|
||||
|
||||
// Step two
|
||||
auto mdi = qobject_cast<QMdiSubWindow*>(parentWidget());
|
||||
if (mdi && mdi->layout())
|
||||
if (mdi && mdi->layout()) {
|
||||
mdi->layout()->invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -874,27 +884,6 @@ RayPickInfo View3DInventor::getObjInfoRay(Base::Vector3d* startvec, Base::Vector
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool View3DInventor::eventFilter(QObject* watched, QEvent* e)
|
||||
{
|
||||
// As long as this widget is a top-level window (either in 'TopLevel' or 'FullScreen' mode) we
|
||||
// need to be notified when an action is added to a widget. This action must also be added to
|
||||
// this window to allow one to make use of its shortcut (if defined).
|
||||
// Note: We don't need to care about removing an action if its parent widget gets destroyed.
|
||||
// This does the action itself for us.
|
||||
if (watched != this && e->type() == QEvent::ActionAdded) {
|
||||
auto a = static_cast<QActionEvent*>(e);
|
||||
QAction* action = a->action();
|
||||
|
||||
if (!action->isSeparator()) {
|
||||
QList<QAction*> actions = this->actions();
|
||||
if (!actions.contains(action))
|
||||
this->addAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void View3DInventor::keyPressEvent (QKeyEvent* e)
|
||||
{
|
||||
// See StdViewDockUndockFullscreen::activated()
|
||||
|
||||
@@ -85,6 +85,8 @@ public:
|
||||
View3DInventor(Gui::Document* pcDocument, QWidget* parent, const QOpenGLWidget* sharewidget = nullptr, Qt::WindowFlags wflags=Qt::WindowFlags());
|
||||
~View3DInventor() override;
|
||||
|
||||
View3DInventor* clone() override;
|
||||
|
||||
/// Message handler
|
||||
bool onMsg(const char* pMsg, const char** ppReturn) override;
|
||||
bool onHasMsg(const char* pMsg) const override;
|
||||
@@ -132,9 +134,6 @@ public Q_SLOTS:
|
||||
protected Q_SLOTS:
|
||||
void stopAnimating();
|
||||
|
||||
public:
|
||||
bool eventFilter(QObject*, QEvent* ) override;
|
||||
|
||||
private:
|
||||
void applySettings();
|
||||
|
||||
|
||||
@@ -227,12 +227,10 @@ void ViewProvider::eventCallback(void * ud, SoEventCallback * node)
|
||||
// react only on key release
|
||||
// Let first selection mode terminate
|
||||
Gui::Document* doc = Gui::Application::Instance->activeDocument();
|
||||
auto view = static_cast<Gui::View3DInventor*>(doc->getActiveView());
|
||||
if (view)
|
||||
{
|
||||
const auto view = qobject_cast<Gui::View3DInventor*>(doc->getActiveView());
|
||||
if (view) {
|
||||
Gui::View3DInventorViewer* viewer = view->getViewer();
|
||||
if (viewer->isSelecting())
|
||||
{
|
||||
if (viewer->isSelecting()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,6 +556,16 @@ void ViewProviderPage::setGrid()
|
||||
}
|
||||
}
|
||||
|
||||
QGSPage* ViewProviderPage::getQGSPage() const
|
||||
{
|
||||
return m_graphicsScene;
|
||||
}
|
||||
|
||||
QGVPage* ViewProviderPage::getQGVPage() const
|
||||
{
|
||||
return m_graphicsView;
|
||||
}
|
||||
|
||||
ViewProviderPageExtension* ViewProviderPage::getVPPExtension() const
|
||||
{
|
||||
auto vpe = getExtensionByType<ViewProviderPageExtension>();
|
||||
|
||||
@@ -131,8 +131,8 @@ public:
|
||||
|
||||
void setGrid();
|
||||
|
||||
QGSPage* getQGSPage() const { return m_graphicsScene; }
|
||||
QGVPage* getQGVPage() const { return m_graphicsView; }
|
||||
QGSPage* getQGSPage() const;
|
||||
QGVPage* getQGVPage() const;
|
||||
|
||||
ViewProviderPageExtension* getVPPExtension() const;
|
||||
|
||||
@@ -149,7 +149,7 @@ protected:
|
||||
private:
|
||||
QPointer<MDIViewPage> m_mdiView;
|
||||
std::string m_pageName;
|
||||
QGVPage* m_graphicsView;
|
||||
QPointer<QGVPage> m_graphicsView;
|
||||
QGSPage* m_graphicsScene;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user