Changeset View
Changeset View
Standalone View
Standalone View
modules/ECMAddQtDesignerPlugin.cmake
- This file was added.
1 | #.rst: | ||||
---|---|---|---|---|---|
2 | # ECMAddQtDesignerPlugin | ||||
3 | # ---------------------- | ||||
4 | # | ||||
5 | # This module provides the ``ecm_add_qtdesignerplugin`` function for generating | ||||
6 | # Qt Designer plugins for custom widgets. Each of those widgets is described | ||||
7 | # using a second function ``ecm_qtdesignerplugin_widget``. | ||||
8 | # | ||||
9 | # :: | ||||
10 | # | ||||
11 | # ecm_add_qtdesignerplugin(<target_name> | ||||
12 | # NAME <name> | ||||
13 | # WIDGETS <widgetid> [<widgetid2> [...]] | ||||
14 | # LINK_LIBRARIES <lib> [<lib2> [...]] | ||||
15 | # INSTALL_DESTINATION <install_path> | ||||
16 | # [OUTPUT_NAME <output_name>] | ||||
17 | # [DEFAULT_GROUP <group>] | ||||
18 | # [DEFAULT_HEADER_CASE <SAME_CASE|LOWER_CASE|UPPER_CASE>] | ||||
19 | # [DEFAULT_HEADER_EXTENSION <header_extension>] | ||||
20 | # [DEFAULT_ICON_DIR <icon_dir>] | ||||
21 | # [INCLUDE_FILES <include_file> [<include_file2> [...]]] | ||||
22 | # [SOURCES <src> [<src2> [...]]] | ||||
23 | # [COMPONENT <component>] | ||||
24 | # ) | ||||
25 | # | ||||
26 | # ``NAME`` specifies the base name to use in the generated sources. | ||||
27 | # The default is <target_name>. | ||||
28 | # | ||||
29 | # ``WIDGETS`` specifies the widgets the plugin should support. Each widget has | ||||
30 | # to be defined before by a call of ``ecm_qtdesignerplugin_widget`` with the | ||||
31 | # respective <widgetid>, in a scope including the current call. | ||||
32 | # | ||||
33 | # ``LINK_LIBRARIES`` specifies the libraries to link against. This will be at | ||||
34 | # least the library providing the widget class(es). | ||||
35 | # | ||||
36 | # ``INSTALL_DESTINATION`` specifies where the generated plugin binary will be | ||||
37 | # installed. | ||||
38 | # | ||||
39 | # ``OUTPUT_NAME`` specifies the name of the plugin binary. The default is | ||||
40 | # "<target_name>". | ||||
41 | # | ||||
42 | # ``DEFAULT_GROUP`` specifies the default group in Qt Designer where the | ||||
43 | # widgets will be placed. The default is "Custom". | ||||
44 | # | ||||
45 | # ``DEFAULT_HEADER_CASE`` specifies how the name of the header is derived from | ||||
46 | # the widget class name. The default is "LOWER_CASE". | ||||
47 | # | ||||
48 | # ``DEFAULT_HEADER_EXTENSION`` specifies what file name extension is used for | ||||
49 | # the header file derived from the class name. The default is "h". | ||||
50 | # | ||||
51 | # ``DEFAULT_ICON_DIR`` specifies what file name extension is used for | ||||
52 | # the header file derived from the class name. The default is "pics". | ||||
53 | # | ||||
54 | # ``INCLUDE_FILES`` specifies additional include files to include with the | ||||
55 | # generated source file. This can be needed for custom code used in | ||||
56 | # initializing or creating widgets. | ||||
57 | # | ||||
58 | # ``SOURCES`` specifies additional source files to build the plugin from. | ||||
59 | # This can be needed to support custom code used in initializing or | ||||
60 | # creating widgets. | ||||
61 | # | ||||
62 | # ``COMPONENT`` specifies the installation component name with which the install | ||||
63 | # rules for the generated plugin are associated. | ||||
64 | # | ||||
65 | # :: | ||||
66 | # | ||||
67 | # ecm_qtdesignerplugin_widget(<widgetid> | ||||
68 | # [CLASS_NAME <class_name>] | ||||
69 | # [INCLUDE_FILE <include_file>] | ||||
70 | # [CONTAINER] | ||||
71 | # [ICON <iconfile>] | ||||
72 | # [TOOLTIP <tooltip>] | ||||
73 | # [WHATSTHIS <whatsthis>] | ||||
74 | # [GROUP <group>] | ||||
75 | # [CREATE_WIDGET_CODE <create_widget_code>] | ||||
76 | # [INITIALIZE_CODE <initialize_code] | ||||
77 | # [DOM_XML <dom_xml>] | ||||
78 | # [IMPL_CLASS_NAME <impl_class_name>] | ||||
79 | # [CONSTRUCTOR_ARGS_CODE <constructor_args_code>] | ||||
80 | # ) | ||||
81 | # | ||||
82 | # ``CLASS_NAME`` specifies the name of the widget class, including namespaces. | ||||
83 | # The default is "<widgetid>". | ||||
84 | # | ||||
85 | # ``INCLUDE_FILE`` specifies the include file to use for the class of this | ||||
86 | # widget. The default is derived from <class_name> as configured by the | ||||
87 | # ``DEFAULT_HEADER_*`` options of ``ecm_add_qtdesignerplugin``, also replacing | ||||
88 | # any namespace separators with "/". | ||||
89 | # | ||||
90 | # ``CONTAINER`` specifies, if set, that this widget is a container | ||||
91 | # for other widgets. | ||||
92 | # | ||||
93 | # ``ICON`` specifies the icon file to use as symbol for this widget. | ||||
94 | # The default is "{lowercased <class_name>}.png" in the default icons dir as | ||||
95 | # configured by the ``DEFAULT_ICON_DIR`` option of | ||||
96 | # ``ecm_add_qtdesignerplugin``, if such a file exists. | ||||
97 | # | ||||
98 | # ``TOOLTIP`` specifies the tooltip text to use for this widget. Default is | ||||
99 | # "<class_name> Widget". | ||||
100 | # | ||||
101 | # ``WHATSTHIS`` specifies the What's-This text to use for this widget. | ||||
102 | # Defaults to the tooltip. | ||||
103 | # | ||||
104 | # ``GROUP`` specifies the group in Qt Designer where the widget will be placed. | ||||
105 | # The default is set as configured by the ``DEFAULT_GROUP`` option of | ||||
106 | # ``ecm_add_qtdesignerplugin``. | ||||
107 | # | ||||
108 | # ``CREATE_WIDGET_CODE`` specifies C++ code to use as factory code to create | ||||
109 | # an instance of the widget, for the override of | ||||
110 | # ``QDesignerCustomWidgetInterface::createWidget(QWidget* parent)``. | ||||
111 | # Replace any occurence of ";" with "@SEMICOLON@", as needed to pass the raw | ||||
112 | # code via CMake. The default is | ||||
113 | # "return new <impl_class_name><constructor_args_code>;". | ||||
114 | # | ||||
115 | # ``INITIALIZE_CODE`` specifies C++ code to use with the override of | ||||
116 | # ``QDesignerCustomWidgetInterface::initialize(QDesignerFormEditorInterface* core)``. | ||||
117 | # The code has to use the present class member ``m_initialized`` to track and | ||||
118 | # update the state. Replace any occurence of ";" with "@SEMICOLON@", as needed | ||||
119 | # to pass the raw code via CMake. The default code simply sets | ||||
120 | # ``m_initialized`` to ``true``, if it was not before. | ||||
121 | # | ||||
122 | # ``DOM_XML`` specifies the string to use with the optional override of | ||||
123 | # ``QDesignerCustomWidgetInterface::domXml()``. Replace any occurence of ";" | ||||
124 | # with "@SEMICOLON@", as needed to pass the raw XML string via CMake. | ||||
125 | # Default does not override. | ||||
126 | # | ||||
127 | # ``IMPL_CLASS_NAME`` specifies the name of the widget class to use for the | ||||
128 | # widget instance with Qt Designer. The default is "<class_name>". | ||||
129 | # | ||||
130 | # ``CONSTRUCTOR_ARGS_CODE`` specifies the C++ code to use for the constructor | ||||
131 | # arguments with the default of <create_widget_code>. Note that the | ||||
132 | # parentheses are required. The default is "(parent)". | ||||
133 | # | ||||
134 | # | ||||
135 | # | ||||
136 | # Example usage: | ||||
137 | # | ||||
138 | # .. code-block:: cmake | ||||
139 | # | ||||
140 | # ecm_qtdesignerplugin_widget(FooWidget | ||||
141 | # TOOLTIP "Enables to browse foo." | ||||
142 | # GROUP "Views (Foo)" | ||||
143 | # ) | ||||
144 | # | ||||
145 | # ecm_qtdesignerplugin_widget(BarWidget | ||||
146 | # TOOLTIP "Displays bars." | ||||
147 | # GROUP "Display (Foo)" | ||||
148 | # ) | ||||
149 | # | ||||
150 | # ecm_add_qtdesignerplugin(foowidgets | ||||
151 | # NAME FooWidgets | ||||
152 | # OUTPUT_NAME foo2widgets | ||||
153 | # WIDGETS | ||||
154 | # FooWidget | ||||
155 | # BarWidget | ||||
156 | # LINK_LIBRARIES | ||||
157 | # Foo::Widgets | ||||
158 | # INSTALL_DESTINATION "${KDE_INSTALL_QTPLUGINDIR}/designer" | ||||
159 | # COMPONENT Devel | ||||
160 | # ) | ||||
161 | # | ||||
162 | # Since 5.62.0. | ||||
163 | | ||||
164 | #============================================================================= | ||||
165 | # Copyright 2019 Friedrich W. H. Kossebau <kossebau@kde.org> | ||||
166 | # | ||||
167 | # Redistribution and use in source and binary forms, with or without | ||||
168 | # modification, are permitted provided that the following conditions | ||||
169 | # are met: | ||||
170 | # | ||||
171 | # 1. Redistributions of source code must retain the copyright | ||||
172 | # notice, this list of conditions and the following disclaimer. | ||||
173 | # 2. Redistributions in binary form must reproduce the copyright | ||||
174 | # notice, this list of conditions and the following disclaimer in the | ||||
175 | # documentation and/or other materials provided with the distribution. | ||||
176 | # 3. The name of the author may not be used to endorse or promote products | ||||
177 | # derived from this software without specific prior written permission. | ||||
178 | # | ||||
179 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
180 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
181 | # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
182 | # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
183 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
184 | # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
185 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
186 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
187 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
188 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
189 | | ||||
190 | include(CMakeParseArguments) | ||||
191 | | ||||
192 | # helper method | ||||
193 | # unescapes "@SEMICOLON@" into ";" | ||||
194 | function(_ecm_qtdesignerplugin_unescape_code _varName input) | ||||
195 | string(REPLACE "@SEMICOLON@" ";" _code ${input}) | ||||
196 | set(${_varName} "${_code}" PARENT_SCOPE) | ||||
197 | endfunction() | ||||
198 | | ||||
199 | # helper method | ||||
200 | # escapes string for C++ code | ||||
201 | function(_ecm_qtdesignerplugin_escape_cpp_string _varName input) | ||||
202 | string(REPLACE "\"" "\\\"" _string ${input}) | ||||
203 | set(${_varName} "${_string}" PARENT_SCOPE) | ||||
204 | endfunction() | ||||
205 | | ||||
206 | # To make the data about the widgets available to the function ecm_add_qtdesignerplugin, | ||||
207 | # variables are created in the scope of the caller of this method, protected by | ||||
208 | # a namespace for this macro file, and otherwise from the widget id and the property id: | ||||
209 | # ECM_QTDESIGNERPLUGIN_${widget}_${property} | ||||
210 | function(ecm_qtdesignerplugin_widget widget) | ||||
211 | set(options | ||||
212 | CONTAINER | ||||
213 | ) | ||||
214 | set(oneValueArgs | ||||
215 | CLASS_NAME | ||||
216 | INCLUDE_FILE | ||||
217 | ICON | ||||
218 | TOOLTIP | ||||
219 | WHATSTHIS | ||||
220 | GROUP | ||||
221 | CREATE_WIDGET_CODE | ||||
222 | INITIALIZE_CODE | ||||
223 | DOM_XML | ||||
224 | IMPL_CLASS_NAME | ||||
225 | CONSTRUCTOR_ARGS_CODE | ||||
226 | ) | ||||
227 | set(multiValueArgs | ||||
228 | ) | ||||
229 | cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) | ||||
230 | | ||||
231 | if(NOT ARGS_CLASS_NAME) | ||||
232 | set(ARGS_CLASS_NAME "${widget}") | ||||
233 | endif() | ||||
234 | if(NOT ARGS_TOOLTIP) | ||||
235 | set(ARGS_TOOLTIP "${ARGS_CLASS_NAME} Widget") | ||||
236 | endif() | ||||
237 | if(NOT ARGS_WHATSTHIS) | ||||
238 | set(ARGS_WHATSTHIS "${ARGS_TOOLTIP}") | ||||
239 | endif() | ||||
240 | if(ARGS_CONTAINER) | ||||
241 | set(_is_container TRUE) | ||||
242 | else() | ||||
243 | set(_is_container FALSE) | ||||
244 | endif() | ||||
245 | if(NOT ARGS_CREATE_WIDGET_CODE) | ||||
246 | if(NOT ARGS_IMPL_CLASS_NAME) | ||||
247 | set(ARGS_IMPL_CLASS_NAME "${ARGS_CLASS_NAME}") | ||||
248 | endif() | ||||
249 | if(NOT ARGS_CONSTRUCTOR_ARGS_CODE) | ||||
250 | set(ARGS_CONSTRUCTOR_ARGS_CODE "(parent)") | ||||
251 | endif() | ||||
252 | set(ARGS_CREATE_WIDGET_CODE " return new ${ARGS_IMPL_CLASS_NAME}${ARGS_CONSTRUCTOR_ARGS_CODE};") | ||||
253 | else() | ||||
254 | _ecm_qtdesignerplugin_unescape_code(ARGS_CREATE_WIDGET_CODE "${ARGS_CREATE_WIDGET_CODE}") | ||||
255 | endif() | ||||
256 | if(ARGS_ICON) | ||||
257 | if (NOT IS_ABSOLUTE ${ARGS_ICON}) | ||||
258 | set(ARGS_ICON "${CMAKE_CURRENT_SOURCE_DIR}/${ARGS_ICON}") | ||||
259 | endif() | ||||
260 | if(NOT EXISTS "${ARGS_ICON}") | ||||
261 | message(FATAL_ERROR "No such file as passed with ICON when calling ecm_qtdesignerplugin_widget(): ${ARGS_ICON}") | ||||
262 | endif() | ||||
263 | endif() | ||||
264 | | ||||
265 | # store data about widget, so ecm_add_qtdesignerplugin can access it | ||||
266 | set(ECM_QTDESIGNERPLUGIN_${widget}_CLASS_NAME "${ARGS_CLASS_NAME}" PARENT_SCOPE) | ||||
267 | set(ECM_QTDESIGNERPLUGIN_${widget}_INCLUDE_FILE "${ARGS_INCLUDE_FILE}" PARENT_SCOPE) | ||||
268 | set(ECM_QTDESIGNERPLUGIN_${widget}_TOOLTIP "${ARGS_TOOLTIP}" PARENT_SCOPE) | ||||
269 | set(ECM_QTDESIGNERPLUGIN_${widget}_WHATSTHIS "${ARGS_WHATSTHIS}" PARENT_SCOPE) | ||||
270 | set(ECM_QTDESIGNERPLUGIN_${widget}_GROUP "${ARGS_GROUP}" PARENT_SCOPE) | ||||
271 | set(ECM_QTDESIGNERPLUGIN_${widget}_ICON "${ARGS_ICON}" PARENT_SCOPE) | ||||
272 | set(ECM_QTDESIGNERPLUGIN_${widget}_IS_CONTAINER "${_is_container}" PARENT_SCOPE) | ||||
273 | set(ECM_QTDESIGNERPLUGIN_${widget}_CREATE_WIDGET_CODE "${ARGS_CREATE_WIDGET_CODE}" PARENT_SCOPE) | ||||
274 | set(ECM_QTDESIGNERPLUGIN_${widget}_INITIALIZE_CODE "${ARGS_INITIALIZE_CODE}" PARENT_SCOPE) | ||||
275 | set(ECM_QTDESIGNERPLUGIN_${widget}_DOM_XML "${ARGS_DOM_XML}" PARENT_SCOPE) | ||||
276 | endfunction() | ||||
277 | | ||||
278 | # helper method | ||||
279 | function(_ecm_qtdesignerplugin_write_widget designer_src_file widget default_group rc_icon_dir) | ||||
280 | # prepare data | ||||
281 | set(_classname "${ECM_QTDESIGNERPLUGIN_${widget}_CLASS_NAME}") | ||||
282 | set(_factory_classname "${_classname}QtDesignerWidgetFactory") | ||||
283 | string(REPLACE "::" "__" _factory_classname "${_factory_classname}") | ||||
284 | set(ECM_QTDESIGNERPLUGIN_${widget}_FACTORY_CLASS_NAME "${_factory_classname}" PARENT_SCOPE) | ||||
285 | if(ECM_QTDESIGNERPLUGIN_${widget}_IS_CONTAINER) | ||||
286 | set(_is_container "true") | ||||
287 | else() | ||||
288 | set(_is_container "false") | ||||
289 | endif() | ||||
290 | _ecm_qtdesignerplugin_escape_cpp_string(_tooltip "${ECM_QTDESIGNERPLUGIN_${widget}_TOOLTIP}") | ||||
291 | _ecm_qtdesignerplugin_escape_cpp_string(_whatsthis "${ECM_QTDESIGNERPLUGIN_${widget}_WHATSTHIS}") | ||||
292 | set(_group ${ECM_QTDESIGNERPLUGIN_${widget}_GROUP}) | ||||
293 | if(NOT _group) | ||||
294 | set(_group "${default_group}") | ||||
295 | endif() | ||||
296 | _ecm_qtdesignerplugin_escape_cpp_string(_group "${_group}") | ||||
297 | set(_dom_xml "${ECM_QTDESIGNERPLUGIN_${widget}_DOM_XML}") | ||||
298 | if(_dom_xml) | ||||
299 | _ecm_qtdesignerplugin_unescape_code(_dom_xml "${_dom_xml}") | ||||
300 | string(REPLACE "\"" "\\\"" _dom_xml "${_dom_xml}") | ||||
301 | set(_dom_xml_method " QString domXml() const override { return QStringLiteral(\"${_dom_xml}\"); }") | ||||
302 | else() | ||||
303 | set(_dom_xml_method) | ||||
304 | endif() | ||||
305 | set(_icon "${ECM_QTDESIGNERPLUGIN_${widget}_ICON}") | ||||
306 | if(_icon) | ||||
307 | get_filename_component(_icon_filename ${_icon} NAME) | ||||
308 | set(_icon_construct "QIcon(QStringLiteral(\":${rc_icon_dir}/${_icon_filename}\"))") | ||||
309 | else() | ||||
310 | set(_icon_construct "QIcon()") | ||||
311 | endif() | ||||
312 | set(_initialize_code "${ECM_QTDESIGNERPLUGIN_${widget}_INITIALIZE_CODE}") | ||||
313 | if(NOT _initialize_code) | ||||
314 | set(_initialize_code | ||||
315 | " Q_UNUSED(core); | ||||
316 | | ||||
317 | if (m_initialized) return; | ||||
318 | | ||||
319 | m_initialized = true;" | ||||
320 | ) | ||||
321 | else() | ||||
322 | _ecm_qtdesignerplugin_unescape_code(_initialize_code "${_initialize_code}") | ||||
323 | endif() | ||||
324 | | ||||
325 | # write code | ||||
326 | file(APPEND ${designer_src_file} " | ||||
327 | class ${_factory_classname} | ||||
328 | : public QObject | ||||
329 | , public QDesignerCustomWidgetInterface | ||||
330 | { | ||||
331 | Q_OBJECT | ||||
332 | Q_INTERFACES(QDesignerCustomWidgetInterface) | ||||
333 | | ||||
334 | public: | ||||
335 | explicit ${_factory_classname}(QObject *parent = nullptr) | ||||
336 | : QObject(parent) | ||||
337 | , m_initialized(false) | ||||
338 | {} | ||||
339 | | ||||
340 | ~${_factory_classname}() override {} | ||||
341 | | ||||
342 | public: // QDesignerCustomWidgetInterface API | ||||
343 | bool isInitialized() const override { return m_initialized; } | ||||
344 | ${_dom_xml_method} | ||||
345 | bool isContainer() const override { return ${_is_container}; } | ||||
346 | QIcon icon() const override { return ${_icon_construct}; } | ||||
347 | QString group() const override { return QStringLiteral(\"${_group}\"); } | ||||
348 | QString includeFile() const override { return QStringLiteral(\"${ECM_QTDESIGNERPLUGIN_${widget}_INCLUDE_FILE}\"); } | ||||
349 | QString name() const override { return QStringLiteral(\"${_classname}\"); } | ||||
350 | QString toolTip() const override { return QStringLiteral(\"${_tooltip}\"); } | ||||
351 | QString whatsThis() const override { return QStringLiteral(\"${_whatsthis}\"); } | ||||
352 | | ||||
353 | QWidget* createWidget(QWidget* parent) override | ||||
354 | { | ||||
355 | ${ECM_QTDESIGNERPLUGIN_${widget}_CREATE_WIDGET_CODE} | ||||
356 | } | ||||
357 | | ||||
358 | void initialize(QDesignerFormEditorInterface* core) override | ||||
359 | { | ||||
360 | ${_initialize_code} | ||||
361 | } | ||||
362 | | ||||
363 | private: | ||||
364 | bool m_initialized; | ||||
365 | }; | ||||
366 | " | ||||
367 | ) | ||||
368 | endfunction() | ||||
369 | | ||||
370 | # helper method | ||||
371 | function(_ecm_qtdesignerplugin_write_icon_qrc_file rc_file rc_icon_dir) | ||||
372 | set(_icons ${ARGN}) | ||||
373 | file(WRITE ${rc_file} | ||||
374 | "<!DOCTYPE RCC><RCC version=\"1.0\"> | ||||
375 | <!-- DO NOT EDIT! Generated from ecm_add_qtdesignerplugin() --> | ||||
376 | <qresource prefix=\"${rc_icon_dir}\"> | ||||
377 | " | ||||
378 | ) | ||||
379 | foreach(_icon ${_icons}) | ||||
380 | get_filename_component(_icon_filename ${_icon} NAME) | ||||
381 | file(APPEND ${rc_file} "<file alias=\"${_icon_filename}\">${_icon}</file>\n") | ||||
382 | endforeach() | ||||
383 | file(APPEND ${rc_file} | ||||
384 | "</qresource> | ||||
385 | </RCC> | ||||
386 | " | ||||
387 | ) | ||||
388 | endfunction() | ||||
389 | | ||||
390 | # This needs to be a macro not a function because of the nested | ||||
391 | # find_package() call, which will set some variables. | ||||
392 | macro(ecm_add_qtdesignerplugin target) | ||||
393 | set(options | ||||
394 | ) | ||||
395 | set(oneValueArgs | ||||
396 | NAME | ||||
397 | OUTPUT_NAME | ||||
398 | INSTALL_DESTINATION | ||||
399 | DEFAULT_GROUP | ||||
400 | COMPONENT | ||||
401 | DEFAULT_HEADER_CASE | ||||
402 | DEFAULT_HEADER_EXTENSION | ||||
403 | DEFAULT_ICON_DIR | ||||
404 | ) | ||||
405 | set(multiValueArgs | ||||
406 | WIDGETS | ||||
407 | LINK_LIBRARIES | ||||
408 | INCLUDE_FILES | ||||
409 | SOURCES | ||||
410 | ) | ||||
411 | cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) | ||||
412 | | ||||
413 | # args sanity check | ||||
414 | if (NOT ARGS_WIDGETS) | ||||
415 | message(FATAL_ERROR "No WIDGETS passed when calling ecm_add_qtdesignerplugin().") | ||||
416 | endif() | ||||
417 | foreach(_widget ${ARGS_WIDGETS}) | ||||
418 | # using _CLASS_NAME as sample property to find if defined | ||||
419 | if (NOT ECM_QTDESIGNERPLUGIN_${_widget}_CLASS_NAME) | ||||
420 | message(FATAL_ERROR "Undefined widget passed when calling ecm_add_qtdesignerplugin(): ${_widget}") | ||||
421 | endif() | ||||
422 | endforeach() | ||||
423 | | ||||
424 | if(NOT ARGS_NAME) | ||||
425 | set(ARGS_NAME "${target}") | ||||
426 | endif() | ||||
427 | if(NOT ARGS_DEFAULT_GROUP) | ||||
428 | set(ARGS_DEFAULT_GROUP "Custom") | ||||
429 | endif() | ||||
430 | if(NOT ARGS_DEFAULT_HEADER_EXTENSION) | ||||
431 | set(ARGS_DEFAULT_HEADER_EXTENSION "h") | ||||
432 | endif() | ||||
433 | if(NOT ARGS_DEFAULT_HEADER_CASE) | ||||
434 | set(ARGS_DEFAULT_HEADER_CASE "LOWER_CASE") | ||||
435 | else() | ||||
436 | set(_allowed_values "LOWER_CASE" "UPPER_CASE" "SAME_CASE") | ||||
437 | list(FIND _allowed_values "${ARGS_DEFAULT_HEADER_CASE}" _index) | ||||
438 | if(_index EQUAL "-1") | ||||
439 | message(FATAL_ERROR "Unexpected value for DEFAULT_HEADER_CASE argument to ecm_add_qtdesignerplugin(): ${ARGS_DEFAULT_HEADER_CASE}") | ||||
440 | endif() | ||||
441 | endif() | ||||
442 | if(NOT ARGS_DEFAULT_ICON_DIR) | ||||
443 | set(ARGS_DEFAULT_ICON_DIR "${CMAKE_CURRENT_SOURCE_DIR}/pics") | ||||
444 | else() | ||||
445 | if (NOT IS_ABSOLUTE ${ARGS_DEFAULT_ICON_DIR}) | ||||
446 | set(ARGS_DEFAULT_ICON_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${ARGS_DEFAULT_ICON_DIR}") | ||||
447 | endif() | ||||
448 | if(NOT EXISTS "${ARGS_DEFAULT_ICON_DIR}") | ||||
449 | message(FATAL_ERROR "No such directory as passed with DEFAULT_ICON_DIR when calling ecm_add_qtdesignerplugin(): ${ARGS_DEFAULT_ICON_DIR}") | ||||
450 | endif() | ||||
451 | endif() | ||||
452 | | ||||
453 | # Check deps | ||||
454 | # peek at Qt5Core to learn about the version to decide whether Qt5UiPlugin is enough | ||||
455 | if(NOT Qt5Core_FOUND) | ||||
456 | find_package(Qt5Core QUIET CONFIG) | ||||
457 | endif() | ||||
458 | if(Qt5Core_VERSION VERSION_LESS "5.5.0") | ||||
459 | set(_qtdesigner_tobefound TRUE) | ||||
460 | elseif(Qt5Core_VERSION VERSION_LESS "5.9.0") | ||||
461 | set(_qtdesigner_tobefound TRUE) | ||||
462 | set(_qtuiplugin_tobefound TRUE) | ||||
463 | else() | ||||
464 | # Since Qt 5.9 only Qt5UiPlugin is needed | ||||
465 | set(_qtuiplugin_tobefound TRUE) | ||||
466 | endif() | ||||
467 | if(NOT Qt5Designer_FOUND AND _qtdesigner_tobefound) | ||||
468 | find_package(Qt5Designer QUIET CONFIG) | ||||
469 | set_package_properties(Qt5Designer PROPERTIES | ||||
470 | PURPOSE "Required to build Qt Designer plugins" | ||||
471 | TYPE REQUIRED | ||||
472 | ) | ||||
473 | endif() | ||||
474 | if(NOT Qt5UiPlugin_FOUND AND _qtuiplugin_tobefound) | ||||
475 | find_package(Qt5UiPlugin QUIET CONFIG) | ||||
476 | set_package_properties(Qt5UiPlugin PROPERTIES | ||||
477 | PURPOSE "Required to build Qt Designer plugins" | ||||
478 | TYPE REQUIRED | ||||
479 | ) | ||||
480 | endif() | ||||
481 | if (Qt5Designer_FOUND) | ||||
482 | set(_qtdesigner_tobefound FALSE) | ||||
483 | endif() | ||||
484 | if (Qt5UiPlugin_FOUND) | ||||
485 | set(_qtuiplugin_tobefound FALSE) | ||||
486 | # in some old versions Qt5UiPlugin does not set its _INCLUDE_DIRS variable. Fill it manually | ||||
487 | get_target_property(Qt5UiPlugin_INCLUDE_DIRS Qt5::UiPlugin INTERFACE_INCLUDE_DIRECTORIES) | ||||
488 | endif() | ||||
489 | | ||||
490 | # setup plugin only if designer/uiplugin libs were found, as we do not abort hard the cmake run otherwise | ||||
491 | if(NOT _qtdesigner_tobefound AND NOT _qtuiplugin_tobefound) | ||||
492 | set(_generation_dir "${CMAKE_CURRENT_BINARY_DIR}/${target}_ECMQtDesignerPlugin") | ||||
493 | file(MAKE_DIRECTORY "${_generation_dir}") | ||||
494 | set(_rc_icon_dir "/${ARGS_NAME}/designer") | ||||
495 | | ||||
496 | # process defaults for widgets | ||||
497 | foreach(_widget ${ARGS_WIDGETS}) | ||||
498 | set(_class_name "${ECM_QTDESIGNERPLUGIN_${_widget}_CLASS_NAME}") | ||||
499 | # include file | ||||
500 | set(_include_file "${ECM_QTDESIGNERPLUGIN_${_widget}_INCLUDE_FILE}") | ||||
501 | if(NOT _include_file) | ||||
502 | set(_include_file "${_class_name}") | ||||
503 | if (ARGS_DEFAULT_HEADER_CASE STREQUAL "LOWER_CASE") | ||||
504 | string(TOLOWER "${_include_file}" _include_file) | ||||
505 | elseif(ARGS_DEFAULT_HEADER_CASE STREQUAL "UPPER_CASE") | ||||
506 | string(TOUPPER "${_include_file}" _include_file) | ||||
507 | endif() | ||||
508 | # turn any namespaces into dir levels | ||||
509 | string(REPLACE "::" "/" _include_file ${_include_file}) | ||||
510 | set(ECM_QTDESIGNERPLUGIN_${_widget}_INCLUDE_FILE "${_include_file}.${ARGS_DEFAULT_HEADER_EXTENSION}") | ||||
511 | endif() | ||||
512 | # icon | ||||
513 | set(_icon "${ECM_QTDESIGNERPLUGIN_${_widget}_ICON}") | ||||
514 | if(NOT _icon) | ||||
515 | string(TOLOWER "${_class_name}" _icon) | ||||
516 | # handle any namespaces | ||||
517 | string(REPLACE "::" "_" _icon "${_icon}") | ||||
518 | set(_icon "${ARGS_DEFAULT_ICON_DIR}/${_icon}.png") | ||||
519 | if(EXISTS "${_icon}") | ||||
520 | set(ECM_QTDESIGNERPLUGIN_${_widget}_ICON "${_icon}") | ||||
521 | endif() | ||||
522 | endif() | ||||
523 | endforeach() | ||||
524 | | ||||
525 | set(_plugin_src_file "${_generation_dir}/designerplugin.cpp") | ||||
526 | set(_srcs | ||||
527 | ${ARGS_SOURCES} | ||||
528 | ${_plugin_src_file} | ||||
529 | ) | ||||
530 | | ||||
531 | set(_icons) | ||||
532 | foreach(_widget ${ARGS_WIDGETS}) | ||||
533 | list(APPEND _icons "${ECM_QTDESIGNERPLUGIN_${_widget}_ICON}") | ||||
534 | endforeach() | ||||
535 | | ||||
536 | # generate qrc file with icons | ||||
537 | if (_icons) | ||||
538 | set(_rc_file "${_generation_dir}/designerplugin.rc") | ||||
539 | set(_rc_work_file "${_rc_file}.work") | ||||
540 | | ||||
541 | _ecm_qtdesignerplugin_write_icon_qrc_file("${_rc_work_file}" "${_rc_icon_dir}" ${_icons}) | ||||
542 | # avoid rebuilding if there was no change | ||||
543 | execute_process( | ||||
544 | COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_rc_work_file}" "${_rc_file}" | ||||
545 | ) | ||||
546 | file(REMOVE "${_rc_work_file}") | ||||
547 | | ||||
548 | qt5_add_resources(_srcs ${_rc_file}) | ||||
549 | endif() | ||||
550 | | ||||
551 | # generate source file | ||||
552 | set(_collection_classname "${ARGS_NAME}QtDesignerWidgetCollection") | ||||
553 | | ||||
554 | set(_include_files | ||||
555 | # classes inherited | ||||
556 | QDesignerCustomWidgetCollectionInterface | ||||
557 | QDesignerCustomWidgetInterface | ||||
558 | QObject | ||||
559 | # classes used | ||||
560 | QIcon | ||||
561 | QString | ||||
562 | ${ARGS_INCLUDE_FILES} | ||||
563 | ) | ||||
564 | foreach(_widget ${ARGS_WIDGETS}) | ||||
565 | list(APPEND _include_files ${ECM_QTDESIGNERPLUGIN_${_widget}_INCLUDE_FILE}) | ||||
566 | endforeach() | ||||
567 | list(REMOVE_DUPLICATES _include_files) | ||||
568 | | ||||
569 | set(_plugin_src_work_file "${_plugin_src_file}.work") | ||||
570 | file(WRITE ${_plugin_src_work_file} "// DO NOT EDIT! Generated from ecm_add_qtdesignerplugin()\n\n") | ||||
571 | foreach(_include_file ${_include_files}) | ||||
572 | if (NOT ${_include_file} MATCHES "^[\"<]") | ||||
573 | set(_include_file "<${_include_file}>") | ||||
574 | endif() | ||||
575 | file(APPEND ${_plugin_src_work_file} "#include ${_include_file}\n") | ||||
576 | endforeach() | ||||
577 | foreach(_widget ${ARGS_WIDGETS}) | ||||
578 | _ecm_qtdesignerplugin_write_widget(${_plugin_src_work_file} ${_widget} ${ARGS_DEFAULT_GROUP} ${_rc_icon_dir}) | ||||
579 | endforeach() | ||||
580 | file(APPEND ${_plugin_src_work_file} " | ||||
581 | class ${_collection_classname} | ||||
582 | : public QObject | ||||
583 | , public QDesignerCustomWidgetCollectionInterface | ||||
584 | { | ||||
585 | Q_OBJECT | ||||
586 | Q_INTERFACES( | ||||
587 | QDesignerCustomWidgetCollectionInterface | ||||
588 | ) | ||||
589 | | ||||
590 | Q_PLUGIN_METADATA(IID \"org.qt-project.Qt.QDesignerCustomWidgetInterface\") | ||||
591 | | ||||
592 | public: | ||||
593 | explicit ${_collection_classname}(QObject* parent = nullptr); | ||||
594 | | ||||
595 | public: // QDesignerCustomWidgetCollectionInterface API | ||||
596 | QList<QDesignerCustomWidgetInterface*> customWidgets() const override; | ||||
597 | | ||||
598 | private: | ||||
599 | QList<QDesignerCustomWidgetInterface*> m_widgetFactories; | ||||
600 | }; | ||||
601 | | ||||
602 | ${_collection_classname}::${_collection_classname}(QObject* parent) | ||||
603 | : QObject(parent) | ||||
604 | { | ||||
605 | m_widgetFactories = QList<QDesignerCustomWidgetInterface*>{ | ||||
606 | " | ||||
607 | ) | ||||
608 | foreach(_widget ${ARGS_WIDGETS}) | ||||
609 | file(APPEND ${_plugin_src_work_file} " new ${ECM_QTDESIGNERPLUGIN_${_widget}_FACTORY_CLASS_NAME}(this),\n") | ||||
610 | endforeach() | ||||
611 | file(APPEND ${_plugin_src_work_file} | ||||
612 | " }; | ||||
613 | } | ||||
614 | | ||||
615 | QList<QDesignerCustomWidgetInterface*> ${_collection_classname}::customWidgets() const | ||||
616 | { | ||||
617 | return m_widgetFactories; | ||||
618 | } | ||||
619 | | ||||
620 | #include \"designerplugin.moc\" | ||||
621 | " | ||||
622 | ) | ||||
623 | | ||||
624 | # avoid rebuilding if there was no change | ||||
625 | execute_process( | ||||
626 | COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_plugin_src_work_file}" "${_plugin_src_file}" | ||||
627 | ) | ||||
628 | file(REMOVE "${_plugin_src_work_file}") | ||||
629 | | ||||
630 | # setup plugin binary | ||||
631 | add_library(${target} MODULE ${_srcs}) | ||||
632 | if(Qt5UiPlugin_VERSION AND NOT Qt5UiPlugin_VERSION VERSION_LESS "5.9.0") | ||||
633 | list(APPEND ARGS_LINK_LIBRARIES Qt5::UiPlugin) | ||||
634 | else() | ||||
635 | # For Qt <5.9 include dir variables needed | ||||
636 | target_include_directories(${target} | ||||
637 | PRIVATE ${Qt5UiPlugin_INCLUDE_DIRS} | ||||
638 | PRIVATE ${Qt5Designer_INCLUDE_DIRS} | ||||
639 | ) | ||||
640 | endif() | ||||
641 | if(NOT WIN32) | ||||
642 | # Since there are no libraries provided by this module, | ||||
643 | # there is no point including the build tree in RPath, | ||||
644 | # and then having to edit it at install time. | ||||
645 | set_target_properties(${target} PROPERTIES | ||||
646 | SKIP_BUILD_RPATH TRUE | ||||
647 | BUILD_WITH_INSTALL_RPATH TRUE | ||||
648 | ) | ||||
649 | endif() | ||||
650 | if (ARGS_OUTPUT_NAME) | ||||
651 | set_target_properties(${target} PROPERTIES | ||||
652 | OUTPUT_NAME ${ARGS_OUTPUT_NAME} | ||||
653 | ) | ||||
654 | endif() | ||||
655 | target_link_libraries(${target} ${ARGS_LINK_LIBRARIES}) | ||||
656 | | ||||
657 | if (DEFINED ARGS_COMPONENT) | ||||
658 | set(_component COMPONENT ${ARGS_COMPONENT}) | ||||
659 | else() | ||||
660 | set(_component) | ||||
661 | endif() | ||||
662 | | ||||
663 | install(TARGETS ${target} DESTINATION ${ARGS_INSTALL_DESTINATION} ${_component}) | ||||
664 | endif() | ||||
665 | endmacro() |