From 7995f32a29c8afac59b46e54f38da390ccf3fdb0 Mon Sep 17 00:00:00 2001 From: I-Bat Date: Sat, 18 Oct 2025 22:14:41 -0600 Subject: [PATCH 01/12] new API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function will adjust the position of the dropdown depending on the alignment: “left,” “center,” “right.” Example: https://github.com/user-attachments/assets/ad2ca280-ecc5-4f9e-887e-bc4799629997 --- src/init.lua | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/init.lua b/src/init.lua index cd30cf4..9be349b 100644 --- a/src/init.lua +++ b/src/init.lua @@ -1067,6 +1067,33 @@ function Icon:setDropdown(arrayOfIcons) return self end +function Icon:setDropdownAlign(leftCenterOrRight) + -- Changes Dropdown Positions: Right = lower right corner, Left = lower left corner, Center = middle of the icon (Default) + local direction = tostring(leftCenterOrRight):lower() + if direction == "mid" or direction == "centre" then + direction = "center" + end + if direction ~= "left" and direction ~= "center" and direction ~= "right" then + direction = "left" + end + + if direction == "left" and self.alignment ~= "Right" then + self:modifyTheme({ + {"Dropdown", "AnchorPoint", Vector2.new(0,0)}, + {"Dropdown", "Position", UDim2.new(0,0,1,7)}, + + }) + elseif direction == "right" and self.alignment ~= "Right" then + self:modifyTheme({ + {"Dropdown", "AnchorPoint", Vector2.new(0.5,0)}, + {"Dropdown", "Position", UDim2.new(1,0,1,7)}, + + }) + end + + return self +end + function Icon:clipOutside(instance) -- This is essential for items such as notices and dropdowns which will exceed the bounds of the widget. This is an issue -- because the widget must have ClipsDescendents enabled to hide items for instance when the menu is closing or opening. From eff4dfd453382d8e5826372954808dc00ffa6591 Mon Sep 17 00:00:00 2001 From: I-Bat Date: Sun, 9 Nov 2025 12:48:13 -0600 Subject: [PATCH 02/12] New Dropdown MaxIcons Calculation This addition to the calculation includes adding padding to TotalHeight, fixing visual errors where even if MaxIcons was greater than the number of icons currently in your dropdown, the dropdown did not adjust correctly. --- src/Elements/Dropdown.lua | 83 +++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/src/Elements/Dropdown.lua b/src/Elements/Dropdown.lua index 906476c..760f69d 100644 --- a/src/Elements/Dropdown.lua +++ b/src/Elements/Dropdown.lua @@ -1,15 +1,15 @@ local TweenService = game:GetService("TweenService") local RunService = game:GetService("RunService") +local Themes = require(script.Parent.Parent.Features.Themes) local PADDING = 0 -- used to be 8 return function(icon) - local dropdown = Instance.new("Frame") -- Instance.new("CanvasGroup") dropdown.Name = "Dropdown" + dropdown.AnchorPoint = Vector2.new(0.5, 0) + dropdown.Position = UDim2.new(0.5,0,1,7) dropdown.AutomaticSize = Enum.AutomaticSize.X dropdown.BackgroundTransparency = 1 dropdown.BorderSizePixel = 0 - dropdown.AnchorPoint = Vector2.new(0.5, 0) - dropdown.Position = UDim2.new(0.5, 0, 1, 10) dropdown.ZIndex = -2 dropdown.ClipsDescendants = true dropdown.Parent = icon.widget @@ -58,9 +58,14 @@ return function(icon) local TweenDuration = Instance.new("NumberValue") -- this helps to change the speed to open / close in modifyTheme() TweenDuration.Name = "DropdownSpeed" - TweenDuration.Value = 0.35 + TweenDuration.Value = 0.07 TweenDuration.Parent = dropdown + local dropdownAlignment = Instance.new("StringValue") + dropdownAlignment.Name = "DropdownAlignment" + dropdownAlignment.Value = "center" + dropdownAlignment.Parent = dropdown + local dropdownPadding = Instance.new("UIPadding") dropdownPadding.Name = "DropdownPadding" dropdownPadding.PaddingTop = UDim.new(0, PADDING) @@ -108,20 +113,11 @@ return function(icon) end end end) - - -- Update visibiliy of dropdown using tween transition - local Utility = require(script.Parent.Parent.Utility) - - local tweenInfo = TweenInfo.new(TweenDuration.Value, Enum.EasingStyle.Quad, Enum.EasingDirection.Out) - local openTween = nil - local closeTween = nil - + local function updateMaxIcons() --icon:modifyTheme({"Dropdown", "Visible", icon.isSelected}) local maxIcons = dropdown:GetAttribute("MaxIcons") if not maxIcons then return 0 end - - local fillDir = dropdownList.FillDirection local children = {} for _, child in pairs(dropdownScroller:GetChildren()) do if child:IsA("GuiObject") and child.Visible then @@ -130,6 +126,8 @@ return function(icon) end table.sort(children, function(a, b) return a.AbsolutePosition.Y < b.AbsolutePosition.Y end) + + -- Calculate total height based on MaxIcons local totalHeight = 0 local maxIconsRoundedUp = math.ceil(maxIcons) for i = 1, maxIconsRoundedUp do @@ -142,11 +140,44 @@ return function(icon) end totalHeight += height end + + -- FIX: Include UIListLayout padding in height calculation + -- Previously, the dropdown height didn't account for spacing between icons + -- causing a slight visual mismatch when MaxIcons was set + local listPadding = dropdownList.Padding.Offset + local visibleIconCount = math.min(maxIconsRoundedUp, #children) + if visibleIconCount > 1 then + totalHeight += listPadding * (visibleIconCount - 1) + end + + -- Add container padding totalHeight += dropdownPadding.PaddingTop.Offset + dropdownPadding.PaddingBottom.Offset return totalHeight end - + + + local openTween = nil + local closeTween = nil + local currentSpeedMultiplier = nil + local currentTweenInfo = nil + local function getTweenInfo() + local speedMultiplier = Themes.getInstanceValue(dropdown, "MaxIcons") or 1 + if currentSpeedMultiplier and currentSpeedMultiplier == speedMultiplier and currentTweenInfo then + return currentTweenInfo + end + local newTweenInfo = TweenInfo.new( + TweenDuration.Value * speedMultiplier, + Enum.EasingStyle.Exponential, + Enum.EasingDirection.Out + ) + currentTweenInfo = newTweenInfo + currentSpeedMultiplier = speedMultiplier + return newTweenInfo + end local function updateVisibility() + -- Update visibiliy of dropdown using tween transition + local tweenInfo = getTweenInfo() + if openTween then openTween:Cancel() openTween = nil @@ -168,7 +199,8 @@ return function(icon) openTween = nil end) else - closeTween = TweenService:Create(dropdown, tweenInfo, {Size = UDim2.new(0, dropdown.Size.X.Offset, 0, 0)}) + local closeTweenInfo = TweenInfo.new(0) + closeTween = TweenService:Create(dropdown, closeTweenInfo, {Size = UDim2.new(0, dropdown.Size.X.Offset, 0, 0)}) closeTween:Play() closeTween.Completed:Connect(function() closeTween = nil @@ -181,6 +213,7 @@ return function(icon) --task.delay(0.2, updateVisibility) local function updateChildSize() + local tweenInfo = getTweenInfo() if not icon.isSelected then return end if openTween then openTween:Cancel() @@ -190,9 +223,9 @@ return function(icon) closeTween:Cancel() closeTween = nil end - + RunService.Heartbeat:Wait() - + local height = updateMaxIcons() openTween = TweenService:Create(dropdown, tweenInfo, {Size = UDim2.new(0, dropdown.Size.X.Offset, 0, height)}) @@ -226,7 +259,7 @@ return function(icon) local orderedInstances = {} for _, child in pairs(dropdownScroller:GetChildren()) do - if child:IsA("GuiObject") then + if child:IsA("GuiObject") and child.Visible then table.insert(orderedInstances, {child, child.AbsolutePosition.Y}) end end @@ -259,6 +292,16 @@ return function(icon) childIcon:getInstance("ClickRegion").NextSelectionUp = nextSelection end end + + -- FIX: Include UIListLayout padding in height calculation + -- Previously, the dropdown height didn't account for spacing between icons + -- causing a slight visual mismatch when MaxIcons was set + local listPadding = dropdownList.Padding.Offset + local visibleIconCount = math.min(maxIconsRoundedUp, #orderedInstances) + if visibleIconCount > 1 then + totalHeight += listPadding * (visibleIconCount - 1) + end + totalHeight += dropdownPadding.PaddingTop.Offset + dropdownPadding.PaddingBottom.Offset dropdownScroller.Size = UDim2.fromOffset(0, totalHeight) @@ -281,7 +324,7 @@ return function(icon) child:GetPropertyChangedSignal("Size"):Connect(updateChildSize) -- -- update max icons when child size changes end end - + -- For existing children for _, child in pairs(dropdownScroller:GetChildren()) do connectVisibilityListeners(child) From 2971ae374d0fbdd00799ff25428ea8719b92dde7 Mon Sep 17 00:00:00 2001 From: I-Bat Date: Sun, 9 Nov 2025 13:00:24 -0600 Subject: [PATCH 03/12] Update init.lua --- src/init.lua | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/init.lua b/src/init.lua index 9be349b..cd30cf4 100644 --- a/src/init.lua +++ b/src/init.lua @@ -1067,33 +1067,6 @@ function Icon:setDropdown(arrayOfIcons) return self end -function Icon:setDropdownAlign(leftCenterOrRight) - -- Changes Dropdown Positions: Right = lower right corner, Left = lower left corner, Center = middle of the icon (Default) - local direction = tostring(leftCenterOrRight):lower() - if direction == "mid" or direction == "centre" then - direction = "center" - end - if direction ~= "left" and direction ~= "center" and direction ~= "right" then - direction = "left" - end - - if direction == "left" and self.alignment ~= "Right" then - self:modifyTheme({ - {"Dropdown", "AnchorPoint", Vector2.new(0,0)}, - {"Dropdown", "Position", UDim2.new(0,0,1,7)}, - - }) - elseif direction == "right" and self.alignment ~= "Right" then - self:modifyTheme({ - {"Dropdown", "AnchorPoint", Vector2.new(0.5,0)}, - {"Dropdown", "Position", UDim2.new(1,0,1,7)}, - - }) - end - - return self -end - function Icon:clipOutside(instance) -- This is essential for items such as notices and dropdowns which will exceed the bounds of the widget. This is an issue -- because the widget must have ClipsDescendents enabled to hide items for instance when the menu is closing or opening. From 3ab9c239ed076e10bdf2bbfd1338895639c5b8a4 Mon Sep 17 00:00:00 2001 From: I-Bat Date: Sun, 9 Nov 2025 13:10:22 -0600 Subject: [PATCH 04/12] Revert "Update init.lua" This reverts commit 2971ae374d0fbdd00799ff25428ea8719b92dde7. --- src/init.lua | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/init.lua b/src/init.lua index cd30cf4..9be349b 100644 --- a/src/init.lua +++ b/src/init.lua @@ -1067,6 +1067,33 @@ function Icon:setDropdown(arrayOfIcons) return self end +function Icon:setDropdownAlign(leftCenterOrRight) + -- Changes Dropdown Positions: Right = lower right corner, Left = lower left corner, Center = middle of the icon (Default) + local direction = tostring(leftCenterOrRight):lower() + if direction == "mid" or direction == "centre" then + direction = "center" + end + if direction ~= "left" and direction ~= "center" and direction ~= "right" then + direction = "left" + end + + if direction == "left" and self.alignment ~= "Right" then + self:modifyTheme({ + {"Dropdown", "AnchorPoint", Vector2.new(0,0)}, + {"Dropdown", "Position", UDim2.new(0,0,1,7)}, + + }) + elseif direction == "right" and self.alignment ~= "Right" then + self:modifyTheme({ + {"Dropdown", "AnchorPoint", Vector2.new(0.5,0)}, + {"Dropdown", "Position", UDim2.new(1,0,1,7)}, + + }) + end + + return self +end + function Icon:clipOutside(instance) -- This is essential for items such as notices and dropdowns which will exceed the bounds of the widget. This is an issue -- because the widget must have ClipsDescendents enabled to hide items for instance when the menu is closing or opening. From a40907fddf6c5b4f5c59cf0ccf60e50b344bed27 Mon Sep 17 00:00:00 2001 From: I-Bat Date: Sun, 9 Nov 2025 13:10:38 -0600 Subject: [PATCH 05/12] Revert "new API" This reverts commit 7995f32a29c8afac59b46e54f38da390ccf3fdb0. --- src/init.lua | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/init.lua b/src/init.lua index 9be349b..cd30cf4 100644 --- a/src/init.lua +++ b/src/init.lua @@ -1067,33 +1067,6 @@ function Icon:setDropdown(arrayOfIcons) return self end -function Icon:setDropdownAlign(leftCenterOrRight) - -- Changes Dropdown Positions: Right = lower right corner, Left = lower left corner, Center = middle of the icon (Default) - local direction = tostring(leftCenterOrRight):lower() - if direction == "mid" or direction == "centre" then - direction = "center" - end - if direction ~= "left" and direction ~= "center" and direction ~= "right" then - direction = "left" - end - - if direction == "left" and self.alignment ~= "Right" then - self:modifyTheme({ - {"Dropdown", "AnchorPoint", Vector2.new(0,0)}, - {"Dropdown", "Position", UDim2.new(0,0,1,7)}, - - }) - elseif direction == "right" and self.alignment ~= "Right" then - self:modifyTheme({ - {"Dropdown", "AnchorPoint", Vector2.new(0.5,0)}, - {"Dropdown", "Position", UDim2.new(1,0,1,7)}, - - }) - end - - return self -end - function Icon:clipOutside(instance) -- This is essential for items such as notices and dropdowns which will exceed the bounds of the widget. This is an issue -- because the widget must have ClipsDescendents enabled to hide items for instance when the menu is closing or opening. From ae5578419cc453acb1e1e8569b8e1d3cf0bb0f87 Mon Sep 17 00:00:00 2001 From: I-Bat Date: Sun, 9 Nov 2025 13:14:16 -0600 Subject: [PATCH 06/12] Update Dropdown.lua --- src/Elements/Dropdown.lua | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Elements/Dropdown.lua b/src/Elements/Dropdown.lua index 760f69d..b372730 100644 --- a/src/Elements/Dropdown.lua +++ b/src/Elements/Dropdown.lua @@ -3,6 +3,7 @@ local RunService = game:GetService("RunService") local Themes = require(script.Parent.Parent.Features.Themes) local PADDING = 0 -- used to be 8 return function(icon) + local dropdown = Instance.new("Frame") -- Instance.new("CanvasGroup") dropdown.Name = "Dropdown" dropdown.AnchorPoint = Vector2.new(0.5, 0) @@ -61,11 +62,6 @@ return function(icon) TweenDuration.Value = 0.07 TweenDuration.Parent = dropdown - local dropdownAlignment = Instance.new("StringValue") - dropdownAlignment.Name = "DropdownAlignment" - dropdownAlignment.Value = "center" - dropdownAlignment.Parent = dropdown - local dropdownPadding = Instance.new("UIPadding") dropdownPadding.Name = "DropdownPadding" dropdownPadding.PaddingTop = UDim.new(0, PADDING) @@ -113,7 +109,7 @@ return function(icon) end end end) - + local function updateMaxIcons() --icon:modifyTheme({"Dropdown", "Visible", icon.isSelected}) local maxIcons = dropdown:GetAttribute("MaxIcons") @@ -305,7 +301,6 @@ return function(icon) totalHeight += dropdownPadding.PaddingTop.Offset + dropdownPadding.PaddingBottom.Offset dropdownScroller.Size = UDim2.fromOffset(0, totalHeight) - end dropdownJanitor:add(dropdownScroller:GetPropertyChangedSignal("AbsoluteCanvasSize"):Connect(updateMaxIconsListener)) @@ -317,6 +312,8 @@ return function(icon) dropdownJanitor:add(icon.childThemeModified:Connect(updateMaxIconsListener)) updateMaxIconsListener() + + -- Ensures each child listens to visibility changes local function connectVisibilityListeners(child) if child:IsA("GuiObject") then From 39b7b7723412af07d2d1fad271d36ca030ea7bd3 Mon Sep 17 00:00:00 2001 From: I-Bat Date: Thu, 13 Nov 2025 14:39:00 -0600 Subject: [PATCH 07/12] Update Overflow center icons alignment --- src/Features/Overflow.lua | 87 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/Features/Overflow.lua b/src/Features/Overflow.lua index 9c5dbe0..e2197b7 100644 --- a/src/Features/Overflow.lua +++ b/src/Features/Overflow.lua @@ -349,6 +349,93 @@ function Overflow.updateBoundary(alignment) overflowIcon:select() end + -- Restore relocated center icons when space is available + if not isCentral then + -- Only restore if both overflow menus are completely inactive + local leftOverflow = overflowIcons["Left"] + local rightOverflow = overflowIcons["Right"] + local leftIsInactive = not leftOverflow or not leftOverflow.isEnabled + local rightIsInactive = not rightOverflow or not rightOverflow.isEnabled + local bothSidesStable = leftIsInactive and rightIsInactive + + if not joinOverflow and bothSidesStable then + local relocatedIcons = {} + for _, icon in pairs(ourOrderedIcons) do + if icon.hasRelocatedInOverflow and not icon.parentIconUID then + table.insert(relocatedIcons, icon) + end + end + + if #relocatedIcons > 0 then + table.sort(relocatedIcons, function(a, b) + return (a.widget.LayoutOrder or 0) < (b.widget.LayoutOrder or 0) + end) + + for _, icon in ipairs(relocatedIcons) do + local centerOrderedIcons = Overflow.getAvailableIcons("Center") + local centerHolder = holders["Center"] + local centerHolderXPos = centerHolder.AbsolutePosition.X + local centerHolderXSize = centerHolder.AbsoluteSize.X + local centerUIList = centerHolder.UIListLayout + local centerPadding = centerUIList.Padding.Offset + + local totalCenterWidth = 0 + for _, centerIcon in pairs(centerOrderedIcons) do + totalCenterWidth += Overflow.getWidth(centerIcon) + centerPadding + end + totalCenterWidth += Overflow.getWidth(icon) + centerPadding + + local leftOrderedIcons = Overflow.getAvailableIcons("Left") + local rightOrderedIcons = Overflow.getAvailableIcons("Right") + + local leftBoundary = centerHolderXPos + local rightBoundary = centerHolderXPos + centerHolderXSize + + -- Calculate boundaries excluding relocated icons + if #leftOrderedIcons > 0 then + local leftRealXPositions = Overflow.getRealXPositions("Left", leftOrderedIcons) + for _, leftIcon in pairs(leftOrderedIcons) do + if not leftIcon.hasRelocatedInOverflow then + local leftIconX = leftRealXPositions[leftIcon.UID] + local leftIconWidth = Overflow.getWidth(leftIcon) + leftBoundary = math.max(leftBoundary, leftIconX + leftIconWidth + BOUNDARY_GAP) + end + end + end + + if #rightOrderedIcons > 0 then + local rightRealXPositions = Overflow.getRealXPositions("Right", rightOrderedIcons) + for _, rightIcon in pairs(rightOrderedIcons) do + if not rightIcon.hasRelocatedInOverflow then + local rightIconX = rightRealXPositions[rightIcon.UID] + rightBoundary = math.min(rightBoundary, rightIconX - BOUNDARY_GAP) + end + end + end + + -- Safety margin to prevent oscillation + local SAFETY_MARGIN = BOUNDARY_GAP * 2.5 + local availableCenterWidth = rightBoundary - leftBoundary - SAFETY_MARGIN + + if availableCenterWidth > 0 and totalCenterWidth <= availableCenterWidth then + icon:align("Center") + icon.hasRelocatedInOverflow = nil + + Overflow.updateAvailableIcons("Center") + Overflow.updateAvailableIcons(alignment) + + task.spawn(function() + task.wait(0.2) + Overflow.updateBoundary("Left") + Overflow.updateBoundary("Right") + end) + + break + end + end + end + end + end end From c8341ecd30b9351338208ca9a584dfa304338454 Mon Sep 17 00:00:00 2001 From: I-Bat Date: Thu, 13 Nov 2025 14:42:41 -0600 Subject: [PATCH 08/12] Update Dropdown.lua --- src/Elements/Dropdown.lua | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/Elements/Dropdown.lua b/src/Elements/Dropdown.lua index b372730..4e1e0a7 100644 --- a/src/Elements/Dropdown.lua +++ b/src/Elements/Dropdown.lua @@ -3,14 +3,14 @@ local RunService = game:GetService("RunService") local Themes = require(script.Parent.Parent.Features.Themes) local PADDING = 0 -- used to be 8 return function(icon) - + local dropdown = Instance.new("Frame") -- Instance.new("CanvasGroup") dropdown.Name = "Dropdown" - dropdown.AnchorPoint = Vector2.new(0.5, 0) - dropdown.Position = UDim2.new(0.5,0,1,7) dropdown.AutomaticSize = Enum.AutomaticSize.X dropdown.BackgroundTransparency = 1 dropdown.BorderSizePixel = 0 + dropdown.AnchorPoint = Vector2.new(0.5, 0) + dropdown.Position = UDim2.new(0.5, 0, 1, 10) dropdown.ZIndex = -2 dropdown.ClipsDescendants = true dropdown.Parent = icon.widget @@ -122,8 +122,6 @@ return function(icon) end table.sort(children, function(a, b) return a.AbsolutePosition.Y < b.AbsolutePosition.Y end) - - -- Calculate total height based on MaxIcons local totalHeight = 0 local maxIconsRoundedUp = math.ceil(maxIcons) for i = 1, maxIconsRoundedUp do @@ -136,7 +134,6 @@ return function(icon) end totalHeight += height end - -- FIX: Include UIListLayout padding in height calculation -- Previously, the dropdown height didn't account for spacing between icons -- causing a slight visual mismatch when MaxIcons was set @@ -150,8 +147,7 @@ return function(icon) totalHeight += dropdownPadding.PaddingTop.Offset + dropdownPadding.PaddingBottom.Offset return totalHeight end - - + local openTween = nil local closeTween = nil local currentSpeedMultiplier = nil @@ -173,7 +169,7 @@ return function(icon) local function updateVisibility() -- Update visibiliy of dropdown using tween transition local tweenInfo = getTweenInfo() - + if openTween then openTween:Cancel() openTween = nil @@ -219,9 +215,9 @@ return function(icon) closeTween:Cancel() closeTween = nil end - + RunService.Heartbeat:Wait() - + local height = updateMaxIcons() openTween = TweenService:Create(dropdown, tweenInfo, {Size = UDim2.new(0, dropdown.Size.X.Offset, 0, height)}) @@ -288,7 +284,6 @@ return function(icon) childIcon:getInstance("ClickRegion").NextSelectionUp = nextSelection end end - -- FIX: Include UIListLayout padding in height calculation -- Previously, the dropdown height didn't account for spacing between icons -- causing a slight visual mismatch when MaxIcons was set @@ -297,9 +292,7 @@ return function(icon) if visibleIconCount > 1 then totalHeight += listPadding * (visibleIconCount - 1) end - totalHeight += dropdownPadding.PaddingTop.Offset + dropdownPadding.PaddingBottom.Offset - dropdownScroller.Size = UDim2.fromOffset(0, totalHeight) end @@ -312,8 +305,6 @@ return function(icon) dropdownJanitor:add(icon.childThemeModified:Connect(updateMaxIconsListener)) updateMaxIconsListener() - - -- Ensures each child listens to visibility changes local function connectVisibilityListeners(child) if child:IsA("GuiObject") then @@ -321,7 +312,7 @@ return function(icon) child:GetPropertyChangedSignal("Size"):Connect(updateChildSize) -- -- update max icons when child size changes end end - + -- For existing children for _, child in pairs(dropdownScroller:GetChildren()) do connectVisibilityListeners(child) From ebc48f99b1407cecd6f47044a64dad1de72bdadf Mon Sep 17 00:00:00 2001 From: I-Bat Date: Thu, 13 Nov 2025 19:07:29 -0600 Subject: [PATCH 09/12] New feature Icon:bindSignal() :unbindSignal() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit con:bindSignal(signal: RBXScriptSignal, callback: (icon, ...any) -> nil) This method would allow developers to link any Roblox signal (like .Changed, .Touched, custom BindableEvents, etc.) directly to an icon. Whenever that signal fires, the callback would automatically run — letting the icon react dynamically without extra code or loops. --- src/Types.lua | 17 +++++++++++++++++ src/init.lua | 23 +++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/Types.lua b/src/Types.lua index 0158f4f..93a049b 100644 --- a/src/Types.lua +++ b/src/Types.lua @@ -273,6 +273,23 @@ type Methods = { return nil :: any end ), + bindSignal: typeof( + --[[ + Connects an RBXScriptSignal (such as Touched or Changed) to the icon. + callback is called with arguments (self, ...) when the signal fires. + ]] + function(self: Icon, signal: RBXScriptSignal, callback: (...any) -> ()): Icon + return nil :: any + end + ), + unbindSignal: typeof( + --[[ + Disconnects a signal previously bound with :bindSignal(). + ]] + function(self: Icon, signal: RBXScriptSignal): Icon + return nil :: any + end + ), bindToggleKey: typeof( --[[ Binds a keycode which toggles the icon when pressed. diff --git a/src/init.lua b/src/init.lua index cd30cf4..0dc32ac 100644 --- a/src/init.lua +++ b/src/init.lua @@ -247,6 +247,7 @@ function Icon.new() self.dropdownIcons = {} self.childIconsDict = {} self.creationTime = os.clock() + self.bindedSignalEvents = {} -- Widget is the new name for an icon local widget = janitor:add(require(elements.Widget)(self, Icon)) @@ -913,6 +914,28 @@ function Icon:unbindEvent(iconEventName) return self end +function Icon:bindSignal(signal, signalFunction) + assert(typeof(signal) == "RBXScriptSignal", "argument[1] must be a valid RBXScriptSignal!") + assert(typeof(signalFunction) == "function", "argument[2] must be a function!") + + local connection = signal:Connect(function(...) + signalFunction(self, ...) + end) + self.bindSignalEvents[signal] = connection + return self +end + +function Icon:unbindSignal(signal) + print(tostring(signal)) + local connection = self.bindSignalEvents[signal] + if connection then + connection:Disconnect() + self.bindSignalEvents[signal] = nil + end + + return self +end + function Icon:bindToggleKey(keyCodeEnum) assert(typeof(keyCodeEnum) == "EnumItem", "argument[1] must be a KeyCode EnumItem!") self.bindedToggleKeys[keyCodeEnum] = true From 59e43d47769fe16a52191aa50daf139bc672c7bc Mon Sep 17 00:00:00 2001 From: I-Bat Date: Fri, 14 Nov 2025 05:43:54 -0600 Subject: [PATCH 10/12] Update init.lua --- src/init.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/src/init.lua b/src/init.lua index 0dc32ac..774d7f9 100644 --- a/src/init.lua +++ b/src/init.lua @@ -926,7 +926,6 @@ function Icon:bindSignal(signal, signalFunction) end function Icon:unbindSignal(signal) - print(tostring(signal)) local connection = self.bindSignalEvents[signal] if connection then connection:Disconnect() From 0c0560069a2fe61c784eda7f7daa0d2f8e92f53a Mon Sep 17 00:00:00 2001 From: I-Bat Date: Wed, 19 Nov 2025 19:41:22 -0600 Subject: [PATCH 11/12] TopbarCentered Alignment --- src/Elements/Container.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Elements/Container.lua b/src/Elements/Container.lua index d21211b..d767b12 100644 --- a/src/Elements/Container.lua +++ b/src/Elements/Container.lua @@ -117,7 +117,7 @@ return function(Icon) holdersCenter.Size = UDim2.new(1, 0, 0, GuiService.TopbarInset.Height+ySizeOffset) end screenGuiCenter.Name = "TopbarCentered" - screenGuiCenter.ScreenInsets = Enum.ScreenInsets.None + screenGuiCenter.ScreenInsets = Enum.ScreenInsets.TopbarSafeInsets Icon.baseDisplayOrderChanged:Connect(function() screenGuiCenter.DisplayOrder = Icon.baseDisplayOrder end) From 34d0ffac74c5cf2307250453654c20b806ab7ca3 Mon Sep 17 00:00:00 2001 From: I-Bat Date: Wed, 19 Nov 2025 19:43:46 -0600 Subject: [PATCH 12/12] Update Container.lua --- src/Elements/Container.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Elements/Container.lua b/src/Elements/Container.lua index d767b12..e57f056 100644 --- a/src/Elements/Container.lua +++ b/src/Elements/Container.lua @@ -117,6 +117,7 @@ return function(Icon) holdersCenter.Size = UDim2.new(1, 0, 0, GuiService.TopbarInset.Height+ySizeOffset) end screenGuiCenter.Name = "TopbarCentered" + screenGuiCenter.DisplayOrder = Icon.baseDisplayOrder screenGuiCenter.ScreenInsets = Enum.ScreenInsets.TopbarSafeInsets Icon.baseDisplayOrderChanged:Connect(function() screenGuiCenter.DisplayOrder = Icon.baseDisplayOrder