From ff2a28347dc30eccc28e7cd7298cccde7aa49d2c Mon Sep 17 00:00:00 2001 From: Shamus Hammons Date: Tue, 8 Sep 2015 09:27:39 -0500 Subject: [PATCH] Layer handling code mostly done; still need to handle layer locking. --- src/applicationwindow.cpp | 3 + src/drawingview.cpp | 85 ++++++++++++++++++++-- src/drawingview.h | 5 +- src/global.cpp | 4 +- src/global.h | 5 +- src/layeritemwidget.cpp | 57 ++++++++++----- src/layeritemwidget.h | 15 +++- src/layerwidget.cpp | 146 ++++++++++++++++++++++++++++++++------ src/layerwidget.h | 6 ++ 9 files changed, 277 insertions(+), 49 deletions(-) diff --git a/src/applicationwindow.cpp b/src/applicationwindow.cpp index a1fd4f1..ac4122d 100644 --- a/src/applicationwindow.cpp +++ b/src/applicationwindow.cpp @@ -89,6 +89,9 @@ ApplicationWindow::ApplicationWindow(): Global::font = new QFont("Verdana", 15, QFont::Bold); connect(lw, SIGNAL(LayerSelected(int)), drawing, SLOT(SetCurrentLayer(int))); + connect(lw, SIGNAL(LayerDeleted(int)), drawing, SLOT(DeleteCurrentLayer(int))); + connect(lw, SIGNAL(LayerToggled()), drawing, SLOT(HandleLayerToggle())); + connect(lw, SIGNAL(LayersSwapped(int, int)), drawing, SLOT(HandleLayerSwap(int, int))); } diff --git a/src/drawingview.cpp b/src/drawingview.cpp index 54e3b34..62f240a 100644 --- a/src/drawingview.cpp +++ b/src/drawingview.cpp @@ -20,6 +20,7 @@ // STILL TO BE DONE: // // - Lots of stuff +// - Layer locking (hiding works) // // Uncomment this for debugging... @@ -84,6 +85,7 @@ DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent), line->thickness = 2.0; line->style = LSDash; line->color = 0xFF7F00; + line->layer = 0; document.objects.push_back(line); document.objects.push_back(new Line(Vector(50, 40), Vector(10, 83))); document.objects.push_back(new Line(Vector(10, 83), Vector(17, 2))); @@ -265,13 +267,77 @@ zero; so we do another modulus operation on the result to achieve this. } -void DrawingView::SetCurrentLayer(int layer) +void DrawingView::SetCurrentLayer(int /*layer*/) { - Global::currentLayer = layer; +//Not needed anymore... +// Global::currentLayer = layer; //printf("DrawingView::CurrentLayer = %i\n", layer); } +// +// Basically, we just make a single pass through the Container. If the layer # +// is less than the layer # being deleted, then do nothing. If the layer # is +// equal to the layer # being deleted, then delete the object. If the layer # +// is greater than the layer # being deleted, then set the layer # to its layer +// # - 1. +// +void DrawingView::DeleteCurrentLayer(int layer) +{ +//printf("DrawingView::DeleteCurrentLayer(): currentLayer = %i\n", layer); + std::vector::iterator i = document.objects.begin(); + + while (i != document.objects.end()) + { + Object * obj = (Object *)(*i); + + if (obj->layer < layer) + i++; + else if (obj->layer == layer) + { + document.objects.erase(i); + delete obj; + } + else + { + obj->layer--; + i++; + } + } + + // We've just done a destructive action, so update the screen! + update(); +} + + +void DrawingView::HandleLayerToggle(void) +{ + // A layer's visibility was toggled, so update the screen... + update(); +} + + +// +// A layer was moved up or down in the layer list, so we have to swap the +// document's object's layer numbers in the layers that were swapped. +// +void DrawingView::HandleLayerSwap(int layer1, int layer2) +{ +//printf("DrawingView: Swapping layers %i and %i.\n", layer1, layer2); + std::vector::iterator i; + + for(i=document.objects.begin(); i!=document.objects.end(); i++) + { + Object * obj = (Object *)(*i); + + if (obj->layer == layer1) + obj->layer = layer2; + else if (obj->layer == layer2) + obj->layer = layer1; + } +} + + QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event) { // This is undoing the transform, e.g. going from client coords to local coords. @@ -306,7 +372,11 @@ void DrawingView::paintEvent(QPaintEvent * /*event*/) painter.DrawLine(-16384, 0, 16384, 0); // Do object rendering... - RenderObjects(&painter, document.objects); + for(int i=0; i & v) +void DrawingView::RenderObjects(Painter * painter, std::vector & v, int layer) { std::vector::iterator i; @@ -344,6 +414,10 @@ void DrawingView::RenderObjects(Painter * painter, std::vector & v) Object * obj = (Object *)(*i); float scaledThickness = Global::scale * obj->thickness; + // If the object isn't on the current layer being drawn, skip it + if (obj->layer != layer) + continue; + if ((Global::tool == TTRotate) && ctrlDown && obj->selected) { painter->SetPen(0x00FF00, 2.0, LSSolid); @@ -789,6 +863,7 @@ void DrawingView::LineHandler(int mode, Point p) else { Line * l = new Line(toolPoint[0], toolPoint[1]); + l->layer = Global::activeLayer; document.objects.push_back(l); toolPoint[0] = toolPoint[1]; } @@ -831,6 +906,7 @@ void DrawingView::CircleHandler(int mode, Point p) { double length = Vector::Magnitude(toolPoint[0], toolPoint[1]); Circle * c = new Circle(toolPoint[0], length); + c->layer = Global::activeLayer; document.objects.push_back(c); toolPoint[0] = toolPoint[1]; Global::toolState = TSNone; @@ -901,6 +977,7 @@ void DrawingView::ArcHandler(int mode, Point p) span += PI_TIMES_2; Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span); + arc->layer = Global::activeLayer; document.objects.push_back(arc); Global::toolState = TSNone; } diff --git a/src/drawingview.h b/src/drawingview.h index 518301a..454d6f2 100644 --- a/src/drawingview.h +++ b/src/drawingview.h @@ -20,7 +20,7 @@ class DrawingView: public QWidget void SetGridSize(uint32_t); void UpdateGridBackground(void); Point SnapPointToGrid(Point); - void RenderObjects(Painter *, std::vector &); + void RenderObjects(Painter *, std::vector &, int); void AddHoveredToSelection(void); void GetSelection(std::vector &); void GetHovered(std::vector &); @@ -37,6 +37,9 @@ class DrawingView: public QWidget public slots: void SetCurrentLayer(int); + void DeleteCurrentLayer(int); + void HandleLayerToggle(void); + void HandleLayerSwap(int, int); protected: void paintEvent(QPaintEvent * event); diff --git a/src/global.cpp b/src/global.cpp index 013f795..8b89b94 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -39,5 +39,7 @@ Vector Global::screenSize(200.0, 200.0); float Global::scale = 0.5; int Global::activeLayer = 0; -bool Global::layerIsLocked = false; +int Global::numLayers = 1; +std::vector Global::layerHidden; +std::vector Global::layerLocked; diff --git a/src/global.h b/src/global.h index d66f93b..6495524 100644 --- a/src/global.h +++ b/src/global.h @@ -5,6 +5,7 @@ // to do any instantiation shite. #include +#include #include #include "vector.h" @@ -44,7 +45,9 @@ class Global static float scale; static int activeLayer; - static bool layerIsLocked; + static int numLayers; + static std::vector layerHidden; + static std::vector layerLocked; }; #endif // __GLOBALS_H__ diff --git a/src/layeritemwidget.cpp b/src/layeritemwidget.cpp index 5763138..40c8b01 100644 --- a/src/layeritemwidget.cpp +++ b/src/layeritemwidget.cpp @@ -14,11 +14,12 @@ #include "layeritemwidget.h" -LayerItemWidget::LayerItemWidget(QString s, bool invisible/*=false*/, bool locked/*=false*/): +LayerItemWidget::LayerItemWidget(QString s, bool i/*=false*/, bool l/*=false*/, QListWidgetItem * p/*=null*/): QWidget(), name(new QLabel(s)), - visibility(new QPushButton), - editibility(new QPushButton) + invisible(new QPushButton), + locked(new QPushButton), + parent(p) { QIcon visibleIcon(":/res/eye-open.png"); visibleIcon.addFile(":/res/eye-closed.png", QSize(16, 16), QIcon::Normal, QIcon::On); @@ -27,25 +28,29 @@ LayerItemWidget::LayerItemWidget(QString s, bool invisible/*=false*/, bool locke QSize buttonSize(20, 20); QHBoxLayout * mainLayout = new QHBoxLayout; - mainLayout->setContentsMargins(0, 0, 0, 0); // This is required, otherwise the layout engine puts too much space around this widget. :-/ - - visibility->setFlat(true); - visibility->setIcon(visibleIcon); - visibility->setCheckable(true); - visibility->setMaximumSize(buttonSize); - visibility->setChecked(invisible); - - editibility->setFlat(true); - editibility->setIcon(lockedIcon); - editibility->setCheckable(true); - editibility->setMaximumSize(buttonSize); - editibility->setChecked(locked); - - mainLayout->addWidget(visibility); - mainLayout->addWidget(editibility); + // This is required, otherwise the layout engine puts too much space around + // this widget. :-/ + mainLayout->setContentsMargins(0, 0, 0, 0); + + invisible->setFlat(true); + invisible->setIcon(visibleIcon); + invisible->setCheckable(true); + invisible->setMaximumSize(buttonSize); + invisible->setChecked(i); + + locked->setFlat(true); + locked->setIcon(lockedIcon); + locked->setCheckable(true); + locked->setMaximumSize(buttonSize); + locked->setChecked(l); + + mainLayout->addWidget(invisible); + mainLayout->addWidget(locked); mainLayout->addWidget(name); setLayout(mainLayout); + connect(invisible, SIGNAL(clicked(bool)), this, SLOT(HandleHideToggle(bool))); + connect(locked, SIGNAL(clicked(bool)), this, SLOT(HandleLockToggle(bool))); } @@ -53,3 +58,17 @@ LayerItemWidget::~LayerItemWidget() { } + +void LayerItemWidget::HandleHideToggle(bool state) +{ +// printf("Eye is: %s\n", !state ? "OPEN" : "closed"); + emit(HideToggled(parent, state)); +} + + +void LayerItemWidget::HandleLockToggle(bool state) +{ +// printf("Lock is: %s\n", !state ? "OPEN" : "closed"); + emit(LockToggled(parent, state)); +} + diff --git a/src/layeritemwidget.h b/src/layeritemwidget.h index 999cccf..7c8a959 100644 --- a/src/layeritemwidget.h +++ b/src/layeritemwidget.h @@ -8,13 +8,22 @@ class LayerItemWidget: public QWidget Q_OBJECT public: - LayerItemWidget(QString, bool invisible = false, bool locked = false); + LayerItemWidget(QString, bool i = false, bool l = false, QListWidgetItem * p = NULL); ~LayerItemWidget(); + private slots: + void HandleHideToggle(bool); + void HandleLockToggle(bool); + + signals: + void HideToggled(QListWidgetItem *, bool); + void LockToggled(QListWidgetItem *, bool); + public: QLabel * name; - QPushButton * visibility; - QPushButton * editibility; + QPushButton * invisible; + QPushButton * locked; + QListWidgetItem * parent; }; #endif // __LAYERITEMWIDGET_H__ diff --git a/src/layerwidget.cpp b/src/layerwidget.cpp index faaf5b9..9ac85a8 100644 --- a/src/layerwidget.cpp +++ b/src/layerwidget.cpp @@ -21,16 +21,10 @@ LayerWidget::LayerWidget(void): QWidget(), editLayer(new QToolButton), layerUp(new QToolButton), layerDown(new QToolButton), list(new QListWidget) { - LayerItemWidget * liw = new LayerItemWidget("Background"); QListWidgetItem * qlwi = new QListWidgetItem(list); + LayerItemWidget * liw = new LayerItemWidget("Background", false, false, qlwi); list->setItemWidget(qlwi, liw); -// QToolButton * pb1 = new QToolButton; -// QToolButton * pb2 = new QToolButton; -// QToolButton * pb3 = new QToolButton; -// QToolButton * pb4 = new QToolButton; -// QToolButton * pb5 = new QToolButton; - addLayer->setIcon(QIcon(":/res/layer-add.png")); removeLayer->setIcon(QIcon(":/res/layer-delete.png")); editLayer->setIcon(QIcon(":/res/layer-edit.png")); @@ -58,13 +52,25 @@ LayerWidget::LayerWidget(void): QWidget(), setLayout(mainLayout); connect(list, SIGNAL(currentRowChanged(int)), this, SLOT(HandleLayerSelected(int))); + connect(list, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(HandleDblClick(QListWidgetItem *))); connect(addLayer, SIGNAL(clicked()), this, SLOT(AddLayer())); connect(removeLayer, SIGNAL(clicked()), this, SLOT(DeleteLayer())); connect(editLayer, SIGNAL(clicked()), this, SLOT(EditLayer())); connect(layerUp, SIGNAL(clicked()), this, SLOT(MoveLayerUp())); connect(layerDown, SIGNAL(clicked()), this, SLOT(MoveLayerDown())); + connect(liw, SIGNAL(HideToggled(QListWidgetItem *, bool)), this, SLOT(HandleHideToggle(QListWidgetItem *, bool))); + connect(liw, SIGNAL(LockToggled(QListWidgetItem *, bool)), this, SLOT(HandleLockToggle(QListWidgetItem *, bool))); + list->setCurrentRow(0); + + // We set global variables here, since we are 'in charge' of them (mostly) + Global::activeLayer = 0; + Global::numLayers = 1; + Global::layerHidden.clear(); + Global::layerLocked.clear(); + Global::layerHidden.push_back(false); + Global::layerLocked.push_back(false); } @@ -78,26 +84,76 @@ void LayerWidget::HandleLayerSelected(int currentRow) //printf("LayerWidget::HandleLayerSelected(): currentRow = %i\n", currentRow); // emit(LayerSelected(currentRow)); - QListWidgetItem * qlwi = list->item(currentRow); - LayerItemWidget * li = (LayerItemWidget *)list->itemWidget(qlwi); - Global::activeLayer = currentRow; - Global::layerIsLocked = li->editibility->isChecked(); - + // This is numbered opposite of how it's presented. In other words, the + // bottom of the list is 0, and items above it count upwards. So like this: + // + // (2) Layer #2 + // (1) Layer #1 + // (0) Background + // + // which is the opposite of the internal numbering. + Global::activeLayer = (Global::numLayers - currentRow) - 1; +//printf("LayerWidget::HandleLayerSelected(): currentRow = %i, numLayers = %i, active = %i\n", currentRow, Global::numLayers, Global::activeLayer); // Set button states to sane values SetButtonStates(); } +// +// What happens here is that for every QListWidgetItem we make, we connect it +// to these handlers. But we only have to worry about that when adding and +// moving a layer. However, when toggling states, we need to toggle the global +// state variables too. +// +void LayerWidget::HandleHideToggle(QListWidgetItem * qlwi, bool state) +{ + int currentRow = list->row(qlwi); + int layer = (Global::numLayers - currentRow) - 1; + std::vector::iterator i = Global::layerHidden.begin() + layer; + (*i) = state; +//printf("Item #%i, new hide state is %s\n", currentRow, (state ? "ON" : "off")); +//printf("LayerWidget: New hide state of layer %i is %s.\n", layer, (state ? "ON" : "off")); + // We do this last, because otherwise the Document would get the wrong state + emit(LayerToggled()); +} + + +void LayerWidget::HandleLockToggle(QListWidgetItem * qlwi, bool state) +{ + int currentRow = list->row(qlwi); + int layer = (Global::numLayers - currentRow) - 1; + std::vector::iterator i = Global::layerLocked.begin() + layer; + (*i) = state; +// printf("Item #%i, new lock state is %s\n", list->row(qlwi), (state ? "ON" : "off")); +} + + +void LayerWidget::HandleDblClick(QListWidgetItem * /*qlwi*/) +{ + EditLayer(); +} + + void LayerWidget::AddLayer(void) { + // We always stick the newest layer at the top of the list... int count = list->count(); QString text = QString("Layer #%1").arg(count); - LayerItemWidget * liw = new LayerItemWidget(text); QListWidgetItem * qlwi = new QListWidgetItem(); + LayerItemWidget * liw = new LayerItemWidget(text, false, false, qlwi); list->insertItem(0, qlwi); list->setItemWidget(qlwi, liw); + // Set up SIGNAL/SLOTs for this LayerItemWidget + connect(liw, SIGNAL(HideToggled(QListWidgetItem *, bool)), this, SLOT(HandleHideToggle(QListWidgetItem *, bool))); + connect(liw, SIGNAL(LockToggled(QListWidgetItem *, bool)), this, SLOT(HandleLockToggle(QListWidgetItem *, bool))); + SetButtonStates(); + + // Fix up the global state + Global::layerHidden.insert(Global::layerHidden.begin(), false); + Global::layerLocked.insert(Global::layerLocked.begin(), false); + Global::numLayers++; } @@ -108,13 +164,29 @@ void LayerWidget::DeleteLayer(void) if (numItems == 1) return; + // N.B.: This *must* go before the item removal because that causes + // HandleLayerSelected() to be fired off which causes the numbers to + // be off. You have been warned! + // Tell the DrawingView to delete this layer in its Container: + emit(LayerDeleted(Global::activeLayer)); + + int currentRow = list->currentRow(); QListWidgetItem * qlwi = list->currentItem(); list->removeItemWidget(qlwi); delete qlwi; SetButtonStates(); - // N.B.: Also, we need to delete the layer in the Drawing as well! + // Fix up the global state + int layer = (Global::numLayers - currentRow) - 1; + Global::layerHidden.erase(Global::layerHidden.begin() + layer); + Global::layerLocked.erase(Global::layerLocked.begin() + layer); + Global::numLayers--; + + // If we're deleting from the top of the list, we have to decrement the + // active layer # by 1 (since we count upward from the bottom of the list). + if (currentRow == 0) + Global::activeLayer--; } @@ -140,15 +212,32 @@ void LayerWidget::MoveLayerUp(void) QListWidgetItem * qlwi = list->currentItem(); LayerItemWidget * li = (LayerItemWidget *)list->itemWidget(qlwi); QString s = li->name->text(); - bool visible = li->visibility->isChecked(); - bool editible = li->editibility->isChecked(); + bool visible = li->invisible->isChecked(); + bool editible = li->locked->isChecked(); // We have to make a new LayerItemWidget because it destroys the old one! list->takeItem(currentRow); list->insertItem(currentRow - 1, qlwi); - li = new LayerItemWidget(s, visible, editible); + li = new LayerItemWidget(s, visible, editible, qlwi); list->setItemWidget(qlwi, li); list->setCurrentItem(qlwi); + + // Set up SIGNAL/SLOTs for this LayerItemWidget + connect(li, SIGNAL(HideToggled(QListWidgetItem *, bool)), this, SLOT(HandleHideToggle(QListWidgetItem *, bool))); + connect(li, SIGNAL(LockToggled(QListWidgetItem *, bool)), this, SLOT(HandleLockToggle(QListWidgetItem *, bool))); + + // Fix up the global state... + // N.B.: Because we handle the button states correctly, we should never + // have a situation where the reference in the vector is bad. + int layer = (Global::numLayers - currentRow) - 1; + bool old = Global::layerHidden[layer]; + Global::layerHidden[layer] = Global::layerHidden[layer + 1]; + Global::layerHidden[layer + 1] = old; + old = Global::layerLocked[layer]; + Global::layerLocked[layer] = Global::layerLocked[layer + 1]; + Global::layerLocked[layer + 1] = old; + // We also have to tell the document to shuffle its layers too + emit(LayersSwapped(layer, layer + 1)); } @@ -159,15 +248,32 @@ void LayerWidget::MoveLayerDown(void) QListWidgetItem * qlwi = list->currentItem(); LayerItemWidget * li = (LayerItemWidget *)list->itemWidget(qlwi); QString s = li->name->text(); - bool visible = li->visibility->isChecked(); - bool editible = li->editibility->isChecked(); + bool visible = li->invisible->isChecked(); + bool editible = li->locked->isChecked(); // We have to make a new LayerItemWidget because it destroys the old one! list->takeItem(currentRow); list->insertItem(currentRow + 1, qlwi); - li = new LayerItemWidget(s, visible, editible); + li = new LayerItemWidget(s, visible, editible, qlwi); list->setItemWidget(qlwi, li); list->setCurrentItem(qlwi); + + // Set up SIGNAL/SLOTs for this LayerItemWidget + connect(li, SIGNAL(HideToggled(QListWidgetItem *, bool)), this, SLOT(HandleHideToggle(QListWidgetItem *, bool))); + connect(li, SIGNAL(LockToggled(QListWidgetItem *, bool)), this, SLOT(HandleLockToggle(QListWidgetItem *, bool))); + + // Fix up the global state... + // N.B.: Because we handle the button states correctly, we should never + // have a situation where the reference in the vector is bad. + int layer = (Global::numLayers - currentRow) - 1; + bool old = Global::layerHidden[layer]; + Global::layerHidden[layer] = Global::layerHidden[layer - 1]; + Global::layerHidden[layer - 1] = old; + old = Global::layerLocked[layer]; + Global::layerLocked[layer] = Global::layerLocked[layer - 1]; + Global::layerLocked[layer - 1] = old; + // We also have to tell the document to shuffle its layers too + emit(LayersSwapped(layer, layer - 1)); } diff --git a/src/layerwidget.h b/src/layerwidget.h index e515dcf..875c3cd 100644 --- a/src/layerwidget.h +++ b/src/layerwidget.h @@ -13,6 +13,9 @@ class LayerWidget: public QWidget private slots: void HandleLayerSelected(int); + void HandleHideToggle(QListWidgetItem *, bool); + void HandleLockToggle(QListWidgetItem *, bool); + void HandleDblClick(QListWidgetItem *); void AddLayer(void); void DeleteLayer(void); void EditLayer(void); @@ -24,6 +27,9 @@ class LayerWidget: public QWidget signals: void LayerSelected(int); + void LayerDeleted(int); + void LayerToggled(void); + void LayersSwapped(int, int); public: QToolButton * addLayer; -- 2.37.2