Line | Count | Source (jump to first uncovered line) |
1 | | //! Requests related to the user interface. |
2 | | |
3 | | use bitflags::bitflags; |
4 | | use serde::Serialize; |
5 | | |
6 | 34 | #[derive(S28 erializ6 e)] Unexecuted instantiation: <<obws::requests::ui::Request as serde::ser::Serialize>::serialize::__AdjacentlyTagged as serde::ser::Serialize>::serialize::<&mut serde_json::ser::Serializer<&mut alloc::vec::Vec<u8>>> <<obws::requests::ui::Request as serde::ser::Serialize>::serialize::__AdjacentlyTagged as serde::ser::Serialize>::serialize::<&mut serde_json::ser::Serializer<&mut alloc::vec::Vec<u8>>> Line | Count | Source | 6 | 6 | #[derive(Serialize)] |
Unexecuted instantiation: <<obws::requests::ui::Request as serde::ser::Serialize>::serialize::__AdjacentlyTagged as serde::ser::Serialize>::serialize::<&mut serde_json::ser::Serializer<&mut alloc::vec::Vec<u8>>> <obws::requests::ui::Request as serde::ser::Serialize>::serialize::<serde::__private::ser::FlatMapSerializer<serde_json::ser::Compound<&mut alloc::vec::Vec<u8>, serde_json::ser::CompactFormatter>>> Line | Count | Source | 6 | 28 | #[derive(Serialize)] |
Unexecuted instantiation: <<obws::requests::ui::Request as serde::ser::Serialize>::serialize::__AdjacentlyTagged as serde::ser::Serialize>::serialize::<&mut serde_json::ser::Serializer<&mut alloc::vec::Vec<u8>>> Unexecuted instantiation: <obws::requests::ui::Request as serde::ser::Serialize>::serialize::<&mut serde_json::ser::Serializer<&mut alloc::vec::Vec<u8>>> Unexecuted instantiation: <<obws::requests::ui::Request as serde::ser::Serialize>::serialize::__AdjacentlyTagged as serde::ser::Serialize>::serialize::<_> Unexecuted instantiation: <<obws::requests::ui::Request as serde::ser::Serialize>::serialize::__AdjacentlyTagged as serde::ser::Serialize>::serialize::<_> Unexecuted instantiation: <<obws::requests::ui::Request as serde::ser::Serialize>::serialize::__AdjacentlyTagged as serde::ser::Serialize>::serialize::<_> Unexecuted instantiation: <obws::requests::ui::Request as serde::ser::Serialize>::serialize::<_> Unexecuted instantiation: <<obws::requests::ui::Request as serde::ser::Serialize>::serialize::__AdjacentlyTagged as serde::ser::Serialize>::serialize::<_> Unexecuted instantiation: <<obws::requests::ui::Request as serde::ser::Serialize>::serialize::__AdjacentlyTagged as serde::ser::Serialize>::serialize::<_> Unexecuted instantiation: <<obws::requests::ui::Request as serde::ser::Serialize>::serialize::__AdjacentlyTagged as serde::ser::Serialize>::serialize::<_> Unexecuted instantiation: <<obws::requests::ui::Request as serde::ser::Serialize>::serialize::__AdjacentlyTagged as serde::ser::Serialize>::serialize::<_> Unexecuted instantiation: <<obws::requests::ui::Request as serde::ser::Serialize>::serialize::__AdjacentlyTagged as serde::ser::Serialize>::serialize::<_> Unexecuted instantiation: <obws::requests::ui::Request as serde::ser::Serialize>::serialize::<_> |
7 | | #[serde(tag = "requestType", content = "requestData")] |
8 | | pub(crate) enum Request<'a> { |
9 | | #[serde(rename = "GetStudioModeEnabled")] |
10 | | GetStudioModeEnabled, |
11 | | #[serde(rename = "SetStudioModeEnabled")] |
12 | | SetStudioModeEnabled { |
13 | | /// Enable or disable the studio mode. |
14 | | #[serde(rename = "studioModeEnabled")] |
15 | | enabled: bool, |
16 | | }, |
17 | | #[serde(rename = "OpenInputPropertiesDialog")] |
18 | | OpenInputPropertiesDialog { |
19 | | /// Name of the input to open the dialog of. |
20 | | #[serde(rename = "inputName")] |
21 | | input: &'a str, |
22 | | }, |
23 | | #[serde(rename = "OpenInputFiltersDialog")] |
24 | | OpenInputFiltersDialog { |
25 | | /// Name of the input to open the dialog of. |
26 | | #[serde(rename = "inputName")] |
27 | | input: &'a str, |
28 | | }, |
29 | | #[serde(rename = "OpenInputInteractDialog")] |
30 | | OpenInputInteractDialog { |
31 | | /// Name of the input to open the dialog of. |
32 | | #[serde(rename = "inputName")] |
33 | | input: &'a str, |
34 | | }, |
35 | | #[serde(rename = "GetMonitorList")] |
36 | | GetMonitorList, |
37 | | #[serde(rename = "OpenVideoMixProjector")] |
38 | | OpenVideoMixProjector(OpenVideoMixProjectorInternal), |
39 | | #[serde(rename = "OpenSourceProjector")] |
40 | | OpenSourceProjector(OpenSourceProjectorInternal<'a>), |
41 | | } |
42 | | |
43 | | impl<'a> From<Request<'a>> for super::RequestType<'a> { |
44 | 28 | fn from(value: Request<'a>) -> Self { |
45 | 28 | super::RequestType::Ui(value) |
46 | 28 | } <obws::requests::RequestType as core::convert::From<obws::requests::ui::Request>>::from Line | Count | Source | 44 | 28 | fn from(value: Request<'a>) -> Self { | 45 | 28 | super::RequestType::Ui(value) | 46 | 28 | } |
Unexecuted instantiation: <obws::requests::RequestType as core::convert::From<obws::requests::ui::Request>>::from |
47 | | } |
48 | | |
49 | | /// Request information for [`crate::client::Ui::open_video_mix_projector`]. |
50 | | pub struct OpenVideoMixProjector { |
51 | | /// Type of mix to open. |
52 | | pub r#type: VideoMixType, |
53 | | /// Optional location for the new projector window. |
54 | | pub location: Option<Location>, |
55 | | } |
56 | | |
57 | | /// Request information for [`crate::client::Ui::open_video_mix_projector`]. |
58 | 1 | #[derive(Serialize)] <obws::requests::ui::OpenVideoMixProjectorInternal as serde::ser::Serialize>::serialize::<&mut serde_json::ser::Serializer<&mut alloc::vec::Vec<u8>>> Line | Count | Source | 58 | 1 | #[derive(Serialize)] |
Unexecuted instantiation: <obws::requests::ui::OpenVideoMixProjectorInternal as serde::ser::Serialize>::serialize::<_> Unexecuted instantiation: <obws::requests::ui::OpenVideoMixProjectorInternal as serde::ser::Serialize>::serialize::<_> |
59 | | pub(crate) struct OpenVideoMixProjectorInternal { |
60 | | /// Type of mix to open. |
61 | | #[serde(rename = "videoMixType")] |
62 | | pub r#type: VideoMixType, |
63 | | /// Optional location for the new projector window. |
64 | | #[serde(flatten)] |
65 | | pub location: Option<LocationInternal>, |
66 | | } |
67 | | |
68 | | /// Request information for [`crate::client::Ui::open_source_projector`]. |
69 | | pub struct OpenSourceProjector<'a> { |
70 | | /// Name of the source to open a projector for. |
71 | | pub source: &'a str, |
72 | | /// Optional location for the new projector window. |
73 | | pub location: Option<Location>, |
74 | | } |
75 | | |
76 | | /// Request information for [`crate::client::Ui::open_source_projector`]. |
77 | 1 | #[derive(Serialize)] <obws::requests::ui::OpenSourceProjectorInternal as serde::ser::Serialize>::serialize::<&mut serde_json::ser::Serializer<&mut alloc::vec::Vec<u8>>> Line | Count | Source | 77 | 1 | #[derive(Serialize)] |
Unexecuted instantiation: <obws::requests::ui::OpenSourceProjectorInternal as serde::ser::Serialize>::serialize::<_> Unexecuted instantiation: <obws::requests::ui::OpenSourceProjectorInternal as serde::ser::Serialize>::serialize::<_> |
78 | | pub(crate) struct OpenSourceProjectorInternal<'a> { |
79 | | /// Name of the source to open a projector for. |
80 | | #[serde(rename = "sourceName")] |
81 | | pub source: &'a str, |
82 | | /// Optional location for the new projector window. |
83 | | #[serde(flatten)] |
84 | | pub location: Option<LocationInternal>, |
85 | | } |
86 | | |
87 | | /// Request information for [`crate::client::Ui::open_video_mix_projector`] as part of |
88 | | /// [`OpenVideoMixProjector`] and [`crate::client::Ui::open_source_projector`] as part of |
89 | | /// [`OpenSourceProjector`], describing the open location of the projector. |
90 | | pub enum Location { |
91 | | /// Monitor index, passing `-1` opens the projector in windowed mode. |
92 | | MonitorIndex(i32), |
93 | | /// Size/Position data for a windowed projector, in `Qt Base64` encoded format. |
94 | | ProjectorGeometry(QtGeometry), |
95 | | } |
96 | | |
97 | | /// Request information for [`crate::client::Ui::open_video_mix_projector`] as part of |
98 | | /// [`OpenVideoMixProjector`] and [`crate::client::Ui::open_source_projector`] as part of |
99 | | /// [`OpenSourceProjector`], describing the open location of the projector. |
100 | 2 | #[derive(Serialize)] <obws::requests::ui::LocationInternal as serde::ser::Serialize>::serialize::<serde::__private::ser::FlatMapSerializer<serde_json::ser::Compound<&mut alloc::vec::Vec<u8>, serde_json::ser::CompactFormatter>>> Line | Count | Source | 100 | 2 | #[derive(Serialize)] |
Unexecuted instantiation: <obws::requests::ui::LocationInternal as serde::ser::Serialize>::serialize::<_> Unexecuted instantiation: <obws::requests::ui::LocationInternal as serde::ser::Serialize>::serialize::<_> |
101 | | pub enum LocationInternal { |
102 | | /// Monitor index, passing `-1` opens the projector in windowed mode. |
103 | | #[serde(rename = "monitorIndex")] |
104 | | MonitorIndex(i32), |
105 | | /// Size/Position data for a windowed projector, in `Qt Base64` encoded format. |
106 | | #[serde(rename = "projectorGeometry")] |
107 | | ProjectorGeometry(String), |
108 | | } |
109 | | |
110 | | impl From<Location> for LocationInternal { |
111 | 2 | fn from(value: Location) -> Self { |
112 | 2 | match value { |
113 | 1 | Location::MonitorIndex(index) => Self::MonitorIndex(index), |
114 | 1 | Location::ProjectorGeometry(geometry) => Self::ProjectorGeometry(geometry.serialize()), |
115 | | } |
116 | 2 | } <obws::requests::ui::LocationInternal as core::convert::From<obws::requests::ui::Location>>::from Line | Count | Source | 111 | 2 | fn from(value: Location) -> Self { | 112 | 2 | match value { | 113 | 1 | Location::MonitorIndex(index) => Self::MonitorIndex(index), | 114 | 1 | Location::ProjectorGeometry(geometry) => Self::ProjectorGeometry(geometry.serialize()), | 115 | | } | 116 | 2 | } |
Unexecuted instantiation: <obws::requests::ui::LocationInternal as core::convert::From<obws::requests::ui::Location>>::from |
117 | | } |
118 | | |
119 | | /// Request information for [`crate::client::Ui::open_video_mix_projector`] as part of |
120 | | /// [`OpenVideoMixProjector`], defining the type of video mix to open. |
121 | 1 | #[derive(Serialize)] <obws::requests::ui::VideoMixType as serde::ser::Serialize>::serialize::<&mut serde_json::ser::Serializer<&mut alloc::vec::Vec<u8>>> Line | Count | Source | 121 | 1 | #[derive(Serialize)] |
Unexecuted instantiation: <obws::requests::ui::VideoMixType as serde::ser::Serialize>::serialize::<_> Unexecuted instantiation: <obws::requests::ui::VideoMixType as serde::ser::Serialize>::serialize::<_> |
122 | | pub enum VideoMixType { |
123 | | /// Show the preview scene. |
124 | | #[serde(rename = "OBS_WEBSOCKET_VIDEO_MIX_TYPE_PREVIEW")] |
125 | | Preview, |
126 | | /// Show the program scene. |
127 | | #[serde(rename = "OBS_WEBSOCKET_VIDEO_MIX_TYPE_PROGRAM")] |
128 | | Program, |
129 | | /// Show a multi-view. |
130 | | #[serde(rename = "OBS_WEBSOCKET_VIDEO_MIX_TYPE_MULTIVIEW")] |
131 | | Multiview, |
132 | | } |
133 | | |
134 | | /// Request information for [`open_projector`](crate::client::General::open_projector) as part of |
135 | | /// [`Projector`]. |
136 | 0 | #[derive(Debug)] Unexecuted instantiation: <obws::requests::ui::QtGeometry as core::fmt::Debug>::fmt Unexecuted instantiation: <obws::requests::ui::QtGeometry as core::fmt::Debug>::fmt |
137 | | pub struct QtGeometry { |
138 | | /// The screen number to display a widget or [`Self::DEFAULT_SCREEN`] to let OBS pick the |
139 | | /// default. |
140 | | pub screen_number: i32, |
141 | | /// Additional window state like maximized or full-screen. |
142 | | pub window_state: QtWindowState, |
143 | | /// The width of the screen. Seems to have no specific effect but is used for some internal |
144 | | /// calculations in Qt. |
145 | | pub screen_width: i32, |
146 | | /// The target position and size for a widget to display at. |
147 | | pub rect: QtRect, |
148 | | } |
149 | | |
150 | | impl QtGeometry { |
151 | | /// Value indicating to use the default screen. |
152 | | pub const DEFAULT_SCREEN: i32 = -1; |
153 | | |
154 | | /// Create a new geometry instance without only size information set. |
155 | 0 | pub fn new(rect: QtRect) -> Self { |
156 | 0 | Self { |
157 | 0 | rect, |
158 | 0 | ..Self::default() |
159 | 0 | } |
160 | 0 | } Unexecuted instantiation: <obws::requests::ui::QtGeometry>::new Unexecuted instantiation: <obws::requests::ui::QtGeometry>::new |
161 | | |
162 | | /// Serialize this instance into a `base64` encoded byte array. |
163 | | /// |
164 | | /// The exact format can be found in the |
165 | | /// [Qt source code](https://code.woboq.org/qt5/qtbase/src/widgets/kernel/qwidget.cpp.html#_ZNK7QWidget12saveGeometryEv). |
166 | | /// |
167 | | /// | Length | Content | |
168 | | /// |--------|----------------------------------------------------------| |
169 | | /// | 4 | Magic number | |
170 | | /// | 2 | Major format version | |
171 | | /// | 2 | Minor format version | |
172 | | /// | 16 | Frame rectangle (left, top, right, bottom) 4 bytes each | |
173 | | /// | 16 | Normal rectangle (left, top, right, bottom) 4 bytes each | |
174 | | /// | 4 | Screen number | |
175 | | /// | 1 | Window maximized (1 or 0) | |
176 | | /// | 1 | Window full-screen (1 or 0) | |
177 | | /// | 4 | Screen width | |
178 | | /// | 16 | Main rectangle (left, top, right, bottom) 4 bytes each | |
179 | 1 | pub(crate) fn serialize(&self) -> String { |
180 | 1 | /// Indicator for serialized Qt geometry data. |
181 | 1 | const MAGIC_NUMBER: u32 = 0x1D9D0CB; |
182 | 1 | /// Major version of this format. |
183 | 1 | const MAJOR_VERSION: u16 = 3; |
184 | 1 | /// Minor version of this format. |
185 | 1 | const MINOR_VERSION: u16 = 0; |
186 | 1 | /// Output data length BEFORE `base64` encoding. This allows to reduce allocations in the |
187 | 1 | /// byte buffer and must be updated whenever the format changes. |
188 | 1 | const DATA_LENGTH: usize = 66; |
189 | 1 | |
190 | 3 | fn serialize_rect(data: &mut Vec<u8>, rect: &QtRect) { |
191 | 3 | data.extend(&rect.left.to_be_bytes()); |
192 | 3 | data.extend(&rect.top.to_be_bytes()); |
193 | 3 | data.extend(&rect.right.to_be_bytes()); |
194 | 3 | data.extend(&rect.bottom.to_be_bytes()); |
195 | 3 | } <obws::requests::ui::QtGeometry>::serialize::serialize_rect Line | Count | Source | 190 | 3 | fn serialize_rect(data: &mut Vec<u8>, rect: &QtRect) { | 191 | 3 | data.extend(&rect.left.to_be_bytes()); | 192 | 3 | data.extend(&rect.top.to_be_bytes()); | 193 | 3 | data.extend(&rect.right.to_be_bytes()); | 194 | 3 | data.extend(&rect.bottom.to_be_bytes()); | 195 | 3 | } |
Unexecuted instantiation: <obws::requests::ui::QtGeometry>::serialize::serialize_rect |
196 | 1 | |
197 | 1 | let mut data = Vec::<u8>::with_capacity(DATA_LENGTH); |
198 | 1 | |
199 | 1 | data.extend(&MAGIC_NUMBER.to_be_bytes()); |
200 | 1 | data.extend(&MAJOR_VERSION.to_be_bytes()); |
201 | 1 | data.extend(&MINOR_VERSION.to_be_bytes()); |
202 | 1 | |
203 | 1 | serialize_rect(&mut data, &self.rect); // frame geometry |
204 | 1 | serialize_rect(&mut data, &self.rect); // normal geometry |
205 | 1 | |
206 | 1 | data.extend(&self.screen_number.to_be_bytes()); |
207 | 1 | data.extend(&self.window_state.to_be_bytes()); |
208 | 1 | data.extend(&self.screen_width.to_be_bytes()); |
209 | 1 | |
210 | 1 | serialize_rect(&mut data, &self.rect); |
211 | 1 | |
212 | 1 | base64::encode(data) |
213 | 1 | } <obws::requests::ui::QtGeometry>::serialize Line | Count | Source | 179 | 1 | pub(crate) fn serialize(&self) -> String { | 180 | 1 | /// Indicator for serialized Qt geometry data. | 181 | 1 | const MAGIC_NUMBER: u32 = 0x1D9D0CB; | 182 | 1 | /// Major version of this format. | 183 | 1 | const MAJOR_VERSION: u16 = 3; | 184 | 1 | /// Minor version of this format. | 185 | 1 | const MINOR_VERSION: u16 = 0; | 186 | 1 | /// Output data length BEFORE `base64` encoding. This allows to reduce allocations in the | 187 | 1 | /// byte buffer and must be updated whenever the format changes. | 188 | 1 | const DATA_LENGTH: usize = 66; | 189 | 1 | | 190 | 1 | fn serialize_rect(data: &mut Vec<u8>, rect: &QtRect) { | 191 | 1 | data.extend(&rect.left.to_be_bytes()); | 192 | 1 | data.extend(&rect.top.to_be_bytes()); | 193 | 1 | data.extend(&rect.right.to_be_bytes()); | 194 | 1 | data.extend(&rect.bottom.to_be_bytes()); | 195 | 1 | } | 196 | 1 | | 197 | 1 | let mut data = Vec::<u8>::with_capacity(DATA_LENGTH); | 198 | 1 | | 199 | 1 | data.extend(&MAGIC_NUMBER.to_be_bytes()); | 200 | 1 | data.extend(&MAJOR_VERSION.to_be_bytes()); | 201 | 1 | data.extend(&MINOR_VERSION.to_be_bytes()); | 202 | 1 | | 203 | 1 | serialize_rect(&mut data, &self.rect); // frame geometry | 204 | 1 | serialize_rect(&mut data, &self.rect); // normal geometry | 205 | 1 | | 206 | 1 | data.extend(&self.screen_number.to_be_bytes()); | 207 | 1 | data.extend(&self.window_state.to_be_bytes()); | 208 | 1 | data.extend(&self.screen_width.to_be_bytes()); | 209 | 1 | | 210 | 1 | serialize_rect(&mut data, &self.rect); | 211 | 1 | | 212 | 1 | base64::encode(data) | 213 | 1 | } |
Unexecuted instantiation: <obws::requests::ui::QtGeometry>::serialize |
214 | | } |
215 | | |
216 | | impl Default for QtGeometry { |
217 | 1 | fn default() -> Self { |
218 | 1 | Self { |
219 | 1 | screen_number: Self::DEFAULT_SCREEN, |
220 | 1 | window_state: QtWindowState::default(), |
221 | 1 | screen_width: 0, |
222 | 1 | rect: QtRect::default(), |
223 | 1 | } |
224 | 1 | } <obws::requests::ui::QtGeometry as core::default::Default>::default Line | Count | Source | 217 | 1 | fn default() -> Self { | 218 | 1 | Self { | 219 | 1 | screen_number: Self::DEFAULT_SCREEN, | 220 | 1 | window_state: QtWindowState::default(), | 221 | 1 | screen_width: 0, | 222 | 1 | rect: QtRect::default(), | 223 | 1 | } | 224 | 1 | } |
Unexecuted instantiation: <obws::requests::ui::QtGeometry as core::default::Default>::default |
225 | | } |
226 | | |
227 | | bitflags! { |
228 | | /// Request information for [`open_projector`](crate::client::General::open_projector) as part of |
229 | | /// [`Projector`]. |
230 | 1 | #[derive(Default)] <obws::requests::ui::QtWindowState as core::default::Default>::default Line | Count | Source | 230 | 1 | #[derive(Default)] |
Unexecuted instantiation: <obws::requests::ui::QtWindowState as core::default::Default>::default |
231 | | pub struct QtWindowState: u32 { |
232 | | /// Window with maximum size, taking up as much space as possible but still showing |
233 | | /// the window frame. |
234 | | const MAXIMIZED = 2; |
235 | | /// Show the window in full-screen mode, taking up the whole display. |
236 | | const FULLSCREEN = 4; |
237 | | } |
238 | | } |
239 | | |
240 | | impl QtWindowState { |
241 | | /// Convert the state into a byte array for usage in [`QtGeometry::serialize`] . |
242 | 1 | fn to_be_bytes(self) -> [u8; 2] { |
243 | 1 | [ |
244 | 1 | if self.contains(Self::MAXIMIZED) { 10 } else { 0 }, |
245 | 1 | if self.contains(Self::FULLSCREEN) { |
246 | 0 | 1 |
247 | | } else { |
248 | 1 | 0 |
249 | | }, |
250 | | ] |
251 | 1 | } <obws::requests::ui::QtWindowState>::to_be_bytes Line | Count | Source | 242 | 1 | fn to_be_bytes(self) -> [u8; 2] { | 243 | 1 | [ | 244 | 1 | if self.contains(Self::MAXIMIZED) { 10 } else { 0 }, | 245 | 1 | if self.contains(Self::FULLSCREEN) { | 246 | 0 | 1 | 247 | | } else { | 248 | 1 | 0 | 249 | | }, | 250 | | ] | 251 | 1 | } |
Unexecuted instantiation: <obws::requests::ui::QtWindowState>::to_be_bytes |
252 | | } |
253 | | |
254 | | /// Request information for [`open_projector`](crate::client::General::open_projector) as part of |
255 | | /// [`Projector`]. |
256 | | /// |
257 | | /// This describes a position on the screen starting from the top left corner with 0. |
258 | | /// |
259 | | /// ```txt |
260 | | /// Screen |
261 | | /// ┌────────────────────── X |
262 | | /// │ |
263 | | /// │ top |
264 | | /// │ ┌────────┐ |
265 | | /// │ left │ Rect │ right |
266 | | /// │ └────────┘ |
267 | | /// │ bottom |
268 | | /// │ |
269 | | /// Y |
270 | | /// |
271 | 1 | #[derive(Clone0 , Copy, Debug0 , Default)] Unexecuted instantiation: <obws::requests::ui::QtRect as core::clone::Clone>::clone Unexecuted instantiation: <obws::requests::ui::QtRect as core::clone::Clone>::clone Unexecuted instantiation: <obws::requests::ui::QtRect as core::fmt::Debug>::fmt Unexecuted instantiation: <obws::requests::ui::QtRect as core::fmt::Debug>::fmt <obws::requests::ui::QtRect as core::default::Default>::default Line | Count | Source | 271 | 1 | #[derive(Clone, Copy, Debug, Default)] |
Unexecuted instantiation: <obws::requests::ui::QtRect as core::default::Default>::default |
272 | | pub struct QtRect { |
273 | | /// Left or X/horizontal position of the rectangle. |
274 | | pub left: i32, |
275 | | /// Top or Y/vertical position of the rectangle. |
276 | | pub top: i32, |
277 | | /// The right side of a rectangle counted from the left. For example with `left = 100` and |
278 | | /// `right = 300` the width would be `200`. |
279 | | pub right: i32, |
280 | | /// Bottom side of a rectangle counted from the top. For example with `top = 100` and |
281 | | //// `bottom = 300` the height would be `200`. |
282 | | pub bottom: i32, |
283 | | } |