How can I fix this logic issue with a custom mouse event implementation?

So Roblox mouse events suck. By those I mean like MouseLeave/MouseEnter specifically, they do not work reliably at all. So with that said, I went and made my own module to handle it more reliably.

It works well, however I just noticed a logical issue I’m pretty confused on how to fix.

Basically you can see it here:

Since the spacing between the two buttons is quite small, there’s an issue with the event callbacks not running in the correct order. (not quite sure, it’s like the callbacks for MouseEnter fire before the MouseLeave ones, so that screws everything up and therefor hides the mouse tooltip)

Here’s my code, if anyone wants to take a peek.

local mouseEvents = {}
mouseEvents.__index = mouseEvents

local player = game.Players.LocalPlayer

local playerMouse = player:GetMouse()

--//Dependencies
local UIController;

function mouseEvents.new(button)
	local self = setmetatable({}, mouseEvents)
	
	--//Field declarations
	self.button = button
	self.eventCallbacks = {onMouseEnter = {}, onMouseLeave = {}, onMouseMoved = {}}
	
	self.mouseOnButton = false
	
	--//Initiate update event for this button
	self:Init()
	
	return self
end

function mouseEvents:loadDependencies() --//Loads dependencies to use
	UIController = require(script.Parent:WaitForChild("UIController"))
end

function mouseEvents:Init() --//Sets up events to handle updating the state of the button
	local parentFrameName = self.button:GetAttribute("ParentFrame")
	local parentFrame = UIController.elements.named[parentFrameName]
	
	--//Handle disabling/enabling event callbacks when ever the button's parent frame changes (visible/invisible)
	parentFrame:GetPropertyChangedSignal("Visible"):Connect(function()
		if not parentFrame.Visible then
			--//When ever the frame is hidden, the mouse can no longer be hovered on the button, so fire those event callbacks
			self:fireCallbacks(self.eventCallbacks.onMouseLeave)
		end
	end)
	
	playerMouse.Move:Connect(function()
		if not parentFrame.Visible then return end --//We don't want to do anything if the button's parent frame is not visible
		
		local isMouseOnButton = self:isMouseOnButton()
		
		if isMouseOnButton and not self.mouseOnButton then
			self.mouseOnButton = true
			
			self:fireCallbacks(self.eventCallbacks.onMouseEnter)
		elseif not isMouseOnButton and self.mouseOnButton then
			self.mouseOnButton = false
			
			self:fireCallbacks(self.eventCallbacks.onMouseLeave)
		elseif isMouseOnButton then
			--//Mouse moved on the button
			self:fireCallbacks(self.eventCallbacks.onMouseMoved)
		end
	end)
end

function mouseEvents:fireCallbacks(callbacks) --//Executes all of the specified callbacks
	for _, callback in ipairs(callbacks) do
		callback()
	end
end

function mouseEvents:isMouseOnButton() --//Determines if the mouse is currently hovering over the button
	local buttonPos, buttonSize = self.button.AbsolutePosition, self.button.AbsoluteSize
	local mouseX, mouseY = playerMouse.X, playerMouse.Y
	
	return (mouseX > buttonPos.X and mouseX < buttonPos.X + buttonSize.X) and (mouseY > buttonPos.Y and mouseY < buttonPos.Y + buttonSize.Y)
end

function mouseEvents:onMouseEnter(callback) --//Attaches callback to be fired when the mouse hovers over the button
	table.insert(self.eventCallbacks.onMouseEnter, callback)
end

function mouseEvents:onMouseLeave(callback) --//Attaches callback to be fired when the mouse leaves the button
	table.insert(self.eventCallbacks.onMouseLeave, callback)
end

function mouseEvents:onMouseMoved(callback) --//Attaches callback to be fired when ever the mouse moves on the button
	table.insert(self.eventCallbacks.onMouseMoved, callback)
end

return mouseEvents

Any help appreciated, thanks.

1 Like

Well turns out I just wasted a bunch of time. I just tried another module, and the logic is better on that one. rip

Here’s the source to that if anyone would like it for themselves:

1 Like