diff --git a/libs/ui/kis_painting_assistant.h b/libs/ui/kis_painting_assistant.h --- a/libs/ui/kis_painting_assistant.h +++ b/libs/ui/kis_painting_assistant.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -99,6 +100,7 @@ bool isSnappingActive() const; void setSnappingActive(bool set); + /** * Adjust the position given in parameter. * @param point the coordinates in point in the document reference @@ -108,6 +110,7 @@ virtual void endStroke() { } virtual QPointF buttonPosition() const = 0; virtual int numHandles() const = 0; + void replaceHandle(KisPaintingAssistantHandleSP _handle, KisPaintingAssistantHandleSP _with); void addHandle(KisPaintingAssistantHandleSP handle, HandleType type); @@ -121,8 +124,13 @@ QList handles(); const QList& sideHandles() const; QList sideHandles(); + QByteArray saveXml( QMap &handleMap); + virtual void saveCustomXml(QXmlStreamWriter* xml); //in case specific assistants have custom properties (like vanishing point) + void loadXml(KoStore *store, QMap &handleMap, QString path); + virtual bool loadCustomXml(QXmlStreamReader* xml); + void saveXmlList(QDomDocument& doc, QDomElement& ssistantsElement, int count); void findPerspectiveAssistantHandleLocation(); KisPaintingAssistantHandleSP oppHandleOne(); diff --git a/libs/ui/kis_painting_assistant.cc b/libs/ui/kis_painting_assistant.cc --- a/libs/ui/kis_painting_assistant.cc +++ b/libs/ui/kis_painting_assistant.cc @@ -19,8 +19,6 @@ */ #include -#include - #include "kis_painting_assistant.h" #include "kis_coordinates_converter.h" #include "kis_debug.h" @@ -167,6 +165,7 @@ d->isSnappingActive = set; } + void KisPaintingAssistant::drawPath(QPainter& painter, const QPainterPath &path, bool isSnappingOn) { int alpha = d->assistantColor.alpha(); @@ -331,6 +330,11 @@ xml.writeStartElement("assistant"); xml.writeAttribute("type",d->id); xml.writeAttribute("active", QString::number(d->isSnappingActive)); + + + saveCustomXml(&xml); // if any specific assistants have custom XML data to save to + + // write individual handle data xml.writeStartElement("handles"); Q_FOREACH (const KisPaintingAssistantHandleSP handle, d->handles) { int id = handleMap.size(); @@ -350,6 +354,11 @@ return data; } +void KisPaintingAssistant::saveCustomXml(QXmlStreamWriter* xml) +{ + Q_UNUSED(xml); +} + void KisPaintingAssistant::loadXml(KoStore* store, QMap &handleMap, QString path) { int id; @@ -365,6 +374,8 @@ d->isSnappingActive = (active != "0"); } + loadCustomXml(&xml); + if (xml.name() == "handle") { QString strId = xml.attributes().value("id").toString(), strX = xml.attributes().value("x").toString(), @@ -387,6 +398,12 @@ store->close(); } +bool KisPaintingAssistant::loadCustomXml(QXmlStreamReader* xml) +{ + Q_UNUSED(xml); + return true; +} + void KisPaintingAssistant::saveXmlList(QDomDocument& doc, QDomElement& assistantsElement,int count) { if (d->id == "ellipse"){ diff --git a/libs/ui/kis_painting_assistants_decoration.h b/libs/ui/kis_painting_assistants_decoration.h --- a/libs/ui/kis_painting_assistants_decoration.h +++ b/libs/ui/kis_painting_assistants_decoration.h @@ -69,6 +69,14 @@ QList handles(); QList assistants() const; + + /// getter and setter functions for what assistant is currently selected + /// this is used to control some tool options that are specific to a assistant + KisPaintingAssistantSP selectedAssistant(); + void setSelectedAssistant(KisPaintingAssistantSP assistant); + void deselectAssistant(); + + /// called when assistant editor is activated /// right now this happens when the assistants tool is selected void activateAssistantsEditor(); @@ -111,6 +119,8 @@ Q_SIGNALS: void assistantChanged(); + void selectedAssistantChanged(); + public Q_SLOTS: /// toggles whether the assistant is active or not diff --git a/libs/ui/kis_painting_assistants_decoration.cpp b/libs/ui/kis_painting_assistants_decoration.cpp --- a/libs/ui/kis_painting_assistants_decoration.cpp +++ b/libs/ui/kis_painting_assistants_decoration.cpp @@ -33,6 +33,7 @@ #include "KisViewManager.h" #include +#include struct KisPaintingAssistantsDecoration::Private { Private() @@ -47,6 +48,7 @@ bool outlineVisible; bool snapOnlyOneAssistant; KisPaintingAssistantSP firstAssistant; + KisPaintingAssistantSP selectedAssistant; bool aFirstStroke; QColor m_assistantsColor = QColor(176, 176, 176, 255); // kis_assistant_tool has same default color specified bool m_isEditingAssistants = false; @@ -290,6 +292,23 @@ return assistants; } +KisPaintingAssistantSP KisPaintingAssistantsDecoration::selectedAssistant() +{ + return d->selectedAssistant; +} + +void KisPaintingAssistantsDecoration::setSelectedAssistant(KisPaintingAssistantSP assistant) +{ + d->selectedAssistant = assistant; + emit selectedAssistantChanged(); +} + +void KisPaintingAssistantsDecoration::deselectAssistant() +{ + d->selectedAssistant.clear(); +} + + void KisPaintingAssistantsDecoration::setAssistantVisible(bool set) { d->assistantVisible=set; @@ -421,6 +440,17 @@ QPainterPath bgPath; bgPath.addRoundedRect(QRectF(actionsBGRectangle.x(), actionsBGRectangle.y(), 110, 40), 6, 6); QPen stroke(QColor(60, 60, 60, 80), 2); + + // if the assistant is selected, make outline stroke fatter and use theme's highlight color + // for better visual feedback + if (selectedAssistant()) { // there might not be a selected assistant, so do not seg fault + if (assistant->buttonPosition() == selectedAssistant()->buttonPosition()) { + stroke.setWidth(4); + stroke.setColor(qApp->palette().color(QPalette::Highlight)); + } + } + + // draw the final result gc.setPen(stroke); gc.fillPath(bgPath, backgroundColor); gc.drawPath(bgPath); @@ -439,4 +469,5 @@ gc.drawPixmap(iconDeletePosition, d->m_iconDelete); + } diff --git a/plugins/assistants/Assistants/AssistantsToolOptions.ui b/plugins/assistants/Assistants/AssistantsToolOptions.ui --- a/plugins/assistants/Assistants/AssistantsToolOptions.ui +++ b/plugins/assistants/Assistants/AssistantsToolOptions.ui @@ -6,8 +6,8 @@ 0 0 - 188 - 211 + 284 + 274 @@ -58,19 +58,6 @@ - - - - - 0 - 0 - - - - Opacity: - - - @@ -78,35 +65,39 @@ - - - - - + - + 0 0 - - Color: + + + 20 + 0 + + + + + + + Qt::Horizontal + + + + + - - - - 0 - 0 - - + - 30 - 0 + 0 + 20 @@ -121,7 +112,7 @@ 20 - 0 + 5 @@ -141,6 +132,12 @@
kis_slider_spin_box.h
1 + + KisDoubleSliderSpinBox + QWidget +
kis_slider_spin_box.h
+ 1 +
diff --git a/plugins/assistants/Assistants/VanishingPointAssistant.h b/plugins/assistants/Assistants/VanishingPointAssistant.h --- a/plugins/assistants/Assistants/VanishingPointAssistant.h +++ b/plugins/assistants/Assistants/VanishingPointAssistant.h @@ -50,7 +50,13 @@ QPointF buttonPosition() const override; int numHandles() const override { return 1; } - bool isAssistantComplete() const; + float referenceLineDensity(); + void setReferenceLineDensity(float value); + + bool isAssistantComplete() const; + + void saveCustomXml(QXmlStreamWriter* xml) override; + bool loadCustomXml(QXmlStreamReader* xml) override; protected: void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached = true,KisCanvas2* canvas=0, bool assistantVisible=true, bool previewVisible=true) override; @@ -58,6 +64,9 @@ private: QPointF project(const QPointF& pt, const QPointF& strokeBegin); KisCanvas2 *m_canvas; + + float m_referenceLineDensity = 15.0; + }; class VanishingPointAssistantFactory : public KisPaintingAssistantFactory diff --git a/plugins/assistants/Assistants/VanishingPointAssistant.cc b/plugins/assistants/Assistants/VanishingPointAssistant.cc --- a/plugins/assistants/Assistants/VanishingPointAssistant.cc +++ b/plugins/assistants/Assistants/VanishingPointAssistant.cc @@ -30,7 +30,7 @@ #include #include #include - +#include #include VanishingPointAssistant::VanishingPointAssistant() @@ -177,12 +177,40 @@ } - gc.restore(); + // draw references guide for vanishing points at specified density + // this is shown as part of the preview, so don't show if preview is off + if( canvas->paintingAssistantsDecoration()->outlineVisibility() && this->isSnappingActive() ) { - KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); + // cycle through degrees from 0 to 180. We are doing an infinite line, so we don't need to go 360 + QTransform initialTransform = converter->documentToWidgetTransform(); + QPointF p0 = initialTransform.map(*handles()[0]); // main vanishing point + for (int currentAngle=0; currentAngle <= 180; currentAngle = currentAngle + m_referenceLineDensity ) { + + // determine the correct angle based on the iteration + float xPos = cos(currentAngle * M_PI / 180); + float yPos = sin(currentAngle * M_PI / 180); + QPointF unitAngle; + unitAngle.setX(p0.x() + xPos); + unitAngle.setY(p0.y() + yPos); + + // find point + QLineF snapLine= QLineF(p0, unitAngle); + QRect viewport= gc.viewport(); + KisAlgebra2D::intersectLineRect(snapLine, viewport); + + // make a line from VP center to edge of canvas with that angle + QPainterPath path; + path.moveTo(snapLine.p1()); + path.lineTo(snapLine.p2()); + drawPreview(gc, path);//and we draw the preview. + } + } + gc.restore(); + + KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); } void VanishingPointAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) @@ -215,11 +243,42 @@ return (*handles()[0]); } +void VanishingPointAssistant::setReferenceLineDensity(float value) +{ + // cannot have less than 1 degree value + if (value < 1.0) { + value = 1.0; + } + + m_referenceLineDensity = value; +} + +float VanishingPointAssistant::referenceLineDensity() +{ + return m_referenceLineDensity; +} + bool VanishingPointAssistant::isAssistantComplete() const { return handles().size() > 0; // only need one point to be ready } +void VanishingPointAssistant::saveCustomXml(QXmlStreamWriter* xml) +{ + xml->writeStartElement("angleDensity"); + xml->writeAttribute("value", KisDomUtils::toString( this->referenceLineDensity())); + xml->writeEndElement(); +} + +bool VanishingPointAssistant::loadCustomXml(QXmlStreamReader* xml) +{ + if (xml && xml->name() == "angleDensity") { + this->setReferenceLineDensity((float)KisDomUtils::toDouble(xml->attributes().value("value").toString())); + } + + return true; +} + VanishingPointAssistantFactory::VanishingPointAssistantFactory() { diff --git a/plugins/assistants/Assistants/kis_assistant_tool.h b/plugins/assistants/Assistants/kis_assistant_tool.h --- a/plugins/assistants/Assistants/kis_assistant_tool.h +++ b/plugins/assistants/Assistants/kis_assistant_tool.h @@ -82,10 +82,18 @@ void addAssistant(); void removeAssistant(KisPaintingAssistantSP assistant); + void assistantSelected(KisPaintingAssistantSP assistant); + + void updateToolOptionsUI(); + + public Q_SLOTS: void activate(ToolActivation toolActivation, const QSet &shapes) override; void deactivate() override; + + void slotChangeVanishingPointAngle(double value); + private Q_SLOTS: void removeAllAssistants(); void saveAssistants(); diff --git a/plugins/assistants/Assistants/kis_assistant_tool.cc b/plugins/assistants/Assistants/kis_assistant_tool.cc --- a/plugins/assistants/Assistants/kis_assistant_tool.cc +++ b/plugins/assistants/Assistants/kis_assistant_tool.cc @@ -46,7 +46,7 @@ #include #include #include "kis_global.h" - +#include "VanishingPointAssistant.h" #include @@ -64,6 +64,7 @@ void KisAssistantTool::activate(ToolActivation toolActivation, const QSet &shapes) { + KisTool::activate(toolActivation, shapes); m_canvas->paintingAssistantsDecoration()->activateAssistantsEditor(); @@ -76,6 +77,12 @@ m_canvas->paintingAssistantsDecoration()->setHandleSize(17); m_handleSize = 17; + if (m_optionsWidget) { + m_canvas->paintingAssistantsDecoration()->deselectAssistant(); + + updateToolOptionsUI(); + } + m_canvas->updateCanvas(); } @@ -115,18 +122,32 @@ // we probably need to stop storing a reference in m_handles and call the assistants directly m_handles = m_canvas->paintingAssistantsDecoration()->handles(); + Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { - // find out which handle on all assistants is closes to the mouse position - Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_handles) { - double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*handle)); - if (dist < minDist) { - minDist = dist; - m_handleDrag = handle; + + // find out which handle on all assistants is closest to the mouse position + // vanishing points have "side handles", so make sure to include that + { + QList allAssistantHandles; + allAssistantHandles.append(assistant->handles()); + allAssistantHandles.append(assistant->sideHandles()); + + Q_FOREACH (const KisPaintingAssistantHandleSP handle, allAssistantHandles) { + + double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*handle)); + if (dist < minDist) { + minDist = dist; + m_handleDrag = handle; + + assistantSelected(assistant); // whatever handle is the closest contains the selected assistant + } } } + + if(m_handleDrag && assistant->id() == "perspective") { // Look for the handle which was pressed @@ -297,6 +318,10 @@ m_cursorStart = event->point; m_currentAdjustment = QPointF(); m_internalMode = MODE_EDITING; + + + assistantSelected(assistant); // whatever handle is the closest contains the selected assistant + return; } @@ -314,6 +339,8 @@ newAssistantAllowed = false; assistant->setSnappingActive(!assistant->isSnappingActive()); // toggle assistant->uncache();//this updates the chache of the assistant, very important. + + assistantSelected(assistant); // whatever handle is the closest contains the selected assistant } } if (newAssistantAllowed==true){//don't make a new assistant when I'm just toogling visiblity// @@ -500,11 +527,16 @@ { m_canvas->paintingAssistantsDecoration()->addAssistant(m_newAssistant); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); + m_canvas->paintingAssistantsDecoration()->setSelectedAssistant(m_newAssistant); + updateToolOptionsUI(); // vanishing point assistant will get an extra option + KisAbstractPerspectiveGrid* grid = dynamic_cast(m_newAssistant.data()); if (grid) { m_canvas->viewManager()->resourceProvider()->addPerspectiveGrid(grid); } m_newAssistant.clear(); + + } void KisAssistantTool::removeAssistant(KisPaintingAssistantSP assistant) @@ -515,6 +547,56 @@ } m_canvas->paintingAssistantsDecoration()->removeAssistant(assistant); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); + + m_canvas->paintingAssistantsDecoration()->deselectAssistant(); + updateToolOptionsUI(); +} + +void KisAssistantTool::assistantSelected(KisPaintingAssistantSP assistant) +{ + m_canvas->paintingAssistantsDecoration()->setSelectedAssistant(assistant); + updateToolOptionsUI(); +} + +void KisAssistantTool::updateToolOptionsUI() +{ + KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant(); + + + + if (m_selectedAssistant) { + bool isVanishingPointAssistant = m_selectedAssistant->id() == "vanishing point"; + m_options.vanishingPointAngleSpinbox->setVisible(isVanishingPointAssistant); + + if (isVanishingPointAssistant) { + QSharedPointer assis = qSharedPointerCast(m_selectedAssistant); + m_options.vanishingPointAngleSpinbox->setValue(assis->referenceLineDensity()); + } + + } else { + // nothing selected, so hide UI controls + m_options.vanishingPointAngleSpinbox->setVisible(false); + } +} + +void KisAssistantTool::slotChangeVanishingPointAngle(double value) +{ + if ( m_canvas->paintingAssistantsDecoration()->assistants().length() == 0) { + return; + } + + // get the selected assistant and change the angle value + KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant(); + if (m_selectedAssistant) { + bool isVanishingPointAssistant = m_selectedAssistant->id() == "vanishing point"; + + if (isVanishingPointAssistant) { + QSharedPointer assis = qSharedPointerCast(m_selectedAssistant); + assis->setReferenceLineDensity((float)value); + } + } + + m_canvas->canvasWidget()->update(); } void KisAssistantTool::mouseMoveEvent(KoPointerEvent *event) @@ -584,6 +666,9 @@ m_canvas->paintingAssistantsDecoration()->removeAll(); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); m_canvas->updateCanvas(); + + m_canvas->paintingAssistantsDecoration()->deselectAssistant(); + updateToolOptionsUI(); } void KisAssistantTool::loadAssistants() @@ -643,6 +728,10 @@ } else { errors = true; } + } else { + if (assistant) { + assistant->loadCustomXml(&xml); + } } break; case QXmlStreamReader::EndElement: @@ -672,6 +761,7 @@ default: break; } + } if (assistant) { errors = true; @@ -710,9 +800,18 @@ } xml.writeEndElement(); xml.writeStartElement("assistants"); + + + + Q_FOREACH (const KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { xml.writeStartElement("assistant"); xml.writeAttribute("type", assistant->id()); + + // custom assistant properties like angle density on vanishing point + assistant->saveCustomXml(&xml); + + // handle information xml.writeStartElement("handles"); Q_FOREACH (const KisPaintingAssistantHandleSP handle, assistant->handles()) { xml.writeStartElement("handle"); @@ -772,8 +871,12 @@ connect(m_options.assistantsOpacitySlider, SIGNAL(valueChanged(int)), SLOT(slotAssistantOpacityChanged())); + connect(m_options.vanishingPointAngleSpinbox, SIGNAL(valueChanged(double)), this, SLOT(slotChangeVanishingPointAngle(double))); + + m_options.assistantsColor->setColor(QColor(176, 176, 176, 255)); // grey default for all assistants m_options.assistantsOpacitySlider->setValue(100); // 100% + m_options.assistantsOpacitySlider->setPrefix(i18n("Opacity: ")); m_options.assistantsOpacitySlider->setSuffix(" %"); m_assistantsOpacity = m_options.assistantsOpacitySlider->value()*0.01; @@ -781,6 +884,16 @@ QColor newColor = m_options.assistantsColor->color(); newColor.setAlpha(m_assistantsOpacity*255); m_canvas->paintingAssistantsDecoration()->setAssistantsColor(newColor); + + m_options.vanishingPointAngleSpinbox->setPrefix(i18n("Density: ")); + m_options.vanishingPointAngleSpinbox->setSuffix(QChar(Qt::Key_degree)); + m_options.vanishingPointAngleSpinbox->setRange(1.0, 30.0); + m_options.vanishingPointAngleSpinbox->setSingleStep(0.5); + + + m_options.vanishingPointAngleSpinbox->setVisible(false); + + } return m_optionsWidget; }