use core::ops::{BitOr, BitOrAssign};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Sense(u8);
impl Sense {
pub const HOVER: Self = Self(1 << 0);
pub const CLICK: Self = Self(1 << 1);
pub const DRAG: Self = Self(1 << 2);
pub const FOCUSABLE: Self = Self(1 << 3);
pub const SCROLL: Self = Self(1 << 4);
pub const SECONDARY_CLICK: Self = Self(1 << 5);
pub const DISABLED: Self = Self(1 << 6);
pub const NONE: Self = Self(0);
#[must_use]
pub const fn click() -> Self {
Self(Self::HOVER.0 | Self::CLICK.0 | Self::FOCUSABLE.0)
}
#[must_use]
pub const fn drag() -> Self {
Self(Self::click().0 | Self::DRAG.0)
}
#[must_use]
pub const fn hover() -> Self {
Self::HOVER
}
#[must_use]
pub const fn scroll() -> Self {
Self(Self::HOVER.0 | Self::SCROLL.0)
}
#[must_use]
pub const fn secondary_click() -> Self {
Self(Self::HOVER.0 | Self::SECONDARY_CLICK.0)
}
#[must_use]
pub const fn disabled_if(self, disabled: bool) -> Self {
if disabled {
Self(self.0 | Self::DISABLED.0)
} else {
self
}
}
#[must_use]
pub const fn contains(self, other: Self) -> bool {
(self.0 & other.0) == other.0
}
#[must_use]
pub const fn is_disabled(self) -> bool {
self.contains(Self::DISABLED)
}
#[must_use]
pub const fn wants_pointer(self) -> bool {
self.0
& (Self::HOVER.0
| Self::CLICK.0
| Self::DRAG.0
| Self::SCROLL.0
| Self::SECONDARY_CLICK.0)
!= 0
}
}
impl BitOr for Sense {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl BitOrAssign for Sense {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn contains_checks_all_bits() {
let s = Sense::HOVER | Sense::FOCUSABLE;
assert!(s.contains(Sense::HOVER));
assert!(s.contains(Sense::FOCUSABLE));
assert!(!s.contains(Sense::CLICK));
assert!(s.contains(Sense::NONE)); }
#[test]
fn constructors_match_their_documented_bit_combinations() {
assert_eq!(
Sense::click(),
Sense::HOVER | Sense::CLICK | Sense::FOCUSABLE
);
assert_eq!(Sense::drag(), Sense::click() | Sense::DRAG);
assert_eq!(Sense::hover(), Sense::HOVER);
assert_eq!(Sense::scroll(), Sense::HOVER | Sense::SCROLL);
}
#[test]
fn wants_pointer_ignores_focusable() {
assert!(!Sense::FOCUSABLE.wants_pointer());
assert!(Sense::HOVER.wants_pointer());
assert!(Sense::CLICK.wants_pointer());
assert!(Sense::DRAG.wants_pointer());
assert!(Sense::SCROLL.wants_pointer());
assert!(!Sense::NONE.wants_pointer());
}
#[test]
fn default_is_none() {
assert_eq!(Sense::default(), Sense::NONE);
}
#[test]
fn disabled_if_sets_the_bit_only_when_true() {
assert_eq!(
Sense::click().disabled_if(true),
Sense::click() | Sense::DISABLED
);
assert_eq!(Sense::click().disabled_if(false), Sense::click());
}
#[test]
fn is_disabled_reads_the_bit() {
assert!(!Sense::click().is_disabled());
assert!((Sense::click() | Sense::DISABLED).is_disabled());
}
}