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 &center) = 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 &center) = 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 &center, 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