# HG changeset patch # User Teemu Piippo # Date 1546374610 -7200 # Node ID 1a77c6156db795c63523870cd2dc40faa3f171dc # Parent 241d3e452b32b4f926a4353824499f3fcf0bfc05 commit work done on mdi diff -r 241d3e452b32 -r 1a77c6156db7 src/basics.h --- a/src/basics.h Fri Dec 28 00:03:47 2018 +0200 +++ b/src/basics.h Tue Jan 01 22:30:10 2019 +0200 @@ -107,6 +107,12 @@ template inline void ignore(Ts&&...) {} +template +QMapIterator createIterator(const QMap& map) +{ + return {map}; +} + qreal determinant(qreal a, qreal b, qreal c, qreal d); qreal determinant(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f, qreal g, qreal h, qreal i); qreal determinant(const QMatrix2x2& matrix); diff -r 241d3e452b32 -r 1a77c6156db7 src/canvas.cpp --- a/src/canvas.cpp Fri Dec 28 00:03:47 2018 +0200 +++ b/src/canvas.cpp Tue Jan 01 22:30:10 2019 +0200 @@ -24,8 +24,8 @@ #include "algorithms/geometry.h" #include "generics/ring.h" -Canvas::Canvas(LDDocument* document, QWidget* parent) : - gl::Renderer {document, parent}, +Canvas::Canvas(LDDocument* document, gl::CameraType cameraType, QWidget* parent) : + gl::Renderer {document, cameraType, parent}, m_document {*document}, m_currentEditMode {AbstractEditMode::createByType (this, EditModeType::Select)} {} @@ -39,7 +39,7 @@ gl::Renderer::overpaint(painter); QFontMetrics metrics {QFont {}}; - if (camera() != gl::FreeCamera) + if (not currentCamera().isModelview()) { // Paint the coordinates onto the screen. Vertex idealized = currentCamera().idealize(m_position3D); @@ -186,10 +186,10 @@ glEnd(); glDisable(GL_LINE_STIPPLE); - if (this->camera() < gl::FreeCamera) + if (not currentCamera().isModelview()) { - GLfloat cullz = this->cullValues[static_cast(this->camera())]; - QMatrix4x4 matrix = { + GLfloat cullz = this->cullValue; + QMatrix4x4 const matrix = { 1, 0, 0, cullz, 0, 1, 0, 0, 0, 0, 1, 0, @@ -211,11 +211,6 @@ delete m_currentEditMode; m_currentEditMode = AbstractEditMode::createByType(this, a); - - // If we cannot use the free camera, use the top one instead. - if (camera() == gl::FreeCamera and not m_currentEditMode->allowFreeCamera()) - setCamera(gl::TopCamera); - m_window->updateEditModeActions(); update(); } @@ -374,20 +369,15 @@ double Canvas::currentCullValue() const { - if (this->camera() < gl::FreeCamera) - return gl::far - this->cullValues[static_cast(this->camera())]; - else - return 0.0; + return gl::far - this->cullValue; } void Canvas::setCullValue(double value) { - if (this->camera() < gl::FreeCamera) - this->cullValues[static_cast(this->camera())] = gl::far - value; + this->cullValue = gl::far - value; } void Canvas::clearCurrentCullValue() { - if (this->camera() < gl::FreeCamera) - this->cullValues[static_cast(this->camera())] = 0.0; + this->cullValue = 0.0; } diff -r 241d3e452b32 -r 1a77c6156db7 src/canvas.h --- a/src/canvas.h Fri Dec 28 00:03:47 2018 +0200 +++ b/src/canvas.h Tue Jan 01 22:30:10 2019 +0200 @@ -23,8 +23,10 @@ class Canvas : public gl::Renderer { + Q_OBJECT + public: - Canvas(LDDocument* document, QWidget* parent = nullptr); + Canvas(LDDocument* document, gl::CameraType cameraType, QWidget* parent = nullptr); ~Canvas(); EditModeType currentEditModeType() const; @@ -62,5 +64,5 @@ AbstractEditMode* m_currentEditMode = nullptr; Vertex m_position3D; Plane m_drawPlane; - double cullValues[6] = {0}; + double cullValue; }; diff -r 241d3e452b32 -r 1a77c6156db7 src/configurationoptions.txt --- a/src/configurationoptions.txt Fri Dec 28 00:03:47 2018 +0200 +++ b/src/configurationoptions.txt Tue Jan 01 22:30:10 2019 +0200 @@ -54,7 +54,6 @@ option SelectColorBlend = "#0080FF" option LineThickness = 2 option BfcRedGreenView = false -option Camera = 6 option BlackEdges = false option DrawAxes = false option DrawWireframe = false diff -r 241d3e452b32 -r 1a77c6156db7 src/documentmanager.cpp --- a/src/documentmanager.cpp Fri Dec 28 00:03:47 2018 +0200 +++ b/src/documentmanager.cpp Tue Jan 01 22:30:10 2019 +0200 @@ -100,9 +100,7 @@ return; } - m_window->openDocumentForEditing(file); - m_window->changeDocument (file); - m_window->doFullRefresh(); + emit mainModelLoaded(file); addRecentFile (path); m_loadingMainFile = false; diff -r 241d3e452b32 -r 1a77c6156db7 src/documentmanager.h --- a/src/documentmanager.h Fri Dec 28 00:03:47 2018 +0200 +++ b/src/documentmanager.h Tue Jan 01 22:30:10 2019 +0200 @@ -52,6 +52,7 @@ signals: void documentCreated(LDDocument* document, bool cache); void documentClosed(LDDocument* document); + void mainModelLoaded(LDDocument* document); private: Q_SLOT void printParseErrorMessage(QString message); diff -r 241d3e452b32 -r 1a77c6156db7 src/editmodes/circleMode.cpp --- a/src/editmodes/circleMode.cpp Fri Dec 28 00:03:47 2018 +0200 +++ b/src/editmodes/circleMode.cpp Tue Jan 01 22:30:10 2019 +0200 @@ -167,10 +167,6 @@ v3.setCoordinate (localx, v3[localx] + c1[i].x1()); v3.setCoordinate (localy, v3[localy] + c1[i].y1()); - // Ensure the quads always are BFC-front towards the camera - if (static_cast(renderer()->camera()) % 3 <= 0) - qSwap(v1, v3); - // Project the vertices onto the draw plane. for (Vertex* vertex : {&v0, &v1, &v2, &v3}) *vertex = projectToDrawPlane(*vertex); diff -r 241d3e452b32 -r 1a77c6156db7 src/glShared.h --- a/src/glShared.h Fri Dec 28 00:03:47 2018 +0200 +++ b/src/glShared.h Tue Jan 01 22:30:10 2019 +0200 @@ -26,6 +26,52 @@ class LDObject; +namespace gl +{ + enum CameraType + { + TopCamera, + FrontCamera, + LeftCamera, + BottomCamera, + BackCamera, + RightCamera, + FreeCamera, + _End + }; + + struct CameraIcon + { + QPixmap image; + QRect sourceRect; + QRect targetRect; + QRect hitRect; + CameraType camera; + }; + + class Renderer; + class Compiler; + + static const QPen thinBorderPen {QColor {0, 0, 0, 208}, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin}; + + // Transformation matrices for the fixed cameras. + static const QMatrix4x4 topCameraMatrix = {}; + static const QMatrix4x4 frontCameraMatrix = {1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1}; + static const QMatrix4x4 leftCameraMatrix = {0, -1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 1}; + static const QMatrix4x4 bottomCameraMatrix = {1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1}; + static const QMatrix4x4 backCameraMatrix = {-1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1}; + static const QMatrix4x4 rightCameraMatrix = {0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1}; + + // Conversion matrix from LDraw to OpenGL coordinates. + static const QMatrix4x4 ldrawToGLAdapterMatrix = {1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1}; + + enum { BlackRgb = 0xff000000 }; + static constexpr GLfloat near = 1.0f; + static constexpr GLfloat far = 10000.0f; +} + +MAKE_ITERABLE_ENUM(gl::CameraType) + struct LDPolygon { enum class Type : qint8 { InvalidPolygon, EdgeLine, Triangle, Quadrilateral, ConditionalEdge }; diff -r 241d3e452b32 -r 1a77c6156db7 src/glcamera.cpp --- a/src/glcamera.cpp Fri Dec 28 00:03:47 2018 +0200 +++ b/src/glcamera.cpp Tue Jan 01 22:30:10 2019 +0200 @@ -167,6 +167,11 @@ return m_depth; } +bool GLCamera::isModelview() const +{ + return m_isFree; +} + /* * Returns the X-panning of this camera. */ diff -r 241d3e452b32 -r 1a77c6156db7 src/glcamera.h --- a/src/glcamera.h Fri Dec 28 00:03:47 2018 +0200 +++ b/src/glcamera.h Tue Jan 01 22:30:10 2019 +0200 @@ -51,6 +51,7 @@ Vertex realize(const Vertex& idealCoordinates) const; Vertex idealize(const Vertex& realCoordinates) const; double depth() const; + bool isModelview() const; bool isAxisNegated(Axis axis) const; const QString& name() const; void pan(int xMove, int yMove); diff -r 241d3e452b32 -r 1a77c6156db7 src/glrenderer.cpp --- a/src/glrenderer.cpp Fri Dec 28 00:03:47 2018 +0200 +++ b/src/glrenderer.cpp Tue Jan 01 22:30:10 2019 +0200 @@ -34,56 +34,41 @@ #include "documentmanager.h" #include "grid.h" +static GLCamera const cameraTemplates[7] = { + {"Top camera", {gl::topCameraMatrix, X, Z, false, false, false}}, + {"Front camera", {gl::frontCameraMatrix, X, Y, false, true, false}}, + {"Left camera", {gl::leftCameraMatrix, Z, Y, true, true, false}}, + {"Bottom camera", {gl::bottomCameraMatrix, X, Z, false, true, true}}, + {"Back camera", {gl::backCameraMatrix, X, Y, true, true, true}}, + {"Right camera", {gl::rightCameraMatrix, Z, Y, false, true, true}}, + {"Free camera", GLCamera::FreeCamera}, +}; + /* * Constructs a GL renderer. */ -gl::Renderer::Renderer(const Model* model, QWidget* parent) : +gl::Renderer::Renderer(const Model* model, CameraType cameraType, QWidget* parent) : QGLWidget {parent}, HierarchyElement {parent}, m_model {model}, - m_cameras { - {"Top camera", {topCameraMatrix, X, Z, false, false, false}}, // top - {"Front camera", {frontCameraMatrix, X, Y, false, true, false}}, // front - {"Left camera", {leftCameraMatrix, Z, Y, true, true, false}}, // left - {"Bottom camera", {bottomCameraMatrix, X, Z, false, true, true}}, // bottom - {"Back camera", {backCameraMatrix, X, Y, true, true, true}}, // back - {"Right camera", {rightCameraMatrix, Z, Y, false, true, true}}, // right - {"Free camera", GLCamera::FreeCamera}, // free - } + m_camera {cameraType}, + m_cameraInfo {::cameraTemplates[cameraType]} { Q_ASSERT(model != nullptr); - m_camera = (gl::CameraType) config::camera(); m_compiler = new gl::Compiler (this); m_toolTipTimer = new QTimer (this); m_toolTipTimer->setSingleShot (true); setAcceptDrops (true); connect (m_toolTipTimer, SIGNAL (timeout()), this, SLOT (showCameraIconTooltip())); - resetAllAngles(); + resetAngles(); m_needZoomToFit = true; - // Init camera icons - for (gl::CameraType camera : iterateEnum()) - { - const char* cameraIconNames[EnumLimits::Count] = - { - "camera-top", "camera-front", "camera-left", - "camera-bottom", "camera-back", "camera-right", - "camera-free" - }; - - CameraIcon* info = &m_cameraIcons[static_cast(camera)]; - info->image = MainWindow::getIcon (cameraIconNames[static_cast(camera)]); - info->camera = camera; - } - connect( this->m_compiler, &gl::Compiler::sceneChanged, this, qOverload<>(&gl::Renderer::update) ); - - calcCameraIcons(); } /* @@ -108,45 +93,11 @@ } /* - * Calculates the camera icon locations. - */ -void gl::Renderer::calcCameraIcons() -{ - int i = 0; - const int columns = 3; - const int firstAtLastRow = countof(m_cameras) - (countof(m_cameras) % columns); - - for (CameraIcon& cameraIcon : m_cameraIcons) - { - int row = i / columns; - int column = i % columns; - - // Do right-justifying on the last row. - if (i >= firstAtLastRow) - column += columns - (countof(m_cameras) % columns); - - int x1 = width() - 48 + (column * 16) - 1; - int y1 = (row * 16) + 1; - - cameraIcon.sourceRect = {0, 0, 16, 16}; - cameraIcon.targetRect = {x1, y1, 16, 16}; - cameraIcon.hitRect = { - cameraIcon.targetRect.x(), - cameraIcon.targetRect.y(), - cameraIcon.targetRect.width() + 1, - cameraIcon.targetRect.height() + 1 - }; - - ++i; - } -} - -/* * Returns the camera currently in use. */ GLCamera& gl::Renderer::currentCamera() { - return m_cameras[static_cast(camera())]; + return m_cameraInfo; } /* @@ -154,7 +105,7 @@ */ const GLCamera& gl::Renderer::currentCamera() const { - return m_cameras[static_cast(camera())]; + return m_cameraInfo; } /* @@ -211,21 +162,6 @@ // ============================================================================= // -void gl::Renderer::resetAllAngles() -{ - gl::CameraType const oldCamera = camera(); - - for (gl::CameraType camera : iterateEnum()) - { - setCamera(camera); - resetAngles(); - } - - setCamera(oldCamera); -} - -// ============================================================================= -// void gl::Renderer::initializeGL() { initializeOpenGLFunctions(); @@ -246,7 +182,7 @@ initializeLighting(); m_initialized = true; // Now that GL is initialized, we can reset angles. - resetAllAngles(); + resetAngles(); } void gl::Renderer::initializeLighting() @@ -332,7 +268,6 @@ // void gl::Renderer::resizeGL (int width, int height) { - calcCameraIcons(); glViewport (0, 0, width, height); glMatrixMode (GL_PROJECTION); glLoadIdentity(); @@ -340,8 +275,7 @@ glMatrixMode (GL_MODELVIEW); // Unfortunately Qt does not provide a resized() signal so we have to manually feed the information. - for (GLCamera& camera : m_cameras) - camera.rendererResized(width, height); + m_cameraInfo.rendererResized(width, height); } /* @@ -378,7 +312,7 @@ else glDisable(GL_LIGHTING); - if (camera() != gl::FreeCamera) + if (not m_cameraInfo.isModelview()) { glMatrixMode (GL_PROJECTION); glPushMatrix(); @@ -563,21 +497,6 @@ void gl::Renderer::overpaint(QPainter &painter) { - // Draw a background for the selected camera - painter.setPen(thinBorderPen); - painter.setBrush(QBrush {QColor {0, 128, 160, 128}}); - painter.drawRect(m_cameraIcons[static_cast(camera())].hitRect); - - // Draw the camera icons - for (const CameraIcon& info : m_cameraIcons) - { - // Don't draw the free camera icon when we can't use the free camera - if (info.camera == gl::FreeCamera and not freeCameraAllowed()) - continue; - - painter.drawPixmap(info.targetRect, info.image, info.sourceRect); - } - // Draw a label for the current camera in the bottom left corner { QFontMetrics metrics {QFont {}}; @@ -591,22 +510,8 @@ // void gl::Renderer::mouseReleaseEvent(QMouseEvent* event) { - bool wasLeft = (m_lastButtons & Qt::LeftButton) and not (event->buttons() & Qt::LeftButton); + ignore(event); m_panning = false; - - // Check if we selected a camera icon - if (wasLeft and not mouseHasMoved()) - { - for (CameraIcon& info : m_cameraIcons) - { - if (info.targetRect.contains (event->pos())) - { - setCamera (info.camera); - break; - } - } - } - update(); m_totalMouseMove = 0; } @@ -638,7 +543,7 @@ m_panning = true; m_isCameraMoving = true; } - else if (left and camera() == gl::FreeCamera and (xMove != 0 or yMove != 0)) + else if (left and m_cameraInfo.isModelview() and (xMove != 0 or yMove != 0)) { QQuaternion versor = QQuaternion::fromAxisAndAngle(yMove, xMove, 0, 0.6 * hypot(xMove, yMove)); m_rotation = versor * m_rotation; @@ -692,18 +597,6 @@ update(); } -// ============================================================================= -// -void gl::Renderer::setCamera(gl::CameraType camera) -{ - // The edit mode may forbid the free camera. - if (freeCameraAllowed() or camera != gl::FreeCamera) - { - m_camera = camera; - config::setCamera(static_cast(camera)); - } -} - /* * Resolves a pixel pointer to an RGB color. * pixel[0..2] must be valid. @@ -829,22 +722,6 @@ return image.rgbSwapped().mirrored(); } -/* - * Show a tooltip if the cursor is currently hovering over a camera icon. - */ -void gl::Renderer::showCameraIconTooltip() -{ - for (CameraIcon & icon : m_cameraIcons) - { - if (icon.targetRect.contains (m_mousePosition)) - { - QToolTip::showText(m_globalpos, m_cameras[static_cast(icon.camera)].name()); - update(); - break; - } - } -} - // ============================================================================= // void gl::Renderer::zoomToFit() diff -r 241d3e452b32 -r 1a77c6156db7 src/glrenderer.h --- a/src/glrenderer.h Fri Dec 28 00:03:47 2018 +0200 +++ b/src/glrenderer.h Tue Jan 01 22:30:10 2019 +0200 @@ -24,59 +24,13 @@ #include "glcamera.h" #include "hierarchyelement.h" -namespace gl -{ - enum CameraType - { - TopCamera, - FrontCamera, - LeftCamera, - BottomCamera, - BackCamera, - RightCamera, - FreeCamera, - _End - }; - - struct CameraIcon - { - QPixmap image; - QRect sourceRect; - QRect targetRect; - QRect hitRect; - CameraType camera; - }; - - class Renderer; - class Compiler; - - static const QPen thinBorderPen {QColor {0, 0, 0, 208}, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin}; - - // Transformation matrices for the fixed cameras. - static const QMatrix4x4 topCameraMatrix = {}; - static const QMatrix4x4 frontCameraMatrix = {1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1}; - static const QMatrix4x4 leftCameraMatrix = {0, -1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 1}; - static const QMatrix4x4 bottomCameraMatrix = {1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1}; - static const QMatrix4x4 backCameraMatrix = {-1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1}; - static const QMatrix4x4 rightCameraMatrix = {0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1}; - - // Conversion matrix from LDraw to OpenGL coordinates. - static const QMatrix4x4 ldrawToGLAdapterMatrix = {1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1}; - - enum { BlackRgb = 0xff000000 }; - static constexpr GLfloat near = 1.0f; - static constexpr GLfloat far = 10000.0f; -} - -MAKE_ITERABLE_ENUM(gl::CameraType) - // The main renderer object, draws the brick on the screen, manages the camera and selection picking. class gl::Renderer : public QGLWidget, protected QOpenGLFunctions, public HierarchyElement { Q_OBJECT public: - Renderer(const Model* model, QWidget* parent = nullptr); + Renderer(const Model* model, gl::CameraType cameraType, QWidget* parent = nullptr); ~Renderer(); gl::CameraType camera() const; @@ -90,11 +44,9 @@ QPersistentModelIndex objectAtCursor() const; QItemSelection pick(const QRect& range); QModelIndex pick(int mouseX, int mouseY); - void resetAllAngles(); void resetAngles(); QImage screenCapture(); void setBackground(); - void setCamera(gl::CameraType cam); QPen textPen() const; QItemSelectionModel* selectionModel() const; void setSelectionModel(QItemSelectionModel* selectionModel); @@ -132,14 +84,14 @@ private: const Model* const m_model; + gl::CameraType const m_camera; gl::Compiler* m_compiler; QPersistentModelIndex m_objectAtCursor; - gl::CameraIcon m_cameraIcons[7]; QTimer* m_toolTipTimer; Qt::MouseButtons m_lastButtons; Qt::KeyboardModifiers m_currentKeyboardModifiers; QQuaternion m_rotation; - GLCamera m_cameras[7]; + GLCamera m_cameraInfo; bool m_useDarkBackground = false; bool m_panning = false; bool m_initialized = false; @@ -150,7 +102,6 @@ QPoint m_mousePosition; QPoint m_globalpos; QPointF m_mousePositionF; - gl::CameraType m_camera; GLuint m_axeslist; int m_totalMouseMove; QColor m_backgroundColor; @@ -167,7 +118,6 @@ void initGLData(); void needZoomToFit(); void setPicking(bool picking); - Q_SLOT void showCameraIconTooltip(); void zoomToFit(); void zoomAllToFit(); }; diff -r 241d3e452b32 -r 1a77c6156db7 src/mainwindow.cpp --- a/src/mainwindow.cpp Fri Dec 28 00:03:47 2018 +0200 +++ b/src/mainwindow.cpp Tue Jan 01 22:30:10 2019 +0200 @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +#include +#include #include #include #include @@ -81,12 +83,14 @@ ui.verticalLayout->insertWidget (0, m_tabs); ui.primitives->setModel(m_primitives); createBlankDocument(); - ui.rendererStack->setCurrentWidget(getRendererForDocument(m_currentDocument)); + getRendererForDocument(m_currentDocument); connect (m_tabs, SIGNAL (currentChanged(int)), this, SLOT (tabSelected())); connect (m_tabs, SIGNAL (tabCloseRequested (int)), this, SLOT (closeTab (int))); connect(m_documents, &DocumentManager::documentCreated, this, &MainWindow::newDocument); connect(m_documents, SIGNAL(documentClosed(LDDocument*)), this, SLOT(documentClosed(LDDocument*))); + connect(m_documents, &DocumentManager::mainModelLoaded, this, &MainWindow::mainModelLoaded); + connect(ui.viewport, &QMdiArea::subWindowActivated, this, &MainWindow::canvasActivated); updateActions(); @@ -360,6 +364,32 @@ documents()->openMainModel (qAct->text()); } +/* + * This slot function is called when a subwindow of the MDI area is selected. + */ +void MainWindow::canvasActivated(QMdiSubWindow* window) +{ + if (window != nullptr) + { + Q_ASSERT(window->mdiArea() == ui.viewport); + Canvas* const canvas = static_cast(window->widget()); + LDDocument* const document = canvas->document(); + + // Move the canvas to the top of its stack. + m_renderers[document].removeOne(canvas); + m_renderers[document].append(canvas); + } +} + +void MainWindow::mainModelLoaded(LDDocument* document) +{ + openDocumentForEditing(document); + changeDocument(document); + + for (Canvas* canvas : m_renderers[document]) + canvas->fullUpdate(); +} + // --------------------------------------------------------------------------------------------------------------------- // // Returns the suggested position to place a new object at. @@ -384,11 +414,17 @@ // --------------------------------------------------------------------------------------------------------------------- // -// Builds the object list and tells the GL renderer to do a soft update. +// Updates all GL renderers. // void MainWindow::refresh() { - renderer()->update(); + auto iterator = createIterator(m_renderers); + + while (iterator.hasNext()) + { + for (Canvas* canvas : iterator.next().value()) + canvas->update(); + } } // --------------------------------------------------------------------------------------------------------------------- @@ -491,7 +527,7 @@ contextMenu->addAction (ui.actionSubfileSelection); } - if (renderer()->camera() != gl::FreeCamera) + if (not renderer()->currentCamera().isModelview()) { contextMenu->addSeparator(); contextMenu->addAction(ui.actionSetDrawPlane); @@ -720,8 +756,13 @@ // Canvas* MainWindow::renderer() { - Q_ASSERT(ui.rendererStack->count() > 0); - return static_cast(ui.rendererStack->currentWidget()); + QMdiSubWindow* const currentSubWindow = ui.viewport->currentSubWindow(); + Q_ASSERT(currentSubWindow != nullptr); + + if (currentSubWindow != nullptr) + return static_cast(currentSubWindow->widget()); + else + return nullptr; } // --------------------------------------------------------------------------------------------------------------------- @@ -788,6 +829,22 @@ updateActions(); } +/* + * Creates a new camera of the specified type for the specified document. + * The canvas is opened in a new MDI sub window. + * The created canvas is returned. + */ +Canvas* MainWindow::createCameraForDocument(LDDocument* document, gl::CameraType cameraType) +{ + Canvas* canvas = new Canvas {document, cameraType, this}; + m_renderers[document].append(canvas); + QMdiSubWindow* const subWindow = ui.viewport->addSubWindow(canvas); + m_subWindows[canvas] = subWindow; + connect(canvas, &QObject::destroyed, this, &MainWindow::canvasClosed); + ui.viewport->setActiveSubWindow(subWindow); + return canvas; +} + // --------------------------------------------------------------------------------------------------------------------- // void MainWindow::newDocument(LDDocument* document, bool cache) @@ -809,6 +866,21 @@ updateDocumentList(); } +/* + * When a canvas is closed, this clears any internal references to it. + */ +void MainWindow::canvasClosed() +{ + Canvas* canvas = qobject_cast(sender()); + + if (canvas != nullptr) + { + LDDocument* const document = canvas->document(); + m_renderers[document].removeAll(canvas); + m_subWindows.remove(canvas); + } +} + void MainWindow::openDocumentForEditing(LDDocument* document) { if (document->isFrozen()) @@ -841,8 +913,6 @@ return; m_currentDocument = document; - Canvas* renderer = getRendererForDocument(document); - ui.rendererStack->setCurrentWidget(renderer); if (document) { @@ -852,13 +922,19 @@ print ("Changed document to %1", document->getDisplayName()); ui.objectList->setModel(document); ui.header->setDocument(document); - renderer->fullUpdate(); - QItemSelectionModel* selection = m_selections.value(document); + + for (Canvas* canvas : m_renderers[document]) + canvas->fullUpdate(); + + QItemSelectionModel* selection = m_selectionModels.value(document); if (selection == nullptr) { - m_selections[document] = ui.objectList->selectionModel(); - renderer->setSelectionModel(m_selections[document]); + selection = new QItemSelectionModel; + m_selectionModels[document] = selection; + + for (Canvas* canvas : m_renderers[document]) + canvas->setSelectionModel(m_selectionModels[document]); } else { @@ -872,16 +948,19 @@ */ Canvas* MainWindow::getRendererForDocument(LDDocument *document) { - Canvas* renderer = m_renderers.value(document); + QStack& renderers = m_renderers[document]; + Canvas* canvas; - if (not renderer) + if (renderers.empty()) { - renderer = new Canvas {document, this}; - m_renderers[document] = renderer; - ui.rendererStack->addWidget(renderer); + canvas = createCameraForDocument(document, gl::FreeCamera); + } + else + { + canvas = renderers.top(); } - return renderer; + return canvas; } void MainWindow::documentClosed(LDDocument *document) @@ -889,19 +968,26 @@ print ("Closed %1", document->name()); updateDocumentList(); - // If the current document just became implicit (i.e. user closed it), we need to get a new one to show. + // If the current document was just close, we need to get a new one to show. if (currentDocument() == document) currentDocumentClosed(); - Canvas* renderer = m_renderers.value(document); - - if (renderer) + for (Canvas* renderer : m_renderers.value(document)) { - ui.rendererStack->removeWidget(renderer); + ui.viewport->removeSubWindow(renderer); + m_subWindows.remove(renderer); renderer->deleteLater(); } m_renderers.remove(document); + + auto selectionModel = m_selectionModels.find(document); + + if (selectionModel != m_selectionModels.end()) + { + delete *selectionModel; + m_selectionModels.erase(selectionModel); + } } QModelIndexList MainWindow::selectedIndexes() const @@ -956,21 +1042,48 @@ void MainWindow::clearSelection() { - m_selections[m_currentDocument]->clear(); + m_selectionModels[m_currentDocument]->clear(); } void MainWindow::select(const QModelIndex &objectIndex) { if (objectIndex.isValid() and objectIndex.model() == m_currentDocument) - m_selections[m_currentDocument]->select(objectIndex, QItemSelectionModel::Select); + m_selectionModels[m_currentDocument]->select(objectIndex, QItemSelectionModel::Select); +} + +/* + * Selects a camera of the specified type for the specified document. + * If the camera does not exist, it will be created. + * The selected canvas is returned. + */ +Canvas* MainWindow::selectCameraForDocument(LDDocument* document, gl::CameraType cameraType) +{ + Canvas* const currentCanvas = renderer(); + QStack& canvasStack = m_renderers[document]; + + if (canvasStack.empty()) + { + return createCameraForDocument(document, cameraType); + } + else + { + Canvas* canvas = canvasStack.top(); + + if (canvas == currentCanvas) + canvas = canvasStack.front(); + + QMdiSubWindow* const subWindow = m_subWindows[canvas]; + ui.viewport->setActiveSubWindow(subWindow); + return canvas; + } } QItemSelectionModel* MainWindow::currentSelectionModel() { - return m_selections[m_currentDocument]; + return m_selectionModels[m_currentDocument]; } void MainWindow::replaceSelection(const QItemSelection& selection) { - m_selections[m_currentDocument]->select(selection, QItemSelectionModel::ClearAndSelect); + m_selectionModels[m_currentDocument]->select(selection, QItemSelectionModel::ClearAndSelect); } diff -r 241d3e452b32 -r 1a77c6156db7 src/mainwindow.h --- a/src/mainwindow.h Fri Dec 28 00:03:47 2018 +0200 +++ b/src/mainwindow.h Tue Jan 01 22:30:10 2019 +0200 @@ -26,7 +26,9 @@ #include #include "linetypes/modelobject.h" #include "colors.h" +#include "glShared.h" +class QMdiSubWindow; class QToolButton; class Canvas; class Toolset; @@ -48,6 +50,7 @@ void changeDocument (LDDocument* f); void clearSelection(); void createBlankDocument(); + Canvas* createCameraForDocument(LDDocument* document, gl::CameraType cameraType); LDDocument* currentDocument(); void currentDocumentClosed(); QItemSelectionModel* currentSelectionModel(); @@ -70,6 +73,7 @@ CircularSection circleToolSection() const; bool save (LDDocument* doc, bool saveAs); void select(const QModelIndex& objectIndex); + Canvas* selectCameraForDocument(LDDocument* document, gl::CameraType cameraType); QModelIndexList selectedIndexes() const; QSet selectedObjects() const; void spawnContextMenu (const QPoint& position); @@ -96,6 +100,7 @@ void updateTitle(); void newDocument (LDDocument* document, bool cache = false); void settingsChanged(); + void canvasClosed(); protected: void closeEvent (QCloseEvent* event); @@ -103,8 +108,8 @@ private: struct ToolInfo; - QMap m_renderers; - QMap m_selections; + QMap> m_renderers; + QMap m_selectionModels; PrimitiveManager* m_primitives; Grid* m_grid; QVector m_colorButtons; @@ -118,9 +123,12 @@ DocumentManager* m_documents; LDDocument* m_currentDocument; QMap m_defaultShortcuts; + QMap m_subWindows; int previousDivisions = MediumResolution; private slots: void finishInitialization(); void recentFileClicked(); + void canvasActivated(QMdiSubWindow* window); + void mainModelLoaded(LDDocument* document); }; diff -r 241d3e452b32 -r 1a77c6156db7 src/mainwindow.ui --- a/src/mainwindow.ui Fri Dec 28 00:03:47 2018 +0200 +++ b/src/mainwindow.ui Tue Jan 01 22:30:10 2019 +0200 @@ -24,31 +24,22 @@ Qt::Horizontal - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised + + + QMdiArea::TabbedView - 2 + 0 0 0 - 100 - 30 + 68 + 377 @@ -70,8 +61,8 @@ 0 0 - 926 - 366 + 88 + 363 @@ -101,8 +92,8 @@ 0 0 - 926 - 366 + 176 + 363 @@ -140,8 +131,8 @@ 0 0 - 93 - 93 + 88 + 363 @@ -174,7 +165,7 @@ 0 0 1010 - 26 + 22 @@ -216,6 +207,34 @@ &View + + + &Create Camera + + + + + + + + + + + + + &Select Camera + + + + + + + + + + + + @@ -1716,6 +1735,174 @@ Resets the drawing plane + + + + :/icons/camera-top.png:/icons/camera-top.png + + + New &Top Camera + + + New Top Camera + + + + + + :/icons/camera-front.png:/icons/camera-front.png + + + New &Front Camera + + + New Front Camera + + + + + + :/icons/camera-left.png:/icons/camera-left.png + + + New &Left Camera + + + New Left Camera + + + + + + :/icons/camera-bottom.png:/icons/camera-bottom.png + + + New &Bottom Camera + + + New Bottom Camera + + + + + + :/icons/camera-back.png:/icons/camera-back.png + + + New B&ack Camera + + + New Back Camera + + + + + + :/icons/camera-right.png:/icons/camera-right.png + + + New &Right Camera + + + New Right Camera + + + + + + :/icons/camera-free.png:/icons/camera-free.png + + + New Fr&ee Camera + + + New Free Camera + + + + + + :/icons/camera-top.png:/icons/camera-top.png + + + Select &Top Camera + + + Ctrl+1 + + + + + + :/icons/camera-front.png:/icons/camera-front.png + + + Select &Front Camera + + + Ctrl+2 + + + + + + :/icons/camera-left.png:/icons/camera-left.png + + + Select &Left Camera + + + Ctrl+3 + + + + + + :/icons/camera-bottom.png:/icons/camera-bottom.png + + + Select &Bottom Camera + + + Ctrl+4 + + + + + + :/icons/camera-back.png:/icons/camera-back.png + + + Select B&ack Camera + + + Ctrl+5 + + + + + + :/icons/camera-right.png:/icons/camera-right.png + + + Select &Right Camera + + + Ctrl+6 + + + + + + :/icons/camera-free.png:/icons/camera-free.png + + + Select Fr&ee Camera + + + Ctrl+7 + + @@ -1733,6 +1920,7 @@ + diff -r 241d3e452b32 -r 1a77c6156db7 src/toolsets/viewtoolset.cpp --- a/src/toolsets/viewtoolset.cpp Fri Dec 28 00:03:47 2018 +0200 +++ b/src/toolsets/viewtoolset.cpp Tue Jan 01 22:30:10 2019 +0200 @@ -163,6 +163,86 @@ m_window->renderer()->update(); } +void ViewToolset::newTopCamera() +{ + createNewCamera(gl::TopCamera); +} + +void ViewToolset::newFrontCamera() +{ + createNewCamera(gl::FrontCamera); +} + +void ViewToolset::newLeftCamera() +{ + createNewCamera(gl::LeftCamera); +} + +void ViewToolset::newBottomCamera() +{ + createNewCamera(gl::BottomCamera); +} + +void ViewToolset::newBackCamera() +{ + createNewCamera(gl::BackCamera); +} + +void ViewToolset::newRightCamera() +{ + createNewCamera(gl::RightCamera); +} + +void ViewToolset::newFreeCamera() +{ + createNewCamera(gl::FreeCamera); +} + +void ViewToolset::selectTopCamera() +{ + selectCamera(gl::TopCamera); +} + +void ViewToolset::selectFrontCamera() +{ + selectCamera(gl::FrontCamera); +} + +void ViewToolset::selectLeftCamera() +{ + selectCamera(gl::LeftCamera); +} + +void ViewToolset::selectBottomCamera() +{ + selectCamera(gl::BottomCamera); +} + +void ViewToolset::selectBackCamera() +{ + selectCamera(gl::BackCamera); +} + +void ViewToolset::selectRightCamera() +{ + selectCamera(gl::RightCamera); +} + +void ViewToolset::selectFreeCamera() +{ + selectCamera(gl::FreeCamera); +} + +void ViewToolset::createNewCamera(gl::CameraType cameraType) +{ + m_window->createCameraForDocument(currentDocument(), cameraType); +} + +void ViewToolset::selectCamera(gl::CameraType cameraType) +{ + m_window->selectCameraForDocument(currentDocument(), cameraType); +} + void ViewToolset::drawAngles() { config::toggleDrawAngles(); @@ -178,10 +258,17 @@ switch (object->type()) { case LDObjectType::Quadrilateral: - if (not static_cast(object)->isCoPlanar()) + case LDObjectType::Triangle: + if ( + object->type() == LDObjectType::Quadrilateral + and not static_cast(object)->isCoPlanar() + ) { return {}; - case LDObjectType::Triangle: - return Plane::fromPoints(object->vertex(0), object->vertex(1), object->vertex(2)); + } + else + { + return Plane::fromPoints(object->vertex(0), object->vertex(1), object->vertex(2)); + } default: return {}; @@ -220,7 +307,7 @@ void ViewToolset::setCullDepth() { - if (m_window->renderer()->camera() == gl::FreeCamera) + if (m_window->renderer()->currentCamera().isModelview()) return; bool ok; diff -r 241d3e452b32 -r 1a77c6156db7 src/toolsets/viewtoolset.h --- a/src/toolsets/viewtoolset.h Fri Dec 28 00:03:47 2018 +0200 +++ b/src/toolsets/viewtoolset.h Tue Jan 01 22:30:10 2019 +0200 @@ -18,6 +18,7 @@ #pragma once #include "toolset.h" +#include "glrenderer.h" class ViewToolset : public Toolset { @@ -48,4 +49,23 @@ Q_INVOKABLE void visibilityReveal(); Q_INVOKABLE void visibilityToggle(); Q_INVOKABLE void wireframe(); + + Q_INVOKABLE void newTopCamera(); + Q_INVOKABLE void newFrontCamera(); + Q_INVOKABLE void newLeftCamera(); + Q_INVOKABLE void newBottomCamera(); + Q_INVOKABLE void newBackCamera(); + Q_INVOKABLE void newRightCamera(); + Q_INVOKABLE void newFreeCamera(); + Q_INVOKABLE void selectTopCamera(); + Q_INVOKABLE void selectFrontCamera(); + Q_INVOKABLE void selectLeftCamera(); + Q_INVOKABLE void selectBottomCamera(); + Q_INVOKABLE void selectBackCamera(); + Q_INVOKABLE void selectRightCamera(); + Q_INVOKABLE void selectFreeCamera(); + +private: + void createNewCamera(gl::CameraType cameraType); + void selectCamera(gl::CameraType cameraType); };