diff --git a/Cargo.toml b/Cargo.toml index bb6f20e..2e2a68a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ piccolo = "0.3.3" unicode-segmentation = "1.11.0" unicode-width = "0.1.13" wasm-bindgen = "0.2.92" -web-sys = { version = "0.3.69", features = ["ResizeObserver"] } +web-sys = { version = "0.3.69", features = ["ResizeObserver", "DomRect", "CssStyleDeclaration"] } yew = { version = "0.21.0", features = ["csr"] } minicrossterm = { git = "https://git.blastmud.org/blasthavers/minicrossterm.git", rev = "494f89daef41162fbd89d5266e261018ed5ff6dc" } thiserror = "1.0.63" diff --git a/src/main.rs b/src/main.rs index 18e8d24..a6464ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,8 +7,10 @@ pub mod command_handler; pub mod lineengine; pub mod lua_state; pub mod parsing; +pub mod split_panel; pub mod term_view; use crate::lua_state::{install_lua_globals, LuaState}; +use crate::split_panel::*; use crate::term_view::*; #[derive(Properties)] @@ -37,9 +39,9 @@ fn app() -> Html { install_lua_globals(&global).expect("Couldn't install Lua globals"); html! { -
- - +
+ }} + second={ html! { } }/>
} } diff --git a/src/split_panel.rs b/src/split_panel.rs new file mode 100644 index 0000000..2e69f3f --- /dev/null +++ b/src/split_panel.rs @@ -0,0 +1,101 @@ +use std::rc::Rc; + +use wasm_bindgen::JsCast; +use web_sys::{Element, PointerEvent}; +use yew::{function_component, html, use_state_eq, Callback, Html, Properties, UseStateHandle}; +#[derive(Clone, Eq, PartialEq)] +pub enum PanelDirection { + Horizontal, + Vertical, +} + +#[derive(PartialEq, Properties)] +pub struct SplitPanelProps { + pub direction: PanelDirection, + pub first: Html, + pub second: Html, +} + +#[function_component(SplitPanel)] +pub fn split_panel(props: &SplitPanelProps) -> Html { + let is_dragging: Rc>> = use_state_eq(|| None).into(); + let dragged_space: Rc>> = use_state_eq(|| None).into(); + let wrapper_class = match props.direction { + PanelDirection::Horizontal => "hpanelwrapper", + PanelDirection::Vertical => "vpanelwrapper", + }; + let wrapper_class = if (**is_dragging).is_some() { + format!("{} dragging", wrapper_class) + } else { + wrapper_class.to_owned() + }; + let panel_class = match props.direction { + PanelDirection::Horizontal => "hpanel", + PanelDirection::Vertical => "vpanel", + }; + let divider_class = match props.direction { + PanelDirection::Horizontal => "hdivider", + PanelDirection::Vertical => "vdivider", + }; + let row_or_col = match props.direction { + PanelDirection::Horizontal => "columns", + PanelDirection::Vertical => "rows", + }; + + let layout = match **dragged_space { + None => format!("grid-template-{}: 1fr 4px 1fr", row_or_col), + Some(custom) => format!("grid-template-{}: {}px 4px 1fr", row_or_col, custom), + }; + let is_dragging_forup = is_dragging.clone(); + let dragged_space_formove = dragged_space.clone(); + let dir = props.direction.clone(); + match **is_dragging { + None => { + html! { +
+
{props.first.clone()}
+
{}, + Some(t) => { + let el: Element = JsCast::unchecked_into::( + JsCast::unchecked_into::(t) + .parent_node().expect("Parent exists")); + let top = + match dir { + PanelDirection::Horizontal => el.get_bounding_client_rect().x(), + PanelDirection::Vertical => el.get_bounding_client_rect().y() + }; + is_dragging.set(Some(top)); + } + } + })} + /> +
{props.second.clone()}
+
+ } + } + Some(start) => { + html! { +
e.client_x() as f64, + PanelDirection::Vertical => e.client_y() as f64 + } - start - 2.0; + (*dragged_space_formove).set(Some(distance)); + })} + > +
{props.first.clone()}
+
+
{props.second.clone()}
+
+ } + } + } +} diff --git a/styles.css b/styles.css index 5bd5bfd..7c066e7 100644 --- a/styles.css +++ b/styles.css @@ -10,24 +10,52 @@ body { position: absolute; top: 0; left: 0; right: 0; bottom: 0; } -.vpane { - display: flex; - flex-direction: column; - justify-content: stretch; +.vpanelwrapper { + display: grid; + grid-template-rows: 1fr 4px 1fr; + width: 100%; + height: 100%; } -.hpane { - display: flex; - flex-direction: row; - justify-content: stretch; +.hpanelwrapper { + display: grid; + grid-template-columns: 1fr 4px 1fr; + width: 100%; + height: 100%; +} +.dragging.vpanelwrapper { + cursor: row-resize; + user-select: none; +} +.dragging.hpanelwrapper { + cursor: col-resize; + user-select: none; +} +.vdivider { + min-height: 4px; + background: grey; + cursor: row-resize; +} +.hdivider { + min-height: 4px; + background: grey; + cursor: col-resize; +} +.vpanel { + min-width: 0px; + min-height: 0px; +} +.hpanel { + min-width: 0px; + min-height: 0px; } .toplevel { position: absolute; top: 0; left: 0; right: 0; bottom: 0; } .hterminal { - flex: 1 1 0; min-width: 10px; min-height: 10px; border: solid grey 1px; padding: 2px; + height: 100%; }