Context: for a custom drag / drop, we have the following problem:
on mouse down:
register a handler
on mouse up/leave:
unregister everything
however, for unregister everything, we need access to the “listeners” to remove; so we have this weird loop ; and because these are primitive values, not functions, we can’t use let rec here.
Thus, we have this ugly ref. Is there a way to get rid of this ?
let onMouseDown (_x : React.Event.Mouse.t) =
let remove_all_ref = ref @@ fun _e -> () in
let remove_all _e = !remove_all_ref _e in
let mouse_move_listener =
Brr.Ev.listen Brr.Ev.mousemove
(fun _e -> Brr.Console.log [ "mousemove|" ])
(Brr.Document.as_target Brr.G.document)
in
let mouse_leave_listener = Brr.Ev.listen Brr.Ev.mouseleave remove_all (Brr.Document.as_target Brr.G.document) in
let mouse_up_listener = Brr.Ev.listen Brr.Ev.mouseup remove_all (Brr.Document.as_target Brr.G.document) in
remove_all_ref :=
fun _e ->
Brr.Ev.unlisten mouse_move_listener;
Brr.Ev.unlisten mouse_leave_listener;
Brr.Ev.unlisten mouse_up_listener;
Brr.Console.log [ "unregistered all" ]
The use of a reference or some analogous structure (eg lazy) seems unavoidable when dealing with cyclic data as in your example. But I think the code can be made a bit more readable, eg:
let onMouseDown (_x : React.Event.Mouse.t) =
let to_be_removed : Brr.Ev.listener list ref = ref [] in
let remove_all _ = List.iter Brr.Ev.unlisten !to_be_removed in
let add listener = to_be_removed := listener :: !to_be_removed in
add (Brr.Ev.listen Brr.Ev.mousemove (fun _e -> Brr.Console.log [ "mousemove|" ]) (Brr.Document.as_target Brr.G.document));
add (Brr.Ev.listen Brr.Ev.mouseleave remove_all (Brr.Document.as_target Brr.G.document));
add (Brr.Ev.listen Brr.Ev.mouseup remove_all (Brr.Document.as_target Brr.G.document))
I’m not convinced it is necessary, as everything is “one use” . Here is the XY problem:
suppose we have a thin title bar and we want to (manually) implement drag & drop; what do we do ? Call the initial title-bar x.
The initial approach is to register mouse-down, mouse-up, mouse-move handlers on x. The problem is that if we move your mouse too fast, we might get outside x, then the drag/drop stops.
So instead re only register “mouse-down” on x – and in that handler, we globally register mouse-move and mouse-up on the global window object. Then, of course, we need to clean everything up on mouse-up@global_wnidow.
In this situation, every time when we click mouse-down, we are generating a new ref – and as a result, don’t need to reset it at the end.