diff options
| -rw-r--r-- | docs/module/ECMAddQtDesignerPlugin.rst | 1 | ||||
| -rw-r--r-- | modules/ECMAddQtDesignerPlugin.cmake | 665 | 
2 files changed, 666 insertions, 0 deletions
| diff --git a/docs/module/ECMAddQtDesignerPlugin.rst b/docs/module/ECMAddQtDesignerPlugin.rst new file mode 100644 index 00000000..052b08fa --- /dev/null +++ b/docs/module/ECMAddQtDesignerPlugin.rst @@ -0,0 +1 @@ +.. ecm-module:: ../../modules/ECMAddQtDesignerPlugin.cmake diff --git a/modules/ECMAddQtDesignerPlugin.cmake b/modules/ECMAddQtDesignerPlugin.cmake new file mode 100644 index 00000000..4b4f467a --- /dev/null +++ b/modules/ECMAddQtDesignerPlugin.cmake @@ -0,0 +1,665 @@ +#.rst: +# ECMAddQtDesignerPlugin +# ---------------------- +# +# This module provides the ``ecm_add_qtdesignerplugin`` function for generating +# Qt Designer plugins for custom widgets. Each of those widgets is described +# using a second function ``ecm_qtdesignerplugin_widget``. +# +# :: +# +#   ecm_add_qtdesignerplugin(<target_name> +#       NAME <name> +#       WIDGETS <widgetid> [<widgetid2> [...]] +#       LINK_LIBRARIES <lib> [<lib2> [...]] +#       INSTALL_DESTINATION <install_path> +#       [OUTPUT_NAME <output_name>] +#       [DEFAULT_GROUP <group>] +#       [DEFAULT_HEADER_CASE <SAME_CASE|LOWER_CASE|UPPER_CASE>] +#       [DEFAULT_HEADER_EXTENSION <header_extension>] +#       [DEFAULT_ICON_DIR <icon_dir>] +#       [INCLUDE_FILES <include_file> [<include_file2> [...]]] +#       [SOURCES <src> [<src2> [...]]] +#       [COMPONENT <component>] +#   ) +# +# ``NAME`` specifies the base name to use in the generated sources. +# The default is <target_name>. +# +# ``WIDGETS`` specifies the widgets the plugin should support. Each widget has +# to be defined before by a call of ``ecm_qtdesignerplugin_widget`` with the +# respective <widgetid>, in a scope including the current call. +# +# ``LINK_LIBRARIES`` specifies the libraries to link against. This will be at +# least the library providing the widget class(es). +# +# ``INSTALL_DESTINATION`` specifies where the generated plugin binary will be +# installed. +# +# ``OUTPUT_NAME`` specifies the name of the plugin binary. The default is +# "<target_name>". +# +# ``DEFAULT_GROUP`` specifies the default group in Qt Designer where the +# widgets will be placed. The default is "Custom". +# +# ``DEFAULT_HEADER_CASE`` specifies how the name of the header is derived from +# the widget class name.  The default is "LOWER_CASE". +# +# ``DEFAULT_HEADER_EXTENSION`` specifies what file name extension is used for +# the header file derived from the class name.  The default is "h". +# +# ``DEFAULT_ICON_DIR`` specifies what file name extension is used for +# the header file derived from the class name.  The default is "pics". +# +# ``INCLUDE_FILES`` specifies additional include files to include with the +# generated source file. This can be needed for custom code used in +# initializing or creating widgets. +# +# ``SOURCES`` specifies additional source files to build the plugin from. +# This can be needed to support custom code used in initializing or +# creating widgets. +# +# ``COMPONENT`` specifies the installation component name with which the install +# rules for the generated plugin are associated. +# +# :: +# +#   ecm_qtdesignerplugin_widget(<widgetid> +#       [CLASS_NAME <class_name>] +#       [INCLUDE_FILE <include_file>] +#       [CONTAINER] +#       [ICON <iconfile>] +#       [TOOLTIP <tooltip>] +#       [WHATSTHIS <whatsthis>] +#       [GROUP <group>] +#       [CREATE_WIDGET_CODE <create_widget_code>] +#       [INITIALIZE_CODE <initialize_code] +#       [DOM_XML <dom_xml>] +#       [IMPL_CLASS_NAME <impl_class_name>] +#       [CONSTRUCTOR_ARGS_CODE <constructor_args_code>] +#   ) +# +# ``CLASS_NAME`` specifies the name of the widget class, including namespaces. +# The default is "<widgetid>". +# +# ``INCLUDE_FILE`` specifies the include file to use for the class of this +# widget. The default is derived from <class_name> as configured by the +# ``DEFAULT_HEADER_*`` options of ``ecm_add_qtdesignerplugin``, also replacing +# any namespace separators with "/". +# +# ``CONTAINER`` specifies, if set, that this widget is a container +# for other widgets. +# +# ``ICON`` specifies the icon file to use as symbol for this widget. +# The default is "{lowercased <class_name>}.png" in the default icons dir as +# configured by the ``DEFAULT_ICON_DIR`` option of +# ``ecm_add_qtdesignerplugin``, if such a file exists. +# +# ``TOOLTIP`` specifies the tooltip text to use for this widget. Default is +# "<class_name> Widget". +# +# ``WHATSTHIS`` specifies the What's-This text to use for this widget. +# Defaults to the tooltip. +# +# ``GROUP`` specifies the group in Qt Designer where the widget will be placed. +# The default is set as configured by the ``DEFAULT_GROUP`` option of +# ``ecm_add_qtdesignerplugin``. +# +# ``CREATE_WIDGET_CODE`` specifies C++ code to use as factory code to create +# an instance of the widget, for the override of +# ``QDesignerCustomWidgetInterface::createWidget(QWidget* parent)``. +# Replace any occurence of ";" with "@SEMICOLON@", as needed to pass the raw +# code via CMake. The default is +# "return new <impl_class_name><constructor_args_code>;". +# +# ``INITIALIZE_CODE`` specifies C++ code to use with the override of +# ``QDesignerCustomWidgetInterface::initialize(QDesignerFormEditorInterface* core)``. +# The code has to use the present class member ``m_initialized`` to track and +# update the state. Replace any occurence of ";" with "@SEMICOLON@", as needed +# to pass the raw code via CMake. The default code simply sets +# ``m_initialized`` to ``true``, if it was not before. +# +# ``DOM_XML`` specifies the string to use with the optional override of +# ``QDesignerCustomWidgetInterface::domXml()``. Replace any occurence of ";" +# with "@SEMICOLON@", as needed to pass the raw XML string via CMake. +# Default does not override. +# +# ``IMPL_CLASS_NAME`` specifies the name of the widget class to use for the +# widget instance with Qt Designer. The default is "<class_name>". +# +# ``CONSTRUCTOR_ARGS_CODE`` specifies the C++ code to use for the constructor +# arguments with the default of <create_widget_code>. Note that the +# parentheses are required. The default is "(parent)". +# +# +# +# Example usage: +# +# .. code-block:: cmake +# +#   ecm_qtdesignerplugin_widget(FooWidget +#       TOOLTIP "Enables to browse foo." +#       GROUP "Views (Foo)" +#   ) +#    +#   ecm_qtdesignerplugin_widget(BarWidget +#       TOOLTIP "Displays bars." +#       GROUP "Display (Foo)" +#   ) +# +#   ecm_add_qtdesignerplugin(foowidgets +#       NAME FooWidgets +#       OUTPUT_NAME foo2widgets +#       WIDGETS +#           FooWidget +#           BarWidget +#       LINK_LIBRARIES +#           Foo::Widgets +#       INSTALL_DESTINATION "${KDE_INSTALL_QTPLUGINDIR}/designer" +#       COMPONENT Devel +#   ) +# +# Since 5.62.0. + +#============================================================================= +# Copyright 2019 Friedrich W. H. Kossebau <kossebau@kde.org> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +#    notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +#    notice, this list of conditions and the following disclaimer in the +#    documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +#    derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +include(CMakeParseArguments) + +# helper method +# unescapes "@SEMICOLON@" into ";" +function(_ecm_qtdesignerplugin_unescape_code _varName input) +    string(REPLACE "@SEMICOLON@" ";" _code ${input}) +    set(${_varName} "${_code}" PARENT_SCOPE) +endfunction() + +# helper method +# escapes string for C++ code +function(_ecm_qtdesignerplugin_escape_cpp_string _varName input) +    string(REPLACE "\"" "\\\"" _string ${input}) +    set(${_varName} "${_string}" PARENT_SCOPE) +endfunction() + +# To make the data about the widgets available to the function ecm_add_qtdesignerplugin, +# variables are created in the scope of the caller of this method, protected by +# a namespace for this macro file, and otherwise from the widget id and the property id: +# ECM_QTDESIGNERPLUGIN_${widget}_${property} +function(ecm_qtdesignerplugin_widget widget) +    set(options +        CONTAINER +    ) +    set(oneValueArgs +        CLASS_NAME +        INCLUDE_FILE +        ICON +        TOOLTIP +        WHATSTHIS +        GROUP +        CREATE_WIDGET_CODE +        INITIALIZE_CODE +        DOM_XML +        IMPL_CLASS_NAME +        CONSTRUCTOR_ARGS_CODE +    ) +    set(multiValueArgs +    ) +    cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + +    if(NOT ARGS_CLASS_NAME) +        set(ARGS_CLASS_NAME "${widget}") +    endif() +    if(NOT ARGS_TOOLTIP) +        set(ARGS_TOOLTIP "${ARGS_CLASS_NAME} Widget") +    endif() +    if(NOT ARGS_WHATSTHIS) +        set(ARGS_WHATSTHIS "${ARGS_TOOLTIP}") +    endif() +    if(ARGS_CONTAINER) +        set(_is_container TRUE) +    else() +        set(_is_container FALSE) +    endif() +    if(NOT ARGS_CREATE_WIDGET_CODE) +        if(NOT ARGS_IMPL_CLASS_NAME) +            set(ARGS_IMPL_CLASS_NAME "${ARGS_CLASS_NAME}") +        endif() +        if(NOT ARGS_CONSTRUCTOR_ARGS_CODE) +            set(ARGS_CONSTRUCTOR_ARGS_CODE "(parent)") +        endif() +        set(ARGS_CREATE_WIDGET_CODE "        return new ${ARGS_IMPL_CLASS_NAME}${ARGS_CONSTRUCTOR_ARGS_CODE};") +    else() +        _ecm_qtdesignerplugin_unescape_code(ARGS_CREATE_WIDGET_CODE "${ARGS_CREATE_WIDGET_CODE}") +    endif() +    if(ARGS_ICON) +        if (NOT IS_ABSOLUTE ${ARGS_ICON}) +            set(ARGS_ICON "${CMAKE_CURRENT_SOURCE_DIR}/${ARGS_ICON}") +        endif() +        if(NOT EXISTS "${ARGS_ICON}") +            message(FATAL_ERROR "No such file as passed with ICON when calling ecm_qtdesignerplugin_widget(): ${ARGS_ICON}") +        endif() +    endif() + +    # store data about widget, so ecm_add_qtdesignerplugin can access it +    set(ECM_QTDESIGNERPLUGIN_${widget}_CLASS_NAME "${ARGS_CLASS_NAME}" PARENT_SCOPE) +    set(ECM_QTDESIGNERPLUGIN_${widget}_INCLUDE_FILE "${ARGS_INCLUDE_FILE}" PARENT_SCOPE) +    set(ECM_QTDESIGNERPLUGIN_${widget}_TOOLTIP "${ARGS_TOOLTIP}" PARENT_SCOPE) +    set(ECM_QTDESIGNERPLUGIN_${widget}_WHATSTHIS "${ARGS_WHATSTHIS}" PARENT_SCOPE) +    set(ECM_QTDESIGNERPLUGIN_${widget}_GROUP "${ARGS_GROUP}" PARENT_SCOPE) +    set(ECM_QTDESIGNERPLUGIN_${widget}_ICON "${ARGS_ICON}" PARENT_SCOPE) +    set(ECM_QTDESIGNERPLUGIN_${widget}_IS_CONTAINER "${_is_container}" PARENT_SCOPE) +    set(ECM_QTDESIGNERPLUGIN_${widget}_CREATE_WIDGET_CODE "${ARGS_CREATE_WIDGET_CODE}" PARENT_SCOPE) +    set(ECM_QTDESIGNERPLUGIN_${widget}_INITIALIZE_CODE "${ARGS_INITIALIZE_CODE}" PARENT_SCOPE) +    set(ECM_QTDESIGNERPLUGIN_${widget}_DOM_XML "${ARGS_DOM_XML}" PARENT_SCOPE) +endfunction() + +# helper method +function(_ecm_qtdesignerplugin_write_widget designer_src_file widget default_group rc_icon_dir) +    # prepare data +    set(_classname "${ECM_QTDESIGNERPLUGIN_${widget}_CLASS_NAME}") +    set(_factory_classname "${_classname}QtDesignerWidgetFactory") +    string(REPLACE "::" "__" _factory_classname "${_factory_classname}") +    set(ECM_QTDESIGNERPLUGIN_${widget}_FACTORY_CLASS_NAME "${_factory_classname}" PARENT_SCOPE) +    if(ECM_QTDESIGNERPLUGIN_${widget}_IS_CONTAINER) +        set(_is_container "true") +    else() +        set(_is_container "false") +    endif() +    _ecm_qtdesignerplugin_escape_cpp_string(_tooltip "${ECM_QTDESIGNERPLUGIN_${widget}_TOOLTIP}") +    _ecm_qtdesignerplugin_escape_cpp_string(_whatsthis "${ECM_QTDESIGNERPLUGIN_${widget}_WHATSTHIS}") +    set(_group ${ECM_QTDESIGNERPLUGIN_${widget}_GROUP}) +    if(NOT _group) +        set(_group "${default_group}") +    endif() +    _ecm_qtdesignerplugin_escape_cpp_string(_group "${_group}") +    set(_dom_xml "${ECM_QTDESIGNERPLUGIN_${widget}_DOM_XML}") +    if(_dom_xml) +        _ecm_qtdesignerplugin_unescape_code(_dom_xml "${_dom_xml}") +        string(REPLACE "\"" "\\\"" _dom_xml "${_dom_xml}") +        set(_dom_xml_method "    QString domXml() const override { return QStringLiteral(\"${_dom_xml}\"); }") +    else() +        set(_dom_xml_method) +    endif() +    set(_icon "${ECM_QTDESIGNERPLUGIN_${widget}_ICON}") +    if(_icon) +        get_filename_component(_icon_filename ${_icon} NAME) +        set(_icon_construct "QIcon(QStringLiteral(\":${rc_icon_dir}/${_icon_filename}\"))") +    else() +        set(_icon_construct "QIcon()") +    endif() +    set(_initialize_code "${ECM_QTDESIGNERPLUGIN_${widget}_INITIALIZE_CODE}") +    if(NOT _initialize_code) +        set(_initialize_code +"        Q_UNUSED(core); + +        if (m_initialized) return; + +        m_initialized = true;" +        ) +    else() +        _ecm_qtdesignerplugin_unescape_code(_initialize_code "${_initialize_code}") +    endif() + +    # write code +    file(APPEND ${designer_src_file} " +class ${_factory_classname} +    : public QObject +    , public QDesignerCustomWidgetInterface +{ +    Q_OBJECT +    Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: +    explicit ${_factory_classname}(QObject *parent = nullptr) +        : QObject(parent) +        , m_initialized(false) +    {} + +    ~${_factory_classname}() override {} +     +public: // QDesignerCustomWidgetInterface API +    bool isInitialized() const override { return m_initialized; } +${_dom_xml_method} +    bool isContainer() const override { return ${_is_container}; } +    QIcon icon() const override { return ${_icon_construct}; } +    QString group() const override { return QStringLiteral(\"${_group}\"); } +    QString includeFile() const override { return QStringLiteral(\"${ECM_QTDESIGNERPLUGIN_${widget}_INCLUDE_FILE}\"); } +    QString name() const override { return QStringLiteral(\"${_classname}\"); } +    QString toolTip() const override { return QStringLiteral(\"${_tooltip}\"); } +    QString whatsThis() const override { return QStringLiteral(\"${_whatsthis}\"); } + +    QWidget* createWidget(QWidget* parent) override +    { +${ECM_QTDESIGNERPLUGIN_${widget}_CREATE_WIDGET_CODE} +    } + +    void initialize(QDesignerFormEditorInterface* core) override  +    { +${_initialize_code} +    } + +private: +    bool m_initialized; +}; +" +    ) +endfunction() + +# helper method +function(_ecm_qtdesignerplugin_write_icon_qrc_file rc_file rc_icon_dir) +    set(_icons ${ARGN}) +    file(WRITE ${rc_file} +"<!DOCTYPE RCC><RCC version=\"1.0\"> +<!-- DO NOT EDIT! Generated from ecm_add_qtdesignerplugin() --> +<qresource prefix=\"${rc_icon_dir}\"> +" +    ) +    foreach(_icon ${_icons}) +        get_filename_component(_icon_filename ${_icon} NAME) +        file(APPEND ${rc_file} "<file alias=\"${_icon_filename}\">${_icon}</file>\n") +    endforeach() +    file(APPEND ${rc_file} +"</qresource> +</RCC> +" +    ) +endfunction() + +# This needs to be a macro not a function because of the nested +# find_package() call, which will set some variables. +macro(ecm_add_qtdesignerplugin target) +    set(options +    ) +    set(oneValueArgs +        NAME +        OUTPUT_NAME +        INSTALL_DESTINATION +        DEFAULT_GROUP +        COMPONENT +        DEFAULT_HEADER_CASE +        DEFAULT_HEADER_EXTENSION +        DEFAULT_ICON_DIR +    ) +    set(multiValueArgs +        WIDGETS +        LINK_LIBRARIES +        INCLUDE_FILES +        SOURCES +    ) +    cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + +    # args sanity check +    if (NOT ARGS_WIDGETS) +        message(FATAL_ERROR "No WIDGETS passed when calling ecm_add_qtdesignerplugin().") +    endif() +    foreach(_widget ${ARGS_WIDGETS}) +        # using _CLASS_NAME as sample property to find if defined +        if (NOT ECM_QTDESIGNERPLUGIN_${_widget}_CLASS_NAME) +            message(FATAL_ERROR "Undefined widget passed when calling ecm_add_qtdesignerplugin(): ${_widget}") +        endif() +    endforeach() + +    if(NOT ARGS_NAME) +        set(ARGS_NAME "${target}") +    endif() +    if(NOT ARGS_DEFAULT_GROUP) +        set(ARGS_DEFAULT_GROUP "Custom") +    endif() +    if(NOT ARGS_DEFAULT_HEADER_EXTENSION) +        set(ARGS_DEFAULT_HEADER_EXTENSION "h") +    endif() +    if(NOT ARGS_DEFAULT_HEADER_CASE) +        set(ARGS_DEFAULT_HEADER_CASE "LOWER_CASE") +    else() +        set(_allowed_values "LOWER_CASE" "UPPER_CASE" "SAME_CASE") +        list(FIND _allowed_values "${ARGS_DEFAULT_HEADER_CASE}" _index) +        if(_index EQUAL "-1") +            message(FATAL_ERROR "Unexpected value for DEFAULT_HEADER_CASE argument to ecm_add_qtdesignerplugin(): ${ARGS_DEFAULT_HEADER_CASE}") +        endif() +    endif() +    if(NOT ARGS_DEFAULT_ICON_DIR) +        set(ARGS_DEFAULT_ICON_DIR "${CMAKE_CURRENT_SOURCE_DIR}/pics") +    else() +        if (NOT IS_ABSOLUTE ${ARGS_DEFAULT_ICON_DIR}) +            set(ARGS_DEFAULT_ICON_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${ARGS_DEFAULT_ICON_DIR}") +        endif() +        if(NOT EXISTS "${ARGS_DEFAULT_ICON_DIR}") +            message(FATAL_ERROR "No such directory as passed with DEFAULT_ICON_DIR when calling ecm_add_qtdesignerplugin(): ${ARGS_DEFAULT_ICON_DIR}") +        endif() +    endif() + +    # Check deps +    # peek at Qt5Core to learn about the version to decide whether Qt5UiPlugin is enough +    if(NOT Qt5Core_FOUND) +        find_package(Qt5Core QUIET CONFIG) +    endif() +    if(Qt5Core_VERSION VERSION_LESS "5.5.0") +        set(_qtdesigner_tobefound TRUE) +    elseif(Qt5Core_VERSION VERSION_LESS "5.9.0") +        set(_qtdesigner_tobefound TRUE) +        set(_qtuiplugin_tobefound TRUE) +    else() +        # Since Qt 5.9 only Qt5UiPlugin is needed +        set(_qtuiplugin_tobefound TRUE) +    endif() +    if(NOT Qt5Designer_FOUND AND _qtdesigner_tobefound) +        find_package(Qt5Designer QUIET CONFIG) +        set_package_properties(Qt5Designer PROPERTIES +            PURPOSE "Required to build Qt Designer plugins" +            TYPE REQUIRED +        ) +    endif() +    if(NOT Qt5UiPlugin_FOUND AND _qtuiplugin_tobefound) +        find_package(Qt5UiPlugin QUIET CONFIG) +        set_package_properties(Qt5UiPlugin PROPERTIES +            PURPOSE "Required to build Qt Designer plugins" +            TYPE REQUIRED +        ) +    endif() +    if (Qt5Designer_FOUND) +        set(_qtdesigner_tobefound FALSE) +    endif() +    if (Qt5UiPlugin_FOUND) +        set(_qtuiplugin_tobefound FALSE) +        # in some old versions Qt5UiPlugin does not set its _INCLUDE_DIRS variable. Fill it manually +        get_target_property(Qt5UiPlugin_INCLUDE_DIRS Qt5::UiPlugin INTERFACE_INCLUDE_DIRECTORIES) +    endif() + +    # setup plugin only if designer/uiplugin libs were found, as we do not abort hard the cmake run otherwise +    if(NOT _qtdesigner_tobefound AND NOT _qtuiplugin_tobefound) +        set(_generation_dir "${CMAKE_CURRENT_BINARY_DIR}/${target}_ECMQtDesignerPlugin") +        file(MAKE_DIRECTORY "${_generation_dir}") +        set(_rc_icon_dir "/${ARGS_NAME}/designer") + +        # process defaults for widgets +        foreach(_widget ${ARGS_WIDGETS}) +            set(_class_name "${ECM_QTDESIGNERPLUGIN_${_widget}_CLASS_NAME}") +            # include file +            set(_include_file "${ECM_QTDESIGNERPLUGIN_${_widget}_INCLUDE_FILE}") +            if(NOT _include_file) +                set(_include_file "${_class_name}") +                if (ARGS_DEFAULT_HEADER_CASE STREQUAL "LOWER_CASE") +                    string(TOLOWER "${_include_file}" _include_file) +                elseif(ARGS_DEFAULT_HEADER_CASE STREQUAL "UPPER_CASE") +                    string(TOUPPER "${_include_file}" _include_file) +                endif() +                # turn any namespaces into dir levels +                string(REPLACE "::" "/" _include_file ${_include_file}) +                set(ECM_QTDESIGNERPLUGIN_${_widget}_INCLUDE_FILE "${_include_file}.${ARGS_DEFAULT_HEADER_EXTENSION}") +            endif() +            # icon +            set(_icon "${ECM_QTDESIGNERPLUGIN_${_widget}_ICON}") +            if(NOT _icon) +                string(TOLOWER "${_class_name}" _icon) +                # handle any namespaces +                string(REPLACE "::" "_" _icon "${_icon}") +                set(_icon "${ARGS_DEFAULT_ICON_DIR}/${_icon}.png") +                if(EXISTS "${_icon}") +                    set(ECM_QTDESIGNERPLUGIN_${_widget}_ICON "${_icon}") +                endif() +            endif() +        endforeach() + +        set(_plugin_src_file "${_generation_dir}/designerplugin.cpp") +        set(_srcs +            ${ARGS_SOURCES} +            ${_plugin_src_file} +        ) + +        set(_icons) +        foreach(_widget ${ARGS_WIDGETS}) +            list(APPEND _icons "${ECM_QTDESIGNERPLUGIN_${_widget}_ICON}") +        endforeach() + +        # generate qrc file with icons +        if (_icons) +            set(_rc_file "${_generation_dir}/designerplugin.rc") +            set(_rc_work_file "${_rc_file}.work") + +            _ecm_qtdesignerplugin_write_icon_qrc_file("${_rc_work_file}" "${_rc_icon_dir}" ${_icons}) +            # avoid rebuilding if there was no change +            execute_process( +                COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_rc_work_file}" "${_rc_file}" +            ) +            file(REMOVE "${_rc_work_file}") + +            qt5_add_resources(_srcs ${_rc_file}) +        endif() + +        # generate source file +        set(_collection_classname "${ARGS_NAME}QtDesignerWidgetCollection") + +        set(_include_files +            # classes inherited +            QDesignerCustomWidgetCollectionInterface +            QDesignerCustomWidgetInterface +            QObject +            # classes used +            QIcon +            QString +            ${ARGS_INCLUDE_FILES} +        ) +        foreach(_widget ${ARGS_WIDGETS}) +            list(APPEND _include_files ${ECM_QTDESIGNERPLUGIN_${_widget}_INCLUDE_FILE}) +        endforeach() +        list(REMOVE_DUPLICATES _include_files) + +        set(_plugin_src_work_file "${_plugin_src_file}.work") +        file(WRITE ${_plugin_src_work_file} "// DO NOT EDIT! Generated from ecm_add_qtdesignerplugin()\n\n") +        foreach(_include_file ${_include_files}) +            if (NOT ${_include_file} MATCHES "^[\"<]") +                set(_include_file "<${_include_file}>") +            endif() +            file(APPEND ${_plugin_src_work_file} "#include ${_include_file}\n") +        endforeach() +        foreach(_widget ${ARGS_WIDGETS}) +            _ecm_qtdesignerplugin_write_widget(${_plugin_src_work_file} ${_widget} ${ARGS_DEFAULT_GROUP} ${_rc_icon_dir}) +        endforeach() +        file(APPEND ${_plugin_src_work_file} " +class ${_collection_classname} +    : public QObject +    , public QDesignerCustomWidgetCollectionInterface +{ +    Q_OBJECT +    Q_INTERFACES( +        QDesignerCustomWidgetCollectionInterface +    ) + +    Q_PLUGIN_METADATA(IID \"org.qt-project.Qt.QDesignerCustomWidgetInterface\") + +public: +    explicit ${_collection_classname}(QObject* parent = nullptr); + +public: // QDesignerCustomWidgetCollectionInterface API +    QList<QDesignerCustomWidgetInterface*> customWidgets() const override; + +private: +    QList<QDesignerCustomWidgetInterface*> m_widgetFactories; +}; + +${_collection_classname}::${_collection_classname}(QObject* parent) +    : QObject(parent) +{ +    m_widgetFactories = QList<QDesignerCustomWidgetInterface*>{ +" +        ) +        foreach(_widget ${ARGS_WIDGETS}) +            file(APPEND ${_plugin_src_work_file} "        new ${ECM_QTDESIGNERPLUGIN_${_widget}_FACTORY_CLASS_NAME}(this),\n") +        endforeach() +        file(APPEND ${_plugin_src_work_file} +"    }; +} + +QList<QDesignerCustomWidgetInterface*> ${_collection_classname}::customWidgets() const +{ +    return m_widgetFactories; +} + +#include \"designerplugin.moc\" +" +        ) + +        # avoid rebuilding if there was no change +        execute_process( +            COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_plugin_src_work_file}" "${_plugin_src_file}" +        ) +        file(REMOVE "${_plugin_src_work_file}") + +        # setup plugin binary +        add_library(${target} MODULE ${_srcs}) +        if(Qt5UiPlugin_VERSION AND NOT Qt5UiPlugin_VERSION VERSION_LESS "5.9.0") +            list(APPEND ARGS_LINK_LIBRARIES Qt5::UiPlugin) +        else() +            # For Qt <5.9 include dir variables needed +            target_include_directories(${target} +                PRIVATE ${Qt5UiPlugin_INCLUDE_DIRS} +                PRIVATE ${Qt5Designer_INCLUDE_DIRS} +            ) +        endif() +        if(NOT WIN32) +            # Since there are no libraries provided by this module, +            # there is no point including the build tree in RPath, +            # and then having to edit it at install time. +            set_target_properties(${target} PROPERTIES +                SKIP_BUILD_RPATH TRUE +                BUILD_WITH_INSTALL_RPATH TRUE +            ) +        endif() +        if (ARGS_OUTPUT_NAME) +            set_target_properties(${target} PROPERTIES +                OUTPUT_NAME ${ARGS_OUTPUT_NAME} +            ) +        endif() +        target_link_libraries(${target} ${ARGS_LINK_LIBRARIES}) + +        if (DEFINED ARGS_COMPONENT) +            set(_component COMPONENT ${ARGS_COMPONENT}) +        else() +            set(_component) +        endif() + +        install(TARGETS ${target} DESTINATION ${ARGS_INSTALL_DESTINATION} ${_component}) +    endif() +endmacro() | 
