File indexing completed on 2024-05-12 05:00:04

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 2023 Stefano Crocco <stefano.crocco@alice.it>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #ifndef KONQINTERFACES_COMMON
0008 #define KONQINTERFACES_COMMON
0009 
0010 #include <QObject>
0011 
0012 namespace KonqInterfaces {
0013     /**
0014      * @brief Casts either the given object or one of its children as a pointer to T if possible
0015      *
0016      * @param obj the object to cast
0017      * @return @p obj or one of its children cast to a `T*` or `nullptr` if neither @p obj nor
0018      * its children can be cast to a `T*`
0019      * @internal
0020      * This function has two implementations, depending on whether or not `T` derives from `QObject`.
0021      * This allows to make use `QObject::findChild` when possible.
0022      *
0023      * To distinguish the two cases, as() uses `std::conditional`, delegating the actual implementation
0024      * either to CallAsForExtension::call() or CallAsForInterface::call()
0025      * @endinternal
0026      */
0027     template <typename T> T* as(QObject *obj);
0028 
0029     /**
0030      * @brief Struct used to implement as() when the template is a `QObject`
0031      */
0032     struct CallAsForExtension {
0033         /**
0034         * @brief Casts either the given object or one of its children as a pointer to T if possible
0035         *
0036         * This function assumes that `T` is a `QObject`-derived class and uses `QObject::findChild`
0037         * to determine the return value, except if @p obj can itself be cast to `T*`
0038         * @return @p obj or one of its children cast to a `T*` or `nullptr` if neither @p obj nor
0039         * its children can be cast to a `T*`
0040         */
0041         template <typename T> T* call(QObject* obj);
0042     };
0043     /**
0044      * @brief Struct used to implement as() when the template is not a `QObject`
0045      */
0046     struct CallAsForInterface {
0047         /**
0048         * @brief Casts either the given object or one of its children as a pointer to T if possible
0049         *
0050         * Unlike CallAsForExtension::call(), this function doesn't make any assumption about the type
0051         * of `T`, so it always works. However, it uses `dynamic_cast` and custom code rather than
0052         * the more specialized `QObject::findChild`.
0053         * @return @p obj or one of its children cast to a `T*` or `nullptr` if neither @p obj nor
0054         * its children can be cast to a `T*`
0055         */
0056         template <typename T> T* call(QObject* obj);
0057     };
0058 }
0059 
0060 template<typename T> T* KonqInterfaces::as(QObject *obj)
0061 {
0062     typename std::conditional<(std::is_base_of<QObject, T>()), CallAsForExtension, CallAsForInterface>::type func;
0063     return func.template call<T>(obj);
0064 }
0065 
0066 template<typename T> T* KonqInterfaces::CallAsForExtension::call(QObject *obj)
0067 {
0068     T* res = qobject_cast<T*>(obj);
0069     return res ? res : obj->findChild<T*>();
0070 }
0071 
0072 template<typename T> T* KonqInterfaces::CallAsForInterface::call(QObject *obj)
0073 {
0074     T* res = dynamic_cast<T*>(obj);
0075     if (res) {
0076         return res;
0077     }
0078     QObjectList children = obj->findChildren<QObject*>();
0079     for (QObject *c : children) {
0080         res = dynamic_cast<T*>(c);
0081         if (res) {
0082             return res;
0083         }
0084     }
0085     return nullptr;
0086 }
0087 
0088 #endif //KONQINTERFACES_COMMON