| 1 | /* |
| 2 | This file is part of the KDE libraries |
| 3 | SPDX-FileCopyrightText: 2014 Thomas Lübking <thomas.luebking@gmail.com> |
| 4 | |
| 5 | SPDX-License-Identifier: LGPL-2.0-or-later |
| 6 | */ |
| 7 | |
| 8 | #include "kstyleextensions.h" |
| 9 | |
| 10 | #include <QWidget> |
| 11 | |
| 12 | namespace KStyleExtensions |
| 13 | { |
| 14 | /* |
| 15 | Custom Style Element runtime extension: |
| 16 | We reserve one StyleHint to let the effective style inform widgets whether it supports certain |
| 17 | string based style elements. |
| 18 | As this could lead to number conflicts (i.e. an app utilizing one of the hints itself for other |
| 19 | purposes) there're various safety mechanisms to rule out such interference. |
| 20 | |
| 21 | 1) It's most unlikely that a widget in some 3rd party app will accidentally call a general |
| 22 | QStyle/KStyle styleHint() or draw*() and (unconditionally) expect a valid return, however: |
| 23 | a. The StyleHint is not directly above Qt's custom base, assuming most 3rd party apps would |
| 24 | - in case - make use of such |
| 25 | b. In order to be accepted, the StyleHint query must pass a widget with a perfectly matching |
| 26 | name, containing the typical element prefix ("CE_", etc.) and being supported by the current |
| 27 | style |
| 28 | c. Instead using Qt's fragile qstyleoption_cast on the QStyleOption provided to the StyleHint |
| 29 | query, try to dump out a string and hope for the best, we now manipulate the widgets |
| 30 | objectName(). |
| 31 | Plain Qt dependent widgets can do that themselves and if a widget uses KStyle's |
| 32 | convenience access functions, it won't notice this at all |
| 33 | |
| 34 | 2) The key problem is that a common KDE widget will run into an apps custom style which will then |
| 35 | falsely respond to the styleHint() call with an invalid value. |
| 36 | To prevent this, supporting styles *must* set a Q_CLASSINFO "X-KDE-CustomElements". |
| 37 | |
| 38 | 3) If any of the above traps snaps, the returned id is 0 - the QStyle default, indicating |
| 39 | that this element is not supported by the current style. |
| 40 | |
| 41 | Obviously, this contains the "diminished clean" action to (temporarily) manipulate the |
| 42 | objectName() of a const QWidget* - but this happens completely inside KStyle or the widget, if |
| 43 | it does not make use of KStyles static convenience functions. |
| 44 | My biggest worry here would be, that in a multithreaded environment a thread (usually not being |
| 45 | owner of the widget) does something crucially relying on the widgets name property... |
| 46 | This however would also have to happen during the widget construction or stylechanges, when |
| 47 | the functions in doubt will typically be called. |
| 48 | So this is imho unlikely causing any trouble, ever. |
| 49 | */ |
| 50 | |
| 51 | /// @private Prevent kapidox's doxygen config to pick up this namespace variable |
| 52 | static const QStyle::StyleHint SH_KCustomStyleElement = (QStyle::StyleHint)0xff000001; |
| 53 | /// @private Prevent kapidox's doxygen config to pick up this namespace variable |
| 54 | static const int X_KdeBase = 0xff000000; |
| 55 | |
| 56 | /* |
| 57 | The functions called by widgets that request custom element support, passed to the effective style. |
| 58 | Collected in a static inline function due to similarity. |
| 59 | */ |
| 60 | |
| 61 | /// @private Prevent kapidox's doxygen config to pick up this namespace method |
| 62 | static inline int customStyleElement(QStyle::StyleHint type, const QString &element, QWidget *widget) |
| 63 | { |
| 64 | if (!widget || widget->style()->metaObject()->indexOfClassInfo(name: "X-KDE-CustomElements" ) < 0) { |
| 65 | return 0; |
| 66 | } |
| 67 | |
| 68 | const QString originalName = widget->objectName(); |
| 69 | widget->setObjectName(element); |
| 70 | const int id = widget->style()->styleHint(stylehint: type, opt: nullptr, widget); |
| 71 | widget->setObjectName(originalName); |
| 72 | return id; |
| 73 | } |
| 74 | |
| 75 | QStyle::StyleHint customStyleHint(const QString &element, const QWidget *widget) |
| 76 | { |
| 77 | return (QStyle::StyleHint)customStyleElement(type: SH_KCustomStyleElement, element, widget: const_cast<QWidget *>(widget)); |
| 78 | } |
| 79 | |
| 80 | QStyle::ControlElement customControlElement(const QString &element, const QWidget *widget) |
| 81 | { |
| 82 | return (QStyle::ControlElement)customStyleElement(type: SH_KCustomStyleElement, element, widget: const_cast<QWidget *>(widget)); |
| 83 | } |
| 84 | |
| 85 | QStyle::SubElement customSubElement(const QString &element, const QWidget *widget) |
| 86 | { |
| 87 | return (QStyle::SubElement)customStyleElement(type: SH_KCustomStyleElement, element, widget: const_cast<QWidget *>(widget)); |
| 88 | } |
| 89 | |
| 90 | } |
| 91 | |