1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date        : 2010-09-09
7 * Description : tag region frame
8 *
9 * Copyright (C) 2007      by Aurelien Gateau <agateau at kde dot org>
10 * Copyright (C) 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
11 *
12 * This program is free software; you can redistribute it
13 * and/or modify it under the terms of the GNU General
14 * Public License as published by the Free Software Foundation;
15 * either version 2, or (at your option)
16 * any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 * GNU General Public License for more details.
22 *
23 * ============================================================ */
24 
25 #include "regionframeitem.h"
26 
27 // C++ includes
28 
29 #include <cmath>
30 
31 // Qt includes
32 
33 #include <QApplication>
34 #include <QFlags>
35 #include <QGraphicsProxyWidget>
36 #include <QGraphicsScene>
37 #include <QGraphicsSceneMouseEvent>
38 #include <QHBoxLayout>
39 #include <QPainter>
40 #include <QPropertyAnimation>
41 #include <QRect>
42 #include <QTimer>
43 #include <QToolButton>
44 
45 // Local includes
46 
47 #include "graphicsdimgitem.h"
48 #include "itemvisibilitycontroller.h"
49 
50 namespace
51 {
52 static const int HANDLE_SIZE = 15;
53 }
54 
55 namespace Digikam
56 {
57 
58 enum CropHandleFlag
59 {
60     CH_None,
61     CH_Top         = 1,
62     CH_Left        = 2,
63     CH_Right       = 4,
64     CH_Bottom      = 8,
65     CH_TopLeft     = CH_Top    | CH_Left,
66     CH_BottomLeft  = CH_Bottom | CH_Left,
67     CH_TopRight    = CH_Top    | CH_Right,
68     CH_BottomRight = CH_Bottom | CH_Right,
69     CH_Content     = 16
70 };
71 
72 enum HudSide
73 {
74     HS_None         = 0, ///< Special value used to avoid initial animation
75     HS_Top          = 1,
76     HS_Bottom       = 2,
77     HS_Inside       = 4,
78     HS_TopInside    = HS_Top    | HS_Inside,
79     HS_BottomInside = HS_Bottom | HS_Inside
80 };
81 
82 typedef QPair<QPointF, HudSide> OptimalPosition;
83 
84 Q_DECLARE_FLAGS(CropHandle, CropHandleFlag)
85 
86 } // namespace Digikam
87 
88 Q_DECLARE_OPERATORS_FOR_FLAGS(Digikam::CropHandle)
89 
90 // --------------------------------------------------------------------------------
91 
92 namespace Digikam
93 {
94 
95 class Q_DECL_HIDDEN RegionFrameItem::Private
96 {
97 public:
98 
99     explicit Private(RegionFrameItem* const q);
100 
101     QRectF handleRect(CropHandle handle) const;
102     CropHandle handleAt(const QPointF& pos) const;
103     void updateCursor(CropHandle handle, bool buttonDown);
104     QRectF keepRectInsideImage(const QRectF& rect, bool moving = true) const;
105     OptimalPosition computeOptimalHudWidgetPosition() const;
106     void updateHudWidgetPosition();
107 
108 public:
109 
110     RegionFrameItem* const q;
111 
112     HudSide                hudSide;
113     QRectF                 viewportRect;
114     QList<CropHandle>      cropHandleList;
115     CropHandle             movingHandle;
116     QPointF                lastMouseMovePos;
117     double                 fixedRatio;
118     QGraphicsWidget*       hudWidget;
119 
120     RegionFrameItem::Flags flags;
121 
122     AnimatedVisibility*    resizeHandleVisibility;
123     qreal                  hoverAnimationOpacity;
124     QTimer*                hudTimer;
125     QPointF                hudEndPos;
126 
127     const int              HUD_TIMER_MAX_PIXELS_PER_UPDATE;
128     const int              HUD_TIMER_ANIMATION_INTERVAL;
129 };
130 
Private(RegionFrameItem * const qq)131 RegionFrameItem::Private::Private(RegionFrameItem* const qq)
132     : q                                 (qq),
133       hudSide                           (HS_None),
134       movingHandle                      (CH_None),
135       fixedRatio                        (0),
136       hudWidget                         (nullptr),
137       flags                             (NoFlags),
138       resizeHandleVisibility            (nullptr),
139       hoverAnimationOpacity             (1.0),
140       hudTimer                          (nullptr),
141       HUD_TIMER_MAX_PIXELS_PER_UPDATE   (20),
142       HUD_TIMER_ANIMATION_INTERVAL      (20)
143 {
144 
145     cropHandleList << CH_Left       << CH_Right << CH_Top << CH_Bottom
146                    << CH_TopLeft    << CH_TopRight
147                    << CH_BottomLeft << CH_BottomRight;
148 }
149 
handleRect(CropHandle handle) const150 QRectF RegionFrameItem::Private::handleRect(CropHandle handle) const
151 {
152     QSizeF size = q->boundingRect().size();
153     double left, top;
154 
155     if      (handle & CH_Top)
156     {
157         top = 0;
158     }
159     else if (handle & CH_Bottom)
160     {
161         top = size.height() - HANDLE_SIZE;
162     }
163     else
164     {
165         top = (size.height() - HANDLE_SIZE) / 2;
166     }
167 
168     if      (handle & CH_Left)
169     {
170         left = 0;
171     }
172     else if (handle & CH_Right)
173     {
174         left = size.width() - HANDLE_SIZE;
175     }
176     else
177     {
178         left = (size.width() - HANDLE_SIZE) / 2;
179     }
180 
181     return QRectF(left, top, HANDLE_SIZE, HANDLE_SIZE);
182 }
183 
handleAt(const QPointF & pos) const184 CropHandle RegionFrameItem::Private::handleAt(const QPointF& pos) const
185 {
186     if (flags & ShowResizeHandles)
187     {
188         foreach (const CropHandle& handle, cropHandleList)
189         {
190             QRectF rect = handleRect(handle);
191 
192             if (rect.contains(pos))
193             {
194                 return handle;
195             }
196         }
197     }
198 
199     if (flags & MoveByDrag)
200     {
201         if (q->boundingRect().contains(pos))
202         {
203             return CH_Content;
204         }
205     }
206 
207 
208     return CH_None;
209 }
210 
updateCursor(CropHandle handle,bool buttonDown)211 void RegionFrameItem::Private::updateCursor(CropHandle handle, bool buttonDown)
212 {
213     Qt::CursorShape shape;
214 
215     switch (handle)
216     {
217         case CH_TopLeft:
218         case CH_BottomRight:
219             shape = Qt::SizeFDiagCursor;
220             break;
221 
222         case CH_TopRight:
223         case CH_BottomLeft:
224             shape = Qt::SizeBDiagCursor;
225             break;
226 
227         case CH_Left:
228         case CH_Right:
229             shape = Qt::SizeHorCursor;
230             break;
231 
232         case CH_Top:
233         case CH_Bottom:
234             shape = Qt::SizeVerCursor;
235             break;
236 
237         case CH_Content:
238             shape = buttonDown ? Qt::ClosedHandCursor : Qt::OpenHandCursor;
239             break;
240 
241         default:
242             shape = Qt::ArrowCursor;
243             break;
244     }
245 
246     q->setCursor(shape);
247 }
248 
keepRectInsideImage(const QRectF & rect,bool moving) const249 QRectF RegionFrameItem::Private::keepRectInsideImage(const QRectF& rect, bool moving) const
250 {
251     QRectF r(rect);
252     const QSizeF imageSize = q->parentDImgItem()->boundingRect().size();
253 
254     if ((r.width() > imageSize.width()) || (r.height() > imageSize.height()))
255     {
256         // This can happen when the crop ratio changes
257 
258         QSizeF rectSize = r.size();
259         rectSize.scale(imageSize, Qt::KeepAspectRatio);
260         r.setSize(rectSize);
261     }
262 
263     if      (r.right() > imageSize.width())
264     {
265         moving ? r.moveRight(imageSize.width()) : r.setRight(imageSize.width());
266     }
267     else if (r.left() < 0)
268     {
269         moving ? r.moveLeft(0) : r.setLeft(0);
270     }
271 
272     if      (r.bottom() > imageSize.height())
273     {
274         moving ? r.moveBottom(imageSize.height()) : r.setBottom(imageSize.height());
275     }
276     else if (r.top() < 0)
277     {
278         moving ? r.moveTop(0) : r.setTop(0);
279     }
280 
281     return r;
282 }
283 
computeOptimalHudWidgetPosition() const284 OptimalPosition RegionFrameItem::Private::computeOptimalHudWidgetPosition() const
285 {
286     const QRectF visibleSceneRect = viewportRect.isValid() ? viewportRect : q->scene()->sceneRect();
287     const QRectF rect             = q->sceneBoundingRect();
288 
289     const int margin              = HANDLE_SIZE;
290     const int hudHeight           = hudWidget->boundingRect().height();
291     const QRectF hudMaxRect       = visibleSceneRect.adjusted(0, 0, 0, -hudHeight);
292 
293     OptimalPosition ret;
294 
295     // Compute preferred and fallback positions. Preferred is outside rect
296     // on the same side, fallback is outside on the other side.
297 
298     OptimalPosition preferred     = OptimalPosition(QPointF(rect.left(), rect.bottom() + margin),          HS_Bottom);
299     OptimalPosition fallback      = OptimalPosition(QPointF(rect.left(), rect.top() - margin - hudHeight), HS_Top);
300 
301     if (hudSide & HS_Top)
302     {
303         std::swap(preferred, fallback);
304     }
305 
306     // Check if a position outside rect fits
307 
308     if (hudMaxRect.contains(preferred.first))
309     {
310         ret = preferred;
311     }
312     else if (hudMaxRect.contains(fallback.first))
313     {
314         ret= fallback;
315     }
316     else
317     {
318         // Does not fit outside, use a position inside rect
319 
320         QPoint pos;
321 
322         if (hudSide & HS_Top)
323         {
324             pos = QPoint(rect.left() + margin, rect.top() + margin);
325         }
326         else
327         {
328             pos = QPoint(rect.left() + margin, rect.bottom() - margin - hudHeight);
329         }
330 
331         ret = OptimalPosition(pos, HudSide(hudSide | HS_Inside));
332     }
333 
334     // Ensure it's always fully visible
335 
336     ret.first.rx() = qMin(ret.first.rx(), hudMaxRect.width() - hudWidget->boundingRect().width());
337 
338     // map from scene to item coordinates
339 
340     ret.first      = q->mapFromScene(ret.first);
341 
342     return ret;
343 }
344 
updateHudWidgetPosition()345 void RegionFrameItem::Private::updateHudWidgetPosition()
346 {
347     if (!hudWidget || !q->scene())
348     {
349         return;
350     }
351 
352     OptimalPosition result = computeOptimalHudWidgetPosition();
353 
354     if ((result.first == hudWidget->pos()) && (result.second == hudSide))
355     {
356         return;
357     }
358 
359     if (hudSide == HS_None)
360     {
361         hudSide = result.second;
362     }
363 
364     if ((hudSide == result.second) && !hudTimer->isActive())
365     {
366         // Not changing side and not in an animation, move directly the hud
367         // to the final position to avoid lagging effect
368 
369         hudWidget->setPos(result.first);
370     }
371     else
372     {
373         hudEndPos = result.first;
374         hudSide   = result.second;
375 
376         if (!hudTimer->isActive())
377         {
378             hudTimer->start();
379         }
380     }
381 }
382 
383 // ---------------------------------------------------------------------------------------
384 
RegionFrameItem(QGraphicsItem * const item)385 RegionFrameItem::RegionFrameItem(QGraphicsItem* const item)
386     : DImgChildItem(item),
387       d            (new Private(this))
388 {
389     d->resizeHandleVisibility = new AnimatedVisibility(this);
390     d->resizeHandleVisibility->controller()->setShallBeShown(false);
391 
392     connect(d->resizeHandleVisibility, SIGNAL(visibleChanged()),
393             this, SLOT(slotUpdate()));
394 
395     connect(d->resizeHandleVisibility, SIGNAL(opacityChanged()),
396             this, SLOT(slotUpdate()));
397 
398     d->hudTimer = new QTimer(this);
399     d->hudTimer->setInterval(d->HUD_TIMER_ANIMATION_INTERVAL);
400 
401     connect(d->hudTimer, SIGNAL(timeout()),
402             this, SLOT(moveHudWidget()));
403 
404     connect(this, SIGNAL(positionChanged()),
405             this, SLOT(slotPosChanged()));
406 
407     connect(this, SIGNAL(sizeChanged()),
408             this, SLOT(slotSizeChanged()));
409 
410     setFlags(GeometryEditable);
411 
412     d->updateHudWidgetPosition();
413 }
414 
~RegionFrameItem()415 RegionFrameItem::~RegionFrameItem()
416 {
417     if (d->hudWidget)
418     {
419         // See bug #359196: hide or close the QGraphicsWidget before delete it. Possible Qt bug?
420 
421         d->hudWidget->hide();
422         delete d->hudWidget;
423     }
424 
425     delete d;
426 }
427 
setHudWidget(QWidget * const widget,Qt::WindowFlags wFlags)428 void RegionFrameItem::setHudWidget(QWidget* const widget, Qt::WindowFlags wFlags)
429 {
430     QGraphicsProxyWidget* const proxy = new QGraphicsProxyWidget(nullptr, wFlags);
431 
432     /*
433      * This is utterly undocumented magic. If you add a normal widget directly,
434      * with transparent parts (round corners), you will have ugly color in the corners.
435      * If you set WA_TranslucentBackground on the widget directly, a lot of the
436      * painting and stylesheets is broken. Like this, with an extra container, it seems to work.
437      */
438 
439     QWidget* const container  = new QWidget;
440     container->setAttribute(Qt::WA_TranslucentBackground);
441     QHBoxLayout* const layout = new QHBoxLayout;
442     layout->setSizeConstraint(QLayout::SetFixedSize);
443     layout->setContentsMargins(QMargins());
444     layout->setSpacing(0);
445     layout->addWidget(widget);
446     container->setLayout(layout);
447     proxy->setWidget(container);
448 
449     // Reset fixed sizes wrongly copied by setWidget onto the QGraphicsWidget
450 
451     proxy->setMinimumSize(QSizeF());
452     proxy->setMaximumSize(QSizeF());
453 
454     setHudWidget(proxy);
455 }
456 
setHudWidget(QGraphicsWidget * const hudWidget)457 void RegionFrameItem::setHudWidget(QGraphicsWidget* const hudWidget)
458 {
459     if (d->hudWidget == hudWidget)
460     {
461         return;
462     }
463 
464     if (d->hudWidget)
465     {
466         d->hudWidget->hide();
467         delete d->hudWidget;
468     }
469 
470     d->hudWidget = hudWidget;
471 
472     if (d->hudWidget)
473     {
474         d->hudWidget->setParentItem(this);
475         d->hudWidget->installEventFilter(this);
476         d->updateHudWidgetPosition();
477     }
478 }
479 
hudWidget() const480 QGraphicsWidget* RegionFrameItem::hudWidget() const
481 {
482     return d->hudWidget;
483 }
484 
setFlags(Flags flags)485 void RegionFrameItem::setFlags(Flags flags)
486 {
487     if (d->flags == flags)
488     {
489         return;
490     }
491 
492     d->flags = flags;
493     update();
494     setAcceptHoverEvents(d->flags & GeometryEditable);
495     d->resizeHandleVisibility->controller()->setShallBeShown(d->flags & ShowResizeHandles);
496 
497     // ensure cursor is reset
498 
499     CropHandle handle = d->handleAt(QCursor::pos());
500     d->updateCursor(handle, false/* buttonDown*/);
501 }
502 
changeFlags(Flags flags,bool addOrRemove)503 void RegionFrameItem::changeFlags(Flags flags, bool addOrRemove)
504 {
505     if (addOrRemove)
506     {
507         setFlags(d->flags | flags);
508     }
509     else
510     {
511         setFlags(d->flags & ~flags);
512     }
513 }
514 
setHudWidgetVisible(bool visible)515 void RegionFrameItem::setHudWidgetVisible(bool visible)
516 {
517     if (d->hudWidget)
518     {
519         d->hudWidget->setVisible(visible);
520     }
521 }
522 
flags() const523 RegionFrameItem::Flags RegionFrameItem::flags() const
524 {
525     return d->flags;
526 }
527 
setFixedRatio(double ratio)528 void RegionFrameItem::setFixedRatio(double ratio)
529 {
530     d->fixedRatio = ratio;
531 }
532 
slotSizeChanged()533 void RegionFrameItem::slotSizeChanged()
534 {
535     d->updateHudWidgetPosition();
536 }
537 
slotPosChanged()538 void RegionFrameItem::slotPosChanged()
539 {
540     d->updateHudWidgetPosition();
541 }
542 
hudSizeChanged()543 void RegionFrameItem::hudSizeChanged()
544 {
545     d->updateHudWidgetPosition();
546 }
547 
setViewportRect(const QRectF & rect)548 void RegionFrameItem::setViewportRect(const QRectF& rect)
549 {
550     d->viewportRect = rect;
551     d->updateHudWidgetPosition();
552 }
553 
boundingRect() const554 QRectF RegionFrameItem::boundingRect() const
555 {
556     return DImgChildItem::boundingRect();   //.adjusted(-1, -1, 1, 1);
557 }
558 
paint(QPainter * painter,const QStyleOptionGraphicsItem *,QWidget *)559 void RegionFrameItem::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*)
560 {
561 /*
562     QRect rect                      = d->viewportCropRect();
563     QRect imageRect                 = imageView()->rect();
564     static const QColor outerColor  = QColor::fromHsvF(0, 0, 0, 0.5);
565     QRegion outerRegion             = QRegion(imageRect) - QRegion(rect);
566 
567     foreach (const QRect& outerRect, outerRegion.rects())
568     {
569         painter->fillRect(outerRect, outerColor);
570     }
571 */
572 
573     const QColor borderColor = QColor::fromHsvF(0, 0, 1.0, 0.66 + 0.34 * d->hoverAnimationOpacity);
574     const QColor fillColor   = QColor::fromHsvF(0, 0, 0.75, 0.66);
575 
576     // will paint to the left and bottom of logical coordinates
577 
578     QRectF drawRect          = boundingRect();
579 
580     painter->setPen(borderColor);
581     painter->drawRect(drawRect);
582 
583     if (d->resizeHandleVisibility->isVisible())
584     {
585         // Only draw handles when user is not resizing
586 
587         if (d->movingHandle == CH_None)
588         {
589             painter->setOpacity(d->resizeHandleVisibility->opacity());
590             painter->setBrush(fillColor);
591 
592             foreach (const CropHandle& handle, d->cropHandleList)
593             {
594                 QRectF rect = d->handleRect(handle);
595                 painter->drawRect(rect);
596             }
597         }
598     }
599 }
600 
slotUpdate()601 void RegionFrameItem::slotUpdate()
602 {
603     update();
604 }
605 
hoverEnterEvent(QGraphicsSceneHoverEvent * e)606 void RegionFrameItem::hoverEnterEvent(QGraphicsSceneHoverEvent* e)
607 {
608     if (boundingRect().contains(e->pos()))
609     {
610         d->resizeHandleVisibility->controller()->show();
611     }
612 }
613 
hoverLeaveEvent(QGraphicsSceneHoverEvent * e)614 void RegionFrameItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* e)
615 {
616     if (!boundingRect().contains(e->pos()))
617     {
618         d->resizeHandleVisibility->controller()->hide();
619     }
620 }
621 
hoverMoveEvent(QGraphicsSceneHoverEvent * e)622 void RegionFrameItem::hoverMoveEvent(QGraphicsSceneHoverEvent* e)
623 {
624     if (boundingRect().contains(e->pos()))
625     {
626         if (d->flags & GeometryEditable)
627         {
628             CropHandle handle = d->handleAt(e->pos());
629             d->updateCursor(handle, false/* buttonDown*/);
630         }
631 
632         d->resizeHandleVisibility->controller()->show();
633     }
634 }
635 
mousePressEvent(QGraphicsSceneMouseEvent * event)636 void RegionFrameItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
637 {
638     // FIXME: Fade out?
639 /*
640     d->hudWidget->hide();
641 */
642     if (!(d->flags & GeometryEditable))
643     {
644         DImgChildItem::mousePressEvent(event);
645         return;
646     }
647 
648     d->movingHandle = d->handleAt(event->pos());
649     d->updateCursor(d->movingHandle, event->buttons() != Qt::NoButton);
650 
651     if (d->movingHandle == CH_Content)
652     {
653         d->lastMouseMovePos = mapToParent(event->pos());
654     }
655 
656     // Update to hide handles
657 
658     update();
659 }
660 
mouseMoveEvent(QGraphicsSceneMouseEvent * event)661 void RegionFrameItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
662 {
663     const QSizeF maxSize = parentDImgItem()->boundingRect().size();
664     const QPointF point  = mapToParent(event->pos());
665     qreal posX           = qBound<qreal>(0., point.x(), maxSize.width());
666     qreal posY           = qBound<qreal>(0., point.y(), maxSize.height());
667     QRectF r             = rect();
668 
669     // Adjust edge
670 
671     if      (d->movingHandle & CH_Top)
672     {
673         r.setTop(posY);
674     }
675     else if (d->movingHandle & CH_Bottom)
676     {
677         r.setBottom(posY);
678     }
679 
680     if      (d->movingHandle & CH_Left)
681     {
682         r.setLeft(posX);
683     }
684     else if (d->movingHandle & CH_Right)
685     {
686         r.setRight(posX);
687     }
688 
689     // Normalize rect and handles (this is useful when user drag the right side
690     // of the crop rect to the left of the left side)
691 
692     if (r.height() < 0)
693     {
694         d->movingHandle = d->movingHandle ^ (CH_Top | CH_Bottom);
695     }
696 
697     if (r.width() < 0)
698     {
699         d->movingHandle = d->movingHandle ^ (CH_Left | CH_Right);
700     }
701 
702     r = r.normalized();
703 
704     // Enforce ratio
705 
706     if (d->fixedRatio > 0.)
707     {
708         if      ((d->movingHandle == CH_Top) || (d->movingHandle == CH_Bottom))
709         {
710             // Top or bottom
711 
712             int width = int(r.height() / d->fixedRatio);
713             r.setWidth(width);
714         }
715         else if ((d->movingHandle == CH_Left) || (d->movingHandle == CH_Right))
716         {
717             // Left or right
718 
719             int height = int(r.width() * d->fixedRatio);
720             r.setHeight(height);
721         }
722         else if (d->movingHandle & CH_Top)
723         {
724             // Top left or top right
725 
726             int height = int(r.width() * d->fixedRatio);
727             r.setTop(r.bottom() - height);
728         }
729         else if (d->movingHandle & CH_Bottom)
730         {
731             // Bottom left or bottom right
732 
733             int height = int(r.width() * d->fixedRatio);
734             r.setHeight(height);
735         }
736     }
737 
738     if (d->movingHandle == CH_Content)
739     {
740         QPointF delta       = point - d->lastMouseMovePos;
741         r.adjust(delta.x(), delta.y(), delta.x(), delta.y());
742         d->lastMouseMovePos = mapToParent(event->pos());
743     }
744 
745     setRect(d->keepRectInsideImage(r));
746 
747     d->updateHudWidgetPosition();
748 }
749 
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)750 void RegionFrameItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
751 {
752     // FIXME: Fade in?
753 /*
754     d->hudWidget->show();
755 */
756     d->movingHandle = CH_None;
757     d->updateCursor(d->handleAt(event->pos()), false);
758 
759     // Update to show handles
760 
761     update();
762 }
763 
eventFilter(QObject * watched,QEvent * event)764 bool RegionFrameItem::eventFilter(QObject* watched, QEvent* event)
765 {
766     if ((watched == d->hudWidget) && (event->type() == QEvent::GraphicsSceneResize))
767     {
768         d->updateHudWidgetPosition();
769     }
770 
771     return DImgChildItem::eventFilter(watched, event);
772 }
773 
moveHudWidget()774 void RegionFrameItem::moveHudWidget()
775 {
776     const QPointF delta   = d->hudEndPos - d->hudWidget->pos();
777     const double distance = sqrt(pow(delta.x(), 2) + pow(delta.y(), 2));
778     QPointF pos;
779 
780     if (distance > double(d->HUD_TIMER_MAX_PIXELS_PER_UPDATE))
781     {
782         pos = d->hudWidget->pos() + delta * double(d->HUD_TIMER_MAX_PIXELS_PER_UPDATE) / distance;
783     }
784     else
785     {
786         pos = d->hudEndPos;
787         d->hudTimer->stop();
788     }
789 
790     d->hudWidget->setPos(pos);
791 }
792 
setRectInSceneCoordinatesAdjusted(const QRectF & rect)793 void RegionFrameItem::setRectInSceneCoordinatesAdjusted(const QRectF& rect)
794 {
795     setRectInSceneCoordinates(d->keepRectInsideImage(rect, false));
796 }
797 
798 } // namespace Digikam
799