diff --git a/CHANGELOG.md b/CHANGELOG.md index a03526840..309bde47b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Support for further types: `QLine`, `QLineF`, `QImage`, `QPainter`, `QFont`, `QPen`, `QPolygon`, `QPolygonF` +- Support for further types: `QLine`, `QLineF`, `QImage`, `QPainter`, `QFont`, `QPen`, `QPolygon`, `QPolygonF`, `QFontMetrics` - `internal_pointer_mut()` function on `QModelIndex` - `c_void` in CXX-Qt-lib for easy access to `void *` - `CxxQtThread` is now marked as `Sync` so that it can be used by reference diff --git a/crates/cxx-qt-lib-headers/include/gui/qfontmetrics.h b/crates/cxx-qt-lib-headers/include/gui/qfontmetrics.h new file mode 100644 index 000000000..355627133 --- /dev/null +++ b/crates/cxx-qt-lib-headers/include/gui/qfontmetrics.h @@ -0,0 +1,22 @@ +// clang-format off +// SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company +// clang-format on +// SPDX-FileContributor: Laurent Montel +// +// SPDX-License-Identifier: MIT OR Apache-2.0 +#pragma once + +#include + +#include "rust/cxx.h" + +// Define namespace otherwise we hit a GCC bug +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 +namespace rust { + +template<> +struct IsRelocatable : ::std::true_type +{ +}; + +} // namespace rust diff --git a/crates/cxx-qt-lib-headers/src/lib.rs b/crates/cxx-qt-lib-headers/src/lib.rs index 9e25d96ff..815ff572b 100644 --- a/crates/cxx-qt-lib-headers/src/lib.rs +++ b/crates/cxx-qt-lib-headers/src/lib.rs @@ -70,6 +70,11 @@ pub fn build_opts() -> cxx_qt_build::CxxQtBuildersOpts { #[cfg(feature = "qt_gui")] (include_str!("../include/gui/qfont.h"), "qfont.h"), #[cfg(feature = "qt_gui")] + ( + include_str!("../include/gui/qfontmetrics.h"), + "qfontmetrics.h", + ), + #[cfg(feature = "qt_gui")] ( include_str!("../include/gui/qguiapplication.h"), "qguiapplication.h", diff --git a/crates/cxx-qt-lib/build.rs b/crates/cxx-qt-lib/build.rs index de6bbb0c5..af7722fb4 100644 --- a/crates/cxx-qt-lib/build.rs +++ b/crates/cxx-qt-lib/build.rs @@ -141,6 +141,7 @@ fn main() { "core/qvector/qvector_qcolor", "gui/qcolor", "gui/qfont", + "gui/qfontmetrics", "gui/qguiapplication", "gui/qimage", "gui/qpainterpath", @@ -201,6 +202,7 @@ fn main() { cpp_files.extend([ "gui/qcolor", "gui/qfont", + "gui/qfontmetrics", "gui/qguiapplication", "gui/qimage", "gui/qpainterpath", diff --git a/crates/cxx-qt-lib/src/gui/mod.rs b/crates/cxx-qt-lib/src/gui/mod.rs index bf5c18937..31c65c839 100644 --- a/crates/cxx-qt-lib/src/gui/mod.rs +++ b/crates/cxx-qt-lib/src/gui/mod.rs @@ -36,6 +36,9 @@ pub use qfont::{ QFontStyleHint, QFontStyleStrategy, }; +mod qfontmetrics; +pub use qfontmetrics::QFontMetrics; + mod qpainterpath; pub use qpainterpath::QPainterPath; diff --git a/crates/cxx-qt-lib/src/gui/qfontmetrics.cpp b/crates/cxx-qt-lib/src/gui/qfontmetrics.cpp new file mode 100644 index 000000000..55629ab47 --- /dev/null +++ b/crates/cxx-qt-lib/src/gui/qfontmetrics.cpp @@ -0,0 +1,22 @@ +// clang-format off +// SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company +// clang-format on +// SPDX-FileContributor: Laurent Montel +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +#include "cxx-qt-lib/qfontmetrics.h" + +#include "../assertion_utils.h" + +// https://code.qt.io/cgit/qt/qtbase.git/tree/src/gui/text/qfontmetrics.h?h=v5.15.6-lts-lgpl#n147 +// https://code.qt.io/cgit/qt/qtbase.git/tree/src/gui/text/qfontmetrics.h?h=v6.2.4#n117 +assert_alignment_and_size(QFontMetrics, + alignof(::std::size_t), + sizeof(::std::size_t)); + +static_assert(!::std::is_trivially_copy_assignable::value); +static_assert(!::std::is_trivially_copy_constructible::value); + +static_assert(!::std::is_trivially_destructible::value); +static_assert(QTypeInfo::isRelocatable); diff --git a/crates/cxx-qt-lib/src/gui/qfontmetrics.rs b/crates/cxx-qt-lib/src/gui/qfontmetrics.rs new file mode 100644 index 000000000..d9cfe8474 --- /dev/null +++ b/crates/cxx-qt-lib/src/gui/qfontmetrics.rs @@ -0,0 +1,146 @@ +// SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileContributor: Laurent Montel +// +// SPDX-License-Identifier: MIT OR Apache-2.0 +use cxx::{type_id, ExternType}; +use std::mem::MaybeUninit; + +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + include!("cxx-qt-lib/qfontmetrics.h"); + type QFontMetrics = super::QFontMetrics; + include!("cxx-qt-lib/qrect.h"); + type QRect = crate::QRect; + include!("cxx-qt-lib/qstring.h"); + type QString = crate::QString; + include!("cxx-qt-lib/qfont.h"); + type QFont = crate::QFont; + + /// Returns the ascent of the font. + fn ascent(self: &QFontMetrics) -> i32; + + /// Returns the average width of glyphs in the font. + #[rust_name = "average_char_width"] + fn averageCharWidth(self: &QFontMetrics) -> i32; + + /// Returns the bounding rectangle of the characters in the string specified by text. + /// The bounding rectangle always covers at least the set of pixels the text would cover if drawn at (0, 0). + #[rust_name = "bounding_rect"] + fn boundingRect(self: &QFontMetrics, text: &QString) -> QRect; + + /// Returns the cap height of the font. + /// The cap height of a font is the height of a capital letter above the baseline. It specifically is + /// the height of capital letters that are flat - such as H or I - as opposed to round letters such + /// as O, or pointed letters like A, both of which may display overshoot. + #[rust_name = "cap_height"] + fn capHeight(self: &QFontMetrics) -> i32; + + /// Returns the descent of the font. + /// The descent is the distance from the base line to the lowest point characters extend to. In practice, + /// some font designers break this rule, e.g. to accommodate a certain character, + /// so it is possible (though rare) that this value will be too small. + fn descent(self: &QFontMetrics) -> i32; + + /// Returns the height of the font. + fn height(self: &QFontMetrics) -> i32; + + /// Returns the horizontal advance in pixels of the first len characters of text. If len is negative (the default), + /// the entire string is used. The entire length of text is analysed even if len is substantially shorter. + #[rust_name = "horizontal_advance"] + fn horizontalAdvance(self: &QFontMetrics, text: &QString, len: i32) -> i32; + + /// Returns the leading of the font. + fn leading(self: &QFontMetrics) -> i32; + + /// Returns the distance from one base line to the next. + /// This value is always equal to leading()+height(). + #[rust_name = "line_spacing"] + fn lineSpacing(self: &QFontMetrics) -> i32; + + /// Returns the width of the underline and strikeout lines, adjusted for the point size of the font. + #[rust_name = "line_width"] + fn lineWidth(self: &QFontMetrics) -> i32; + + /// Returns the width of the widest character in the font. + #[rust_name = "max_width"] + fn maxWidth(self: &QFontMetrics) -> i32; + + /// Returns the minimum left bearing of the font. + #[rust_name = "min_left_bearing"] + fn minLeftBearing(self: &QFontMetrics) -> i32; + + /// Returns the minimum right bearing of the font. + #[rust_name = "min_right_bearing"] + fn minRightBearing(self: &QFontMetrics) -> i32; + + /// Returns the distance from the base line to where an overline should be drawn. + #[rust_name = "overline_position"] + fn overlinePos(self: &QFontMetrics) -> i32; + + /// Returns the distance from the base line to where the strikeout line should be drawn. + #[rust_name = "strike_out_position"] + fn strikeOutPos(self: &QFontMetrics) -> i32; + + /// Returns a tight bounding rectangle around the characters in the string specified by text. + /// The bounding rectangle always covers at least the set of pixels the text would cover if drawn at (0, 0). + #[rust_name = "tight_bounding_rect"] + fn tightBoundingRect(self: &QFontMetrics, text: &QString) -> QRect; + + /// Returns the distance from the base line to where an underscore should be drawn. + #[rust_name = "underline_position"] + fn underlinePos(self: &QFontMetrics) -> i32; + + /// Returns the 'x' height of the font. This is often but not always the same as the height of the character 'x'. + #[rust_name = "x_height"] + fn xHeight(self: &QFontMetrics) -> i32; + } + + #[namespace = "rust::cxxqtlib1"] + unsafe extern "C++" { + include!("cxx-qt-lib/common.h"); + + #[doc(hidden)] + #[rust_name = "qfontmetrics_drop"] + fn drop(fontmetics: &mut QFontMetrics); + + #[doc(hidden)] + #[rust_name = "qfontmetrics_clone"] + fn construct(font: &QFontMetrics) -> QFontMetrics; + + #[doc(hidden)] + #[rust_name = "qfontmetrics_init_from_qfont"] + fn construct(font: &QFont) -> QFontMetrics; + } +} + +#[repr(C)] +pub struct QFontMetrics { + _cspec: MaybeUninit, +} + +impl Drop for QFontMetrics { + fn drop(&mut self) { + ffi::qfontmetrics_drop(self); + } +} + +impl Clone for QFontMetrics { + fn clone(&self) -> Self { + ffi::qfontmetrics_clone(self) + } +} + +impl From<&ffi::QFont> for QFontMetrics { + fn from(font: &ffi::QFont) -> Self { + ffi::qfontmetrics_init_from_qfont(font) + } +} + +// Safety: +// +// Static checks on the C++ side to ensure the size is the same. +unsafe impl ExternType for QFontMetrics { + type Id = type_id!("QFontMetrics"); + type Kind = cxx::kind::Trivial; +} diff --git a/crates/cxx-qt-lib/src/gui/qpainter.rs b/crates/cxx-qt-lib/src/gui/qpainter.rs index 3e69417a6..b035b7d44 100644 --- a/crates/cxx-qt-lib/src/gui/qpainter.rs +++ b/crates/cxx-qt-lib/src/gui/qpainter.rs @@ -173,6 +173,8 @@ mod ffi { type QPen = crate::QPen; include!("cxx-qt-lib/qpolygon.h"); type QPolygon = crate::QPolygon; + include!("cxx-qt-lib/qfontmetrics.h"); + type QFontMetrics = crate::QFontMetrics; /// Returns the current background mode. #[rust_name = "background_mode"] @@ -286,6 +288,10 @@ mod ffi { /// Returns the currently set font used for drawing text. fn font(self: &QPainter) -> &QFont; + /// Returns the font metrics for the painter if the painter is active. + #[rust_name = "font_metrics_or_undefined"] + fn fontMetrics(self: &QPainter) -> QFontMetrics; + /// Returns true if clipping has been set; otherwise returns false. #[rust_name = "has_clipping"] fn hasClipping(self: &QPainter) -> bool; @@ -415,4 +421,14 @@ impl QPainter { None } } + + /// Returns the bounding rectangle of the current clip if there is a clip; + /// otherwise returns `None`. Note that the clip region is given in logical coordinates. + pub fn font_metrics(&self) -> Option { + if self.is_active() { + Some(self.font_metrics_or_undefined()) + } else { + None + } + } } diff --git a/tests/qt_types_standalone/CMakeLists.txt b/tests/qt_types_standalone/CMakeLists.txt index bd2e1cd07..48bdd982b 100644 --- a/tests/qt_types_standalone/CMakeLists.txt +++ b/tests/qt_types_standalone/CMakeLists.txt @@ -53,6 +53,7 @@ add_executable(${APP_NAME} cpp/qcoreapplication.h cpp/qdate.h cpp/qdatetime.h + cpp/qfontmetrics.h cpp/qguiapplication.h cpp/qhash.h cpp/qline.h diff --git a/tests/qt_types_standalone/cpp/main.cpp b/tests/qt_types_standalone/cpp/main.cpp index beae4764d..83dac5405 100644 --- a/tests/qt_types_standalone/cpp/main.cpp +++ b/tests/qt_types_standalone/cpp/main.cpp @@ -12,6 +12,7 @@ #include "qcoreapplication.h" #include "qdate.h" #include "qdatetime.h" +#include "qfontmetrics.h" #include "qguiapplication.h" #include "qhash.h" #include "qline.h" @@ -92,6 +93,7 @@ main(int argc, char* argv[]) runTest(QScopedPointer(new QVector3DTest)); runTest(QScopedPointer(new QVector4DTest)); runTest(QScopedPointer(new QPolygonTest)); + runTest(QScopedPointer(new QFontMetricsTest)); return status; } diff --git a/tests/qt_types_standalone/cpp/qfontmetrics.h b/tests/qt_types_standalone/cpp/qfontmetrics.h new file mode 100644 index 000000000..a0d969856 --- /dev/null +++ b/tests/qt_types_standalone/cpp/qfontmetrics.h @@ -0,0 +1,59 @@ +// clang-format off +// SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company +// clang-format on +// SPDX-FileContributor: Laurent Montel +// +// SPDX-License-Identifier: MIT OR Apache-2.0 +#pragma once + +#include +#include +#include +#include + +#include "cxx-qt-gen/qfontmetrics.cxx.h" + +class QFontMetricsTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void construct() + { + std::vector args; + std::string path = "/path"; + args.push_back(path.data()); + auto argc = static_cast(args.size()); + + // QFontDatabase needs a QGuiApplication first + QGuiApplication app(argc, args.data()); + app.setApplicationName(QStringLiteral("kdab")); + + QFont f; + const int pointSize = 40; + f.setPointSize(pointSize); + const auto m = constructor_qfontmetrics(f); + QCOMPARE(m.ascent(), 57); + QCOMPARE(m.height(), 73); + } + void clone() + { + std::vector args; + std::string path = "/path"; + args.push_back(path.data()); + auto argc = static_cast(args.size()); + + // QFontDatabase needs a QGuiApplication first + QGuiApplication app(argc, args.data()); + app.setApplicationName(QStringLiteral("kdab")); + + QFont f; + f.setBold(true); + f.setPointSize(30); + const auto m = QFontMetrics(f); + + const auto c = clone_qfontmetrics(m); + QCOMPARE(m.ascent(), c.ascent()); + QCOMPARE(m.height(), c.height()); + } +}; diff --git a/tests/qt_types_standalone/rust/build.rs b/tests/qt_types_standalone/rust/build.rs index ae458ce59..35740496e 100644 --- a/tests/qt_types_standalone/rust/build.rs +++ b/tests/qt_types_standalone/rust/build.rs @@ -12,6 +12,7 @@ fn main() { .file("src/qcoreapplication.rs") .file("src/qdate.rs") .file("src/qdatetime.rs") + .file("src/qfontmetrics.rs") .file("src/qguiapplication.rs") .file("src/qhash.rs") .file("src/qline.rs") diff --git a/tests/qt_types_standalone/rust/src/lib.rs b/tests/qt_types_standalone/rust/src/lib.rs index d81d4ccb3..88a91d80e 100644 --- a/tests/qt_types_standalone/rust/src/lib.rs +++ b/tests/qt_types_standalone/rust/src/lib.rs @@ -9,6 +9,7 @@ mod qcolor; mod qcoreapplication; mod qdate; mod qdatetime; +mod qfontmetrics; mod qguiapplication; mod qhash; mod qline; diff --git a/tests/qt_types_standalone/rust/src/qfontmetrics.rs b/tests/qt_types_standalone/rust/src/qfontmetrics.rs new file mode 100644 index 000000000..86131be11 --- /dev/null +++ b/tests/qt_types_standalone/rust/src/qfontmetrics.rs @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileContributor: Laurent Montel +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use cxx_qt_lib::QFont; +use cxx_qt_lib::QFontMetrics; + +#[cxx::bridge] +mod qfontmetrics_cxx { + unsafe extern "C++" { + include!("cxx-qt-lib/qfontmetrics.h"); + + type QFontMetrics = cxx_qt_lib::QFontMetrics; + type QFont = cxx_qt_lib::QFont; + } + + extern "Rust" { + fn clone_qfontmetrics(f: &QFontMetrics) -> QFontMetrics; + fn constructor_qfontmetrics(f: &QFont) -> QFontMetrics; + } +} + +fn clone_qfontmetrics(p: &QFontMetrics) -> QFontMetrics { + p.clone() +} + +fn constructor_qfontmetrics(f: &QFont) -> QFontMetrics { + QFontMetrics::from(f) +}