├── .github └── workflows │ └── rust.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── graph │ ├── Cargo.toml │ ├── build.rs │ ├── shaders │ │ ├── line.fsh │ │ ├── line.vsh │ │ ├── noisy.fsh │ │ └── noisy.vsh │ └── src │ │ ├── gridnode.cpp │ │ ├── gridnode.h │ │ ├── linenode.cpp │ │ ├── linenode.h │ │ ├── main.qml │ │ ├── main.rs │ │ ├── nodes.rs │ │ ├── noisynode.cpp │ │ └── noisynode.h ├── kefia │ ├── 0001-Fix-compile-with-stable.patch │ ├── 0002-Port-to-qmetaobject-rs.patch │ └── README ├── nested_qobjects │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── qenum │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── qmlextensionplugins │ ├── Cargo.toml │ ├── README │ └── src │ │ └── lib.rs ├── rqbg │ ├── 0001-Port-to-qmetaobject-rs.patch │ ├── 0002-Fix-build.patch │ ├── README.md │ └── mailmodel.patch ├── todos │ ├── Cargo.toml │ ├── README │ ├── main.qml │ └── src │ │ ├── implementation.rs │ │ └── main.rs └── webengine │ ├── Cargo.toml │ ├── build.rs │ ├── index.html │ ├── main.qml │ └── src │ └── main.rs ├── qmetaobject ├── Cargo.toml ├── build.rs ├── qmetaobject_rust.hpp ├── src │ ├── connections.rs │ ├── future.rs │ ├── itemmodel.rs │ ├── lib.rs │ ├── listmodel.rs │ ├── log.rs │ ├── qmetatype.rs │ ├── qrc.rs │ ├── qtcore │ │ ├── core_application.rs │ │ └── mod.rs │ ├── qtdeclarative.rs │ ├── qtquickcontrols2.rs │ ├── scenegraph.rs │ ├── tablemodel.rs │ └── webengine.rs └── tests │ ├── common │ └── mod.rs │ ├── models.rs │ ├── qml │ ├── Bar.qml │ └── main.qml │ └── tests.rs ├── qmetaobject_impl ├── Cargo.toml └── src │ ├── lib.rs │ ├── qbjs.rs │ ├── qobject_impl.rs │ ├── qrc_impl.rs │ └── simplelistitem_impl.rs ├── qttypes ├── Cargo.toml ├── README.md ├── build.rs └── src │ ├── lib.rs │ ├── qtcore │ ├── mod.rs │ ├── primitives.rs │ ├── qbytearray.rs │ ├── qchar.rs │ ├── qlist │ │ ├── common.rs │ │ ├── mod.rs │ │ ├── qstringlist.rs │ │ └── qvariantlist.rs │ ├── qsettings.rs │ ├── qstring.rs │ ├── qurl.rs │ └── qvariant.rs │ └── qtgui │ ├── mod.rs │ └── qcolor.rs └── rustfmt.toml /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | workflow_dispatch: 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | build: 15 | env: 16 | QT_QPA_PLATFORM: offscreen 17 | strategy: 18 | matrix: 19 | os: [ubuntu-latest, windows-latest] 20 | qt: [5.15.2, 5.9.9, 5.12.9] 21 | rust: [stable, nightly] 22 | runs-on: ${{ matrix.os }} 23 | steps: 24 | - uses: actions/checkout@v4 25 | - uses: dtolnay/rust-toolchain@stable 26 | with: 27 | toolchain: ${{ matrix.rust }} 28 | - uses: Swatinem/rust-cache@v2 29 | with: 30 | key: rust_cache 31 | 32 | - name: Install Qt 33 | uses: jurplel/install-qt-action@v4 34 | with: 35 | version: ${{ matrix.qt }} 36 | cache: true 37 | modules: 'qtwebengine' 38 | - name: Build 39 | run: cargo build --all-features 40 | - name: Run tests 41 | run: cargo test --all-features 42 | shell: bash 43 | 44 | 45 | # Qt6 is in a different job right now because it does not have many modules and most example don't compile 46 | qt6: 47 | strategy: 48 | matrix: 49 | os: [ubuntu-latest, windows-latest] 50 | ver: [6.2.0, 6.5.0] 51 | env: 52 | QT_QPA_PLATFORM: offscreen 53 | runs-on: ${{ matrix.os }} 54 | steps: 55 | - uses: actions/checkout@v4 56 | - uses: dtolnay/rust-toolchain@stable 57 | - uses: Swatinem/rust-cache@v2 58 | with: 59 | key: rust_cache6 60 | 61 | - name: Install Qt 62 | uses: jurplel/install-qt-action@v4 63 | with: 64 | version: ${{ matrix.ver }} 65 | cache: true 66 | - name: Test 67 | run: | 68 | cargo test --manifest-path qttypes/Cargo.toml 69 | cargo test --manifest-path qmetaobject_impl/Cargo.toml 70 | cargo test --manifest-path qmetaobject/Cargo.toml 71 | 72 | 73 | no_qt: 74 | strategy: 75 | matrix: 76 | os: [ubuntu-latest, windows-latest] 77 | runs-on: ${{ matrix.os }} 78 | steps: 79 | - uses: actions/checkout@v4 80 | - uses: dtolnay/rust-toolchain@stable 81 | - uses: Swatinem/rust-cache@v2 82 | with: 83 | key: rust_cache 84 | - name: Build qttypes without qt 85 | run: | 86 | cargo build --manifest-path qttypes/Cargo.toml --no-default-features 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). 6 | 7 | ## [Unreleased] 8 | 9 | ## 0.2.12 2024-10-22 (qttype only) 10 | 11 | - Prefer Qt6 over Qt5 if there is a `qmake6` binary in PATH 12 | - `impl From for QVariant` 13 | - Add QVariant::to_qvariantmap wrapper 14 | - Add QVariant::to_qstring wrapper 15 | - Add QVariant::type_name wrapper 16 | - Add Debug for QVariantList 17 | - Add QVariant::toInt wrapper 18 | - Fix Qt linkage on macOS when Qt was configured with -no-framework 19 | 20 | ## 0.2.11 2023-11-03 (qttype only) 21 | 22 | - reenable `cargo:rustc-cdylib-link-arg=-Wl,-rpath,` command even if it is depracated as it broke people's build 23 | 24 | ## 0.2.10 2023-11-03 25 | 26 | - qttypes: detect MSVC and MinGW incompatibilities 27 | - qttypes: Added wrapper around QSettings 28 | - Added QmlEngine::invoke_method_noreturn 29 | - Added QmlEngine::trim_component_cache and QmlEngine::clear_component_cache 30 | 31 | ## 0.2.9 2023-06-15 32 | 33 | - Implement QMetaType for QStringList and QVariantMap 34 | - Added QGlobalColor 35 | - implemented Add and AddAsing for QString 36 | - Added QVariant::is_null 37 | - Expose begin_move_rows and end_move_rows 38 | - Fixed UTF8 in QJSonObject 39 | - Fixed compulation with Qt 6.5 40 | 41 | ## 0.2.8 2022-02-25 (qttype only) 42 | 43 | - Fix qttype crate always being rebuilt (#252) 44 | 45 | ## 0.2.7 2022-02-23 46 | 47 | - Expand the API of QStringList, QString 48 | - Expand the API of QJSValue 49 | - Added QVariantMap 50 | - Add wrapper around qmlRegisterModule 51 | - Fix compilation with Qt 6.3 and MSVC 52 | - qttypes: Expose the flags through a COMPILE_FLAGS env variable 53 | 54 | 55 | ## 0.2.6 2021-11-19 (qttype only) 56 | 57 | - Fix build when Qt is not found and the required feature is off 58 | 59 | ## 0.2.5 2021-11-19 60 | 61 | - Completed QColor API 62 | - Added wrapper around QJSon* types, QPainter, QPen, QBrush, QLineF 63 | - Added QQuickPaintedItem 64 | - Fixes to the qttype build script 65 | 66 | ## 0.2.4 2021-09-30 67 | 68 | - Fixed build with Qt < 5.8 and >= 6.2 69 | 70 | ## 0.2.3 2021-08-11 71 | 72 | ### Added 73 | - Tutorial on adding Rust wrappers #171. 74 | - QCoreApplication: wrappers around public static getters & setters #185. 75 | 76 | ### Deprecated 77 | - Rename QObjectDescription in favor of its new name RustQObjectDescriptor #172. 78 | 79 | ### Removed 80 | - No longer set QT_SELECT environment variable when running qmake #173. 81 | 82 | ### Fixed 83 | - Build scripts improvements #174, d7c6587. 84 | - Symbol required for the QEnum macro are in the prelude 85 | 86 | ## 0.2.2 - 2021-06-28 87 | 88 | - Added QVariant conversion from QObjectPinned 89 | - Added release_resources to QQuickItem 90 | - Fix non-MSVC Windows build and cross compilation 91 | - Fixed SimpleListItem when not QVariant or QByteArray are not in scope 92 | 93 | ## 0.2.1 - 2021-05-22 94 | 95 | - Qt6 support 96 | - allow to select qt with env variables QT_INCLUDE_PATH and QT_LIBRARY_PATH 97 | - Added more features to link to more modules 98 | - Added a prelude 99 | - Set the rpath on linux 100 | 101 | ## 0.2.0 - 2021-04-21 102 | 103 | 104 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | 'qmetaobject', 4 | 'qmetaobject_impl', 5 | 'qttypes', 6 | 7 | 'examples/graph', 8 | 'examples/qmlextensionplugins', 9 | 'examples/todos', 10 | 'examples/webengine', 11 | 'examples/qenum', 12 | 'examples/nested_qobjects', 13 | ] 14 | 15 | resolver = "2" 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Olivier Goffart 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /examples/graph/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "graph" 3 | version = "0.1.0" 4 | edition = "2018" 5 | authors = ["Olivier Goffart "] 6 | build = "build.rs" 7 | 8 | [dependencies] 9 | qmetaobject = { path = "../../qmetaobject" } 10 | qttypes = { path = "../../qttypes" } # required to get the DEP_QT_* env variables 11 | cstr = "0.2" 12 | cpp = "0.5.6" 13 | 14 | [build-dependencies] 15 | cpp_build = "0.5.6" 16 | semver = "1" 17 | -------------------------------------------------------------------------------- /examples/graph/build.rs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Olivier Goffart 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 4 | associated documentation files (the "Software"), to deal in the Software without restriction, 5 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 6 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 7 | subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial 10 | portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 13 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 15 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | use semver::Version; 20 | 21 | fn main() { 22 | let qt_include_path = std::env::var("DEP_QT_INCLUDE_PATH").unwrap(); 23 | let qt_version = std::env::var("DEP_QT_VERSION") 24 | .unwrap() 25 | .parse::() 26 | .expect("Parsing Qt version failed"); 27 | 28 | if qt_version >= Version::new(6, 0, 0) { 29 | // This example is not supported on Qt 6 and above because graphics 30 | // API used for it were removed. 31 | println!("cargo:rustc-cfg=no_qt"); 32 | return; 33 | } 34 | 35 | #[allow(unused_mut)] 36 | let mut config = cpp_build::Config::new(); 37 | 38 | for f in std::env::var("DEP_QT_COMPILE_FLAGS").unwrap().split_terminator(";") { 39 | config.flag(f); 40 | } 41 | 42 | config 43 | .include(&qt_include_path) 44 | .include(format!("{}/QtQuick", qt_include_path)) 45 | .include(format!("{}/QtCore", qt_include_path)) 46 | // See https://github.com/woboq/qmetaobject-rs/pull/168 47 | // 48 | // QSGSimpleMaterial{,Shader} classes ain't going to be removed from Qt5 49 | // which is on a life support at this point; and we know for sure they are 50 | // already gone in Qt6. So, there's just no point seeing these warning 51 | // over and over again. 52 | .flag_if_supported("-Wno-deprecated-declarations") 53 | .build("src/main.rs"); 54 | } 55 | -------------------------------------------------------------------------------- /examples/graph/shaders/line.fsh: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | uniform lowp vec4 color; 52 | uniform lowp float qt_Opacity; 53 | uniform lowp float spread; 54 | 55 | varying lowp float vT; 56 | 57 | #define PI 3.14159265358979323846 58 | 59 | void main(void) 60 | { 61 | lowp float tt = smoothstep(spread, 1.0, sin(vT * PI)); 62 | 63 | gl_FragColor = color * qt_Opacity * tt; 64 | } 65 | -------------------------------------------------------------------------------- /examples/graph/shaders/line.vsh: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | attribute highp vec4 pos; 52 | attribute highp float t; 53 | 54 | uniform lowp float size; 55 | uniform highp mat4 qt_Matrix; 56 | 57 | varying lowp float vT; 58 | 59 | void main(void) 60 | { 61 | vec4 adjustedPos = pos; 62 | adjustedPos.y += (t * size); 63 | gl_Position = qt_Matrix * adjustedPos; 64 | 65 | vT = t; 66 | } 67 | -------------------------------------------------------------------------------- /examples/graph/shaders/noisy.fsh: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | uniform sampler2D texture; 52 | uniform lowp float qt_Opacity; 53 | uniform lowp vec4 color; 54 | 55 | varying highp vec2 vTexCoord; 56 | varying lowp vec2 vShadeCoord; 57 | 58 | #define PI 3.14159265358979323846 59 | 60 | void main() 61 | { 62 | lowp float shade = texture2D(texture, vTexCoord).r * 0.05 - length(vec2(0.5, 0.4) - vShadeCoord) * 0.3; 63 | lowp vec4 c = vec4(color.xyz + shade, color.w); 64 | gl_FragColor = c * qt_Opacity; 65 | } 66 | -------------------------------------------------------------------------------- /examples/graph/shaders/noisy.vsh: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | attribute highp vec4 aVertex; 52 | attribute highp vec2 aTexCoord; 53 | 54 | uniform highp mat4 qt_Matrix; 55 | uniform highp vec2 textureSize; 56 | 57 | varying highp vec2 vTexCoord; 58 | varying lowp vec2 vShadeCoord; 59 | 60 | void main() { 61 | gl_Position = qt_Matrix * aVertex; 62 | vTexCoord = aVertex.xy * textureSize; 63 | vShadeCoord = aTexCoord; 64 | } 65 | -------------------------------------------------------------------------------- /examples/graph/src/gridnode.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #include "gridnode.h" 52 | 53 | #include 54 | 55 | #define GRID_SIZE 32 56 | 57 | GridNode::GridNode() 58 | : m_geometry(QSGGeometry::defaultAttributes_Point2D(), 0) 59 | { 60 | setGeometry(&m_geometry); 61 | m_geometry.setDrawingMode(GL_LINES); 62 | 63 | setMaterial(&m_material); 64 | m_material.setColor(Qt::gray); 65 | } 66 | 67 | /* 68 | * The function hardcodes a fixed set of grid lines and scales 69 | * those to the bounding rect. 70 | */ 71 | void GridNode::setRect(const QRectF &rect) 72 | { 73 | int vCount = int((rect.width() - 1) / GRID_SIZE); 74 | int hCount = int((rect.height() - 1) / GRID_SIZE); 75 | 76 | int lineCount = vCount + hCount; 77 | 78 | QSGGeometry *g = geometry(); 79 | 80 | g->allocate(lineCount * 2); 81 | 82 | float x = rect.x(); 83 | float y = rect.y(); 84 | float w = rect.width(); 85 | float h = rect.height(); 86 | 87 | QSGGeometry::Point2D *v = g->vertexDataAsPoint2D(); 88 | 89 | // Then write the vertical lines 90 | for (int i=0; i 55 | #include 56 | 57 | class GridNode : public QSGGeometryNode 58 | { 59 | public: 60 | GridNode(); 61 | 62 | void setRect(const QRectF &rect); 63 | 64 | private: 65 | QSGFlatColorMaterial m_material; 66 | QSGGeometry m_geometry; 67 | }; 68 | 69 | #endif // GRIDNODE_H 70 | -------------------------------------------------------------------------------- /examples/graph/src/linenode.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #include "linenode.h" 52 | 53 | #include 54 | 55 | #include 56 | 57 | struct LineMaterial 58 | { 59 | QColor color; 60 | float spread; 61 | float size; 62 | }; 63 | 64 | class LineShader : public QSGSimpleMaterialShader 65 | { 66 | QSG_DECLARE_SIMPLE_SHADER(LineShader, LineMaterial) 67 | 68 | public: 69 | LineShader() : id_color(-1), id_spread(-1), id_size(-1) { 70 | setShaderSourceFile(QOpenGLShader::Vertex, ":/scenegraph/graph/shaders/line.vsh"); 71 | setShaderSourceFile(QOpenGLShader::Fragment, ":/scenegraph/graph/shaders/line.fsh"); 72 | } 73 | 74 | QList attributes() const { return QList() << "pos" << "t"; } 75 | 76 | void updateState(const LineMaterial *m, const LineMaterial *) { 77 | program()->setUniformValue(id_color, m->color); 78 | program()->setUniformValue(id_spread, m->spread); 79 | program()->setUniformValue(id_size, m->size); 80 | } 81 | 82 | void resolveUniforms() { 83 | id_spread = program()->uniformLocation("spread"); 84 | id_size = program()->uniformLocation("size"); 85 | id_color = program()->uniformLocation("color"); 86 | } 87 | 88 | private: 89 | int id_color; 90 | int id_spread; 91 | int id_size; 92 | }; 93 | 94 | struct LineVertex { 95 | float x; 96 | float y; 97 | float t; 98 | inline void set(float xx, float yy, float tt) { x = xx; y = yy; t = tt; } 99 | }; 100 | 101 | static const QSGGeometry::AttributeSet &attributes() 102 | { 103 | static QSGGeometry::Attribute attr[] = { 104 | QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), 105 | QSGGeometry::Attribute::create(1, 1, GL_FLOAT) 106 | }; 107 | static QSGGeometry::AttributeSet set = { 2, 3 * sizeof(float), attr }; 108 | return set; 109 | } 110 | 111 | LineNode::LineNode(float size, float spread, const QColor &color) 112 | : m_geometry(attributes(), 0) 113 | { 114 | setGeometry(&m_geometry); 115 | m_geometry.setDrawingMode(GL_TRIANGLE_STRIP); 116 | 117 | QSGSimpleMaterial *m = LineShader::createMaterial(); 118 | m->state()->color = color; 119 | m->state()->size = size; 120 | m->state()->spread = spread; 121 | m->setFlag(QSGMaterial::Blending); 122 | setMaterial(m); 123 | setFlag(OwnsMaterial); 124 | } 125 | 126 | /* 127 | * Assumes that samples have values in the range of 0 to 1 and scales them to 128 | * the height of bounds. The samples are stretched out horizontally along the 129 | * width of the bounds. 130 | * 131 | * The position of each pair of points is identical, but we use the third value 132 | * "t" to shift the point up or down and to add antialiasing. 133 | */ 134 | void LineNode::updateGeometry(const QRectF &bounds, const QList &samples) 135 | { 136 | m_geometry.allocate(samples.size() * 2); 137 | 138 | float x = bounds.x(); 139 | float y = bounds.y(); 140 | float w = bounds.width(); 141 | float h = bounds.height(); 142 | 143 | float dx = w / (samples.size() - 1); 144 | 145 | LineVertex *v = (LineVertex *) m_geometry.vertexData(); 146 | for (int i=0; i 55 | 56 | class LineNode : public QSGGeometryNode 57 | { 58 | public: 59 | LineNode(float size, float spread, const QColor &color); 60 | 61 | void updateGeometry(const QRectF &bounds, const QList &samples); 62 | 63 | private: 64 | QSGGeometry m_geometry; 65 | 66 | }; 67 | 68 | #endif // LINENODE_H 69 | -------------------------------------------------------------------------------- /examples/graph/src/main.qml: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | import QtQuick 2.0 52 | import Graph 1.0 53 | 54 | Item { 55 | width: 800 56 | height: 400 57 | 58 | Graph { 59 | id: graph 60 | anchors.fill: parent 61 | anchors.margins: 100 62 | 63 | function newSample(i) { 64 | return (Math.sin(i / 100.0 * Math.PI * 2) + 1) * 0.4 + Math.random() * 0.05; 65 | } 66 | 67 | Component.onCompleted: { 68 | for (let i = 0; i < 100; ++i) { 69 | appendSample(newSample(i)); 70 | } 71 | } 72 | 73 | property int offset: 100 74 | } 75 | 76 | Timer { 77 | id: timer 78 | interval: 500 79 | repeat: true 80 | running: true 81 | onTriggered: { 82 | graph.removeFirstSample(); 83 | graph.appendSample(graph.newSample(++graph.offset)); 84 | } 85 | } 86 | 87 | Rectangle { 88 | anchors.fill: graph 89 | color: "transparent" 90 | border.color: "black" 91 | border.width: 2 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /examples/graph/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | #![allow(unused_variables)] 3 | 4 | use cstr::cstr; 5 | 6 | #[cfg(not(no_qt))] 7 | use cpp::cpp; 8 | 9 | #[cfg(no_qt)] 10 | mod no_qt { 11 | pub fn panic() -> T { 12 | panic!("This example is not supported on Qt 6 and above") 13 | } 14 | } 15 | 16 | #[cfg(no_qt)] 17 | macro_rules! cpp { 18 | {{ $($t:tt)* }} => {}; 19 | {$(unsafe)? [$($a:tt)*] -> $ret:ty as $b:tt { $($t:tt)* } } => { 20 | crate::no_qt::panic::<$ret>() 21 | }; 22 | { $($t:tt)* } => { 23 | crate::no_qt::panic::<()>() 24 | }; 25 | } 26 | 27 | use qmetaobject::prelude::*; 28 | use qmetaobject::scenegraph::*; 29 | 30 | mod nodes; 31 | 32 | cpp! {{ 33 | #include 34 | }} 35 | 36 | #[derive(Default, QObject)] 37 | struct Graph { 38 | base: qt_base_class!(trait QQuickItem), 39 | 40 | m_samples: Vec, 41 | m_samplesChanged: bool, 42 | m_geometryChanged: bool, 43 | 44 | appendSample: qt_method!(fn(&mut self, value: f64)), 45 | removeFirstSample: qt_method!( 46 | fn removeFirstSample(&mut self) { 47 | self.m_samples.drain(0..1); 48 | self.m_samplesChanged = true; 49 | (self as &dyn QQuickItem).update(); 50 | } 51 | ), 52 | } 53 | 54 | // Example of adding an enum wrapper. 55 | 56 | /// Wrapper for [`QQuickItem::Flag`][enum] enum. 57 | /// 58 | /// [enum]: https://doc.qt.io/qt-5/qquickitem.html#Flag-enum 59 | #[allow(unused)] 60 | #[repr(C)] 61 | enum QQuickItemFlag { 62 | ItemClipsChildrenToShape = 0x01, 63 | ItemAcceptsInputMethod = 0x02, 64 | ItemIsFocusScope = 0x04, 65 | ItemHasContents = 0x08, 66 | ItemAcceptsDrops = 0x10, 67 | } 68 | 69 | impl Graph { 70 | // Example of adding a method wrapper with wrapper-specific notice. 71 | 72 | /// Wrapper for [`QQuickItem::setFlag(QQuickItem::Flag flag, bool enabled = true)`][method] method. 73 | /// 74 | /// # Wrapper-specific behavior 75 | /// 76 | /// The `enabled` argument is always set to true. 77 | /// 78 | /// [method]: https://doc.qt.io/qt-5/qquickitem.html#setFlag 79 | fn set_flag(&mut self, flag: QQuickItemFlag) { 80 | let obj = self.get_cpp_object(); 81 | assert!(!obj.is_null()); 82 | cpp!(unsafe [obj as "QQuickItem *", flag as "QQuickItem::Flag"] { 83 | obj->setFlag(flag); 84 | }); 85 | } 86 | 87 | fn appendSample(&mut self, value: f64) { 88 | self.m_samples.push(value); 89 | self.m_samplesChanged = true; 90 | // FIXME! find a better way maybe 91 | self.set_flag(QQuickItemFlag::ItemHasContents); 92 | (self as &dyn QQuickItem).update(); 93 | } 94 | } 95 | 96 | impl QQuickItem for Graph { 97 | fn geometry_changed(&mut self, new_geometry: QRectF, old_geometry: QRectF) { 98 | self.m_geometryChanged = true; 99 | (self as &dyn QQuickItem).update(); 100 | } 101 | 102 | fn update_paint_node(&mut self, mut node: SGNode) -> SGNode { 103 | let rect = (self as &dyn QQuickItem).bounding_rect(); 104 | 105 | node.update_static(( 106 | |mut n| -> SGNode { 107 | nodes::create_noisy_node(&mut n, self); 108 | if self.m_geometryChanged { 109 | nodes::noisy_node_set_rect(&mut n, rect); 110 | } 111 | n 112 | }, 113 | |mut n| -> SGNode { 114 | if self.m_geometryChanged { 115 | nodes::update_grid_node(&mut n, rect); 116 | } 117 | n 118 | }, 119 | |mut n| { 120 | if self.m_geometryChanged || self.m_samplesChanged { 121 | nodes::create_line_node(&mut n, 10., 0.5, QColor::from_name("steelblue")); 122 | nodes::update_line_node(&mut n, rect, &self.m_samples); 123 | } 124 | n 125 | }, 126 | |mut n| { 127 | if self.m_geometryChanged || self.m_samplesChanged { 128 | nodes::create_line_node( 129 | &mut n, 130 | 20., 131 | 0.2, 132 | QColor::from_rgba_f(0.2, 0.2, 0.2, 0.4), 133 | ); 134 | // Fixme! share the geometry 135 | nodes::update_line_node(&mut n, rect, &self.m_samples); 136 | } 137 | n 138 | }, 139 | )); 140 | 141 | self.m_geometryChanged = false; 142 | self.m_samplesChanged = false; 143 | node 144 | } 145 | } 146 | 147 | fn main() { 148 | nodes::init_resources(); 149 | qml_register_type::(cstr!("Graph"), 1, 0, cstr!("Graph")); 150 | let mut view = QQuickView::new(); 151 | view.set_source("qrc:/qml/main.qml".into()); 152 | view.show(); 153 | view.engine().exec(); 154 | } 155 | -------------------------------------------------------------------------------- /examples/graph/src/nodes.rs: -------------------------------------------------------------------------------- 1 | use qmetaobject::scenegraph::SGNode; 2 | use qmetaobject::{qrc, QQuickItem}; 3 | use qttypes::{QColor, QRectF}; 4 | 5 | #[cfg(not(no_qt))] 6 | use cpp::cpp; 7 | 8 | qrc! { 9 | pub init_resources, 10 | "scenegraph/graph" { 11 | "shaders/noisy.vsh", 12 | "shaders/noisy.fsh", 13 | "shaders/line.vsh", 14 | "shaders/line.fsh", 15 | }, 16 | "src" as "qml" { 17 | "main.qml" 18 | } 19 | } 20 | 21 | // Ideally, everything should be possible to do in plain rust. 22 | // However, there is quite some API to expose. 23 | 24 | cpp! {{ 25 | #include 26 | 27 | #include "src/gridnode.cpp" 28 | #include "src/linenode.cpp" 29 | #include "src/noisynode.cpp" 30 | }} 31 | 32 | pub enum NoisyNode {} 33 | 34 | pub fn create_noisy_node(s: &mut SGNode, ctx: &dyn QQuickItem) { 35 | let item_ptr = ctx.get_cpp_object(); 36 | cpp!(unsafe [s as "NoisyNode**", item_ptr as "QQuickItem*"] { 37 | if (!*s && item_ptr) { 38 | *s = new NoisyNode(item_ptr->window()); 39 | } 40 | }); 41 | } 42 | 43 | pub fn noisy_node_set_rect(s: &mut SGNode, rect: QRectF) { 44 | cpp!(unsafe [s as "NoisyNode**", rect as "QRectF"] { 45 | if (*s) { 46 | (*s)->setRect(rect); 47 | } 48 | }); 49 | } 50 | 51 | pub enum GridNode {} 52 | pub fn update_grid_node(s: &mut SGNode, rect: QRectF) { 53 | cpp!(unsafe [s as "GridNode**", rect as "QRectF"] { 54 | if (!*s) *s = new GridNode; 55 | (*s)->setRect(rect); 56 | }); 57 | } 58 | 59 | pub enum LineNode {} 60 | pub fn create_line_node(s: &mut SGNode, size: f32, spread: f32, color: QColor) { 61 | cpp!(unsafe [s as "LineNode**", size as "float", spread as "float", color as "QColor"] { 62 | if (!*s) *s = new LineNode(size, spread, color); 63 | }); 64 | } 65 | 66 | pub fn update_line_node(s: &mut SGNode, rect: QRectF, samples: &[f64]) { 67 | let samples_ptr = samples.as_ptr(); 68 | let samples_len = samples.len(); 69 | cpp!(unsafe [s as "LineNode**", rect as "QRectF", samples_ptr as "double*", 70 | samples_len as "std::size_t"] { 71 | if (!*s) return; 72 | QList samples; 73 | samples.reserve(samples_len); 74 | std::copy(samples_ptr, samples_ptr + samples_len, std::back_inserter(samples)); 75 | (*s)->updateGeometry(rect, samples); 76 | }); 77 | } 78 | -------------------------------------------------------------------------------- /examples/graph/src/noisynode.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #include "noisynode.h" 52 | 53 | #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) 54 | #include 55 | #endif 56 | #include 57 | #include 58 | #include 59 | 60 | #define NOISE_SIZE 64 61 | 62 | struct NoisyMaterial 63 | { 64 | ~NoisyMaterial() { 65 | delete texture; 66 | } 67 | 68 | QColor color; 69 | QSGTexture *texture; 70 | }; 71 | 72 | class NoisyShader : public QSGSimpleMaterialShader 73 | { 74 | QSG_DECLARE_SIMPLE_SHADER(NoisyShader, NoisyMaterial) 75 | 76 | public: 77 | NoisyShader() : id_color(-1), id_texture(-1), id_textureSize(-1) { 78 | setShaderSourceFile(QOpenGLShader::Vertex, ":/scenegraph/graph/shaders/noisy.vsh"); 79 | setShaderSourceFile(QOpenGLShader::Fragment, ":/scenegraph/graph/shaders/noisy.fsh"); 80 | } 81 | 82 | QList attributes() const { return QList() << "aVertex" << "aTexCoord"; } 83 | 84 | void updateState(const NoisyMaterial *m, const NoisyMaterial *) { 85 | 86 | // Set the color 87 | program()->setUniformValue(id_color, m->color); 88 | 89 | // Bind the texture and set program to use texture unit 0 (the default) 90 | m->texture->bind(); 91 | 92 | // Then set the texture size so we can adjust the texture coordinates accordingly in the 93 | // vertex shader.. 94 | QSize s = m->texture->textureSize(); 95 | program()->setUniformValue(id_textureSize, QSizeF(1.0 / s.width(), 1.0 / s.height())); 96 | } 97 | 98 | void resolveUniforms() { 99 | id_texture = program()->uniformLocation("texture"); 100 | id_textureSize = program()->uniformLocation("textureSize"); 101 | id_color = program()->uniformLocation("color"); 102 | 103 | // We will only use texture unit 0, so set it only once. 104 | program()->setUniformValue(id_texture, 0); 105 | } 106 | 107 | private: 108 | int id_color; 109 | int id_texture; 110 | int id_textureSize; 111 | }; 112 | 113 | NoisyNode::NoisyNode(QQuickWindow *window) 114 | { 115 | // Make some noise... 116 | QImage image(NOISE_SIZE, NOISE_SIZE, QImage::Format_RGB32); 117 | uint *data = (uint *) image.bits(); 118 | for (int i=0; i= QT_VERSION_CHECK(5, 10, 0) 120 | uint g = QRandomGenerator::global()->bounded(0xff); 121 | #else 122 | uint g = qrand() % 0xff; 123 | #endif 124 | data[i] = 0xff000000 | (g << 16) | (g << 8) | g; 125 | } 126 | 127 | QSGTexture *t = window->createTextureFromImage(image); 128 | t->setFiltering(QSGTexture::Nearest); 129 | t->setHorizontalWrapMode(QSGTexture::Repeat); 130 | t->setVerticalWrapMode(QSGTexture::Repeat); 131 | 132 | QSGSimpleMaterial *m = NoisyShader::createMaterial(); 133 | m->state()->texture = t; 134 | m->state()->color = QColor::fromRgbF(0.95, 0.95, 0.97); 135 | m->setFlag(QSGMaterial::Blending); 136 | 137 | setMaterial(m); 138 | setFlag(OwnsMaterial, true); 139 | 140 | QSGGeometry *g = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4); 141 | QSGGeometry::updateTexturedRectGeometry(g, QRect(), QRect()); 142 | setGeometry(g); 143 | setFlag(OwnsGeometry, true); 144 | } 145 | 146 | void NoisyNode::setRect(const QRectF &bounds) 147 | { 148 | QSGGeometry::updateTexturedRectGeometry(geometry(), bounds, QRectF(0, 0, 1, 1)); 149 | markDirty(QSGNode::DirtyGeometry); 150 | } 151 | -------------------------------------------------------------------------------- /examples/graph/src/noisynode.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #ifndef NOISYNODE_H 52 | #define NOISYNODE_H 53 | 54 | #include 55 | #include 56 | 57 | class NoisyNode : public QSGGeometryNode 58 | { 59 | public: 60 | NoisyNode(QQuickWindow *window); 61 | 62 | void setRect(const QRectF &bounds); 63 | }; 64 | 65 | #endif // NOISYNODE_H 66 | -------------------------------------------------------------------------------- /examples/kefia/0001-Fix-compile-with-stable.patch: -------------------------------------------------------------------------------- 1 | From 8356d25ae66ebe130a73e9782a9e3134b94a293d Mon Sep 17 00:00:00 2001 2 | From: Olivier Goffart 3 | Date: Wed, 28 Feb 2018 17:57:59 +0100 4 | Subject: [PATCH 1/2] Fix compile with stable 5 | 6 | --- 7 | Cargo.lock | 18 +++++++++--------- 8 | src/main.rs | 1 - 9 | src/view.rs | 10 +++++----- 10 | 3 files changed, 14 insertions(+), 15 deletions(-) 11 | 12 | diff --git a/Cargo.lock b/Cargo.lock 13 | index 4cbcbc1..49cdf47 100644 14 | --- a/Cargo.lock 15 | +++ b/Cargo.lock 16 | @@ -1,12 +1,3 @@ 17 | -[root] 18 | -name = "kefia" 19 | -version = "0.1.0" 20 | -dependencies = [ 21 | - "lazysort 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 22 | - "qml 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 23 | - "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 24 | -] 25 | - 26 | [[package]] 27 | name = "aho-corasick" 28 | version = "0.6.1" 29 | @@ -15,6 +6,15 @@ dependencies = [ 30 | "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 31 | ] 32 | 33 | +[[package]] 34 | +name = "kefia" 35 | +version = "0.1.0" 36 | +dependencies = [ 37 | + "lazysort 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 38 | + "qml 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 39 | + "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 40 | +] 41 | + 42 | [[package]] 43 | name = "kernel32-sys" 44 | version = "0.2.2" 45 | diff --git a/src/main.rs b/src/main.rs 46 | index 68c3013..d540594 100644 47 | --- a/src/main.rs 48 | +++ b/src/main.rs 49 | @@ -1,4 +1,3 @@ 50 | -#![feature(box_syntax)] 51 | extern crate regex; 52 | #[macro_use] 53 | extern crate qml; 54 | diff --git a/src/view.rs b/src/view.rs 55 | index 3b80ca6..162d6ab 100644 56 | --- a/src/view.rs 57 | +++ b/src/view.rs 58 | @@ -126,14 +126,14 @@ impl QPackages { 59 | let data = { 60 | let s = &self; 61 | let closure: Box bool> = match (self.chosen_repo, self.chosen_group) { 62 | - (-1, -1) => box |_| true, 63 | - (-1, group) => box move |pkg| pkg.meta.contains(&s.groups[group as usize]), 64 | - (repo, -1) => box move |pkg| pkg.group == s.repos[repo as usize], 65 | + (-1, -1) => Box::new( |_| true ), 66 | + (-1, group) => Box::new( move |pkg| pkg.meta.contains(&s.groups[group as usize])), 67 | + (repo, -1) => Box::new( move |pkg| pkg.group == s.repos[repo as usize]), 68 | (repo, group) => { 69 | - box move |pkg| { 70 | + Box::new( move |pkg| { 71 | pkg.group == s.repos[repo as usize] && 72 | pkg.meta.contains(&s.groups[group as usize]) 73 | - } 74 | + }) 75 | } 76 | }; 77 | let selected = Some(&self.selected); 78 | -- 79 | 2.17.1 80 | 81 | -------------------------------------------------------------------------------- /examples/kefia/0002-Port-to-qmetaobject-rs.patch: -------------------------------------------------------------------------------- 1 | From 5c2c980b83a571523206e494313b8c6d6b306aaf Mon Sep 17 00:00:00 2001 2 | From: Olivier Goffart 3 | Date: Mon, 4 Jun 2018 18:00:30 +0200 4 | Subject: [PATCH 2/2] Port to qmetaobject-rs 5 | 6 | --- 7 | Cargo.toml | 2 +- 8 | src/main.rs | 2 +- 9 | src/view.qml | 2 +- 10 | src/view.rs | 163 +++++++++++++++++++++++---------------------------- 11 | 4 files changed, 77 insertions(+), 92 deletions(-) 12 | 13 | diff --git a/Cargo.toml b/Cargo.toml 14 | index 63740d2..7a03132 100644 15 | --- a/Cargo.toml 16 | +++ b/Cargo.toml 17 | @@ -15,4 +15,4 @@ license = "MIT" 18 | [dependencies] 19 | regex = "0.2.1" 20 | lazysort = "0.1.1" 21 | -qml = "0.0.9" 22 | +qmetaobject = { path = "../qmetaobject-rs/qmetaobject/"} 23 | diff --git a/src/main.rs b/src/main.rs 24 | index d540594..e32654f 100644 25 | --- a/src/main.rs 26 | +++ b/src/main.rs 27 | @@ -1,7 +1,7 @@ 28 | extern crate regex; 29 | #[macro_use] 30 | -extern crate qml; 31 | extern crate lazysort; 32 | +extern crate qmetaobject; 33 | 34 | use lazysort::*; 35 | 36 | diff --git a/src/view.qml b/src/view.qml 37 | index 94202a4..74be2e6 100644 38 | --- a/src/view.qml 39 | +++ b/src/view.qml 40 | @@ -121,7 +121,7 @@ ApplicationWindow { 41 | Layout.row: 2 42 | ListView { 43 | id: mainList 44 | - model: packages 45 | + model: qpkgs.list 46 | delegate: 47 | Rectangle { 48 | width: mainList.width 49 | diff --git a/src/view.rs b/src/view.rs 50 | index 162d6ab..c0ad025 100644 51 | --- a/src/view.rs 52 | +++ b/src/view.rs 53 | @@ -1,14 +1,15 @@ 54 | -use qml::*; 55 | +use qmetaobject; 56 | +use qmetaobject::*; 57 | use lazysort::*; 58 | +use std; 59 | 60 | use super::Package; 61 | 62 | pub fn show(gathered: Vec) { 63 | let mut engine = QmlEngine::new(); 64 | - let list = form_list(&gathered); 65 | - let qvar: QVariant = list.get_qvar(); 66 | + let list = std::cell::RefCell::new(form_list(&gathered)); 67 | 68 | - let mut repos = gathered.iter().map(|p| p.group.clone()).collect::>(); 69 | + let mut repos = gathered.iter().map(|p| p.group.clone().into()).collect::>(); 70 | repos.dedup(); 71 | let mut groups = gathered.iter() 72 | .flat_map(|p| p.meta.iter()) 73 | @@ -16,18 +17,18 @@ pub fn show(gathered: Vec) { 74 | .cloned() 75 | .collect::>(); 76 | groups.dedup(); 77 | - let qrepos = repos.iter().map(|s| s.clone().into()).collect::>(); 78 | + let qrepos = repos.iter().map(|s| QString::from(&**s)).collect::(); 79 | let qgroups = groups.iter() 80 | .map(|s| { 81 | if s == "" { 82 | - "(no group)".into() 83 | + QString::from("(no group)") 84 | } else { 85 | - s.as_str().into() 86 | + QString::from(&**s) 87 | } 88 | }) 89 | - .collect::>(); 90 | + .collect::(); 91 | 92 | - let qpckgs = QPackages::new(Packages { 93 | + let qpckgs = QObjectBox::new(Packages { 94 | vec: gathered, 95 | list: list, 96 | repos: repos, 97 | @@ -35,42 +36,31 @@ pub fn show(gathered: Vec) { 98 | chosen_repo: -1, 99 | chosen_group: -1, 100 | selected: SelectedPackages::new(), 101 | + ..Default::default() 102 | }); 103 | - engine.set_property("packages", &qvar); 104 | - engine.set_and_store_property("qpkgs", qpckgs.get_qobj()); 105 | - engine.set_and_store_property("repos", qrepos); 106 | - engine.set_and_store_property("groups", qgroups); 107 | - engine.load_data(include_str!("view.qml")); 108 | + engine.set_object_property("qpkgs".into(), qpckgs.pinned()); 109 | + engine.set_property("repos".into(), qrepos.into()); 110 | + engine.set_property("groups".into(), qgroups.into()); 111 | + engine.load_data(include_str!("view.qml").into()); 112 | 113 | engine.exec(); 114 | } 115 | 116 | -Q_LISTMODEL!{ 117 | - pub QPackageList { 118 | - name: String, 119 | - version: String, 120 | - repo: String, 121 | - group: String, 122 | - selected: bool 123 | - } 124 | +#[derive(Default, Clone, SimpleListItem)] 125 | +pub struct QPackage { 126 | + pub name: QString, 127 | + pub version: QString, 128 | + pub repo: QString, 129 | + pub group: QString, 130 | + pub selected: bool 131 | } 132 | 133 | -fn form_list(gathered: &[Package]) -> QPackageList { 134 | - let mut qalm = QPackageList::new(); 135 | - qalm.set_data(filter_for_qml(gathered, &|_| true, None)); 136 | - qalm 137 | +fn form_list(gathered: &[Package]) -> qmetaobject::listmodel::SimpleListModel { 138 | + filter_for_qml(gathered, &|_| true, None).iter().collect() 139 | } 140 | 141 | -pub struct Packages { 142 | - vec: Vec, 143 | - list: QPackageList, 144 | - repos: Vec, 145 | - groups: Vec, 146 | - chosen_repo: i32, 147 | - chosen_group: i32, 148 | - selected: SelectedPackages, 149 | -} 150 | 151 | +#[derive(Default)] 152 | pub struct SelectedPackages { 153 | vec: Vec, 154 | } 155 | @@ -78,7 +68,7 @@ pub struct SelectedPackages { 156 | fn filter_for_qml(vec: &[Package], 157 | filter: &Fn(&&Package) -> bool, 158 | selecteds: Option<&SelectedPackages>) 159 | - -> Vec<(String, String, String, String, bool)> { 160 | + -> Vec { 161 | vec.into_iter() 162 | .filter(filter) 163 | .map(|pkg| { 164 | @@ -91,36 +81,60 @@ fn filter_for_qml(vec: &[Package], 165 | } else { 166 | false 167 | }; 168 | - (pkg.name.clone(), pkg.version.clone(), pkg.group.clone(), meta, selected) 169 | + QPackage { 170 | + name: (&*pkg.name).into(), 171 | + version: (&*pkg.version).into(), 172 | + repo: (&*pkg.group).into(), // !!sic 173 | + group: (&*meta).into(), // !! sic 174 | + selected: selected 175 | + } 176 | }) 177 | .collect() 178 | } 179 | 180 | -Q_OBJECT!( 181 | - pub Packages as QPackages { 182 | - signals: 183 | - fn notify_packages_changed(text: String); 184 | - slots: 185 | - fn request_update_repo(r: i32); 186 | - fn request_update_group(r: i32); 187 | - fn add_package(i: i32); 188 | - fn remove_package(i: i32); 189 | - properties: 190 | - } 191 | -); 192 | +#[derive(Default, QObject)] 193 | +pub struct Packages { 194 | + vec: Vec, 195 | + repos: Vec, 196 | + groups: Vec, 197 | + chosen_repo: i32, 198 | + chosen_group: i32, 199 | + selected: SelectedPackages, 200 | 201 | -impl QPackages { 202 | - fn request_update_repo(&mut self, r: i32) -> Option<&QVariant> { 203 | + 204 | + base: qt_base_class!(trait QObject), 205 | + list: qt_property!(std::cell::RefCell>; CONST), 206 | + notify_packages_changed: qt_signal!(text: QString), 207 | + request_update_repo: qt_method!( fn request_update_repo(&mut self, r: i32) { 208 | self.chosen_repo = r; 209 | self.decide_and_update(); 210 | - None 211 | - } 212 | - 213 | - fn request_update_group(&mut self, r: i32) -> Option<&QVariant> { 214 | + }), 215 | + request_update_group: qt_method!(fn request_update_group(&mut self, r: i32) { 216 | self.chosen_group = r; 217 | self.decide_and_update(); 218 | - None 219 | - } 220 | + }), 221 | + add_package: qt_method!(fn add_package(&mut self, index: i32) { 222 | + let mut pkg_in_list = self.list.borrow()[index as usize].clone(); 223 | + let pkg = self.vec.iter().find(|pkg| pkg_in_list.name.to_string() == pkg.name).unwrap().clone(); 224 | + self.selected.add_package(pkg); 225 | + pkg_in_list.selected = true; 226 | + self.list.borrow_mut().change_line(index as usize, pkg_in_list); 227 | + let c = self.selected.get_text(); 228 | + self.notify_packages_changed(c); 229 | + }), 230 | + remove_package: qt_method!(fn remove_package(&mut self, index: i32) { 231 | + let mut pkg_in_list = self.list.borrow()[index as usize].clone(); 232 | + let pkg = self.vec.iter().find(|pkg| pkg_in_list.name.to_string() == pkg.name).unwrap().clone(); 233 | + self.selected.remove_package(pkg); 234 | + pkg_in_list.selected = false; 235 | + self.list.borrow_mut().change_line(index as usize, pkg_in_list); 236 | + let c = self.selected.get_text(); 237 | + self.notify_packages_changed(c); 238 | + }), 239 | + 240 | +} 241 | + 242 | +impl Packages { 243 | 244 | fn decide_and_update(&mut self) { 245 | let data = { 246 | @@ -139,38 +153,9 @@ impl QPackages { 247 | let selected = Some(&self.selected); 248 | filter_for_qml(&self.vec, closure.as_ref(), selected) 249 | }; 250 | - self.list.set_data(data); 251 | - } 252 | - 253 | - fn add_package(&mut self, index: i32) -> Option<&QVariant> { 254 | - let pkg_in_list = &self.list.view_data()[index as usize]; 255 | - self.list.change_line(index as usize, 256 | - pkg_in_list.0.clone(), 257 | - pkg_in_list.1.clone(), 258 | - pkg_in_list.2.clone(), 259 | - pkg_in_list.3.clone(), 260 | - true); 261 | - let pkg_name = &pkg_in_list.0; 262 | - let pkg = self.vec.iter().find(|pkg| pkg_name == &pkg.name).unwrap().clone(); 263 | - self.selected.add_package(pkg); 264 | - self.notify_packages_changed(self.selected.get_text()); 265 | - None 266 | + self.list.borrow_mut().reset_data(data); 267 | } 268 | 269 | - fn remove_package(&mut self, index: i32) -> Option<&QVariant> { 270 | - let pkg_in_list = &self.list.view_data()[index as usize]; 271 | - self.list.change_line(index as usize, 272 | - pkg_in_list.0.clone(), 273 | - pkg_in_list.1.clone(), 274 | - pkg_in_list.2.clone(), 275 | - pkg_in_list.3.clone(), 276 | - false); 277 | - let pkg_name = &pkg_in_list.0; 278 | - let pkg = self.vec.iter().find(|pkg| pkg_name == &pkg.name).unwrap().clone(); 279 | - self.selected.remove_package(pkg); 280 | - self.notify_packages_changed(self.selected.get_text()); 281 | - None 282 | - } 283 | } 284 | 285 | impl SelectedPackages { 286 | @@ -186,7 +171,7 @@ impl SelectedPackages { 287 | self.vec.retain(|p| p.name != package.name); 288 | } 289 | 290 | - fn get_text(&self) -> String { 291 | - self.vec.iter().map(|p| &p.name as &str).collect::>().join(" ") 292 | + fn get_text(&self) -> QString { 293 | + self.vec.iter().map(|p| &p.name as &str).collect::>().join(" ").into() 294 | } 295 | } 296 | -- 297 | 2.21.0 298 | 299 | -------------------------------------------------------------------------------- /examples/kefia/README: -------------------------------------------------------------------------------- 1 | Here is patches to port the kefia examples from qml-rust to the qmetaobject crate. 2 | 3 | To try this example, go to the parent repository of qmetaobject-rs, and run 4 | 5 | git clone https://github.com/White-Oak/kefia 6 | cd kefia 7 | git am ../qmetaobject-rs/examples/kefia/*.patch 8 | cargo run 9 | 10 | (The patches apply to commit ec8b9ed82446b79f1eece70860cc1995c2d43558) 11 | -------------------------------------------------------------------------------- /examples/nested_qobjects/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nested_qobjects" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | qmetaobject = { path = "../../qmetaobject" } 10 | cstr = "0.2" -------------------------------------------------------------------------------- /examples/nested_qobjects/README.md: -------------------------------------------------------------------------------- 1 | # Nested QObjects 2 | 3 | This example illustrate how to put a `QObject` inside another `QObject` using `RefCell`. 4 | -------------------------------------------------------------------------------- /examples/nested_qobjects/src/main.rs: -------------------------------------------------------------------------------- 1 | use cstr::cstr; 2 | use qmetaobject::{ 3 | qml_register_type, qt_base_class, qt_method, qt_property, qt_signal, QObject, QString, 4 | QmlEngine, 5 | }; 6 | use std::cell::RefCell; 7 | 8 | // Here we define a custom QObject Person with a property and two methods. 9 | #[derive(QObject, Default)] 10 | struct Person { 11 | base: qt_base_class!(trait QObject), 12 | name: qt_property!(QString; NOTIFY name_changed), 13 | name_changed: qt_signal!(), 14 | } 15 | 16 | impl Person { 17 | fn set_name(&mut self, name: String) { 18 | self.name = name.into(); 19 | self.name_changed(); 20 | } 21 | 22 | fn get_name(&self) -> String { 23 | self.name.to_string() 24 | } 25 | } 26 | 27 | // Now we want to use the Person as a property of another QObject. 28 | #[derive(QObject, Default)] 29 | struct Greeter { 30 | base: qt_base_class!(trait QObject), 31 | 32 | // To store our Person QObject as a property of another QObject, we need to use a RefCell. 33 | person: qt_property!(RefCell; NOTIFY person_changed), 34 | person_changed: qt_signal!(), 35 | 36 | compute_greetings: qt_method!( 37 | fn compute_greetings(&self, verb: String) -> QString { 38 | // To access the person, we need to borrow it. 39 | format!("{} {}", verb, self.person.borrow().get_name()).into() 40 | } 41 | ), 42 | set_person_name: qt_method!( 43 | fn set_person_name(&mut self, name: String) { 44 | // To modify the nested object we need to borrow it as mutable 45 | println!("Person name set to {}", &name); 46 | self.person.borrow_mut().set_name(name); 47 | self.person_changed(); 48 | } 49 | ), 50 | } 51 | 52 | fn main() { 53 | // We need to register our two custom QObjects with the QML engine. 54 | qml_register_type::(cstr!("Greeter"), 1, 0, cstr!("Greeter")); 55 | qml_register_type::(cstr!("Person"), 1, 0, cstr!("Person")); 56 | 57 | let mut engine = QmlEngine::new(); 58 | engine.load_data( 59 | r#" 60 | import QtQuick 2.6 61 | import QtQuick.Window 2.0 62 | import Greeter 1.0 63 | 64 | Window { 65 | visible: true 66 | Greeter { 67 | id: greeter; 68 | // Here we can directly set the person name inside the Greeter's Person property 69 | person.name: "World" 70 | // or we can use the set_person_name method to set the name 71 | //Component.onCompleted : { 72 | // greeter.set_person_name("foo"); 73 | //} 74 | } 75 | Text { 76 | id: txt 77 | anchors.centerIn: parent 78 | text: greeter.compute_greetings("hello") 79 | 80 | // When the person's name changes, we update the text 81 | Connections { 82 | target: greeter 83 | function onPersonChanged() { 84 | txt.text = greeter.compute_greetings("hello") 85 | } 86 | } 87 | } 88 | 89 | 90 | } 91 | "# 92 | .into(), 93 | ); 94 | engine.exec(); 95 | } 96 | -------------------------------------------------------------------------------- /examples/qenum/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "qenum" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [dependencies] 7 | qmetaobject = { path = "../../qmetaobject" } 8 | cstr = "0.2" -------------------------------------------------------------------------------- /examples/qenum/src/main.rs: -------------------------------------------------------------------------------- 1 | use cstr::cstr; 2 | 3 | use qmetaobject::prelude::*; 4 | 5 | #[derive(Copy, Clone, Debug, Eq, PartialEq, QEnum)] 6 | #[repr(C)] 7 | enum Options { 8 | Foo = 1, 9 | Bar = 2, 10 | Quaz = 3, 11 | } 12 | 13 | fn main() { 14 | qml_register_enum::(cstr!("RustCode"), 1, 0, cstr!("Options")); 15 | let mut engine = QmlEngine::new(); 16 | engine.load_data( 17 | r#" 18 | import QtQuick 2.6 19 | import QtQuick.Window 2.0 20 | // Import our Rust classes 21 | import RustCode 1.0 22 | 23 | Window { 24 | visible: true 25 | Text { 26 | anchors.centerIn: parent 27 | text: `Hello! Bar is ${Options.Bar}, Foo is ${Options.Foo}.` 28 | } 29 | } 30 | "# 31 | .into(), 32 | ); 33 | engine.exec(); 34 | } 35 | -------------------------------------------------------------------------------- /examples/qmlextensionplugins/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "qmlextensionplugins" 3 | version = "0.1.0" 4 | edition = "2018" 5 | authors = ["Olivier Goffart "] 6 | 7 | [lib] 8 | name = "qmlqtimeexampleplugin" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | qmetaobject = { path = "../../qmetaobject" } 13 | chrono = "^0.4" 14 | cstr = "0.2" 15 | -------------------------------------------------------------------------------- /examples/qmlextensionplugins/README: -------------------------------------------------------------------------------- 1 | This example shows how you can use plugins with qml builder. 2 | It is based on the `qmlextensionplugins` example found in the qtdeclarative 3 | source code. It needs the QML files from that example in the Qt source tree. 4 | 5 | Build with: 6 | 7 | cargo build 8 | 9 | 10 | Run with: 11 | 12 | qmlscene -P ./target/debug/ \ 13 | -I $QTSRCDIR/qtdeclarative/examples/qml/qmlextensionplugins/imports \ 14 | $QTSRCDIR/qtdeclarative/examples/qml/qmlextensionplugins/plugins.qml 15 | 16 | Where $QTSRCDIR is the path to the Qt source 17 | 18 | 19 | -------------------------------------------------------------------------------- /examples/qmlextensionplugins/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::sync::{Arc, Condvar, Mutex}; 3 | use std::thread::JoinHandle; 4 | 5 | use chrono::Timelike; 6 | use cstr::cstr; 7 | 8 | use qmetaobject::prelude::*; 9 | 10 | #[derive(Default)] 11 | struct AbortCondVar { 12 | is_aborted: Mutex, 13 | abort_condvar: Condvar, 14 | } 15 | 16 | #[allow(non_snake_case)] 17 | #[derive(Default, QObject)] 18 | struct TimeModel { 19 | base: qt_base_class!(trait QObject), 20 | hour: qt_property!(u32; NOTIFY timeChanged READ get_hour), 21 | minute: qt_property!(u32; NOTIFY timeChanged READ get_minute), 22 | timeChanged: qt_signal!(), 23 | 24 | thread: Option<(JoinHandle<()>, Arc)>, 25 | } 26 | 27 | impl Drop for TimeModel { 28 | fn drop(&mut self) { 29 | self.thread.as_ref().map(|x| { 30 | let mut lock = x.1.is_aborted.lock().unwrap(); 31 | *lock = true; 32 | x.1.abort_condvar.notify_one(); 33 | }); 34 | } 35 | } 36 | 37 | impl TimeModel { 38 | fn lazy_init(&mut self) { 39 | if self.thread.is_none() { 40 | let ptr = QPointer::from(&*self); 41 | let cb = qmetaobject::queued_callback(move |()| { 42 | ptr.as_ref().map(|x| x.timeChanged()); 43 | }); 44 | let arc = Arc::::new(Default::default()); 45 | let arc2 = arc.clone(); 46 | let thread = std::thread::spawn(move || loop { 47 | let lock = arc2.is_aborted.lock().unwrap(); 48 | if *lock { 49 | break; 50 | } 51 | // We just wait on the condition variable for 1 second to simulate a one second timer 52 | let lock = arc2 53 | .abort_condvar 54 | .wait_timeout(lock, std::time::Duration::from_millis(1000)) 55 | .unwrap() 56 | .0; 57 | std::mem::drop(lock); 58 | cb(()); 59 | }); 60 | self.thread = Some((thread, arc)); 61 | } 62 | } 63 | fn get_hour(&mut self) -> u32 { 64 | self.lazy_init(); 65 | chrono::offset::Local::now().time().hour() 66 | } 67 | fn get_minute(&mut self) -> u32 { 68 | self.lazy_init(); 69 | chrono::offset::Local::now().time().minute() 70 | } 71 | } 72 | 73 | #[derive(Default, QObject)] 74 | struct QExampleQmlPlugin { 75 | base: qt_base_class!(trait QQmlExtensionPlugin), 76 | plugin: qt_plugin!("org.qt-project.Qt.QQmlExtensionInterface/1.0"), 77 | } 78 | 79 | impl QQmlExtensionPlugin for QExampleQmlPlugin { 80 | fn register_types(&mut self, uri: &CStr) { 81 | //assert_eq!(uri, cstr!("TimeExample")); 82 | qml_register_type::(uri, 1, 0, cstr!("Time")); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /examples/rqbg/0002-Fix-build.patch: -------------------------------------------------------------------------------- 1 | From c9b77aa3120c0c8e10701baf125ebd7d3c699f1c Mon Sep 17 00:00:00 2001 2 | From: Olivier Robert 3 | Date: Sun, 22 Nov 2020 14:23:48 +0100 4 | Subject: [PATCH] Fix build 5 | 6 | --- 7 | Cargo.toml | 4 ++-- 8 | src/implementation.rs | 2 +- 9 | 2 files changed, 3 insertions(+), 3 deletions(-) 10 | 11 | diff --git a/Cargo.toml b/Cargo.toml 12 | index 27c07ee..9bd68e6 100644 13 | --- a/Cargo.toml 14 | +++ b/Cargo.toml 15 | @@ -12,8 +12,8 @@ grep-regex = "0.1.1" 16 | grep-searcher = "0.1.1" 17 | htmlescape = "0.3.1" 18 | libc = "0.2" 19 | -spmc = "0.2.2" 20 | +spmc = "0.3.0" 21 | walkdir = "2" 22 | -qmetaobject = "0.0.4" 23 | +qmetaobject = { path = "../qmetaobject-rs/qmetaobject/"} 24 | cstr = "0.1" 25 | 26 | diff --git a/src/implementation.rs b/src/implementation.rs 27 | index 8330769..b6be809 100644 28 | --- a/src/implementation.rs 29 | +++ b/src/implementation.rs 30 | @@ -124,7 +124,7 @@ fn grep( 31 | if query.is_empty() { 32 | return list(&emit, item_sender, active); 33 | } 34 | - let (sender, receiver) = spmc::channel(); 35 | + let (mut sender, receiver) = spmc::channel(); 36 | let mut threads = Vec::new(); 37 | for _ in 0..4 { 38 | let sender = item_sender.clone(); 39 | -- 40 | 2.29.2 41 | 42 | -------------------------------------------------------------------------------- /examples/rqbg/README.md: -------------------------------------------------------------------------------- 1 | ## More port from rust-qt-binding-generator 2 | 3 | This directory contains patches that can be applied to port example 4 | using rust-qt-binding-generator to this crate. 5 | 6 | The goal of the patch is to attempt to show that using this crate is 7 | simpler than using the binding generator. 8 | 9 | ### qrep 10 | 11 | Is the tool presented here: 12 | https://www.vandenoever.info/blog/2018/10/30/building_qt_apps_with_cargo.html 13 | 14 | To apply the patch and run the program: 15 | 16 | ``` 17 | git clone https://invent.kde.org/vandenoever/qrep 18 | cd qrep 19 | git checkout bdbde040e74819351609581c0d98a59bbfeecbf9 -b qmetaobject-rs 20 | git am ../qmetaobject-rs/examples/rqbg/0001-Port-to-qmetaobject-rs.patch 21 | git am ../qmetaobject-rs/examples/rqbg/0002-Fix-build.patch 22 | cargo run 23 | ``` 24 | 25 | The port does the same as the original. 26 | Contrary to the original, there is no need to write a single line of C++. 27 | And even implementations.rs has less lines than before. 28 | 29 | ### mailmodel 30 | 31 | Mailmodel was introduced here 32 | https://www.vandenoever.info/blog/2018/09/16/browsing_your_mail_with_rust_and_qt.html 33 | 34 | To apply the patch and run the program: 35 | 36 | ``` 37 | git clone https://anongit.kde.org/scratch/vandenoever/mailmodel 38 | cd mailmodel 39 | git checkout 87991f1090b57706f5c713c8425684eba144cec2 -b qmetaobject-rs 40 | git am ../qmetaobject-rs/examples/rqbg/mailmodel.patch 41 | cat README.md 42 | # create a configuration file as explained 43 | cargo run config.json 44 | ``` 45 | 46 | Note: If you get compilation error because of openssl, try setting these environment variable: 47 | `OPENSSL_LIB_DIR=/usr/lib/openssl-1.0 OPENSSL_INCLUDE_DIR=/usr/include/openssl-1.0` 48 | 49 | -------------------------------------------------------------------------------- /examples/todos/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "todos" 3 | version = "0.1.0" 4 | edition = "2018" 5 | authors = ["Olivier Goffart "] 6 | 7 | [dependencies] 8 | qmetaobject = { path = "../../qmetaobject" } 9 | cstr = "0.2" 10 | -------------------------------------------------------------------------------- /examples/todos/README: -------------------------------------------------------------------------------- 1 | This example is based on a rust-qt-binding-generator example from Jos van den Oever . 2 | https://github.com/KDE/rust-qt-binding-generator/tree/9822dcef071f8c6d6ec2f6b39bab436613493221/examples/todos 3 | The file main.qml is imported unmodified, and the file implementation.rs was used and adapted to 4 | the qmetaobject crate 5 | -------------------------------------------------------------------------------- /examples/todos/main.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * Based on an example from rust-qt-binding-generator 3 | * Copyright 2017 Jos van den Oever 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation; either version 2 of 8 | * the License or (at your option) version 3 or any later version 9 | * accepted by the membership of KDE e.V. (or its successor approved 10 | * by the membership of KDE e.V.), which shall act as a proxy 11 | * defined in Section 14 of version 3 of the license. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | import QtQuick 2.9 23 | import QtQuick.Controls 2.2 24 | import QtQuick.Layouts 1.3 25 | import RustCode 1.0 26 | 27 | ApplicationWindow { 28 | visible: true 29 | width: 450 30 | height: 580 31 | header: ToolBar { 32 | Label { 33 | anchors.fill: parent 34 | text: qsTr("todos") 35 | font.pixelSize: 30 36 | horizontalAlignment: Text.AlignHCenter 37 | verticalAlignment: Text.AlignVCenter 38 | } 39 | } 40 | 41 | Component.onCompleted: { 42 | input.forceActiveFocus() 43 | } 44 | 45 | Todos { 46 | id: todoModel 47 | 48 | Component.onCompleted: { 49 | add("Learn rust!") 50 | add("Learn qml!") 51 | add("check out the qmetaobject crate") 52 | } 53 | } 54 | 55 | Component { 56 | id: todoDelegate 57 | RowLayout { 58 | // the active tab determines if this item should be shown 59 | // 0: all, 1: active, 2: completed 60 | property bool show: filter.currentIndex === 0 61 | || (filter.currentIndex === 1 && !completed) 62 | || (filter.currentIndex === 2 && completed) 63 | visible: show 64 | width: parent.width 65 | height: show ? implicitHeight : 0 66 | CheckBox { 67 | checked: completed 68 | onToggled: todoModel.setCompleted(index, checked) 69 | } 70 | Item { 71 | Layout.fillWidth: true 72 | Layout.fillHeight: true 73 | Label { 74 | id: label 75 | visible: !editInput.visible 76 | text: description 77 | anchors.fill: parent 78 | verticalAlignment: Text.AlignVCenter 79 | font.strikeout: completed 80 | font.pixelSize: 20 81 | } 82 | MouseArea { 83 | id: mouse 84 | anchors.fill: parent 85 | hoverEnabled: true 86 | onDoubleClicked: { 87 | editInput.text = label.text 88 | editInput.visible = true 89 | editInput.forceActiveFocus() 90 | } 91 | } 92 | Button { 93 | text: 'X' 94 | visible: (mouse.containsMouse && !editInput.visible) 95 | || closeMouse.containsMouse 96 | anchors.right: parent.right 97 | MouseArea { 98 | id: closeMouse 99 | anchors.fill: parent 100 | hoverEnabled: true 101 | onClicked: todoModel.remove(index) 102 | } 103 | } 104 | TextField { 105 | id: editInput 106 | visible: false 107 | anchors.fill: parent 108 | text: description 109 | font.pixelSize: label.font.pixelSize 110 | onAccepted: { 111 | todoModel.setDescription(index, text) 112 | visible = false 113 | } 114 | onActiveFocusChanged: { 115 | // hide when focus is lost 116 | if (!activeFocus) { 117 | visible = false 118 | } 119 | } 120 | Keys.onPressed: { 121 | // on escape, set value, hide (and lose focus) 122 | if (event.key === Qt.Key_Escape) { 123 | todoModel.setDescription(index, text) 124 | visible = false 125 | event.accepted = true 126 | } 127 | } 128 | } 129 | } 130 | } 131 | } 132 | 133 | Pane { 134 | anchors.fill: parent 135 | leftPadding: 0 136 | Page { 137 | anchors.fill: parent 138 | header: RowLayout { 139 | CheckBox { 140 | tristate: true 141 | // if there are no todos, do not show this checkbox 142 | // but let it take up the same space 143 | enabled: todoModel.count > 0 144 | opacity: todoModel.count === 0 ? 0 : 1 145 | checkState: { 146 | if (todoModel.activeCount === 0) { 147 | return Qt.Checked 148 | } else if (todoModel.activeCount >= todoModel.count) { 149 | return Qt.Unchecked 150 | } 151 | return Qt.PartiallyChecked 152 | } 153 | onCheckStateChanged: { 154 | // if the change is triggered by a user action on this 155 | // checkbox, check or uncheck all todos 156 | // otherwise, do nothing 157 | // (onToggle does not emit for tristate buttons) 158 | if (activeFocus) { 159 | var checked = checkState !== Qt.Unchecked 160 | todoModel.setAll(checked) 161 | } 162 | } 163 | } 164 | TextField { 165 | id: input 166 | Layout.fillWidth: true 167 | placeholderText: qsTr("What needs to be done?") 168 | onAccepted: { 169 | const todo = text.trim() 170 | if (todo) { 171 | todoModel.add(todo) 172 | } 173 | input.clear() 174 | } 175 | } 176 | } 177 | Flickable { 178 | anchors.fill: parent 179 | ListView { 180 | anchors.fill: parent 181 | model: todoModel 182 | delegate: todoDelegate 183 | } 184 | } 185 | } 186 | } 187 | 188 | footer: Pane { 189 | padding: 0 190 | ColumnLayout { 191 | width: parent.width 192 | TabBar { 193 | id: filter 194 | Layout.fillWidth: true 195 | visible: todoModel.count > 0 196 | TabButton { 197 | text: qsTr("All") 198 | checked: true 199 | } 200 | TabButton { 201 | text: qsTr("Active") 202 | } 203 | TabButton { 204 | text: qsTr("Completed") 205 | } 206 | } 207 | RowLayout { 208 | visible: todoModel.count > 0 209 | width: parent.width 210 | Label { 211 | Layout.fillWidth: true 212 | text: (todoModel.activeCount === 1) 213 | ? qsTr("1 item left") 214 | : todoModel.activeCount + qsTr(" items left") 215 | } 216 | Button { 217 | enabled: todoModel.count > todoModel.activeCount 218 | opacity: enabled 219 | text: qsTr("Clear completed") 220 | onClicked: todoModel.clearCompleted() 221 | } 222 | } 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /examples/todos/src/implementation.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Based on an example from rust-qt-binding-generator 3 | * Copyright 2017 Jos van den Oever 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation; either version 2 of 8 | * the License or (at your option) version 3 or any later version 9 | * accepted by the membership of KDE e.V. (or its successor approved 10 | * by the membership of KDE e.V.), which shall act as a proxy 11 | * defined in Section 14 of version 3 of the license. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | use std::collections::HashMap; 22 | 23 | use qmetaobject::*; 24 | 25 | #[derive(Default, Clone)] 26 | struct TodosItem { 27 | completed: bool, 28 | description: String, 29 | } 30 | 31 | #[allow(non_snake_case)] 32 | #[derive(Default, QObject)] 33 | pub struct Todos { 34 | base: qt_base_class!(trait QAbstractListModel), 35 | count: qt_property!(i32; READ row_count NOTIFY count_changed), 36 | count_changed: qt_signal!(), 37 | list: Vec, 38 | activeCount: qt_property!(usize; NOTIFY active_count_changed), 39 | active_count_changed: qt_signal!(), 40 | 41 | setCompleted: qt_method!(fn(&mut self, item: usize, v: bool) -> bool), 42 | setDescription: qt_method!(fn(&mut self, item: usize, v: String) -> bool), 43 | insert_rows: qt_method!(fn(&mut self, row: usize, count: usize) -> bool), 44 | remove_rows: qt_method!(fn(&mut self, row: usize, count: usize) -> bool), 45 | clearCompleted: qt_method!(fn(&mut self)), 46 | add: qt_method!(fn(&mut self, description: String)), 47 | remove: qt_method!(fn(&mut self, index: u64) -> bool), 48 | setAll: qt_method!(fn(&mut self, completed: bool)), 49 | } 50 | 51 | impl Todos { 52 | fn update_active_count(&mut self) { 53 | let ac = self.list.iter().filter(|i| !i.completed).count(); 54 | if self.activeCount != ac { 55 | self.activeCount = ac; 56 | self.active_count_changed(); 57 | } 58 | } 59 | 60 | #[allow(non_snake_case)] 61 | fn setCompleted(&mut self, item: usize, v: bool) -> bool { 62 | if item >= self.list.len() { 63 | return false; 64 | } 65 | self.list[item].completed = v; 66 | let idx = (self as &mut dyn QAbstractListModel).row_index(item as i32); 67 | (self as &mut dyn QAbstractListModel).data_changed(idx.clone(), idx); 68 | self.update_active_count(); 69 | true 70 | } 71 | 72 | #[allow(non_snake_case)] 73 | fn setDescription(&mut self, item: usize, v: String) -> bool { 74 | if item >= self.list.len() { 75 | return false; 76 | } 77 | self.list[item].description = v; 78 | let idx = (self as &mut dyn QAbstractListModel).row_index(item as i32); 79 | (self as &mut dyn QAbstractListModel).data_changed(idx.clone(), idx); 80 | true 81 | } 82 | 83 | fn insert_rows(&mut self, row: usize, count: usize) -> bool { 84 | if count == 0 || row > self.list.len() { 85 | return false; 86 | } 87 | (self as &mut dyn QAbstractListModel) 88 | .begin_insert_rows(row as i32, (row + count - 1) as i32); 89 | for i in 0..count { 90 | self.list.insert(row + i, TodosItem::default()); 91 | } 92 | (self as &mut dyn QAbstractListModel).end_insert_rows(); 93 | self.activeCount += count; 94 | self.active_count_changed(); 95 | self.count_changed(); 96 | true 97 | } 98 | 99 | fn remove_rows(&mut self, row: usize, count: usize) -> bool { 100 | if count == 0 || row + count > self.list.len() { 101 | return false; 102 | } 103 | (self as &mut dyn QAbstractListModel) 104 | .begin_remove_rows(row as i32, (row + count - 1) as i32); 105 | self.list.drain(row..row + count); 106 | (self as &mut dyn QAbstractListModel).end_remove_rows(); 107 | self.count_changed(); 108 | self.update_active_count(); 109 | true 110 | } 111 | 112 | #[allow(non_snake_case)] 113 | fn clearCompleted(&mut self) { 114 | (self as &mut dyn QAbstractListModel).begin_reset_model(); 115 | self.list.retain(|i| !i.completed); 116 | (self as &mut dyn QAbstractListModel).end_reset_model(); 117 | self.count_changed(); 118 | } 119 | 120 | fn add(&mut self, description: String) { 121 | let end = self.list.len(); 122 | (self as &mut dyn QAbstractListModel).begin_insert_rows(end as i32, end as i32); 123 | self.list.insert(end, TodosItem { completed: false, description }); 124 | (self as &mut dyn QAbstractListModel).end_insert_rows(); 125 | self.activeCount += 1; 126 | self.active_count_changed(); 127 | self.count_changed(); 128 | } 129 | 130 | fn remove(&mut self, index: u64) -> bool { 131 | self.remove_rows(index as usize, 1) 132 | } 133 | 134 | #[allow(non_snake_case)] 135 | fn setAll(&mut self, completed: bool) { 136 | for i in &mut self.list { 137 | i.completed = completed; 138 | } 139 | 140 | let idx1 = (self as &mut dyn QAbstractListModel).row_index(0); 141 | let end = self.list.len() as i32; 142 | let idx2 = (self as &mut dyn QAbstractListModel).row_index(end - 1); 143 | (self as &mut dyn QAbstractListModel).data_changed(idx1, idx2); 144 | self.update_active_count(); 145 | } 146 | } 147 | 148 | impl QAbstractListModel for Todos { 149 | fn row_count(&self) -> i32 { 150 | self.list.len() as i32 151 | } 152 | fn data(&self, index: QModelIndex, role: i32) -> QVariant { 153 | let idx = index.row() as usize; 154 | if idx < self.list.len() { 155 | if role == USER_ROLE { 156 | self.list[idx].completed.into() 157 | } else if role == USER_ROLE + 1 { 158 | QString::from(self.list[idx].description.clone()).into() 159 | } else { 160 | QVariant::default() 161 | } 162 | } else { 163 | QVariant::default() 164 | } 165 | } 166 | fn role_names(&self) -> HashMap { 167 | let mut map = HashMap::new(); 168 | map.insert(USER_ROLE, "completed".into()); 169 | map.insert(USER_ROLE + 1, "description".into()); 170 | map 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /examples/todos/src/main.rs: -------------------------------------------------------------------------------- 1 | use cstr::cstr; 2 | 3 | use qmetaobject::prelude::*; 4 | 5 | mod implementation; 6 | 7 | qrc!(my_resource, 8 | "todos/qml" { 9 | "main.qml", 10 | }, 11 | ); 12 | 13 | fn main() { 14 | my_resource(); 15 | qml_register_type::(cstr!("RustCode"), 1, 0, cstr!("Todos")); 16 | let mut engine = QmlEngine::new(); 17 | engine.load_file("qrc:/todos/qml/main.qml".into()); 18 | engine.exec(); 19 | } 20 | -------------------------------------------------------------------------------- /examples/webengine/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "webengine" 3 | version = "0.1.0" 4 | edition = "2018" 5 | build = "build.rs" 6 | 7 | [dependencies] 8 | qmetaobject = { path = "../../qmetaobject", features = ["webengine"] } 9 | qttypes = { path = "../../qttypes" } # required to get the DEP_QT_* env variables 10 | 11 | [build-dependencies] 12 | semver = "1" 13 | -------------------------------------------------------------------------------- /examples/webengine/build.rs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Olivier Goffart 2 | Copyright (C) 2021 ivan tkachenko a.k.a. ratijas 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 5 | associated documentation files (the "Software"), to deal in the Software without restriction, 6 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial 11 | portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 14 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 15 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 16 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 17 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | */ 19 | 20 | use semver::Version; 21 | 22 | fn main() { 23 | let cargo_target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); 24 | let cargo_target_env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap(); 25 | 26 | if (cargo_target_os == "windows") && (cargo_target_env != "msvc") { 27 | println!("cargo:warning=On Windows, WebEngine module is only available under MSVC 2017 or MSVC2019."); 28 | println!("cargo:rustc-cfg=no_qt"); 29 | } 30 | 31 | let qt_version = std::env::var("DEP_QT_VERSION") 32 | .unwrap() 33 | .parse::() 34 | .expect("Parsing Qt version failed"); 35 | 36 | if qt_version >= Version::new(6, 0, 0) && qt_version < Version::new(6, 2, 0) { 37 | println!( 38 | "cargo:warning=WebEngine is not supported on Qt {} yet. It is planned for Qt 6.2 LTS.", 39 | qt_version 40 | ); 41 | println!("cargo:rustc-cfg=no_qt"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /examples/webengine/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello from WebEngineView 4 | 5 | -------------------------------------------------------------------------------- /examples/webengine/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.6; 2 | import QtQuick.Window 2.0; 3 | import QtWebEngine 1.4 4 | 5 | Window { 6 | visible: true 7 | title: "WebEngineView" 8 | width: 800 9 | height: 600 10 | WebEngineView { 11 | anchors.fill: parent 12 | url: "qrc:/webengine/index.html" 13 | } 14 | } -------------------------------------------------------------------------------- /examples/webengine/src/main.rs: -------------------------------------------------------------------------------- 1 | use qmetaobject::prelude::*; 2 | #[cfg(not(no_qt))] 3 | use qmetaobject::webengine; 4 | 5 | qrc!(my_resource, 6 | "webengine" { 7 | "main.qml", 8 | "index.html", 9 | }, 10 | ); 11 | 12 | fn main() { 13 | #[cfg(not(no_qt))] 14 | webengine::initialize(); 15 | my_resource(); 16 | let mut engine = QmlEngine::new(); 17 | engine.load_file("qrc:/webengine/main.qml".into()); 18 | #[cfg(not(no_qt))] 19 | engine.exec(); 20 | } 21 | -------------------------------------------------------------------------------- /qmetaobject/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "qmetaobject" 3 | version = "0.2.10" 4 | edition = "2018" 5 | authors = ["Olivier Goffart "] 6 | build = "build.rs" 7 | description = "Expose rust object to Qt and QML." 8 | readme = "../README.md" 9 | license = "MIT" 10 | categories = ["api-bindings", "gui"] 11 | keywords = ["Qt", "QML", "QMetaObject",] 12 | repository = "https://github.com/woboq/qmetaobject-rs" 13 | 14 | [features] 15 | default = ["log"] 16 | chrono_qdatetime = ["qttypes/chrono"] 17 | webengine = ["qttypes/qtwebengine"] 18 | 19 | [dependencies] 20 | qttypes = { path = "../qttypes", version = "0.2.0", features = ["qtquick"] } 21 | qmetaobject_impl = { path = "../qmetaobject_impl", version = "=0.2.10"} 22 | lazy_static = "1.0" 23 | cpp = "0.5.6" 24 | log = { version = "0.4", optional = true } 25 | 26 | [build-dependencies] 27 | cpp_build = "0.5.6" 28 | semver = "1" 29 | 30 | [dev-dependencies] 31 | cstr = "0.2" 32 | if_rust_version = "1" 33 | tempfile = "^3" 34 | 35 | [package.metadata.docs.rs] 36 | dependencies = [ "qtbase5-dev", "qtdeclarative5-dev" ] 37 | -------------------------------------------------------------------------------- /qmetaobject/build.rs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Olivier Goffart 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 4 | associated documentation files (the "Software"), to deal in the Software without restriction, 5 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 6 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 7 | subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial 10 | portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 13 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 15 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | use semver::Version; 20 | 21 | fn main() { 22 | let qt_include_path = std::env::var("DEP_QT_INCLUDE_PATH").unwrap(); 23 | let qt_version = std::env::var("DEP_QT_VERSION") 24 | .unwrap() 25 | .parse::() 26 | .expect("Parsing Qt version failed"); 27 | 28 | let mut config = cpp_build::Config::new(); 29 | for f in std::env::var("DEP_QT_COMPILE_FLAGS").unwrap().split_terminator(";") { 30 | config.flag(f); 31 | } 32 | config.include(&qt_include_path).build("src/lib.rs"); 33 | 34 | for minor in 7..=15 { 35 | if qt_version >= Version::new(5, minor, 0) { 36 | println!("cargo:rustc-cfg=qt_{}_{}", 5, minor); 37 | } 38 | } 39 | let mut minor = 0; 40 | while qt_version >= Version::new(6, minor, 0) { 41 | println!("cargo:rustc-cfg=qt_{}_{}", 6, minor); 42 | minor += 1; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /qmetaobject/qmetaobject_rust.hpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Olivier Goffart 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 4 | associated documentation files (the "Software"), to deal in the Software without restriction, 5 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 6 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 7 | subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial 10 | portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 13 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 15 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | /// Pointer to a method of QObject which takes no arguments and returns nothing. 25 | /// Actually this is a "type-erased" method with various arguments and return 26 | /// value, but it merely represents a generic pointer, and let other code 27 | /// handle the types and memory safety. 28 | using QObjectErasedMethod = void (QObject::*)(); 29 | 30 | /// This type represents signals defined both in C++ and Rust, and provides 31 | /// handy conversions. 32 | /// 33 | /// Internally Qt signals are represented by some ID which must be unique 34 | /// within class hierarchy (not to be confused with indices). So, things 35 | /// like `&QObject::objectNameChanged` are only meaningful as far as they 36 | /// can be converted to some kind of magic representation (`void **`), and 37 | /// those representations can be compared for equality. 38 | /// 39 | /// In C++, signals are represented as pointers to member functions, but with 40 | /// types erased down to `void (QObject::*)()`. 41 | /// From `QMetaObject::Connection QObject::connectImpl(...)` documentation: 42 | /// > signal is a pointer to a pointer to a member signal of the sender 43 | /// 44 | /// For classes defined in Rust, signals are represented as offsets of 45 | /// corresponding `RustSignal` fields from the base address of their struct. 46 | /// This provides an easy way to guarantee ID uniqueness at a low price of 47 | /// having one bool field per signal. 48 | /// 49 | /// # Safety 50 | /// 51 | /// Users of `SignalInner` must ensure that they only ever use a 52 | /// signal of one class with instances of that class or its subclasses. 53 | /// 54 | /// Erased `cpp_erased_method` is not directly used as a function pointer anyway, 55 | /// so it is safe even if it contains garbage. 56 | /// 57 | /// # Further reading 58 | /// 59 | /// - http://itanium-cxx-abi.github.io/cxx-abi/abi.html#member-pointers 60 | /// - https://docs.microsoft.com/en-us/cpp/cpp/pointers-to-members?view=vs-2019 61 | union SignalInner { 62 | // No need to be public. Pointer to a signal is exposed via safe public getter. 63 | private: 64 | /// For signals derived from `RustSignal` Rust structs, e.g. `greeter.name_changed`. 65 | ptrdiff_t rust_field_offset; 66 | /// For signals defined in C++ classes, e.g. `&QObject::objectNameChanged`. 67 | QObjectErasedMethod cpp_erased_method; 68 | 69 | public: 70 | /// Construct signal representation for an arbitrary Qt signal defined in Rust 71 | /// as an offset of signal's field within Rust struct. 72 | explicit SignalInner(ptrdiff_t field_offset) 73 | : rust_field_offset(field_offset) 74 | {} 75 | 76 | /// Construct signal representation for an arbitrary Qt signal defined in C++. 77 | /// 78 | /// Note: this is an implicit conversion. 79 | template 80 | SignalInner(R (Type::* qt_signal)(Args...)) 81 | // (there is a double indirection in the reinterpret_cast to avoid -Wcast-function-type) 82 | : cpp_erased_method(*reinterpret_cast(&qt_signal)) 83 | {} 84 | 85 | /// Qt uses "pointer to a pointer to a member" signal representation inside 86 | /// `QObject::connect(...)` functions. This little helper encapsulates the 87 | /// required cast. 88 | void **asRawSignal() { 89 | return reinterpret_cast(&cpp_erased_method); 90 | // equivalently: 91 | // return reinterpret_cast(&rust_field_offset); 92 | } 93 | }; 94 | 95 | /// Wrapper for Rust `std::raw::TraitObject` struct. 96 | /// 97 | /// Note: `std::raw` is marked unstable as of Rust 1.43.0, so for future 98 | /// compatibility it would be better to box the trait object on the heap, 99 | /// and never manipulate its content directly from C++. For the time being, 100 | /// though, let it be. 101 | struct TraitObject { 102 | void *data; 103 | void *vtable; 104 | 105 | /// Nullability check. 106 | bool isValid() const { 107 | return data && vtable; 108 | } 109 | 110 | /// Forget about referenced object. 111 | /// 112 | /// If this TraitObject represented a `Box` (owned object) rather than a 113 | /// `&dyn` reference (borrowed object) then it may cause memory leaks, 114 | /// unless a copy was made for later proper destruction. 115 | inline void invalidate() { 116 | data = nullptr; 117 | vtable = nullptr; 118 | } 119 | }; 120 | 121 | extern "C" QMetaObject *RustObject_metaObject(TraitObject); 122 | extern "C" void RustObject_destruct(TraitObject); 123 | 124 | /// "513 reserved for Qt Jambi's DeleteOnMainThread event" 125 | /// We are just re-using this event type for our purposes. 126 | /// 127 | /// Source: https://github.com/qtjambi/qtjambi/blob/8ef99da63315945e6ab540cc31d66e5b021b69e4/src/cpp/qtjambi/qtjambidebugevent.cpp#L857 128 | static constexpr int QtJambi_EventType_DeleteOnMainThread = 513; 129 | 130 | template 131 | struct RustObject : Base { 132 | TraitObject rust_object; // A QObjectPinned where XXX is the base trait 133 | TraitObject ptr_qobject; // a QObjectPinned 134 | void (*extra_destruct)(QObject *); 135 | const QMetaObject *metaObject() const override { 136 | return ptr_qobject.isValid() ? RustObject_metaObject(ptr_qobject) : Base::metaObject(); 137 | } 138 | int qt_metacall(QMetaObject::Call _c, int _id, void **_a) override { 139 | _id = Base::qt_metacall(_c, _id, _a); 140 | if (_id < 0) 141 | return _id; 142 | const QMetaObject *mo = metaObject(); 143 | if (_c == QMetaObject::InvokeMetaMethod || _c == QMetaObject::RegisterMethodArgumentMetaType) { 144 | int methodCount = mo->methodCount(); 145 | if (_id < methodCount) 146 | mo->d.static_metacall(this, _c, _id, _a); 147 | _id -= methodCount; 148 | } else if ((_c >= QMetaObject::ReadProperty && _c <= 149 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 150 | QMetaObject::QueryPropertyUser 151 | #else 152 | QMetaObject::ResetProperty 153 | #endif 154 | ) || _c == QMetaObject::RegisterPropertyMetaType) { 155 | int propertyCount = mo->propertyCount(); 156 | if (_id < propertyCount) 157 | mo->d.static_metacall(this, _c, _id, _a); 158 | _id -= propertyCount; 159 | } 160 | return _id; 161 | } 162 | bool event(QEvent *event) override { 163 | if (ptr_qobject.isValid() && event->type() == QtJambi_EventType_DeleteOnMainThread) { 164 | // This event is sent by rust when we are deleted. 165 | ptr_qobject.invalidate(); // so the destructor don't recurse 166 | delete this; 167 | return true; 168 | } 169 | return Base::event(event); 170 | } 171 | ~RustObject() { 172 | auto r = ptr_qobject; 173 | ptr_qobject.invalidate(); 174 | if (extra_destruct) 175 | extra_destruct(this); 176 | if (r.isValid()) 177 | RustObject_destruct(r); 178 | } 179 | }; 180 | 181 | struct RustQObjectDescriptor { 182 | size_t size; 183 | const QMetaObject *baseMetaObject; 184 | QObject *(*create)(const TraitObject *, const TraitObject *); 185 | void (*qmlConstruct)(void *, const TraitObject *, const TraitObject *, void (*extra_destruct)(QObject *)); 186 | TraitObject (*get_rust_refcell)(QObject *); // Possible optimisation: make this an offset 187 | 188 | /// Get singleton-per-type descriptor. 189 | template 190 | static const RustQObjectDescriptor *instance(); 191 | }; 192 | 193 | template 194 | const RustQObjectDescriptor *RustQObjectDescriptor::instance() { 195 | static RustQObjectDescriptor desc { 196 | /*size*/ sizeof(T), 197 | /*baseMetaObject*/ &T::staticMetaObject, 198 | /*create*/ []( 199 | const TraitObject *self_pinned, 200 | const TraitObject *self_ptr 201 | ) -> QObject * { 202 | auto q = new T(); 203 | q->ptr_qobject = *self_ptr; 204 | q->rust_object = *self_pinned; 205 | return q; 206 | }, 207 | /*qmlConstruct*/ []( 208 | void *data, 209 | const TraitObject *self_pinned, 210 | const TraitObject *self_ptr, 211 | void (*extra_destruct)(QObject *) 212 | ) { 213 | auto *q = new (data) T(); 214 | q->rust_object = *self_pinned; 215 | q->ptr_qobject = *self_ptr; 216 | q->extra_destruct = extra_destruct; 217 | }, 218 | /*get_rust_refcell*/ [](QObject *q) { 219 | return static_cast(q)->ptr_qobject; 220 | } 221 | }; 222 | return &desc; 223 | } 224 | -------------------------------------------------------------------------------- /qmetaobject/src/future.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::mem::replace; 3 | use std::os::raw::c_void; 4 | use std::pin::Pin; 5 | use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; 6 | 7 | use cpp::cpp; 8 | 9 | use crate::connections::SignalArgArrayToTuple; 10 | 11 | static QT_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new( 12 | |s: *const ()| { 13 | RawWaker::new( 14 | cpp!(unsafe [s as "Waker *"] -> *const() as "Waker *" { 15 | s->refs++; 16 | return s; 17 | }), 18 | &QT_WAKER_VTABLE, 19 | ) 20 | }, 21 | |s: *const ()| { 22 | cpp!(unsafe [s as "Waker *"] { 23 | s->wake(); 24 | s->deref(); 25 | }) 26 | }, 27 | |s: *const ()| { 28 | cpp!(unsafe [s as "Waker *"] { 29 | s->wake(); 30 | }) 31 | }, 32 | |s: *const ()| { 33 | cpp!(unsafe [s as "Waker *"] { 34 | s->deref(); 35 | }) 36 | }, 37 | ); 38 | 39 | cpp! {{ 40 | 41 | #include 42 | 43 | /// Special QObject subclass to glue together internals of Rust's futures and Qt's events. 44 | /// It's lifetime is determined through reference counting, and its lifecycle is based on 45 | /// Qt's QObject rather than C++ RAII. 46 | struct Waker : QObject { 47 | /// Wrapped Rust's Future as a dynamic trait object. 48 | TraitObject future; 49 | /// Guard against redundant processing of multiple consecutive wake-up calls. 50 | bool woken = false; 51 | /// Guard against polling a future after it has been completed. 52 | bool completed = false; 53 | /// Reference counter. 54 | QAtomicInt refs = 0; 55 | 56 | // start with refs count of 1, because caller gets the ownership. 57 | Waker(TraitObject f): future(f), refs(1) {} 58 | 59 | void customEvent(QEvent *e) override { 60 | Q_UNUSED(e); 61 | woken = false; 62 | // future must not be polled after it returned `Poll::Ready` 63 | if (completed) { 64 | return; 65 | } 66 | completed = rust!(ProcessQtEvent [ 67 | this: *const () as "Waker *", 68 | future: *mut dyn Future as "TraitObject" 69 | ] -> bool as "bool" { 70 | poll_with_qt_waker(this, Pin::new_unchecked(&mut *future)) 71 | }); 72 | if (completed) { 73 | deref(); 74 | } 75 | } 76 | 77 | void deref() { 78 | if (!--refs) { 79 | deleteLater(); 80 | } 81 | } 82 | 83 | void wake() { 84 | if (woken) { 85 | return; 86 | } 87 | woken = true; 88 | // This line results in invocation of customEvent(QEvent*) method above. 89 | // Note that object may be waken multiple times before the wake up call 90 | // actually gets proceeded by the Qt's event loop. 91 | QCoreApplication::postEvent(this, new QEvent(QEvent::User)); 92 | } 93 | 94 | ~Waker() { 95 | rust!(QtDestroyFuture [future: *mut dyn Future as "TraitObject"] { 96 | drop(Box::from_raw(future)); 97 | }); 98 | } 99 | }; 100 | }} 101 | 102 | /// Execute a future on the Qt Event loop 103 | /// 104 | /// Waking the waker will post an event to the Qt event loop which will poll the future 105 | /// from the event handler 106 | /// 107 | /// Note that this function returns immediately. A Qt event loop need to be running 108 | /// on the current thread so the future can be executed. (It is Ok if the Qt event 109 | /// loop hasn't started yet when this function is called) 110 | pub fn execute_async(f: impl Future + 'static) { 111 | let f: *mut dyn Future = Box::into_raw(Box::new(f)); 112 | unsafe { 113 | let waker = cpp!([f as "TraitObject"] -> *const() as "Waker *" { 114 | return new Waker(f); 115 | }); 116 | poll_with_qt_waker(waker, Pin::new_unchecked(&mut *f)); 117 | } 118 | } 119 | 120 | // SAFETY: caller must ensure that given future hasn't returned Poll::Ready earlier. 121 | unsafe fn poll_with_qt_waker(waker: *const (), future: Pin<&mut dyn Future>) -> bool { 122 | cpp!([waker as "Waker *"] { waker->refs++; }); 123 | let waker = RawWaker::new(waker, &QT_WAKER_VTABLE); 124 | let waker = Waker::from_raw(waker); 125 | let mut context = Context::from_waker(&waker); 126 | future.poll(&mut context).is_ready() 127 | } 128 | 129 | /// Create a future that waits on the emission of a signal. 130 | /// 131 | /// The arguments of the signal need to implement `Clone`, and the Output of the future is a tuple 132 | /// containing the arguments of the signal (or the empty tuple if there are none.) 133 | /// 134 | /// The future will be ready as soon as the signal is emitted. 135 | /// 136 | /// This is unsafe for the same reason that [`connections::connect`][] is unsafe. 137 | /// 138 | /// [`connections::connect`]: ../connections/fn.connect.html 139 | pub unsafe fn wait_on_signal( 140 | sender: *const c_void, 141 | signal: crate::connections::Signal, 142 | ) -> impl Future::Tuple> { 143 | enum ConnectionFutureState { 144 | Init { sender: *const c_void, signal: crate::connections::Signal }, 145 | Started { handle: crate::connections::ConnectionHandle, waker: Waker }, 146 | Finished { result: ::Tuple }, 147 | Invalid, 148 | } 149 | 150 | impl std::marker::Unpin for ConnectionFutureState {} 151 | 152 | struct ConnectionFuture(ConnectionFutureState); 153 | 154 | impl Drop for ConnectionFuture { 155 | fn drop(&mut self) { 156 | if let ConnectionFutureState::Started { ref mut handle, .. } = &mut self.0 { 157 | handle.disconnect(); 158 | } 159 | } 160 | } 161 | 162 | impl Future for ConnectionFuture { 163 | type Output = ::Tuple; 164 | fn poll(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll { 165 | let state = &mut self.0; 166 | *state = match replace(state, ConnectionFutureState::Invalid) { 167 | ConnectionFutureState::Finished { result } => { 168 | return Poll::Ready(result); 169 | } 170 | ConnectionFutureState::Init { sender, signal } => { 171 | let s_ptr = state as *mut ConnectionFutureState<_>; 172 | let handle = unsafe { crate::connections::connect(sender, signal, s_ptr) }; 173 | debug_assert!(handle.is_valid()); 174 | ConnectionFutureState::Started { handle, waker: ctx.waker().clone() } 175 | } 176 | s @ ConnectionFutureState::Started { .. } => s, 177 | ConnectionFutureState::Invalid => unreachable!(), 178 | }; 179 | Poll::Pending 180 | } 181 | } 182 | 183 | impl crate::connections::Slot 184 | for *mut ConnectionFutureState 185 | { 186 | unsafe fn apply(&mut self, a: *const *const c_void) { 187 | if let ConnectionFutureState::Started { mut handle, waker } = replace( 188 | &mut **self, 189 | ConnectionFutureState::Finished { result: Args::args_array_to_tuple(a) }, 190 | ) { 191 | handle.disconnect(); 192 | waker.wake(); 193 | } else { 194 | unreachable!(); 195 | } 196 | } 197 | } 198 | 199 | ConnectionFuture(ConnectionFutureState::Init { sender, signal }) 200 | } 201 | -------------------------------------------------------------------------------- /qmetaobject/src/log.rs: -------------------------------------------------------------------------------- 1 | //! Logging facilities and forwarding 2 | 3 | use std::ffi::CStr; 4 | use std::os::raw::c_char; 5 | 6 | use cpp::{cpp, cpp_class}; 7 | 8 | #[cfg(feature = "log")] 9 | use log::{logger, Level, Record, RecordBuilder}; 10 | 11 | use crate::QString; 12 | 13 | cpp! {{ 14 | #include 15 | }} 16 | 17 | cpp_class!( 18 | /// Wrapper for [`QMessageLogContext`] class. 19 | /// 20 | /// [`QMessageLogContext`]: https://doc.qt.io/qt-5/qmessagelogcontext.html 21 | pub unsafe struct QMessageLogContext as "QMessageLogContext" 22 | ); 23 | 24 | impl QMessageLogContext { 25 | /// Wrapper for `QMessageLogContext::line`. 26 | pub fn line(&self) -> i32 { 27 | cpp!(unsafe [self as "QMessageLogContext*"] -> i32 as "int" { return self->line; }) 28 | } 29 | 30 | /// Wrapper for `QMessageLogContext::file`. 31 | pub fn file(&self) -> &str { 32 | unsafe { 33 | let x = cpp!([self as "QMessageLogContext*"] -> *const c_char as "const char*" { 34 | return self->file; 35 | }); 36 | if x.is_null() { 37 | return ""; 38 | } 39 | CStr::from_ptr(x).to_str().unwrap() 40 | } 41 | } 42 | 43 | /// Wrapper for `QMessageLogContext::function`. 44 | pub fn function(&self) -> &str { 45 | unsafe { 46 | let x = cpp!([self as "QMessageLogContext*"] -> *const c_char as "const char*" { 47 | return self->function; 48 | }); 49 | if x.is_null() { 50 | return ""; 51 | } 52 | CStr::from_ptr(x).to_str().unwrap() 53 | } 54 | } 55 | 56 | /// Wrapper for `QMessageLogContext::category`. 57 | pub fn category(&self) -> &str { 58 | unsafe { 59 | let x = cpp!([self as "QMessageLogContext*"] -> *const c_char as "const char*" { 60 | return self->category; 61 | }); 62 | if x.is_null() { 63 | return ""; 64 | } 65 | CStr::from_ptr(x).to_str().unwrap() 66 | } 67 | } 68 | } 69 | 70 | /// Wrapper for [`Qt::QtMsgType`][] enum. 71 | /// 72 | /// [`Qt::QtMsgType`]: https://doc.qt.io/qt-5/qtglobal.html#QtMsgType-enum 73 | #[repr(C)] 74 | // XXX: Do NOT derive Ord and PartialOrd. 75 | // XXX: Variants are not ordered by severity. 76 | // XXX: Also, levels ordering is not implemented in Qt, only == equality. 77 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 78 | pub enum QtMsgType { 79 | QtDebugMsg, 80 | QtWarningMsg, 81 | QtCriticalMsg, 82 | QtFatalMsg, 83 | QtInfoMsg, 84 | // there is also one level defined in C++ code: 85 | // QtSystemMsg = QtCriticalMsg 86 | } 87 | 88 | #[cfg(feature = "log")] 89 | impl From for Level { 90 | /// Mapping from Qt logging levels to Rust logging facade's levels. 91 | /// 92 | /// Due to the limited range of levels from both sides, 93 | /// [`QtCriticalMsg`][`Qt::QtMsgType`] and [`QtFatalMsg`][`Qt::QtMsgType`] 94 | /// both map to [`log::Level::Error`][Level], 95 | /// while [`log::Level::Trace`][Level] is never returned. 96 | /// 97 | /// [Level]: https://docs.rs/log/0.4.10/log/enum.Level.html 98 | /// [`Qt::QtMsgType`]: https://doc.qt.io/qt-5/qtglobal.html#QtMsgType-enum 99 | fn from(lvl: QtMsgType) -> Self { 100 | match lvl { 101 | QtMsgType::QtDebugMsg => Level::Debug, 102 | QtMsgType::QtInfoMsg => Level::Info, 103 | QtMsgType::QtWarningMsg => Level::Warn, 104 | QtMsgType::QtCriticalMsg => Level::Error, 105 | QtMsgType::QtFatalMsg => Level::Error, 106 | // XXX: What are the external guarantees about possible values of QtMsgType? 107 | // XXX: Are they promised to be limited to the valid enum variants? 108 | } 109 | } 110 | } 111 | 112 | #[cfg(feature = "log")] 113 | impl From for QtMsgType { 114 | /// Mapping back from Rust logging facade's levels to Qt logging levels. 115 | /// 116 | /// Not used internally, exists just for completeness of API. 117 | /// 118 | /// Due to the limited range of levels from both sides, 119 | /// [`log::Level::Debug`][Level] and [`log::Level::Trace`][Level] 120 | /// both map to [`QtDebugMsg`][`Qt::QtMsgType`], 121 | /// while [`QtFatalMsg`][`Qt::QtMsgType`] is never returned. 122 | /// 123 | /// [Level]: https://docs.rs/log/0.4.10/log/enum.Level.html 124 | /// [`Qt::QtMsgType`]: https://doc.qt.io/qt-5/qtglobal.html#QtMsgType-enum 125 | fn from(lvl: Level) -> Self { 126 | match lvl { 127 | Level::Error => QtMsgType::QtCriticalMsg, 128 | Level::Warn => QtMsgType::QtWarningMsg, 129 | Level::Info => QtMsgType::QtInfoMsg, 130 | Level::Debug => QtMsgType::QtDebugMsg, 131 | Level::Trace => QtMsgType::QtDebugMsg, 132 | } 133 | } 134 | } 135 | 136 | /// Wrapper for [`QtMessageHandler`][] typedef. 137 | /// 138 | /// [`QtMessageHandler`]: https://doc.qt.io/qt-5/qtglobal.html#QtMessageHandler-typedef 139 | pub type QtMessageHandler = Option; 140 | 141 | /// Wrapper for [`qInstallMessageHandler`] function. 142 | /// 143 | /// # Wrapper-specific behavior 144 | /// 145 | /// To restore the message handler, call `install_message_handler(None)`. 146 | /// 147 | /// [`qInstallMessageHandler`]: https://doc.qt.io/qt-5/qtglobal.html#qInstallMessageHandler 148 | pub fn install_message_handler(logger: QtMessageHandler) -> QtMessageHandler { 149 | cpp!(unsafe [logger as "QtMessageHandler"] -> QtMessageHandler as "QtMessageHandler" { 150 | return qInstallMessageHandler(logger); 151 | }) 152 | } 153 | 154 | // Logging middleware, pass-though, or just proxy function. 155 | // It is called from Qt code, then it converts Qt logging data 156 | // into Rust logging facade's log::Record object, and sends it 157 | // to the currently active logger. 158 | #[cfg(feature = "log")] 159 | extern "C" fn log_capture(msg_type: QtMsgType, context: &QMessageLogContext, message: &QString) { 160 | let level = msg_type.into(); 161 | let target = match context.category() { 162 | "" => "default", 163 | x => x, 164 | }; 165 | let file = match context.file() { 166 | "" => None, 167 | x => Some(x), 168 | }; 169 | let line = match context.line() { 170 | // In Qt, line numbers start from 1, while 0 is just a placeholder 171 | 0 => None, 172 | x => Some(x as _), 173 | }; 174 | let mut record = Record::builder(); 175 | record.level(level).target(target).file(file).line(line).module_path(None); 176 | // Match expression with single all-capturing arm is a hack that allows 177 | // to extend the lifetime of a matched object for "a little longer". 178 | // Passing an expression with temporaries as a function argument 179 | // works exactly the same way. 180 | // Basically, it retains bounded temporary values together with their 181 | // intermediate values etc. This is also the way how println! macro works. 182 | match context.function() { 183 | "" => finish(record, format_args!("{}", message)), 184 | f => finish(record, format_args!("[in {}] {}", f, message)), 185 | } 186 | fn finish<'a>(mut record: RecordBuilder<'a>, args: std::fmt::Arguments<'a>) { 187 | logger().log(&record.args(args).build()) 188 | } 189 | } 190 | 191 | /// Installs into [Qt logging system][qt-log] a function which forwards messages to the 192 | /// [Rust logging facade][log]. 193 | /// 194 | /// Most metadata from Qt logging context is retained and passed to [`log::Record`][]. 195 | /// Logging levels are mapped with the default [`From`][lvl] implementation. 196 | /// 197 | /// This function may be called more than once. 198 | /// 199 | /// [qt-log]: https://doc.qt.io/qt-5/qtglobal.html#qInstallMessageHandler 200 | /// [log]: https://crates.io/crates/log 201 | /// [`log::Record`]: https://docs.rs/log/0.4.10/log/struct.Record.html 202 | /// [lvl]: ./struct.QtMsgType.html 203 | #[cfg(feature = "log")] 204 | pub fn init_qt_to_rust() { 205 | // The reason it is named so complex instead of simple `init` is that 206 | // such descriptive name is future-proof. Consider if someone someday 207 | // would want to implement the opposite forwarding logger? 208 | install_message_handler(Some(log_capture)); 209 | } 210 | 211 | #[cfg(test)] 212 | #[cfg(feature = "log")] 213 | mod tests { 214 | use super::*; 215 | 216 | #[test] 217 | fn test_double_init() { 218 | // must not crash 219 | init_qt_to_rust(); 220 | init_qt_to_rust(); 221 | } 222 | 223 | #[test] 224 | fn test_convert() { 225 | assert_eq!(Level::from(QtMsgType::QtInfoMsg), Level::Info); 226 | assert_eq!(QtMsgType::QtCriticalMsg, QtMsgType::from(Level::Error)) 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /qmetaobject/src/qrc.rs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Olivier Goffart 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 4 | associated documentation files (the "Software"), to deal in the Software without restriction, 5 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 6 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 7 | subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial 10 | portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 13 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 15 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | use cpp::cpp; 19 | 20 | cpp! {{ 21 | Q_CORE_EXPORT bool qRegisterResourceData(int, const unsigned char *, 22 | const unsigned char *, const unsigned char *); 23 | }} 24 | 25 | /// Internal function used from qrc procedural macro. 26 | /// Unsafe because it can crash if the data structure are not proper. 27 | #[doc(hidden)] 28 | pub unsafe fn register_resource_data( 29 | version: i32, 30 | tree: &'static [u8], 31 | names: &'static [u8], 32 | payload: &'static [u8], 33 | ) { 34 | let tree_ptr = tree.as_ptr(); 35 | let names_ptr = names.as_ptr(); 36 | let payload_ptr = payload.as_ptr(); 37 | cpp!([version as "int", tree_ptr as "const unsigned char*", names_ptr as "const unsigned char*", payload_ptr as "const unsigned char*"] { 38 | qRegisterResourceData(version, tree_ptr, names_ptr, payload_ptr); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /qmetaobject/src/qtcore/core_application.rs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Olivier Goffart 2 | Copyright (C) 2021 ivan tkachenko a.k.a. ratijas 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 5 | associated documentation files (the "Software"), to deal in the Software without restriction, 6 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial 11 | portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 14 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 15 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 16 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 17 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | */ 19 | //! Wrappers around [`QtCore/QCoreApplication`][qt] header. 20 | //! 21 | //! [qt]: https://doc.qt.io/qt-5/qcoreapplication.html 22 | 23 | use cpp::cpp; 24 | 25 | use crate::*; 26 | 27 | cpp! {{ 28 | #include 29 | }} 30 | 31 | /// Wrapper around [`QCoreApplication`][qt] class. 32 | /// 33 | /// # Wrapper-specific 34 | /// 35 | /// Currently it is uncreatible, non-obtainable, and exists purely as a 36 | /// namespace for public static members (associated functions in Rust). 37 | /// 38 | /// [qt]: https://doc.qt.io/qt-5/qcoreapplication.html 39 | pub struct QCoreApplication { 40 | // Private field makes this dummy struct uncreatable by user. Since 41 | // there's no way to obtain an instance of it, we won't have to worry 42 | // about layout compatibility and stuff. 43 | _private: (), 44 | } 45 | 46 | impl QCoreApplication { 47 | /// Wrapper around [`QString applicationName()`][qt] static method. 48 | /// 49 | /// [qt]: https://doc.qt.io/qt-5/qcoreapplication.html#applicationName-prop 50 | pub fn application_name() -> QString { 51 | cpp!(unsafe [] -> QString as "QString" { 52 | return QCoreApplication::applicationName(); 53 | }) 54 | } 55 | 56 | /// Wrapper around [`void setApplicationName(const QString &application)`][qt] static method. 57 | /// 58 | /// [qt]: https://doc.qt.io/qt-5/qcoreapplication.html#applicationName-prop 59 | pub fn set_application_name(application: QString) { 60 | cpp!(unsafe [application as "QString"] { 61 | QCoreApplication::setApplicationName(application); 62 | }); 63 | } 64 | 65 | /// Wrapper around [`QString applicationVersion()`][qt] static method. 66 | /// 67 | /// [qt]: https://doc.qt.io/qt-5/qcoreapplication.html#applicationVersion-prop 68 | pub fn application_version() -> QString { 69 | cpp!(unsafe [] -> QString as "QString" { 70 | return QCoreApplication::applicationVersion(); 71 | }) 72 | } 73 | 74 | /// Wrapper around [`void setApplicationVersion(const QString &version)`][qt] static method. 75 | /// 76 | /// [qt]: https://doc.qt.io/qt-5/qcoreapplication.html#applicationVersion-prop 77 | pub fn set_application_version(version: QString) { 78 | cpp!(unsafe [version as "QString"] { 79 | QCoreApplication::setApplicationVersion(version); 80 | }); 81 | } 82 | 83 | /// Wrapper around [`QString organizationDomain()`][qt] static method. 84 | /// 85 | /// [qt]: https://doc.qt.io/qt-5/qcoreapplication.html#organizationDomain-prop 86 | pub fn organization_domain() -> QString { 87 | cpp!(unsafe [] -> QString as "QString" { 88 | return QCoreApplication::organizationDomain(); 89 | }) 90 | } 91 | 92 | /// Wrapper around [`void setOrganizationDomain(const QString &orgDomain)`][qt] static method. 93 | /// 94 | /// [qt]: https://doc.qt.io/qt-5/qcoreapplication.html#organizationDomain-prop 95 | pub fn set_organization_domain(org_domain: QString) { 96 | cpp!(unsafe [org_domain as "QString"] { 97 | QCoreApplication::setOrganizationDomain(org_domain); 98 | }); 99 | } 100 | 101 | /// Wrapper around [`QString organizationName()`][qt] static method. 102 | /// 103 | /// [qt]: https://doc.qt.io/qt-5/qcoreapplication.html#organizationName-prop 104 | pub fn organization_name() -> QString { 105 | cpp!(unsafe [] -> QString as "QString" { 106 | return QCoreApplication::organizationName(); 107 | }) 108 | } 109 | 110 | /// Wrapper around [`void setOrganizationName(const QString &orgName)`][qt] static method. 111 | /// 112 | /// [qt]: https://doc.qt.io/qt-5/qcoreapplication.html#organizationName-prop 113 | pub fn set_organization_name(org_name: QString) { 114 | cpp!(unsafe [org_name as "QString"] { 115 | QCoreApplication::setOrganizationName(org_name); 116 | }); 117 | } 118 | 119 | /// Wrapper around [`void QCoreApplication::quit()`][qt] static method. 120 | /// 121 | /// # Note 122 | /// 123 | /// Unlike the C library function of the same name, this function does 124 | /// return to the caller — it is event processing that stops. 125 | /// 126 | /// [qt]: https://doc.qt.io/qt-5/qcoreapplication.html#quit 127 | pub fn quit() { 128 | cpp!(unsafe [] { 129 | QCoreApplication::quit(); 130 | }); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /qmetaobject/src/qtcore/mod.rs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Olivier Goffart 2 | Copyright (C) 2021 ivan tkachenko a.k.a. ratijas 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 5 | associated documentation files (the "Software"), to deal in the Software without restriction, 6 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial 11 | portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 14 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 15 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 16 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 17 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | */ 19 | //! Wrappers around QtCore module. 20 | 21 | pub mod core_application; 22 | -------------------------------------------------------------------------------- /qmetaobject/src/qtquickcontrols2.rs: -------------------------------------------------------------------------------- 1 | /// Refer to the Qt documentation for QQuickStyle 2 | pub struct QQuickStyle {} 3 | 4 | impl QQuickStyle { 5 | /// Refer to the Qt documentation for QQuickStyle::setStyle 6 | pub fn set_style(style: &str) { 7 | std::env::set_var("QT_QUICK_CONTROLS_STYLE", style); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /qmetaobject/src/tablemodel.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use cpp::cpp; 4 | 5 | use super::*; 6 | 7 | pub trait QAbstractTableModel: QObject { 8 | fn get_object_description() -> &'static QObjectDescriptor 9 | where 10 | Self: Sized, 11 | { 12 | unsafe { 13 | &*cpp!([]-> *const QObjectDescriptor as "RustQObjectDescriptor const*" { 14 | return RustQObjectDescriptor::instance(); 15 | }) 16 | } 17 | } 18 | /// Refer to the Qt documentation of QAbstractTableModel::rowCount 19 | fn row_count(&self) -> i32; 20 | /// Refer to the Qt documentation of QAbstractTableModel::columnCount 21 | fn column_count(&self) -> i32; 22 | /// Refer to the Qt documentation of QAbstractTableModel::data 23 | fn data(&self, index: QModelIndex, role: i32) -> QVariant; 24 | /// Refer to the Qt documentation of QAbstractTableModel::setData 25 | fn set_data(&mut self, _index: QModelIndex, _value: &QVariant, _role: i32) -> bool { 26 | false 27 | } 28 | /// Refer to the Qt documentation of QAbstractTableModel::roleNames 29 | fn role_names(&self) -> HashMap { 30 | HashMap::new() 31 | } 32 | 33 | /// Refer to the Qt documentation of QAbstractItemModel::beginInsertRows 34 | fn begin_insert_rows(&mut self, first: i32, last: i32) { 35 | let p = QModelIndex::default(); 36 | let obj = self.get_cpp_object(); 37 | unsafe { 38 | cpp!([obj as "Rust_QAbstractTableModel*", p as "QModelIndex", first as "int", last as "int"]{ 39 | if(obj) obj->beginInsertRows(p, first, last); 40 | }) 41 | } 42 | } 43 | /// Refer to the Qt documentation of QAbstractItemModel::endInsertRows 44 | fn end_insert_rows(&mut self) { 45 | let obj = self.get_cpp_object(); 46 | unsafe { 47 | cpp!([obj as "Rust_QAbstractTableModel*"]{ 48 | if(obj) obj->endInsertRows(); 49 | }) 50 | } 51 | } 52 | /// Refer to the Qt documentation of QAbstractItemModel::beginInsertColumns 53 | fn begin_insert_columns(&mut self, first: i32, last: i32) { 54 | let p = QModelIndex::default(); 55 | let obj = self.get_cpp_object(); 56 | unsafe { 57 | cpp!([obj as "Rust_QAbstractTableModel*", p as "QModelIndex", first as "int", last as "int"]{ 58 | if(obj) obj->beginInsertColumns(p, first, last); 59 | }) 60 | } 61 | } 62 | /// Refer to the Qt documentation of QAbstractItemModel::endInsertColumns 63 | fn end_insert_columns(&mut self) { 64 | let obj = self.get_cpp_object(); 65 | unsafe { 66 | cpp!([obj as "Rust_QAbstractTableModel*"]{ 67 | if(obj) obj->endInsertColumns(); 68 | }) 69 | } 70 | } 71 | /// Refer to the Qt documentation of QAbstractItemModel::beginRemoveRows 72 | fn begin_remove_rows(&mut self, first: i32, last: i32) { 73 | let p = QModelIndex::default(); 74 | let obj = self.get_cpp_object(); 75 | unsafe { 76 | cpp!([obj as "Rust_QAbstractTableModel*", p as "QModelIndex", first as "int", last as "int"]{ 77 | if(obj) obj->beginRemoveRows(p, first, last); 78 | }) 79 | } 80 | } 81 | /// Refer to the Qt documentation of QAbstractItemModel::endRemoveRows 82 | fn end_remove_rows(&mut self) { 83 | let obj = self.get_cpp_object(); 84 | unsafe { 85 | cpp!([obj as "Rust_QAbstractTableModel*"]{ 86 | if(obj) obj->endRemoveRows(); 87 | }) 88 | } 89 | } 90 | /// Refer to the Qt documentation of QAbstractItemModel::beginRemoveColumns 91 | fn begin_remove_columns(&mut self, first: i32, last: i32) { 92 | let p = QModelIndex::default(); 93 | let obj = self.get_cpp_object(); 94 | unsafe { 95 | cpp!([obj as "Rust_QAbstractTableModel*", p as "QModelIndex", first as "int", last as "int"]{ 96 | if(obj) obj->beginRemoveColumns(p, first, last); 97 | }) 98 | } 99 | } 100 | /// Refer to the Qt documentation of QAbstractItemModel::endRemoveColumns 101 | fn end_remove_columns(&mut self) { 102 | let obj = self.get_cpp_object(); 103 | unsafe { 104 | cpp!([obj as "Rust_QAbstractTableModel*"]{ 105 | if(obj) obj->endRemoveColumns(); 106 | }) 107 | } 108 | } 109 | /// Refer to the Qt documentation of QAbstractItemModel::beginResetModel 110 | fn begin_reset_model(&mut self) { 111 | let obj = self.get_cpp_object(); 112 | unsafe { 113 | cpp!([obj as "Rust_QAbstractTableModel*"]{ 114 | if(obj) obj->beginResetModel(); 115 | }) 116 | } 117 | } 118 | /// Refer to the Qt documentation of QAbstractItemModel::endResetModel 119 | fn end_reset_model(&mut self) { 120 | let obj = self.get_cpp_object(); 121 | unsafe { 122 | cpp!([obj as "Rust_QAbstractTableModel*"]{ 123 | if(obj) obj->endResetModel(); 124 | }) 125 | } 126 | } 127 | /// Refer to the Qt documentation of QAbstractItemModel::dataChanged 128 | fn data_changed(&mut self, top_left: QModelIndex, bottom_right: QModelIndex) { 129 | let obj = self.get_cpp_object(); 130 | unsafe { 131 | cpp!([obj as "Rust_QAbstractTableModel*", top_left as "QModelIndex", bottom_right as "QModelIndex"]{ 132 | if(obj) obj->dataChanged(top_left, bottom_right); 133 | }) 134 | } 135 | } 136 | /// Returns a QModelIndex for the given row and column 137 | fn index(&self, row: i32, col: i32) -> QModelIndex { 138 | let obj = self.get_cpp_object(); 139 | unsafe { 140 | cpp!([obj as "Rust_QAbstractTableModel*", row as "int", col as "int"] -> QModelIndex as "QModelIndex" { 141 | return obj ? obj->index(row, col) : QModelIndex(); 142 | }) 143 | } 144 | } 145 | } 146 | 147 | cpp! {{ 148 | #include 149 | #include 150 | 151 | struct Rust_QAbstractTableModel : RustObject { 152 | 153 | using QAbstractTableModel::beginInsertRows; 154 | using QAbstractTableModel::endInsertRows; 155 | using QAbstractTableModel::beginInsertColumns; 156 | using QAbstractTableModel::endInsertColumns; 157 | using QAbstractTableModel::beginRemoveRows; 158 | using QAbstractTableModel::endRemoveRows; 159 | using QAbstractTableModel::beginRemoveColumns; 160 | using QAbstractTableModel::endRemoveColumns; 161 | using QAbstractTableModel::beginResetModel; 162 | using QAbstractTableModel::endResetModel; 163 | 164 | int rowCount(const QModelIndex & = QModelIndex()) const override { 165 | return rust!(Rust_QAbstractTableModel_rowCount[rust_object : QObjectPinned as "TraitObject"] 166 | -> i32 as "int" { 167 | rust_object.borrow().row_count() 168 | }); 169 | } 170 | 171 | int columnCount(const QModelIndex & = QModelIndex()) const override { 172 | return rust!(Rust_QAbstractTableModel_columnCount[rust_object : QObjectPinned as "TraitObject"] 173 | -> i32 as "int" { 174 | rust_object.borrow().column_count() 175 | }); 176 | } 177 | 178 | QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override { 179 | return rust!(Rust_QAbstractTableModel_data[rust_object : QObjectPinned as "TraitObject", 180 | index : QModelIndex as "QModelIndex", role : i32 as "int"] -> QVariant as "QVariant" { 181 | rust_object.borrow().data(index, role) 182 | }); 183 | } 184 | 185 | bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override { 186 | return rust!(Rust_QAbstractTableModel_setData[rust_object : QObjectPinned as "TraitObject", 187 | index : QModelIndex as "QModelIndex", value : QVariant as "QVariant", role : i32 as "int"] 188 | -> bool as "bool" { 189 | rust_object.borrow_mut().set_data(index, &value, role) 190 | }); 191 | } 192 | 193 | //Qt::ItemFlags flags(const QModelIndex &index) const override; 194 | 195 | //QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; 196 | 197 | QHash roleNames() const override { 198 | QHash base = QAbstractTableModel::roleNames(); 199 | rust!(Rust_QAbstractTableModel_roleNames[rust_object : QObjectPinned as "TraitObject", 200 | base: *mut c_void as "QHash&"] { 201 | for (key, val) in rust_object.borrow().role_names().iter() { 202 | add_to_hash(base, *key, val.clone()); 203 | } 204 | }); 205 | return base; 206 | } 207 | 208 | //QModelIndex index(int row, int column, const QModelIndex &parent) const override; 209 | 210 | //QModelIndex parent(const QModelIndex &child) const override; 211 | }; 212 | }} 213 | -------------------------------------------------------------------------------- /qmetaobject/src/webengine.rs: -------------------------------------------------------------------------------- 1 | use cpp::cpp; 2 | 3 | cpp! {{ 4 | #if !(_WIN32 && ! defined(_MSC_VER)) 5 | # if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 6 | # if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) 7 | # include 8 | # endif 9 | # else 10 | # include 11 | # endif 12 | #endif 13 | }} 14 | 15 | /// Refer to the Qt documentation of QtWebEngine::initialize() 16 | pub fn initialize() { 17 | cpp!(unsafe [] { 18 | #if !(_WIN32 && ! defined(_MSC_VER)) 19 | # if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 20 | # if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) 21 | QtWebEngineQuick::initialize(); 22 | # endif 23 | # else 24 | QtWebEngine::initialize(); 25 | # endif 26 | #endif 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /qmetaobject/tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Olivier Goffart 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 4 | associated documentation files (the "Software"), to deal in the Software without restriction, 5 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 6 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 7 | subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial 10 | portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 13 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 15 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | #![allow(dead_code)] 20 | 21 | use std::cell::RefCell; 22 | use std::sync::Mutex; 23 | 24 | use qmetaobject::*; 25 | 26 | lazy_static! { 27 | pub static ref TEST_MUTEX: Mutex<()> = Mutex::new(()); 28 | pub static ref QML_LOGS: Mutex> = Mutex::new(Vec::new()); 29 | } 30 | 31 | /// There can only be one thread running at the time with a QQuickEngine 32 | /// (in principle, everything should be in the same main thread) 33 | pub fn lock_for_test() -> std::sync::MutexGuard<'static, ()> { 34 | TEST_MUTEX.lock().unwrap_or_else(|e| e.into_inner()) 35 | } 36 | 37 | extern "C" fn log_capture(msg_type: QtMsgType, context: &QMessageLogContext, message: &QString) { 38 | let log = format!( 39 | "{}:{} [{:?} {} {}] {}", 40 | context.file(), 41 | context.line(), 42 | msg_type, 43 | context.category(), 44 | context.function(), 45 | message 46 | ); 47 | println!("{}", log); 48 | let mut logs = QML_LOGS.lock().unwrap_or_else(|e| e.into_inner()); 49 | logs.push(log); 50 | } 51 | 52 | pub fn do_test(obj: T, qml: &str) -> bool { 53 | let _lock = lock_for_test(); 54 | QML_LOGS.lock().unwrap_or_else(|e| e.into_inner()).clear(); 55 | 56 | install_message_handler(Some(log_capture)); 57 | 58 | let qml_text = "import QtQuick 2.0\n".to_owned() + qml; 59 | 60 | let obj = RefCell::new(obj); 61 | 62 | let mut engine = QmlEngine::new(); 63 | engine.set_object_property("_obj".into(), unsafe { QObjectPinned::new(&obj) }); 64 | engine.load_data(qml_text.into()); 65 | engine.invoke_method("doTest".into(), &[]).to_bool() 66 | } 67 | 68 | /// Expect error from QmlEngine. Return it. 69 | pub fn do_test_error_with_url(obj: T, qml: &str, url: &str) -> String { 70 | let _lock = lock_for_test(); 71 | QML_LOGS.lock().unwrap_or_else(|e| e.into_inner()).clear(); 72 | 73 | install_message_handler(Some(log_capture)); 74 | 75 | let qml_text = "import QtQuick 2.0\n".to_owned() + qml; 76 | 77 | let obj = RefCell::new(obj); 78 | 79 | let mut engine = QmlEngine::new(); 80 | engine.set_object_property("_obj".into(), unsafe { QObjectPinned::new(&obj) }); 81 | engine.load_data_as(qml_text.into(), QString::from(url).into()); 82 | engine.invoke_method("doTest".into(), &[]); 83 | let errors = QML_LOGS.lock().unwrap_or_else(|e| e.into_inner()); 84 | errors.last().expect("An error from QmlEngine was expected").clone() 85 | } 86 | 87 | pub fn do_test_variant(obj: QVariant, qml: &str) -> bool { 88 | let _lock = lock_for_test(); 89 | QML_LOGS.lock().unwrap_or_else(|e| e.into_inner()).clear(); 90 | 91 | install_message_handler(Some(log_capture)); 92 | 93 | let qml_text = "import QtQuick 2.0\n".to_owned() + qml; 94 | 95 | let mut engine = QmlEngine::new(); 96 | engine.set_property("_obj".into(), obj); 97 | engine.load_data(qml_text.into()); 98 | engine.invoke_method("doTest".into(), &[]).to_bool() 99 | } 100 | 101 | pub fn test_loading_logs(qml: &str, log: &str) -> bool { 102 | let _lock = TEST_MUTEX.lock().unwrap_or_else(|e| e.into_inner()); 103 | QML_LOGS.lock().unwrap_or_else(|e| e.into_inner()).clear(); 104 | 105 | install_message_handler(Some(log_capture)); 106 | 107 | let qml_text = "import QtQuick 2.0\n".to_owned() + qml; 108 | 109 | let mut engine = QmlEngine::new(); 110 | engine.load_data(qml_text.into()); 111 | 112 | let logs = QML_LOGS.lock().unwrap_or_else(|e| e.into_inner()); 113 | logs.iter().any(|x| x.contains(log)) 114 | } 115 | -------------------------------------------------------------------------------- /qmetaobject/tests/models.rs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Olivier Goffart 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 4 | associated documentation files (the "Software"), to deal in the Software without restriction, 5 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 6 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 7 | subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial 10 | portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 13 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 15 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | use std::cell::RefCell; 19 | use std::iter::FromIterator; 20 | 21 | use qmetaobject::*; 22 | 23 | mod common; 24 | use self::common::*; 25 | 26 | #[test] 27 | fn simple_model() { 28 | #[derive(Default, SimpleListItem)] 29 | struct TM { 30 | pub a: QString, 31 | pub b: u32, 32 | } 33 | let model: qmetaobject::listmodel::SimpleListModel = 34 | std::iter::once(TM { a: "hello".into(), b: 42 }).collect(); 35 | assert!(do_test( 36 | model, 37 | " 38 | Item { 39 | Repeater { 40 | id: rep; 41 | model: _obj 42 | Text { 43 | text: a + b 44 | } 45 | } 46 | function doTest() { 47 | console.log('simple_model:', rep.count, rep.itemAt(0).text); 48 | return rep.count === 1 && rep.itemAt(0).text === 'hello42'; 49 | } 50 | } 51 | " 52 | )); 53 | } 54 | 55 | #[test] 56 | fn simple_model_remove() { 57 | #[derive(QObject, Default)] 58 | pub struct Foo { 59 | base: qt_base_class!(trait QObject), 60 | pub list: qt_property!(RefCell>; CONST), 61 | pub remove: qt_method!( 62 | fn remove(&mut self, index: usize) { 63 | self.list.borrow_mut().remove(index); 64 | } 65 | ), 66 | } 67 | 68 | #[derive(Debug, Clone, SimpleListItem, Default)] 69 | pub struct X { 70 | pub val: usize, 71 | } 72 | 73 | impl Foo { 74 | pub fn new() -> Self { 75 | Self { 76 | list: RefCell::new(FromIterator::from_iter(vec![ 77 | X { val: 10 }, 78 | X { val: 11 }, 79 | X { val: 12 }, 80 | X { val: 13 }, 81 | ])), 82 | ..Default::default() 83 | } 84 | } 85 | } 86 | 87 | let obj = Foo::new(); 88 | 89 | assert!(do_test( 90 | obj, 91 | " 92 | Item { 93 | Repeater{ 94 | id: rep 95 | model: _obj.list 96 | Text { 97 | text: val 98 | } 99 | } 100 | function doTest() { 101 | _obj.remove(1); 102 | console.log('simple_model_remove', rep.count, rep.itemAt(0).text, rep.itemAt(1).text, rep.itemAt(2).text); 103 | return rep.count === 3 104 | && rep.itemAt(0).text === '10' 105 | && rep.itemAt(1).text === '12' 106 | && rep.itemAt(2).text === '13'; 107 | } 108 | } 109 | " 110 | )); 111 | } 112 | 113 | #[test] 114 | fn simple_model_iter() { 115 | #[derive(QObject, Default)] 116 | pub struct Foo { 117 | base: qt_base_class!(trait QObject), 118 | pub list: qt_property!(RefCell>; CONST), 119 | } 120 | 121 | #[derive(Debug, Clone, SimpleListItem, Default, PartialEq)] 122 | pub struct X { 123 | pub val: usize, 124 | } 125 | 126 | let original_items: Vec = vec![X { val: 10 }, X { val: 11 }, X { val: 12 }, X { val: 13 }]; 127 | 128 | let obj = Foo { 129 | list: RefCell::new(FromIterator::from_iter(original_items.iter())), 130 | ..Default::default() 131 | }; 132 | 133 | let iterated_items = obj.list.borrow().iter().cloned().collect::>(); 134 | assert_eq!(original_items, iterated_items); 135 | } 136 | -------------------------------------------------------------------------------- /qmetaobject/tests/qml/Bar.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | Item {} 3 | // This file exists only for doctests 4 | -------------------------------------------------------------------------------- /qmetaobject/tests/qml/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | Item {} 3 | // This file exists only for doctests 4 | -------------------------------------------------------------------------------- /qmetaobject_impl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "qmetaobject_impl" 3 | version = "0.2.10" 4 | edition = "2018" 5 | authors = ["Olivier Goffart "] 6 | description = "Custom derive for the qmetaobject crate." 7 | readme = "../README.md" 8 | license = "MIT" 9 | keywords = ["Qt", "QML", "QMetaObject",] 10 | repository = "https://github.com/woboq/qmetaobject-rs" 11 | 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [dependencies] 17 | syn = { version = "1", features = ["full"] } 18 | quote = "1" 19 | proc-macro2 = "1" 20 | -------------------------------------------------------------------------------- /qmetaobject_impl/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Olivier Goffart 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 4 | associated documentation files (the "Software"), to deal in the Software without restriction, 5 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 6 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 7 | subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial 10 | portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 13 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 15 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | //! This crates implement the custom derive used by the `qmetaobject` crate. 20 | 21 | #![recursion_limit = "256"] 22 | #![allow(clippy::unreadable_literal)] // Because we copy-paste constants from Qt 23 | #![allow(clippy::cognitive_complexity)] 24 | 25 | use proc_macro::TokenStream; 26 | use quote::{quote, ToTokens}; 27 | use syn::DeriveInput; 28 | 29 | mod qbjs; 30 | mod qobject_impl; 31 | mod qrc_impl; 32 | mod simplelistitem_impl; 33 | 34 | /// Get the tokens to refer to the qmetaobject crate. By default, return "::qmetaobject" unless 35 | /// the QMetaObjectCrate is specified 36 | fn get_crate(input: &DeriveInput) -> impl ToTokens { 37 | for i in input.attrs.iter() { 38 | if let Ok(x) = i.parse_meta() { 39 | if x.path().is_ident("QMetaObjectCrate") { 40 | if let syn::Meta::NameValue(mnv) = x { 41 | use syn::Lit::*; 42 | let lit: syn::Path = match mnv.lit { 43 | Str(s) => syn::parse_str(&s.value()) 44 | .expect("Can't parse QMetaObjectCrate Attribute"), 45 | _ => panic!("Can't parse QMetaObjectCrate Attribute"), 46 | }; 47 | return quote!( #lit ); 48 | } 49 | } 50 | } 51 | } 52 | 53 | quote!(::qmetaobject) 54 | } 55 | 56 | /// Implementation of #[derive(QObject)] 57 | #[proc_macro_derive(QObject, attributes(QMetaObjectCrate, qt_base_class))] 58 | pub fn qobject_impl(input: TokenStream) -> TokenStream { 59 | qobject_impl::generate(input, true, 5) 60 | } 61 | 62 | /// Implementation of #[derive(QObject)] 63 | #[proc_macro_derive(QObject6, attributes(QMetaObjectCrate, qt_base_class))] 64 | pub fn qobject_impl6(input: TokenStream) -> TokenStream { 65 | qobject_impl::generate(input, true, 6) 66 | } 67 | 68 | /// Implementation of #[derive(QGadget)] 69 | #[proc_macro_derive(QGadget, attributes(QMetaObjectCrate))] 70 | pub fn qgadget_impl(input: TokenStream) -> TokenStream { 71 | qobject_impl::generate(input, false, 5) 72 | } 73 | 74 | /// Implementation of #[derive(QGadget)] 75 | #[proc_macro_derive(QGadget6, attributes(QMetaObjectCrate))] 76 | pub fn qgadget_impl6(input: TokenStream) -> TokenStream { 77 | qobject_impl::generate(input, false, 6) 78 | } 79 | 80 | /// Implementation of #[derive(QEnum)] 81 | #[proc_macro_derive(QEnum, attributes(QMetaObjectCrate))] 82 | pub fn qenum_impl(input: TokenStream) -> TokenStream { 83 | qobject_impl::generate_enum(input, 5) 84 | } 85 | 86 | /// Implementation of #[derive(QEnum)] 87 | #[proc_macro_derive(QEnum6, attributes(QMetaObjectCrate))] 88 | pub fn qenum_impl6(input: TokenStream) -> TokenStream { 89 | qobject_impl::generate_enum(input, 6) 90 | } 91 | 92 | // Implementation of the qmetaobject::qrc! macro 93 | #[proc_macro] 94 | pub fn qrc_internal(input: TokenStream) -> TokenStream { 95 | qrc_impl::process_qrc(input) 96 | } 97 | 98 | /// Implementation of #[derive(SimpleListItem)] 99 | #[proc_macro_derive(SimpleListItem, attributes(QMetaObjectCrate))] 100 | pub fn simplelistitem(input: TokenStream) -> TokenStream { 101 | simplelistitem_impl::derive(input) 102 | } 103 | -------------------------------------------------------------------------------- /qmetaobject_impl/src/qbjs.rs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Olivier Goffart 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 4 | associated documentation files (the "Software"), to deal in the Software without restriction, 5 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 6 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 7 | subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial 10 | portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 13 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 15 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | // That's the same as parent::write_u32, but it should always be little endian 20 | fn write_u32(val: u32) -> [u8; 4] { 21 | [ 22 | (val & 0xff) as u8, 23 | ((val >> 8) & 0xff) as u8, 24 | ((val >> 16) & 0xff) as u8, 25 | ((val >> 24) & 0xff) as u8, 26 | ] 27 | } 28 | 29 | // That's the same as parent::write_u32, but it should always be little endian 30 | fn write_u16(val: u16) -> [u8; 2] { 31 | [(val & 0xff) as u8, ((val >> 8) & 0xff) as u8] 32 | } 33 | 34 | pub enum Value { 35 | String(String), 36 | Double(f64), 37 | Bool(bool), 38 | // TODO: other things 39 | } 40 | 41 | fn write_string(result: &mut Vec, str: &str) { 42 | result.extend_from_slice(&write_u16(str.len() as u16)); 43 | result.extend_from_slice(str.as_bytes()); 44 | let pad = (str.len() + 2) % 4; 45 | if pad != 0 { 46 | for _ in 0..(4 - pad) { 47 | result.push(0); //Padding 48 | } 49 | } 50 | } 51 | 52 | fn string_size(str: &str) -> u32 { 53 | ((2 + str.len() + 3) & !3) as u32 54 | } 55 | 56 | fn write_value(result: &mut Vec, v: &Value) { 57 | match v { 58 | Value::String(ref s) => { 59 | write_string(result, &s); 60 | } 61 | Value::Double(d) => { 62 | let bits = unsafe { std::mem::transmute::(*d) }; 63 | result.extend_from_slice(&write_u32((bits & 0xffff_ffff) as u32)); 64 | result.extend_from_slice(&write_u32((bits >> 32) as u32)); 65 | } 66 | Value::Bool(_) => {} // value encoded in header 67 | } 68 | } 69 | 70 | fn compute_header(v: &Value, off: u32) -> u32 { 71 | match v { 72 | Value::String(_) => (3 | (1 << 3)) | (off << 5), // FIXME: Assume ascii (as does all the code) 73 | Value::Double(_) => 2 | (off << 5), 74 | Value::Bool(v) => 1 | ((*v as u32) << 5), 75 | } 76 | } 77 | 78 | fn compute_size(v: &Value) -> u32 { 79 | match v { 80 | Value::String(ref s) => string_size(&s), 81 | Value::Double(_) => 8, 82 | Value::Bool(_) => 0, 83 | } 84 | } 85 | 86 | pub fn serialize(obj: &[(&'static str, Value)]) -> Vec { 87 | let mut result: Vec = Vec::new(); 88 | let mut size = 12; 89 | for e in obj { 90 | size += string_size(e.0) + compute_size(&e.1) + 8; 91 | } 92 | 93 | result.extend_from_slice(&write_u32(size as u32)); 94 | result.extend_from_slice(&write_u32(1 | (obj.len() as u32) << 1)); 95 | result.extend_from_slice(&write_u32(size - (obj.len() as u32) * 4)); 96 | 97 | let mut table: Vec = Vec::new(); 98 | let mut off = 12; 99 | for e in obj { 100 | table.push(off); 101 | off += 4 + string_size(e.0); 102 | let mut h = compute_header(&e.1, off); 103 | h |= 1 << 4; 104 | result.extend_from_slice(&write_u32(h)); 105 | write_string(&mut result, e.0); 106 | write_value(&mut result, &e.1); 107 | off += compute_size(&e.1); 108 | } 109 | for x in table { 110 | result.extend_from_slice(&write_u32(x)); 111 | } 112 | result 113 | } 114 | -------------------------------------------------------------------------------- /qmetaobject_impl/src/simplelistitem_impl.rs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 Olivier Goffart 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 4 | associated documentation files (the "Software"), to deal in the Software without restriction, 5 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 6 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 7 | subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial 10 | portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 13 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 15 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | use proc_macro::TokenStream; 19 | use quote::quote; 20 | use syn::{parse_macro_input, Data, DeriveInput, Ident, Visibility}; 21 | 22 | pub fn derive(input: TokenStream) -> TokenStream { 23 | let input = parse_macro_input!(input as DeriveInput); 24 | let crate_ = super::get_crate(&input); 25 | 26 | let values = if let Data::Struct(ref data) = input.data { 27 | data.fields 28 | .iter() 29 | .filter_map(|field| { 30 | if let Visibility::Public(_) = field.vis { 31 | field.ident.clone() 32 | } else { 33 | None 34 | } 35 | }) 36 | .collect::>() 37 | } else { 38 | panic!("#[derive(SimpleListItem)] is only defined for structs"); 39 | }; 40 | 41 | if values.is_empty() { 42 | panic!("#[derive(SimpleListItem)] only expose public named member, and there are none") 43 | } 44 | 45 | let arms = values 46 | .iter() 47 | .enumerate() 48 | .map(|(i, ref ident)| { 49 | let i = i as i32; 50 | quote! { #i => #crate_::QMetaType::to_qvariant(&self.#ident), } 51 | }) 52 | .collect::>(); 53 | 54 | let name = &input.ident; 55 | let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); 56 | 57 | quote!( 58 | impl #impl_generics #crate_::listmodel::SimpleListItem for #name #ty_generics #where_clause { 59 | fn get(&self, idx : i32) -> #crate_::QVariant { 60 | match idx { 61 | #(#arms)* 62 | _ => #crate_::QVariant::default() 63 | } 64 | } 65 | fn names() -> Vec<#crate_::QByteArray> { 66 | vec![ #(#crate_::QByteArray::from(stringify!(#values))),* ] 67 | } 68 | } 69 | ).into() 70 | } 71 | -------------------------------------------------------------------------------- /qttypes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "qttypes" 3 | version = "0.2.12" 4 | edition = "2018" 5 | authors = ["Olivier Goffart "] 6 | build = "build.rs" 7 | description = "Manually maintained buildings for Qt value types" 8 | readme = "README.md" 9 | license = "MIT" 10 | categories = ["api-bindings"] 11 | keywords = ["Qt", "QtCore", "QtGui"] 12 | repository = "https://github.com/woboq/qmetaobject-rs" 13 | links = "qt" 14 | rust-version = "1.56" 15 | 16 | [features] 17 | # When this feature is enabled (the default), the build script will panic with an error 18 | # if Qt is not found. 19 | required = [] 20 | 21 | # Link against QtQuick 22 | qtquick = [] 23 | # Link against QtWebEngine 24 | qtwebengine = [] 25 | # Link against QtQuickControls2 26 | qtquickcontrols2 = [] 27 | # Link against QtMultimedia 28 | qtmultimedia = [] 29 | # Link against QtMultimediaWidgets 30 | qtmultimediawidgets = [] 31 | # Link against QtSql 32 | qtsql = [] 33 | # Link against QtTest 34 | qttest = [] 35 | 36 | default = ["required"] 37 | 38 | [dependencies] 39 | cpp = "0.5.6" 40 | chrono = { version = "0.4", optional = true } 41 | 42 | [build-dependencies] 43 | cpp_build = "0.5.6" 44 | semver = "1" 45 | 46 | [dev-dependencies] 47 | cpp_build = "0.5.6" 48 | tempfile = { version = "3.4.0", default-features = false } 49 | 50 | [package.metadata.docs.rs] 51 | dependencies = [ "qtbase5-dev", "qtdeclarative5-dev" ] 52 | -------------------------------------------------------------------------------- /qttypes/README.md: -------------------------------------------------------------------------------- 1 | # qttypes 2 | 3 | This crate contains manually generated bindings to Qt basic value types. 4 | It is meant to be used by other crates, such as the `qmetaobject` crate which re-expose them 5 | 6 | The Qt types are basically exposed using the `cpp` crate. They have manually writen rust idiomatic 7 | API which expose the C++ API. 8 | These types are the direct equivalent of the Qt types and are exposed on the stack. 9 | 10 | In addition, the build script of this crate expose some metadata to downstream crate that also 11 | want to use Qt's C++ API: 12 | - `DEP_QT_VERSION`: The Qt version as given by qmake 13 | - `DEP_QT_INCLUDE_PATH`: The include directory to give to the `cpp_build` crate to locate the Qt headers 14 | - `DEP_QT_LIBRARY_PATH`: The path containing the Qt libraries. 15 | 16 | See the [crate documentation](https://docs.rs/qttypes) for more info. 17 | 18 | ## Philosophy 19 | 20 | The goal of this crate is to expose a idiomatic Qt API for the core value type classes. 21 | The API is manually generated to expose required feature in the most rust-like API, while 22 | still keeping the similarities with the Qt API itself. 23 | 24 | It is not meant to expose all of the Qt API exhaustively, but only the part which is 25 | relevant for the usage in other crate. 26 | If you see a feature missing, feel free to write a issue or a pull request. 27 | 28 | Note that this crate concentrate on the value types, not the widgets or the 29 | the `QObject`. For that, there is the `qmetaobject` crate. -------------------------------------------------------------------------------- /qttypes/src/qtcore/mod.rs: -------------------------------------------------------------------------------- 1 | mod primitives; 2 | mod qbytearray; 3 | mod qchar; 4 | mod qlist; 5 | mod qsettings; 6 | mod qstring; 7 | mod qurl; 8 | mod qvariant; 9 | 10 | pub use self::primitives::qreal; 11 | pub use self::qbytearray::QByteArray; 12 | pub use self::qchar::UnicodeVersion; 13 | pub use self::qlist::{QListIterator, QStringList, QVariantList}; 14 | pub use self::qsettings::QSettings; 15 | pub use self::qstring::{NormalizationForm, QString}; 16 | pub use self::qurl::QUrl; 17 | pub use self::qvariant::QVariant; 18 | -------------------------------------------------------------------------------- /qttypes/src/qtcore/primitives.rs: -------------------------------------------------------------------------------- 1 | /// Bindings for [`qreal`][type] typedef. 2 | /// 3 | /// [type]: https://doc.qt.io/qt-5/qtglobal.html#qreal-typedef 4 | #[allow(non_camel_case_types)] 5 | #[cfg(qreal_is_float)] 6 | pub type qreal = f32; 7 | 8 | #[allow(non_camel_case_types)] 9 | #[cfg(not(qreal_is_float))] 10 | pub type qreal = f64; 11 | -------------------------------------------------------------------------------- /qttypes/src/qtcore/qbytearray.rs: -------------------------------------------------------------------------------- 1 | use crate::internal_prelude::*; 2 | use crate::QString; 3 | use std::fmt::Display; 4 | use std::os::raw::c_char; 5 | use std::str::Utf8Error; 6 | 7 | cpp! {{ 8 | #include 9 | #include 10 | }} 11 | 12 | cpp_class!( 13 | /// Wrapper around [`QByteArray`][class] class. 14 | /// 15 | /// [class]: https://doc.qt.io/qt-5/qbytearray.html 16 | #[derive(PartialEq, PartialOrd, Eq, Ord)] 17 | pub unsafe struct QByteArray as "QByteArray" 18 | ); 19 | impl QByteArray { 20 | pub fn to_slice(&self) -> &[u8] { 21 | unsafe { 22 | let mut size: usize = 0; 23 | let c_ptr = cpp!([self as "const QByteArray*", mut size as "size_t"] -> *const u8 as "const char*" { 24 | size = self->size(); 25 | return self->constData(); 26 | }); 27 | std::slice::from_raw_parts(c_ptr, size) 28 | } 29 | } 30 | pub fn to_str(&self) -> Result<&str, Utf8Error> { 31 | std::str::from_utf8(self.to_slice()) 32 | } 33 | } 34 | impl<'a> From<&'a [u8]> for QByteArray { 35 | /// Constructs a `QByteArray` from a slice. (Copy the slice.) 36 | fn from(s: &'a [u8]) -> QByteArray { 37 | let len = s.len(); 38 | let ptr = s.as_ptr(); 39 | cpp!(unsafe [len as "size_t", ptr as "char*"] -> QByteArray as "QByteArray" { 40 | return QByteArray(ptr, len); 41 | }) 42 | } 43 | } 44 | impl<'a> From<&'a str> for QByteArray { 45 | /// Constructs a `QByteArray` from a `&str`. (Copy the string.) 46 | fn from(s: &'a str) -> QByteArray { 47 | s.as_bytes().into() 48 | } 49 | } 50 | impl From for QByteArray { 51 | /// Constructs a `QByteArray` from a `String`. (Copy the string.) 52 | fn from(s: String) -> QByteArray { 53 | QByteArray::from(&*s) 54 | } 55 | } 56 | impl From for QByteArray { 57 | /// Converts a `QString` to a `QByteArray` 58 | fn from(s: QString) -> QByteArray { 59 | cpp!(unsafe [s as "QString"] -> QByteArray as "QByteArray" { 60 | return std::move(s).toUtf8(); 61 | }) 62 | } 63 | } 64 | impl Display for QByteArray { 65 | /// Prints the contents of the `QByteArray` if it contains UTF-8, do nothing otherwise. 66 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 67 | unsafe { 68 | let c_ptr = cpp!([self as "const QByteArray*"] -> *const c_char as "const char*" { 69 | return self->constData(); 70 | }); 71 | f.write_str(std::ffi::CStr::from_ptr(c_ptr).to_str().map_err(|_| Default::default())?) 72 | } 73 | } 74 | } 75 | impl std::fmt::Debug for QByteArray { 76 | /// Prints the contents of the `QByteArray` if it contains UTF-8, nothing otherwise 77 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 78 | write!(f, "{}", self) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /qttypes/src/qtcore/qchar.rs: -------------------------------------------------------------------------------- 1 | /// Bindings for [`QChar::UnicodeVersion`][enum] enum. 2 | /// 3 | /// [enum]: https://doc.qt.io/qt-5/qchar.html#UnicodeVersion-enum 4 | #[repr(C)] 5 | #[derive(Clone, Copy, PartialEq, Debug)] 6 | #[allow(non_camel_case_types)] 7 | pub enum UnicodeVersion { 8 | Unicode_Unassigned = 0, 9 | Unicode_1_1 = 1, 10 | Unicode_2_0 = 2, 11 | Unicode_2_1_2 = 3, 12 | Unicode_3_0 = 4, 13 | Unicode_3_1 = 5, 14 | Unicode_3_2 = 6, 15 | Unicode_4_0 = 7, 16 | Unicode_4_1 = 8, 17 | Unicode_5_0 = 9, 18 | Unicode_5_1 = 10, 19 | Unicode_5_2 = 11, 20 | Unicode_6_0 = 12, 21 | Unicode_6_1 = 13, 22 | Unicode_6_2 = 14, 23 | Unicode_6_3 = 15, 24 | Unicode_7_0 = 16, 25 | Unicode_8_0 = 17, 26 | #[cfg(qt_5_11)] 27 | Unicode_9_0 = 18, 28 | #[cfg(qt_5_11)] 29 | Unicode_10_0 = 19, 30 | #[cfg(qt_5_15)] 31 | Unicode_11_0 = 20, 32 | #[cfg(qt_5_15)] 33 | Unicode_12_0 = 21, 34 | #[cfg(qt_5_15)] 35 | Unicode_12_1 = 22, 36 | #[cfg(qt_5_15)] 37 | Unicode_13_0 = 23, 38 | } 39 | -------------------------------------------------------------------------------- /qttypes/src/qtcore/qlist/common.rs: -------------------------------------------------------------------------------- 1 | use std::{marker::PhantomData, ops::Index}; 2 | 3 | /// Internal class used to iterate over a [`QList`][] 4 | /// 5 | /// [`QList`]: https://doc.qt.io/qt-5/qlist.html 6 | pub struct QListIterator<'a, T, I> 7 | where 8 | T: Index, 9 | { 10 | list: &'a T, 11 | index: usize, 12 | size: usize, 13 | item: PhantomData<&'a I>, 14 | } 15 | 16 | impl<'a, T, I> QListIterator<'a, T, I> 17 | where 18 | T: Index, 19 | { 20 | pub fn new(list: &'a T, index: usize, size: usize) -> Self { 21 | Self { list, index, size, item: PhantomData } 22 | } 23 | } 24 | 25 | impl<'a, T, I> Iterator for QListIterator<'a, T, I> 26 | where 27 | T: Index, 28 | { 29 | type Item = &'a I; 30 | 31 | fn next(&mut self) -> Option { 32 | if self.index == self.size { 33 | None 34 | } else { 35 | self.index += 1; 36 | Some(&self.list[self.index - 1]) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /qttypes/src/qtcore/qlist/mod.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | mod qstringlist; 3 | mod qvariantlist; 4 | 5 | pub use self::common::QListIterator; 6 | pub use self::qstringlist::QStringList; 7 | pub use self::qvariantlist::QVariantList; 8 | -------------------------------------------------------------------------------- /qttypes/src/qtcore/qlist/qstringlist.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt, iter::FromIterator, ops::Index}; 2 | 3 | use crate::internal_prelude::*; 4 | 5 | use crate::QString; 6 | 7 | use super::common::QListIterator; 8 | 9 | cpp_class!( 10 | /// Wrapper around [`QStringList`][class] class. 11 | /// 12 | /// [class]: https://doc.qt.io/qt-5/qstringlist.html 13 | #[derive(Default, Clone, PartialEq, Eq)] 14 | pub unsafe struct QStringList as "QStringList" 15 | ); 16 | impl QStringList { 17 | pub fn new() -> QStringList { 18 | cpp!(unsafe [] -> QStringList as "QStringList" { 19 | return QStringList(); 20 | }) 21 | } 22 | 23 | pub fn insert(&mut self, index: usize, value: QString) { 24 | cpp!(unsafe [self as "QStringList*", index as "size_t", value as "QString"] { 25 | self->insert(index, value); 26 | }); 27 | } 28 | 29 | pub fn push(&mut self, value: QString) { 30 | cpp!(unsafe [self as "QStringList*", value as "QString"] { 31 | self->append(value); 32 | }); 33 | } 34 | 35 | pub fn clear(&mut self) { 36 | cpp!(unsafe [self as "QStringList*"] { 37 | self->clear(); 38 | }); 39 | } 40 | 41 | pub fn remove(&mut self, index: usize) { 42 | cpp!(unsafe [self as "QStringList*", index as "size_t"] { 43 | self->removeAt(index); 44 | }) 45 | } 46 | 47 | pub fn len(&self) -> usize { 48 | cpp!(unsafe [self as "QStringList*"] -> usize as "size_t" { return self->size(); }) 49 | } 50 | } 51 | 52 | impl Index for QStringList { 53 | type Output = QString; 54 | 55 | fn index(&self, index: usize) -> &Self::Output { 56 | unsafe { 57 | &*cpp!([self as "QStringList*", index as "size_t"] -> *const QString as "const QString*" { 58 | return &(*self)[index]; 59 | }) 60 | } 61 | } 62 | } 63 | 64 | impl fmt::Debug for QStringList { 65 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 66 | f.debug_list().entries(self.into_iter()).finish() 67 | } 68 | } 69 | 70 | impl From<[T; N]> for QStringList 71 | where 72 | QString: From, 73 | { 74 | fn from(s: [T; N]) -> Self { 75 | let mut list = QStringList::new(); 76 | for i in s { 77 | list.push(QString::from(i)); 78 | } 79 | list 80 | } 81 | } 82 | 83 | impl From> for QStringList 84 | where 85 | QString: From, 86 | { 87 | fn from(s: Vec) -> Self { 88 | s.into_iter().map(QString::from).collect() 89 | } 90 | } 91 | 92 | impl From<&[T]> for QStringList 93 | where 94 | T: Clone, 95 | QString: From, 96 | { 97 | fn from(s: &[T]) -> Self { 98 | s.iter().cloned().map(QString::from).collect() 99 | } 100 | } 101 | 102 | impl From for Vec 103 | where 104 | T: Clone, 105 | QString: Into, 106 | { 107 | fn from(arr: QStringList) -> Self { 108 | arr.into_iter().cloned().map(|x| x.into()).collect() 109 | } 110 | } 111 | 112 | impl<'a> IntoIterator for &'a QStringList { 113 | type Item = &'a QString; 114 | type IntoIter = QListIterator<'a, QStringList, QString>; 115 | 116 | fn into_iter(self) -> Self::IntoIter { 117 | QListIterator::new(self, 0, self.len()) 118 | } 119 | } 120 | 121 | impl FromIterator for QStringList 122 | where 123 | T: Into, 124 | { 125 | fn from_iter>(iter: I) -> Self { 126 | let mut l = QStringList::default(); 127 | for i in iter { 128 | l.push(i.into()); 129 | } 130 | l 131 | } 132 | } 133 | 134 | #[cfg(test)] 135 | mod tests { 136 | use super::*; 137 | 138 | #[test] 139 | fn test_qstringlist() { 140 | let mut qstringlist = QStringList::new(); 141 | qstringlist.push("One".into()); 142 | qstringlist.push("Two".into()); 143 | 144 | assert_eq!(qstringlist.len(), 2); 145 | assert_eq!(qstringlist[0], QString::from("One")); 146 | 147 | qstringlist.remove(0); 148 | assert_eq!(qstringlist[0], QString::from("Two")); 149 | 150 | qstringlist.insert(0, "Three".into()); 151 | assert_eq!(qstringlist[0], QString::from("Three")); 152 | 153 | qstringlist.clear(); 154 | assert_eq!(qstringlist.len(), 0); 155 | } 156 | 157 | #[test] 158 | fn test_qstringlist_from_iter() { 159 | let v = vec!["abc", "efg", "hij"]; 160 | let qvl: QStringList = v.clone().into_iter().collect(); 161 | assert_eq!(qvl.len(), 3); 162 | assert_eq!(qvl[1].to_string(), v[1].to_string()); 163 | } 164 | 165 | #[test] 166 | fn test_vec_from_qstringlist() { 167 | let qstringlist = generate_qstring_list(); 168 | let temp: Vec = qstringlist.clone().into(); 169 | assert_eq!(temp, vec!["Three".to_string(), "Two".to_string()]); 170 | let temp: Vec = qstringlist.clone().into(); 171 | assert_eq!(temp, vec![QString::from("Three"), QString::from("Two")]); 172 | } 173 | 174 | #[test] 175 | fn test_qstringlist_from_slice_ref() { 176 | let qstringlist = generate_qstring_list(); 177 | let t = ["Three", "Two"]; 178 | assert_eq!(qstringlist, QStringList::from(t)); 179 | let t = ["Three".to_string(), "Two".to_string()]; 180 | assert_eq!(qstringlist, QStringList::from(t)); 181 | let t = [QString::from("Three"), QString::from("Two")]; 182 | assert_eq!(qstringlist, QStringList::from(t)); 183 | } 184 | 185 | #[test] 186 | fn test_qstringlist_from_slice() { 187 | let qstringlist = generate_qstring_list(); 188 | 189 | assert_eq!(qstringlist, QStringList::from(["Three", "Two"])); 190 | assert_eq!(qstringlist, QStringList::from(["Three".to_string(), "Two".to_string()])); 191 | assert_eq!(qstringlist, QStringList::from([QString::from("Three"), QString::from("Two")])); 192 | } 193 | 194 | #[test] 195 | fn test_qstringlist_from_vec() { 196 | let qstringlist = generate_qstring_list(); 197 | assert_eq!(qstringlist, QStringList::from(vec!["Three", "Two"])); 198 | assert_eq!(qstringlist, QStringList::from(vec!["Three".to_string(), "Two".to_string()])); 199 | assert_eq!( 200 | qstringlist, 201 | QStringList::from(vec![QString::from("Three"), QString::from("Two")]) 202 | ); 203 | } 204 | 205 | fn generate_qstring_list() -> QStringList { 206 | let mut qstringlist = QStringList::new(); 207 | qstringlist.push("Three".into()); 208 | qstringlist.push("Two".into()); 209 | return qstringlist; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /qttypes/src/qtcore/qlist/qvariantlist.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | iter::FromIterator, 3 | ops::{Index, IndexMut}, 4 | }; 5 | 6 | use crate::internal_prelude::*; 7 | 8 | use super::common::QListIterator; 9 | use crate::QVariant; 10 | 11 | cpp_class!( 12 | /// Wrapper around [`QVariantList`][type] typedef. 13 | /// 14 | /// [type]: https://doc.qt.io/qt-5/qvariant.html#QVariantList-typedef 15 | pub unsafe struct QVariantList as "QVariantList" 16 | ); 17 | impl QVariantList { 18 | /// Wrapper around [`append(const T &)`][method] method. 19 | /// 20 | /// [method]: https://doc.qt.io/qt-5/qlist.html#append 21 | pub fn push(&mut self, value: QVariant) { 22 | cpp!(unsafe [self as "QVariantList*", value as "QVariant"] { 23 | self->append(std::move(value)); 24 | }) 25 | } 26 | 27 | /// Wrapper around [`insert(int, const QVariant &)`][method] method. 28 | /// 29 | /// [method]: https://doc.qt.io/qt-5/qlist.html#insert 30 | pub fn insert(&mut self, index: usize, element: QVariant) { 31 | cpp!(unsafe [self as "QVariantList*", index as "size_t", element as "QVariant"] { 32 | self->insert(index, std::move(element)); 33 | }) 34 | } 35 | 36 | /// Wrapper around [`takeAt(int)`][method] method. 37 | /// 38 | /// [method]: https://doc.qt.io/qt-5/qlist.html#takeAt 39 | pub fn remove(&mut self, index: usize) -> QVariant { 40 | cpp!(unsafe [self as "QVariantList*", index as "size_t"] -> QVariant as "QVariant" { 41 | return self->takeAt(index); 42 | }) 43 | } 44 | 45 | /// Wrapper around [`size()`][method] method. 46 | /// 47 | /// [method]: https://doc.qt.io/qt-5/qlist.html#size 48 | pub fn len(&self) -> usize { 49 | cpp!(unsafe [self as "QVariantList*"] -> usize as "size_t" { 50 | return self->size(); 51 | }) 52 | } 53 | 54 | /// Wrapper around [`isEmpty()`][method] method. 55 | /// 56 | /// [method]: https://doc.qt.io/qt-5/qlist.html#isEmpty 57 | pub fn is_empty(&self) -> bool { 58 | cpp!(unsafe [self as "QVariantList*"] -> bool as "bool" { 59 | return self->isEmpty(); 60 | }) 61 | } 62 | } 63 | 64 | impl Index for QVariantList { 65 | type Output = QVariant; 66 | 67 | /// Wrapper around [`at(int)`][method] method. 68 | /// 69 | /// [method]: https://doc.qt.io/qt-5/qlist.html#at 70 | fn index(&self, index: usize) -> &QVariant { 71 | assert!(index < self.len()); 72 | unsafe { 73 | &*cpp!([self as "QVariantList*", index as "size_t"] -> *const QVariant as "const QVariant*" { 74 | return &self->at(index); 75 | }) 76 | } 77 | } 78 | } 79 | 80 | impl IndexMut for QVariantList { 81 | /// Wrapper around [`operator[](int)`][method] operator method. 82 | /// 83 | /// [method]: https://doc.qt.io/qt-5/qlist.html#operator-5b-5d 84 | fn index_mut(&mut self, index: usize) -> &mut QVariant { 85 | assert!(index < self.len()); 86 | unsafe { 87 | &mut *cpp!([self as "QVariantList*", index as "size_t"] -> *mut QVariant as "QVariant*" { 88 | return &(*self)[index]; 89 | }) 90 | } 91 | } 92 | } 93 | 94 | impl std::fmt::Debug for QVariantList { 95 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 96 | f.debug_map().entries(self.into_iter().enumerate()).finish() 97 | } 98 | } 99 | 100 | impl<'a> IntoIterator for &'a QVariantList { 101 | type Item = &'a QVariant; 102 | type IntoIter = QListIterator<'a, QVariantList, QVariant>; 103 | 104 | fn into_iter(self) -> Self::IntoIter { 105 | QListIterator::new(self, 0, self.len()) 106 | } 107 | } 108 | 109 | impl FromIterator for QVariantList 110 | where 111 | T: Into, 112 | { 113 | fn from_iter>(iter: I) -> QVariantList { 114 | let mut l = QVariantList::default(); 115 | for i in iter { 116 | l.push(i.into()); 117 | } 118 | l 119 | } 120 | } 121 | 122 | #[cfg(test)] 123 | mod tests { 124 | use super::*; 125 | use crate::{QByteArray, QString}; 126 | 127 | #[test] 128 | fn test_qvariantlist() { 129 | let mut q = QVariantList::default(); 130 | q.push(42.into()); 131 | q.push(QString::from("Hello").into()); 132 | q.push(QByteArray::from("Hello").into()); 133 | assert_eq!(q[0].to_qbytearray().to_string(), "42"); 134 | assert_eq!(q[1].to_qbytearray().to_string(), "Hello"); 135 | assert_eq!(q[2].to_qbytearray().to_string(), "Hello"); 136 | let x: Vec = q.into_iter().map(|x| x.to_qbytearray()).collect(); 137 | assert_eq!(x[0].to_string(), "42"); 138 | assert_eq!(x[1].to_string(), "Hello"); 139 | assert_eq!(x[2].to_string(), "Hello"); 140 | } 141 | 142 | #[test] 143 | fn test_qvariantlist_from_iter() { 144 | let v = vec![1u32, 2u32, 3u32]; 145 | let qvl: QVariantList = v.iter().collect(); 146 | assert_eq!(qvl.len(), 3); 147 | assert_eq!(qvl[1].to_qbytearray().to_string(), "2"); 148 | } 149 | 150 | #[test] 151 | fn test_qstring_and_qbytearray() { 152 | let qba1: QByteArray = (b"hello" as &[u8]).into(); 153 | let qba2: QByteArray = "hello".into(); 154 | let s: String = "hello".into(); 155 | let qba3: QByteArray = s.clone().into(); 156 | 157 | assert_eq!(qba1.to_string(), "hello"); 158 | assert_eq!(qba2.to_string(), "hello"); 159 | assert_eq!(qba3.to_string(), "hello"); 160 | 161 | let qs1: QString = "hello".into(); 162 | let qs2: QString = s.into(); 163 | let qba4: QByteArray = qs1.clone().into(); 164 | 165 | assert_eq!(qs1.to_string(), "hello"); 166 | assert_eq!(qs2.to_string(), "hello"); 167 | assert_eq!(qba4.to_string(), "hello"); 168 | } 169 | 170 | #[test] 171 | fn qvariantlist_debug() { 172 | let mut list = QVariantList::default(); 173 | list.push(42.into()); 174 | list.push(QString::from("String!").into()); 175 | list.push(QByteArray::from("Bytearray!").into()); 176 | assert_eq!( 177 | format!("{:?}", list), 178 | "{0: QVariant(int: \"42\"), 1: QVariant(QString: \"String!\"), 2: QVariant(QByteArray: \"Bytearray!\")}" 179 | ); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /qttypes/src/qtcore/qsettings.rs: -------------------------------------------------------------------------------- 1 | use crate::internal_prelude::*; 2 | use crate::QString; 3 | 4 | cpp! {{ 5 | #include 6 | #include 7 | #include 8 | }} 9 | 10 | cpp_class!( 11 | /// Wrapper around [`QSettings`][class] class. 12 | /// 13 | /// [class]: https://doc.qt.io/qt-5/qsettings.html 14 | pub unsafe struct QSettings as "std::unique_ptr" 15 | ); 16 | 17 | impl QSettings { 18 | /// Wrapper around [`QSettings(const QString &organization, const QString &application = QString(), QObject *parent = nullptr)`][ctor] constructor. 19 | /// 20 | /// Note: Under the hood it uses `QSettings(format, scope, org, app)` (like Qt does internally already), 21 | /// with setting `format` set to `IniFormat` and `scope` to (default) `UserScope` 22 | /// 23 | /// [ctor]: https://doc.qt.io/qt-5/qsettings.html#QSettings-3 24 | pub fn new(organization: &str, application: &str) -> Self { 25 | let organization = QString::from(organization); 26 | let application = QString::from(application); 27 | cpp!( 28 | unsafe [organization as "QString", application as "QString"] -> QSettings as "std::unique_ptr" { 29 | return std::unique_ptr(new QSettings(QSettings::IniFormat, QSettings::UserScope, organization, application)); 30 | } 31 | ) 32 | } 33 | 34 | /// Wrapper around [`QSettings(const QString &fileName, QSettings::Format format, QObject *parent = nullptr)`][ctor] constructor. 35 | /// 36 | /// [ctor]: https://doc.qt.io/qt-5/qsettings.html#QSettings 37 | pub fn from_path(file_name: &str) -> Self { 38 | let file_name = QString::from(file_name); 39 | cpp!( 40 | unsafe [file_name as "QString"] -> QSettings as "std::unique_ptr" { 41 | return std::unique_ptr(new QSettings(file_name, QSettings::IniFormat)); 42 | } 43 | ) 44 | } 45 | 46 | pub fn filename(&self) -> String { 47 | let filename: QString = cpp!( 48 | unsafe [self as "QSettings **"] -> QString as "QString" { 49 | return (*self)->fileName(); 50 | } 51 | ); 52 | filename.to_string() 53 | } 54 | 55 | pub fn contains(&self, key: &str) -> bool { 56 | let key = QString::from(key); 57 | unsafe { 58 | cpp!([self as "QSettings **", key as "QString"] -> bool as "bool" { 59 | return (*self)->contains(key); 60 | }) 61 | } 62 | } 63 | 64 | pub fn value_bool(&self, key: &str) -> bool { 65 | let key = QString::from(key); 66 | unsafe { 67 | cpp!([self as "QSettings **", key as "QString"] -> bool as "bool" { 68 | return (*self)->value(key).toBool(); 69 | }) 70 | } 71 | } 72 | 73 | pub fn set_bool(&mut self, key: &str, value: bool) { 74 | let key = QString::from(key); 75 | unsafe { 76 | cpp!([self as "QSettings **", key as "QString", value as "bool"] { 77 | (*self)->setValue(key, value); 78 | }) 79 | }; 80 | } 81 | 82 | pub fn value_string(&self, key: &str) -> String { 83 | let key = QString::from(key); 84 | let val = unsafe { 85 | cpp!([self as "QSettings **", key as "QString"] -> QString as "QString" { 86 | return (*self)->value(key).toString(); 87 | }) 88 | }; 89 | val.into() 90 | } 91 | 92 | pub fn set_string(&mut self, key: &str, value: &str) { 93 | let key = QString::from(key); 94 | let value = QString::from(value); 95 | unsafe { 96 | cpp!([self as "QSettings **", key as "QString", value as "QString"] { 97 | (*self)->setValue(key, value); 98 | }) 99 | }; 100 | } 101 | 102 | pub fn sync(&self) { 103 | unsafe { 104 | cpp!([self as "QSettings **"] { 105 | (*self)->sync(); 106 | }) 107 | }; 108 | } 109 | } 110 | 111 | #[test] 112 | fn test_qsettings_filename() { 113 | let qsettings = QSettings::new("qmetaobject", "qsettings"); 114 | 115 | assert!( 116 | qsettings.filename().ends_with("/qmetaobject/qsettings.ini"), 117 | "'{}' does not end with '/qmetaobject/qsettings.ini'", 118 | qsettings.filename() 119 | ); 120 | } 121 | 122 | #[test] 123 | fn test_qsettings_new_from_path() { 124 | let qsettings = QSettings::from_path("/tmp/my_settings.conf"); 125 | 126 | assert!( 127 | qsettings.filename().ends_with("/tmp/my_settings.conf"), 128 | "'{}' does not end with '/tmp/my_settings.conf'", 129 | qsettings.filename() 130 | ); 131 | } 132 | 133 | #[test] 134 | fn test_qsettings_values() { 135 | let temp_dir = tempfile::tempdir().unwrap(); 136 | let config_pathbuf = temp_dir.path().join("qsettings.conf"); 137 | let config_file = config_pathbuf.to_str().unwrap(); 138 | 139 | let mut qsettings = QSettings::from_path(config_file); 140 | 141 | qsettings.set_bool("test_true", false); 142 | qsettings.set_bool("test_false", true); 143 | qsettings.set_string("test_empty", ""); 144 | qsettings.set_string("test_string", "Lorem Ipsum"); 145 | qsettings.set_string("test_emoji", "🦀"); 146 | 147 | qsettings.sync(); 148 | 149 | assert_eq!(qsettings.value_bool("test_true"), false); 150 | assert_eq!(qsettings.value_bool("test_false"), true); 151 | assert_eq!(qsettings.value_string("test_empty"), ""); 152 | assert_eq!(qsettings.value_string("test_string"), "Lorem Ipsum"); 153 | assert_eq!(qsettings.value_string("test_emoji"), "🦀"); 154 | 155 | drop(qsettings); 156 | 157 | let qsettings = QSettings::from_path(config_file); 158 | 159 | assert_eq!(qsettings.value_bool("test_true"), false); 160 | assert_eq!(qsettings.value_bool("test_false"), true); 161 | assert_eq!(qsettings.value_string("test_empty"), ""); 162 | assert_eq!(qsettings.value_string("test_string"), "Lorem Ipsum"); 163 | assert_eq!(qsettings.value_string("test_emoji"), "🦀"); 164 | 165 | drop(qsettings); 166 | 167 | drop(temp_dir); 168 | assert!(!config_pathbuf.as_path().exists()); 169 | } 170 | -------------------------------------------------------------------------------- /qttypes/src/qtcore/qurl.rs: -------------------------------------------------------------------------------- 1 | use crate::internal_prelude::*; 2 | use crate::QString; 3 | 4 | cpp! {{ 5 | #include 6 | #include 7 | }} 8 | 9 | cpp_class!( 10 | /// Wrapper around [`QUrl`][class] class. 11 | /// 12 | /// [class]: https://doc.qt.io/qt-5/qurl.html 13 | #[derive(PartialEq, PartialOrd, Eq, Ord)] 14 | pub unsafe struct QUrl as "QUrl" 15 | ); 16 | impl QUrl { 17 | /// Wrapper around [`QUrl::fromUserInput(const QString &userInput)`][method] static method. 18 | /// 19 | /// [method]: https://doc.qt.io/qt-5/qurl.html#fromUserInput 20 | pub fn from_user_input(user_input: QString) -> QUrl { 21 | cpp!(unsafe [user_input as "QString"] -> QUrl as "QUrl" { 22 | return QUrl::fromUserInput(user_input); 23 | }) 24 | } 25 | } 26 | impl From for QUrl { 27 | fn from(qstring: QString) -> QUrl { 28 | cpp!(unsafe [qstring as "QString"] -> QUrl as "QUrl" { 29 | return QUrl(qstring); 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /qttypes/src/qtcore/qvariant.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::{ 4 | cpp, cpp_class, QByteArray, QDate, QDateTime, QString, QStringList, QTime, QUrl, QVariantList, 5 | QVariantMap, 6 | }; 7 | 8 | cpp_class!( 9 | /// Wrapper around [`QVariant`][class] class. 10 | /// 11 | /// [class]: https://doc.qt.io/qt-5/qvariant.html 12 | #[derive(PartialEq, Eq)] 13 | pub unsafe struct QVariant as "QVariant" 14 | ); 15 | impl QVariant { 16 | /// Wrapper around [`toByteArray()`][method] method. 17 | /// 18 | /// [method]: https://doc.qt.io/qt-5/qvariant.html#toByteArray 19 | pub fn to_qbytearray(&self) -> QByteArray { 20 | cpp!(unsafe [self as "const QVariant*"] -> QByteArray as "QByteArray" { 21 | return self->toByteArray(); 22 | }) 23 | } 24 | 25 | /// Wrapper around [`toBool()`][method] method. 26 | /// 27 | /// [method]: https://doc.qt.io/qt-5/qvariant.html#toBool 28 | pub fn to_bool(&self) -> bool { 29 | cpp!(unsafe [self as "const QVariant*"] -> bool as "bool" { 30 | return self->toBool(); 31 | }) 32 | } 33 | 34 | /// Wrapper around [`userType()`][method] method. 35 | /// 36 | /// [method]: https://doc.qt.io/qt-5/qvariant.html#userType 37 | pub fn user_type(&self) -> i32 { 38 | cpp!(unsafe [self as "const QVariant*"] -> i32 as "int" { 39 | return self->userType(); 40 | }) 41 | } 42 | 43 | /// Wrapper around [`isValid()`][method] method. 44 | /// 45 | /// [method]: https://doc.qt.io/qt-5/qvariant.html#isValid 46 | pub fn is_valid(&self) -> bool { 47 | cpp!(unsafe [self as "const QVariant*"] -> bool as "bool" { 48 | return self->isValid(); 49 | }) 50 | } 51 | 52 | /// Wrapper around [`isNull()`][method] method. 53 | /// 54 | /// [method]: https://doc.qt.io/qt-5/qvariant.html#isNull 55 | pub fn is_null(&self) -> bool { 56 | cpp!(unsafe [self as "const QVariant*"] -> bool as "bool" { 57 | return self->isNull(); 58 | }) 59 | } 60 | 61 | /// Wrapper around [`toMap()`][method] method. 62 | /// 63 | /// [method]: https://doc.qt.io/qt-5/qvariant.html#toMap 64 | pub fn to_qvariantmap(&self) -> QVariantMap { 65 | cpp!(unsafe [self as "const QVariant*"] -> QVariantMap as "QVariantMap" { 66 | return self->toMap(); 67 | }) 68 | } 69 | 70 | /// Wrapper around [`toString()`][method] method. 71 | /// 72 | /// [method]: https://doc.qt.io/qt-5/qvariant.html#toString 73 | pub fn to_qstring(&self) -> QString { 74 | cpp!(unsafe [self as "const QVariant*"] -> QString as "QString" { 75 | return self->toString(); 76 | }) 77 | } 78 | 79 | /// Wrapper around [`toInt()`][method] method. 80 | /// 81 | /// [method]: https://doc.qt.io/qt-5/qvariant.html#toInt 82 | pub fn to_int(&self) -> u32 { 83 | cpp!(unsafe [self as "const QVariant*"] -> u32 as "int" { 84 | return self->toInt(); 85 | }) 86 | } 87 | 88 | /// Wrapper around ['typeName()`][method] method. 89 | /// 90 | /// [method]: https://doc.qt.io/qt-5/qvariant.html#typeName 91 | pub fn type_name(&self) -> QString { 92 | cpp!(unsafe [self as "const QVariant*"] -> QString as "QString" { 93 | return self->typeName(); 94 | }) 95 | } 96 | 97 | // FIXME: do more wrappers 98 | } 99 | 100 | impl fmt::Debug for QVariant { 101 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 102 | let data = self.to_qstring().to_string(); 103 | let qtype = self.type_name().to_string(); 104 | if data.len() == 0 { 105 | write!(f, "QVariant({})", qtype.as_str()) 106 | } else { 107 | write!(f, "QVariant({}: \"{}\")", qtype.as_str(), data.as_str()) 108 | } 109 | } 110 | } 111 | 112 | impl From for QVariant { 113 | /// Wrapper around [`QVariant(const QString &)`][ctor] constructor. 114 | /// 115 | /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-14 116 | fn from(a: QString) -> QVariant { 117 | cpp!(unsafe [a as "QString"] -> QVariant as "QVariant" { 118 | return QVariant(a); 119 | }) 120 | } 121 | } 122 | impl From for QVariant { 123 | /// Wrapper around [`QVariant(const QMap &)`][ctor] constructor. 124 | /// 125 | /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-22 126 | fn from(a: QVariantMap) -> QVariant { 127 | cpp!(unsafe [a as "QVariantMap"] -> QVariant as "QVariant" { 128 | return QVariant(a); 129 | }) 130 | } 131 | } 132 | impl From for QVariant { 133 | /// Wrapper around [`QVariant(const QByteArray &)`][ctor] constructor. 134 | /// 135 | /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-12 136 | fn from(a: QByteArray) -> QVariant { 137 | cpp!(unsafe [a as "QByteArray"] -> QVariant as "QVariant" { 138 | return QVariant(a); 139 | }) 140 | } 141 | } 142 | impl From for QVariant { 143 | /// Wrapper around [`QVariant(const QDate &)`][ctor] constructor. 144 | /// 145 | /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-18 146 | fn from(a: QDate) -> QVariant { 147 | cpp!(unsafe [a as "QDate"] -> QVariant as "QVariant" { 148 | return QVariant(a); 149 | }) 150 | } 151 | } 152 | impl From for QVariant { 153 | /// Wrapper around [`QVariant(const QTime &)`][ctor] constructor. 154 | /// 155 | /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-19 156 | fn from(a: QTime) -> QVariant { 157 | cpp!(unsafe [a as "QTime"] -> QVariant as "QVariant" { 158 | return QVariant(a); 159 | }) 160 | } 161 | } 162 | impl From for QVariant { 163 | /// Wrapper around [`QVariant(const QDateTime &)`][ctor] constructor. 164 | /// 165 | /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-20 166 | fn from(a: QDateTime) -> QVariant { 167 | cpp!(unsafe [a as "QDateTime"] -> QVariant as "QVariant" { 168 | return QVariant(a); 169 | }) 170 | } 171 | } 172 | impl From for QVariant { 173 | /// Wrapper around [`QVariant(const QUrl &)`][ctor] constructor. 174 | /// 175 | /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-35 176 | fn from(a: QUrl) -> QVariant { 177 | cpp!(unsafe [a as "QUrl"] -> QVariant as "QVariant" { 178 | return QVariant(a); 179 | }) 180 | } 181 | } 182 | impl From for QVariant { 183 | /// Wrapper around [`QVariant(const QVariantList &)`][ctor] constructor. 184 | /// 185 | /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-21 186 | fn from(a: QVariantList) -> QVariant { 187 | cpp!(unsafe [a as "QVariantList"] -> QVariant as "QVariant" { 188 | return QVariant(a); 189 | }) 190 | } 191 | } 192 | 193 | impl From for QVariant { 194 | /// Wrapper around [`QVariant(const QStringList &)`][ctor] constructor. 195 | /// 196 | /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-16 197 | fn from(a: QStringList) -> Self { 198 | cpp!(unsafe [a as "QStringList"] -> QVariant as "QVariant" { 199 | return QVariant(a); 200 | }) 201 | } 202 | } 203 | 204 | impl From for QVariant { 205 | /// Wrapper around [`QVariant(int)`][ctor] constructor. 206 | /// 207 | /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-4 208 | fn from(a: i32) -> QVariant { 209 | cpp!(unsafe [a as "int"] -> QVariant as "QVariant" { 210 | return QVariant(a); 211 | }) 212 | } 213 | } 214 | impl From for QVariant { 215 | /// Wrapper around [`QVariant(uint)`][ctor] constructor. 216 | /// 217 | /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-5 218 | fn from(a: u32) -> QVariant { 219 | cpp!(unsafe [a as "uint"] -> QVariant as "QVariant" { 220 | return QVariant(a); 221 | }) 222 | } 223 | } 224 | impl From for QVariant { 225 | /// Wrapper around [`QVariant(int)`][ctor] constructor. 226 | /// 227 | /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-4 228 | fn from(a: i64) -> QVariant { 229 | cpp!(unsafe [a as "qlonglong"] -> QVariant as "QVariant" { 230 | return QVariant(a); 231 | }) 232 | } 233 | } 234 | impl From for QVariant { 235 | /// Wrapper around [`QVariant(uint)`][ctor] constructor. 236 | /// 237 | /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-5 238 | fn from(a: u64) -> QVariant { 239 | cpp!(unsafe [a as "qulonglong"] -> QVariant as "QVariant" { 240 | return QVariant(a); 241 | }) 242 | } 243 | } 244 | impl From for QVariant { 245 | /// Wrapper around [`QVariant(float)`][ctor] constructor. 246 | /// 247 | /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-10 248 | fn from(a: f32) -> QVariant { 249 | cpp!(unsafe [a as "float"] -> QVariant as "QVariant" { 250 | return QVariant(a); 251 | }) 252 | } 253 | } 254 | impl From for QVariant { 255 | /// Wrapper around [`QVariant(double)`][ctor] constructor. 256 | /// 257 | /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-9 258 | fn from(a: f64) -> QVariant { 259 | cpp!(unsafe [a as "double"] -> QVariant as "QVariant" { 260 | return QVariant(a); 261 | }) 262 | } 263 | } 264 | impl From for QVariant { 265 | /// Wrapper around [`QVariant(bool)`][ctor] constructor. 266 | /// 267 | /// [ctor]: https://doc.qt.io/qt-5/qvariant.html#QVariant-8 268 | fn from(a: bool) -> QVariant { 269 | cpp!(unsafe [a as "bool"] -> QVariant as "QVariant" { 270 | return QVariant(a); 271 | }) 272 | } 273 | } 274 | impl<'a, T> From<&'a T> for QVariant 275 | where 276 | T: Into + Clone, 277 | { 278 | fn from(a: &'a T) -> QVariant { 279 | (*a).clone().into() 280 | } 281 | } 282 | 283 | #[cfg(test)] 284 | mod tests { 285 | use super::*; 286 | 287 | #[test] 288 | fn qvariant_debug_qstring() { 289 | let qv: QVariant = QString::from("Hello, QVariant!").into(); 290 | assert_eq!(qv.to_qstring().to_string(), "Hello, QVariant!"); 291 | assert_eq!(format!("{:?}", qv), "QVariant(QString: \"Hello, QVariant!\")"); 292 | } 293 | 294 | #[test] 295 | fn qvariant_debug_bool() { 296 | let qv = QVariant::from(false); 297 | assert_eq!(qv.to_qstring().to_string(), String::from("false")); 298 | assert_eq!(format!("{:?}", qv), "QVariant(bool: \"false\")"); 299 | } 300 | 301 | #[test] 302 | fn qvariant_debug_int() { 303 | let qv = QVariant::from(313); 304 | assert_eq!(qv.to_int(), 313); 305 | assert_eq!(format!("{:?}", qv), "QVariant(int: \"313\")"); 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /qttypes/src/qtgui/mod.rs: -------------------------------------------------------------------------------- 1 | mod qcolor; 2 | pub use self::qcolor::{QColor, QColorNameFormat, QColorSpec, QRgb, QRgba64}; 3 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | use_small_heuristics = "Max" 2 | --------------------------------------------------------------------------------