Add navbars, infra for tab frames
This commit is contained in:
parent
13225859a1
commit
0da685d4f5
@ -10,6 +10,7 @@ use crate::{
|
||||
editor_view::CodeEditorView,
|
||||
html_view::HtmlView,
|
||||
lineengine::line::{Readline, ReadlineEvent},
|
||||
tab_panel::TabPanel,
|
||||
term_split::TermSplit,
|
||||
timer_host::TimerHost,
|
||||
GlobalLayoutCell, GlobalMemoCell, PanelDirection, SplitPanel,
|
||||
@ -279,6 +280,10 @@ pub fn term_view_tree(props: &TermViewTreeProps) -> Html {
|
||||
second={mk_term_view_tree(global, layout, bottom)}
|
||||
/>
|
||||
},
|
||||
Tabs { tabs } => html! {
|
||||
<TabPanel tabs={tabs.iter().map(|(k, t)| (*k, mk_term_view_tree(global, layout.clone(), t))).collect::<Vec<_>>()}
|
||||
/>
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
use self::{frameroutes::*, frames::*, muds::*, storage::*};
|
||||
use self::{frameroutes::*, frames::*, muds::*, navbar::*, storage::*};
|
||||
use anyhow::Error;
|
||||
use help::{cmd_help, load_help};
|
||||
use muds::telopt::configure_telopt_table;
|
||||
use navbar::init_navbar;
|
||||
use piccolo::{
|
||||
async_callback::{AsyncSequence, Locals},
|
||||
meta_ops::{self, MetaResult},
|
||||
@ -33,6 +34,7 @@ pub mod frames;
|
||||
pub mod help;
|
||||
pub mod muds;
|
||||
pub mod named_closure;
|
||||
pub mod navbar;
|
||||
pub mod storage;
|
||||
|
||||
impl LuaState {
|
||||
@ -226,6 +228,7 @@ pub fn install_lua_globals(
|
||||
register_stateless_command!(cmd_include, "include");
|
||||
register_command!(cmd_list_logs, "listlogs");
|
||||
register_command!(mud_log, "log");
|
||||
register_command!(syncnavbar);
|
||||
register_command!(panel_merge);
|
||||
register_command!(panel_swap);
|
||||
register_command!(sendmud_raw);
|
||||
@ -398,6 +401,8 @@ pub fn install_lua_globals(
|
||||
match_table_try_run_sub
|
||||
);
|
||||
|
||||
init_navbar(ctx, global_memo, &global_layout)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
117
src/lua_engine/navbar.rs
Normal file
117
src/lua_engine/navbar.rs
Normal file
@ -0,0 +1,117 @@
|
||||
use anyhow::Result;
|
||||
use piccolo::{Callback, CallbackReturn, Context, IntoValue, Table};
|
||||
use std::{ops::Deref, rc::Rc};
|
||||
use yew::UseStateSetter;
|
||||
|
||||
use crate::{navbar::NavbarButton, FrameId, GlobalLayoutCell, GlobalLayoutState, GlobalMemoCell};
|
||||
|
||||
pub fn init_navbar(
|
||||
ctx: Context,
|
||||
global_memo: &GlobalMemoCell,
|
||||
global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Result<()> {
|
||||
let info_tbl: Table = ctx.get_global::<Table>("info")?;
|
||||
let nav_tbl: Table = Table::new(&ctx);
|
||||
|
||||
let default_buttons: Vec<NavbarButton> = vec![
|
||||
NavbarButton {
|
||||
icon: "code-square".to_owned(),
|
||||
tooltip: "Open the script editor".to_owned(),
|
||||
frame: FrameId(1),
|
||||
command: "#editor".to_owned(),
|
||||
},
|
||||
NavbarButton {
|
||||
icon: "arrow-left".to_owned(),
|
||||
tooltip: "Go west".to_owned(),
|
||||
frame: FrameId(1),
|
||||
command: "w".to_owned(),
|
||||
},
|
||||
NavbarButton {
|
||||
icon: "arrow-up-left".to_owned(),
|
||||
tooltip: "Go northwest".to_owned(),
|
||||
frame: FrameId(1),
|
||||
command: "nw".to_owned(),
|
||||
},
|
||||
NavbarButton {
|
||||
icon: "arrow-up".to_owned(),
|
||||
tooltip: "Go north".to_owned(),
|
||||
frame: FrameId(1),
|
||||
command: "n".to_owned(),
|
||||
},
|
||||
NavbarButton {
|
||||
icon: "arrow-up-right".to_owned(),
|
||||
tooltip: "Go northeast".to_owned(),
|
||||
frame: FrameId(1),
|
||||
command: "ne".to_owned(),
|
||||
},
|
||||
NavbarButton {
|
||||
icon: "arrow-right".to_owned(),
|
||||
tooltip: "Go east".to_owned(),
|
||||
frame: FrameId(1),
|
||||
command: "e".to_owned(),
|
||||
},
|
||||
NavbarButton {
|
||||
icon: "arrow-down-right".to_owned(),
|
||||
tooltip: "Go southeast".to_owned(),
|
||||
frame: FrameId(1),
|
||||
command: "se".to_owned(),
|
||||
},
|
||||
NavbarButton {
|
||||
icon: "arrow-down".to_owned(),
|
||||
tooltip: "Go south".to_owned(),
|
||||
frame: FrameId(1),
|
||||
command: "s".to_owned(),
|
||||
},
|
||||
NavbarButton {
|
||||
icon: "arrow-down-left".to_owned(),
|
||||
tooltip: "Go southwest".to_owned(),
|
||||
frame: FrameId(1),
|
||||
command: "sw".to_owned(),
|
||||
},
|
||||
];
|
||||
|
||||
let mut i: i64 = 1;
|
||||
for button in &default_buttons {
|
||||
nav_tbl.set(ctx, i, button.clone().into_value(ctx))?;
|
||||
i += 1;
|
||||
}
|
||||
nav_tbl.set(ctx, "showing", true)?;
|
||||
info_tbl.set(ctx, "navbar", nav_tbl)?;
|
||||
|
||||
let mut new_layout: GlobalLayoutState = global_memo.layout.borrow().deref().deref().clone();
|
||||
new_layout.navstate.buttons = default_buttons;
|
||||
let new_layout: Rc<GlobalLayoutState> = new_layout.into();
|
||||
*global_memo.layout.borrow_mut() = new_layout.clone();
|
||||
global_layout.set(new_layout);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn syncnavbar<'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, _stack| {
|
||||
let mut new_layout: GlobalLayoutState = global_memo.layout.borrow().deref().deref().clone();
|
||||
|
||||
let tbl: Table = ctx.get_global::<Table>("info")?.get(ctx, "navbar")?;
|
||||
|
||||
new_layout.navstate.showing = tbl.get(ctx, "showing")?;
|
||||
let mut buttons: Vec<NavbarButton> = vec![];
|
||||
let mut i: i64 = 1;
|
||||
loop {
|
||||
match tbl.get(ctx, i) {
|
||||
Err(_) => break,
|
||||
Ok(v) => buttons.push(v),
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
new_layout.navstate.buttons = buttons;
|
||||
let new_layout: Rc<GlobalLayoutState> = new_layout.into();
|
||||
*global_memo.layout.borrow_mut() = new_layout.clone();
|
||||
global_layout.set(new_layout);
|
||||
Ok(CallbackReturn::Return)
|
||||
})
|
||||
}
|
15
src/main.rs
15
src/main.rs
@ -3,6 +3,7 @@ use std::collections::VecDeque;
|
||||
use std::rc::Rc;
|
||||
|
||||
use logging::LoggingEngine;
|
||||
use navbar::{NavState, Navbar};
|
||||
use parsing::ParsedCommand;
|
||||
use term_split::TermSplit;
|
||||
use web_sys::console;
|
||||
@ -17,8 +18,10 @@ pub mod lineengine;
|
||||
pub mod logging;
|
||||
pub mod lua_engine;
|
||||
pub mod match_table;
|
||||
pub mod navbar;
|
||||
pub mod parsing;
|
||||
pub mod split_panel;
|
||||
pub mod tab_panel;
|
||||
pub mod telnet;
|
||||
pub mod term_split;
|
||||
pub mod timer_host;
|
||||
@ -59,6 +62,7 @@ impl PartialEq for GlobalMemoState {
|
||||
pub struct GlobalLayoutState {
|
||||
term_splits: TermSplit,
|
||||
frame_views: im::OrdMap<FrameId, FrameViewType>,
|
||||
navstate: NavState,
|
||||
}
|
||||
// Note: Despite interior mutability, you still should always call the
|
||||
// setter to force a re-render. Interior mutability is used to allow this
|
||||
@ -72,6 +76,7 @@ fn app() -> Html {
|
||||
Rc::new(GlobalLayoutState {
|
||||
term_splits: TermSplit::Term { frame: FrameId(1) },
|
||||
frame_views: im::OrdMap::new(),
|
||||
navstate: Default::default(),
|
||||
})
|
||||
});
|
||||
let global_memo = use_memo((), |_| GlobalMemoState {
|
||||
@ -106,9 +111,13 @@ fn app() -> Html {
|
||||
});
|
||||
|
||||
html! {
|
||||
<div class="toplevel" data-bs-theme="dark">
|
||||
<TermViewTree global_memo={global_memo.clone()}
|
||||
global_layout={global_layout.clone()}/>
|
||||
<div class="toplevel d-flex flex-column" data-bs-theme="dark">
|
||||
<Navbar global_memo={global_memo.clone()}
|
||||
global_layout={global_layout.clone()}/>
|
||||
<div class="termtreewrapper">
|
||||
<TermViewTree global_memo={global_memo.clone()}
|
||||
global_layout={global_layout.clone()}/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
108
src/navbar.rs
Normal file
108
src/navbar.rs
Normal file
@ -0,0 +1,108 @@
|
||||
use piccolo::{Context, FromValue, IntoValue, Table, Value};
|
||||
use yew::{function_component, html, Callback, Html, Properties, UseStateHandle};
|
||||
|
||||
use crate::{command_handler::command_handler, FrameId, GlobalLayoutCell, GlobalMemoCell};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct NavbarProps {
|
||||
pub global_memo: GlobalMemoCell,
|
||||
pub global_layout: UseStateHandle<GlobalLayoutCell>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct NavbarButton {
|
||||
pub icon: String,
|
||||
pub tooltip: String,
|
||||
pub frame: FrameId,
|
||||
pub command: String,
|
||||
}
|
||||
|
||||
impl<'gc> IntoValue<'gc> for NavbarButton {
|
||||
fn into_value(self, ctx: Context<'gc>) -> Value<'gc> {
|
||||
let tbl = Table::new(&ctx);
|
||||
let _ = tbl.set(ctx, "icon", self.icon);
|
||||
let _ = tbl.set(ctx, "tooltip", self.tooltip);
|
||||
let _ = tbl.set(ctx, "frame", self.frame.0 as i64);
|
||||
let _ = tbl.set(ctx, "command", self.command);
|
||||
tbl.into_value(ctx)
|
||||
}
|
||||
}
|
||||
impl<'gc> FromValue<'gc> for NavbarButton {
|
||||
fn from_value(ctx: Context<'gc>, value: Value<'gc>) -> Result<Self, piccolo::TypeError> {
|
||||
let tbl: Table = Table::from_value(ctx, value)?;
|
||||
Ok(NavbarButton {
|
||||
icon: tbl.get(ctx, "icon")?,
|
||||
tooltip: tbl.get(ctx, "tooltip")?,
|
||||
frame: FrameId(tbl.get(ctx, "frame")?),
|
||||
command: tbl.get(ctx, "command")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub struct NavState {
|
||||
pub showing: bool,
|
||||
pub buttons: Vec<NavbarButton>,
|
||||
}
|
||||
impl Default for NavState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
showing: true,
|
||||
// Overridden later in init_navbar, starts off minimal.
|
||||
buttons: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component(Navbar)]
|
||||
pub fn navbar(props: &NavbarProps) -> Html {
|
||||
if !props.global_layout.navstate.showing {
|
||||
return html! { <></> };
|
||||
}
|
||||
html! {
|
||||
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||
<div>
|
||||
{
|
||||
props.global_layout.navstate.buttons.iter().map(|button| {
|
||||
let global_memo = props.global_memo.clone();
|
||||
let button = button.clone();
|
||||
let onclick = Callback::from(move |_| {
|
||||
command_handler(&global_memo, &button.frame, &button.command);
|
||||
});
|
||||
html! {
|
||||
<button type="button" title={button.tooltip} class="btn" {onclick}>
|
||||
<i class={format!("bi bi-{}", button.icon)}></i>
|
||||
</button>
|
||||
}
|
||||
}).collect::<Vec<_>>()
|
||||
}
|
||||
</div>
|
||||
</nav>
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use piccolo::{FromValue, IntoValue, Lua};
|
||||
|
||||
use crate::FrameId;
|
||||
|
||||
use super::NavbarButton;
|
||||
|
||||
#[test]
|
||||
fn button_tovalue_fromvalue_roundtrips() {
|
||||
let mut lua = Lua::empty();
|
||||
lua.enter(|ctx| {
|
||||
let b = NavbarButton {
|
||||
icon: "myicon".to_owned(),
|
||||
tooltip: "I'm a tooltip".to_owned(),
|
||||
frame: FrameId(42),
|
||||
command: "hello world".to_owned(),
|
||||
};
|
||||
assert_eq!(
|
||||
NavbarButton::from_value(ctx, b.clone().into_value(ctx)).unwrap(),
|
||||
b
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
38
src/tab_panel.rs
Normal file
38
src/tab_panel.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use yew::{function_component, html, use_state_eq, Callback, Html, Properties};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct TabPanelProps {
|
||||
pub tabs: Vec<(char, Html)>,
|
||||
}
|
||||
|
||||
#[function_component(TabPanel)]
|
||||
pub fn tab_panel_props(props: &TabPanelProps) -> Html {
|
||||
let active_tab = use_state_eq(|| props.tabs.get(0).map(|(k, _)| *k).unwrap_or('0'));
|
||||
html! {
|
||||
<div class="container-fluid d-flex flex-column">
|
||||
<ul class="nav nav-tabs">
|
||||
{props.tabs.iter().map(|(c, _)| {
|
||||
let class_name = if *c == *active_tab { "nav-link active" } else { "nav-link" };
|
||||
let c_click: char = *c;
|
||||
let active_tab_click = active_tab.clone();
|
||||
let on_click = Callback::from(move |_| {
|
||||
active_tab_click.set(c_click);
|
||||
});
|
||||
html! {
|
||||
<li class="nav-item" key={format!("nav-{}", c)}>
|
||||
<a class={class_name} onclick={on_click} href="#">{c}</a>
|
||||
</li>
|
||||
}}).collect::<Vec<_>>()
|
||||
}
|
||||
</ul>
|
||||
{props.tabs.iter().map(|(c, v)| {
|
||||
let class_name = if *c == *active_tab { "container-fluid visible"} else { "container-fluid invisible"};
|
||||
html! {
|
||||
<div class={class_name} key={format!("content-{}", c)}>
|
||||
{v.clone()}
|
||||
</div>
|
||||
}
|
||||
}).collect::<Vec<_>>()}
|
||||
</div>
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
use im::OrdMap;
|
||||
use itertools::Itertools;
|
||||
use std::{
|
||||
collections::{BTreeMap, VecDeque},
|
||||
ops::Deref,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
@ -19,6 +21,9 @@ pub enum TermSplit {
|
||||
top: Rc<TermSplit>,
|
||||
bottom: Rc<TermSplit>,
|
||||
},
|
||||
Tabs {
|
||||
tabs: OrdMap<char, Rc<TermSplit>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl TermSplit {
|
||||
@ -43,6 +48,11 @@ impl TermSplit {
|
||||
top.collect_term_frames(into);
|
||||
bottom.collect_term_frames(into);
|
||||
}
|
||||
TermSplit::Tabs { tabs } => {
|
||||
for tab in tabs.values() {
|
||||
tab.collect_term_frames(into);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,10 +115,21 @@ impl TermSplit {
|
||||
}),
|
||||
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::Tabs { tabs } => {
|
||||
match pathstr.split_first() {
|
||||
None => mod_with(self),
|
||||
Some((c, path_rest)) => {
|
||||
match tabs.get(c) {
|
||||
None => Err(format!("Reference to tab '{}', which doesn't exist, before {} in split path", c, path_rest.iter().collect::<String>())),
|
||||
Some(t) => Ok(TermSplit::Tabs { tabs: tabs.update(*c, t.modify_at_pathstr_vec(path_rest, mod_with)?.into())}),
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
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>()))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,6 +151,13 @@ impl TermSplit {
|
||||
Some(('b', path_rest)) => bottom.get_at_pathstr_vec(path_rest),
|
||||
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::Tabs { tabs } => match pathstr.split_first() {
|
||||
None => Ok(self),
|
||||
Some((c, path_rest)) => match tabs.get(c) {
|
||||
None => Err(format!("Reference to tab '{}', which doesn't exist, before {} in split path", c, path_rest.iter().collect::<String>())),
|
||||
Some(t) => t.get_at_pathstr_vec(path_rest),
|
||||
}
|
||||
},
|
||||
TermSplit::Term { .. } => match pathstr.split_first() {
|
||||
None => Ok(self),
|
||||
Some(_) => Err(format!("In split path, found trailing junk {} after addressing terminal", pathstr.iter().collect::<String>()))
|
||||
@ -162,10 +190,12 @@ impl TermSplit {
|
||||
pub fn join(&self, pathstr: &str) -> Result<TermSplit, 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())
|
||||
Err("Can only join vertical or horizontal splits (or tab with one entry), not a terminal".to_owned())
|
||||
}
|
||||
TermSplit::Horizontal { left, .. } => Ok((**left).clone()),
|
||||
TermSplit::Vertical { top, .. } => Ok((**top).clone()),
|
||||
TermSplit::Tabs { tabs } if tabs.len() != 1 => Err("Can only join tabs when only one tab left".to_owned()),
|
||||
TermSplit::Tabs { tabs } => Ok((**tabs.iter().next().unwrap().1).clone()),
|
||||
})
|
||||
}
|
||||
|
||||
@ -179,6 +209,33 @@ impl TermSplit {
|
||||
new.validate()?;
|
||||
Ok(new)
|
||||
}
|
||||
|
||||
pub fn tabbed(&self, first_tab: char, pathstr: &str) -> Result<TermSplit, String> {
|
||||
self.modify_at_pathstr(pathstr, |t| {
|
||||
Ok(TermSplit::Tabs {
|
||||
tabs: OrdMap::unit(first_tab, t.clone().into()),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_tab(
|
||||
&self,
|
||||
pathstr: &str,
|
||||
new_tab: char,
|
||||
new_frame: FrameId,
|
||||
) -> Result<TermSplit, String> {
|
||||
let new = self.modify_at_pathstr(pathstr, |t| match t {
|
||||
TermSplit::Tabs { tabs } => Ok(TermSplit::Tabs {
|
||||
tabs: tabs.update(new_tab, TermSplit::Term { frame: new_frame }.into()),
|
||||
}),
|
||||
_ => Err(format!(
|
||||
"Tried to add tab at path {}, which isn't a tab set.",
|
||||
pathstr
|
||||
)),
|
||||
})?;
|
||||
new.validate()?;
|
||||
Ok(new)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AccessibleSplitIter<'t> {
|
||||
@ -192,12 +249,15 @@ impl<'t> Iterator for AccessibleSplitIter<'t> {
|
||||
loop {
|
||||
match self.queue.pop_back() {
|
||||
Some(TermSplit::Horizontal { left, right }) => {
|
||||
self.queue.push_front(left);
|
||||
self.queue.push_front(right);
|
||||
self.queue.push_back(right);
|
||||
self.queue.push_back(left);
|
||||
}
|
||||
Some(TermSplit::Vertical { top, bottom }) => {
|
||||
self.queue.push_front(top);
|
||||
self.queue.push_front(bottom);
|
||||
self.queue.push_back(bottom);
|
||||
self.queue.push_back(top);
|
||||
}
|
||||
Some(TermSplit::Tabs { tabs }) => {
|
||||
self.queue.extend(tabs.values().rev().map(|v| v.deref()));
|
||||
}
|
||||
Some(TermSplit::Term { frame }) => break Some(frame),
|
||||
None => break None,
|
||||
@ -315,15 +375,22 @@ mod tests {
|
||||
top: Horizontal {
|
||||
left: Horizontal {
|
||||
left: Term { frame: FrameId(42) }.into(),
|
||||
right: Term { frame: FrameId(64) }.into(),
|
||||
right: Term { frame: FrameId(43) }.into(),
|
||||
}
|
||||
.into(),
|
||||
right: Term { frame: FrameId(42) }.into(),
|
||||
right: Term { frame: FrameId(44) }.into(),
|
||||
}
|
||||
.into(),
|
||||
bottom: Vertical {
|
||||
top: Term { frame: FrameId(43) }.into(),
|
||||
bottom: Term { frame: FrameId(44) }.into(),
|
||||
top: Term { frame: FrameId(45) }.into(),
|
||||
bottom: Tabs {
|
||||
tabs: vec![
|
||||
('0', Term { frame: FrameId(46) }),
|
||||
('1', Term { frame: FrameId(47) }),
|
||||
]
|
||||
.into(),
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
.into(),
|
||||
};
|
||||
@ -334,8 +401,9 @@ mod tests {
|
||||
&FrameId(42),
|
||||
&FrameId(43),
|
||||
&FrameId(44),
|
||||
&FrameId(42),
|
||||
&FrameId(64),
|
||||
&FrameId(45),
|
||||
&FrameId(46),
|
||||
&FrameId(47),
|
||||
]
|
||||
);
|
||||
}
|
||||
@ -379,4 +447,97 @@ mod tests {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn framing_works() {
|
||||
use TermSplit::*;
|
||||
let t = Vertical {
|
||||
top: Horizontal {
|
||||
left: Horizontal {
|
||||
left: Term { frame: FrameId(42) }.into(),
|
||||
right: Term { frame: FrameId(43) }.into(),
|
||||
}
|
||||
.into(),
|
||||
right: Term { frame: FrameId(44) }.into(),
|
||||
}
|
||||
.into(),
|
||||
bottom: Vertical {
|
||||
top: Term { frame: FrameId(45) }.into(),
|
||||
bottom: Term { frame: FrameId(46) }.into(),
|
||||
}
|
||||
.into(),
|
||||
};
|
||||
assert_eq!(
|
||||
t.tabbed('0', "bb").unwrap(),
|
||||
Vertical {
|
||||
top: Horizontal {
|
||||
left: Horizontal {
|
||||
left: Term { frame: FrameId(42) }.into(),
|
||||
right: Term { frame: FrameId(43) }.into(),
|
||||
}
|
||||
.into(),
|
||||
right: Term { frame: FrameId(44) }.into(),
|
||||
}
|
||||
.into(),
|
||||
bottom: Vertical {
|
||||
top: Term { frame: FrameId(45) }.into(),
|
||||
bottom: Tabs {
|
||||
tabs: vec![('0', Term { frame: FrameId(46) })].into()
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_add_tab() {
|
||||
use TermSplit::*;
|
||||
let t = Vertical {
|
||||
top: Horizontal {
|
||||
left: Horizontal {
|
||||
left: Term { frame: FrameId(42) }.into(),
|
||||
right: Term { frame: FrameId(43) }.into(),
|
||||
}
|
||||
.into(),
|
||||
right: Term { frame: FrameId(44) }.into(),
|
||||
}
|
||||
.into(),
|
||||
bottom: Vertical {
|
||||
top: Term { frame: FrameId(45) }.into(),
|
||||
bottom: Tabs {
|
||||
tabs: vec![('0', Term { frame: FrameId(46) })].into(),
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
.into(),
|
||||
};
|
||||
assert_eq!(
|
||||
t.add_tab("bb", '1', FrameId(47)).unwrap(),
|
||||
Vertical {
|
||||
top: Horizontal {
|
||||
left: Horizontal {
|
||||
left: Term { frame: FrameId(42) }.into(),
|
||||
right: Term { frame: FrameId(43) }.into(),
|
||||
}
|
||||
.into(),
|
||||
right: Term { frame: FrameId(44) }.into(),
|
||||
}
|
||||
.into(),
|
||||
bottom: Vertical {
|
||||
top: Term { frame: FrameId(45) }.into(),
|
||||
bottom: Tabs {
|
||||
tabs: vec![
|
||||
('0', Term { frame: FrameId(46) }),
|
||||
('1', Term { frame: FrameId(47) }),
|
||||
]
|
||||
.into()
|
||||
}
|
||||
.into()
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -92,3 +92,8 @@ body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.termtreewrapper {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
min-height: 1px;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user