From d9e6e1221c92f09c0fd9d1dc7a5e0caf0070a56d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=BCller?= Date: Sat, 15 Aug 2020 16:49:48 +0200 Subject: [PATCH 1/5] CMake: Add build options BUILD_SHARED_LIB and BUILD_OBJECT_LIB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SortFilterProxyModel is an awesome piece of code and we use it in several projects. This is the reason for this patch introducing shared library support. By doing so we: * Save memory by using shared library - at least RAM when running multiple applications using SortFilterProxyModel * Save build time: SortFilterProxyModel is build once for all To remain compatible with previous behaviour user can select what to do by cmake options: BUILD_OBJECT_LIB: (Default ON): Same as before: Add sources to your project and build obj-files that can be linked to your binary. It was taken care that projects behaves same as before (tested) - no need for develepers to make changes in their project. BUILD_SHARED_LIB: Build and install a shared library so that other projects can use it. This is the way packagers prefer deploying code. Signed-off-by: Andreas Müller --- CMakeLists.txt | 90 +++++++++++++++++++++++++---- SortFilterProxyModelConfig.cmake.in | 8 +++ 2 files changed, 88 insertions(+), 10 deletions(-) create mode 100644 SortFilterProxyModelConfig.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d3bf2b..16a7ee7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,15 +2,34 @@ cmake_minimum_required(VERSION 3.1) set(CMAKE_CXX_STANDARD 11) -find_package(Qt5 REQUIRED - Core - Qml - ) +project(SortFilterProxyModel LANGUAGES CXX) +set(SFPM_VERSION_MAJOR "0") +set(SFPM_VERSION_MINOR "1") +set(SFPM_VERSION_PATCH "1") + +set(PROJECT_VERSION "${SFPM_VERSION_MAJOR}.${SFPM_VERSION_MINOR}.${SFPM_VERSION_PATCH}") + +# cmake macros +include(FeatureSummary) +include(GNUInstallDirs) + +# qt5 libs +find_package(Qt5 COMPONENTS Core Qml CONFIG REQUIRED) + +# Build options (exclusive on or the other - not both) +# * developers might prefer BUILD_OBJECT_LIB +# * packagers should use BUILD_SHARED_LIB +option(BUILD_OBJECT_LIB "Build object library. Add code in your project and add \$ in your CMakeLists.txt" ON) +option(BUILD_SHARED_LIB "Build shared library. Build and install library to a folder wher cmake can find it for your project" OFF) + +if(BUILD_OBJECT_LIB AND BUILD_SHARED_LIB) + message(FATAL_ERROR "You cannot select both BUILD_OBJECT_LIB and BUILD_SHARED_LIB") +endif(BUILD_OBJECT_LIB AND BUILD_SHARED_LIB) set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) # This is to find generated *.moc and *.h files in build dir -add_library(SortFilterProxyModel OBJECT +set(SFPM_SOURCES qqmlsortfilterproxymodel.cpp filters/filter.cpp filters/filtercontainer.cpp @@ -42,8 +61,59 @@ add_library(SortFilterProxyModel OBJECT proxyroles/filterrole.cpp ) -target_include_directories(SortFilterProxyModel PUBLIC - ${CMAKE_CURRENT_LIST_DIR} - $ - $ - ) +if(BUILD_OBJECT_LIB) + message("Build SortFilterProxyModel object library for in-tree usage") + + add_library(SortFilterProxyModel OBJECT ${SFPM_SOURCES}) + + target_include_directories(SortFilterProxyModel PUBLIC + $ + $ + ) +endif(BUILD_OBJECT_LIB) + +if(BUILD_SHARED_LIB) + message("Build SortFilterProxyModel shared library for target installation") + + add_library(SortFilterProxyModel SHARED ${SFPM_SOURCES} ) + set_target_properties(SortFilterProxyModel PROPERTIES VERSION ${PROJECT_VERSION}) + set_target_properties(SortFilterProxyModel PROPERTIES SOVERSION ${SFPM_VERSION_MAJOR}) + + target_link_libraries(SortFilterProxyModel + PRIVATE + Qt5::Core + Qt5::Qml + # ask linker to help us finding unresolved symbols + "-Wl,--no-undefined" + ) + # install library + install(TARGETS SortFilterProxyModel + EXPORT SortFilterProxyModelExport + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + # configure SortFilterProxyModelConfigVersion.cmake + include(CMakePackageConfigHelpers) + write_basic_package_version_file( + SortFilterProxyModelConfigVersion.cmake + VERSION ${PACKAGE_VERSION} + COMPATIBILITY SameMinorVersion + ) + + # configure SortFilterProxyModelConfig.cmake + configure_file(SortFilterProxyModelConfig.cmake.in SortFilterProxyModelConfig.cmake @ONLY) + + # install SortFilterProxyModelConfig(Version).cmake + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/SortFilterProxyModelConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/SortFilterProxyModelConfigVersion.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SortFilterProxyModel + ) + + # install targets cmake-files + install(EXPORT SortFilterProxyModelExport + FILE SortFilterProxyModelTargets.cmake + NAMESPACE SortFilterProxyModel:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SortFilterProxyModel + ) + +endif(BUILD_SHARED_LIB) diff --git a/SortFilterProxyModelConfig.cmake.in b/SortFilterProxyModelConfig.cmake.in new file mode 100644 index 0000000..8dbd042 --- /dev/null +++ b/SortFilterProxyModelConfig.cmake.in @@ -0,0 +1,8 @@ +include(CMakeFindDependencyMacro) + +# dependencies +find_dependency(Qt5 COMPONENTS Core Qml REQUIRED) + +# targets file +include("${CMAKE_CURRENT_LIST_DIR}/SortFilterProxyModelTargets.cmake") + From 14a3627c6d959e9f4c96e5a6c97e70e2e0fa2c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=BCller?= Date: Sun, 16 Aug 2020 18:59:01 +0200 Subject: [PATCH 2/5] .gitignore: Add CMakeLists.txt.user* to gitignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that SortFilterProxyModel can be build standalone, Qt-Creator creates CMake.user.. files once it configures SortFilterProxyModel. User configurations are nothing to be added to repository (accidentally) so add an entry in .gitignore. Signed-off-by: Andreas Müller --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 73d1dfd..5c69265 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ Thumbs.db *.rc /.qmake.cache /.qmake.stash +CMakeLists.txt.user* # qtcreator generated files *.pro.user* From d86a98d076dc73d1f45c8e1ae92a0230e09d8f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=BCller?= Date: Sun, 16 Aug 2020 22:53:32 +0200 Subject: [PATCH 3/5] Add headers to library sources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This does not cause any change to build but it adds the headers to the file list of IDEs like qt-creator gives developers much easier access. Signed-off-by: Andreas Müller --- CMakeLists.txt | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 16a7ee7..556d786 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,10 +61,39 @@ set(SFPM_SOURCES proxyroles/filterrole.cpp ) +set(SFPM_HEADERS + proxyroles/filterrole.h + proxyroles/switchrole.h + proxyroles/proxyrolecontainer.h + proxyroles/singlerole.h + proxyroles/joinrole.h + proxyroles/expressionrole.h + proxyroles/regexprole.h + proxyroles/proxyrole.h + sorters/sorter.h + sorters/stringsorter.h + sorters/filtersorter.h + sorters/expressionsorter.h + sorters/sortercontainer.h + sorters/rolesorter.h + filters/regexpfilter.h + filters/filtercontainerfilter.h + filters/anyoffilter.h + filters/alloffilter.h + filters/indexfilter.h + filters/filter.h + filters/expressionfilter.h + filters/valuefilter.h + filters/rangefilter.h + filters/rolefilter.h + filters/filtercontainer.h + qqmlsortfilterproxymodel.h + ) + if(BUILD_OBJECT_LIB) message("Build SortFilterProxyModel object library for in-tree usage") - add_library(SortFilterProxyModel OBJECT ${SFPM_SOURCES}) + add_library(SortFilterProxyModel OBJECT ${SFPM_SOURCES} ${SFPM_HEADERS}) target_include_directories(SortFilterProxyModel PUBLIC $ Date: Mon, 17 Aug 2020 00:25:49 +0200 Subject: [PATCH 4/5] Add SortFilterProxyModel::registerQml() for environments linking with --as-needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are build environments that link their binaries with --as-needed for good reasons [1]. This causes trouble here too: The application linking against SortFilterProxyModel has no reference to a symbol within SortFilterProxyModel so the library will simply be skipped and not loaded at runtime causing: | qrc:/qml/FaFilter.qml:2:1: module "SortFilterProxyModel" is not installed Linker with --as-needed behave similar as linking against static libraries and Qt documatation [2] warns explicitly not to use Q_COREAPP_STARTUP_FUNCTION when linking against static libraries. To get around a stateless class SortFilterProxyModel (luckily this name was not used yet) was created, that just contains the static method registerQml(). To avoid multile calls on environemts that do not link with --as-needed all register functions ensure to be called once only by a variable wasRegistered. In the following patch Readme.md will updated and will recommend highly to call SortFilterProxyModel::registerQml() - who knows which environment your application will be build in? [1] https://wiki.gentoo.org/wiki/Project:Quality_Assurance/As-needed [2] https://doc.qt.io/qt-5/qcoreapplication.html#Q_COREAPP_STARTUP_FUNCTION Signed-off-by: Andreas Müller --- CMakeLists.txt | 12 ++++++++++++ filters/filtersqmltypes.cpp | 23 ++++++++++++++--------- proxyroles/proxyrolesqmltypes.cpp | 17 +++++++++++------ qqmlsortfilterproxymodel.cpp | 7 ++++++- sorters/sortersqmltypes.cpp | 17 +++++++++++------ sortfilterproxymodel.cpp | 19 +++++++++++++++++++ sortfilterproxymodel.h | 10 ++++++++++ 7 files changed, 83 insertions(+), 22 deletions(-) create mode 100644 sortfilterproxymodel.cpp create mode 100644 sortfilterproxymodel.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 556d786..3499d69 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) # This is to find generated *.moc and *.h files in build dir set(SFPM_SOURCES + sortfilterproxymodel.cpp qqmlsortfilterproxymodel.cpp filters/filter.cpp filters/filtercontainer.cpp @@ -62,6 +63,7 @@ set(SFPM_SOURCES ) set(SFPM_HEADERS + sortfilterproxymodel.h proxyroles/filterrole.h proxyroles/switchrole.h proxyroles/proxyrolecontainer.h @@ -144,5 +146,15 @@ if(BUILD_SHARED_LIB) NAMESPACE SortFilterProxyModel:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SortFilterProxyModel ) + # install the one and only header + install(FILES + ${CMAKE_CURRENT_SOURCE_DIR}/sortfilterproxymodel.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/sortfilterproxymodel + ) + # announce header to our consumers + target_include_directories(SortFilterProxyModel + PUBLIC + $ + ) endif(BUILD_SHARED_LIB) diff --git a/filters/filtersqmltypes.cpp b/filters/filtersqmltypes.cpp index 6704472..0e759b8 100644 --- a/filters/filtersqmltypes.cpp +++ b/filters/filtersqmltypes.cpp @@ -9,18 +9,23 @@ #include #include +static bool wasRegistered = false; + namespace qqsfpm { void registerFiltersTypes() { - qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "Filter", "Filter is an abstract class"); - qmlRegisterType("SortFilterProxyModel", 0, 2, "ValueFilter"); - qmlRegisterType("SortFilterProxyModel", 0, 2, "IndexFilter"); - qmlRegisterType("SortFilterProxyModel", 0, 2, "RegExpFilter"); - qmlRegisterType("SortFilterProxyModel", 0, 2, "RangeFilter"); - qmlRegisterType("SortFilterProxyModel", 0, 2, "ExpressionFilter"); - qmlRegisterType("SortFilterProxyModel", 0, 2, "AnyOf"); - qmlRegisterType("SortFilterProxyModel", 0, 2, "AllOf"); - qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "FilterContainer", "FilterContainer can only be used as an attaching type"); + if(!wasRegistered) { + qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "Filter", "Filter is an abstract class"); + qmlRegisterType("SortFilterProxyModel", 0, 2, "ValueFilter"); + qmlRegisterType("SortFilterProxyModel", 0, 2, "IndexFilter"); + qmlRegisterType("SortFilterProxyModel", 0, 2, "RegExpFilter"); + qmlRegisterType("SortFilterProxyModel", 0, 2, "RangeFilter"); + qmlRegisterType("SortFilterProxyModel", 0, 2, "ExpressionFilter"); + qmlRegisterType("SortFilterProxyModel", 0, 2, "AnyOf"); + qmlRegisterType("SortFilterProxyModel", 0, 2, "AllOf"); + qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "FilterContainer", "FilterContainer can only be used as an attaching type"); + wasRegistered = true; + } } Q_COREAPP_STARTUP_FUNCTION(registerFiltersTypes) diff --git a/proxyroles/proxyrolesqmltypes.cpp b/proxyroles/proxyrolesqmltypes.cpp index efea256..d708041 100644 --- a/proxyroles/proxyrolesqmltypes.cpp +++ b/proxyroles/proxyrolesqmltypes.cpp @@ -7,15 +7,20 @@ #include #include +static bool wasRegistered = false; + namespace qqsfpm { void registerProxyRoleTypes() { - qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "ProxyRole", "ProxyRole is an abstract class"); - qmlRegisterType("SortFilterProxyModel", 0, 2, "JoinRole"); - qmlRegisterType("SortFilterProxyModel", 0, 2, "SwitchRole"); - qmlRegisterType("SortFilterProxyModel", 0, 2, "ExpressionRole"); - qmlRegisterType("SortFilterProxyModel", 0, 2, "RegExpRole"); - qmlRegisterType("SortFilterProxyModel", 0, 2, "FilterRole"); + if(!wasRegistered) { + qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "ProxyRole", "ProxyRole is an abstract class"); + qmlRegisterType("SortFilterProxyModel", 0, 2, "JoinRole"); + qmlRegisterType("SortFilterProxyModel", 0, 2, "SwitchRole"); + qmlRegisterType("SortFilterProxyModel", 0, 2, "ExpressionRole"); + qmlRegisterType("SortFilterProxyModel", 0, 2, "RegExpRole"); + qmlRegisterType("SortFilterProxyModel", 0, 2, "FilterRole"); + wasRegistered = true; + } } Q_COREAPP_STARTUP_FUNCTION(registerProxyRoleTypes) diff --git a/qqmlsortfilterproxymodel.cpp b/qqmlsortfilterproxymodel.cpp index bd06435..2cef627 100644 --- a/qqmlsortfilterproxymodel.cpp +++ b/qqmlsortfilterproxymodel.cpp @@ -5,6 +5,8 @@ #include "sorters/sorter.h" #include "proxyroles/proxyrole.h" +static bool wasRegistered = false; + namespace qqsfpm { /*! @@ -571,7 +573,10 @@ void QQmlSortFilterProxyModel::onProxyRolesCleared() } void registerQQmlSortFilterProxyModelTypes() { - qmlRegisterType("SortFilterProxyModel", 0, 2, "SortFilterProxyModel"); + if(!wasRegistered) { + qmlRegisterType("SortFilterProxyModel", 0, 2, "SortFilterProxyModel"); + wasRegistered = true; + } } Q_COREAPP_STARTUP_FUNCTION(registerQQmlSortFilterProxyModelTypes) diff --git a/sorters/sortersqmltypes.cpp b/sorters/sortersqmltypes.cpp index ceba423..a2f7ef7 100644 --- a/sorters/sortersqmltypes.cpp +++ b/sorters/sortersqmltypes.cpp @@ -7,15 +7,20 @@ #include #include +static bool wasRegistered = false; + namespace qqsfpm { void registerSorterTypes() { - qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "Sorter", "Sorter is an abstract class"); - qmlRegisterType("SortFilterProxyModel", 0, 2, "RoleSorter"); - qmlRegisterType("SortFilterProxyModel", 0, 2, "StringSorter"); - qmlRegisterType("SortFilterProxyModel", 0, 2, "FilterSorter"); - qmlRegisterType("SortFilterProxyModel", 0, 2, "ExpressionSorter"); - qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "SorterContainer", "SorterContainer can only be used as an attaching type"); + if(!wasRegistered) { + qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "Sorter", "Sorter is an abstract class"); + qmlRegisterType("SortFilterProxyModel", 0, 2, "RoleSorter"); + qmlRegisterType("SortFilterProxyModel", 0, 2, "StringSorter"); + qmlRegisterType("SortFilterProxyModel", 0, 2, "FilterSorter"); + qmlRegisterType("SortFilterProxyModel", 0, 2, "ExpressionSorter"); + qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "SorterContainer", "SorterContainer can only be used as an attaching type"); + wasRegistered = true; + } } Q_COREAPP_STARTUP_FUNCTION(registerSorterTypes) diff --git a/sortfilterproxymodel.cpp b/sortfilterproxymodel.cpp new file mode 100644 index 0000000..1398ea5 --- /dev/null +++ b/sortfilterproxymodel.cpp @@ -0,0 +1,19 @@ +#include "sortfilterproxymodel.h" + +// forwards +namespace qqsfpm { + +void registerFiltersTypes(); +void registerProxyRoleTypes(); +void registerQQmlSortFilterProxyModelTypes(); +void registerSorterTypes(); + +} + +void SortFilterProxyModel::registerQml() +{ + qqsfpm::registerFiltersTypes(); + qqsfpm::registerProxyRoleTypes(); + qqsfpm::registerQQmlSortFilterProxyModelTypes(); + qqsfpm::registerSorterTypes(); +} diff --git a/sortfilterproxymodel.h b/sortfilterproxymodel.h new file mode 100644 index 0000000..44ea831 --- /dev/null +++ b/sortfilterproxymodel.h @@ -0,0 +1,10 @@ +#ifndef SORTFILTERPROXYMODEL_H +#define SORTFILTERPROXYMODEL_H + +class SortFilterProxyModel +{ +public: + static void registerQml(); +}; + +#endif // SORTFILTERPROXYMODEL_H From 3931d10330dfe01eb855805fdcb0d6da5c31b024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=BCller?= Date: Mon, 17 Aug 2020 12:00:34 +0200 Subject: [PATCH 5/5] README.md: Add/rework decription how to use SortFilterProxyModel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andreas Müller --- README.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2a0cf7c..4a27080 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,62 @@ Install 2. add `include(vendor/vendor.pri)` in your .pro if it is not already done 3. `import SortFilterProxyModel 0.2` to use this library in your QML files -##### Without qpm : +##### With qmake : 1. clone or download this repository -2. * `qmake` add `include (/SortFilterProxyModel.pri)` in your `.pro` - * `CMake` add $ to the sources of your executable target in your cmake project +2. `qmake` add `include (/SortFilterProxyModel.pri)` in your `.pro` 3. `import SortFilterProxyModel 0.2` to use this library in your QML files +##### With [CMake](https://cmake.org/) / SortFilterProxyModel sources included in your project (for compatibilty with existing projects / quick setup - not recommended for packagers): +1. clone / download / add submodule this repository into your project +2. In CMakeLists.txt of your project add + ```CMake + ... + add_subdirectory(/SortFilterProxyModel) + ... + target_link_libraries( + ... + $ + ... + ) + ``` +3. `import SortFilterProxyModel 0.2` to use this library in your QML files + +##### With [CMake](https://cmake.org/) / SortFilterProxyModel build as shared library (recommended): +1. clone / download this repository +2. ensure SortFilterProxyModel is build with **BUILD_OBJECT_LIB=OFF** and **BUILD_SHARED_LIB=ON** either by + * adding ```-DBUILD_OBJECT_LIB=OFF -DBUILD_SHARED_LIB=ON``` to cmake's commandline - or + * setting options in your IDE (Qt-Creator: Project/Build Settings) +3. developers only: make sure SortFilterProxyModel is installed to a location where cmake can find it e.g by + * In SortFilterProxyModel set CMAKE_INSTALL_PREFIX to / add install to your build steps + * In your project: Change CMAKE_PREFIX_PATH to ;/usr +4. In CMakeLists.txt of your project add + ```CMake + ... + find_package(SortFilterProxyModel REQUIRED) + ... + target_link_libraries( + ... + SortFilterProxyModel::SortFilterProxyModel + ... + ) + ``` +5. In your main.cpp + ```cpp + #include + ... + int main(int argc, char *argv[]) + { + QQmlApplicationEngine engine; + ... + SortFilterProxyModel::registerQml(); + ... + engine.load(url); + ... + } + ``` +6. `import SortFilterProxyModel 0.2` to use this library in your QML files + + Sample Usage ------------