ID:2958858
 
Applies to:Dream Seeker
Status: Open

Issue hasn't been assigned a status value.
Currently, the way that mouse drag and drop works is a little unintuitive. I've come to the conclusion that either there is a missing parameter, or an existing parameter is implemented wrong. Without aftermarket state tracking per-element (messy) or globally (brittle), it's not possible to implement a simple button state tracker that only calls state update hooks when the state of a mouse interactible is actually changed.

hud
parent_type = /obj
hud/component
parent_type = /obj
hud/widget
parent_type = /hud/component

client
var/mouse_mode = null

hud/widget/button
dir = NORTH
proc
Hovered()
Unhovered()
Pressed()
Released()
Dropped()

MouseDown(atom/location,control,params)
var/list/p = params2list(params)
if(!p["drag"])
usr.client.mouse_mode = "press"
Unhovered()
if(p["button"]=="left")
Pressed()
dir = SOUTH

MouseUp(atom/location,control,params)
var/list/p = params2list(params)
if(!p["drag"])
if(usr.client.mouse_mode=="press")
if(p["button"]=="left")
Released()
Hovered()
dir = EAST
else
Hovered()
dir = EAST
usr.client.mouse_mode = null

MouseDrop(over_object,src_location,over_location,src_control,over_control,params)
var/list/p = params2list(params)
if(p["button"]=="left" && !p["drag"])
if(over_object==src)
Released()
Hovered()
dir = EAST
else
Dropped()
dir = NORTH
usr.client.mouse_mode = null

MouseDrag(over_object,src_location,over_location,src_control,over_control,params)
if(usr.client.mouse_mode!="drag")
usr.client.mouse_mode = "drag"

MouseEntered(atom/location,control,params)
Hovered()
dir = EAST

MouseExited(atom/location,control,params)
Unhovered()
dir = NORTH


This is an example of a very minimal button widget. I do not, as this time, see a way to implement this feature without tracking the state of the mouse on the client. This is fairly minor, of course, but I don't feel it should be necessary to track the mouse state. The existing list of parameters contains what we need already to do this state differentiation.

The reason we need to keep track of client.mouse_mode is because MouseUp() gets called first on the element with no params flagging us the MouseUp() will be followed by a MouseDrop() or MouseEntered().

There are four possible scenarios here in three groups:

Press group:
A) this.MouseDown() -> this.MouseUp()

Drag-and-drop group:
B) this.MouseDown() -> this.MouseDrag() -> this.MouseUp() -> this.MouseDrop(over_object=this)
C) other.MouseDown() -> other.MouseDrag() -> this.MouseUp() -> other.MouseDrop(over_object=this) -> this.MouseEntered()

Drag group:
D) either.MouseDown() -> either.MouseDrag() -> this.MouseUp(drag=!button)

Cases B and C share the same MouseUp() params as case A. Case D is the only one of the four cases that can be differentiate from one another without external variables. I do not feel it is important to differentiate cases B and C from each other. It is, however, very important to differentiate cases A, B/C, and D from one another.

Solution 1: Supply drag parameter to MouseUp() in cases B and C:



If the drag parameter was supplied to MouseUp() any time the MouseUp was following a drag and drop action, instead of just part of a drag action, our above code would look like this:

    MouseDown(atom/location,control,params)
var/list/p = params2list(params)
if(!p["drag"])
Unhovered()
if(p["button"]=="left")
Pressed()
dir = SOUTH

MouseUp(atom/location,control,params)
var/list/p = params2list(params)
if(p["button"]=="left" && !p["drag"])
Released()
Hovered()
dir = EAST

MouseDrop(over_object,src_location,over_location,src_control,over_control,params)
var/list/p = params2list(params)
if(p["button"]=="left")
if(over_object==src)
Released()
Hovered()
dir = EAST
else
Dropped()
dir = NORTH

MouseEntered(atom/location,control,params)
Hovered()
dir = EAST

MouseExited(atom/location,control,params)
Unhovered()
dir = NORTH


Press group:
A) this.MouseDown() -> this.MouseUp()

Drag-and-drop group:
B) this.MouseDown() -> this.MouseDrag() -> this.MouseUp(drag=button) -> this.MouseDrop(over_object=this)
C) other.MouseDown() -> other.MouseDrag() -> this.MouseUp(drag=button) -> other.MouseDrop(over_object=this) -> this.MouseEntered()

Drag group:
D) either.MouseDown() -> either.MouseDrag() -> this.MouseUp(drag=!button)

Solution 2: Create a drop parameter, supplied to any mouse actions related to a MouseDrop()



This solution preserves legacy behavior, but I'm not sure it's the one I'd root for. It's my argument that the drag parameter is just too conservatively supplied by the engine to be fully useful. The above solution is elegeant. For completeness, let's look at what the code would be with the drop parameter added.

    MouseDown(atom/location,control,params)
var/list/p = params2list(params)
if(!p["drag"])
Unhovered()
if(p["button"]=="left")
Pressed()
dir = SOUTH

MouseUp(atom/location,control,params)
var/list/p = params2list(params)
if(p["button"]=="left" && !p["drag"] && !p["drop"])
Released()
Hovered()
dir = EAST

MouseDrop(over_object,src_location,over_location,src_control,over_control,params)
var/list/p = params2list(params)
if(p["button"]=="left")
if(over_object==src)
Released()
Hovered()
dir = EAST
else
Dropped()
dir = NORTH

MouseEntered(atom/location,control,params)
Hovered()
dir = EAST

MouseExited(atom/location,control,params)
Unhovered()
dir = NORTH


Press group:
A) this.MouseDown() -> this.MouseUp()

Drag-and-drop group:
B) this.MouseDown() -> this.MouseDrag() -> this.MouseUp(drop=button) -> this.MouseDrop(over_object=this)
C) other.MouseDown() -> other.MouseDrag() -> this.MouseUp(drop=button) -> other.MouseDrop(over_object=this) -> this.MouseEntered()

Drag group:
D) either.MouseDown() -> either.MouseDrag() -> this.MouseUp(drag=!button)

In either approach, I feel like the drag/drop parameter should just be the name of the button that initiated the drag/drop event. This at least allows us to differentiate between groups A, B/C, and D. Again, I'm not sure there's actually value in differentiating between B/C.

The only real difference between supplying a "drop" parameter vs just integrating the "drag" parameter across the entire lifetime of the Mouse drag/drop action, is that the latter approach preserves legacy behavior.

Login to reply.