Skip to main content

eave_api/
widget.rs

1use std::fmt::Debug;
2
3use serde::de::DeserializeOwned;
4
5use crate::ui::{Action, Bay, Item, Notice, Panel, Part};
6use crate::{Effect, Subscription, Task};
7
8/// Something that sits in the bar, with an optional popover.
9///
10/// It follows the same Elm shape as the host: state lives in `Self`, changes arrive as messages in
11/// [`update`](Widget::update), and the views are pure functions of state. `update` must not block:
12/// do I/O in a [`Task`] or a [`Subscription`].
13pub trait Widget: Sized + 'static {
14    /// Options from this widget's entry in `config.toml`, minus the `widget` key.
15    ///
16    /// The entry may be empty, so use `#[serde(default)]` for options that have one.
17    type Config: DeserializeOwned;
18    type Message: Clone + Debug + Send + Sync + 'static;
19
20    fn new(config: Self::Config) -> (Self, Task<Self::Message>);
21
22    fn update(&mut self, message: Self::Message) -> Effect<Self::Message>;
23
24    fn subscription(&self) -> Subscription<Self::Message> {
25        Subscription::none()
26    }
27
28    /// What the widget shows in the bar.
29    fn item(&self) -> Item<Self::Message>;
30
31    /// The popover contents, if this widget has one.
32    ///
33    /// When it returns `Some` and the item has no `on_press`, a click on the item opens and
34    /// closes the popover. Returning `None` while the popover is open closes it.
35    fn popover(&self) -> Option<Panel<Self::Message>> {
36        None
37    }
38
39    /// The dialog contents, if this widget has one: a panel in the middle of the screen, wider
40    /// than a popover, for what needs room, like a conversation. It opens only when asked
41    /// ([`Effect::open_dialog`]), never on a click. Returning `None` while it's open closes it.
42    fn dialog(&self) -> Option<Panel<Self::Message>> {
43        None
44    }
45
46    /// Notifications to show on screen now, newest first. Only for a notification engine: the
47    /// widget that serves `org.freedesktop.Notifications` lists here what it received. Any other
48    /// widget sends its notifications with `Notify` on the session bus, like any app.
49    ///
50    /// While there are any, the bar's right island turns into a panel of them, like a popover. The
51    /// host takes each one away once its key is no longer in the list. Keep the notices you mean to show in your state, and drop
52    /// one when it's dismissed or expires.
53    fn notices(&self) -> Vec<Notice<Self::Message>> {
54        Vec::new()
55    }
56
57    /// What the widget can do, for the dormer: the host's search dialog, which a keybind opens.
58    /// Typing finds an action by its title, subtitle and keywords, and picking it sends its message
59    /// to this widget, like a click would.
60    ///
61    /// The host reads them only while the dormer is open, after each of your messages, so they can
62    /// follow your state: "Mute" while sound plays, "Unmute" while muted. Leave out what can't be
63    /// done right now. Keep it pure and cheap, like the views.
64    fn actions(&self) -> Vec<Action<Self::Message>> {
65        Vec::new()
66    }
67
68    /// What this widget shows in another widget's [`bay`](crate::ui::bay), when the user attached
69    /// it there with `parts = [...]` in config.toml. `None` for a bay or subject it has nothing for.
70    ///
71    /// Where a widget sits is the user's choice: in a zone, the host reads [`item`](Widget::item)
72    /// and [`popover`](Widget::popover) and never this; as a part, it reads only this. Everything
73    /// else works the same in both places, and effects land where the widget shows:
74    /// `open_popover` opens the owner's popover, a nudge nudges the owner's island. A widget that
75    /// fits both places can return its popover here (`Some(self.popover()?.into())`), or share
76    /// its content without the header, since the owner already says what it's about.
77    ///
78    /// Asked only while that popover or dialog is on screen. Keep it pure and cheap, like the views.
79    fn part(&self, bay: &Bay) -> Option<Part<Self::Message>> {
80        let _ = bay;
81        None
82    }
83}