File indexing completed on 2025-02-23 04:09:01
0001 /* 0002 * SPDX-FileCopyrightText: 2013 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kis_infinity_manager.h" 0008 0009 #include <QPainter> 0010 0011 #include <klocalizedstring.h> 0012 0013 #include <KoCanvasController.h> 0014 0015 #include <kis_debug.h> 0016 #include <KisViewManager.h> 0017 #include <kis_canvas2.h> 0018 #include <input/kis_input_manager.h> 0019 #include <kis_config.h> 0020 #include <KisDocument.h> 0021 #include <kis_image.h> 0022 #include <kis_canvas_controller.h> 0023 #include <KisView.h> 0024 #include <kis_algebra_2d.h> 0025 0026 KisInfinityManager::KisInfinityManager(QPointer<KisView>view, KisCanvas2 *canvas) 0027 : KisCanvasDecoration(INFINITY_DECORATION_ID, view), 0028 m_filteringEnabled(false), 0029 m_cursorSwitched(false), 0030 m_sideRects(NSides), 0031 m_canvas(canvas) 0032 { 0033 connect(canvas, SIGNAL(documentOffsetUpdateFinished()), SLOT(imagePositionChanged())); 0034 } 0035 0036 inline void KisInfinityManager::addDecoration(const QRect &areaRect, const QPointF &handlePoint, qreal angle, Side side) 0037 { 0038 QTransform t; 0039 t.rotate(angle); 0040 t = t * QTransform::fromTranslate(handlePoint.x(), handlePoint.y()); 0041 m_handleTransform << t; 0042 0043 m_decorationPath.addRect(areaRect); 0044 m_sideRects[side] = areaRect; 0045 } 0046 0047 void KisInfinityManager::imagePositionChanged() 0048 { 0049 const QRect imageRect = m_canvas->coordinatesConverter()->imageRectInWidgetPixels().toAlignedRect(); 0050 const QRect widgetRect = m_canvas->canvasWidget()->rect(); 0051 0052 KisConfig cfg(true); 0053 qreal vastScrolling = cfg.vastScrolling(); 0054 0055 int xReserve = vastScrolling * widgetRect.width(); 0056 int yReserve = vastScrolling * widgetRect.height(); 0057 0058 int xThreshold = imageRect.width() - 0.4 * xReserve; 0059 int yThreshold = imageRect.height() - 0.4 * yReserve; 0060 0061 const int stripeWidth = 48; 0062 0063 int xCut = widgetRect.width() - stripeWidth; 0064 int yCut = widgetRect.height() - stripeWidth; 0065 0066 m_decorationPath = QPainterPath(); 0067 m_decorationPath.setFillRule(Qt::WindingFill); 0068 0069 m_handleTransform.clear(); 0070 0071 m_sideRects.clear(); 0072 m_sideRects.resize(NSides); 0073 0074 bool visible = false; 0075 0076 if (imageRect.x() <= -xThreshold) { 0077 QRect areaRect(widgetRect.adjusted(xCut, 0, 0, 0)); 0078 QPointF pt = areaRect.center() + QPointF(-0.1 * stripeWidth, 0); 0079 addDecoration(areaRect, pt, 0, Right); 0080 visible = true; 0081 } 0082 0083 if (imageRect.y() <= -yThreshold) { 0084 QRect areaRect(widgetRect.adjusted(0, yCut, 0, 0)); 0085 QPointF pt = areaRect.center() + QPointF(0, -0.1 * stripeWidth); 0086 addDecoration(areaRect, pt, 90, Bottom); 0087 visible = true; 0088 } 0089 0090 if (imageRect.right() > widgetRect.width() + xThreshold) { 0091 QRect areaRect(widgetRect.adjusted(0, 0, -xCut, 0)); 0092 QPointF pt = areaRect.center() + QPointF(0.1 * stripeWidth, 0); 0093 addDecoration(areaRect, pt, 180, Left); 0094 visible = true; 0095 } 0096 0097 if (imageRect.bottom() > widgetRect.height() + yThreshold) { 0098 QRect areaRect(widgetRect.adjusted(0, 0, 0, -yCut)); 0099 QPointF pt = areaRect.center() + QPointF(0, 0.1 * stripeWidth); 0100 addDecoration(areaRect, pt, 270, Top); 0101 visible = true; 0102 } 0103 0104 if (!m_filteringEnabled && visible && this->visible()) { 0105 KisInputManager *inputManager = m_canvas->globalInputManager(); 0106 if (inputManager) { 0107 inputManager->attachPriorityEventFilter(this); 0108 } 0109 0110 m_filteringEnabled = true; 0111 } 0112 0113 if (m_filteringEnabled && (!visible || !this->visible())) { 0114 KisInputManager *inputManager = m_canvas->globalInputManager(); 0115 if (inputManager) { 0116 inputManager->detachPriorityEventFilter(this); 0117 } 0118 0119 m_filteringEnabled = false; 0120 } 0121 } 0122 0123 void KisInfinityManager::drawDecoration(QPainter& gc, const QRectF& updateArea, const KisCoordinatesConverter *converter, KisCanvas2 *canvas) 0124 { 0125 Q_UNUSED(updateArea); 0126 Q_UNUSED(converter); 0127 Q_UNUSED(canvas); 0128 0129 if (!m_filteringEnabled) return; 0130 0131 gc.save(); 0132 gc.setTransform(QTransform(), false); 0133 0134 KisConfig cfg(true); 0135 QColor color = cfg.canvasBorderColor(); 0136 gc.fillPath(m_decorationPath, color.darker(115)); 0137 0138 QPainterPath p = KisAlgebra2D::smallArrow(); 0139 0140 Q_FOREACH (const QTransform &t, m_handleTransform) { 0141 gc.fillPath(t.map(p), color); 0142 } 0143 0144 gc.restore(); 0145 } 0146 0147 inline int expandLeft(int x0, int x1, int maxExpand) 0148 { 0149 return qMax(x0 - maxExpand, qMin(x0, x1)); 0150 } 0151 0152 inline int expandRight(int x0, int x1, int maxExpand) 0153 { 0154 return qMin(x0 + maxExpand, qMax(x0, x1)); 0155 } 0156 0157 inline QPoint getPointFromEvent(QEvent *event) 0158 { 0159 QPoint result; 0160 0161 if (event->type() == QEvent::MouseMove || 0162 event->type() == QEvent::MouseButtonPress || 0163 event->type() == QEvent::MouseButtonRelease || 0164 event->type() == QEvent::Enter) { 0165 0166 QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event); 0167 result = mouseEvent->pos(); 0168 0169 } else if (event->type() == QEvent::TabletMove || 0170 event->type() == QEvent::TabletPress || 0171 event->type() == QEvent::TabletRelease) { 0172 0173 QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event); 0174 result = tabletEvent->pos(); 0175 } 0176 0177 return result; 0178 } 0179 0180 inline Qt::MouseButton getButtonFromEvent(QEvent *event) 0181 { 0182 Qt::MouseButton button = Qt::NoButton; 0183 0184 if (event->type() == QEvent::MouseMove || 0185 event->type() == QEvent::MouseButtonPress || 0186 event->type() == QEvent::MouseButtonRelease) { 0187 0188 QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event); 0189 button = mouseEvent->button(); 0190 0191 } else if (event->type() == QEvent::TabletMove || 0192 event->type() == QEvent::TabletPress || 0193 event->type() == QEvent::TabletRelease) { 0194 0195 QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event); 0196 button = tabletEvent->button(); 0197 } 0198 0199 return button; 0200 } 0201 0202 bool KisInfinityManager::eventFilter(QObject *obj, QEvent *event) 0203 { 0204 /** 0205 * We connect our event filter to the global InputManager which is 0206 * shared among all the canvases. Ideally we should disconnect our 0207 * event filter whin this canvas is not active, but for now we can 0208 * just check the destination of the event, if it is correct. 0209 */ 0210 if (m_canvas == NULL || obj != m_canvas->canvasWidget()) return false; 0211 0212 KIS_ASSERT_RECOVER_NOOP(m_filteringEnabled); 0213 0214 bool retval = false; 0215 0216 switch (event->type()) { 0217 case QEvent::Enter: 0218 case QEvent::MouseMove: 0219 case QEvent::TabletMove: { 0220 QPoint pos = getPointFromEvent(event); 0221 0222 if (m_decorationPath.contains(pos)) { 0223 if (!m_cursorSwitched) { 0224 m_oldCursor = m_canvas->canvasWidget()->cursor(); 0225 m_cursorSwitched = true; 0226 } 0227 m_canvas->canvasWidget()->setCursor(Qt::PointingHandCursor); 0228 retval = true; 0229 } else if (m_cursorSwitched) { 0230 m_canvas->canvasWidget()->setCursor(m_oldCursor); 0231 m_cursorSwitched = false; 0232 } 0233 break; 0234 } 0235 case QEvent::Leave: { 0236 if (m_cursorSwitched) { 0237 m_canvas->canvasWidget()->setCursor(m_oldCursor); 0238 m_cursorSwitched = false; 0239 } 0240 break; 0241 } 0242 case QEvent::MouseButtonPress: 0243 case QEvent::TabletPress: { 0244 Qt::MouseButton button = getButtonFromEvent(event); 0245 retval = button == Qt::LeftButton && m_cursorSwitched; 0246 0247 if (button == Qt::RightButton) { 0248 imagePositionChanged(); 0249 } 0250 0251 break; 0252 } 0253 case QEvent::MouseButtonRelease: 0254 case QEvent::TabletRelease: { 0255 Qt::MouseButton button = getButtonFromEvent(event); 0256 retval = button == Qt::LeftButton && m_cursorSwitched; 0257 0258 if (retval) { 0259 QPoint pos = getPointFromEvent(event); 0260 0261 const KisCoordinatesConverter *converter = m_canvas->coordinatesConverter(); 0262 QRect widgetRect = converter->widgetToImage(m_canvas->canvasWidget()->rect()).toAlignedRect(); 0263 KisImageWSP image = view()->image(); 0264 0265 QRect cropRect = image->bounds(); 0266 0267 const int hLimit = cropRect.width(); 0268 const int vLimit = cropRect.height(); 0269 0270 if (m_sideRects[Right].contains(pos)) { 0271 cropRect.setRight(expandRight(cropRect.right(), widgetRect.right(), hLimit)); 0272 } 0273 if (m_sideRects[Bottom].contains(pos)) { 0274 cropRect.setBottom(expandRight(cropRect.bottom(), widgetRect.bottom(), vLimit)); 0275 } 0276 if (m_sideRects[Left].contains(pos)) { 0277 cropRect.setLeft(expandLeft(cropRect.left(), widgetRect.left(), hLimit)); 0278 } 0279 if (m_sideRects[Top].contains(pos)) { 0280 cropRect.setTop(expandLeft(cropRect.top(), widgetRect.top(), vLimit)); 0281 } 0282 0283 image->resizeImage(cropRect); 0284 0285 // since resizing the image can cause the cursor to end up on the canvas without a move event, 0286 // it can get stuck in an overridden state until it is changed by another event, 0287 // and we don't want that. 0288 if (m_cursorSwitched) { 0289 m_canvas->canvasWidget()->setCursor(m_oldCursor); 0290 m_cursorSwitched = false; 0291 } 0292 } 0293 break; 0294 } 0295 default: 0296 break; 0297 } 0298 0299 return !retval ? KisCanvasDecoration::eventFilter(obj, event) : true; 0300 }