local g="\078\101\120\117\115\083\101\108\101\099\116\105\111\110\067\111\110\115\116\114\097\105\110\116"local A=.5 local I=.03 local O=(require(script.Parent.Parent.Parent.Parent:WaitForChild("\078\101\120\117\115\080\108\117\103\105\110\070\114\097\109\101\119\111\114\107\080\114\111\106\101\099\116"))):GetContext(script)local Y=O:GetResource("\080\108\117\103\105\110\046\078\101\120\117\115\085\115\101\114\073\110\112\117\116")local z=O:GetResource("\085\073\046\067\111\108\108\097\112\115\097\098\108\101\076\105\115\116\046\067\111\110\115\116\114\097\105\110\116\046\078\101\120\117\115\067\111\108\108\097\112\115\097\098\108\101\076\105\115\116\070\114\097\109\101\067\111\110\115\116\114\097\105\110\116")local q=z:Extend()q:SetClassName(g)O:SetContextResource(q)function q.__new(O)O:InitializeSuper()O.Name=g local z,q=false,false local a=Y.new()local i={}local function W(g)a:AddContext(g)end local function j(g)a:RemoveContext(g)end O.ListFrameAdded:Connect(W)O.ListFrameRemoved:Connect(j)Y.InputBegan:Connect(function(g)if g.KeyCode==Enum.KeyCode.LeftControl or g.KeyCode==Enum.KeyCode.RightControl then z=true elseif g.KeyCode==Enum.KeyCode.LeftShift or g.KeyCode==Enum.KeyCode.RightShift then q=true end end)Y.InputEnded:Connect(function(g)if g.KeyCode==Enum.KeyCode.LeftControl or g.KeyCode==Enum.KeyCode.RightControl then z=false elseif g.KeyCode==Enum.KeyCode.LeftShift or g.KeyCode==Enum.KeyCode.RightShift then q=false end end)local C,S local J={}local c={}local function M(g)local A local I=O:GetAllListFrames(true)local Y=J[#J]if not Y then Y=I[1]end for g,I in pairs(I)do if I==Y then A=g break end end if A then local O=I[A+g]if O then if O.Selected then Y.Selected=false else O.Selected=true end end end end local function r()local g=0 if C and tick()-C>A then g=g-1 end if S and tick()-S>A then g=g+1 end if g~=0 then M(g)end end a.InputBegan:Connect(function(g)if g.KeyCode==Enum.KeyCode.Up then local g=tick()C=g M(-1)wait(A)while C==g do r()wait(I)end elseif g.KeyCode==Enum.KeyCode.Down then local g=tick()S=g M(1)wait(A)while S==g do r()wait(I)end end end)a.InputEnded:Connect(function(g)if g.KeyCode==Enum.KeyCode.Up then C=nil elseif g.KeyCode==Enum.KeyCode.Down then S=nil end end)local function u(g)local A={}if g.Selected then if not O.AllowMultipleSelections and#J>=1 then g.Selected=false else table.insert(J,g)end end table.insert(A,(g:GetPropertyChangedSignal("\083\101\108\101\099\116\101\100")):Connect(function()local A=g.Selected if A then if O.AllowMultipleSelections and(z or q)then table.insert(J,g)if q and J[1]~=g then local A=O:GetAllListFrames(true)local I=J[1]local Y,z for A,O in pairs(A)do if O==I then Y=A elseif O==g then z=A end if Y and z then break end end if not Y or not z or Y==z then return end if Y>z then Y,z=z,Y end for g=Y+1,z-1,1 do A[g].Selected=true end end else local A=J J={}for A,I in pairs(A)do if I~=g then I.Selected=false end end J={g}end else local A for I,O in pairs(J)do if O==g then A=I break end end if A then table.remove(J,A)end end end))c[g]=A end local function d(g)u(g)for g,A in pairs((g:GetCollapsableContainer()):GetDescendants())do u(A)end local A=c[g]table.insert(A,(g:GetCollapsableContainer()).DescendantAdded:Connect(function(g)u(g)end))table.insert(A,g.DescendantRemoving:Connect(function(g)local A=c[g]if A then for g,A in pairs(A)do A:Disconnect()end c[g]=nil end end))end local function L(g)local A=c[g]if A then for g,A in pairs(A)do A:Disconnect()end c[g]=nil end for g,A in pairs(g:GetDescendants())do if A:IsA("\078\101\120\117\115\067\111\108\108\097\112\115\097\098\108\101\076\105\115\116\070\114\097\109\101")then local g=c[A]if g then for g,A in pairs(g)do A:Disconnect()end c[A]=nil end end end end O.ListFrameAdded:Connect(d)O.ListFrameRemoved:Connect(L);(O:GetPropertyChangedSignal("\065\108\108\111\119\077\117\108\116\105\112\108\101\083\101\108\101\099\116\105\111\110\115")):Connect(function()if not O.AllowMultipleSelections then local g=J J={J[1]}for g,A in pairs(g)do if g~=1 then A.Selected=false end end end end)O.AllowMultipleSelections=false end return q
--[[
TheNexusAvenger

Constraint that ensures only one list frame or certain
list frames based on the keys pressed are selected. Also
adds support for arrow keys.

TODO: The method for Shift isn't exactly correct compared
to the normal implementation.
--]]

local CLASS_NAME = "NexusSelectionConstraint"

local MIN_WAIT_TIME_FOR_ARROWS = 0.5
local INCREMENT_WAIT_TIME = 0.03



local NexusPluginFramework = require(script.Parent.Parent.Parent.Parent:WaitForChild("NexusPluginFrameworkProject")):GetContext(script)
local NexusUserInput = NexusPluginFramework:GetResource("Plugin.NexusUserInput")
local NexusCollapsableListFrameConstraint = NexusPluginFramework:GetResource("UI.CollapsableList.Constraint.NexusCollapsableListFrameConstraint")

local NexusSelectionConstraint = NexusCollapsableListFrameConstraint:Extend()
NexusSelectionConstraint:SetClassName(CLASS_NAME)
NexusPluginFramework:SetContextResource(NexusSelectionConstraint)



--[[
Creates a Nexus Collapsable List Frame Selection Constraint object.
--]]
function NexusSelectionConstraint:__new()
	self:InitializeSuper()
	self.Name = CLASS_NAME
	
	--Set up checking for Control and Shift being pressed.
	local ControlPressed,ShiftPressed = false,false
	local KeyboardContext = NexusUserInput.new()
	local PressedEvents = {}
	
	--[[
	Sets up the inputs for a list frame.
	--]]
	local function ConnectPressedEvents(ListFrame)
		KeyboardContext:AddContext(ListFrame)
	end
	
	--[[
	Disconnects the events for a list frame.
	--]]
	local function DisconnectPressedEvents(ListFrame)
		KeyboardContext:RemoveContext(ListFrame)
	end
	self.ListFrameAdded:Connect(ConnectPressedEvents)
	self.ListFrameRemoved:Connect(DisconnectPressedEvents)
	
	--Connect the events for Control and Shift.
	NexusUserInput.InputBegan:Connect(function(Input)
		if Input.KeyCode == Enum.KeyCode.LeftControl or Input.KeyCode == Enum.KeyCode.RightControl then
			ControlPressed = true
		elseif Input.KeyCode == Enum.KeyCode.LeftShift or Input.KeyCode == Enum.KeyCode.RightShift then
			ShiftPressed = true
		end
	end)
	
	NexusUserInput.InputEnded:Connect(function(Input)
		if Input.KeyCode == Enum.KeyCode.LeftControl or Input.KeyCode == Enum.KeyCode.RightControl then
			ControlPressed = false
		elseif Input.KeyCode == Enum.KeyCode.LeftShift or Input.KeyCode == Enum.KeyCode.RightShift then
			ShiftPressed = false
		end
	end)
	
	--Connect the selection events.
	local UpPressStart,DownPressStart
	local SelectionOrder = {}
	local SelectionEvents = {}
	
	--[[
	Increments the selection.
	--]]
	local function IncrementSelection(IndexIncrement)
		--Get the base increment.
		local CurrentIndex
		local ListFrames = self:GetAllListFrames(true)
		local LastSelection = SelectionOrder[#SelectionOrder]
		if not LastSelection then
			LastSelection = ListFrames[1]
		end
		
		--Get the index of the first and new selection.
		for Index,Frame in pairs(ListFrames) do
			if Frame == LastSelection then
				CurrentIndex = Index
				break
			end
		end
		
		--Increment the selection.
		if CurrentIndex then
			local NextSelection = ListFrames[CurrentIndex + IndexIncrement]
			if NextSelection then
				if NextSelection.Selected then
					LastSelection.Selected = false
				else
					NextSelection.Selected = true
				end
			end
		end
	end
	
	--[[
	Updates the quick-increment direction.
	--]]
	local function UpdateIncrement()
		local Direction = 0
		
		--Determine the direction.
		if UpPressStart and tick() - UpPressStart > MIN_WAIT_TIME_FOR_ARROWS then
			Direction = Direction - 1
		end
		if DownPressStart and tick() - DownPressStart > MIN_WAIT_TIME_FOR_ARROWS then
			Direction = Direction + 1
		end
		
		--If the direction isn't 0, move the selection.
		if Direction ~= 0 then
			IncrementSelection(Direction)
		end
	end
	
	--Connect the events for up and down.
	KeyboardContext.InputBegan:Connect(function(Input)
		if Input.KeyCode == Enum.KeyCode.Up then
			--Move the selection up.
			local StartTime = tick()
			UpPressStart = StartTime
			IncrementSelection(-1)
			wait(MIN_WAIT_TIME_FOR_ARROWS)
			
			--Start the loop for the up key.
			while UpPressStart == StartTime do
				UpdateIncrement()
				wait(INCREMENT_WAIT_TIME)
			end
		elseif Input.KeyCode == Enum.KeyCode.Down then
			--Move the selection down.
			local StartTime = tick()
			DownPressStart = StartTime
			IncrementSelection(1)
			wait(MIN_WAIT_TIME_FOR_ARROWS)
			
			--Start the loop for the down key.
			while DownPressStart == StartTime do
				UpdateIncrement()
				wait(INCREMENT_WAIT_TIME)
			end
		end
	end)
	
	KeyboardContext.InputEnded:Connect(function(Input)
		if Input.KeyCode == Enum.KeyCode.Up then
			UpPressStart = nil
		elseif Input.KeyCode == Enum.KeyCode.Down then
			DownPressStart = nil
		end
	end)
	
	--[[
	Connects the events for a list frame.
	--]]
	local function ConnectSelectionChanged(ListFrame)
		local Events = {}
		
		--Unselect the frame.
		if ListFrame.Selected then
			if not self.AllowMultipleSelections and #SelectionOrder >= 1 then
				ListFrame.Selected = false
			else
				table.insert(SelectionOrder,ListFrame)
			end
		end
		
		--Connect the selection changed events.
		table.insert(Events,ListFrame:GetPropertyChangedSignal("Selected"):Connect(function()
			local Selected = ListFrame.Selected
			if Selected then
				--Set the selection.
				if self.AllowMultipleSelections and (ControlPressed or ShiftPressed) then
					--Add the selection.
					table.insert(SelectionOrder,ListFrame)
					
					--Select the range.
					if ShiftPressed and SelectionOrder[1] ~= ListFrame then
						local ListFrames = self:GetAllListFrames(true)
						local FirstSelection = SelectionOrder[1]
						
						--Get the index of the first and new selection.
						local FirstIndex,CurrentIndex
						for Index,Frame in pairs(ListFrames) do
							if Frame == FirstSelection then
								FirstIndex = Index
							elseif Frame == ListFrame then
								CurrentIndex = Index
							end
							
							if FirstIndex and CurrentIndex then
								break
							end
						end
						
						--Return if an index is missing.
						if not FirstIndex or not CurrentIndex or FirstIndex == CurrentIndex then
							return
						end
						
						--Swap the indexes if the current is less than the first.
						if FirstIndex > CurrentIndex then
							FirstIndex,CurrentIndex = CurrentIndex,FirstIndex
						end
						
						--Select the frames.
						for i = FirstIndex + 1,CurrentIndex - 1 do
							ListFrames[i].Selected = true
						end
					end
				else
					--Deselect the frames.
					local OldSelectionOrder = SelectionOrder
					SelectionOrder = {}
					for _,Frame in pairs(OldSelectionOrder) do
						if Frame ~= ListFrame then
							Frame.Selected = false
						end
					end
					
					--Select the frame.
					SelectionOrder = {ListFrame}
				end
			else
				--Remove the index.
				local IndexToRemove
				for i,Frame in pairs(SelectionOrder) do
					if Frame == ListFrame then
						IndexToRemove = i
						break
					end
				end
				
				if IndexToRemove then
					table.remove(SelectionOrder,IndexToRemove)
				end
			end
		end))
		
		--Store the events.
		SelectionEvents[ListFrame] = Events
	end
	
	--[[
	Connects the events for a list frame being added.
	--]]
	local function ConnectSelectionAdded(ListFrame)
		--Connect the events.
		ConnectSelectionChanged(ListFrame)
		
		--Connect the descendants.
		for _,Frame in pairs(ListFrame:GetCollapsableContainer():GetDescendants()) do
			ConnectSelectionChanged(Frame)
		end
		
		--Connect the descendant added and removed events.
		local Events = SelectionEvents[ListFrame]
		table.insert(Events,ListFrame:GetCollapsableContainer().DescendantAdded:Connect(function(Descendant)
			ConnectSelectionChanged(Descendant)
		end))
		
		table.insert(Events,ListFrame.DescendantRemoving:Connect(function(Descendant)
			local Events = SelectionEvents[Descendant]
			if Events then
				for _,Event in pairs(Events) do
					Event:Disconnect()
				end
				SelectionEvents[Descendant] = nil
			end
		end))
	end
	
	--[[
	Connects the events for a list frame being removed.
	--]]
	local function ConnectSelectionRemoved(ListFrame)
		--Disconnect the events.
		local Events = SelectionEvents[ListFrame]
		if Events then
			for _,Event in pairs(Events) do
				Event:Disconnect()
			end
			SelectionEvents[ListFrame] = nil
		end
		
		--Disconnect the events of the descendants.
		for _,Frame in pairs(ListFrame:GetDescendants()) do
			if Frame:IsA("NexusCollapsableListFrame") then
				local Events = SelectionEvents[Frame]
				if Events then
					for _,Event in pairs(Events) do
						Event:Disconnect()
					end
					SelectionEvents[Frame] = nil
				end
			end
		end
	end
	self.ListFrameAdded:Connect(ConnectSelectionAdded)
	self.ListFrameRemoved:Connect(ConnectSelectionRemoved)
	
	--Connect the property changes.
	self:GetPropertyChangedSignal("AllowMultipleSelections"):Connect(function()
		--If allowing multiple selections was disabled, disable all but the first selection.
		if not self.AllowMultipleSelections then
			--Deselect the frames.
			local OldSelectionOrder = SelectionOrder
			SelectionOrder = {SelectionOrder[1]}
			for i,Frame in pairs(OldSelectionOrder) do
				if i ~= 1 then
					Frame.Selected = false
				end
			end
		end
	end)
	
	--Set the default properties.
	self.AllowMultipleSelections = false
end



return NexusSelectionConstraint