msg type -> wire::push() -> update handle wire into module/service/ect
This commit is contained in:
+20
-6
@@ -1,10 +1,11 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use iced::window;
|
||||
|
||||
use common::{BarModule, Service};
|
||||
use common::{BarModule, Inbox, Service};
|
||||
use toml::Table;
|
||||
|
||||
/// Application state: module registry + popup bookkeeping.
|
||||
/// Application state: module registry, routing, popup bookkeeping.
|
||||
pub(crate) struct Bar {
|
||||
/// Module whose popup is open; None = closed.
|
||||
pub(crate) active_popup: Option<String>,
|
||||
@@ -14,22 +15,35 @@ pub(crate) struct Bar {
|
||||
pub(crate) popup_id: Option<window::Id>,
|
||||
/// Fan-out routing table: module id -> services it wants.
|
||||
pub(crate) routes: BTreeMap<&'static str, Vec<Service>>,
|
||||
/// One inbound mailbox per running service, keyed by route key. Modules
|
||||
/// send here (`Target::Service`) to talk to a running service.
|
||||
pub(crate) inputs: BTreeMap<&'static str, Inbox>,
|
||||
}
|
||||
|
||||
impl Bar {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub(crate) fn new(modules_config_table: &Table) -> Self {
|
||||
// Registry owns construction: adding a module never edits this file.
|
||||
let modules: BTreeMap<&'static str, Box<dyn BarModule>> =
|
||||
modules::all().into_iter().map(|m| (m.id(), m)).collect();
|
||||
let routes = modules
|
||||
modules::all(modules_config_table.clone())
|
||||
.into_iter()
|
||||
.map(|m| (m.id(), m))
|
||||
.collect();
|
||||
let routes: BTreeMap<&'static str, Vec<Service>> = modules
|
||||
.iter()
|
||||
.map(|(id, module)| (*id, module.services()))
|
||||
.collect();
|
||||
// One inbox per distinct service any module wants.
|
||||
let wanted: BTreeSet<&'static str> = routes.values().flatten().map(|s| s.0).collect();
|
||||
let inputs = wanted
|
||||
.into_iter()
|
||||
.map(|key| (key, Inbox::new(key)))
|
||||
.collect();
|
||||
Self {
|
||||
active_popup: None,
|
||||
modules,
|
||||
popup_id: None,
|
||||
routes,
|
||||
inputs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,10 @@ fn main() -> Result<(), iced_layershell::Error> {
|
||||
None => StartMode::Active,
|
||||
};
|
||||
|
||||
let modules = config.modules.clone().unwrap_or_default();
|
||||
|
||||
daemon(
|
||||
app::Bar::new,
|
||||
move || Bar::new(&modules),
|
||||
subscription::namespace,
|
||||
update::update,
|
||||
view::view,
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
use iced::window;
|
||||
use iced_layershell::to_layer_message;
|
||||
|
||||
use common::{ModuleEffect, ModuleMsg, ServiceEvent};
|
||||
use common::{ModuleEffect, Wire};
|
||||
|
||||
/// Synchronous input: module traffic, subscription payloads, window events.
|
||||
/// Synchronous input: wires plus window events.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum Msg {
|
||||
/// Routed module message; dispatch by `id`, module downcasts.
|
||||
Module(ModuleMsg),
|
||||
/// Service event; fan out by route key.
|
||||
Subscription(ServiceEvent),
|
||||
/// One wire; core dispatches by its `to` target.
|
||||
Wire(Wire),
|
||||
/// The compositor confirmed a window closed. The popup is really gone.
|
||||
WindowClosed(window::Id),
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ pub fn view(bar: &Bar) -> Element<'_, Message> {
|
||||
.map(|module| {
|
||||
module
|
||||
.view(Some(popup_id))
|
||||
.map(|m| Message::Event(Msg::Module(m)))
|
||||
.map(|w| Message::Event(Msg::Wire(w)))
|
||||
})
|
||||
.unwrap_or_else(|| text("unknown module").into());
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use iced::{window, Subscription};
|
||||
|
||||
use common::Service;
|
||||
use services::IntoSubscription;
|
||||
|
||||
use crate::app::Bar;
|
||||
@@ -10,17 +11,15 @@ pub(crate) fn namespace() -> String {
|
||||
}
|
||||
|
||||
/// Route subscriptions + window-close events (popup really destroyed).
|
||||
/// Each module's wants go through the services registry, so adding a
|
||||
/// service never edits this file.
|
||||
/// One subscription per service inbox, so adding a service never edits this.
|
||||
pub(crate) fn gather_subscriptions(bar: &Bar) -> Subscription<Message> {
|
||||
let route_sub = Subscription::batch(
|
||||
bar.routes
|
||||
.values()
|
||||
.flat_map(|wants| wants.iter())
|
||||
.map(|&service| {
|
||||
service
|
||||
.into_subscription()
|
||||
.map(|event| Message::Event(Msg::Subscription(event)))
|
||||
bar.inputs
|
||||
.iter()
|
||||
.map(|(&key, inbox)| {
|
||||
Service(key)
|
||||
.into_subscription(inbox.clone())
|
||||
.map(|wire| Message::Event(Msg::Wire(wire)))
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
+36
-29
@@ -1,6 +1,6 @@
|
||||
use iced::Task;
|
||||
|
||||
use common::{ModuleEffect, ModuleMsg};
|
||||
use common::{ModuleEffect, Service, Target, Wire};
|
||||
|
||||
use crate::app::Bar;
|
||||
use crate::msg::{Message, Msg};
|
||||
@@ -18,34 +18,7 @@ pub(crate) fn update(bar: &mut Bar, msg: Message) -> Task<Message> {
|
||||
/// Applies an input event to state; effects come back as `Task`s.
|
||||
fn handle_event(bar: &mut Bar, event: Msg) -> Task<Message> {
|
||||
match event {
|
||||
Msg::Module(m) => match bar.modules.get_mut(m.id) {
|
||||
// Modules return protocol tasks; wrap effects into messages.
|
||||
Some(module) => module.update(m).map(Message::Effect),
|
||||
None => Task::none(),
|
||||
},
|
||||
|
||||
Msg::Subscription(event) => {
|
||||
// Fan out by route key; the payload is opaque here, the module
|
||||
// downcasts it. New services never touch this file.
|
||||
let key = event.key;
|
||||
let payload = event.payload;
|
||||
bar.modules
|
||||
.iter_mut()
|
||||
.filter(|(id, _)| {
|
||||
bar.routes
|
||||
.get(*id)
|
||||
.is_some_and(|kinds| kinds.contains(&key))
|
||||
})
|
||||
.map(|(_, module)| {
|
||||
module
|
||||
.update(ModuleMsg {
|
||||
id: module.id(),
|
||||
payload: payload.clone(),
|
||||
})
|
||||
.map(Message::Effect)
|
||||
})
|
||||
.fold(Task::none(), |acc, t| acc.chain(t))
|
||||
}
|
||||
Msg::Wire(wire) => route(bar, wire),
|
||||
|
||||
Msg::WindowClosed(id) => {
|
||||
// Popup gone; forget it. `popup_id` stays set until this event
|
||||
@@ -59,6 +32,38 @@ fn handle_event(bar: &mut Bar, event: Msg) -> Task<Message> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Delivers a wire by its target — the only place routing lives.
|
||||
fn route(bar: &mut Bar, wire: Wire) -> Task<Message> {
|
||||
match wire.to {
|
||||
Target::Module(id) => match bar.modules.get_mut(id) {
|
||||
Some(module) => module.update(wire).map(Message::Effect),
|
||||
None => Task::none(),
|
||||
},
|
||||
|
||||
Target::Service(key) => {
|
||||
if let Some(inbox) = bar.inputs.get(key) {
|
||||
inbox.send(wire);
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Target::Topic(key) => {
|
||||
// Fan out by route key; the payload is opaque here, each module
|
||||
// downcasts it. New services never touch this file.
|
||||
let topic = Service(key);
|
||||
bar.modules
|
||||
.iter_mut()
|
||||
.filter(|(id, _)| {
|
||||
bar.routes
|
||||
.get(*id)
|
||||
.is_some_and(|keys| keys.contains(&topic))
|
||||
})
|
||||
.map(|(_, module)| module.update(wire.clone()).map(Message::Effect))
|
||||
.fold(Task::none(), |acc, t| acc.chain(t))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs an effect; may set up bar state for it (e.g. which popup is open).
|
||||
fn handle_effect(bar: &mut Bar, effect: ModuleEffect) -> Task<Message> {
|
||||
match effect {
|
||||
@@ -78,5 +83,7 @@ fn handle_effect(bar: &mut Bar, effect: ModuleEffect) -> Task<Message> {
|
||||
// renders until `WindowClosed` confirms it's gone.
|
||||
Task::done(Message::RemoveWindow(id))
|
||||
}
|
||||
|
||||
ModuleEffect::Send(wire) => route(bar, wire),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ fn bar_view(bar: &Bar) -> Element<'_, Message> {
|
||||
let mut children: Vec<Element<Message>> = Vec::new();
|
||||
|
||||
for (i, (_, module)) in bar.modules.iter().enumerate() {
|
||||
children.push(module.view(None).map(|m| Message::Event(Msg::Module(m))));
|
||||
children.push(module.view(None).map(|w| Message::Event(Msg::Wire(w))));
|
||||
if i < bar.modules.len() - 1 {
|
||||
children.push(text("|").size(14).into());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user