use std::{ops::Deref, rc::Rc}; use crate::{FrameId, GlobalLayoutState, GlobalMemoCell, PanelDirection, SplitPanel}; use anyhow::Error; use editor_area::EditorArea; use editor_nav::EditorNav; use storage::fetch_initial_editor_state; use wasm_bindgen::{closure::Closure, JsCast}; use web_sys::{window, HtmlInputElement, KeyboardEvent}; use yew::{ function_component, html, use_effect_with, use_node_ref, use_state, use_state_eq, AttrValue, Callback, Html, Properties, UseStateHandle, }; use self::storage::load_file_contents; mod editor_area; mod editor_nav; mod storage; #[derive(Properties, PartialEq, Clone)] pub struct EditorViewProps { pub frame: FrameId, pub global_memo: GlobalMemoCell, pub global_layout: UseStateHandle>, } #[derive(Properties, PartialEq, Clone)] pub struct EditorViewDetailProps { pub frame: FrameId, pub global_memo: GlobalMemoCell, pub global_layout: UseStateHandle>, pub editor_state: UseStateHandle>, } fn close_editor(frame: &FrameId, global_layout: &UseStateHandle>) { let mut gl_new: GlobalLayoutState = global_layout.as_ref().clone(); gl_new.frame_views.remove(frame); global_layout.set(gl_new.into()); } #[derive(Clone, PartialEq)] pub struct EditorViewState { error_msg: Option, available_files: Vec, open_file: AttrValue, show_create_dialog: bool, show_delete_dialog: Option, } pub fn try_run_script(script: &str, global_memo: &GlobalMemoCell) -> anyhow::Result<()> { let script = load_file_contents(script)?; global_memo .lua_engine .borrow_mut() .execute(&script) .map_err(Error::msg)?; Ok(()) } fn run_script(script: &str, global_memo: &GlobalMemoCell, set_err: Rc) where F: Fn(Option<&str>), { match try_run_script(script, global_memo) { Ok(()) => { set_err(None); } Err(e) => { set_err(Some(&format!("Script error: {}", e))); } } } #[derive(Properties, PartialEq, Clone)] pub struct ErrorBarProps { pub msg: AttrValue, pub dismiss: Callback<()>, } #[function_component(ErrorBar)] fn error_bar(props: &ErrorBarProps) -> Html { let props = props.clone(); html! { } } fn close_modals(state: &UseStateHandle>) { let mut new_state = (*state.deref().deref()).clone(); new_state.show_create_dialog = false; new_state.show_delete_dialog = None; state.set(new_state.into()); } fn create_script(state: &UseStateHandle>) { let mut scriptname = window() .and_then(|w| w.document()) .and_then(|d| d.get_element_by_id("newscriptname")) .map(|el| el.unchecked_into::().value()) .unwrap_or_default(); if scriptname.is_empty() { scriptname = "untitled.lua".to_owned(); } let mut new_state = (*state.deref().deref()).clone(); let scriptname: AttrValue = scriptname.into(); if new_state.available_files.contains(&scriptname) { new_state.error_msg = Some("A script with that name already exists".into()); } new_state.available_files.push(scriptname.clone()); new_state.open_file = scriptname; new_state.show_create_dialog = false; state.set(new_state.into()); } #[function_component(CodeEditorView)] pub fn editor_view(props: &EditorViewProps) -> Html { let editor_state: UseStateHandle> = use_state_eq(fetch_initial_editor_state); let detail_props: EditorViewDetailProps = EditorViewDetailProps { frame: props.frame.clone(), global_memo: props.global_memo.clone(), global_layout: props.global_layout.clone(), editor_state: editor_state.clone(), }; let current_script = editor_state.open_file.clone(); let set_err_state = editor_state.clone(); let set_err = Rc::new(move |msg: Option<&str>| { let mut new_state = (*set_err_state.as_ref()).clone(); new_state.error_msg = msg.map(|v| AttrValue::from(String::from(v))); set_err_state.set(new_state.into()); }); let editor_ref = use_node_ref(); let global_layout = props.global_layout.clone(); let frame = props.frame.clone(); type KbClosure = Closure; let editor_closure: UseStateHandle>> = use_state(|| None); let editor_ref_eff = editor_ref.clone(); let global_memo = props.global_memo.clone(); use_effect_with(current_script.clone(), move |_| { if let Some(editor_node) = editor_ref_eff.get() { let closure = Closure::new(move |ev: KeyboardEvent| { if ev.code() == "Enter" && ev.get_modifier_state("Control") { run_script(¤t_script, &global_memo, set_err.clone()); ev.prevent_default(); } else if ev.code() == "Escape" { close_editor(&frame, &global_layout); } }); let _ = editor_node .add_event_listener_with_callback("keydown", closure.as_ref().unchecked_ref()); let closure: Rc> = closure.into(); editor_closure.set(Some(closure.clone())); let cleanup: Box = Box::new(move || { let _ = editor_node.remove_event_listener_with_callback( "keydown", (*closure.as_ref()).as_ref().unchecked_ref(), ); }); return cleanup; } Box::new(|| {}) }); let state_modalclose1 = editor_state.clone(); let state_modalclose2 = editor_state.clone(); let state_createscript = editor_state.clone(); html! {
{if editor_state.show_create_dialog { html! { <> } }