1use crate::docks::code_undo::*;
2use crate::prelude::*;
3use theframework::prelude::*;
4use theframework::theui::thewidget::thetextedit::TheTextEditState;
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
8pub enum EntityKey {
9 Character(Uuid),
10 Item(Uuid),
11}
12
13pub struct CodeDock {
14 entity_undos: FxHashMap<EntityKey, CodeUndo>,
16 current_entity: Option<EntityKey>,
17 max_undo: usize,
18 prev_state: Option<TheTextEditState>,
19}
20
21impl Dock for CodeDock {
22 fn new() -> Self
23 where
24 Self: Sized,
25 {
26 Self {
27 entity_undos: FxHashMap::default(),
28 current_entity: None,
29 max_undo: 30,
30 prev_state: None,
31 }
32 }
33
34 fn setup(&mut self, _ctx: &mut TheContext) -> TheCanvas {
35 let mut center = TheCanvas::new();
36
37 let mut textedit = TheTextAreaEdit::new(TheId::named("DockCodeEditor"));
38
39 if let Some(bytes) = crate::Embedded::get("parser/eldrin.sublime-syntax") {
40 if let Ok(source) = std::str::from_utf8(bytes.data.as_ref()) {
41 textedit.add_syntax_from_string(source);
42 textedit.set_code_type("Eldrin Script");
43 }
44 }
45
46 if let Some(bytes) = crate::Embedded::get("parser/gruvbox-dark.tmTheme") {
47 if let Ok(source) = std::str::from_utf8(bytes.data.as_ref()) {
48 textedit.add_theme_from_string(source);
49 textedit.set_code_theme("Gruvbox Dark");
50 }
51 }
52
53 textedit.set_continuous(true);
54 textedit.display_line_number(true);
55 textedit.use_global_statusbar(true);
57 textedit.set_font_size(14.0);
58 textedit.set_supports_undo(false);
60 center.set_widget(textedit);
61
62 center
63 }
64
65 fn activate(
66 &mut self,
67 ui: &mut TheUI,
68 ctx: &mut TheContext,
69 project: &Project,
70 server_ctx: &mut ServerContext,
71 ) {
72 if let Some(id) = server_ctx.pc.id() {
73 if server_ctx.pc.is_character() {
74 if let Some(character) = project.characters.get(&id) {
75 ui.set_widget_value(
76 "DockCodeEditor",
77 ctx,
78 TheValue::Text(character.source.clone()),
79 );
80 self.switch_to_entity(EntityKey::Character(id), ctx);
82 }
83 } else if server_ctx.pc.is_item() {
84 if let Some(item) = project.items.get(&id) {
85 ui.set_widget_value("DockCodeEditor", ctx, TheValue::Text(item.source.clone()));
86 self.switch_to_entity(EntityKey::Item(id), ctx);
88 }
89 }
90 }
91
92 if let Some(edit) = ui.get_text_area_edit("DockCodeEditor") {
94 self.prev_state = Some(edit.get_state());
95 }
96 }
97
98 fn handle_event(
99 &mut self,
100 event: &TheEvent,
101 ui: &mut TheUI,
102 ctx: &mut TheContext,
103 project: &mut Project,
104 server_ctx: &mut ServerContext,
105 ) -> bool {
106 let mut redraw = false;
107
108 match event {
109 TheEvent::ValueChanged(id, value) => {
110 if id.name == "DockCodeEditor" {
111 if let Some(edit) = ui.get_text_area_edit("DockCodeEditor") {
112 if let Some(prev) = &self.prev_state {
114 let current_state = edit.get_state();
115 let atom = CodeUndoAtom::TextEdit(prev.clone(), current_state.clone());
116 self.add_undo(atom, ctx);
117 self.prev_state = Some(current_state);
118 }
119 }
120
121 if let Some(id) = server_ctx.pc.id() {
122 if server_ctx.pc.is_character() {
123 if let Some(code) = value.to_string() {
124 if let Some(character) = project.characters.get_mut(&id) {
125 character.source = code.clone();
126 character.source_debug = code;
127 redraw = true;
128 }
129 }
130 } else if server_ctx.pc.is_item() {
131 if let Some(code) = value.to_string() {
132 if let Some(item) = project.items.get_mut(&id) {
133 item.source = code.clone();
134 item.source_debug = code;
135 redraw = true;
136 }
137 }
138 }
139 }
140 }
141 }
142 _ => {}
143 }
144 redraw
145 }
146
147 fn supports_undo(&self) -> bool {
148 true
149 }
150
151 fn has_changes(&self) -> bool {
152 self.entity_undos.values().any(|undo| undo.has_changes())
154 }
155
156 fn mark_saved(&mut self) {
157 for undo in self.entity_undos.values_mut() {
158 undo.index = -1;
159 }
160 }
161
162 fn undo(
163 &mut self,
164 ui: &mut TheUI,
165 ctx: &mut TheContext,
166 project: &mut Project,
167 server_ctx: &mut ServerContext,
168 ) {
169 if let Some(entity_key) = self.current_entity {
170 if let Some(undo) = self.entity_undos.get_mut(&entity_key) {
171 if let Some(edit) = ui.get_text_area_edit("DockCodeEditor") {
172 undo.undo(edit);
173 self.prev_state = Some(edit.get_state());
174 self.set_undo_state_to_ui(ctx);
175
176 self.update_project_code(ui, project, server_ctx);
178 }
179 }
180 }
181 }
182
183 fn redo(
184 &mut self,
185 ui: &mut TheUI,
186 ctx: &mut TheContext,
187 project: &mut Project,
188 server_ctx: &mut ServerContext,
189 ) {
190 if let Some(entity_key) = self.current_entity {
191 if let Some(undo) = self.entity_undos.get_mut(&entity_key) {
192 if let Some(edit) = ui.get_text_area_edit("DockCodeEditor") {
193 undo.redo(edit);
194 self.prev_state = Some(edit.get_state());
195 self.set_undo_state_to_ui(ctx);
196
197 self.update_project_code(ui, project, server_ctx);
199 }
200 }
201 }
202 }
203
204 fn set_undo_state_to_ui(&self, ctx: &mut TheContext) {
205 if let Some(entity_key) = self.current_entity {
206 if let Some(undo) = self.entity_undos.get(&entity_key) {
207 if undo.has_undo() {
208 ctx.ui.set_enabled("Undo");
209 } else {
210 ctx.ui.set_disabled("Undo");
211 }
212
213 if undo.has_redo() {
214 ctx.ui.set_enabled("Redo");
215 } else {
216 ctx.ui.set_disabled("Redo");
217 }
218 return;
219 }
220 }
221
222 ctx.ui.set_disabled("Undo");
224 ctx.ui.set_disabled("Redo");
225 }
226}
227
228impl CodeDock {
229 fn switch_to_entity(&mut self, entity_key: EntityKey, ctx: &mut TheContext) {
231 self.current_entity = Some(entity_key);
232 self.set_undo_state_to_ui(ctx);
233 }
234
235 fn add_undo(&mut self, atom: CodeUndoAtom, ctx: &mut TheContext) {
237 if let Some(entity_key) = self.current_entity {
238 let undo = self
239 .entity_undos
240 .entry(entity_key)
241 .or_insert_with(CodeUndo::new);
242 undo.add(atom);
243 undo.truncate_to_limit(self.max_undo);
244 self.set_undo_state_to_ui(ctx);
245 }
246 }
247
248 fn update_project_code(
250 &mut self,
251 ui: &mut TheUI,
252 project: &mut Project,
253 server_ctx: &mut ServerContext,
254 ) {
255 if let Some(id) = server_ctx.pc.id() {
256 if let Some(edit) = ui.get_text_area_edit("DockCodeEditor") {
257 let state = edit.get_state();
258 let text = state.rows.join("\n");
259
260 if server_ctx.pc.is_character() {
261 if let Some(character) = project.characters.get_mut(&id) {
262 character.source = text.clone();
263 character.source_debug = text;
264 }
265 } else if server_ctx.pc.is_item() {
266 if let Some(item) = project.items.get_mut(&id) {
267 item.source = text.clone();
268 item.source_debug = text;
269 }
270 }
271 }
272 }
273 }
274}