Krita Source Code Documentation
Loading...
Searching...
No Matches
overviewdocker_page.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2009 Cyrille Berger <cberger@cberger.net>
3 * SPDX-FileCopyrightText: 2021 Deif Lou <ginoba@gmail.com>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
9#include "overviewwidget.h"
10#include "overviewdocker_page.h"
11
12#include <QVBoxLayout>
13#include <QHBoxLayout>
14#include <QToolButton>
15#include <QStatusBar>
16#include <QApplication>
17
18#include <KisAngleSelector.h>
19#include <klocalizedstring.h>
20#include "kis_canvas2.h"
21#include <KisViewManager.h>
22#include <kactioncollection.h>
23#include <kis_action.h>
24#include <kis_zoom_manager.h>
25#include "kis_image.h"
26#include "kis_paint_device.h"
29#include "kis_icon_utils.h"
30#include "kis_signals_blocker.h"
31#include <KoZoomWidget.h>
32#include <kis_icon_utils.h>
33
34#include <kconfiggroup.h>
35#include <ksharedconfig.h>
36
37OverviewDockerPage::OverviewDockerPage(QWidget *parent, const char *name, Qt::WindowFlags f)
38 : QWidget(parent, f)
39 , m_lastOverviewMousePos(0.0, 0.0)
40{
41 setObjectName(name);
42
44 m_overviewWidget->setMinimumHeight(50);
45 m_overviewWidget->setBackgroundRole(QPalette::Base);
46 // paints background role before paint()
47 m_overviewWidget->setAutoFillBackground(true);
48 m_overviewWidget->setAttribute(Qt::WA_AcceptTouchEvents, true);
49 m_overviewWidget->installEventFilter(this);
50 connect(m_overviewWidget, SIGNAL(signalDraggingStarted()), SLOT(on_overviewWidget_signalDraggingStarted()));
51 connect(m_overviewWidget, SIGNAL(signalDraggingFinished()), SLOT(on_overviewWidget_signalDraggingFinished()));
52
53 m_controlsContainer = new QWidget(this);
54
55 m_controlsLayout = new QVBoxLayout;
56 m_controlsLayout->setContentsMargins(2, 2, 2, 2);
57 m_controlsLayout->setSpacing(2);
59
60 m_showControlsTimer.setSingleShot(true);
61
62 m_showControlsAnimation.setEasingCurve(QEasingCurve(QEasingCurve::InOutCubic));
63 connect(&m_showControlsAnimation, &QVariantAnimation::valueChanged, this, &OverviewDockerPage::layoutMainWidgets);
64
65 KConfigGroup config = KSharedConfig::openConfig()->group("OverviewDocker");
66 m_pinControls = config.readEntry("pinControls", true);
68
69 setEnabled(false);
70}
71
73{
74 KConfigGroup config = KSharedConfig::openConfig()->group("OverviewDocker");
75 config.writeEntry("pinControls", m_pinControls);
76}
77
79{
80 if(m_canvas == canvas)
81 return;
82
83 setEnabled(canvas != nullptr);
84
85 if (m_canvas) {
86 m_canvas->disconnectCanvasObserver(this);
87 m_canvas->image()->disconnect(this);
88 m_canvas = nullptr;
89 }
90
91 if (m_zoomSlider) {
93 delete m_zoomSlider;
94 m_zoomSlider = nullptr;
95 }
96
100 m_rotateAngleSelector = nullptr;
101 }
102
103 if (m_mirrorCanvas) {
105 delete m_mirrorCanvas;
106 m_mirrorCanvas = nullptr;
107 }
108
111 delete m_pinControlsButton;
112 m_pinControlsButton = nullptr;
113 }
114
115 // Delete the stretch
116 while (m_controlsSecondRowLayout && m_controlsSecondRowLayout->count() && m_controlsSecondRowLayout->itemAt(0)->spacerItem()) {
117 delete m_controlsSecondRowLayout->takeAt(0);
118 }
119
121
124
125 m_canvas = dynamic_cast<KisCanvas2*>(canvas);
126
128 if (m_canvas && m_canvas->viewManager()) {
129 bool usePrintResolutionMode = m_canvas->imageView()->canvasController()->usePrintResolutionMode();
130 m_zoomSlider = m_canvas->imageView()->zoomManager()->zoomAction()->createWidget(m_canvas->imageView()->KisView::statusBar());
131 KoZoomWidget* zoomWidget = static_cast<KoZoomWidget*>(m_zoomSlider);
132 zoomWidget->setZoomInputFlat(false);
133 zoomWidget->setUsePrintResolutionMode(usePrintResolutionMode);
134 m_controlsLayout->addWidget(m_zoomSlider);
135
137 m_rotateAngleSelector->setRange(-360.00, 360.0);
138 m_rotateAngleSelector->setAngle(m_canvas->rotationAngle());
141 connect(m_rotateAngleSelector, SIGNAL(angleChanged(qreal)), this, SLOT(rotateCanvasView(qreal)), Qt::UniqueConnection);
142 connect(m_canvas->canvasController()->proxyObject, SIGNAL(documentRotationChanged(qreal)), this, SLOT(updateRotationSlider(qreal)));
143
144 m_mirrorCanvas = new QToolButton();
145 QList<QAction *> actions = m_canvas->viewManager()->actionCollection()->actions();
146 Q_FOREACH(QAction* action, actions) {
147 if (action->objectName()=="mirror_canvas") {
148 m_mirrorCanvas->setDefaultAction(action);
149 }
150 }
151 m_mirrorCanvas->setIcon(KisIconUtils::loadIcon("mirror-view-16"));
152 m_mirrorCanvas->setAutoRaise(true);
153 connect(m_mirrorCanvas, SIGNAL(toggled(bool)), this, SLOT(mirrorUpdateIcon()));
154
155 m_pinControlsButton = new QToolButton;
156 m_pinControlsButton->setCheckable(true);
158 m_pinControlsButton->setToolTip(
159 i18nc("Make the controls in the overview docker auto-hide or always visible", "Pin navigation controls")
160 );
161 m_pinControlsButton->setIcon(KisIconUtils::loadIcon("krita_tool_reference_images"));
162 m_pinControlsButton->setAutoRaise(true);
163 connect(m_pinControlsButton, SIGNAL(toggled(bool)), SLOT(setPinControls(bool)));
164
165 m_controlsSecondRowLayout = new QHBoxLayout();
166
168 m_controlsSecondRowLayout->addStretch();
170 m_controlsSecondRowLayout->addStretch();
173
174 //m_zoomSlider->setVisible(true);
175 m_rotateAngleSelector->setVisible(true);
176
177 // Show/hide the controls
178 if (m_pinControls) {
179 showControls(0);
180 } else {
181 if (m_cursorIsHover) {
182 showControls(0);
183 } else {
184 hideControls(0);
185 }
186 }
187 }
188}
189
191{
192 setEnabled(false);
193 m_canvas = nullptr;
195}
196
198{
199 if(!m_mirrorCanvas) return;
200 m_mirrorCanvas->setIcon(KisIconUtils::loadIcon("mirror-view-16"));
201}
202
204{
205 if (!m_canvas) return;
206 KisCanvasController *canvasController =
207 dynamic_cast<KisCanvasController*>(m_canvas->viewManager()->canvasBase()->canvasController());
208 if (canvasController) {
209 canvasController->rotateCanvas(rotation-m_canvas->rotationAngle());
210 }
211}
212
214{
215 if (qAbs(m_rotateAngleSelector->angle() - angle) > 0.01) {
217 m_rotateAngleSelector->setAngle(m_canvas->rotationAngle());
218 }
219}
220
222{
223 m_pinControls = pin;
224}
225
227{
229}
230
232{
233 m_cursorIsHover = false;
234 if (isEnabled() && !m_pinControls) {
235 hideControls(0);
237 }
238}
239
240#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
242#else
244#endif
245{
246 m_cursorIsHover = true;
247 if (isEnabled() && !m_pinControls) {
248 showControls(showControlsTimerDuration);
249 }
250}
251
253{
254 if (e->type() == QEvent::StyleChange || e->type() == QEvent::FontChange) {
255 resizeEvent(nullptr);
256 }
257 return QWidget::event(e);
258}
259
260bool OverviewDockerPage::eventFilter(QObject *o, QEvent *e)
261{
262 if (!isEnabled()) {
263 return false;
264 }
265
266 if (o == m_overviewWidget) {
267 // Filter out the mouse events if we are touching the overview widget
268 // and the event was not synthesized in this function from the touch events
269 if (e->type() == QEvent::MouseButtonPress) {
270 if (m_isTouching) {
271 return static_cast<QMouseEvent*>(e)->source() != Qt::MouseEventSynthesizedByApplication;
272 }
273
274 } else if (e->type() == QEvent::MouseButtonRelease) {
275 if (m_isTouching) {
276 return static_cast<QMouseEvent*>(e)->source() != Qt::MouseEventSynthesizedByApplication;
277 }
278
279 } else if (e->type() == QEvent::MouseMove) {
280 if (m_isTouching) {
281 return static_cast<QMouseEvent*>(e)->source() != Qt::MouseEventSynthesizedByApplication;
282 }
284 QMouseEvent *me = static_cast<QMouseEvent*>(e);
285 constexpr double showControlsAreaRadiusSquared = showControlsAreaRadius * showControlsAreaRadius;
286 const QPointF d = me->localPos() - m_lastOverviewMousePos;
287 const double distanceSquared = d.x() * d.x() + d.y() * d.y();
288 if (distanceSquared > m_cumulatedMouseDistanceSquared) {
289 if (distanceSquared >= showControlsAreaRadiusSquared) {
291 m_lastOverviewMousePos = me->localPos();
293 } else {
294 m_cumulatedMouseDistanceSquared = distanceSquared;
295 }
296 }
297 }
298
299 } else if (e->type() == QEvent::TouchBegin) {
300 if (!m_isTouching) {
301 QTouchEvent *te = static_cast<QTouchEvent*>(e);
302 m_isTouching = true;
303 // Store the first touch point. We will only track this one
304 m_touchPointId = te->touchPoints().first().id();
305 m_lastTouchPos = te->touchPoints().first().pos();
306 }
307 // Accept the event so that other touch events keep coming
308 e->accept();
309 return true;
310
311 } else if (e->type() == QEvent::TouchUpdate) {
312 if (!m_isTouching) {
313 return true;
314 }
315 QTouchEvent *te = static_cast<QTouchEvent*>(e);
316 // Get the touch point position
317 QPointF currentPosition;
318 for (const QTouchEvent::TouchPoint &touchPoint : te->touchPoints()) {
319 if (touchPoint.id() == m_touchPointId) {
320 // If the touch point wasn't moved, this event wasn't
321 // generated from our touch point
322 if (touchPoint.state() == Qt::TouchPointStationary) {
323 return true;
324 }
325 currentPosition = touchPoint.pos();
326 break;
327 }
328 }
330 // Compute distance
331 const QPointF delta = currentPosition - m_lastTouchPos;
332 const qreal distanceSquared = delta.x() * delta.x() + delta.y() * delta.y();
333 if (distanceSquared >= touchDragDistanceSquared) {
335 // synthesize mouse press event
336 QMouseEvent *se = new QMouseEvent(QEvent::MouseButtonPress,
337 m_lastTouchPos, QPointF(), QPointF(),
338 Qt::LeftButton, Qt::LeftButton, Qt::NoModifier,
339 Qt::MouseEventSynthesizedByApplication);
340 qApp->sendEvent(m_overviewWidget, se);
341 }
342 }
344 // Synthesize mouse move event
345 QMouseEvent *se = new QMouseEvent(QEvent::MouseMove,
346 currentPosition, QPointF(), QPointF(),
347 Qt::LeftButton, Qt::LeftButton, Qt::NoModifier,
348 Qt::MouseEventSynthesizedByApplication);
349 qApp->sendEvent(m_overviewWidget, se);
350 // Update
351 m_lastTouchPos = currentPosition;
352 }
353 return true;
354
355 } else if (e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) {
356 if (!m_isTouching) {
357 return true;
358 }
359 QTouchEvent *te = static_cast<QTouchEvent*>(e);
360 if (e->type() == QEvent::TouchEnd) {
361 // If the touch point is not in the released state
362 // then this event wasn't generated from our touch point
363 for (const QTouchEvent::TouchPoint &touchPoint : te->touchPoints()) {
364 if (touchPoint.id() == m_touchPointId) {
365 if (touchPoint.state() != Qt::TouchPointReleased) {
366 return true;
367 }
368 break;
369 }
370 }
371 }
372 // If we are dragging then synthesize mouse release event.
373 // Show/hide the controls otherwise
375 QMouseEvent *se = new QMouseEvent(QEvent::MouseButtonRelease,
376 m_lastTouchPos, QPointF(), QPointF(),
377 Qt::LeftButton, Qt::LeftButton, Qt::NoModifier,
378 Qt::MouseEventSynthesizedByApplication);
379 qApp->sendEvent(m_overviewWidget, se);
380 } else if (e->type() == QEvent::TouchEnd) {
385 showControls(0);
386 } else {
387 hideControls(0);
388 }
389 }
390 // Reset
391 m_isTouching = false;
392 m_isDraggingWithTouch = false;
393 return true;
394 }
395 }
396 return false;
397}
398
400{
401 this->setMinimumHeight(m_overviewWidget->minimumHeight() +
402 m_controlsContainer->minimumSizeHint().height());
403
404 if (m_showControlsAnimation.state() == QVariantAnimation::Running) {
405 const qreal pageHeight = static_cast<qreal>(this->height());
406 const qreal controlsContainerHeight = static_cast<qreal>(m_controlsContainer->sizeHint().height());
407 const qreal animationProgress = m_showControlsAnimation.currentValue().toReal();
408 const int widgetLimitPosition = static_cast<int>(std::round(pageHeight - animationProgress * controlsContainerHeight));
409 m_overviewWidget->setGeometry(0, 0, this->width(), widgetLimitPosition);
410 m_controlsContainer->setGeometry(0, widgetLimitPosition, this->width(), static_cast<int>(controlsContainerHeight));
411 } else {
412 const int controlsContainerHeight = m_controlsContainer->sizeHint().height();
414 const int widgetLimitPosition = this->height() - controlsContainerHeight;
415 m_overviewWidget->setGeometry(0, 0, this->width(), widgetLimitPosition);
416 m_controlsContainer->setGeometry(0, widgetLimitPosition, this->width(), controlsContainerHeight);
417 } else {
418 m_overviewWidget->setGeometry(0, 0, this->width(), this->height());
419 m_controlsContainer->setGeometry(0, this->height(), this->width(), controlsContainerHeight);
420 }
421 }
422}
423
425{
426 auto animFunction =
427 [this]() -> void
428 {
429 int animationDuration;
430 qreal animationStartValue;
431
433 if (m_showControlsAnimation.state() == QVariantAnimation::Running) {
435 animationDuration =
436 static_cast<int>(std::round((1.0 - m_showControlsAnimation.currentValue().toReal()) * showControlsAnimationDuration));
437 animationStartValue = m_showControlsAnimation.currentValue().toReal();
438 } else {
439 animationDuration = showControlsAnimationDuration;
440 animationStartValue = 0.0;
441 }
442 } else {
443 animationDuration = 1;
444 animationStartValue = 1.0;
445 }
446
447 m_areControlsHidden = false;
448 m_showControlsAnimation.setStartValue(animationStartValue);
449 m_showControlsAnimation.setEndValue(1.0);
450 m_showControlsAnimation.setDuration(animationDuration);
452 };
453
454 delay = qMax(delay, 0);
455
456 m_showControlsTimer.disconnect();
457 connect(&m_showControlsTimer, &QTimer::timeout, animFunction);
458 m_showControlsTimer.start(delay);
459}
460
462{
463 auto animFunction =
464 [this]() -> void
465 {
466 int animationDuration;
467 qreal animationStartValue;
468
469 if (!m_areControlsHidden) {
470 if (m_showControlsAnimation.state() == QVariantAnimation::Running) {
472 animationDuration =
473 static_cast<int>(std::round(m_showControlsAnimation.currentValue().toReal() * showControlsAnimationDuration));
474 animationStartValue = m_showControlsAnimation.currentValue().toReal();
475 } else {
476 animationDuration = showControlsAnimationDuration;
477 animationStartValue = 1.0;
478 }
479 } else {
480 animationDuration = 1;
481 animationStartValue = 0.0;
482 }
483
484 m_areControlsHidden = true;
485 m_showControlsAnimation.setStartValue(animationStartValue);
486 m_showControlsAnimation.setEndValue(0.0);
487 m_showControlsAnimation.setDuration(animationDuration);
489 };
490
491 delay = qMax(delay, 0);
492
493 m_showControlsTimer.disconnect();
494 connect(&m_showControlsTimer, &QTimer::timeout, animFunction);
495 m_showControlsTimer.start(delay);
496}
497
504
KisMagneticGraph::vertex_descriptor source(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
@ IncreasingDirection_Clockwise
A widget with several options to select an angle.
@ FlipOptionsMode_ContextMenu
The flip options are shown only as a context menu when right-clicking the gauge widget.
qreal angle() const
Gets the current angle.
void setFlipOptionsMode(FlipOptionsMode newMode)
Sets the mode in which the flip options should be shown.
void setIncreasingDirection(KisAngleGauge::IncreasingDirection newIncreasingDirection)
Sets the increasing direction in the angle gauge.
void setAngle(qreal newAngle)
Sets the current angle.
void setRange(qreal newMinimum, qreal newMaximum)
Sets the minimum and maximum values for the angle.
void rotateCanvas(qreal angle, const std::optional< KoViewTransformStillPoint > &stillPoint, bool isNativeGesture=false)
void setZoomInputFlat(bool flat)
void setUsePrintResolutionMode(bool value)
void leaveEvent(QEvent *) override
bool eventFilter(QObject *o, QEvent *e) override
void showControls(int delay) const
OverviewDockerPage(QWidget *parent=0, const char *name=0, Qt::WindowFlags f=Qt::WindowFlags())
QToolButton * m_pinControlsButton
static constexpr double touchDragDistanceSquared
static constexpr double showControlsAnimationDuration
QHBoxLayout * m_controlsSecondRowLayout
static constexpr double showControlsAreaRadius
QVariantAnimation m_showControlsAnimation
KisAngleSelector * m_rotateAngleSelector
static constexpr int showControlsTimerDuration
QVBoxLayout * m_controlsLayout
void setCanvas(KoCanvasBase *canvas)
void resizeEvent(QResizeEvent *) override
bool event(QEvent *e) override
QPointer< KisCanvas2 > m_canvas
void on_overviewWidget_signalDraggingFinished()
void rotateCanvasView(qreal rotation)
void updateRotationSlider(qreal rotation)
void hideControls(int delay) const
void enterEvent(QEnterEvent *event) override
QToolButton * m_mirrorCanvas
OverviewWidget * m_overviewWidget
void setCanvas(KisCanvas2 *canvas) override
bool isDragging() const
QIcon loadIcon(const QString &name)