event loop: Decouple event handler from concrete state
This commit is contained in:
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user