From 19c8d0e71d41c02ac61b29cf159dc42c02a91190 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Loi=CC=88c=20Correnson?= <loic.correnson@cea.fr>
Date: Wed, 5 Jan 2022 10:02:32 +0100
Subject: [PATCH] [dome/qsplit] x-y-dragging

---
 ivette/src/dome/renderer/layout/qsplit.tsx | 120 ++++++++++++++++++---
 ivette/src/dome/renderer/layout/style.css  |  12 +++
 2 files changed, 116 insertions(+), 16 deletions(-)

diff --git a/ivette/src/dome/renderer/layout/qsplit.tsx b/ivette/src/dome/renderer/layout/qsplit.tsx
index 6577bbdb8e2..d05aa185cf1 100644
--- a/ivette/src/dome/renderer/layout/qsplit.tsx
+++ b/ivette/src/dome/renderer/layout/qsplit.tsx
@@ -63,7 +63,7 @@ type Dragging = undefined | DragPos;
 
 const getDragPosition = (d: DragPos) => d.position + d.offset - d.anchor;
 
-interface QSplitterProps {
+interface BSplitterProps {
   hsplit: boolean;
   style: React.CSSProperties;
   dragging: Dragging;
@@ -72,15 +72,17 @@ interface QSplitterProps {
   resetPosition: () => void;
 }
 
+const HPOS = 'dome-xSplitter-hpos-R';
+const VPOS = 'dome-xSplitter-vpos-R';
+const HVPOS = 'dome-xSplitter-hvpos';
 const HANDLE = '.dome-xSplitter-grab';
 const HGRAB = 'dome-xSplitter-grab dome-xSplitter-hgrab';
 const VGRAB = 'dome-xSplitter-grab dome-xSplitter-vgrab';
-const HPOSR = 'dome-xSplitter-hline dome-xSplitter-hpos-R';
-const VPOSR = 'dome-xSplitter-vline dome-xSplitter-vpos-R';
+const HVGRAB = 'dome-xSplitter-grab dome-xSplitter-hvgrab';
 const DRAGGING = 'dome-color-dragging';
 const DRAGZONE = 'dome-color-dragzone';
 
-function QSplitter(props: QSplitterProps) {
+function BSplitter(props: BSplitterProps) {
   const { hsplit, style, dragging } = props;
 
   const onStart: DraggableEventHandler =
@@ -113,7 +115,7 @@ function QSplitter(props: QSplitterProps) {
     dragging ? DRAGGING : DRAGZONE,
   );
 
-  const css = hsplit ? HPOSR : VPOSR;
+  const css = hsplit ? HPOS : VPOS;
 
   return (
     <DraggableCore
@@ -132,6 +134,70 @@ function QSplitter(props: QSplitterProps) {
   );
 }
 
+/* -------------------------------------------------------------------------- */
+/* --- Split Node                                                         --- */
+/* -------------------------------------------------------------------------- */
+
+interface CSplitterProps {
+  style: React.CSSProperties;
+  dragX: Dragging;
+  dragY: Dragging;
+  setDragX: (dx: Dragging) => void;
+  setDragY: (dy: Dragging) => void;
+  resetPosition: () => void;
+  setPosition: (X: number, Y: number) => void;
+}
+
+function CSplitter(props: CSplitterProps) {
+  const { style, dragX, dragY } = props;
+
+  const onStart: DraggableEventHandler =
+    (_evt, data) => {
+      const startX = data.node.offsetLeft;
+      const startY = data.node.offsetTop;
+      const anchorX = data.x;
+      const anchorY = data.y;
+      props.setDragX({ position: startX, offset: anchorX, anchor: anchorX });
+      props.setDragY({ position: startY, offset: anchorY, anchor: anchorY });
+    };
+
+  const onDrag: DraggableEventHandler =
+    (_evt, data) => {
+      if (dragX) props.setDragX({ ...dragX, offset: data.x });
+      if (dragY) props.setDragY({ ...dragY, offset: data.y });
+    };
+
+  const onStop: DraggableEventHandler =
+    (evt, _data) => {
+      if (evt.metaKey || evt.altKey || evt.ctrlKey) {
+        props.resetPosition();
+      } else if (dragX && dragY) {
+        const X = getDragPosition(dragX);
+        const Y = getDragPosition(dragY);
+        props.setPosition(X, Y);
+      }
+      props.setDragX(undefined);
+      props.setDragY(undefined);
+    };
+
+  const dragging = dragX !== undefined && dragY !== undefined;
+  const dragger = Utils.classes(HVGRAB, dragging ? DRAGGING : DRAGZONE);
+  return (
+    <DraggableCore
+      handle={HANDLE}
+      onStart={onStart}
+      onDrag={onDrag}
+      onStop={onStop}
+    >
+      <div
+        className={HVPOS}
+        style={style}
+      >
+        <div className={dragger} />
+      </div>
+    </DraggableCore>
+  );
+}
 
 /* -------------------------------------------------------------------------- */
 /* --- Q-Split Engine                                                     --- */
@@ -179,6 +245,7 @@ function QSplitEngine(props: QSplitEngineProps) {
   const layout: QSplitLayout = new Map();
   let hsplit: React.CSSProperties = NODISPLAY;
   let vsplit: React.CSSProperties = NODISPLAY;
+  let hvsplit: React.CSSProperties = NODISPLAY;
   const { A, B, C, D, H = 0.5, V = 0.5, size, setPosition } = props;
   const { width, height } = size;
   const setX = React.useCallback((X: number) => {
@@ -187,21 +254,27 @@ function QSplitEngine(props: QSplitEngineProps) {
   const setY = React.useCallback((Y: number) => {
     if (setPosition) setPosition(H, getRatio(Y, height));
   }, [setPosition, height, H]);
+  const setXY = React.useCallback((X: number, Y: number) => {
+    if (setPosition) setPosition(getRatio(X, width), getRatio(Y, height));
+  }, [setPosition, width, height]);
   const resetX = React.useCallback(() => {
     if (setPosition) setPosition(0.5, V);
   }, [setPosition, V]);
   const resetY = React.useCallback(() => {
     if (setPosition) setPosition(H, 0.5);
   }, [setPosition, H]);
+  const resetXY = React.useCallback(() => {
+    if (setPosition) setPosition(0.5, 0.5);
+  }, [setPosition]);
   const X = getPosition(dragX, width, H);
   const Y = getPosition(dragY, height, V);
   const RX = width - X - 1;
   const RY = height - Y - 1;
   const FULL = A !== undefined && A === D;
-  const AB = A !== undefined && A === B;
-  const AC = A !== undefined && A === C;
-  const BD = B !== undefined && B === D;
-  const CD = C !== undefined && C === D;
+  const AB = A !== undefined && (A === B);
+  const AC = A !== undefined && (A === C);
+  const BD = B !== undefined && (B === D);
+  const CD = C !== undefined && (C === D);
   //----------------------------------------
   // [ A ]
   //---------------------------------------
@@ -209,7 +282,7 @@ function QSplitEngine(props: QSplitEngineProps) {
     DISPLAY(layout, A, 0, width, 0, height);
   }
   //----------------------------------------
-  // [ A - CD ]
+  // [ A - C ]
   //---------------------------------------
   else if (AB && CD) {
     vsplit = VSPLIT(0, Y, width);
@@ -276,26 +349,41 @@ function QSplitEngine(props: QSplitEngineProps) {
     DISPLAY(layout, D, X + 1, RX, Y + 1, RY);
   }
   //----------------------------------------
+  if (hsplit !== NODISPLAY && vsplit !== NODISPLAY)
+    hvsplit = { display: 'block', left: X, top: Y };
+  //----------------------------------------
   // Rendering
   //----------------------------------------
   return (
     <QSplitContext.Provider value={layout}>
-      <QSplitter
-        key='SPLIT-H'
+      <BSplitter
+        key='HSPLIT'
         hsplit={true}
         dragging={dragX}
         setDragging={setDragX}
         setPosition={setX}
         resetPosition={resetX}
-        style={hsplit} />
-      <QSplitter
-        key='SPLIT-V'
+        style={hsplit}
+      />
+      <BSplitter
+        key='VSPLIT'
         hsplit={false}
         dragging={dragY}
         setDragging={setDragY}
         setPosition={setY}
         resetPosition={resetY}
-        style={vsplit} />
+        style={vsplit}
+      />
+      <CSplitter
+        key='HVSPLIT'
+        dragX={dragX}
+        dragY={dragY}
+        setDragX={setDragX}
+        setDragY={setDragY}
+        setPosition={setXY}
+        resetPosition={resetXY}
+        style={hvsplit}
+      />
       {props.children}
     </QSplitContext.Provider>
   );
diff --git a/ivette/src/dome/renderer/layout/style.css b/ivette/src/dome/renderer/layout/style.css
index aa5b34cf3a8..a2e0fca13db 100644
--- a/ivette/src/dome/renderer/layout/style.css
+++ b/ivette/src/dome/renderer/layout/style.css
@@ -177,6 +177,17 @@
     cursor: row-resize ;
 }
 
+.dome-xSplitter-hvgrab {
+    position: relative;
+    z-index: 2 ;
+    top: -4px ;
+    left: -4px ;
+    height: 9px;
+    width: 9px;
+    border-radius: 4px;
+    cursor: move;
+}
+
 .dome-xSplitter-hpos-A { position: absolute; left: 0px; height: 100% }
 .dome-xSplitter-hpos-R { position: absolute; width: 1px; height: 100% }
 .dome-xSplitter-hpos-B { position: absolute; right: 0px; height: 100% }
@@ -184,6 +195,7 @@
 .dome-xSplitter-vpos-A { position: absolute; top: 0px; width: 100% }
 .dome-xSplitter-vpos-R { position: absolute; height: 1px; width: 100% }
 .dome-xSplitter-vpos-B { position: absolute; bottom: 0px; width: 100% }
+.dome-xSplitter-hvpos { position: absolute; width: 1px; height: 1px }
 
 .dome-xSplitter-hline,
 .dome-xSplitter-vline
-- 
GitLab