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 | |