Skip to content

Encode event lifecycle in the type-system #2903

@madsmtm

Description

@madsmtm

We have a lot of invariants in how our events are delivered, but these are only vaguely documented. Examples of things that commonly go wrong:

  • Initializing windows outside NewEvents(Init)/Resumed
  • Not suspending / resetting the draw buffers on Suspended

To remedy this situation, I propose we change the run method(s) to use a trait object instead of just a closure.

Initial draft for what typical usage will look like (this will need a lot of tweaking, especially with regards to ControlFlow, and possibly we should consider a SimpleApplication trait that handles some of this for you):

struct MyApp {
    window: Window,
}

struct SuspendedApp {
    // ...
}

impl ApplicationHandler for MyApp {
    type SuspendedState = SuspendedApp;

    fn init(event_loop: &EventLoopWindowTarget) -> Self {
        let window = WindowBuilder::new().build(event_loop);
        Self { window }
    }

    fn suspend(self) -> Self::SuspendedState {
        todo!()
    }

    fn resume(state: Self::SuspendedState, event_loop: &EventLoopWindowTarget) -> Self {
        todo!()
    }

    fn on_event(
        &mut self,
        event: Event<'_>,
        event_loop: &EvenLoopWindowTarget,
        control_flow: &mut ControlFlow,
    ) {
         // The usual event handling in here
    }
}

impl Drop for MyApp { ... } // LoopDestroyed

fn main() {
    let event_loop = EventLoop::new();
    event_loop.run::<MyApp>();
}

The closure we have now is really nice though, we loose the easiness offered by that, but this is strictly more correct, which I think is important!

Again, this will need a lot of tweaking, opening as an issue first to gather opinions.

Relates to #2900, #2010, and many, many more.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions