File indexing completed on 2024-04-28 03:59:15

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2014 Thomas Lübking <thomas.luebking@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kstyleextensions.h"
0009 
0010 #include <QWidget>
0011 
0012 namespace KStyleExtensions
0013 {
0014 /*
0015     Custom Style Element runtime extension:
0016     We reserve one StyleHint to let the effective style inform widgets whether it supports certain
0017     string based style elements.
0018     As this could lead to number conflicts (i.e. an app utilizing one of the hints itself for other
0019     purposes) there're various safety mechanisms to rule out such interference.
0020 
0021     1) It's most unlikely that a widget in some 3rd party app will accidentally call a general
0022     QStyle/KStyle styleHint() or draw*() and (unconditionally) expect a valid return, however:
0023         a. The StyleHint is not directly above Qt's custom base, assuming most 3rd party apps would
0024            - in case - make use of such
0025         b. In order to be accepted, the StyleHint query must pass a widget with a perfectly matching
0026            name, containing the typical element prefix ("CE_", etc.) and being supported by the current
0027            style
0028         c. Instead using Qt's fragile qstyleoption_cast on the QStyleOption provided to the StyleHint
0029            query, try to dump out a string and hope for the best, we now manipulate the widgets
0030            objectName().
0031            Plain Qt dependent widgets can do that themselves and if a widget uses KStyle's
0032            convenience access functions, it won't notice this at all
0033 
0034     2) The key problem is that a common KDE widget will run into an apps custom style which will then
0035     falsely respond to the styleHint() call with an invalid value.
0036     To prevent this, supporting styles *must* set a Q_CLASSINFO "X-KDE-CustomElements".
0037 
0038     3) If any of the above traps snaps, the returned id is 0 - the QStyle default, indicating
0039     that this element is not supported by the current style.
0040 
0041     Obviously, this contains the "diminished clean" action to (temporarily) manipulate the
0042     objectName() of a const QWidget* - but this happens completely inside KStyle or the widget, if
0043     it does not make use of KStyles static convenience functions.
0044     My biggest worry here would be, that in a multithreaded environment a thread (usually not being
0045     owner of the widget) does something crucially relying on the widgets name property...
0046     This however would also have to happen during the widget construction or stylechanges, when
0047     the functions in doubt will typically be called.
0048     So this is imho unlikely causing any trouble, ever.
0049 */
0050 
0051 /// @private Prevent kapidox's doxygen config to pick up this namespace variable
0052 static const QStyle::StyleHint SH_KCustomStyleElement = (QStyle::StyleHint)0xff000001;
0053 /// @private Prevent kapidox's doxygen config to pick up this namespace variable
0054 static const int X_KdeBase = 0xff000000;
0055 
0056 /*
0057     The functions called by widgets that request custom element support, passed to the effective style.
0058     Collected in a static inline function due to similarity.
0059 */
0060 
0061 /// @private Prevent kapidox's doxygen config to pick up this namespace method
0062 static inline int customStyleElement(QStyle::StyleHint type, const QString &element, QWidget *widget)
0063 {
0064     if (!widget || widget->style()->metaObject()->indexOfClassInfo("X-KDE-CustomElements") < 0) {
0065         return 0;
0066     }
0067 
0068     const QString originalName = widget->objectName();
0069     widget->setObjectName(element);
0070     const int id = widget->style()->styleHint(type, nullptr, widget);
0071     widget->setObjectName(originalName);
0072     return id;
0073 }
0074 
0075 QStyle::StyleHint customStyleHint(const QString &element, const QWidget *widget)
0076 {
0077     return (QStyle::StyleHint)customStyleElement(SH_KCustomStyleElement, element, const_cast<QWidget *>(widget));
0078 }
0079 
0080 QStyle::ControlElement customControlElement(const QString &element, const QWidget *widget)
0081 {
0082     return (QStyle::ControlElement)customStyleElement(SH_KCustomStyleElement, element, const_cast<QWidget *>(widget));
0083 }
0084 
0085 QStyle::SubElement customSubElement(const QString &element, const QWidget *widget)
0086 {
0087     return (QStyle::SubElement)customStyleElement(SH_KCustomStyleElement, element, const_cast<QWidget *>(widget));
0088 }
0089 
0090 }