diff --git a/rtengine/spot.cc b/rtengine/spot.cc index 2244d882e..e9d43ef31 100644 --- a/rtengine/spot.cc +++ b/rtengine/spot.cc @@ -23,18 +23,6 @@ #include "imagesource.h" #include -namespace -{ - -// "ceil" rounding -template -constexpr T skips(T a, T b) -{ - return a / b + static_cast(a % b); -} - -} - namespace rtengine { @@ -48,14 +36,12 @@ public: }; struct Rectangle { - public: int x1; int y1; int x2; int y2; - Rectangle() : x1(0), y1(0), x2(0), y2(0) {} - Rectangle(const Rectangle &other) : x1(other.x1), y1(other.y1), x2(other.x2), y2(other.y2) {} + Rectangle() : Rectangle(0, 0, 0, 0) {} Rectangle(int X1, int Y1, int X2, int Y2) : x1(X1), y1(Y1), x2(X2), y2(Y2) {} bool intersects(const Rectangle &other) const { @@ -65,21 +51,24 @@ public: bool getIntersection(const Rectangle &other, std::unique_ptr &intersection) const { if (intersects(other)) { - if (!intersection) { - intersection.reset(new Rectangle()); - } - intersection->x1 = rtengine::max(x1, other.x1); - intersection->x2 = rtengine::min(x2, other.x2); - intersection->y1 = rtengine::max(y1, other.y1); - intersection->y2 = rtengine::min(y2, other.y2); + std::unique_ptr intsec( + new Rectangle( + rtengine::max(x1, other.x1), + rtengine::max(y1, other.y1), + rtengine::min(x2, other.x2), + rtengine::min(y2, other.y2) + ) + ); - if (intersection->x1 > intersection->x2 || intersection->y1 > intersection->y2) { - intersection.release(); + if (intsec->x1 > intsec->x2 || intsec->y1 > intsec->y2) { return false; } + + intersection = std::move(intsec); return true; } if (intersection) { + // There's no intersection, we delete the Rectangle structure intersection.release(); } return false; @@ -101,15 +90,15 @@ public: return *this; } - Rectangle& operator/=(const int &v) { + Rectangle& operator/=(int v) { if (v == 1) { return *this; } int w = x2 - x1 + 1; int h = y2 - y1 + 1; - w = w / v + (w % v > 0); - h = h / v + (h % v > 0); + w = w / v + static_cast(w % v); + h = h / v + static_cast(h % v); x1 /= v; y1 /= v; x2 = x1 + w - 1; @@ -117,7 +106,7 @@ public: return *this; } -}; + }; private: Type type; @@ -173,7 +162,7 @@ public: } } - SpotBox& operator /=(const int& v) { + SpotBox& operator /=(int v) { if (v == 1) { return *this; } diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc index 1e6ae3238..69ee6b30e 100644 --- a/rtgui/cropwindow.cc +++ b/rtgui/cropwindow.cc @@ -497,7 +497,7 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) cropgl->cropInit (cropHandler.cropParams->x, cropHandler.cropParams->y, cropHandler.cropParams->w, cropHandler.cropParams->h); } else if (iarea->getToolMode () == TMHand) { if (editSubscriber) { - if ((cropgl && cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y) && editSubscriber->getEditingType() == ET_PIPETTE && (bstate & GDK_CONTROL_MASK))) { + if ((cropgl && cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y) && (editSubscriber->getEditingType() == ET_PIPETTE && (bstate & GDK_CONTROL_MASK))) || editSubscriber->getEditingType() == ET_OBJECTS) { needRedraw = editSubscriber->button1Pressed(bstate); if (editSubscriber->isDragging()) { state = SEditDrag1; @@ -1277,7 +1277,10 @@ void CropWindow::updateCursor (int x, int y) } else if (onArea (CropToolBar, x, y)) { newType = CSMove; } else if (iarea->getObject() > -1 && editSubscriber && editSubscriber->getEditingType() == ET_OBJECTS) { - newType = editSubscriber->getCursor(iarea->getObject()); + int cursorX; + int cursorY; + screenCoordToImage (x, y, cursorX, cursorY); + newType = editSubscriber->getCursor(iarea->getObject(), cursorX, cursorY); } else if (onArea (CropResize, x, y)) { newType = CSResizeDiagonal; } else if (tm == TMColorPicker && hoveredPicker) { @@ -1304,7 +1307,10 @@ void CropWindow::updateCursor (int x, int y) } if (objectID > -1) { - newType = editSubscriber->getCursor(objectID); + int cursorX; + int cursorY; + screenCoordToImage (x, y, cursorX, cursorY); + newType = editSubscriber->getCursor(objectID, cursorX, cursorY); } else if (tm == TMHand) { if (onArea (CropObserved, x, y)) { newType = CSMove; @@ -1330,7 +1336,10 @@ void CropWindow::updateCursor (int x, int y) } if (objectID > -1) { - newType = editSubscriber->getCursor(objectID); + int cursorX; + int cursorY; + screenCoordToImage (x, y, cursorX, cursorY); + newType = editSubscriber->getCursor(objectID, cursorX, cursorY); } else { newType = CSArrow; } @@ -1359,6 +1368,16 @@ void CropWindow::updateCursor (int x, int y) newType = CSResizeDiagonal; } else if (state == SDragPicker) { newType = CSMove2D; + } else if (editSubscriber && editSubscriber->getEditingType() == ET_OBJECTS) { + int objectID = iarea->getObject(); + if (objectID > -1) { + int cursorX; + int cursorY; + screenCoordToImage (x, y, cursorX, cursorY); + newType = editSubscriber->getCursor(objectID, cursorX, cursorY); + } else { + newType = CSArrow; + } } if (newType != cursor_type) { diff --git a/rtgui/curveeditor.cc b/rtgui/curveeditor.cc index 528378423..99e371850 100644 --- a/rtgui/curveeditor.cc +++ b/rtgui/curveeditor.cc @@ -490,7 +490,7 @@ bool CurveEditor::drag1(int modifierKey) return false; } -CursorShape CurveEditor::getCursor(int objectID) const +CursorShape CurveEditor::getCursor(int objectID, int xPos, int yPos) const { if (remoteDrag) { return CSResizeHeight; diff --git a/rtgui/curveeditor.h b/rtgui/curveeditor.h index e38d3f205..10963aa1d 100644 --- a/rtgui/curveeditor.h +++ b/rtgui/curveeditor.h @@ -133,7 +133,7 @@ public: bool button1Pressed(int modifierKey) override; bool button1Released() override; bool drag1(int modifierKey) override; - CursorShape getCursor(int objectID) const override; + CursorShape getCursor(int objectID, int xPos, int yPos) const override; }; diff --git a/rtgui/editcallbacks.cc b/rtgui/editcallbacks.cc index d51d672a5..711e56f11 100644 --- a/rtgui/editcallbacks.cc +++ b/rtgui/editcallbacks.cc @@ -172,10 +172,10 @@ void EditDataProvider::setPipetteVal3(float newVal) pipetteVal3 = newVal; } -CursorShape EditDataProvider::getCursor(int objectID) const +CursorShape EditDataProvider::getCursor(int objectID, int xPos, int yPos) const { if (currSubscriber) { - currSubscriber->getCursor(objectID); + currSubscriber->getCursor(objectID, xPos, yPos); } return CSHandOpen; @@ -186,12 +186,12 @@ EditSubscriber* EditDataProvider::getCurrSubscriber() const return currSubscriber; } -EditDataProvider* EditSubscriber::getEditProvider() +EditDataProvider* EditSubscriber::getEditProvider() const { return provider; } -CursorShape EditSubscriber::getCursor(int objectID) const +CursorShape EditSubscriber::getCursor(int objectID, int xPos, int yPos) const { return CSHandOpen; } diff --git a/rtgui/editcallbacks.h b/rtgui/editcallbacks.h index c4003f9ca..0f5f1a0bc 100644 --- a/rtgui/editcallbacks.h +++ b/rtgui/editcallbacks.h @@ -57,7 +57,7 @@ public: virtual ~EditSubscriber () = default; void setEditProvider(EditDataProvider *provider); - EditDataProvider* getEditProvider (); + EditDataProvider* getEditProvider () const; void setEditID(EditUniqueID ID, BufferType buffType); bool isCurrentSubscriber() const; virtual void subscribe(); @@ -70,8 +70,10 @@ public: bool isPicking() const; /// Returns true if something is being picked /** @brief Get the cursor to be displayed when above handles - @param objectID object currently "hovered" */ - virtual CursorShape getCursor (int objectID) const; + @param objectID object currently "hovered" + @param xPos X cursor position in image space + @param yPos Y cursor position in image space */ + virtual CursorShape getCursor (int objectID, int xPos, int yPos) const; /** @brief Triggered when the mouse is moving over an object This method is also triggered when the cursor is moving over the image in ET_PIPETTE mode @@ -188,7 +190,7 @@ public: void setPipetteVal1(float newVal); void setPipetteVal2(float newVal); void setPipetteVal3(float newVal); - virtual CursorShape getCursor(int objectID) const; + virtual CursorShape getCursor(int objectID, int xPos, int yPos) const; int getPipetteRectSize () const; EditSubscriber* getCurrSubscriber() const; virtual void getImageSize (int &w, int&h) = 0; diff --git a/rtgui/editwidgets.cc b/rtgui/editwidgets.cc index 16cd67cd4..1728d1811 100644 --- a/rtgui/editwidgets.cc +++ b/rtgui/editwidgets.cc @@ -20,7 +20,11 @@ #include "editwidgets.h" #include "editbuffer.h" #include "editcallbacks.h" -#include "../rtengine/rt_math.h" + +const std::vector Geometry::dash = {3., 1.5}; + +#define INNERGEOM_OPACITY 1. +#define OUTERGEOM_OPACITY 0.7 RGBColor Geometry::getInnerLineColor () { @@ -65,7 +69,8 @@ RGBColor Geometry::getOuterLineColor () void Circle::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) { - if ((flags & F_VISIBLE) && state != INSENSITIVE) { + double lineWidth = getOuterLineWidth(); + if ((flags & F_VISIBLE) && state != INSENSITIVE && lineWidth > 0. && innerLineWidth > 0.) { RGBColor color; if (flags & F_AUTO_COLOR) { @@ -74,8 +79,9 @@ void Circle::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuffer color = outerLineColor; } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); - cr->set_line_width( getOuterLineWidth() ); + cr->set_source_rgba (color.getR(), color.getG(), color.getB(), OUTERGEOM_OPACITY * rtengine::min(innerLineWidth / 2.f, 1.f)); + cr->set_line_width (lineWidth); + cr->set_line_cap(Cairo::LINE_CAP_ROUND); rtengine::Coord center_ = center; double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius); @@ -105,10 +111,11 @@ void Circle::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuffer color = innerLineColor; } - cr->set_source_rgb(color.getR(), color.getG(), color.getB()); + cr->set_source_rgba (color.getR(), color.getG(), color.getB(), INNERGEOM_OPACITY); } - cr->set_line_width( innerLineWidth ); + cr->set_line_width(innerLineWidth); + cr->set_line_cap(flags & F_DASHED ? Cairo::LINE_CAP_BUTT : Cairo::LINE_CAP_ROUND); rtengine::Coord center_ = center; double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius); @@ -121,9 +128,12 @@ void Circle::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuffer center_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; } - if (filled && state != INSENSITIVE) { - cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI); + if (flags & F_DASHED) { + cr->set_dash(dash, 0.); + } + if (filled) { + cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI); if (innerLineWidth > 0.) { cr->fill_preserve(); cr->stroke(); @@ -132,28 +142,20 @@ void Circle::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuffer } } else if (innerLineWidth > 0.) { cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI); - - if (state == INSENSITIVE) { - std::valarray ds(1); - ds[0] = 4; - cr->set_source_rgba(1.0, 1.0, 1.0, 0.618); - cr->stroke_preserve(); - cr->set_source_rgba(0.0, 0.0, 0.0, 0.618); - cr->set_dash(ds, 0); - cr->stroke(); - ds.resize(0); - cr->set_dash(ds, 0); - } else { - cr->stroke(); - } + cr->stroke(); } - } + + if (flags & F_DASHED) { + cr->unset_dash(); + } +} } void Circle::drawToMOChannel (Cairo::RefPtr &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) { if (flags & F_HOVERABLE) { cr->set_line_width( getMouseOverLineWidth() ); + cr->set_line_cap(Cairo::LINE_CAP_ROUND); rtengine::Coord center_ = center; double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius); @@ -188,7 +190,8 @@ void Circle::drawToMOChannel (Cairo::RefPtr &cr, unsigned short void Line::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) { - if ((flags & F_VISIBLE) && state != INSENSITIVE) { + double lineWidth = getOuterLineWidth(); + if ((flags & F_VISIBLE) && state != INSENSITIVE && lineWidth > 0. && innerLineWidth > 0.) { RGBColor color; if (flags & F_AUTO_COLOR) { @@ -197,8 +200,9 @@ void Line::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuffer * color = outerLineColor; } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); - cr->set_line_width( getOuterLineWidth() ); + cr->set_source_rgba (color.getR(), color.getG(), color.getB(), OUTERGEOM_OPACITY * rtengine::min(innerLineWidth / 2.f, 1.f)); + cr->set_line_width (lineWidth); + cr->set_line_cap(Cairo::LINE_CAP_ROUND); rtengine::Coord begin_ = begin; rtengine::Coord end_ = end; @@ -232,10 +236,11 @@ void Line::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuffer * color = innerLineColor; } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_source_rgba (color.getR(), color.getG(), color.getB(), INNERGEOM_OPACITY); } cr->set_line_width(innerLineWidth); + cr->set_line_cap(flags & F_DASHED ? Cairo::LINE_CAP_BUTT : Cairo::LINE_CAP_ROUND); rtengine::Coord begin_ = begin; rtengine::Coord end_ = end; @@ -251,21 +256,16 @@ void Line::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuffer * end_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; } + if (flags & F_DASHED) { + cr->set_dash(dash, 0.); + } + cr->move_to(begin_.x + 0.5, begin_.y + 0.5); cr->line_to(end_.x + 0.5, end_.y + 0.5); + cr->stroke(); - if (state == INSENSITIVE) { - std::valarray ds(1); - ds[0] = 4; - cr->set_source_rgba(1.0, 1.0, 1.0, 0.618); - cr->stroke_preserve(); - cr->set_source_rgba(0.0, 0.0, 0.0, 0.618); - cr->set_dash(ds, 0); - cr->stroke(); - ds.resize(0); - cr->set_dash(ds, 0); - } else { - cr->stroke(); + if (flags & F_DASHED) { + cr->unset_dash(); } } } @@ -274,6 +274,7 @@ void Line::drawToMOChannel(Cairo::RefPtr &cr, unsigned short id, { if (flags & F_HOVERABLE) { cr->set_line_width( getMouseOverLineWidth() ); + cr->set_line_cap(Cairo::LINE_CAP_ROUND); rtengine::Coord begin_ = begin; rtengine::Coord end_ = end; @@ -302,7 +303,8 @@ void Line::drawToMOChannel(Cairo::RefPtr &cr, unsigned short id, void Polyline::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) { - if ((flags & F_VISIBLE) && state != INSENSITIVE && points.size() > 1) { + double lineWidth = getOuterLineWidth(); + if ((flags & F_VISIBLE) && state != INSENSITIVE && points.size() > 1 && lineWidth > 0.) { RGBColor color; if (flags & F_AUTO_COLOR) { @@ -311,8 +313,10 @@ void Polyline::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuff color = outerLineColor; } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); - cr->set_line_width( getOuterLineWidth() ); + cr->set_source_rgba (color.getR(), color.getG(), color.getB(), OUTERGEOM_OPACITY * rtengine::min(innerLineWidth / 2.f, 1.f)); + cr->set_line_width (lineWidth); + cr->set_line_cap(Cairo::LINE_CAP_ROUND); + cr->set_line_join(Cairo::LINE_JOIN_ROUND); rtengine::Coord currPos; @@ -355,10 +359,16 @@ void Polyline::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuff color = innerLineColor; } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_source_rgba (color.getR(), color.getG(), color.getB(), INNERGEOM_OPACITY); } - cr->set_line_width( innerLineWidth ); + cr->set_line_width(innerLineWidth); + cr->set_line_cap(flags & F_DASHED ? Cairo::LINE_CAP_BUTT : Cairo::LINE_CAP_ROUND); + cr->set_line_join(Cairo::LINE_JOIN_ROUND); + + if (flags & F_DASHED) { + cr->set_dash(dash, 0.); + } if (filled && state != INSENSITIVE) { rtengine::Coord currPos; @@ -407,20 +417,11 @@ void Polyline::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuff cr->line_to(currPos.x + 0.5, currPos.y + 0.5); } } + cr->stroke(); + } - if (state == INSENSITIVE) { - std::valarray ds(1); - ds[0] = 4; - cr->set_source_rgba(1.0, 1.0, 1.0, 0.618); - cr->stroke_preserve(); - cr->set_source_rgba(0.0, 0.0, 0.0, 0.618); - cr->set_dash(ds, 0); - cr->stroke(); - ds.resize(0); - cr->set_dash(ds, 0); - } else { - cr->stroke(); - } + if (flags & F_DASHED) { + cr->unset_dash(); } } } @@ -437,8 +438,11 @@ void Polyline::drawToMOChannel (Cairo::RefPtr &cr, unsigned shor cr->set_source_rgba (0., 0., 0., (id + 1) / 65535.); } + cr->set_line_width( getMouseOverLineWidth() ); + cr->set_line_cap(Cairo::LINE_CAP_ROUND); + cr->set_line_join(Cairo::LINE_JOIN_ROUND); + for (unsigned int i = 0; i < points.size(); ++i) { - cr->set_line_width( getMouseOverLineWidth() ); currPos = points.at(i); if (datum == IMAGE) { @@ -495,7 +499,8 @@ void Rectangle::setXYXY(rtengine::Coord topLeft, rtengine::Coord bottomRight) void Rectangle::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) { - if ((flags & F_VISIBLE) && state != INSENSITIVE) { + double lineWidth = getOuterLineWidth(); + if ((flags & F_VISIBLE) && state != INSENSITIVE && lineWidth > 0. && innerLineWidth > 0.) { RGBColor color; if (flags & F_AUTO_COLOR) { @@ -504,8 +509,9 @@ void Rectangle::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuf color = outerLineColor; } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); - cr->set_line_width( getOuterLineWidth() ); + cr->set_source_rgba (color.getR(), color.getG(), color.getB(), OUTERGEOM_OPACITY * rtengine::min(innerLineWidth / 2.f, 1.f)); + cr->set_line_width (lineWidth); + cr->set_line_join(Cairo::LINE_JOIN_BEVEL); rtengine::Coord tl, br; @@ -548,10 +554,11 @@ void Rectangle::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuf color = innerLineColor; } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_source_rgba (color.getR(), color.getG(), color.getB(), INNERGEOM_OPACITY); } - cr->set_line_width( innerLineWidth ); + cr->set_line_width(innerLineWidth); + cr->set_line_join(Cairo::LINE_JOIN_BEVEL); rtengine::Coord tl, br; @@ -571,7 +578,11 @@ void Rectangle::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuf br = bottomRight + objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; } - if (filled && state != INSENSITIVE) { + if (flags & F_DASHED) { + cr->set_dash(dash, 0.); + } + + if (filled) { cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y); if (innerLineWidth > 0.) { @@ -582,20 +593,11 @@ void Rectangle::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuf } } else if (innerLineWidth > 0.) { cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y); + cr->stroke(); + } - if (state == INSENSITIVE) { - std::valarray ds(1); - ds[0] = 4; - cr->set_source_rgba(1.0, 1.0, 1.0, 0.618); - cr->stroke_preserve(); - cr->set_source_rgba(0.0, 0.0, 0.0, 0.618); - cr->set_dash(ds, 0); - cr->stroke(); - ds.resize(0); - cr->set_dash(ds, 0); - } else { - cr->stroke(); - } + if (flags & F_DASHED) { + cr->unset_dash(); } } } @@ -604,6 +606,7 @@ void Rectangle::drawToMOChannel(Cairo::RefPtr &cr, unsigned shor { if (flags & F_HOVERABLE) { cr->set_line_width( getMouseOverLineWidth() ); + cr->set_line_join(Cairo::LINE_JOIN_ROUND); rtengine::Coord tl, br; diff --git a/rtgui/editwidgets.h b/rtgui/editwidgets.h index 1ae185f7a..0282e2bb1 100644 --- a/rtgui/editwidgets.h +++ b/rtgui/editwidgets.h @@ -24,6 +24,7 @@ #include "editbuffer.h" #include "editcoordsys.h" #include "../rtengine/coord.h" +#include "../rtengine/rt_math.h" class ObjectMOBuffer; @@ -208,6 +209,8 @@ public: F_VISIBLE = 1 << 0, /// true if the geometry have to be drawn on the visible layer F_HOVERABLE = 1 << 1, /// true if the geometry have to be drawn on the "mouse over" layer F_AUTO_COLOR = 1 << 2, /// true if the color depend on the state value, not the color field above + F_DASHED = 1 << 3, /// true if the geometry have to be drawn as a dash line + // (TODO: add a F_LARGE_DASH to have two different dash size ?) }; /// @brief Key point of the image's rectangle that is used to locate the icon copy to the target point: @@ -224,6 +227,7 @@ public: }; protected: + static const std::vector dash; RGBColor innerLineColor; RGBColor outerLineColor; short flags; @@ -249,6 +253,8 @@ public: void setVisible (bool visible); bool isHoverable (); void setHoverable (bool visible); + bool isDashed (); + void setDashed (bool dashed); // setActive will enable/disable the visible and hoverable flags in one shot! @@ -427,7 +433,7 @@ inline void Geometry::setOuterLineColor (char r, char g, char b) { } inline double Geometry::getMouseOverLineWidth () { - return getOuterLineWidth () + 2.; + return rtengine::max(double(innerLineWidth), 1.) + 2.; } inline void Geometry::setAutoColor (bool aColor) { @@ -462,6 +468,18 @@ inline void Geometry::setHoverable (bool hoverable) { } } +inline bool Geometry::isDashed () { + return flags & F_DASHED; +} + +inline void Geometry::setDashed (bool dashed) { + if (dashed) { + flags |= F_DASHED; + } else { + flags &= ~F_DASHED; + } +} + inline void Geometry::setActive (bool active) { if (active) { flags |= (F_VISIBLE | F_HOVERABLE); diff --git a/rtgui/filmnegative.cc b/rtgui/filmnegative.cc index 6e93b2364..d490be488 100644 --- a/rtgui/filmnegative.cc +++ b/rtgui/filmnegative.cc @@ -225,7 +225,7 @@ void FilmNegative::setEditProvider(EditDataProvider* provider) EditSubscriber::setEditProvider(provider); } -CursorShape FilmNegative::getCursor(int objectID) const +CursorShape FilmNegative::getCursor(int objectID, int xPos, int yPos) const { return CSSpotWB; } diff --git a/rtgui/filmnegative.h b/rtgui/filmnegative.h index a1efdc455..46993dba5 100644 --- a/rtgui/filmnegative.h +++ b/rtgui/filmnegative.h @@ -59,7 +59,7 @@ public: void setEditProvider(EditDataProvider* provider) override; // EditSubscriber interface - CursorShape getCursor(int objectID) const override; + CursorShape getCursor(int objectID, int xPos, int yPos) const override; bool mouseOver(int modifierKey) override; bool button1Pressed(int modifierKey) override; bool button1Released() override; diff --git a/rtgui/gradient.cc b/rtgui/gradient.cc index 4f389e112..fda961912 100644 --- a/rtgui/gradient.cc +++ b/rtgui/gradient.cc @@ -327,7 +327,7 @@ void Gradient::editToggled () } } -CursorShape Gradient::getCursor(int objectID) const +CursorShape Gradient::getCursor(int objectID, int xPos, int yPos) const { switch (objectID) { case (0): diff --git a/rtgui/gradient.h b/rtgui/gradient.h index 05a267a0d..3b8a18f97 100644 --- a/rtgui/gradient.h +++ b/rtgui/gradient.h @@ -51,7 +51,7 @@ public: void setEditProvider (EditDataProvider* provider) override; // EditSubscriber interface - CursorShape getCursor(int objectID) const override; + CursorShape getCursor(int objectID, int xPos, int yPos) const override; bool mouseOver(int modifierKey) override; bool button1Pressed(int modifierKey) override; bool button1Released() override; diff --git a/rtgui/spot.cc b/rtgui/spot.cc index 6392b95ab..5fb1de8c9 100644 --- a/rtgui/spot.cc +++ b/rtgui/spot.cc @@ -32,8 +32,14 @@ using namespace rtengine::procparams; #define STATIC_VISIBLE_OBJ_NBR 6 #define STATIC_MO_OBJ_NBR 6 -Spot::Spot() : FoldableToolPanel (this, "spot", M ("TP_SPOT_LABEL"), true, true), EditSubscriber (ET_OBJECTS), lastObject (-1), activeSpot (-1), - sourceIcon ("spot-normal.png", "spot-active.png", "spot-active.png", "spot-prelight.png", "", Geometry::DP_CENTERCENTER), editedCheckBox (nullptr) +Spot::Spot() : + FoldableToolPanel(this, "spot", M ("TP_SPOT_LABEL"), true, true), + EditSubscriber(ET_OBJECTS), + draggedSide(DraggedSide::NONE), + lastObject(-1), + activeSpot(-1), + sourceIcon("spot-normal.png", "spot-active.png", "spot-prelight.png", "", "", Geometry::DP_CENTERCENTER), + editedCheckBox(nullptr) { countLabel = Gtk::manage (new Gtk::Label (Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), 0))); @@ -61,6 +67,7 @@ Spot::Spot() : FoldableToolPanel (this, "spot", M ("TP_SPOT_LABEL"), true, true) sourceCircle.datum = Geometry::IMAGE; sourceCircle.setActive (false); sourceCircle.radiusInImageSpace = true; + sourceCircle.setDashed(true); sourceMODisc.datum = Geometry::IMAGE; sourceMODisc.setActive (false); sourceMODisc.radiusInImageSpace = true; @@ -77,9 +84,12 @@ Spot::Spot() : FoldableToolPanel (this, "spot", M ("TP_SPOT_LABEL"), true, true) sourceFeatherCircle.datum = Geometry::IMAGE; sourceFeatherCircle.setActive (false); sourceFeatherCircle.radiusInImageSpace = true; + sourceFeatherCircle.setDashed(true); + sourceFeatherCircle.innerLineWidth = 0.7; targetFeatherCircle.datum = Geometry::IMAGE; targetFeatherCircle.setActive (false); targetFeatherCircle.radiusInImageSpace = true; + targetFeatherCircle.innerLineWidth = 0.7; link.datum = Geometry::IMAGE; link.setActive (false); @@ -241,6 +251,10 @@ Geometry* Spot::getVisibleGeometryFromMO (int MOID) return &sourceIcon; } + if (MOID > STATIC_MO_OBJ_NBR) { + return EditSubscriber::visibleGeometry.at(MOID - STATIC_MO_OBJ_NBR); + } + return EditSubscriber::mouseOverGeometry.at (MOID); } @@ -259,10 +273,12 @@ void Spot::createGeometry () delete EditSubscriber::visibleGeometry.at (i); } - size_t i = 0, j = 0; + // mouse over geometry starts with the static geometry, then the spot's icon geometry EditSubscriber::mouseOverGeometry.resize (STATIC_MO_OBJ_NBR + nbrEntry); + // visible geometry starts with the spot's icon geometry, then the static geometry EditSubscriber::visibleGeometry.resize (nbrEntry + STATIC_VISIBLE_OBJ_NBR); + size_t i = 0, j = 0; EditSubscriber::mouseOverGeometry.at (i++) = &targetMODisc; // STATIC_MO_OBJ_NBR + 0 EditSubscriber::mouseOverGeometry.at (i++) = &sourceMODisc; // STATIC_MO_OBJ_NBR + 1 EditSubscriber::mouseOverGeometry.at (i++) = &targetCircle; // STATIC_MO_OBJ_NBR + 2 @@ -351,6 +367,10 @@ void Spot::updateGeometry() } else { link.setActive (false); } + + sourceCircle.setVisible(draggedSide != DraggedSide::SOURCE); + targetCircle.setVisible(draggedSide != DraggedSide::TARGET); + link.setVisible(draggedSide == DraggedSide::NONE); } else { targetCircle.setActive (false); targetMODisc.setActive (false); @@ -432,10 +452,37 @@ void Spot::deleteSelectedEntry() } } -// TODO -CursorShape Spot::getCursor (int objectID) const +CursorShape Spot::getCursor (int objectID, int xPos, int yPos) const { - return CSHandOpen; + const EditDataProvider* editProvider = getEditProvider(); + if (editProvider) { + if (draggedSide != DraggedSide::NONE) { + return CSEmpty; + } + + int object = editProvider->getObject(); + if (object == 0 || object == 1) { + return CSMove2D; + } + if (object >= 2 || object <= 5) { + Coord delta(Coord(xPos, yPos) - ((object == 3 || object == 5) ? spots.at(activeSpot).sourcePos : spots.at(activeSpot).targetPos)); + PolarCoord polarPos(delta); + if (polarPos.angle < 0.) { + polarPos.angle += 180.; + } + if (polarPos.angle < 22.5 || polarPos.angle >= 157.5) { + return CSMove1DH; + } + if (polarPos.angle < 67.5) { + return CSResizeBottomRight; + } + if (polarPos.angle < 112.5) { + return CSMove1DV; + } + return CSResizeBottomLeft; + } + } + return CSCrosshair; } bool Spot::mouseOver (int modifierKey) @@ -494,10 +541,12 @@ bool Spot::button1Pressed (int modifierKey) if (editProvider) { if (lastObject == -1 && (modifierKey & GDK_CONTROL_MASK)) { + draggedSide = DraggedSide::SOURCE; addNewEntry(); EditSubscriber::action = EditSubscriber::Action::DRAGGING; return true; } else if (lastObject > -1) { + draggedSide = lastObject == 0 ? DraggedSide::TARGET : lastObject == 1 ? DraggedSide::SOURCE : DraggedSide::NONE; getVisibleGeometryFromMO (lastObject)->state = Geometry::DRAGGED; EditSubscriber::action = EditSubscriber::Action::DRAGGING; return true; @@ -519,6 +568,7 @@ bool Spot::button1Released() loGeom->state = Geometry::PRELIGHT; EditSubscriber::action = EditSubscriber::Action::NONE; + draggedSide = DraggedSide::NONE; updateGeometry(); return true; } @@ -552,6 +602,7 @@ bool Spot::button3Pressed (int modifierKey) lastObject = 1; // sourceMODisc sourceIcon.state = Geometry::DRAGGED; EditSubscriber::action = EditSubscriber::Action::DRAGGING; + draggedSide = DraggedSide::SOURCE; return true; } else if (! (modifierKey & (GDK_SHIFT_MASK | GDK_SHIFT_MASK))) { EditSubscriber::action = EditSubscriber::Action::PICKING; @@ -571,6 +622,7 @@ bool Spot::button3Released() lastObject = -1; sourceIcon.state = Geometry::ACTIVE; + draggedSide = DraggedSide::NONE; updateGeometry(); EditSubscriber::action = EditSubscriber::Action::NONE; return true; diff --git a/rtgui/spot.h b/rtgui/spot.h index 7604d7846..a29eb582c 100644 --- a/rtgui/spot.h +++ b/rtgui/spot.h @@ -57,6 +57,13 @@ class Spot : public ToolParamBlock, public FoldableToolPanel, public rtengine::T { private: + enum class DraggedSide { + NONE, + SOURCE, + TARGET + }; + + DraggedSide draggedSide; // tells which of source or target is being dragged int lastObject; // current object that is hovered int activeSpot; // currently active spot, being edited std::vector spots; // list of edited spots @@ -103,7 +110,7 @@ public: void setBatchMode (bool batchMode) override; // EditSubscriber interface - CursorShape getCursor (int objectID) const override; + CursorShape getCursor (int objectID, int xPos, int yPos) const override; bool mouseOver (int modifierKey) override; bool button1Pressed (int modifierKey) override; bool button1Released () override;