Render runtime split-screen layout.
This commit is contained in:
parent
a002ac1c69
commit
525f69296e
10
src/main.rs
10
src/main.rs
@ -1,6 +1,7 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use term_split::TermSplit;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
pub mod command_handler;
|
pub mod command_handler;
|
||||||
@ -8,6 +9,7 @@ pub mod lineengine;
|
|||||||
pub mod lua_state;
|
pub mod lua_state;
|
||||||
pub mod parsing;
|
pub mod parsing;
|
||||||
pub mod split_panel;
|
pub mod split_panel;
|
||||||
|
pub mod term_split;
|
||||||
pub mod term_view;
|
pub mod term_view;
|
||||||
use crate::lua_state::{install_lua_globals, LuaState};
|
use crate::lua_state::{install_lua_globals, LuaState};
|
||||||
use crate::split_panel::*;
|
use crate::split_panel::*;
|
||||||
@ -18,6 +20,7 @@ pub struct GlobalState {
|
|||||||
// No strong references allowed between each of these groups of state.
|
// No strong references allowed between each of these groups of state.
|
||||||
frame_registry: RefCell<RegisteredTermFrames>,
|
frame_registry: RefCell<RegisteredTermFrames>,
|
||||||
lua_engine: RefCell<LuaState>,
|
lua_engine: RefCell<LuaState>,
|
||||||
|
term_splits: RefCell<TermSplit>,
|
||||||
}
|
}
|
||||||
type GlobalCell = Rc<GlobalState>;
|
type GlobalCell = Rc<GlobalState>;
|
||||||
|
|
||||||
@ -35,13 +38,16 @@ fn app() -> Html {
|
|||||||
let global = use_memo((), |_| GlobalState {
|
let global = use_memo((), |_| GlobalState {
|
||||||
frame_registry: RegisteredTermFrames::new().into(),
|
frame_registry: RegisteredTermFrames::new().into(),
|
||||||
lua_engine: LuaState::setup().expect("Can create interpreter").into(),
|
lua_engine: LuaState::setup().expect("Can create interpreter").into(),
|
||||||
|
term_splits: TermSplit::Term {
|
||||||
|
frame: TermFrame(1),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
});
|
});
|
||||||
install_lua_globals(&global).expect("Couldn't install Lua globals");
|
install_lua_globals(&global).expect("Couldn't install Lua globals");
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<div class="toplevel">
|
<div class="toplevel">
|
||||||
<SplitPanel direction={PanelDirection::Vertical} first={ html! { <TermView terminal={TermFrame(0)} global={global.clone()}/> }}
|
<TermViewTree global={global.clone()}/>
|
||||||
second={ html! { <TermView terminal={TermFrame(1)} global={global.clone()}/> } }/>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ mod tests {
|
|||||||
parse_commands(""),
|
parse_commands(""),
|
||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
commands: vec![ParsedCommand {
|
||||||
arguments: vec![""]
|
arguments: vec![""].into()
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -136,7 +136,7 @@ mod tests {
|
|||||||
parse_commands("north"),
|
parse_commands("north"),
|
||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
commands: vec![ParsedCommand {
|
||||||
arguments: vec!["north"]
|
arguments: vec!["north"].into()
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -145,7 +145,7 @@ mod tests {
|
|||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
commands: vec![ParsedCommand {
|
||||||
// This is deliberate, ensures we can reconstruct the input.
|
// This is deliberate, ensures we can reconstruct the input.
|
||||||
arguments: vec!["north", ""]
|
arguments: vec!["north", ""].into()
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -154,10 +154,10 @@ mod tests {
|
|||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![
|
commands: vec![
|
||||||
ParsedCommand {
|
ParsedCommand {
|
||||||
arguments: vec!["#blah", "{x = 1 + 2; y = 3}"]
|
arguments: vec!["#blah", "{x = 1 + 2; y = 3}"].into()
|
||||||
},
|
},
|
||||||
ParsedCommand {
|
ParsedCommand {
|
||||||
arguments: vec!["#home"]
|
arguments: vec!["#home"].into()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -166,7 +166,7 @@ mod tests {
|
|||||||
parse_commands("#blah {x = 1 + 2"),
|
parse_commands("#blah {x = 1 + 2"),
|
||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
commands: vec![ParsedCommand {
|
||||||
arguments: vec!["#blah", "{x = 1 + 2"]
|
arguments: vec!["#blah", "{x = 1 + 2"].into()
|
||||||
},]
|
},]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -174,7 +174,7 @@ mod tests {
|
|||||||
parse_commands("#blah {x = 1} {y = 1}"),
|
parse_commands("#blah {x = 1} {y = 1}"),
|
||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
commands: vec![ParsedCommand {
|
||||||
arguments: vec!["#blah", "{x = 1}", "{y = 1}"]
|
arguments: vec!["#blah", "{x = 1}", "{y = 1}"].into()
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -182,7 +182,7 @@ mod tests {
|
|||||||
parse_commands("#blah \"hello\" \"world\""),
|
parse_commands("#blah \"hello\" \"world\""),
|
||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
commands: vec![ParsedCommand {
|
||||||
arguments: vec!["#blah", "\"hello\"", "\"world\""]
|
arguments: vec!["#blah", "\"hello\"", "\"world\""].into()
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -190,7 +190,7 @@ mod tests {
|
|||||||
parse_commands("#blah {x = \"}\"} {y = 1}"),
|
parse_commands("#blah {x = \"}\"} {y = 1}"),
|
||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
commands: vec![ParsedCommand {
|
||||||
arguments: vec!["#blah", "{x = \"}\"}", "{y = 1}"]
|
arguments: vec!["#blah", "{x = \"}\"}", "{y = 1}"].into()
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -204,9 +204,10 @@ mod tests {
|
|||||||
"{x = \"}\"; a = \"{\"; y = {}; z = 1;}",
|
"{x = \"}\"; a = \"{\"; y = {}; z = 1;}",
|
||||||
"{ q = 5 }"
|
"{ q = 5 }"
|
||||||
]
|
]
|
||||||
|
.into()
|
||||||
},
|
},
|
||||||
ParsedCommand {
|
ParsedCommand {
|
||||||
arguments: vec![""]
|
arguments: vec![""].into()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -215,7 +216,7 @@ mod tests {
|
|||||||
parse_commands("#blah {\\}\\}\\}} {y = 1}"),
|
parse_commands("#blah {\\}\\}\\}} {y = 1}"),
|
||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
commands: vec![ParsedCommand {
|
||||||
arguments: vec!["#blah", "{\\}\\}\\}}", "{y = 1}"]
|
arguments: vec!["#blah", "{\\}\\}\\}}", "{y = 1}"].into()
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -223,7 +224,7 @@ mod tests {
|
|||||||
parse_commands("#blah \"This is a \\\"test\\\"\""),
|
parse_commands("#blah \"This is a \\\"test\\\"\""),
|
||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
commands: vec![ParsedCommand {
|
||||||
arguments: vec!["#blah", "\"This is a \\\"test\\\"\""]
|
arguments: vec!["#blah", "\"This is a \\\"test\\\"\""].into()
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
332
src/term_split.rs
Normal file
332
src/term_split.rs
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
use itertools::Itertools;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use crate::TermFrame;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
|
pub enum TermSplit {
|
||||||
|
Term {
|
||||||
|
frame: TermFrame,
|
||||||
|
},
|
||||||
|
Horizontal {
|
||||||
|
left: Box<TermSplit>,
|
||||||
|
right: Box<TermSplit>,
|
||||||
|
},
|
||||||
|
Vertical {
|
||||||
|
top: Box<TermSplit>,
|
||||||
|
bottom: Box<TermSplit>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TermSplit {
|
||||||
|
fn collect_term_frames(&self, into: &mut BTreeMap<TermFrame, usize>) {
|
||||||
|
match self {
|
||||||
|
TermSplit::Term { frame } => {
|
||||||
|
into.entry(frame.clone())
|
||||||
|
.and_modify(|v| *v += 1)
|
||||||
|
.or_insert(1);
|
||||||
|
}
|
||||||
|
TermSplit::Horizontal { left, right } => {
|
||||||
|
left.collect_term_frames(into);
|
||||||
|
right.collect_term_frames(into);
|
||||||
|
}
|
||||||
|
TermSplit::Vertical { top, bottom } => {
|
||||||
|
top.collect_term_frames(into);
|
||||||
|
bottom.collect_term_frames(into);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate(&self) -> Result<(), String> {
|
||||||
|
let mut frame_count: BTreeMap<TermFrame, usize> = BTreeMap::new();
|
||||||
|
self.collect_term_frames(&mut frame_count);
|
||||||
|
let duplicate_terminal = frame_count
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(k, v)| {
|
||||||
|
if *v > 1 {
|
||||||
|
Some(format!("Terminal {}", k.0))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.join(", ");
|
||||||
|
if !duplicate_terminal.is_empty() {
|
||||||
|
Err(format!(
|
||||||
|
"Attempt to create layout that duplicates reference to: {}",
|
||||||
|
duplicate_terminal
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modify_at_pathstr<F>(&mut self, pathstr: &str, mod_with: F) -> Result<(), String>
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut TermSplit) -> Result<(), String>,
|
||||||
|
{
|
||||||
|
self.modify_at_pathstr_vec(&pathstr.chars().collect::<Vec<char>>(), mod_with)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn modify_at_pathstr_vec<F>(&mut self, pathstr: &[char], mod_with: F) -> Result<(), String>
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut TermSplit) -> Result<(), String>,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
TermSplit::Horizontal { left, right } => match pathstr.split_first() {
|
||||||
|
None => mod_with(self),
|
||||||
|
Some(('l', path_rest)) => left.modify_at_pathstr_vec(path_rest, mod_with),
|
||||||
|
Some(('r', path_rest)) => right.modify_at_pathstr_vec(path_rest, mod_with),
|
||||||
|
Some((c, path_rest)) => Err(format!("In split path, found {} before {}, which was unexpected for a horizontal split", c, path_rest.iter().collect::<String>()))
|
||||||
|
},
|
||||||
|
TermSplit::Vertical { top, bottom } => match pathstr.split_first() {
|
||||||
|
None => mod_with(self),
|
||||||
|
Some(('t', path_rest)) => top.modify_at_pathstr_vec(path_rest, mod_with),
|
||||||
|
Some(('b', path_rest)) => bottom.modify_at_pathstr_vec(path_rest, mod_with),
|
||||||
|
Some((c, path_rest)) => Err(format!("In split path, found {} before {}, which was unexpected for a vertical split", c, path_rest.iter().collect::<String>()))
|
||||||
|
},
|
||||||
|
TermSplit::Term { .. } => match pathstr.split_first() {
|
||||||
|
None => mod_with(self),
|
||||||
|
Some(_) => Err(format!("In split path, found trailing junk {} after addressing terminal", pathstr.iter().collect::<String>()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hsplit(&mut self, pathstr: &str, new_frame: TermFrame) -> Result<(), String> {
|
||||||
|
let mut new = self.clone();
|
||||||
|
new.modify_at_pathstr(pathstr, move |n| {
|
||||||
|
*n = TermSplit::Horizontal {
|
||||||
|
left: n.clone().into(),
|
||||||
|
right: TermSplit::Term { frame: new_frame }.into(),
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
new.validate()?;
|
||||||
|
*self = new;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vsplit(&mut self, pathstr: &str, new_frame: TermFrame) -> Result<(), String> {
|
||||||
|
let mut new = self.clone();
|
||||||
|
new.modify_at_pathstr(pathstr, move |n| {
|
||||||
|
*n = TermSplit::Vertical {
|
||||||
|
top: n.clone().into(),
|
||||||
|
bottom: TermSplit::Term { frame: new_frame }.into(),
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
new.validate()?;
|
||||||
|
*self = new;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn join(&mut self, pathstr: &str) -> Result<(), String> {
|
||||||
|
self.modify_at_pathstr(pathstr, move |n| match n {
|
||||||
|
TermSplit::Term { .. } => {
|
||||||
|
Err("Can only join vertical or horizontal splits, not a terminal".to_owned())
|
||||||
|
}
|
||||||
|
TermSplit::Horizontal { left, .. } => {
|
||||||
|
*n = (**left).clone();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
TermSplit::Vertical { top, .. } => {
|
||||||
|
*n = (**top).clone();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_accepts_valid() {
|
||||||
|
use TermSplit::*;
|
||||||
|
assert_eq!(
|
||||||
|
(Vertical {
|
||||||
|
top: Term {
|
||||||
|
frame: TermFrame(1)
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
bottom: Horizontal {
|
||||||
|
left: Term {
|
||||||
|
frame: TermFrame(2)
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
right: Term {
|
||||||
|
frame: TermFrame(3)
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.validate(),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_rejects_duplicate() {
|
||||||
|
use TermSplit::*;
|
||||||
|
assert_eq!(
|
||||||
|
(Vertical {
|
||||||
|
top: Term {
|
||||||
|
frame: TermFrame(1)
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
bottom: Horizontal {
|
||||||
|
left: Term {
|
||||||
|
frame: TermFrame(1)
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
right: Term {
|
||||||
|
frame: TermFrame(3)
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.validate(),
|
||||||
|
Err("Attempt to create layout that duplicates reference to: Terminal 1".to_owned())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
(Vertical {
|
||||||
|
top: Term {
|
||||||
|
frame: TermFrame(42)
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
bottom: Horizontal {
|
||||||
|
left: Term {
|
||||||
|
frame: TermFrame(1)
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
right: Term {
|
||||||
|
frame: TermFrame(42)
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.validate(),
|
||||||
|
Err("Attempt to create layout that duplicates reference to: Terminal 42".to_owned())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn modify_at_pathstr_works() {
|
||||||
|
use TermSplit::*;
|
||||||
|
let mut t = Term {
|
||||||
|
frame: TermFrame(1),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
t.modify_at_pathstr("", |v| {
|
||||||
|
*v = Term {
|
||||||
|
frame: TermFrame(2),
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
t,
|
||||||
|
Term {
|
||||||
|
frame: TermFrame(2)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
t.modify_at_pathstr("tlr", |v| {
|
||||||
|
*v = Term {
|
||||||
|
frame: TermFrame(2),
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
Err("In split path, found trailing junk tlr after addressing terminal".to_owned())
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut t = Vertical {
|
||||||
|
top: Horizontal {
|
||||||
|
left: Horizontal {
|
||||||
|
left: Term {
|
||||||
|
frame: TermFrame(42),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
right: Term {
|
||||||
|
frame: TermFrame(64),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
right: Term {
|
||||||
|
frame: TermFrame(42),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
bottom: Vertical {
|
||||||
|
top: Term {
|
||||||
|
frame: TermFrame(43),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
bottom: Term {
|
||||||
|
frame: TermFrame(44),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
t.modify_at_pathstr("tlr", |v| {
|
||||||
|
*v = Term {
|
||||||
|
frame: TermFrame(2),
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
t.modify_at_pathstr("bb", |v| {
|
||||||
|
*v = Term {
|
||||||
|
frame: TermFrame(3),
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
t,
|
||||||
|
Vertical {
|
||||||
|
top: Horizontal {
|
||||||
|
left: Horizontal {
|
||||||
|
left: Term {
|
||||||
|
frame: TermFrame(42),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
right: Term {
|
||||||
|
frame: TermFrame(2),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
right: Term {
|
||||||
|
frame: TermFrame(42),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
bottom: Vertical {
|
||||||
|
top: Term {
|
||||||
|
frame: TermFrame(43),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
bottom: Term {
|
||||||
|
frame: TermFrame(3),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,8 @@ use yew::prelude::*;
|
|||||||
use crate::{
|
use crate::{
|
||||||
command_handler::command_handler,
|
command_handler::command_handler,
|
||||||
lineengine::line::{Readline, ReadlineEvent},
|
lineengine::line::{Readline, ReadlineEvent},
|
||||||
GlobalCell,
|
term_split::TermSplit,
|
||||||
|
GlobalCell, PanelDirection, SplitPanel,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
@ -64,7 +65,7 @@ extern "C" {
|
|||||||
fn fit(this: &FitAddon);
|
fn fit(this: &FitAddon);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, Ord, Hash, PartialEq, PartialOrd, Clone)]
|
#[derive(Eq, Ord, Hash, PartialEq, PartialOrd, Clone, Debug)]
|
||||||
pub struct TermFrame(pub u64);
|
pub struct TermFrame(pub u64);
|
||||||
|
|
||||||
#[derive(Properties)]
|
#[derive(Properties)]
|
||||||
@ -212,6 +213,39 @@ pub fn term_view(props: &TermViewProps) -> Html {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Properties, PartialEq)]
|
||||||
|
pub struct TermViewTreeProps {
|
||||||
|
pub global: GlobalCell,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[function_component(TermViewTree)]
|
||||||
|
pub fn term_view_tree(props: &TermViewTreeProps) -> Html {
|
||||||
|
fn mk_term_view_tree(global: &GlobalCell, split: &TermSplit) -> Html {
|
||||||
|
use TermSplit::*;
|
||||||
|
match split {
|
||||||
|
Term { frame } => html! {
|
||||||
|
<TermView global={global.clone()} terminal={frame.clone()}/>
|
||||||
|
},
|
||||||
|
Horizontal { left, right } => html! {
|
||||||
|
<SplitPanel
|
||||||
|
direction={PanelDirection::Horizontal}
|
||||||
|
first={mk_term_view_tree(global, left)}
|
||||||
|
second={mk_term_view_tree(global, right)}
|
||||||
|
/>
|
||||||
|
},
|
||||||
|
Vertical { top, bottom } => html! {
|
||||||
|
<SplitPanel
|
||||||
|
direction={PanelDirection::Vertical}
|
||||||
|
first={mk_term_view_tree(global, top)}
|
||||||
|
second={mk_term_view_tree(global, bottom)}
|
||||||
|
/>
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mk_term_view_tree(&props.global, &props.global.term_splits.borrow())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn echo_to_term_frame(
|
pub fn echo_to_term_frame(
|
||||||
global: &GlobalCell,
|
global: &GlobalCell,
|
||||||
frame_id: &TermFrame,
|
frame_id: &TermFrame,
|
||||||
|
Loading…
Reference in New Issue
Block a user