event loop: Decouple event handler from concrete state

This commit is contained in:
Dorota Czaplejewicz
2022-11-28 14:57:06 +00:00
parent d51408a3e0
commit e7c2350c92
2 changed files with 17 additions and 11 deletions

View File

@ -38,12 +38,6 @@
pub mod driver; pub mod driver;
// This module is tightly coupled to the shape of data passed around in this project.
// That's not a problem as long as there's only one loop.
// They can still be abstracted into Traits,
// and the loop parametrized over them.
use crate::main::Commands;
use crate::state;
use std::cmp; use std::cmp;
use std::time::{ Duration, Instant }; use std::time::{ Duration, Instant };
@ -55,13 +49,22 @@ pub trait Event: Clone {
fn get_timeout_reached(&self) -> Option<Instant>; fn get_timeout_reached(&self) -> Option<Instant>;
} }
/// Contains and updates the intenal state of the actor. /// The externally observable state of the actor.
pub trait Outcome {
type Commands;
/// Returns the instructions to emit in order to change the current visible state to the desired one.
fn get_commands_to_reach(&self, desired: &Self) -> Self::Commands;
}
/// Contains and calculates the intenal state of the actor.
pub trait ActorState: Clone { pub trait ActorState: Clone {
type Event: Event; type Event: Event;
type Outcome: Outcome;
/// Returns the new internal state after the event gets processed. /// Returns the new internal state after the event gets processed.
fn apply_event(self, e: Self::Event, time: Instant) -> Self; fn apply_event(self, e: Self::Event, time: Instant) -> Self;
/// Returns the observable state of the actor given this internal state. /// Returns the observable state of the actor given this internal state.
fn get_outcome(&self, time: Instant) -> state::Outcome; fn get_outcome(&self, time: Instant) -> Self::Outcome;
/// Returns the next wake up to schedule if one is needed. /// Returns the next wake up to schedule if one is needed.
/// This may be called at any time, so should always return the correct value. /// This may be called at any time, so should always return the correct value.
fn get_next_wake(&self, now: Instant) -> Option<Instant>; fn get_next_wake(&self, now: Instant) -> Option<Instant>;
@ -95,7 +98,7 @@ fn handle_event<S: ActorState>(
mut loop_state: State<S>, mut loop_state: State<S>,
event: S::Event, event: S::Event,
now: Instant, now: Instant,
) -> (State<S>, Commands) { ) -> (State<S>, <S::Outcome as Outcome>::Commands) {
// Calculate changes to send to the consumer, // Calculate changes to send to the consumer,
// based on publicly visible state. // based on publicly visible state.
// The internal state may change more often than the publicly visible one, // The internal state may change more often than the publicly visible one,
@ -170,6 +173,7 @@ mod test {
use crate::animation; use crate::animation;
use crate::imservice::{ ContentHint, ContentPurpose }; use crate::imservice::{ ContentHint, ContentPurpose };
use crate::panel; use crate::panel;
use crate::state;
use crate::state::{ Application, InputMethod, InputMethodDetails, Presence, visibility }; use crate::state::{ Application, InputMethod, InputMethodDetails, Presence, visibility };
use crate::state::test::application_with_fake_output; use crate::state::test::application_with_fake_output;

View File

@ -130,13 +130,14 @@ pub struct Outcome {
pub im: InputMethod, pub im: InputMethod,
} }
impl Outcome { impl event_loop::Outcome for Outcome {
type Commands = Commands;
/// Returns the commands needed to apply changes as required by the new state. /// Returns the commands needed to apply changes as required by the new state.
/// This implementation doesn't actually take the old state into account, /// This implementation doesn't actually take the old state into account,
/// instead issuing all the commands as needed to reach the new state. /// instead issuing all the commands as needed to reach the new state.
/// The receivers of the commands bear the burden /// The receivers of the commands bear the burden
/// of checking if the commands end up being no-ops. /// of checking if the commands end up being no-ops.
pub fn get_commands_to_reach(&self, new_state: &Self) -> Commands { fn get_commands_to_reach(&self, new_state: &Self) -> Commands {
// FIXME: handle switching outputs // FIXME: handle switching outputs
let (dbus_visible_set, panel_visibility) = match new_state.panel { let (dbus_visible_set, panel_visibility) = match new_state.panel {
animation::Outcome::Visible{output, height, ..} animation::Outcome::Visible{output, height, ..}
@ -440,6 +441,7 @@ Outcome:
impl ActorState for Application { impl ActorState for Application {
type Event = Event; type Event = Event;
type Outcome = Outcome;
fn apply_event(self, e: Self::Event, time: Instant) -> Self { fn apply_event(self, e: Self::Event, time: Instant) -> Self {
Self::apply_event(self, e, time) Self::apply_event(self, e, time)