diff --git a/src/Elements/Container.lua b/src/Elements/Container.lua index cb166e7..49ed456 100644 --- a/src/Elements/Container.lua +++ b/src/Elements/Container.lua @@ -130,7 +130,7 @@ return function(Icon) end screenGuiCenter.Name = "TopbarCentered" screenGuiCenter.DisplayOrder = Icon.baseDisplayOrder - screenGuiCenter.ScreenInsets = Enum.ScreenInsets.None + screenGuiCenter.ScreenInsets = Enum.ScreenInsets.TopbarSafeInsets Icon.baseDisplayOrderChanged:Connect(function() screenGuiCenter.DisplayOrder = Icon.baseDisplayOrder end) diff --git a/src/Elements/Dropdown.lua b/src/Elements/Dropdown.lua index 13b99fe..4e1e0a7 100644 --- a/src/Elements/Dropdown.lua +++ b/src/Elements/Dropdown.lua @@ -134,6 +134,16 @@ 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 @@ -274,10 +284,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) - end dropdownJanitor:add(dropdownScroller:GetPropertyChangedSignal("AbsoluteCanvasSize"):Connect(updateMaxIconsListener)) diff --git a/src/Features/Overflow.lua b/src/Features/Overflow.lua index bc7b18d..a1037dd 100644 --- a/src/Features/Overflow.lua +++ b/src/Features/Overflow.lua @@ -353,6 +353,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 diff --git a/src/Types.lua b/src/Types.lua index 922488c..a2adc62 100644 --- a/src/Types.lua +++ b/src/Types.lua @@ -279,6 +279,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 55b5dd8..c4e0945 100644 --- a/src/init.lua +++ b/src/init.lua @@ -252,6 +252,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)) @@ -924,6 +925,27 @@ 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) + 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