diff --git a/examples/urgency.rs b/examples/urgency.rs index 3931b43ba..41970bea6 100644 --- a/examples/urgency.rs +++ b/examples/urgency.rs @@ -1,26 +1,47 @@ -#[cfg(any(target_os = "windows", target_os = "macos"))] +#[cfg(target_os = "macos")] fn main() { - println!("this is an xdg only feature") + println!("Urgency is not supported on macOS") } -#[cfg(all(unix, not(target_os = "macos")))] +#[cfg(any(all(unix, not(target_os = "macos")), target_os = "windows"))] fn main() -> Result<(), Box> { use notify_rust::{Notification, Urgency::*}; - // use it this way + + #[cfg(all(unix, not(target_os = "macos")))] + println!("Testing urgency on Linux/BSD (XDG)"); + #[cfg(all(unix, not(target_os = "macos")))] + println!("Urgency is sent as a hint. Critical notifications should not timeout.\n"); + + #[cfg(target_os = "windows")] + println!("Testing urgency on Windows"); + #[cfg(target_os = "windows")] + println!("Low/Normal → Default scenario, Critical → Reminder scenario (stays on screen)\n"); + + // Test all urgency levels for urgency in &[Low, Normal, Critical] { - Notification::new() + let mut notification = Notification::new(); + notification .summary(&format!("Urgency {:?}", urgency)) - .body("This notification uses hints") - .icon("firefox") - .urgency(*urgency) - .show()?; + .body("This notification demonstrates urgency levels") + .urgency(*urgency); + + #[cfg(all(unix, not(target_os = "macos")))] + notification.icon("firefox"); + + notification.show()?; } - Notification::new() - .body("Urgency from String") - .icon("dialog-warning") - .urgency("high".try_into()?) - .show()?; + // Test urgency from string + let mut notification = Notification::new(); + notification + .summary("Urgency from String") + .body("This uses 'high' which maps to Critical") + .urgency("high".try_into()?); + + #[cfg(all(unix, not(target_os = "macos")))] + notification.icon("dialog-warning"); + + notification.show()?; Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 71feb169e..0ac5613b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,7 +91,7 @@ //! | `fn auto_icon(...)`| ✔︎ | | | //! | `fn hint(...)` | ✔︎ | ❌ | ❌ | //! | `fn timeout(...)` | ✔︎ | | ✔︎ | -//! | `fn urgency(...)` | ✔︎ | ❌ | ❌ | +//! | `fn urgency(...)` | ✔︎ | ❌ | ✔︎ | //! | `fn action(...)` | ✔︎ | | | //! | `fn id(...)` | ✔︎ | | | //! | `fn finalize(...)` | ✔︎ | ✔︎ | ✔︎ | diff --git a/src/notification.rs b/src/notification.rs index a0e636c59..e24fbca0a 100644 --- a/src/notification.rs +++ b/src/notification.rs @@ -88,6 +88,9 @@ pub struct Notification { #[cfg(target_os = "windows")] pub(crate) app_id: Option, + #[cfg(target_os = "windows")] + pub(crate) urgency: Option, + #[cfg(all(unix, not(target_os = "macos")))] pub(crate) bus: xdg::NotificationBus, @@ -325,17 +328,50 @@ impl Notification { /// Set the `urgency`. /// - /// Pick between Medium, Low and High. + /// Pick between Low, Normal, and Critical. /// /// # Platform support - /// Most Desktops on linux and bsd are far too relaxed to pay any attention to this. - /// In macOS this does not exist + /// + /// ## Linux/BSD (XDG) + /// Urgency is sent as a hint to the notification server. Most desktops are fairly relaxed + /// about urgency and may not change behavior significantly. Critical notifications are + /// intended to not timeout automatically. + /// + /// ## Windows + /// Urgency is mapped to toast scenarios: + /// - `Low` and `Normal` → Default scenario (standard toast behavior) + /// - `Critical` → Reminder scenario (stays on screen until user dismisses) + /// + /// ## macOS + /// Not currently supported. #[cfg(all(unix, not(target_os = "macos")))] pub fn urgency(&mut self, urgency: Urgency) -> &mut Notification { self.hint(Hint::Urgency(urgency)); // TODO impl as T where T: Into self } + /// Set the `urgency`. + /// + /// Pick between Low, Normal, and Critical. + /// + /// # Platform support + /// + /// ## Windows + /// Urgency is mapped to toast scenarios: + /// - `Low` and `Normal` → Default scenario (standard toast behavior) + /// - `Critical` → Reminder scenario (stays on screen until user dismisses) + /// + /// ## Linux/BSD (XDG) + /// See the Unix implementation documentation. + /// + /// ## macOS + /// Not currently supported. + #[cfg(target_os = "windows")] + pub fn urgency(&mut self, urgency: Urgency) -> &mut Notification { + self.urgency = Some(urgency); + self + } + /// Set `actions`. /// /// To quote @@ -514,6 +550,7 @@ impl Default for Notification { id: None, path_to_image: None, app_id: None, + urgency: None, } } } diff --git a/src/windows.rs b/src/windows.rs index 390ca805d..3efc138d5 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -1,6 +1,6 @@ use winrt_notification::Toast; -pub use crate::{error::*, notification::Notification, timeout::Timeout}; +pub use crate::{error::*, notification::Notification, timeout::Timeout, urgency::Urgency}; use std::{path::Path, str::FromStr}; @@ -22,6 +22,14 @@ pub(crate) fn show_notification(notification: &Notification) -> Result<()> { } }; + // Map urgency to Windows toast scenario + // Low/Normal -> Default (standard behavior) + // Critical -> Reminder (stays on screen until dismissed, matching XDG spec) + let scenario = match notification.urgency { + Some(Urgency::Critical) => Some(winrt_notification::Scenario::Reminder), + Some(Urgency::Low) | Some(Urgency::Normal) | None => None, // Default scenario + }; + let powershell_app_id = &Toast::POWERSHELL_APP_ID.to_string(); let app_id = ¬ification.app_id.as_ref().unwrap_or(powershell_app_id); let mut toast = Toast::new(app_id) @@ -30,6 +38,11 @@ pub(crate) fn show_notification(notification: &Notification) -> Result<()> { .text2(¬ification.body) .sound(sound) .duration(duration); + + // Apply scenario if urgency was set + if let Some(scenario) = scenario { + toast = toast.scenario(scenario); + } if let Some(image_path) = ¬ification.path_to_image { toast = toast.image(Path::new(&image_path), ""); }