├── .clang-format ├── .clazy ├── .cmake-format.py ├── .codespellrc ├── .github └── workflows │ ├── build.yml │ └── nightly.yml ├── .gitignore ├── .gitmodules ├── .gitreview ├── .krazy ├── .mdlrc ├── .mdlrc.rb ├── .pep8 ├── .pre-commit-config.yaml ├── .pylintrc ├── .qmake.conf ├── CMakeLists.txt ├── LICENSE.txt ├── LICENSES ├── BSD-3-Clause.txt └── MIT.txt ├── README.md ├── REUSE.toml ├── cmake └── modules │ └── CheckSubmoduleExists.cmake ├── config.tests └── cpp20 │ └── cpp20.cpp ├── cpp ├── CMakeLists.txt ├── duplicatetracker │ ├── CMakeLists.txt │ ├── README.md │ ├── include │ │ └── duplicatetracker.h │ └── tests │ │ ├── CMakeLists.txt │ │ └── duplicatetracker │ │ ├── CMakeLists.txt │ │ ├── cxx11 │ │ └── CMakeLists.txt │ │ ├── cxx17 │ │ └── CMakeLists.txt │ │ ├── cxx20 │ │ └── CMakeLists.txt │ │ └── tst_duplicatetracker.cpp ├── future-backports │ ├── CMakeLists.txt │ ├── README.md │ ├── include │ │ └── k20 │ │ │ ├── deque.h │ │ │ ├── detail │ │ │ └── erase_if.h │ │ │ ├── forward_list.h │ │ │ ├── list.h │ │ │ ├── map.h │ │ │ ├── set.h │ │ │ ├── string.h │ │ │ ├── unordered_map.h │ │ │ ├── unordered_set.h │ │ │ └── vector.h │ └── tests │ │ ├── CMakeLists.txt │ │ └── k20 │ │ ├── CMakeLists.txt │ │ └── erase_if │ │ ├── CMakeLists.txt │ │ ├── cxx11 │ │ └── CMakeLists.txt │ │ ├── cxx14 │ │ └── CMakeLists.txt │ │ ├── cxx17 │ │ └── CMakeLists.txt │ │ ├── cxx20 │ │ └── CMakeLists.txt │ │ └── tst_erase_if.cpp ├── propagate_const │ ├── CMakeLists.txt │ ├── README.md │ ├── propagate_const.h │ └── test │ │ ├── CMakeLists.txt │ │ └── tst_propagate_const.cpp └── toContainer │ ├── CMakeLists.txt │ ├── README.md │ ├── testcpp14 │ └── CMakeLists.txt │ ├── testcpplatest │ └── CMakeLists.txt │ ├── toContainer.h │ └── tst_toContainer.cpp ├── general ├── CMakeLists.txt └── asan_assert_fail │ ├── CMakeLists.txt │ ├── README.md │ └── asan_assert_fail.c ├── qt ├── CMakeLists.txt ├── KDCoro │ ├── CMakeLists.txt │ ├── KDCoro.h │ ├── README.md │ └── test │ │ ├── CMakeLists.txt │ │ └── tst_KDCoro.cpp ├── KDSignalThrottler │ ├── CMakeLists.txt │ ├── README.md │ ├── examples │ │ ├── CMakeLists.txt │ │ ├── debouncedSearch │ │ │ ├── CMakeLists.txt │ │ │ └── main.cpp │ │ └── signalThrottlersDemo │ │ │ ├── CMakeLists.txt │ │ │ └── main.cpp │ ├── src │ │ ├── KDSignalThrottler.cpp │ │ └── KDSignalThrottler.h │ └── test │ │ ├── CMakeLists.txt │ │ └── tst_KDSignalThrottler.cpp ├── KDSqlDatabaseTransaction │ ├── CMakeLists.txt │ ├── KDSqlDatabaseTransaction.h │ ├── README.md │ └── test │ │ ├── CMakeLists.txt │ │ └── tst_KDSqlDatabaseTransaction.cpp ├── KDStlContainerAdaptor │ ├── CMakeLists.txt │ ├── KDStlContainerAdaptor.h │ ├── README.md │ └── test │ │ ├── CMakeLists.txt │ │ └── tst_KDStlContainerAdaptor.cpp ├── asan_assert_fail_qt │ ├── CMakeLists.txt │ ├── README.md │ └── asan_assert_fail_qt.cpp ├── cmake-project │ ├── README.md │ └── cmake-project ├── dpiinfo.h ├── eventfilter │ ├── CMakeLists.txt │ ├── README.md │ ├── eventfilter.h │ └── example │ │ ├── CMakeLists.txt │ │ └── main.cpp ├── includemocs │ ├── README.md │ ├── check-includemocs-hook.sh │ └── includemocs.py ├── messagehandler │ ├── CMakeLists.txt │ ├── README.md │ ├── example │ │ ├── CMakeLists.txt │ │ └── main.cpp │ ├── src │ │ ├── messagehandler.cpp │ │ └── messagehandler.h │ └── tests │ │ ├── CMakeLists.txt │ │ └── messagehandler │ │ ├── CMakeLists.txt │ │ └── tst_messagehandler.cpp ├── model_view │ ├── CMakeLists.txt │ ├── KDFunctionalSortFilterProxyModel │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── example │ │ │ ├── CMakeLists.txt │ │ │ └── main.cpp │ │ ├── src │ │ │ ├── KDFunctionalSortFilterProxyModel.cpp │ │ │ └── KDFunctionalSortFilterProxyModel.h │ │ └── test │ │ │ ├── CMakeLists.txt │ │ │ └── tst_kdfunctionalsortfilterproxymodel.cpp │ ├── KDTableToListProxyModel │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── examples │ │ │ ├── CMakeLists.txt │ │ │ └── countries │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── countries.qrc │ │ │ │ ├── images │ │ │ │ ├── Austria.png │ │ │ │ ├── Belgium.png │ │ │ │ ├── Bulgaria.png │ │ │ │ ├── Croatia.png │ │ │ │ ├── Cyprus.png │ │ │ │ ├── Czech Republic.png │ │ │ │ ├── Denmark.png │ │ │ │ ├── Estonia.png │ │ │ │ ├── Finland.png │ │ │ │ ├── France.png │ │ │ │ ├── Germany.png │ │ │ │ ├── Greece.png │ │ │ │ ├── Hungary.png │ │ │ │ ├── Ireland.png │ │ │ │ ├── Italy.png │ │ │ │ ├── Latvia.png │ │ │ │ ├── Lithuania.png │ │ │ │ ├── Luxembourg.png │ │ │ │ ├── Malta.png │ │ │ │ ├── Netherlands.png │ │ │ │ ├── Poland.png │ │ │ │ ├── Portugal.png │ │ │ │ ├── Romania.png │ │ │ │ ├── Slovakia.png │ │ │ │ ├── Slovenia.png │ │ │ │ ├── Spain.png │ │ │ │ ├── Sweden.png │ │ │ │ └── United Kingdom.png │ │ │ │ ├── main.cpp │ │ │ │ └── main.qml │ │ ├── src │ │ │ ├── KDTableToListProxyModel.cpp │ │ │ └── KDTableToListProxyModel.h │ │ └── tests │ │ │ ├── CMakeLists.txt │ │ │ └── tst_kdtabletolistproxymodel.cpp │ ├── ModelIterator │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── example │ │ │ ├── CMakeLists.txt │ │ │ └── main.cpp │ │ └── src │ │ │ ├── ModelIterator.cpp │ │ │ └── ModelIterator.h │ ├── README.md │ ├── sortProxyModel │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── sortproxymodel.cpp │ │ ├── sortproxymodel.h │ │ └── test │ │ │ ├── CMakeLists.txt │ │ │ ├── tst_sortproxymodeltest.cpp │ │ │ └── vectormodel.h │ └── updateableModel │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── UpdateableModel.h │ │ └── example │ │ ├── CMakeLists.txt │ │ ├── Data.h │ │ ├── MainWindow.cpp │ │ ├── MainWindow.h │ │ ├── MainWindow.ui │ │ ├── main.cpp │ │ ├── tableModel.cpp │ │ └── tableModel.h ├── notify_guard │ ├── CMakeLists.txt │ ├── README.md │ ├── src │ │ ├── notifyguard.cpp │ │ └── notifyguard.h │ └── test │ │ ├── CMakeLists.txt │ │ └── tst_notifyguard.cpp ├── pointer_cast │ ├── CMakeLists.txt │ ├── README.md │ ├── pointer_cast.h │ └── test │ │ ├── CMakeLists.txt │ │ └── tst_pointer_cast.cpp ├── qml │ ├── CMakeLists.txt │ ├── PropertySelector │ │ ├── PropertySelector.cpp │ │ ├── PropertySelector.h │ │ └── README.md │ └── QmlStackTraceHelper │ │ ├── CMakeLists.txt │ │ ├── QmlStackTraceHelper.cpp │ │ ├── README.md │ │ └── example │ │ ├── CMakeLists.txt │ │ ├── main.cpp │ │ ├── main.qml │ │ └── qrc.qrc ├── qt6_natvis │ └── README.md ├── qt_fmt │ ├── CMakeLists.txt │ ├── README.md │ ├── qt_fmt.h │ ├── qt_fmt_helpers.h │ ├── qt_std_format.h │ ├── testfmt │ │ ├── CMakeLists.txt │ │ └── tst_qt_fmt.cpp │ └── teststd │ │ ├── CMakeLists.txt │ │ └── tst_qt_std_format.cpp ├── qt_hasher │ ├── CMakeLists.txt │ ├── README.md │ ├── qt_hasher.h │ └── test │ │ ├── CMakeLists.txt │ │ ├── tst_qt_hasher.cpp │ │ └── tst_qt_hasher.h ├── singleshot_connect │ ├── CMakeLists.txt │ ├── README.md │ ├── singleshot_connect.h │ └── test │ │ ├── CMakeLists.txt │ │ └── tst_singleshot_connect.cpp ├── stringtokenizer │ ├── CMakeLists.txt │ ├── README.md │ ├── include │ │ ├── QStringTokenizer │ │ ├── QtCore │ │ │ ├── QStringTokenizer │ │ │ └── qstringtokenizer.h │ │ └── qstringtokenizer.h │ ├── src │ │ └── qstringtokenizer.cpp │ └── tests │ │ ├── CMakeLists.txt │ │ └── qstringtokenizer │ │ ├── CMakeLists.txt │ │ ├── cxx11 │ │ └── CMakeLists.txt │ │ ├── cxx14 │ │ └── CMakeLists.txt │ │ ├── cxx17 │ │ └── CMakeLists.txt │ │ ├── cxx20 │ │ └── CMakeLists.txt │ │ └── tst_qstringtokenizer.cpp ├── tabWindow │ ├── CMakeLists.txt │ ├── README.md │ ├── example │ │ ├── CMakeLists.txt │ │ └── main.cpp │ └── src │ │ ├── tabwindow.cpp │ │ └── tabwindow.h └── ui_watchdog │ ├── CMakeLists.txt │ ├── README.md │ ├── example │ ├── CMakeLists.txt │ └── main.cpp │ └── uiwatchdog.h └── squish ├── kdrunsquish.py └── tests.json /.clazy: -------------------------------------------------------------------------------- 1 | SKIP /qt/qt_fmt/fmt|/test/|/qt/qt6_natvis/ 2 | -------------------------------------------------------------------------------- /.codespellrc: -------------------------------------------------------------------------------- 1 | [codespell] 2 | skip = ./build-*,.git,./qt/qt_fmt/fmt 3 | interactive = 3 4 | #ignore camelCase, regardless. also mixed case words with correct spelling 5 | ignore-regex = \b([a-z]+[A-Z0-9][a-z0-9]*|THEREFROM|uffer)\b 6 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | name: CI 6 | 7 | on: 8 | push: 9 | branches: 10 | - master 11 | pull_request: 12 | branches: 13 | - master 14 | 15 | jobs: 16 | build: 17 | runs-on: ${{ matrix.os }} 18 | strategy: 19 | fail-fast: true 20 | matrix: 21 | os: 22 | - ubuntu-latest 23 | - windows-latest 24 | - macos-latest 25 | compiler: 26 | - name: default 27 | config: 28 | - qt_version: 5.15.2 29 | macos_architectures: "x86_64" 30 | - qt_version: 6.6.2 31 | macos_architectures: "x86_64;arm64" 32 | - qt_version: 6.9.0 # bump freely to latest 33 | macos_architectures: "x86_64;arm64" 34 | 35 | include: 36 | # Add clang builds for Ubuntu 37 | - os: ubuntu-latest 38 | compiler: 39 | name: clang 40 | cc: clang 41 | cxx: clang++ 42 | config: 43 | qt_version: 6.6.2 44 | macos_architectures: "x86_64;arm64" 45 | - os: ubuntu-latest 46 | compiler: 47 | name: clang 48 | cc: clang 49 | cxx: clang++ 50 | config: 51 | qt_version: 6.9.0 # bump freely to latest 52 | macos_architectures: "x86_64;arm64" 53 | 54 | steps: 55 | - name: Install Qt ${{ matrix.config.qt_version }} with options and default aqtversion 56 | uses: jurplel/install-qt-action@v4 57 | with: 58 | version: ${{ matrix.config.qt_version }} 59 | cache: true 60 | 61 | - name: Install ninja-build tool (must be after Qt due PATH changes) 62 | uses: turtlesec-no/get-ninja@main 63 | 64 | - name: Checkout sources 65 | uses: actions/checkout@v4 66 | with: 67 | submodules: recursive 68 | 69 | - name: Make sure MSVC is found when Ninja generator is in use 70 | if: ${{ runner.os == 'Windows' }} 71 | uses: ilammy/msvc-dev-cmd@v1 72 | 73 | - name: Configure project 74 | run: > 75 | cmake -S . -B ./build 76 | -DCMAKE_OSX_ARCHITECTURES="${{ matrix.config.macos_architectures }}" 77 | ${{ matrix.compiler.cc != '' && format('-DCMAKE_C_COMPILER={0}', matrix.compiler.cc) || '' }} 78 | ${{ matrix.compiler.cxx != '' && format('-DCMAKE_CXX_COMPILER={0}', matrix.compiler.cxx) || '' }} 79 | 80 | - name: Build Project 81 | run: cmake --build ./build 82 | 83 | - name: Run tests on Linux (offscreen) 84 | if: ${{ runner.os == 'Linux' }} 85 | run: ctest --test-dir ./build --output-on-failure 86 | env: 87 | QT_QPA_PLATFORM: offscreen 88 | QT_QUICK_BACKEND: software 89 | 90 | - name: Run tests on Window/macOS 91 | if: ${{ runner.os != 'Linux' }} 92 | run: ctest --test-dir ./build --output-on-failure 93 | 94 | - name: Read tests log when it fails 95 | uses: andstor/file-reader-action@v1 96 | if: ${{ failure() && startsWith(matrix.preset.name, 'ci-dev-') }} 97 | with: 98 | path: "./build/Testing/Temporary/LastTest.log" 99 | -------------------------------------------------------------------------------- /.github/workflows/nightly.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | name: CI Nightly 6 | 7 | on: 8 | schedule: 9 | - cron: '0 3 * * *' 10 | 11 | jobs: 12 | build: 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | fail-fast: true 16 | matrix: 17 | os: 18 | - ubuntu-latest 19 | 20 | config: 21 | - name: clang-tidy 22 | cmake_arg: '-DCMAKE_CXX_CLANG_TIDY=clang-tidy' 23 | qt_version: "5.15" 24 | 25 | - name: clazy 26 | cmake_arg: '-DCMAKE_CXX_COMPILER=clazy' 27 | qt_version: "6.6.0" 28 | apt_pgks: 29 | - clazy 30 | 31 | steps: 32 | - name: Install Qt ${{ matrix.config.qt_version }} with options and default aqtversion 33 | uses: jurplel/install-qt-action@v3 34 | with: 35 | version: ${{ matrix.config.qt_version }} 36 | cache: true 37 | 38 | - name: Install ninja-build tool (must be after Qt due PATH changes) 39 | uses: turtlesec-no/get-ninja@main 40 | 41 | - name: Install dependencies on Ubuntu (${{ join(matrix.config.apt_pgks, ' ') }}) 42 | if: ${{ runner.os == 'Linux' && matrix.config.apt_pgks }} 43 | run: | 44 | sudo apt update -qq 45 | echo ${{ join(matrix.config.apt_pgks, ' ') }} | xargs sudo apt install -y 46 | 47 | - uses: actions/checkout@v4 48 | 49 | - name: Fetch Git submodule 50 | run: git submodule update --init --recursive 51 | 52 | - name: Configure project 53 | run: > 54 | cmake -S . -B ./build -G Ninja ${{ matrix.config.cmake_arg }} 55 | -DCMAKE_BUILD_TYPE=Debug 56 | 57 | - name: Build Project 58 | run: cmake --build ./build 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile* 2 | release 3 | debug 4 | *.pro.user 5 | *.o 6 | /qt/ui_watchdog/example/ui_watchdog_example 7 | *~ 8 | build* 9 | 10 | # Visual Studio 11 | .vs 12 | 13 | # VS Code 14 | .cache 15 | compile_commands.json 16 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "qt/qt-fmt/fmt"] 2 | path = qt/qt_fmt/fmt 3 | url = https://github.com/fmtlib/fmt 4 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=codereview.kdab.com 3 | port=29418 4 | project=kdab/KDToolBox 5 | -------------------------------------------------------------------------------- /.krazy: -------------------------------------------------------------------------------- 1 | CHECKSETS qt5,c++ 2 | 3 | #KDAB-specific checks 4 | EXTRA kdabcopyright-reuse,fosslicense-reuse 5 | 6 | #exclude checks now being done by clazy or clang-tools 7 | EXCLUDE strings,explicit,normalize,passbyvalue,operators,nullstrcompare,nullstrassign,doublequote_chars,qobject,sigsandslots,staticobjects,dpointer,inline,postfixop 8 | #exclude spelling as codespell is much, much better tool 9 | EXCLUDE spelling 10 | #exclude more checks 11 | EXCLUDE style 12 | 13 | #skip these 14 | SKIP /examples/|/example/ 15 | #forwardinging headers 16 | SKIP /qt/stringtokenizer/include/QtCore/ 17 | SKIP /squish/tests\.json 18 | SKIP \.cmake-format\.py 19 | #3rdparty 20 | SKIP /qt/qt_fmt/fmt 21 | SKIP /qt/qt6_natvis/qt6.natvis 22 | -------------------------------------------------------------------------------- /.mdlrc: -------------------------------------------------------------------------------- 1 | style ".mdlrc.rb" 2 | -------------------------------------------------------------------------------- /.mdlrc.rb: -------------------------------------------------------------------------------- 1 | all 2 | rule 'MD007', :indent => 2, :start_indented => false 3 | rule 'MD013', :line_length => 100, :tables => false, :ignore_code_blocks => true 4 | rule 'MD029', :style => :ordered 5 | exclude_rule 'MD033' 6 | -------------------------------------------------------------------------------- /.pep8: -------------------------------------------------------------------------------- 1 | [pycodestyle] 2 | max_line_length = 120 3 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | # See https://pre-commit.com/hooks.html for more hooks 3 | ci: 4 | skip: [pylint] 5 | autoupdate_schedule: monthly 6 | 7 | exclude: ^(qt/qt6_natvis/qt6.natvis) 8 | repos: 9 | - repo: https://github.com/pre-commit/pre-commit-hooks 10 | rev: v5.0.0 11 | hooks: 12 | - id: trailing-whitespace 13 | - id: end-of-file-fixer 14 | - id: check-added-large-files 15 | - id: check-case-conflict 16 | - id: check-yaml 17 | args: [--allow-multiple-documents] 18 | - id: check-json 19 | - repo: https://github.com/pre-commit/mirrors-clang-format 20 | rev: v19.1.7 21 | hooks: 22 | - id: clang-format 23 | - repo: https://github.com/PyCQA/pylint 24 | rev: v3.3.4 25 | hooks: 26 | - id: pylint 27 | exclude: ^(.cmake-format.py) 28 | additional_dependencies: ["psutil"] 29 | - repo: https://github.com/hhatto/autopep8 30 | rev: v2.3.2 31 | hooks: 32 | - id: autopep8 33 | additional_dependencies: 34 | - psutil 35 | - repo: https://github.com/codespell-project/codespell 36 | rev: v2.4.1 37 | hooks: 38 | - id: codespell 39 | - repo: https://github.com/cheshirekow/cmake-format-precommit 40 | rev: v0.6.13 41 | hooks: 42 | - id: cmake-lint 43 | exclude: (.py.cmake|Doxyfile.cmake) 44 | - id: cmake-format 45 | exclude: (.py.cmake|Doxyfile.cmake) 46 | - repo: https://github.com/markdownlint/markdownlint 47 | rev: v0.12.0 48 | hooks: 49 | - id: markdownlint 50 | entry: mdl 51 | language: ruby 52 | files: \.(md|mdown|markdown)$ 53 | - repo: https://github.com/fsfe/reuse-tool 54 | rev: v5.0.2 55 | hooks: 56 | - id: reuse 57 | -------------------------------------------------------------------------------- /.qmake.conf: -------------------------------------------------------------------------------- 1 | DEFINES += \ 2 | QT_NO_CAST_TO_ASCII \ 3 | QT_RESTRICTED_CAST_FROM_ASCII \ 4 | QT_USE_STRINGBUILDER \ 5 | QT_DEPRECATED_WARNINGS \ 6 | QT_NO_URL_CAST_FROM_STRING \ 7 | QT_NO_NARROWING_CONVERSIONS_IN_CONNECT \ 8 | QT_NO_FOREACH \ 9 | QT_DISABLE_DEPRECATED_BEFORE=0x050C00 \ 10 | QT_NO_KEYWORDS \ 11 | QT_NO_JAVA_STYLE_ITERATORS \ 12 | 13 | msvc { 14 | QMAKE_CXXFLAGS *= \ 15 | -permissive- \ 16 | 17 | } 18 | 19 | CONFIG += \ 20 | strict_c++ 21 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | cmake_minimum_required(VERSION 3.5) 8 | 9 | project(KDToolBox LANGUAGES CXX) 10 | 11 | option(KDTOOLBOX_CXX20 "Enabling C++20 tests" OFF) 12 | option(KDTOOLBOX_CXX23 "Enabling C++23 tests" OFF) 13 | 14 | include(GNUInstallDirs) 15 | 16 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") 17 | 18 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 19 | set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE TRUE) 20 | set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE TRUE) 21 | set(CMAKE_LINK_DEPENDS_NO_SHARED TRUE) 22 | 23 | set(QT_REQUIRED_VERSION "5.15.0") 24 | if(NOT DEFINED QT_VERSION_MAJOR) 25 | find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core) 26 | endif() 27 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Gui) 28 | 29 | set(CMAKE_AUTOMOC ON) 30 | 31 | if(MSVC) 32 | add_compile_options(/Zc:__cplusplus) 33 | endif() 34 | 35 | add_definitions( 36 | -DQT_NO_CAST_TO_ASCII 37 | -DQT_NO_CAST_FROM_ASCII 38 | -DQT_STRICT_ITERATORS 39 | -DQT_NO_URL_CAST_FROM_STRING 40 | -DQT_NO_CAST_FROM_BYTEARRAY 41 | -DQT_USE_QSTRINGBUILDER 42 | -DQT_USE_FAST_OPERATOR_PLUS 43 | -DQT_DISABLE_DEPRECATED_BEFORE=0x060200 44 | ) 45 | 46 | add_subdirectory(cpp) 47 | add_subdirectory(qt) 48 | add_subdirectory(general) 49 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | KDToolBox is © Klarälvdalens Datakonsult AB (KDAB) and is made available 2 | under the terms of the MIT license (see LICENSES/MIT.txt). 3 | 4 | Contact KDAB at to inquire about commercial licensing. 5 | -------------------------------------------------------------------------------- /LICENSES/BSD-3-Clause.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) . 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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. 12 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /REUSE.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | SPDX-PackageName = "KDToolBox" 3 | SPDX-PackageSupplier = "" 4 | SPDX-PackageDownloadLocation = "http://github.com/KDABLabs/KDToolBox" 5 | 6 | #misc source code 7 | [[annotations]] 8 | path = ["qt/stringtokenizer/include/QStringTokenizer", "qt/stringtokenizer/include/QtCore/QStringTokenizer", "**.qrc", "**.json", "**.ui"] 9 | precedence = "aggregate" 10 | SPDX-FileCopyrightText = "Klarälvdalens Datakonsult AB, a KDAB Group company " 11 | SPDX-License-Identifier = "MIT" 12 | 13 | #misc documentation 14 | [[annotations]] 15 | path = "**.md" 16 | precedence = "aggregate" 17 | SPDX-FileCopyrightText = "Klarälvdalens Datakonsult AB, a KDAB Group company " 18 | SPDX-License-Identifier = "MIT" 19 | 20 | #misc config files 21 | [[annotations]] 22 | path = [".clang-format", ".clazy", ".cmake-format.py", ".codespellrc", ".gitignore", ".gitmodules", ".gitreview", ".krazy", ".pep8", ".pre-commit-config.yaml", ".pylintrc", ".qmake.conf", ".mdlrc", ".mdlrc.rb", "REUSE.toml", "qt/qt6_natvis/natvis.code-workspace"] 23 | precedence = "aggregate" 24 | SPDX-FileCopyrightText = "Klarälvdalens Datakonsult AB, a KDAB Group company " 25 | SPDX-License-Identifier = "MIT" 26 | 27 | #artwork 28 | [[annotations]] 29 | path = "**.png" 30 | precedence = "aggregate" 31 | SPDX-FileCopyrightText = "Klarälvdalens Datakonsult AB, a KDAB Group company " 32 | SPDX-License-Identifier = "MIT" 33 | 34 | #3rdparty fmt 35 | [[annotations]] 36 | path = "qt/qt_fmt/fmt" 37 | precedence = "aggregate" 38 | SPDX-FileCopyrightText = "Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors" 39 | SPDX-License-Identifier = "MIT" 40 | -------------------------------------------------------------------------------- /cmake/modules/CheckSubmoduleExists.cmake: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2021 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: BSD-3-Clause 6 | # 7 | 8 | # Submodule handling 9 | function(check_submodule_exists name path) 10 | if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${path}") 11 | if(EXISTS "${CMAKE_SOURCE_DIR}/.git") 12 | message( 13 | FATAL_ERROR 14 | "The ${name} git submodule is not initialized.\n" 15 | "Please run the following commands in the source directory (${PROJECT_SOURCE_DIR}):\n" 16 | " git submodule update --init --recursive\n" 17 | ) 18 | else() 19 | message(FATAL_ERROR "The ${name} submodule is missing - please report a broken source package.\n") 20 | endif() 21 | endif() 22 | endfunction() 23 | -------------------------------------------------------------------------------- /config.tests/cpp20/cpp20.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2021 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | #include 10 | 11 | int main() {} 12 | -------------------------------------------------------------------------------- /cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(duplicatetracker) 8 | add_subdirectory(future-backports) 9 | add_subdirectory(propagate_const) 10 | add_subdirectory(toContainer) 11 | -------------------------------------------------------------------------------- /cpp/duplicatetracker/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(tests) 8 | -------------------------------------------------------------------------------- /cpp/duplicatetracker/README.md: -------------------------------------------------------------------------------- 1 | # DuplicateTracker 2 | 3 | DuplicateTracker is a helper class to keep track of "seen" elements, e.g. 4 | to avoid handling duplicate elements: 5 | 6 | ```cpp 7 | DuplicateTracker tracker; 8 | 9 | for (auto &e : collection) { 10 | if (tracker.hasSeen(e)) 11 | continue; 12 | peruse(e); 13 | } 14 | ``` 15 | 16 | DuplicateTracker transparently wraps `std::pmr::unordered_set` with a 17 | `std::pmr::monotonic_memory_resource` in a non-copyable, non-movable 18 | class with a narrowly-focussed API (basically, `hasSeen()`). If C++17 19 | `` isn't available, it falls back to the default 20 | `std::allocator`. 21 | 22 | Porting from, say, `QSet` to `DuplicateTracker` is simple: 23 | 24 | ```cpp 25 | - QSet seen; 26 | - seen.reserve(expectedNumElements); 27 | + DuplicateTracker tracker(expectedNumElements); 28 | ~~~ 29 | - if (!seen.contains(x)) { 30 | - seen.insert(x); 31 | - peruse(x); 32 | - } 33 | + if (!tracker.hasSeen(x)) 34 | + peruse(x); 35 | ``` 36 | 37 | The second template argument (a size) instructs DuplicateTracker to size its 38 | internal static buffer such that it can hold that many elements without 39 | allocating memory from the heap. The tracker can contain more than these elements, 40 | but it will allocate more memory from the heap to hold them (which will still be 41 | much faster than using raw `QSet` or `std::unordered_set`). 42 | 43 | If you use `DuplicateTracker` with Qt types that don't have `std::hash`-support, 44 | yet, you want to use [Qt Hasher](https://github.com/KDAB/KDToolBox/tree/master/qt/qt_hasher) 45 | as the third template argument: 46 | 47 | ```cpp 48 | DuplicateTracker> tracker(expectedNumElements); 49 | ``` 50 | 51 | Do _not_ specialize `std::hash` for types you don't control (that includes Qt types). 52 | `QtHasher` works with Qt 5.14 and 5.15. If you specialized `std::hash` instead, it would 53 | break in Qt 5.15, which added `std::hash`. 54 | -------------------------------------------------------------------------------- /cpp/duplicatetracker/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(duplicatetracker) 8 | -------------------------------------------------------------------------------- /cpp/duplicatetracker/tests/duplicatetracker/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(cxx11) 8 | add_subdirectory(cxx17) 9 | if(KDTOOLBOX_CXX20) 10 | add_subdirectory(cxx20) 11 | endif() 12 | -------------------------------------------------------------------------------- /cpp/duplicatetracker/tests/duplicatetracker/cxx11/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_STANDARD 11) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | include_directories(../../../include) 13 | 14 | set(tst_duplicatetracker_11_SOURCES ../tst_duplicatetracker.cpp) 15 | 16 | add_executable(tst_duplicatetracker_11 ${tst_duplicatetracker_11_SOURCES}) 17 | target_link_libraries(tst_duplicatetracker_11 PUBLIC Qt::Core Qt::Test) 18 | -------------------------------------------------------------------------------- /cpp/duplicatetracker/tests/duplicatetracker/cxx17/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_STANDARD 17) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | include_directories(../../../include) 13 | 14 | set(tst_duplicatetracker_17_SOURCES ../tst_duplicatetracker.cpp) 15 | 16 | add_executable(tst_duplicatetracker_17 ${tst_duplicatetracker_17_SOURCES}) 17 | target_link_libraries(tst_duplicatetracker_17 PUBLIC Qt::Core Qt::Test) 18 | -------------------------------------------------------------------------------- /cpp/duplicatetracker/tests/duplicatetracker/cxx20/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_STANDARD 20) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | include_directories(../../../include) 13 | 14 | set(tst_duplicatetracker_20_SOURCES ../tst_duplicatetracker.cpp) 15 | 16 | add_executable(tst_duplicatetracker_20 ${tst_duplicatetracker_20_SOURCES}) 17 | target_link_libraries(tst_duplicatetracker_20 PUBLIC Qt::Core Qt::Test) 18 | -------------------------------------------------------------------------------- /cpp/duplicatetracker/tests/duplicatetracker/tst_duplicatetracker.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2021 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Marc Mutz 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #include "duplicatetracker.h" 11 | 12 | #include 13 | 14 | #include 15 | 16 | using namespace KDToolBox; 17 | 18 | namespace 19 | { 20 | template 21 | struct prealloc 22 | { 23 | }; 24 | template 25 | struct prealloc> : std::integral_constant 26 | { 27 | }; 28 | } // unnamed namespace 29 | 30 | class tst_DuplicateTracker : public QObject 31 | { 32 | Q_OBJECT 33 | public: 34 | using QObject::QObject; 35 | 36 | private Q_SLOTS: 37 | void defaultCtor(); 38 | void reserve(); 39 | void hasSeen(); 40 | }; 41 | 42 | void tst_DuplicateTracker::defaultCtor() 43 | { 44 | DuplicateTracker tracker; 45 | QVERIFY(tracker.set().empty()); 46 | QVERIFY(tracker.set().bucket_count() >= prealloc::value); 47 | } 48 | 49 | void tst_DuplicateTracker::reserve() 50 | { 51 | for (size_t i : {2, 13, 63, 64, 65, 1024}) 52 | { 53 | { 54 | DuplicateTracker tracker(i); 55 | QVERIFY(tracker.set().bucket_count() >= i); 56 | } 57 | { 58 | DuplicateTracker tracker; 59 | tracker.reserve(i); 60 | QVERIFY(tracker.set().bucket_count() >= i); 61 | } 62 | } 63 | } 64 | 65 | void tst_DuplicateTracker::hasSeen() 66 | { 67 | DuplicateTracker tracker; 68 | QVERIFY(!tracker.contains("hello")); 69 | QVERIFY(!tracker.hasSeen("hello")); 70 | QVERIFY(tracker.contains("hello")); 71 | QVERIFY(tracker.hasSeen("hello")); 72 | 73 | QVERIFY(!tracker.contains("world")); 74 | QVERIFY(!tracker.hasSeen("world")); 75 | QVERIFY(tracker.contains("world")); 76 | QVERIFY(tracker.hasSeen("world")); 77 | 78 | const auto exclamation = std::string("!"); 79 | QVERIFY(!tracker.contains(exclamation)); 80 | QVERIFY(!tracker.hasSeen(exclamation)); 81 | QVERIFY(tracker.contains(exclamation)); 82 | QVERIFY(tracker.hasSeen(exclamation)); 83 | } 84 | 85 | QTEST_APPLESS_MAIN(tst_DuplicateTracker) 86 | 87 | #include "tst_duplicatetracker.moc" 88 | -------------------------------------------------------------------------------- /cpp/future-backports/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(tests) 8 | -------------------------------------------------------------------------------- /cpp/future-backports/README.md: -------------------------------------------------------------------------------- 1 | Welcome to the Future 2 | ===================== 3 | 4 | This is a collection of C++ standard library components backported to 5 | C++11. They are designed to be drop-in replacements for the 6 | corresponding `std` library components, so the only reference here is 7 | the standard itself. Nothing must be added, though some features can 8 | be left out, as long as those features which are implemented work 9 | exactly as the `std` component. 10 | 11 | Example: `k17::optional` 12 | 13 | Ok difference: 14 | 15 | - leave out `value_or()` 16 | 17 | Not ok: 18 | 19 | - make `value()` return by value 20 | - not have a trivial copy ctor for trivially-copyable payloads 21 | 22 | If you can, it's probably better to use corresponding Boost 23 | components, because this library will never be complete. 24 | 25 | When you use these components, though, we've made sure that you can 26 | easily migrate to the corresponding `std` components if you increase 27 | your project's minimum-required C++ version. E.g., if you update from 28 | C++11 to C++17, then you can textually replace 29 | 30 | s,k14::,std::,g; 31 | s,,<\1>, 32 | 33 | s,k17::,std::,g; 34 | s,,<\1>, 35 | 36 | Due to the way the header names and component namespaces are 37 | constructed, this replacement won't change the layout of the source 38 | code, preserving as much of the VCS history as possible. 39 | 40 | Existing Components 41 | ------------------- 42 | 43 | - `k20::erase`: C++20 Uniform Container Erasure 44 | -------------------------------------------------------------------------------- /cpp/future-backports/include/k20/deque.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Marc Mutz 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | 16 | // like std::erase/_if 17 | namespace k20 18 | { 19 | #if defined(__cpp_lib_erase_if) && _cpp_lib_erase_if >= 202002L // the version that returns size_type 20 | using std::erase; 21 | using std::erase_if; 22 | #else 23 | template 24 | typename std::deque::size_type erase(std::deque &c, const Value &value) 25 | { 26 | return detail::seq_erase(c, value); 27 | } 28 | 29 | template 30 | typename std::deque::size_type erase_if(std::deque &c, UnaryPredicate pred) 31 | { 32 | return detail::seq_erase_if(c, pred); 33 | } 34 | #endif 35 | } // namespace k20 36 | -------------------------------------------------------------------------------- /cpp/future-backports/include/k20/detail/erase_if.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | namespace k20 14 | { 15 | namespace detail 16 | { 17 | 18 | template 19 | typename SequenceContainer::size_type seq_erase(SequenceContainer &c, const Value &value) 20 | { 21 | using std::distance; 22 | using std::remove; 23 | auto it = remove(c.begin(), c.end(), value); 24 | auto r = distance(it, c.end()); 25 | c.erase(it, c.end()); 26 | return r; 27 | } 28 | 29 | template 30 | typename SequenceContainer::size_type seq_erase_if(SequenceContainer &c, Predicate pred) 31 | { 32 | using std::distance; 33 | using std::remove; 34 | auto it = remove_if(c.begin(), c.end(), pred); 35 | auto r = distance(it, c.end()); 36 | c.erase(it, c.end()); 37 | return r; 38 | } 39 | 40 | template 41 | typename NodeContainer::size_type node_erase_if(NodeContainer &c, Predicate pred) 42 | { 43 | auto original_size = c.size(); 44 | for (auto i = c.begin(), last = c.end(); i != last;) 45 | { 46 | if (pred(*i)) 47 | { 48 | i = c.erase(i); 49 | } 50 | else 51 | { 52 | ++i; 53 | } 54 | } 55 | return original_size - c.size(); 56 | } 57 | 58 | template 59 | typename ListContainer::size_type list_erase_if(ListContainer &c, Predicate pred) 60 | { 61 | #ifdef __cpp_lib_list_remove_return_type // P0646 implemented 62 | return c.remove_if(pred); 63 | #else 64 | typename ListContainer::size_type count = 0; 65 | c.remove_if([pred, &count](const typename ListContainer::value_type &e) { 66 | if (pred(e)) 67 | { 68 | // this counting works, because the standard guarantees exactly 69 | // one application of the predicate per element: 70 | // for list: https://eel.is/c++draft/list#ops-18 71 | // for forward_list: https://eel.is/c++draft/forwardlist#ops-16 72 | ++count; 73 | return true; 74 | } 75 | else 76 | { 77 | return false; 78 | } 79 | }); 80 | return count; 81 | #endif 82 | } 83 | 84 | template 85 | typename ListContainer::size_type list_erase(ListContainer &c, const Value &value) 86 | { 87 | return list_erase_if(c, [&](const typename ListContainer::value_type &e) { return e == value; }); 88 | } 89 | 90 | } // namespace detail 91 | } // namespace k20 92 | -------------------------------------------------------------------------------- /cpp/future-backports/include/k20/forward_list.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Marc Mutz 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | 16 | // like std::erase/_if 17 | namespace k20 18 | { 19 | #if defined(__cpp_lib_erase_if) && _cpp_lib_erase_if >= 202002L // the version that returns size_type 20 | using std::erase; 21 | using std::erase_if; 22 | #else 23 | template 24 | typename std::forward_list::size_type erase(std::forward_list &c, const Value &value) 25 | { 26 | return detail::list_erase(c, value); 27 | } 28 | 29 | template 30 | typename std::forward_list::size_type erase_if(std::forward_list &c, UnaryPredicate pred) 31 | { 32 | return detail::list_erase_if(c, pred); 33 | } 34 | #endif 35 | } // namespace k20 36 | -------------------------------------------------------------------------------- /cpp/future-backports/include/k20/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Marc Mutz 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | 16 | // like std::erase/_if 17 | namespace k20 18 | { 19 | #if defined(__cpp_lib_erase_if) && _cpp_lib_erase_if >= 202002L // the version that returns size_type 20 | using std::erase; 21 | using std::erase_if; 22 | #else 23 | template 24 | typename std::list::size_type erase(std::list &c, const Value &value) 25 | { 26 | return detail::list_erase(c, value); 27 | } 28 | 29 | template 30 | typename std::list::size_type erase_if(std::list &c, UnaryPredicate pred) 31 | { 32 | return detail::list_erase_if(c, pred); 33 | } 34 | #endif 35 | } // namespace k20 36 | -------------------------------------------------------------------------------- /cpp/future-backports/include/k20/map.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Marc Mutz 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | 16 | // like std::erase/_if 17 | namespace k20 18 | { 19 | #if defined(__cpp_lib_erase_if) && _cpp_lib_erase_if >= 202002L // the version that returns size_type 20 | using std::erase_if; 21 | 22 | // std::experimental::erase_if isn't good enough: returns void 23 | #else 24 | template 25 | typename std::map::size_type erase_if(std::map &c, UnaryPredicate pred) 26 | { 27 | return detail::node_erase_if(c, pred); 28 | } 29 | 30 | template 31 | typename std::multimap::size_type erase_if(std::multimap &c, UnaryPredicate pred) 32 | { 33 | return detail::node_erase_if(c, pred); 34 | } 35 | #endif 36 | } // namespace k20 37 | -------------------------------------------------------------------------------- /cpp/future-backports/include/k20/set.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Marc Mutz 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | 16 | // like std::erase/_if 17 | namespace k20 18 | { 19 | #if defined(__cpp_lib_erase_if) && _cpp_lib_erase_if >= 202002L // the version that returns size_type 20 | using std::erase_if; 21 | 22 | // std::experimental::erase_if isn't good enough: returns void 23 | #else 24 | template 25 | typename std::set::size_type erase_if(std::set &c, UnaryPredicate pred) 26 | { 27 | return detail::node_erase_if(c, pred); 28 | } 29 | 30 | template 31 | typename std::multiset::size_type erase_if(std::multiset &c, UnaryPredicate pred) 32 | { 33 | return detail::node_erase_if(c, pred); 34 | } 35 | #endif 36 | } // namespace k20 37 | -------------------------------------------------------------------------------- /cpp/future-backports/include/k20/string.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Marc Mutz 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | 16 | // like std::erase/_if 17 | namespace k20 18 | { 19 | #if defined(__cpp_lib_erase_if) && _cpp_lib_erase_if >= 202002L // the version that returns size_type 20 | using std::erase; 21 | using std::erase_if; 22 | #else 23 | template 24 | typename std::basic_string::size_type erase(std::basic_string &c, const Value &value) 25 | { 26 | return detail::seq_erase(c, value); 27 | } 28 | 29 | template 30 | typename std::basic_string::size_type erase_if(std::basic_string &c, UnaryPredicate pred) 31 | { 32 | return detail::seq_erase_if(c, pred); 33 | } 34 | #endif 35 | } // namespace k20 36 | -------------------------------------------------------------------------------- /cpp/future-backports/include/k20/unordered_map.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Marc Mutz 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | 16 | // like std::erase/_if 17 | namespace k20 18 | { 19 | #if defined(__cpp_lib_erase_if) && _cpp_lib_erase_if >= 202002L // the version that returns size_type 20 | using std::erase_if; 21 | 22 | // std::experimental::erase_if isn't good enough: returns void 23 | #else 24 | template 25 | typename std::unordered_map::size_type erase_if(std::unordered_map &c, UnaryPredicate pred) 26 | { 27 | return detail::node_erase_if(c, pred); 28 | } 29 | 30 | template 31 | typename std::unordered_multimap::size_type erase_if(std::unordered_multimap &c, UnaryPredicate pred) 32 | { 33 | return detail::node_erase_if(c, pred); 34 | } 35 | #endif 36 | } // namespace k20 37 | -------------------------------------------------------------------------------- /cpp/future-backports/include/k20/unordered_set.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Marc Mutz 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | 16 | // like std::erase/_if 17 | namespace k20 18 | { 19 | #if defined(__cpp_lib_erase_if) && _cpp_lib_erase_if >= 202002L // the version that returns size_type 20 | using std::erase_if; 21 | 22 | // std::experimental::erase_if isn't good enough: returns void 23 | #else 24 | template 25 | typename std::unordered_set::size_type erase_if(std::unordered_set &c, UnaryPredicate pred) 26 | { 27 | return detail::node_erase_if(c, pred); 28 | } 29 | 30 | template 31 | typename std::unordered_multiset::size_type erase_if(std::unordered_multiset &c, UnaryPredicate pred) 32 | { 33 | return detail::node_erase_if(c, pred); 34 | } 35 | #endif 36 | } // namespace k20 37 | -------------------------------------------------------------------------------- /cpp/future-backports/include/k20/vector.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Marc Mutz 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | 16 | // like std::erase/_if 17 | namespace k20 18 | { 19 | #if defined(__cpp_lib_erase_if) && _cpp_lib_erase_if >= 202002L // the version that returns size_type 20 | using std::erase; 21 | using std::erase_if; 22 | #else 23 | template 24 | typename std::vector::size_type erase(std::vector &c, const Value &value) 25 | { 26 | return detail::seq_erase(c, value); 27 | } 28 | 29 | template 30 | typename std::vector::size_type erase_if(std::vector &c, UnaryPredicate pred) 31 | { 32 | return detail::seq_erase_if(c, pred); 33 | } 34 | #endif 35 | } // namespace k20 36 | -------------------------------------------------------------------------------- /cpp/future-backports/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(k20) 8 | -------------------------------------------------------------------------------- /cpp/future-backports/tests/k20/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(erase_if) 8 | -------------------------------------------------------------------------------- /cpp/future-backports/tests/k20/erase_if/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(cxx11) 8 | add_subdirectory(cxx14) 9 | add_subdirectory(cxx17) 10 | if(KDTOOLBOX_CXX20) 11 | add_subdirectory(cxx20) 12 | endif() 13 | -------------------------------------------------------------------------------- /cpp/future-backports/tests/k20/erase_if/cxx11/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_STANDARD 11) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | include_directories(../../../../include) 13 | 14 | set(tst_erase_if_11_SOURCES ../tst_erase_if.cpp) 15 | 16 | add_executable(tst_erase_if_11 ${tst_erase_if_11_SOURCES}) 17 | target_link_libraries(tst_erase_if_11 PUBLIC Qt::Core Qt::Test) 18 | -------------------------------------------------------------------------------- /cpp/future-backports/tests/k20/erase_if/cxx14/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_STANDARD 14) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | include_directories(../../../../include) 13 | 14 | set(tst_erase_if_14_SOURCES ../tst_erase_if.cpp) 15 | 16 | add_executable(tst_erase_if_14 ${tst_erase_if_14_SOURCES}) 17 | target_link_libraries(tst_erase_if_14 PUBLIC Qt::Core Qt::Test) 18 | -------------------------------------------------------------------------------- /cpp/future-backports/tests/k20/erase_if/cxx17/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_STANDARD 17) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | include_directories(../../../../include) 13 | 14 | set(tst_erase_if_17_SOURCES ../tst_erase_if.cpp) 15 | 16 | add_executable(tst_erase_if_17 ${tst_erase_if_17_SOURCES}) 17 | target_link_libraries(tst_erase_if_17 PUBLIC Qt::Core Qt::Test) 18 | -------------------------------------------------------------------------------- /cpp/future-backports/tests/k20/erase_if/cxx20/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_STANDARD 20) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | include_directories(../../../../include) 13 | 14 | set(tst_erase_if_20_SOURCES ../tst_erase_if.cpp) 15 | 16 | add_executable(tst_erase_if_20 ${tst_erase_if_20_SOURCES}) 17 | target_link_libraries(tst_erase_if_20 PUBLIC Qt::Core Qt::Test) 18 | -------------------------------------------------------------------------------- /cpp/propagate_const/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(test) 8 | -------------------------------------------------------------------------------- /cpp/propagate_const/README.md: -------------------------------------------------------------------------------- 1 | # propagate_const 2 | 3 | ** 4 | A minimal reimplementation of `std::experimental_::propagate_const` from 5 | the C++ Extensions for Library Fundamentals version 3. 6 | ** 7 | 8 | The propagate_const class is a deep-const wrapper for pointers and 9 | smart pointers. If you have a class and want to ensure const 10 | correctness even for its data members that are held by (smart) 11 | pointer, wrap them in a propagate_const: 12 | 13 | ```cpp 14 | class MyClass { 15 | KDToolBox::propagate_const m_data; 16 | 17 | public: 18 | void observer() const { 19 | m_data->mutate(); // ERROR 20 | } 21 | }; 22 | ``` 23 | 24 | "Ordinary" pointers (raw and smart) do not deeply propagate const, and 25 | would let the user accidentally (or deliberately) mutate m_data above. 26 | If the state of m_data logically belongs to the observable state of 27 | `*this`, then the ordinary (smart) pointer would violate const 28 | correctness. The usage of `propagate_const` prevents that mistake from 29 | happening. 30 | 31 | Requires a C++17 capable compiler. 32 | -------------------------------------------------------------------------------- /cpp/propagate_const/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | if(MSVC) 10 | set(CMAKE_CXX_STANDARD 20) 11 | else() 12 | set(CMAKE_CXX_STANDARD 17) 13 | endif() 14 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 15 | 16 | set(tst_propagate_const_SOURCES tst_propagate_const.cpp) 17 | 18 | add_executable(tst_propagate_const ${tst_propagate_const_SOURCES}) 19 | target_link_libraries(tst_propagate_const PUBLIC Qt::Core Qt::Test) 20 | -------------------------------------------------------------------------------- /cpp/toContainer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(testcpp14) 8 | add_subdirectory(testcpplatest) 9 | -------------------------------------------------------------------------------- /cpp/toContainer/README.md: -------------------------------------------------------------------------------- 1 | kdToContainer 2 | ============= 3 | 4 | **A minimal reimplementation of `ranges::to` from ranges-v3.** 5 | 6 | Qt 5.15 deprecated (and then Qt 6 removed) most of the converting 7 | `toContainer()` functions on Qt containers (e.g. `QList::toSet()`, 8 | converting to a `QSet`, has been deprecated). 9 | 10 | The target type gained a range constructor instead. Now, while using 11 | such constructor is indeed suggested as the *direct* replacement of 12 | calling the conversion function, it's more verbose to use, and 13 | requires an extra line in case of the source container is returned from 14 | a function or so. 15 | 16 | `kdToContainer` tries to bring back the convenience of the conversion 17 | function, while being more general, as it works between any range-like 18 | type (as the source) and any container-like type (as the destination): 19 | 20 | ```cpp 21 | using namespace KDToolBox::Ranges; 22 | 23 | QList myList = ~~~; 24 | 25 | // pipeline style 26 | auto set1 = myList | kdToContainer(); // converts the QList to QSet 27 | auto set2 = myList | kdToContainer>(); // ditto 28 | 29 | // function call style 30 | auto set3 = kdToContainer(myList); // ditto 31 | auto set4 = kdToContainer>(myList); // ditto 32 | 33 | 34 | auto vec = set1 | kdToContainer(); // converts the QSet to QVector 35 | 36 | 37 | QStringList getNames(); 38 | auto nameset = getNames() | kdToContainer(); // converts to std::unordered_set 39 | 40 | ``` 41 | 42 | --- 43 | 44 | Note that the underlying reason for deprecating such functionality in 45 | Qt containers is not merely because there's an equivalent, more general 46 | one (so the "redundant", "limited" approach got removed). 47 | 48 | *The real reason* is because most of the time conversions between 49 | containers are hiding a design problem: usage of containers instead of 50 | algorithms, poorly designed interfaces, and similar. 51 | 52 | Therefore: before reaching for a conversion between containers, try to 53 | understand if your _design_ is sound! For instance, if you want to remove 54 | duplicates from a sequential container, use `std::sort` + `std::unique`; 55 | if you want to process elements avoiding duplicate elements (without 56 | altering the source container), then use 57 | [DuplicateTracker](https://github.com/KDAB/KDToolBox/tree/master/cpp/duplicatetracker); 58 | and so on. 59 | 60 | **It's highly recommended to use ranges-v3 instead of this.** 61 | The implementation offered here is knowingly incomplete in some cases. 62 | Use this only if ranges-v3 doesn't work for you. 63 | 64 | Requires a C++14 capable compiler. 65 | -------------------------------------------------------------------------------- /cpp/toContainer/testcpp14/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_STANDARD 14) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | set(tst_toContainer_SOURCES ../tst_toContainer.cpp) 13 | 14 | add_executable(tst_toContainer_cpp_14 ${tst_toContainer_SOURCES}) 15 | target_link_libraries(tst_toContainer_cpp_14 PUBLIC Qt::Core Qt::Test) 16 | -------------------------------------------------------------------------------- /cpp/toContainer/testcpplatest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_STANDARD 17) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | set(tst_toContainer_SOURCES ../tst_toContainer.cpp) 13 | 14 | add_executable(tst_toContainer_cpp_latest ${tst_toContainer_SOURCES}) 15 | target_link_libraries(tst_toContainer_cpp_latest PUBLIC Qt::Core Qt::Test) 16 | -------------------------------------------------------------------------------- /cpp/toContainer/tst_toContainer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2021 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Giuseppe D'Angelo 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #include "toContainer.h" 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | using namespace KDToolBox::Ranges; 25 | 26 | class tst_toContainer : public QObject 27 | { 28 | Q_OBJECT 29 | public: 30 | using QObject::QObject; 31 | 32 | private Q_SLOTS: 33 | void initTestCase(); 34 | void toContainer(); 35 | 36 | private: 37 | template class Container> 38 | void toContainer_impl(); 39 | }; 40 | 41 | void tst_toContainer::initTestCase() 42 | { 43 | qDebug() << "Test built under C++" << __cplusplus; 44 | } 45 | 46 | void tst_toContainer::toContainer() 47 | { 48 | toContainer_impl(); 49 | toContainer_impl(); 50 | toContainer_impl(); 51 | toContainer_impl(); 52 | toContainer_impl(); 53 | } 54 | 55 | template 56 | static void toContainer_helper(const SourceContainer &s) 57 | { 58 | DestinationContainer d(s.begin(), s.end()); 59 | QCOMPARE(kdToContainer(s), d); 60 | QCOMPARE((s | kdToContainer()), d); 61 | 62 | const auto functionReturningS = [&s]() { return s; }; 63 | QCOMPARE(kdToContainer(functionReturningS()), d); 64 | QCOMPARE((functionReturningS() | kdToContainer()), d); 65 | } 66 | 67 | template class DestinationContainerTemplate, typename SourceContainer> 68 | static void toContainer_helper2(const SourceContainer &s) 69 | { 70 | DestinationContainerTemplate d(s.begin(), s.end()); 71 | QCOMPARE(kdToContainer(s), d); 72 | QCOMPARE((s | kdToContainer()), d); 73 | 74 | const auto functionReturningS = [&s]() { return s; }; 75 | QCOMPARE(kdToContainer(functionReturningS()), d); 76 | QCOMPARE((functionReturningS() | kdToContainer()), d); 77 | } 78 | 79 | template class Container> 80 | void tst_toContainer::toContainer_impl() 81 | { 82 | Container container{1, 2, 3, 4, 5, 1, 2, 6, -1, 1, 2, 45}; 83 | 84 | toContainer_helper>(container); 85 | toContainer_helper>(container); 86 | toContainer_helper>(container); 87 | toContainer_helper>(container); 88 | toContainer_helper>(container); 89 | toContainer_helper>(container); 90 | 91 | // Same but without specifying's the container value type template argument 92 | toContainer_helper2(container); 93 | toContainer_helper2(container); 94 | toContainer_helper2(container); 95 | toContainer_helper2(container); 96 | toContainer_helper2(container); 97 | toContainer_helper2(container); 98 | 99 | // Same but changing the value_type 100 | toContainer_helper>(container); 101 | toContainer_helper>(container); 102 | toContainer_helper>(container); 103 | toContainer_helper>(container); 104 | toContainer_helper>(container); 105 | toContainer_helper>(container); 106 | } 107 | 108 | QTEST_MAIN(tst_toContainer) 109 | 110 | #include "tst_toContainer.moc" 111 | -------------------------------------------------------------------------------- /general/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(asan_assert_fail) 8 | -------------------------------------------------------------------------------- /general/asan_assert_fail/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | set(asan_assert_fail_SOURCES asan_assert_fail.c) 8 | 9 | add_library(asan_assert_fail SHARED ${asan_assert_fail_SOURCES}) 10 | -------------------------------------------------------------------------------- /general/asan_assert_fail/README.md: -------------------------------------------------------------------------------- 1 | # ASAN assert_fail 2 | 3 | The `asan_assert_fail` library can be preloaded to any application compiled with 4 | ASAN support enabled to get fancy backtraces for assertion failures. 5 | 6 | ## Example Usage 7 | 8 | Let's take the following code and compile it with ASAN: 9 | 10 | ```text 11 | $ cat test-asan.cpp 12 | #include 13 | 14 | int main(int argc, char **argv) 15 | { 16 | assert(argc == 2); 17 | return 0; 18 | } 19 | $ g++ -fsanitize=address test-asan.cpp -o test-asan 20 | ``` 21 | 22 | When you run this as-is, the ASAN crash handler ASAN will not kick in, so all 23 | you are left with is the normal assertion output: 24 | 25 | ```text 26 | $ ./test-asan 27 | test-asan: test.cpp:5: int main(int, char**): Assertion `argc == 2' failed. 28 | Aborted (core dumped) 29 | ``` 30 | 31 | When we add our little `libasan_assert_fail.so` utility, we do get a nice backtrace 32 | instead. We have to also explicitly preload `libasan.so` as it has to come first: 33 | 34 | ```text 35 | $ LD_PRELOAD="$(readlink -f /usr/lib/libasan.so) $(readlink -f path/to/libasan_assert_fail.so)" ./test-asan 36 | test-asan: test.cpp:5: int main(int, char**): Assertion `argc == 2' failed. 37 | ================================================================= 38 | ==241773==ERROR: AddressSanitizer: unknown-crash on address 0x000000000000 at pc 0x7ffd5a782050 bp 0x7ffd5a781ff0 sp 0x7ffd5a781ff0 39 | READ of size 1 at 0x000000000000 thread T0 40 | #0 0x7ffd5a78204f ([stack]+0x1f04f) 41 | #1 0x7f61a199cd0f in __asan::ErrorDescription::Print() /build/gcc/src/gcc/libsanitizer/asan/asan_errors.h:420 42 | #2 0x7f61a199cd0f in __asan::ScopedInErrorReport::~ScopedInErrorReport() /build/gcc/src/gcc/libsanitizer/asan/asan_report.cc:140 43 | #3 0x7f61a199c668 in __asan::ReportGenericError(unsigned long, unsigned long, unsigned long, unsigned long, bool, unsigned long, unsigned int, bool) /build/gcc/src/gcc/libsanitizer/asan/asan_report.cc:458 44 | #4 0x7f61a199c773 in __asan_report_error /build/gcc/src/gcc/libsanitizer/asan/asan_report.cc:473 45 | #5 0x7f61a187ec4c in __assert_fail (/home/milian/projects/kdab/KDToolBox/build/debug/asan_assert_fail/libasan_assert_fail.so.1.0.0+0xc4c) 46 | #6 0x560b69653981 in main (/tmp/test-asan+0x981) 47 | #7 0x7f61a133a022 in __libc_start_main (/usr/lib/libc.so.6+0x27022) 48 | #8 0x560b6965387d in _start (/tmp/test-asan+0x87d) 49 | ``` 50 | -------------------------------------------------------------------------------- /general/asan_assert_fail/asan_assert_fail.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Milian Wolff 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | extern const char *__progname; 16 | 17 | void __assert_fail(const char *assertion, const char *file, unsigned int line, const char *function) 18 | { 19 | int stack_var = 0; 20 | fprintf(stderr, "%s: %s:%u: %s: Assertion `%s' failed.\n", __progname, file, line, function, assertion); 21 | __asan_report_error(__builtin_frame_address(0), &stack_var, &stack_var, NULL, 0, 1); 22 | abort(); 23 | } 24 | -------------------------------------------------------------------------------- /qt/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(eventfilter) 8 | add_subdirectory(KDSignalThrottler) 9 | add_subdirectory(KDSqlDatabaseTransaction) 10 | add_subdirectory(KDStlContainerAdaptor) 11 | add_subdirectory(messagehandler) 12 | add_subdirectory(model_view) 13 | add_subdirectory(notify_guard) 14 | add_subdirectory(pointer_cast) 15 | add_subdirectory(qml) 16 | add_subdirectory(qt_hasher) 17 | add_subdirectory(singleshot_connect) 18 | add_subdirectory(tabWindow) 19 | add_subdirectory(ui_watchdog) 20 | add_subdirectory(qt_fmt) 21 | 22 | if(LINUX) 23 | add_subdirectory(asan_assert_fail_qt) 24 | endif() 25 | 26 | if(QT_VERSION_MAJOR EQUAL 5) 27 | # Qt6 has it's own QStringTokenizer 28 | add_subdirectory(stringtokenizer) 29 | endif() 30 | 31 | if(KDTOOLBOX_CXX23) 32 | add_subdirectory(KDCoro) 33 | endif() 34 | -------------------------------------------------------------------------------- /qt/KDCoro/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(test) 8 | -------------------------------------------------------------------------------- /qt/KDCoro/README.md: -------------------------------------------------------------------------------- 1 | KDCoro (required C++23) 2 | ======================== 3 | 4 | Some awaitable classes to allow for safe usage of coroutines in C++23 5 | 6 | ```cpp 7 | void MyDialog::save() 8 | { 9 | auto coroutine = [this]() -> KDCoroTerminator { 10 | // Tell KDCoroTerminator that this coroutine should be destroyed 11 | // if MyDialog is destroyed. 12 | co_yeild this; 13 | 14 | // Wait thill the input dialog 15 | std::expected filepath = co_await inputDialog.filenameCoroutine(); 16 | 17 | if (!filepath.has_value()) { 18 | qDebug() << "Input dialog did not provide a finepath" << filepath.error(); 19 | co_return; 20 | } 21 | 22 | QFile file(filepath); 23 | // save information 24 | 25 | MyDialog::accepted(); 26 | }; 27 | 28 | coroutine(); // co_await is forbidden 29 | } 30 | ``` 31 | 32 | Another coroutine helper, is the ability to transform any signal into an awaitable: 33 | 34 | ```cpp 35 | void MyDialog::save() 36 | { 37 | auto coroutine = [this]() -> KDCoroTerminator { 38 | // Tell KDCoroTerminator that this coroutine should be destroyed 39 | // if MyDialog is destroyed. 40 | co_yeild this; 41 | 42 | // Wait thill the input dialog 43 | // Note that "this" was passed to kdCoroSignal(), this allows 44 | // this awaitable to return an std::unexpected in case MyDialog is 45 | // destroyed. 46 | // While we already do "co_yeild this;", passing "this" is the only way to 47 | // finish this coroutine if filepath is not emitted on an rejected dialog. 48 | // Care must be takend that either inputDialog or this are destroyed or that 49 | // the emitted guarantees to emit it's signal. 50 | std::expected filepath = co_await kdCoroSignal(inputDialog, &InputDialog::filePath, this); 51 | 52 | if (!filepath.has_value()) { 53 | qDebug() << "Input dialog did not provide a finepath" << filepath.error(); 54 | co_return; 55 | } 56 | 57 | QFile file(filepath); 58 | // save information 59 | 60 | MyDialog::accepted(); 61 | }; 62 | 63 | coroutine(); // co_await is forbidden 64 | } 65 | ``` 66 | -------------------------------------------------------------------------------- /qt/KDCoro/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Sql 14 | Test 15 | ) 16 | 17 | set(CMAKE_CXX_STANDARD 23) 18 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 19 | 20 | set(tst_KDCoro_SOURCES tst_KDCoro.cpp ../KDCoro.h) 21 | 22 | add_executable(tst_KDCoro ${tst_KDCoro_SOURCES}) 23 | target_link_libraries(tst_KDCoro PUBLIC Qt::Core Qt::Test) 24 | -------------------------------------------------------------------------------- /qt/KDSignalThrottler/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(examples) 8 | add_subdirectory(test) 9 | -------------------------------------------------------------------------------- /qt/KDSignalThrottler/README.md: -------------------------------------------------------------------------------- 1 | # KDSignalThrottler 2 | 3 | This project contains an implementation of throttlers and debouncers 4 | for signals and slots; that is, objects that rate-limit the activation 5 | of a given slot. 6 | 7 | * A **throttler** calls the slot only once every X ms, no matter what's 8 | the input frequency of the signal. Use case: filtering out input 9 | events which may occur at a very high frequency (example: mouse events); 10 | an expensive operation triggered by these events (like scrolling and 11 | repainting) can be performed less often. 12 | 13 | * A **debouncer** activates the slot only once, after a timeout / grace 14 | period calculated since the last signal emission. In other words: if 15 | a signal keeps coming, the slot is _not_ activated. Use case: a 16 | search box that actually starts searching only after the user 17 | stops typing (that is, after a short timeout since the last user input). 18 | 19 | Throttlers and debouncers can be trailing (the default) or leading. 20 | 21 | * Trailing means that the throttler/debouncer does not activate the slot 22 | immediately, but waits until its own timeout occurs before activating. 23 | 24 | * Leading means to activate the slot as soon as the throttler/debouncer 25 | itself is activated, and then does not trigger again until the timeout 26 | occurs (and only if another signal is received in the meanwhile). 27 | 28 | The usage is straightforward. First and foremost create an instance 29 | of the right debouncer/throttler class, and configure its timeout: 30 | 31 | ```cpp 32 | // trailing throttler, rate limiting at 1 emission every 100ms 33 | KDSignalThrottler *throttler = new KDSignalThrottler(parent); 34 | throttler->setTimeout(100ms); 35 | ``` 36 | 37 | Connect the signal you want to rate limit to the `throttle` slot 38 | of the throttler, and connect the `triggered` signal to the actual slot: 39 | 40 | ```cpp 41 | connect(sender, &Sender::signal, 42 | throttler, &KDSignalThrottler::throttle); 43 | connect(throttler, &KDSignalThrottler::triggered, 44 | receiver, &Receiver::slot); 45 | ``` 46 | 47 | Look into `KDSignalThrottler.h` for the other available types. 48 | 49 | Requires a C++14 capable compiler. 50 | -------------------------------------------------------------------------------- /qt/KDSignalThrottler/examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(debouncedSearch) 8 | add_subdirectory(signalThrottlersDemo) 9 | -------------------------------------------------------------------------------- /qt/KDSignalThrottler/examples/debouncedSearch/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Gui 14 | Widgets 15 | ) 16 | 17 | include_directories(../../src/) 18 | 19 | set(debouncedSearch_SOURCES ../../src/KDSignalThrottler.cpp ../../src/KDSignalThrottler.h main.cpp) 20 | 21 | add_executable(debouncedSearch ${debouncedSearch_SOURCES}) 22 | target_link_libraries(debouncedSearch PUBLIC Qt::Core Qt::Gui Qt::Widgets) 23 | -------------------------------------------------------------------------------- /qt/KDSignalThrottler/examples/debouncedSearch/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Giuseppe D'Angelo 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | class Widget : public QWidget 18 | { 19 | Q_OBJECT 20 | public: 21 | explicit Widget(QWidget *parent = nullptr) 22 | : QWidget(parent) 23 | { 24 | auto helpLabel = new QLabel(this); 25 | helpLabel->setWordWrap(true); 26 | helpLabel->setText(tr("

Debouncer example

" 27 | "

Type some text in the line edit below.

" 28 | "

The label below it will be updated only after 250ms of inactivity " 29 | "on the line edit, due to a debouncer filtering the line edit's " 30 | "signal emissions.

")); 31 | 32 | auto line = new QFrame(this); 33 | line->setFrameShape(QFrame::HLine); 34 | 35 | m_edit = new QLineEdit(this); 36 | m_edit->setPlaceholderText(tr("Search with a delay")); 37 | 38 | m_label = new QLabel(this); 39 | 40 | auto debouncer = new KDToolBox::KDSignalDebouncer(this); 41 | debouncer->setTimeout(250); 42 | 43 | connect(m_edit, &QLineEdit::textChanged, debouncer, &KDToolBox::KDSignalDebouncer::throttle); 44 | connect(debouncer, &KDToolBox::KDSignalDebouncer::triggered, this, &Widget::updateLabel); 45 | 46 | QVBoxLayout *layout = new QVBoxLayout(this); 47 | layout->addWidget(helpLabel); 48 | layout->addWidget(line); 49 | layout->addWidget(m_edit); 50 | layout->addWidget(m_label); 51 | setLayout(layout); 52 | 53 | updateLabel(); 54 | } 55 | 56 | private: 57 | void updateLabel() 58 | { 59 | const auto text = m_edit->text(); 60 | if (text.isEmpty()) 61 | m_label->setText(tr("

Nothing to search for.

")); 62 | else 63 | m_label->setText(tr("

Searching for: \"%1\"...

").arg(m_edit->text())); 64 | } 65 | 66 | QLineEdit *m_edit; 67 | QLabel *m_label; 68 | }; 69 | 70 | int main(int argc, char **argv) 71 | { 72 | QApplication::setApplicationName(QStringLiteral("Debouncer example")); 73 | 74 | QApplication app(argc, argv); 75 | Widget w; 76 | w.show(); 77 | return app.exec(); 78 | } 79 | 80 | #include "main.moc" 81 | -------------------------------------------------------------------------------- /qt/KDSignalThrottler/examples/signalThrottlersDemo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | 8 | set(CMAKE_CXX_STANDARD 14) 9 | 10 | find_package( 11 | Qt${QT_VERSION_MAJOR} 12 | ${QT_REQUIRED_VERSION} 13 | CONFIG 14 | REQUIRED 15 | Core 16 | Gui 17 | Widgets 18 | ) 19 | 20 | include_directories(../../src/) 21 | 22 | set(signalThrottlersDemo_SOURCES ../../src/KDSignalThrottler.cpp ../../src/KDSignalThrottler.h main.cpp) 23 | 24 | add_executable(signalThrottlersDemo ${signalThrottlersDemo_SOURCES}) 25 | target_link_libraries(signalThrottlersDemo PUBLIC Qt::Core Qt::Gui Qt::Widgets) 26 | -------------------------------------------------------------------------------- /qt/KDSignalThrottler/src/KDSignalThrottler.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Giuseppe D'Angelo 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #ifndef KDSIGNALTHROTTLER_H 11 | #define KDSIGNALTHROTTLER_H 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | namespace KDToolBox 19 | { 20 | 21 | class KDGenericSignalThrottler : public QObject 22 | { 23 | Q_OBJECT 24 | 25 | Q_PROPERTY(Kind kind READ kind CONSTANT) 26 | Q_PROPERTY(EmissionPolicy emissionPolicy READ emissionPolicy CONSTANT) 27 | Q_PROPERTY(int timeout READ timeout WRITE setTimeout NOTIFY timeoutChanged) 28 | Q_PROPERTY(Qt::TimerType timerType READ timerType WRITE setTimerType NOTIFY timerTypeChanged) 29 | 30 | public: 31 | enum class Kind 32 | { 33 | Throttler, 34 | Debouncer, 35 | }; 36 | Q_ENUM(Kind) 37 | 38 | enum class EmissionPolicy 39 | { 40 | Trailing, 41 | Leading, 42 | }; 43 | Q_ENUM(EmissionPolicy) 44 | 45 | explicit KDGenericSignalThrottler(Kind kind, EmissionPolicy emissionPolicy, QObject *parent = nullptr); 46 | ~KDGenericSignalThrottler() override; 47 | 48 | Kind kind() const; 49 | EmissionPolicy emissionPolicy() const; 50 | 51 | int timeout() const; 52 | void setTimeout(int timeout); 53 | void setTimeout(std::chrono::milliseconds timeout); 54 | 55 | Qt::TimerType timerType() const; 56 | void setTimerType(Qt::TimerType timerType); 57 | 58 | public Q_SLOTS: 59 | void throttle(); 60 | 61 | Q_SIGNALS: 62 | void triggered(); 63 | void timeoutChanged(int timeout); 64 | void timerTypeChanged(Qt::TimerType timerType); 65 | 66 | private: 67 | void maybeEmitTriggered(); 68 | void emitTriggered(); 69 | 70 | QTimer m_timer; 71 | Kind m_kind; 72 | EmissionPolicy m_emissionPolicy; 73 | bool m_hasPendingEmission; 74 | }; 75 | 76 | // Convenience subclasses, e.g. for registering into QML 77 | 78 | class KDSignalThrottler : public KDGenericSignalThrottler 79 | { 80 | Q_OBJECT 81 | public: 82 | explicit KDSignalThrottler(QObject *parent = nullptr); 83 | ~KDSignalThrottler() override; 84 | }; 85 | 86 | class KDSignalLeadingThrottler : public KDGenericSignalThrottler 87 | { 88 | Q_OBJECT 89 | public: 90 | explicit KDSignalLeadingThrottler(QObject *parent = nullptr); 91 | ~KDSignalLeadingThrottler() override; 92 | }; 93 | 94 | class KDSignalDebouncer : public KDGenericSignalThrottler 95 | { 96 | Q_OBJECT 97 | public: 98 | explicit KDSignalDebouncer(QObject *parent = nullptr); 99 | ~KDSignalDebouncer() override; 100 | }; 101 | 102 | class KDSignalLeadingDebouncer : public KDGenericSignalThrottler 103 | { 104 | Q_OBJECT 105 | public: 106 | explicit KDSignalLeadingDebouncer(QObject *parent = nullptr); 107 | ~KDSignalLeadingDebouncer() override; 108 | }; 109 | 110 | } // namespace KDToolBox 111 | 112 | #endif // KDSIGNALTHROTTLER_H 113 | -------------------------------------------------------------------------------- /qt/KDSignalThrottler/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_STANDARD 14) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | include_directories(../src/) 13 | 14 | set(test_SOURCES ../src/KDSignalThrottler.cpp ../src/KDSignalThrottler.h tst_KDSignalThrottler.cpp) 15 | 16 | add_executable(test ${test_SOURCES}) 17 | target_link_libraries(test PUBLIC Qt::Core Qt::Test) 18 | -------------------------------------------------------------------------------- /qt/KDSqlDatabaseTransaction/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(test) 8 | -------------------------------------------------------------------------------- /qt/KDSqlDatabaseTransaction/README.md: -------------------------------------------------------------------------------- 1 | KDSqlDatabaseTransaction 2 | ======================== 3 | 4 | A simple RAII wrapper for transaction support when using the Qt SQL 5 | database classes (QSqlQuery, QSqlDatabase, and so on). 6 | 7 | ```cpp 8 | KDSqlDatabaseTransaction transaction(dbConnection); // begins the transaction 9 | 10 | // ... do database work ... 11 | 12 | if (!transaction.commit()) // ends the transaction 13 | qWarning() << "Error!"; 14 | ``` 15 | 16 | Destroying a `KDSqlDatabaseTransaction` without committing the 17 | transaction will automatically roll it back. (It's also possible to 18 | call `rollback()`.) 19 | 20 | Requires C++14. 21 | -------------------------------------------------------------------------------- /qt/KDSqlDatabaseTransaction/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Sql 14 | Test 15 | ) 16 | 17 | set(CMAKE_CXX_STANDARD 14) 18 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 19 | 20 | set(tst_KDSqlDatabaseTransaction_SOURCES tst_KDSqlDatabaseTransaction.cpp) 21 | 22 | add_executable(tst_KDSqlDatabaseTransaction ${tst_KDSqlDatabaseTransaction_SOURCES}) 23 | target_link_libraries(tst_KDSqlDatabaseTransaction PUBLIC Qt::Core Qt::Sql Qt::Test) 24 | -------------------------------------------------------------------------------- /qt/KDSqlDatabaseTransaction/test/tst_KDSqlDatabaseTransaction.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "../KDSqlDatabaseTransaction.h" 15 | 16 | #define QSL(x) QStringLiteral(x) 17 | 18 | class tst_KDSqlDatabaseTransaction : public QObject 19 | { 20 | Q_OBJECT 21 | public: 22 | using QObject::QObject; 23 | 24 | private Q_SLOTS: 25 | void initTestCase(); 26 | void testTransactions(); 27 | }; 28 | 29 | void tst_KDSqlDatabaseTransaction::initTestCase() 30 | { 31 | QSqlDatabase db = QSqlDatabase::addDatabase(QSL("QSQLITE")); 32 | db.setDatabaseName(QString()); 33 | QVERIFY(db.open()); 34 | 35 | QSqlQuery q; 36 | QVERIFY(q.exec(QSL("CREATE TABLE t(x INTEGER)"))); 37 | } 38 | 39 | static void verifyRowCount(int expectedCount) 40 | { 41 | QSqlQuery q; 42 | QVERIFY(q.exec(QSL("SELECT COUNT(*) FROM t"))); 43 | QVERIFY(q.next()); 44 | QCOMPARE(q.value(0).toInt(), expectedCount); 45 | } 46 | 47 | void tst_KDSqlDatabaseTransaction::testTransactions() 48 | { 49 | int rowCount = 0; 50 | 51 | verifyRowCount(rowCount); 52 | 53 | { 54 | // no transaction 55 | QSqlQuery q; 56 | QVERIFY(q.exec(QSL("INSERT INTO t VALUES(42)"))); 57 | verifyRowCount(++rowCount); 58 | } 59 | 60 | { 61 | // commit 62 | KDToolBox::KDSqlDatabaseTransaction t; 63 | QSqlQuery q; 64 | QVERIFY(q.exec(QSL("INSERT INTO t VALUES(42)"))); 65 | verifyRowCount(++rowCount); 66 | QVERIFY(t.commit()); 67 | verifyRowCount(rowCount); 68 | } 69 | 70 | verifyRowCount(rowCount); 71 | 72 | { 73 | // commit 74 | KDToolBox::KDSqlDatabaseTransaction t; 75 | QSqlQuery q; 76 | QVERIFY(q.exec(QSL("INSERT INTO t VALUES(123)"))); 77 | verifyRowCount(++rowCount); 78 | QVERIFY(t.commit()); 79 | verifyRowCount(rowCount); 80 | } 81 | 82 | verifyRowCount(rowCount); 83 | 84 | { 85 | // rollback (via destructor) 86 | KDToolBox::KDSqlDatabaseTransaction t; 87 | QSqlQuery q; 88 | QVERIFY(q.exec(QSL("INSERT INTO t VALUES(-1)"))); 89 | verifyRowCount(rowCount + 1); 90 | } 91 | 92 | verifyRowCount(rowCount); 93 | 94 | { 95 | // rollback (via explicit call) 96 | KDToolBox::KDSqlDatabaseTransaction t; 97 | QSqlQuery q; 98 | QVERIFY(q.exec(QSL("INSERT INTO t VALUES(-1)"))); 99 | verifyRowCount(rowCount + 1); 100 | QVERIFY(t.rollback()); 101 | verifyRowCount(rowCount); 102 | } 103 | 104 | verifyRowCount(rowCount); 105 | } 106 | 107 | QTEST_MAIN(tst_KDSqlDatabaseTransaction) 108 | 109 | #include "tst_KDSqlDatabaseTransaction.moc" 110 | -------------------------------------------------------------------------------- /qt/KDStlContainerAdaptor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(test) 8 | -------------------------------------------------------------------------------- /qt/KDStlContainerAdaptor/README.md: -------------------------------------------------------------------------------- 1 | KDStlContainerAdaptor 2 | ===================== 3 | 4 | **Adaptors to ease the transition from Qt containers to the C++ STL containers.** 5 | 6 | Qt containers feature a somehow richer and easy-to-use API than the STL 7 | counterparts. For instance, this is possible on a QVector: 8 | 9 | ```cpp 10 | QVector v; 11 | 12 | v << 1 << 2 << 3; // chained push_back via operator<< 13 | if (v.contains(42)) // algorithm as member 14 | doSomething(); 15 | 16 | qDebug() << v.first() << v.last(); // front/back 17 | v.append(100); // push_back 18 | v.prepend(0); // "push_front" 19 | v.removeAt(2); // removes at index #2 20 | ``` 21 | 22 | If, for whatever reason, we wish to migrate `v` from `QVector` to the 23 | equivalent C++ Standard Library container (that is, to `std::vector`) 24 | then we would need to fix all of these usages, making the porting quite 25 | tedious. 26 | 27 | `KDStlContainerAdaptor` tries to minimize the effort by letting the 28 | existing source code compile mostly unchanged. All we need to do is to 29 | to use its datatypes in place of of the Qt ones; the existing Qt-based 30 | source code will compile unchanged. 31 | 32 | To ease porting from `QVector` (and possibly `QList`) just use the 33 | `StdVectorAdaptor` class template. It transparently inherits from 34 | `std::vector`, but adds back the Qt-specific APIs: 35 | 36 | ```cpp 37 | // Use this instead of QVector 38 | using IntVector = KDToolBox::StlContainerAdaptor::StdVectorAdaptor; 39 | IntVector v; // is-a std::vector 40 | 41 | // The previous code still works! 42 | v << 1 << 2 << 3; // chained push_back via operator<< 43 | if (v.contains(42)) // algorithm as member 44 | doSomething(); 45 | 46 | qDebug() << v.first() << v.last(); // front/back 47 | v.append(100); // push_back 48 | v.removeAt(2); // removes at index #2 49 | ``` 50 | 51 | More helpers may come in the future. 52 | 53 | Requires a C++17 capable compiler. 54 | -------------------------------------------------------------------------------- /qt/KDStlContainerAdaptor/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_STANDARD 17) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | set(tst_KDStlContainerAdaptor_SOURCES tst_KDStlContainerAdaptor.cpp) 13 | 14 | add_executable(tst_KDStlContainerAdaptor ${tst_KDStlContainerAdaptor_SOURCES}) 15 | target_link_libraries(tst_KDStlContainerAdaptor PUBLIC Qt::Core Qt::Test) 16 | -------------------------------------------------------------------------------- /qt/asan_assert_fail_qt/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | set(asan_assert_fail_qt_SOURCES asan_assert_fail_qt.cpp) 8 | 9 | add_library(asan_assert_fail_qt SHARED ${asan_assert_fail_qt_SOURCES}) 10 | target_link_libraries(asan_assert_fail_qt PUBLIC Qt::Core) 11 | -------------------------------------------------------------------------------- /qt/asan_assert_fail_qt/README.md: -------------------------------------------------------------------------------- 1 | # ASAN assert_fail 2 | 3 | The `asan_assert_fail_qt` library can be preloaded to any application compiled with 4 | ASAN support enabled to get fancy backtraces for assertion failures: 5 | 6 | ## Example Usage 7 | 8 | Let's take the following code and compile it with ASAN: 9 | 10 | ```text 11 | $ cat test-asan.cpp 12 | #include 13 | 14 | int main(int argc, char **argv) 15 | { 16 | Q_ASSERT(argc == 2); 17 | return 0; 18 | } 19 | $ g++ -fsanitize=address -fPIC -lQt5Core -I/usr/include/qt -I/usr/include/qt/QtCore test-asan.cpp -o test-asan 20 | ``` 21 | 22 | When you run this as-is, the ASAN crash handler ASAN will not kick in, so all 23 | you are left with is the normal assertion output: 24 | 25 | ```text 26 | $ ./test-asan 27 | 0.000 fatal: unknown[test_qt.cpp:5]: ASSERT: "argc == 2" in file test_qt.cpp, line 5 28 | Aborted (core dumped) 29 | ``` 30 | 31 | When we add our little `libasan_assert_fail_qt.so` utility, we do get a nice backtrace 32 | instead. We have to also explicitly preload `libasan.so` as it has to come first: 33 | 34 | ```text 35 | $ LD_PRELOAD="$(readlink -f /usr/lib/libasan.so) $(readlink -f path/to/libasan_assert_fail_qt.so)" ./test-asan 36 | 0.000 warning: unknown[unknown:0]: ASSERT: "argc == 2" in file test_qt.cpp, line 5 37 | ================================================================= 38 | ==247141==ERROR: AddressSanitizer: unknown-crash on address 0x000000000000 at pc 0x7ffc24e13ec0 bp 0x7ffc24e13e40 sp 0x7ffc24e13e40 39 | READ of size 1 at 0x000000000000 thread T0 40 | #0 0x7ffc24e13ebf ([stack]+0x1eebf) 41 | #1 0x7fa8e75bad0f in __asan::ErrorDescription::Print() /build/gcc/src/gcc/libsanitizer/asan/asan_errors.h:420 42 | #2 0x7fa8e75bad0f in __asan::ScopedInErrorReport::~ScopedInErrorReport() /build/gcc/src/gcc/libsanitizer/asan/asan_report.cc:140 43 | #3 0x7fa8e75ba668 in __asan::ReportGenericError(unsigned long, unsigned long, unsigned long, unsigned long, bool, unsigned long, unsigned int, bool) /build/gcc/src/gcc/libsanitizer/asan/asan_report.cc:458 44 | #4 0x7fa8e75ba773 in __asan_report_error /build/gcc/src/gcc/libsanitizer/asan/asan_report.cc:473 45 | #5 0x7fa8e749d1a6 in qt_assert(char const*, char const*, int) (/home/milian/projects/kdab/KDToolBox/build/qt/asan_assert_fail_qt/libasan_assert_fail_qt.so.1.0.0+0x11a6) 46 | #6 0x556cb2b43715 in main (/tmp/a.out+0x715) 47 | #7 0x7fa8e6a1d022 in __libc_start_main (/usr/lib/libc.so.6+0x27022) 48 | #8 0x556cb2b4361d in _start (/tmp/a.out+0x61d) 49 | ``` 50 | -------------------------------------------------------------------------------- /qt/asan_assert_fail_qt/asan_assert_fail_qt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Milian Wolff 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #include 11 | 12 | #include 13 | 14 | #include 15 | 16 | Q_DECL_EXPORT void qt_assert(const char *assertion, const char *file, int line) noexcept 17 | { 18 | int stack_var = 0; 19 | qWarning("ASSERT: \"%s\" in file %s, line %d", assertion, file, line); 20 | __asan_report_error(__builtin_frame_address(0), &stack_var, &stack_var, NULL, 0, 1); 21 | std::abort(); 22 | } 23 | 24 | extern "C" { 25 | #include "../../general/asan_assert_fail/asan_assert_fail.c" 26 | } 27 | -------------------------------------------------------------------------------- /qt/cmake-project/README.md: -------------------------------------------------------------------------------- 1 | # cmake-project 2 | 3 | This project contains a shell script that generates a `CMakeLists.txt` 4 | based on the .cpp / .ui / .rcc files in the current directory. 5 | 6 | It's the CMake equivalent of `qmake -project`. 7 | 8 | It supports Qt 5, Qt 6 as well not using Qt at all. For instance: 9 | 10 | ```bash 11 | cmake-project --qt 6 12 | ``` 13 | -------------------------------------------------------------------------------- /qt/cmake-project/cmake-project: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # SPDX-FileCopyrightText: 2020 Klaravdalens Datakonsult AB (KDAB), author David Faure 3 | # SPDX-License-Identifier: MIT 4 | 5 | # Generate a CMakeLists.txt based on the .cpp files in the current directory 6 | # Similar to qmake -project, in essence. 7 | 8 | 9 | usage() 10 | { 11 | echo "Usage: $0 [ -q | --qt 5 | 6 | 5-and-6 | none ]" 12 | echo "The -q or --qt option allows to choose between Qt 5, Qt 6, both, or no Qt dependency" 13 | echo "Qt 6 is used by default" 14 | exit 2 15 | } 16 | 17 | PARSED_ARGUMENTS=$(getopt -a -n cmake-project -o q:,h --long qt:,help -- "$@") 18 | INVALID_ARGUMENTS=$? 19 | if [ "$INVALID_ARGUMENTS" != "0" ]; then 20 | usage 21 | fi 22 | 23 | eval set -- "$PARSED_ARGUMENTS" 24 | 25 | QTVERSION=6 26 | while : 27 | do 28 | case "$1" in 29 | -q | --qt) QTVERSION="$2" ; shift 2 ;; 30 | -h | --help) usage; ;; 31 | --) shift; break;; 32 | *) echo "Unexpected option: $1"; usage; ;; # can't happen 33 | esac 34 | done 35 | 36 | if [ -f CMakeLists.txt ]; then 37 | echo "CMakeLists.txt already exists, aborting for safety reasons" 38 | exit 1 39 | fi 40 | FILES="`find -name '*.cpp' -o -name '*.qrc' -o -name '*.ui' | sed -e 's,\.\/, ,' | sort -V`" 41 | if [ -z "$FILES" ]; then 42 | echo "No cpp files found in the current directory" 43 | exit 1 44 | fi 45 | PROJECTNAME=`basename "$PWD" | sed -e 's/ /_/g'` 46 | TARGET="$PROJECTNAME" 47 | 48 | if [ "$QTVERSION" != "none" ]; then 49 | if [ "$QTVERSION" = "5-and-6" ]; then 50 | QT="Qt\${QT_VERSION_MAJOR}" 51 | else 52 | QT=Qt$QTVERSION 53 | fi 54 | QTLIBS="Core Gui" 55 | QTTARGETS="$QT::Core $QT::Gui" 56 | if [ -n "`grep QApplication *.cpp`" ]; then 57 | QTLIBS="$QTLIBS Widgets" 58 | QTTARGETS="$QTTARGETS $QT::Widgets" 59 | AUTOUIC="set(CMAKE_AUTOUIC TRUE)" 60 | fi 61 | fi 62 | 63 | cat > CMakeLists.txt <> CMakeLists.txt <> CMakeLists.txt <> CMakeLists.txt <> CMakeLists.txt < 5 | Author: Sérgio Martins 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #ifndef KDTOOLBOX_DPIINFO_H 11 | #define KDTOOLBOX_DPIINFO_H 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #ifdef Q_OS_WIN 20 | #include "shellscalingapi.h" 21 | #endif 22 | 23 | namespace KDToolBox 24 | { 25 | 26 | inline QString hdpiInfo() 27 | { 28 | const auto envVars = {"QT_AUTO_SCREEN_SCALE_FACTOR", 29 | "QT_STYLE_OVERRIDE", 30 | "QT_SCREEN_SCALE_FACTORS", 31 | "QT_DEVICE_PIXEL_RATIO", 32 | "QT_ENABLE_HIGHDPI_SCALING", 33 | "QT_SCALE_FACTOR", 34 | "QT_FONT_DPI", 35 | "QT_USE_PHYSICAL_DPI"}; 36 | 37 | const QFont font = qApp->font(); 38 | QString result = QStringLiteral("defaultFontPx=%1; defaultFontPt=%2; app.dpr=%3; qpa=%4\n") 39 | .arg(font.pixelSize()) 40 | .arg(font.pointSize()) 41 | .arg(qApp->devicePixelRatio()) 42 | .arg(qApp->platformName()); 43 | for (const char *envVar : envVars) 44 | { 45 | if (qEnvironmentVariableIsSet(envVar)) 46 | { 47 | result += QStringLiteral("%1=%2; ").arg(QString::fromLatin1(envVar)).arg(qEnvironmentVariable(envVar)); 48 | } 49 | } 50 | 51 | const QList screens = qApp->screens(); 52 | for (QScreen *screen : screens) 53 | { 54 | result += QStringLiteral("\nScreen %1: %2x%3, physDpi=%4, logicalDpi=%5, dpr=%6") 55 | .arg(screen->name()) 56 | .arg(screen->size().width()) 57 | .arg(screen->size().height()) 58 | .arg(screen->physicalDotsPerInch()) 59 | .arg(screen->logicalDotsPerInch()) 60 | .arg(screen->devicePixelRatio()); 61 | } 62 | 63 | #ifdef Q_OS_WIN 64 | 65 | PROCESS_DPI_AWARENESS pda; 66 | auto res = GetProcessDpiAwareness(NULL, &pda); 67 | if (res == S_OK) 68 | { 69 | result += QStringLiteral("\nDPI AWARENESS = %1 (%2)") 70 | .arg((pda == PROCESS_DPI_UNAWARE) ? QStringLiteral("PROCESS_DPI_UNAWARE") 71 | : (pda == PROCESS_SYSTEM_DPI_AWARE) ? QStringLiteral("PROCESS_SYSTEM_DPI_AWARE") 72 | : (pda == PROCESS_PER_MONITOR_DPI_AWARE) ? QStringLiteral("PROCESS_PER_MONITOR_DPI_AWARE") 73 | : QStringLiteral("Unknown")) 74 | .arg(pda); 75 | } 76 | else 77 | { 78 | result += "Error while retrieving DPI awareness setting"; 79 | } 80 | 81 | #endif 82 | 83 | return result; 84 | } 85 | 86 | } 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /qt/eventfilter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(example) 8 | -------------------------------------------------------------------------------- /qt/eventfilter/README.md: -------------------------------------------------------------------------------- 1 | # EventFilter 2 | 3 | A header only class to quickly "connect" an event to a callback. 4 | 5 | It's common that classes sometimes miss signals and we're forced to write boilerplate event filters. 6 | For example, QWidget::isEnabled() doesn't emit any signal when enabled changed. 7 | 8 | You can now simply write: 9 | 10 | ```cpp 11 | auto filter = KDToolBox::installEventFilter(button, QEvent::EnabledChange, [button] { 12 | qDebug() << "Enabled changed" << button->isEnabled(); 13 | return false; 14 | }); 15 | ``` 16 | 17 | Two signatures are provided, one receiving a QObject parent as 4th argument and another one which 18 | instead returns a std::unique_ptr. 19 | -------------------------------------------------------------------------------- /qt/eventfilter/example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Gui 14 | Widgets 15 | ) 16 | 17 | set(CMAKE_CXX_STANDARD 14) 18 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 19 | 20 | set(example_SOURCES main.cpp) 21 | 22 | add_executable(example ${example_SOURCES}) 23 | target_link_libraries(example PUBLIC Qt::Core Qt::Gui Qt::Widgets) 24 | -------------------------------------------------------------------------------- /qt/eventfilter/example/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | #include "../eventfilter.h" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | int main(int argc, char *argv[]) 17 | { 18 | QApplication app(argc, argv); 19 | 20 | QPushButton b(QStringLiteral("Hello World")); 21 | QPushButton *button = &b; // avoid cluttering the code with & 22 | 23 | button->setMouseTracking(true); 24 | 25 | // directly capturing the target in the lambda 26 | auto filter = KDToolBox::installEventFilter(button, QEvent::Enter, [button] { 27 | qDebug() << "Mouse entered button" << button; 28 | return false; 29 | }); 30 | 31 | // taking target+event in the lambda 32 | auto filter2 = KDToolBox::installEventFilter(button, QEvent::FocusIn, [](QObject *target, QEvent *event) { 33 | qDebug() << "Focus in onto" << target << "because" << static_cast(event)->reason(); 34 | return false; 35 | }); 36 | 37 | // not returning anything, return false implied 38 | auto filter3 = KDToolBox::installEventFilter(button, QEvent::MouseMove, [button](QObject *, QEvent *event) { 39 | qDebug() << "Mouse moved on" << button << "at" << static_cast(event)->pos(); 40 | }); 41 | 42 | // move-only mutable lambda, not returning anything 43 | auto filter4 = KDToolBox::installEventFilter( 44 | button, QEvent::FocusOut, 45 | [button, x = std::make_unique(0), y = 0](QObject *target, QEvent *event) mutable { 46 | qDebug() << "Focus out from" << target << "because" << static_cast(event)->reason() 47 | << "invoked" << ++(y) << "times"; 48 | *x = y; 49 | }); 50 | 51 | button->show(); 52 | button->resize(500, 500); 53 | return app.exec(); 54 | } 55 | -------------------------------------------------------------------------------- /qt/includemocs/README.md: -------------------------------------------------------------------------------- 1 | # Includemocs 2 | 3 | When you use Q_OBJECT in a header file a moc file will be generated 4 | which needs to be compiled with the rest of your project. 5 | 6 | If you are using CMake, you will likely have a line like this in your 7 | CMakeLists.txt file: 8 | 9 | > set(CMAKE_AUTOMOC ON) 10 | 11 | This results in all moc files being included from one file, namely mocs_compilation.cpp. 12 | This has the advantage that it speeds up the initial build drastically, but the disadvantage that 13 | incremental builds - when you e.g. touch just a single header file - will be much slower. 14 | 15 | Including the moc files in the source files implementing the header file, gives us the best 16 | of both worlds - both fast initial build and fast incremental builds. 17 | 18 | This script does exactly that. 19 | 20 | Run it using -h for details. 21 | 22 | ## git-hook 23 | 24 | The file *check-includemocs-hook.sh* is a git hook to ensure all files which need it have moc files included. 25 | -------------------------------------------------------------------------------- /qt/includemocs/check-includemocs-hook.sh: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | #! /bin/sh 8 | 9 | # This is an example of a git pre-commit hook. 10 | # Replace the script variable below with the correct location of the script in your setup, 11 | # plus of course the excludes that make sense in your setup. 12 | 13 | script="3rdparty/KDToolBox/qt/includemocs/includemocs.py" 14 | if [ ! -e "$script" ]; then 15 | echo "Failed to find script $script" 16 | exit 1 17 | fi 18 | 19 | python3 "$script" --dry-run --exclude 3rdparty --quiet 20 | 21 | if [ $? -ne 0 ]; then 22 | python3 "$script" --dry-run --exclude 3rdparty 23 | echo "Run the following command to fix this:" 24 | echo "python3 $script --exclude 3rdparty" 25 | exit 1 26 | fi 27 | 28 | exit 0 29 | -------------------------------------------------------------------------------- /qt/messagehandler/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(example) 8 | add_subdirectory(tests) 9 | -------------------------------------------------------------------------------- /qt/messagehandler/README.md: -------------------------------------------------------------------------------- 1 | # MessageHandler 2 | 3 | Some convenience code to debug messages logged through Qt's logging facilities 4 | (qDebug(), qWarning(), etc.). 5 | 6 | You can use this code to figure out under what conditions a certain warning is 7 | printed, for instance like this: 8 | 9 | ```cpp 10 | KDToolBox::handleMessage(QtWarningMsg, 11 | QRegularExpression("SomeClass::someFunction: .*"), 12 | [](){ /* put a breakpoint here */ }); 13 | ``` 14 | 15 | This avoids the annoyance of installing a global message handler just for 16 | small debugging tasks. 17 | -------------------------------------------------------------------------------- /qt/messagehandler/example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Gui 14 | Widgets 15 | ) 16 | 17 | include_directories(../src/) 18 | set(example_SOURCES ../src/messagehandler.cpp ../src/messagehandler.h main.cpp) 19 | 20 | add_executable(example_messagehandler ${example_SOURCES}) 21 | target_link_libraries(example_messagehandler PUBLIC Qt::Core Qt::Gui Qt::Widgets) 22 | -------------------------------------------------------------------------------- /qt/messagehandler/example/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Giuseppe D'Angelo 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | int main(int argc, char **argv) 17 | { 18 | qSetMessagePattern(QStringLiteral("[%{if-debug}DBUG%{endif}%{if-info}INFO%{endif}%{if-warning}WARN%{endif}%{if-" 19 | "critical}CRIT%{endif}%{if-fatal}FATL%{endif}] %{message}")); 20 | 21 | QApplication app(argc, argv); 22 | 23 | qWarning() << "This is a warning message; no message handler has been registered yet."; 24 | 25 | qDebug() << "This is a debug message. We're now installing a handler for all warnings"; 26 | 27 | KDToolBox::handleMessage(QtWarningMsg, 28 | []() { qDebug() << "***!!!*** 1st warning handler here: a warning happened"; }); 29 | 30 | qWarning() << "This is warning again. Before this warning, you should've had an extra print."; 31 | 32 | qDebug() 33 | << "This is a debug message; this did not trigger the handler because it's a debug, not a warning message."; 34 | qDebug() << "Now installing a handler that reacts only on warnings that begin with 'PANIC'."; 35 | 36 | KDToolBox::handleMessage(QtWarningMsg, QRegularExpression(QStringLiteral("^PANIC")), []() { 37 | qDebug() << "***!!!*** 2nd warning handler here: a warning that begins with PANIC has been received"; 38 | }); 39 | 40 | qWarning() << "PANIC! Both warning handlers should've fired before this message"; 41 | qWarning() << "Another warning, this time only the first warning handler should've fired"; 42 | qCritical() << "Critical message. Handled also by a warning handler."; 43 | 44 | QPushButton button(QStringLiteral("Click to quit")); 45 | button.resize(500, 500); 46 | button.show(); 47 | QObject::connect(&button, &QPushButton::clicked, &app, &QApplication::quit); 48 | 49 | return app.exec(); 50 | } 51 | -------------------------------------------------------------------------------- /qt/messagehandler/src/messagehandler.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Giuseppe D'Angelo 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #include "messagehandler.h" 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | namespace 19 | { 20 | 21 | struct RegisteredCallback 22 | { 23 | const RegisteredCallback *next; 24 | QtMsgType messageType; 25 | QRegularExpression pattern; 26 | std::function callback; 27 | }; 28 | 29 | std::once_flag oldMessageHandlerFlag; 30 | std::atomic oldMessageHandler; 31 | std::atomic callbacks; 32 | 33 | bool isMessageTypeCompatibleWith(QtMsgType in, QtMsgType reference) 34 | { 35 | switch (in) 36 | { 37 | case QtDebugMsg: 38 | if (reference == QtInfoMsg) 39 | return false; 40 | break; 41 | case QtWarningMsg: 42 | if (reference == QtDebugMsg || reference == QtInfoMsg) 43 | return false; 44 | break; 45 | case QtCriticalMsg: 46 | if (reference == QtWarningMsg || reference == QtDebugMsg || reference == QtInfoMsg) 47 | return false; 48 | break; 49 | case QtFatalMsg: 50 | if (reference == QtCriticalMsg || reference == QtWarningMsg || reference == QtDebugMsg || 51 | reference == QtInfoMsg) 52 | return false; 53 | break; 54 | case QtInfoMsg: 55 | break; 56 | } 57 | 58 | return true; 59 | } 60 | 61 | void ourMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message) 62 | { 63 | for (auto it = callbacks.load(std::memory_order_acquire); it; it = it->next) 64 | { // synchronizes-with store-release in registerMessageHandler 65 | if (!isMessageTypeCompatibleWith(it->messageType, type)) 66 | continue; 67 | if (message.contains(it->pattern)) 68 | it->callback(); 69 | } 70 | 71 | if (auto h = oldMessageHandler.load( 72 | std::memory_order_acquire)) // synchronizes-with the store-release in installOurMessageHandler() 73 | h(type, context, message); 74 | } 75 | 76 | } // anonymous namespace 77 | 78 | void KDToolBox::Private::registerMessageHandler(QtMsgType type, const QRegularExpression &pattern, 79 | std::function func) 80 | { 81 | const auto installOurMessageHandler = [] { 82 | oldMessageHandler.store(qInstallMessageHandler(&ourMessageHandler), 83 | std::memory_order_release); // synchronizes-with the load-acquire in ourMessageHandler() 84 | }; 85 | std::call_once(oldMessageHandlerFlag, installOurMessageHandler); 86 | 87 | auto tmp = new RegisteredCallback{nullptr, type, pattern, std::move(func)}; 88 | auto expected = callbacks.load(std::memory_order_relaxed); // just the pointer value 89 | do 90 | { 91 | tmp->next = expected; 92 | } while (!callbacks.compare_exchange_weak( 93 | expected, tmp, std::memory_order_release)); // synchronizes-with load-acquire in outMessageHandler 94 | } 95 | -------------------------------------------------------------------------------- /qt/messagehandler/src/messagehandler.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Giuseppe D'Angelo 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #ifndef KDTOOLBOX_MESSAGEHANDLER_H 11 | #define KDTOOLBOX_MESSAGEHANDLER_H 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | namespace KDToolBox 20 | { 21 | 22 | namespace Private 23 | { 24 | void registerMessageHandler(QtMsgType type, const QRegularExpression &pattern, std::function func); 25 | } // namespace Private 26 | 27 | template 28 | void handleMessage(QtMsgType type, const QRegularExpression &pattern, Callable &&callback) 29 | { 30 | Private::registerMessageHandler(type, pattern, std::forward(callback)); 31 | } 32 | 33 | template 34 | void handleMessage(QtMsgType type, const QString &needle, Callable &&callback) 35 | { 36 | Private::registerMessageHandler(type, QRegularExpression{QRegularExpression::escape(needle)}, 37 | std::forward(callback)); 38 | } 39 | 40 | template 41 | void handleMessage(QtMsgType type, Callable &&callback) 42 | { 43 | Private::registerMessageHandler(type, QRegularExpression(), std::forward(callback)); 44 | } 45 | 46 | } // namespace KDToolBox 47 | 48 | #endif // KDTOOLBOX_MESSAGEHANDLER_H 49 | -------------------------------------------------------------------------------- /qt/messagehandler/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(messagehandler) 8 | -------------------------------------------------------------------------------- /qt/messagehandler/tests/messagehandler/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") 10 | 11 | include_directories(../../src/) 12 | 13 | set(tst_messagehandler_SOURCES ../../src/messagehandler.cpp ../../src/messagehandler.h tst_messagehandler.cpp) 14 | 15 | add_executable(tst_messagehandler ${tst_messagehandler_SOURCES}) 16 | target_link_libraries(tst_messagehandler PUBLIC Qt::Core Qt::Test) 17 | -------------------------------------------------------------------------------- /qt/messagehandler/tests/messagehandler/tst_messagehandler.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Marc Mutz 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | class tst_MessageHandler : public QObject 20 | { 21 | Q_OBJECT 22 | public: 23 | using QObject::QObject; 24 | 25 | private Q_SLOTS: 26 | void threading(); 27 | }; 28 | 29 | void tst_MessageHandler::threading() 30 | { 31 | // to be run with tsan 32 | static const size_t num_iterations = 100; 33 | static const size_t max_threads = 16; 34 | static const QtMsgType msg_types[] = {QtDebugMsg, QtWarningMsg, QtCriticalMsg, /*QtFatalMsg,*/ QtInfoMsg}; 35 | static const auto num_msg_types = sizeof msg_types / sizeof *msg_types; 36 | static const auto num_threads = qBound(num_msg_types, size_t{std::thread::hardware_concurrency()} * 2, max_threads); 37 | qDebug() << "num_threads" << num_threads; 38 | 39 | std::vector threads; 40 | threads.reserve(num_threads); 41 | 42 | std::vector counts(num_threads); 43 | 44 | for (size_t thread = 0; thread < num_threads; ++thread) 45 | { 46 | auto &count = counts[thread]; 47 | threads.emplace_back([thread, &count] { 48 | const auto msg_type = msg_types[thread % num_msg_types]; 49 | const QString pattern = QStringLiteral("^thread %1 iteration").arg(thread); 50 | KDToolBox::handleMessage(msg_type, QRegularExpression{pattern}, [&count] { ++count; }); 51 | for (size_t i = 0; i < num_iterations; ++i) 52 | { 53 | [=] { 54 | switch (msg_type) 55 | { 56 | case QtDebugMsg: 57 | return qDebug(); 58 | case QtWarningMsg: 59 | return qWarning(); 60 | case QtCriticalMsg: 61 | return qCritical(); 62 | case QtInfoMsg: 63 | return qInfo(); 64 | case QtFatalMsg:; 65 | }; 66 | Q_UNREACHABLE(); 67 | }() << "thread" 68 | << thread << "iteration" << i + 1; 69 | } 70 | }); 71 | } 72 | 73 | for (auto &t : threads) 74 | t.join(); 75 | 76 | for (size_t count : counts) 77 | QCOMPARE(count, num_iterations); 78 | } 79 | 80 | QTEST_APPLESS_MAIN(tst_MessageHandler) 81 | #include "tst_messagehandler.moc" 82 | -------------------------------------------------------------------------------- /qt/model_view/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(KDFunctionalSortFilterProxyModel) 8 | add_subdirectory(KDTableToListProxyModel) 9 | add_subdirectory(ModelIterator) 10 | add_subdirectory(sortProxyModel) 11 | add_subdirectory(updateableModel) 12 | -------------------------------------------------------------------------------- /qt/model_view/KDFunctionalSortFilterProxyModel/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(example) 8 | add_subdirectory(test) 9 | -------------------------------------------------------------------------------- /qt/model_view/KDFunctionalSortFilterProxyModel/README.md: -------------------------------------------------------------------------------- 1 | # Functional Sort/Filter Proxy Model 2 | 3 | This is a convenience `QSortFilterProxyModel` subclass that allows to 4 | filter rows/columns using a functional predicate, removing the need to subclass. 5 | 6 | ```cpp 7 | KDFunctionalSortFilterProxyModel *proxy = new KDFunctionalSortFilterProxyModel; 8 | 9 | auto acceptFunction = [](const QAbstractItemModel *model, int source_row, const QModelIndex &parent) 10 | { 11 | // decide whether to accept `source_row` from `model`, under `parent`; 12 | // return true to accept, false to filter the row out 13 | }; 14 | 15 | proxy->setFilterAcceptsRowFunction(acceptFunction); 16 | proxy->setSourceModel(sourceModel); 17 | 18 | view->setModel(proxy); 19 | ``` 20 | 21 | Similarly, you can set a sorting predicate: 22 | 23 | ```cpp 24 | KDFunctionalSortFilterProxyModel *proxy = new KDFunctionalSortFilterProxyModel; 25 | 26 | auto lessThanFunction = [](const QModelIndex &lhs, const QModelIndex &rhs) 27 | { 28 | // return true if `lhs` should be sorted before `rhs` 29 | }; 30 | 31 | proxy->setLessThanFunction(lessThanFunction); 32 | proxy->setSourceModel(sourceModel); 33 | 34 | view->setModel(proxy); 35 | ``` 36 | 37 | Note that when a sorting predicate is set `KDFunctionalSortFilterProxyModel` 38 | will *not* fall back to `QSortFilterProxyModel` default implementation. 39 | -------------------------------------------------------------------------------- /qt/model_view/KDFunctionalSortFilterProxyModel/example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Gui 14 | Widgets 15 | ) 16 | 17 | include_directories(../src/) 18 | set(example_SOURCES ../src/KDFunctionalSortFilterProxyModel.cpp ../src/KDFunctionalSortFilterProxyModel.h main.cpp) 19 | 20 | add_executable(example_KDFunctionalSortFilterProxyModel ${example_SOURCES}) 21 | target_link_libraries(example_KDFunctionalSortFilterProxyModel PUBLIC Qt::Core Qt::Gui Qt::Widgets) 22 | -------------------------------------------------------------------------------- /qt/model_view/KDFunctionalSortFilterProxyModel/example/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | #include 10 | 11 | #include 12 | 13 | int main(int argc, char **argv) 14 | { 15 | QApplication::setApplicationName(QStringLiteral("Colors with long names")); 16 | QApplication app(argc, argv); 17 | 18 | QStandardItemModel model; 19 | const auto colorNames = QColor::colorNames(); 20 | for (const QString &name : colorNames) 21 | { 22 | auto *item = new QStandardItem; 23 | item->setData(QVariant::fromValue(name), Qt::DisplayRole); 24 | item->setData(QVariant::fromValue(QColor(name)), Qt::DecorationRole); 25 | model.appendRow(item); 26 | } 27 | 28 | KDFunctionalSortFilterProxyModel proxy; 29 | proxy.setSourceModel(&model); 30 | 31 | auto colorIsLong = [](const QAbstractItemModel *model, int source_row, const QModelIndex &parent) { 32 | auto index = model->index(source_row, 0, parent); 33 | return index.data(Qt::DisplayRole).toString().size() >= 10; 34 | }; 35 | 36 | proxy.setFilterAcceptsRowFunction(colorIsLong); 37 | 38 | QListView view; 39 | view.setModel(&proxy); 40 | view.show(); 41 | 42 | return app.exec(); 43 | } 44 | -------------------------------------------------------------------------------- /qt/model_view/KDFunctionalSortFilterProxyModel/src/KDFunctionalSortFilterProxyModel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | #include "KDFunctionalSortFilterProxyModel.h" 10 | 11 | KDFunctionalSortFilterProxyModel::KDFunctionalSortFilterProxyModel(QObject *parent) 12 | : QSortFilterProxyModel(parent) 13 | { 14 | } 15 | 16 | KDFunctionalSortFilterProxyModel::~KDFunctionalSortFilterProxyModel() = default; 17 | 18 | void KDFunctionalSortFilterProxyModel::setFilterAcceptsRowFunction(AcceptsFunction function) 19 | { 20 | m_acceptsRowFunction = std::move(function); 21 | invalidateFilter(); 22 | } 23 | 24 | void KDFunctionalSortFilterProxyModel::clearFilterAcceptsRowFunction() 25 | { 26 | setFilterAcceptsRowFunction({}); 27 | } 28 | 29 | void KDFunctionalSortFilterProxyModel::setFilterAcceptsColumnFunction(AcceptsFunction function) 30 | { 31 | m_acceptsColumnFunction = std::move(function); 32 | invalidateFilter(); 33 | } 34 | 35 | void KDFunctionalSortFilterProxyModel::clearFilterAcceptsColumnFunction() 36 | { 37 | setFilterAcceptsColumnFunction({}); 38 | } 39 | 40 | void KDFunctionalSortFilterProxyModel::setLessThanFunction(LessThanFunction function) 41 | { 42 | m_lessThanFunction = std::move(function); 43 | invalidate(); 44 | } 45 | 46 | void KDFunctionalSortFilterProxyModel::clearLessThanFunction() 47 | { 48 | setLessThanFunction({}); 49 | } 50 | 51 | void KDFunctionalSortFilterProxyModel::invalidateFilter() 52 | { 53 | QSortFilterProxyModel::invalidateFilter(); 54 | } 55 | 56 | bool KDFunctionalSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const 57 | { 58 | if (m_acceptsRowFunction && !m_acceptsRowFunction(sourceModel(), source_row, source_parent)) 59 | return false; 60 | 61 | return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); 62 | } 63 | 64 | bool KDFunctionalSortFilterProxyModel::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const 65 | { 66 | if (m_acceptsColumnFunction && !m_acceptsColumnFunction(sourceModel(), source_column, source_parent)) 67 | return false; 68 | 69 | return QSortFilterProxyModel::filterAcceptsColumn(source_column, source_parent); 70 | } 71 | 72 | bool KDFunctionalSortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const 73 | { 74 | if (m_lessThanFunction) 75 | return m_lessThanFunction(source_left, source_right); 76 | 77 | return QSortFilterProxyModel::lessThan(source_left, source_right); 78 | } 79 | -------------------------------------------------------------------------------- /qt/model_view/KDFunctionalSortFilterProxyModel/src/KDFunctionalSortFilterProxyModel.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | #ifndef KDFUNCTIONALSORTFILTERPROXYMODEL_H 10 | #define KDFUNCTIONALSORTFILTERPROXYMODEL_H 11 | 12 | #include 13 | 14 | #include 15 | 16 | class KDFunctionalSortFilterProxyModel : public QSortFilterProxyModel 17 | { 18 | Q_OBJECT 19 | Q_DISABLE_COPY(KDFunctionalSortFilterProxyModel) 20 | 21 | public: 22 | explicit KDFunctionalSortFilterProxyModel(QObject *parent = nullptr); 23 | ~KDFunctionalSortFilterProxyModel(); 24 | 25 | using AcceptsFunction = std::function; 26 | using LessThanFunction = std::function; 27 | 28 | void setFilterAcceptsRowFunction(AcceptsFunction function); 29 | void clearFilterAcceptsRowFunction(); 30 | 31 | void setFilterAcceptsColumnFunction(AcceptsFunction function); 32 | void clearFilterAcceptsColumnFunction(); 33 | 34 | void setLessThanFunction(LessThanFunction function); 35 | void clearLessThanFunction(); 36 | 37 | public Q_SLOTS: 38 | // invalidate is already public; let's make invalidateFilter public too 39 | // (and a slot, while at it) 40 | void invalidateFilter(); 41 | 42 | protected: 43 | bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; 44 | bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const override; 45 | bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const final; 46 | 47 | private: 48 | AcceptsFunction m_acceptsRowFunction; 49 | AcceptsFunction m_acceptsColumnFunction; 50 | LessThanFunction m_lessThanFunction; 51 | }; 52 | 53 | #endif // KDFUNCTIONALSORTFILTERPROXYMODEL_H 54 | -------------------------------------------------------------------------------- /qt/model_view/KDFunctionalSortFilterProxyModel/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Gui 14 | Test 15 | ) 16 | 17 | include_directories(../src/) 18 | 19 | set(tst_kdfunctionalsortfilterproxymodel_SOURCES 20 | ../src/KDFunctionalSortFilterProxyModel.cpp ../src/KDFunctionalSortFilterProxyModel.h 21 | tst_kdfunctionalsortfilterproxymodel.cpp 22 | ) 23 | 24 | add_executable(tst_kdfunctionalsortfilterproxymodel ${tst_kdfunctionalsortfilterproxymodel_SOURCES}) 25 | target_link_libraries(tst_kdfunctionalsortfilterproxymodel PUBLIC Qt::Core Qt::Gui Qt::Test) 26 | -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(examples) 8 | add_subdirectory(tests) 9 | -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/README.md: -------------------------------------------------------------------------------- 1 | # Table to List Proxy Model 2 | 3 | KDTableToListProxyModel provides a convenient proxy model flattening a table model into a list model. 4 | 5 | The main use-case is Qt Quick 2: many of its views only support list models, and (ab)use multiple 6 | roles for transferring multiple pieces of information from the model to the view. In case we already 7 | have a table model, we need to flatten it 8 | 9 | ## Usage 10 | 11 | KDTableToListProxyModel is set up like an ordinary proxy model: 12 | 13 | ```cpp 14 | KDTableToListProxyModel tableToListProxyModel; 15 | tableToListProxyModel.setSourceModel(&sourceModel); 16 | ``` 17 | 18 | Then we need to specify a mapping from roles in KDTableToListProxyModel to columns (and optionally 19 | roles) in the source model. KDTableToListProxyModel has only one column, and the same amount of rows 20 | as the source model. 21 | 22 | ```cpp 23 | // map column 0 in the source to Qt::UserRole on this model 24 | tableToListProxyModel.setRoleMapping(0, Qt::UserRole, "name"); 25 | 26 | // map column 1 in the source to Qt::UserRole + 1 on this model 27 | tableToListProxyModel.setRoleMapping(1, Qt::UserRole + 1, "picture"); 28 | ``` 29 | 30 | Role names can be provided, for extra convenience when using Qt Quick. 31 | 32 | KDTableToListProxyModel does not support tree models (it will only show elements directly below the root). 33 | -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(countries) 8 | -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Gui 14 | Quick 15 | ) 16 | 17 | include_directories(../../src/) 18 | set(countries_SOURCES ../../src/KDTableToListProxyModel.cpp ../../src/KDTableToListProxyModel.h main.cpp) 19 | 20 | set(countries_QRCS countries.qrc) 21 | qt_add_resources(countries_SOURCES ${countries_QRCS}) 22 | 23 | add_executable(countries ${countries_SOURCES}) 24 | target_link_libraries(countries PUBLIC Qt::Core Qt::Gui Qt::Quick) 25 | -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/countries.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | images/Austria.png 5 | images/Belgium.png 6 | images/Bulgaria.png 7 | images/Croatia.png 8 | images/Cyprus.png 9 | images/Czech Republic.png 10 | images/Denmark.png 11 | images/Estonia.png 12 | images/Finland.png 13 | images/France.png 14 | images/Germany.png 15 | images/Greece.png 16 | images/Hungary.png 17 | images/Ireland.png 18 | images/Italy.png 19 | images/Latvia.png 20 | images/Lithuania.png 21 | images/Luxembourg.png 22 | images/Malta.png 23 | images/Netherlands.png 24 | images/Poland.png 25 | images/Portugal.png 26 | images/Romania.png 27 | images/Slovakia.png 28 | images/Slovenia.png 29 | images/Spain.png 30 | images/Sweden.png 31 | images/United Kingdom.png 32 | 33 | 34 | -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Austria.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Austria.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Belgium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Belgium.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Bulgaria.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Bulgaria.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Croatia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Croatia.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Cyprus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Cyprus.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Czech Republic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Czech Republic.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Denmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Denmark.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Estonia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Estonia.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Finland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Finland.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/France.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/France.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Germany.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Germany.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Greece.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Greece.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Hungary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Hungary.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Ireland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Ireland.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Italy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Italy.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Latvia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Latvia.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Lithuania.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Lithuania.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Luxembourg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Luxembourg.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Malta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Malta.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Netherlands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Netherlands.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Poland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Poland.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Portugal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Portugal.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Romania.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Romania.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Slovakia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Slovakia.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Slovenia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Slovenia.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Spain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Spain.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/Sweden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/Sweden.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/images/United Kingdom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDABLabs/KDToolBox/f43ed5916f3f0c8e27388e27e130a68cc9d10b49/qt/model_view/KDTableToListProxyModel/examples/countries/images/United Kingdom.png -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/examples/countries/main.qml: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | import QtQuick 2.12 10 | import QtQuick.Controls 2.12 11 | 12 | Item { 13 | width: 400 14 | height: 400 15 | 16 | TextField { 17 | id: searchField 18 | anchors { 19 | top: parent.top 20 | left: parent.left 21 | right: parent.right 22 | } 23 | placeholderText: "Find a country..." 24 | focus: true 25 | // one would normally use a Binding element here, but fixed string 26 | // isn't a property of QSortFilterProxymodel... 27 | onTextChanged: _filter.setFilterFixedString(text) 28 | } 29 | 30 | ListView { 31 | anchors { 32 | top: searchField.bottom 33 | left: parent.left 34 | right: parent.right 35 | bottom: parent.bottom 36 | } 37 | 38 | model: _model 39 | clip: true 40 | 41 | delegate: Rectangle { 42 | height: childrenRect.height 43 | width: parent.width 44 | 45 | Image { 46 | id: image 47 | source: model.flag 48 | width: 64 49 | height: 64 50 | fillMode: Image.PreserveAspectFit 51 | 52 | anchors.left: parent.left 53 | } 54 | 55 | Text { 56 | text: model.name + "\n" + "Population: " + model.population.toFixed(3) + " millions" 57 | anchors { 58 | left: image.right 59 | verticalCenter: image.verticalCenter 60 | leftMargin: 5 61 | } 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /qt/model_view/KDTableToListProxyModel/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Gui 14 | Test 15 | ) 16 | 17 | include_directories(../src/) 18 | set(tst_kdtabletolistproxymodel_SOURCES ../src/KDTableToListProxyModel.cpp ../src/KDTableToListProxyModel.h 19 | tst_kdtabletolistproxymodel.cpp 20 | ) 21 | 22 | add_executable(tst_kdtabletolistproxymodel ${tst_kdtabletolistproxymodel_SOURCES}) 23 | target_link_libraries(tst_kdtabletolistproxymodel PUBLIC Qt::Core Qt::Gui Qt::Test) 24 | -------------------------------------------------------------------------------- /qt/model_view/ModelIterator/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(example) 8 | -------------------------------------------------------------------------------- /qt/model_view/ModelIterator/README.md: -------------------------------------------------------------------------------- 1 | # Model Iterator 2 | 3 | Sometimes, you may find yourself in a situation where your data is only available in the form of 4 | QAbstractItemModel. Iterating over data in this model using the QAIM API is quite annoying, for 5 | instance to find a specific record. Wouldn't it be great to just be able to use std::find or 6 | std::find_if for that? Well, with the code in ModelIterator.h and ModelIterator.cpp you do just that! 7 | 8 | ## Contents 9 | 10 | ModelIterator contains two iterator types for iterating over a model: FlatIterator and DepthFirstIterator. 11 | There is a helper template class DataValueWrapper that allows directly accessing values in the model, 12 | and finally ModelAdaptor affords an easy way to wrap a model by providing a standard begin/end method 13 | pair returning a pair of iterators ready to use with standard algorithms or a ranged based for loop. 14 | 15 | For details, see the documentation in ModelIterator.h 16 | 17 | main.cpp provides some code that tests the provided functionality in ModelIterator and by doing that 18 | presents how to use the different classes. 19 | -------------------------------------------------------------------------------- /qt/model_view/ModelIterator/example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Gui) 8 | 9 | if(KDTOOLBOX_CXX20) 10 | set(CMAKE_CXX_STANDARD 20) 11 | else() 12 | set(CMAKE_CXX_STANDARD 14) 13 | endif() 14 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 15 | 16 | set(example_tmp_3_SOURCES ../src/ModelIterator.cpp ../src/ModelIterator.h main.cpp) 17 | 18 | add_executable(example_tmp_3 ${example_tmp_3_SOURCES}) 19 | target_link_libraries(example_tmp_3 PUBLIC Qt::Core Qt::Gui) 20 | -------------------------------------------------------------------------------- /qt/model_view/README.md: -------------------------------------------------------------------------------- 1 | # Model View tools 2 | 3 | This part of the repository contains some Qt Model/View related tools 4 | -------------------------------------------------------------------------------- /qt/model_view/sortProxyModel/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(test) 8 | -------------------------------------------------------------------------------- /qt/model_view/sortProxyModel/README.md: -------------------------------------------------------------------------------- 1 | # SortProxyModel 2 | 3 | SortProxyModel is a drop-in replacement for QSortFilterProxyModel in so far as 4 | it touches the sorting capabilities. It duplicates most of the sort-related 5 | API from its Qt counterpart. Where it differs is that unlike 6 | QSortFilterProxyModel, SortProxyModel emits the row move signals for QML to 7 | animate the reordering if the ordering criteria change. 8 | 9 | ## Usage 10 | 11 | Just like you would use QSortFilterProxyModel, only without the filtering. If 12 | you also need filtering, you will need a stack like `underlyingModel <-> 13 | QSortFilterProxyModel <-> SortProxyModel <-> View/QML' 14 | 15 | Add the SortProxyModel.h and SortProxyModel.cpp to your application sources, 16 | and the unit test in `test/` to your unit tests. 17 | 18 | ## Limitations 19 | 20 | SortProxyModel does currently not support sorting tree models. Only the root 21 | level of the underlying model is sorted. Furthermore, the model is always 22 | sorting. There is no unsorted pass-through mode where the model shows the 23 | order of the underlying model. Also, sorting only happens by row, not by 24 | column. 25 | -------------------------------------------------------------------------------- /qt/model_view/sortProxyModel/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Gui 14 | Test 15 | ) 16 | 17 | add_definitions(-DQT_DEPRECATED_WARNINGS) 18 | set(tst_sortproxymodeltest_SOURCES ../sortproxymodel.cpp ../sortproxymodel.h tst_sortproxymodeltest.cpp vectormodel.h) 19 | 20 | add_executable(tst_sortproxymodeltest ${tst_sortproxymodeltest_SOURCES}) 21 | target_link_libraries(tst_sortproxymodeltest PUBLIC Qt::Core Qt::Gui Qt::Test Qt::CorePrivate) 22 | -------------------------------------------------------------------------------- /qt/model_view/sortProxyModel/test/vectormodel.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2018 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: André Somers 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | 16 | /** 17 | * Simple vector-based test model for the unit tests. It is templated so it can operate on different 18 | * types of content, allowing tests with integers, doubles, strings, etc. 19 | * This class obviously does not have production quality, it is only meant to provide a source model 20 | * for unit testing. 21 | */ 22 | template 23 | class VectorModel : public QAbstractListModel 24 | { 25 | // cannot use Q_OBJECT but that's ok here 26 | 27 | int size() const noexcept { return static_cast(contents.size()); } 28 | 29 | public: 30 | explicit VectorModel(std::initializer_list initialContents, QObject *parent = nullptr) 31 | : QAbstractListModel(parent) 32 | , contents(initialContents) 33 | { 34 | } 35 | 36 | void setValue(int row, const T &value) 37 | { 38 | auto &e = contents[row]; 39 | if (e != value) 40 | { 41 | e = value; 42 | const auto idx = index(row); 43 | Q_EMIT dataChanged(idx, idx, QVector{Qt::DisplayRole}); 44 | } 45 | } 46 | 47 | void append(const T &value) 48 | { 49 | beginInsertRows(QModelIndex(), size(), size()); 50 | contents.push_back(value); 51 | endInsertRows(); 52 | } 53 | 54 | void append(std::initializer_list values) 55 | { 56 | beginInsertRows(QModelIndex(), size(), size() + static_cast(values.size()) - 1); 57 | 58 | contents.insert(contents.end(), values); 59 | endInsertRows(); 60 | } 61 | 62 | void insert(int row, const T &value) 63 | { 64 | beginInsertRows({}, row, row); 65 | contents.insert(contents.begin() + row, value); 66 | endInsertRows(); 67 | } 68 | 69 | void insert(int row, std::initializer_list values) 70 | { 71 | beginInsertRows({}, row, row + static_cast(values.size()) - 1); 72 | contents.insert(contents.begin() + row, values); 73 | endInsertRows(); 74 | } 75 | 76 | // QAbstractItemModel interface 77 | public: 78 | int rowCount(const QModelIndex &parent = QModelIndex()) const override 79 | { 80 | if (parent.isValid()) 81 | return 0; 82 | 83 | return size(); 84 | } 85 | 86 | QVariant data(const QModelIndex &index, int role) const override 87 | { 88 | if (role == Qt::DisplayRole) 89 | { 90 | return QVariant::fromValue(contents[index.row()]); 91 | } 92 | 93 | return {}; 94 | } 95 | 96 | bool removeRows(int row, int count, const QModelIndex &parent = {}) override 97 | { 98 | if (row < 0 || count < 0 || row + count >= size()) 99 | return false; 100 | 101 | beginRemoveRows(parent, row, row + count - 1); 102 | const auto first = contents.begin() + row; 103 | const auto last = first + count; 104 | contents.erase(first, last); 105 | endRemoveRows(); 106 | 107 | return true; 108 | } 109 | 110 | public: 111 | std::vector contents; 112 | 113 | // QAbstractItemModel interface 114 | }; 115 | -------------------------------------------------------------------------------- /qt/model_view/updateableModel/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(example) 8 | -------------------------------------------------------------------------------- /qt/model_view/updateableModel/README.md: -------------------------------------------------------------------------------- 1 | # Updateable Model 2 | 3 | UpdateableModel is a template class that provides an easy way to create models that properly signals 4 | updates to values. The user of the class simply has to provide an updated list of values, and the 5 | class automatically identifies what changes have been made in terms of inserts, removals and updates, 6 | and signals these in the right way. Signalling these changes in the right way is important for a 7 | good user experience, as it will allow things like keeping a stable selection, keeping the visible 8 | position, and not triggering more redraws than needed. 9 | 10 | This class is especially useful in cases where the backend just provides the complete new data or a 11 | signal that something has changed, but no details and no warnings before the change actually happens. 12 | 13 | ## How to use 14 | 15 | The user of class needs to: 16 | 17 | 1. Create a class that inherits from UpdateableModel, templated on the base item class you want 18 | (QAbstractListModel, QAbstractTableModel) and a type T that contains the data for each item in 19 | the model. 20 | 21 | 2. Use some kind of container to contain the data of type T (a std::vector, a QVector, a QList, whatever). 22 | 23 | 3. Implement the normal QAIM API methods like `data`, `columnCount`, `headerData`, `rowCount` 24 | and if needed `flags`. 25 | 26 | 4. Add an update method that takes a container (or a pair of iterators into a data structure) with 27 | data items that need to be in the model after update. In that method call updateData. 28 | updateData needs three things: 29 | 30 | 4.1. There needs to be a comparison method lessThan that orders items of type T 31 | 32 | This may take the form of T having operator< defined, or some other comparison callable you 33 | pass in to the updateData method. 34 | 35 | 4.3. The data needs to be sorted using that comparison 36 | 37 | 4.4. There needs to be a `hasChanged` method that returns a DataChanges structure containing 38 | information on what changed (what roles and columns) if anything. 39 | 40 | This may be a memberfunction of the class, or a callable passed to the updateData method. 41 | 42 | 5. updateData will then iterate through the data you pass in and the data already in the model, 43 | and signal all changes as efficiently as possible. 44 | 45 | ## Limitations 46 | 47 | 1. The `lessThan` function is used to uniquely identify items in the model. Item1 and Item2 are the 48 | same item if `(!(Item1 < Item2) && !(Item2 < Item1))` 49 | 50 | 2. The model only works on "list-type" models (possibly with multiple columns per list item). 51 | Tree models and models where data in different columns is not related are not supported. 52 | -------------------------------------------------------------------------------- /qt/model_view/updateableModel/example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Gui 14 | Widgets 15 | ) 16 | 17 | set(listUpdateAlgorithm_SOURCES 18 | ../UpdateableModel.h 19 | Data.h 20 | MainWindow.cpp 21 | MainWindow.h 22 | main.cpp 23 | tableModel.cpp 24 | tableModel.h 25 | ) 26 | 27 | set(listUpdateAlgorithm_UIS MainWindow.ui) 28 | # cmake-lint: disable=E1120 29 | qt_wrap_ui(listUpdateAlgorithm_SOURCES ${listUpdateAlgorithm_UIS}) 30 | 31 | add_executable(listUpdateAlgorithm ${listUpdateAlgorithm_SOURCES}) 32 | target_link_libraries(listUpdateAlgorithm PUBLIC Qt::Core Qt::Gui Qt::Widgets) 33 | -------------------------------------------------------------------------------- /qt/model_view/updateableModel/example/Data.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2018 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: André Somers 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #ifndef DATA_H 11 | #define DATA_H 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | struct Data 19 | { 20 | // pre Qt 5.13: for using a QVector as data type, we need a default constructor 21 | // Data(): id(-1) {} 22 | 23 | Data(int id, QString value1, QString value2) 24 | : id(id) 25 | , value1(std::move(value1)) 26 | , value2(std::move(value2)) 27 | { 28 | } 29 | 30 | int id; 31 | QString value1; 32 | QString value2; 33 | }; 34 | 35 | // using DataContainer = QVector; 36 | using DataContainer = std::vector; 37 | 38 | #endif // DATA_H 39 | -------------------------------------------------------------------------------- /qt/model_view/updateableModel/example/MainWindow.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2018 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: André Somers 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #include "MainWindow.h" 11 | #include "Data.h" 12 | #include "tableModel.h" 13 | #include "ui_MainWindow.h" 14 | #include 15 | #include 16 | 17 | MainWindow::MainWindow(QWidget *parent) 18 | : QMainWindow(parent) 19 | , ui(new Ui::MainWindow) 20 | , m_model(new TableModel(this)) 21 | { 22 | ui->setupUi(this); 23 | ui->tableView->setModel(m_model); 24 | 25 | DataContainer data{ 26 | {0, QStringLiteral("0"), QTime::currentTime().toString()}, 27 | {1, QStringLiteral("1"), QTime::currentTime().toString()}, 28 | {2, QStringLiteral("2"), QTime::currentTime().toString()}, 29 | }; 30 | m_model->update(data); 31 | m_data = data; 32 | 33 | auto timer = new QTimer(this); 34 | connect(timer, &QTimer::timeout, this, &MainWindow::updateData); 35 | 36 | timer->start(1000); 37 | } 38 | 39 | MainWindow::~MainWindow() 40 | { 41 | delete ui; 42 | } 43 | 44 | void MainWindow::updateData() 45 | { 46 | // update the data once every second. We update some items, we sometimes add an item and sometimes remove one. 47 | auto curTime = QTime::currentTime(); 48 | 49 | // every 5 seconds, either add or remove an item 50 | if (curTime.second() % 5 == 0) 51 | { 52 | auto itAt1 = std::next(m_data.begin(), 1); 53 | if (m_data.size() == 3) 54 | { 55 | m_data.erase(itAt1); 56 | } 57 | else 58 | { 59 | m_data.insert(itAt1, Data{1, QStringLiteral("1"), QTime::currentTime().toString()}); 60 | } 61 | } 62 | else 63 | { // otherwise, update the time for every other item. 64 | for (size_t i = 0; i < m_data.size(); ++i) 65 | { 66 | if ((i % 2) == static_cast((curTime.second() % 2))) 67 | { 68 | Data &item = m_data[i]; 69 | item.value2 = QTime::currentTime().toString(); 70 | } 71 | } 72 | } 73 | m_model->update(m_data); 74 | } 75 | -------------------------------------------------------------------------------- /qt/model_view/updateableModel/example/MainWindow.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2018 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: André Somers 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #ifndef MAINWINDOW_H 11 | #define MAINWINDOW_H 12 | 13 | #include "Data.h" 14 | #include 15 | 16 | class TableModel; 17 | 18 | QT_BEGIN_NAMESPACE 19 | namespace Ui 20 | { 21 | class MainWindow; 22 | } 23 | QT_END_NAMESPACE 24 | 25 | class MainWindow : public QMainWindow 26 | { 27 | Q_OBJECT 28 | 29 | public: 30 | explicit MainWindow(QWidget *parent = 0); 31 | ~MainWindow(); 32 | 33 | private: 34 | // make some updates to the data 35 | void updateData(); 36 | 37 | private: 38 | Ui::MainWindow *ui; 39 | 40 | // the model inheriting from UpdateableModel 41 | TableModel *m_model; 42 | 43 | // the data that we are changing and then pushing to the model 44 | DataContainer m_data; 45 | }; 46 | 47 | #endif // MAINWINDOW_H 48 | -------------------------------------------------------------------------------- /qt/model_view/updateableModel/example/MainWindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /qt/model_view/updateableModel/example/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2018 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: André Somers 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #include "MainWindow.h" 11 | #include 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | QApplication a(argc, argv); 16 | MainWindow w; 17 | w.show(); 18 | 19 | return a.exec(); 20 | } 21 | -------------------------------------------------------------------------------- /qt/model_view/updateableModel/example/tableModel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2018 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | #include "tableModel.h" 10 | #include 11 | #include 12 | 13 | TableModel::TableModel(QObject *parent) 14 | : UpdateableModel(parent) 15 | { 16 | } 17 | 18 | TableModel::~TableModel() = default; 19 | 20 | // Implementing this model is the crux of the example of using UpdateableModel 21 | void TableModel::update(const DataContainer &updatedData) 22 | { 23 | // tests 24 | // allow comparing items. Could also have used an operator< on Data 25 | auto lessThan = [](const Data &lhs, const Data &rhs) { return lhs.id < rhs.id; }; 26 | // check if the item has changed data, but is otherwise the same object. Needs to return the actual changes made. 27 | auto itemHasChanged = [](const Data &lhs, const Data &rhs) { 28 | DataChanges changes; 29 | if (lhs.value1 != rhs.value1) 30 | changes.changedColumns.append(1); 31 | if (lhs.value2 != rhs.value2) 32 | changes.changedColumns.append(2); 33 | changes.changedRoles = QVector{Qt::DisplayRole}; 34 | return changes; 35 | }; 36 | 37 | // call the updateData method to trigger merging in the changes to the model 38 | auto changes = updateData(updatedData.cbegin(), updatedData.cend(), m_data, lessThan, itemHasChanged); 39 | 40 | // the method above returns some stats, so lets print them. 41 | qDebug() << "changes in model: inserts:" << changes.inserts << " deletes:" << changes.removals 42 | << " updates:" << changes.updates; 43 | } 44 | 45 | // Under here you'll find the normal QAIM method reimplementations to make a model work 46 | int TableModel::rowCount(const QModelIndex &parent) const 47 | { 48 | if (parent.isValid()) 49 | return 0; 50 | 51 | return int(m_data.size()); 52 | } 53 | 54 | int TableModel::columnCount(const QModelIndex &parent) const 55 | { 56 | if (parent.isValid()) 57 | return 0; 58 | 59 | return 3; 60 | } 61 | 62 | QVariant TableModel::data(const QModelIndex &index, int role) const 63 | { 64 | if (!hasIndex(index.row(), index.column(), index.parent())) 65 | return {}; 66 | 67 | if (role != Qt::DisplayRole) 68 | return {}; 69 | 70 | const Data &item = m_data[index.row()]; 71 | 72 | switch (index.column()) 73 | { 74 | case 0: 75 | return item.id; 76 | case 1: 77 | return item.value1; 78 | case 2: 79 | return item.value2; 80 | } 81 | 82 | return {}; 83 | } 84 | 85 | QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const 86 | { 87 | if (orientation == Qt::Vertical || role != Qt::DisplayRole) 88 | return QVariant(); 89 | 90 | switch (section) 91 | { 92 | case 0: 93 | return QStringLiteral("ID"); 94 | case 1: 95 | return QStringLiteral("Value 1"); 96 | case 2: 97 | return QStringLiteral("Value 2"); 98 | } 99 | 100 | return QVariant(); 101 | } 102 | -------------------------------------------------------------------------------- /qt/model_view/updateableModel/example/tableModel.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2018 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: André Somers 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #ifndef TABLEMODEL_H 11 | #define TABLEMODEL_H 12 | 13 | #include "../UpdateableModel.h" 14 | #include "Data.h" 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | class TableModel : public UpdateableModel 22 | { 23 | Q_OBJECT 24 | public: 25 | explicit TableModel(QObject *parent = nullptr); 26 | ~TableModel() override; 27 | 28 | Q_SLOT void update(const DataContainer &updatedData); 29 | 30 | protected: // QAIM API 31 | int rowCount(const QModelIndex &parent) const override; 32 | int columnCount(const QModelIndex &parent) const override; 33 | QVariant data(const QModelIndex &index, int role) const override; 34 | QVariant headerData(int section, Qt::Orientation orientation, int role) const override; 35 | 36 | private: 37 | // should work with any random-access container with index-based access: 38 | 39 | // QVector m_data; // if you use QVector, you also need to enable the default constructor of Data in 40 | // Data.h 41 | std::vector m_data; 42 | // QList m_data; 43 | }; 44 | 45 | #endif // TABLEMODEL_H 46 | -------------------------------------------------------------------------------- /qt/notify_guard/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(test) 8 | -------------------------------------------------------------------------------- /qt/notify_guard/README.md: -------------------------------------------------------------------------------- 1 | # NotifyGuard 2 | 3 | This class is a generic property notification guard, that can 4 | monitor if a property was modified between the point of construction 5 | and destruction of the guard object. If the property was modified, 6 | the property's notifySignal will be emitted on destruction of 7 | the guard object. This is useful for classes with very complex 8 | property settings, classes with interdependent properties and classes 9 | where a single method may set change multiple properties at the 10 | same time. 11 | 12 | NotifyGuard requires the property to have a notify signal, and 13 | that the notify signal has either no or one argument that must 14 | then match the type of the property itself. 15 | 16 | A NotifyGuard can be constructed using a property name or using 17 | a pointer to a notify signal. In the first case, only the 18 | indicated property will be monitored for changes. When using a 19 | pointer to a notify signal, all properties using that notification 20 | signal will be monitored for changes. 21 | 22 | ```cpp 23 | NotifyGuard singlePropertyGuard(this, "thePropertyName"); 24 | NotifyGuard potentiallyMultiPropertyGuard(this, &ThisClass::theNotifySignal); 25 | ``` 26 | 27 | NotifyGuard can work either in SingleScope or in RecursiveScope 28 | mode (the default). In SingleScope, NotifyGuard only cares about 29 | itself and will always emit the notify signal if the value of the 30 | property has changed on destruction of the guard object. In 31 | RecursiveScope mode, NotifyGuard will make sure that a notification 32 | signal is only emitted once, from the outermost scope created for 33 | the given notify signal. This minimizes the number of signal 34 | emissions to prevent potentially expensive updates. 35 | 36 | ```cpp 37 | NotifyGuard singlePropertyGuard(this, "thePropertyName", NotifyGuard::SingleScope); 38 | NotifyGuard potentiallyMultiPropertyGuard(this, &ThisClass::theNotifySignal, NotifyGuard::RecursiveScope); 39 | ``` 40 | 41 | RecursiveScope is the default mode and may be left off. 42 | -------------------------------------------------------------------------------- /qt/notify_guard/src/notifyguard.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: André Somers 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #ifndef KDTOOLBOX_NOTIFYGUARD_H 11 | #define KDTOOLBOX_NOTIFYGUARD_H 12 | 13 | #include 14 | #include 15 | 16 | namespace KDToolBox 17 | { 18 | 19 | namespace Internal 20 | { 21 | struct SignalData; 22 | using SignalDataSPtr = std::shared_ptr; 23 | } 24 | 25 | /** Generic property notification guard 26 | * 27 | * This class is a generic property notification guard, that can 28 | * monitor if a property was modified between the point of construction 29 | * and destruction of the guard object. If the property was modified, 30 | * the property's notifySignal will be emitted on destruction of 31 | * the guard object. 32 | * 33 | * NotifyGuard requires the property to have a notify signal, and 34 | * that the notify signal has either no or one argument that must 35 | * then match the type of the property itself. 36 | * 37 | * A NotifyGuard can be constructed using a property name or using 38 | * a pointer to a notify signal. In the first case, only the 39 | * indicated property will be monitored for changes. When using a 40 | * pointer to a notify signal, all properties using that notification 41 | * signal will be monitored for changes. 42 | * 43 | * NotifyGuard can work either in SingleScope or in RecursiveScope 44 | * mode (the default). In SingleScope, NotifyGuard only cares about 45 | * itself and will always emit the notify signal if the value of the 46 | * property has changed on destruction of the guard object. In 47 | * RecursiveScope mode, NotifyGuard will make sure that a notification 48 | * signal is only emitted once, from the outermost scope created for 49 | * the given notify signal. This minimizes the number of signal 50 | * emissions to prevent potentially expensive updates. 51 | */ 52 | class NotifyGuard 53 | { 54 | public: 55 | enum GuardOptions 56 | { 57 | RecursiveScope, //!< This guard will only activate if there isn't already another guard on the same property 58 | //!< active (in an outer scope). 59 | SingleScope, //!< This guard only considers its own scope. 60 | }; 61 | 62 | public: 63 | NotifyGuard() = default; 64 | explicit NotifyGuard(QObject *target, const char *property, GuardOptions options = RecursiveScope); 65 | template 66 | explicit NotifyGuard(QObject *target, PointerToMemberFunction notifySignal, GuardOptions options = RecursiveScope) 67 | : NotifyGuard(target, QMetaMethod::fromSignal(notifySignal), options) 68 | { 69 | } 70 | // we allow moving from a NotifyGuard 71 | NotifyGuard(NotifyGuard &&) = default; 72 | NotifyGuard &operator=(NotifyGuard &&) = default; 73 | 74 | ~NotifyGuard(); 75 | 76 | inline bool isActive() const { return bool(m_signalData); } 77 | 78 | private: // methods 79 | Q_DISABLE_COPY(NotifyGuard) 80 | explicit NotifyGuard(QObject *target, QMetaMethod notifySignal, GuardOptions options); 81 | 82 | private: // members 83 | Internal::SignalDataSPtr m_signalData; 84 | }; 85 | 86 | } // namespace KDToolBox 87 | 88 | #endif // KDTOOLBOX_NOTIFYGUARD_H 89 | -------------------------------------------------------------------------------- /qt/notify_guard/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Gui 14 | Test 15 | ) 16 | 17 | add_definitions(-Wall) 18 | include_directories(../src/) 19 | 20 | set(test_tmp_1_SOURCES ../src/notifyguard.cpp ../src/notifyguard.h tst_notifyguard.cpp) 21 | 22 | add_executable(test_tmp_1 ${test_tmp_1_SOURCES}) 23 | target_link_libraries(test_tmp_1 PUBLIC Qt::Core Qt::Gui Qt::Test) 24 | -------------------------------------------------------------------------------- /qt/pointer_cast/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(test) 8 | -------------------------------------------------------------------------------- /qt/pointer_cast/README.md: -------------------------------------------------------------------------------- 1 | # pointer-cast 2 | 3 | A collection of helper functions to ease porting from `QSharedPointer` 4 | to `std::shared_ptr`, by helping with the most egregious API 5 | incompatibilites of QSharedPointer, namely: 6 | 7 | - static_pointer_cast overload for QSharedPointer 8 | - dynamic_pointer_cast overload for QSharedPointer 9 | - const_pointer_cast overload for QSharedPointer 10 | - make_qshared - like std::make_shared, but returning QSharedPointer 11 | 12 | The casts are designed to be used like their `std` counterparts 13 | operating on `std::shared_ptr`. We advise to used unqualified lookup 14 | 15 | ```cpp 16 | QSharedPointer base = make_qshared(~~~); 17 | auto derived = dynamic_pointer_cast(base); // not KDToolBox::... 18 | ``` 19 | 20 | If you target C++20, then this is also the way to call 21 | `std::dynamic_pointer_cast` (unqualifed lookup of functions with 22 | explicit template arguments doesn't find candidates by ADL prior to 23 | C++20) For this reason, the functions are defined in `inline namespace 24 | KDToolBox`. 25 | 26 | If you restrict your code to the std-compatible subset of 27 | QSharedPointer and use the above four functions, then porting from 28 | QSharedPointer to std::shared_ptr will be as easy as 29 | 30 | ```text 31 | s/QSharedPointer/std::shared_ptr/ 32 | s/QWeakPointer/std::weak_ptr/ 33 | s/make_qshared/std::make_shared/ 34 | ``` 35 | 36 | Enjoy! 37 | -------------------------------------------------------------------------------- /qt/pointer_cast/pointer_cast.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Giuseppe D'Angelo 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | 16 | inline namespace KDToolBox 17 | { 18 | 19 | template 20 | QSharedPointer static_pointer_cast(const QSharedPointer &from) 21 | { 22 | return from.template staticCast(); 23 | } 24 | 25 | template 26 | QSharedPointer dynamic_pointer_cast(const QSharedPointer &from) 27 | { 28 | return from.template dynamicCast(); 29 | } 30 | 31 | template 32 | QSharedPointer const_pointer_cast(const QSharedPointer &from) 33 | { 34 | return from.template constCast(); 35 | } 36 | 37 | template 38 | QSharedPointer make_qshared(Args &&...args) 39 | { 40 | return QSharedPointer::create(std::forward(args)...); 41 | } 42 | 43 | } // namespace KDToolBox 44 | -------------------------------------------------------------------------------- /qt/pointer_cast/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_STANDARD 14) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | set(tst_pointer_cast_SOURCES tst_pointer_cast.cpp) 13 | 14 | add_executable(tst_pointer_cast ${tst_pointer_cast_SOURCES}) 15 | target_link_libraries(tst_pointer_cast PUBLIC Qt::Core Qt::Test) 16 | -------------------------------------------------------------------------------- /qt/pointer_cast/test/tst_pointer_cast.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | #include 10 | 11 | #include "../pointer_cast.h" 12 | 13 | class tst_pointer_cast : public QObject 14 | { 15 | Q_OBJECT 16 | public: 17 | using QObject::QObject; 18 | 19 | private Q_SLOTS: 20 | void makeQShared(); 21 | void staticCast(); 22 | void dynamicCast(); 23 | void constCast(); 24 | }; 25 | 26 | namespace 27 | { 28 | 29 | struct B 30 | { 31 | B() = default; 32 | B(const B &) = delete; 33 | B &operator=(const B &) = delete; 34 | virtual ~B() = default; 35 | }; 36 | 37 | struct D : B 38 | { 39 | }; 40 | 41 | struct D2 : B 42 | { 43 | }; 44 | 45 | } // unnamed namespace 46 | 47 | void tst_pointer_cast::makeQShared() 48 | { 49 | { 50 | auto p = make_qshared(42); 51 | Q_STATIC_ASSERT((std::is_same>::value)); 52 | QVERIFY(p); 53 | QCOMPARE(*p, 42); 54 | } 55 | { 56 | auto p = make_qshared(QStringLiteral("Hello")); 57 | Q_STATIC_ASSERT((std::is_same>::value)); 58 | QVERIFY(p); 59 | QCOMPARE(*p, QStringLiteral("Hello")); 60 | } 61 | } 62 | 63 | void tst_pointer_cast::staticCast() 64 | { 65 | auto b = make_qshared(); 66 | QVERIFY(b); 67 | auto d = static_pointer_cast(b); 68 | QCOMPARE(b, d); 69 | } 70 | 71 | void tst_pointer_cast::dynamicCast() 72 | { 73 | auto b = make_qshared(); 74 | QVERIFY(b); 75 | auto d = dynamic_pointer_cast(b); 76 | QCOMPARE(b, d); 77 | auto d2 = dynamic_pointer_cast(b); 78 | QCOMPARE(d2, nullptr); 79 | } 80 | 81 | void tst_pointer_cast::constCast() 82 | { 83 | auto cd = make_qshared(); 84 | QVERIFY(cd); 85 | auto md = const_pointer_cast(cd); 86 | QCOMPARE(md, cd); 87 | } 88 | 89 | QTEST_MAIN(tst_pointer_cast) 90 | 91 | #include "tst_pointer_cast.moc" 92 | -------------------------------------------------------------------------------- /qt/qml/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(QmlStackTraceHelper) 8 | -------------------------------------------------------------------------------- /qt/qml/QmlStackTraceHelper/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(example) 8 | -------------------------------------------------------------------------------- /qt/qml/QmlStackTraceHelper/QmlStackTraceHelper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2018 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Sérgio Martins 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | extern "C" char *qt_v4StackTrace(void *executionContext); 19 | 20 | namespace KDAB 21 | { 22 | 23 | QString qmlStackTrace(QV4::ExecutionEngine *engine) 24 | { 25 | return QString::fromUtf8(qt_v4StackTrace(engine->currentContext())); 26 | } 27 | 28 | void printQmlStackTraces() 29 | { 30 | const auto windows = qApp->topLevelWindows(); 31 | for (QWindow *w : windows) 32 | { 33 | if (auto qw = qobject_cast(w)) 34 | { 35 | QQuickItem *item = qw->contentItem(); 36 | QQmlContext *context = QQmlEngine::contextForObject(item); 37 | if (!context) 38 | continue; 39 | QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(context->engine()); 40 | QV4::ExecutionEngine *v4engine = enginePriv->v4engine(); 41 | qDebug() << "Stack trace for" << qw; 42 | qDebug().noquote() << qmlStackTrace(v4engine); 43 | qDebug() << "\n"; 44 | } 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /qt/qml/QmlStackTraceHelper/README.md: -------------------------------------------------------------------------------- 1 | # Qml Stacktrace Helper 2 | 3 | An helper function so you can get a QML backtrace from gdb. 4 | 5 | ## Example Usage 6 | 7 | Build `QmlStackTraceHelper.cpp` with your project. 8 | 9 | ```text 10 | (gdb) call KDAB::printQmlStackTraces() 11 | Stack trace for QQuickView(0x7fffffffd2e0 active exposed, visibility=QWindow::Windowed, flags=QFlags(Window), geometry=0,1290 500x500) 12 | onSomeIndirection2Changed [qrc:/main.qml:28] 13 | onSomeIndirectionChanged [qrc:/main.qml:22] 14 | onClicked [qrc:/main.qml:17] 15 | ``` 16 | 17 | Also useful when running under `valgrind --vgdb=yes`. 18 | Just link `QmlStackTraceHelper.cpp` with your application. 19 | 20 | ## Upstreaming to Qt 21 | 22 | We're open to ideas on how to upstream this. 23 | -------------------------------------------------------------------------------- /qt/qml/QmlStackTraceHelper/example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Gui 14 | Qml 15 | Quick 16 | ) 17 | 18 | set(example_tmp_4_SOURCES ../QmlStackTraceHelper.cpp main.cpp) 19 | 20 | set(example_tmp_4_QRCS qrc.qrc) 21 | qt_add_resources(example_tmp_4_SOURCES ${example_tmp_4_QRCS}) 22 | 23 | add_executable(example_tmp_4 ${example_tmp_4_SOURCES}) 24 | target_link_libraries(example_tmp_4 PUBLIC Qt::Core Qt::Gui Qt::QmlPrivate Qt::Quick) 25 | -------------------------------------------------------------------------------- /qt/qml/QmlStackTraceHelper/example/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | class Controller : public QObject 14 | { 15 | Q_OBJECT 16 | public: 17 | using QObject::QObject; 18 | 19 | Q_INVOKABLE void crash() 20 | { 21 | delete this; 22 | delete this; 23 | } 24 | }; 25 | 26 | int main(int a, char **b) 27 | { 28 | QGuiApplication app(a, b); 29 | 30 | QQuickView view; 31 | view.rootContext()->setContextProperty(QStringLiteral("_controller"), new Controller()); 32 | view.setSource(QUrl(QStringLiteral("qrc:/main.qml"))); 33 | view.show(); 34 | 35 | return app.exec(); 36 | } 37 | 38 | #include 39 | -------------------------------------------------------------------------------- /qt/qml/QmlStackTraceHelper/example/main.qml: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | import QtQuick 2.5 10 | 11 | Rectangle { 12 | color: "red" 13 | width: 500 14 | height: 500 15 | 16 | property int someIndirection: 0 17 | property int someIndirection2: 0 18 | 19 | Text { 20 | text: "click to crash" 21 | } 22 | MouseArea { 23 | anchors.fill: parent 24 | onClicked: { 25 | someIndirection = 1; 26 | } 27 | } 28 | 29 | onSomeIndirectionChanged: { 30 | if (someIndirection > 0) 31 | someIndirection2 = 1; 32 | } 33 | 34 | onSomeIndirection2Changed: { 35 | if (someIndirection2 > 0) 36 | _controller.crash(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /qt/qml/QmlStackTraceHelper/example/qrc.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | 5 | 6 | -------------------------------------------------------------------------------- /qt/qt6_natvis/README.md: -------------------------------------------------------------------------------- 1 | # Qt6 Natvis 2 | 3 | The Qt6 Natvis file has moved to: [natvis4qt](https://github.com/narnaud/natvis4qt) 4 | 5 | You can find the latest natvis file here: 6 | 7 | - [qt5.natvis](https://github.com/narnaud/natvis4qt/releases/latest/download/qt5.natvis) 8 | - [qt6.natvis](https://github.com/narnaud/natvis4qt/releases/latest/download/qt6.natvis) 9 | 10 | Or better, install the `natvis4qt` tool to automatically install and update 11 | natvis files in known locations (see 12 | [documentation](https://github.com/narnaud/natvis4qt?tab=readme-ov-file#natvis4qt)). 13 | -------------------------------------------------------------------------------- /qt/qt_fmt/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | include(CheckSubmoduleExists) 8 | 9 | check_submodule_exists(fmt fmt) 10 | 11 | add_subdirectory(testfmt) 12 | 13 | if(KDTOOLBOX_CXX20) 14 | add_subdirectory(teststd) 15 | endif() 16 | -------------------------------------------------------------------------------- /qt/qt_fmt/README.md: -------------------------------------------------------------------------------- 1 | # libfmt support for Qt datatypes 2 | 3 | This project provides a convenience `fmt::formatter` partial 4 | specialization which enables formatting of Qt-related datatypes using 5 | [libfmt](https://github.com/fmtlib/fmt). 6 | 7 | The formatting is implemented by reusing the QDebug streaming 8 | operators. Most Qt datatypes already offer such an operator: 9 | 10 | ```cpp 11 | QDateTime dt = ~~~; 12 | qDebug() << dt; // QDateTime can be streamed into QDebug 13 | ``` 14 | 15 | This header-only project allows to format/print such an object using 16 | fmt's facilities, for instance like this: 17 | 18 | ```cpp 19 | #include 20 | 21 | QDateTime dt = ~~~; 22 | fmt::print("{}\n", dt); // works 23 | ``` 24 | 25 | No other code is necessary. 26 | 27 | This approach works with *any* datatype that has a QDebug streaming 28 | operator, whether it's a Qt-provided class or a user-defined one: 29 | 30 | ```cpp 31 | // A custom class that has a QDebug operator 32 | class MyClass { ~~~ }; 33 | QDebug operator<<(QDebug stream, const MyClass &c) { ~~~ } 34 | 35 | void f() { 36 | MyClass obj; 37 | fmt::print("My object is: {}\n", obj); // works 38 | } 39 | ``` 40 | 41 | Requires a C++17-capable compiler. 42 | 43 | Note: unlike `libfmt`'s facilities, `QDebug` isn't designed with 44 | efficiency in mind. The formatter can help integrating `libfmt` within 45 | Qt-based projects, but it should not be used in scenarios where 46 | performances are paramount. 47 | 48 | ## Formatting options 49 | 50 | At the moment, only `{}` is supported. 51 | -------------------------------------------------------------------------------- /qt/qt_fmt/qt_fmt.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "qt_fmt_helpers.h" 12 | 13 | #include 14 | #include 15 | 16 | template 17 | struct fmt::formatter::value>, 19 | decltype(std::declval() << std::declval())>> 20 | { 21 | constexpr auto parse(fmt::format_parse_context &ctx) 22 | { 23 | auto it = ctx.begin(); 24 | auto end = ctx.end(); 25 | if (it != end && *it != '}') 26 | throw fmt::format_error("Only {} is supported"); 27 | return it; 28 | } 29 | 30 | template 31 | auto format(const T &t, FormatContext &ctx) const 32 | { 33 | // This is really expensive (lots of allocations). Unfortunately 34 | // there isn't something as easy to do that also performs better. 35 | // 36 | // * storing buffer+debug as thread_local has reentrancy issues. 37 | // Say you format an object of type A; this calls operator<<(QDebug d, A a). 38 | // Inside that, you call d << a.b to stream a subobject of type B. 39 | // operator<<(QDebug d, B b) might be implemented itself using fmt, e.g. 40 | // via std::format(~~~, b.c, b.d, b.e). 41 | // Now if any sub-object ends up being printed using *this* 42 | // streaming (via QDebug), it won't work. 43 | // 44 | // * Using QByteArray + QBuffer to avoid the final conversion 45 | // to UTF-8 doesn't really help, it'll replace it with a bunch 46 | // of transient small allocations (as QDebug will convert each 47 | // string streamed into it in UTF-8 and then append to its 48 | // internal buffer) 49 | 50 | QString buffer; 51 | QDebug debug(&buffer); 52 | debug.noquote().nospace() << t; 53 | return fmt::format_to(ctx.out(), "{}", buffer.toStdString()); 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /qt/qt_fmt/qt_fmt_helpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | namespace Qt_fmt 27 | { 28 | namespace detail 29 | { 30 | 31 | template typename Primary> 32 | struct is_specialization_of : std::false_type 33 | { 34 | }; 35 | 36 | template typename Primary, typename... Args> 37 | struct is_specialization_of, Primary> : std::true_type 38 | { 39 | }; 40 | 41 | } // namespace detail 42 | 43 | // Offering this as a customization point for users who might want to 44 | // define both QDebug streaming and fmt formatters, so they need a way 45 | // to shut down this path. 46 | // 47 | // Note: keeping this in sync between fmt and QDebug sounds like a 48 | // nightmare. 49 | // clang-format off 50 | template 51 | struct exclude_from_qdebug_fmt 52 | : std::disjunction, 53 | // QByteArray is a thorn in the side. 54 | // fmt handles automatically types that convert to const char *, 55 | // but not types that convert to const void *. QByteArray by default 56 | // converts to both; so be careful about it here. 57 | std::conjunction< 58 | std::is_convertible, 59 | std::negation, QByteArray>> 60 | >, 61 | std::conjunction< 62 | std::is_convertible, 63 | std::negation, QByteArray>> 64 | >, 65 | // Re-include char-arrays, since the above would exclude them 66 | std::conjunction< 67 | std::is_array, 68 | std::is_same, char> 69 | >, 70 | detail::is_specialization_of, 71 | detail::is_specialization_of, 72 | // fmt doesn't necessarily offer these as builtins, but let's be conservative 73 | detail::is_specialization_of, 74 | detail::is_specialization_of, 75 | detail::is_specialization_of, 76 | detail::is_specialization_of, 77 | detail::is_specialization_of, 78 | detail::is_specialization_of, 79 | detail::is_specialization_of, 80 | detail::is_specialization_of, 81 | detail::is_specialization_of, 82 | detail::is_specialization_of, 83 | detail::is_specialization_of, 84 | detail::is_specialization_of 85 | > 86 | { 87 | }; 88 | // clang-format on 89 | 90 | } // namespace Qt_fmt 91 | -------------------------------------------------------------------------------- /qt/qt_fmt/qt_std_format.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | #pragma once 10 | 11 | #if __cplusplus < 202002L 12 | #error "This header requires C++20" 13 | #endif 14 | 15 | #include "qt_fmt_helpers.h" 16 | #include 17 | #include 18 | 19 | namespace Qt_fmt::detail 20 | { 21 | template 22 | concept IsFormattableViaQDebug = requires(QDebug &d, const T &t) { 23 | d << t; 24 | requires !Qt_fmt::exclude_from_qdebug_fmt::value; 25 | }; 26 | } 27 | 28 | template 29 | requires Qt_fmt::detail::IsFormattableViaQDebug 30 | struct std::formatter 31 | { 32 | template 33 | constexpr auto parse(ParseContext &ctx) 34 | { 35 | auto it = ctx.begin(); 36 | auto end = ctx.end(); 37 | if (it != end && *it != '}') 38 | throw std::format_error("Only {} is supported"); 39 | return it; 40 | } 41 | 42 | template 43 | auto format(const T &t, FormatContext &ctx) const 44 | { 45 | // See the comment in the libfmt formatter 46 | QString buffer; 47 | QDebug debug(&buffer); 48 | debug.noquote().nospace() << t; 49 | return std::format_to(ctx.out(), "{}", buffer.toStdString()); 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /qt/qt_fmt/testfmt/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Gui 14 | Test 15 | ) 16 | 17 | set(CMAKE_CXX_STANDARD 17) 18 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 19 | 20 | include_directories(../fmt/include/) 21 | 22 | set(test_qt_fmt_SOURCES ../fmt/src/format.cc tst_qt_fmt.cpp) 23 | 24 | add_executable(test_qt_fmt ${test_qt_fmt_SOURCES}) 25 | target_link_libraries(test_qt_fmt PUBLIC Qt::Core Qt::Gui Qt::Test) 26 | -------------------------------------------------------------------------------- /qt/qt_fmt/teststd/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Gui 14 | Test 15 | ) 16 | 17 | set(CMAKE_CXX_STANDARD 20) 18 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 19 | 20 | set(test_qt_std_format_SOURCES tst_qt_std_format.cpp) 21 | 22 | add_executable(test_qt_std_format ${test_qt_std_format_SOURCES}) 23 | target_link_libraries(test_qt_std_format PUBLIC Qt::Core Qt::Gui Qt::Test) 24 | -------------------------------------------------------------------------------- /qt/qt_fmt/teststd/tst_qt_std_format.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | #include "../qt_std_format.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | class QtStdFormatTest : public QObject 21 | { 22 | Q_OBJECT 23 | 24 | private Q_SLOTS: 25 | void testQtStdFormat(); 26 | }; 27 | 28 | // Custom class streamable via QDebug 29 | class CustomClass 30 | { 31 | }; 32 | QDebug operator<<(QDebug d, CustomClass) 33 | { 34 | return d << "CustomClass()"; 35 | } 36 | 37 | // Custom class streamable via std::formatBothStdAndQDebugClass and QDebug 38 | class BothStdAndQDebugClass 39 | { 40 | }; 41 | template<> 42 | struct std::formatter 43 | { 44 | template 45 | constexpr auto parse(ParseContext &ctx) 46 | { 47 | auto it = ctx.begin(); 48 | auto end = ctx.end(); 49 | if (it != end && *it != '}') 50 | throw std::format_error("Only {} is supported"); 51 | return it; 52 | } 53 | template 54 | auto format(const BothStdAndQDebugClass &, FormatContext &ctx) const 55 | { 56 | return std::format_to(ctx.out(), "BothStdAndQDebugClass via std"); 57 | } 58 | }; 59 | 60 | QDebug operator<<(QDebug d, BothStdAndQDebugClass) 61 | { 62 | return d << "BothStdAndQDebugClass via QDebug"; 63 | } 64 | 65 | template<> 66 | struct Qt_fmt::exclude_from_qdebug_fmt : std::true_type 67 | { 68 | }; 69 | 70 | void QtStdFormatTest::testQtStdFormat() 71 | { 72 | // This is a bit sketchy because it depends on QDebug's format. Better than nothing? 73 | using namespace std::literals; 74 | 75 | // Fundamental / Standard Library types supported by std::format _and_ QDebug; 76 | // should all go through std::format 77 | QCOMPARE(std::format("{}", 42), "42"); 78 | QCOMPARE(std::format("{}", 10.5), "10.5"); 79 | QCOMPARE(std::format("{}", "hello"), "hello"); 80 | QCOMPARE(std::format("{}", (const char *)"hello"), "hello"); 81 | 82 | QCOMPARE(std::format("{}", "hello"s), "hello"); 83 | QCOMPARE(std::format("{}", "hello"sv), "hello"); 84 | 85 | // Qt types 86 | QCOMPARE(std::format("{}", QStringLiteral("hello")), "hello"); 87 | QCOMPARE(std::format("{}", QByteArray("hello")), "hello"); 88 | QCOMPARE(std::format("{}", QDateTime(QDate(2000, 2, 29), QTime(12, 12, 12), Qt::UTC)), 89 | "QDateTime(2000-02-29 12:12:12.000 UTC Qt::UTC)"); 90 | QCOMPARE(std::format("{}", QUuid()), "QUuid({00000000-0000-0000-0000-000000000000})"); 91 | QCOMPARE(std::format("{}", QRect()), "QRect(0,0 0x0)"); 92 | QCOMPARE(std::format("{}", QSizeF()), "QSizeF(-1, -1)"); 93 | 94 | // Q_FLAG / Q_ENUM 95 | QCOMPARE(std::format("{}", Qt::Alignment(Qt::AlignLeft | Qt::AlignVCenter)), 96 | "QFlags(AlignLeading|AlignVCenter)"); 97 | QCOMPARE(std::format("{}", Qt::AlignLeft), "Qt::AlignLeft"); 98 | 99 | // User defined types 100 | QCOMPARE(std::format("{}", CustomClass()), "CustomClass()"); 101 | QCOMPARE(std::format("{}", BothStdAndQDebugClass()), "BothStdAndQDebugClass via std"); 102 | } 103 | 104 | static_assert(Qt_fmt::detail::IsFormattableViaQDebug); 105 | static_assert(Qt_fmt::detail::IsFormattableViaQDebug); 106 | static_assert(Qt_fmt::detail::IsFormattableViaQDebug); 107 | static_assert(Qt_fmt::detail::IsFormattableViaQDebug); 108 | static_assert(Qt_fmt::detail::IsFormattableViaQDebug); 109 | 110 | QTEST_APPLESS_MAIN(QtStdFormatTest) 111 | 112 | #include "tst_qt_std_format.moc" 113 | -------------------------------------------------------------------------------- /qt/qt_hasher/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(test) 8 | -------------------------------------------------------------------------------- /qt/qt_hasher/README.md: -------------------------------------------------------------------------------- 1 | # QtHasher 2 | 3 | A hasher to be able to use Qt datatypes in the unordered associative containers from 4 | the Standard Library. 5 | 6 | ```cpp 7 | std::unordered_map> map; 8 | 9 | map.insert(~~~); 10 | ``` 11 | 12 | By default, types such as `std::unordered_map` or `std::unordered_set` require 13 | a specialization of `std::hash` for the key type. The problem is that such a 14 | specialization is not actually provided by most Qt datatypes -- only for string-like 15 | and byte arrays (QString, QStringView, QByteArray, etc.), and only starting 16 | from Qt 5.14. 17 | 18 | On the other hand, most Qt value types do have a hashing function -- `qHash()` -- 19 | already defined for them. We can use that one to implement a custom hasher. 20 | 21 | Note that one is not authorized to specialize customization points for types 22 | not under their direct control. Case in point, one is not authorized to specialize 23 | `std::hash` for Qt datatypes: Qt reserves the right to add specializations at 24 | any time (breaking our code and violating ODR). Hence, we define a custom 25 | hasher. 26 | 27 | Requires a C++11 capable compiler. 28 | -------------------------------------------------------------------------------- /qt/qt_hasher/qt_hasher.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Giuseppe D'Angelo 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #ifndef KDTOOLBOX_QT_HASHER_H 11 | #define KDTOOLBOX_QT_HASHER_H 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | namespace KDToolBox 19 | { 20 | 21 | namespace detail 22 | { 23 | 24 | QT_USE_NAMESPACE 25 | 26 | template 27 | using void_t = void; 28 | 29 | using QHashSeedType = decltype(qHash(0)); // which is also the return type of qHash 30 | 31 | template 32 | class QtHasherBase 33 | { 34 | // poison 35 | private: 36 | ~QtHasherBase(); 37 | QtHasherBase(QtHasherBase &&); 38 | }; 39 | 40 | template 41 | class QtHasherBase()))>> 42 | { 43 | public: 44 | using result_type = std::size_t; 45 | using argument_type = T; 46 | 47 | constexpr std::size_t operator()(const T &t) const noexcept 48 | { 49 | // this seeds qHash with the result of 50 | // std::hash applied to an int, to reap 51 | // any protection against predictable hash 52 | // values the std implementation may provide 53 | return static_cast(qHash(t, static_cast(std::hash{}(0)))); 54 | } 55 | 56 | protected: 57 | // prevent accidental slicing, or compilers complaining about a non-virtual dtor 58 | QtHasherBase() = default; 59 | ~QtHasherBase() = default; 60 | QtHasherBase(const QtHasherBase &) = default; 61 | QtHasherBase(QtHasherBase &&) = default; 62 | QtHasherBase &operator=(const QtHasherBase &) = default; 63 | QtHasherBase &operator=(QtHasherBase &&) = default; 64 | }; 65 | 66 | } // namespace detail 67 | 68 | template 69 | struct QtHasher : detail::QtHasherBase 70 | { 71 | }; 72 | 73 | } // namespace KDToolBox 74 | 75 | #endif // KDTOOLBOX_QT_HASHER_H 76 | -------------------------------------------------------------------------------- /qt/qt_hasher/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Test 14 | Gui 15 | ) 16 | 17 | set(CMAKE_CXX_STANDARD 11) 18 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 19 | 20 | set(tst_qt_hasher_SOURCES ../qt_hasher.h tst_qt_hasher.cpp tst_qt_hasher.h) 21 | 22 | add_executable(tst_qt_hasher ${tst_qt_hasher_SOURCES}) 23 | target_link_libraries(tst_qt_hasher PUBLIC Qt::Core Qt::Test Qt::Gui) 24 | -------------------------------------------------------------------------------- /qt/qt_hasher/test/tst_qt_hasher.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | class tst_QtHasher : public QObject 14 | { 15 | Q_OBJECT 16 | public: 17 | using QObject::QObject; 18 | 19 | private Q_SLOTS: 20 | void hash(); 21 | void poison(); 22 | }; 23 | -------------------------------------------------------------------------------- /qt/singleshot_connect/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(test) 8 | -------------------------------------------------------------------------------- /qt/singleshot_connect/README.md: -------------------------------------------------------------------------------- 1 | # singleshot-connect 2 | 3 | An helper for QObject::connect() that connects a signal to a slot (or to a function object), 4 | but creates a single shot connection: the connection gets automatically broken after 5 | the first activation. 6 | 7 | ```cpp 8 | KDToolBox::connectSingleShot(sender, &Sender::signal, receiver, &Receiver::slot); 9 | 10 | sender->causeSignalEmission(); // calls the slot, and breaks the connection 11 | sender->causeSignalEmission(); // does NOT call the slot 12 | 13 | ``` 14 | 15 | Requires a C++14 capable compiler. 16 | -------------------------------------------------------------------------------- /qt/singleshot_connect/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_STANDARD 17) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | set(tst_singleshot_connect_SOURCES ../singleshot_connect.h tst_singleshot_connect.cpp) 13 | 14 | add_executable(tst_singleshot_connect ${tst_singleshot_connect_SOURCES}) 15 | target_link_libraries(tst_singleshot_connect PUBLIC Qt::Core Qt::Test) 16 | -------------------------------------------------------------------------------- /qt/stringtokenizer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(tests) 8 | -------------------------------------------------------------------------------- /qt/stringtokenizer/README.md: -------------------------------------------------------------------------------- 1 | # QStringTokenizer 2 | 3 | QStringTokenizer is a universal, safe, zero-allocation string splitter. 4 | 5 | See qstringtokenizer.cpp for more. 6 | -------------------------------------------------------------------------------- /qt/stringtokenizer/include/QStringTokenizer: -------------------------------------------------------------------------------- 1 | #include "qstringtokenizer.h" 2 | -------------------------------------------------------------------------------- /qt/stringtokenizer/include/QtCore/QStringTokenizer: -------------------------------------------------------------------------------- 1 | #include "../qstringtokenizer.h" 2 | -------------------------------------------------------------------------------- /qt/stringtokenizer/include/QtCore/qstringtokenizer.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | 6 | SPDX-License-Identifier: MIT 7 | */ 8 | 9 | #include "../qstringtokenizer.h" 10 | -------------------------------------------------------------------------------- /qt/stringtokenizer/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(qstringtokenizer) 8 | -------------------------------------------------------------------------------- /qt/stringtokenizer/tests/qstringtokenizer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(cxx11) 8 | add_subdirectory(cxx14) 9 | add_subdirectory(cxx17) 10 | 11 | if(KDTOOLBOX_CXX20) 12 | add_subdirectory(cxx20) 13 | endif() 14 | -------------------------------------------------------------------------------- /qt/stringtokenizer/tests/qstringtokenizer/cxx11/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_STANDARD 11) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | include_directories(../../../include) 13 | 14 | set(tst_qstringtokenizer_cxx11_SOURCES ../tst_qstringtokenizer.cpp) 15 | 16 | add_executable(tst_qstringtokenizer_cxx11 ${tst_qstringtokenizer_cxx11_SOURCES}) 17 | target_link_libraries(tst_qstringtokenizer_cxx11 PUBLIC Qt::Core Qt::Test) 18 | -------------------------------------------------------------------------------- /qt/stringtokenizer/tests/qstringtokenizer/cxx14/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_STANDARD 14) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | include_directories(../../../include) 13 | 14 | set(tst_qstringtokenizer_cxx14_SOURCES ../tst_qstringtokenizer.cpp) 15 | 16 | add_executable(tst_qstringtokenizer_cxx14 ${tst_qstringtokenizer_cxx14_SOURCES}) 17 | target_link_libraries(tst_qstringtokenizer_cxx14 PUBLIC Qt::Core Qt::Test) 18 | -------------------------------------------------------------------------------- /qt/stringtokenizer/tests/qstringtokenizer/cxx17/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_STANDARD 17) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | include_directories(../../../include) 13 | 14 | set(tst_qstringtokenizer_cxx17_SOURCES ../tst_qstringtokenizer.cpp) 15 | 16 | add_executable(tst_qstringtokenizer_cxx17 ${tst_qstringtokenizer_cxx17_SOURCES}) 17 | target_link_libraries(tst_qstringtokenizer_cxx17 PUBLIC Qt::Core Qt::Test) 18 | -------------------------------------------------------------------------------- /qt/stringtokenizer/tests/qstringtokenizer/cxx20/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Test) 8 | 9 | set(CMAKE_CXX_STANDARD 20) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | include_directories(../../../include) 13 | 14 | set(tst_qstringtokenizer_cxx20_SOURCES ../tst_qstringtokenizer.cpp) 15 | 16 | add_executable(tst_qstringtokenizer_cxx20 ${tst_qstringtokenizer_cxx20_SOURCES}) 17 | target_link_libraries(tst_qstringtokenizer_cxx20 PUBLIC Qt::Core Qt::Test) 18 | -------------------------------------------------------------------------------- /qt/tabWindow/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(example) 8 | -------------------------------------------------------------------------------- /qt/tabWindow/README.md: -------------------------------------------------------------------------------- 1 | # TabWindow 2 | 3 | The `TabWindow` widget is a specific widget working like a tab widget, but where tabs could be 4 | extracted and become a new window, or be re-added later on to the main application. This is the 5 | kind of behavior you see in modern browsers. 6 | 7 | Note that behind the scene, the `TabWindow` is using a singleton manager, so it's not possible to 8 | have multiple `TabWindow` in the same application. 9 | 10 | ## Example Usage 11 | 12 | This widget is working like the `QTabWidget`, here is a small example: 13 | 14 | ```cpp 15 | TabWindow w; 16 | w.addTab(new QWidget, "Tab 1"); 17 | w.addTab(new QWidget, "Tab 2"); 18 | w.addTab(new QWidget, "Tab 3"); 19 | w.addTab(new QWidget, "Tab 4"); 20 | ``` 21 | -------------------------------------------------------------------------------- /qt/tabWindow/example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Gui 14 | Widgets 15 | ) 16 | 17 | set(tabwindow_SOURCES ../src/tabwindow.cpp ../src/tabwindow.h main.cpp) 18 | 19 | add_executable(tabwindow ${tabwindow_SOURCES}) 20 | target_link_libraries(tabwindow PUBLIC Qt::Core Qt::Gui Qt::Widgets) 21 | -------------------------------------------------------------------------------- /qt/tabWindow/example/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Nicolas Arnaud-Cormos 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #include "../src/tabwindow.h" 11 | #include 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | QApplication a(argc, argv); 16 | 17 | TabWindow w; 18 | w.addTab(new QWidget, QStringLiteral("Tab 1")); 19 | w.addTab(new QWidget, QStringLiteral("Tab 2")); 20 | w.addTab(new QWidget, QStringLiteral("Tab 3")); 21 | w.addTab(new QWidget, QStringLiteral("Tab 4")); 22 | w.show(); 23 | 24 | return a.exec(); 25 | } 26 | -------------------------------------------------------------------------------- /qt/tabWindow/src/tabwindow.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Nicolas Arnaud-Cormos 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #ifndef TABWINDOW_H 11 | #define TABWINDOW_H 12 | 13 | #include 14 | #include 15 | 16 | QT_BEGIN_NAMESPACE 17 | class QWindow; 18 | QT_END_NAMESPACE 19 | class TabWindow; 20 | 21 | class TabWindowManager : public QObject 22 | { 23 | Q_OBJECT 24 | 25 | public: 26 | static TabWindowManager *instance(); 27 | 28 | QList windows() const; 29 | 30 | TabWindow *currentWindow() const; 31 | QWidget *currentWidget() const; 32 | 33 | Q_SIGNALS: 34 | void tabCloseRequested(QWidget *widget, TabWindow *window); 35 | 36 | protected: 37 | void addWindow(TabWindow *window); 38 | void removeWindow(TabWindow *window); 39 | 40 | TabWindow *possibleWindow(TabWindow *currentWindow, QPoint globalPos = QCursor::pos()); 41 | 42 | private: 43 | void activateWindow(QWindow *window); 44 | void requestCloseTab(int index); 45 | 46 | private: 47 | using QObject::QObject; 48 | TabWindowManager(); 49 | 50 | friend class TabWindow; 51 | QList m_windows; 52 | }; 53 | 54 | class TabWindow : public QTabWidget 55 | { 56 | Q_OBJECT 57 | public: 58 | explicit TabWindow(QWidget *parent = 0); 59 | ~TabWindow(); 60 | 61 | bool eventFilter(QObject *object, QEvent *event) override; 62 | 63 | private: 64 | bool m_isMoving = false; 65 | bool m_ignoreMouseEvent = false; 66 | TabWindow *m_movingWindow = nullptr; 67 | QPoint m_mouseDelta; 68 | }; 69 | 70 | #endif // TABWINDOW_H 71 | -------------------------------------------------------------------------------- /qt/ui_watchdog/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | add_subdirectory(example) 8 | -------------------------------------------------------------------------------- /qt/ui_watchdog/README.md: -------------------------------------------------------------------------------- 1 | # UI Watchdog 2 | 3 | Header-only tool that monitors the main thread and breaks the program whenever 4 | its event loop hasn't run for `MAX_TIME_BLOCKED` (default 300ms). 5 | 6 | Currently it only breaks the program when running on Windows, but actions to perform 7 | can be customized next to the "Add custom action here" comment. 8 | 9 | ## Example Usage 10 | 11 | ```cpp 12 | #include "uiwatchdog.h" 13 | 14 | (...) 15 | 16 | UiWatchdog dog; 17 | dog.start(); 18 | 19 | return app.exec(); 20 | ``` 21 | -------------------------------------------------------------------------------- /qt/ui_watchdog/example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of KDToolBox. 2 | # 3 | # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | find_package( 8 | Qt${QT_VERSION_MAJOR} 9 | ${QT_REQUIRED_VERSION} 10 | CONFIG 11 | REQUIRED 12 | Core 13 | Gui 14 | Widgets 15 | ) 16 | 17 | include_directories(..) 18 | 19 | set(ui_watchdog_example_SOURCES ../uiwatchdog.h main.cpp) 20 | 21 | add_executable(ui_watchdog_example ${ui_watchdog_example_SOURCES}) 22 | target_link_libraries(ui_watchdog_example PUBLIC Qt::Core Qt::Gui Qt::Widgets) 23 | -------------------------------------------------------------------------------- /qt/ui_watchdog/example/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of KDToolBox. 3 | 4 | SPDX-FileCopyrightText: 2017 Klarälvdalens Datakonsult AB, a KDAB Group company 5 | Author: Sérgio Martins 6 | 7 | SPDX-License-Identifier: MIT 8 | */ 9 | 10 | #include "uiwatchdog.h" 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | int main(int argc, char **argv) 17 | { 18 | QApplication app(argc, argv); 19 | 20 | QWidget w; 21 | auto layout = new QVBoxLayout(&w); 22 | auto button = new QPushButton(QStringLiteral("Block UI completely")); 23 | auto button2 = new QPushButton(QStringLiteral("Sleep every other second")); 24 | auto button3 = new QPushButton(QStringLiteral("Cancel sleep")); 25 | layout->addWidget(button); 26 | layout->addWidget(button2); 27 | layout->addStretch(); 28 | layout->addWidget(button3); 29 | 30 | QObject::connect(button, &QPushButton::clicked, [] { 31 | qDebug() << "Blocking forever!"; 32 | while (true) 33 | ; 34 | }); 35 | 36 | auto sleepTimer = new QTimer(&w); 37 | sleepTimer->setInterval(1000); 38 | QObject::connect(sleepTimer, &QTimer::timeout, [] { 39 | qDebug() << "Sleeping"; 40 | QThread::sleep(1); 41 | qDebug() << "Waking up"; 42 | }); 43 | 44 | QObject::connect(button2, &QPushButton::clicked, sleepTimer, QOverload<>::of(&QTimer::start)); 45 | QObject::connect(button3, &QPushButton::clicked, sleepTimer, &QTimer::stop); 46 | 47 | UiWatchdog dog; 48 | dog.start(); 49 | w.resize(800, 800); 50 | w.show(); 51 | 52 | return app.exec(); 53 | } 54 | -------------------------------------------------------------------------------- /squish/tests.json: -------------------------------------------------------------------------------- 1 | { 2 | "tests": [ 3 | { 4 | "suite": "mysuite1", 5 | "name": "tst_myfeature1" 6 | }, 7 | { 8 | "suite": "mysuite1", 9 | "name": "tst_myfeature2" 10 | }, 11 | { 12 | "suite": "mysuite1", 13 | "name": "tst_myfeature3", 14 | "supports_offscreen": false 15 | }, 16 | { 17 | "suite": "mysuite1", 18 | "name": "tst_myfeature4", 19 | "disabled": false 20 | }, 21 | { 22 | "suite": "mysuite1", 23 | "name": "tst_myfeature5", 24 | "failure_expected": true 25 | }, 26 | { 27 | "suite": "mysuite1", 28 | "name": "tst_myfeature6", 29 | "failure_expected": "linux" 30 | }, 31 | { 32 | "suite": "mysuite1", 33 | "name": "tst_myfeature6", 34 | "supports_offscreen": [ 35 | "macos", 36 | "windows" 37 | ] 38 | }, 39 | { 40 | "suite": "mysuite1", 41 | "name": "tst_myfeature7", 42 | "categories": [ 43 | "mycategory" 44 | ] 45 | } 46 | ] 47 | } 48 | --------------------------------------------------------------------------------