use std::{ops::Deref, rc::Rc}; use crate::{FrameId, GlobalLayoutState, GlobalMemoCell, PanelDirection, SplitPanel}; use anyhow::Error; use create_script_dialog::CreateScriptDialog; use editor_area::EditorArea; use editor_nav::EditorNav; use storage::fetch_initial_editor_state; use wasm_bindgen::{closure::Closure, JsCast}; use web_sys::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 create_script_dialog; 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()); } #[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(|| {}) }); html! {
{if editor_state.show_create_dialog { html! { } } else { html! { <> } }} {match editor_state.error_msg.as_ref() { None => { html! { <> }} Some(msg) => { let editor_state = editor_state.clone(); html! { } } }} }} second={html!{}} />
} }