1use serde::{Deserialize, Serialize};
8
9use crate::content::SurfaceContent;
10
11pub type SurfaceId = String;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17#[serde(rename_all = "lowercase")]
18pub enum Role {
19 Main,
21 Aside,
23 Float,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
29#[serde(rename_all = "lowercase")]
30pub enum Edge {
31 Left,
32 Right,
33 Top,
34 Bottom,
35}
36
37#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
39#[serde(tag = "scope", rename_all = "camelCase")]
40pub enum SurfaceOwner {
41 Page { page_instance_id: String },
42 Lxapp { app_id: String },
43 Host,
44}
45
46#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
48#[serde(rename_all = "camelCase")]
49pub struct Placement {
50 #[serde(default, skip_serializing_if = "Option::is_none")]
51 pub edge: Option<Edge>,
52 #[serde(default, skip_serializing_if = "Option::is_none")]
53 pub preferred_size: Option<f64>,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
58#[serde(rename_all = "lowercase")]
59pub enum SurfaceState {
60 Mounted,
61 Hidden,
62 Minimized,
63}
64
65#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
67#[serde(tag = "to", rename_all = "lowercase")]
68pub enum FloatAnchor {
69 Screen,
70 Surface { surface_id: SurfaceId },
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
75#[serde(rename_all = "camelCase")]
76pub enum FloatDismiss {
77 TapOutside,
78 Manual,
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
83#[serde(rename_all = "camelCase")]
84pub struct SurfaceInteraction {
85 pub close_button: bool,
86 pub dismiss: FloatDismiss,
87 pub modal: bool,
88}
89
90impl SurfaceInteraction {
91 pub const fn standard() -> Self {
92 Self {
93 close_button: false,
94 dismiss: FloatDismiss::TapOutside,
95 modal: false,
96 }
97 }
98
99 pub const fn url_callback() -> Self {
100 Self {
101 close_button: true,
102 dismiss: FloatDismiss::Manual,
103 modal: true,
104 }
105 }
106
107 pub const fn window() -> Self {
108 Self {
109 close_button: false,
110 dismiss: FloatDismiss::Manual,
111 modal: false,
112 }
113 }
114}
115
116impl Default for SurfaceInteraction {
117 fn default() -> Self {
118 Self::standard()
119 }
120}
121
122#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
124#[serde(rename_all = "camelCase")]
125pub struct FloatSpec {
126 pub anchor: FloatAnchor,
127 pub dismiss: FloatDismiss,
128 pub modal: bool,
130 pub close_button: bool,
132}
133
134impl Default for FloatSpec {
135 fn default() -> Self {
136 Self {
137 anchor: FloatAnchor::Screen,
138 dismiss: FloatDismiss::TapOutside,
139 modal: false,
140 close_button: false,
141 }
142 }
143}
144
145#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
147#[serde(rename_all = "camelCase")]
148pub struct Surface {
149 pub id: SurfaceId,
150 pub role: Role,
151 pub content: SurfaceContent,
152 pub owner: SurfaceOwner,
153 #[serde(default)]
154 pub placement: Placement,
155 pub state: SurfaceState,
156 #[serde(default, skip_serializing_if = "Option::is_none")]
158 pub float: Option<FloatSpec>,
159}
160
161impl Surface {
162 pub fn lxapp(id: impl Into<SurfaceId>, role: Role, app_id: impl Into<String>) -> Self {
163 Self {
164 id: id.into(),
165 role,
166 content: SurfaceContent::Lxapp {
167 app_id: app_id.into(),
168 path: None,
169 },
170 owner: SurfaceOwner::Host,
171 placement: Placement::default(),
172 state: SurfaceState::Mounted,
173 float: if role == Role::Float {
174 Some(FloatSpec::default())
175 } else {
176 None
177 },
178 }
179 }
180
181 pub fn native(id: impl Into<SurfaceId>, role: Role, capability: impl Into<String>) -> Self {
182 Self::native_instance(id, role, capability, None)
183 }
184
185 pub fn native_instance(
186 id: impl Into<SurfaceId>,
187 role: Role,
188 capability: impl Into<String>,
189 instance_key: Option<String>,
190 ) -> Self {
191 Self {
192 id: id.into(),
193 role,
194 content: SurfaceContent::Native {
195 capability: capability.into(),
196 instance_key,
197 },
198 owner: SurfaceOwner::Host,
199 placement: Placement::default(),
200 state: SurfaceState::Mounted,
201 float: (role == Role::Float).then(FloatSpec::default),
202 }
203 }
204
205 pub fn browser(id: impl Into<SurfaceId>, role: Role, initial_url: impl Into<String>) -> Self {
206 Self {
207 id: id.into(),
208 role,
209 content: SurfaceContent::Browser {
210 initial_url: initial_url.into(),
211 reuse_by_url: true,
212 },
213 owner: SurfaceOwner::Host,
214 placement: Placement::default(),
215 state: SurfaceState::Mounted,
216 float: (role == Role::Float).then(FloatSpec::default),
217 }
218 }
219
220 pub fn is_modal_float(&self) -> bool {
221 self.role == Role::Float && self.float.as_ref().is_some_and(|f| f.modal)
222 }
223}