1use crate::prelude::*;
2use rusterix::{PixelSource, Value};
3
4pub struct Window {
5 id: TheId,
6 nodeui: TheNodeUI,
7}
8
9impl Action for Window {
10 fn new() -> Self
11 where
12 Self: Sized,
13 {
14 let mut nodeui: TheNodeUI = TheNodeUI::default();
15
16 nodeui.add_item(TheNodeUIItem::OpenTree("window".into()));
17 nodeui.add_item(TheNodeUIItem::FloatEditSlider(
18 "actionWindowInset".into(),
19 "".into(),
20 "".into(),
21 0.0,
22 -1.0..=1.0,
23 false,
24 ));
25 nodeui.add_item(TheNodeUIItem::FloatEditSlider(
26 "actionWindowFrameWidth".into(),
27 "".into(),
28 "".into(),
29 0.08,
30 0.01..=1.0,
31 false,
32 ));
33 nodeui.add_item(TheNodeUIItem::CloseTree);
34
35 nodeui.add_item(TheNodeUIItem::OpenTree("material".into()));
36 nodeui.add_item(TheNodeUIItem::Icons(
37 "actionMaterialFrameTile".into(),
38 "".into(),
39 "".into(),
40 vec![(
41 TheRGBABuffer::new(TheDim::sized(36, 36)),
42 "".to_string(),
43 Uuid::nil(),
44 )],
45 ));
46 nodeui.add_item(TheNodeUIItem::Text(
47 "actionMaterialFrameTileId".into(),
48 "".into(),
49 "".into(),
50 "".into(),
51 None,
52 false,
53 ));
54 nodeui.add_item(TheNodeUIItem::Icons(
55 "actionMaterialGlassTile".into(),
56 "".into(),
57 "".into(),
58 vec![(
59 TheRGBABuffer::new(TheDim::sized(36, 36)),
60 "".to_string(),
61 Uuid::nil(),
62 )],
63 ));
64 nodeui.add_item(TheNodeUIItem::Text(
65 "actionMaterialGlassTileId".into(),
66 "".into(),
67 "".into(),
68 "".into(),
69 None,
70 false,
71 ));
72 nodeui.add_item(TheNodeUIItem::CloseTree);
73
74 nodeui.add_item(TheNodeUIItem::Markdown("desc".into(), "".into()));
75
76 Self {
77 id: TheId::named(&fl!("action_window")),
78 nodeui,
79 }
80 }
81
82 fn id(&self) -> TheId {
83 self.id.clone()
84 }
85
86 fn info(&self) -> String {
87 fl!("action_window_desc")
88 }
89
90 fn role(&self) -> ActionRole {
91 ActionRole::Editor
92 }
93
94 fn is_applicable(&self, map: &Map, _ctx: &mut TheContext, server_ctx: &ServerContext) -> bool {
95 !map.selected_sectors.is_empty()
96 && server_ctx.editor_view_mode == EditorViewMode::D2
97 && server_ctx.editing_surface.is_some()
98 }
99
100 fn load_params(&mut self, map: &Map) {
101 if let Some(sector_id) = map.selected_sectors.first()
102 && let Some(sector) = map.find_sector(*sector_id)
103 {
104 self.nodeui.set_f32_value(
105 "actionWindowInset",
106 sector.properties.get_float_default("profile_inset", 0.0),
107 );
108 self.nodeui.set_f32_value(
109 "actionWindowFrameWidth",
110 sector
111 .properties
112 .get_float_default("window_frame_width", 0.08),
113 );
114 }
115 }
116
117 fn load_params_project(&mut self, project: &Project, server_ctx: &mut ServerContext) {
118 let mut frame_icon = TheRGBABuffer::new(TheDim::sized(36, 36));
119 let mut frame_id = Uuid::nil();
120 let mut frame_text = String::new();
121 let mut glass_icon = TheRGBABuffer::new(TheDim::sized(36, 36));
122 let mut glass_id = Uuid::nil();
123 let mut glass_text = String::new();
124
125 if let Some(map) = project.get_map(server_ctx)
126 && let Some(sector_id) = map.selected_sectors.first()
127 && let Some(sector) = map.find_sector(*sector_id)
128 {
129 if let Some(Value::Source(PixelSource::TileId(id))) =
130 sector.properties.get("window_frame_source")
131 {
132 if let Some(tile) = project.tiles.get(id)
133 && !tile.is_empty()
134 {
135 frame_icon = tile.textures[0].to_rgba();
136 frame_id = *id;
137 frame_text = id.to_string();
138 }
139 }
140 if let Some(Value::Source(PixelSource::TileId(id))) =
141 sector.properties.get("window_glass_source")
142 {
143 if let Some(tile) = project.tiles.get(id)
144 && !tile.is_empty()
145 {
146 glass_icon = tile.textures[0].to_rgba();
147 glass_id = *id;
148 glass_text = id.to_string();
149 }
150 }
151 if let Some(Value::Source(PixelSource::PaletteIndex(index))) =
152 sector.properties.get("window_frame_source")
153 {
154 frame_id = Uuid::nil();
155 frame_icon = TheRGBABuffer::new(TheDim::sized(36, 36));
156 frame_text = index.to_string();
157 }
158 if let Some(Value::Source(PixelSource::PaletteIndex(index))) =
159 sector.properties.get("window_glass_source")
160 {
161 glass_id = Uuid::nil();
162 glass_icon = TheRGBABuffer::new(TheDim::sized(36, 36));
163 glass_text = index.to_string();
164 }
165 }
166 self.nodeui
167 .set_text_value("actionMaterialFrameTileId", frame_text);
168 self.nodeui
169 .set_text_value("actionMaterialGlassTileId", glass_text);
170
171 if let Some(TheNodeUIItem::Icons(_, _, _, items)) =
172 self.nodeui.get_item_mut("actionMaterialFrameTile")
173 && items.len() == 1
174 {
175 items[0].0 = frame_icon;
176 items[0].2 = frame_id;
177 }
178
179 if let Some(TheNodeUIItem::Icons(_, _, _, items)) =
180 self.nodeui.get_item_mut("actionMaterialGlassTile")
181 && items.len() == 1
182 {
183 items[0].0 = glass_icon;
184 items[0].2 = glass_id;
185 }
186 }
187
188 fn apply(
189 &self,
190 map: &mut Map,
191 _ui: &mut TheUI,
192 _ctx: &mut TheContext,
193 server_ctx: &mut ServerContext,
194 ) -> Option<ProjectUndoAtom> {
195 let prev = map.clone();
196 let mut changed = false;
197
198 let inset = self
199 .nodeui
200 .get_f32_value("actionWindowInset")
201 .unwrap_or(0.0);
202 let frame_w = self
203 .nodeui
204 .get_f32_value("actionWindowFrameWidth")
205 .unwrap_or(0.08);
206
207 let frame_icon_id = self
208 .nodeui
209 .get_tile_id("actionMaterialFrameTile", 0)
210 .unwrap_or(Uuid::nil());
211 let glass_icon_id = self
212 .nodeui
213 .get_tile_id("actionMaterialGlassTile", 0)
214 .unwrap_or(Uuid::nil());
215
216 let frame_text = self
217 .nodeui
218 .get_text_value("actionMaterialFrameTileId")
219 .unwrap_or_default();
220 let glass_text = self
221 .nodeui
222 .get_text_value("actionMaterialGlassTileId")
223 .unwrap_or_default();
224
225 let parse_source = |text: &str, fallback: Uuid| -> Option<PixelSource> {
226 let t = text.trim();
227 if !t.is_empty() {
228 if let Ok(id) = Uuid::parse_str(t) {
229 return Some(PixelSource::TileId(id));
230 }
231 if let Ok(idx) = t.parse::<u16>() {
232 return Some(PixelSource::PaletteIndex(idx));
233 }
234 }
235 if fallback != Uuid::nil() {
236 Some(PixelSource::TileId(fallback))
237 } else {
238 None
239 }
240 };
241 let frame_source = parse_source(&frame_text, frame_icon_id);
242 let glass_source = parse_source(&glass_text, glass_icon_id);
243
244 for sector_id in &map.selected_sectors.clone() {
245 if let Some(sector) = map.find_sector_mut(*sector_id) {
246 sector.properties.set("profile_op", Value::Int(4));
247 sector.properties.set("profile_inset", Value::Float(inset));
248 sector
249 .properties
250 .set("window_frame_width", Value::Float(frame_w.max(0.01)));
251
252 if let Some(source) = frame_source.clone() {
253 sector
254 .properties
255 .set("window_frame_source", Value::Source(source));
256 } else {
257 sector.properties.remove("window_frame_source");
258 }
259
260 if let Some(source) = glass_source.clone() {
261 sector
262 .properties
263 .set("window_glass_source", Value::Source(source));
264 } else {
265 sector.properties.remove("window_glass_source");
266 }
267
268 changed = true;
269 }
270 }
271
272 if changed {
273 Some(ProjectUndoAtom::MapEdit(
274 server_ctx.pc,
275 Box::new(prev),
276 Box::new(map.clone()),
277 ))
278 } else {
279 None
280 }
281 }
282
283 fn params(&self) -> TheNodeUI {
284 self.nodeui.clone()
285 }
286
287 fn handle_event(
288 &mut self,
289 event: &TheEvent,
290 project: &mut Project,
291 _ui: &mut TheUI,
292 ctx: &mut TheContext,
293 _server_ctx: &mut ServerContext,
294 ) -> bool {
295 if let TheEvent::TileDropped(id, tile_id, index) = event
296 && let Some(item) = self.nodeui.get_item_mut(&id.name)
297 && let TheNodeUIItem::Icons(_, _, _, items) = item
298 && *index < items.len()
299 && let Some(tile) = project.tiles.get(tile_id)
300 && !tile.is_empty()
301 {
302 items[*index].0 = tile.textures[0].to_rgba();
303 items[*index].2 = *tile_id;
304
305 if id.name == "actionMaterialFrameTile" {
306 self.nodeui
307 .set_text_value("actionMaterialFrameTileId", tile_id.to_string());
308 } else if id.name == "actionMaterialGlassTile" {
309 self.nodeui
310 .set_text_value("actionMaterialGlassTileId", tile_id.to_string());
311 }
312
313 ctx.ui.send(TheEvent::Custom(
314 TheId::named("Update Action List"),
315 TheValue::Empty,
316 ));
317 return true;
318 }
319 self.nodeui.handle_event(event)
320 }
321}