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 "qquickstyleitem.h" |
5 | |
6 | #include <QtCore/qscopedvaluerollback.h> |
7 | #include <QtCore/qdir.h> |
8 | |
9 | #include <QtQuick/qsgninepatchnode.h> |
10 | #include <QtQuick/private/qquickwindow_p.h> |
11 | #include <QtQuick/qquickwindow.h> |
12 | #include <QtQuick/qquickrendercontrol.h> |
13 | |
14 | #include <QtQuickTemplates2/private/qquickcontrol_p.h> |
15 | #include <QtQuickTemplates2/private/qquickbutton_p.h> |
16 | |
17 | #include <QtQml/qqml.h> |
18 | |
19 | #include "qquickstyleitembutton.h" |
20 | #include "qquickstylehelper_p.h" |
21 | |
22 | QT_BEGIN_NAMESPACE |
23 | |
24 | QDebug operator<<(QDebug debug, const QQuickStyleMargins &padding) |
25 | { |
26 | QDebugStateSaver saver(debug); |
27 | debug.nospace(); |
28 | debug << "StyleMargins(" ; |
29 | debug << padding.left() << ", " ; |
30 | debug << padding.top() << ", " ; |
31 | debug << padding.right() << ", " ; |
32 | debug << padding.bottom(); |
33 | debug << ')'; |
34 | return debug; |
35 | } |
36 | |
37 | QDebug operator<<(QDebug debug, const StyleItemGeometry &cg) |
38 | { |
39 | QDebugStateSaver saver(debug); |
40 | debug.nospace(); |
41 | debug << "StyleItemGeometry(" ; |
42 | debug << "implicitSize:" << cg.implicitSize << ", " ; |
43 | debug << "contentRect:" << cg.contentRect << ", " ; |
44 | debug << "layoutRect:" << cg.layoutRect << ", " ; |
45 | debug << "minimumSize:" << cg.minimumSize << ", " ; |
46 | debug << "9patchMargins:" << cg.ninePatchMargins; |
47 | debug << ')'; |
48 | return debug; |
49 | } |
50 | |
51 | int QQuickStyleItem::dprAlignedSize(const int size) const |
52 | { |
53 | // Return the first value equal to or bigger than size |
54 | // that is a whole number when multiplied with the dpr. |
55 | static int multiplier = [&]() { |
56 | const qreal dpr = window()->effectiveDevicePixelRatio(); |
57 | for (int m = 1; m <= 10; ++m) { |
58 | const qreal v = m * dpr; |
59 | if (v == int(v)) |
60 | return m; |
61 | } |
62 | |
63 | qWarning() << "The current dpr (" << dpr << ") is not supported" |
64 | << "by the style and might result in drawing artifacts" ; |
65 | return 1; |
66 | }(); |
67 | |
68 | return int(qCeil(v: qreal(size) / qreal(multiplier)) * multiplier); |
69 | } |
70 | |
71 | QQuickStyleItem::QQuickStyleItem(QQuickItem *parent) |
72 | : QQuickItem(parent) |
73 | { |
74 | setFlag(flag: QQuickItem::ItemHasContents); |
75 | } |
76 | |
77 | QQuickStyleItem::~QQuickStyleItem() |
78 | { |
79 | } |
80 | |
81 | void QQuickStyleItem::connectToControl() const |
82 | { |
83 | connect(sender: m_control, signal: &QQuickStyleItem::enabledChanged, context: this, slot: &QQuickStyleItem::markImageDirty); |
84 | connect(sender: m_control, signal: &QQuickItem::activeFocusChanged, context: this, slot: &QQuickStyleItem::markImageDirty); |
85 | |
86 | if (QQuickWindow *win = window()) { |
87 | connect(sender: win, signal: &QQuickWindow::activeChanged, context: this, slot: &QQuickStyleItem::markImageDirty); |
88 | m_connectedWindow = win; |
89 | } |
90 | } |
91 | |
92 | void QQuickStyleItem::markImageDirty() |
93 | { |
94 | m_dirty.setFlag(flag: DirtyFlag::Image); |
95 | if (isComponentComplete()) |
96 | polish(); |
97 | } |
98 | |
99 | void QQuickStyleItem::markGeometryDirty() |
100 | { |
101 | m_dirty.setFlag(flag: DirtyFlag::Geometry); |
102 | if (isComponentComplete()) |
103 | polish(); |
104 | } |
105 | |
106 | QSGNode *QQuickStyleItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *) |
107 | { |
108 | QSGNinePatchNode *node = static_cast<QSGNinePatchNode *>(oldNode); |
109 | if (!node) |
110 | node = window()->createNinePatchNode(); |
111 | |
112 | if (m_paintedImage.isNull()) { |
113 | // If we cannot create a texture, the node should not exist either |
114 | // because its material requires a texture. |
115 | delete node; |
116 | return nullptr; |
117 | } |
118 | |
119 | const auto texture = window()->createTextureFromImage(image: m_paintedImage, options: QQuickWindow::TextureCanUseAtlas); |
120 | |
121 | QRectF bounds = boundingRect(); |
122 | const qreal dpr = window()->effectiveDevicePixelRatio(); |
123 | const QSizeF unscaledImageSize = QSizeF(m_paintedImage.size()) / dpr; |
124 | |
125 | // We can scale the image up with a nine patch node, but should |
126 | // avoid to scale it down. Otherwise the nine patch image will look |
127 | // wrapped (or look truncated, in case of no padding). So if the |
128 | // item is smaller that the image, don't scale. |
129 | if (bounds.width() < unscaledImageSize.width()) |
130 | bounds.setWidth(unscaledImageSize.width()); |
131 | if (bounds.height() < unscaledImageSize.height()) |
132 | bounds.setHeight(unscaledImageSize.height()); |
133 | |
134 | #ifdef QT_DEBUG |
135 | if (m_debugFlags.testFlag(flag: Unscaled)) { |
136 | bounds.setSize(unscaledImageSize); |
137 | qqc2Info() << "Setting qsg node size to the unscaled size of m_paintedImage:" << bounds; |
138 | } |
139 | #endif |
140 | |
141 | if (m_useNinePatchImage) { |
142 | QMargins padding = m_styleItemGeometry.ninePatchMargins; |
143 | if (padding.right() == -1) { |
144 | // Special case: a padding of -1 means that |
145 | // the image shouldn't scale in the given direction. |
146 | padding.setLeft(0); |
147 | padding.setRight(0); |
148 | } |
149 | if (padding.bottom() == -1) { |
150 | padding.setTop(0); |
151 | padding.setBottom(0); |
152 | } |
153 | node->setPadding(left: padding.left(), top: padding.top(), right: padding.right(), bottom: padding.bottom()); |
154 | } |
155 | |
156 | node->setBounds(bounds); |
157 | node->setTexture(texture); |
158 | node->setDevicePixelRatio(dpr); |
159 | node->update(); |
160 | |
161 | return node; |
162 | } |
163 | |
164 | QStyle::State QQuickStyleItem::controlSize(QQuickItem *item) |
165 | { |
166 | // TODO: add proper API for small and mini |
167 | if (item->metaObject()->indexOfProperty(name: "qqc2_style_small" ) != -1) |
168 | return QStyle::State_Small; |
169 | if (item->metaObject()->indexOfProperty(name: "qqc2_style_mini" ) != -1) |
170 | return QStyle::State_Mini; |
171 | return QStyle::State_None; |
172 | } |
173 | |
174 | static QWindow *effectiveWindow(QQuickWindow *window) |
175 | { |
176 | QWindow *renderWindow = QQuickRenderControl::renderWindowFor(win: window); |
177 | return renderWindow ? renderWindow : window; |
178 | } |
179 | |
180 | void QQuickStyleItem::initStyleOptionBase(QStyleOption &styleOption) const |
181 | { |
182 | Q_ASSERT(m_control); |
183 | |
184 | styleOption.control = const_cast<QQuickItem *>(control<QQuickItem>()); |
185 | styleOption.window = effectiveWindow(window: window()); |
186 | styleOption.palette = QQuickItemPrivate::get(item: m_control)->palette()->toQPalette(); |
187 | styleOption.rect = QRect(QPoint(0, 0), imageSize()); |
188 | |
189 | styleOption.state = QStyle::State_None; |
190 | styleOption.state |= controlSize(item: styleOption.control); |
191 | |
192 | // Note: not all controls inherit from QQuickControl (e.g QQuickTextField) |
193 | if (const auto quickControl = dynamic_cast<QQuickControl *>(m_control.data())) |
194 | styleOption.direction = quickControl->isMirrored() ? Qt::RightToLeft : Qt::LeftToRight; |
195 | |
196 | if (styleOption.window) { |
197 | if (styleOption.window->isActive()) |
198 | styleOption.state |= QStyle::State_Active; |
199 | if (m_control->isEnabled()) |
200 | styleOption.state |= QStyle::State_Enabled; |
201 | if (m_control->hasActiveFocus()) |
202 | styleOption.state |= QStyle::State_HasFocus; |
203 | if (m_control->isUnderMouse()) |
204 | styleOption.state |= QStyle::State_MouseOver; |
205 | // Should this depend on the focusReason (e.g. only TabFocus) ? |
206 | styleOption.state |= QStyle::State_KeyboardFocusChange; |
207 | } |
208 | |
209 | if (m_overrideState != None) { |
210 | // In Button.qml we fade between two versions of |
211 | // the handle, depending on if it's hovered or not |
212 | if (m_overrideState & AlwaysHovered) |
213 | styleOption.state |= QStyle::State_MouseOver; |
214 | else if (m_overrideState & NeverHovered) |
215 | styleOption.state &= ~QStyle::State_MouseOver; |
216 | } |
217 | |
218 | qqc2Info() << styleOption; |
219 | } |
220 | |
221 | void QQuickStyleItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) |
222 | { |
223 | QQuickItem::geometryChange(newGeometry, oldGeometry); |
224 | |
225 | // Ensure that we only schedule a new geometry update |
226 | // and polish if this geometry change was caused by |
227 | // something else than us already updating geometry. |
228 | if (!m_polishing) |
229 | markGeometryDirty(); |
230 | } |
231 | |
232 | void QQuickStyleItem::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) |
233 | { |
234 | QQuickItem::itemChange(change, data); |
235 | |
236 | switch (change) { |
237 | case QQuickItem::ItemVisibleHasChanged: |
238 | if (data.boolValue) |
239 | markImageDirty(); |
240 | break; |
241 | case QQuickItem::ItemSceneChange: { |
242 | markImageDirty(); |
243 | QQuickWindow *win = data.window; |
244 | if (m_connectedWindow) |
245 | disconnect(sender: m_connectedWindow, signal: &QQuickWindow::activeChanged, receiver: this, slot: &QQuickStyleItem::markImageDirty); |
246 | if (win) |
247 | connect(sender: win, signal: &QQuickWindow::activeChanged, context: this, slot: &QQuickStyleItem::markImageDirty); |
248 | m_connectedWindow = win; |
249 | break;} |
250 | default: |
251 | break; |
252 | } |
253 | } |
254 | |
255 | bool QQuickStyleItem::event(QEvent *event) |
256 | { |
257 | if (event->type() == QEvent::ApplicationPaletteChange) { |
258 | markImageDirty(); |
259 | if (auto *style = QQuickStyleItem::style()) |
260 | style->polish(); |
261 | } |
262 | |
263 | return QQuickItem::event(event); |
264 | } |
265 | |
266 | void QQuickStyleItem::updateGeometry() |
267 | { |
268 | qqc2InfoHeading("GEOMETRY" ); |
269 | m_dirty.setFlag(flag: DirtyFlag::Geometry, on: false); |
270 | |
271 | const QQuickStyleMargins oldContentPadding = contentPadding(); |
272 | const QQuickStyleMargins oldLayoutMargins = layoutMargins(); |
273 | const QSize oldMinimumSize = minimumSize(); |
274 | |
275 | m_styleItemGeometry = calculateGeometry(); |
276 | |
277 | #ifdef QT_DEBUG |
278 | if (m_styleItemGeometry.minimumSize.isEmpty()) |
279 | qmlWarning(me: this) << "(StyleItem) minimumSize is empty!" ; |
280 | #endif |
281 | |
282 | if (m_styleItemGeometry.implicitSize.isEmpty()) { |
283 | // If the item has no contents (or its size is |
284 | // empty), we just use the minimum size as implicit size. |
285 | m_styleItemGeometry.implicitSize = m_styleItemGeometry.minimumSize; |
286 | qqc2Info() << "implicitSize is empty, using minimumSize instead" ; |
287 | } |
288 | |
289 | #ifdef QT_DEBUG |
290 | if (m_styleItemGeometry.implicitSize.width() < m_styleItemGeometry.minimumSize.width()) |
291 | qmlWarning(me: this) << "(StyleItem) implicit width is smaller than minimum width!" ; |
292 | if (m_styleItemGeometry.implicitSize.height() < m_styleItemGeometry.minimumSize.height()) |
293 | qmlWarning(me: this) << "(StyleItem) implicit height is smaller than minimum height!" ; |
294 | #endif |
295 | |
296 | if (contentPadding() != oldContentPadding) |
297 | emit contentPaddingChanged(); |
298 | if (layoutMargins() != oldLayoutMargins) |
299 | emit layoutMarginsChanged(); |
300 | if (minimumSize() != oldMinimumSize) |
301 | emit minimumSizeChanged(); |
302 | |
303 | setImplicitSize(m_styleItemGeometry.implicitSize.width(), m_styleItemGeometry.implicitSize.height()); |
304 | |
305 | qqc2Info() << m_styleItemGeometry |
306 | << "bounding rect:" << boundingRect() |
307 | << "layout margins:" << layoutMargins() |
308 | << "content padding:" << contentPadding() |
309 | << "input content size:" << m_contentSize; |
310 | } |
311 | |
312 | void QQuickStyleItem::paintControlToImage() |
313 | { |
314 | qqc2InfoHeading("PAINT" ); |
315 | const QSize imgSize = imageSize(); |
316 | if (imgSize.isEmpty()) |
317 | return; |
318 | |
319 | m_dirty.setFlag(flag: DirtyFlag::Image, on: false); |
320 | |
321 | // The size of m_paintedImage should normally be imgSize * dpr. The problem is |
322 | // that the dpr can be e.g 1.25, which means that the size can end up having a |
323 | // fraction. But an image cannot have a size with a fraction, so it would need |
324 | // to be rounded. But on the flip side, rounding the size means that the size |
325 | // of the scene graph node (which is, when the texture is not scaled, |
326 | // m_paintedImage.size() / dpr), will end up with a fraction instead. And this |
327 | // causes rendering artifacts in the scene graph when the texture is mapped |
328 | // to physical screen coordinates. So for that reason we calculate an image size |
329 | // that might be slightly larger than imgSize, so that imgSize * dpr lands on a |
330 | // whole number. The result is that neither the image size, nor the scene graph |
331 | // node, ends up with a size that has a fraction. |
332 | const qreal dpr = window()->effectiveDevicePixelRatio(); |
333 | const int alignedW = int(dprAlignedSize(size: imgSize.width()) * dpr); |
334 | const int alignedH = int(dprAlignedSize(size: imgSize.height()) * dpr); |
335 | const QSize alignedSize = QSize(alignedW, alignedH); |
336 | |
337 | if (m_paintedImage.size() != alignedSize) { |
338 | m_paintedImage = QImage(alignedSize, QImage::Format_ARGB32_Premultiplied); |
339 | m_paintedImage.setDevicePixelRatio(dpr); |
340 | qqc2Info() << "created image with dpr aligned size:" << alignedSize; |
341 | } |
342 | |
343 | m_paintedImage.fill(color: Qt::transparent); |
344 | |
345 | QPainter painter(&m_paintedImage); |
346 | paintEvent(painter: &painter); |
347 | |
348 | #ifdef QT_DEBUG |
349 | if (m_debugFlags != NoDebug) { |
350 | painter.setPen(QColor(255, 0, 0, 255)); |
351 | if (m_debugFlags.testFlag(flag: ImageRect)) |
352 | painter.drawRect(r: QRect(QPoint(0, 0), alignedSize / dpr)); |
353 | if (m_debugFlags.testFlag(flag: LayoutRect)) { |
354 | const auto m = layoutMargins(); |
355 | QRect rect = QRect(QPoint(0, 0), imgSize); |
356 | rect.adjust(dx1: m.left(), dy1: m.top(), dx2: -m.right(), dy2: -m.bottom()); |
357 | painter.drawRect(r: rect); |
358 | } |
359 | if (m_debugFlags.testFlag(flag: ContentRect)) { |
360 | const auto p = contentPadding(); |
361 | QRect rect = QRect(QPoint(0, 0), imgSize); |
362 | rect.adjust(dx1: p.left(), dy1: p.top(), dx2: -p.right(), dy2: -p.bottom()); |
363 | painter.drawRect(r: rect); |
364 | } |
365 | if (m_debugFlags.testFlag(flag: InputContentSize)) { |
366 | const int offset = 2; |
367 | const QPoint p = m_styleItemGeometry.contentRect.topLeft(); |
368 | painter.drawLine(x1: p.x() - offset, y1: p.y() - offset, x2: p.x() + m_contentSize.width(), y2: p.y() - offset); |
369 | painter.drawLine(x1: p.x() - offset, y1: p.y() - offset, x2: p.x() - offset, y2: p.y() + m_contentSize.height()); |
370 | } |
371 | if (m_debugFlags.testFlag(flag: NinePatchMargins)) { |
372 | const QMargins m = m_styleItemGeometry.ninePatchMargins; |
373 | if (m.right() != -1) { |
374 | painter.drawLine(x1: m.left(), y1: 0, x2: m.left(), y2: imgSize.height()); |
375 | painter.drawLine(x1: imgSize.width() - m.right(), y1: 0, x2: imgSize.width() - m.right(), y2: imgSize.height()); |
376 | } |
377 | if (m.bottom() != -1) { |
378 | painter.drawLine(x1: 0, y1: m.top(), x2: imgSize.width(), y2: m.top()); |
379 | painter.drawLine(x1: 0, y1: imgSize.height() - m.bottom(), x2: imgSize.width(), y2: imgSize.height() - m.bottom()); |
380 | } |
381 | } |
382 | if (m_debugFlags.testFlag(flag: SaveImage)) { |
383 | static int nr = -1; |
384 | ++nr; |
385 | static QString filename = QStringLiteral("styleitem_saveimage_" ); |
386 | const QString path = QDir::current().absoluteFilePath(fileName: filename); |
387 | const QString name = path + QString::number(nr) + QStringLiteral(".png" ); |
388 | m_paintedImage.save(fileName: name); |
389 | qDebug() << "image saved to:" << name; |
390 | } |
391 | } |
392 | #endif |
393 | |
394 | update(); |
395 | } |
396 | |
397 | void QQuickStyleItem::updatePolish() |
398 | { |
399 | QScopedValueRollback<bool> guard(m_polishing, true); |
400 | |
401 | const bool dirtyGeometry = m_dirty & DirtyFlag::Geometry; |
402 | const bool dirtyImage = isVisible() && ((m_dirty & DirtyFlag::Image) || (!m_useNinePatchImage && dirtyGeometry)); |
403 | |
404 | if (dirtyGeometry) |
405 | updateGeometry(); |
406 | if (dirtyImage) |
407 | paintControlToImage(); |
408 | } |
409 | |
410 | #ifdef QT_DEBUG |
411 | void QQuickStyleItem::addDebugInfo() |
412 | { |
413 | // Example debug strings: |
414 | // "QQC2_NATIVESTYLE_DEBUG="myButton info contentRect" |
415 | // "QQC2_NATIVESTYLE_DEBUG="ComboBox ninepatchmargins" |
416 | // "QQC2_NATIVESTYLE_DEBUG="All layoutrect" |
417 | |
418 | static const auto debugString = qEnvironmentVariable(varName: "QQC2_NATIVESTYLE_DEBUG" ); |
419 | static const auto matchAll = debugString.startsWith(s: QLatin1String("All " )); |
420 | static const auto prefix = QStringLiteral("QQuickStyleItem" ); |
421 | if (debugString.isEmpty()) |
422 | return; |
423 | |
424 | const auto objectName = m_control->objectName(); |
425 | const auto typeName = QString::fromUtf8(utf8: metaObject()->className()).remove(s: prefix); |
426 | const bool matchName = !objectName.isEmpty() && debugString.startsWith(s: objectName); |
427 | const bool matchType = debugString.startsWith(s: typeName); |
428 | |
429 | if (!(matchAll || matchName || matchType)) |
430 | return; |
431 | |
432 | #define QQC2_DEBUG_FLAG(FLAG) \ |
433 | if (debugString.contains(QLatin1String(#FLAG), Qt::CaseInsensitive)) m_debugFlags |= FLAG |
434 | |
435 | QQC2_DEBUG_FLAG(Info); |
436 | QQC2_DEBUG_FLAG(ImageRect); |
437 | QQC2_DEBUG_FLAG(ContentRect); |
438 | QQC2_DEBUG_FLAG(LayoutRect); |
439 | QQC2_DEBUG_FLAG(InputContentSize); |
440 | QQC2_DEBUG_FLAG(DontUseNinePatchImage); |
441 | QQC2_DEBUG_FLAG(NinePatchMargins); |
442 | QQC2_DEBUG_FLAG(Unscaled); |
443 | QQC2_DEBUG_FLAG(Debug); |
444 | QQC2_DEBUG_FLAG(SaveImage); |
445 | |
446 | if (m_debugFlags & (DontUseNinePatchImage |
447 | | InputContentSize |
448 | | ContentRect |
449 | | LayoutRect |
450 | | NinePatchMargins)) { |
451 | // Some rects will not fit inside the drawn image unless |
452 | // we switch off (nine patch) image scaling. |
453 | m_debugFlags |= DontUseNinePatchImage; |
454 | m_useNinePatchImage = false; |
455 | } |
456 | |
457 | if (m_debugFlags != NoDebug) |
458 | qDebug() << "debug options set for" << typeName << "(" << objectName << "):" << m_debugFlags; |
459 | else |
460 | qDebug() << "available debug options:" << DebugFlags(0xFFFF); |
461 | } |
462 | #endif |
463 | |
464 | void QQuickStyleItem::componentComplete() |
465 | { |
466 | Q_ASSERT_X(m_control, Q_FUNC_INFO, "You need to assign a value to property 'control'" ); |
467 | #ifdef QT_DEBUG |
468 | addDebugInfo(); |
469 | #endif |
470 | QQuickItem::componentComplete(); |
471 | updateGeometry(); |
472 | connectToControl(); |
473 | polish(); |
474 | } |
475 | |
476 | qreal QQuickStyleItem::contentWidth() |
477 | { |
478 | return m_contentSize.width(); |
479 | } |
480 | |
481 | void QQuickStyleItem::setContentWidth(qreal contentWidth) |
482 | { |
483 | if (qFuzzyCompare(p1: m_contentSize.width(), p2: contentWidth)) |
484 | return; |
485 | |
486 | m_contentSize.setWidth(contentWidth); |
487 | markGeometryDirty(); |
488 | } |
489 | |
490 | qreal QQuickStyleItem::contentHeight() |
491 | { |
492 | return m_contentSize.height(); |
493 | } |
494 | |
495 | void QQuickStyleItem::setContentHeight(qreal contentHeight) |
496 | { |
497 | if (qFuzzyCompare(p1: m_contentSize.height(), p2: contentHeight)) |
498 | return; |
499 | |
500 | m_contentSize.setHeight(contentHeight); |
501 | markGeometryDirty(); |
502 | } |
503 | |
504 | QQuickStyleMargins QQuickStyleItem::contentPadding() const |
505 | { |
506 | const QRect outerRect(QPoint(0, 0), m_styleItemGeometry.implicitSize); |
507 | return QQuickStyleMargins(outerRect, m_styleItemGeometry.contentRect); |
508 | } |
509 | |
510 | QQuickStyleMargins QQuickStyleItem::layoutMargins() const |
511 | { |
512 | // ### TODO: layoutRect is currently not being used for anything. But |
513 | // eventually this information will be needed by layouts to align the controls |
514 | // correctly. This because the images drawn by QStyle are usually a bit bigger |
515 | // than the control(frame) itself, to e.g make room for shadow effects |
516 | // or focus rects/glow. And this will differ from control to control. The |
517 | // layoutRect will then inform where the frame of the control is. |
518 | QQuickStyleMargins margins; |
519 | if (m_styleItemGeometry.layoutRect.isValid()) { |
520 | const QRect outerRect(QPoint(0, 0), m_styleItemGeometry.implicitSize); |
521 | margins = QQuickStyleMargins(outerRect, m_styleItemGeometry.layoutRect); |
522 | } |
523 | return margins; |
524 | } |
525 | |
526 | QSize QQuickStyleItem::minimumSize() const |
527 | { |
528 | // The style item should not be scaled below this size. |
529 | // Otherwise the image will be truncated. |
530 | return m_styleItemGeometry.minimumSize; |
531 | } |
532 | |
533 | QSize QQuickStyleItem::imageSize() const |
534 | { |
535 | // Returns the size of the QImage (unscaled) that |
536 | // is used to draw the control from QStyle. |
537 | return m_useNinePatchImage ? m_styleItemGeometry.minimumSize : size().toSize(); |
538 | } |
539 | |
540 | qreal QQuickStyleItem::focusFrameRadius() const |
541 | { |
542 | return m_styleItemGeometry.focusFrameRadius; |
543 | } |
544 | |
545 | QFont QQuickStyleItem::styleFont(QQuickItem *control) const |
546 | { |
547 | Q_ASSERT(control); |
548 | // Note: This function should be treated as if it was static |
549 | // (meaning, don't assume anything in this object to be initialized). |
550 | // Resolving the font/font size should be done early on from QML, before we get |
551 | // around to calculate geometry and paint. Otherwise we typically need to do it |
552 | // all over again when/if the font changes. In practice this means that other |
553 | // items in QML that uses a style font, and at the same time, affects our input |
554 | // contentSize, cannot wait for this item to be fully constructed before it |
555 | // gets the font. So we need to resolve it here and now, even if this |
556 | // object might be in a half initialized state (hence also the control |
557 | // argument, instead of relying on m_control to be set). |
558 | return QGuiApplication::font(); |
559 | } |
560 | |
561 | QT_END_NAMESPACE |
562 | |
563 | #include "moc_qquickstyleitem.cpp" |
564 | |