Tue, 01 Jan 2019 22:30:10 +0200
commit work done on mdi
--- 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<typename... Ts> inline void ignore(Ts&&...) {} +template<typename T, typename R> +QMapIterator<T, R> createIterator(const QMap<T, R>& 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);
--- 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<int>(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<int>(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<int>(this->camera())] = gl::far - value; + this->cullValue = gl::far - value; } void Canvas::clearCurrentCullValue() { - if (this->camera() < gl::FreeCamera) - this->cullValues[static_cast<int>(this->camera())] = 0.0; + this->cullValue = 0.0; }
--- 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; };
--- 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
--- 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;
--- 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);
--- 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<int>(renderer()->camera()) % 3 <= 0) - qSwap(v1, v3); - // Project the vertices onto the draw plane. for (Vertex* vertex : {&v0, &v1, &v2, &v3}) *vertex = projectToDrawPlane(*vertex);
--- 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 };
--- 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. */
--- 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);
--- 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<gl::CameraType>()) - { - const char* cameraIconNames[EnumLimits<gl::CameraType>::Count] = - { - "camera-top", "camera-front", "camera-left", - "camera-bottom", "camera-back", "camera-right", - "camera-free" - }; - - CameraIcon* info = &m_cameraIcons[static_cast<int>(camera)]; - info->image = MainWindow::getIcon (cameraIconNames[static_cast<int>(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<int>(camera())]; + return m_cameraInfo; } /* @@ -154,7 +105,7 @@ */ const GLCamera& gl::Renderer::currentCamera() const { - return m_cameras[static_cast<int>(camera())]; + return m_cameraInfo; } /* @@ -211,21 +162,6 @@ // ============================================================================= // -void gl::Renderer::resetAllAngles() -{ - gl::CameraType const oldCamera = camera(); - - for (gl::CameraType camera : iterateEnum<gl::CameraType>()) - { - 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<int>(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<int>(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<int>(icon.camera)].name()); - update(); - break; - } - } -} - // ============================================================================= // void gl::Renderer::zoomToFit()
--- 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(); };
--- 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 <http://www.gnu.org/licenses/>. */ +#include <QMdiArea> +#include <QMdiSubWindow> #include <QMessageBox> #include <QContextMenuEvent> #include <QToolButton> @@ -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<Canvas*>(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<Canvas*>(ui.rendererStack->currentWidget()); + QMdiSubWindow* const currentSubWindow = ui.viewport->currentSubWindow(); + Q_ASSERT(currentSubWindow != nullptr); + + if (currentSubWindow != nullptr) + return static_cast<Canvas*>(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<Canvas*>(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<Canvas*>& 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<Canvas*>& 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); }
--- 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 <QMetaMethod> #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<LDObject*> 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<LDDocument*, Canvas*> m_renderers; - QMap<LDDocument*, QItemSelectionModel*> m_selections; + QMap<LDDocument*, QStack<Canvas*>> m_renderers; + QMap<LDDocument*, QItemSelectionModel*> m_selectionModels; PrimitiveManager* m_primitives; Grid* m_grid; QVector<QToolButton*> m_colorButtons; @@ -118,9 +123,12 @@ DocumentManager* m_documents; LDDocument* m_currentDocument; QMap<QAction*, QKeySequence> m_defaultShortcuts; + QMap<Canvas*, QMdiSubWindow*> m_subWindows; int previousDivisions = MediumResolution; private slots: void finishInitialization(); void recentFileClicked(); + void canvasActivated(QMdiSubWindow* window); + void mainModelLoaded(LDDocument* document); };
--- 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 @@ <property name="orientation"> <enum>Qt::Horizontal</enum> </property> - <widget class="QStackedWidget" name="rendererStack"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="frameShape"> - <enum>QFrame::StyledPanel</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> + <widget class="QMdiArea" name="viewport"> + <property name="viewMode"> + <enum>QMdiArea::TabbedView</enum> </property> </widget> <widget class="QToolBox" name="toolBox"> <property name="currentIndex"> - <number>2</number> + <number>0</number> </property> <widget class="QWidget" name="page"> <property name="geometry"> <rect> <x>0</x> <y>0</y> - <width>100</width> - <height>30</height> + <width>68</width> + <height>377</height> </rect> </property> <attribute name="label"> @@ -70,8 +61,8 @@ <rect> <x>0</x> <y>0</y> - <width>926</width> - <height>366</height> + <width>88</width> + <height>363</height> </rect> </property> <attribute name="label"> @@ -101,8 +92,8 @@ <rect> <x>0</x> <y>0</y> - <width>926</width> - <height>366</height> + <width>176</width> + <height>363</height> </rect> </property> <attribute name="label"> @@ -140,8 +131,8 @@ <rect> <x>0</x> <y>0</y> - <width>93</width> - <height>93</height> + <width>88</width> + <height>363</height> </rect> </property> <attribute name="label"> @@ -174,7 +165,7 @@ <x>0</x> <y>0</y> <width>1010</width> - <height>26</height> + <height>22</height> </rect> </property> <widget class="QMenu" name="menuFile"> @@ -216,6 +207,34 @@ <property name="title"> <string>&View</string> </property> + <widget class="QMenu" name="menuCreate_View"> + <property name="title"> + <string>&Create Camera</string> + </property> + <addaction name="actionNewTopCamera"/> + <addaction name="actionNewFrontCamera"/> + <addaction name="actionNewLeftCamera"/> + <addaction name="actionNewBottomCamera"/> + <addaction name="actionNewBackCamera"/> + <addaction name="actionNewRightCamera"/> + <addaction name="separator"/> + <addaction name="actionNewFreeCamera"/> + </widget> + <widget class="QMenu" name="menu_Select_Camera"> + <property name="title"> + <string>&Select Camera</string> + </property> + <addaction name="actionSelectTopCamera"/> + <addaction name="actionSelectFrontCamera"/> + <addaction name="actionSelectLeftCamera"/> + <addaction name="actionSelectBottomCamera"/> + <addaction name="actionSelectBackCamera"/> + <addaction name="actionSelectRightCamera"/> + <addaction name="separator"/> + <addaction name="actionSelectFreeCamera"/> + </widget> + <addaction name="menuCreate_View"/> + <addaction name="menu_Select_Camera"/> <addaction name="actionResetView"/> <addaction name="actionAxes"/> <addaction name="actionWireframe"/> @@ -1716,6 +1735,174 @@ <string>Resets the drawing plane</string> </property> </action> + <action name="actionNewTopCamera"> + <property name="icon"> + <iconset resource="../ldforge.qrc"> + <normaloff>:/icons/camera-top.png</normaloff>:/icons/camera-top.png</iconset> + </property> + <property name="text"> + <string>New &Top Camera</string> + </property> + <property name="toolTip"> + <string>New Top Camera</string> + </property> + </action> + <action name="actionNewFrontCamera"> + <property name="icon"> + <iconset resource="../ldforge.qrc"> + <normaloff>:/icons/camera-front.png</normaloff>:/icons/camera-front.png</iconset> + </property> + <property name="text"> + <string>New &Front Camera</string> + </property> + <property name="toolTip"> + <string>New Front Camera</string> + </property> + </action> + <action name="actionNewLeftCamera"> + <property name="icon"> + <iconset resource="../ldforge.qrc"> + <normaloff>:/icons/camera-left.png</normaloff>:/icons/camera-left.png</iconset> + </property> + <property name="text"> + <string>New &Left Camera</string> + </property> + <property name="toolTip"> + <string>New Left Camera</string> + </property> + </action> + <action name="actionNewBottomCamera"> + <property name="icon"> + <iconset resource="../ldforge.qrc"> + <normaloff>:/icons/camera-bottom.png</normaloff>:/icons/camera-bottom.png</iconset> + </property> + <property name="text"> + <string>New &Bottom Camera</string> + </property> + <property name="toolTip"> + <string>New Bottom Camera</string> + </property> + </action> + <action name="actionNewBackCamera"> + <property name="icon"> + <iconset resource="../ldforge.qrc"> + <normaloff>:/icons/camera-back.png</normaloff>:/icons/camera-back.png</iconset> + </property> + <property name="text"> + <string>New B&ack Camera</string> + </property> + <property name="toolTip"> + <string>New Back Camera</string> + </property> + </action> + <action name="actionNewRightCamera"> + <property name="icon"> + <iconset resource="../ldforge.qrc"> + <normaloff>:/icons/camera-right.png</normaloff>:/icons/camera-right.png</iconset> + </property> + <property name="text"> + <string>New &Right Camera</string> + </property> + <property name="toolTip"> + <string>New Right Camera</string> + </property> + </action> + <action name="actionNewFreeCamera"> + <property name="icon"> + <iconset resource="../ldforge.qrc"> + <normaloff>:/icons/camera-free.png</normaloff>:/icons/camera-free.png</iconset> + </property> + <property name="text"> + <string>New Fr&ee Camera</string> + </property> + <property name="toolTip"> + <string>New Free Camera</string> + </property> + </action> + <action name="actionSelectTopCamera"> + <property name="icon"> + <iconset resource="../ldforge.qrc"> + <normaloff>:/icons/camera-top.png</normaloff>:/icons/camera-top.png</iconset> + </property> + <property name="text"> + <string>Select &Top Camera</string> + </property> + <property name="shortcut"> + <string>Ctrl+1</string> + </property> + </action> + <action name="actionSelectFrontCamera"> + <property name="icon"> + <iconset resource="../ldforge.qrc"> + <normaloff>:/icons/camera-front.png</normaloff>:/icons/camera-front.png</iconset> + </property> + <property name="text"> + <string>Select &Front Camera</string> + </property> + <property name="shortcut"> + <string>Ctrl+2</string> + </property> + </action> + <action name="actionSelectLeftCamera"> + <property name="icon"> + <iconset resource="../ldforge.qrc"> + <normaloff>:/icons/camera-left.png</normaloff>:/icons/camera-left.png</iconset> + </property> + <property name="text"> + <string>Select &Left Camera</string> + </property> + <property name="shortcut"> + <string>Ctrl+3</string> + </property> + </action> + <action name="actionSelectBottomCamera"> + <property name="icon"> + <iconset resource="../ldforge.qrc"> + <normaloff>:/icons/camera-bottom.png</normaloff>:/icons/camera-bottom.png</iconset> + </property> + <property name="text"> + <string>Select &Bottom Camera</string> + </property> + <property name="shortcut"> + <string>Ctrl+4</string> + </property> + </action> + <action name="actionSelectBackCamera"> + <property name="icon"> + <iconset resource="../ldforge.qrc"> + <normaloff>:/icons/camera-back.png</normaloff>:/icons/camera-back.png</iconset> + </property> + <property name="text"> + <string>Select B&ack Camera</string> + </property> + <property name="shortcut"> + <string>Ctrl+5</string> + </property> + </action> + <action name="actionSelectRightCamera"> + <property name="icon"> + <iconset resource="../ldforge.qrc"> + <normaloff>:/icons/camera-right.png</normaloff>:/icons/camera-right.png</iconset> + </property> + <property name="text"> + <string>Select &Right Camera</string> + </property> + <property name="shortcut"> + <string>Ctrl+6</string> + </property> + </action> + <action name="actionSelectFreeCamera"> + <property name="icon"> + <iconset resource="../ldforge.qrc"> + <normaloff>:/icons/camera-free.png</normaloff>:/icons/camera-free.png</iconset> + </property> + <property name="text"> + <string>Select Fr&ee Camera</string> + </property> + <property name="shortcut"> + <string>Ctrl+7</string> + </property> + </action> </widget> <customwidgets> <customwidget> @@ -1733,6 +1920,7 @@ </customwidgets> <resources> <include location="../ldforge.qrc"/> + <include location="../ldforge.qrc"/> </resources> <connections/> </ui>
--- 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<LDQuadrilateral*>(object)->isCoPlanar()) + case LDObjectType::Triangle: + if ( + object->type() == LDObjectType::Quadrilateral + and not static_cast<LDQuadrilateral*>(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;
--- 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); };