Easy convenient EventHandler trait

Hello, piston world!

I’m coming from ggez which has this neat mechanism to easily work with events, a trait called EventHandler. It has functions for all events and provides (usually empty) default implementations for them. The user implements that on it’s state struct, and ggez calls the appropriate method when an event occurs. This has the advantage that the user doens’t have to write the same match table for every event in every project. I started implementing this for piston, here is the code I have so far:

// The contents of this file are licensed under:
// - pistons license if this will become part of piston
// - or AGPL-3.0 otherwise

use std::any::Any;
use std::sync::Arc;

use piston::event_id::EventId;
use piston::*;

/// The aim of this trait is to reduce boilerplate code for event handling.
/// The intended way to use this trait is to implement it on the struct
/// holding the application's state, overwrite the methods for the event that
/// need to be handled and pass events to the [`handle_event`] method. It will
/// then call the appropriate handle methods.
///
/// ## Warning
///
/// This trait is not yet stable and will likely receive breaking changes!
/// Ideas: error handling, early exits
pub trait EventHandler {
    /// This function takes an event and passes the arguments on the the
    /// appropriate handling function.
    fn handle_event(&mut self, event: Event) {
        match event {
            Event::Input(Input::Button(args), number) => self.button(args, number),
            Event::Input(Input::Move(args), number) => self.motion(args, number),
            Event::Input(Input::Text(args), number) => self.text(args, number),
            Event::Input(Input::Resize(args), number) => self.resize(args, number),
            Event::Input(Input::Focus(args), number) => self.focus(args, number),
            Event::Input(Input::Cursor(args), number) => self.cursor(args, number),
            Event::Input(Input::FileDrag(args), number) => self.file_drag(args, number),
            Event::Input(Input::Close(args), number) => self.close(args, number),
            Event::Loop(Loop::Render(args)) => self.render(args),
            Event::Loop(Loop::AfterRender(args)) => self.after_render(args),
            Event::Loop(Loop::Update(args)) => self.update(args),
            Event::Loop(Loop::Idle(args)) => self.idle(args),
            Event::Custom(event_id, any, number) => self.custom(event_id, any, number),
        }
    }
    // Input event handlers
    fn button(&mut self, _: ButtonArgs, _: Option<u32>) {}
    fn motion(&mut self, _: Motion, _: Option<u32>) {}
    fn text(&mut self, _: String, _: Option<u32>) {}
    fn resize(&mut self, _: ResizeArgs, _: Option<u32>) {}
    fn focus(&mut self, _: bool, _: Option<u32>) {}
    fn cursor(&mut self, _: bool, _: Option<u32>) {}
    fn file_drag(&mut self, _: FileDrag, _: Option<u32>) {}
    fn close(&mut self, _: CloseArgs, _: Option<u32>) {}
    // Loop event handler
    fn render(&mut self, _: RenderArgs) {}
    fn after_render(&mut self, _: AfterRenderArgs) {}
    fn update(&mut self, _: UpdateArgs) {}
    fn idle(&mut self, _: IdleArgs) {}
    // Custom
    fn custom(&mut self, _: EventId, _: Arc<dyn Any + 'static + Send + Sync>, _: Option<u32>) {}
}

I’m quite new to piston and I have some questions:

  • is this a welcome idea / would it get accepted?
  • if so, in what crate (and file) shoud the trait go?
  • how should error handling work, what should the methods return?
  • piston::Event::Input and piston::Event::Custom have an Option<u32>. what does it mean?

Your feedback is appreciated!