Implement HTML view type.
This commit is contained in:
parent
3623acbd21
commit
de00a4de66
@ -8,6 +8,7 @@ use yew::prelude::*;
|
|||||||
use crate::{
|
use crate::{
|
||||||
command_handler::command_handler,
|
command_handler::command_handler,
|
||||||
editor_view::CodeEditorView,
|
editor_view::CodeEditorView,
|
||||||
|
html_view::HtmlView,
|
||||||
lineengine::line::{Readline, ReadlineEvent},
|
lineengine::line::{Readline, ReadlineEvent},
|
||||||
term_split::TermSplit,
|
term_split::TermSplit,
|
||||||
timer_host::TimerHost,
|
timer_host::TimerHost,
|
||||||
@ -76,6 +77,7 @@ pub struct FrameId(pub u64);
|
|||||||
pub enum FrameViewType {
|
pub enum FrameViewType {
|
||||||
Terminal,
|
Terminal,
|
||||||
Editor,
|
Editor,
|
||||||
|
Html { inner: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Properties)]
|
#[derive(Properties)]
|
||||||
@ -239,6 +241,9 @@ pub fn term_view(props: &TermViewProps) -> Html {
|
|||||||
global_memo={props.global_memo.clone()}
|
global_memo={props.global_memo.clone()}
|
||||||
global_layout={props.global_layout.clone()}/>
|
global_layout={props.global_layout.clone()}/>
|
||||||
},
|
},
|
||||||
|
FrameViewType::Html { inner } => html! {
|
||||||
|
<HtmlView inner={inner.clone()}/>
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
src/html_view.rs
Normal file
12
src/html_view.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use yew::prelude::*;
|
||||||
|
use yew::{function_component, Properties};
|
||||||
|
|
||||||
|
#[derive(Properties, PartialEq)]
|
||||||
|
pub struct HtmlViewProps {
|
||||||
|
pub inner: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[function_component(HtmlView)]
|
||||||
|
pub fn html_view(props: &HtmlViewProps) -> Html {
|
||||||
|
html! { <iframe class="htmlview" srcdoc={props.inner.clone()} sandbox=""/> }
|
||||||
|
}
|
@ -221,6 +221,7 @@ pub fn install_lua_globals(
|
|||||||
register_command!(echo_frame_raw);
|
register_command!(echo_frame_raw);
|
||||||
register_command!(editor);
|
register_command!(editor);
|
||||||
register_stateless_command!(cmd_help, "help");
|
register_stateless_command!(cmd_help, "help");
|
||||||
|
register_command!(html);
|
||||||
register_command!(hsplit);
|
register_command!(hsplit);
|
||||||
register_stateless_command!(cmd_include, "include");
|
register_stateless_command!(cmd_include, "include");
|
||||||
register_command!(cmd_list_logs, "listlogs");
|
register_command!(cmd_list_logs, "listlogs");
|
||||||
|
@ -12,6 +12,8 @@ use piccolo::{
|
|||||||
SequenceReturn, StashedTable, StashedUserData, StashedValue, Table, UserData, Value, Variadic,
|
SequenceReturn, StashedTable, StashedUserData, StashedValue, Table, UserData, Value, Variadic,
|
||||||
};
|
};
|
||||||
use std::{rc::Rc, str};
|
use std::{rc::Rc, str};
|
||||||
|
use wasm_bindgen::JsValue;
|
||||||
|
use web_sys::console;
|
||||||
use yew::UseStateSetter;
|
use yew::UseStateSetter;
|
||||||
|
|
||||||
use super::call_checking_metatable;
|
use super::call_checking_metatable;
|
||||||
@ -774,7 +776,57 @@ pub(super) fn editor<'gc>(
|
|||||||
new_gl
|
new_gl
|
||||||
.frame_views
|
.frame_views
|
||||||
.insert(cur_frame_id, FrameViewType::Editor);
|
.insert(cur_frame_id, FrameViewType::Editor);
|
||||||
global_layout.set(new_gl.into());
|
let new_gl: Rc<GlobalLayoutState> = new_gl.into();
|
||||||
|
global_layout.set(new_gl.clone());
|
||||||
|
(*global_memo.layout.borrow_mut()) = new_gl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(piccolo::CallbackReturn::Return)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn html<'gc>(
|
||||||
|
ctx: Context<'gc>,
|
||||||
|
global_memo: &GlobalMemoCell,
|
||||||
|
global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||||
|
) -> Callback<'gc> {
|
||||||
|
let global_memo = global_memo.clone();
|
||||||
|
let global_layout = global_layout.clone();
|
||||||
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||||
|
let (frame, inner): (u64, String) = stack.consume(ctx)?;
|
||||||
|
let target_frame_id: FrameId = FrameId(frame);
|
||||||
|
let will_have_remaining_terminal = {
|
||||||
|
let layout = global_memo.layout.borrow();
|
||||||
|
layout.term_splits.iter().any(|f| {
|
||||||
|
(*f != target_frame_id)
|
||||||
|
&& matches!(
|
||||||
|
layout
|
||||||
|
.frame_views
|
||||||
|
.get(f)
|
||||||
|
.unwrap_or(&FrameViewType::Terminal),
|
||||||
|
FrameViewType::Terminal
|
||||||
|
)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
if !will_have_remaining_terminal {
|
||||||
|
Err(anyhow::Error::msg(
|
||||||
|
"html command that would leave user with no remaining terminal is invalid",
|
||||||
|
))?
|
||||||
|
}
|
||||||
|
match global_memo
|
||||||
|
.frame_registry
|
||||||
|
.borrow_mut()
|
||||||
|
.get_mut(&target_frame_id)
|
||||||
|
{
|
||||||
|
None => Err(anyhow::Error::msg("Frame no longer exists"))?,
|
||||||
|
Some(_frame_dat) => {
|
||||||
|
let mut new_gl: GlobalLayoutState = (*global_memo.layout.borrow().as_ref()).clone();
|
||||||
|
new_gl
|
||||||
|
.frame_views
|
||||||
|
.insert(target_frame_id, FrameViewType::Html { inner });
|
||||||
|
let new_gl: Rc<GlobalLayoutState> = new_gl.into();
|
||||||
|
global_layout.set(new_gl.clone());
|
||||||
|
(*global_memo.layout.borrow_mut()) = new_gl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(piccolo::CallbackReturn::Return)
|
Ok(piccolo::CallbackReturn::Return)
|
||||||
|
@ -11,6 +11,7 @@ use yew::prelude::*;
|
|||||||
pub mod command_handler;
|
pub mod command_handler;
|
||||||
pub mod editor_view;
|
pub mod editor_view;
|
||||||
pub mod frame_view;
|
pub mod frame_view;
|
||||||
|
pub mod html_view;
|
||||||
pub mod id_intern;
|
pub mod id_intern;
|
||||||
pub mod lineengine;
|
pub mod lineengine;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::{collections::BTreeMap, rc::Rc};
|
use std::{
|
||||||
|
collections::{BTreeMap, VecDeque},
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::FrameId;
|
use crate::FrameId;
|
||||||
|
|
||||||
@ -19,6 +22,12 @@ pub enum TermSplit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TermSplit {
|
impl TermSplit {
|
||||||
|
pub fn iter<'t>(&'t self) -> AccessibleSplitIter<'t> {
|
||||||
|
AccessibleSplitIter {
|
||||||
|
queue: vec![self].into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn collect_term_frames(&self, into: &mut BTreeMap<FrameId, usize>) {
|
fn collect_term_frames(&self, into: &mut BTreeMap<FrameId, usize>) {
|
||||||
match self {
|
match self {
|
||||||
TermSplit::Term { frame } => {
|
TermSplit::Term { frame } => {
|
||||||
@ -136,6 +145,31 @@ impl TermSplit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct AccessibleSplitIter<'t> {
|
||||||
|
queue: VecDeque<&'t TermSplit>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'t> Iterator for AccessibleSplitIter<'t> {
|
||||||
|
type Item = &'t FrameId;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
loop {
|
||||||
|
match self.queue.pop_back() {
|
||||||
|
Some(TermSplit::Horizontal { left, right }) => {
|
||||||
|
self.queue.push_front(left);
|
||||||
|
self.queue.push_front(right);
|
||||||
|
}
|
||||||
|
Some(TermSplit::Vertical { top, bottom }) => {
|
||||||
|
self.queue.push_front(top);
|
||||||
|
self.queue.push_front(bottom);
|
||||||
|
}
|
||||||
|
Some(TermSplit::Term { frame }) => break Some(frame),
|
||||||
|
None => break None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -237,4 +271,36 @@ mod tests {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iterates_termframes() {
|
||||||
|
use TermSplit::*;
|
||||||
|
let t = Vertical {
|
||||||
|
top: Horizontal {
|
||||||
|
left: Horizontal {
|
||||||
|
left: Term { frame: FrameId(42) }.into(),
|
||||||
|
right: Term { frame: FrameId(64) }.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
right: Term { frame: FrameId(42) }.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
bottom: Vertical {
|
||||||
|
top: Term { frame: FrameId(43) }.into(),
|
||||||
|
bottom: Term { frame: FrameId(44) }.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
};
|
||||||
|
let frames: Vec<_> = t.iter().collect();
|
||||||
|
assert_eq!(
|
||||||
|
frames,
|
||||||
|
vec![
|
||||||
|
&FrameId(42),
|
||||||
|
&FrameId(43),
|
||||||
|
&FrameId(44),
|
||||||
|
&FrameId(42),
|
||||||
|
&FrameId(64),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,3 +85,10 @@ body {
|
|||||||
min-width: 1px;
|
min-width: 1px;
|
||||||
min-height: 1px;
|
min-height: 1px;
|
||||||
}
|
}
|
||||||
|
.htmlview {
|
||||||
|
margin: 10px;
|
||||||
|
min-width: 1px;
|
||||||
|
min-height: 1px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user