-- 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["fakeIconLabel"] = instances.iconButton.FakeIconLabel
	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"},
			["forcedIconSizeX"] = {},
			["forcedIconSizeY"] = {},
			["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"] = {callMethods = {self._updateIconSize}, 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"},
		},
		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 and self._parentIcon == nil and self._bringBackPlayerlist == nil then
			if #self.dropdownIcons > 0 and isOpen and hidePlayerlist then
				if starterGui:GetCoreGuiEnabled(Enum.CoreGuiType.PlayerList) then
					IconController._bringBackPlayerlist = (IconController._bringBackPlayerlist and IconController._bringBackPlayerlist + 1) or 1
					self._bringBackPlayerlist = true
					starterGui:SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList, false)
				end
			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
			dropdownFrame.ScrollingEnabled = isOpen -- It's important scrolling is only enabled when the dropdown is visible otherwise it could block the scrolling behaviour of other icons
			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
			menuFrame.ScrollingEnabled = isOpen -- It's important scrolling is only enabled when the menu is visible otherwise it could block the scrolling behaviour of other icons
			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.userSelected = maid:give(Signal.new())
	self.userDeselected = maid:give(Signal.new())
	self.userToggled = 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 = {}
	self.UID = httpService:GenerateGUID(true)
	self.blockBackBehaviourChecks = {}
	
	-- 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.locked then return end
		if self._draggingFinger then
			return false
		elseif self.isSelected then
			self:deselect()
			return true
		end
		self:select()
	end)--]]
	instances.iconButton.MouseButton1Click:Connect(function()
		if self.locked then return end
		if self.isSelected then
			self:deselect()
			self.userDeselected:Fire()
			self.userToggled:Fire(false)
			return true
		end
		self:select()
		self.userSelected:Fire()
		self.userToggled:Fire(true)
	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.overlayLocked 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 and not self.locked then
			if self.isSelected then
				self:deselect()
				self.userDeselected:Fire()
				self.userToggled:Fire(false)
			else
				self:select()
				self.userSelected:Fire()
				self.userToggled:Fire(true)
			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