Buttons and mouse behaviour

Observations about button & mouse behavior

Entities that receive mouse events: only buttons and sprites, AFAIK

When the mouse button goes down, it becomes "captured" by whatever element is topmost, directly below the mouse at that moment. While the mouse is captured, no other entity receives mouse events, regardless of how the mouse or other elements move.

The mouse remains captured until the mouse button goes up. The mouse remains captured even if the element that captured it is removed from the display list.

If the mouse isn't above a button or sprite when the mouse button goes down, then the mouse is captured by the background (i.e. mouse events just don't get sent, until the mouse button goes up again).

Mouse events:

+------------------+---------------+-------------------------------------+ | Event | Mouse Button | description | ========================================================================= | onRollOver | up | sent to topmost entity when mouse | | | | cursor initially goes over it | +------------------+---------------+-------------------------------------+ | onRollOut | up | when mouse leaves entity, after | | | | onRollOver | +------------------+---------------+-------------------------------------+ | onPress | up -> down | sent to topmost entity when mouse | | | | button goes down. onRollOver | | | | always precedes onPress. Initiates | | | | mouse capture. | +------------------+---------------+-------------------------------------+ | onRelease | down -> up | sent to active entity if mouse goes | | | | up while over the element | +------------------+---------------+-------------------------------------+ | onDragOut | down | sent to active entity if mouse | | | | is no longer over the entity | +------------------+---------------+-------------------------------------+ | onReleaseOutside | down -> up | sent to active entity if mouse goes | | | | up while not over the entity. | | | | onDragOut always precedes | | | | onReleaseOutside | +------------------+---------------+-------------------------------------+ | onDragOver | down | sent to active entity if mouse is | | | | dragged back over it after | | | | onDragOut | +------------------+---------------+-------------------------------------+

There is always one active entity at any given time (considering NULL to be an active entity, representing the background, and other objects that don't receive mouse events).

When the mouse button is up, the active entity is the topmost element directly under the mouse pointer.

When the mouse button is down, the active entity remains whatever it was when the button last went down.

The active entity is the only object that receives mouse events.

!!! The "trackAsMenu" property alters this behavior! If trackAsMenu is set on the active entity, then onReleaseOutside is filtered out, and onDragOver from another entity is allowed (from the background, or another trackAsMenu entity). !!!

Pseudocode:

active_entity = NULL mouse_button_state = UP mouse_inside_entity_state = false frame loop: if mouse_button_state == DOWN

Handle trackAsMenu if (active_entity->trackAsMenu) possible_entity = topmost entity below mouse if (possible_entity != active_entity && possible_entity->trackAsMenu) Transfer to possible entity active_entity = possible_entity active_entity->onDragOver() mouse_inside_entity_state = true;

Handle onDragOut, onDragOver if (mouse_inside_entity_state == false) if (mouse is actually inside the active_entity) onDragOver active_entity->onDragOver() mouse_inside_entity_state = true;

else // mouse_inside_entity_state == true if (mouse is actually outside the active_entity) onDragOut active_entity->onDragOut() mouse_inside_entity_state = false;

Handle onRelease, onReleaseOutside if (mouse button is up) if (mouse_inside_entity_state) onRelease active_entity->onRelease() else onReleaseOutside if (active_entity->trackAsMenu == false) active_entity->onReleaseOutside() mouse_button_state = UP

if mouse_button_state == UP new_active_entity = topmost entity below the mouse if (new_active_entity != active_entity) onRollOut, onRollOver active_entity->onRollOut() active_entity = new_active_entity active_entity->onRollOver()

Handle press if (mouse button is down) onPress active_entity->onPress() mouse_inside_entity_state = true mouse_button_state = DOWN