1use serde::{Deserialize, Serialize};
8
9pub type SurfaceId = String;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(rename_all = "lowercase")]
16pub enum Role {
17 Main,
19 Aside,
21 Float,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
27#[serde(rename_all = "lowercase")]
28pub enum Edge {
29 Left,
30 Right,
31 Top,
32 Bottom,
33}
34
35#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
38#[serde(tag = "kind", rename_all = "lowercase")]
39pub enum SurfaceContent {
40 Entry {
42 id: String,
43 #[serde(default, skip_serializing_if = "Option::is_none")]
45 path: Option<String>,
46 },
47 Web { url: String },
51}
52
53#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
55#[serde(tag = "scope", rename_all = "camelCase")]
56pub enum SurfaceOwner {
57 Page { page_instance_id: String },
58 Lxapp { app_id: String },
59 Host,
60}
61
62#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
64#[serde(rename_all = "camelCase")]
65pub struct Placement {
66 #[serde(default, skip_serializing_if = "Option::is_none")]
67 pub edge: Option<Edge>,
68 #[serde(default, skip_serializing_if = "Option::is_none")]
69 pub preferred_size: Option<f64>,
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
74#[serde(rename_all = "lowercase")]
75pub enum SurfaceState {
76 Mounted,
77 Hidden,
78 Minimized,
79}
80
81#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
83#[serde(tag = "to", rename_all = "lowercase")]
84pub enum FloatAnchor {
85 Screen,
86 Surface { surface_id: SurfaceId },
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
91#[serde(rename_all = "camelCase")]
92pub enum FloatDismiss {
93 TapOutside,
94 Manual,
95}
96
97#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
99#[serde(rename_all = "camelCase")]
100pub struct FloatSpec {
101 pub anchor: FloatAnchor,
102 pub dismiss: FloatDismiss,
103 pub modal: bool,
105}
106
107impl Default for FloatSpec {
108 fn default() -> Self {
109 Self {
110 anchor: FloatAnchor::Screen,
111 dismiss: FloatDismiss::TapOutside,
112 modal: false,
113 }
114 }
115}
116
117#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
119#[serde(rename_all = "camelCase")]
120pub struct Surface {
121 pub id: SurfaceId,
122 pub role: Role,
123 pub content: SurfaceContent,
124 pub owner: SurfaceOwner,
125 #[serde(default)]
126 pub placement: Placement,
127 pub state: SurfaceState,
128 #[serde(default, skip_serializing_if = "Option::is_none")]
130 pub float: Option<FloatSpec>,
131}
132
133impl Surface {
134 pub fn entry(id: impl Into<SurfaceId>, role: Role, entry_id: impl Into<String>) -> Self {
136 Self {
137 id: id.into(),
138 role,
139 content: SurfaceContent::Entry {
140 id: entry_id.into(),
141 path: None,
142 },
143 owner: SurfaceOwner::Host,
144 placement: Placement::default(),
145 state: SurfaceState::Mounted,
146 float: if role == Role::Float {
147 Some(FloatSpec::default())
148 } else {
149 None
150 },
151 }
152 }
153
154 pub fn is_modal_float(&self) -> bool {
155 self.role == Role::Float && self.float.as_ref().is_some_and(|f| f.modal)
156 }
157}