asked the clanker to change the arcidecture a bit
This commit is contained in:
Generated
+53
-4
@@ -335,6 +335,7 @@ name = "common"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"iced",
|
"iced",
|
||||||
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -354,8 +355,8 @@ dependencies = [
|
|||||||
"common",
|
"common",
|
||||||
"iced",
|
"iced",
|
||||||
"iced_layershell",
|
"iced_layershell",
|
||||||
"module-clock",
|
"modules",
|
||||||
"service-datatime",
|
"services",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1718,7 +1719,16 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"common",
|
"common",
|
||||||
"iced",
|
"iced",
|
||||||
"service-datatime",
|
"thiserror 2.0.20",
|
||||||
|
"toml",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "modules"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"common",
|
||||||
|
"module-clock",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2519,7 +2529,16 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "service-datatime"
|
name = "serde_spanned"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "service-datetime"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -2528,6 +2547,15 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "services"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"common",
|
||||||
|
"iced",
|
||||||
|
"service-datetime",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
@@ -2929,6 +2957,21 @@ dependencies = [
|
|||||||
"syn 3.0.5",
|
"syn 3.0.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "1.1.5+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "12c0ba9680044b4ce98d391a62094047eada0d64860b80166c39f4a6b5640785"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"serde_core",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_parser",
|
||||||
|
"toml_writer",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "1.1.1+spec-1.1.0"
|
version = "1.1.1+spec-1.1.0"
|
||||||
@@ -2959,6 +3002,12 @@ dependencies = [
|
|||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_writer"
|
||||||
|
version = "1.1.2+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.44"
|
version = "0.1.44"
|
||||||
|
|||||||
+4
-1
@@ -1,5 +1,5 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["crates/core", "crates/modules/clock", "crates/services/datatime", "crates/common"]
|
members = ["crates/core", "crates/common", "crates/modules", "crates/modules/clock", "crates/services", "crates/services/datetime"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
@@ -11,6 +11,9 @@ iced = { version = "0.14", default-features = false, features = ["wgpu", "waylan
|
|||||||
iced_layershell = { version = "0.19", default-features = false }
|
iced_layershell = { version = "0.19", default-features = false }
|
||||||
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time"] }
|
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time"] }
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
|
toml = "1.1"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
thiserror = "2"
|
||||||
|
|
||||||
# Optimize all dependencies even in dev builds; workspace members keep dev
|
# Optimize all dependencies even in dev builds; workspace members keep dev
|
||||||
# defaults (opt-level 0) and release settings under --release.
|
# defaults (opt-level 0) and release settings under --release.
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ path = "src/lib.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { workspace = true }
|
iced = { workspace = true }
|
||||||
|
toml = { workspace = true }
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
//! Barbar's protocol crate: the types that cross crate boundaries.
|
//! Barbar's protocol crate: the types that cross crate boundaries.
|
||||||
//!
|
//!
|
||||||
//! Nothing here may reference app-level types (`Message`, `Cmd`, `Msg`,
|
//! Nothing here may reference app-level types (`Message`, `Msg`,
|
||||||
//! layer-shell effects). `common` is the hub every other crate depends on,
|
//! layer-shell effects). `common` is the hub every other crate depends on,
|
||||||
//! so it must stay acyclic and pure.
|
//! so it must stay acyclic and pure.
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use iced::window;
|
use iced::window;
|
||||||
use iced::{Element, Task};
|
use iced::{Element, Rectangle, Task};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Service model
|
// Service model
|
||||||
@@ -95,12 +96,16 @@ impl fmt::Debug for ModuleMsg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What a module asks the app to do. Modules never name app types; the app
|
/// What a module asks the app to do — the single effect vocabulary shared by
|
||||||
/// maps these into its own effects (`Cmd`).
|
/// modules and app, so mapping module output into a `Message` is a plain
|
||||||
|
/// `.map(Message::Effect)` with no mirror enum.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ModuleEffect {
|
pub enum ModuleEffect {
|
||||||
/// Toggle a popup for the given module id (element id anchors it).
|
/// Toggle a popup for the given module id (element id anchors it).
|
||||||
RequestPopup(String, String),
|
RequestPopup(String, String),
|
||||||
|
/// A widget-tree pass reported a module's laid-out bounds; anchor the
|
||||||
|
/// popup there. App-internal, but one effect type keeps routing trivial.
|
||||||
|
BoundsFound(String, Rectangle),
|
||||||
/// Request removal of the popup surface.
|
/// Request removal of the popup surface.
|
||||||
ClosePopup(window::Id),
|
ClosePopup(window::Id),
|
||||||
}
|
}
|
||||||
@@ -124,4 +129,11 @@ pub trait BarModule: Send {
|
|||||||
fn popup_size(&self) -> Option<(u32, u32)> {
|
fn popup_size(&self) -> Option<(u32, u32)> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Digest config from `module.{module-id}`. Error is boxed because this
|
||||||
|
/// runs through `dyn BarModule`; each impl keeps its own concrete error
|
||||||
|
/// internally and may surface it pre-box via its constructor instead.
|
||||||
|
fn config(&mut self, _config: toml::Table) -> Result<(), Box<dyn Error>> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ iced = { workspace = true }
|
|||||||
iced_layershell = { workspace = true }
|
iced_layershell = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
# Modules
|
# Modules + services
|
||||||
module_clock = { package = "module-clock", path = "../modules/clock" }
|
modules = { path = "../modules" }
|
||||||
# Services
|
services = { path = "../services" }
|
||||||
service_datatime = { package = "service-datatime", path = "../services/datatime" }
|
|
||||||
|
|||||||
+32
-54
@@ -7,7 +7,7 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use iced::widget::{column, container, text};
|
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::daemon;
|
||||||
use iced_layershell::reexport::Anchor;
|
use iced_layershell::reexport::Anchor;
|
||||||
use iced_layershell::settings::{LayerShellSettings, Settings, StartMode};
|
use iced_layershell::settings::{LayerShellSettings, Settings, StartMode};
|
||||||
@@ -38,8 +38,9 @@ struct Bar {
|
|||||||
|
|
||||||
impl Bar {
|
impl Bar {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
let mut modules: BTreeMap<&'static str, Box<dyn BarModule>> = BTreeMap::new();
|
// Registry owns construction: adding a module never edits this file.
|
||||||
modules.insert("clock", Box::new(module_clock::Clock::new()));
|
let modules: BTreeMap<&'static str, Box<dyn BarModule>> =
|
||||||
|
modules::all().into_iter().map(|m| (m.id(), m)).collect();
|
||||||
let routes = modules
|
let routes = modules
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(id, module)| (*id, module.services()))
|
.map(|(id, module)| (*id, module.services()))
|
||||||
@@ -68,24 +69,15 @@ pub(crate) enum Msg {
|
|||||||
WindowClosed(window::Id),
|
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
|
/// iced needs one `Message` type. `to_layer_message` must sit here: it
|
||||||
/// injects the layer-shell effect variants + their `TryInto` impl.
|
/// injects the layer-shell effect variants + their `TryInto` impl.
|
||||||
#[to_layer_message(multi)]
|
#[to_layer_message(multi)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) enum Message {
|
pub(crate) enum Message {
|
||||||
Event(Msg),
|
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")
|
String::from("barbar")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maps a module-requested effect into an app message. Modules never name
|
/// Route subscriptions + window-close events (popup really destroyed).
|
||||||
/// app types; this is the only place the two meet.
|
/// Each module's wants go through the services registry, so adding a
|
||||||
fn module_effect_to_message(effect: ModuleEffect) -> Message {
|
/// service never edits this file.
|
||||||
match effect {
|
fn gather_subscriptions(bar: &Bar) -> Subscription<Message> {
|
||||||
ModuleEffect::RequestPopup(module_id, element_id) => {
|
let route_sub = Subscription::batch(
|
||||||
Message::Effect(Cmd::RequestPopup(module_id, element_id))
|
bar.routes
|
||||||
}
|
.values()
|
||||||
ModuleEffect::ClosePopup(id) => Message::Effect(Cmd::ClosePopup(id)),
|
.flat_map(|wants| wants.iter())
|
||||||
}
|
.map(|service| {
|
||||||
}
|
services::subscription(service)
|
||||||
|
.map(|event| Message::Event(Msg::Subscription(event)))
|
||||||
/// One arm per service kind: turns a route key into its subscription.
|
})
|
||||||
/// Adding a service touches only this match (plus the leaf crate).
|
.collect::<Vec<_>>(),
|
||||||
fn service_subscription(service: &ModuleService) -> Subscription<Message> {
|
);
|
||||||
match service {
|
let close_events = window::close_events().map(|id| Message::Event(Msg::WindowClosed(id)));
|
||||||
ModuleService::Clock(_) => service_datatime::ClockTicker::run()
|
Subscription::batch(vec![route_sub, close_events])
|
||||||
.map(|event| Message::Event(Msg::Subscription(event))),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), iced_layershell::Error> {
|
fn main() -> Result<(), iced_layershell::Error> {
|
||||||
@@ -138,18 +128,6 @@ fn main() -> Result<(), iced_layershell::Error> {
|
|||||||
.run()
|
.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
|
// Update
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -157,7 +135,7 @@ fn gather_subscriptions(bar: &Bar) -> Subscription<Message> {
|
|||||||
fn update(bar: &mut Bar, msg: Message) -> Task<Message> {
|
fn update(bar: &mut Bar, msg: Message) -> Task<Message> {
|
||||||
match msg {
|
match msg {
|
||||||
Message::Event(event) => handle_event(bar, event),
|
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`.
|
// Layer-shell variants injected by `to_layer_message`.
|
||||||
_ => Task::none(),
|
_ => 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> {
|
fn handle_event(bar: &mut Bar, event: Msg) -> Task<Message> {
|
||||||
match event {
|
match event {
|
||||||
Msg::Module(m) => match bar.modules.get_mut(m.id) {
|
Msg::Module(m) => match bar.modules.get_mut(m.id) {
|
||||||
// Modules return protocol tasks; map effects into `Cmd`s.
|
// Modules return protocol tasks; wrap effects into messages.
|
||||||
Some(module) => module.update(m).map(module_effect_to_message),
|
Some(module) => module.update(m).map(Message::Effect),
|
||||||
None => Task::none(),
|
None => Task::none(),
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -190,7 +168,7 @@ fn handle_event(bar: &mut Bar, event: Msg) -> Task<Message> {
|
|||||||
id: module.id(),
|
id: module.id(),
|
||||||
payload: payload.clone(),
|
payload: payload.clone(),
|
||||||
})
|
})
|
||||||
.map(module_effect_to_message)
|
.map(Message::Effect)
|
||||||
})
|
})
|
||||||
.fold(Task::none(), |acc, t| acc.chain(t))
|
.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).
|
/// 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> {
|
fn handle_effect(bar: &mut Bar, effect: ModuleEffect) -> Task<Message> {
|
||||||
match cmd {
|
match effect {
|
||||||
Cmd::RequestPopup(module_id, element_id) => {
|
ModuleEffect::RequestPopup(module_id, element_id) => {
|
||||||
// Toggle: close if already open for this module, else open.
|
// Toggle: close if already open for this module, else open.
|
||||||
if bar.active_popup.as_deref() == Some(module_id.as_str()) {
|
if bar.active_popup.as_deref() == Some(module_id.as_str()) {
|
||||||
popup::close_popup(bar)
|
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
|
// Request removal only; keep popup state so the popup content
|
||||||
// renders until `WindowClosed` confirms it's gone.
|
// renders until `WindowClosed` confirms it's gone.
|
||||||
Task::done(Message::RemoveWindow(id))
|
Task::done(Message::RemoveWindow(id))
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ use iced::{Alignment, Element, Length, Rectangle, Task};
|
|||||||
use iced_layershell::actions::IcedNewPopupSettings;
|
use iced_layershell::actions::IcedNewPopupSettings;
|
||||||
use iced_layershell::reexport::{PopupAnchor, PopupGravity};
|
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.
|
/// Default (small menu) popup size.
|
||||||
const POPUP_W: u32 = 150;
|
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.
|
/// `Msg::WindowClosed`, so popup content keeps rendering until then.
|
||||||
pub fn close_popup(bar: &mut Bar) -> Task<Message> {
|
pub fn close_popup(bar: &mut Bar) -> Task<Message> {
|
||||||
if let Some(id) = bar.popup_id {
|
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()
|
Task::none()
|
||||||
}
|
}
|
||||||
@@ -93,7 +95,7 @@ pub fn capture_bounds(module_id: String, element_id: String) -> Task<Message> {
|
|||||||
target,
|
target,
|
||||||
found: None,
|
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
|
/// 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)
|
.width(Length::Fill)
|
||||||
.align_x(Alignment::Center),
|
.align_x(Alignment::Center),
|
||||||
content,
|
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)
|
.spacing(1)
|
||||||
.into()
|
.into()
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "modules"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "modules"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
common = { path = "../common" }
|
||||||
|
module_clock = { package = "module-clock", path = "clock" }
|
||||||
@@ -9,5 +9,6 @@ path = "src/lib.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
common = { path = "../../common" }
|
common = { path = "../../common" }
|
||||||
service_datatime = { package = "service-datatime", path = "../../services/datatime" }
|
|
||||||
iced = { workspace = true }
|
iced = { workspace = true }
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
toml = { workspace = true }
|
||||||
|
|||||||
@@ -11,6 +11,14 @@ use common::{BarModule, ClockKind, ClockPayload, ModuleEffect, ModuleMsg, Servic
|
|||||||
const POPUP_W: u32 = 360;
|
const POPUP_W: u32 = 360;
|
||||||
const POPUP_H: u32 = 160;
|
const POPUP_H: u32 = 160;
|
||||||
|
|
||||||
|
/// Errors digesting the `module.clock` config table.
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum ClockError {
|
||||||
|
/// Config value was present but not a bool.
|
||||||
|
#[error("module.clock.{0} must be a bool")]
|
||||||
|
NotBool(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
/// A module-local message for the clock.
|
/// A module-local message for the clock.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum ClockMsg {
|
pub enum ClockMsg {
|
||||||
@@ -100,4 +108,12 @@ impl BarModule for Clock {
|
|||||||
fn popup_size(&self) -> Option<(u32, u32)> {
|
fn popup_size(&self) -> Option<(u32, u32)> {
|
||||||
Some((POPUP_W, POPUP_H))
|
Some((POPUP_W, POPUP_H))
|
||||||
}
|
}
|
||||||
|
fn config(&mut self, config: toml::Table) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
if let toml::Value::Boolean(e) = config["format"] {
|
||||||
|
if e {
|
||||||
|
self.show_seconds = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
//! Bar module registry: constructs every enabled module. Adding a module is
|
||||||
|
//! a new crate under `crates/modules/` plus one entry in `all()` — core
|
||||||
|
//! never changes.
|
||||||
|
|
||||||
|
use common::BarModule;
|
||||||
|
|
||||||
|
/// One instance of every enabled module, ready to insert by `id()`.
|
||||||
|
pub fn all() -> Vec<Box<dyn BarModule>> {
|
||||||
|
vec![Box::new(module_clock::Clock::new())]
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "services"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "services"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
common = { path = "../common" }
|
||||||
|
service_datetime = { package = "service-datetime", path = "datetime" }
|
||||||
|
iced = { workspace = true }
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "service-datatime"
|
name = "service-datetime"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "service_datatime"
|
name = "service_datetime"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
//! Service registry: turns a route key into the subscription that backs it.
|
||||||
|
//! Adding a service is a new crate under `crates/services/` plus one arm in
|
||||||
|
//! `subscription` — core never changes.
|
||||||
|
|
||||||
|
use iced::Subscription;
|
||||||
|
|
||||||
|
use common::{Service, ServicePayloadKind};
|
||||||
|
|
||||||
|
/// The subscription backing a route key.
|
||||||
|
pub fn subscription(service: &Service) -> Subscription<ServicePayloadKind> {
|
||||||
|
match service {
|
||||||
|
Service::Clock(_) => service_datetime::ClockTicker::run(),
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user