1use crate::{DisplayId, Fullscreen, PhysicalScreenPosition, PhysicalSize};
4use incular_core::Size;
5use std::{fmt, sync::Arc};
6
7#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
9pub enum WindowLevel {
10 #[default]
11 Normal,
12 AlwaysOnTop,
13}
14
15#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
17pub enum UserAttentionType {
18 #[default]
19 Informational,
20 Critical,
21}
22
23#[derive(Clone, PartialEq, Eq)]
29pub struct WindowIcon {
30 rgba: Arc<[u8]>,
31 width: u32,
32 height: u32,
33}
34
35impl WindowIcon {
36 pub fn from_rgba(
37 rgba: impl Into<Vec<u8>>,
38 width: u32,
39 height: u32,
40 ) -> Result<Self, WindowIconError> {
41 let rgba = rgba.into();
42 if width == 0 || height == 0 {
43 return Err(WindowIconError::ZeroDimension);
44 }
45 let pixels = usize::try_from(width)
46 .ok()
47 .and_then(|width| {
48 usize::try_from(height)
49 .ok()
50 .and_then(|height| width.checked_mul(height))
51 })
52 .and_then(|pixels| pixels.checked_mul(4))
53 .ok_or(WindowIconError::DimensionsOverflow)?;
54 if rgba.len() != pixels {
55 return Err(WindowIconError::InvalidByteCount {
56 expected: pixels,
57 actual: rgba.len(),
58 });
59 }
60 Ok(Self {
61 rgba: rgba.into(),
62 width,
63 height,
64 })
65 }
66
67 #[must_use]
68 pub fn rgba(&self) -> &[u8] {
69 &self.rgba
70 }
71
72 #[must_use]
73 pub const fn width(&self) -> u32 {
74 self.width
75 }
76
77 #[must_use]
78 pub const fn height(&self) -> u32 {
79 self.height
80 }
81}
82
83impl fmt::Debug for WindowIcon {
84 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
85 formatter
86 .debug_struct("WindowIcon")
87 .field("width", &self.width)
88 .field("height", &self.height)
89 .field("rgba_bytes", &self.rgba.len())
90 .finish()
91 }
92}
93
94#[derive(Clone, Debug, PartialEq, Eq)]
95pub enum WindowIconError {
96 ZeroDimension,
97 DimensionsOverflow,
98 InvalidByteCount { expected: usize, actual: usize },
99}
100
101impl fmt::Display for WindowIconError {
102 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
103 match self {
104 Self::ZeroDimension => formatter.write_str("window icon dimensions must be non-zero"),
105 Self::DimensionsOverflow => {
106 formatter.write_str("window icon dimensions overflow addressable memory")
107 }
108 Self::InvalidByteCount { expected, actual } => write!(
109 formatter,
110 "window icon RGBA byte count is {actual}, expected {expected}"
111 ),
112 }
113 }
114}
115
116impl std::error::Error for WindowIconError {}
117
118#[derive(Clone, Copy, Debug, Default, PartialEq)]
120pub struct LogicalSizeLimits {
121 minimum: Option<Size>,
122 maximum: Option<Size>,
123}
124
125impl LogicalSizeLimits {
126 pub fn new(
127 minimum: Option<Size>,
128 maximum: Option<Size>,
129 ) -> Result<Self, LogicalSizeLimitsError> {
130 if minimum.is_some_and(|size| !is_positive_size(size)) {
131 return Err(LogicalSizeLimitsError::InvalidMinimum);
132 }
133 if maximum.is_some_and(|size| !is_positive_size(size)) {
134 return Err(LogicalSizeLimitsError::InvalidMaximum);
135 }
136 if let (Some(minimum), Some(maximum)) = (minimum, maximum)
137 && (minimum.width > maximum.width || minimum.height > maximum.height)
138 {
139 return Err(LogicalSizeLimitsError::MinimumExceedsMaximum);
140 }
141 Ok(Self { minimum, maximum })
142 }
143
144 #[must_use]
145 pub const fn minimum(self) -> Option<Size> {
146 self.minimum
147 }
148
149 #[must_use]
150 pub const fn maximum(self) -> Option<Size> {
151 self.maximum
152 }
153}
154
155#[derive(Clone, Copy, Debug, PartialEq, Eq)]
156pub enum LogicalSizeLimitsError {
157 InvalidMinimum,
158 InvalidMaximum,
159 MinimumExceedsMaximum,
160}
161
162impl fmt::Display for LogicalSizeLimitsError {
163 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
164 formatter.write_str(match self {
165 Self::InvalidMinimum => "minimum logical size must be finite and positive",
166 Self::InvalidMaximum => "maximum logical size must be finite and positive",
167 Self::MinimumExceedsMaximum => {
168 "minimum logical size cannot exceed maximum logical size"
169 }
170 })
171 }
172}
173
174impl std::error::Error for LogicalSizeLimitsError {}
175
176#[derive(Clone, Copy, Debug, PartialEq)]
182pub struct WindowRequestedState {
183 pub visible: bool,
184 pub minimized: bool,
185 pub maximized: bool,
186 pub fullscreen: Option<Fullscreen>,
187 pub resizable: bool,
188 pub decorations: bool,
189 pub size_limits: LogicalSizeLimits,
190 pub level: WindowLevel,
191}
192
193#[derive(Clone, Copy, Debug, Default, PartialEq)]
198pub struct WindowObservedState {
199 pub visible: Option<bool>,
200 pub minimized: Option<bool>,
201 pub maximized: Option<bool>,
202 pub fullscreen: Option<bool>,
203 pub resizable: Option<bool>,
204 pub decorations: Option<bool>,
205 pub outer_position: Option<PhysicalScreenPosition>,
206 pub outer_size: Option<PhysicalSize>,
207 pub current_display: Option<DisplayId>,
208}
209
210const fn is_positive_size(size: Size) -> bool {
211 size.width.is_finite() && size.height.is_finite() && size.width > 0.0 && size.height > 0.0
212}