local A=game:GetService("\084\119\101\101\110\083\101\114\118\105\099\101")local f=game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")local m=game:GetService("\068\101\098\114\105\115")local x=game:GetService("\085\115\101\114\073\110\112\117\116\083\101\114\118\105\099\101")local n=game:GetService("\072\116\116\112\083\101\114\118\105\099\101")local N=game:GetService("\082\117\110\083\101\114\118\105\099\101")local v=game:GetService("\084\101\120\116\083\101\114\118\105\099\101")local t=game:GetService("\071\117\105\083\101\114\118\105\099\101")local u=game:GetService("\083\116\097\114\116\101\114\071\117\105")local M=game:GetService("\080\108\097\121\101\114\115")local w=require(script.IconController)local Y=require(script.Signal)local G=require(script.Maid)local U=require(script.TopbarPlusGui)local j=require(script.TopbarPlusReference)local a=j.getObject()local P=require(script.Themes)local h=U.ActiveItems local o=U.TopbarContainer local V=o.IconContainer local Q=P.Default local r=55 local X={}local K=a and require(a.Value)or{}K.__index=K if not a then j.addToReplicatedStorage()end function K.new()local f={}setmetatable(f,K)local m=G.new()f._maid=m f._hoveringMaid=m:give(G.new())f._dropdownClippingMaid=m:give(G.new())f._menuClippingMaid=m:give(G.new())local n={}f.instances=n local v=m:give(V:Clone())v.Visible=true v.Parent=o n.iconContainer=v n.iconButton=v.IconButton n.iconImage=n.iconButton.IconImage n.iconLabel=n.iconButton.IconLabel n.iconGradient=n.iconButton.IconGradient n.iconCorner=n.iconButton.IconCorner n.iconOverlay=v.IconOverlay n.iconOverlayCorner=n.iconOverlay.IconOverlayCorner n.noticeFrame=n.iconButton.NoticeFrame n.noticeLabel=n.noticeFrame.NoticeLabel n.captionContainer=v.CaptionContainer n.captionFrame=n.captionContainer.CaptionFrame n.captionLabel=n.captionContainer.CaptionLabel n.captionCorner=n.captionFrame.CaptionCorner n.captionOverlineContainer=n.captionContainer.CaptionOverlineContainer n.captionOverline=n.captionOverlineContainer.CaptionOverline n.captionOverlineCorner=n.captionOverline.CaptionOverlineCorner n.captionVisibilityBlocker=n.captionFrame.CaptionVisibilityBlocker n.captionVisibilityCorner=n.captionVisibilityBlocker.CaptionVisibilityCorner n.tipFrame=v.TipFrame n.tipLabel=n.tipFrame.TipLabel n.tipCorner=n.tipFrame.TipCorner n.dropdownContainer=v.DropdownContainer n.dropdownFrame=n.dropdownContainer.DropdownFrame n.dropdownList=n.dropdownFrame.DropdownList n.menuContainer=v.MenuContainer n.menuFrame=n.menuContainer.MenuFrame n.menuList=n.menuFrame.MenuList n.clickSound=v.ClickSound f._settings={action={toggleTransitionInfo={},resizeInfo={};repositionInfo={},captionFadeInfo={},tipFadeInfo={},dropdownSlideInfo={},menuSlideInfo={}};toggleable={iconBackgroundColor={instanceNames={"\105\099\111\110\066\117\116\116\111\110"},propertyName="\066\097\099\107\103\114\111\117\110\100\067\111\108\111\114\051"};iconBackgroundTransparency={instanceNames={"\105\099\111\110\066\117\116\116\111\110"};propertyName="\066\097\099\107\103\114\111\117\110\100\084\114\097\110\115\112\097\114\101\110\099\121"};iconCornerRadius={instanceNames={"\105\099\111\110\067\111\114\110\101\114";"\105\099\111\110\079\118\101\114\108\097\121\067\111\114\110\101\114"};propertyName="\067\111\114\110\101\114\082\097\100\105\117\115"},iconGradientColor={instanceNames={"\105\099\111\110\071\114\097\100\105\101\110\116"},propertyName="\067\111\108\111\114"},iconGradientRotation={instanceNames={"\105\099\111\110\071\114\097\100\105\101\110\116"},propertyName="\082\111\116\097\116\105\111\110"},iconImage={callMethods={f._updateIconSize};instanceNames={"\105\099\111\110\073\109\097\103\101"},propertyName="\073\109\097\103\101"};iconImageColor={instanceNames={"\105\099\111\110\073\109\097\103\101"};propertyName="\073\109\097\103\101\067\111\108\111\114\051"},iconImageTransparency={instanceNames={"\105\099\111\110\073\109\097\103\101"},propertyName="\073\109\097\103\101\084\114\097\110\115\112\097\114\101\110\099\121"};iconScale={instanceNames={"\105\099\111\110\066\117\116\116\111\110"};propertyName="\083\105\122\101"};forcedIconSize={},iconSize={callSignals={f.updated},callMethods={f._updateIconSize},instanceNames={"\105\099\111\110\067\111\110\116\097\105\110\101\114"};propertyName="\083\105\122\101",tweenAction="\114\101\115\105\122\101\073\110\102\111"};iconOffset={instanceNames={"\105\099\111\110\066\117\116\116\111\110"};propertyName="\080\111\115\105\116\105\111\110"},iconText={callMethods={f._updateIconSize};instanceNames={"\105\099\111\110\076\097\098\101\108"},propertyName="\084\101\120\116"},iconTextColor={instanceNames={"\105\099\111\110\076\097\098\101\108"},propertyName="\084\101\120\116\067\111\108\111\114\051"},iconFont={instanceNames={"\105\099\111\110\076\097\098\101\108"},propertyName="\070\111\110\116"};iconImageYScale={callMethods={f._updateIconSize}},iconImageRatio={callMethods={f._updateIconSize}};iconLabelYScale={callMethods={f._updateIconSize}};noticeCircleColor={instanceNames={"\110\111\116\105\099\101\070\114\097\109\101"},propertyName="\073\109\097\103\101\067\111\108\111\114\051"};noticeCircleImage={instanceNames={"\110\111\116\105\099\101\070\114\097\109\101"};propertyName="\073\109\097\103\101"};noticeTextColor={instanceNames={"\110\111\116\105\099\101\076\097\098\101\108"},propertyName="\084\101\120\116\067\111\108\111\114\051"},noticeImageTransparency={instanceNames={"\110\111\116\105\099\101\070\114\097\109\101"},propertyName="\073\109\097\103\101\084\114\097\110\115\112\097\114\101\110\099\121"};noticeTextTransparency={instanceNames={"\110\111\116\105\099\101\076\097\098\101\108"},propertyName="\084\101\120\116\084\114\097\110\115\112\097\114\101\110\099\121"},baseZIndex={callMethods={f._updateBaseZIndex}};order={callSignals={f.updated},instanceNames={"\105\099\111\110\067\111\110\116\097\105\110\101\114"},propertyName="\076\097\121\111\117\116\079\114\100\101\114"};alignment={callSignals={f.updated};callMethods={f._updateDropdown}},iconImageVisible={instanceNames={"\105\099\111\110\073\109\097\103\101"};propertyName="\086\105\115\105\098\108\101"};iconImageAnchorPoint={instanceNames={"\105\099\111\110\073\109\097\103\101"};propertyName="\065\110\099\104\111\114\080\111\105\110\116"},iconImagePosition={instanceNames={"\105\099\111\110\073\109\097\103\101"},propertyName="\080\111\115\105\116\105\111\110";tweenAction="\114\101\115\105\122\101\073\110\102\111"},iconImageSize={instanceNames={"\105\099\111\110\073\109\097\103\101"};propertyName="\083\105\122\101",tweenAction="\114\101\115\105\122\101\073\110\102\111"},iconImageTextXAlignment={instanceNames={"\105\099\111\110\073\109\097\103\101"};propertyName="\084\101\120\116\088\065\108\105\103\110\109\101\110\116"};iconLabelVisible={instanceNames={"\105\099\111\110\076\097\098\101\108"};propertyName="\086\105\115\105\098\108\101"};iconLabelAnchorPoint={instanceNames={"\105\099\111\110\076\097\098\101\108"};propertyName="\065\110\099\104\111\114\080\111\105\110\116"},iconLabelPosition={instanceNames={"\105\099\111\110\076\097\098\101\108"},propertyName="\080\111\115\105\116\105\111\110",tweenAction="\114\101\115\105\122\101\073\110\102\111"};iconLabelSize={instanceNames={"\105\099\111\110\076\097\098\101\108"};propertyName="\083\105\122\101",tweenAction="\114\101\115\105\122\101\073\110\102\111"},iconLabelTextXAlignment={instanceNames={"\105\099\111\110\076\097\098\101\108"};propertyName="\084\101\120\116\088\065\108\105\103\110\109\101\110\116"};iconLabelTextSize={instanceNames={"\105\099\111\110\076\097\098\101\108"},propertyName="\084\101\120\116\083\105\122\101"},noticeFramePosition={instanceNames={"\110\111\116\105\099\101\070\114\097\109\101"},propertyName="\080\111\115\105\116\105\111\110"},clickSoundId={instanceNames={"\099\108\105\099\107\083\111\117\110\100"};propertyName="\083\111\117\110\100\073\100"},clickVolume={instanceNames={"\099\108\105\099\107\083\111\117\110\100"};propertyName="\086\111\108\117\109\101"};clickPlaybackSpeed={instanceNames={"\099\108\105\099\107\083\111\117\110\100"},propertyName="\080\108\097\121\098\097\099\107\083\112\101\101\100"};clickTimePosition={instanceNames={"\099\108\105\099\107\083\111\117\110\100"};propertyName="\084\105\109\101\080\111\115\105\116\105\111\110"},iconRectOffset={instanceNames={"\105\099\111\110\073\109\097\103\101"};propertyName="\073\109\097\103\101\082\101\099\116\079\102\102\115\101\116"};iconRectSize={instanceNames={"\105\099\111\110\073\109\097\103\101"};propertyName="\073\109\097\103\101\082\101\099\116\083\105\122\101"}};other={captionBackgroundColor={instanceNames={"\099\097\112\116\105\111\110\070\114\097\109\101"},propertyName="\066\097\099\107\103\114\111\117\110\100\067\111\108\111\114\051"};captionBackgroundTransparency={instanceNames={"\099\097\112\116\105\111\110\070\114\097\109\101"},propertyName="\066\097\099\107\103\114\111\117\110\100\084\114\097\110\115\112\097\114\101\110\099\121",group="\099\097\112\116\105\111\110"},captionBlockerTransparency={instanceNames={"\099\097\112\116\105\111\110\086\105\115\105\098\105\108\105\116\121\066\108\111\099\107\101\114"};propertyName="\066\097\099\107\103\114\111\117\110\100\084\114\097\110\115\112\097\114\101\110\099\121";group="\099\097\112\116\105\111\110"};captionOverlineColor={instanceNames={"\099\097\112\116\105\111\110\079\118\101\114\108\105\110\101"},propertyName="\066\097\099\107\103\114\111\117\110\100\067\111\108\111\114\051"};captionOverlineTransparency={instanceNames={"\099\097\112\116\105\111\110\079\118\101\114\108\105\110\101"};propertyName="\066\097\099\107\103\114\111\117\110\100\084\114\097\110\115\112\097\114\101\110\099\121";group="\099\097\112\116\105\111\110"};captionTextColor={instanceNames={"\099\097\112\116\105\111\110\076\097\098\101\108"};propertyName="\084\101\120\116\067\111\108\111\114\051"};captionTextTransparency={instanceNames={"\099\097\112\116\105\111\110\076\097\098\101\108"};propertyName="\084\101\120\116\084\114\097\110\115\112\097\114\101\110\099\121";group="\099\097\112\116\105\111\110"},captionFont={instanceNames={"\099\097\112\116\105\111\110\076\097\098\101\108"},propertyName="\070\111\110\116"};captionCornerRadius={instanceNames={"\099\097\112\116\105\111\110\067\111\114\110\101\114";"\099\097\112\116\105\111\110\079\118\101\114\108\105\110\101\067\111\114\110\101\114";"\099\097\112\116\105\111\110\086\105\115\105\098\105\108\105\116\121\067\111\114\110\101\114"};propertyName="\067\111\114\110\101\114\082\097\100\105\117\115"},tipBackgroundColor={instanceNames={"\116\105\112\070\114\097\109\101"},propertyName="\066\097\099\107\103\114\111\117\110\100\067\111\108\111\114\051"};tipBackgroundTransparency={instanceNames={"\116\105\112\070\114\097\109\101"};propertyName="\066\097\099\107\103\114\111\117\110\100\084\114\097\110\115\112\097\114\101\110\099\121";group="\116\105\112"},tipTextColor={instanceNames={"\116\105\112\076\097\098\101\108"};propertyName="\084\101\120\116\067\111\108\111\114\051"};tipTextTransparency={instanceNames={"\116\105\112\076\097\098\101\108"},propertyName="\084\101\120\116\084\114\097\110\115\112\097\114\101\110\099\121";group="\116\105\112"},tipFont={instanceNames={"\116\105\112\076\097\098\101\108"};propertyName="\070\111\110\116"},tipCornerRadius={instanceNames={"\116\105\112\067\111\114\110\101\114"},propertyName="\067\111\114\110\101\114\082\097\100\105\117\115"};dropdownSize={instanceNames={"\100\114\111\112\100\111\119\110\067\111\110\116\097\105\110\101\114"};propertyName="\083\105\122\101";unique="\100\114\111\112\100\111\119\110"},dropdownCanvasSize={instanceNames={"\100\114\111\112\100\111\119\110\070\114\097\109\101"};propertyName="\067\097\110\118\097\115\083\105\122\101"};dropdownMaxIconsBeforeScroll={callMethods={f._updateDropdown}},dropdownMinWidth={callMethods={f._updateDropdown}},dropdownSquareCorners={callMethods={f._updateDropdown}};dropdownBindToggleToIcon={};dropdownToggleOnLongPress={},dropdownToggleOnRightClick={},dropdownCloseOnTapAway={};dropdownHidePlayerlistOnOverlap={},dropdownListPadding={callMethods={f._updateDropdown};instanceNames={"\100\114\111\112\100\111\119\110\076\105\115\116"};propertyName="\080\097\100\100\105\110\103"};dropdownAlignment={callMethods={f._updateDropdown}};dropdownScrollBarColor={instanceNames={"\100\114\111\112\100\111\119\110\070\114\097\109\101"},propertyName="\083\099\114\111\108\108\066\097\114\073\109\097\103\101\067\111\108\111\114\051"};dropdownScrollBarTransparency={instanceNames={"\100\114\111\112\100\111\119\110\070\114\097\109\101"},propertyName="\083\099\114\111\108\108\066\097\114\073\109\097\103\101\084\114\097\110\115\112\097\114\101\110\099\121"},dropdownScrollBarThickness={instanceNames={"\100\114\111\112\100\111\119\110\070\114\097\109\101"};propertyName="\083\099\114\111\108\108\066\097\114\084\104\105\099\107\110\101\115\115"},dropdownIgnoreClipping={callMethods={f._dropdownIgnoreClipping}},menuSize={instanceNames={"\109\101\110\117\067\111\110\116\097\105\110\101\114"},propertyName="\083\105\122\101",unique="\109\101\110\117"};menuCanvasSize={instanceNames={"\109\101\110\117\070\114\097\109\101"};propertyName="\067\097\110\118\097\115\083\105\122\101"};menuMaxIconsBeforeScroll={callMethods={f._updateMenu}};menuBindToggleToIcon={};menuToggleOnLongPress={},menuToggleOnRightClick={},menuCloseOnTapAway={},menuListPadding={callMethods={f._updateMenu},instanceNames={"\109\101\110\117\076\105\115\116"},propertyName="\080\097\100\100\105\110\103"};menuDirection={callMethods={f._updateMenu}},menuScrollBarColor={instanceNames={"\109\101\110\117\070\114\097\109\101"},propertyName="\083\099\114\111\108\108\066\097\114\073\109\097\103\101\067\111\108\111\114\051"};menuScrollBarTransparency={instanceNames={"\109\101\110\117\070\114\097\109\101"},propertyName="\083\099\114\111\108\108\066\097\114\073\109\097\103\101\084\114\097\110\115\112\097\114\101\110\099\121"},menuScrollBarThickness={instanceNames={"\109\101\110\117\070\114\097\109\101"};propertyName="\083\099\114\111\108\108\066\097\114\084\104\105\099\107\110\101\115\115"},menuIgnoreClipping={callMethods={f._menuIgnoreClipping}}}}f._groupSettings={}for A,m in pairs(f._settings)do for A,m in pairs(m)do local x=m.group if x then local n=f._groupSettings[x]if not n then n={}f._groupSettings[x]=n end table.insert(n,A)m.forcedGroupValue=X[x]m.useForcedGroupValue=true end end end f._settingsDictionary={}f._uniqueSettings={}f._uniqueSettingsDictionary={}f.uniqueValues={}local t={dropdown=function(m,x,n,N)local v=f:get("\100\114\111\112\100\111\119\110\083\108\105\100\101\073\110\102\111")local t=f:get("\100\114\111\112\100\111\119\110\066\105\110\100\084\111\103\103\108\101\084\111\073\099\111\110")local M=f:get("\100\114\111\112\100\111\119\110\072\105\100\101\080\108\097\121\101\114\108\105\115\116\079\110\079\118\101\114\108\097\112")==true and f:get("\097\108\105\103\110\109\101\110\116")=="\114\105\103\104\116"local Y=f.instances.dropdownContainer local G=f.instances.dropdownFrame local U=N local j=true local a=not f.isSelected if t==false then a=not f.dropdownOpen end local P=f._longPressing or f._rightClicking if f._tappingAway or a and not P or P and f.dropdownOpen then local A=f:get("\100\114\111\112\100\111\119\110\083\105\122\101")local m=A and A.X.Offset/1 or 0 U=UDim2.new(0,m,0,0)j=false end if#f.dropdownIcons>0 and(j and M)then if u:GetCoreGuiEnabled(Enum.CoreGuiType.PlayerList)then u:SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList,false)end w._bringBackPlayerlist=w._bringBackPlayerlist and w._bringBackPlayerlist+1 or 1 f._bringBackPlayerlist=true elseif f._bringBackPlayerlist and(not j and w._bringBackPlayerlist)then w._bringBackPlayerlist-=1 if w._bringBackPlayerlist<=0 then w._bringBackPlayerlist=nil u:SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList,true)end f._bringBackPlayerlist=nil end local h=A:Create(x,v,{[n]=U})local o o=h.Completed:Connect(function()o:Disconnect()end)h:Play()if j then G.CanvasPosition=f._dropdownCanvasPos else f._dropdownCanvasPos=G.CanvasPosition end f.dropdownOpen=j f:_decideToCallSignal("\100\114\111\112\100\111\119\110")end;menu=function(m,x,n,N)local v=f:get("\109\101\110\117\083\108\105\100\101\073\110\102\111")local t=f:get("\109\101\110\117\066\105\110\100\084\111\103\103\108\101\084\111\073\099\111\110")local u=f.instances.menuContainer local M=f.instances.menuFrame local w=N local Y=true local G=not f.isSelected if t==false then G=not f.menuOpen end local U=f._longPressing or f._rightClicking if f._tappingAway or G and not U or U and f.menuOpen then local A=f:get("\109\101\110\117\083\105\122\101")local m=A and A.Y.Offset/1 or 0 w=UDim2.new(0,0,0,m)Y=false end if Y~=f.menuOpen then f.updated:Fire()end if Y and v.EasingDirection==Enum.EasingDirection.Out then v=TweenInfo.new(v.Time,v.EasingStyle,Enum.EasingDirection.In)end local j=A:Create(x,v,{[n]=w})local a a=j.Completed:Connect(function()a:Disconnect()end)j:Play()if Y then if f._menuCanvasPos then M.CanvasPosition=f._menuCanvasPos end else f._menuCanvasPos=M.CanvasPosition end f.menuOpen=Y f:_decideToCallSignal("\109\101\110\117")end}for A,m in pairs(f._settings)do for m,x in pairs(m)do if A=="\116\111\103\103\108\101\097\098\108\101"then x.values=x.values or{deselected=nil,selected=nil}else x.value=nil end x.additionalValues={}x.type=A f._settingsDictionary[m]=x local n=x.unique if n then local A=f._uniqueSettings[n]or{}table.insert(A,m)f._uniqueSettings[n]=A f._uniqueSettingsDictionary[m]=t[n]end end end f.updated=m:give(Y.new())f.selected=m:give(Y.new())f.deselected=m:give(Y.new())f.toggled=m:give(Y.new())f.hoverStarted=m:give(Y.new())f.hoverEnded=m:give(Y.new())f.dropdownOpened=m:give(Y.new())f.dropdownClosed=m:give(Y.new())f.menuOpened=m:give(Y.new())f.menuClosed=m:give(Y.new())f.notified=m:give(Y.new())f._endNotices=m:give(Y.new())f._ignoreClippingChanged=m:give(Y.new())local function M(A,m)local x=f._parentIcon f:set(A.."\073\103\110\111\114\101\067\108\105\112\112\105\110\103",m)if m==true and x then local m=x._ignoreClippingChanged:Connect(function(m,x)f:set(A.."\073\103\110\111\114\101\067\108\105\112\112\105\110\103",x)end)local n n=f[A.."\067\108\111\115\101\100"]:Connect(function()n:Disconnect()m:Disconnect()end)end end f.dropdownOpened:Connect(function()M("\100\114\111\112\100\111\119\110",true)end)f.dropdownClosed:Connect(function()M("\100\114\111\112\100\111\119\110",false)end)f.menuOpened:Connect(function()M("\109\101\110\117",true)end)f.menuClosed:Connect(function()M("\109\101\110\117",false)end)f.deselectWhenOtherIconSelected=true f.name=""f.isSelected=false f.presentOnTopbar=true f.accountForWhenDisabled=false f.enabled=true f.hovering=false f.tipText=nil f.captionText=nil f.totalNotices=0 f.notices={}f.dropdownIcons={}f.menuIcons={}f.dropdownOpen=false f.menuOpen=false f.locked=false f.topPadding=UDim.new(0,4)f.targetPosition=nil f.toggleItems={}f.lockedSettings={}f._draggingFinger=false f._updatingIconSize=true f._previousDropdownOpen=false f._previousMenuOpen=false f._bindedToggleKeys={}f._bindedEvents={}f:setName("\085\110\110\097\109\101\100\073\099\111\110")f:setTheme(Q,true)n.iconButton.MouseButton1Click:Connect(function()if f._draggingFinger then return false elseif f.isSelected then f:deselect()return true end f:select()end)n.iconButton.MouseButton2Click:Connect(function()f._rightClicking=true if f:get("\100\114\111\112\100\111\119\110\084\111\103\103\108\101\079\110\082\105\103\104\116\067\108\105\099\107")==true then f:_update("\100\114\111\112\100\111\119\110\083\105\122\101")end if f:get("\109\101\110\117\084\111\103\103\108\101\079\110\082\105\103\104\116\067\108\105\099\107")==true then f:_update("\109\101\110\117\083\105\122\101")end f._rightClicking=false end)n.iconButton.MouseButton1Down:Connect(function()if f.locked then return end f:_updateStateOverlay(.7,Color3.new(0,0,0))end)n.iconButton.MouseButton1Up:Connect(function()if f.locked then return end f:_updateStateOverlay(.9,Color3.new(1,1,1))end)x.InputBegan:Connect(function(A,m)local x={[Enum.UserInputType.MouseButton1]=true;[Enum.UserInputType.MouseButton2]=true,[Enum.UserInputType.MouseButton3]=true,[Enum.UserInputType.Touch]=true}if not m and x[A.UserInputType]then f._tappingAway=true if f.dropdownOpen and f:get("\100\114\111\112\100\111\119\110\067\108\111\115\101\079\110\084\097\112\065\119\097\121")==true then f:_update("\100\114\111\112\100\111\119\110\083\105\122\101")end if f.menuOpen and f:get("\109\101\110\117\067\108\111\115\101\079\110\084\097\112\065\119\097\121")==true then f:_update("\109\101\110\117\083\105\122\101")end f._tappingAway=false end if f._bindedToggleKeys[A.KeyCode]and not m then if f.isSelected then f:deselect()else f:select()end end end)f.hoverStarted:Connect(function(A,m)f.hovering=true if not f.locked then f:_updateStateOverlay(.9,Color3.fromRGB(255,255,255))end f:_updateHovering()end)f.hoverEnded:Connect(function()f.hovering=false f:_updateStateOverlay(1)f._hoveringMaid:clean()f:_updateHovering()end)n.iconButton.MouseEnter:Connect(function(A,m)f.hoverStarted:Fire(A,m)end)n.iconButton.MouseLeave:Connect(function()f.hoverEnded:Fire()end)n.iconButton.SelectionGained:Connect(function()f.hoverStarted:Fire()end)n.iconButton.SelectionLost:Connect(function()f.hoverEnded:Fire()end)n.iconButton.MouseButton1Down:Connect(function()if f._draggingFinger then f.hoverStarted:Fire()end local A local m local x=.7 local v=tick()+x A=N.Heartbeat:Connect(function()if tick()>=v then m:Disconnect()A:Disconnect()f._longPressing=true if f:get("\100\114\111\112\100\111\119\110\084\111\103\103\108\101\079\110\076\111\110\103\080\114\101\115\115")==true then f:_update("\100\114\111\112\100\111\119\110\083\105\122\101")end if f:get("\109\101\110\117\084\111\103\103\108\101\079\110\076\111\110\103\080\114\101\115\115")==true then f:_update("\109\101\110\117\083\105\122\101")end f._longPressing=false end end)m=n.iconButton.MouseButton1Up:Connect(function()m:Disconnect()A:Disconnect()end)end)if x.TouchEnabled then n.iconButton.MouseButton1Up:Connect(function()if f.hovering then f.hoverEnded:Fire()end end)local A=0 x.TouchMoved:Connect(function(A,m)if m then return end f._draggingFinger=true end)x.TouchEnded:Connect(function()f._draggingFinger=false end)end f._updatingIconSize=false f:_updateIconSize()w.iconAdded:Fire(f)return f end function K.mimic(A)local f=A.."\077\105\109\105\099"local m=w.getIcon(f)if m then return m end m=K.new()m:setName(f)if A=="\067\104\097\116"then m:setOrder(-1)m:setImage("\114\098\120\097\115\115\101\116\058\047\047\116\101\120\116\117\114\101\115\047\117\105\047\084\111\112\066\097\114\047\099\104\097\116\079\102\102\046\112\110\103","\100\101\115\101\108\101\099\116\101\100")m:setImage("\114\098\120\097\115\115\101\116\058\047\047\116\101\120\116\117\114\101\115\047\117\105\047\084\111\112\066\097\114\047\099\104\097\116\079\110\046\112\110\103","\115\101\108\101\099\116\101\100")m:setImageYScale(.625)end return m end function K.set(n,A,f,m,x)local N=n._settingsDictionary[A]assert(N~=nil,("\115\101\116\116\105\110\103 \'\037\115\' \100\111\101\115 \110\111\116 \101\120\105\115\116"):format(A))if type(m)=="\115\116\114\105\110\103"then m=m:lower()end local v=n:get(A,m)if m=="\104\111\118\101\114\105\110\103"then N.hoveringValue=f if x~="\095\105\103\110\111\114\101\080\114\101\118\105\111\117\115"then N.additionalValues["\112\114\101\118\105\111\117\115\095"..m]=v end if type(x)=="\115\116\114\105\110\103"then N.additionalValues[x..("\095"..m)]=v end n:_update(A)else local t=m local u=N.type if u=="\116\111\103\103\108\101\097\098\108\101"then local A={}if t=="\100\101\115\101\108\101\099\116\101\100"or t=="\115\101\108\101\099\116\101\100"then table.insert(A,t)else table.insert(A,"\100\101\115\101\108\101\099\116\101\100")table.insert(A,"\115\101\108\101\099\116\101\100")t=nil end for A,m in pairs(A)do N.values[m]=f if x~="\095\105\103\110\111\114\101\080\114\101\118\105\111\117\115"then N.additionalValues["\112\114\101\118\105\111\117\115\095"..m]=v end if type(x)=="\115\116\114\105\110\103"then N.additionalValues[x..("\095"..m)]=v end end else N.value=f if type(x)=="\115\116\114\105\110\103"then if x~="\095\105\103\110\111\114\101\080\114\101\118\105\111\117\115"then N.additionalValues.previous=v end N.additionalValues[x]=v end end if v==f then return n,"\086\097\108\117\101 \119\097\115 \097\108\114\101\097\100\121 \115\101\116"end local M=n:getToggleState()if not n._updateAfterSettingAll and(N.instanceNames and(M==t or t==nil))then local f=A=="\105\099\111\110\083\105\122\101"and(v and v.X.Scale==1)local m=N.tweenAction and(not f and n:get(N.tweenAction))or TweenInfo.new(0)n:_update(A,M,m)end end if N.callMethods then for A,x in pairs(N.callMethods)do x(n,f,m)end end if N.callSignals then for A,f in pairs(N.callSignals)do f:Fire()end end return n end function K.get(x,A,f,m)local n=x._settingsDictionary[A]assert(n~=nil,("\115\101\116\116\105\110\103 \'\037\115\' \100\111\101\115 \110\111\116 \101\120\105\115\116"):format(A))local N,v if typeof(f)=="\115\116\114\105\110\103"then f=f:lower()end if f=="\104\111\118\101\114\105\110\103"and m==nil then N=n.hoveringValue v=type(m)=="\115\116\114\105\110\103"and n.additionalValues[m..("\095"..f)]end local t=n.type if t=="\116\111\103\103\108\101\097\098\108\101"then local A=(f=="\100\101\115\101\108\101\099\116\101\100"or f=="\115\101\108\101\099\116\101\100")and f or x:getToggleState()if v==nil then v=type(m)=="\115\116\114\105\110\103"and n.additionalValues[m..("\095"..A)]end if N==nil then N=n.values[A]end else if v==nil then v=type(m)=="\115\116\114\105\110\103"and n.additionalValues[m]end if N==nil then N=n.value end end return N,v end function K.getHovering(f,A)local m=f._settingsDictionary[A]assert(m~=nil,("\115\101\116\116\105\110\103 \'\037\115\' \100\111\101\115 \110\111\116 \101\120\105\115\116"):format(A))return m.hoveringValue end function K.getToggleState(f,A)A=A or f.isSelected return A and"\115\101\108\101\099\116\101\100"or"\100\101\115\101\108\101\099\116\101\100"end function K.getIconState(A)if A.hovering then return"\104\111\118\101\114\105\110\103"else return A:getToggleState()end end function K._update(n,f,m,x)local N=n._settingsDictionary[f]assert(N~=nil,("\115\101\116\116\105\110\103 \'\037\115\' \100\111\101\115 \110\111\116 \101\120\105\115\116"):format(f))m=m or n:getToggleState()local v=N.value or N.values and N.values[m]if n.hovering and N.hoveringValue then v=N.hoveringValue end if v==nil then return end local t=x or N.tweenAction and n:get(N.tweenAction)or n:get("\116\111\103\103\108\101\084\114\097\110\115\105\116\105\111\110\073\110\102\111")or TweenInfo.new(.15)local u=N.propertyName local M={string=true;NumberSequence=true;Text=true;EnumItem=true;ColorSequence=true}local w=n._uniqueSettingsDictionary[f]local Y=v if N.useForcedGroupValue then Y=N.forcedGroupValue end if N.instanceNames then for m,x in pairs(N.instanceNames)do local N=n.instances[x]local G=typeof(N[u])local U=M[G]or typeof(N)=="\116\097\098\108\101"if w then w(f,N,u,Y)elseif U then N[u]=v else(A:Create(N,t,{[u]=Y})):Play()end if f=="\105\099\111\110\083\105\122\101"and N[u]~=Y then n.updated:Fire()end end end end function K._updateAll(m,A,f)for x,n in pairs(m._settingsDictionary)do if n.instanceNames then m:_update(x,A,f)end end end function K._updateHovering(f,A)for m,x in pairs(f._settingsDictionary)do if x.instanceNames and x.hoveringValue~=nil then f:_update(m,nil,A)end end end function K._updateStateOverlay(m,A,f)local x=m.instances.iconOverlay x.BackgroundTransparency=A or 1 x.BackgroundColor3=f or Color3.new(1,1,1)end function K.setTheme(m,A,f)m._updateAfterSettingAll=f for A,f in pairs(A)do if A=="\116\111\103\103\108\101\097\098\108\101"then for A,f in pairs(f.deselected)do if not m.lockedSettings[A]then m:set(A,f,"\098\111\116\104")end end for A,f in pairs(f.selected)do if not m.lockedSettings[A]then m:set(A,f,"\115\101\108\101\099\116\101\100")end end else for A,f in pairs(f)do if not m.lockedSettings[A]then m:set(A,f)end end end end m._updateAfterSettingAll=nil if f then m:_updateAll()end return m end function K.getInstance(f,A)return f.instances[A]end function K.setInstance(m,A,f)local x=m.instances[A]m.instances[A]=f if x then x:Destroy()end return m end function K.getSettingDetail(f,A)for f,m in pairs(f._settings)do for f,m in pairs(m)do if f==A then return m end end end return false end function K.modifySetting(m,A,f)local x=m:getSettingDetail(A)for A,f in pairs(f)do x[A]=f end return m end function K.convertLabelToNumberSpinner(f,A)f:set("\105\099\111\110\076\097\098\101\108\083\105\122\101",UDim2.new(1,0,1,0))A.Parent=f:getInstance("\105\099\111\110\066\117\116\116\111\110")local m={}setmetatable(m,{__newindex=function(f,m,x)for A,f in pairs(A.Frame:GetDescendants())do if f:IsA("\084\101\120\116\076\097\098\101\108")then f[m]=x end end end})local x=f:getInstance("\105\099\111\110\066\117\116\116\111\110")x.ZIndex=0 f:setInstance("\105\099\111\110\076\097\098\101\108",m)f:modifySetting("\105\099\111\110\084\101\120\116",{instanceNames={}})f:setInstance("\105\099\111\110\076\097\098\101\108\083\112\105\110\110\101\114",A.Frame)local n={"\105\099\111\110\076\097\098\101\108\086\105\115\105\098\108\101";"\105\099\111\110\076\097\098\101\108\065\110\099\104\111\114\080\111\105\110\116","\105\099\111\110\076\097\098\101\108\080\111\115\105\116\105\111\110","\105\099\111\110\076\097\098\101\108\083\105\122\101"}for A,m in pairs(n)do f:modifySetting(m,{instanceNames={"\105\099\111\110\076\097\098\101\108\083\112\105\110\110\101\114"}})end f:_updateAll()return f end function K.setEnabled(f,A)f.enabled=A f.instances.iconContainer.Visible=A f.updated:Fire()return f end function K.setName(f,A)f.name=A f.instances.iconContainer.Name=A return f end function K.setProperty(m,A,f)m[A]=f return m end function K._playClickSound(A)local f=A.instances.clickSound if f.SoundId~=nil and(#f.SoundId>0 and f.Volume>0)then local A=f:Clone()A.Parent=f.Parent A:Play()m:AddItem(A,f.TimeLength)end end function K.select(f,A)if f.locked then return f end f.isSelected=true f:_setToggleItemsVisible(true,A)f:_updateNotice()f:_updateAll()f:_playClickSound()if#f.dropdownIcons>0 or#f.menuIcons>0 then w:_updateSelectionGroup()end f.selected:Fire()f.toggled:Fire(f.isSelected)return f end function K.deselect(f,A)if f.locked then return f end f.isSelected=false f:_setToggleItemsVisible(false,A)f:_updateNotice()f:_updateAll()f:_playClickSound()if#f.dropdownIcons>0 or#f.menuIcons>0 then w:_updateSelectionGroup()end f.deselected:Fire()f.toggled:Fire(f.isSelected)return f end function K.notify(m,A,f)(coroutine.wrap(function()if not A then A=m.deselected end if m._parentIcon then m._parentIcon:notify(A)end local x=Y.new()local N=m._endNotices:Connect(function()x:Fire()end)local v=A:Connect(function()x:Fire()end)f=f or n:GenerateGUID(true)m.notices[f]={completeSignal=x,clearNoticeEvent=A}m.totalNotices+=1 m:_updateNotice()m.notified:Fire(f)x:Wait()N:Disconnect()v:Disconnect()x:Disconnect()m.totalNotices-=1 m.notices[f]=nil m:_updateNotice()end))()return m end function K._updateNotice(A)local f=true if A.totalNotices<1 then f=false end if not A.isSelected then if(#A.dropdownIcons>0 or#A.menuIcons>0)and A.totalNotices>0 then f=true end end if A.isSelected then if#A.dropdownIcons>0 or#A.menuIcons>0 then f=false end end local m=f and 0 or 1 A:set("\110\111\116\105\099\101\073\109\097\103\101\084\114\097\110\115\112\097\114\101\110\099\121",m)A:set("\110\111\116\105\099\101\084\101\120\116\084\114\097\110\115\112\097\114\101\110\099\121",m)A.instances.noticeLabel.Text=A.totalNotices<100 and A.totalNotices or"\057\057\043"end function K.clearNotices(A)A._endNotices:Fire()return A end function K.disableStateOverlay(f,A)if A==nil then A=true end local m=f.instances.iconOverlay m.Visible=not A return f end function K.setLabel(m,A,f)A=A or""m:set("\105\099\111\110\084\101\120\116",A,f)return m end function K.setCornerRadius(x,A,f,m)local n=x.instances.iconCorner.CornerRadius local N=UDim.new(A or n.Scale,f or n.Offset)x:set("\105\099\111\110\067\111\114\110\101\114\082\097\100\105\117\115",N,m)return x end function K.setImage(m,A,f)local x=tonumber(A)and"\104\116\116\112\058\047\047\119\119\119\046\114\111\098\108\111\120\046\099\111\109\047\097\115\115\101\116\047\063\105\100\061"..A or A or""return m:set("\105\099\111\110\073\109\097\103\101",x,f)end function K.setOrder(m,A,f)local x=tonumber(A)or 1 return m:set("\111\114\100\101\114",x,f)end function K.setLeft(f,A)return f:set("\097\108\105\103\110\109\101\110\116","\108\101\102\116",A)end function K.setMid(f,A)return f:set("\097\108\105\103\110\109\101\110\116","\109\105\100",A)end function K.setRight(f,A)return f:set("\097\108\105\103\110\109\101\110\116","\114\105\103\104\116",A)end function K.setImageYScale(m,A,f)local x=tonumber(A)or.63 return m:set("\105\099\111\110\073\109\097\103\101\089\083\099\097\108\101",x,f)end function K.setImageRatio(m,A,f)local x=tonumber(A)or 1 return m:set("\105\099\111\110\073\109\097\103\101\082\097\116\105\111",x,f)end function K.setLabelYScale(m,A,f)local x=tonumber(A)or.45 return m:set("\105\099\111\110\076\097\098\101\108\089\083\099\097\108\101",x,f)end function K.setBaseZIndex(m,A,f)local x=tonumber(A)or 1 return m:set("\098\097\115\101\090\073\110\100\101\120",x,f)end function K._updateBaseZIndex(f,A)local m=f.instances.iconContainer local x=tonumber(A)or m.ZIndex local n=x-m.ZIndex if n==0 then return"\084\104\101 \098\097\115\101\086\097\108\117\101 \105\115 \116\104\101 \115\097\109\101"end for A,f in pairs(f.instances)do f.ZIndex=f.ZIndex+n end return true end function K.setSize(x,A,f,m)local n=tonumber(A)or 32 local N=tonumber(f)or n x:set("\102\111\114\099\101\100\073\099\111\110\083\105\122\101",UDim2.new(0,n,0,N),m)x:set("\105\099\111\110\083\105\122\101",UDim2.new(0,n,0,N),m)return x end function K._updateIconSize(m,A,f)if m._destroyed then return end local x=12 local n=8 local N={iconImage=m:get("\105\099\111\110\073\109\097\103\101",f)or"\095\078\073\076",iconText=m:get("\105\099\111\110\084\101\120\116",f)or"\095\078\073\076",iconFont=m:get("\105\099\111\110\070\111\110\116",f)or"\095\078\073\076",iconSize=m:get("\105\099\111\110\083\105\122\101",f)or"\095\078\073\076",forcedIconSize=m:get("\102\111\114\099\101\100\073\099\111\110\083\105\122\101",f)or"\095\078\073\076";iconImageYScale=m:get("\105\099\111\110\073\109\097\103\101\089\083\099\097\108\101",f)or"\095\078\073\076",iconImageRatio=m:get("\105\099\111\110\073\109\097\103\101\082\097\116\105\111",f)or"\095\078\073\076",iconLabelYScale=m:get("\105\099\111\110\076\097\098\101\108\089\083\099\097\108\101",f)or"\095\078\073\076"}for A,f in pairs(N)do if f=="\095\078\073\076"then return end end local t=m.instances.iconContainer if not t.Parent then return end local u=N.iconSize.X.Offset local M=N.iconSize.X.Scale local w=u+M*t.Parent.AbsoluteSize.X local Y=N.forcedIconSize.X.Offset local G=M>0 and w or 9999 local U=N.iconSize.Y.Offset local j=N.iconSize.Y.Scale local a=U+j*t.Parent.AbsoluteSize.Y local P=a*N.iconLabelYScale local h=(v:GetTextSize(N.iconText,P,N.iconFont,Vector2.new(10000,P))).X local o=(a*N.iconImageYScale)*N.iconImageRatio local V=N.iconImage~=""local Q=N.iconText~=""local r=.5 local X local K=P/2 if V and not Q then r=.45 m:set("\105\099\111\110\073\109\097\103\101\086\105\115\105\098\108\101",true,f)m:set("\105\099\111\110\073\109\097\103\101\065\110\099\104\111\114\080\111\105\110\116",Vector2.new(.5,.5),f)m:set("\105\099\111\110\073\109\097\103\101\080\111\115\105\116\105\111\110",UDim2.new(.5,0,.5,0),f)m:set("\105\099\111\110\073\109\097\103\101\083\105\122\101",UDim2.new(N.iconImageYScale*N.iconImageRatio,0,N.iconImageYScale,0),f)m:set("\105\099\111\110\076\097\098\101\108\086\105\115\105\098\108\101",false,f)elseif not V and Q then X=h+x*2 m:set("\105\099\111\110\076\097\098\101\108\086\105\115\105\098\108\101",true,f)m:set("\105\099\111\110\076\097\098\101\108\065\110\099\104\111\114\080\111\105\110\116",Vector2.new(0,.5),f)m:set("\105\099\111\110\076\097\098\101\108\080\111\115\105\116\105\111\110",UDim2.new(0,x,.5,0),f)m:set("\105\099\111\110\076\097\098\101\108\083\105\122\101",UDim2.new(1,-x*2,N.iconLabelYScale,K),f)m:set("\105\099\111\110\076\097\098\101\108\084\101\120\116\088\065\108\105\103\110\109\101\110\116",Enum.TextXAlignment.Center,f)m:set("\105\099\111\110\073\109\097\103\101\086\105\115\105\098\108\101",false,f)elseif V and Q then local A=(x+o)+n X=(A+h)+x m:set("\105\099\111\110\073\109\097\103\101\086\105\115\105\098\108\101",true,f)m:set("\105\099\111\110\073\109\097\103\101\065\110\099\104\111\114\080\111\105\110\116",Vector2.new(0,.5),f)m:set("\105\099\111\110\073\109\097\103\101\080\111\115\105\116\105\111\110",UDim2.new(0,x,.5,0),f)m:set("\105\099\111\110\073\109\097\103\101\083\105\122\101",UDim2.new(0,o,N.iconImageYScale,0),f)m:set("\105\099\111\110\076\097\098\101\108\086\105\115\105\098\108\101",true,f)m:set("\105\099\111\110\076\097\098\101\108\065\110\099\104\111\114\080\111\105\110\116",Vector2.new(0,.5),f)m:set("\105\099\111\110\076\097\098\101\108\080\111\115\105\116\105\111\110",UDim2.new(0,A,.5,0),f)m:set("\105\099\111\110\076\097\098\101\108\083\105\122\101",UDim2.new(1,-A-x,N.iconLabelYScale,K),f)m:set("\105\099\111\110\076\097\098\101\108\084\101\120\116\088\065\108\105\103\110\109\101\110\116",Enum.TextXAlignment.Left,f)end if X then if not m._updatingIconSize then m._updatingIconSize=true local A=M>0 and M or 0 local x=M>0 and 0 or math.clamp(X,Y,G)m:set("\105\099\111\110\083\105\122\101",UDim2.new(A,x,N.iconSize.Y.Scale,N.iconSize.Y.Offset),f,"\095\105\103\110\111\114\101\080\114\101\118\105\111\117\115")m._updatingIconSize=false end end m:set("\105\099\111\110\076\097\098\101\108\084\101\120\116\083\105\122\101",P,f)m:set("\110\111\116\105\099\101\070\114\097\109\101\080\111\115\105\116\105\111\110",UDim2.new(r,0,0,-2),f)m._updatingIconSize=false end function K.bindEvent(m,A,f)local x=m[A]assert(x and(typeof(x)=="\116\097\098\108\101"and x.Connect),"\097\114\103\117\109\101\110\116\091\049\093 \109\117\115\116 \098\101 \097 \118\097\108\105\100 \116\111\112\098\097\114\112\108\117\115 \105\099\111\110 \101\118\101\110\116 \110\097\109\101\033")assert(typeof(f)=="\102\117\110\099\116\105\111\110","\097\114\103\117\109\101\110\116\091\050\093 \109\117\115\116 \098\101 \097 \102\117\110\099\116\105\111\110\033")m._bindedEvents[A]=x:Connect(function(...)f(m,...)end)return m end function K.unbindEvent(f,A)local m=f._bindedEvents[A]if m then m:Disconnect()f._bindedEvents[A]=nil end return f end function K.bindToggleKey(f,A)assert(typeof(A)=="\069\110\117\109\073\116\101\109","\097\114\103\117\109\101\110\116\091\049\093 \109\117\115\116 \098\101 \097 \075\101\121\067\111\100\101 \069\110\117\109\073\116\101\109\033")f._bindedToggleKeys[A]=true return f end function K.unbindToggleKey(f,A)assert(typeof(A)=="\069\110\117\109\073\116\101\109","\097\114\103\117\109\101\110\116\091\049\093 \109\117\115\116 \098\101 \097 \075\101\121\067\111\100\101 \069\110\117\109\073\116\101\109\033")f._bindedToggleKeys[A]=nil return f end function K.lock(A)A.locked=true return A end function K.unlock(A)A.locked=false return A end function K.setTopPadding(m,A,f)local x=A or 4 local n=f or 0 m.topPadding=UDim.new(n,x)m.updated:Fire()return m end function K.bindToggleItem(f,A)if not A:IsA("\071\117\105\079\098\106\101\099\116")and not A:IsA("\076\097\121\101\114\067\111\108\108\101\099\116\111\114")then error("\084\111\103\103\108\101 \105\116\101\109 \109\117\115\116 \098\101 \097 \071\117\105\079\098\106\101\099\116 \111\114 \076\097\121\101\114\067\111\108\108\101\099\116\111\114\033")end f.toggleItems[A]=true return f end function K.unbindToggleItem(f,A)f.toggleItems[A]=nil return f end function K._setToggleItemsVisible(m,A,f)for m,x in pairs(m.toggleItems)do if not f or f.toggleItems[m]==nil then local f="\086\105\115\105\098\108\101"if m:IsA("\076\097\121\101\114\067\111\108\108\101\099\116\111\114")then f="\069\110\097\098\108\101\100"end m[f]=A end end end function K.give(f,A)local m=A if typeof(A)=="\102\117\110\099\116\105\111\110"then local x=A(f)if typeof(A)~="\102\117\110\099\116\105\111\110"then m=x end end f._maid:give(m)return f end X.tip=1 function K.setTip(f,A)assert(typeof(A)=="\115\116\114\105\110\103"or A==nil,"\069\120\112\101\099\116\101\100 \115\116\114\105\110\103\044 \103\111\116 "..typeof(A))local m=A or""local x=m~=""local n=v:GetTextSize(m,12,Enum.Font.GothamSemibold,Vector2.new(1000,14))f.instances.tipLabel.Text=m f.instances.tipFrame.Size=x and UDim2.new(0,n.X+6,0,20)or UDim2.new(0,0,0,0)f.instances.tipFrame.Parent=x and h or f.instances.iconContainer f.tipText=A local N=G.new()f._maid.tipMaid=N if x then N:give(f.hoverStarted:Connect(function()if not f.isSelected then f:displayTip(true)end end))N:give(f.hoverEnded:Connect(function()f:displayTip(false)end))N:give(f.selected:Connect(function()if f.hovering then f:displayTip(false)end end))end f:displayTip(f.hovering and x)return f end function K.displayTip(f,A)if x.TouchEnabled and not f._draggingFinger then return end local m=f.tipVisible or false if typeof(A)=="\098\111\111\108\101\097\110"then m=A end f.tipVisible=m local n=f.instances.tipFrame if m then local function A(A,f)local m=A local N=f local v=workspace.CurrentCamera local t=v and v.ViewportSize if x.TouchEnabled then local A=m-n.Size.X.Offset/2 local f=0 local x=t.X-n.Size.X.Offset local v=(N+r)+60 local u=((n.AbsoluteSize.Y+r)+64)+3 local M=t.Y-n.Size.Y.Offset m=math.clamp(A,f,x)N=math.clamp(v,u,M)elseif w.controllerModeEnabled then local A=U.Indicator local f=A.AbsolutePosition m=(f.X-n.Size.X.Offset/2)+A.AbsoluteSize.X/2 N=f.Y+90 else local A=m local f=0 local x=(t.X-n.Size.X.Offset)-48 local v=N local u=n.Size.Y.Offset+3 local M=t.Y m=math.clamp(A,f,x)N=math.clamp(v,u,M)end n.Position=UDim2.new(0,m,0,N-20)end local m=x:GetMouseLocation()if m then A(m.X,m.Y)end f._hoveringMaid:give(f.instances.iconButton.MouseMoved:Connect(A))end for A,x in pairs(f._groupSettings.tip)do local n=f._settingsDictionary[x]n.useForcedGroupValue=not m f:_update(x)end end X.caption=1 function K.setCaption(f,A)assert(typeof(A)=="\115\116\114\105\110\103"or A==nil,"\069\120\112\101\099\116\101\100 \115\116\114\105\110\103\044 \103\111\116 "..typeof(A))local m=A or""local x=m~=""f.captionText=A f.instances.captionLabel.Text=m f.instances.captionContainer.Parent=x and h or f.instances.iconContainer f:_updateIconSize(nil,f:getIconState())local n=G.new()f._maid.captionMaid=n if x then n:give(f.hoverStarted:Connect(function()if not f.isSelected then f:displayCaption(true)end end))n:give(f.hoverEnded:Connect(function()f:displayCaption(false)end))n:give(f.selected:Connect(function()if f.hovering then f:displayCaption(false)end end))local A=f.instances.iconContainer n:give((A:GetPropertyChangedSignal("\065\098\115\111\108\117\116\101\083\105\122\101")):Connect(function()if f.hovering then f:displayCaption()end end))n:give((A:GetPropertyChangedSignal("\065\098\115\111\108\117\116\101\080\111\115\105\116\105\111\110")):Connect(function()if f.hovering then f:displayCaption()end end))end local N=6 local t=.8 local u=.58 local M=f:get("\105\099\111\110\083\105\122\101")local w=f:get("\099\097\112\116\105\111\110\070\111\110\116")if M and w then local A=M.Y.Offset local m=M.Y.Scale local n=f.instances.iconContainer local Y=f.instances.captionContainer if x then local x=A+m*n.Parent.AbsoluteSize.Y local M=f.instances.captionLabel local G=x*t local U=G*u local j=(v:GetTextSize(f.captionText,U,w,Vector2.new(10000,U))).X M.TextSize=U M.Size=UDim2.new(0,j,u,0)Y.Size=UDim2.new(0,j+N*2,0,x*t)else Y.Size=UDim2.new(0,0,0,0)end end f:displayCaption(f.hovering and x)return f end function K.displayCaption(f,A)if x.TouchEnabled and not f._draggingFinger then return end local m=8 if f._draggingFinger then m=m+r end local n=f.instances.iconContainer local N=f.instances.captionContainer local v=UDim2.new(0,(n.AbsolutePosition.X+n.AbsoluteSize.X/2)-N.AbsoluteSize.X/2,0,(n.AbsolutePosition.Y+n.AbsoluteSize.Y*2)+m)N.Position=v local t=f.captionVisible or false if typeof(A)=="\098\111\111\108\101\097\110"then t=A end f.captionVisible=t local u=f:get("\099\097\112\116\105\111\110\070\097\100\101\073\110\102\111")for A,m in pairs(f._groupSettings.caption)do local x=f._settingsDictionary[m]x.useForcedGroupValue=not t f:_update(m)end end function K.join(x,A,f,m)if x._parentIcon then x:leave()end local n=f and f:lower()or"\100\114\111\112\100\111\119\110"local N="\098\101\102\111\114\101"..((f:sub(1,1)):upper()..f:sub(2))local v=A.instances[f.."\070\114\097\109\101"]x.presentOnTopbar=false x.joinedFeatureName=f x._parentIcon=A x.instances.iconContainer.Parent=v for f,m in pairs(x.notices)do A:notify(m.clearNoticeEvent,f)end if f=="\100\114\111\112\100\111\119\110"then local f=A:get("\100\114\111\112\100\111\119\110\083\113\117\097\114\101\067\111\114\110\101\114\115")x:set("\105\099\111\110\083\105\122\101",UDim2.new(1,0,0,(x:get("\105\099\111\110\083\105\122\101","\100\101\115\101\108\101\099\116\101\100")).Y.Offset),"\100\101\115\101\108\101\099\116\101\100",N)x:set("\105\099\111\110\083\105\122\101",UDim2.new(1,0,0,(x:get("\105\099\111\110\083\105\122\101","\115\101\108\101\099\116\101\100")).Y.Offset),"\115\101\108\101\099\116\101\100",N)if f then x:set("\105\099\111\110\067\111\114\110\101\114\082\097\100\105\117\115",UDim.new(0,0),"\100\101\115\101\108\101\099\116\101\100",N)x:set("\105\099\111\110\067\111\114\110\101\114\082\097\100\105\117\115",UDim.new(0,0),"\115\101\108\101\099\116\101\100",N)end x:set("\099\097\112\116\105\111\110\066\108\111\099\107\101\114\084\114\097\110\115\112\097\114\101\110\099\121",.4,nil,N)end local t=A[n.."\073\099\111\110\115"]table.insert(t,x)if not m then A:_updateDropdown()end A.deselectWhenOtherIconSelected=false w:_updateSelectionGroup()x:_decideToCallSignal("\100\114\111\112\100\111\119\110")x:_decideToCallSignal("\109\101\110\117")return x end function K.leave(A)if A._destroyed then return end local f={"\105\099\111\110\083\105\122\101";"\099\097\112\116\105\111\110\066\108\111\099\107\101\114\084\114\097\110\115\112\097\114\101\110\099\121","\105\099\111\110\067\111\114\110\101\114\082\097\100\105\117\115"}local m=A._parentIcon A.instances.iconContainer.Parent=o A.presentOnTopbar=true A.joinedFeatureName=nil local function x(x,n,N)for v,t in pairs(x)do if t==A then for f,m in pairs(f)do local x={"\100\101\115\101\108\101\099\116\101\100","\115\101\108\101\099\116\101\100"}for f,x in pairs(x)do local N,v=A:get(m,x,n)if v then A:set(m,v,x)end end end table.remove(x,v)N(m)if#x==0 then A._parentIcon.deselectWhenOtherIconSelected=true end break end end end x(m.dropdownIcons,"\098\101\102\111\114\101\068\114\111\112\100\111\119\110",m._updateDropdown)x(m.menuIcons,"\098\101\102\111\114\101\077\101\110\117",m._updateMenu)for A,f in pairs(A.notices)do local x=m.notices[A]if x then x.completeSignal:Fire()end end A._parentIcon=nil w:_updateSelectionGroup()A:_decideToCallSignal("\100\114\111\112\100\111\119\110")A:_decideToCallSignal("\109\101\110\117")return A end function K._decideToCallSignal(f,A)local m=f[A.."\079\112\101\110"]local x="\095\112\114\101\118\105\111\117\115"..((string.sub(A,1,1)):upper()..(A:sub(2).."\079\112\101\110"))local n=f[x]local N=#f[A.."\073\099\111\110\115"]if m and(N>0 and n==false)then f[x]=true f[A.."\079\112\101\110\101\100"]:Fire()elseif(not m or N==0)and n==true then f[x]=false f[A.."\067\108\111\115\101\100"]:Fire()end end function K._ignoreClipping(f,A)local m=f:get(A.."\073\103\110\111\114\101\067\108\105\112\112\105\110\103")if f._parentIcon then local x=f["\095"..(A.."\067\108\105\112\112\105\110\103\077\097\105\100")]local n=f.instances[A.."\067\111\110\116\097\105\110\101\114"]x:clean()if m then local A=Instance.new("\070\114\097\109\101")A.Name=n.Name.."\070\097\107\101\070\114\097\109\101"A.ClipsDescendants=true A.BackgroundTransparency=1 A.Size=n.Size A.Position=n.Position A.Parent=h for f,m in pairs(n:GetChildren())do m.Parent=A end local function f()local f=n.AbsoluteSize A.Size=UDim2.new(0,f.X,0,f.Y)end x:give((n:GetPropertyChangedSignal("\065\098\115\111\108\117\116\101\083\105\122\101")):Connect(function()f()end))f()local function m()local f=n.absolutePosition A.Position=UDim2.new(0,f.X,0,f.Y+36)end x:give((n:GetPropertyChangedSignal("\065\098\115\111\108\117\116\101\080\111\115\105\116\105\111\110")):Connect(function()m()end))m()x:give(function()for A,f in pairs(A:GetChildren())do f.Parent=n end A.Name="\068\101\115\116\114\111\121\105\110\103\046\046\046"A:Destroy()end)end end f._ignoreClippingChanged:Fire(A,m)end function K.setDropdown(f,A)for A,f in pairs(f.dropdownIcons)do f:leave()end if type(A)=="\116\097\098\108\101"then for A,m in pairs(A)do m:join(f,"\100\114\111\112\100\111\119\110",true)end end f:_updateDropdown()return f end function K._updateDropdown(A)local f={maxIconsBeforeScroll=A:get("\100\114\111\112\100\111\119\110\077\097\120\073\099\111\110\115\066\101\102\111\114\101\083\099\114\111\108\108")or"\095\078\073\076";minWidth=A:get("\100\114\111\112\100\111\119\110\077\105\110\087\105\100\116\104")or"\095\078\073\076";padding=A:get("\100\114\111\112\100\111\119\110\076\105\115\116\080\097\100\100\105\110\103")or"\095\078\073\076",dropdownAlignment=A:get("\100\114\111\112\100\111\119\110\065\108\105\103\110\109\101\110\116")or"\095\078\073\076";iconAlignment=A:get("\097\108\105\103\110\109\101\110\116")or"\095\078\073\076",scrollBarThickness=A:get("\100\114\111\112\100\111\119\110\083\099\114\111\108\108\066\097\114\084\104\105\099\107\110\101\115\115")or"\095\078\073\076"}for A,f in pairs(f)do if f=="\095\078\073\076"then return end end local m=f.padding.Offset local x=A.instances.dropdownContainer local n=A.instances.dropdownFrame local N=A.instances.dropdownList local v=#A.dropdownIcons local t=v>f.maxIconsBeforeScroll and f.maxIconsBeforeScroll or v local u=-m local M=0 local w=f.minWidth table.sort(A.dropdownIcons,function(A,f)return A:get("\111\114\100\101\114")<f:get("\111\114\100\101\114")end)for f=1,v,1 do local x=A.dropdownIcons[f]local n,N=x:get("\105\099\111\110\083\105\122\101",nil,"\098\101\102\111\114\101\068\114\111\112\100\111\119\110")local Y=N.Y.Offset+m if f<=t then M=M+Y end if f==v then M=M+Y/4 end u=u+Y local G=N.X.Offset if G>w then w=G end end local Y=t==v and 0 or u A:set("\100\114\111\112\100\111\119\110\067\097\110\118\097\115\083\105\122\101",UDim2.new(0,0,0,Y))A:set("\100\114\111\112\100\111\119\110\083\105\122\101",UDim2.new(0,(w+4)*2,0,M))local G=f.dropdownAlignment:lower()local U={left={AnchorPoint=Vector2.new(0,0);PositionXScale=0,ThicknessMultiplier=0},mid={AnchorPoint=Vector2.new(.5,0),PositionXScale=.5,ThicknessMultiplier=.5};right={AnchorPoint=Vector2.new(.5,0),PositionXScale=1;FrameAnchorPoint=Vector2.new(0,0),FramePositionXScale=0;ThicknessMultiplier=1}}local j=U[G]if not j then j=U[f.iconAlignment:lower()]end x.AnchorPoint=j.AnchorPoint x.Position=UDim2.new(j.PositionXScale,0,1,m+0)local a=f.scrollBarThickness local P=a*j.ThicknessMultiplier local h=n.VerticalScrollBarPosition==Enum.VerticalScrollBarPosition.Right and P or-P n.AnchorPoint=j.FrameAnchorPoint or j.AnchorPoint n.Position=UDim2.new(j.FramePositionXScale or j.PositionXScale,h,0,0)A._dropdownCanvasPos=Vector2.new(0,0)end function K._dropdownIgnoreClipping(A)A:_ignoreClipping("\100\114\111\112\100\111\119\110")end function K.setMenu(f,A)for A,f in pairs(f.menuIcons)do f:leave()end if type(A)=="\116\097\098\108\101"then for A,m in pairs(A)do m:join(f,"\109\101\110\117",true)end end f:_updateMenu()return f end function K._getMenuDirection(A)local f=(A:get("\109\101\110\117\068\105\114\101\099\116\105\111\110")or"\095\078\073\076"):lower()local m=(A:get("\097\108\105\103\110\109\101\110\116")or"\095\078\073\076"):lower()if f~="\108\101\102\116"and f~="\114\105\103\104\116"then f=m=="\108\101\102\116"and"\114\105\103\104\116"or"\108\101\102\116"end return f end function K._updateMenu(A)local f={maxIconsBeforeScroll=A:get("\109\101\110\117\077\097\120\073\099\111\110\115\066\101\102\111\114\101\083\099\114\111\108\108")or"\095\078\073\076",direction=A:get("\109\101\110\117\068\105\114\101\099\116\105\111\110")or"\095\078\073\076";iconAlignment=A:get("\097\108\105\103\110\109\101\110\116")or"\095\078\073\076";scrollBarThickness=A:get("\109\101\110\117\083\099\114\111\108\108\066\097\114\084\104\105\099\107\110\101\115\115")or"\095\078\073\076"}for A,f in pairs(f)do if f=="\095\078\073\076"then return end end local m=w[f.iconAlignment.."\071\097\112"]local x=A.instances.menuContainer local n=A.instances.menuFrame local N=A.instances.menuList local v=#A.menuIcons local t=A:_getMenuDirection()local u=v>f.maxIconsBeforeScroll and f.maxIconsBeforeScroll or v local M=-m local Y=0 local G=0 local U=t=="\114\105\103\104\116"and function(A,f)return A:get("\111\114\100\101\114")<f:get("\111\114\100\101\114")end or function(A,f)return A:get("\111\114\100\101\114")>f:get("\111\114\100\101\114")end table.sort(A.menuIcons,U)for f=1,v,1 do local x=A.menuIcons[f]local n=x:get("\105\099\111\110\083\105\122\101")local N=n.X.Offset+m if f<=u then Y=Y+N end if f==u and f~=v then Y=Y-2 end M=M+N local t=n.Y.Offset if t>G then G=t end end local j=u==v and 0 or M+m A:set("\109\101\110\117\067\097\110\118\097\115\083\105\122\101",UDim2.new(0,j,0,0))A:set("\109\101\110\117\083\105\122\101",UDim2.new(0,Y,0,(G+f.scrollBarThickness)+3))local a={left={containerAnchorPoint=Vector2.new(1,0),containerPosition=UDim2.new(0,-4,0,0);canvasPosition=Vector2.new(j,0)},right={containerAnchorPoint=Vector2.new(0,0),containerPosition=UDim2.new(1,m-2,0,0);canvasPosition=Vector2.new(0,0)}}local P=a[t]x.AnchorPoint=P.containerAnchorPoint x.Position=P.containerPosition n.CanvasPosition=P.canvasPosition A._menuCanvasPos=P.canvasPosition N.Padding=UDim.new(0,m)end function K._menuIgnoreClipping(A)A:_ignoreClipping("\109\101\110\117")end function K.destroy(A)if A._destroyed then return end w.iconRemoved:Fire(A)A:clearNotices()if A._parentIcon then A:leave()end A:setDropdown()A:setMenu()A._destroyed=true A._maid:clean()end K.Destroy=K.destroy return K
--[[ icon:header
[themes]: https://1foreverhd.github.io/TopbarPlus/features/#themes
[set method]: https://1foreverhd.github.io/TopbarPlus/api/icon/#set

## Construtors

#### new
```lua
local icon = Icon.new()
```
Constructs an empty ``32x32`` icon on the topbar.

----



## Methods

#### set
{chainable}
```lua
icon:set(settingName, value, iconState)
```
Applies a specific setting to an icon. All settings can be found [here](https://github.com/1ForeverHD/TopbarPlus/blob/main/src/Icon/Themes/Default.lua). If the setting falls under the 'toggleable' category then an iconState can be specified. For most scenarious it's recommended instead to apply settings using [themes].

----
#### get
```lua
local value = icon:get(settingName, iconState)
```
Retrieves the given settings value. If the setting falls under the 'toggleable' category then an iconState can be specified.

----
#### getToggleState
```lua
local selectedOrDeselectedString = icon:getToggleState()
```
Returns the current toggleState, either "deselected" or "selected".

----
#### setTheme
{chainable}
```lua
icon:setTheme(theme)
```
Applies a theme to the given icon. See [themes] for more information.

----
#### setEnabled
{chainable}
```lua
icon:setEnabled(bool)
```
When set to ``false``, the icon will be disabled and hidden.

----
#### setName
{chainable}
```lua
icon:setName(string)
```
Associates the given name to the icon which enables it to be retrieved with ``IconController.getIcon(name)``.

----
#### setProperty
{chainable}
```lua
icon:setProperty(propertyName, value)
```
An alternative way of doing ``zone[propertyName] = value``. This enables the easy-configuration of icon properties within chained methods.

----
#### select
{chainable}
```lua
icon:select()
```
Selects the icon (as if it were clicked once).

----
#### deselect
{chainable}
```lua
icon:deselect()
```
Deselects the icon (as if it were clicked, then clicked again).

----
#### notify
{chainable}
```lua
icon:notify(clearNoticeEvent)
```
Prompts a notice bubble which accumulates the further it is prompted. If the icon belongs to a dropdown or menu, then the notice will appear on the parent icon when the parent icon is deselected.

----
#### clearNotices
{chainable}
```lua
icon:clearNotices()
```

----
#### disableStateOverlay
{chainable}
```lua
icon:disableStateOverlay(bool)
```
When set to ``true``, disables the shade effect which appears when the icon is pressed and released.

----
#### convertLabelToNumberSpinner
{chainable}
```lua
icon:convertLabelToNumberSpinner(numberSpinner)
```
Takes a [NumberSpinner](https://devforum.roblox.com/t/numberspinner-module/1105961) object (by boatbomber) and converts it into the icons label.

Example usage:

```lua
Icon.new()
    :setName("CashSpinnerIcon")
    :setRight()
    :lock()
    :setSize(100, 32)
    :give(function(icon)
        local NumberSpinner = require(replicatedStorage.NumberSpinner)
        local labelSpinner = NumberSpinner.new()
        icon:convertLabelToNumberSpinner(labelSpinner)
        labelSpinner.Name = "LabelSpinner"
        labelSpinner.Decimals = 3
        labelSpinner.Duration = 0.25
        coroutine.wrap(function()
            while wait(0.5) do
                labelSpinner.Value = math.random(100000)/1000
            end
        end)()
    end)
```

----
#### setImage
{chainable} {toggleable}
```lua
icon:setImage(imageId, iconState)
```
Applies an image to the icon based on the given ``imaageId``. ``imageId`` can be an assetId or a complete asset string.

----
#### setLabel
{chainable} {toggleable}
```lua
icon:setLabel(text, iconState)
```

----
#### setOrder
{chainable} {toggleable}
```lua
icon:setOrder(order, iconState)
```

----
#### setCornerRadius
{chainable} {toggleable}
```lua
icon:setCornerRadius(scale, offset, iconState)
```

----
#### setLeft
{chainable} {toggleable}
```lua
icon:setLeft(iconState)
```

----
#### setMid
{chainable} {toggleable}
```lua
icon:setMid(iconState)
```

----
#### setRight
{chainable} {toggleable}
```lua
icon:setRight(iconState)
```

----
#### setImageYScale
{chainable} {toggleable}
```lua
icon:setImageYScale(YScale, iconState)
```
Defines the proportional space the icons image takes up within the icons container.

----
#### setImageRatio
{chainable} {toggleable}
```lua
icon:setImageRatio(ratio, iconState)
```
Defines the x:y ratio dimensions as a number. By default ``ratio`` is ``1.00``.

----
#### setLabelYScale
{chainable} {toggleable}
```lua
icon:setLabelYScale(YScale, iconState)
```
Defines how large label text appears.By default ``YScale`` is ``0.45``.

----
#### setBaseZIndex
{chainable} {toggleable}
```lua
icon:setBaseZIndex(ZIndex, iconState)
```
Calculates the difference between the existing baseZIndex (i.e. ``instances.iconContainer.ZIndex``) and new value, then updates the ZIndex of all objects within the icon accoridngly using this difference.

----
#### setSize
{chainable} {toggleable}
```lua
icon:setSize(XOffset, YOffset, iconState)
```
Determines the icons container size. By default ``XOffset`` and ``YOffset`` are ``32``.

----
#### bindToggleItem
{chainable}
```lua
icon:bindToggleItem(guiObjectOrLayerCollector)
```
Binds a GuiObject or LayerCollector to appear and disappeared when the icon is toggled.

----
#### unbindToggleItem
{chainable}
```lua
icon:unbindToggleItem(guiObjectOrLayerCollector)
```
Unbinds the given GuiObject or LayerCollector from the toggle.

----
#### bindEvent
{chainable}
```lua
icon:bindEvent(iconEventName, eventFunction)
```
Connects to an [icon event](https://1foreverhd.github.io/TopbarPlus/api/icon/#events) based upon the given ``iconEventName`` and call ``eventFunction`` with arguments ``(self, ...)`` when the event is triggered.

----
#### unbindEvent
{chainable}
```lua
icon:unbindEvent(iconEventName)
```
Unbinds the connection of the associated ``iconEventName``.

----
#### bindToggleKey
{chainable}
```lua
icon:bindToggleKey(keyCodeEnum)
```
Binds a [keycode](https://developer.roblox.com/en-us/api-reference/enum/KeyCode) which toggles the icon when pressed.

----
#### unbindToggleKey
{chainable}
```lua
icon:unbindToggleKey(keyCodeEnum)
```
Unbinds the given keycode.

----
#### give
{chainable}
```lua
icon:give(userdata)
```
Passes the given userdata to the Icons maid to be destroyed/disconnected on the icons destruction. If a function is passed, it will be executed right away with its self (the icon) being passed as the first argument. The return value is then given to the maid (instead of the function).

----
#### lock
{chainable}
```lua
icon:lock()
```
Prevents the icon from being pressed and toggled.

----
#### unlock
{chainable}
```lua
icon:unlock()
```
Enables the icon to be pressed and toggled.

----
#### setTopPadding
{chainable}
```lua
icon:setTopPadding(offset, scale)
```
The gap between the top of the screen and the icon.

----
#### setTip
{chainable}
```lua
icon:setTip(text)
```
Sets a tip. To remove, pass ``nil`` as ``text``.

----
#### setCaption
{chainable}
```lua
icon:setCaption(text)
```
Sets a caption. To remove, pass ``nil`` as ``text``.

----
#### join
{chainable}
```lua
icon:join(parentIcon, featureName)
```
Parents the icon to the given parentIcon under the specified feature, either "dropdown" or "menu".

----
#### leave
{chainable}
```lua
icon:leave()
```
Unparents an icon from a parentIcon if it belongs to a dropdown or menu.

----
#### setDropdown
{chainable}
```lua
icon:setDropdown(arrayOfIcons)
```
Creates a vertical dropdown based upon the given ``table array`` of ``icons``. Pass an empty table ``{}`` to remove the dropdown. Dropdown settings can be configured using [themes] or the [set method].

----
#### setMenu
{chainable}
```lua
icon:setMenu(arrayOfIcons)
```
Creates a horizontal menu based upon the given ``table array`` of ``icons``. Pass an empty table ``{}`` to remove the menu. Menu settings can be configured using [themes] or the [set method].

----
#### destroy
{chainable}
```lua
icon:destroy()
```
Clears all connections and destroys all instances associated with the icon.

----



## Events
#### selected 
```lua
icon.selected:Connect(function()
    print("The icon was selected")
end)
```

----
#### deselected 
```lua
icon.deselected:Connect(function()
    print("The icon was deselected")
end)
```

----
#### toggled 
```lua
icon.toggled:Connect(function(isSelected)
    print(("The icon was %s"):format(icon:getToggleState(isSelected)))
end)
```

----
#### hoverStarted 
```lua
icon.hoverStarted:Connect(function()
    print("A mouse, finger or controller selection is hovering over the icon")
end)
```

----
#### hoverEnded 
```lua
icon.hoverEnded:Connect(function()
    print("The item is no longer hovering over the icon")
end)
```

----
#### dropdownOpened 
```lua
icon.dropdownOpened:Connect(function()
    print("The dropdown was opened")
end)
```

----
#### dropdownClosed 
```lua
icon.dropdownClosed:Connect(function()
    print("The dropdown was closed")
end)
```

----
#### menuOpened 
```lua
icon.menuOpened:Connect(function()
    print("The menu was opened")
end)
```

----
#### menuClosed 
```lua
icon.menuClosed:Connect(function()
    print("The menu was closed")
end)
```

----
#### notified 
```lua
icon.notified:Connect(function()
    print("New notice")
end)
```

----



## Properties
#### deselectWhenOtherIconSelected
```lua
local bool = icon.deselectWhenOtherIconSelected --[default: 'true']
```
A bool deciding whether the icon will be deselected when another icon is selected. Defaults to ``true``.

----
#### accountForWhenDisabled
```lua
local bool = icon.accountForWhenDisabled --[default: 'false']
```
A bool deciding whether to continue accounting for and updating the icons position on the topbar when disabled

----
#### name
{read-only}
```lua
local string = icon.name --[default: '"Unnamed Icon"']
```

----
#### isSelected
{read-only}
```lua
local bool = icon.isSelected
```

----
#### enabled
{read-only}
```lua
local bool = icon.enabled
```

----
#### hovering
{read-only}
```lua
local bool = icon.hovering
```

----
#### tipText
{read-only}
```lua
local stringOrNil = icon.tipText
```

----
#### captionText
{read-only}
```lua
local stringOrNil = icon.captionText
```

----
#### totalNotices
{read-only}
```lua
local int = icon.totalNotices
```

----
#### dropdownIcons
{read-only}
```lua
local arrayOfIcons = icon.dropdownIcons
```

----
#### menuIcons
{read-only}
```lua
local arrayOfIcons = icon.menuIcons
```

----
#### dropdownOpen
{read-only}
```lua
local bool = icon.dropdownOpen
```

----
#### menuOpen
{read-only}
```lua
local bool = icon.menuOpen
```

----
#### locked
{read-only}
```lua
local bool = icon.locked
```

----
#### topPadding
{read-only}
```lua
local udim = icon.topPadding
```

----
#### targetPosition
{read-only}
```lua
local udim2 = icon.targetPosition
```
The position the icon is at or aims to move to.
--]]



-- LOCAL
local tweenService = game:GetService("TweenService")
local replicatedStorage = game:GetService("ReplicatedStorage")
local debris = game:GetService("Debris")
local userInputService = game:GetService("UserInputService")
local httpService = game:GetService("HttpService") -- This is to generate GUIDs
local runService = game:GetService("RunService")
local textService = game:GetService("TextService")
local guiService = game:GetService("GuiService")
local starterGui = game:GetService("StarterGui")
local players = game:GetService("Players")
local IconController = require(script.IconController)
local Signal = require(script.Signal)
local Maid = require(script.Maid)
local TopbarPlusGui = require(script.TopbarPlusGui)
local TopbarPlusReference = require(script.TopbarPlusReference)
local referenceObject = TopbarPlusReference.getObject()
local Themes = require(script.Themes)
local activeItems = TopbarPlusGui.ActiveItems
local topbarContainer = TopbarPlusGui.TopbarContainer
local iconTemplate = topbarContainer["IconContainer"]
local DEFAULT_THEME = Themes.Default
local THUMB_OFFSET = 55
local DEFAULT_FORCED_GROUP_VALUES = {}
local Icon = (referenceObject and require(referenceObject.Value)) or {}
Icon.__index = Icon
if not referenceObject then
	TopbarPlusReference.addToReplicatedStorage()
end



-- CONSTRUCTORS
function Icon.new()
	local self = {}
	setmetatable(self, Icon)

	-- Maids (for autocleanup)
	local maid = Maid.new()
	self._maid = maid
	self._hoveringMaid = maid:give(Maid.new())
	self._dropdownClippingMaid = maid:give(Maid.new())
	self._menuClippingMaid = maid:give(Maid.new())

	-- These are the GuiObjects that make up the icon
	local instances = {}
	self.instances = instances
	local iconContainer = maid:give(iconTemplate:Clone())
	iconContainer.Visible = true
	iconContainer.Parent = topbarContainer
	instances["iconContainer"] = iconContainer
	instances["iconButton"] = iconContainer.IconButton
	instances["iconImage"] = instances.iconButton.IconImage
	instances["iconLabel"] = instances.iconButton.IconLabel
	instances["iconGradient"] = instances.iconButton.IconGradient
	instances["iconCorner"] = instances.iconButton.IconCorner
	instances["iconOverlay"] = iconContainer.IconOverlay
	instances["iconOverlayCorner"] = instances.iconOverlay.IconOverlayCorner
	instances["noticeFrame"] = instances.iconButton.NoticeFrame
	instances["noticeLabel"] = instances.noticeFrame.NoticeLabel
	instances["captionContainer"] = iconContainer.CaptionContainer
	instances["captionFrame"] = instances.captionContainer.CaptionFrame
	instances["captionLabel"] = instances.captionContainer.CaptionLabel
	instances["captionCorner"] = instances.captionFrame.CaptionCorner
	instances["captionOverlineContainer"] = instances.captionContainer.CaptionOverlineContainer
	instances["captionOverline"] = instances.captionOverlineContainer.CaptionOverline
	instances["captionOverlineCorner"] = instances.captionOverline.CaptionOverlineCorner
	instances["captionVisibilityBlocker"] = instances.captionFrame.CaptionVisibilityBlocker
	instances["captionVisibilityCorner"] = instances.captionVisibilityBlocker.CaptionVisibilityCorner
	instances["tipFrame"] = iconContainer.TipFrame
	instances["tipLabel"] = instances.tipFrame.TipLabel
	instances["tipCorner"] = instances.tipFrame.TipCorner
	instances["dropdownContainer"] = iconContainer.DropdownContainer
	instances["dropdownFrame"] = instances.dropdownContainer.DropdownFrame
	instances["dropdownList"] = instances.dropdownFrame.DropdownList
	instances["menuContainer"] = iconContainer.MenuContainer
	instances["menuFrame"] = instances.menuContainer.MenuFrame
	instances["menuList"] = instances.menuFrame.MenuList
	instances["clickSound"] = iconContainer.ClickSound

	-- These determine and describe how instances behave and appear
	self._settings = {
		action = {
			["toggleTransitionInfo"] = {},
			["resizeInfo"] = {},
			["repositionInfo"] = {},
			["captionFadeInfo"] = {},
			["tipFadeInfo"] = {},
			["dropdownSlideInfo"] = {},
			["menuSlideInfo"] = {},
		},
		toggleable = {
			["iconBackgroundColor"] = {instanceNames = {"iconButton"}, propertyName = "BackgroundColor3"},
			["iconBackgroundTransparency"] = {instanceNames = {"iconButton"}, propertyName = "BackgroundTransparency"},
			["iconCornerRadius"] = {instanceNames = {"iconCorner", "iconOverlayCorner"}, propertyName = "CornerRadius"},
			["iconGradientColor"] = {instanceNames = {"iconGradient"}, propertyName = "Color"},
			["iconGradientRotation"] = {instanceNames = {"iconGradient"}, propertyName = "Rotation"},
			["iconImage"] = {callMethods = {self._updateIconSize}, instanceNames = {"iconImage"}, propertyName = "Image"},
			["iconImageColor"] = {instanceNames = {"iconImage"}, propertyName = "ImageColor3"},
			["iconImageTransparency"] = {instanceNames = {"iconImage"}, propertyName = "ImageTransparency"},
			["iconScale"] = {instanceNames = {"iconButton"}, propertyName = "Size"},
			["forcedIconSize"] = {},
			["iconSize"] = {callSignals = {self.updated}, callMethods = {self._updateIconSize}, instanceNames = {"iconContainer"}, propertyName = "Size", tweenAction = "resizeInfo"},
			["iconOffset"] = {instanceNames = {"iconButton"}, propertyName = "Position"},
			["iconText"] = {callMethods = {self._updateIconSize}, instanceNames = {"iconLabel"}, propertyName = "Text"},
			["iconTextColor"] = {instanceNames = {"iconLabel"}, propertyName = "TextColor3"},
			["iconFont"] = {instanceNames = {"iconLabel"}, propertyName = "Font"},
			["iconImageYScale"] = {callMethods = {self._updateIconSize}},
			["iconImageRatio"] = {callMethods = {self._updateIconSize}},
			["iconLabelYScale"] = {callMethods = {self._updateIconSize}},
			["noticeCircleColor"] = {instanceNames = {"noticeFrame"}, propertyName = "ImageColor3"},
			["noticeCircleImage"] = {instanceNames = {"noticeFrame"}, propertyName = "Image"},
			["noticeTextColor"] = {instanceNames = {"noticeLabel"}, propertyName = "TextColor3"},
			["noticeImageTransparency"] = {instanceNames = {"noticeFrame"}, propertyName = "ImageTransparency"},
			["noticeTextTransparency"] = {instanceNames = {"noticeLabel"}, propertyName = "TextTransparency"},
			["baseZIndex"] = {callMethods = {self._updateBaseZIndex}},
			["order"] = {callSignals = {self.updated}, instanceNames = {"iconContainer"}, propertyName = "LayoutOrder"},
			["alignment"] = {callSignals = {self.updated}, callMethods = {self._updateDropdown}},
			["iconImageVisible"] = {instanceNames = {"iconImage"}, propertyName = "Visible"},
			["iconImageAnchorPoint"] = {instanceNames = {"iconImage"}, propertyName = "AnchorPoint"},
			["iconImagePosition"] = {instanceNames = {"iconImage"}, propertyName = "Position", tweenAction = "resizeInfo"},
			["iconImageSize"] = {instanceNames = {"iconImage"}, propertyName = "Size", tweenAction = "resizeInfo"},
			["iconImageTextXAlignment"] = {instanceNames = {"iconImage"}, propertyName = "TextXAlignment"},
			["iconLabelVisible"] = {instanceNames = {"iconLabel"}, propertyName = "Visible"},
			["iconLabelAnchorPoint"] = {instanceNames = {"iconLabel"}, propertyName = "AnchorPoint"},
			["iconLabelPosition"] = {instanceNames = {"iconLabel"}, propertyName = "Position", tweenAction = "resizeInfo"},
			["iconLabelSize"] = {instanceNames = {"iconLabel"}, propertyName = "Size", tweenAction = "resizeInfo"},
			["iconLabelTextXAlignment"] = {instanceNames = {"iconLabel"}, propertyName = "TextXAlignment"},
			["iconLabelTextSize"] = {instanceNames = {"iconLabel"}, propertyName = "TextSize"},
			["noticeFramePosition"] = {instanceNames = {"noticeFrame"}, propertyName = "Position"},
			["clickSoundId"] = {instanceNames = {"clickSound"}, propertyName = "SoundId"},
			["clickVolume"] = {instanceNames = {"clickSound"}, propertyName = "Volume"},
			["clickPlaybackSpeed"] = {instanceNames = {"clickSound"}, propertyName = "PlaybackSpeed"},
			["clickTimePosition"] = {instanceNames = {"clickSound"}, propertyName = "TimePosition"},
			-- 0_1195
			["iconRectOffset"] = {instanceNames = {"iconImage"}, propertyName = "ImageRectOffset"},
			["iconRectSize"] = {instanceNames = {"iconImage"}, propertyName = "ImageRectSize"},
		},
		other = {
			["captionBackgroundColor"] = {instanceNames = {"captionFrame"}, propertyName = "BackgroundColor3"},
			["captionBackgroundTransparency"] = {instanceNames = {"captionFrame"}, propertyName = "BackgroundTransparency", group = "caption"},
			["captionBlockerTransparency"] = {instanceNames = {"captionVisibilityBlocker"}, propertyName = "BackgroundTransparency", group = "caption"},
			["captionOverlineColor"] = {instanceNames = {"captionOverline"}, propertyName = "BackgroundColor3"},
			["captionOverlineTransparency"] = {instanceNames = {"captionOverline"}, propertyName = "BackgroundTransparency", group = "caption"},
			["captionTextColor"] = {instanceNames = {"captionLabel"}, propertyName = "TextColor3"},
			["captionTextTransparency"] = {instanceNames = {"captionLabel"}, propertyName = "TextTransparency", group = "caption"},
			["captionFont"] = {instanceNames = {"captionLabel"}, propertyName = "Font"},
			["captionCornerRadius"] = {instanceNames = {"captionCorner", "captionOverlineCorner", "captionVisibilityCorner"}, propertyName = "CornerRadius"},
			["tipBackgroundColor"] = {instanceNames = {"tipFrame"}, propertyName = "BackgroundColor3"},
			["tipBackgroundTransparency"] = {instanceNames = {"tipFrame"}, propertyName = "BackgroundTransparency", group = "tip"},
			["tipTextColor"] = {instanceNames = {"tipLabel"}, propertyName = "TextColor3"},
			["tipTextTransparency"] = {instanceNames = {"tipLabel"}, propertyName = "TextTransparency", group = "tip"},
			["tipFont"] = {instanceNames = {"tipLabel"}, propertyName = "Font"},
			["tipCornerRadius"] = {instanceNames = {"tipCorner"}, propertyName = "CornerRadius"},
			["dropdownSize"] = {instanceNames = {"dropdownContainer"}, propertyName = "Size", unique = "dropdown"},
			["dropdownCanvasSize"] = {instanceNames = {"dropdownFrame"}, propertyName = "CanvasSize"},
			["dropdownMaxIconsBeforeScroll"] = {callMethods = {self._updateDropdown}},
			["dropdownMinWidth"] = {callMethods = {self._updateDropdown}},
			["dropdownSquareCorners"] = {callMethods = {self._updateDropdown}},
			["dropdownBindToggleToIcon"] = {},
			["dropdownToggleOnLongPress"] = {},
			["dropdownToggleOnRightClick"] = {},
			["dropdownCloseOnTapAway"] = {},
			["dropdownHidePlayerlistOnOverlap"] = {},
			["dropdownListPadding"] = {callMethods = {self._updateDropdown}, instanceNames = {"dropdownList"}, propertyName = "Padding"},
			["dropdownAlignment"] = {callMethods = {self._updateDropdown}},
			["dropdownScrollBarColor"] = {instanceNames = {"dropdownFrame"}, propertyName = "ScrollBarImageColor3"},
			["dropdownScrollBarTransparency"] = {instanceNames = {"dropdownFrame"}, propertyName = "ScrollBarImageTransparency"},
			["dropdownScrollBarThickness"] = {instanceNames = {"dropdownFrame"}, propertyName = "ScrollBarThickness"},
			["dropdownIgnoreClipping"] = {callMethods = {self._dropdownIgnoreClipping}},
			["menuSize"] = {instanceNames = {"menuContainer"}, propertyName = "Size", unique = "menu"},
			["menuCanvasSize"] = {instanceNames = {"menuFrame"}, propertyName = "CanvasSize"},
			["menuMaxIconsBeforeScroll"] = {callMethods = {self._updateMenu}},
			["menuBindToggleToIcon"] = {},
			["menuToggleOnLongPress"] = {},
			["menuToggleOnRightClick"] = {},
			["menuCloseOnTapAway"] = {},
			["menuListPadding"] = {callMethods = {self._updateMenu}, instanceNames = {"menuList"}, propertyName = "Padding"},
			["menuDirection"] = {callMethods = {self._updateMenu}},
			["menuScrollBarColor"] = {instanceNames = {"menuFrame"}, propertyName = "ScrollBarImageColor3"},
			["menuScrollBarTransparency"] = {instanceNames = {"menuFrame"}, propertyName = "ScrollBarImageTransparency"},
			["menuScrollBarThickness"] = {instanceNames = {"menuFrame"}, propertyName = "ScrollBarThickness"},
			["menuIgnoreClipping"] = {callMethods = {self._menuIgnoreClipping}},
		}
	}

	---------------------------------
	self._groupSettings = {}
	for _, settingsDetails in pairs(self._settings) do
		for settingName, settingDetail in pairs(settingsDetails) do
			local group = settingDetail.group
			if group then
				local groupSettings = self._groupSettings[group]
				if not groupSettings then
					groupSettings = {}
					self._groupSettings[group] = groupSettings
				end
				table.insert(groupSettings, settingName)
				settingDetail.forcedGroupValue = DEFAULT_FORCED_GROUP_VALUES[group]
				settingDetail.useForcedGroupValue = true
			end
		end
	end
	---------------------------------

	-- The setting values themselves will be set within _settings
	-- Setup a dictionary to make it quick and easy to reference setting by name
	self._settingsDictionary = {}
	-- Some instances require unique behaviours. These are defined with the 'unique' key
	-- for instance, we only want caption transparency effects to be applied on hovering
	self._uniqueSettings = {}
	self._uniqueSettingsDictionary = {}
	self.uniqueValues = {}
	local uniqueBehaviours = {
		["dropdown"] = function(settingName, instance, propertyName, value)
			local tweenInfo = self:get("dropdownSlideInfo")
			local bindToggleToIcon = self:get("dropdownBindToggleToIcon")
			local hidePlayerlist = self:get("dropdownHidePlayerlistOnOverlap") == true and self:get("alignment") == "right"
			local dropdownContainer = self.instances.dropdownContainer
			local dropdownFrame = self.instances.dropdownFrame
			local newValue = value
			local isOpen = true
			local isDeselected = not self.isSelected
			if bindToggleToIcon == false then
				isDeselected = not self.dropdownOpen
			end
			local isSpecialPressing = self._longPressing or self._rightClicking
			if self._tappingAway or (isDeselected and not isSpecialPressing) or (isSpecialPressing and self.dropdownOpen) then 
				local dropdownSize = self:get("dropdownSize")
				local XOffset = (dropdownSize and dropdownSize.X.Offset/1) or 0
				newValue = UDim2.new(0, XOffset, 0, 0)
				isOpen = false
			end
			if #self.dropdownIcons > 0 and isOpen and hidePlayerlist then
				if starterGui:GetCoreGuiEnabled(Enum.CoreGuiType.PlayerList) then
					starterGui:SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList, false)
				end
				IconController._bringBackPlayerlist = (IconController._bringBackPlayerlist and IconController._bringBackPlayerlist + 1) or 1
				self._bringBackPlayerlist = true
			elseif self._bringBackPlayerlist and not isOpen and IconController._bringBackPlayerlist then
				IconController._bringBackPlayerlist -= 1
				if IconController._bringBackPlayerlist <= 0 then
					IconController._bringBackPlayerlist = nil
					starterGui:SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList, true)
				end
				self._bringBackPlayerlist = nil
			end
			local tween = tweenService:Create(instance, tweenInfo, {[propertyName] = newValue})
			local connection
			connection = tween.Completed:Connect(function()
				connection:Disconnect()
				--dropdownContainer.ClipsDescendants = not self.dropdownOpen
			end)
			tween:Play()
			if isOpen then
				dropdownFrame.CanvasPosition = self._dropdownCanvasPos
			else
				self._dropdownCanvasPos = dropdownFrame.CanvasPosition
			end
			self.dropdownOpen = isOpen
			self:_decideToCallSignal("dropdown")
		end,
		["menu"] = function(settingName, instance, propertyName, value)
			local tweenInfo = self:get("menuSlideInfo")
			local bindToggleToIcon = self:get("menuBindToggleToIcon")
			local menuContainer = self.instances.menuContainer
			local menuFrame = self.instances.menuFrame
			local newValue = value
			local isOpen = true
			local isDeselected = not self.isSelected
			if bindToggleToIcon == false then
				isDeselected = not self.menuOpen
			end
			local isSpecialPressing = self._longPressing or self._rightClicking
			if self._tappingAway or (isDeselected and not isSpecialPressing) or (isSpecialPressing and self.menuOpen) then 
				local menuSize = self:get("menuSize")
				local YOffset = (menuSize and menuSize.Y.Offset/1) or 0
				newValue = UDim2.new(0, 0, 0, YOffset)
				isOpen = false
			end
			if isOpen ~= self.menuOpen then
				self.updated:Fire()
			end
			if isOpen and tweenInfo.EasingDirection == Enum.EasingDirection.Out then
				tweenInfo = TweenInfo.new(tweenInfo.Time, tweenInfo.EasingStyle, Enum.EasingDirection.In)
			end
			local tween = tweenService:Create(instance, tweenInfo, {[propertyName] = newValue})
			local connection
			connection = tween.Completed:Connect(function()
				connection:Disconnect()
				--menuContainer.ClipsDescendants = not self.menuOpen
			end)
			tween:Play()
			if isOpen then
				if self._menuCanvasPos then
					menuFrame.CanvasPosition = self._menuCanvasPos
				end
			else
				self._menuCanvasPos = menuFrame.CanvasPosition
			end
			self.menuOpen = isOpen
			self:_decideToCallSignal("menu")
		end,
	}
	for settingsType, settingsDetails in pairs(self._settings) do
		for settingName, settingDetail in pairs(settingsDetails) do
			if settingsType == "toggleable" then
				settingDetail.values = settingDetail.values or {
					deselected = nil,
					selected = nil,
				}
			else
				settingDetail.value = nil
			end
			settingDetail.additionalValues = {}
			settingDetail.type = settingsType
			self._settingsDictionary[settingName] = settingDetail
			--
			local uniqueCat = settingDetail.unique
			if uniqueCat then
				local uniqueCatArray = self._uniqueSettings[uniqueCat] or {}
				table.insert(uniqueCatArray, settingName)
				self._uniqueSettings[uniqueCat] = uniqueCatArray
				self._uniqueSettingsDictionary[settingName] = uniqueBehaviours[uniqueCat]
			end
			--
		end
	end
	
	-- Signals (events)
	self.updated = maid:give(Signal.new())
	self.selected = maid:give(Signal.new())
    self.deselected = maid:give(Signal.new())
    self.toggled = maid:give(Signal.new())
	self.hoverStarted = maid:give(Signal.new())
	self.hoverEnded = maid:give(Signal.new())
	self.dropdownOpened = maid:give(Signal.new())
	self.dropdownClosed = maid:give(Signal.new())
	self.menuOpened = maid:give(Signal.new())
	self.menuClosed = maid:give(Signal.new())
	self.notified = maid:give(Signal.new())
	self._endNotices = maid:give(Signal.new())
	self._ignoreClippingChanged = maid:give(Signal.new())
	
	-- Connections
	-- This enables us to chain icons and features like menus and dropdowns together without them being hidden by parent frame with ClipsDescendants enabled
	local function setFeatureChange(featureName, value)
		local parentIcon = self._parentIcon
		self:set(featureName.."IgnoreClipping", value)
		if value == true and parentIcon then
			local connection = parentIcon._ignoreClippingChanged:Connect(function(_, value)
				self:set(featureName.."IgnoreClipping", value)
			end)
			local endConnection
			endConnection = self[featureName.."Closed"]:Connect(function()
				endConnection:Disconnect()
				connection:Disconnect()
			end)
		end
	end
	self.dropdownOpened:Connect(function()
		setFeatureChange("dropdown", true)
	end)
	self.dropdownClosed:Connect(function()
		setFeatureChange("dropdown", false)
	end)
	self.menuOpened:Connect(function()
		setFeatureChange("menu", true)
	end)
	self.menuClosed:Connect(function()
		setFeatureChange("menu", false)
	end)
	--]]

	-- Properties
	self.deselectWhenOtherIconSelected = true
	self.name = ""
	self.isSelected = false
	self.presentOnTopbar = true
	self.accountForWhenDisabled = false
	self.enabled = true
	self.hovering = false
	self.tipText = nil
	self.captionText = nil
	self.totalNotices = 0
	self.notices = {}
	self.dropdownIcons = {}
	self.menuIcons = {}
	self.dropdownOpen = false
	self.menuOpen = false
	self.locked = false
	self.topPadding = UDim.new(0, 4)
	self.targetPosition = nil
	self.toggleItems = {}
	self.lockedSettings = {}
	
	-- Private Properties
	self._draggingFinger = false
	self._updatingIconSize = true
	self._previousDropdownOpen = false
	self._previousMenuOpen = false
	self._bindedToggleKeys = {}
	self._bindedEvents = {}
	
	-- Apply start values
	self:setName("UnnamedIcon")
	self:setTheme(DEFAULT_THEME, true)

	-- Input handlers
	-- Calls deselect/select when the icon is clicked
	instances.iconButton.MouseButton1Click:Connect(function()
		if self._draggingFinger then
			return false
		elseif self.isSelected then
			self:deselect()
			return true
		end
		self:select()
	end)
	instances.iconButton.MouseButton2Click:Connect(function()
		self._rightClicking = true
		if self:get("dropdownToggleOnRightClick") == true then
			self:_update("dropdownSize")
		end
		if self:get("menuToggleOnRightClick") == true then
			self:_update("menuSize")
		end
		self._rightClicking = false
	end)

	-- Shows/hides the dark overlay when the icon is presssed/released
	instances.iconButton.MouseButton1Down:Connect(function()
		if self.locked then return end
		self:_updateStateOverlay(0.7, Color3.new(0, 0, 0))
	end)
	instances.iconButton.MouseButton1Up:Connect(function()
		if self.locked then return end
		self:_updateStateOverlay(0.9, Color3.new(1, 1, 1))
	end)

	-- Tap away + KeyCode toggles
	userInputService.InputBegan:Connect(function(input, touchingAnObject)
		local validTapAwayInputs = {
			[Enum.UserInputType.MouseButton1] = true,
			[Enum.UserInputType.MouseButton2] = true,
			[Enum.UserInputType.MouseButton3] = true,
			[Enum.UserInputType.Touch] = true,
		}
		if not touchingAnObject and validTapAwayInputs[input.UserInputType] then
			self._tappingAway = true
			if self.dropdownOpen and self:get("dropdownCloseOnTapAway") == true then
				self:_update("dropdownSize")
			end
			if self.menuOpen and self:get("menuCloseOnTapAway") == true then
				self:_update("menuSize")
			end
			self._tappingAway = false
		end
		--
		if self._bindedToggleKeys[input.KeyCode] and not touchingAnObject then
			if self.isSelected then
				self:deselect()
			else
				self:select()
			end
		end
		--
	end)
	
	-- hoverStarted and hoverEnded triggers and actions
	-- these are triggered when a mouse enters/leaves the icon with a mouse, is highlighted with
	-- a controller selection box, or dragged over with a touchpad
	self.hoverStarted:Connect(function(x, y)
		self.hovering = true
		if not self.locked then
			self:_updateStateOverlay(0.9, Color3.fromRGB(255, 255, 255))
		end
		self:_updateHovering()
	end)
	self.hoverEnded:Connect(function()
		self.hovering = false
		self:_updateStateOverlay(1)
		self._hoveringMaid:clean()
		self:_updateHovering()
	end)
	instances.iconButton.MouseEnter:Connect(function(x, y) -- Mouse (started)
		self.hoverStarted:Fire(x, y)
	end)
	instances.iconButton.MouseLeave:Connect(function() -- Mouse (ended)
		self.hoverEnded:Fire()
	end)
	instances.iconButton.SelectionGained:Connect(function() -- Controller (started)
		self.hoverStarted:Fire()
	end)
	instances.iconButton.SelectionLost:Connect(function() -- Controller (ended)
		self.hoverEnded:Fire()
	end)
	instances.iconButton.MouseButton1Down:Connect(function() -- TouchPad (started)
		if self._draggingFinger then
			self.hoverStarted:Fire()
		end
		-- Long press check
		local heartbeatConnection
		local releaseConnection
		local longPressTime = 0.7
		local endTick = tick() + longPressTime
		heartbeatConnection = runService.Heartbeat:Connect(function()
			if tick() >= endTick then
				releaseConnection:Disconnect()
				heartbeatConnection:Disconnect()
				self._longPressing = true
				if self:get("dropdownToggleOnLongPress") == true then
					self:_update("dropdownSize")
				end
				if self:get("menuToggleOnLongPress") == true then
					self:_update("menuSize")
				end
				self._longPressing = false
			end
		end)
		releaseConnection = instances.iconButton.MouseButton1Up:Connect(function()
			releaseConnection:Disconnect()
			heartbeatConnection:Disconnect()
		end)
	end)
	if userInputService.TouchEnabled then
		instances.iconButton.MouseButton1Up:Connect(function() -- TouchPad (ended), this was originally enabled for non-touchpads too
			if self.hovering then
				self.hoverEnded:Fire()
			end
		end)
		-- This is used to highlight when a mobile/touch device is dragging their finger accross the screen
		-- this is important for determining the hoverStarted and hoverEnded events on mobile
		local dragCount = 0
		userInputService.TouchMoved:Connect(function(touch, touchingAnObject)
			if touchingAnObject then
				return
			end
			self._draggingFinger = true
		end)
		userInputService.TouchEnded:Connect(function()
			self._draggingFinger = false
		end)
	end

	-- Finish
	self._updatingIconSize = false
	self:_updateIconSize()
	IconController.iconAdded:Fire(self)
	
	return self
end

-- This is the same as Icon.new(), except it adds additional behaviour for certain specified names designed to mimic core icons, such as 'Chat'
function Icon.mimic(coreIconToMimic)
	local iconName = coreIconToMimic.."Mimic"
	local icon = IconController.getIcon(iconName)
	if icon then
		return icon
	end
	icon = Icon.new()
	icon:setName(iconName)

	if coreIconToMimic == "Chat" then
		icon:setOrder(-1)
		icon:setImage("rbxasset://textures/ui/TopBar/chatOff.png", "deselected")
		icon:setImage("rbxasset://textures/ui/TopBar/chatOn.png", "selected")
		icon:setImageYScale(0.625)
		-- Since roblox's core gui api sucks melons I reverted to listening for signals within the chat modules
		-- unfortunately however they've just gone and removed *these* signals therefore 
		-- this mimic chat and similar features are now impossible to recreate accurately, so I'm disabling for now
		-- ill go ahead and post a feature request; fingers crossed we get something by the next decade

		--[[
		-- Setup maid and cleanup actioon
		local maid = icon._maid
		icon._fakeChatMaid = maid:give(Maid.new())
		maid.chatMimicCleanup = function()
			starterGui:SetCoreGuiEnabled("Chat", icon.enabled)
		end
		-- Tap into chat module
		local chatMainModule = players.LocalPlayer.PlayerScripts:WaitForChild("ChatScript").ChatMain
		local ChatMain = require(chatMainModule)
		local function displayChatBar(visibility)
			icon.ignoreVisibilityStateChange = true
			ChatMain.CoreGuiEnabled:fire(visibility)
			ChatMain.IsCoreGuiEnabled = false
			ChatMain:SetVisible(visibility)
			icon.ignoreVisibilityStateChange = nil
		end
		local function setIconEnabled(visibility)
			icon.ignoreVisibilityStateChange = true
			ChatMain.CoreGuiEnabled:fire(visibility)
			icon:setEnabled(visibility)
			starterGui:SetCoreGuiEnabled("Chat", false)
			icon:deselect()
			icon.updated:Fire()
			icon.ignoreVisibilityStateChange = nil
		end
		-- Open chat via Slash key
		icon._fakeChatMaid:give(userInputService.InputEnded:Connect(function(inputObject, gameProcessedEvent)
			if gameProcessedEvent then
				return "Another menu has priority"
			elseif not(inputObject.KeyCode == Enum.KeyCode.Slash or inputObject.KeyCode == Enum.SpecialKey.ChatHotkey) then
				return "No relavent key pressed"
			elseif ChatMain.IsFocused() then
				return "Chat bar already open"
			elseif not icon.enabled then
				return "Icon disabled"
			end
			ChatMain:FocusChatBar(true)
			icon:select()
		end))
		-- ChatActive
		icon._fakeChatMaid:give(ChatMain.VisibilityStateChanged:Connect(function(visibility)
			if not icon.ignoreVisibilityStateChange then
				if visibility == true then
					icon:select()
				else
					icon:deselect()
				end
			end
		end))
		-- Keep when other icons selected
		icon.deselectWhenOtherIconSelected = false
		-- Mimic chat notifications
		icon._fakeChatMaid:give(ChatMain.MessagesChanged:connect(function()
			if ChatMain:GetVisibility() == true then
				return "ChatWindow was open"
			end
			icon:notify(icon.selected)
		end))
		-- Mimic visibility when StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Chat, state) is called
		coroutine.wrap(function()
			runService.Heartbeat:Wait()
			icon._fakeChatMaid:give(ChatMain.CoreGuiEnabled:connect(function(newState)
				if icon.ignoreVisibilityStateChange then
					return "ignoreVisibilityStateChange enabled"
				end
				local topbarEnabled = starterGui:GetCore("TopbarEnabled")
				if topbarEnabled ~= IconController.previousTopbarEnabled then
					return "SetCore was called instead of SetCoreGuiEnabled"
				end
				if not icon.enabled and userInputService:IsKeyDown(Enum.KeyCode.LeftShift) and userInputService:IsKeyDown(Enum.KeyCode.P) then
					icon:setEnabled(true)
				else
					setIconEnabled(newState)
				end
			end))
		end)()
		icon.deselected:Connect(function()
			displayChatBar(false)
		end)
		icon.selected:Connect(function()
			displayChatBar(true)
		end)
		setIconEnabled(starterGui:GetCoreGuiEnabled("Chat"))
		--]]
	end
	return icon
end



-- CORE UTILITY METHODS
function Icon:set(settingName, value, iconState, setAdditional)
	local settingDetail = self._settingsDictionary[settingName]
	assert(settingDetail ~= nil, ("setting '%s' does not exist"):format(settingName))
	if type(iconState) == "string" then
		iconState = iconState:lower()
	end
	local previousValue = self:get(settingName, iconState)

	if iconState == "hovering" then
		-- Apply hovering state if valid
		settingDetail.hoveringValue = value
		if setAdditional ~= "_ignorePrevious" then
			settingDetail.additionalValues["previous_"..iconState] = previousValue
		end
		if type(setAdditional) == "string" then
			settingDetail.additionalValues[setAdditional.."_"..iconState] = previousValue
		end
		self:_update(settingName)

	else
		-- Update the settings value
		local toggleState = iconState
		local settingType = settingDetail.type
		if settingType == "toggleable" then
			local valuesToSet = {}
			if toggleState == "deselected" or toggleState == "selected" then
				table.insert(valuesToSet, toggleState)
			else
				table.insert(valuesToSet, "deselected")
				table.insert(valuesToSet, "selected")
				toggleState = nil
			end
			for i, v in pairs(valuesToSet) do
				settingDetail.values[v] = value
				if setAdditional ~= "_ignorePrevious" then
					settingDetail.additionalValues["previous_"..v] = previousValue
				end
				if type(setAdditional) == "string" then
					settingDetail.additionalValues[setAdditional.."_"..v] = previousValue
				end
			end
		else
			settingDetail.value = value
			if type(setAdditional) == "string" then
				if setAdditional ~= "_ignorePrevious" then
					settingDetail.additionalValues["previous"] = previousValue
				end
				settingDetail.additionalValues[setAdditional] = previousValue
			end
		end

		-- Check previous and new are not the same
		if previousValue == value then
			return self, "Value was already set"
		end

		-- Update appearances of associated instances
		local currentToggleState = self:getToggleState()
		if not self._updateAfterSettingAll and settingDetail.instanceNames and (currentToggleState == toggleState or toggleState == nil) then
			local ignoreTweenAction = (settingName == "iconSize" and previousValue and previousValue.X.Scale == 1)
			local tweenInfo = (settingDetail.tweenAction and not ignoreTweenAction and self:get(settingDetail.tweenAction)) or TweenInfo.new(0)
			self:_update(settingName, currentToggleState, tweenInfo)
		end
	end

	-- Call any methods present
	if settingDetail.callMethods then
		for _, callMethod in pairs(settingDetail.callMethods) do
			callMethod(self, value, iconState)
		end
	end
	
	-- Call any signals present
	if settingDetail.callSignals then
		for _, callSignal in pairs(settingDetail.callSignals) do
			callSignal:Fire()
		end
	end
	return self
end

function Icon:get(settingName, iconState, getAdditional)
	local settingDetail = self._settingsDictionary[settingName]
	assert(settingDetail ~= nil, ("setting '%s' does not exist"):format(settingName))
	local valueToReturn, additionalValueToReturn
	if typeof(iconState) == "string" then
		iconState = iconState:lower()
	end

	--if ((self.hovering and settingDetail.hoveringValue) or iconState == "hovering") and getAdditional == nil then
	if (iconState == "hovering") and getAdditional == nil then
		valueToReturn = settingDetail.hoveringValue
		additionalValueToReturn = type(getAdditional) == "string" and settingDetail.additionalValues[getAdditional.."_"..iconState]
	end

	local settingType = settingDetail.type
	if settingType == "toggleable" then
		local toggleState = ((iconState == "deselected" or iconState == "selected") and iconState) or self:getToggleState()
		if additionalValueToReturn == nil then
			additionalValueToReturn = type(getAdditional) == "string" and settingDetail.additionalValues[getAdditional.."_"..toggleState]
		end
		if valueToReturn == nil then
			valueToReturn = settingDetail.values[toggleState]
		end
	
	else
		if additionalValueToReturn == nil then
			additionalValueToReturn = type(getAdditional) == "string" and settingDetail.additionalValues[getAdditional]
		end
		if valueToReturn == nil then
			valueToReturn = settingDetail.value
		end
	end

	return valueToReturn, additionalValueToReturn
end

function Icon:getHovering(settingName)
	local settingDetail = self._settingsDictionary[settingName]
	assert(settingDetail ~= nil, ("setting '%s' does not exist"):format(settingName))
	return settingDetail.hoveringValue
end

function Icon:getToggleState(isSelected)
	isSelected = isSelected or self.isSelected
	return (isSelected and "selected") or "deselected"
end

function Icon:getIconState()
	if self.hovering then
		return "hovering"
	else
		return self:getToggleState()
	end
end

function Icon:_update(settingName, toggleState, customTweenInfo)
	local settingDetail = self._settingsDictionary[settingName]
	assert(settingDetail ~= nil, ("setting '%s' does not exist"):format(settingName))
	toggleState = toggleState or self:getToggleState()
	local value = settingDetail.value or (settingDetail.values and settingDetail.values[toggleState])
	if self.hovering and settingDetail.hoveringValue then
		value = settingDetail.hoveringValue
	end
	if value == nil then return end
	local tweenInfo = customTweenInfo or (settingDetail.tweenAction and self:get(settingDetail.tweenAction)) or self:get("toggleTransitionInfo") or TweenInfo.new(0.15)
	local propertyName = settingDetail.propertyName
	local invalidPropertiesTypes = {
		["string"] = true,
		["NumberSequence"] = true,
		["Text"] = true,
		["EnumItem"] = true,
		["ColorSequence"] = true,
	}
	local uniqueSetting = self._uniqueSettingsDictionary[settingName]
	local newValue = value
	if settingDetail.useForcedGroupValue then
		newValue = settingDetail.forcedGroupValue
	end
	if settingDetail.instanceNames then
		for _, instanceName in pairs(settingDetail.instanceNames) do
			local instance = self.instances[instanceName]
			local propertyType = typeof(instance[propertyName])
			local cannotTweenProperty = invalidPropertiesTypes[propertyType] or typeof(instance) == "table"
			if uniqueSetting then
				uniqueSetting(settingName, instance, propertyName, newValue)
			elseif cannotTweenProperty then
				instance[propertyName] = value
			else
				tweenService:Create(instance, tweenInfo, {[propertyName] = newValue}):Play()
			end
			--
			if settingName == "iconSize" and instance[propertyName] ~= newValue then
				self.updated:Fire()
			end
			--
		end
	end
end

function Icon:_updateAll(iconState, customTweenInfo)
	for settingName, settingDetail in pairs(self._settingsDictionary) do
		if settingDetail.instanceNames then
			self:_update(settingName, iconState, customTweenInfo)
		end
	end
end

function Icon:_updateHovering(customTweenInfo)
	for settingName, settingDetail in pairs(self._settingsDictionary) do
		if settingDetail.instanceNames and settingDetail.hoveringValue ~= nil then
			self:_update(settingName, nil, customTweenInfo)
		end
	end
end

function Icon:_updateStateOverlay(transparency, color)
	local stateOverlay = self.instances.iconOverlay
	stateOverlay.BackgroundTransparency = transparency or 1
	stateOverlay.BackgroundColor3 = color or Color3.new(1, 1, 1)
end

function Icon:setTheme(theme, updateAfterSettingAll)
	self._updateAfterSettingAll = updateAfterSettingAll
	for settingsType, settingsDetails in pairs(theme) do
		if settingsType == "toggleable" then
			for settingName, settingValue in pairs(settingsDetails.deselected) do
				if not self.lockedSettings[settingName] then
					self:set(settingName, settingValue, "both")
				end
			end
			for settingName, settingValue in pairs(settingsDetails.selected) do
				if not self.lockedSettings[settingName] then
					self:set(settingName, settingValue, "selected")
				end
			end
		else
			for settingName, settingValue in pairs(settingsDetails) do
				if not self.lockedSettings[settingName] then
					self:set(settingName, settingValue)
				end
			end
		end
	end
	self._updateAfterSettingAll = nil
	if updateAfterSettingAll then
		self:_updateAll()
	end
	return self
end

function Icon:getInstance(instanceName)
	return self.instances[instanceName]
end

function Icon:setInstance(instanceName, instance)
	local originalInstance = self.instances[instanceName]
	self.instances[instanceName] = instance
	if originalInstance then
		originalInstance:Destroy()
	end
	return self
end

function Icon:getSettingDetail(targetSettingName)
	for _, settingsDetails in pairs(self._settings) do
		for settingName, settingDetail in pairs(settingsDetails) do
			if settingName == targetSettingName then
				return settingDetail
			end
		end
	end
	return false
end

function Icon:modifySetting(settingName, dictionary)
	local settingDetail = self:getSettingDetail(settingName)
	for key, value in pairs(dictionary) do
		settingDetail[key] = value
	end
	return self
end

function Icon:convertLabelToNumberSpinner(numberSpinner)
	-- This updates the number spinners appearance
	self:set("iconLabelSize", UDim2.new(1,0,1,0))
	numberSpinner.Parent = self:getInstance("iconButton")

	-- This creates a fake iconLabel which updates the property of all descendant spinner TextLabels when indexed
	local textLabel = {}
	setmetatable(textLabel, {__newindex = function(_, index, value)
		for _, label in pairs(numberSpinner.Frame:GetDescendants()) do
			if label:IsA("TextLabel") then
				label[index] = value
			end
		end
	end})

	-- This overrides existing instances and settings so that they update the spinners properties (instead of the old textlabel)
	local iconButton = self:getInstance("iconButton")
	iconButton.ZIndex = 0
	self:setInstance("iconLabel", textLabel)
	self:modifySetting("iconText", {instanceNames = {}}) -- We do this to prevent text being modified within the metatable above
	self:setInstance("iconLabelSpinner", numberSpinner.Frame)
	local settingsToConvert = {"iconLabelVisible", "iconLabelAnchorPoint", "iconLabelPosition", "iconLabelSize"}
	for _, settingName in pairs(settingsToConvert) do
		self:modifySetting(settingName, {instanceNames = {"iconLabelSpinner"}})
	end

	-- This applies all the values we just updated
	self:_updateAll()
	return self
end

function Icon:setEnabled(bool)
	self.enabled = bool
	self.instances.iconContainer.Visible = bool
	self.updated:Fire()
	return self
end

function Icon:setName(string)
	self.name = string
	self.instances.iconContainer.Name = string
	return self
end

function Icon:setProperty(propertyName, value)
	self[propertyName] = value
	return self
end

function Icon:_playClickSound()
	local clickSound = self.instances.clickSound
	if clickSound.SoundId ~= nil and #clickSound.SoundId > 0 and clickSound.Volume > 0 then
		local clickSoundCopy = clickSound:Clone()
		clickSoundCopy.Parent = clickSound.Parent
		clickSoundCopy:Play()
		debris:AddItem(clickSoundCopy, clickSound.TimeLength)
	end
end

function Icon:select(byIcon)
	if self.locked then return self end
	self.isSelected = true
	self:_setToggleItemsVisible(true, byIcon)
	self:_updateNotice()
	self:_updateAll()
	self:_playClickSound()
	if #self.dropdownIcons > 0 or #self.menuIcons > 0 then
		IconController:_updateSelectionGroup()
	end
    self.selected:Fire()
    self.toggled:Fire(self.isSelected)
	return self
end

function Icon:deselect(byIcon)
	if self.locked then return self end
	self.isSelected = false
	self:_setToggleItemsVisible(false, byIcon)
	self:_updateNotice()
	self:_updateAll()
	self:_playClickSound()
	if #self.dropdownIcons > 0 or #self.menuIcons > 0 then
		IconController:_updateSelectionGroup()
	end
    self.deselected:Fire()
    self.toggled:Fire(self.isSelected)
	return self
end

function Icon:notify(clearNoticeEvent, noticeId)
	coroutine.wrap(function()
		if not clearNoticeEvent then
			clearNoticeEvent = self.deselected
		end
		if self._parentIcon then
			self._parentIcon:notify(clearNoticeEvent)
		end
		
		local notifComplete = Signal.new()
		local endEvent = self._endNotices:Connect(function()
			notifComplete:Fire()
		end)
		local customEvent = clearNoticeEvent:Connect(function()
			notifComplete:Fire()
		end)
		
		noticeId = noticeId or httpService:GenerateGUID(true)
		self.notices[noticeId] = {
			completeSignal = notifComplete,
			clearNoticeEvent = clearNoticeEvent,
		}
		self.totalNotices += 1
		self:_updateNotice()

		self.notified:Fire(noticeId)
		notifComplete:Wait()
		
		endEvent:Disconnect()
		customEvent:Disconnect()
		notifComplete:Disconnect()
		
		self.totalNotices -= 1
		self.notices[noticeId] = nil
		self:_updateNotice()
	end)()
	return self
end

function Icon:_updateNotice()
	local enabled = true
	if self.totalNotices < 1 then
		enabled = false
	end
	-- Deselect
	if not self.isSelected then
		if (#self.dropdownIcons > 0 or #self.menuIcons > 0) and self.totalNotices > 0 then
			enabled = true
		end
	end
	-- Select
	if self.isSelected then
		if #self.dropdownIcons > 0 or #self.menuIcons > 0 then
			enabled = false
		end
	end
	local value = (enabled and 0) or 1
	self:set("noticeImageTransparency", value)
	self:set("noticeTextTransparency", value)
	self.instances.noticeLabel.Text = (self.totalNotices < 100 and self.totalNotices) or "99+"
end

function Icon:clearNotices()
	self._endNotices:Fire()
	return self
end

function Icon:disableStateOverlay(bool)
	if bool == nil then
		bool = true
	end
	local stateOverlay = self.instances.iconOverlay
	stateOverlay.Visible = not bool
	return self
end



-- TOGGLEABLE METHODS
function Icon:setLabel(text, iconState)
	text = text or ""
	self:set("iconText", text, iconState)
	return self
end

function Icon:setCornerRadius(scale, offset, iconState)
	local oldCornerRadius = self.instances.iconCorner.CornerRadius
	local newCornerRadius = UDim.new(scale or oldCornerRadius.Scale, offset or oldCornerRadius.Offset)
	self:set("iconCornerRadius", newCornerRadius, iconState)
	return self
end

function Icon:setImage(imageId, iconState)
	local textureId = (tonumber(imageId) and "http://www.roblox.com/asset/?id="..imageId) or imageId or ""
	return self:set("iconImage", textureId, iconState)
end

function Icon:setOrder(order, iconState)
	local newOrder = tonumber(order) or 1
	return self:set("order", newOrder, iconState)
end

function Icon:setLeft(iconState)
	return self:set("alignment", "left", iconState)
end

function Icon:setMid(iconState)
	return self:set("alignment", "mid", iconState)
end

function Icon:setRight(iconState)
	return self:set("alignment", "right", iconState)
end

function Icon:setImageYScale(YScale, iconState)
	local newYScale = tonumber(YScale) or 0.63
	return self:set("iconImageYScale", newYScale, iconState)
end

function Icon:setImageRatio(ratio, iconState)
	local newRatio = tonumber(ratio) or 1
	return self:set("iconImageRatio", newRatio, iconState)
end

function Icon:setLabelYScale(YScale, iconState)
	local newYScale = tonumber(YScale) or 0.45
	return self:set("iconLabelYScale", newYScale, iconState)
end
	
function Icon:setBaseZIndex(ZIndex, iconState)
	local newBaseZIndex = tonumber(ZIndex) or 1
	return self:set("baseZIndex", newBaseZIndex, iconState)
end

function Icon:_updateBaseZIndex(baseValue)
	local container = self.instances.iconContainer
	local newBaseValue = tonumber(baseValue) or container.ZIndex
	local difference = newBaseValue - container.ZIndex
	if difference == 0 then return "The baseValue is the same" end
	for _, object in pairs(self.instances) do
		object.ZIndex = object.ZIndex + difference
	end
	return true
end

function Icon:setSize(XOffset, YOffset, iconState)
	local newXOffset = tonumber(XOffset) or 32
	local newYOffset = tonumber(YOffset) or newXOffset
	self:set("forcedIconSize", UDim2.new(0, newXOffset, 0, newYOffset), iconState)
	self:set("iconSize", UDim2.new(0, newXOffset, 0, newYOffset), iconState)
	return self
end

function Icon:_updateIconSize(_, iconState)
	if self._destroyed then return end
	-- This is responsible for handling the appearance and size of the icons label and image, in additon to its own size
	local X_MARGIN = 12
	local X_GAP = 8

	local values = {
		iconImage = self:get("iconImage", iconState) or "_NIL",
		iconText = self:get("iconText", iconState) or "_NIL",
		iconFont = self:get("iconFont", iconState) or "_NIL",
		iconSize = self:get("iconSize", iconState) or "_NIL",
		forcedIconSize = self:get("forcedIconSize", iconState) or "_NIL",
		iconImageYScale = self:get("iconImageYScale", iconState) or "_NIL",
		iconImageRatio = self:get("iconImageRatio", iconState) or "_NIL",
		iconLabelYScale = self:get("iconLabelYScale", iconState) or "_NIL",
	}
	for k,v in pairs(values) do
		if v == "_NIL" then
			return
		end
	end

	local iconContainer = self.instances.iconContainer
	if not iconContainer.Parent then return end

	-- We calculate the cells dimensions as apposed to reading because there's a possibility the cells dimensions were changed at the exact time and have not yet updated
	-- this essentially saves us from waiting a heartbeat which causes additonal complications
	local cellSizeXOffset = values.iconSize.X.Offset
	local cellSizeXScale = values.iconSize.X.Scale
	local cellWidth = cellSizeXOffset + (cellSizeXScale * iconContainer.Parent.AbsoluteSize.X)
	local minCellWidth = values.forcedIconSize.X.Offset--cellWidth
	local maxCellWidth = (cellSizeXScale > 0 and cellWidth) or 9999
	local cellSizeYOffset = values.iconSize.Y.Offset
	local cellSizeYScale = values.iconSize.Y.Scale
	local cellHeight = cellSizeYOffset + (cellSizeYScale * iconContainer.Parent.AbsoluteSize.Y)
	local labelHeight = cellHeight * values.iconLabelYScale
	local labelWidth = textService:GetTextSize(values.iconText, labelHeight, values.iconFont, Vector2.new(10000, labelHeight)).X
	local imageWidth = cellHeight * values.iconImageYScale * values.iconImageRatio
	
	local usingImage = values.iconImage ~= ""
	local usingText = values.iconText ~= ""
	local notifPosYScale = 0.5
	local desiredCellWidth
	local preventClippingOffset = labelHeight/2
	
	if usingImage and not usingText then
		notifPosYScale = 0.45
		self:set("iconImageVisible", true, iconState)
		self:set("iconImageAnchorPoint", Vector2.new(0.5, 0.5), iconState)
		self:set("iconImagePosition", UDim2.new(0.5, 0, 0.5, 0), iconState)
		self:set("iconImageSize", UDim2.new(values.iconImageYScale*values.iconImageRatio, 0, values.iconImageYScale, 0), iconState)
		self:set("iconLabelVisible", false, iconState)

	elseif not usingImage and usingText then
		desiredCellWidth = labelWidth+(X_MARGIN*2)
		self:set("iconLabelVisible", true, iconState)
		self:set("iconLabelAnchorPoint", Vector2.new(0, 0.5), iconState)
		self:set("iconLabelPosition", UDim2.new(0, X_MARGIN, 0.5, 0), iconState)
		self:set("iconLabelSize", UDim2.new(1, -X_MARGIN*2, values.iconLabelYScale, preventClippingOffset), iconState)
		self:set("iconLabelTextXAlignment", Enum.TextXAlignment.Center, iconState)
		self:set("iconImageVisible", false, iconState)

	elseif usingImage and usingText then
		local labelGap = X_MARGIN + imageWidth + X_GAP
		desiredCellWidth = labelGap + labelWidth + X_MARGIN
		self:set("iconImageVisible", true, iconState)
		self:set("iconImageAnchorPoint", Vector2.new(0, 0.5), iconState)
		self:set("iconImagePosition", UDim2.new(0, X_MARGIN, 0.5, 0), iconState)
		self:set("iconImageSize", UDim2.new(0, imageWidth, values.iconImageYScale, 0), iconState)
		----
		self:set("iconLabelVisible", true, iconState)
		self:set("iconLabelAnchorPoint", Vector2.new(0, 0.5), iconState)
		self:set("iconLabelPosition", UDim2.new(0, labelGap, 0.5, 0), iconState)
		self:set("iconLabelSize", UDim2.new(1, -labelGap-X_MARGIN, values.iconLabelYScale, preventClippingOffset), iconState)
		self:set("iconLabelTextXAlignment", Enum.TextXAlignment.Left, iconState)
	end
	if desiredCellWidth then
		if not self._updatingIconSize then
			self._updatingIconSize = true
			local widthScale = (cellSizeXScale > 0 and cellSizeXScale) or 0
			local widthOffset = (cellSizeXScale > 0 and 0) or math.clamp(desiredCellWidth, minCellWidth, maxCellWidth)
			self:set("iconSize", UDim2.new(widthScale, widthOffset, values.iconSize.Y.Scale, values.iconSize.Y.Offset), iconState, "_ignorePrevious")
			self._updatingIconSize = false
		end
	end
	self:set("iconLabelTextSize", labelHeight, iconState)
	self:set("noticeFramePosition", UDim2.new(notifPosYScale, 0, 0, -2), iconState)

	self._updatingIconSize = false
end



-- FEATURE METHODS
function Icon:bindEvent(iconEventName, eventFunction)
	local event = self[iconEventName]
	assert(event and typeof(event) == "table" and event.Connect, "argument[1] must be a valid topbarplus icon event name!")
	assert(typeof(eventFunction) == "function", "argument[2] must be a function!")
	self._bindedEvents[iconEventName] = event:Connect(function(...)
		eventFunction(self, ...)
	end)
	return self
end

function Icon:unbindEvent(iconEventName)
	local eventConnection = self._bindedEvents[iconEventName]
	if eventConnection then
		eventConnection:Disconnect()
		self._bindedEvents[iconEventName] = nil
	end
	return self
end

function Icon:bindToggleKey(keyCodeEnum)
	assert(typeof(keyCodeEnum) == "EnumItem", "argument[1] must be a KeyCode EnumItem!")
	self._bindedToggleKeys[keyCodeEnum] = true
	return self
end

function Icon:unbindToggleKey(keyCodeEnum)
	assert(typeof(keyCodeEnum) == "EnumItem", "argument[1] must be a KeyCode EnumItem!")
	self._bindedToggleKeys[keyCodeEnum] = nil
	return self
end

function Icon:lock()
	self.locked = true
	return self
end

function Icon:unlock()
	self.locked = false
	return self
end

function Icon:setTopPadding(offset, scale)
	local newOffset = offset or 4
	local newScale = scale or 0
	self.topPadding = UDim.new(newScale, newOffset)
	self.updated:Fire()
	return self
end

function Icon:bindToggleItem(guiObjectOrLayerCollector)
	if not guiObjectOrLayerCollector:IsA("GuiObject") and not guiObjectOrLayerCollector:IsA("LayerCollector") then
		error("Toggle item must be a GuiObject or LayerCollector!")
	end
	self.toggleItems[guiObjectOrLayerCollector] = true
	return self
end

function Icon:unbindToggleItem(guiObjectOrLayerCollector)
	self.toggleItems[guiObjectOrLayerCollector] = nil
	return self
end

function Icon:_setToggleItemsVisible(bool, byIcon)
	for toggleItem, _ in pairs(self.toggleItems) do
		if not byIcon or byIcon.toggleItems[toggleItem] == nil then
			local property = "Visible"
			if toggleItem:IsA("LayerCollector") then
				property = "Enabled"
			end
			toggleItem[property] = bool
		end
	end
end

function Icon:give(userdata)
	local valueToGive = userdata
	if typeof(userdata) == "function" then
		local returnValue = userdata(self)
		if typeof(userdata) ~= "function" then
			valueToGive = returnValue
		end
	end
	self._maid:give(valueToGive)
	return self
end

-- Tips
DEFAULT_FORCED_GROUP_VALUES["tip"] = 1

function Icon:setTip(text)
	assert(typeof(text) == "string" or text == nil, "Expected string, got "..typeof(text))
	local realText = text or ""
	local isVisible = realText ~= ""
	local textSize = textService:GetTextSize(realText, 12, Enum.Font.GothamSemibold, Vector2.new(1000, 20-6))
	self.instances.tipLabel.Text = realText
	self.instances.tipFrame.Size = (isVisible and UDim2.new(0, textSize.X+6, 0, 20)) or UDim2.new(0, 0, 0, 0)
	self.instances.tipFrame.Parent = (isVisible and activeItems) or self.instances.iconContainer
	self.tipText = text
	
	local tipMaid = Maid.new()
	self._maid.tipMaid = tipMaid
	if isVisible then
		tipMaid:give(self.hoverStarted:Connect(function()
			if not self.isSelected then
				self:displayTip(true)
			end
		end))
		tipMaid:give(self.hoverEnded:Connect(function()
			self:displayTip(false)
		end))
		tipMaid:give(self.selected:Connect(function()
			if self.hovering then
				self:displayTip(false)
			end
		end))
	end
	self:displayTip(self.hovering and isVisible)
	return self
end

function Icon:displayTip(bool)
	if userInputService.TouchEnabled and not self._draggingFinger then return end

	-- Determine caption visibility
	local isVisible = self.tipVisible or false
	if typeof(bool) == "boolean" then
		isVisible = bool
	end
	self.tipVisible = isVisible

	-- Have tip position track mouse or finger
	local tipFrame = self.instances.tipFrame
	if isVisible then
		-- When the user moves their cursor/finger, update tip to match the position
		local function updateTipPositon(x, y)
			local newX = x
			local newY = y
			local camera = workspace.CurrentCamera
			local viewportSize = camera and camera.ViewportSize
			if userInputService.TouchEnabled then
				--tipFrame.AnchorPoint = Vector2.new(0.5, 0.5)
				local desiredX = newX - tipFrame.Size.X.Offset/2
				local minX = 0
				local maxX = viewportSize.X - tipFrame.Size.X.Offset
				local desiredY = newY + THUMB_OFFSET + 60
				local minY = tipFrame.AbsoluteSize.Y + THUMB_OFFSET + 64 + 3
				local maxY = viewportSize.Y - tipFrame.Size.Y.Offset
				newX = math.clamp(desiredX, minX, maxX)
				newY = math.clamp(desiredY, minY, maxY)
			elseif IconController.controllerModeEnabled then
				local indicator = TopbarPlusGui.Indicator
				local newPos = indicator.AbsolutePosition
				newX = newPos.X - tipFrame.Size.X.Offset/2 + indicator.AbsoluteSize.X/2
				newY = newPos.Y + 90
			else
				local desiredX = newX
				local minX = 0
				local maxX = viewportSize.X - tipFrame.Size.X.Offset - 48
				local desiredY = newY
				local minY = tipFrame.Size.Y.Offset+3
				local maxY = viewportSize.Y
				newX = math.clamp(desiredX, minX, maxX)
				newY = math.clamp(desiredY, minY, maxY)
			end
			--local difX = tipFrame.AbsolutePosition.X - tipFrame.Position.X.Offset
			--local difY = tipFrame.AbsolutePosition.Y - tipFrame.Position.Y.Offset
			--local globalX = newX - difX
			--local globalY = newY - difY
			--tipFrame.Position = UDim2.new(0, globalX, 0, globalY-55)
			tipFrame.Position = UDim2.new(0, newX, 0, newY-20)
		end
		local cursorLocation = userInputService:GetMouseLocation()
		if cursorLocation then
			updateTipPositon(cursorLocation.X, cursorLocation.Y)
		end
		self._hoveringMaid:give(self.instances.iconButton.MouseMoved:Connect(updateTipPositon))
	end

	-- Change transparency of relavent tip instances
	for _, settingName in pairs(self._groupSettings.tip) do
		local settingDetail = self._settingsDictionary[settingName]
		settingDetail.useForcedGroupValue = not isVisible
		self:_update(settingName)
	end
end

-- Captions
DEFAULT_FORCED_GROUP_VALUES["caption"] = 1

function Icon:setCaption(text)
	assert(typeof(text) == "string" or text == nil, "Expected string, got "..typeof(text))
	local realText = text or ""
	local isVisible = realText ~= ""
	self.captionText = text
	self.instances.captionLabel.Text = realText
	self.instances.captionContainer.Parent = (isVisible and activeItems) or self.instances.iconContainer
	self:_updateIconSize(nil, self:getIconState())
	local captionMaid = Maid.new()
	self._maid.captionMaid = captionMaid
	if isVisible then
		captionMaid:give(self.hoverStarted:Connect(function()
			if not self.isSelected then
				self:displayCaption(true)
			end
		end))
		captionMaid:give(self.hoverEnded:Connect(function()
			self:displayCaption(false)
		end))
		captionMaid:give(self.selected:Connect(function()
			if self.hovering then
				self:displayCaption(false)
			end
		end))
		local iconContainer = self.instances.iconContainer
		captionMaid:give(iconContainer:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
			if self.hovering then
				self:displayCaption()
			end
		end))
		captionMaid:give(iconContainer:GetPropertyChangedSignal("AbsolutePosition"):Connect(function()
			if self.hovering then
				self:displayCaption()
			end
		end))
	end

	-- This adapts the caption size
	local CAPTION_X_MARGIN = 6
	local CAPTION_CONTAINER_Y_SIZE_SCALE = 0.8
	local CAPTION_LABEL_Y_SCALE = 0.58
	local iconSize = self:get("iconSize")
	local labelFont = self:get("captionFont")
	if iconSize and labelFont then
		local cellSizeYOffset = iconSize.Y.Offset
		local cellSizeYScale = iconSize.Y.Scale
		local iconContainer = self.instances.iconContainer
		local captionContainer = self.instances.captionContainer
		if isVisible then
			local cellHeight = cellSizeYOffset + (cellSizeYScale * iconContainer.Parent.AbsoluteSize.Y)
			local captionLabel = self.instances.captionLabel
			local captionContainerHeight = cellHeight * CAPTION_CONTAINER_Y_SIZE_SCALE
			local captionLabelHeight = captionContainerHeight * CAPTION_LABEL_Y_SCALE
			local textWidth = textService:GetTextSize(self.captionText, captionLabelHeight, labelFont, Vector2.new(10000, captionLabelHeight)).X
			captionLabel.TextSize = captionLabelHeight
			captionLabel.Size = UDim2.new(0, textWidth, CAPTION_LABEL_Y_SCALE, 0)
			captionContainer.Size = UDim2.new(0, textWidth + CAPTION_X_MARGIN*2, 0, cellHeight*CAPTION_CONTAINER_Y_SIZE_SCALE)
		else
			captionContainer.Size = UDim2.new(0, 0, 0, 0)
		end
	end

	self:displayCaption(self.hovering and isVisible)
	return self
end

function Icon:displayCaption(bool)
	if userInputService.TouchEnabled and not self._draggingFinger then return end
	local yOffset = 8
	
	-- Determine caption position
	if self._draggingFinger then
		yOffset = yOffset + THUMB_OFFSET
	end
	local iconContainer = self.instances.iconContainer
	local captionContainer = self.instances.captionContainer
	local newPos = UDim2.new(0, iconContainer.AbsolutePosition.X+iconContainer.AbsoluteSize.X/2-captionContainer.AbsoluteSize.X/2, 0, iconContainer.AbsolutePosition.Y+(iconContainer.AbsoluteSize.Y*2)+yOffset)
	captionContainer.Position = newPos

	-- Determine caption visibility
	local isVisible = self.captionVisible or false
	if typeof(bool) == "boolean" then
		isVisible = bool
	end
	self.captionVisible = isVisible

	-- Change transparency of relavent caption instances
	local captionFadeInfo = self:get("captionFadeInfo")
	for _, settingName in pairs(self._groupSettings.caption) do
		local settingDetail = self._settingsDictionary[settingName]
		settingDetail.useForcedGroupValue = not isVisible
		self:_update(settingName)
	end
end

-- Join or leave a special feature such as a Dropdown or Menu
function Icon:join(parentIcon, featureName, dontUpdate)
	if self._parentIcon then
		self:leave()
	end
	local newFeatureName = (featureName and featureName:lower()) or "dropdown"
	local beforeName = "before"..featureName:sub(1,1):upper()..featureName:sub(2)
	local parentFrame = parentIcon.instances[featureName.."Frame"]
	self.presentOnTopbar = false
	self.joinedFeatureName = featureName
	self._parentIcon = parentIcon
	self.instances.iconContainer.Parent = parentFrame
	for noticeId, noticeDetail in pairs(self.notices) do
		parentIcon:notify(noticeDetail.clearNoticeEvent, noticeId)
		--parentIcon:notify(noticeDetail.completeSignal, noticeId)
	end
	
	if featureName == "dropdown" then
		local squareCorners = parentIcon:get("dropdownSquareCorners")
		self:set("iconSize", UDim2.new(1, 0, 0, self:get("iconSize", "deselected").Y.Offset), "deselected", beforeName)
		self:set("iconSize", UDim2.new(1, 0, 0, self:get("iconSize", "selected").Y.Offset), "selected", beforeName)
		if squareCorners then
			self:set("iconCornerRadius", UDim.new(0, 0), "deselected", beforeName)
			self:set("iconCornerRadius", UDim.new(0, 0), "selected", beforeName)
		end
		self:set("captionBlockerTransparency", 0.4, nil, beforeName)
	end
	local array = parentIcon[newFeatureName.."Icons"]
	table.insert(array, self)
	if not dontUpdate then
		parentIcon:_updateDropdown()
	end
	parentIcon.deselectWhenOtherIconSelected = false
	--
	IconController:_updateSelectionGroup()
	self:_decideToCallSignal("dropdown")
	self:_decideToCallSignal("menu")
	--
	return self
end

function Icon:leave()
	if self._destroyed then return end
	local settingsToReset = {"iconSize", "captionBlockerTransparency", "iconCornerRadius"}
	local parentIcon = self._parentIcon
	self.instances.iconContainer.Parent = topbarContainer
	self.presentOnTopbar = true
	self.joinedFeatureName = nil
	local function scanFeature(t, prevReference, updateMethod)
		for i, otherIcon in pairs(t) do
			if otherIcon == self then
				for _, settingName in pairs(settingsToReset) do
					local states = {"deselected", "selected"}
					for _, toggleState in pairs(states) do
						local currentSetting, previousSetting = self:get(settingName, toggleState, prevReference)
						if previousSetting then
							self:set(settingName, previousSetting, toggleState)
						end
					end
				end
				table.remove(t, i)
				updateMethod(parentIcon)
				if #t == 0 then
					self._parentIcon.deselectWhenOtherIconSelected = true
				end
				break
			end
		end
	end
	scanFeature(parentIcon.dropdownIcons, "beforeDropdown", parentIcon._updateDropdown)
	scanFeature(parentIcon.menuIcons, "beforeMenu", parentIcon._updateMenu)
	--
	for noticeId, noticeDetail in pairs(self.notices) do
		local parentIconNoticeDetail = parentIcon.notices[noticeId]
		if parentIconNoticeDetail then
			parentIconNoticeDetail.completeSignal:Fire()
		end
	end
	--
	self._parentIcon = nil
	--
	IconController:_updateSelectionGroup()
	self:_decideToCallSignal("dropdown")
	self:_decideToCallSignal("menu")
	--
	return self
end

function Icon:_decideToCallSignal(featureName)
	local isOpen = self[featureName.."Open"]
	local previousIsOpenName = "_previous"..string.sub(featureName, 1, 1):upper()..featureName:sub(2).."Open"
	local previousIsOpen = self[previousIsOpenName]
	local totalIcons = #self[featureName.."Icons"]
	if isOpen and totalIcons > 0 and previousIsOpen == false then
		self[previousIsOpenName] = true
		self[featureName.."Opened"]:Fire()
	elseif (not isOpen or totalIcons == 0) and previousIsOpen == true then
		self[previousIsOpenName] = false
		self[featureName.."Closed"]:Fire()
	end
end

function Icon:_ignoreClipping(featureName)
	local ignoreClipping = self:get(featureName.."IgnoreClipping")
	if self._parentIcon then
		local maid = self["_"..featureName.."ClippingMaid"]
		local frame = self.instances[featureName.."Container"]
		maid:clean()
		if ignoreClipping then
			local fakeFrame = Instance.new("Frame")
			fakeFrame.Name = frame.Name.."FakeFrame"
			fakeFrame.ClipsDescendants = true
			fakeFrame.BackgroundTransparency = 1
			fakeFrame.Size = frame.Size
			fakeFrame.Position = frame.Position
			fakeFrame.Parent = activeItems
			--
			for a,b in pairs(frame:GetChildren()) do
				b.Parent = fakeFrame
			end
			--
			local function updateSize()
				local absoluteSize = frame.AbsoluteSize
				fakeFrame.Size = UDim2.new(0, absoluteSize.X, 0, absoluteSize.Y)
			end
			maid:give(frame:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
				updateSize()
			end))
			updateSize()
			local function updatePos()
				local absolutePosition = frame.absolutePosition
				fakeFrame.Position = UDim2.new(0, absolutePosition.X, 0, absolutePosition.Y+36)
			end
			maid:give(frame:GetPropertyChangedSignal("AbsolutePosition"):Connect(function()
				updatePos()
			end))
			updatePos()
			maid:give(function()
				for a,b in pairs(fakeFrame:GetChildren()) do
					b.Parent = frame
				end
				fakeFrame.Name = "Destroying..."
				fakeFrame:Destroy()
			end)
		end
	end
	self._ignoreClippingChanged:Fire(featureName, ignoreClipping)
end

-- Dropdowns
function Icon:setDropdown(arrayOfIcons)
	-- Reset any previous icons
	for i, otherIcon in pairs(self.dropdownIcons) do
		otherIcon:leave()
	end
	-- Apply new icons
	if type(arrayOfIcons) == "table" then
		for i, otherIcon in pairs(arrayOfIcons) do
			otherIcon:join(self, "dropdown", true)
		end
	end
	-- Update dropdown
	self:_updateDropdown()
	return self
end

function Icon:_updateDropdown()
	local values = {
		maxIconsBeforeScroll = self:get("dropdownMaxIconsBeforeScroll") or "_NIL",
		minWidth = self:get("dropdownMinWidth") or "_NIL",
		padding = self:get("dropdownListPadding") or "_NIL",
		dropdownAlignment = self:get("dropdownAlignment") or "_NIL",
		iconAlignment = self:get("alignment") or "_NIL",
		scrollBarThickness = self:get("dropdownScrollBarThickness") or "_NIL",
	}
	for k, v in pairs(values) do if v == "_NIL" then return end end
	
	local YPadding = values.padding.Offset
	local dropdownContainer = self.instances.dropdownContainer
	local dropdownFrame = self.instances.dropdownFrame
	local dropdownList = self.instances.dropdownList
	local totalIcons = #self.dropdownIcons

	local lastVisibleIconIndex = (totalIcons > values.maxIconsBeforeScroll and values.maxIconsBeforeScroll) or totalIcons
	local newCanvasSizeY = -YPadding
	local newFrameSizeY = 0
	local newMinWidth = values.minWidth
	table.sort(self.dropdownIcons, function(a,b) return a:get("order") < b:get("order") end)
	for i = 1, totalIcons do
		local otherIcon = self.dropdownIcons[i]
		local _, otherIconSize = otherIcon:get("iconSize", nil, "beforeDropdown")
		local increment = otherIconSize.Y.Offset + YPadding
		if i <= lastVisibleIconIndex then
			newFrameSizeY = newFrameSizeY + increment
		end
		if i == totalIcons then
			newFrameSizeY = newFrameSizeY + increment/4
		end
		newCanvasSizeY = newCanvasSizeY + increment
		local otherIconWidth = otherIconSize.X.Offset --+ 4 + 100 -- the +100 is to allow for notices
		if otherIconWidth > newMinWidth then
			newMinWidth = otherIconWidth
		end
	end

	local finalCanvasSizeY = (lastVisibleIconIndex == totalIcons and 0) or newCanvasSizeY
	self:set("dropdownCanvasSize", UDim2.new(0, 0, 0, finalCanvasSizeY))
	self:set("dropdownSize", UDim2.new(0, (newMinWidth+4)*2, 0, newFrameSizeY))

	-- Set alignment while considering screen bounds
	local dropdownAlignment = values.dropdownAlignment:lower()
	local alignmentDetails = {
		left = {
			AnchorPoint = Vector2.new(0, 0),
			PositionXScale = 0,
			ThicknessMultiplier = 0,
		},
		mid = {
			AnchorPoint = Vector2.new(0.5, 0),
			PositionXScale = 0.5,
			ThicknessMultiplier = 0.5,
		},
		right = {
			AnchorPoint = Vector2.new(0.5, 0),
			PositionXScale = 1,
			FrameAnchorPoint = Vector2.new(0, 0),
			FramePositionXScale = 0,
			ThicknessMultiplier = 1,
		}
	}
	local alignmentDetail = alignmentDetails[dropdownAlignment]
	if not alignmentDetail then
		alignmentDetail = alignmentDetails[values.iconAlignment:lower()]
	end
	dropdownContainer.AnchorPoint = alignmentDetail.AnchorPoint
	dropdownContainer.Position = UDim2.new(alignmentDetail.PositionXScale, 0, 1, YPadding+0)
	local scrollbarThickness = values.scrollBarThickness
	local newThickness = scrollbarThickness * alignmentDetail.ThicknessMultiplier
	local additionalOffset = (dropdownFrame.VerticalScrollBarPosition == Enum.VerticalScrollBarPosition.Right and newThickness) or -newThickness
	dropdownFrame.AnchorPoint = alignmentDetail.FrameAnchorPoint or alignmentDetail.AnchorPoint
	dropdownFrame.Position = UDim2.new(alignmentDetail.FramePositionXScale or alignmentDetail.PositionXScale, additionalOffset, 0, 0)
	self._dropdownCanvasPos = Vector2.new(0, 0)
end

function Icon:_dropdownIgnoreClipping()
	self:_ignoreClipping("dropdown")
end


-- Menus
function Icon:setMenu(arrayOfIcons)
	-- Reset any previous icons
	for i, otherIcon in pairs(self.menuIcons) do
		otherIcon:leave()
	end
	-- Apply new icons
	if type(arrayOfIcons) == "table" then
		for i, otherIcon in pairs(arrayOfIcons) do
			otherIcon:join(self, "menu", true)
		end
	end
	-- Update menu
	self:_updateMenu()
	return self
end

function Icon:_getMenuDirection()
	local direction = (self:get("menuDirection") or "_NIL"):lower()
	local alignment = (self:get("alignment") or "_NIL"):lower()
	if direction ~= "left" and direction ~= "right" then
		direction = (alignment == "left" and "right") or "left" 
	end
	return direction
end

function Icon:_updateMenu()
	local values = {
		maxIconsBeforeScroll = self:get("menuMaxIconsBeforeScroll") or "_NIL",
		direction = self:get("menuDirection") or "_NIL",
		iconAlignment = self:get("alignment") or "_NIL",
		scrollBarThickness = self:get("menuScrollBarThickness") or "_NIL",
	}
	for k, v in pairs(values) do if v == "_NIL" then return end end
	
	local XPadding = IconController[values.iconAlignment.."Gap"]--12
	local menuContainer = self.instances.menuContainer
	local menuFrame = self.instances.menuFrame
	local menuList = self.instances.menuList
	local totalIcons = #self.menuIcons

	local direction = self:_getMenuDirection()
	local lastVisibleIconIndex = (totalIcons > values.maxIconsBeforeScroll and values.maxIconsBeforeScroll) or totalIcons
	local newCanvasSizeX = -XPadding
	local newFrameSizeX = 0
	local newMinHeight = 0
	local sortFunc = (direction == "right" and function(a,b) return a:get("order") < b:get("order") end) or function(a,b) return a:get("order") > b:get("order") end
	table.sort(self.menuIcons, sortFunc)
	for i = 1, totalIcons do
		local otherIcon = self.menuIcons[i]
		local otherIconSize = otherIcon:get("iconSize")
		local increment = otherIconSize.X.Offset + XPadding
		if i <= lastVisibleIconIndex then
			newFrameSizeX = newFrameSizeX + increment
		end
		if i == lastVisibleIconIndex and i ~= totalIcons then
			newFrameSizeX = newFrameSizeX -2--(increment/4)
		end
		newCanvasSizeX = newCanvasSizeX + increment
		local otherIconHeight = otherIconSize.Y.Offset
		if otherIconHeight > newMinHeight then
			newMinHeight = otherIconHeight
		end
	end

	local canvasSize = (lastVisibleIconIndex == totalIcons and 0) or newCanvasSizeX + XPadding
	self:set("menuCanvasSize", UDim2.new(0, canvasSize, 0, 0))
	self:set("menuSize", UDim2.new(0, newFrameSizeX, 0, newMinHeight + values.scrollBarThickness + 3))

	-- Set direction
	local directionDetails = {
		left = {
			containerAnchorPoint = Vector2.new(1, 0),
			containerPosition = UDim2.new(0, -4, 0, 0),
			canvasPosition = Vector2.new(canvasSize, 0)
		},
		right = {
			containerAnchorPoint = Vector2.new(0, 0),
			containerPosition = UDim2.new(1, XPadding-2, 0, 0),
			canvasPosition = Vector2.new(0, 0),
		}
	}
	local directionDetail = directionDetails[direction]
	menuContainer.AnchorPoint = directionDetail.containerAnchorPoint
	menuContainer.Position = directionDetail.containerPosition
	menuFrame.CanvasPosition = directionDetail.canvasPosition
	self._menuCanvasPos = directionDetail.canvasPosition

	menuList.Padding = UDim.new(0, XPadding)
end

function Icon:_menuIgnoreClipping()
	self:_ignoreClipping("menu")
end



-- DESTROY/CLEANUP METHOD
function Icon:destroy()
	if self._destroyed then return end
	IconController.iconRemoved:Fire(self)
	self:clearNotices()
	if self._parentIcon then
		self:leave()
	end
	self:setDropdown()
	self:setMenu()
	self._destroyed = true
	self._maid:clean()
end
Icon.Destroy = Icon.destroy -- an alias for you maid-using Pascal lovers



return Icon