mirror of
https://github.com/Zedfrigg/ironbar.git
synced 2025-08-17 14:51:04 +02:00
223 lines
6.1 KiB
Rust
223 lines
6.1 KiB
Rust
use crate::clients::swaync;
|
|
use crate::config::CommonConfig;
|
|
use crate::gtk_helpers::IronbarGtkExt;
|
|
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
|
use crate::{glib_recv, module_impl, send_async, spawn, try_send};
|
|
use gtk::prelude::*;
|
|
use gtk::{Align, Button, Label, Overlay};
|
|
use serde::Deserialize;
|
|
use tokio::sync::mpsc::Receiver;
|
|
use tracing::error;
|
|
|
|
#[derive(Debug, Deserialize, Clone)]
|
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
|
pub struct NotificationsModule {
|
|
/// Whether to show the current notification count.
|
|
///
|
|
/// **Default**: `true`
|
|
#[serde(default = "crate::config::default_true")]
|
|
show_count: bool,
|
|
|
|
/// SwayNC state icons.
|
|
///
|
|
/// See [icons](#icons).
|
|
#[serde(default)]
|
|
icons: Icons,
|
|
|
|
/// See [common options](module-level-options#common-options).
|
|
#[serde(flatten)]
|
|
pub common: Option<CommonConfig>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Clone)]
|
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
|
struct Icons {
|
|
/// Icon to show when the panel is closed, with no notifications.
|
|
///
|
|
/// **Default**: ``
|
|
#[serde(default = "default_icon_closed_none")]
|
|
closed_none: String,
|
|
|
|
/// Icon to show when the panel is closed, with notifications.
|
|
///
|
|
/// **Default**: ``
|
|
#[serde(default = "default_icon_closed_some")]
|
|
closed_some: String,
|
|
|
|
/// Icon to show when the panel is closed, with DnD enabled.
|
|
/// Takes higher priority than count-based icons.
|
|
///
|
|
/// **Default**: ``
|
|
#[serde(default = "default_icon_closed_dnd")]
|
|
closed_dnd: String,
|
|
|
|
/// Icon to show when the panel is open, with no notifications.
|
|
///
|
|
/// **Default**: ``
|
|
#[serde(default = "default_icon_open_none")]
|
|
open_none: String,
|
|
|
|
/// Icon to show when the panel is open, with notifications.
|
|
///
|
|
/// **Default**: ``
|
|
#[serde(default = "default_icon_open_some")]
|
|
open_some: String,
|
|
|
|
/// Icon to show when the panel is open, with DnD enabled.
|
|
/// Takes higher priority than count-based icons.
|
|
///
|
|
/// **Default**: ``
|
|
#[serde(default = "default_icon_open_dnd")]
|
|
open_dnd: String,
|
|
}
|
|
|
|
impl Default for Icons {
|
|
fn default() -> Self {
|
|
Self {
|
|
closed_none: default_icon_closed_none(),
|
|
closed_some: default_icon_closed_some(),
|
|
closed_dnd: default_icon_closed_dnd(),
|
|
open_none: default_icon_open_none(),
|
|
open_some: default_icon_open_some(),
|
|
open_dnd: default_icon_open_dnd(),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn default_icon_closed_none() -> String {
|
|
String::from("")
|
|
}
|
|
|
|
fn default_icon_closed_some() -> String {
|
|
String::from("")
|
|
}
|
|
|
|
fn default_icon_closed_dnd() -> String {
|
|
String::from("")
|
|
}
|
|
|
|
fn default_icon_open_none() -> String {
|
|
String::from("")
|
|
}
|
|
|
|
fn default_icon_open_some() -> String {
|
|
String::from("")
|
|
}
|
|
|
|
fn default_icon_open_dnd() -> String {
|
|
String::from("")
|
|
}
|
|
|
|
impl Icons {
|
|
fn icon(&self, value: swaync::Event) -> &str {
|
|
match (value.cc_open, value.count > 0, value.dnd) {
|
|
(true, _, true) => &self.open_dnd,
|
|
(true, true, false) => &self.open_some,
|
|
(true, false, false) => &self.open_none,
|
|
(false, _, true) => &self.closed_dnd,
|
|
(false, true, false) => &self.closed_some,
|
|
(false, false, false) => &self.closed_none,
|
|
}
|
|
.as_str()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub enum UiEvent {
|
|
ToggleVisibility,
|
|
}
|
|
|
|
impl Module<Overlay> for NotificationsModule {
|
|
type SendMessage = swaync::Event;
|
|
type ReceiveMessage = UiEvent;
|
|
|
|
module_impl!("notifications");
|
|
|
|
fn spawn_controller(
|
|
&self,
|
|
_info: &ModuleInfo,
|
|
context: &WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
|
mut rx: Receiver<Self::ReceiveMessage>,
|
|
) -> color_eyre::Result<()>
|
|
where
|
|
<Self as Module<Overlay>>::SendMessage: Clone,
|
|
{
|
|
let client = context.try_client::<swaync::Client>()?;
|
|
|
|
{
|
|
let client = client.clone();
|
|
let mut rx = client.subscribe();
|
|
let tx = context.tx.clone();
|
|
|
|
spawn(async move {
|
|
let initial_state = client.state().await;
|
|
|
|
match initial_state {
|
|
Ok(ev) => send_async!(tx, ModuleUpdateEvent::Update(ev)),
|
|
Err(err) => error!("{err:?}"),
|
|
};
|
|
|
|
while let Ok(ev) = rx.recv().await {
|
|
send_async!(tx, ModuleUpdateEvent::Update(ev));
|
|
}
|
|
});
|
|
}
|
|
|
|
spawn(async move {
|
|
while let Some(event) = rx.recv().await {
|
|
match event {
|
|
UiEvent::ToggleVisibility => client.toggle_visibility().await,
|
|
}
|
|
}
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn into_widget(
|
|
self,
|
|
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
|
_info: &ModuleInfo,
|
|
) -> color_eyre::Result<ModuleParts<Overlay>>
|
|
where
|
|
<Self as Module<Overlay>>::SendMessage: Clone,
|
|
{
|
|
let overlay = Overlay::new();
|
|
let button = Button::with_label(&self.icons.closed_none);
|
|
overlay.add(&button);
|
|
|
|
let label = Label::builder()
|
|
.label("0")
|
|
.halign(Align::End)
|
|
.valign(Align::Start)
|
|
.build();
|
|
|
|
if self.show_count {
|
|
label.add_class("count");
|
|
overlay.add_overlay(&label);
|
|
overlay.set_overlay_pass_through(&label, true);
|
|
}
|
|
|
|
let ctx = context.controller_tx.clone();
|
|
button.connect_clicked(move |_| {
|
|
try_send!(ctx, UiEvent::ToggleVisibility);
|
|
});
|
|
|
|
{
|
|
let button = button.clone();
|
|
|
|
glib_recv!(context.subscribe(), ev => {
|
|
let icon = self.icons.icon(ev);
|
|
button.set_label(icon);
|
|
|
|
label.set_label(&ev.count.to_string());
|
|
label.set_visible(self.show_count && ev.count > 0);
|
|
});
|
|
}
|
|
|
|
Ok(ModuleParts {
|
|
widget: overlay,
|
|
popup: None,
|
|
})
|
|
}
|
|
}
|