I agree re mouse event availability
interesting related (?) problem to this, not sure if it's just my outside-the-effects-system implementation, but when you implement a click-and-drag, you can "lose" the drag if you move too fast
because the event is not being passed to the element, because now the mouse is outside the bounds of the element
so: click, drag right slowly, unclick => all good click, drag right quickly, (once the mouse leaves the object bounds it stops being dragged), unclick => you also lose the 'mouseup' event after
I'd be curious to know whether somehow the component effect system prevents this (I'm just swap!ing an atom to change the state), my intuition is that it wouldn,t because mouse move events must be debounced somewhere in the browser stack, so you can always move the mouse fast enough that the diff between the positions of two consecutive mousemove events is greater than the distance to the edge of the object, so it's not that 'we're not redrawing fast enough/between events', but I'm not certain
The root cause is that things like drag and drop need more context.
They should get support from a top level component.
There's some initial work on drag and drop https://github.com/phronmophobic/membrane/blob/master/src/membrane/alpha/component/drag_and_drop.cljc, but the key idea is that if you want to response to global mouse events, then the handler should be implemented by the top level component.
Having a top level component that supports drag and drop can be done through functional bubbling.
Basically, you have a child component emit a [:drag-start ...] event which the top level catches and handles. The top level component can then capture all mouse-move events and turn then into drag events until the dragging is complete (you almost never want mouse-move handlers from other elements to fire while dragging is happening).
having a top level component doesn't preclude that though, could imagine eg. highlight to drop onto, which is logical and a top level handler could as you say support
it's a nice solution, does complicate/make it more involved to encapsulate those behaviours though
I've started using it a few places and it seems pretty nice. I would like to have it as a builtin option so drag and drop can work consistently across apps (it would still be able to be enabled/disabled).
another thing which is currently happening is that when I have eg. one rectangle, my drag-release moves it down and right. Now, if I click to the above-left of that element, the drag still works. I presume that this is something to do with bounds checking and maybe origin calculation
I think it's because the "on" is outside (contains) the "translate" element, if the "on" was inside the "translate" I'm guessing it would work as expected
that sounds right
commuting them fixed the bounds issue but created a weird broken drag experience (very slow, flipping backwards and forwards a bit), maybe some weird thing in my code
good chilled saturday investigation lol
in the original form you have the bounds issue but the drag itself is smooth, very strange that flipping them does this
was my code, you can't use the same accumulation of changes in local position if the translate is outside, I forgot that the code worked on local-pos which will be different and so I shouldn't expect it to commute
haven't been doing it solidly lol
Ah, right. When I was doing drag and drop, I would typically use deltas (ie. drag distance was (dx, dy) rather than drag position is (x,y)).
Then it doesn't really matter where the origin is.
yeah so the original code recorded the first click and kept swapping in the delta from that, like (assoc (delta local-pos original-click)) but the new code (on the other side of translate) is like (update #(+ %1 (delta local-pos original-click ))), because the local position is shifting, need to accumulate changes not just bash in the delta relative to the first click
interestingly, I'm not losing my events now when the mouse is slightly outside the bounds of the rect, maybe because it's 'catching up' before the next mouse-move is emitted
is your new mouse-mouse-down to the original the same as mouse-move-global to the original?
No, the change ended up being more subtle. regular mouse-down events work just like they used to. They check the bounds of the child element and only return values for clicks inside their bounds. There's now a new mouse-down-raw handler which can return values for any mouse-event it receives, but most of the time, it's similar since parent components probably still filter out events outside their bounds.
Overall, I don't think mouse-move-global is the right solution, but it's still available for backwards compatibility. I think a better solution is to use top level components+bubbling for the cases where mouse-move-global was previously used.
I see, I only just remembered it existed actually, just looking at the implementation - the relationship with has-mouse-move-global is only created in the component namespace, in terms of base protocols in membrane.ui they're not forced to be related right? Basically it will propagate down to every element by default, instead of shortcutting on a bounds check? because everything delegates to -default-mouse-move-global with that behaviour
Yes, for mouse-move-global. It logically gets called on every element. membrane.component does some caching to speed things up, but if you'r not using membrane.component, then the mouse-move-global event is called on every element.