Skip to content

Commit 24b0d7f

Browse files
Support QFlags (#1231)
* cxx-qt-lib: create QFlags * cxx-qt-lib: add license info to src/core/qflags/util.rs * cxx-qt-lib: fix namespacing of Qt enums * cxx-qt-lib: fix qflag tests * cxx-qt-lib: fix test header file * cxx-qt-lib: fix QFlags doc * don't run doctests on QFlag * cxx-qt-lib: rename impl_qflag to unsafe_impl_qflag * cxx-qt-lib: make keyboard_modifiers and mouse_buttons &self fns * cxx-qt-lib: seal QFlagRepr trait * cxx-qt-lib: clarify QFlagRepr::Int vs QFlag::Repr * cxx-qt-lib: remove Qt version gates from QFlags * Update crates/cxx-qt-lib/src/core/qflags/qflag.rs Co-authored-by: Leon Matthes <[email protected]> * cxx-qt-lib: add license header to core/qflags/repr.rs * cxx-qt-lib: add docs for QFlagRepr --------- Co-authored-by: Leon Matthes <[email protected]>
1 parent 50ca5e2 commit 24b0d7f

File tree

15 files changed

+751
-6
lines changed

15 files changed

+751
-6
lines changed

crates/cxx-qt-lib/include/gui/qguiapplication.h

+9
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,14 @@ qguiapplicationSetDesktopFileName(const QString& name);
3131
QString
3232
qguiapplicationDesktopFileName();
3333

34+
Qt::KeyboardModifiers
35+
qguiapplicationKeyboardModifiers();
36+
37+
Qt::MouseButtons
38+
qguiapplicationMouseButtons();
39+
40+
Qt::KeyboardModifiers
41+
qguiapplicationQueryKeyboardModifiers();
42+
3443
}
3544
}

crates/cxx-qt-lib/src/core/mod.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ mod qdatetime;
1717
#[cfg(not(target_os = "emscripten"))]
1818
pub use qdatetime::QDateTime;
1919

20+
mod qflags;
21+
pub use qflags::{QFlag, QFlagRepr, QFlags};
22+
2023
mod qhash;
2124
pub use qhash::{QHash, QHashPair, QHashPair_QString_QVariant, QHashPair_i32_QByteArray};
2225

@@ -76,8 +79,8 @@ pub use qstringlist::QStringList;
7679
mod qt;
7780
pub use qt::{
7881
AspectRatioMode, BGMode, CaseSensitivity, ClipOperation, ConnectionType, DateFormat, FillRule,
79-
LayoutDirection, PenCapStyle, PenJoinStyle, PenStyle, SizeMode, SplitBehaviorFlags, TimeSpec,
80-
TransformationMode,
82+
KeyboardModifier, KeyboardModifiers, LayoutDirection, MouseButton, MouseButtons, PenCapStyle,
83+
PenJoinStyle, PenStyle, SizeMode, SplitBehaviorFlags, TimeSpec, TransformationMode,
8184
};
8285

8386
mod qtime;
+340
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
2+
// SPDX-FileContributor: Joshua Booth <[email protected]>
3+
//
4+
// SPDX-License-Identifier: MIT OR Apache-2.0
5+
6+
use cxx::ExternType;
7+
use std::fmt::Debug;
8+
use std::hash::Hash;
9+
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};
10+
11+
mod qflag;
12+
pub use qflag::QFlag;
13+
use qflag::QFlagExt;
14+
15+
mod repr;
16+
pub use repr::QFlagRepr;
17+
18+
mod util;
19+
20+
/// The `QFlags<T>` class is a template class, where T is an enum type.
21+
/// QFlags are used throughout Qt for storing combinations of enum values.
22+
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
23+
#[repr(transparent)]
24+
pub struct QFlags<T: QFlag> {
25+
repr: <T::Repr as QFlagRepr>::Int,
26+
}
27+
28+
impl<T: QFlag> Copy for QFlags<T> {}
29+
30+
impl<T: QFlag> Clone for QFlags<T> {
31+
fn clone(&self) -> Self {
32+
*self
33+
}
34+
}
35+
36+
impl<T: QFlag> From<T> for QFlags<T> {
37+
/// Returns the value stored in the QFlags object as an integer.
38+
fn from(value: T) -> Self {
39+
Self {
40+
repr: value.to_int(),
41+
}
42+
}
43+
}
44+
45+
impl<T: QFlag> Default for QFlags<T> {
46+
/// Constructs an empty QFlags object.
47+
fn default() -> Self {
48+
Self::new()
49+
}
50+
}
51+
52+
impl<T: QFlag> QFlags<T> {
53+
/// Constructs an empty QFlags object.
54+
pub const fn new() -> Self {
55+
Self::from_int(T::Repr::ZERO)
56+
}
57+
58+
/// Constructs a QFlags object representing the integer value *i*.
59+
pub const fn from_int(i: <T::Repr as QFlagRepr>::Int) -> Self {
60+
Self { repr: i }
61+
}
62+
63+
/// Returns the value stored in the QFlags object as an integer.
64+
pub const fn to_int(self) -> <T::Repr as QFlagRepr>::Int {
65+
self.repr
66+
}
67+
68+
/// Returns `true` if no flag is set (i.e., if the value stored by the QFlags object is 0);
69+
/// otherwise returns `false`.
70+
pub fn is_empty(self) -> bool {
71+
self.repr == T::Repr::ZERO
72+
}
73+
74+
/// Sets the flag *flag* if *on* is `true` or unsets it if *on* is `false`.
75+
/// Returns a mutable reference to this object.
76+
pub fn set_flag(&mut self, flag: T, on: bool) -> &mut Self {
77+
if on {
78+
self.repr |= flag.to_int();
79+
} else {
80+
self.repr &= !flag.to_int();
81+
}
82+
self
83+
}
84+
85+
/// Returns `true` if the flag `flag` is set, otherwise false.
86+
///
87+
/// Note: if *flag* contains multiple bits set to 1 (for instance, if it's an enumerator equal
88+
/// to the bitwise-OR of other enumerators) then this function will return `true` if and only if
89+
/// all the bits are set in this flags object. On the other hand, if *flag* contains no bits set
90+
/// to 1 (that is, its value as a integer is 0), then this function will return `true` if and
91+
/// only if this flags object also has no bits set to 1.
92+
pub fn test_flag(self, flag: T) -> bool {
93+
self.test_flags(Self::from(flag))
94+
}
95+
96+
/// Returns `true` if this *flags* object matches the given flags.
97+
///
98+
/// If *flags* has any flags set, this flags object matches precisely if all flags set in
99+
/// *flags* are also set in this flags object. Otherwise, when *flags* has no flags set, this
100+
/// flags object only matches if it also has no flags set.
101+
pub fn test_flags(self, flags: Self) -> bool {
102+
if flags.is_empty() {
103+
self.is_empty()
104+
} else {
105+
self.repr & flags.repr == flags.repr
106+
}
107+
}
108+
109+
/// Returns `true` if any flag set in *flag* is also set in this flags object, otherwise
110+
/// `false`. If *flag* has no flags set, the return will always be `false`.
111+
pub fn test_any_flag(self, flag: T) -> bool {
112+
self.test_any_flags(Self::from(flag))
113+
}
114+
115+
/// Returns `true` if any flag set in *flags* is also set in this flags object, otherwise
116+
/// `false`. If *flags* has no flags set, the return will always be `false`.
117+
pub fn test_any_flags(self, flags: Self) -> bool {
118+
(self.repr & flags.repr) != T::Repr::ZERO
119+
}
120+
}
121+
122+
impl<T: QFlag> Not for QFlags<T> {
123+
type Output = Self;
124+
125+
/// Returns a QFlags object that contains the bitwise negation of this object.
126+
fn not(self) -> Self::Output {
127+
Self { repr: !self.repr }
128+
}
129+
}
130+
131+
impl<T: QFlag> BitAnd for QFlags<T> {
132+
type Output = Self;
133+
134+
/// Returns a QFlags object containing the result of the bitwise AND operation on this object
135+
/// and `mask`.
136+
fn bitand(self, mask: Self) -> Self::Output {
137+
Self {
138+
repr: self.repr & mask.repr,
139+
}
140+
}
141+
}
142+
impl<T: QFlag> BitAnd<T> for QFlags<T> {
143+
type Output = Self;
144+
145+
/// Returns a QFlags object containing the result of the bitwise AND operation on this object
146+
/// and `mask`.
147+
fn bitand(self, mask: T) -> Self::Output {
148+
Self {
149+
repr: self.repr & mask.to_int(),
150+
}
151+
}
152+
}
153+
impl<T: QFlag> BitAndAssign for QFlags<T> {
154+
/// Performs a bitwise AND operation with mask and stores the result in this QFlags object.
155+
fn bitand_assign(&mut self, mask: Self) {
156+
self.repr &= mask.repr;
157+
}
158+
}
159+
impl<T: QFlag> BitAndAssign<T> for QFlags<T> {
160+
/// Performs a bitwise AND operation with mask and stores the result in this QFlags object.
161+
fn bitand_assign(&mut self, mask: T) {
162+
self.repr &= mask.to_int();
163+
}
164+
}
165+
166+
impl<T: QFlag> BitXor for QFlags<T> {
167+
type Output = Self;
168+
169+
/// Returns a QFlags object containing the result of the bitwise XOR operation on this object
170+
/// and `other`.
171+
fn bitxor(self, other: Self) -> Self::Output {
172+
Self {
173+
repr: self.repr ^ other.repr,
174+
}
175+
}
176+
}
177+
impl<T: QFlag> BitXor<T> for QFlags<T> {
178+
type Output = Self;
179+
180+
/// Returns a QFlags object containing the result of the bitwise XOR operation on this object
181+
/// and `other`.
182+
fn bitxor(self, other: T) -> Self::Output {
183+
Self {
184+
repr: self.repr ^ other.to_int(),
185+
}
186+
}
187+
}
188+
impl<T: QFlag> BitXorAssign for QFlags<T> {
189+
/// Performs a bitwise XOR operation with `other` and stores the result in this QFlags object.
190+
fn bitxor_assign(&mut self, other: Self) {
191+
self.repr ^= other.repr;
192+
}
193+
}
194+
impl<T: QFlag> BitXorAssign<T> for QFlags<T> {
195+
/// Performs a bitwise XOR operation with `other` and stores the result in this QFlags object.
196+
fn bitxor_assign(&mut self, other: T) {
197+
self.repr ^= other.to_int();
198+
}
199+
}
200+
201+
impl<T: QFlag> BitOr for QFlags<T> {
202+
type Output = Self;
203+
204+
/// Returns a QFlags object containing the result of the bitwise OR operation on this object and
205+
/// `other`.
206+
fn bitor(self, other: Self) -> Self::Output {
207+
Self {
208+
repr: self.repr | other.repr,
209+
}
210+
}
211+
}
212+
impl<T: QFlag> BitOr<T> for QFlags<T> {
213+
type Output = Self;
214+
215+
/// Returns a QFlags object containing the result of the bitwise OR operation on this object and
216+
/// `other`.
217+
fn bitor(self, other: T) -> Self::Output {
218+
Self {
219+
repr: self.repr | other.to_int(),
220+
}
221+
}
222+
}
223+
impl<T: QFlag> BitOrAssign for QFlags<T> {
224+
/// Performs a bitwise OR operation with `other` and stores the result in this QFlags object.
225+
fn bitor_assign(&mut self, other: Self) {
226+
self.repr |= other.repr;
227+
}
228+
}
229+
impl<T: QFlag> BitOrAssign<T> for QFlags<T> {
230+
/// Performs a bitwise OR operation with `other` and stores the result in this QFlags object.
231+
fn bitor_assign(&mut self, mask: T) {
232+
self.repr |= mask.to_int();
233+
}
234+
}
235+
236+
impl<T: QFlag> FromIterator<T> for QFlags<T> {
237+
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
238+
let repr = iter
239+
.into_iter()
240+
.fold(T::Repr::ZERO, |repr, item| repr | item.to_int());
241+
Self { repr }
242+
}
243+
}
244+
245+
// Safety:
246+
//
247+
// Established by the `QFlag` contract.
248+
unsafe impl<T: QFlag> ExternType for QFlags<T> {
249+
type Id = T::TypeId;
250+
251+
type Kind = cxx::kind::Trivial;
252+
}
253+
254+
#[cfg(test)]
255+
mod test {
256+
use crate::{KeyboardModifier, KeyboardModifiers};
257+
258+
use super::*;
259+
260+
const ALL_KEYBOARD_MODIFIERS: &[KeyboardModifier] = &[
261+
KeyboardModifier::AltModifier,
262+
KeyboardModifier::ControlModifier,
263+
KeyboardModifier::GroupSwitchModifier,
264+
KeyboardModifier::KeypadModifier,
265+
KeyboardModifier::MetaModifier,
266+
KeyboardModifier::ShiftModifier,
267+
];
268+
269+
#[test]
270+
fn qflags_set_flag() {
271+
let mut flags = KeyboardModifiers::new();
272+
flags
273+
.set_flag(KeyboardModifier::AltModifier, true)
274+
.set_flag(KeyboardModifier::ControlModifier, true)
275+
.set_flag(KeyboardModifier::ShiftModifier, true)
276+
.set_flag(KeyboardModifier::AltModifier, false);
277+
let contained = ALL_KEYBOARD_MODIFIERS
278+
.iter()
279+
.copied()
280+
.filter(|&key| flags.test_flag(key))
281+
.collect::<Vec<_>>();
282+
assert_eq!(
283+
contained,
284+
vec![
285+
KeyboardModifier::ControlModifier,
286+
KeyboardModifier::ShiftModifier
287+
]
288+
);
289+
}
290+
291+
#[test]
292+
fn qflags_test_flags() {
293+
let flags = KeyboardModifier::ControlModifier
294+
| KeyboardModifier::ShiftModifier
295+
| KeyboardModifier::KeypadModifier;
296+
let mut other = KeyboardModifier::AltModifier
297+
| KeyboardModifier::ControlModifier
298+
| KeyboardModifier::KeypadModifier;
299+
assert!(!flags.test_flags(other));
300+
other.set_flag(KeyboardModifier::AltModifier, false);
301+
assert!(flags.test_flags(other));
302+
}
303+
304+
#[test]
305+
fn qflags_test_any_flags() {
306+
let flags = KeyboardModifier::ControlModifier
307+
| KeyboardModifier::ShiftModifier
308+
| KeyboardModifier::KeypadModifier;
309+
let mut other = KeyboardModifier::AltModifier | KeyboardModifier::ControlModifier;
310+
assert!(flags.test_any_flags(other));
311+
other.set_flag(KeyboardModifier::ControlModifier, false);
312+
assert!(!flags.test_any_flags(other));
313+
}
314+
315+
#[test]
316+
fn qflags_test_no_flags() {
317+
let mut flags = KeyboardModifiers::from(KeyboardModifier::AltModifier);
318+
assert!(!flags.test_flag(KeyboardModifier::NoModifier));
319+
flags.set_flag(KeyboardModifier::AltModifier, false);
320+
assert!(flags.test_flag(KeyboardModifier::NoModifier));
321+
}
322+
323+
#[test]
324+
fn qflags_from_iter() {
325+
let flags = [
326+
KeyboardModifier::AltModifier,
327+
KeyboardModifier::MetaModifier,
328+
KeyboardModifier::ShiftModifier,
329+
]
330+
.iter()
331+
.copied()
332+
.collect::<QFlags<_>>();
333+
assert_eq!(
334+
flags.to_int(),
335+
KeyboardModifier::AltModifier.repr
336+
| KeyboardModifier::MetaModifier.repr
337+
| KeyboardModifier::ShiftModifier.repr
338+
);
339+
}
340+
}

0 commit comments

Comments
 (0)