From 6aa2f73a956cc5b6f00a5c3e4d62dc0ca4f3e67b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Loi=CC=88c=20Correnson?= <loic.correnson@cea.fr>
Date: Fri, 10 Nov 2023 13:32:06 +0100
Subject: [PATCH] [dome/richtext] gutter clicks

---
 ivette/src/dome/renderer/text/richtext.tsx | 29 +++++++++++++++++++---
 ivette/src/sandbox/text.tsx                | 12 +++++++--
 2 files changed, 36 insertions(+), 5 deletions(-)

diff --git a/ivette/src/dome/renderer/text/richtext.tsx b/ivette/src/dome/renderer/text/richtext.tsx
index a78353fff81..22405ddb87c 100644
--- a/ivette/src/dome/renderer/text/richtext.tsx
+++ b/ivette/src/dome/renderer/text/richtext.tsx
@@ -479,12 +479,26 @@ const OnClick = new MouseCallbackField();
 const OnPopup = new MouseCallbackField();
 const OnHover = new MouseCallbackField();
 const OnDouble = new MouseCallbackField();
+const OnGutter = new MouseCallbackField();
+
+const gutterEventHandlers = {
+  click: (view: CM.EditorView, block: CM.BlockInfo, evt: Event) => {
+    const fn = view.state.field(OnGutter.field);
+    if (fn) {
+      const offset = block.from;
+      const line = view.state.doc.lineAt(offset).number;
+      fn({ offset, line }, evt as MouseEvent);
+    }
+    return false;
+  }
+};
 
 const MouseEvents : CS.Extension = [
   OnClick,
   OnHover,
   OnPopup,
   OnDouble,
+  OnGutter,
   CM.EditorView.domEventHandlers({
     click: (evt: MouseEvent, view: CM.EditorView) => {
       OnClick.callback(evt, view);
@@ -618,7 +632,7 @@ class GutterMark extends CM.GutterMarker {
     const {  gutter, className, title } = this.spec;
     const textNode = document.createTextNode(gutter);
     if (!className && !title) return textNode;
-    const span = document.createElement("div");
+    const span = document.createElement("span");
     span.appendChild(textNode);
     if (className) span.className = className;
     if (title) span.title = title;
@@ -759,14 +773,20 @@ function dispatchDecorations(view: View, spec: Decorations): void {
 const Decorations: CS.Extension = [
   DecoratorState,
   CM.EditorView.decorations.from(DecoratorState, ({ ranges }) => ranges),
-  CM.gutter({ markers: (view) => view.state.field(DecoratorState).gutters, }),
+  OnGutter,
+  CM.gutter({
+    markers: (view) => view.state.field(DecoratorState).gutters,
+    domEventHandlers: gutterEventHandlers,
+  }),
 ];
 
 /* -------------------------------------------------------------------------- */
 /* --- Line Numbers                                                       --- */
 /* -------------------------------------------------------------------------- */
 
-const LineNumbers = new Option(CM.lineNumbers());
+const LineNumbers = new Option(
+  CM.lineNumbers({ domEventHandlers: gutterEventHandlers })
+);
 
 /* -------------------------------------------------------------------------- */
 /* --- Active Line                                                        --- */
@@ -810,6 +830,7 @@ export interface TextViewProps {
   onClick?: MouseCallback;
   onPopup?: MouseCallback;
   onHover?: MouseCallback;
+  onGutter?: MouseCallback;
   onDoubleClick?: MouseCallback;
   decorations?: Decorations;
   lineNumbers?: boolean;
@@ -838,6 +859,7 @@ export function TextView(props: TextViewProps) : JSX.Element {
     onClick = null,
     onPopup = null,
     onHover = null,
+    onGutter = null,
     onChange = null,
     readOnly = false,
     onViewport: onReview = null,
@@ -850,6 +872,7 @@ export function TextView(props: TextViewProps) : JSX.Element {
   React.useEffect(() => OnPopup.dispatch(view, onPopup), [view, onPopup]);
   React.useEffect(() => OnHover.dispatch(view, onHover), [view, onHover]);
   React.useEffect(() => OnDouble.dispatch(view, onDouble), [view, onDouble]);
+  React.useEffect(() => OnGutter.dispatch(view, onGutter), [view, onGutter]);
   React.useEffect(() => OnChange.dispatch(view, onChange), [view, onChange]);
   React.useEffect(() => OnSelect.dispatch(view, onSelect), [view, onSelect]);
   React.useEffect(() => Viewport.dispatch(view, onReview), [view, onReview]);
diff --git a/ivette/src/sandbox/text.tsx b/ivette/src/sandbox/text.tsx
index 6b588b943c3..e1e9bf2c6ae 100644
--- a/ivette/src/sandbox/text.tsx
+++ b/ivette/src/sandbox/text.tsx
@@ -102,7 +102,7 @@ function UseText(): JSX.Element {
     setDecorations([...decorations, {
       line: s.fromLine,
       className: 'line-decoration',
-      title: 'Line Decorated'
+      title: 'Line Decorated',
     }]);
   }, [decorations, s]);
 
@@ -111,6 +111,7 @@ function UseText(): JSX.Element {
     setDecorations([...decorations, {
       line: s.fromLine,
       gutter: '*',
+      title: 'Gutter Mark',
     }]);
   }, [decorations, s]);
 
@@ -122,7 +123,7 @@ function UseText(): JSX.Element {
   }, [decorations, h]);
 
   const clearEvent = React.useCallback(() => setEvent(''), []);
-  const triggerCancelEvent = Dome.useDebounced(clearEvent, 1000);
+  const triggerCancelEvent = Dome.useDebounced(clearEvent, 1200);
 
   const onClick = React.useCallback(
     (pos: Position | null, evt: MouseEvent) => {
@@ -147,6 +148,12 @@ function UseText(): JSX.Element {
       triggerCancelEvent();
     }, [triggerCancelEvent]);
 
+  const onGutter = React.useCallback(
+    (pos: Position | null) => {
+      setEvent(`Gutter-Click L${pos ? pos.line : 'null'}`);
+      triggerCancelEvent();
+    }, [triggerCancelEvent]);
+
   return (
     <>
       <ToolBar>
@@ -213,6 +220,7 @@ function UseText(): JSX.Element {
         onHover={onHover}
         onClick={onClick}
         onPopup={onPopup}
+        onGutter={onGutter}
         onDoubleClick={onDoubleClick}
         onViewport={onViewport}
         decorations={allDecorations}
-- 
GitLab