16.12.2021, 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

Commit ebb0ef7b authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Merge branch 'mdi-cleanup' of /mnt/bigone/git/repositories/berge/campvis into development

parents d97f06d1 e2830976
......@@ -7,8 +7,9 @@ MESSAGE(STATUS "Configuring CAMPVis Application")
FILE(GLOB CampvisApplicationSources
*.cpp
gui/*.cpp
gui/properties/*.cpp
gui/adjusterwidgets/*.cpp
gui/mdi/*.cpp
gui/properties/*.cpp
tools/*.cpp
)
......@@ -36,9 +37,9 @@ SET(CampvisApplicationToBeMocced
gui/qtdatahandle.h
gui/logviewerwidget.h
gui/loghighlighter.h
gui/pipelinemdisubwindow.h
gui/visualizationpipelinewidget.h
gui/visualizationpipelinewrapper.h
gui/mdi/mdidockarea.h
gui/mdi/mdidockedwindow.h
gui/mdi/mdifloatingwindow.h
gui/adjusterwidgets/doubleadjusterwidget.h
gui/adjusterwidgets/intadjusterwidget.h
gui/properties/abstractpropertywidget.h
......
......@@ -34,7 +34,6 @@
#include "application/gui/datacontainerinspectorwidget.h"
#include "application/gui/datacontainerinspectorcanvas.h"
#include "application/gui/qtdatahandle.h"
#include "application/gui/visualizationpipelinewrapper.h"
#include "core/datastructures/datacontainer.h"
#include "core/pipeline/abstractpipeline.h"
#include "core/pipeline/abstractprocessor.h"
......@@ -86,7 +85,7 @@ namespace campvis {
setTabPosition(Qt::TopDockWidgetArea, QTabWidget::North);
_mdiArea = new QMdiArea();
_mdiArea = new MdiDockArea();
_mdiArea->tileSubWindows();
setCentralWidget(_mdiArea);
......@@ -260,7 +259,9 @@ namespace campvis {
}
void MainWindow::addVisualizationPipelineWidget(const std::string& name, QWidget* canvas) {
VisualizationPipelineWrapper* widget = new VisualizationPipelineWrapper(name, canvas, _mdiArea, this);
QMdiSubWindow* mdiSubWindow = _mdiArea->addSubWindow(canvas);
const QString& windowTitle = QString::fromStdString(name);
mdiSubWindow->setWindowTitle(windowTitle);
}
QDockWidget* MainWindow::dockPrimaryWidget(const std::string& name, QWidget* widget) {
......
......@@ -32,6 +32,7 @@
#include "sigslot/sigslot.h"
#include "application/campvisapplication.h"
#include "application/gui/mdi/mdidockarea.h"
#include "application/gui/pipelinetreewidget.h"
#include "application/gui/properties/propertycollectionwidget.h"
#include "application/gui/logviewerwidget.h"
......@@ -43,7 +44,6 @@
#include <QHBoxLayout>
#include <QPushButton>
#include <QComboBox>
#include <QMdiArea>
#include <QScrollArea>
#include <vector>
......@@ -163,7 +163,7 @@ namespace campvis {
CampVisApplication* _application; ///< Pointer to the application hosting the whole stuff
QMdiArea* _mdiArea; ///< MDI area (the window's central widget)
MdiDockArea* _mdiArea; ///< MDI area (the window's central widget)
QWidget* _containerWidget; ///< Widget to manage the app's DataContainers and pipelines
QComboBox* _cbPipelineFactory; ///< Combobox for selecting the Pipelines from the PipelineFactory
QPushButton* _btnPipelineFactory; ///< Button to add a Pipeline from the factory
......
......@@ -27,71 +27,68 @@
//
// ================================================================================================
#include "visualizationpipelinewrapper.h"
#include "mdidockarea.h"
namespace campvis {
VisualizationPipelineWrapper::VisualizationPipelineWrapper(const std::string& name, QWidget* canvas,
QMdiArea* mdiArea, QObject* parent /*= 0*/)
: QObject(parent)
, _mdiArea(mdiArea)
{
_pipelineWidget = new VisualizationPipelineWidget(canvas);
_mdiSubWindow = new PipelineMdiSubWindow();
MdiDockedWindow* MdiDockArea::addSubWindow(QWidget* widget, Qt::WindowFlags windowFlags /*= 0*/) {
MdiDockedWindow* dockedWindow = new MdiDockedWindow();
_mdiSubWindow->setWidget(_pipelineWidget);
mdiArea->addSubWindow(_mdiSubWindow);
dockedWindow->setWidget(widget);
QMdiArea::addSubWindow(dockedWindow, windowFlags);
widget->show();
this->tileSubWindows();
const QString& windowTitle = QString::fromStdString(name);
_mdiSubWindow->setWindowTitle(windowTitle);
_pipelineWidget->setWindowTitle(windowTitle);
_pipelineWidget->show();
_mdiArea->tileSubWindows();
connect(dockedWindow, SIGNAL(s_positionChanged(MdiDockedWindow*, const QPoint&)),
this, SLOT(trackMdiSubWindowsPosition(MdiDockedWindow*, const QPoint&)));
connect(_mdiSubWindow, SIGNAL(s_positionChanged(const QPoint&)),
this, SLOT(trackMdiSubWindowsPosition(const QPoint&)));
connect(_pipelineWidget, SIGNAL(s_positionChanged(const QPoint&)),
this, SLOT(trackFloatingWindowsPosition(const QPoint&)));
return dockedWindow;
}
void VisualizationPipelineWrapper::trackFloatingWindowsPosition(const QPoint& newPos) {
const QRect& widgetGeometry = _pipelineWidget->frameGeometry();
const QRect& mdiAreaRect = _mdiArea->contentsRect();
const QRect mdiAreaGeometry(_mdiArea->mapToGlobal(mdiAreaRect.topLeft()),
_mdiArea->mapToGlobal(mdiAreaRect.bottomRight()));
void MdiDockArea::trackFloatingWindowsPosition(MdiFloatingWindow* floatingWindow, const QPoint& newPos) {
const QRect& widgetGeometry = floatingWindow->frameGeometry();
const QRect& mdiAreaRect = this->contentsRect();
const QRect mdiAreaGeometry(this->mapToGlobal(mdiAreaRect.topLeft()),
this->mapToGlobal(mdiAreaRect.bottomRight()));
const QRect& intersection = widgetGeometry & mdiAreaGeometry;
// Dock the widget if at least 60% of it is over the MDI area
// Dock the floating window's widget if at least 60% of it is over the MDI area
if (widgetGeometry.width() * widgetGeometry.height() * 3 <
intersection.width() * intersection.height() * 5) {
_pipelineWidget->stopWindowDrag();
floatingWindow->stopWindowDrag();
_mdiSubWindow->setWidget(_pipelineWidget);
_mdiArea->addSubWindow(_mdiSubWindow);
_pipelineWidget->show();
QWidget* widget = floatingWindow->widget();
MdiDockedWindow* dockedWindow = this->addSubWindow(widget);
dockedWindow->setWindowTitle(floatingWindow->windowTitle());
widget->show();
floatingWindow->deleteLater();
_mdiSubWindow->move(_mdiArea->mapFromGlobal(newPos));
_mdiSubWindow->grabMouse();
dockedWindow->forceWindowDrag();
}
}
void VisualizationPipelineWrapper::trackMdiSubWindowsPosition(const QPoint& newPos) {
const QRect& subWindowGeometry = _mdiSubWindow->frameGeometry();
const QRect& mdiAreaGeometry = _mdiArea->contentsRect();
void MdiDockArea::trackMdiSubWindowsPosition(MdiDockedWindow *dockedWindow, const QPoint& /*newPos*/) {
const QRect& subWindowGeometry = dockedWindow->frameGeometry();
const QRect& mdiAreaGeometry = contentsRect();
const QRect& intersection = subWindowGeometry & mdiAreaGeometry;
// Detach the subwindow if at least 60% of it has left the MDI area
// Detach the docked window's widget if at least 60% of it has left the MDI area
if (subWindowGeometry.width() * subWindowGeometry.height() * 2 >
intersection.width() * intersection.height() * 5) {
_mdiSubWindow->stopWindowDrag();
_mdiSubWindow->setWidget(0);
_mdiArea->removeSubWindow(_mdiSubWindow);
_mdiArea->tileSubWindows();
QWidget* widget = dockedWindow->widget();
dockedWindow->stopWindowDrag();
dockedWindow->setWidget(0);
removeSubWindow(dockedWindow);
dockedWindow->deleteLater();
tileSubWindows();
MdiFloatingWindow* floatingWindow = new MdiFloatingWindow(widget);
floatingWindow->setWindowTitle(dockedWindow->windowTitle());
floatingWindow->show();
floatingWindow->forceWindowDrag();
_pipelineWidget->move(_mdiArea->mapToGlobal(newPos));
_pipelineWidget->show();
_pipelineWidget->forceWindowDrag();
connect(floatingWindow, SIGNAL(s_positionChanged(MdiFloatingWindow*, const QPoint&)),
this, SLOT(trackFloatingWindowsPosition(MdiFloatingWindow*, const QPoint&)));
}
}
......
......@@ -27,63 +27,55 @@
//
// ================================================================================================
#ifndef VISUALIZATIONPIPELINEWRAPPER_H__
#define VISUALIZATIONPIPELINEWRAPPER_H__
#ifndef MDIDOCKAREA_H__
#define MDIDOCKAREA_H__
#include "pipelinemdisubwindow.h"
#include "visualizationpipelinewidget.h"
#include "mdidockedwindow.h"
#include "mdifloatingwindow.h"
#include <QMdiArea>
namespace campvis {
/**
* Display wrapper for visualization pipelines.
* MDI area whose subwindows can be docked and undocked.
*
* VisualizationPipelineWrapper takes care of creating all necessary representations (widget,
* MDI subwindow) of a visualization pipeline and seamlessly switching between them in
* response to the user's actions (window dragging, key presses, etc).
* MdiDockArea takes care of creating all necessary representations (docked and floating window)
* of the widgets passed to \ref addSubWindow and seamlessly switching between them in response
* to the user's actions (window dragging, key presses, etc).
*/
class VisualizationPipelineWrapper : public QObject {
class MdiDockArea : public QMdiArea {
Q_OBJECT
public:
/**
* Construct a wrapper for a visualization pipeline.
* Wrap \p widget in an MDI window and dock it in the MDI area.
*
* This constructor creates a widget for the visualization pipeline it's passed. It then
* adds it to an MDI subwindow, and places it in the specified MDI area.
* This method creates a MdiDockedWindow wrapping the widget, and adds it to the MDI area.
*
* \param name the name of the visualization pipeline
* \param canvas the pipeline's canvas
* \param mdiArea the MDI are to which the widget should be added
* \param parent the widget's parent
* \param widget the widget to add to the MDI area
* \param windowFlags flags used to customize the frame of the created subwindow
* \return the PipelineMdiSubWindow instance that was added to the MDI area
*/
VisualizationPipelineWrapper(const std::string& name, QWidget* canvas,
QMdiArea* mdiArea, QObject* parent = 0);
MdiDockedWindow* addSubWindow(QWidget* widget, Qt::WindowFlags windowFlags = 0);
private slots:
/**
* Track the position of the pipeline's widget and dock it if necessary.
* Track the position of a floating MDI window and dock it if necessary.
*
* This slot is invoked when the pipeline's widget is floating and its position changes.
* This slot is invoked when the position of a floating MDI window changes.
*/
void trackFloatingWindowsPosition(const QPoint& newPos);
void trackFloatingWindowsPosition(MdiFloatingWindow* floatingWindow, const QPoint& newPos);
/**
* Track the position of the pipeline's MDI subwindow and detach it if necessary.
* Track the position of a docked MDI window and detach it if necessary.
*
* This slot is invoked when the position of the pipeline's MDI subwindow changes.
* This slot is invoked when the position of an MDI subwindow changes.
*/
void trackMdiSubWindowsPosition(const QPoint& newPos);
private:
QMdiArea* _mdiArea; ///< The MDI area associated with the widget
PipelineMdiSubWindow* _mdiSubWindow; ///< An MDI subwindow for the pipeline
VisualizationPipelineWidget* _pipelineWidget; ///< A widget for the pipeline
void trackMdiSubWindowsPosition(MdiDockedWindow* mdiSubWindow, const QPoint& newPos);
};
}
#endif // VISUALIZATIONPIPELINEWRAPPER_H__
#endif // MDIDOCKAREA_H__
......@@ -27,27 +27,41 @@
//
// ================================================================================================
#include "pipelinemdisubwindow.h"
#include "mdidockedwindow.h"
#include <QMdiArea>
#include <QMoveEvent>
#include <QStyle>
namespace campvis {
PipelineMdiSubWindow::PipelineMdiSubWindow(QWidget* parent /*= 0*/, Qt::WindowFlags flags /*= 0*/)
MdiDockedWindow::MdiDockedWindow(QWidget* parent /*= 0*/, Qt::WindowFlags flags /*= 0*/)
: QMdiSubWindow(parent, flags)
, _dragActive(false)
, _lastMousePos()
{}
void PipelineMdiSubWindow::stopWindowDrag() {
void MdiDockedWindow::forceWindowDrag() {
_dragActive = true;
_lastMousePos = QCursor::pos();
const QPoint& mousePos = this->mapToParent(this->mapFromGlobal(_lastMousePos));
int x = mousePos.x() - this->frameSize().width() / 2;
int y = mousePos.y() - this->style()->pixelMetric(QStyle::PM_TitleBarHeight) / 2;
QPoint newPos = QPoint(x, y);
move(newPos);
this->grabMouse();
}
void MdiDockedWindow::stopWindowDrag() {
if (_dragActive) {
_dragActive = false;
releaseMouse();
this->releaseMouse();
}
}
void PipelineMdiSubWindow::mouseMoveEvent(QMouseEvent* event) {
void MdiDockedWindow::mouseMoveEvent(QMouseEvent* event) {
if (event->buttons().testFlag(Qt::LeftButton)) {
const QPoint& mousePos = event->globalPos();
......@@ -61,8 +75,8 @@ namespace campvis {
/*
* Dragging the subwindow upwards out of the MDI area is blocked for 2 reasons:
* - the subwindow can't be detached and focused reliably in such cases, possibly due
* to the main window's title bar being in the way
* - the docked window can't be detached and focused reliably in such cases, possibly
* due to the main window's title bar being in the way
* - that's how moving subwindows in an MDI area works by default
*/
if (newPos.y() < 0) {
......@@ -74,14 +88,14 @@ namespace campvis {
}
move(newPos);
emit s_positionChanged(newPos);
emit s_positionChanged(this, newPos);
}
else {
QMdiSubWindow::mouseMoveEvent(event);
}
}
void PipelineMdiSubWindow::mouseReleaseEvent(QMouseEvent* event) {
void MdiDockedWindow::mouseReleaseEvent(QMouseEvent* event) {
if (event->button() == Qt::LeftButton) {
stopWindowDrag();
mdiArea()->tileSubWindows();
......
......@@ -27,34 +27,41 @@
//
// ================================================================================================
#ifndef PIPELINEMDISUBWINDOW_H__
#define PIPELINEMDISUBWINDOW_H__
#ifndef MDIDOCKEDWINDOW_H__
#define MDIDOCKEDWINDOW_H__
#include <QMdiSubWindow>
namespace campvis {
/**
* QMdiSubWindow subclass for visualization pipeline widgets.
* QMdiSubWindow subclass representing a window docked in an MdiDockArea.
*
* PipelineMdiSubWindow reports changes in its position via the s_positionChanged signal.
* Higher-level components listen to this signal to decide when to detach the subwindow from
* its MDI area. PipelineMdiSubWindow also implements additional methods (stopWindowDrag) that
* should be used to coordinate this detaching with respect to grabbing/releasing the mouse
* input.
* MdiDockedWindow reports changes in its position via the s_positionChanged signal. Its
* MdiDockArea listens to this signal to decide when to detach the window.
*
* MdiDockedWindow implements additional methods (stopWindowDrag) that should be used to
* coordinate its detaching with respect to grabbing/releasing the mouse input.
*/
class PipelineMdiSubWindow : public QMdiSubWindow {
class MdiDockedWindow : public QMdiSubWindow {
Q_OBJECT
public:
/**
* Construct an MDI subwindow for a visualization pipeline.
* Construct a new docked MDI window.
*
* \param parent the window's parent
* \param flags options customizing the frame of the subwindow
*/
explicit PipelineMdiSubWindow(QWidget* parent = 0, Qt::WindowFlags flags = 0);
explicit MdiDockedWindow(QWidget* parent = 0, Qt::WindowFlags flags = 0);
/**
* Enter the window into forced drag mode.
*
* This method causes the window to grab the mouse input and follow the cursor.
*/
void forceWindowDrag();
/**
* Cancel the dragging of the window.
......@@ -66,20 +73,20 @@ namespace campvis {
signals:
/**
* Emitted when the subwindow's position changes.
* Emitted when the window's position changes.
*
* \param newPos the subwindow's new position
* \param newPos the window's new position
*/
void s_positionChanged(const QPoint& newPos);
void s_positionChanged(MdiDockedWindow* mdiSubWindow, const QPoint& newPos);
protected:
/**
* Event handler that receives mouse move events for the widget.
* Event handler that receives mouse move events for the window.
*/
virtual void mouseMoveEvent(QMouseEvent* event);
/**
* Event handler that receives mouse release events for the widget.
* Event handler that receives mouse release events for the window.
*/
virtual void mouseReleaseEvent(QMouseEvent * event);
......@@ -90,4 +97,4 @@ namespace campvis {
};
}
#endif // PIPELINEMDISUBWINDOW_H__
#endif // MDIDOCKEDWINDOW_H__
......@@ -27,57 +27,65 @@
//
// ================================================================================================
#include "visualizationpipelinewidget.h"
#include "mdifloatingwindow.h"
#include <QMdiArea>
#include <QMoveEvent>
#include <QHBoxLayout>
#include <QStyle>
namespace campvis {
VisualizationPipelineWidget::VisualizationPipelineWidget(QWidget* canvas, QWidget* parent /*= 0*/)
MdiFloatingWindow::MdiFloatingWindow(QWidget* widget, QWidget* parent /*= 0*/)
: QWidget(parent)
, _widget(widget)
, _dragActive(false)
, _lastMousePos()
{
QLayout* layout = new QHBoxLayout();
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(canvas);
layout->addWidget(widget);
setLayout(layout);
}
void VisualizationPipelineWidget::forceWindowDrag() {
void MdiFloatingWindow::forceWindowDrag() {
if (!_dragActive && parent() == 0) {
_dragActive = true;
_lastMousePos = QCursor::pos();
this->snapToCursor(QCursor::pos());
grabMouse();
}
}
void VisualizationPipelineWidget::stopWindowDrag() {
void MdiFloatingWindow::stopWindowDrag() {
if (_dragActive) {
_dragActive = false;
releaseMouse();
}
}
void VisualizationPipelineWidget::mouseMoveEvent(QMouseEvent* event) {
const QPoint& mousePos = event->globalPos();
const QPoint& newPos = pos() + (mousePos - _lastMousePos);
QWidget* MdiFloatingWindow::widget() {
return _widget;
}
move(newPos);
_lastMousePos = mousePos;
void MdiFloatingWindow::mouseMoveEvent(QMouseEvent* event) {
this->snapToCursor(event->globalPos());
}
void VisualizationPipelineWidget::mouseReleaseEvent(QMouseEvent* event) {
void MdiFloatingWindow::mouseReleaseEvent(QMouseEvent* event) {
if (event->button() == Qt::LeftButton) {
stopWindowDrag();
}
}
void VisualizationPipelineWidget::moveEvent(QMoveEvent* /*event*/) {
emit s_positionChanged(frameGeometry().topLeft());
void MdiFloatingWindow::moveEvent(QMoveEvent* /*event*/) {
emit s_positionChanged(this, frameGeometry().topLeft());
}
void MdiFloatingWindow::snapToCursor(const QPoint& cursorPos) {
int x = cursorPos.x() - this->frameSize().width() / 2;
int y = cursorPos.y() - this->style()->pixelMetric(QStyle::PM_TitleBarHeight) / 2;
this->move(x, y);
}
}
......@@ -27,70 +27,73 @@
//
// ================================================================================================
#ifndef VISUALIZATIONPIPELINEWIDGET_H__
#define VISUALIZATIONPIPELINEWIDGET_H__
#ifndef MDIFLOATINGWINDOW_H__
#define MDIFLOATINGWINDOW_H__
#include <QWidget>
namespace campvis {
/**
* Specialised widget for visualization pipelines.
* Specialised widget for detached MDI subwindows.
*
* VisualizationPipelineWidget can be seamlessly used with MDI subwindows and as a top-level
* floating window. When detached, it reports changes in its position via the s_positionChanged
* signal. Higher-level components listen to this signal to decide when to dock the widget in
* an MDI area. VisualizationPipelineWidget also implements additional methods (forceWindowDrag,
* stopWindowDrag) that should be used to coordinate the docking with respect to
* grabbing/releasing the mouse input.
* MdiFloatingWindow is a top-level floating window representing an undocked MDI subwindow.
* It reports changes in its position via the s_positionChanged signal. The MDI area that
* created it listens to this signal to decide when to scrap the floating window and dock back
* the widget that it wraps.
*
* MdiFloatingWindow also implements additional methods (forceWindowDrag, stopWindowDrag)
* that should be used to coordinate its creation/disposal with respect to grabbing/releasing
* the mouse input.
*/
class VisualizationPipelineWidget : public QWidget {
class MdiFloatingWindow : public QWidget {
Q_OBJECT
public:
/**
* Construct a widget for a visualization pipeline.
* Construct a new MdiFloatingWindow.
*
* \param canvas the pipeline's canvas
* \param parent the widget's parent
* \param canvas the widget this window is to wrap
* \param parent the windows's parent
*/
explicit VisualizationPipelineWidget(QWidget* canvas, QWidget* parent = 0);
explicit MdiFloatingWindow(QWidget* widget, QWidget* parent = 0);
/**
* Enter the widget into forced drag mode.
* Enter the window into forced drag mode.
*
* This method is to be invoked after the the widget has been detached from an MDI area and
* become a floating window. It causes the widget to grab the mouse input and follow the
* cursor. As a result, the user can seamlessly continue dragging the widget after it has
* been "pulled out" of the MDI area.
* This method causes the window to grab the mouse input and follow the cursor.
*/
void forceWindowDrag();
/**
* Cancel the dragging of the widget.
* Cancel the dragging of the window.
*
* This method causes the widget to release the mouse grab and stop following the cursor.
* It's supposed to be called when the widget is re-docked in an MDI area.
* This method causes the window to release the mouse grab and stop following the cursor.
*/
void stopWindowDrag();
/**
* Return the widget this window wraps.
*/
QWidget* widget();
signals:
/**
* Emitted when the widget's position changes.
* Emitted when the window's position changes.
*
* \param newPos the widget's new position
* \param newPos the window's new position
*/
void s_positionChanged(const QPoint& newPos);
void s_positionChanged(MdiFloatingWindow* pipelineWidget, const QPoint& newPos);
protected:
/**
* Event handler that receives mouse move events for the widget.
* Event handler that receives mouse move events for the window.
*/