use cxx::ExternType;
use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};
mod qflag;
pub use qflag::QFlag;
use qflag::QFlagExt;
mod repr;
pub use repr::QFlagRepr;
mod util;
#[repr(transparent)]
pub struct QFlags<T: QFlag> {
repr: <T::Repr as QFlagRepr>::Int,
}
impl<T: QFlag> Copy for QFlags<T> {}
impl<T: QFlag> Clone for QFlags<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: QFlag> Debug for QFlags<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QFlags").field("repr", &self.repr).finish()
}
}
impl<T: QFlag> PartialEq for QFlags<T> {
fn eq(&self, other: &Self) -> bool {
self.repr == other.repr
}
}
impl<T: QFlag> Eq for QFlags<T> {}
impl<T: QFlag> PartialOrd for QFlags<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T: QFlag> Ord for QFlags<T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.repr.cmp(&other.repr)
}
}
impl<T: QFlag> Hash for QFlags<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.repr.hash(state);
}
}
impl<T: QFlag> From<T> for QFlags<T> {
fn from(value: T) -> Self {
Self {
repr: value.to_int(),
}
}
}
impl<T: QFlag> Default for QFlags<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: QFlag> QFlags<T> {
pub const fn new() -> Self {
Self::from_int(T::Repr::ZERO)
}
pub const fn from_int(i: <T::Repr as QFlagRepr>::Int) -> Self {
Self { repr: i }
}
pub const fn to_int(self) -> <T::Repr as QFlagRepr>::Int {
self.repr
}
pub fn is_empty(self) -> bool {
self.repr == T::Repr::ZERO
}
pub fn set_flag(&mut self, flag: T, on: bool) -> &mut Self {
if on {
self.repr |= flag.to_int();
} else {
self.repr &= !flag.to_int();
}
self
}
pub fn test_flag(self, flag: T) -> bool {
self.test_flags(Self::from(flag))
}
pub fn test_flags(self, flags: Self) -> bool {
if flags.is_empty() {
self.is_empty()
} else {
self.repr & flags.repr == flags.repr
}
}
pub fn test_any_flag(self, flag: T) -> bool {
self.test_any_flags(Self::from(flag))
}
pub fn test_any_flags(self, flags: Self) -> bool {
(self.repr & flags.repr) != T::Repr::ZERO
}
}
impl<T: QFlag> Not for QFlags<T> {
type Output = Self;
fn not(self) -> Self::Output {
Self { repr: !self.repr }
}
}
impl<T: QFlag> BitAnd for QFlags<T> {
type Output = Self;
fn bitand(self, mask: Self) -> Self::Output {
Self {
repr: self.repr & mask.repr,
}
}
}
impl<T: QFlag> BitAnd<T> for QFlags<T> {
type Output = Self;
fn bitand(self, mask: T) -> Self::Output {
Self {
repr: self.repr & mask.to_int(),
}
}
}
impl<T: QFlag> BitAndAssign for QFlags<T> {
fn bitand_assign(&mut self, mask: Self) {
self.repr &= mask.repr;
}
}
impl<T: QFlag> BitAndAssign<T> for QFlags<T> {
fn bitand_assign(&mut self, mask: T) {
self.repr &= mask.to_int();
}
}
impl<T: QFlag> BitXor for QFlags<T> {
type Output = Self;
fn bitxor(self, other: Self) -> Self::Output {
Self {
repr: self.repr ^ other.repr,
}
}
}
impl<T: QFlag> BitXor<T> for QFlags<T> {
type Output = Self;
fn bitxor(self, other: T) -> Self::Output {
Self {
repr: self.repr ^ other.to_int(),
}
}
}
impl<T: QFlag> BitXorAssign for QFlags<T> {
fn bitxor_assign(&mut self, other: Self) {
self.repr ^= other.repr;
}
}
impl<T: QFlag> BitXorAssign<T> for QFlags<T> {
fn bitxor_assign(&mut self, other: T) {
self.repr ^= other.to_int();
}
}
impl<T: QFlag> BitOr for QFlags<T> {
type Output = Self;
fn bitor(self, other: Self) -> Self::Output {
Self {
repr: self.repr | other.repr,
}
}
}
impl<T: QFlag> BitOr<T> for QFlags<T> {
type Output = Self;
fn bitor(self, other: T) -> Self::Output {
Self {
repr: self.repr | other.to_int(),
}
}
}
impl<T: QFlag> BitOrAssign for QFlags<T> {
fn bitor_assign(&mut self, other: Self) {
self.repr |= other.repr;
}
}
impl<T: QFlag> BitOrAssign<T> for QFlags<T> {
fn bitor_assign(&mut self, mask: T) {
self.repr |= mask.to_int();
}
}
impl<T: QFlag> FromIterator<T> for QFlags<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let repr = iter
.into_iter()
.fold(T::Repr::ZERO, |repr, item| repr | item.to_int());
Self { repr }
}
}
unsafe impl<T: QFlag> ExternType for QFlags<T> {
type Id = T::TypeId;
type Kind = cxx::kind::Trivial;
}
#[cfg(test)]
mod test {
use crate::{KeyboardModifier, KeyboardModifiers};
use super::*;
const ALL_KEYBOARD_MODIFIERS: &[KeyboardModifier] = &[
KeyboardModifier::AltModifier,
KeyboardModifier::ControlModifier,
KeyboardModifier::GroupSwitchModifier,
KeyboardModifier::KeypadModifier,
KeyboardModifier::MetaModifier,
KeyboardModifier::ShiftModifier,
];
#[test]
fn qflags_set_flag() {
let mut flags = KeyboardModifiers::new();
flags
.set_flag(KeyboardModifier::AltModifier, true)
.set_flag(KeyboardModifier::ControlModifier, true)
.set_flag(KeyboardModifier::ShiftModifier, true)
.set_flag(KeyboardModifier::AltModifier, false);
let contained = ALL_KEYBOARD_MODIFIERS
.iter()
.copied()
.filter(|&key| flags.test_flag(key))
.collect::<Vec<_>>();
assert_eq!(
contained,
vec![
KeyboardModifier::ControlModifier,
KeyboardModifier::ShiftModifier
]
);
}
#[test]
fn qflags_test_flags() {
let flags = KeyboardModifier::ControlModifier
| KeyboardModifier::ShiftModifier
| KeyboardModifier::KeypadModifier;
let mut other = KeyboardModifier::AltModifier
| KeyboardModifier::ControlModifier
| KeyboardModifier::KeypadModifier;
assert!(!flags.test_flags(other));
other.set_flag(KeyboardModifier::AltModifier, false);
assert!(flags.test_flags(other));
}
#[test]
fn qflags_test_any_flags() {
let flags = KeyboardModifier::ControlModifier
| KeyboardModifier::ShiftModifier
| KeyboardModifier::KeypadModifier;
let mut other = KeyboardModifier::AltModifier | KeyboardModifier::ControlModifier;
assert!(flags.test_any_flags(other));
other.set_flag(KeyboardModifier::ControlModifier, false);
assert!(!flags.test_any_flags(other));
}
#[test]
fn qflags_test_no_flags() {
let mut flags = KeyboardModifiers::from(KeyboardModifier::AltModifier);
assert!(!flags.test_flag(KeyboardModifier::NoModifier));
flags.set_flag(KeyboardModifier::AltModifier, false);
assert!(flags.test_flag(KeyboardModifier::NoModifier));
}
#[test]
fn qflags_from_iter() {
let flags = [
KeyboardModifier::AltModifier,
KeyboardModifier::MetaModifier,
KeyboardModifier::ShiftModifier,
]
.iter()
.copied()
.collect::<QFlags<_>>();
assert_eq!(
flags.to_int(),
KeyboardModifier::AltModifier.repr
| KeyboardModifier::MetaModifier.repr
| KeyboardModifier::ShiftModifier.repr
);
}
}