1 /*
2     SPDX-FileCopyrightText: 2008 Aaron Seigo <aseigo@kde.org>
3     SPDX-FileCopyrightText: 2008 Marco Martin <notmart@gmail.com>
4 
5     SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #ifndef PLASMA_FRAMESVG_H
9 #define PLASMA_FRAMESVG_H
10 
11 #include <QObject>
12 #include <QPixmap>
13 
14 #include <plasma/plasma_export.h>
15 
16 #include <plasma/plasma.h>
17 #include <plasma/svg.h>
18 
19 class QPainter;
20 class QPoint;
21 class QPointF;
22 class QRect;
23 class QRectF;
24 class QSize;
25 class QSizeF;
26 class QMatrix;
27 
28 namespace Plasma
29 {
30 class FrameSvgPrivate;
31 
32 /**
33  * @class FrameSvg plasma/framesvg.h <Plasma/FrameSvg>
34  *
35  * @short Provides an SVG with borders.
36  *
37  * When using SVG images for a background of an object that may change
38  * its aspect ratio, such as a dialog, simply scaling a single image
39  * may not be enough.
40  *
41  * FrameSvg allows SVGs to provide several elements for borders as well
42  * as a central element, each of which are scaled individually.  These
43  * elements should be named
44  *
45  *  - @c center  - the central element, which will be scaled in both directions
46  *  - @c top     - the top border; the height is fixed, but it will be scaled
47  *                 horizontally to the same width as @c center
48  *  - @c bottom  - the bottom border; scaled in the same way as @c top
49  *  - @c left    - the left border; the width is fixed, but it will be scaled
50  *                 vertically to the same height as @c center
51  *  - @c right   - the right border; scaled in the same way as @c left
52  *  - @c topleft - fixed size; must be the same height as @c top and the same
53  *                 width as @c left
54  *  - @c bottomleft, @c topright, @c bottomright - similar to @c topleft
55  *
56  * @c center must exist, but all the others are optional.  @c topleft and
57  * @c topright will be ignored if @c top does not exist, and similarly for
58  * @c bottomleft and @c bottomright.
59  *
60  * @see Plasma::Svg
61  **/
62 class PLASMA_EXPORT FrameSvg : public Svg
63 {
64     Q_OBJECT
65 
66     Q_PROPERTY(EnabledBorders enabledBorders READ enabledBorders WRITE setEnabledBorders)
67 
68 public:
69     /**
70      * These flags represents what borders should be drawn
71      */
72     enum EnabledBorder {
73         NoBorder = 0,
74         TopBorder = 1,
75         BottomBorder = 2,
76         LeftBorder = 4,
77         RightBorder = 8,
78         AllBorders = TopBorder | BottomBorder | LeftBorder | RightBorder,
79     };
80     Q_DECLARE_FLAGS(EnabledBorders, EnabledBorder)
81     Q_FLAG(EnabledBorders)
82 
83     /**
84      * Constructs a new FrameSvg that paints the proper named subelements
85      * as borders. It may also be used as a regular Plasma::Svg object
86      * for direct access to elements in the Svg.
87      *
88      * @param parent options QObject to parent this to
89      *
90      * @related Plasma::Theme
91      */
92     explicit FrameSvg(QObject *parent = nullptr);
93     ~FrameSvg() override;
94 
95     /**
96      * Loads a new Svg
97      * @param imagePath the new file
98      */
99     Q_INVOKABLE void setImagePath(const QString &path) override;
100 
101     /**
102      * Sets what borders should be painted
103      * @param flags borders we want to paint
104      */
105     void setEnabledBorders(const EnabledBorders borders);
106 
107     /**
108      * Convenience method to get the enabled borders
109      * @return what borders are painted
110      */
111     EnabledBorders enabledBorders() const;
112 
113     /**
114      * Resize the frame maintaining the same border size
115      * @param size the new size of the frame
116      */
117     Q_INVOKABLE void resizeFrame(const QSizeF &size);
118 
119     /**
120      * @returns the size of the frame
121      */
122     Q_INVOKABLE QSizeF frameSize() const;
123 
124     /**
125      * Returns the margin size given the margin edge we want
126      * If the given margin is disabled, it will return 0.
127      * If you don't care about the margin being on or off, use fixedMarginSize()
128      * @param edge the margin edge we want, top, bottom, left or right
129      * @return the margin size
130      */
131     Q_INVOKABLE qreal marginSize(const Plasma::Types::MarginEdge edge) const;
132 
133     /**
134      * Convenience method that extracts the size of the four margins
135      * in the four output parameters
136      * The disabled margins will be 0.
137      * If you don't care about the margins being on or off, use getFixedMargins()
138      * @param left left margin size
139      * @param top top margin size
140      * @param right right margin size
141      * @param bottom bottom margin size
142      */
143     Q_INVOKABLE void getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const;
144 
145     /**
146      * Returns the margin size given the margin edge we want.
147      * Compared to marginSize(), this doesn't depend whether the margin is enabled or not
148      * @param edge the margin edge we want, top, bottom, left or right
149      * @return the margin size
150      */
151     Q_INVOKABLE qreal fixedMarginSize(const Plasma::Types::MarginEdge edge) const;
152 
153     /**
154      * Convenience method that extracts the size of the four margins
155      * in the four output parameters
156      * Compared to getMargins(), this doesn't depend whether the margins are enabled or not
157      * @param left left margin size
158      * @param top top margin size
159      * @param right right margin size
160      * @param bottom bottom margin size
161      */
162     Q_INVOKABLE void getFixedMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const;
163 
164     /**
165      * Returns the insets margin size given the margin edge we want.
166      * @param edge the margin edge we want, top, bottom, left or right
167      * @return the margin size
168      * @since 5.77
169      */
170     Q_INVOKABLE qreal insetSize(const Plasma::Types::MarginEdge edge) const;
171 
172     /**
173      * Convenience method that extracts the size of the four inset margins
174      * in the four output parameters
175      * @param left left margin size
176      * @param top top margin size
177      * @param right right margin size
178      * @param bottom bottom margin size
179      * @since 5.77
180      */
181     Q_INVOKABLE void getInset(qreal &left, qreal &top, qreal &right, qreal &bottom) const;
182 
183     /**
184      * @return the rectangle of the center element, taking the margins into account.
185      */
186     Q_INVOKABLE QRectF contentsRect() const;
187 
188     /**
189      * Sets the prefix (@see setElementPrefix) to 'north', 'south', 'west' and 'east'
190      * when the location is TopEdge, BottomEdge, LeftEdge and RightEdge,
191      * respectively. Clears the prefix in other cases.
192      *
193      * The prefix must exist in the SVG document, which means that this can only be
194      * called successfully after setImagePath is called.
195      * @param location location in the UI this frame will be drawn
196      */
197     Q_INVOKABLE void setElementPrefix(Plasma::Types::Location location);
198 
199     /**
200      * Sets the prefix for the SVG elements to be used for painting. For example,
201      * if prefix is 'active', then instead of using the 'top' element of the SVG
202      * file to paint the top border, 'active-top' element will be used. The same
203      * goes for other SVG elements.
204      *
205      * If the elements with prefixes are not present, the default ones are used.
206      * (for the sake of speed, the test is present only for the 'center' element)
207      *
208      * Setting the prefix manually resets the location to Floating.
209      *
210      * The prefix must exist in the SVG document, which means that this can only be
211      * called successfully after setImagePath is called.
212      *
213      * @param prefix prefix for the SVG elements that make up the frame
214      */
215     Q_INVOKABLE void setElementPrefix(const QString &prefix);
216 
217     /**
218      * @return true if the svg has the necessary elements with the given prefix
219      * to draw a frame
220      * @param prefix the given prefix we want to check if drawable (can have trailing '-' since 5.59)
221      */
222     Q_INVOKABLE bool hasElementPrefix(const QString &prefix) const;
223 
224     /**
225      * This is an overloaded method provided for convenience equivalent to
226      * hasElementPrefix("north"), hasElementPrefix("south")
227      * hasElementPrefix("west") and hasElementPrefix("east")
228      * @return true if the svg has the necessary elements with the given prefix
229      * to draw a frame.
230      * @param location the given prefix we want to check if drawable
231      */
232     Q_INVOKABLE bool hasElementPrefix(Plasma::Types::Location location) const;
233 
234     /**
235      * Returns the prefix for SVG elements of the FrameSvg (including a '-' at the end if not empty)
236      * @return the prefix
237      * @sa actualPrefix()
238      */
239     Q_INVOKABLE QString prefix();
240 
241     /**
242      * Returns a mask that tightly contains the fully opaque areas of the svg
243      * @return a region of opaque areas
244      */
245     Q_INVOKABLE QRegion mask() const;
246 
247     /**
248      * @return a pixmap whose alpha channel is the opacity of the frame. It may be the frame itself or a special frame with the mask- prefix
249      */
250     QPixmap alphaMask() const;
251 
252     /**
253      * Sets whether saving all the rendered prefixes in a cache or not
254      * @param cache if use the cache or not
255      */
256     Q_INVOKABLE void setCacheAllRenderedFrames(bool cache);
257 
258     /**
259      * @return if all the different prefixes should be kept in a cache when rendered
260      */
261     Q_INVOKABLE bool cacheAllRenderedFrames() const;
262 
263     /**
264      * Deletes the internal cache freeing memory: use this if you want to switch the rendered
265      * element and you don't plan to switch back to the previous one for a long time and you
266      * used setUsingRenderingCache(true)
267      */
268     Q_INVOKABLE void clearCache();
269 
270     /**
271      * Returns a pixmap of the SVG represented by this object.
272      *
273      * @param elelementId the ID string of the element to render, or an empty
274      *                  string for the whole SVG (the default)
275      * @return a QPixmap of the rendered SVG
276      */
277     Q_INVOKABLE QPixmap framePixmap();
278 
279     /**
280      * Paints the loaded SVG with the elements that represents the border
281      * @param painter the QPainter to use
282      * @param target the target rectangle on the paint device
283      * @param source the portion rectangle of the source image
284      */
285     Q_INVOKABLE void paintFrame(QPainter *painter, const QRectF &target, const QRectF &source = QRectF());
286 
287     /**
288      * Paints the loaded SVG with the elements that represents the border
289      * This is an overloaded member provided for convenience
290      * @param painter the QPainter to use
291      * @param pos where to paint the svg
292      */
293     Q_INVOKABLE void paintFrame(QPainter *painter, const QPointF &pos = QPointF(0, 0));
294 
295     /**
296      * @returns the prefix that is actually used (including a '-' at the end if not empty)
297      * @sa prefix()
298      */
299     QString actualPrefix() const;
300 
301     /**
302      * @returns true if we are in a transaction of many changes at once
303      * and we don't want to rebuild the generated graphics for each change yet
304      * @since 5.31
305      */
306     bool isRepaintBlocked() const;
307 
308     /**
309      * If we will do several changes at once in the frame properties,
310      * such as prefix, enabled borders and size, in order to not regenerate
311      * the graphics for each change, set this property to true, and set
312      * it to false again after applying all the changes needed.
313      * Note that any change will not be visible in the painted frame while this property is set to true.
314      * @since 5.31
315      */
316     void setRepaintBlocked(bool blocked);
317 
318 private:
319     FrameSvgPrivate *const d;
320     friend class FrameData;
321 
322     // Q_PRIVATE_SLOT(d, void updateSizes())
323 };
324 
325 Q_DECLARE_OPERATORS_FOR_FLAGS(FrameSvg::EnabledBorders)
326 
327 } // Plasma namespace
328 
329 #endif // multiple inclusion guard
330