File indexing completed on 2024-05-12 15:56:38
0001 /* This file is part of the KDE project 0002 * SPDX-FileCopyrightText: 2006, 2008 Thomas Zander <zander@kde.org> 0003 * SPDX-FileCopyrightText: 2007-2010 Boudewijn Rempt <boud@valdyas.org> 0004 * SPDX-FileCopyrightText: 2007-2008 C. Boemann <cbo@boemann.dk> 0005 * SPDX-FileCopyrightText: 2006-2007 Jan Hambrecht <jaham@gmx.net> 0006 * SPDX-FileCopyrightText: 2009 Thorsten Zachmann <zachmann@kde.org> 0007 * 0008 * SPDX-License-Identifier: LGPL-2.0-or-later 0009 */ 0010 0011 #ifndef KOCANVASCONTROLLER_H 0012 #define KOCANVASCONTROLLER_H 0013 0014 #include "kritaflake_export.h" 0015 #include <QObject> 0016 0017 #include <QSize> 0018 #include <QPoint> 0019 #include <QPointF> 0020 #include <QPointer> 0021 0022 class KisKActionCollection; 0023 class QRect; 0024 class QRectF; 0025 0026 0027 class KoShape; 0028 class KoCanvasBase; 0029 class KoCanvasControllerProxyObject; 0030 0031 /** 0032 * KoCanvasController is the base class for wrappers around your canvas 0033 * that provides scrolling and zooming for your canvas. 0034 * 0035 * Flake does not provide a canvas, the application will have to 0036 * implement a canvas themselves. You canvas can be QWidget-based 0037 * or something we haven't invented yet -- as long the class that holds the canvas 0038 * implements KoCanvasController, tools, scrolling and zooming will work. 0039 * 0040 * A KoCanvasController implementation acts as a decorator around the canvas widget 0041 * and provides a way to scroll the canvas, allows the canvas to be centered 0042 * in the viewArea and manages tool activation. 0043 * 0044 * <p>The using application can instantiate this class and add its 0045 * canvas using the setCanvas() call. Which is designed so it can be 0046 * called multiple times if you need to exchange one canvas 0047 * widget for another, for instance, switching between a plain QWidget or a QOpenGLWidget. 0048 * 0049 * <p>There is _one_ KoCanvasController per canvas in your 0050 * application. 0051 * 0052 * <p>The canvas widget is at most as big as the viewport of the scroll 0053 * area, and when the view on the document is near its edges, smaller. 0054 * In your canvas widget code, you can find the right place in your 0055 * document in view coordinates (pixels) by adding the documentOffset 0056 */ 0057 class KRITAFLAKE_EXPORT KoCanvasController 0058 { 0059 public: 0060 0061 // proxy QObject: use this to connect to slots and signals. 0062 QPointer<KoCanvasControllerProxyObject> proxyObject; 0063 0064 /** 0065 * Constructor. 0066 * @param actionCollection the action collection for this canvas 0067 */ 0068 explicit KoCanvasController(KisKActionCollection* actionCollection); 0069 virtual ~KoCanvasController(); 0070 0071 public: 0072 /** 0073 * Returns the current margin that is used to pad the canvas with. 0074 * This value is read from the KConfig property "canvasmargin" 0075 */ 0076 virtual int margin() const; 0077 0078 /** 0079 * Set the new margin to pad the canvas with. 0080 */ 0081 virtual void setMargin(int margin); 0082 0083 /** 0084 * compatibility with QAbstractScrollArea 0085 */ 0086 virtual void scrollContentsBy(int dx, int dy) = 0; 0087 0088 /** 0089 * @return the size of the viewport 0090 */ 0091 virtual QSizeF viewportSize() const = 0; 0092 0093 /** 0094 * Set the new canvas to be shown as a child 0095 * Calling this will emit canvasRemoved() if there was a canvas before, and will emit 0096 * canvasSet() with the new canvas. 0097 * @param canvas the new canvas. The KoCanvasBase::canvas() will be called to retrieve the 0098 * actual widget which will then be added as child of this one. 0099 */ 0100 virtual void setCanvas(KoCanvasBase *canvas) = 0; 0101 0102 /** 0103 * Return the currently set canvas. The default implementation will return Null 0104 * @return the currently set canvas 0105 */ 0106 virtual KoCanvasBase *canvas() const; 0107 0108 /** 0109 * return the amount of pixels vertically visible of the child canvas. 0110 * @return the amount of pixels vertically visible of the child canvas. 0111 */ 0112 virtual int visibleHeight() const = 0; 0113 0114 /** 0115 * return the amount of pixels horizontally visible of the child canvas. 0116 * @return the amount of pixels horizontally visible of the child canvas. 0117 */ 0118 virtual int visibleWidth() const = 0; 0119 0120 /** 0121 * return the amount of pixels that are not visible on the left side of the canvas. 0122 * The leftmost pixel that is shown is returned. 0123 */ 0124 virtual int canvasOffsetX() const = 0; 0125 0126 /** 0127 * return the amount of pixels that are not visible on the top side of the canvas. 0128 * The topmost pixel that is shown is returned. 0129 */ 0130 virtual int canvasOffsetY() const = 0; 0131 0132 /** 0133 * @brief Scrolls the content of the canvas so that the given rect is visible. 0134 * 0135 * The rect is to be specified in view coordinates (pixels). The scrollbar positions 0136 * are changed so that the centerpoint of the rectangle is centered if possible. 0137 * 0138 * @param rect the rectangle to make visible 0139 * @param smooth if true the viewport translation will make be just enough to ensure visibility, no more. 0140 * @see KoViewConverter::documentToView() 0141 */ 0142 virtual void ensureVisible(const QRectF &rect, bool smooth = false) = 0; 0143 0144 /** 0145 * @brief Scrolls the content of the canvas so that the given shape is visible. 0146 * 0147 * This is just a wrapper function of the above function. 0148 * 0149 * @param shape the shape to make visible 0150 */ 0151 virtual void ensureVisible(KoShape *shape) = 0; 0152 0153 /** 0154 * @brief zooms in around the center. 0155 * 0156 * The center must be specified in **widget** coordinates. The scrollbar positions 0157 * are changed so that the center becomes center if possible. 0158 * 0159 * @param center the position to zoom in on 0160 */ 0161 virtual void zoomIn(const QPoint ¢er) = 0; 0162 0163 /** 0164 * @brief zooms out around the center. 0165 * 0166 * The center must be specified in **widget** coordinates. The scrollbar positions 0167 * are changed so that the center becomes center if possible. 0168 * 0169 * @param center the position to zoom out around 0170 */ 0171 virtual void zoomOut(const QPoint ¢er) = 0; 0172 0173 /** 0174 * @brief zooms around the center. 0175 * 0176 * The center must be specified in **widget** coordinates. The scrollbar positions 0177 * are changed so that the center becomes center if possible. 0178 * 0179 * @param center the position to zoom around 0180 * @param zoom the zoom to apply 0181 */ 0182 virtual void zoomBy(const QPoint ¢er, qreal zoom) = 0; 0183 0184 /** 0185 * @brief zoom so that rect is exactly visible (as close as possible) 0186 * 0187 * The rect must be specified in **widget** coordinates. The scrollbar positions 0188 * are changed so that the center of the rect becomes center if possible. 0189 * 0190 * @param rect the rect in **widget** coordinates that should fit the view afterwards 0191 */ 0192 virtual void zoomTo(const QRect &rect) = 0; 0193 0194 /** 0195 * @brief repositions the scrollbars so previous center is once again center 0196 * 0197 * The previous center is cached from when the user uses the scrollbars or zoomTo 0198 * are called. zoomTo is mostly used when a zoom tool of sorts have marked an area 0199 * to zoom in on 0200 * 0201 * The success of this method is limited by the size of thing. But we try our best. 0202 */ 0203 virtual void recenterPreferred() = 0; 0204 0205 /** 0206 * Sets the preferred center point in view coordinates (pixels). 0207 * @param viewPoint the new preferred center 0208 */ 0209 virtual void setPreferredCenter(const QPointF &viewPoint) = 0; 0210 0211 /// Returns the currently set preferred center point in view coordinates (pixels) 0212 virtual QPointF preferredCenter() const = 0; 0213 0214 /** 0215 * Move the canvas over the x and y distance of the parameter distance 0216 * @param distance the distance in view coordinates (pixels). A positive distance means moving the canvas up/left. 0217 */ 0218 virtual void pan(const QPoint &distance) = 0; 0219 0220 /** 0221 * Move the canvas up. This behaves the same as \sa pan() with a positive y coordinate. 0222 */ 0223 virtual void panUp() = 0; 0224 0225 /** 0226 * Move the canvas down. This behaves the same as \sa pan() with a negative y coordinate. 0227 */ 0228 virtual void panDown() = 0; 0229 0230 /** 0231 * Move the canvas to the left. This behaves the same as \sa pan() with a positive x coordinate. 0232 */ 0233 virtual void panLeft() = 0; 0234 0235 /** 0236 * Move the canvas to the right. This behaves the same as \sa pan() with a negative x coordinate. 0237 */ 0238 virtual void panRight() = 0; 0239 0240 /** 0241 * Get the position of the scrollbar 0242 */ 0243 virtual QPoint scrollBarValue() const = 0; 0244 0245 /** 0246 * Set the position of the scrollbar 0247 * @param value the new values of the scroll bars 0248 */ 0249 virtual void setScrollBarValue(const QPoint &value) = 0; 0250 0251 /** 0252 * Update the range of scroll bars 0253 */ 0254 virtual void resetScrollBars() = 0; 0255 0256 /** 0257 * Called when the size of your document in view coordinates (pixels) changes, for instance when zooming. 0258 * 0259 * @param newSize the new size, in view coordinates (pixels), of the document. 0260 * @param recalculateCenter if true the offset in the document we center on after calling 0261 * recenterPreferred() will be recalculated for the new document size so the visual offset stays the same. 0262 */ 0263 virtual void updateDocumentSize(const QSizeF &sz, bool recalculateCenter) = 0; 0264 0265 /** 0266 * Set mouse wheel to zoom behaviour 0267 * @param zoom if true wheel will zoom instead of scroll, control modifier will scroll 0268 */ 0269 virtual void setZoomWithWheel(bool zoom) = 0; 0270 0271 /** 0272 * Set scroll area to be bigger than actual document. 0273 * It allows the user to move the corner of the document 0274 * to e.g. the center of the screen 0275 * 0276 * @param factor the coefficient, defining how much we can scroll out, 0277 * measured in parts of the widget size. Null value means vast 0278 * scrolling is disabled. 0279 */ 0280 virtual void setVastScrolling(qreal factor) = 0; 0281 0282 /** 0283 * Returns the action collection for the window 0284 * @returns action collection for this window, can be 0 0285 */ 0286 KisKActionCollection* actionCollection() const; 0287 0288 QPoint documentOffset() const; 0289 0290 /** 0291 * @return the current position of the cursor fetched from QCursor::pos() and 0292 * converted into document coordinates 0293 */ 0294 virtual QPointF currentCursorPosition() const = 0; 0295 0296 protected: 0297 void setDocumentSize(const QSizeF &sz); 0298 QSizeF documentSize() const; 0299 0300 void setPreferredCenterFractionX(qreal); 0301 qreal preferredCenterFractionX() const; 0302 0303 void setPreferredCenterFractionY(qreal); 0304 qreal preferredCenterFractionY() const; 0305 0306 void setDocumentOffset( QPoint &offset); 0307 0308 0309 private: 0310 class Private; 0311 Private * const d; 0312 }; 0313 0314 0315 /** 0316 * Workaround class for the problem that Qt does not allow two QObject base classes. 0317 * KoCanvasController can be implemented by for instance QWidgets, so it cannot be 0318 * a QObject directly. The interface of this class should be considered public interface 0319 * for KoCanvasController. 0320 */ 0321 class KRITAFLAKE_EXPORT KoCanvasControllerProxyObject : public QObject 0322 { 0323 Q_OBJECT 0324 Q_DISABLE_COPY(KoCanvasControllerProxyObject) 0325 public: 0326 explicit KoCanvasControllerProxyObject(KoCanvasController *canvasController, QObject *parent = 0); 0327 0328 public: 0329 0330 // Convenience methods to invoke the signals from subclasses 0331 0332 void emitCanvasRemoved(KoCanvasController *canvasController) { emit canvasRemoved(canvasController); } 0333 void emitCanvasSet(KoCanvasController *canvasController) { emit canvasSet(canvasController); } 0334 void emitCanvasOffsetXChanged(int offset) { emit canvasOffsetXChanged(offset); } 0335 void emitCanvasOffsetYChanged(int offset) { emit canvasOffsetYChanged(offset); } 0336 void emitCanvasMousePositionChanged(const QPoint &position) { emit canvasMousePositionChanged(position); } 0337 void emitDocumentMousePositionChanged(const QPointF &position) { emit documentMousePositionChanged(position); } 0338 void emitSizeChanged(const QSize &size) { emit sizeChanged(size); } 0339 void emitMoveDocumentOffset(const QPoint &point) { emit moveDocumentOffset(point); } 0340 void emitZoomRelative(const qreal factor, const QPointF &stillPoint) { emit zoomRelative(factor, stillPoint); } 0341 0342 // Convenience method to retrieve the canvas controller for who needs to use QPointer 0343 KoCanvasController *canvasController() const { return m_canvasController; } 0344 0345 Q_SIGNALS: 0346 /** 0347 * Emitted when a previously added canvas is about to be removed. 0348 * @param canvasController this object 0349 */ 0350 void canvasRemoved(KoCanvasController *canvasController); 0351 0352 /** 0353 * Emitted when a canvas is set on this widget 0354 * @param canvasController this object 0355 */ 0356 void canvasSet(KoCanvasController *canvasController); 0357 0358 /** 0359 * Emitted when canvasOffsetX() changes 0360 * @param offset the new canvas offset 0361 */ 0362 void canvasOffsetXChanged(int offset); 0363 0364 /** 0365 * Emitted when canvasOffsetY() changes 0366 * @param offset the new canvas offset 0367 */ 0368 void canvasOffsetYChanged(int offset); 0369 0370 /** 0371 * Emitted when the cursor is moved over the canvas widget. 0372 * @param position the position in view coordinates (pixels). 0373 */ 0374 void canvasMousePositionChanged(const QPoint &position); 0375 0376 /** 0377 * Emitted when the cursor is moved over the canvas widget. 0378 * @param position the position in document coordinates. 0379 * 0380 * Use \ref canvasMousePositionChanged to get the position 0381 * in view coordinates. 0382 */ 0383 void documentMousePositionChanged(const QPointF &position); 0384 0385 /** 0386 * Emitted when the entire controller size changes 0387 * @param size the size in widget pixels. 0388 */ 0389 void sizeChanged(const QSize &size); 0390 0391 /** 0392 * Emitted whenever the document is scrolled. 0393 * 0394 * @param point the new top-left point from which the document should 0395 * be drawn. 0396 */ 0397 void moveDocumentOffset(const QPoint &point); 0398 0399 /** 0400 * Emitted when zoomRelativeToPoint have calculated a factor by which 0401 * the zoom should change and the point which should stand still 0402 * on screen. 0403 * Someone needs to connect to this and take action 0404 * 0405 * @param factor by how much the zoom needs to change. 0406 * @param stillPoint the point which will not change its position 0407 * in widget during the zooming. It is measured in 0408 * view coordinate system *before* zoom. 0409 */ 0410 void zoomRelative(const qreal factor, const QPointF &stillPoint); 0411 0412 public Q_SLOTS: 0413 /** 0414 * Call this slot whenever the size of your document in view coordinates (pixels) 0415 * changes, for instance when zooming. 0416 * @param newSize the new size, in view coordinates (pixels), of the document. 0417 * @param recalculateCenter if true the offset in the document we center on after calling 0418 * recenterPreferred() will be recalculated for the new document size so the visual offset stays the same. 0419 */ 0420 void updateDocumentSize(const QSize &newSize, bool recalculateCenter = true); 0421 0422 private: 0423 KoCanvasController *m_canvasController; 0424 }; 0425 0426 class KRITAFLAKE_EXPORT KoDummyCanvasController : public KoCanvasController { 0427 0428 public: 0429 0430 explicit KoDummyCanvasController(KisKActionCollection* actionCollection) 0431 : KoCanvasController(actionCollection) 0432 {} 0433 0434 ~KoDummyCanvasController() override 0435 {} 0436 0437 0438 void scrollContentsBy(int /*dx*/, int /*dy*/) override {} 0439 QSizeF viewportSize() const override { return QSizeF(); } 0440 void setCanvas(KoCanvasBase *canvas) override {Q_UNUSED(canvas)} 0441 KoCanvasBase *canvas() const override {return 0;} 0442 int visibleHeight() const override {return 0;} 0443 int visibleWidth() const override {return 0;} 0444 int canvasOffsetX() const override {return 0;} 0445 int canvasOffsetY() const override {return 0;} 0446 void ensureVisible(const QRectF &/*rect*/, bool /*smooth */ = false) override {} 0447 void ensureVisible(KoShape *shape) override {Q_UNUSED(shape)} 0448 void zoomIn(const QPoint &/*center*/) override {} 0449 void zoomOut(const QPoint &/*center*/) override {} 0450 void zoomBy(const QPoint &/*center*/, qreal /*zoom*/) override {} 0451 void zoomTo(const QRect &/*rect*/) override {} 0452 void recenterPreferred() override {} 0453 void setPreferredCenter(const QPointF &/*viewPoint*/) override {} 0454 QPointF preferredCenter() const override {return QPointF();} 0455 void pan(const QPoint &/*distance*/) override {} 0456 void panUp() override {} 0457 void panDown() override {} 0458 void panLeft() override {} 0459 void panRight() override {} 0460 QPoint scrollBarValue() const override {return QPoint();} 0461 void setScrollBarValue(const QPoint &/*value*/) override {} 0462 void resetScrollBars() override {} 0463 void updateDocumentSize(const QSizeF &/*sz*/, bool /*recalculateCenter*/) override {} 0464 void setZoomWithWheel(bool /*zoom*/) override {} 0465 void setVastScrolling(qreal /*factor*/) override {} 0466 QPointF currentCursorPosition() const override { return QPointF(); } 0467 }; 0468 0469 #endif