Weird SelectionChanged behavior

Edit: Moving this from Development Discussion to Studio Bugs because this doesn’t seem like proper behavior

ROBLOX’s SelectionService is a bit funky in the sense that when you call SelectionService:Set({}), it doesn’t clear your selection. Instead, it runs through the selection one by one firing SelectionChanged each time it removes something. If your selection is 8 objects and you deselect everything by clicking in the explorer, SelectionChanged will fire 8 times to empty the selection completely. Adding items to the selection behaves similarly – if I select 8 items at once, SelectionChanged will fire 8 times as each of those objects is added to the selection one by one.

It’s really only relevant when the user’s selection is finished, so if they went from 8 to 0 selected, I’d only want to know when 0 items are selected. If they go from 0 to 5, I’d only want to know when 5 were selected. If the user goes from 5 to 3, I’d only want to know when 3 items were selected. The current behavior is not intuitive and prevents SelectionChanged from being used in some instances (caused crashing in a plugin I’m developing which assumed SelectionChanged would only fire once per change in selection) – it should be changed.

3 Likes

This isn’t an issue for :Set anymore, but it still happens for Ctrl-A.
Here’s the workaround I use for studio build suite

---------------------------------------
-- RBXScriptSignal SelectionChanged(
--   array<Instance> selection
-- )
---------------------------------------
local SelectionChanged do
	local RunService = game:GetService'RunService'
	local Selection = game:GetService'Selection'
	local onRender = (RunService:IsServer() and not RunService:IsClient()) and RunService.Stepped or RunService.RenderStepped
	local Await = onRender.wait
	local event = Instance.new'BindableEvent'
	local Fire = event.Fire
	local Get = Selection.Get

	local d0, d1 = true, true

	onRender:Connect(function()
		d0, d1 = true, true
	end)

	Selection.SelectionChanged:Connect(function()
		if d0 then
			d0 = false
			Fire(event, Get(Selection))
		elseif d1 then
			d1 = false
			Await(onRender)
			Fire(event, Get(Selection))
		end
	end)

	SelectionChanged = event.Event
end