1use crate::editor::TOOLLIST;
2use crate::prelude::*;
3use codegridfx::DebugModule;
4
5#[derive(Clone, Copy, PartialEq)]
6pub enum DockManagerState {
7 Minimized,
8 Maximized,
9 Editor,
10}
11
12pub struct DockManager {
13 pub state: DockManagerState,
14
15 pub docks: IndexMap<String, Box<dyn Dock>>,
16
17 pub editor_canvases: IndexMap<String, usize>,
18 pub editor_docks: IndexMap<String, Box<dyn Dock>>,
19
20 pub dock: String,
21 pub index: usize,
22 pub editor_index: Option<usize>,
23
24 pub supports_undo: bool,
25}
26
27impl Default for DockManager {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl DockManager {
34 pub fn new() -> Self {
35 let mut docks = IndexMap::default();
36
37 let dock: Box<dyn Dock> = Box::new(crate::docks::tiles::TilesDock::new());
38 docks.insert("Tiles".into(), dock);
39
40 let dock: Box<dyn Dock> = Box::new(crate::docks::visual_code::VisualCodeDock::new());
41 docks.insert("Visual Code".into(), dock);
42
43 let dock: Box<dyn Dock> = Box::new(crate::docks::code::CodeDock::new());
44 docks.insert("Code".into(), dock);
45
46 let dock: Box<dyn Dock> = Box::new(crate::docks::data::DataDock::new());
47 docks.insert("Data".into(), dock);
48
49 let dock: Box<dyn Dock> = Box::new(crate::docks::log::LogDock::new());
50 docks.insert("Log".into(), dock);
51
52 let dock: Box<dyn Dock> = Box::new(crate::docks::console::ConsoleDock::new());
53 docks.insert("Console".into(), dock);
54
55 let dock: Box<dyn Dock> = Box::new(crate::docks::tilemap::TilemapDock::new());
56 docks.insert("Tilemap".into(), dock);
57
58 Self {
59 state: DockManagerState::Minimized,
60 docks,
61 editor_canvases: IndexMap::default(),
62 editor_docks: IndexMap::default(),
63 dock: "".into(),
64 index: 0,
65 editor_index: None,
66 supports_undo: false,
67 }
68 }
69
70 pub fn init(&mut self, ctx: &mut TheContext) -> TheCanvas {
71 let mut canvas: TheCanvas = TheCanvas::new();
72
73 let mut shared_layout = TheSharedHLayout::new(TheId::named("Dock Shared Layout"));
74 shared_layout.set_shared_ratio(1.0 - 0.27);
75 shared_layout.set_mode(TheSharedHLayoutMode::Shared);
76
77 let mut dock_canvas = TheCanvas::new();
80 let mut dock_stack = TheStackLayout::new(TheId::named("Dock Stack"));
81
82 for dock in &mut self.docks.values_mut() {
83 let canvas = dock.setup(ctx);
84 dock_stack.add_canvas(canvas);
85 }
86
87 dock_canvas.set_layout(dock_stack);
88 shared_layout.add_canvas(dock_canvas);
89
90 let mut action_canvas: TheCanvas = TheCanvas::new();
92
93 let mut toolbar_canvas = TheCanvas::default();
94 let traybar_widget = TheTraybar::new(TheId::empty());
95 toolbar_canvas.set_widget(traybar_widget);
96 let mut toolbar_hlayout = TheHLayout::new(TheId::empty());
97 toolbar_hlayout.set_background_color(None);
98
99 let mut text = TheText::new(TheId::named("Action Text"));
100 text.set_text(fl!("dock_auto"));
101 text.set_text_size(12.0);
102
103 let mut action_auto_button = TheCheckButton::new(TheId::named("Action Auto"));
104 action_auto_button.set_status_text(&fl!("status_dock_action_auto"));
105 action_auto_button.set_value(TheValue::Bool(true));
106
107 let mut action_apply_button = TheTraybarButton::new(TheId::named("Action Apply"));
108 action_apply_button.set_text(fl!("apply"));
109 action_apply_button.set_status_text(&fl!("status_dock_action_apply"));
110
111 toolbar_hlayout.set_margin(Vec4::new(10, 1, 5, 1));
112 toolbar_hlayout.set_padding(3);
113 toolbar_hlayout.add_widget(Box::new(text));
114 toolbar_hlayout.add_widget(Box::new(action_auto_button));
115 toolbar_hlayout.add_widget(Box::new(action_apply_button));
116 toolbar_hlayout.set_reverse_index(Some(1));
117 toolbar_canvas.set_layout(toolbar_hlayout);
118
119 let action_list_layout = TheListLayout::new(TheId::named("Action List"));
120 action_canvas.set_layout(action_list_layout);
121 action_canvas.set_top(toolbar_canvas);
122
123 shared_layout.add_canvas(action_canvas);
126
127 canvas.set_layout(shared_layout);
128
129 canvas
130 }
131
132 pub fn set_dock(
133 &mut self,
134 dock: String,
135 ui: &mut TheUI,
136 ctx: &mut TheContext,
137 project: &Project,
138 server_ctx: &mut ServerContext,
139 ) {
140 if dock != self.dock {
141 self.minimize(ui, ctx);
142
143 if let Some(index) = self.docks.get_index_of(&dock) {
144 self.index = index;
145 self.dock = dock;
146
147 if let Some(stack) = ui.get_stack_layout("Dock Stack") {
148 stack.set_index(index);
149 }
150
151 self.editor_index = self.editor_canvases.get(&self.dock).copied();
152 } else {
153 eprint!("Dock \"{}\" not found!", self.dock);
154 return;
155 }
156
157 if let Some(layout) = ui.get_sharedhlayout("Dock Shared Layout") {
159 if self.docks[self.index].supports_actions() {
160 layout.set_mode(TheSharedHLayoutMode::Shared);
161 } else {
162 layout.set_mode(TheSharedHLayoutMode::Left);
163 }
164 }
165
166 if let Some(layout) = ui.get_sharedvlayout("Shared VLayout") {
167 let state = self.docks[self.index].default_state();
168 if state == DockDefaultState::Minimized {
169 self.state = DockManagerState::Minimized;
170 layout.set_mode(TheSharedVLayoutMode::Shared);
171 } else {
172 self.state = DockManagerState::Maximized;
173 layout.set_mode(TheSharedVLayoutMode::Bottom);
174 }
175 }
176 }
177 self.docks[self.index].activate(ui, ctx, project, server_ctx);
178 self.set_supports_undo(self.docks[self.index].supports_undo(), ctx);
179 if self.supports_undo {
180 self.docks[self.index].set_undo_state_to_ui(ctx);
181 }
182 }
183
184 pub fn import(
185 &mut self,
186 content: String,
187 ui: &mut TheUI,
188 ctx: &mut TheContext,
189 project: &mut Project,
190 server_ctx: &mut ServerContext,
191 ) {
192 if let Some((_, dock)) = self.docks.get_index_mut(self.index) {
193 dock.import(content.clone(), ui, ctx, project, server_ctx);
194
195 if let Some(editor_dock) = self.editor_docks.get_mut(&self.dock) {
196 editor_dock.import(content, ui, ctx, project, server_ctx);
197 }
198 }
199 }
200
201 pub fn export(&self) -> Option<String> {
202 if let Some((_, dock)) = self.docks.get_index(self.index) {
203 dock.export()
204 } else {
205 None
206 }
207 }
208
209 pub fn handle_event(
210 &mut self,
211 event: &TheEvent,
212 ui: &mut TheUI,
213 ctx: &mut TheContext,
214 project: &mut Project,
215 server_ctx: &mut ServerContext,
216 ) -> bool {
217 let mut redraw = false;
218
219 if let Some((_, dock)) = self.docks.get_index_mut(self.index) {
220 redraw = dock.handle_event(event, ui, ctx, project, server_ctx);
221
222 if let Some(editor_dock) = self.editor_docks.get_mut(&self.dock) {
223 if editor_dock.handle_event(event, ui, ctx, project, server_ctx) {
224 redraw = true;
225 }
226 }
227 }
228 redraw
229 }
230
231 pub fn get_state(&self) -> DockManagerState {
233 self.state
234 }
235
236 pub fn add_editors_to_stack(&mut self, stack: &mut TheStackLayout, ctx: &mut TheContext) {
238 let mut tiles_editor: Box<dyn Dock> =
239 Box::new(crate::docks::tiles_editor::TilesEditorDock::new());
240 let tiles_editor_canvas = tiles_editor.setup(ctx);
241 let index = stack.add_canvas(tiles_editor_canvas);
242 self.editor_canvases.insert("Tiles".to_string(), index);
243 self.editor_docks.insert("Tiles".to_string(), tiles_editor);
244
245 let mut data_editor: Box<dyn Dock> =
246 Box::new(crate::docks::data_editor::DataEditorDock::new());
247 let data_editor_canvas = data_editor.setup(ctx);
248 let index = stack.add_canvas(data_editor_canvas);
249 self.editor_canvases.insert("Data".to_string(), index);
250 self.editor_docks.insert("Data".to_string(), data_editor);
251 }
252
253 pub fn edit_maximize(
255 &mut self,
256 ui: &mut TheUI,
257 ctx: &mut TheContext,
258 project: &mut Project,
259 server_ctx: &mut ServerContext,
260 ) {
261 let use_editor_canvas = if self.dock == "Data" {
262 matches!(server_ctx.pc, ProjectContext::CharacterPreviewRigging(_))
263 } else {
264 self.editor_index.is_some()
265 };
266
267 if use_editor_canvas {
268 let Some(editor_index) = self.editor_index else {
269 return;
270 };
271 if let Some(stack) = ui.get_stack_layout("Editor Stack") {
272 stack.set_index(editor_index);
273 self.state = DockManagerState::Editor;
274
275 let mut supports_undo = None;
276 if let Some(editor_dock) = self.editor_docks.get_mut(&self.dock) {
277 editor_dock.activate(ui, ctx, project, server_ctx);
278 supports_undo = Some(editor_dock.supports_undo());
279 if let Some(supports_undo) = supports_undo
280 && supports_undo
281 {
282 editor_dock.set_undo_state_to_ui(ctx);
283 }
284
285 if let Some(tools) = editor_dock.editor_tools() {
287 TOOLLIST.write().unwrap().set_editor_tools(tools, ui, ctx);
288 }
289 }
290
291 if let Some(supports_undo) = supports_undo {
292 self.set_supports_undo(supports_undo, ctx);
293 }
294 }
295 } else if let Some(layout) = ui.get_sharedvlayout("Shared VLayout") {
296 layout.set_mode(TheSharedVLayoutMode::Bottom);
297 self.state = DockManagerState::Maximized;
298 }
299 }
300
301 pub fn minimize(&mut self, ui: &mut TheUI, ctx: &mut TheContext) {
303 if self.state != DockManagerState::Minimized {
304 if self.state == DockManagerState::Editor {
306 if let Some(editor_dock) = self.editor_docks.get_mut(&self.dock) {
307 editor_dock.minimized(ui, ctx);
308 }
309 TOOLLIST.write().unwrap().set_game_tools(ui, ctx);
310 if let Some(stack) = ui.get_stack_layout("Editor Stack") {
311 stack.set_index(0);
312 }
313 self.state = DockManagerState::Minimized;
314 } else if let Some(layout) = ui.get_sharedvlayout("Shared VLayout") {
315 layout.set_mode(TheSharedVLayoutMode::Shared);
316 self.state = DockManagerState::Minimized;
317 }
318
319 self.set_supports_undo(self.docks[self.index].supports_undo(), ctx);
320 }
321 }
322
323 pub fn current_dock_supports_undo(&self) -> bool {
325 self.supports_undo
326 }
327
328 fn set_supports_undo(&mut self, supports_undo: bool, ctx: &mut TheContext) {
330 if !supports_undo {
331 ctx.ui.send(TheEvent::Custom(
332 TheId::named("Set Project Undo State"),
333 TheValue::Empty,
334 ));
335 }
336 self.supports_undo = supports_undo;
337 }
338
339 pub fn undo(
340 &mut self,
341 ui: &mut TheUI,
342 ctx: &mut TheContext,
343 project: &mut Project,
344 server_ctx: &mut ServerContext,
345 ) {
346 if self.state == DockManagerState::Editor {
347 if let Some(editor_dock) = self.editor_docks.get_mut(&self.dock) {
348 editor_dock.undo(ui, ctx, project, server_ctx);
349 }
350 } else {
351 self.docks[self.index].undo(ui, ctx, project, server_ctx);
352 }
353 }
354
355 pub fn redo(
356 &mut self,
357 ui: &mut TheUI,
358 ctx: &mut TheContext,
359 project: &mut Project,
360 server_ctx: &mut ServerContext,
361 ) {
362 if self.state == DockManagerState::Editor {
363 if let Some(editor_dock) = self.editor_docks.get_mut(&self.dock) {
364 editor_dock.redo(ui, ctx, project, server_ctx);
365 }
366 } else {
367 self.docks[self.index].redo(ui, ctx, project, server_ctx);
368 }
369 }
370
371 pub fn current_dock_supports_minimap_animation(&self) -> bool {
373 match self.state {
374 DockManagerState::Editor => self
375 .editor_docks
376 .get(&self.dock)
377 .map(|d| d.supports_minimap_animation())
378 .unwrap_or(false),
379 _ => self
380 .docks
381 .get_index(self.index)
382 .map(|(_, d)| d.supports_minimap_animation())
383 .unwrap_or(false),
384 }
385 }
386
387 pub fn get_active_dock(&self) -> Option<&dyn Dock> {
389 if self.state == DockManagerState::Editor {
390 self.editor_docks.get(&self.dock).map(|d| d.as_ref())
391 } else {
392 Some(self.docks[self.index].as_ref())
393 }
394 }
395
396 pub fn has_dock_changes(&self) -> bool {
398 for dock in self.docks.values() {
400 if dock.has_changes() {
401 return true;
402 }
403 }
404
405 for dock in self.editor_docks.values() {
407 if dock.has_changes() {
408 return true;
409 }
410 }
411
412 false
413 }
414
415 pub fn mark_saved(&mut self) {
417 for dock in self.docks.values_mut() {
418 dock.mark_saved();
419 }
420 for dock in self.editor_docks.values_mut() {
421 dock.mark_saved();
422 }
423 }
424
425 pub fn apply_debug_data(
426 &mut self,
427 ui: &mut TheUI,
428 ctx: &mut TheContext,
429 project: &Project,
430 server_ctx: &ServerContext,
431 debug: &DebugModule,
432 ) {
433 if self.state == DockManagerState::Editor {
434 if let Some(editor_dock) = self.editor_docks.get_mut(&self.dock) {
435 editor_dock.apply_debug_data(ui, ctx, project, server_ctx, debug);
436 }
437 } else if let Some((_, dock)) = self.docks.get_index_mut(self.index) {
438 dock.apply_debug_data(ui, ctx, project, server_ctx, debug);
439 }
440 }
441}