use crate::docks::code_undo::*;
use crate::prelude::*;
use theframework::prelude::*;
use theframework::theui::thewidget::thetextedit::TheTextEditState;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum EntityKey {
World,
Region(Uuid),
CharacterInstance(Uuid, Uuid),
Character(Uuid),
ItemInstance(Uuid, Uuid),
Item(Uuid),
}
pub struct CodeDock {
entity_undos: FxHashMap<EntityKey, CodeUndo>,
current_entity: Option<EntityKey>,
max_undo: usize,
prev_state: Option<TheTextEditState>,
}
impl Dock for CodeDock {
fn new() -> Self
where
Self: Sized,
{
Self {
entity_undos: FxHashMap::default(),
current_entity: None,
max_undo: 30,
prev_state: None,
}
}
fn setup(&mut self, _ctx: &mut TheContext) -> TheCanvas {
let mut center = TheCanvas::new();
let mut textedit = TheTextAreaEdit::new(TheId::named("DockCodeEditor"));
if let Some(bytes) = crate::Embedded::get("parser/eldrin.sublime-syntax") {
if let Ok(source) = std::str::from_utf8(bytes.data.as_ref()) {
textedit.add_syntax_from_string(source);
textedit.set_code_type("Eldrin Script");
}
}
if let Some(bytes) = crate::Embedded::get("parser/gruvbox-dark.tmTheme") {
if let Ok(source) = std::str::from_utf8(bytes.data.as_ref()) {
textedit.add_theme_from_string(source);
textedit.set_code_theme("Gruvbox Dark");
}
}
textedit.set_continuous(true);
textedit.display_line_number(true);
textedit.use_global_statusbar(true);
textedit.set_font_size(14.0);
textedit.set_supports_undo(false);
center.set_widget(textedit);
center
}
fn activate(
&mut self,
ui: &mut TheUI,
ctx: &mut TheContext,
project: &Project,
server_ctx: &mut ServerContext,
) {
if server_ctx.pc.is_world_code() {
ui.set_widget_value(
"DockCodeEditor",
ctx,
TheValue::Text(project.world_source.clone()),
);
self.switch_to_entity(EntityKey::World, ctx);
} else if let Some(region_id) = server_ctx.pc.id() {
if server_ctx.pc.is_region_code() {
if let Some(region) = project.get_region(®ion_id) {
ui.set_widget_value(
"DockCodeEditor",
ctx,
TheValue::Text(region.source.clone()),
);
self.switch_to_entity(EntityKey::Region(region_id), ctx);
}
} else if let Some(instance_id) = server_ctx.pc.get_region_character_instance_id() {
if let Some(region) = project.get_region(®ion_id)
&& let Some(character_instance) = region.characters.get(&instance_id)
{
ui.set_widget_value(
"DockCodeEditor",
ctx,
TheValue::Text(character_instance.source.clone()),
);
self.switch_to_entity(
EntityKey::CharacterInstance(region_id, instance_id),
ctx,
);
}
} else if let Some(instance_id) = server_ctx.pc.get_region_item_instance_id() {
if let Some(region) = project.get_region(®ion_id)
&& let Some(item_instance) = region.items.get(&instance_id)
{
ui.set_widget_value(
"DockCodeEditor",
ctx,
TheValue::Text(item_instance.source.clone()),
);
self.switch_to_entity(EntityKey::ItemInstance(region_id, instance_id), ctx);
}
} else if server_ctx.pc.is_character() {
let id = region_id;
if let Some(character) = project.characters.get(&id) {
ui.set_widget_value(
"DockCodeEditor",
ctx,
TheValue::Text(character.source.clone()),
);
self.switch_to_entity(EntityKey::Character(id), ctx);
}
} else if server_ctx.pc.is_item() {
let id = region_id;
if let Some(item) = project.items.get(&id) {
ui.set_widget_value("DockCodeEditor", ctx, TheValue::Text(item.source.clone()));
self.switch_to_entity(EntityKey::Item(id), ctx);
}
}
}
if let Some(edit) = ui.get_text_area_edit("DockCodeEditor") {
self.prev_state = Some(edit.get_state());
}
}
fn handle_event(
&mut self,
event: &TheEvent,
ui: &mut TheUI,
ctx: &mut TheContext,
project: &mut Project,
server_ctx: &mut ServerContext,
) -> bool {
let mut redraw = false;
match event {
TheEvent::ValueChanged(id, value) => {
if id.name == "DockCodeEditor" {
if let Some(edit) = ui.get_text_area_edit("DockCodeEditor") {
if let Some(prev) = &self.prev_state {
let current_state = edit.get_state();
let atom = CodeUndoAtom::TextEdit(prev.clone(), current_state.clone());
self.add_undo(atom, ctx);
self.prev_state = Some(current_state);
}
}
if server_ctx.pc.is_world_code() {
if let Some(code) = value.to_string() {
project.world_source = code.clone();
project.world_source_debug = code;
redraw = true;
}
} else if let Some(id) = server_ctx.pc.id() {
if server_ctx.pc.is_region_code() {
if let Some(code) = value.to_string()
&& let Some(region) = project.get_region_mut(&id)
{
region.source = code.clone();
region.source_debug = code;
redraw = true;
}
} else if let Some(instance_id) =
server_ctx.pc.get_region_character_instance_id()
{
if let Some(code) = value.to_string()
&& let Some(region) = project.get_region_mut(&id)
&& let Some(character) = region.characters.get_mut(&instance_id)
{
character.source = code.clone();
character.source_debug = code;
redraw = true;
}
} else if let Some(instance_id) =
server_ctx.pc.get_region_item_instance_id()
{
if let Some(code) = value.to_string()
&& let Some(region) = project.get_region_mut(&id)
&& let Some(item) = region.items.get_mut(&instance_id)
{
item.source = code.clone();
item.source_debug = code;
redraw = true;
}
} else if server_ctx.pc.is_character() {
if let Some(code) = value.to_string() {
if let Some(character) = project.characters.get_mut(&id) {
character.source = code.clone();
character.source_debug = code;
redraw = true;
}
}
} else if server_ctx.pc.is_item() {
if let Some(code) = value.to_string() {
if let Some(item) = project.items.get_mut(&id) {
item.source = code.clone();
item.source_debug = code;
redraw = true;
}
}
}
}
}
}
_ => {}
}
redraw
}
fn supports_undo(&self) -> bool {
true
}
fn has_changes(&self) -> bool {
self.entity_undos.values().any(|undo| undo.has_changes())
}
fn mark_saved(&mut self) {
for undo in self.entity_undos.values_mut() {
undo.index = -1;
}
}
fn undo(
&mut self,
ui: &mut TheUI,
ctx: &mut TheContext,
project: &mut Project,
server_ctx: &mut ServerContext,
) {
if let Some(entity_key) = self.current_entity {
if let Some(undo) = self.entity_undos.get_mut(&entity_key) {
if let Some(edit) = ui.get_text_area_edit("DockCodeEditor") {
undo.undo(edit);
self.prev_state = Some(edit.get_state());
self.set_undo_state_to_ui(ctx);
self.update_project_code(ui, project, server_ctx);
}
}
}
}
fn redo(
&mut self,
ui: &mut TheUI,
ctx: &mut TheContext,
project: &mut Project,
server_ctx: &mut ServerContext,
) {
if let Some(entity_key) = self.current_entity {
if let Some(undo) = self.entity_undos.get_mut(&entity_key) {
if let Some(edit) = ui.get_text_area_edit("DockCodeEditor") {
undo.redo(edit);
self.prev_state = Some(edit.get_state());
self.set_undo_state_to_ui(ctx);
self.update_project_code(ui, project, server_ctx);
}
}
}
}
fn set_undo_state_to_ui(&self, ctx: &mut TheContext) {
if let Some(entity_key) = self.current_entity {
if let Some(undo) = self.entity_undos.get(&entity_key) {
if undo.has_undo() {
ctx.ui.set_enabled("Undo");
} else {
ctx.ui.set_disabled("Undo");
}
if undo.has_redo() {
ctx.ui.set_enabled("Redo");
} else {
ctx.ui.set_disabled("Redo");
}
return;
}
}
ctx.ui.set_disabled("Undo");
ctx.ui.set_disabled("Redo");
}
}
impl CodeDock {
fn switch_to_entity(&mut self, entity_key: EntityKey, ctx: &mut TheContext) {
self.current_entity = Some(entity_key);
self.set_undo_state_to_ui(ctx);
}
fn add_undo(&mut self, atom: CodeUndoAtom, ctx: &mut TheContext) {
if let Some(entity_key) = self.current_entity {
let undo = self
.entity_undos
.entry(entity_key)
.or_insert_with(CodeUndo::new);
undo.add(atom);
undo.truncate_to_limit(self.max_undo);
self.set_undo_state_to_ui(ctx);
}
}
fn update_project_code(
&mut self,
ui: &mut TheUI,
project: &mut Project,
server_ctx: &mut ServerContext,
) {
if let Some(edit) = ui.get_text_area_edit("DockCodeEditor") {
let state = edit.get_state();
let text = state.rows.join("\n");
if server_ctx.pc.is_world_code() {
project.world_source = text.clone();
project.world_source_debug = text;
} else if let Some(id) = server_ctx.pc.id() {
if server_ctx.pc.is_region_code() {
if let Some(region) = project.get_region_mut(&id) {
region.source = text.clone();
region.source_debug = text;
}
} else if let Some(instance_id) = server_ctx.pc.get_region_character_instance_id() {
if let Some(region) = project.get_region_mut(&id)
&& let Some(character) = region.characters.get_mut(&instance_id)
{
character.source = text.clone();
character.source_debug = text;
}
} else if let Some(instance_id) = server_ctx.pc.get_region_item_instance_id() {
if let Some(region) = project.get_region_mut(&id)
&& let Some(item) = region.items.get_mut(&instance_id)
{
item.source = text.clone();
item.source_debug = text;
}
} else if server_ctx.pc.is_character() {
if let Some(character) = project.characters.get_mut(&id) {
character.source = text.clone();
character.source_debug = text;
}
} else if server_ctx.pc.is_item() {
if let Some(item) = project.items.get_mut(&id) {
item.source = text.clone();
item.source_debug = text;
}
}
}
}
}
}