#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub struct Sense {
pub click: bool,
pub drag: bool,
pub hover: bool,
pub focus: bool,
}
impl Sense {
pub const NONE: Sense = Sense {
click: false,
drag: false,
hover: false,
focus: false,
};
pub const HOVER: Sense = Sense {
click: false,
drag: false,
hover: true,
focus: false,
};
pub const CLICK: Sense = Sense {
click: true,
drag: false,
hover: true,
focus: false,
};
pub const DRAG: Sense = Sense {
click: false,
drag: true,
hover: true,
focus: false,
};
pub const CLICK_AND_DRAG: Sense = Sense {
click: true,
drag: true,
hover: true,
focus: false,
};
pub const FOCUSABLE: Sense = Sense {
click: false,
drag: false,
hover: true,
focus: true,
};
pub const ALL: Sense = Sense {
click: true,
drag: true,
hover: true,
focus: true,
};
}
impl Sense {
#[inline]
pub fn none() -> Self {
Self::NONE
}
#[inline]
pub fn hover() -> Self {
Self::HOVER
}
#[inline]
pub fn click() -> Self {
Self::CLICK
}
#[inline]
pub fn drag() -> Self {
Self::DRAG
}
#[inline]
pub fn click_and_drag() -> Self {
Self::CLICK_AND_DRAG
}
#[inline]
pub fn focusable() -> Self {
Self::FOCUSABLE
}
#[inline]
pub fn all() -> Self {
Self::ALL
}
}
impl Sense {
#[inline]
pub fn union(self, other: Sense) -> Sense {
Sense {
click: self.click || other.click,
drag: self.drag || other.drag,
hover: self.hover || other.hover,
focus: self.focus || other.focus,
}
}
#[inline]
pub fn intersection(self, other: Sense) -> Sense {
Sense {
click: self.click && other.click,
drag: self.drag && other.drag,
hover: self.hover && other.hover,
focus: self.focus && other.focus,
}
}
#[inline]
pub fn with_click(mut self) -> Self {
self.click = true;
self.hover = true;
self
}
#[inline]
pub fn with_drag(mut self) -> Self {
self.drag = true;
self.hover = true;
self
}
#[inline]
pub fn with_focus(mut self) -> Self {
self.focus = true;
self
}
}
impl Sense {
#[inline]
pub fn interactive(&self) -> bool {
self.click || self.drag || self.focus
}
#[inline]
pub fn has_click_and_drag(&self) -> bool {
self.click && self.drag
}
#[inline]
pub fn is_passive(&self) -> bool {
!self.click && !self.drag && !self.focus
}
}
impl std::ops::BitOr for Sense {
type Output = Sense;
#[inline]
fn bitor(self, rhs: Self) -> Self::Output {
self.union(rhs)
}
}
impl std::ops::BitOrAssign for Sense {
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
*self = self.union(rhs);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_predefined_constants() {
assert!(!Sense::NONE.click);
assert!(!Sense::NONE.drag);
assert!(!Sense::NONE.hover);
assert!(!Sense::NONE.focus);
assert!(!Sense::HOVER.click);
assert!(Sense::HOVER.hover);
assert!(Sense::CLICK.click);
assert!(!Sense::CLICK.drag);
assert!(Sense::CLICK.hover);
assert!(!Sense::DRAG.click);
assert!(Sense::DRAG.drag);
assert!(Sense::DRAG.hover);
assert!(Sense::CLICK_AND_DRAG.click);
assert!(Sense::CLICK_AND_DRAG.drag);
assert!(Sense::CLICK_AND_DRAG.hover);
assert!(!Sense::FOCUSABLE.click);
assert!(Sense::FOCUSABLE.hover);
assert!(Sense::FOCUSABLE.focus);
assert!(Sense::ALL.click);
assert!(Sense::ALL.drag);
assert!(Sense::ALL.hover);
assert!(Sense::ALL.focus);
}
#[test]
fn test_constructor_methods() {
assert_eq!(Sense::none(), Sense::NONE);
assert_eq!(Sense::hover(), Sense::HOVER);
assert_eq!(Sense::click(), Sense::CLICK);
assert_eq!(Sense::drag(), Sense::DRAG);
assert_eq!(Sense::click_and_drag(), Sense::CLICK_AND_DRAG);
assert_eq!(Sense::focusable(), Sense::FOCUSABLE);
assert_eq!(Sense::all(), Sense::ALL);
}
#[test]
fn test_union() {
let click = Sense::click();
let drag = Sense::drag();
let combined = click.union(drag);
assert!(combined.click);
assert!(combined.drag);
assert!(combined.hover);
assert!(!combined.focus);
assert_eq!(combined, Sense::CLICK_AND_DRAG);
}
#[test]
fn test_intersection() {
let click_and_drag = Sense::CLICK_AND_DRAG;
let click = Sense::click();
let common = click_and_drag.intersection(click);
assert!(common.click);
assert!(!common.drag);
assert!(common.hover);
assert!(!common.focus);
}
#[test]
fn test_with_methods() {
let sense = Sense::none().with_click();
assert!(sense.click);
assert!(sense.hover);
assert!(!sense.drag);
let sense = Sense::none().with_drag();
assert!(sense.drag);
assert!(sense.hover);
assert!(!sense.click);
let sense = Sense::click().with_focus();
assert!(sense.click);
assert!(sense.focus);
assert!(sense.hover);
let sense = Sense::none().with_click().with_drag().with_focus();
assert_eq!(sense, Sense::ALL);
}
#[test]
fn test_query_methods() {
assert!(Sense::click().interactive());
assert!(Sense::drag().interactive());
assert!(Sense::focusable().interactive());
assert!(!Sense::hover().interactive());
assert!(!Sense::none().interactive());
assert!(Sense::CLICK_AND_DRAG.has_click_and_drag());
assert!(Sense::ALL.has_click_and_drag());
assert!(!Sense::click().has_click_and_drag());
assert!(!Sense::drag().has_click_and_drag());
assert!(Sense::none().is_passive());
assert!(Sense::hover().is_passive());
assert!(!Sense::click().is_passive());
assert!(!Sense::drag().is_passive());
assert!(!Sense::focusable().is_passive());
}
#[test]
fn test_bitor_operator() {
let combined = Sense::click() | Sense::drag();
assert_eq!(combined, Sense::CLICK_AND_DRAG);
let mut sense = Sense::click();
sense |= Sense::drag();
assert_eq!(sense, Sense::CLICK_AND_DRAG);
let complex = Sense::click() | Sense::drag() | Sense::focusable();
assert!(complex.click);
assert!(complex.drag);
assert!(complex.focus);
assert!(complex.hover);
}
#[test]
fn test_default() {
let default_sense = Sense::default();
assert_eq!(default_sense, Sense::NONE);
}
#[test]
fn test_eq_and_hash() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(Sense::click());
set.insert(Sense::click());
set.insert(Sense::drag());
assert_eq!(set.len(), 2);
assert!(set.contains(&Sense::click()));
assert!(set.contains(&Sense::drag()));
}
#[test]
fn test_clone_and_copy() {
let original = Sense::click();
let cloned = original.clone();
let copied = original;
assert_eq!(original, cloned);
assert_eq!(original, copied);
}
}