asked the clanker to change the arcidecture a bit

This commit is contained in:
2026-09-09 15:50:07 +01:00
parent 3490af52e4
commit b9c7e73824
15 changed files with 185 additions and 74 deletions
+32 -54
View File
@@ -7,7 +7,7 @@
use std::collections::BTreeMap;
use iced::widget::{column, container, text};
use iced::{window, Alignment, Element, Length, Rectangle, Subscription, Task, Theme};
use iced::{window, Alignment, Element, Length, Subscription, Task, Theme};
use iced_layershell::daemon;
use iced_layershell::reexport::Anchor;
use iced_layershell::settings::{LayerShellSettings, Settings, StartMode};
@@ -38,8 +38,9 @@ struct Bar {
impl Bar {
fn new() -> Self {
let mut modules: BTreeMap<&'static str, Box<dyn BarModule>> = BTreeMap::new();
modules.insert("clock", Box::new(module_clock::Clock::new()));
// 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
.iter()
.map(|(id, module)| (*id, module.services()))
@@ -68,24 +69,15 @@ pub(crate) enum Msg {
WindowClosed(window::Id),
}
/// App-level effects (popups, window ops), emitted via `Task<Message>`.
#[derive(Debug, Clone)]
pub(crate) enum Cmd {
/// Toggle a module's popup: close if open for it, else open it.
RequestPopup(String, String),
/// Widget-tree pass reported a module's laid-out bounds; anchor popup.
BoundsFound(String, Rectangle),
/// Request removal of the popup surface.
ClosePopup(window::Id),
}
/// iced needs one `Message` type. `to_layer_message` must sit here: it
/// injects the layer-shell effect variants + their `TryInto` impl.
#[to_layer_message(multi)]
#[derive(Debug, Clone)]
pub(crate) enum Message {
Event(Msg),
Effect(Cmd),
/// Effects. `common::ModuleEffect` is the one effect vocabulary, so
/// mapping module output is a plain `.map(Message::Effect)`.
Effect(ModuleEffect),
}
// ---------------------------------------------------------------------------
@@ -96,24 +88,22 @@ fn namespace() -> String {
String::from("barbar")
}
/// Maps a module-requested effect into an app message. Modules never name
/// app types; this is the only place the two meet.
fn module_effect_to_message(effect: ModuleEffect) -> Message {
match effect {
ModuleEffect::RequestPopup(module_id, element_id) => {
Message::Effect(Cmd::RequestPopup(module_id, element_id))
}
ModuleEffect::ClosePopup(id) => Message::Effect(Cmd::ClosePopup(id)),
}
}
/// One arm per service kind: turns a route key into its subscription.
/// Adding a service touches only this match (plus the leaf crate).
fn service_subscription(service: &ModuleService) -> Subscription<Message> {
match service {
ModuleService::Clock(_) => service_datatime::ClockTicker::run()
.map(|event| Message::Event(Msg::Subscription(event))),
}
/// 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.
fn gather_subscriptions(bar: &Bar) -> Subscription<Message> {
let route_sub = Subscription::batch(
bar.routes
.values()
.flat_map(|wants| wants.iter())
.map(|service| {
services::subscription(service)
.map(|event| Message::Event(Msg::Subscription(event)))
})
.collect::<Vec<_>>(),
);
let close_events = window::close_events().map(|id| Message::Event(Msg::WindowClosed(id)));
Subscription::batch(vec![route_sub, close_events])
}
fn main() -> Result<(), iced_layershell::Error> {
@@ -138,18 +128,6 @@ fn main() -> Result<(), iced_layershell::Error> {
.run()
}
/// Route subscriptions + window-close events (popup really destroyed).
fn gather_subscriptions(bar: &Bar) -> Subscription<Message> {
let route_sub = Subscription::batch(
bar.routes
.values()
.flat_map(|x| x.iter().map(service_subscription))
.collect::<Vec<_>>(),
);
let close_events = window::close_events().map(|id| Message::Event(Msg::WindowClosed(id)));
Subscription::batch(vec![route_sub, close_events])
}
// ---------------------------------------------------------------------------
// Update
// ---------------------------------------------------------------------------
@@ -157,7 +135,7 @@ fn gather_subscriptions(bar: &Bar) -> Subscription<Message> {
fn update(bar: &mut Bar, msg: Message) -> Task<Message> {
match msg {
Message::Event(event) => handle_event(bar, event),
Message::Effect(cmd) => handle_effect(bar, cmd),
Message::Effect(effect) => handle_effect(bar, effect),
// Layer-shell variants injected by `to_layer_message`.
_ => Task::none(),
}
@@ -167,8 +145,8 @@ fn update(bar: &mut Bar, msg: Message) -> Task<Message> {
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; map effects into `Cmd`s.
Some(module) => module.update(m).map(module_effect_to_message),
// Modules return protocol tasks; wrap effects into messages.
Some(module) => module.update(m).map(Message::Effect),
None => Task::none(),
},
@@ -190,7 +168,7 @@ fn handle_event(bar: &mut Bar, event: Msg) -> Task<Message> {
id: module.id(),
payload: payload.clone(),
})
.map(module_effect_to_message)
.map(Message::Effect)
})
.fold(Task::none(), |acc, t| acc.chain(t))
}
@@ -208,9 +186,9 @@ fn handle_event(bar: &mut Bar, event: Msg) -> Task<Message> {
}
/// Runs an effect; may set up bar state for it (e.g. which popup is open).
fn handle_effect(bar: &mut Bar, cmd: Cmd) -> Task<Message> {
match cmd {
Cmd::RequestPopup(module_id, element_id) => {
fn handle_effect(bar: &mut Bar, effect: ModuleEffect) -> Task<Message> {
match effect {
ModuleEffect::RequestPopup(module_id, element_id) => {
// Toggle: close if already open for this module, else open.
if bar.active_popup.as_deref() == Some(module_id.as_str()) {
popup::close_popup(bar)
@@ -219,9 +197,9 @@ fn handle_effect(bar: &mut Bar, cmd: Cmd) -> Task<Message> {
}
}
Cmd::BoundsFound(module_id, bounds) => popup::open_popup(bar, module_id, bounds),
ModuleEffect::BoundsFound(module_id, bounds) => popup::open_popup(bar, module_id, bounds),
Cmd::ClosePopup(id) => {
ModuleEffect::ClosePopup(id) => {
// Request removal only; keep popup state so the popup content
// renders until `WindowClosed` confirms it's gone.
Task::done(Message::RemoveWindow(id))
+7 -4
View File
@@ -10,7 +10,9 @@ use iced::{Alignment, Element, Length, Rectangle, Task};
use iced_layershell::actions::IcedNewPopupSettings;
use iced_layershell::reexport::{PopupAnchor, PopupGravity};
use crate::{Bar, Cmd, Message, Msg};
use common::ModuleEffect;
use crate::{Bar, Message, Msg};
/// Default (small menu) popup size.
const POPUP_W: u32 = 150;
@@ -52,7 +54,7 @@ pub fn open_popup(bar: &mut Bar, module_id: String, bounds: Rectangle) -> Task<M
/// `Msg::WindowClosed`, so popup content keeps rendering until then.
pub fn close_popup(bar: &mut Bar) -> Task<Message> {
if let Some(id) = bar.popup_id {
return Task::done(Message::Effect(Cmd::ClosePopup(id)));
return Task::done(Message::Effect(ModuleEffect::ClosePopup(id)));
}
Task::none()
}
@@ -93,7 +95,7 @@ pub fn capture_bounds(module_id: String, element_id: String) -> Task<Message> {
target,
found: None,
})
.map(move |bounds| Message::Effect(Cmd::BoundsFound(module_id.clone(), bounds)))
.map(move |bounds| Message::Effect(ModuleEffect::BoundsFound(module_id.clone(), bounds)))
}
/// Popup widget tree on its own LayerShell surface; the active module
@@ -117,7 +119,8 @@ pub fn view(bar: &Bar) -> Element<'_, Message> {
.width(Length::Fill)
.align_x(Alignment::Center),
content,
mouse_area(text("close").size(12)).on_press(Message::Effect(Cmd::ClosePopup(popup_id))),
mouse_area(text("close").size(12))
.on_press(Message::Effect(ModuleEffect::ClosePopup(popup_id))),
]
.spacing(1)
.into()