use std::{ops::Deref, rc::Rc}; use crate::{ command_handler::execute_queue, FrameId, GlobalLayoutState, GlobalMemoCell, PanelDirection, SplitPanel, }; use anyhow::Error; use create_script_dialog::CreateScriptDialog; use delete_script_dialog::DeleteScriptDialog; use editor_area::EditorArea; use editor_nav::EditorNav; use storage::fetch_initial_editor_state; use web_sys::KeyboardEvent; use yew::{ function_component, html, use_node_ref, use_state_eq, AttrValue, Callback, Html, Properties, UseStateHandle, }; pub use self::storage::load_extant_file_contents; use self::storage::load_file_contents; mod create_script_dialog; mod delete_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, Debug)] 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(()) } // Only intended for use from the editor, since it borrows from global_memo. fn run_script(script: &str, global_memo: &GlobalMemoCell, set_err: Rc, frame: &FrameId) where F: Fn(Option<&str>), { global_memo.lua_engine.borrow_mut().set_current_frame(frame); match try_run_script(script, global_memo) { Ok(()) => { set_err(None); } Err(e) => { set_err(Some(&format!("Script error: {}", e))); } } execute_queue(global_memo); } fn select_file(script: &AttrValue, state: &UseStateHandle>) { let mut new_state: EditorViewState = state.as_ref().clone(); new_state.open_file = script.clone(); state.set(new_state.into()); } #[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 keyboard_handler( editor_state: UseStateHandle>, global_memo: GlobalMemoCell, global_layout: UseStateHandle>, set_err: Rc, frame: FrameId, ) -> impl Fn(KeyboardEvent) where F: Fn(Option<&str>) + 'static, { move |ev: KeyboardEvent| { if ev.code() == "Enter" && ev.get_modifier_state("Control") { run_script( &editor_state.open_file, &global_memo, set_err.clone(), &frame, ); ev.prevent_default(); } else if ev.code() == "Escape" || (ev.code() == "KeyX" && ev.get_modifier_state("Control")) { close_editor(&frame, &global_layout); } } } #[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 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(); let global_memo = props.global_memo.clone(); let kb_editor_state = editor_state.clone(); html! {
{if editor_state.show_create_dialog { html! { } } else if let Some(ref delete_file) = editor_state.show_delete_dialog { html! { } } else { html! { <> } }} {match editor_state.error_msg.as_ref() { None => { html! { <> }} Some(msg) => { let editor_state = editor_state.clone(); html! { } } }} }} second={html!{}} />
} }