1use crate::{WindowIcon, WindowId};
9use std::fmt;
10
11macro_rules! generational_id {
12 ($name:ident, $label:literal) => {
13 #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
14 pub struct $name {
15 index: u32,
16 generation: u32,
17 }
18
19 impl $name {
20 #[must_use]
21 pub const fn from_parts(index: u32, generation: u32) -> Self {
22 Self { index, generation }
23 }
24
25 #[must_use]
26 pub const fn index(self) -> u32 {
27 self.index
28 }
29
30 #[must_use]
31 pub const fn generation(self) -> u32 {
32 self.generation
33 }
34 }
35
36 impl fmt::Debug for $name {
37 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
38 write!(
39 formatter,
40 concat!($label, "Id({}, {})"),
41 self.index, self.generation
42 )
43 }
44 }
45 };
46}
47
48generational_id!(TrayItemId, "TrayItem");
49generational_id!(NotificationId, "Notification");
50
51#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
53pub enum ApplicationShellFeature {
54 TrayOrStatusItem,
55 Notifications,
56 NotificationActions,
57 NotificationUpdate,
58 NotificationDismiss,
59 TaskbarProgress,
60 ApplicationBadge,
61 TaskbarOverlayIcon,
62}
63
64#[derive(Clone, Debug, PartialEq, Eq)]
66pub enum ApplicationShellError {
67 Unsupported(ApplicationShellFeature),
68 PermissionDenied,
69 PlatformConfigurationRequired(String),
70 StaleResource,
71 ApplicationStopped,
72 NativeFailure(String),
73}
74
75impl fmt::Display for ApplicationShellError {
76 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
77 match self {
78 Self::Unsupported(feature) => write!(formatter, "{feature:?} is not supported"),
79 Self::PermissionDenied => formatter.write_str("application-shell permission denied"),
80 Self::PlatformConfigurationRequired(detail) => {
81 write!(formatter, "platform configuration is required: {detail}")
82 }
83 Self::StaleResource => formatter.write_str("application-shell resource is stale"),
84 Self::ApplicationStopped => formatter.write_str("application has stopped"),
85 Self::NativeFailure(detail) => {
86 write!(formatter, "native shell operation failed: {detail}")
87 }
88 }
89 }
90}
91
92impl std::error::Error for ApplicationShellError {}
93
94#[derive(Clone, Debug, PartialEq, Eq)]
96pub struct TrayItemPresentation {
97 pub icon: Option<WindowIcon>,
98 pub tooltip: Option<String>,
99 pub title: Option<String>,
100 pub visible: bool,
101}
102
103impl TrayItemPresentation {
104 #[must_use]
105 pub fn new() -> Self {
106 Self::default()
107 }
108
109 #[must_use]
110 pub fn icon(mut self, icon: WindowIcon) -> Self {
111 self.icon = Some(icon);
112 self
113 }
114
115 #[must_use]
116 pub fn tooltip(mut self, tooltip: impl Into<String>) -> Self {
117 self.tooltip = Some(tooltip.into());
118 self
119 }
120
121 #[must_use]
122 pub fn title(mut self, title: impl Into<String>) -> Self {
123 self.title = Some(title.into());
124 self
125 }
126
127 #[must_use]
128 pub fn visible(mut self, visible: bool) -> Self {
129 self.visible = visible;
130 self
131 }
132}
133
134impl Default for TrayItemPresentation {
135 fn default() -> Self {
136 Self {
137 icon: None,
138 tooltip: None,
139 title: None,
140 visible: true,
141 }
142 }
143}
144
145#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
147pub struct NotificationActionId(String);
148
149impl NotificationActionId {
150 #[must_use]
151 pub fn new(id: impl Into<String>) -> Self {
152 Self(id.into())
153 }
154
155 #[must_use]
156 pub fn as_str(&self) -> &str {
157 &self.0
158 }
159}
160
161impl From<&str> for NotificationActionId {
162 fn from(value: &str) -> Self {
163 Self::new(value)
164 }
165}
166
167impl From<String> for NotificationActionId {
168 fn from(value: String) -> Self {
169 Self::new(value)
170 }
171}
172
173#[derive(Clone, Debug, PartialEq, Eq)]
175pub struct NotificationAction {
176 pub id: NotificationActionId,
177 pub label: String,
178}
179
180impl NotificationAction {
181 #[must_use]
182 pub fn new(id: impl Into<NotificationActionId>, label: impl Into<String>) -> Self {
183 Self {
184 id: id.into(),
185 label: label.into(),
186 }
187 }
188}
189
190#[derive(Clone, Debug, PartialEq, Eq)]
192pub struct NotificationPresentation {
193 pub title: String,
194 pub body: String,
195 pub icon: Option<WindowIcon>,
196 pub actions: Vec<NotificationAction>,
197}
198
199impl NotificationPresentation {
200 #[must_use]
201 pub fn new(title: impl Into<String>, body: impl Into<String>) -> Self {
202 Self {
203 title: title.into(),
204 body: body.into(),
205 icon: None,
206 actions: Vec::new(),
207 }
208 }
209
210 #[must_use]
211 pub fn icon(mut self, icon: WindowIcon) -> Self {
212 self.icon = Some(icon);
213 self
214 }
215
216 #[must_use]
217 pub fn action(mut self, action: NotificationAction) -> Self {
218 self.actions.push(action);
219 self
220 }
221}
222
223#[derive(Clone, Copy, Debug, Default, PartialEq)]
225pub enum TaskbarProgress {
226 #[default]
227 None,
228 Indeterminate,
229 Normal(f64),
230 Paused(f64),
231 Error(f64),
232}
233
234impl TaskbarProgress {
235 pub fn normal(value: f64) -> Result<Self, TaskbarProgressError> {
236 validate_progress(value).map(Self::Normal)
237 }
238
239 pub fn paused(value: f64) -> Result<Self, TaskbarProgressError> {
240 validate_progress(value).map(Self::Paused)
241 }
242
243 pub fn error(value: f64) -> Result<Self, TaskbarProgressError> {
244 validate_progress(value).map(Self::Error)
245 }
246
247 #[must_use]
248 pub const fn fraction(self) -> Option<f64> {
249 match self {
250 Self::Normal(value) | Self::Paused(value) | Self::Error(value) => Some(value),
251 Self::None | Self::Indeterminate => None,
252 }
253 }
254}
255
256fn validate_progress(value: f64) -> Result<f64, TaskbarProgressError> {
257 (value.is_finite() && (0.0..=1.0).contains(&value))
258 .then_some(value)
259 .ok_or(TaskbarProgressError)
260}
261
262#[derive(Clone, Copy, Debug, PartialEq, Eq)]
263pub struct TaskbarProgressError;
264
265impl fmt::Display for TaskbarProgressError {
266 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
267 formatter.write_str("taskbar progress must be a finite fraction between 0 and 1")
268 }
269}
270
271impl std::error::Error for TaskbarProgressError {}
272
273#[derive(Clone, Debug, Default, PartialEq, Eq)]
276pub enum ApplicationBadge {
277 #[default]
278 None,
279 Count(u64),
280 Text(String),
281}
282
283#[derive(Clone, Debug, Default, PartialEq)]
287pub struct TaskbarDockState {
288 pub window_id: Option<WindowId>,
289 pub progress: TaskbarProgress,
290 pub badge: ApplicationBadge,
291 pub overlay_icon: Option<WindowIcon>,
292}
293
294impl TaskbarDockState {
295 #[must_use]
296 pub fn for_window(window_id: WindowId) -> Self {
297 Self {
298 window_id: Some(window_id),
299 ..Self::default()
300 }
301 }
302
303 #[must_use]
304 pub fn progress(mut self, progress: TaskbarProgress) -> Self {
305 self.progress = progress;
306 self
307 }
308
309 #[must_use]
310 pub fn badge(mut self, badge: ApplicationBadge) -> Self {
311 self.badge = badge;
312 self
313 }
314
315 #[must_use]
316 pub fn overlay_icon(mut self, icon: WindowIcon) -> Self {
317 self.overlay_icon = Some(icon);
318 self
319 }
320
321 #[must_use]
323 pub fn required_feature(&self) -> ApplicationShellFeature {
324 if self.overlay_icon.is_some() {
325 ApplicationShellFeature::TaskbarOverlayIcon
326 } else if self.badge != ApplicationBadge::None {
327 ApplicationShellFeature::ApplicationBadge
328 } else {
329 ApplicationShellFeature::TaskbarProgress
330 }
331 }
332}