local A=game:GetService("\083\116\097\114\116\101\114\071\117\105")local f=game:GetService("\071\117\105\083\101\114\118\105\099\101")local m=game:GetService("\072\097\112\116\105\099\083\101\114\118\105\099\101")local x=game:GetService("\082\117\110\083\101\114\118\105\099\101")local n=game:GetService("\085\115\101\114\073\110\112\117\116\083\101\114\118\105\099\101")local N=game:GetService("\084\119\101\101\110\083\101\114\118\105\099\101")local v=game:GetService("\080\108\097\121\101\114\115")local t={}local u=game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")local M=require(script.Parent.Signal)local w=require(script.Parent.TopbarPlusGui)local Y={}local G="\095\070\097\107\101\067\104\097\116"local U=false local j local a=false local P=32 local function h()local f,m=xpcall(function()return A:GetCore("\084\111\112\098\097\114\069\110\097\098\108\101\100")end,function(A)return true end)return f and m end local function o()local A=h()or not t.mimicCoreGui return A end local V={}V.left={startScale=0,getOffset=function()local f=48+t.leftOffset if h()and A:GetCoreGuiEnabled("\067\104\097\116")then f+=44 end return f end,getStartOffset=function()local A=t.leftGap local f=V.left.getOffset()+A return f end;records={}}V.mid={startScale=.5,getOffset=function()return 0 end,getStartOffset=function(A)local f=t.midGap return-A/2+f/2 end,records={}}V.right={startScale=1,getOffset=function()local f=t.rightOffset if h()and(A:GetCoreGuiEnabled(Enum.CoreGuiType.PlayerList)or A:GetCoreGuiEnabled(Enum.CoreGuiType.Backpack)or A:GetCoreGuiEnabled(Enum.CoreGuiType.EmotesMenu))then f+=48 end return f end;getStartOffset=function(A)local f=-A-V.right.getOffset()return f end;records={}}t.topbarEnabled=true t.controllerModeEnabled=false t.previousTopbarEnabled=h()t.leftGap=12 t.midGap=12 t.rightGap=12 t.leftOffset=0 t.rightOffset=0 t.mimicCoreGui=true t.iconAdded=M.new()t.iconRemoved=M.new()t.controllerModeStarted=M.new()t.controllerModeEnded=M.new()local Q=0 t.iconAdded:Connect(function(A)Y[A]=true if t.gameTheme then A:setTheme(t.gameTheme)end A.updated:Connect(function()t.updateTopbar()end)A.selected:Connect(function()local f=t.getIcons()for f,m in pairs(f)do if A.deselectWhenOtherIconSelected and(m~=A and(m.deselectWhenOtherIconSelected and m:getToggleState()=="\115\101\108\101\099\116\101\100"))then m:deselect(A)end end end)Q=Q+1 A:setOrder(Q)if t.controllerModeEnabled then t._enableControllerModeForIcon(A,true)end t:_updateSelectionGroup()t.updateTopbar()end)t.iconRemoved:Connect(function(A)Y[A]=nil A:setEnabled(false)A:deselect()A.updated:Fire()t:_updateSelectionGroup()end)function t.setGameTheme(A)t.gameTheme=A local f=t.getIcons()for f,m in pairs(f)do m:setTheme(A)end end function t.setDisplayOrder(A)A=tonumber(A)or w.DisplayOrder w.DisplayOrder=A end t.setDisplayOrder(10)function t.getIcons()local A={}for f,m in pairs(Y)do table.insert(A,f)end return A end function t.getIcon(A)for f,m in pairs(Y)do if f.name==A then return f end end return false end function t.canShowIconOnTopbar(A)if(A.enabled==true or A.accountForWhenDisabled)and A.presentOnTopbar then return true end return false end function t.getMenuOffset(A)local f=A:get("\097\108\105\103\110\109\101\110\116")local m=t[f.."\071\097\112"]local x=A:get("\105\099\111\110\083\105\122\101")or UDim2.new(0,32,0,32)local n=x.X.Offset local N=n+m local v=0 local u=0 local M=0 if A.menuOpen then local f=A:get("\109\101\110\117\083\105\122\101")local x=f.X.Offset local n=A:_getMenuDirection()if n=="\114\105\103\104\116"then u+=x+m/6 elseif n=="\108\101\102\116"then v=x+4 u+=m/3 M=x end end return v,u,M end local r=false function t.updateTopbar()local function A(A,f)local m=A:get("\105\099\111\110\083\105\122\101",A:getIconState())or UDim2.new(0,32,0,32)local x=m.X.Offset local n=t[f.."\071\097\112"]local N=x+n local v=N local u=0 if A._parentIcon==nil then local f,m,x=t.getMenuOffset(A)u+=f v+=m+x end return v,u end if a then r=true return false end;(coroutine.wrap(function()a=true x.Heartbeat:Wait()a=false for A,f in pairs(V)do f.records={}end for A,f in pairs(Y)do if t.canShowIconOnTopbar(A)then local f=A:get("\097\108\105\103\110\109\101\110\116")table.insert(V[f].records,A)end end local f=workspace.CurrentCamera.ViewportSize for m,x in pairs(V)do local n=x.records if#n>1 then if x.reverseSort then table.sort(n,function(A,f)return A:get("\111\114\100\101\114")>f:get("\111\114\100\101\114")end)else table.sort(n,function(A,f)return A:get("\111\114\100\101\114")<f:get("\111\114\100\101\114")end)end end local v=0 for f,x in pairs(n)do local n=A(x,m)v=v+n end local t=x.getStartOffset(v,m)local u=t local M=w.TopbarContainer.AbsoluteSize.X for f,n in pairs(n)do local N,v=A(n,m)local t=(x.startScale*M+u)+v u=u+N end for n,v in pairs(n)do local u=v.instances.iconContainer local M,w=A(v,m)local Y=v.topPadding local G=UDim2.new(x.startScale,t+w,Y.Scale,Y.Offset)local U=string.match(v.name,"\095\111\118\101\114\102\108\111\119\073\099\111\110-")local j=v:get("\114\101\112\111\115\105\116\105\111\110\073\110\102\111")if j then(N:Create(u,j,{Position=G})):Play()else u.Position=G end t=t+M v.targetPosition=UDim2.new(0,G.X.Scale*f.X+G.X.Offset,0,G.Y.Scale*f.Y+G.Y.Offset)end end local m=10 local function n(A,f,m)local x=m or 0 local n=A:get("\105\099\111\110\083\105\122\101",A:getIconState())local N=n.X.Offset local v,u=t.getMenuOffset(A)local M=f=="\108\101\102\116"and-x-v or f=="\114\105\103\104\116"and(N+x)+u local w=A.targetPosition.X.Offset+M return w end local function v(A,f)local m,x=A:get("\105\099\111\110\083\105\122\101",A:getIconState(),"\098\101\102\111\114\101\068\114\111\112\100\111\119\110")local n=f and x or m local N,v=t.getMenuOffset(A)local u=(n.X.Offset+N)+v return u end for A,x in pairs(V)do local N=x.overflowIcon if N then local u=t[A.."\071\097\112"]local M=A=="\108\101\102\116"and"\114\105\103\104\116"or"\108\101\102\116"local w=V[M]local Y=t.getIcon("\095\111\118\101\114\102\108\111\119\073\099\111\110-"..M)local G=n(N,A)if N.enabled then G=n(N,M,u)end local function U(f)local m=A=="\108\101\102\116"and f<G or A=="\114\105\103\104\116"and f>G return m end local j=w.getOffset()if not N.enabled then j+=m end local a=A=="\108\101\102\116"and f.X-j or A=="\114\105\103\104\116"and j local P=a local h=U(P)local function o(f)local x=#f for v=1,x,1 do local u=f[(x+1)-v]if t.canShowIconOnTopbar(u)then local f=string.match(u.name,"\095\111\118\101\114\102\108\111\119\073\099\111\110-")if f and x~=1 then break elseif f and not u.enabled then continue end local v=0 if not N.enabled then v=m end local t=n(u,A,v)local M=A=="\108\101\102\116"and t<P or A=="\114\105\103\104\116"and t>P if M then P=t if U(t)then h=true end end end end end o(V[M].records)o(V.mid.records)if h then local f=x.records local m=#f for x=1,m,1 do local v=A=="\108\101\102\116"and f[(m+1)-x]or A=="\114\105\103\104\116"and f[x]if v~=N and t.canShowIconOnTopbar(v)then local f=u local m=(N:get("\105\099\111\110\083\105\122\101",N:getIconState())).X.Offset if N.enabled then f+=u+m end local x=n(v,M,f)local t=A=="\108\101\102\116"and x>=P or A=="\114\105\103\104\116"and x<=P if t then if not N.enabled then local f=N.instances.iconContainer local m=f.Position.Y local x=A=="\108\101\102\116"and-f.Size.X.Offset or 0 local t=n(v,M,x)f.Position=UDim2.new(0,t,m.Scale,m.Offset)N:setEnabled(true)end if#v.dropdownIcons>0 then v._overflowConvertedToMenu=true local A=v.isSelected v:deselect()local f={}for A,m in pairs(v.dropdownIcons)do table.insert(f,m)end for A,f in pairs(v.dropdownIcons)do f:leave()end v:setMenu(f)if A and N.isSelected then v:select()end end v:join(N,"\100\114\111\112\100\111\119\110")if#v.menuIcons>0 and v.menuOpen then v:deselect()v:select()N:select()end end break end end else local f,t local G=#N.dropdownIcons if not(Y and(Y.enabled and(#x.records==1 and#w.records~=1)))then for m,x in pairs(N.dropdownIcons)do local n=x:get("\111\114\100\101\114")if t==nil or A=="\108\101\102\116"and n<f or A=="\114\105\103\104\116"and n>f then f=n t=x end end end if t then local f=v(t,true)local x=n(N,M)if G==1 then x=n(N,A,u-m)end local w=math.abs(P-x)-u*2 local Y=f<w if Y then if#N.dropdownIcons==1 then N:setEnabled(false)end local A=N.instances.iconContainer local f=A.Position.Y A.Position=UDim2.new(0,x,f.Scale,f.Offset)t:leave()if t._overflowConvertedToMenu then t._overflowConvertedToMenu=nil local A={}for f,m in pairs(t.menuIcons)do table.insert(A,m)end for A,f in pairs(t.menuIcons)do f:leave()end t:setDropdown(A)end end end end end end if r then r=false t.updateTopbar()end return true end))()end function t.setTopbarEnabled(A,n)if n==nil then n=true end local N=w.Indicator if n and not A then U=true elseif n and A then U=false end local v=o()if t.controllerModeEnabled then if A then if w.TopbarContainer.Visible or U or j or not v then return end if n then N.Visible=v else if m:IsVibrationSupported(Enum.UserInputType.Gamepad1)and m:IsMotorSupported(Enum.UserInputType.Gamepad1,Enum.VibrationMotor.Small)then m:SetMotor(Enum.UserInputType.Gamepad1,Enum.VibrationMotor.Small,1)delay(.2,function()pcall(function()m:SetMotor(Enum.UserInputType.Gamepad1,Enum.VibrationMotor.Small,0)end)end)end w.TopbarContainer.Visible=true w.TopbarContainer:TweenPosition(UDim2.new(0,0,0,5+P),Enum.EasingDirection.Out,Enum.EasingStyle.Quad,.1,true)local A local n=0 t:_updateSelectionGroup()x.Heartbeat:Wait()local v=50 for f,m in pairs(Y)do if t.canShowIconOnTopbar(f)and(A==nil or f:get("\111\114\100\101\114")>A:get("\111\114\100\101\114"))then A=f end local x=f.instances.iconContainer local N=(-27+x.AbsoluteSize.Y)+v if N>n then n=N end end if f:GetEmotesMenuOpen()then f:SetEmotesMenuOpen(false)end if f:GetInspectMenuEnabled()then f:CloseInspectMenu()end local u=t._previousSelectedObject or A.instances.iconButton t._setControllerSelectedObject(u)N.Image="\114\098\120\097\115\115\101\116\105\100\058\047\047\053\050\055\056\049\053\049\048\055\049"N:TweenPosition(UDim2.new(.5,0,0,n+P),Enum.EasingDirection.Out,Enum.EasingStyle.Quad,.1,true)end else if n then N.Visible=false else N.Visible=v end if not w.TopbarContainer.Visible then return end f.AutoSelectGuiEnabled=true t:_updateSelectionGroup(true)w.TopbarContainer:TweenPosition(UDim2.new(0,0,0,-w.TopbarContainer.Size.Y.Offset+P),Enum.EasingDirection.Out,Enum.EasingStyle.Quad,.1,true,function()w.TopbarContainer.Visible=false end)N.Image="\114\098\120\097\115\115\101\116\105\100\058\047\047\053\050\055\056\049\053\049\053\053\054"N:TweenPosition(UDim2.new(.5,0,0,5),Enum.EasingDirection.Out,Enum.EasingStyle.Quad,.1,true)end else local f=w.TopbarContainer if v then f.Visible=A else f.Visible=false end end end function t.setGap(A,f)local m=tonumber(A)or 12 local x=(tostring(f)):lower()if x=="\108\101\102\116"or x=="\109\105\100"or x=="\114\105\103\104\116"then t[x.."\071\097\112"]=m t.updateTopbar()return end t.leftGap=m t.midGap=m t.rightGap=m t.updateTopbar()end function t.setLeftOffset(A)t.leftOffset=tonumber(A)or 0 t.updateTopbar()end function t.setRightOffset(A)t.rightOffset=tonumber(A)or 0 t.updateTopbar()end local X=v.LocalPlayer local K={}X.CharacterAdded:Connect(function()for A,f in pairs(K)do f:destroy()end K={}end)function t.clearIconOnSpawn(A)(coroutine.wrap(function()local f=X.Character or X.CharacterAdded:Wait()table.insert(K,A)end))()end function t._updateSelectionGroup(m,A)if t._navigationEnabled then f:RemoveSelectionGroup("\084\111\112\098\097\114\080\108\117\115\073\099\111\110\115")end if A then f.CoreGuiNavigationEnabled=t._originalCoreGuiNavigationEnabled f.GuiNavigationEnabled=t._originalGuiNavigationEnabled t._navigationEnabled=nil elseif t.controllerModeEnabled then local A=t.getIcons()local m={}for A,f in pairs(A)do local x=f.joinedFeatureName if not x or f._parentIcon[f.joinedFeatureName.."\079\112\101\110"]==true then table.insert(m,f.instances.iconButton)end end f:AddSelectionTuple("\084\111\112\098\097\114\080\108\117\115\073\099\111\110\115",table.unpack(m))if not t._navigationEnabled then t._originalCoreGuiNavigationEnabled=f.CoreGuiNavigationEnabled t._originalGuiNavigationEnabled=f.GuiNavigationEnabled f.CoreGuiNavigationEnabled=false f.GuiNavigationEnabled=true t._navigationEnabled=true end end end local function D()if f:IsTenFootInterface()then return 3 else return 1.3 end end function t._setControllerSelectedObject(A)local m=t._controllerSetCount and t._controllerSetCount+1 or 0 t._controllerSetCount=m f.SelectedObject=A delay(.1,function()local x=t._controllerSetCount if m==x then f.SelectedObject=A end end)end function t._enableControllerMode(A)local f=w.Indicator local m=t.getIcon("\095\084\111\112\098\097\114\067\111\110\116\114\111\108\108\101\114\079\112\116\105\111\110")if t.controllerModeEnabled==A then return end t.controllerModeEnabled=A if A then w.TopbarContainer.Position=UDim2.new(0,0,0,5)w.TopbarContainer.Visible=false local A=D()f.Position=UDim2.new(.5,0,0,5)f.Size=UDim2.new(0,18*A,0,18*A)f.Image="\114\098\120\097\115\115\101\116\105\100\058\047\047\053\050\055\056\049\053\049\053\053\054"f.Visible=o()f.Position=UDim2.new(.5,0,0,5)else w.TopbarContainer.Position=UDim2.new(0,0,0,0)w.TopbarContainer.Visible=o()f.Visible=false t._setControllerSelectedObject(nil)end for f,m in pairs(Y)do t._enableControllerModeForIcon(f,A)end end function t._enableControllerModeForIcon(A,f)local m=A._parentIcon local x=A.joinedFeatureName if m then A:leave()end if f then local f=D()local m=A:get("\105\099\111\110\083\105\122\101","\100\101\115\101\108\101\099\116\101\100")local x=A:get("\105\099\111\110\083\105\122\101","\115\101\108\101\099\116\101\100")local n=A:getHovering("\105\099\111\110\083\105\122\101")A:set("\105\099\111\110\083\105\122\101",UDim2.new(0,m.X.Offset*f,0,m.Y.Offset*f),"\100\101\115\101\108\101\099\116\101\100","\099\111\110\116\114\111\108\108\101\114\077\111\100\101")A:set("\105\099\111\110\083\105\122\101",UDim2.new(0,x.X.Offset*f,0,x.Y.Offset*f),"\115\101\108\101\099\116\101\100","\099\111\110\116\114\111\108\108\101\114\077\111\100\101")if n then A:set("\105\099\111\110\083\105\122\101",UDim2.new(0,x.X.Offset*f,0,x.Y.Offset*f),"\104\111\118\101\114\105\110\103","\099\111\110\116\114\111\108\108\101\114\077\111\100\101")end A:set("\097\108\105\103\110\109\101\110\116","\109\105\100","\100\101\115\101\108\101\099\116\101\100","\099\111\110\116\114\111\108\108\101\114\077\111\100\101")A:set("\097\108\105\103\110\109\101\110\116","\109\105\100","\115\101\108\101\099\116\101\100","\099\111\110\116\114\111\108\108\101\114\077\111\100\101")else local f={"\100\101\115\101\108\101\099\116\101\100";"\115\101\108\101\099\116\101\100";"\104\111\118\101\114\105\110\103"}for f,m in pairs(f)do local x,n=A:get("\097\108\105\103\110\109\101\110\116",m,"\099\111\110\116\114\111\108\108\101\114\077\111\100\101")if n then A:set("\097\108\105\103\110\109\101\110\116",n,m)end local N,v=A:get("\105\099\111\110\083\105\122\101",m,"\099\111\110\116\114\111\108\108\101\114\077\111\100\101")if v then A:set("\105\099\111\110\083\105\122\101",v,m)end end end if m then A:join(m,x)end end;(coroutine.wrap(function()x.Heartbeat:Wait()local A=require(script.Parent)local m=((((((A.new()):setName("\095\084\111\112\098\097\114\067\111\110\116\114\111\108\108\101\114\079\112\116\105\111\110")):setOrder(100)):setImage("\114\098\120\097\115\115\101\116\105\100\058\047\047\053\050\055\056\049\053\048\057\052\050")):setRight()):setEnabled(false)):setTip("\067\111\110\116\114\111\108\108\101\114 \109\111\100\101")m.deselectWhenOtherIconSelected=false local function N()local A=n.MouseEnabled local f=n.GamepadEnabled local x=m.isSelected if A and f then m:setEnabled(true)elseif A and not f then m:setEnabled(false)t._enableControllerMode(false)m:deselect()elseif not A and f then m:setEnabled(false)t._enableControllerMode(true)end end;(n:GetPropertyChangedSignal("\077\111\117\115\101\069\110\097\098\108\101\100")):Connect(N)n.GamepadConnected:Connect(N)n.GamepadDisconnected:Connect(N)N()local function v()local A=m.isSelected local f=A and"\078\111\114\109\097\108 \109\111\100\101"or"\067\111\110\116\114\111\108\108\101\114 \109\111\100\101"m:setTip(f)t._enableControllerMode(A)end m.selected:Connect(v)m.deselected:Connect(v)n.InputBegan:Connect(function(A,m)if not t.controllerModeEnabled then return end if A.KeyCode==Enum.KeyCode.DPadDown then if not f.SelectedObject and o()then t.setTopbarEnabled(true,false)end elseif A.KeyCode==Enum.KeyCode.ButtonB then t._previousSelectedObject=f.SelectedObject t._setControllerSelectedObject(nil)t.setTopbarEnabled(false,false)end A:Destroy()end)for f,m in pairs(V)do if f~="\109\105\100"then local x="\095\111\118\101\114\102\108\111\119\073\099\111\110-"..f local n=(((A.new()):setImage(6069276526)):setName(x)):setEnabled(false)m.overflowIcon=n n.accountForWhenDisabled=true if f=="\108\101\102\116"then n:setOrder(math.huge)n:setLeft()n:set("\100\114\111\112\100\111\119\110\065\108\105\103\110\109\101\110\116","\114\105\103\104\116")elseif f=="\114\105\103\104\116"then n:setOrder(-math.huge)n:setRight()n:set("\100\114\111\112\100\111\119\110\065\108\105\103\110\109\101\110\116","\108\101\102\116")end n.lockedSettings={iconImage=true;order=true,alignment=true}end end end))();(coroutine.wrap(function()local A=v.LocalPlayer.PlayerScripts:WaitForChild("\067\104\097\116\083\099\114\105\112\116",4)or(game:GetService("\067\104\097\116")):WaitForChild("\067\104\097\116\083\099\114\105\112\116",4)if not A then return end local f=A:FindFirstChild("\067\104\097\116\077\097\105\110")if not f then return end local m=require(f)m.CoreGuiEnabled:connect(function()local A=h()if A==t.previousTopbarEnabled then t.updateTopbar()return"\083\101\116\067\111\114\101\071\117\105\069\110\097\098\108\101\100 \119\097\115 \099\097\108\108\101\100 \105\110\115\116\101\097\100 \111\102 \083\101\116\067\111\114\101"end if t.mimicCoreGui then t.previousTopbarEnabled=A if t.controllerModeEnabled then t.setTopbarEnabled(false,false)else t.setTopbarEnabled(A,false)end end t.updateTopbar()end)local x=h()if not x and not t.mimicCoreGui then x=true end t.setTopbarEnabled(x,false)end))()f.MenuClosed:Connect(function()j=false if not t.controllerModeEnabled then t.setTopbarEnabled(t.topbarEnabled,false)end end)f.MenuOpened:Connect(function()j=true t.setTopbarEnabled(false,false)end);(workspace.CurrentCamera:GetPropertyChangedSignal("\086\105\101\119\112\111\114\116\083\105\122\101")):Connect(function()t.updateTopbar()end)return t
--[[ icon_controller:header
## Functions

#### setGameTheme
```lua
IconController.setGameTheme(theme)
```
Sets the default theme which is applied to all existing and future icons.

----
#### setDisplayOrder
```lua
IconController.setDisplayOrder(number)
```
Changes the DisplayOrder of the TopbarPlus ScreenGui to the given value.

----
#### setTopbarEnabled
```lua
IconController.setTopbarEnabled(bool)
```
When set to ``false``, hides all icons created with TopbarPlus. This can also be achieved by calling ``starterGui:SetCore("TopbarEnabled", false)``.

----
#### setGap
```lua
IconController.setGap(integer, alignment)
```
Defines the offset width (i.e. gap) between each icon for the given alignment, ``left``, ``mid``, ``right``, or all alignments if not specified. 

----
#### setLeftOffset
```lua
IconController.setLeftOffset(integer)
```
Defines the offset from the left side of the screen to the nearest left-set icon. 

----
#### setRightOffset
```lua
IconController.setRightOffset(integer)
```
Defines the offset from the right side of the screen to the nearest right-set icon. 

----
#### updateTopbar
```lua
IconController.updateTopbar()
```
Determines how icons should be positioned on the topbar and moves them accordingly.  

----
#### clearIconOnSpawn
```lua
IconController.clearIconOnSpawn(icon)
```
Calls destroy on the given icon when the player respawns. This is useful for scenarious where you wish to cleanup icons that are constructed within a Gui with ``ResetOnSpawn`` set to ``true``. For example:

```lua
-- Place at the bottom of your icon creator localscript
local icons = IconController.getIcons()
for _, icon in pairs(icons) do
	IconController.clearIconOnSpawn(icon)
end
```

----
#### getIcons
```lua
local arrayOfIcons = IconController.getIcons()
```
Returns all icons as an array.

----
#### getIcon
```lua
local icon = IconController.getIcon(name)
```
Returns the icon with the given name (or ``false`` if not found). If multiple icons have the same name, then one will be returned randomly.

----



## Properties
#### mimicCoreGui
```lua
local bool = IconController.mimicCoreGui --[default: 'true']
```
Set to ``false`` to have the topbar persist even when ``game:GetService("StarterGui"):SetCore("TopbarEnabled", false)`` is called.

----
#### controllerModeEnabled
{read-only}
```lua
local bool = IconController.controllerModeEnabled
```

----
#### leftGap
{read-only}
```lua
local gapNumber = IconController.leftGap --[default: '12']
```

----
#### midGap
{read-only}
```lua
local gapNumber = IconController.midGap --[default: '12']
```

----
#### rightGap
{read-only}
```lua
local gapNumber = IconController.rightGap --[default: '12']
```

----
#### leftOffset
{read-only}
```lua
local offset = IconController.leftGap --[default: '0']
```

----
#### rightOffset
{read-only}
```lua
local offset = IconController.rightGap --[default: '0']
```
--]]



-- LOCAL
local starterGui = game:GetService("StarterGui")
local guiService = game:GetService("GuiService")
local hapticService = game:GetService("HapticService")
local runService = game:GetService("RunService")
local userInputService = game:GetService("UserInputService")
local tweenService = game:GetService("TweenService")
local players = game:GetService("Players")
local IconController = {}
local replicatedStorage = game:GetService("ReplicatedStorage")
local Signal = require(script.Parent.Signal)
local TopbarPlusGui = require(script.Parent.TopbarPlusGui)
local topbarIcons = {}
local fakeChatName = "_FakeChat"
local forceTopbarDisabled = false
local menuOpen
local topbarUpdating = false
local STUPID_CONTROLLER_OFFSET = 32



-- LOCAL FUNCTIONS
local function checkTopbarEnabled()
	local success, bool = xpcall(function()
		return starterGui:GetCore("TopbarEnabled")
	end,function(err)
		--has not been registered yet, but default is that is enabled
		return true	
	end)
	return (success and bool)
end

local function checkTopbarEnabledAccountingForMimic()
	local topbarEnabledAccountingForMimic = (checkTopbarEnabled() or not IconController.mimicCoreGui)
	return topbarEnabledAccountingForMimic
end



-- OFFSET HANDLERS
local alignmentDetails = {}
alignmentDetails["left"] = {
	startScale = 0,
	getOffset = function()
		local offset = 48 + IconController.leftOffset
		if checkTopbarEnabled() and starterGui:GetCoreGuiEnabled("Chat") then
			offset += 12 + 32
		end
		return offset
	end,
	getStartOffset = function()
		local alignmentGap = IconController["leftGap"]
		local startOffset = alignmentDetails.left.getOffset() + alignmentGap
		return startOffset
	end,
	records = {}
}
alignmentDetails["mid"] = {
	startScale = 0.5,
	getOffset = function()
		return 0
	end,
	getStartOffset = function(totalIconX) 
		local alignmentGap = IconController["midGap"]
		return -totalIconX/2 + (alignmentGap/2)
	end,
	records = {}
}
alignmentDetails["right"] = {
	startScale = 1,
	getOffset = function()
		local offset = IconController.rightOffset
		if checkTopbarEnabled() and (starterGui:GetCoreGuiEnabled(Enum.CoreGuiType.PlayerList) or starterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Backpack) or starterGui:GetCoreGuiEnabled(Enum.CoreGuiType.EmotesMenu)) then
			offset += 48
		end
		return offset
	end,
	getStartOffset = function(totalIconX)
		local startOffset = -totalIconX - alignmentDetails.right.getOffset()
		return startOffset
	end,
	records = {}
	--reverseSort = true
}



-- PROPERTIES
IconController.topbarEnabled = true
IconController.controllerModeEnabled = false
IconController.previousTopbarEnabled = checkTopbarEnabled()
IconController.leftGap = 12
IconController.midGap = 12
IconController.rightGap = 12
IconController.leftOffset = 0
IconController.rightOffset = 0
IconController.mimicCoreGui = true



-- EVENTS
IconController.iconAdded = Signal.new()
IconController.iconRemoved = Signal.new()
IconController.controllerModeStarted = Signal.new()
IconController.controllerModeEnded = Signal.new()



-- CONNECTIONS
local iconCreationCount = 0
IconController.iconAdded:Connect(function(icon)
	topbarIcons[icon] = true
	if IconController.gameTheme then
		icon:setTheme(IconController.gameTheme)
	end
	icon.updated:Connect(function()
		IconController.updateTopbar()
	end)
	-- When this icon is selected, deselect other icons if necessary
	icon.selected:Connect(function()
		local allIcons = IconController.getIcons()
		for _, otherIcon in pairs(allIcons) do
			if icon.deselectWhenOtherIconSelected and otherIcon ~= icon and otherIcon.deselectWhenOtherIconSelected and otherIcon:getToggleState() == "selected" then
				otherIcon:deselect(icon)
			end
		end
	end)
	-- Order by creation if no order specified
	iconCreationCount = iconCreationCount + 1
	icon:setOrder(iconCreationCount)
	-- Apply controller view if enabled
	if IconController.controllerModeEnabled then
		IconController._enableControllerModeForIcon(icon, true)
	end
	IconController:_updateSelectionGroup()
	IconController.updateTopbar()
end)

IconController.iconRemoved:Connect(function(icon)
	topbarIcons[icon] = nil
	icon:setEnabled(false)
	icon:deselect()
	icon.updated:Fire()
	IconController:_updateSelectionGroup()
end)



-- METHODS
function IconController.setGameTheme(theme)
	IconController.gameTheme = theme
	local icons = IconController.getIcons()
	for _, icon in pairs(icons) do
		icon:setTheme(theme)
	end
end

function IconController.setDisplayOrder(value)
	value = tonumber(value) or TopbarPlusGui.DisplayOrder
	TopbarPlusGui.DisplayOrder = value
end
IconController.setDisplayOrder(10)

function IconController.getIcons()
	local allIcons = {}
	for otherIcon, _ in pairs(topbarIcons) do
		table.insert(allIcons, otherIcon)
	end
	return allIcons
end

function IconController.getIcon(name)
	for otherIcon, _ in pairs(topbarIcons) do
		if otherIcon.name == name then
			return otherIcon
		end
	end
	return false
end

function IconController.canShowIconOnTopbar(icon)
	if (icon.enabled == true or icon.accountForWhenDisabled) and icon.presentOnTopbar then
		return true
	end
	return false
end

function IconController.getMenuOffset(icon)
	local alignment = icon:get("alignment")
	local alignmentGap = IconController[alignment.."Gap"]
	local iconSize = icon:get("iconSize") or UDim2.new(0, 32, 0, 32)
	local sizeX = iconSize.X.Offset
	local iconWidthAndGap = (sizeX + alignmentGap)
	local extendLeft = 0
	local extendRight = 0
	local additionalRight = 0
	if icon.menuOpen then
		local menuSize = icon:get("menuSize")
		local menuSizeXOffset = menuSize.X.Offset
		local direction = icon:_getMenuDirection()
		if direction == "right" then
			extendRight += menuSizeXOffset + alignmentGap/6--2
		elseif direction == "left" then
			extendLeft = menuSizeXOffset + 4
			extendRight += alignmentGap/3--4
			additionalRight = menuSizeXOffset
		end
	end
	return extendLeft, extendRight, additionalRight
end

-- This is responsible for positioning the topbar icons
local requestedTopbarUpdate = false
function IconController.updateTopbar()
	local function getIncrement(otherIcon, alignment)
		--local container = otherIcon.instances.iconContainer
		--local sizeX = container.Size.X.Offset
		local iconSize = otherIcon:get("iconSize", otherIcon:getIconState()) or UDim2.new(0, 32, 0, 32)
		local sizeX = iconSize.X.Offset
		local alignmentGap = IconController[alignment.."Gap"]
		local iconWidthAndGap = (sizeX + alignmentGap)
		local increment = iconWidthAndGap
		local preOffset = 0
		if otherIcon._parentIcon == nil then
			local extendLeft, extendRight, additionalRight = IconController.getMenuOffset(otherIcon)
			preOffset += extendLeft
			increment += extendRight + additionalRight
		end
		return increment, preOffset
	end
	if topbarUpdating then -- This prevents the topbar updating and shifting icons more than it needs to
		requestedTopbarUpdate = true
		return false
	end
	coroutine.wrap(function()
		topbarUpdating = true
		runService.Heartbeat:Wait()
		topbarUpdating = false
		
		for alignment, alignmentInfo in pairs(alignmentDetails) do
			alignmentInfo.records = {}
		end

		for otherIcon, _ in pairs(topbarIcons) do
			if IconController.canShowIconOnTopbar(otherIcon) then
				local alignment = otherIcon:get("alignment")
				table.insert(alignmentDetails[alignment].records, otherIcon)
			end
		end
		local viewportSize = workspace.CurrentCamera.ViewportSize
		for alignment, alignmentInfo in pairs(alignmentDetails) do
			local records = alignmentInfo.records
			if #records > 1 then
				if alignmentInfo.reverseSort then
					table.sort(records, function(a,b) return a:get("order") > b:get("order") end)
				else
					table.sort(records, function(a,b) return a:get("order") < b:get("order") end)
				end
			end
			local totalIconX = 0
			for i, otherIcon in pairs(records) do
				local increment = getIncrement(otherIcon, alignment)
				totalIconX = totalIconX + increment
			end
			local offsetX = alignmentInfo.getStartOffset(totalIconX, alignment)
			local preOffsetX = offsetX
			local containerX = TopbarPlusGui.TopbarContainer.AbsoluteSize.X
			for i, otherIcon in pairs(records) do
				local increment, preOffset = getIncrement(otherIcon, alignment)
				local newAbsoluteX = alignmentInfo.startScale*containerX + preOffsetX+preOffset
				preOffsetX = preOffsetX + increment
			end
			for i, otherIcon in pairs(records) do
				local container = otherIcon.instances.iconContainer
				local increment, preOffset = getIncrement(otherIcon, alignment)
				local topPadding = otherIcon.topPadding
				local newPositon = UDim2.new(alignmentInfo.startScale, offsetX+preOffset, topPadding.Scale, topPadding.Offset)
				local isAnOverflowIcon = string.match(otherIcon.name, "_overflowIcon-")
				local repositionInfo = otherIcon:get("repositionInfo")
				if repositionInfo then
					tweenService:Create(container, repositionInfo, {Position = newPositon}):Play()
				else
					container.Position = newPositon
				end
				offsetX = offsetX + increment
				otherIcon.targetPosition = UDim2.new(0, (newPositon.X.Scale*viewportSize.X) + newPositon.X.Offset, 0, (newPositon.Y.Scale*viewportSize.Y) + newPositon.Y.Offset)
			end
		end

		-- OVERFLOW HANDLER
		--------
		local START_LEEWAY = 10 -- The additional offset where the end icon will be converted to ... without an apparant change in position
		local function getBoundaryX(iconToCheck, side, gap)
			local additionalGap = gap or 0
			local currentSize = iconToCheck:get("iconSize", iconToCheck:getIconState())
			local sizeX = currentSize.X.Offset
			local extendLeft, extendRight = IconController.getMenuOffset(iconToCheck)
			local boundaryXOffset = (side == "left" and (-additionalGap-extendLeft)) or (side == "right" and sizeX+additionalGap+extendRight)
			local boundaryX = iconToCheck.targetPosition.X.Offset + boundaryXOffset
			return boundaryX
		end
		local function getSizeX(iconToCheck, usePrevious)
			local currentSize, previousSize = iconToCheck:get("iconSize", iconToCheck:getIconState(), "beforeDropdown")
			local newSize = (usePrevious and previousSize) or currentSize
			local extendLeft, extendRight = IconController.getMenuOffset(iconToCheck)
			local sizeX = newSize.X.Offset + extendLeft + extendRight
			return sizeX
		end

		for alignment, alignmentInfo in pairs(alignmentDetails) do
			local overflowIcon = alignmentInfo.overflowIcon
			if overflowIcon then
				local alignmentGap = IconController[alignment.."Gap"]
				local oppositeAlignment = (alignment == "left" and "right") or "left"
				local oppositeAlignmentInfo = alignmentDetails[oppositeAlignment]
				local oppositeOverflowIcon = IconController.getIcon("_overflowIcon-"..oppositeAlignment)
				
				-- This determines whether any icons (from opposite or mid alignment) are overlapping with this alignment
				local overflowBoundaryX = getBoundaryX(overflowIcon, alignment)
				if overflowIcon.enabled then
					overflowBoundaryX = getBoundaryX(overflowIcon, oppositeAlignment, alignmentGap)
				end
				local function doesExceed(givenBoundaryX)
					local exceeds = (alignment == "left" and givenBoundaryX < overflowBoundaryX) or (alignment == "right" and givenBoundaryX > overflowBoundaryX)
					return exceeds
				end
				local alignmentOffset = oppositeAlignmentInfo.getOffset()
				if not overflowIcon.enabled then
					alignmentOffset += START_LEEWAY
				end
				local alignmentBorderX = (alignment == "left" and viewportSize.X - alignmentOffset) or (alignment == "right" and alignmentOffset)
				local closestBoundaryX = alignmentBorderX
				local exceededCriticalBoundary = doesExceed(closestBoundaryX)
				local function checkBoundaryExceeded(recordToCheck)
					local totalIcons = #recordToCheck
					for i = 1, totalIcons do
						local endIcon = recordToCheck[totalIcons+1 - i]
						if IconController.canShowIconOnTopbar(endIcon) then
							local isAnOverflowIcon = string.match(endIcon.name, "_overflowIcon-")
							if isAnOverflowIcon and totalIcons ~= 1 then --!!!
								break
							elseif isAnOverflowIcon and not endIcon.enabled then
								continue
							end
							local additionalMyX = 0
							if not overflowIcon.enabled then
								additionalMyX = START_LEEWAY
							end
							local myBoundaryX = getBoundaryX(endIcon, alignment, additionalMyX)
							local isNowClosest = (alignment == "left" and myBoundaryX < closestBoundaryX) or (alignment == "right" and myBoundaryX > closestBoundaryX)
							if isNowClosest then
								closestBoundaryX = myBoundaryX
								if doesExceed(myBoundaryX) then
									exceededCriticalBoundary = true
								end
							end
						end
					end
				end
				checkBoundaryExceeded(alignmentDetails[oppositeAlignment].records)
				checkBoundaryExceeded(alignmentDetails.mid.records)

				-- This determines which icons to give to the overflow if an overlap is present
				if exceededCriticalBoundary then
					local recordToCheck = alignmentInfo.records
					local totalIcons = #recordToCheck
					for i = 1, totalIcons do
						local endIcon = (alignment == "left" and recordToCheck[totalIcons+1 - i]) or (alignment == "right" and recordToCheck[i])
						if endIcon ~= overflowIcon and IconController.canShowIconOnTopbar(endIcon) then
							local additionalGap = alignmentGap
							local overflowIconSizeX = overflowIcon:get("iconSize", overflowIcon:getIconState()).X.Offset
							if overflowIcon.enabled then
								additionalGap += alignmentGap + overflowIconSizeX
							end
							local myBoundaryXPlusGap = getBoundaryX(endIcon, oppositeAlignment, additionalGap)
							local exceeds = (alignment == "left" and myBoundaryXPlusGap >= closestBoundaryX) or (alignment == "right" and myBoundaryXPlusGap <= closestBoundaryX)
							if exceeds then
								if not overflowIcon.enabled then
									local overflowContainer = overflowIcon.instances.iconContainer
									local yPos = overflowContainer.Position.Y
									local appearXAdditional = (alignment == "left" and -overflowContainer.Size.X.Offset) or 0
									local appearX = getBoundaryX(endIcon, oppositeAlignment, appearXAdditional)
									overflowContainer.Position = UDim2.new(0, appearX, yPos.Scale, yPos.Offset)
									overflowIcon:setEnabled(true)
								end
								if #endIcon.dropdownIcons > 0 then
									endIcon._overflowConvertedToMenu = true
									local wasSelected = endIcon.isSelected
									endIcon:deselect()
									local iconsToConvert = {}
									for _, dIcon in pairs(endIcon.dropdownIcons) do
										table.insert(iconsToConvert, dIcon)
									end
									for _, dIcon in pairs(endIcon.dropdownIcons) do
										dIcon:leave()
									end
									endIcon:setMenu(iconsToConvert)
									if wasSelected and overflowIcon.isSelected then
										endIcon:select()
									end
								end
								endIcon:join(overflowIcon, "dropdown")
								if #endIcon.menuIcons > 0 and endIcon.menuOpen then
									endIcon:deselect()
									endIcon:select()
									overflowIcon:select()
								end
							end
							break
						end
					end
				
				else
					
					-- This checks to see if the lowest/highest (depending on left/right) ordered overlapping icon is no longer overlapping, removes from the dropdown, and repeats if valid
					local winningOrder, winningOverlappedIcon
					local totalOverlappingIcons = #overflowIcon.dropdownIcons
					if not (oppositeOverflowIcon and oppositeOverflowIcon.enabled and #alignmentInfo.records == 1 and #oppositeAlignmentInfo.records ~= 1) then
						for _, overlappedIcon in pairs(overflowIcon.dropdownIcons) do
							local iconOrder = overlappedIcon:get("order")
							if winningOverlappedIcon == nil or (alignment == "left" and iconOrder < winningOrder) or (alignment == "right" and iconOrder > winningOrder) then
								winningOrder = iconOrder
								winningOverlappedIcon = overlappedIcon
							end
						end
					end
					if winningOverlappedIcon then
						local sizeX = getSizeX(winningOverlappedIcon, true)
						local myForesightBoundaryX = getBoundaryX(overflowIcon, oppositeAlignment)
						if totalOverlappingIcons == 1 then
							myForesightBoundaryX = getBoundaryX(overflowIcon, alignment, alignmentGap-START_LEEWAY)
						end
						local availableGap = math.abs(closestBoundaryX - myForesightBoundaryX) - (alignmentGap*2)
						local noLongerExeeds = (sizeX < availableGap)
						if noLongerExeeds then
							if #overflowIcon.dropdownIcons == 1 then
								overflowIcon:setEnabled(false)
							end
							local overflowContainer = overflowIcon.instances.iconContainer
							local yPos = overflowContainer.Position.Y
							overflowContainer.Position = UDim2.new(0, myForesightBoundaryX, yPos.Scale, yPos.Offset)
							winningOverlappedIcon:leave()
							--
							if winningOverlappedIcon._overflowConvertedToMenu then
								winningOverlappedIcon._overflowConvertedToMenu = nil
								local iconsToConvert = {}
								for _, dIcon in pairs(winningOverlappedIcon.menuIcons) do
									table.insert(iconsToConvert, dIcon)
								end
								for _, dIcon in pairs(winningOverlappedIcon.menuIcons) do
									dIcon:leave()
								end
								winningOverlappedIcon:setDropdown(iconsToConvert)
							end
							--
						end
					end

				end
			end
		end
		--------
		if requestedTopbarUpdate then
			requestedTopbarUpdate = false
			IconController.updateTopbar()
		end
		return true
	end)()
end

function IconController.setTopbarEnabled(bool, forceBool)
	if forceBool == nil then
		forceBool = true
	end
	local indicator = TopbarPlusGui.Indicator
	if forceBool and not bool then
		forceTopbarDisabled = true
	elseif forceBool and bool then
		forceTopbarDisabled = false
	end
	local topbarEnabledAccountingForMimic = checkTopbarEnabledAccountingForMimic()
	if IconController.controllerModeEnabled then
		if bool then
			if TopbarPlusGui.TopbarContainer.Visible or forceTopbarDisabled or menuOpen or not topbarEnabledAccountingForMimic then return end
			if forceBool then
				indicator.Visible = topbarEnabledAccountingForMimic
			else
				if hapticService:IsVibrationSupported(Enum.UserInputType.Gamepad1) and hapticService:IsMotorSupported(Enum.UserInputType.Gamepad1,Enum.VibrationMotor.Small) then
					hapticService:SetMotor(Enum.UserInputType.Gamepad1,Enum.VibrationMotor.Small,1)
					delay(0.2,function()
						pcall(function()
							hapticService:SetMotor(Enum.UserInputType.Gamepad1,Enum.VibrationMotor.Small,0)
						end)
					end)
				end
				TopbarPlusGui.TopbarContainer.Visible = true
				TopbarPlusGui.TopbarContainer:TweenPosition(
					UDim2.new(0,0,0,5 + STUPID_CONTROLLER_OFFSET),
					Enum.EasingDirection.Out,
					Enum.EasingStyle.Quad,
					0.1,
					true
				)
				
				
				local selectIcon
				local targetOffset = 0
				IconController:_updateSelectionGroup()
				runService.Heartbeat:Wait()
				local indicatorSizeTrip = 50 --indicator.AbsoluteSize.Y * 2
				for otherIcon, _ in pairs(topbarIcons) do
					if IconController.canShowIconOnTopbar(otherIcon) and (selectIcon == nil or otherIcon:get("order") > selectIcon:get("order")) then
						selectIcon = otherIcon
					end
					local container = otherIcon.instances.iconContainer
					local newTargetOffset = -27 + container.AbsoluteSize.Y + indicatorSizeTrip
					if newTargetOffset > targetOffset then
						targetOffset = newTargetOffset
					end
				end
				if guiService:GetEmotesMenuOpen() then
					guiService:SetEmotesMenuOpen(false)
				end
				if guiService:GetInspectMenuEnabled() then
					guiService:CloseInspectMenu()
				end
				local newSelectedObject = IconController._previousSelectedObject or selectIcon.instances.iconButton
				IconController._setControllerSelectedObject(newSelectedObject)
				indicator.Image = "rbxassetid://5278151071"
				indicator:TweenPosition(
					UDim2.new(0.5,0,0,targetOffset + STUPID_CONTROLLER_OFFSET),
					Enum.EasingDirection.Out,
					Enum.EasingStyle.Quad,
					0.1,
					true
				)
			end
		else
			if forceBool then
				indicator.Visible = false
			else
				indicator.Visible = topbarEnabledAccountingForMimic
			end
			if not TopbarPlusGui.TopbarContainer.Visible then return end
			guiService.AutoSelectGuiEnabled = true
			IconController:_updateSelectionGroup(true)
			TopbarPlusGui.TopbarContainer:TweenPosition(
				UDim2.new(0,0,0,-TopbarPlusGui.TopbarContainer.Size.Y.Offset + STUPID_CONTROLLER_OFFSET),
				Enum.EasingDirection.Out,
				Enum.EasingStyle.Quad,
				0.1,
				true,
				function()
					TopbarPlusGui.TopbarContainer.Visible = false
				end
			)
			indicator.Image = "rbxassetid://5278151556"
			indicator:TweenPosition(
				UDim2.new(0.5,0,0,5),
				Enum.EasingDirection.Out,
				Enum.EasingStyle.Quad,
				0.1,
				true
			)
		end
	else
		local topbarContainer = TopbarPlusGui.TopbarContainer
		if topbarEnabledAccountingForMimic then
			topbarContainer.Visible = bool
		else
			topbarContainer.Visible = false
		end
	end
end

function IconController.setGap(value, alignment)
	local newValue = tonumber(value) or 12
	local newAlignment = tostring(alignment):lower()
	if newAlignment == "left" or newAlignment == "mid" or newAlignment == "right" then
		IconController[newAlignment.."Gap"] = newValue
		IconController.updateTopbar()
		return
	end
	IconController.leftGap = newValue
	IconController.midGap = newValue
	IconController.rightGap = newValue
	IconController.updateTopbar()
end

function IconController.setLeftOffset(value)
	IconController.leftOffset = tonumber(value) or 0
	IconController.updateTopbar()
end

function IconController.setRightOffset(value)
	IconController.rightOffset = tonumber(value) or 0
	IconController.updateTopbar()
end

local localPlayer = players.LocalPlayer
local iconsToClearOnSpawn = {}
localPlayer.CharacterAdded:Connect(function()
	for _, icon in pairs(iconsToClearOnSpawn) do
		icon:destroy()
	end
	iconsToClearOnSpawn = {}
end)
function IconController.clearIconOnSpawn(icon)
	coroutine.wrap(function()
		local char = localPlayer.Character or localPlayer.CharacterAdded:Wait()
		table.insert(iconsToClearOnSpawn, icon)
	end)()
end



-- PRIVATE METHODS
function IconController:_updateSelectionGroup(clearAll)
	if IconController._navigationEnabled then
		guiService:RemoveSelectionGroup("TopbarPlusIcons")
	end
	if clearAll then
		guiService.CoreGuiNavigationEnabled = IconController._originalCoreGuiNavigationEnabled
		guiService.GuiNavigationEnabled = IconController._originalGuiNavigationEnabled
		IconController._navigationEnabled = nil
	elseif IconController.controllerModeEnabled then
		local icons = IconController.getIcons()
		local iconContainers = {}
		for i, otherIcon in pairs(icons) do
			local featureName = otherIcon.joinedFeatureName
			if not featureName or otherIcon._parentIcon[otherIcon.joinedFeatureName.."Open"] == true then
				table.insert(iconContainers, otherIcon.instances.iconButton)
			end
		end
		guiService:AddSelectionTuple("TopbarPlusIcons", table.unpack(iconContainers))
		if not IconController._navigationEnabled then
			IconController._originalCoreGuiNavigationEnabled = guiService.CoreGuiNavigationEnabled
			IconController._originalGuiNavigationEnabled = guiService.GuiNavigationEnabled
			guiService.CoreGuiNavigationEnabled = false
			guiService.GuiNavigationEnabled = true
			IconController._navigationEnabled = true
		end
	end
end

local function getScaleMultiplier()
	if guiService:IsTenFootInterface() then
		return 3
	else
		return 1.3
	end
end

function IconController._setControllerSelectedObject(object)
	local startId = (IconController._controllerSetCount and IconController._controllerSetCount + 1) or 0
	IconController._controllerSetCount = startId
	guiService.SelectedObject = object
	delay(0.1, function() -- blame the roblox guiservice its a piece of doo doo
		local finalId = IconController._controllerSetCount
		if startId == finalId then
			guiService.SelectedObject = object
		end
	end)
end

function IconController._enableControllerMode(bool)
	local indicator = TopbarPlusGui.Indicator
	local controllerOptionIcon = IconController.getIcon("_TopbarControllerOption")
	if IconController.controllerModeEnabled == bool then
		return
	end
	IconController.controllerModeEnabled = bool
	if bool then
		TopbarPlusGui.TopbarContainer.Position = UDim2.new(0,0,0,5)
		TopbarPlusGui.TopbarContainer.Visible = false
		local scaleMultiplier = getScaleMultiplier()
		indicator.Position = UDim2.new(0.5,0,0,5)
		indicator.Size = UDim2.new(0, 18*scaleMultiplier, 0, 18*scaleMultiplier)
		indicator.Image = "rbxassetid://5278151556"
		indicator.Visible = checkTopbarEnabledAccountingForMimic()
		indicator.Position = UDim2.new(0.5,0,0,5)
	else
		TopbarPlusGui.TopbarContainer.Position = UDim2.new(0,0,0,0)
		TopbarPlusGui.TopbarContainer.Visible = checkTopbarEnabledAccountingForMimic()
		indicator.Visible = false
		IconController._setControllerSelectedObject(nil)
	end
	for icon, _ in pairs(topbarIcons) do
		IconController._enableControllerModeForIcon(icon, bool)
	end
end

function IconController._enableControllerModeForIcon(icon, bool)
	local parentIcon = icon._parentIcon
	local featureName = icon.joinedFeatureName
	if parentIcon then
		icon:leave()
	end
	if bool then
		local scaleMultiplier = getScaleMultiplier()
		local currentSizeDeselected = icon:get("iconSize", "deselected")
		local currentSizeSelected = icon:get("iconSize", "selected")
		local currentSizeHovering = icon:getHovering("iconSize")
		icon:set("iconSize", UDim2.new(0, currentSizeDeselected.X.Offset*scaleMultiplier, 0, currentSizeDeselected.Y.Offset*scaleMultiplier), "deselected", "controllerMode")
		icon:set("iconSize", UDim2.new(0, currentSizeSelected.X.Offset*scaleMultiplier, 0, currentSizeSelected.Y.Offset*scaleMultiplier), "selected", "controllerMode")
		if currentSizeHovering then
			icon:set("iconSize", UDim2.new(0, currentSizeSelected.X.Offset*scaleMultiplier, 0, currentSizeSelected.Y.Offset*scaleMultiplier), "hovering", "controllerMode")
		end
		icon:set("alignment", "mid", "deselected", "controllerMode")
		icon:set("alignment", "mid", "selected", "controllerMode")
	else
		local states = {"deselected", "selected", "hovering"}
		for _, iconState in pairs(states) do
			local _, previousAlignment = icon:get("alignment", iconState, "controllerMode")
			if previousAlignment then
				icon:set("alignment", previousAlignment, iconState)
			end
			local currentSize, previousSize = icon:get("iconSize", iconState, "controllerMode")
			if previousSize then
				icon:set("iconSize", previousSize, iconState)
			end
		end
	end
	if parentIcon then
		icon:join(parentIcon, featureName)
	end
end



-- BEHAVIOUR
--Controller support
coroutine.wrap(function()
	
	-- Create PC 'Enter Controller Mode' Icon
	runService.Heartbeat:Wait() -- This is required to prevent an infinite recursion
	local Icon = require(script.Parent)
	local controllerOptionIcon = Icon.new()
		:setName("_TopbarControllerOption")
		:setOrder(100)
		:setImage("rbxassetid://5278150942")
		:setRight()
		:setEnabled(false)
		:setTip("Controller mode")
	controllerOptionIcon.deselectWhenOtherIconSelected = false

	-- This decides what controller widgets and displays to show based upon their connected inputs
	-- For example, if on PC with a controller, give the player the option to enable controller mode with a toggle
	-- While if using a console (no mouse, but controller) then bypass the toggle and automatically enable controller mode
	local function determineDisplay()
		local mouseEnabled = userInputService.MouseEnabled
		local controllerEnabled = userInputService.GamepadEnabled
		local iconIsSelected = controllerOptionIcon.isSelected
		if mouseEnabled and controllerEnabled then
			-- Show icon
			controllerOptionIcon:setEnabled(true)
		elseif mouseEnabled and not controllerEnabled then
			-- Hide icon, disableControllerMode
			controllerOptionIcon:setEnabled(false)
			IconController._enableControllerMode(false)
			controllerOptionIcon:deselect()
		elseif not mouseEnabled and controllerEnabled then
			-- Hide icon, _enableControllerMode
			controllerOptionIcon:setEnabled(false)
			IconController._enableControllerMode(true)
		end
	end
	userInputService:GetPropertyChangedSignal("MouseEnabled"):Connect(determineDisplay)
	userInputService.GamepadConnected:Connect(determineDisplay)
	userInputService.GamepadDisconnected:Connect(determineDisplay)
	determineDisplay()

	-- Enable/Disable Controller Mode when icon clicked
	local function iconClicked()
		local isSelected = controllerOptionIcon.isSelected
		local iconTip = (isSelected and "Normal mode") or "Controller mode"
		controllerOptionIcon:setTip(iconTip)
		IconController._enableControllerMode(isSelected)
	end
	controllerOptionIcon.selected:Connect(iconClicked)
	controllerOptionIcon.deselected:Connect(iconClicked)

	-- Hide/show topbar when indicator action selected in controller mode
	userInputService.InputBegan:Connect(function(input,gpe)
		if not IconController.controllerModeEnabled then return end
		if input.KeyCode == Enum.KeyCode.DPadDown then
			if not guiService.SelectedObject and checkTopbarEnabledAccountingForMimic() then
				IconController.setTopbarEnabled(true,false)
			end
		elseif input.KeyCode == Enum.KeyCode.ButtonB then
			IconController._previousSelectedObject = guiService.SelectedObject
			IconController._setControllerSelectedObject(nil)
			IconController.setTopbarEnabled(false,false)
		end
		input:Destroy()
	end)

	-- Setup overflow icons
	for alignment, detail in pairs(alignmentDetails) do
		if alignment ~= "mid" then
			local overflowName = "_overflowIcon-"..alignment
			local overflowIcon = Icon.new()
				:setImage(6069276526)
				:setName(overflowName)
				:setEnabled(false)
			detail.overflowIcon = overflowIcon
			overflowIcon.accountForWhenDisabled = true
			if alignment == "left" then
				overflowIcon:setOrder(math.huge)
				overflowIcon:setLeft()
				overflowIcon:set("dropdownAlignment", "right")
			elseif alignment == "right" then
				overflowIcon:setOrder(-math.huge)
				overflowIcon:setRight()
				overflowIcon:set("dropdownAlignment", "left")
			end
			overflowIcon.lockedSettings = {
				["iconImage"] = true,
				["order"] = true,
				["alignment"] = true,
			}
		end
	end
end)()

-- Mimic the enabling of the topbar when StarterGui:SetCore("TopbarEnabled", state) is called
coroutine.wrap(function()
	local chatScript = players.LocalPlayer.PlayerScripts:WaitForChild("ChatScript", 4) or game:GetService("Chat"):WaitForChild("ChatScript", 4)
	if not chatScript then return end
	local chatMain = chatScript:FindFirstChild("ChatMain")
	if not chatMain then return end
	local ChatMain = require(chatMain)
	ChatMain.CoreGuiEnabled:connect(function()
		local topbarEnabled = checkTopbarEnabled()
		if topbarEnabled == IconController.previousTopbarEnabled then
			IconController.updateTopbar()
			return "SetCoreGuiEnabled was called instead of SetCore"
		end
		if IconController.mimicCoreGui then
			IconController.previousTopbarEnabled = topbarEnabled
			if IconController.controllerModeEnabled then
				IconController.setTopbarEnabled(false,false)
			else
				IconController.setTopbarEnabled(topbarEnabled,false)
			end
		end
		IconController.updateTopbar()
	end)
	local makeVisible = checkTopbarEnabled()
	if not makeVisible and not IconController.mimicCoreGui then
		makeVisible = true
	end
	IconController.setTopbarEnabled(makeVisible, false)
end)()

-- Mimic roblox menu when opened and closed
guiService.MenuClosed:Connect(function()
	menuOpen = false
	if not IconController.controllerModeEnabled then
		IconController.setTopbarEnabled(IconController.topbarEnabled,false)
	end
end)
guiService.MenuOpened:Connect(function()
	menuOpen = true
	IconController.setTopbarEnabled(false,false)
end)

-- Add icons to an overflow if they overlap the screen bounds or other icons
workspace.CurrentCamera:GetPropertyChangedSignal("ViewportSize"):Connect(function()
	IconController.updateTopbar()
end)



return IconController