#[doc(hidden)]
#[macro_export]
macro_rules! mouse_env {
{} => {
use sketchbook::derive_sketch;
use sketchbook::Point2;
use macro_rules_attribute::apply;
use sketchbook::aspects::mouse::SketchExt;
mod single_aspect {
use sketchbook::aspects::mouse;
use sketchbook::aspects::mouse::*;
sketchbook::env_for_aspect!(mouse);
sketchbook::compose! {
pub enum Events {
#[part]
Aspect(mouse::Event<EnvSpecificMarker>),
}
}
sketchbook::compose! {
#[derive(Default)]
pub struct Page {
#[part]
pub aspect: mouse::EnvData<EnvSpecificMarker>,
}
}
impl mouse::EnvSpecific for EnvSpecificMarker {
type Button = u8;
type ButtonSet = std::collections::HashSet<u8>;
type Num = f32;
}
}
}
}
use core::{borrow::Borrow, marker::PhantomData, ops::Add};
use crate::{compose::AsPart, Environment, PageOf, Point2, Sketch};
pub trait Handlers
where
Self: Sketch,
Self::Env: AssociatedEnvSpecificMarker,
{
#[allow(unused_variables)]
#[inline]
fn dragged(&mut self, previous: Point2<NumFor<SpecificallyFor<Self::Env>>>) {
}
#[allow(unused_variables)]
#[inline]
fn moved(&mut self, previous: Point2<NumFor<SpecificallyFor<Self::Env>>>) {
}
#[allow(unused_variables)]
#[inline]
fn clicked(&mut self, button: &ButtonFor<SpecificallyFor<Self::Env>>) {
}
#[allow(unused_variables)]
#[inline]
fn pressed(&mut self, button: &ButtonFor<SpecificallyFor<Self::Env>>) {
}
#[allow(unused_variables)]
#[inline]
fn released(&mut self, button: &ButtonFor<SpecificallyFor<Self::Env>>) {
}
#[allow(unused_variables)]
#[inline]
fn wheel(&mut self, count: &NumFor<SpecificallyFor<Self::Env>>) {
}
}
pub trait SketchExt<M>
where
Self: AsPart<EnvData<M>>,
M: EnvSpecific + 'static,
{
#[inline]
fn mouse_x(&self) -> &NumFor<M> {
&self.as_part().position.x
}
#[inline]
fn mouse_y(&self) -> &NumFor<M> {
&self.as_part().position.y
}
#[inline]
fn mouse_point(&self) -> &Point2<NumFor<M>> {
&self.as_part().position
}
#[inline]
fn button_pressed<B: Borrow<ButtonFor<M>>>(&self, button: B) -> bool {
self.as_part()
.pressed_buttons
.button_set_contains(button.borrow())
}
#[inline]
fn any_button_pressed(&self) -> bool {
!self.as_part().pressed_buttons.button_set_is_empty()
}
}
impl<M, T> SketchExt<M> for T
where
Self: AsPart<EnvData<M>>,
M: EnvSpecific,
{
}
pub trait RunHandlers
where
Self: Sketch + Handlers,
Self::Env: AssociatedEnvSpecificMarker + Environment,
PageOf<Self::Env>: AsPart<EnvData<SpecificallyFor<Self::Env>>>,
{
fn run_handlers(&mut self, event: &Event<SpecificallyFor<Self::Env>>) {
match event {
Event::MoveRelative(point) => {
let data = self.get_page_mut().as_part_mut();
let new_position = &data.position + point;
let previous = core::mem::replace(&mut data.position, new_position);
if data.pressed_buttons.button_set_is_empty() {
self.moved(previous);
} else {
self.dragged(previous);
}
}
Event::MoveAbsolute(point) => {
let data = self.get_page_mut().as_part_mut();
let previous = core::mem::replace(&mut data.position, point.clone());
if data.pressed_buttons.button_set_is_empty() {
self.moved(previous);
} else {
self.dragged(previous);
}
}
Event::Wheel(count) => {
self.wheel(count);
}
Event::Press(button) => {
let data = self.get_page_mut().as_part_mut();
data.pressed_buttons.button_set_insert(button.clone());
self.pressed(button);
}
Event::Release(button) => {
let data = self.get_page_mut().as_part_mut();
let was_pressed = data.pressed_buttons.button_set_remove(button);
self.released(button);
if was_pressed {
self.clicked(button);
}
}
Event::_MetaPhantom(_, _) => unreachable!(),
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum Event<M>
where
M: EnvSpecific,
{
#[doc(hidden)]
#[cfg_attr(feature = "serde", serde(skip))]
_MetaPhantom(core::convert::Infallible, PhantomData<M>),
MoveRelative(Point2<M::Num>),
MoveAbsolute(Point2<M::Num>),
Press(M::Button),
Release(M::Button),
Wheel(M::Num),
}
pub trait AssociatedEnvSpecificMarker {
type EnvSpecificMarker: EnvSpecific;
}
pub trait EnvSpecific: 'static {
type Button: Clone;
type Num: Default + Add<Output = Self::Num> + Clone;
type ButtonSet: ButtonSet<Self::Button>;
}
pub trait ButtonSet<B> {
fn button_set_is_empty(&self) -> bool;
fn button_set_insert(&mut self, value: B);
fn button_set_contains(&self, value: &B) -> bool;
fn button_set_remove(&mut self, value: &B) -> bool;
}
#[cfg(feature = "std")]
impl<B> ButtonSet<B> for std::collections::HashSet<B>
where
B: std::hash::Hash + Eq,
{
#[inline]
fn button_set_is_empty(&self) -> bool {
self.is_empty()
}
#[inline]
fn button_set_insert(&mut self, value: B) {
let _ = self.insert(value);
}
#[inline]
fn button_set_remove(&mut self, value: &B) -> bool {
self.remove(value)
}
#[inline]
fn button_set_contains(&self, value: &B) -> bool {
self.contains(value)
}
}
#[cfg(feature = "alloc")]
impl<B> ButtonSet<B> for alloc::collections::BTreeSet<B>
where
B: Ord,
{
#[inline]
fn button_set_is_empty(&self) -> bool {
self.is_empty()
}
#[inline]
fn button_set_insert(&mut self, value: B) {
let _ = self.insert(value);
}
#[inline]
fn button_set_remove(&mut self, value: &B) -> bool {
self.remove(value)
}
#[inline]
fn button_set_contains(&self, value: &B) -> bool {
self.contains(value)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(bound = "M::Num: serde::Serialize + serde::de::DeserializeOwned, M::ButtonSet: serde::Serialize + serde::de::DeserializeOwned"))]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct EnvData<M>
where
M: EnvSpecific,
{
marker: PhantomData<M>,
position: Point2<M::Num>,
pressed_buttons: M::ButtonSet,
}
impl<M> Default for EnvData<M>
where
M: EnvSpecific,
M::ButtonSet: Default,
{
#[inline]
fn default() -> Self {
Self {
marker: PhantomData,
pressed_buttons: M::ButtonSet::default(),
position: Point2 {
x: M::Num::default(),
y: M::Num::default(),
},
}
}
}
pub type ButtonFor<T> = <T as EnvSpecific>::Button;
pub type NumFor<T> = <T as EnvSpecific>::Num;
pub type SpecificallyFor<T> = <T as AssociatedEnvSpecificMarker>::EnvSpecificMarker;
#[test]
fn test_mouse() {
use crate::aspects::mouse;
use crate::env::*;
use crate::*;
mod single_aspect {
use std::collections::HashSet;
use crate::aspects::mouse;
crate::env_for_aspect!(mouse);
crate::compose! {
pub enum Events {
#[part]
Aspect(super::Event<EnvSpecificMarker>),
}
}
crate::compose! {
#[derive(Default)]
pub struct Page {
#[part]
pub aspect: super::EnvData<EnvSpecificMarker>,
}
}
impl mouse::EnvSpecific for EnvSpecificMarker {
type Button = u8;
type ButtonSet = HashSet<u8>;
type Num = f32;
}
}
derive_sketch! {
#[sketch(
env = single_aspect,
aspects = (mouse),
)]
struct App {
#[page] page: single_aspect::Page,
dragged: bool,
moved: bool,
clicked: bool,
pressed: bool,
released: bool,
wheel: bool,
}
}
impl Setup for App {
fn setup(page: single_aspect::Page, _: ()) -> Self {
App {
page,
dragged: false,
moved: false,
clicked: false,
pressed: false,
released: false,
wheel: false,
}
}
}
impl Handlers for App {
fn dragged(&mut self, previous: Point2<f32>) {
assert_eq!(previous, Point2 { x: 1.0, y: -2.0 });
assert_eq!(*self.mouse_x(), -2.0);
assert_eq!(*self.mouse_y(), 1.0);
assert_eq!(*self.mouse_point(), Point2 { x: -2.0, y: 1.0 });
self.dragged = true;
}
fn moved(&mut self, previous: Point2<f32>) {
assert_eq!(previous, Point2 { x: 0.0, y: 0.0 });
assert_eq!(*self.mouse_x(), 1.0);
assert_eq!(*self.mouse_y(), -2.0);
assert_eq!(*self.mouse_point(), Point2 { x: 1.0, y: -2.0 });
self.moved = true;
}
fn clicked(&mut self, &button: &u8) {
assert_eq!(button, 4);
self.clicked = true;
}
fn pressed(&mut self, &button: &u8) {
assert_eq!(button, 4);
self.pressed = true;
}
fn released(&mut self, &button: &u8) {
assert_eq!(button, 4);
self.released = true;
}
fn wheel(&mut self, count: &f32) {
assert_eq!(*count, -20.0);
self.wheel = true;
}
}
let mut mill = single_aspect::Mill;
let mut app = App::setup(mill.new_page(), ());
app.handle_environment_event(&single_aspect::Events::Aspect(mouse::Event::MoveAbsolute(
Point2 { x: 1.0, y: -2.0 },
)));
app.handle_environment_event(&single_aspect::Events::Aspect(mouse::Event::Press(4)));
app.handle_environment_event(&single_aspect::Events::Aspect(mouse::Event::MoveRelative(
Point2 { x: -3.0, y: 3.0 },
)));
app.handle_environment_event(&single_aspect::Events::Aspect(mouse::Event::Release(4)));
app.handle_environment_event(&single_aspect::Events::Aspect(mouse::Event::Wheel(-20.0)));
assert!(app.moved);
assert!(app.pressed);
assert!(app.dragged);
assert!(app.released);
assert!(app.clicked);
assert!(app.wheel);
}