diff --git a/src-tauri/src/entity.rs b/src-tauri/src/entity.rs index 19ef225..5b175a3 100644 --- a/src-tauri/src/entity.rs +++ b/src-tauri/src/entity.rs @@ -796,10 +796,8 @@ pub fn get_decorations( .context("No resource property on object ZRuntimeResourceID")? .as_str() .context("Resource was not string")? - } else if let Some(x) = property_data.value.as_str() { - x } else { - "" + property_data.value.as_str().unwrap_or_default() }; if let Some(entry) = hash_list.entries.get(&normalise_to_hash(res.to_owned())) @@ -825,10 +823,8 @@ pub fn get_decorations( .context("No resource property on object ZRuntimeResourceID")? .as_str() .context("Resource was not string")? - } else if let Some(x) = val.as_str() { - x } else { - "" + val.as_str().unwrap_or_default() }; if let Some(entry) = hash_list.entries.get(&normalise_to_hash(res.to_owned())) @@ -907,10 +903,8 @@ pub fn get_decorations( .context("No resource property on object ZRuntimeResourceID")? .as_str() .context("Resource was not string")? - } else if let Some(x) = property_data.value.as_str() { - x } else { - "" + property_data.value.as_str().unwrap_or_default() }; if let Some(entry) = hash_list.entries.get(&normalise_to_hash(res.to_owned())) @@ -936,10 +930,8 @@ pub fn get_decorations( .context("No resource property on object ZRuntimeResourceID")? .as_str() .context("Resource was not string")? - } else if let Some(x) = val.as_str() { - x } else { - "" + val.as_str().unwrap_or_default() }; if let Some(entry) = hash_list.entries.get(&normalise_to_hash(res.to_owned())) diff --git a/src-tauri/src/event_handling/entity/general.rs b/src-tauri/src/event_handling/entity/general.rs new file mode 100644 index 0000000..0444b9c --- /dev/null +++ b/src-tauri/src/event_handling/entity/general.rs @@ -0,0 +1,65 @@ +use anyhow::{anyhow, Context, Result}; +use fn_error_context::context; +use tauri::{AppHandle, Manager}; +use tryvial::try_fn; + +use crate::{ + model::{AppState, EditorData, EditorRequest, EntityEditorRequest, EntityGeneralEvent, EntityTreeRequest, Request}, + send_request +}; + +#[try_fn] +#[context("Couldn't handle update content event")] +pub async fn handle(app: &AppHandle, event: EntityGeneralEvent) -> Result<()> { + let app_state = app.state::(); + + match event { + EntityGeneralEvent::SetShowReverseParentRefs { + editor_id, + show_reverse_parent_refs + } => { + let mut editor_state = app_state.editor_states.get_mut(&editor_id).context("No such editor")?; + + let settings = match editor_state.data { + EditorData::QNEntity { ref mut settings, .. } => settings, + EditorData::QNPatch { ref mut settings, .. } => settings, + + _ => { + Err(anyhow!("Editor {} is not a QN editor", editor_id))?; + panic!(); + } + }; + + settings.show_reverse_parent_refs = show_reverse_parent_refs; + } + + EntityGeneralEvent::SetShowChangesFromOriginal { + editor_id, + show_changes_from_original + } => { + let mut editor_state = app_state.editor_states.get_mut(&editor_id).context("No such editor")?; + + let settings = match editor_state.data { + EditorData::QNEntity { ref mut settings, .. } => settings, + EditorData::QNPatch { ref mut settings, .. } => settings, + + _ => { + Err(anyhow!("Editor {} is not a QN editor", editor_id))?; + panic!(); + } + }; + + settings.show_changes_from_original = show_changes_from_original; + + send_request( + app, + Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( + EntityTreeRequest::SetShowDiff { + editor_id, + show_diff: show_changes_from_original + } + ))) + )?; + } + } +} diff --git a/src-tauri/src/event_handling/entity/meta_pane.rs b/src-tauri/src/event_handling/entity/meta_pane.rs new file mode 100644 index 0000000..4a575de --- /dev/null +++ b/src-tauri/src/event_handling/entity/meta_pane.rs @@ -0,0 +1,72 @@ +use anyhow::{anyhow, Context, Result}; +use fn_error_context::context; +use quickentity_rs::qn_structs::{CommentEntity, Ref}; +use tauri::{AppHandle, Manager}; +use tryvial::try_fn; + +use crate::{ + entity::get_local_reference, + model::{ + AppState, EditorData, EditorRequest, EntityEditorRequest, EntityMetaPaneEvent, EntityTreeRequest, + GlobalRequest, Request + }, + send_request +}; + +#[try_fn] +#[context("Couldn't handle entity meta pane event")] +pub async fn handle(app: &AppHandle, event: EntityMetaPaneEvent) -> Result<()> { + let app_state = app.state::(); + + match event { + EntityMetaPaneEvent::JumpToReference { editor_id, reference } => { + send_request( + app, + Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( + EntityTreeRequest::Select { + editor_id, + id: Some(reference) + } + ))) + )?; + } + + EntityMetaPaneEvent::SetNotes { + editor_id, + entity_id, + notes + } => { + let mut editor_state = app_state.editor_states.get_mut(&editor_id).context("No such editor")?; + + let entity = match editor_state.data { + EditorData::QNEntity { ref mut entity, .. } => entity, + EditorData::QNPatch { ref mut current, .. } => current, + + _ => { + Err(anyhow!("Editor {} is not a QN editor", editor_id))?; + panic!(); + } + }; + + // Remove comment referring to given entity + entity + .comments + .retain(|x| get_local_reference(&x.parent).map(|x| x != entity_id).unwrap_or(true)); + + // Add new comment + entity.comments.push(CommentEntity { + parent: Ref::Short(Some(entity_id)), + name: "Notes".into(), + text: notes + }); + + send_request( + app, + Request::Global(GlobalRequest::SetTabUnsaved { + id: editor_id.to_owned(), + unsaved: true + }) + )?; + } + } +} diff --git a/src-tauri/src/event_handling/entity_metadata.rs b/src-tauri/src/event_handling/entity/metadata.rs similarity index 98% rename from src-tauri/src/event_handling/entity_metadata.rs rename to src-tauri/src/event_handling/entity/metadata.rs index 674e1b4..4e78133 100644 --- a/src-tauri/src/event_handling/entity_metadata.rs +++ b/src-tauri/src/event_handling/entity/metadata.rs @@ -18,7 +18,7 @@ use crate::{ #[try_fn] #[context("Couldn't handle entity metadata event")] -pub async fn handle_entity_metadata_event(app: &AppHandle, event: EntityMetadataEvent) -> Result<()> { +pub async fn handle(app: &AppHandle, event: EntityMetadataEvent) -> Result<()> { let app_state = app.state::(); match event { diff --git a/src-tauri/src/event_handling/entity/mod.rs b/src-tauri/src/event_handling/entity/mod.rs new file mode 100644 index 0000000..4b39547 --- /dev/null +++ b/src-tauri/src/event_handling/entity/mod.rs @@ -0,0 +1,43 @@ +use anyhow::Result; +use fn_error_context::context; +use tauri::AppHandle; +use tryvial::try_fn; + +use crate::model::EntityEditorEvent; + +pub mod general; +pub mod meta_pane; +pub mod metadata; +pub mod monaco; +pub mod overrides; +pub mod tree; + +#[try_fn] +#[context("Couldn't handle entity editor event")] +pub async fn handle(app: &AppHandle, event: EntityEditorEvent) -> Result<()> { + match event { + EntityEditorEvent::General(event) => { + general::handle(app, event).await?; + } + + EntityEditorEvent::Tree(event) => { + tree::handle(app, event).await?; + } + + EntityEditorEvent::Monaco(event) => { + monaco::handle(app, event).await?; + } + + EntityEditorEvent::MetaPane(event) => { + meta_pane::handle(app, event).await?; + } + + EntityEditorEvent::Metadata(event) => { + metadata::handle(app, event).await?; + } + + EntityEditorEvent::Overrides(event) => { + overrides::handle(app, event).await?; + } + } +} diff --git a/src-tauri/src/event_handling/entity_monaco.rs b/src-tauri/src/event_handling/entity/monaco.rs similarity index 80% rename from src-tauri/src/event_handling/entity_monaco.rs rename to src-tauri/src/event_handling/entity/monaco.rs index 2f9722b..6fc0b87 100644 --- a/src-tauri/src/event_handling/entity_monaco.rs +++ b/src-tauri/src/event_handling/entity/monaco.rs @@ -22,9 +22,9 @@ use crate::{ get_loaded_game_version, model::{ AppSettings, AppState, EditorData, EditorRequest, EditorState, EditorType, EditorValidity, EntityEditorRequest, - EntityMonacoRequest, EntityTreeRequest, GlobalRequest, Request + EntityMonacoEvent, EntityMonacoRequest, EntityTreeRequest, GlobalRequest, Request }, - rpkg::{ensure_entity_in_cache, extract_latest_overview_info, normalise_to_hash}, + rpkg::{extract_latest_overview_info, normalise_to_hash}, send_notification, send_request, start_task, Notification, NotificationKind }; @@ -74,9 +74,112 @@ pub const SAFE_TO_SYNC: [&str; 43] = [ "SEntityTemplateReference" ]; +#[try_fn] +#[context("Couldn't handle monaco event")] +pub async fn handle(app: &AppHandle, event: EntityMonacoEvent) -> Result<()> { + let app_state = app.state::(); + + match event { + EntityMonacoEvent::UpdateContent { + editor_id, + entity_id, + content + } => { + update_content(app, editor_id, entity_id, content).await?; + } + + EntityMonacoEvent::FollowReference { editor_id, reference } => { + send_request( + app, + Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( + EntityTreeRequest::Select { + editor_id, + id: Some(reference) + } + ))) + )?; + } + + EntityMonacoEvent::OpenFactory { factory, .. } => { + open_factory(app, factory).await?; + } + + EntityMonacoEvent::SignalPin { + editor_id, + entity_id, + pin, + output + } => { + let editor_state = app_state.editor_states.get(&editor_id).context("No such editor")?; + + let entity = match editor_state.data { + EditorData::QNEntity { ref entity, .. } => entity, + EditorData::QNPatch { ref current, .. } => current, + + _ => { + Err(anyhow!("Editor {} is not a QN editor", editor_id))?; + panic!(); + } + }; + + app_state + .editor_connection + .signal_pin(&entity_id, &entity.blueprint_hash, &pin, output) + .await?; + } + + EntityMonacoEvent::OpenResourceOverview { resource, .. } => { + if let Some(resource_reverse_dependencies) = app_state.resource_reverse_dependencies.load().as_ref() { + let resource = normalise_to_hash(resource); + + if resource_reverse_dependencies.contains_key(&resource) { + let id = Uuid::new_v4(); + + app_state.editor_states.insert( + id.to_owned(), + EditorState { + file: None, + data: EditorData::ResourceOverview { + hash: resource.to_owned() + } + } + ); + + send_request( + app, + Request::Global(GlobalRequest::CreateTab { + id, + name: format!("Resource overview ({resource})"), + editor_type: EditorType::ResourceOverview + }) + )?; + } else { + send_notification( + app, + Notification { + kind: NotificationKind::Error, + title: "Not a vanilla resource".into(), + subtitle: "This factory doesn't exist in the base game files.".into() + } + )?; + } + } else { + send_notification( + app, + Notification { + kind: NotificationKind::Error, + title: "No game selected".into(), + subtitle: "You can't open game files without a copy of the game selected.".into() + } + )?; + } + } + } +} + #[try_fn] #[context("Couldn't handle update content event")] -pub async fn handle_updatecontent(app: &AppHandle, editor_id: Uuid, entity_id: String, content: String) -> Result<()> { +pub async fn update_content(app: &AppHandle, editor_id: Uuid, entity_id: String, content: String) -> Result<()> { let app_settings = app.state::>(); let app_state = app.state::(); @@ -391,7 +494,7 @@ pub async fn handle_updatecontent(app: &AppHandle, editor_id: Uuid, entity_id: S #[try_fn] #[context("Couldn't handle open factory event")] -pub async fn handle_openfactory(app: &AppHandle, factory: String) -> Result<()> { +pub async fn open_factory(app: &AppHandle, factory: String) -> Result<()> { let app_settings = app.state::>(); let app_state = app.state::(); diff --git a/src-tauri/src/event_handling/entity_overrides.rs b/src-tauri/src/event_handling/entity/overrides.rs similarity index 99% rename from src-tauri/src/event_handling/entity_overrides.rs rename to src-tauri/src/event_handling/entity/overrides.rs index 970bf27..4ef71c1 100644 --- a/src-tauri/src/event_handling/entity_overrides.rs +++ b/src-tauri/src/event_handling/entity/overrides.rs @@ -183,7 +183,7 @@ pub fn send_overrides_decorations(app: &AppHandle, editor_id: Uuid, entity: &Ent #[try_fn] #[context("Couldn't handle entity overrides event")] -pub async fn handle_entity_overrides_event(app: &AppHandle, event: EntityOverridesEvent) -> Result<()> { +pub async fn handle(app: &AppHandle, event: EntityOverridesEvent) -> Result<()> { let app_state = app.state::(); match event { diff --git a/src-tauri/src/event_handling/entity_tree.rs b/src-tauri/src/event_handling/entity/tree.rs similarity index 84% rename from src-tauri/src/event_handling/entity_tree.rs rename to src-tauri/src/event_handling/entity/tree.rs index edbeb17..d593c40 100644 --- a/src-tauri/src/event_handling/entity_tree.rs +++ b/src-tauri/src/event_handling/entity/tree.rs @@ -1,6 +1,7 @@ use std::ops::Deref; use anyhow::{anyhow, Context, Result}; +use arboard::Clipboard; use arc_swap::ArcSwap; use fn_error_context::context; use hashbrown::{HashMap, HashSet}; @@ -12,8 +13,9 @@ use quickentity_rs::{ patch_structs::{Patch, PatchOperation, SubEntityOperation}, qn_structs::{FullRef, Property, Ref, RefMaybeConstantValue, RefWithConstantValue, SubEntity} }; +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use serde::Serialize; -use serde_json::{from_value, json, to_value, Value}; +use serde_json::{from_slice, from_str, from_value, json, to_string, to_value, Value}; use tauri::{AppHandle, Manager}; use tryvial::try_fn; use uuid::Uuid; @@ -29,8 +31,8 @@ use crate::{ game_detection::GameVersion, get_loaded_game_version, model::{ - AppSettings, AppState, EditorData, EditorRequest, EditorValidity, EntityEditorRequest, EntityMetaPaneRequest, - EntityMonacoRequest, EntityTreeRequest, GlobalRequest, Request + AppSettings, AppState, EditorData, EditorRequest, EditorValidity, EntityEditorRequest, EntityGeneralRequest, + EntityMetaPaneRequest, EntityMonacoRequest, EntityTreeEvent, EntityTreeRequest, GlobalRequest, Request }, resourcelib::{ h2016_convert_binary_to_factory, h2016_convert_cppt, h2_convert_binary_to_factory, h2_convert_cppt, @@ -42,11 +44,336 @@ use crate::{ Notification, NotificationKind }; -use super::entity_monaco::SAFE_TO_SYNC; +use super::monaco::SAFE_TO_SYNC; + +#[try_fn] +#[context("Couldn't handle tree event")] +pub async fn handle(app: &AppHandle, event: EntityTreeEvent) -> Result<()> { + match event { + EntityTreeEvent::Initialise { editor_id } => { + initialise(app, editor_id).await?; + } + + EntityTreeEvent::Select { editor_id, id } => { + select(app, editor_id, id).await?; + } + + EntityTreeEvent::Create { editor_id, id, content } => { + create(app, editor_id, id, content).await?; + } + + EntityTreeEvent::Delete { editor_id, id } => { + delete(app, editor_id, id).await?; + } + + EntityTreeEvent::Rename { + editor_id, + id, + new_name + } => { + rename(app, editor_id, id, new_name).await?; + } + + EntityTreeEvent::Reparent { + editor_id, + id, + new_parent + } => { + reparent(app, editor_id, id, new_parent).await?; + } + + EntityTreeEvent::Copy { editor_id, id } => { + copy(app, editor_id, id).await?; + } + + EntityTreeEvent::Paste { editor_id, parent_id } => { + paste( + app, + editor_id, + parent_id, + from_str::(&Clipboard::new()?.get_text()?)? + ) + .await?; + } + + EntityTreeEvent::Search { editor_id, query } => { + search(app, editor_id, query).await?; + } + + EntityTreeEvent::ShowHelpMenu { editor_id, entity_id } => { + help_menu(app, editor_id, entity_id).await?; + } + + EntityTreeEvent::UseTemplate { + editor_id, + parent_id, + template + } => { + paste(app, editor_id, parent_id, template).await?; + } + + EntityTreeEvent::AddGameBrowserItem { + editor_id, + parent_id, + file + } => { + add_game_browser_item(app, editor_id, parent_id, file).await?; + } + + EntityTreeEvent::SelectEntityInEditor { editor_id, entity_id } => { + select_entity_in_editor(app, editor_id, entity_id).await?; + } + + EntityTreeEvent::MoveEntityToPlayer { editor_id, entity_id } => { + move_entity_to_player(app, editor_id, entity_id).await?; + } + + EntityTreeEvent::RotateEntityAsPlayer { editor_id, entity_id } => { + rotate_entity_as_player(app, editor_id, entity_id).await?; + } + + EntityTreeEvent::MoveEntityToCamera { editor_id, entity_id } => { + move_entity_to_camera(app, editor_id, entity_id).await?; + } + + EntityTreeEvent::RotateEntityAsCamera { editor_id, entity_id } => { + rotate_entity_as_camera(app, editor_id, entity_id).await?; + } + + EntityTreeEvent::RestoreToOriginal { editor_id, entity_id } => { + restore_to_original(app, editor_id, entity_id).await?; + } + } +} + +#[try_fn] +#[context("Couldn't handle initialise event")] +pub async fn initialise(app: &AppHandle, editor_id: Uuid) -> Result<()> { + let app_state = app.state::(); + + let editor_state = app_state.editor_states.get(&editor_id).context("No such editor")?; + + let entity = match editor_state.data { + EditorData::QNEntity { ref entity, .. } => entity, + EditorData::QNPatch { ref current, .. } => current, + + _ => { + Err(anyhow!("Editor {} is not a QN editor", editor_id))?; + panic!(); + } + }; + + let mut entities = vec![]; + let mut reverse_parent_refs: HashMap> = HashMap::new(); + + for (entity_id, entity_data) in entity.entities.iter() { + match entity_data.parent { + Ref::Full(ref reference) if reference.external_scene.is_none() => { + reverse_parent_refs + .entry(reference.entity_ref.to_owned()) + .and_modify(|x| x.push(entity_id.to_owned())) + .or_insert(vec![entity_id.to_owned()]); + } + + Ref::Short(Some(ref reference)) => { + reverse_parent_refs + .entry(reference.to_owned()) + .and_modify(|x| x.push(entity_id.to_owned())) + .or_insert(vec![entity_id.to_owned()]); + } + + _ => {} + } + } + + for (entity_id, entity_data) in entity.entities.iter() { + entities.push(( + entity_id.to_owned(), + entity_data.parent.to_owned(), + entity_data.name.to_owned(), + entity_data.factory.to_owned(), + reverse_parent_refs.contains_key(entity_id) + )); + } + + send_request( + app, + Request::Editor(EditorRequest::Entity(EntityEditorRequest::General( + EntityGeneralRequest::SetIsPatchEditor { + editor_id: editor_id.to_owned(), + is_patch_editor: matches!(editor_state.data, EditorData::QNPatch { .. }) + } + ))) + )?; + + send_request( + app, + Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( + EntityTreeRequest::NewTree { + editor_id: editor_id.to_owned(), + entities + } + ))) + )?; + + send_request( + app, + Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( + EntityTreeRequest::SetTemplates { + editor_id: editor_id.to_owned(), + templates: from_slice(include_bytes!("../../../assets/templates.json")).unwrap() + } + ))) + )?; + + let editor_connected = app_state.editor_connection.is_connected().await; + + send_request( + app, + Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( + EntityTreeRequest::SetEditorConnectionAvailable { + editor_id: editor_id.to_owned(), + editor_connection_available: editor_connected + } + ))) + )?; + + send_request( + app, + Request::Editor(EditorRequest::Entity(EntityEditorRequest::Monaco( + EntityMonacoRequest::SetEditorConnected { + editor_id: editor_id.to_owned(), + connected: editor_connected + } + ))) + )?; + + if let EditorData::QNPatch { + ref base, ref current, .. + } = editor_state.data + { + send_request( + app, + Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( + EntityTreeRequest::SetDiffInfo { + editor_id, + diff_info: get_diff_info(base, current) + } + ))) + )?; + } +} + +#[try_fn] +#[context("Couldn't handle create event")] +pub async fn create(app: &AppHandle, editor_id: Uuid, id: String, content: SubEntity) -> Result<()> { + let app_state = app.state::(); + + let mut editor_state = app_state.editor_states.get_mut(&editor_id).context("No such editor")?; + + let entity = match editor_state.data { + EditorData::QNEntity { ref mut entity, .. } => entity, + EditorData::QNPatch { ref mut current, .. } => current, + + _ => { + Err(anyhow!("Editor {} is not a QN editor", editor_id))?; + panic!(); + } + }; + + entity.entities.insert(id, content); + + send_request( + app, + Request::Global(GlobalRequest::SetTabUnsaved { + id: editor_id.to_owned(), + unsaved: true + }) + )?; + + if let EditorData::QNPatch { + ref base, ref current, .. + } = editor_state.data + { + send_request( + app, + Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( + EntityTreeRequest::SetDiffInfo { + editor_id, + diff_info: get_diff_info(base, current) + } + ))) + )?; + } +} + +#[try_fn] +#[context("Couldn't handle rename event")] +pub async fn rename(app: &AppHandle, editor_id: Uuid, id: String, new_name: String) -> Result<()> { + let app_state = app.state::(); + + let mut editor_state = app_state.editor_states.get_mut(&editor_id).context("No such editor")?; + + let entity = match editor_state.data { + EditorData::QNEntity { ref mut entity, .. } => entity, + EditorData::QNPatch { ref mut current, .. } => current, + + _ => { + Err(anyhow!("Editor {} is not a QN editor", editor_id))?; + panic!(); + } + }; + + entity.entities.get_mut(&id).context("No such entity")?.name = new_name; + + send_request( + app, + Request::Global(GlobalRequest::SetTabUnsaved { + id: editor_id, + unsaved: true + }) + )?; + + let mut buf = Vec::new(); + let formatter = serde_json::ser::PrettyFormatter::with_indent(b"\t"); + let mut ser = serde_json::Serializer::with_formatter(&mut buf, formatter); + + entity + .entities + .get(&id) + .context("No such entity")? + .serialize(&mut ser)?; + + send_request( + app, + Request::Editor(EditorRequest::Entity(EntityEditorRequest::Monaco( + EntityMonacoRequest::ReplaceContentIfSameEntityID { + editor_id: editor_id.to_owned(), + entity_id: id.to_owned(), + content: String::from_utf8(buf)? + } + ))) + )?; + + if let EditorData::QNPatch { + ref base, ref current, .. + } = editor_state.data + { + send_request( + app, + Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( + EntityTreeRequest::SetDiffInfo { + editor_id, + diff_info: get_diff_info(base, current) + } + ))) + )?; + } +} #[try_fn] #[context("Couldn't handle select event")] -pub async fn handle_select(app: &AppHandle, editor_id: Uuid, id: String) -> Result<()> { +pub async fn select(app: &AppHandle, editor_id: Uuid, id: String) -> Result<()> { let app_settings = app.state::>(); let app_state = app.state::(); @@ -240,9 +567,73 @@ pub async fn handle_select(app: &AppHandle, editor_id: Uuid, id: String) -> Resu finish_task(app, task)?; } +#[try_fn] +#[context("Couldn't handle reparent event")] +pub async fn reparent(app: &AppHandle, editor_id: Uuid, id: String, new_parent: Ref) -> Result<()> { + let app_state = app.state::(); + + let mut editor_state = app_state.editor_states.get_mut(&editor_id).context("No such editor")?; + + let entity = match editor_state.data { + EditorData::QNEntity { ref mut entity, .. } => entity, + EditorData::QNPatch { ref mut current, .. } => current, + + _ => { + Err(anyhow!("Editor {} is not a QN editor", editor_id))?; + panic!(); + } + }; + + entity.entities.get_mut(&id).context("No such entity")?.parent = new_parent; + + send_request( + app, + Request::Global(GlobalRequest::SetTabUnsaved { + id: editor_id, + unsaved: true + }) + )?; + + let mut buf = Vec::new(); + let formatter = serde_json::ser::PrettyFormatter::with_indent(b"\t"); + let mut ser = serde_json::Serializer::with_formatter(&mut buf, formatter); + + entity + .entities + .get(&id) + .context("No such entity")? + .serialize(&mut ser)?; + + send_request( + app, + Request::Editor(EditorRequest::Entity(EntityEditorRequest::Monaco( + EntityMonacoRequest::ReplaceContentIfSameEntityID { + editor_id: editor_id.to_owned(), + entity_id: id.to_owned(), + content: String::from_utf8(buf)? + } + ))) + )?; + + if let EditorData::QNPatch { + ref base, ref current, .. + } = editor_state.data + { + send_request( + app, + Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( + EntityTreeRequest::SetDiffInfo { + editor_id, + diff_info: get_diff_info(base, current) + } + ))) + )?; + } +} + #[try_fn] #[context("Couldn't handle delete event")] -pub async fn handle_delete(app: &AppHandle, editor_id: Uuid, id: String) -> Result<()> { +pub async fn delete(app: &AppHandle, editor_id: Uuid, id: String) -> Result<()> { let app_state = app.state::(); let task = start_task(app, format!("Deleting entity {}", id))?; @@ -594,9 +985,49 @@ pub async fn handle_delete(app: &AppHandle, editor_id: Uuid, id: String) -> Resu } } +#[try_fn] +#[context("Couldn't handle copy event")] +pub async fn copy(app: &AppHandle, editor_id: Uuid, id: String) -> Result<()> { + let app_state = app.state::(); + + let task = start_task(app, format!("Copying entity {} and its children", id))?; + + let editor_state = app_state.editor_states.get(&editor_id).context("No such editor")?; + + let entity = match editor_state.data { + EditorData::QNEntity { ref entity, .. } => entity, + EditorData::QNPatch { ref current, .. } => current, + + _ => { + Err(anyhow!("Editor {} is not a QN editor", editor_id))?; + panic!(); + } + }; + + let reverse_refs = calculate_reverse_references(entity)?; + + let entities_to_copy = get_recursive_children(entity, &id, &reverse_refs)? + .into_iter() + .collect::>(); + + let data_to_copy = CopiedEntityData { + root_entity: id.to_owned(), + data: entity + .entities + .iter() + .filter(|(x, _)| entities_to_copy.contains(*x)) + .map(|(x, y)| (x.to_owned(), y.to_owned())) + .collect() + }; + + Clipboard::new()?.set_text(to_string(&data_to_copy)?)?; + + finish_task(app, task)?; +} + #[try_fn] #[context("Couldn't handle paste event")] -pub async fn handle_paste( +pub async fn paste( app: &AppHandle, editor_id: Uuid, parent_id: String, @@ -1157,9 +1588,50 @@ pub async fn handle_paste( } } +#[try_fn] +#[context("Couldn't handle search event")] +pub async fn search(app: &AppHandle, editor_id: Uuid, query: String) -> Result<()> { + let app_state = app.state::(); + + let task = start_task(app, format!("Searching for {}", query))?; + + let editor_state = app_state.editor_states.get(&editor_id).context("No such editor")?; + + let entity = match editor_state.data { + EditorData::QNEntity { ref entity, .. } => entity, + EditorData::QNPatch { ref current, .. } => current, + + _ => { + Err(anyhow!("Editor {} is not a QN editor", editor_id))?; + panic!(); + } + }; + + send_request( + app, + Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( + EntityTreeRequest::SearchResults { + editor_id, + results: entity + .entities + .par_iter() + .filter(|(id, ent)| { + let mut s = format!("{}{}", id, to_string(ent).unwrap()); + s.make_ascii_lowercase(); + query.split(' ').all(|q| s.contains(q)) + }) + .map(|(id, _)| id.to_owned()) + .collect() + } + ))) + )?; + + finish_task(app, task)?; +} + #[try_fn] #[context("Couldn't handle help menu event")] -pub async fn handle_helpmenu(app: &AppHandle, editor_id: Uuid, entity_id: String) -> Result<()> { +pub async fn help_menu(app: &AppHandle, editor_id: Uuid, entity_id: String) -> Result<()> { let app_settings = app.state::>(); let app_state = app.state::(); @@ -1306,7 +1778,7 @@ pub async fn handle_helpmenu(app: &AppHandle, editor_id: Uuid, entity_id: String #[try_fn] #[context("Couldn't handle game browser add event")] -pub async fn handle_gamebrowseradd(app: &AppHandle, editor_id: Uuid, parent_id: String, file: String) -> Result<()> { +pub async fn add_game_browser_item(app: &AppHandle, editor_id: Uuid, parent_id: String, file: String) -> Result<()> { let app_settings = app.state::>(); let app_state = app.state::(); @@ -1991,7 +2463,7 @@ pub async fn handle_gamebrowseradd(app: &AppHandle, editor_id: Uuid, parent_id: #[try_fn] #[context("Couldn't handle select entity in editor event")] -pub async fn handle_selectentityineditor(app: &AppHandle, editor_id: Uuid, entity_id: String) -> Result<()> { +pub async fn select_entity_in_editor(app: &AppHandle, editor_id: Uuid, entity_id: String) -> Result<()> { let app_state = app.state::(); let task = start_task(app, format!("Selecting {} in editor", entity_id))?; @@ -2018,7 +2490,7 @@ pub async fn handle_selectentityineditor(app: &AppHandle, editor_id: Uuid, entit #[try_fn] #[context("Couldn't handle move entity to player event")] -pub async fn handle_moveentitytoplayer(app: &AppHandle, editor_id: Uuid, entity_id: String) -> Result<()> { +pub async fn move_entity_to_player(app: &AppHandle, editor_id: Uuid, entity_id: String) -> Result<()> { let app_settings = app.state::>(); let app_state = app.state::(); @@ -2206,7 +2678,7 @@ pub async fn handle_moveentitytoplayer(app: &AppHandle, editor_id: Uuid, entity_ #[try_fn] #[context("Couldn't handle rotate entity as player event")] -pub async fn handle_rotateentityasplayer(app: &AppHandle, editor_id: Uuid, entity_id: String) -> Result<()> { +pub async fn rotate_entity_as_player(app: &AppHandle, editor_id: Uuid, entity_id: String) -> Result<()> { let app_settings = app.state::>(); let app_state = app.state::(); @@ -2394,7 +2866,7 @@ pub async fn handle_rotateentityasplayer(app: &AppHandle, editor_id: Uuid, entit #[try_fn] #[context("Couldn't handle move entity to camera event")] -pub async fn handle_moveentitytocamera(app: &AppHandle, editor_id: Uuid, entity_id: String) -> Result<()> { +pub async fn move_entity_to_camera(app: &AppHandle, editor_id: Uuid, entity_id: String) -> Result<()> { let app_settings = app.state::>(); let app_state = app.state::(); @@ -2582,7 +3054,7 @@ pub async fn handle_moveentitytocamera(app: &AppHandle, editor_id: Uuid, entity_ #[try_fn] #[context("Couldn't handle rotate entity as camera event")] -pub async fn handle_rotateentityascamera(app: &AppHandle, editor_id: Uuid, entity_id: String) -> Result<()> { +pub async fn rotate_entity_as_camera(app: &AppHandle, editor_id: Uuid, entity_id: String) -> Result<()> { let app_settings = app.state::>(); let app_state = app.state::(); @@ -2770,7 +3242,7 @@ pub async fn handle_rotateentityascamera(app: &AppHandle, editor_id: Uuid, entit #[try_fn] #[context("Couldn't handle restore to original event")] -pub async fn handle_restoretooriginal(app: &AppHandle, editor_id: Uuid, entity_id: String) -> Result<()> { +pub async fn restore_to_original(app: &AppHandle, editor_id: Uuid, entity_id: String) -> Result<()> { let app_settings = app.state::>(); let app_state = app.state::(); @@ -2788,25 +3260,21 @@ pub async fn handle_restoretooriginal(app: &AppHandle, editor_id: Uuid, entity_i panic!(); }; - match check_local_references_exist( + if let EditorValidity::Invalid(err) = check_local_references_exist( base.entities.get(&entity_id).context("Entity didn't exist in base")?, current )? { - EditorValidity::Invalid(err) => { - send_notification( - app, - Notification { - kind: NotificationKind::Error, - title: "Entity would be invalid".into(), - subtitle: err - } - )?; - - finish_task(app, task)?; - return Ok(()); - } + send_notification( + app, + Notification { + kind: NotificationKind::Error, + title: "Entity would be invalid".into(), + subtitle: err + } + )?; - _ => {} + finish_task(app, task)?; + return Ok(()); } if let Some(previous) = current.entities.get(&entity_id).cloned() { diff --git a/src-tauri/src/event_handling/mod.rs b/src-tauri/src/event_handling/mod.rs index dac3e0d..b1229a3 100644 --- a/src-tauri/src/event_handling/mod.rs +++ b/src-tauri/src/event_handling/mod.rs @@ -1,8 +1,5 @@ pub mod content_search; -pub mod entity_metadata; -pub mod entity_monaco; -pub mod entity_overrides; -pub mod entity_tree; +pub mod entity; pub mod repository_patch; pub mod resource_overview; pub mod tools; diff --git a/src-tauri/src/event_handling/resource_overview.rs b/src-tauri/src/event_handling/resource_overview.rs index 6838611..e32d150 100644 --- a/src-tauri/src/event_handling/resource_overview.rs +++ b/src-tauri/src/event_handling/resource_overview.rs @@ -1,4 +1,4 @@ -use std::{fs, io::Cursor, ops::Deref, path::PathBuf, sync::Arc}; +use std::{fs, io::Cursor, ops::Deref, sync::Arc}; use anyhow::{anyhow, bail, Context, Result}; use arc_swap::ArcSwap; @@ -9,7 +9,7 @@ use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use rfd::AsyncFileDialog; use rpkg_rs::{resource::partition_manager::PartitionManager, GlacierResource}; use serde::Serialize; -use serde_json::{from_slice, json, to_string, to_vec, Value}; +use serde_json::{json, to_string, to_vec, Value}; use tauri::{api::process::Command, AppHandle, Manager, State}; use tauri_plugin_aptabase::EventTracker; use tex_rs::texture_map::TextureMap; diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index d4c53c0..4ea45a4 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -37,52 +37,34 @@ use std::{ }; use anyhow::{anyhow, bail, Context, Error, Result}; -use arboard::Clipboard; use arc_swap::ArcSwap; use biome::format_json; use dashmap::DashMap; use editor_connection::EditorConnection; -use entity::{ - calculate_reverse_references, get_diff_info, get_local_reference, get_recursive_children, CopiedEntityData -}; +use entity::get_diff_info; use event_handling::{ - entity_metadata::handle_entity_metadata_event, - entity_monaco::{handle_openfactory, handle_updatecontent}, - entity_overrides::handle_entity_overrides_event, - entity_tree::{ - handle_delete, handle_gamebrowseradd, handle_helpmenu, handle_moveentitytocamera, handle_moveentitytoplayer, - handle_paste, handle_restoretooriginal, handle_rotateentityascamera, handle_rotateentityasplayer, - handle_select, handle_selectentityineditor - }, - repository_patch::handle_repository_patch_event, - resource_overview::handle_resource_overview_event, - tools::handle_tool_event, - unlockables_patch::handle_unlockables_patch_event + repository_patch::handle_repository_patch_event, resource_overview::handle_resource_overview_event, + tools::handle_tool_event, unlockables_patch::handle_unlockables_patch_event }; use fn_error_context::context; use game_detection::{detect_installs, GameVersion}; use general::open_file; -use hashbrown::{HashMap, HashSet}; +use hashbrown::HashMap; use indexmap::IndexMap; use json_patch::Patch; use log::{info, trace, LevelFilter}; use model::{ AppSettings, AppState, ContentSearchResultsEvent, ContentSearchResultsRequest, EditorConnectionEvent, EditorData, - EditorEvent, EditorRequest, EditorState, EditorType, EntityEditorEvent, EntityEditorRequest, EntityGeneralEvent, - EntityGeneralRequest, EntityMetaPaneEvent, EntityMetadataRequest, EntityMonacoEvent, EntityMonacoRequest, - EntityTreeEvent, EntityTreeRequest, Event, FileBrowserRequest, GlobalEvent, GlobalRequest, JsonPatchType, Project, - ProjectSettings, Request, SettingsRequest, TextEditorEvent, TextEditorRequest, TextFileType, ToolRequest + EditorEvent, EditorRequest, EditorState, EditorType, EntityEditorRequest, EntityMetadataRequest, + EntityMonacoRequest, EntityTreeRequest, Event, FileBrowserRequest, GlobalEvent, GlobalRequest, JsonPatchType, + Project, ProjectSettings, Request, SettingsRequest, TextEditorEvent, TextEditorRequest, TextFileType, ToolRequest }; use notify::Watcher; -use quickentity_rs::{ - generate_patch, - qn_structs::{CommentEntity, Property, Ref} -}; -use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; +use quickentity_rs::{generate_patch, qn_structs::Property}; +use rayon::iter::ParallelIterator; use rfd::AsyncFileDialog; -use rpkg::normalise_to_hash; use serde::{Deserialize, Serialize}; -use serde_json::{from_slice, from_str, json, to_string, to_value, to_vec, Value}; +use serde_json::{from_slice, json, to_value, to_vec, Value}; use show_in_folder::show_in_folder; use tauri::{api::process::Command, async_runtime, AppHandle, Manager}; use tauri_plugin_aptabase::{EventTracker, InitOptions}; @@ -333,651 +315,9 @@ fn event(app: AppHandle, event: Event) { } }, - EditorEvent::Entity(event) => match event { - EntityEditorEvent::General(event) => match event { - EntityGeneralEvent::SetShowReverseParentRefs { - editor_id, - show_reverse_parent_refs - } => { - let mut editor_state = - app_state.editor_states.get_mut(&editor_id).context("No such editor")?; - - let settings = match editor_state.data { - EditorData::QNEntity { ref mut settings, .. } => settings, - EditorData::QNPatch { ref mut settings, .. } => settings, - - _ => { - Err(anyhow!("Editor {} is not a QN editor", editor_id))?; - panic!(); - } - }; - - settings.show_reverse_parent_refs = show_reverse_parent_refs; - } - - EntityGeneralEvent::SetShowChangesFromOriginal { - editor_id, - show_changes_from_original - } => { - let mut editor_state = - app_state.editor_states.get_mut(&editor_id).context("No such editor")?; - - let settings = match editor_state.data { - EditorData::QNEntity { ref mut settings, .. } => settings, - EditorData::QNPatch { ref mut settings, .. } => settings, - - _ => { - Err(anyhow!("Editor {} is not a QN editor", editor_id))?; - panic!(); - } - }; - - settings.show_changes_from_original = show_changes_from_original; - - send_request( - &app, - Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( - EntityTreeRequest::SetShowDiff { - editor_id, - show_diff: show_changes_from_original - } - ))) - )?; - } - }, - - EntityEditorEvent::Tree(event) => match event { - EntityTreeEvent::Initialise { editor_id } => { - let editor_state = - app_state.editor_states.get(&editor_id).context("No such editor")?; - - let entity = match editor_state.data { - EditorData::QNEntity { ref entity, .. } => entity, - EditorData::QNPatch { ref current, .. } => current, - - _ => { - Err(anyhow!("Editor {} is not a QN editor", editor_id))?; - panic!(); - } - }; - - let mut entities = vec![]; - let mut reverse_parent_refs: HashMap> = HashMap::new(); - - for (entity_id, entity_data) in entity.entities.iter() { - match entity_data.parent { - Ref::Full(ref reference) if reference.external_scene.is_none() => { - reverse_parent_refs - .entry(reference.entity_ref.to_owned()) - .and_modify(|x| x.push(entity_id.to_owned())) - .or_insert(vec![entity_id.to_owned()]); - } - - Ref::Short(Some(ref reference)) => { - reverse_parent_refs - .entry(reference.to_owned()) - .and_modify(|x| x.push(entity_id.to_owned())) - .or_insert(vec![entity_id.to_owned()]); - } - - _ => {} - } - } - - for (entity_id, entity_data) in entity.entities.iter() { - entities.push(( - entity_id.to_owned(), - entity_data.parent.to_owned(), - entity_data.name.to_owned(), - entity_data.factory.to_owned(), - reverse_parent_refs.contains_key(entity_id) - )); - } - - send_request( - &app, - Request::Editor(EditorRequest::Entity(EntityEditorRequest::General( - EntityGeneralRequest::SetIsPatchEditor { - editor_id: editor_id.to_owned(), - is_patch_editor: matches!( - editor_state.data, - EditorData::QNPatch { .. } - ) - } - ))) - )?; - - send_request( - &app, - Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( - EntityTreeRequest::NewTree { - editor_id: editor_id.to_owned(), - entities - } - ))) - )?; - - send_request( - &app, - Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( - EntityTreeRequest::SetTemplates { - editor_id: editor_id.to_owned(), - templates: from_slice(include_bytes!("../assets/templates.json")) - .unwrap() - } - ))) - )?; - - let editor_connected = app_state.editor_connection.is_connected().await; - - send_request( - &app, - Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( - EntityTreeRequest::SetEditorConnectionAvailable { - editor_id: editor_id.to_owned(), - editor_connection_available: editor_connected - } - ))) - )?; - - send_request( - &app, - Request::Editor(EditorRequest::Entity(EntityEditorRequest::Monaco( - EntityMonacoRequest::SetEditorConnected { - editor_id: editor_id.to_owned(), - connected: editor_connected - } - ))) - )?; - - if let EditorData::QNPatch { - ref base, ref current, .. - } = editor_state.data - { - send_request( - &app, - Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( - EntityTreeRequest::SetDiffInfo { - editor_id, - diff_info: get_diff_info(base, current) - } - ))) - )?; - } - } - - EntityTreeEvent::Select { editor_id, id } => { - handle_select(&app, editor_id, id).await?; - } - - EntityTreeEvent::Create { editor_id, id, content } => { - let mut editor_state = - app_state.editor_states.get_mut(&editor_id).context("No such editor")?; - - let entity = match editor_state.data { - EditorData::QNEntity { ref mut entity, .. } => entity, - EditorData::QNPatch { ref mut current, .. } => current, - - _ => { - Err(anyhow!("Editor {} is not a QN editor", editor_id))?; - panic!(); - } - }; - - entity.entities.insert(id, content); - - send_request( - &app, - Request::Global(GlobalRequest::SetTabUnsaved { - id: editor_id.to_owned(), - unsaved: true - }) - )?; - - if let EditorData::QNPatch { - ref base, ref current, .. - } = editor_state.data - { - send_request( - &app, - Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( - EntityTreeRequest::SetDiffInfo { - editor_id, - diff_info: get_diff_info(base, current) - } - ))) - )?; - } - } - - EntityTreeEvent::Delete { editor_id, id } => { - handle_delete(&app, editor_id, id).await?; - } - - EntityTreeEvent::Rename { - editor_id, - id, - new_name - } => { - let mut editor_state = - app_state.editor_states.get_mut(&editor_id).context("No such editor")?; - - let entity = match editor_state.data { - EditorData::QNEntity { ref mut entity, .. } => entity, - EditorData::QNPatch { ref mut current, .. } => current, - - _ => { - Err(anyhow!("Editor {} is not a QN editor", editor_id))?; - panic!(); - } - }; - - entity.entities.get_mut(&id).context("No such entity")?.name = new_name; - - send_request( - &app, - Request::Global(GlobalRequest::SetTabUnsaved { - id: editor_id, - unsaved: true - }) - )?; - - let mut buf = Vec::new(); - let formatter = serde_json::ser::PrettyFormatter::with_indent(b"\t"); - let mut ser = serde_json::Serializer::with_formatter(&mut buf, formatter); - - entity - .entities - .get(&id) - .context("No such entity")? - .serialize(&mut ser)?; - - send_request( - &app, - Request::Editor(EditorRequest::Entity(EntityEditorRequest::Monaco( - EntityMonacoRequest::ReplaceContentIfSameEntityID { - editor_id: editor_id.to_owned(), - entity_id: id.to_owned(), - content: String::from_utf8(buf)? - } - ))) - )?; - - if let EditorData::QNPatch { - ref base, ref current, .. - } = editor_state.data - { - send_request( - &app, - Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( - EntityTreeRequest::SetDiffInfo { - editor_id, - diff_info: get_diff_info(base, current) - } - ))) - )?; - } - } - - EntityTreeEvent::Reparent { - editor_id, - id, - new_parent - } => { - let mut editor_state = - app_state.editor_states.get_mut(&editor_id).context("No such editor")?; - - let entity = match editor_state.data { - EditorData::QNEntity { ref mut entity, .. } => entity, - EditorData::QNPatch { ref mut current, .. } => current, - - _ => { - Err(anyhow!("Editor {} is not a QN editor", editor_id))?; - panic!(); - } - }; - - entity.entities.get_mut(&id).context("No such entity")?.parent = new_parent; - - send_request( - &app, - Request::Global(GlobalRequest::SetTabUnsaved { - id: editor_id, - unsaved: true - }) - )?; - - let mut buf = Vec::new(); - let formatter = serde_json::ser::PrettyFormatter::with_indent(b"\t"); - let mut ser = serde_json::Serializer::with_formatter(&mut buf, formatter); - - entity - .entities - .get(&id) - .context("No such entity")? - .serialize(&mut ser)?; - - send_request( - &app, - Request::Editor(EditorRequest::Entity(EntityEditorRequest::Monaco( - EntityMonacoRequest::ReplaceContentIfSameEntityID { - editor_id: editor_id.to_owned(), - entity_id: id.to_owned(), - content: String::from_utf8(buf)? - } - ))) - )?; - - if let EditorData::QNPatch { - ref base, ref current, .. - } = editor_state.data - { - send_request( - &app, - Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( - EntityTreeRequest::SetDiffInfo { - editor_id, - diff_info: get_diff_info(base, current) - } - ))) - )?; - } - } - - EntityTreeEvent::Copy { editor_id, id } => { - let task = start_task(&app, format!("Copying entity {} and its children", id))?; - - let editor_state = - app_state.editor_states.get(&editor_id).context("No such editor")?; - - let entity = match editor_state.data { - EditorData::QNEntity { ref entity, .. } => entity, - EditorData::QNPatch { ref current, .. } => current, - - _ => { - Err(anyhow!("Editor {} is not a QN editor", editor_id))?; - panic!(); - } - }; - - let reverse_refs = calculate_reverse_references(entity)?; - - let entities_to_copy = get_recursive_children(entity, &id, &reverse_refs)? - .into_iter() - .collect::>(); - - let data_to_copy = CopiedEntityData { - root_entity: id.to_owned(), - data: entity - .entities - .iter() - .filter(|(x, _)| entities_to_copy.contains(*x)) - .map(|(x, y)| (x.to_owned(), y.to_owned())) - .collect() - }; - - Clipboard::new()?.set_text(to_string(&data_to_copy)?)?; - - finish_task(&app, task)?; - } - - EntityTreeEvent::Paste { editor_id, parent_id } => { - handle_paste( - &app, - editor_id, - parent_id, - from_str::(&Clipboard::new()?.get_text()?)? - ) - .await?; - } - - EntityTreeEvent::Search { editor_id, query } => { - let task = start_task(&app, format!("Searching for {}", query))?; - - let editor_state = - app_state.editor_states.get(&editor_id).context("No such editor")?; - - let entity = match editor_state.data { - EditorData::QNEntity { ref entity, .. } => entity, - EditorData::QNPatch { ref current, .. } => current, - - _ => { - Err(anyhow!("Editor {} is not a QN editor", editor_id))?; - panic!(); - } - }; - - send_request( - &app, - Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( - EntityTreeRequest::SearchResults { - editor_id, - results: entity - .entities - .par_iter() - .filter(|(id, ent)| { - let mut s = format!("{}{}", id, to_string(ent).unwrap()); - s.make_ascii_lowercase(); - query.split(' ').all(|q| s.contains(q)) - }) - .map(|(id, _)| id.to_owned()) - .collect() - } - ))) - )?; - - finish_task(&app, task)?; - } - - EntityTreeEvent::ShowHelpMenu { editor_id, entity_id } => { - handle_helpmenu(&app, editor_id, entity_id).await?; - } - - EntityTreeEvent::UseTemplate { - editor_id, - parent_id, - template - } => { - handle_paste(&app, editor_id, parent_id, template).await?; - } - - EntityTreeEvent::AddGameBrowserItem { - editor_id, - parent_id, - file - } => { - handle_gamebrowseradd(&app, editor_id, parent_id, file).await?; - } - - EntityTreeEvent::SelectEntityInEditor { editor_id, entity_id } => { - handle_selectentityineditor(&app, editor_id, entity_id).await?; - } - - EntityTreeEvent::MoveEntityToPlayer { editor_id, entity_id } => { - handle_moveentitytoplayer(&app, editor_id, entity_id).await?; - } - - EntityTreeEvent::RotateEntityAsPlayer { editor_id, entity_id } => { - handle_rotateentityasplayer(&app, editor_id, entity_id).await?; - } - - EntityTreeEvent::MoveEntityToCamera { editor_id, entity_id } => { - handle_moveentitytocamera(&app, editor_id, entity_id).await?; - } - - EntityTreeEvent::RotateEntityAsCamera { editor_id, entity_id } => { - handle_rotateentityascamera(&app, editor_id, entity_id).await?; - } - - EntityTreeEvent::RestoreToOriginal { editor_id, entity_id } => { - handle_restoretooriginal(&app, editor_id, entity_id).await?; - } - }, - - EntityEditorEvent::Monaco(event) => match event { - EntityMonacoEvent::UpdateContent { - editor_id, - entity_id, - content - } => { - handle_updatecontent(&app, editor_id, entity_id, content).await?; - } - - EntityMonacoEvent::FollowReference { editor_id, reference } => { - send_request( - &app, - Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( - EntityTreeRequest::Select { - editor_id, - id: Some(reference) - } - ))) - )?; - } - - EntityMonacoEvent::OpenFactory { factory, .. } => { - handle_openfactory(&app, factory).await?; - } - - EntityMonacoEvent::SignalPin { - editor_id, - entity_id, - pin, - output - } => { - let editor_state = - app_state.editor_states.get(&editor_id).context("No such editor")?; - - let entity = match editor_state.data { - EditorData::QNEntity { ref entity, .. } => entity, - EditorData::QNPatch { ref current, .. } => current, - - _ => { - Err(anyhow!("Editor {} is not a QN editor", editor_id))?; - panic!(); - } - }; - - app_state - .editor_connection - .signal_pin(&entity_id, &entity.blueprint_hash, &pin, output) - .await?; - } - - EntityMonacoEvent::OpenResourceOverview { resource, .. } => { - if let Some(resource_reverse_dependencies) = - app_state.resource_reverse_dependencies.load().as_ref() - { - let resource = normalise_to_hash(resource); - - if resource_reverse_dependencies.contains_key(&resource) { - let id = Uuid::new_v4(); - - app_state.editor_states.insert( - id.to_owned(), - EditorState { - file: None, - data: EditorData::ResourceOverview { - hash: resource.to_owned() - } - } - ); - - send_request( - &app, - Request::Global(GlobalRequest::CreateTab { - id, - name: format!("Resource overview ({resource})"), - editor_type: EditorType::ResourceOverview - }) - )?; - } else { - send_notification( - &app, - Notification { - kind: NotificationKind::Error, - title: "Not a vanilla resource".into(), - subtitle: "This factory doesn't exist in the base game files." - .into() - } - )?; - } - } else { - send_notification( - &app, - Notification { - kind: NotificationKind::Error, - title: "No game selected".into(), - subtitle: "You can't open game files without a copy of the game \ - selected." - .into() - } - )?; - } - } - }, - - EntityEditorEvent::MetaPane(event) => match event { - EntityMetaPaneEvent::JumpToReference { editor_id, reference } => { - send_request( - &app, - Request::Editor(EditorRequest::Entity(EntityEditorRequest::Tree( - EntityTreeRequest::Select { - editor_id, - id: Some(reference) - } - ))) - )?; - } - - EntityMetaPaneEvent::SetNotes { - editor_id, - entity_id, - notes - } => { - let mut editor_state = - app_state.editor_states.get_mut(&editor_id).context("No such editor")?; - - let entity = match editor_state.data { - EditorData::QNEntity { ref mut entity, .. } => entity, - EditorData::QNPatch { ref mut current, .. } => current, - - _ => { - Err(anyhow!("Editor {} is not a QN editor", editor_id))?; - panic!(); - } - }; - - // Remove comment referring to given entity - entity.comments.retain(|x| { - get_local_reference(&x.parent).map(|x| x != entity_id).unwrap_or(true) - }); - - // Add new comment - entity.comments.push(CommentEntity { - parent: Ref::Short(Some(entity_id)), - name: "Notes".into(), - text: notes - }); - - send_request( - &app, - Request::Global(GlobalRequest::SetTabUnsaved { - id: editor_id.to_owned(), - unsaved: true - }) - )?; - } - }, - - EntityEditorEvent::Metadata(event) => { - handle_entity_metadata_event(&app, event).await?; - } - - EntityEditorEvent::Overrides(event) => { - handle_entity_overrides_event(&app, event).await?; - } - }, + EditorEvent::Entity(event) => { + event_handling::entity::handle(&app, event).await?; + } EditorEvent::ResourceOverview(event) => { handle_resource_overview_event(&app, event).await?;