1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qbitmap.h" |
5 | #include "qevent.h" |
6 | #include "qstylepainter.h" |
7 | #include "qrubberband.h" |
8 | #include "qtimer.h" |
9 | |
10 | #include "qstyle.h" |
11 | #include "qstyleoption.h" |
12 | |
13 | #include <qdebug.h> |
14 | |
15 | #include <private/qwidget_p.h> |
16 | |
17 | QT_BEGIN_NAMESPACE |
18 | |
19 | //### a rubberband window type would be a more elegant solution |
20 | #define RUBBERBAND_WINDOW_TYPE Qt::ToolTip |
21 | |
22 | class QRubberBandPrivate : public QWidgetPrivate |
23 | { |
24 | Q_DECLARE_PUBLIC(QRubberBand) |
25 | public: |
26 | QRect rect; |
27 | QRubberBand::Shape shape; |
28 | QRegion clipping; |
29 | void updateMask(); |
30 | }; |
31 | |
32 | /*! |
33 | Initialize \a option with the values from this QRubberBand. This method |
34 | is useful for subclasses when they need a QStyleOptionRubberBand, but don't want |
35 | to fill in all the information themselves. |
36 | |
37 | \sa QStyleOption::initFrom() |
38 | */ |
39 | void QRubberBand::initStyleOption(QStyleOptionRubberBand *option) const |
40 | { |
41 | if (!option) |
42 | return; |
43 | option->initFrom(w: this); |
44 | option->shape = d_func()->shape; |
45 | #ifndef Q_OS_MAC |
46 | option->opaque = true; |
47 | #else |
48 | option->opaque = windowFlags() & RUBBERBAND_WINDOW_TYPE; |
49 | #endif |
50 | } |
51 | |
52 | /*! |
53 | \class QRubberBand |
54 | \brief The QRubberBand class provides a rectangle or line that can |
55 | indicate a selection or a boundary. |
56 | |
57 | \inmodule QtWidgets |
58 | |
59 | A rubber band is often used to show a new bounding area (as in a |
60 | QSplitter or a QDockWidget that is undocking). Historically this has |
61 | been implemented using a QPainter and XOR, but this approach |
62 | doesn't always work properly since rendering can happen in the |
63 | window below the rubber band, but before the rubber band has been |
64 | "erased". |
65 | |
66 | You can create a QRubberBand whenever you need to render a rubber band |
67 | around a given area (or to represent a single line), then call |
68 | setGeometry(), move() or resize() to position and size it. A common |
69 | pattern is to do this in conjunction with mouse events. For example: |
70 | |
71 | \snippet code/src_gui_widgets_qrubberband.cpp 0 |
72 | |
73 | If you pass a parent to QRubberBand's constructor, the rubber band will |
74 | display only inside its parent, but stays on top of other child widgets. |
75 | If no parent is passed, QRubberBand will act as a top-level widget. |
76 | |
77 | Call show() to make the rubber band visible; also when the |
78 | rubber band is not a top-level. Hiding or destroying |
79 | the widget will make the rubber band disappear. The rubber band |
80 | can be a \l Rectangle or a \l Line (vertical or horizontal), |
81 | depending on the shape() it was given when constructed. |
82 | */ |
83 | |
84 | // ### DOC: How about some nice convenience constructors? |
85 | //QRubberBand::QRubberBand(QRubberBand::Type t, const QRect &rect, QWidget *p) |
86 | //QRubberBand::QRubberBand(QRubberBand::Type t, int x, int y, int w, int h, QWidget *p) |
87 | |
88 | /*! |
89 | Constructs a rubber band of shape \a s, with parent \a p. |
90 | |
91 | By default a rectangular rubber band (\a s is \c Rectangle) will |
92 | use a mask, so that a small border of the rectangle is all |
93 | that is visible. Some styles (e.g., native \macos) will |
94 | change this and call QWidget::setWindowOpacity() to make a |
95 | semi-transparent filled selection rectangle. |
96 | */ |
97 | QRubberBand::QRubberBand(Shape s, QWidget *p) |
98 | : QWidget(*new QRubberBandPrivate, p, (p && p->windowType() != Qt::Desktop) ? Qt::Widget : RUBBERBAND_WINDOW_TYPE) |
99 | { |
100 | Q_D(QRubberBand); |
101 | d->shape = s; |
102 | setAttribute(Qt::WA_TransparentForMouseEvents); |
103 | setAttribute(Qt::WA_NoSystemBackground); |
104 | setAttribute(Qt::WA_WState_ExplicitShowHide); |
105 | setVisible(false); |
106 | } |
107 | |
108 | /*! |
109 | Destructor. |
110 | */ |
111 | QRubberBand::~QRubberBand() |
112 | { |
113 | } |
114 | |
115 | /*! |
116 | \enum QRubberBand::Shape |
117 | |
118 | This enum specifies what shape a QRubberBand should have. This is |
119 | a drawing hint that is passed down to the style system, and can be |
120 | interpreted by each QStyle. |
121 | |
122 | \value Line A QRubberBand can represent a vertical or horizontal |
123 | line. Geometry is still given in rect() and the line |
124 | will fill the given geometry on most styles. |
125 | |
126 | \value Rectangle A QRubberBand can represent a rectangle. Some |
127 | styles will interpret this as a filled (often |
128 | semi-transparent) rectangle, or a rectangular |
129 | outline. |
130 | */ |
131 | |
132 | /*! |
133 | Returns the shape of this rubber band. The shape can only be set |
134 | upon construction. |
135 | */ |
136 | QRubberBand::Shape QRubberBand::shape() const |
137 | { |
138 | Q_D(const QRubberBand); |
139 | return d->shape; |
140 | } |
141 | |
142 | /*! |
143 | \internal |
144 | */ |
145 | void QRubberBandPrivate::updateMask() |
146 | { |
147 | Q_Q(QRubberBand); |
148 | QStyleHintReturnMask mask; |
149 | QStyleOptionRubberBand opt; |
150 | q->initStyleOption(option: &opt); |
151 | if (q->style()->styleHint(stylehint: QStyle::SH_RubberBand_Mask, opt: &opt, widget: q, returnData: &mask)) { |
152 | q->setMask(mask.region); |
153 | } else { |
154 | q->clearMask(); |
155 | } |
156 | } |
157 | |
158 | /*! |
159 | \reimp |
160 | */ |
161 | void QRubberBand::paintEvent(QPaintEvent *) |
162 | { |
163 | QStylePainter painter(this); |
164 | QStyleOptionRubberBand option; |
165 | initStyleOption(option: &option); |
166 | painter.drawControl(ce: QStyle::CE_RubberBand, opt: option); |
167 | } |
168 | |
169 | /*! |
170 | \reimp |
171 | */ |
172 | void QRubberBand::changeEvent(QEvent *e) |
173 | { |
174 | QWidget::changeEvent(e); |
175 | switch (e->type()) { |
176 | case QEvent::ParentChange: |
177 | if (parent()) { |
178 | setWindowFlags(windowFlags() & ~RUBBERBAND_WINDOW_TYPE); |
179 | } else { |
180 | setWindowFlags(windowFlags() | RUBBERBAND_WINDOW_TYPE); |
181 | } |
182 | break; |
183 | default: |
184 | break; |
185 | } |
186 | |
187 | if (e->type() == QEvent::ZOrderChange) |
188 | raise(); |
189 | } |
190 | |
191 | /*! |
192 | \reimp |
193 | */ |
194 | void QRubberBand::showEvent(QShowEvent *e) |
195 | { |
196 | raise(); |
197 | e->ignore(); |
198 | } |
199 | |
200 | /*! |
201 | \reimp |
202 | */ |
203 | void QRubberBand::resizeEvent(QResizeEvent *) |
204 | { |
205 | Q_D(QRubberBand); |
206 | d->updateMask(); |
207 | } |
208 | |
209 | /*! |
210 | \reimp |
211 | */ |
212 | void QRubberBand::moveEvent(QMoveEvent *) |
213 | { |
214 | Q_D(QRubberBand); |
215 | d->updateMask(); |
216 | } |
217 | |
218 | /*! |
219 | \fn void QRubberBand::move(const QPoint &p); |
220 | |
221 | \overload |
222 | |
223 | Moves the rubberband to point \a p. |
224 | |
225 | \sa resize() |
226 | */ |
227 | |
228 | /*! |
229 | \fn void QRubberBand::move(int x, int y); |
230 | |
231 | Moves the rubberband to point (\a x, \a y). |
232 | |
233 | \sa resize() |
234 | */ |
235 | |
236 | /*! |
237 | \fn void QRubberBand::resize(const QSize &size); |
238 | |
239 | \overload |
240 | |
241 | Resizes the rubberband so that its new size is \a size. |
242 | |
243 | \sa move() |
244 | */ |
245 | |
246 | /*! |
247 | \fn void QRubberBand::resize(int width, int height); |
248 | |
249 | Resizes the rubberband so that its width is \a width, and its |
250 | height is \a height. |
251 | |
252 | \sa move() |
253 | */ |
254 | |
255 | /*! |
256 | \fn void QRubberBand::setGeometry(const QRect &rect) |
257 | |
258 | Sets the geometry of the rubber band to \a rect, specified in the coordinate system |
259 | of its parent widget. |
260 | |
261 | \sa QWidget::geometry |
262 | */ |
263 | void QRubberBand::setGeometry(const QRect &geom) |
264 | { |
265 | QWidget::setGeometry(geom); |
266 | } |
267 | |
268 | /*! |
269 | \fn void QRubberBand::setGeometry(int x, int y, int width, int height) |
270 | \overload |
271 | |
272 | Sets the geometry of the rubberband to the rectangle whose top-left corner lies at |
273 | the point (\a x, \a y), and with dimensions specified by \a width and \a height. |
274 | The geometry is specified in the parent widget's coordinate system. |
275 | */ |
276 | |
277 | /*! \reimp */ |
278 | bool QRubberBand::event(QEvent *e) |
279 | { |
280 | return QWidget::event(event: e); |
281 | } |
282 | |
283 | QT_END_NAMESPACE |
284 | |
285 | #include "moc_qrubberband.cpp" |
286 | |