1// Copyright (C) 2020 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 "qquickstyle.h"
5#include "qquickstyle_p.h"
6#include "qquickstyleoption.h"
7
8#include <QtGui/qpainter.h>
9#include <QtGui/qbitmap.h>
10#include <QtGui/qpixmapcache.h>
11#include <QtGui/qpa/qplatformtheme.h>
12
13#include <QtGui/private/qguiapplication_p.h>
14
15#ifndef QT_NO_DEBUG
16# include <QtCore/qdebug.h>
17#endif
18
19#include <limits.h>
20#include <algorithm>
21
22QT_BEGIN_NAMESPACE
23
24namespace QQC2 {
25
26/*!
27 Constructs a style object.
28*/
29QStyle::QStyle()
30 : QObject(*new QStylePrivate)
31{
32 Q_D(QStyle);
33 d->proxyStyle = this;
34}
35
36/*!
37 \internal
38
39 Constructs a style object.
40*/
41QStyle::QStyle(QStylePrivate &dd)
42 : QObject(dd)
43{
44 Q_D(QStyle);
45 d->proxyStyle = this;
46}
47
48/*!
49 Destroys the style object.
50*/
51QStyle::~QStyle()
52{
53}
54
55/*!
56 \fn QRect QStyle::itemTextRect(const QFontMetrics &metrics, const QRect &rectangle, int alignment, bool enabled, const QString &text) const
57
58 Returns the area within the given \a rectangle in which to draw
59 the provided \a text according to the specified font \a metrics
60 and \a alignment. The \a enabled parameter indicates whether or
61 not the associated item is enabled.
62
63 If the given \a rectangle is larger than the area needed to render
64 the \a text, the rectangle that is returned will be offset within
65 \a rectangle according to the specified \a alignment. For
66 example, if \a alignment is Qt::AlignCenter, the returned
67 rectangle will be centered within \a rectangle. If the given \a
68 rectangle is smaller than the area needed, the returned rectangle
69 will be the smallest rectangle large enough to render the \a text.
70
71 \sa Qt::Alignment
72*/
73QRect QStyle::itemTextRect(const QFontMetrics &metrics, const QRect &rect, int alignment, bool enabled,
74 const QString &text) const
75{
76 QRect result;
77 int x, y, w, h;
78 rect.getRect(ax: &x, ay: &y, aw: &w, ah: &h);
79 if (!text.isEmpty()) {
80 result = metrics.boundingRect(x, y, w, h, flags: alignment, text);
81 if (!enabled && proxy()->styleHint(stylehint: SH_EtchDisabledText)) {
82 result.setWidth(result.width()+1);
83 result.setHeight(result.height()+1);
84 }
85 } else {
86 result = QRect(x, y, w, h);
87 }
88 return result;
89}
90
91/*!
92 \fn QRect QStyle::itemPixmapRect(const QRect &rectangle, int alignment, const QPixmap &pixmap) const
93
94 Returns the area within the given \a rectangle in which to draw
95 the specified \a pixmap according to the defined \a alignment.
96*/
97QRect QStyle::itemPixmapRect(const QRect &rect, int alignment, const QPixmap &pixmap) const
98{
99 QRect result;
100 int x, y, w, h;
101 rect.getRect(ax: &x, ay: &y, aw: &w, ah: &h);
102
103 const int pixmapWidth = pixmap.width()/pixmap.devicePixelRatio();
104 const int pixmapHeight = pixmap.height()/pixmap.devicePixelRatio();
105
106 if ((alignment & Qt::AlignVCenter) == Qt::AlignVCenter)
107 y += h/2 - pixmapHeight/2;
108 else if ((alignment & Qt::AlignBottom) == Qt::AlignBottom)
109 y += h - pixmapHeight;
110 if ((alignment & Qt::AlignRight) == Qt::AlignRight)
111 x += w - pixmapWidth;
112 else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter)
113 x += w/2 - pixmapWidth/2;
114 else if ((alignment & Qt::AlignLeft) != Qt::AlignLeft && QGuiApplication::isRightToLeft())
115 x += w - pixmapWidth;
116 result = QRect(x, y, pixmapWidth, pixmapHeight);
117 return result;
118}
119
120/*!
121 \fn void QStyle::drawItemText(QPainter *painter, const QRect &rectangle, int alignment, const QPalette &palette, bool enabled, const QString& text, QPalette::ColorRole textRole) const
122
123 Draws the given \a text in the specified \a rectangle using the
124 provided \a painter and \a palette.
125
126 The text is drawn using the painter's pen, and aligned and wrapped
127 according to the specified \a alignment. If an explicit \a
128 textRole is specified, the text is drawn using the \a palette's
129 color for the given role. The \a enabled parameter indicates
130 whether or not the item is enabled; when reimplementing this
131 function, the \a enabled parameter should influence how the item is
132 drawn.
133
134 \sa Qt::Alignment, drawItemPixmap()
135*/
136void QStyle::drawItemText(QPainter *painter, const QRect &rect, int alignment, const QPalette &pal,
137 bool enabled, const QString& text, QPalette::ColorRole textRole) const
138{
139 if (text.isEmpty())
140 return;
141 QPen savedPen;
142 if (textRole != QPalette::NoRole) {
143 savedPen = painter->pen();
144 painter->setPen(QPen(pal.brush(cr: textRole), savedPen.widthF()));
145 }
146 if (!enabled) {
147 if (proxy()->styleHint(stylehint: SH_DitherDisabledText)) {
148 QRect br;
149 painter->drawText(r: rect, flags: alignment, text, br: &br);
150 painter->fillRect(br, QBrush(painter->background().color(), Qt::Dense5Pattern));
151 return;
152 } else if (proxy()->styleHint(stylehint: SH_EtchDisabledText)) {
153 QPen pen = painter->pen();
154 painter->setPen(pal.light().color());
155 painter->drawText(r: rect.adjusted(xp1: 1, yp1: 1, xp2: 1, yp2: 1), flags: alignment, text);
156 painter->setPen(pen);
157 }
158 }
159 painter->drawText(r: rect, flags: alignment, text);
160 if (textRole != QPalette::NoRole)
161 painter->setPen(savedPen);
162}
163
164/*!
165 \fn void QStyle::drawItemPixmap(QPainter *painter, const QRect &rectangle, int alignment,
166 const QPixmap &pixmap) const
167
168 Draws the given \a pixmap in the specified \a rectangle, according
169 to the specified \a alignment, using the provided \a painter.
170
171 \sa drawItemText()
172*/
173
174void QStyle::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment,
175 const QPixmap &pixmap) const
176{
177 qreal scale = pixmap.devicePixelRatio();
178 QRect aligned = alignedRect(direction: QGuiApplication::layoutDirection(), alignment: QFlag(alignment), size: pixmap.size() / scale, rectangle: rect);
179 QRect inter = aligned.intersected(other: rect);
180
181 painter->drawPixmap(x: inter.x(), y: inter.y(), pm: pixmap, sx: inter.x() - aligned.x(), sy: inter.y() - aligned.y(), sw: inter.width() * scale, sh: inter.height() *scale);
182}
183
184/*!
185 \fn QRect QStyle::visualRect(Qt::LayoutDirection direction, const QRect &boundingRectangle, const QRect &logicalRectangle)
186
187 Returns the given \a logicalRectangle converted to screen
188 coordinates based on the specified \a direction. The \a
189 boundingRectangle is used when performing the translation.
190
191 This function is provided to support right-to-left desktops, and
192 is typically used in implementations of the subControlRect()
193 function.
194
195 \sa QWidget::layoutDirection
196*/
197QRect QStyle::visualRect(Qt::LayoutDirection direction, const QRect &boundingRect, const QRect &logicalRect)
198{
199 if (direction == Qt::LeftToRight)
200 return logicalRect;
201 QRect rect = logicalRect;
202 rect.translate(dx: 2 * (boundingRect.right() - logicalRect.right()) +
203 logicalRect.width() - boundingRect.width(), dy: 0);
204 return rect;
205}
206
207/*!
208 \fn QPoint QStyle::visualPos(Qt::LayoutDirection direction, const QRect &boundingRectangle, const QPoint &logicalPosition)
209
210 Returns the given \a logicalPosition converted to screen
211 coordinates based on the specified \a direction. The \a
212 boundingRectangle is used when performing the translation.
213
214 \sa QWidget::layoutDirection
215*/
216QPoint QStyle::visualPos(Qt::LayoutDirection direction, const QRect &boundingRect, const QPoint &logicalPos)
217{
218 if (direction == Qt::LeftToRight)
219 return logicalPos;
220 return QPoint(boundingRect.right() - logicalPos.x(), logicalPos.y());
221}
222
223/*!
224 Returns a new rectangle of the specified \a size that is aligned to the given \a
225 rectangle according to the specified \a alignment and \a direction.
226 */
227QRect QStyle::alignedRect(Qt::LayoutDirection direction, Qt::Alignment alignment, const QSize &size, const QRect &rectangle)
228{
229 alignment = visualAlignment(direction, alignment);
230 int x = rectangle.x();
231 int y = rectangle.y();
232 int w = size.width();
233 int h = size.height();
234 if ((alignment & Qt::AlignVCenter) == Qt::AlignVCenter)
235 y += rectangle.size().height()/2 - h/2;
236 else if ((alignment & Qt::AlignBottom) == Qt::AlignBottom)
237 y += rectangle.size().height() - h;
238 if ((alignment & Qt::AlignRight) == Qt::AlignRight)
239 x += rectangle.size().width() - w;
240 else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter)
241 x += rectangle.size().width()/2 - w/2;
242 return QRect(x, y, w, h);
243}
244
245/*!
246 Transforms an \a alignment of Qt::AlignLeft or Qt::AlignRight
247 without Qt::AlignAbsolute into Qt::AlignLeft or Qt::AlignRight with
248 Qt::AlignAbsolute according to the layout \a direction. The other
249 alignment flags are left untouched.
250
251 If no horizontal alignment was specified, the function returns the
252 default alignment for the given layout \a direction.
253
254 QWidget::layoutDirection
255*/
256Qt::Alignment QStyle::visualAlignment(Qt::LayoutDirection direction, Qt::Alignment alignment)
257{
258 return QGuiApplicationPrivate::visualAlignment(direction, alignment);
259}
260
261/*!
262 Converts the given \a logicalValue to a pixel position. The \a min
263 parameter maps to 0, \a max maps to \a span and other values are
264 distributed evenly in-between.
265
266 This function can handle the entire integer range without
267 overflow, providing that \a span is less than 4096.
268
269 By default, this function assumes that the maximum value is on the
270 right for horizontal items and on the bottom for vertical items.
271 Set the \a upsideDown parameter to true to reverse this behavior.
272
273 \sa sliderValueFromPosition()
274*/
275
276int QStyle::sliderPositionFromValue(int min, int max, int logicalValue, int span, bool upsideDown)
277{
278 if (span <= 0 || logicalValue < min || max <= min)
279 return 0;
280 if (logicalValue > max)
281 return upsideDown ? span : min;
282
283 uint range = max - min;
284 uint p = upsideDown ? max - logicalValue : logicalValue - min;
285
286 if (range > (uint)INT_MAX/4096) {
287 double dpos = (double(p))/(double(range)/span);
288 return int(dpos);
289 } else if (range > (uint)span) {
290 return (2 * p * span + range) / (2*range);
291 } else {
292 uint div = span / range;
293 uint mod = span % range;
294 return p * div + (2 * p * mod + range) / (2 * range);
295 }
296 // equiv. to (p * span) / range + 0.5
297 // no overflow because of this implicit assumption:
298 // span <= 4096
299}
300
301/*!
302 \fn int QStyle::sliderValueFromPosition(int min, int max, int position, int span, bool upsideDown)
303
304 Converts the given pixel \a position to a logical value. 0 maps to
305 the \a min parameter, \a span maps to \a max and other values are
306 distributed evenly in-between.
307
308 This function can handle the entire integer range without
309 overflow.
310
311 By default, this function assumes that the maximum value is on the
312 right for horizontal items and on the bottom for vertical
313 items. Set the \a upsideDown parameter to true to reverse this
314 behavior.
315
316 \sa sliderPositionFromValue()
317*/
318
319int QStyle::sliderValueFromPosition(int min, int max, int pos, int span, bool upsideDown)
320{
321 if (span <= 0 || pos <= 0)
322 return upsideDown ? max : min;
323 if (pos >= span)
324 return upsideDown ? min : max;
325
326 uint range = max - min;
327
328 if ((uint)span > range) {
329 int tmp = (2 * pos * range + span) / (2 * span);
330 return upsideDown ? max - tmp : tmp + min;
331 } else {
332 uint div = range / span;
333 uint mod = range % span;
334 int tmp = pos * div + (2 * pos * mod + span) / (2 * span);
335 return upsideDown ? max - tmp : tmp + min;
336 }
337 // equiv. to min + (pos*range)/span + 0.5
338 // no overflow because of this implicit assumption:
339 // pos <= span < sqrt(INT_MAX+0.0625)+0.25 ~ sqrt(INT_MAX)
340}
341
342/*!
343 Returns the style's standard palette.
344
345 Note that on systems that support system colors, the style's
346 standard palette is not used. In particular, the Windows
347 Vista and Mac styles do not use the standard palette, but make
348 use of native theme engines. With these styles, you should not set
349 the palette with QApplication::setPalette().
350
351 \sa QApplication::setPalette()
352 */
353QPalette QStyle::standardPalette() const
354{
355 QColor background = QColor(0xd4, 0xd0, 0xc8); // win 2000 grey
356
357 QColor light(background.lighter());
358 QColor dark(background.darker());
359 QColor mid(Qt::gray);
360 QPalette palette(Qt::black, background, light, dark, mid, Qt::black, Qt::white);
361 palette.setBrush(cg: QPalette::Disabled, cr: QPalette::WindowText, brush: dark);
362 palette.setBrush(cg: QPalette::Disabled, cr: QPalette::Text, brush: dark);
363 palette.setBrush(cg: QPalette::Disabled, cr: QPalette::ButtonText, brush: dark);
364 palette.setBrush(cg: QPalette::Disabled, cr: QPalette::Base, brush: background);
365 return palette;
366}
367
368//Windows and KDE allow menus to cover the taskbar, while GNOME and macOS don't
369bool QStylePrivate::useFullScreenForPopup()
370{
371 auto theme = QGuiApplicationPrivate::platformTheme();
372 return theme && theme->themeHint(hint: QPlatformTheme::UseFullScreenForPopupMenu).toBool();
373}
374
375} // namespace QQC2
376
377QT_END_NAMESPACE
378
379#include "moc_qquickstyle.cpp"
380

source code of qtdeclarative/src/quicknativestyle/qstyle/qquickstyle.cpp