#[doc(hidden)]
#[macro_export]
macro_rules! keyboard_env {
{} => {
use sketchbook::derive_sketch;
use macro_rules_attribute::apply;
use sketchbook::aspects::keyboard::SketchExt;
mod single_aspect {
use sketchbook::aspects::keyboard;
use sketchbook::aspects::keyboard::*;
sketchbook::env_for_aspect!(keyboard);
sketchbook::compose! {
pub enum Events {
#[part]
Aspect(keyboard::Event<EnvSpecificMarker>),
}
}
sketchbook::compose! {
#[derive(Default)]
pub struct Page {
#[part]
pub aspect: keyboard::EnvData<EnvSpecificMarker>,
}
}
impl keyboard::EnvSpecific for EnvSpecificMarker {
type Key = u8;
type KeySet = std::collections::HashSet<u8>;
}
}
}
}
use core::{borrow::Borrow, marker::PhantomData};
use crate::{compose::AsPart, Environment, PageOf, Sketch};
pub trait Handlers
where
Self: Sketch,
Self::Env: AssociatedEnvSpecificMarker,
{
#[allow(unused_variables)]
#[inline]
fn pressed(&mut self, key: &KeyFor<SpecificallyFor<Self::Env>>) {
}
#[allow(unused_variables)]
#[inline]
fn repeated(&mut self, key: &KeyFor<SpecificallyFor<Self::Env>>) {
}
#[allow(unused_variables)]
#[inline]
fn released(&mut self, key: &KeyFor<SpecificallyFor<Self::Env>>) {
}
}
pub trait SketchExt<M>
where
Self: AsPart<EnvData<M>>,
M: EnvSpecific,
{
#[inline]
fn key_pressed<K: Borrow<KeyFor<M>>>(&self, key: K) -> bool {
self.as_part().key_pressed(key.borrow())
}
}
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::Press(key) => {
let data = self.get_page_mut().as_part_mut();
data.pressed_keys.key_set_insert(key.clone());
self.pressed(key);
}
Event::Repeat(key) => {
let data = self.get_page_mut().as_part_mut();
data.pressed_keys.key_set_insert(key.clone());
self.repeated(key);
}
Event::Release(key) => {
let data = self.get_page_mut().as_part_mut();
let _was_pressed = data.pressed_keys.key_set_remove(key);
self.released(key);
}
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>),
Press(M::Key),
Repeat(M::Key),
Release(M::Key),
}
pub trait AssociatedEnvSpecificMarker {
type EnvSpecificMarker: EnvSpecific;
}
pub trait EnvSpecific: 'static {
type Key: Ord + Clone;
type KeySet: KeySet<Self::Key>;
}
pub trait KeySet<B> {
fn key_set_is_empty(&self) -> bool;
fn key_set_insert(&mut self, value: B);
fn key_set_contains(&self, value: &B) -> bool;
fn key_set_remove(&mut self, value: &B) -> bool;
}
#[cfg(feature = "std")]
impl<B> KeySet<B> for std::collections::HashSet<B>
where
B: std::hash::Hash + Eq,
{
#[inline]
fn key_set_is_empty(&self) -> bool {
self.is_empty()
}
#[inline]
fn key_set_insert(&mut self, value: B) {
let _ = self.insert(value);
}
#[inline]
fn key_set_remove(&mut self, value: &B) -> bool {
self.remove(value)
}
#[inline]
fn key_set_contains(&self, value: &B) -> bool {
self.contains(value)
}
}
#[cfg(feature = "alloc")]
impl<B> KeySet<B> for alloc::collections::BTreeSet<B>
where
B: Ord,
{
#[inline]
fn key_set_is_empty(&self) -> bool {
self.is_empty()
}
#[inline]
fn key_set_insert(&mut self, value: B) {
let _ = self.insert(value);
}
#[inline]
fn key_set_remove(&mut self, value: &B) -> bool {
self.remove(value)
}
#[inline]
fn key_set_contains(&self, value: &B) -> bool {
self.contains(value)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct EnvData<M>
where
M: EnvSpecific,
{
marker: PhantomData<M>,
pressed_keys: M::KeySet,
}
impl<M> EnvData<M>
where
M: EnvSpecific,
{
#[inline]
fn key_pressed(&self, key: &M::Key) -> bool {
self.pressed_keys.key_set_contains(key)
}
}
impl<M> Default for EnvData<M>
where
M: EnvSpecific,
M::KeySet: Default,
{
#[inline]
fn default() -> Self {
Self {
marker: PhantomData,
pressed_keys: M::KeySet::default(),
}
}
}
pub type KeyFor<T> = <T as EnvSpecific>::Key;
pub type SpecificallyFor<T> = <T as AssociatedEnvSpecificMarker>::EnvSpecificMarker;
#[test]
fn test_keyboard() {
use crate::aspects::keyboard;
use crate::env::*;
use crate::*;
mod single_aspect {
use std::collections::HashSet;
use crate::aspects::keyboard;
crate::env_for_aspect!(keyboard);
crate::compose! {
pub enum Events {
#[part]
Aspect(super::Event<EnvSpecificMarker>),
}
}
crate::compose! {
#[derive(Default)]
pub struct Page {
#[part]
pub aspect: super::EnvData<EnvSpecificMarker>,
pub time: i16,
}
}
impl keyboard::EnvSpecific for EnvSpecificMarker {
type Key = u8;
type KeySet = HashSet<u8>;
}
}
derive_sketch! {
#[sketch(
env = single_aspect,
aspects = (keyboard),
)]
struct App {
#[page] page: single_aspect::Page,
pressed: bool,
released: bool,
repeated: bool,
}
}
impl Setup for App {
fn setup(page: single_aspect::Page, _: ()) -> Self {
App {
page,
pressed: false,
released: false,
repeated: false,
}
}
}
impl Handlers for App {
fn pressed(&mut self, &key: &u8) {
assert_eq!(key, 123);
assert!(self.key_pressed(123));
self.pressed = true;
}
fn repeated(&mut self, &key: &u8) {
assert_eq!(key, 123);
assert!(self.key_pressed(123));
self.repeated = true;
}
fn released(&mut self, &key: &u8) {
assert_eq!(key, 123);
assert!(!self.key_pressed(123));
self.released = true;
}
}
let mut mill = single_aspect::Mill;
let mut app = App::setup(mill.new_page(), ());
app.handle_environment_event(&single_aspect::Events::Aspect(keyboard::Event::Press(123)));
app.handle_environment_event(&single_aspect::Events::Aspect(keyboard::Event::Repeat(123)));
app.handle_environment_event(&single_aspect::Events::Aspect(keyboard::Event::Release(
123,
)));
assert!(app.pressed);
assert!(app.released);
assert!(app.repeated);
}