Implement control and display of tab splits.
This commit is contained in:
parent
0da685d4f5
commit
6dd6cd1f71
@ -208,9 +208,11 @@ pub fn install_lua_globals(
|
||||
}
|
||||
register_stateless_command!(mud_trigger, "act");
|
||||
register_stateless_command!(mud_trigger, "action");
|
||||
register_command!(add_tab);
|
||||
register_stateless_command!(alias);
|
||||
register_stateless_command!(unalias);
|
||||
register_command!(close_mud);
|
||||
register_command!(close_tab);
|
||||
register_command!(command);
|
||||
register_command!(connect_mud);
|
||||
register_stateless_command!(create_match_table);
|
||||
@ -234,6 +236,7 @@ pub fn install_lua_globals(
|
||||
register_command!(sendmud_raw);
|
||||
register_stateless_command!(storage);
|
||||
register_command!(tick);
|
||||
register_command!(tsplit);
|
||||
register_stateless_command!(mud_trigger, "untrigger");
|
||||
register_stateless_command!(mud_untrigger, "unact");
|
||||
register_stateless_command!(mud_untrigger, "unaction");
|
||||
|
@ -265,6 +265,99 @@ pub fn hsplit<'gc>(
|
||||
})
|
||||
}
|
||||
|
||||
pub fn tsplit<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_layout = global_layout.clone();
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let tabid: String = stack.from_front(ctx)?;
|
||||
let path: String = stack.from_front(ctx)?;
|
||||
if tabid.len() != 1 {
|
||||
Err(anyhow::Error::msg(
|
||||
"Tab identifier should be a single character".to_owned(),
|
||||
))?;
|
||||
}
|
||||
let tabid = tabid.chars().next().unwrap();
|
||||
let new_splits = global_memo
|
||||
.layout
|
||||
.borrow()
|
||||
.term_splits
|
||||
.tabbed(tabid, &path)
|
||||
.map_err(|e| e.into_value(ctx))?;
|
||||
let new_layout = Rc::new(GlobalLayoutState {
|
||||
term_splits: new_splits.clone(),
|
||||
..(*(global_memo.layout.borrow().as_ref())).clone()
|
||||
});
|
||||
global_layout.set(new_layout.clone());
|
||||
*(global_memo.layout.borrow_mut()) = new_layout;
|
||||
Ok(CallbackReturn::Return)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_tab<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_layout = global_layout.clone();
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let path: String = stack.from_front(ctx)?;
|
||||
let tabid: String = stack.from_front(ctx)?;
|
||||
if tabid.len() != 1 {
|
||||
Err(anyhow::Error::msg(
|
||||
"Tab identifier should be a single character".to_owned(),
|
||||
))?;
|
||||
}
|
||||
let tabid = tabid.chars().next().unwrap();
|
||||
let frame: FrameId = FrameId(stack.from_front(ctx)?);
|
||||
let new_splits = global_memo
|
||||
.layout
|
||||
.borrow()
|
||||
.term_splits
|
||||
.add_tab(&path, tabid, frame.clone())
|
||||
.map_err(|e| e.into_value(ctx))?;
|
||||
let new_layout = Rc::new(GlobalLayoutState {
|
||||
term_splits: new_splits.clone(),
|
||||
..(*(global_memo.layout.borrow().as_ref())).clone()
|
||||
});
|
||||
global_layout.set(new_layout.clone());
|
||||
*(global_memo.layout.borrow_mut()) = new_layout;
|
||||
Ok(CallbackReturn::Call {
|
||||
function: Function::Callback(ensure_frame_instance(ctx, &frame)),
|
||||
then: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn close_tab<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_layout = global_layout.clone();
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let path: String = stack.from_front(ctx)?;
|
||||
let new_splits = global_memo
|
||||
.layout
|
||||
.borrow()
|
||||
.term_splits
|
||||
.close_tab(&path)
|
||||
.map_err(|e| e.into_value(ctx))?;
|
||||
let new_layout = Rc::new(GlobalLayoutState {
|
||||
term_splits: new_splits.clone(),
|
||||
..(*(global_memo.layout.borrow().as_ref())).clone()
|
||||
});
|
||||
global_layout.set(new_layout.clone());
|
||||
*(global_memo.layout.borrow_mut()) = new_layout;
|
||||
Ok(CallbackReturn::Return)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn panel_merge<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
|
@ -7,11 +7,14 @@ pub struct TabPanelProps {
|
||||
|
||||
#[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'));
|
||||
let active_tab = use_state_eq(|| props.tabs.first().map(|(k, _)| *k).unwrap_or('0'));
|
||||
html! {
|
||||
<div class="container-fluid d-flex flex-column">
|
||||
<div class="d-flex flex-column h-100">
|
||||
<ul class="nav nav-tabs">
|
||||
{props.tabs.iter().map(|(c, _)| {
|
||||
if !props.tabs.is_empty() && !props.tabs.iter().any(|t| t.0 == *active_tab) {
|
||||
active_tab.set(props.tabs.first().map(|v| v.0).unwrap_or('0'));
|
||||
}
|
||||
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();
|
||||
@ -26,7 +29,7 @@ pub fn tab_panel_props(props: &TabPanelProps) -> Html {
|
||||
}
|
||||
</ul>
|
||||
{props.tabs.iter().map(|(c, v)| {
|
||||
let class_name = if *c == *active_tab { "container-fluid visible"} else { "container-fluid invisible"};
|
||||
let class_name = if *c == *active_tab { "termtreewrapper"} else { "d-none"};
|
||||
html! {
|
||||
<div class={class_name} key={format!("content-{}", c)}>
|
||||
{v.clone()}
|
||||
|
@ -236,6 +236,36 @@ impl TermSplit {
|
||||
new.validate()?;
|
||||
Ok(new)
|
||||
}
|
||||
|
||||
pub fn close_tab(&self, pathstr: &str) -> Result<TermSplit, String> {
|
||||
if pathstr.is_empty() {
|
||||
return Err("Closing tab is invalid with empty path".to_owned());
|
||||
}
|
||||
let (head, last) = pathstr.split_at(pathstr.len() - 1);
|
||||
let last: char = last.chars().next().unwrap();
|
||||
|
||||
let new = self.modify_at_pathstr(head, |t| match t {
|
||||
TermSplit::Tabs { tabs } => {
|
||||
let tabs_with_close = tabs.without(&last);
|
||||
if tabs_with_close.is_empty() {
|
||||
Err(format!(
|
||||
"Tried to close tab {} at path {}, but that would leave the tab set empty.",
|
||||
last, head
|
||||
))
|
||||
} else {
|
||||
Ok(TermSplit::Tabs {
|
||||
tabs: tabs_with_close,
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => Err(format!(
|
||||
"Tried to close tab at path {}, which isn't a tab set.",
|
||||
pathstr
|
||||
)),
|
||||
})?;
|
||||
new.validate()?;
|
||||
Ok(new)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AccessibleSplitIter<'t> {
|
||||
@ -540,4 +570,54 @@ mod tests {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_close_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) }),
|
||||
('1', Term { frame: FrameId(47) }),
|
||||
]
|
||||
.into(),
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
.into(),
|
||||
};
|
||||
assert_eq!(
|
||||
t.close_tab("bb1").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(),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user