Skip to content

Commit

Permalink
Update hover states on 'Context::Update()'. Add 'Context::ProcessMous…
Browse files Browse the repository at this point in the history
…eLeave()' to remove hover states and prevent them from updating, see #220.
  • Loading branch information
mikke89 committed Jun 21, 2022
1 parent 35b1132 commit 55e8a79
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 36 deletions.
14 changes: 10 additions & 4 deletions Include/RmlUi/Core/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,10 @@ class RMLUICORE_API Context : public ScriptInterface
/// @param[in] key_modifier_state The state of key modifiers (shift, control, caps-lock, etc) keys; this should be generated by ORing together members of the Input::KeyModifier enumeration.
/// @return True if the event was not consumed (ie, was prevented from propagating by an element), false if it was.
bool ProcessMouseWheel(float wheel_delta, int key_modifier_state);
/// Tells the context the mouse has left the window. This removes any hover state from all elements and prevents 'Update()' from setting the hover state for elements under the mouse.
/// @return True if the mouse is not interacting with any elements in the context (see 'IsMouseInteracting'), otherwise false.
/// @note The mouse is considered activate again after the next call to 'ProcessMouseMove()'.
bool ProcessMouseLeave();

/// Returns a hint on whether the mouse is currently interacting with any elements in this context, based on previously submitted 'ProcessMouse...()' commands.
/// @note Interaction is determined irrespective of background and opacity. See the RCSS property 'pointer-events' to disable interaction for specific elements.
Expand Down Expand Up @@ -308,6 +312,10 @@ class RMLUICORE_API Context : public ScriptInterface
// Mouse position during the last mouse_down event.
Vector2i last_click_mouse_position;

// Input state; stored from the most recent input events we receive from the application.
Vector2i mouse_position;
bool mouse_active;

// Enables cursor handling.
bool enable_cursor;
String cursor_name;
Expand All @@ -330,9 +338,6 @@ class RMLUICORE_API Context : public ScriptInterface
// itself can't be part of it.
ElementSet drag_hover_chain;

// Input state; stored from the most recent input events we receive from the application.
Vector2i mouse_position;

// The render interface this context renders through.
RenderInterface* render_interface;
Vector2i clip_origin;
Expand All @@ -352,7 +357,8 @@ class RMLUICORE_API Context : public ScriptInterface
void GenerateClickEvent(Element* element);

// Updates the current hover elements, sending required events.
void UpdateHoverChain(const Dictionary& parameters, const Dictionary& drag_parameters, Vector2i old_mouse_position);
void UpdateHoverChain(Vector2i old_mouse_position, int key_modifier_state = 0, Dictionary* out_parameters = nullptr,
Dictionary* out_drag_parameters = nullptr);

// Creates the drag clone from the given element. The old drag clone will be released if necessary.
void CreateDragClone(Element* element);
Expand Down
76 changes: 44 additions & 32 deletions Source/Core/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,6 @@ Context::Context(const String& name) : name(name), dimensions(0, 0), density_ind
cursor_proxy_document->SetProperty(PropertyId::OverflowX, Property(Style::Overflow::Visible));
cursor_proxy_document->SetProperty(PropertyId::OverflowY, Property(Style::Overflow::Visible));

enable_cursor = true;

document_focus_history.push_back(root.get());
focus = root.get();
hover = nullptr;
Expand All @@ -96,7 +94,10 @@ Context::Context(const String& name) : name(name), dimensions(0, 0), density_ind

last_click_element = nullptr;
last_click_time = 0;
last_click_mouse_position = Vector2i(0, 0);

mouse_active = false;

enable_cursor = true;
}

Context::~Context()
Expand Down Expand Up @@ -181,8 +182,12 @@ float Context::GetDensityIndependentPixelRatio() const
bool Context::Update()
{
RMLUI_ZoneScoped;

// Update the hover chain to detect any new or moved elements under the mouse.
if (mouse_active)
UpdateHoverChain(mouse_position);

// Update all data models first
// Update all the data models before updating properties and layout.
for (auto& data_model : data_models)
data_model.second->Update(true);

Expand Down Expand Up @@ -368,7 +373,7 @@ void Context::UnloadDocument(ElementDocument* _document)
}

// Rebuild the hover state.
UpdateHoverChain(Dictionary(), Dictionary(), mouse_position);
UpdateHoverChain(mouse_position);
}

// Unload all the currently loaded documents
Expand Down Expand Up @@ -591,26 +596,13 @@ bool Context::ProcessMouseMove(int x, int y, int key_modifier_state)
{
// Check whether the mouse moved since the last event came through.
Vector2i old_mouse_position = mouse_position;
bool mouse_moved = (x != mouse_position.x) || (y != mouse_position.y);
if (mouse_moved)
{
mouse_position.x = x;
mouse_position.y = y;
}

// Generate the parameters for the mouse events (there could be a few!).
Dictionary parameters;
GenerateMouseEventParameters(parameters, -1);
GenerateKeyModifierEventParameters(parameters, key_modifier_state);

Dictionary drag_parameters;
GenerateMouseEventParameters(drag_parameters);
GenerateDragEventParameters(drag_parameters);
GenerateKeyModifierEventParameters(drag_parameters, key_modifier_state);
mouse_position = {x, y};
const bool mouse_moved = (mouse_position != old_mouse_position || !mouse_active);
mouse_active = true;

// Update the current hover chain. This will send all necessary 'onmouseout', 'onmouseover', 'ondragout' and
// 'ondragover' messages.
UpdateHoverChain(parameters, drag_parameters, old_mouse_position);
// Update the current hover chain. This will send all necessary 'onmouseout', 'onmouseover', 'ondragout' and 'ondragover' messages.
Dictionary parameters, drag_parameters;
UpdateHoverChain(old_mouse_position, key_modifier_state, &parameters, &drag_parameters);

// Dispatch any 'onmousemove' events.
if (mouse_moved)
Expand All @@ -619,15 +611,14 @@ bool Context::ProcessMouseMove(int x, int y, int key_modifier_state)
{
hover->DispatchEvent(EventId::Mousemove, parameters);

if (drag_hover &&
drag_verbose)
if (drag_hover && drag_verbose)
drag_hover->DispatchEvent(EventId::Dragmove, drag_parameters);
}
}

return !IsMouseInteracting();
}

static Element* FindFocusElement(Element* element)
{
ElementDocument* owner_document = element->GetOwnerDocument();
Expand Down Expand Up @@ -758,9 +749,8 @@ bool Context::ProcessMouseButtonUp(int button_index, int key_modifier_state)

// Unset the 'active' pseudo-class on all the elements in the active chain; because they may not necessarily
// have had 'onmouseup' called on them, we can't guarantee this has happened already.
std::for_each(active_chain.begin(), active_chain.end(), [](Element* element) {
for (Element* element : active_chain)
element->SetPseudoClass("active", false);
});
active_chain.clear();
active = nullptr;

Expand Down Expand Up @@ -823,6 +813,16 @@ bool Context::ProcessMouseWheel(float wheel_delta, int key_modifier_state)
return true;
}

bool Context::ProcessMouseLeave()
{
mouse_active = false;

// Update the hover chain. Now that 'mouse_active' is disabled this will remove the hover state from all elements.
UpdateHoverChain(mouse_position);

return !IsMouseInteracting();
}

bool Context::IsMouseInteracting() const
{
return (hover && hover != root.get()) || (active && active != root.get());
Expand Down Expand Up @@ -1040,10 +1040,22 @@ void Context::GenerateClickEvent(Element* element)
}

// Updates the current hover elements, sending required events.
void Context::UpdateHoverChain(const Dictionary& parameters, const Dictionary& drag_parameters, const Vector2i old_mouse_position)
void Context::UpdateHoverChain(Vector2i old_mouse_position, int key_modifier_state, Dictionary* out_parameters, Dictionary* out_drag_parameters)
{
const Vector2f position(mouse_position);

Dictionary local_parameters, local_drag_parameters;
Dictionary& parameters = out_parameters ? *out_parameters : local_parameters;
Dictionary& drag_parameters = out_drag_parameters ? *out_drag_parameters : local_drag_parameters;

// Generate the parameters for the mouse events (there could be a few!).
GenerateMouseEventParameters(parameters);
GenerateKeyModifierEventParameters(parameters, key_modifier_state);

GenerateMouseEventParameters(drag_parameters);
GenerateDragEventParameters(drag_parameters);
GenerateKeyModifierEventParameters(drag_parameters, key_modifier_state);

// Send out drag events.
if (drag)
{
Expand All @@ -1068,7 +1080,7 @@ void Context::UpdateHoverChain(const Dictionary& parameters, const Dictionary& d
}
}

hover = GetElementAtPoint(position);
hover = mouse_active ? GetElementAtPoint(position) : nullptr;

if(enable_cursor)
{
Expand Down Expand Up @@ -1100,7 +1112,7 @@ void Context::UpdateHoverChain(const Dictionary& parameters, const Dictionary& d
SendEvents(new_hover_chain, hover_chain, EventId::Mouseover, parameters);

// Send out drag events.
if (drag)
if (drag && mouse_active)
{
drag_hover = GetElementAtPoint(position, drag);

Expand Down

0 comments on commit 55e8a79

Please sign in to comment.