refec
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
//! Barbar's protocol crate: the types that cross crate boundaries.
|
||||
//!
|
||||
//! Nothing here may reference app-level types (`Message`, `Cmd`, `Msg`,
|
||||
//! layer-shell effects). `common` is the hub every other crate depends on,
|
||||
//! so it must stay acyclic and pure.
|
||||
|
||||
use std::any::Any;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use iced::window;
|
||||
use iced::{Element, Task};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Service model
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// What a clock subscription wants: the tick interval and payload selector.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum ClockKind {
|
||||
Mins,
|
||||
Seconds,
|
||||
}
|
||||
|
||||
/// Clock tick payload: the value plus the kind that produced it.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct ClockPayload {
|
||||
pub kind: ClockKind,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
/// Route key a module subscribes with; `Clock(kind)` selects the ticker.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Service {
|
||||
Clock(ClockKind),
|
||||
}
|
||||
|
||||
/// A service event with a typed payload. The app only reads
|
||||
/// `key()` for fan-out; the consuming module downcasts the payload.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ServicePayloadKind {
|
||||
Clock(ClockPayload),
|
||||
}
|
||||
|
||||
impl ServicePayloadKind {
|
||||
/// Route key for this event. One arm per kind — adding a service
|
||||
/// touches only this match, never the routing code.
|
||||
pub fn key(&self) -> Service {
|
||||
match self {
|
||||
Self::Clock(payload) => Service::Clock(payload.kind),
|
||||
}
|
||||
}
|
||||
|
||||
/// Erases the typed payload for the module to downcast.
|
||||
pub fn into_payload(self) -> Arc<dyn Any + Send + Sync> {
|
||||
match self {
|
||||
Self::Clock(payload) => Arc::new(payload),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Module protocol
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// A module-local message, boxed with its owner's `id`. The app routes by
|
||||
/// `id` and never names a module's message type; the module downcasts.
|
||||
#[derive(Clone)]
|
||||
pub struct ModuleMsg {
|
||||
pub id: &'static str,
|
||||
pub payload: Arc<dyn Any + Send + Sync>,
|
||||
}
|
||||
|
||||
impl ModuleMsg {
|
||||
/// Wraps a module's own message; `id` must match its `BarModule::id`.
|
||||
pub fn new<T: Any + Send + Sync>(id: &'static str, msg: T) -> Self {
|
||||
Self {
|
||||
id,
|
||||
payload: Arc::new(msg),
|
||||
}
|
||||
}
|
||||
|
||||
/// Downcasts to the module's own message type.
|
||||
pub fn downcast<T: Any + Send + Sync>(&self) -> Option<&T> {
|
||||
self.payload.downcast_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ModuleMsg {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ModuleMsg")
|
||||
.field("id", &self.id)
|
||||
.field("payload", &self.payload.type_id())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// What a module asks the app to do. Modules never name app types; the app
|
||||
/// maps these into its own effects (`Cmd`).
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ModuleEffect {
|
||||
/// Toggle a popup for the given module id (element id anchors it).
|
||||
RequestPopup(String, String),
|
||||
/// Request removal of the popup surface.
|
||||
ClosePopup(window::Id),
|
||||
}
|
||||
|
||||
/// A bar module. Object-safe; modules live in a `BTreeMap` keyed by id. The
|
||||
/// `ModuleMsg` boundary keeps the app ignorant of each module's message enum.
|
||||
pub trait BarModule: Send {
|
||||
fn id(&self) -> &'static str;
|
||||
|
||||
/// Bar contents. When `window_id` is `Some`, this is the module's popup
|
||||
/// surface and the module returns its popup contents instead.
|
||||
fn view(&self, window_id: Option<window::Id>) -> Element<'_, ModuleMsg>;
|
||||
|
||||
/// Handles a routed message; may return app-level tasks (e.g. popups).
|
||||
fn update(&mut self, msg: ModuleMsg) -> Task<ModuleEffect>;
|
||||
|
||||
/// What services this module wants.
|
||||
fn services(&self) -> Vec<Service>;
|
||||
|
||||
/// Requested popup size when it's not the generic small menu.
|
||||
fn popup_size(&self) -> Option<(u32, u32)> {
|
||||
None
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user