diff --git a/CHANGELOG.md b/CHANGELOG.md index fbc01f684..088a96697 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ * Improved settings UI ([#886]) * `Open Scripts Externally` option can now be changed while syncing ([#911]) * The sync reminder notification will now tell you what was last synced and when ([#987]) +* Fixed notification and tooltip text sometimes getting cut off ([#988]) * Projects may now specify rules for syncing files as if they had a different file extension. ([#813]) This is specified via a new field on project files, `syncRules`: @@ -89,6 +90,7 @@ [#915]: https://github.com/rojo-rbx/rojo/pull/915 [#974]: https://github.com/rojo-rbx/rojo/pull/974 [#987]: https://github.com/rojo-rbx/rojo/pull/987 +[#988]: https://github.com/rojo-rbx/rojo/pull/988 ## [7.4.3] - August 6th, 2024 * Fixed issue with building binary files introduced in 7.4.2 diff --git a/plugin/src/App/Components/Checkbox.lua b/plugin/src/App/Components/Checkbox.lua index 0d37fd860..c4572e930 100644 --- a/plugin/src/App/Components/Checkbox.lua +++ b/plugin/src/App/Components/Checkbox.lua @@ -32,7 +32,7 @@ end function Checkbox:render() return Theme.with(function(theme) - theme = theme.Checkbox + local checkboxTheme = theme.Checkbox local activeTransparency = Roact.joinBindings({ self.binding:map(function(value) @@ -63,14 +63,14 @@ function Checkbox:render() Active = e(SlicedImage, { slice = Assets.Slices.RoundedBackground, - color = theme.Active.BackgroundColor, + color = checkboxTheme.Active.BackgroundColor, transparency = activeTransparency, size = UDim2.new(1, 0, 1, 0), zIndex = 2, }, { Icon = e("ImageLabel", { Image = if self.props.locked then Assets.Images.Checkbox.Locked else Assets.Images.Checkbox.Active, - ImageColor3 = theme.Active.IconColor, + ImageColor3 = checkboxTheme.Active.IconColor, ImageTransparency = activeTransparency, Size = UDim2.new(0, 16, 0, 16), @@ -83,7 +83,7 @@ function Checkbox:render() Inactive = e(SlicedImage, { slice = Assets.Slices.RoundedBorder, - color = theme.Inactive.BorderColor, + color = checkboxTheme.Inactive.BorderColor, transparency = self.props.transparency, size = UDim2.new(1, 0, 1, 0), }, { @@ -91,7 +91,7 @@ function Checkbox:render() Image = if self.props.locked then Assets.Images.Checkbox.Locked else Assets.Images.Checkbox.Inactive, - ImageColor3 = theme.Inactive.IconColor, + ImageColor3 = checkboxTheme.Inactive.IconColor, ImageTransparency = self.props.transparency, Size = UDim2.new(0, 16, 0, 16), diff --git a/plugin/src/App/Components/CodeLabel.lua b/plugin/src/App/Components/CodeLabel.lua index 0cdba8e93..1951106b9 100644 --- a/plugin/src/App/Components/CodeLabel.lua +++ b/plugin/src/App/Components/CodeLabel.lua @@ -1,4 +1,5 @@ local Rojo = script:FindFirstAncestor("Rojo") +local Plugin = Rojo.Plugin local Packages = Rojo.Packages local Roact = require(Packages.Roact) @@ -7,6 +8,8 @@ Highlighter.matchStudioSettings() local e = Roact.createElement +local Theme = require(Plugin.App.Theme) + local CodeLabel = Roact.PureComponent:extend("CodeLabel") function CodeLabel:init() @@ -40,22 +43,24 @@ function CodeLabel:updateHighlights() end function CodeLabel:render() - return e("TextLabel", { - Size = self.props.size, - Position = self.props.position, - Text = self.props.text, - BackgroundTransparency = 1, - Font = Enum.Font.RobotoMono, - TextSize = 16, - TextXAlignment = Enum.TextXAlignment.Left, - TextYAlignment = Enum.TextYAlignment.Top, - TextColor3 = Color3.fromRGB(255, 255, 255), - [Roact.Ref] = self.labelRef, - }, { - SyntaxHighlights = e("Folder", { - [Roact.Ref] = self.highlightsRef, - }), - }) + return Theme.with(function(theme) + return e("TextLabel", { + Size = self.props.size, + Position = self.props.position, + Text = self.props.text, + BackgroundTransparency = 1, + FontFace = theme.Font.Code, + TextSize = theme.TextSize.Code, + TextXAlignment = Enum.TextXAlignment.Left, + TextYAlignment = Enum.TextYAlignment.Top, + TextColor3 = Color3.fromRGB(255, 255, 255), + [Roact.Ref] = self.labelRef, + }, { + SyntaxHighlights = e("Folder", { + [Roact.Ref] = self.highlightsRef, + }), + }) + end) end return CodeLabel diff --git a/plugin/src/App/Components/Dropdown.lua b/plugin/src/App/Components/Dropdown.lua index a5dda6f29..72629c18f 100644 --- a/plugin/src/App/Components/Dropdown.lua +++ b/plugin/src/App/Components/Dropdown.lua @@ -1,5 +1,3 @@ -local TextService = game:GetService("TextService") - local Rojo = script:FindFirstAncestor("Rojo") local Plugin = Rojo.Plugin local Packages = Rojo.Packages @@ -10,6 +8,7 @@ local Flipper = require(Packages.Flipper) local Assets = require(Plugin.Assets) local Theme = require(Plugin.App.Theme) local bindingUtil = require(Plugin.App.bindingUtil) +local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync) local SlicedImage = require(script.Parent.SlicedImage) local ScrollingFrame = require(script.Parent.ScrollingFrame) @@ -44,29 +43,29 @@ end function Dropdown:render() return Theme.with(function(theme) - theme = theme.Dropdown + local dropdownTheme = theme.Dropdown local optionButtons = {} local width = -1 for i, option in self.props.options do local text = tostring(option or "") - local textSize = TextService:GetTextSize(text, 15, Enum.Font.GothamMedium, Vector2.new(math.huge, 20)) - if textSize.X > width then - width = textSize.X + local textBounds = getTextBoundsAsync(text, theme.Font.Main, theme.TextSize.Body, math.huge) + if textBounds.X > width then + width = textBounds.X end optionButtons[text] = e("TextButton", { Text = text, LayoutOrder = i, Size = UDim2.new(1, 0, 0, 24), - BackgroundColor3 = theme.BackgroundColor, + BackgroundColor3 = dropdownTheme.BackgroundColor, TextTransparency = self.props.transparency, BackgroundTransparency = self.props.transparency, BorderSizePixel = 0, - TextColor3 = theme.TextColor, + TextColor3 = dropdownTheme.TextColor, TextXAlignment = Enum.TextXAlignment.Left, - TextSize = 15, - Font = Enum.Font.GothamMedium, + TextSize = theme.TextSize.Body, + FontFace = theme.Font.Main, [Roact.Event.Activated] = function() if self.props.locked then @@ -103,13 +102,13 @@ function Dropdown:render() }, { Border = e(SlicedImage, { slice = Assets.Slices.RoundedBorder, - color = theme.BorderColor, + color = dropdownTheme.BorderColor, transparency = self.props.transparency, size = UDim2.new(1, 0, 1, 0), }, { DropArrow = e("ImageLabel", { Image = if self.props.locked then Assets.Images.Dropdown.Locked else Assets.Images.Dropdown.Arrow, - ImageColor3 = theme.IconColor, + ImageColor3 = dropdownTheme.IconColor, ImageTransparency = self.props.transparency, Size = UDim2.new(0, 18, 0, 18), @@ -126,9 +125,9 @@ function Dropdown:render() Position = UDim2.new(0, 6, 0, 0), BackgroundTransparency = 1, Text = self.props.active, - Font = Enum.Font.GothamMedium, - TextSize = 15, - TextColor3 = theme.TextColor, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Body, + TextColor3 = dropdownTheme.TextColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = self.props.transparency, }), @@ -136,7 +135,7 @@ function Dropdown:render() Options = if self.state.open then e(SlicedImage, { slice = Assets.Slices.RoundedBackground, - color = theme.BackgroundColor, + color = dropdownTheme.BackgroundColor, position = UDim2.new(1, 0, 1, 3), size = self.openBinding:map(function(a) return UDim2.new(1, 0, a * math.min(3, #self.props.options), 0) @@ -145,7 +144,7 @@ function Dropdown:render() }, { Border = e(SlicedImage, { slice = Assets.Slices.RoundedBorder, - color = theme.BorderColor, + color = dropdownTheme.BorderColor, transparency = self.props.transparency, size = UDim2.new(1, 0, 1, 0), }), diff --git a/plugin/src/App/Components/Header.lua b/plugin/src/App/Components/Header.lua index ca6acecf3..81e2f46ac 100644 --- a/plugin/src/App/Components/Header.lua +++ b/plugin/src/App/Components/Header.lua @@ -31,13 +31,13 @@ local function Header(props) Version = e("TextLabel", { Text = Version.display(Config.version), - Font = Enum.Font.Gotham, - TextSize = 14, + FontFace = theme.Font.Thin, + TextSize = theme.TextSize.Body, TextColor3 = theme.Header.VersionColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, - Size = UDim2.new(1, 0, 0, 14), + Size = UDim2.new(1, 0, 0, theme.TextSize.Body), LayoutOrder = 2, BackgroundTransparency = 1, diff --git a/plugin/src/App/Components/PatchVisualizer/ChangeList.lua b/plugin/src/App/Components/PatchVisualizer/ChangeList.lua index 2f10d4587..fd6f83ccf 100644 --- a/plugin/src/App/Components/PatchVisualizer/ChangeList.lua +++ b/plugin/src/App/Components/PatchVisualizer/ChangeList.lua @@ -39,8 +39,8 @@ local function ViewDiffButton(props) Label = e("TextLabel", { Text = "View Diff", BackgroundTransparency = 1, - Font = Enum.Font.GothamMedium, - TextSize = 14, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Body, TextColor3 = theme.Settings.Setting.DescriptionColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, @@ -170,8 +170,8 @@ function ChangeList:render() ColumnA = e("TextLabel", { Text = tostring(headerRow[1]), BackgroundTransparency = 1, - Font = Enum.Font.GothamBold, - TextSize = 14, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Body, TextColor3 = theme.TextColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, @@ -182,8 +182,8 @@ function ChangeList:render() ColumnB = e("TextLabel", { Text = tostring(headerRow[2]), BackgroundTransparency = 1, - Font = Enum.Font.GothamBold, - TextSize = 14, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Body, TextColor3 = theme.TextColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, @@ -194,8 +194,8 @@ function ChangeList:render() ColumnC = e("TextLabel", { Text = tostring(headerRow[3]), BackgroundTransparency = 1, - Font = Enum.Font.GothamBold, - TextSize = 14, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Body, TextColor3 = theme.TextColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, @@ -230,8 +230,8 @@ function ChangeList:render() ColumnA = e("TextLabel", { Text = (if isWarning then "⚠ " else "") .. tostring(values[1]), BackgroundTransparency = 1, - Font = Enum.Font.GothamMedium, - TextSize = 14, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Body, TextColor3 = if isWarning then theme.Diff.Warning else theme.TextColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, diff --git a/plugin/src/App/Components/PatchVisualizer/DisplayValue.lua b/plugin/src/App/Components/PatchVisualizer/DisplayValue.lua index f7e772b93..94106af64 100644 --- a/plugin/src/App/Components/PatchVisualizer/DisplayValue.lua +++ b/plugin/src/App/Components/PatchVisualizer/DisplayValue.lua @@ -32,8 +32,8 @@ local function DisplayValue(props) Label = e("TextLabel", { Text = string.format("%d, %d, %d", props.value.R * 255, props.value.G * 255, props.value.B * 255), BackgroundTransparency = 1, - Font = Enum.Font.GothamMedium, - TextSize = 14, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Body, TextColor3 = props.textColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, @@ -90,8 +90,8 @@ local function DisplayValue(props) return e("TextLabel", { Text = textRepresentation, BackgroundTransparency = 1, - Font = Enum.Font.GothamMedium, - TextSize = 14, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Body, TextColor3 = props.textColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, @@ -112,8 +112,8 @@ local function DisplayValue(props) return e("TextLabel", { Text = textRepresentation, BackgroundTransparency = 1, - Font = Enum.Font.GothamMedium, - TextSize = 14, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Body, TextColor3 = props.textColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, diff --git a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua index a842dcbc5..689f81439 100644 --- a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua +++ b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua @@ -225,8 +225,8 @@ function DomLabel:render() Text = (if props.isWarning then "⚠ " else "") .. props.name, RichText = true, BackgroundTransparency = 1, - Font = if props.patchType then Enum.Font.GothamBold else Enum.Font.GothamMedium, - TextSize = 14, + FontFace = if props.patchType then theme.Font.Bold else theme.Font.Main, + TextSize = theme.TextSize.Body, TextColor3 = color, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, @@ -251,11 +251,11 @@ function DomLabel:render() then e("TextLabel", { Text = props.changeInfo.edits .. if props.changeInfo.failed then "," else "", BackgroundTransparency = 1, - Font = Enum.Font.Gotham, - TextSize = 14, + FontFace = theme.Font.Thin, + TextSize = theme.TextSize.Body, TextColor3 = theme.SubTextColor, TextTransparency = props.transparency, - Size = UDim2.new(0, 0, 0, 16), + Size = UDim2.new(0, 0, 0, theme.TextSize.Body), AutomaticSize = Enum.AutomaticSize.X, LayoutOrder = 2, }) @@ -264,11 +264,11 @@ function DomLabel:render() then e("TextLabel", { Text = props.changeInfo.failed, BackgroundTransparency = 1, - Font = Enum.Font.Gotham, - TextSize = 14, + FontFace = theme.Font.Thin, + TextSize = theme.TextSize.Body, TextColor3 = theme.Diff.Warning, TextTransparency = props.transparency, - Size = UDim2.new(0, 0, 0, 16), + Size = UDim2.new(0, 0, 0, theme.TextSize.Body), AutomaticSize = Enum.AutomaticSize.X, LayoutOrder = 6, }) diff --git a/plugin/src/App/Components/PatchVisualizer/init.lua b/plugin/src/App/Components/PatchVisualizer/init.lua index a87d499bb..3e600dca3 100644 --- a/plugin/src/App/Components/PatchVisualizer/init.lua +++ b/plugin/src/App/Components/PatchVisualizer/init.lua @@ -124,8 +124,8 @@ function PatchVisualizer:render() CleanMerge = e("TextLabel", { Visible = #scrollElements == 0, Text = "No changes to sync, project is up to date.", - Font = Enum.Font.GothamMedium, - TextSize = 15, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Medium, TextColor3 = theme.TextColor, TextWrapped = true, Size = UDim2.new(1, 0, 1, 0), diff --git a/plugin/src/App/Components/StringDiffVisualizer/init.lua b/plugin/src/App/Components/StringDiffVisualizer/init.lua index e4203c21c..a826c6496 100644 --- a/plugin/src/App/Components/StringDiffVisualizer/init.lua +++ b/plugin/src/App/Components/StringDiffVisualizer/init.lua @@ -1,5 +1,3 @@ -local TextService = game:GetService("TextService") - local Rojo = script:FindFirstAncestor("Rojo") local Plugin = Rojo.Plugin local Packages = Rojo.Packages @@ -11,6 +9,7 @@ local StringDiff = require(script:FindFirstChild("StringDiff")) local Timer = require(Plugin.Timer) local Theme = require(Plugin.App.Theme) +local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync) local CodeLabel = require(Plugin.App.Components.CodeLabel) local BorderedContainer = require(Plugin.App.Components.BorderedContainer) @@ -32,7 +31,6 @@ function StringDiffVisualizer:init() end) end) - self:calculateContentSize() self:updateScriptBackground() self:setState({ @@ -54,7 +52,6 @@ end function StringDiffVisualizer:didUpdate(previousProps) if previousProps.oldString ~= self.props.oldString or previousProps.newString ~= self.props.newString then - self:calculateContentSize() local add, remove = self:calculateDiffLines() self:setState({ add = add, @@ -63,11 +60,11 @@ function StringDiffVisualizer:didUpdate(previousProps) end end -function StringDiffVisualizer:calculateContentSize() +function StringDiffVisualizer:calculateContentSize(theme) local oldString, newString = self.props.oldString, self.props.newString - local oldStringBounds = TextService:GetTextSize(oldString, 16, Enum.Font.RobotoMono, Vector2.new(99999, 99999)) - local newStringBounds = TextService:GetTextSize(newString, 16, Enum.Font.RobotoMono, Vector2.new(99999, 99999)) + local oldStringBounds = getTextBoundsAsync(oldString, theme.Font.Code, theme.TextSize.Code, math.huge) + local newStringBounds = getTextBoundsAsync(newString, theme.Font.Code, theme.TextSize.Code, math.huge) self.setContentSize( Vector2.new(math.max(oldStringBounds.X, newStringBounds.X), math.max(oldStringBounds.Y, newStringBounds.Y)) @@ -143,6 +140,8 @@ function StringDiffVisualizer:render() local oldString, newString = self.props.oldString, self.props.newString return Theme.with(function(theme) + self:calculateContentSize(theme) + return e(BorderedContainer, { size = self.props.size, position = self.props.position, diff --git a/plugin/src/App/Components/TableDiffVisualizer/Array.lua b/plugin/src/App/Components/TableDiffVisualizer/Array.lua index febd07e97..4e9a3daf0 100644 --- a/plugin/src/App/Components/TableDiffVisualizer/Array.lua +++ b/plugin/src/App/Components/TableDiffVisualizer/Array.lua @@ -152,8 +152,8 @@ function Array:render() BackgroundTransparency = 1, Text = "Old", TextXAlignment = Enum.TextXAlignment.Left, - Font = Enum.Font.GothamBold, - TextSize = 14, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Body, TextColor3 = theme.Settings.Setting.DescriptionColor, TextTruncate = Enum.TextTruncate.AtEnd, }), @@ -163,8 +163,8 @@ function Array:render() BackgroundTransparency = 1, Text = "New", TextXAlignment = Enum.TextXAlignment.Left, - Font = Enum.Font.GothamBold, - TextSize = 14, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Body, TextColor3 = theme.Settings.Setting.DescriptionColor, TextTruncate = Enum.TextTruncate.AtEnd, }), diff --git a/plugin/src/App/Components/TableDiffVisualizer/Dictionary.lua b/plugin/src/App/Components/TableDiffVisualizer/Dictionary.lua index 033956c30..db3697bde 100644 --- a/plugin/src/App/Components/TableDiffVisualizer/Dictionary.lua +++ b/plugin/src/App/Components/TableDiffVisualizer/Dictionary.lua @@ -112,8 +112,8 @@ function Dictionary:render() BackgroundTransparency = 1, Text = key, TextXAlignment = Enum.TextXAlignment.Left, - Font = Enum.Font.GothamMedium, - TextSize = 14, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Body, TextColor3 = theme.Settings.Setting.DescriptionColor, TextTruncate = Enum.TextTruncate.AtEnd, }), @@ -157,8 +157,8 @@ function Dictionary:render() BackgroundTransparency = 1, Text = "Key", TextXAlignment = Enum.TextXAlignment.Left, - Font = Enum.Font.GothamBold, - TextSize = 14, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Body, TextColor3 = theme.Settings.Setting.DescriptionColor, TextTruncate = Enum.TextTruncate.AtEnd, }), @@ -168,8 +168,8 @@ function Dictionary:render() BackgroundTransparency = 1, Text = "Old", TextXAlignment = Enum.TextXAlignment.Left, - Font = Enum.Font.GothamBold, - TextSize = 14, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Body, TextColor3 = theme.Settings.Setting.DescriptionColor, TextTruncate = Enum.TextTruncate.AtEnd, }), @@ -179,8 +179,8 @@ function Dictionary:render() BackgroundTransparency = 1, Text = "New", TextXAlignment = Enum.TextXAlignment.Left, - Font = Enum.Font.GothamBold, - TextSize = 14, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Body, TextColor3 = theme.Settings.Setting.DescriptionColor, TextTruncate = Enum.TextTruncate.AtEnd, }), diff --git a/plugin/src/App/Components/Tag.lua b/plugin/src/App/Components/Tag.lua index 9fed3d33f..6e6d4c4f0 100644 --- a/plugin/src/App/Components/Tag.lua +++ b/plugin/src/App/Components/Tag.lua @@ -4,6 +4,7 @@ local Packages = Rojo.Packages local Roact = require(Packages.Roact) +local Theme = require(Plugin.App.Theme) local Assets = require(Plugin.Assets) local SlicedImage = require(Plugin.App.Components.SlicedImage) @@ -11,46 +12,48 @@ local SlicedImage = require(Plugin.App.Components.SlicedImage) local e = Roact.createElement return function(props) - return e(SlicedImage, { - slice = Assets.Slices.RoundedBackground, - color = props.color, - transparency = props.transparency:map(function(transparency) - return 0.9 + (0.1 * transparency) - end), - layoutOrder = props.layoutOrder, - position = props.position, - anchorPoint = props.anchorPoint, - size = UDim2.new(0, 0, 0, 16), - automaticSize = Enum.AutomaticSize.X, - }, { - Padding = e("UIPadding", { - PaddingLeft = UDim.new(0, 4), - PaddingRight = UDim.new(0, 4), - PaddingTop = UDim.new(0, 2), - PaddingBottom = UDim.new(0, 2), - }), - Icon = if props.icon - then e("ImageLabel", { - Size = UDim2.new(0, 12, 0, 12), - Position = UDim2.new(0, 0, 0.5, 0), - AnchorPoint = Vector2.new(0, 0.5), - Image = props.icon, + return Theme.with(function(theme) + return e(SlicedImage, { + slice = Assets.Slices.RoundedBackground, + color = props.color, + transparency = props.transparency:map(function(transparency) + return 0.9 + (0.1 * transparency) + end), + layoutOrder = props.layoutOrder, + position = props.position, + anchorPoint = props.anchorPoint, + size = UDim2.new(0, 0, 0, theme.TextSize.Medium), + automaticSize = Enum.AutomaticSize.X, + }, { + Padding = e("UIPadding", { + PaddingLeft = UDim.new(0, 4), + PaddingRight = UDim.new(0, 4), + PaddingTop = UDim.new(0, 2), + PaddingBottom = UDim.new(0, 2), + }), + Icon = if props.icon + then e("ImageLabel", { + Size = UDim2.new(0, 12, 0, 12), + Position = UDim2.new(0, 0, 0.5, 0), + AnchorPoint = Vector2.new(0, 0.5), + Image = props.icon, + BackgroundTransparency = 1, + ImageColor3 = props.color, + ImageTransparency = props.transparency, + }) + else nil, + Text = e("TextLabel", { + Text = props.text, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Small, + TextColor3 = props.color, + TextXAlignment = Enum.TextXAlignment.Center, + TextTransparency = props.transparency, + Size = UDim2.new(0, 0, 1, 0), + Position = UDim2.new(0, if props.icon then 15 else 0, 0, 0), + AutomaticSize = Enum.AutomaticSize.X, BackgroundTransparency = 1, - ImageColor3 = props.color, - ImageTransparency = props.transparency, - }) - else nil, - Text = e("TextLabel", { - Text = props.text, - Font = Enum.Font.GothamMedium, - TextSize = 12, - TextColor3 = props.color, - TextXAlignment = Enum.TextXAlignment.Center, - TextTransparency = props.transparency, - Size = UDim2.new(0, 0, 1, 0), - Position = UDim2.new(0, if props.icon then 15 else 0, 0, 0), - AutomaticSize = Enum.AutomaticSize.X, - BackgroundTransparency = 1, - }), - }) + }), + }) + end) end diff --git a/plugin/src/App/Components/TextButton.lua b/plugin/src/App/Components/TextButton.lua index bd643c795..f4243c1b3 100644 --- a/plugin/src/App/Components/TextButton.lua +++ b/plugin/src/App/Components/TextButton.lua @@ -1,5 +1,3 @@ -local TextService = game:GetService("TextService") - local Rojo = script:FindFirstAncestor("Rojo") local Plugin = Rojo.Plugin local Packages = Rojo.Packages @@ -10,6 +8,7 @@ local Flipper = require(Packages.Flipper) local Theme = require(Plugin.App.Theme) local Assets = require(Plugin.Assets) local bindingUtil = require(Plugin.App.bindingUtil) +local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync) local SlicedImage = require(script.Parent.SlicedImage) local TouchRipple = require(script.Parent.TouchRipple) @@ -41,18 +40,17 @@ end function TextButton:render() return Theme.with(function(theme) - local textSize = - TextService:GetTextSize(self.props.text, 18, Enum.Font.GothamMedium, Vector2.new(math.huge, math.huge)) + local textBounds = getTextBoundsAsync(self.props.text, theme.Font.Main, theme.TextSize.Large, math.huge) local style = self.props.style - theme = theme.Button[style] + local buttonTheme = theme.Button[style] local bindingHover = bindingUtil.deriveProperty(self.binding, "hover") local bindingEnabled = bindingUtil.deriveProperty(self.binding, "enabled") return e("ImageButton", { - Size = UDim2.new(0, 15 + textSize.X + 15, 0, 34), + Size = UDim2.new(0, (theme.TextSize.Body * 2) + textBounds.X, 0, 34), Position = self.props.position, AnchorPoint = self.props.anchorPoint, @@ -74,18 +72,22 @@ function TextButton:render() end, }, { TouchRipple = e(TouchRipple, { - color = theme.ActionFillColor, + color = buttonTheme.ActionFillColor, transparency = self.props.transparency:map(function(value) - return bindingUtil.blendAlpha({ theme.ActionFillTransparency, value }) + return bindingUtil.blendAlpha({ buttonTheme.ActionFillTransparency, value }) end), zIndex = 2, }), Text = e("TextLabel", { Text = self.props.text, - Font = Enum.Font.GothamMedium, - TextSize = 18, - TextColor3 = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.TextColor, theme.Disabled.TextColor), + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Large, + TextColor3 = bindingUtil.mapLerp( + bindingEnabled, + buttonTheme.Enabled.TextColor, + buttonTheme.Disabled.TextColor + ), TextTransparency = self.props.transparency, Size = UDim2.new(1, 0, 1, 0), @@ -95,7 +97,11 @@ function TextButton:render() Border = style == "Bordered" and e(SlicedImage, { slice = Assets.Slices.RoundedBorder, - color = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.BorderColor, theme.Disabled.BorderColor), + color = bindingUtil.mapLerp( + bindingEnabled, + buttonTheme.Enabled.BorderColor, + buttonTheme.Disabled.BorderColor + ), transparency = self.props.transparency, size = UDim2.new(1, 0, 1, 0), @@ -105,14 +111,18 @@ function TextButton:render() HoverOverlay = e(SlicedImage, { slice = Assets.Slices.RoundedBackground, - color = theme.ActionFillColor, + color = buttonTheme.ActionFillColor, transparency = Roact.joinBindings({ hover = bindingHover:map(function(value) return 1 - value end), transparency = self.props.transparency, }):map(function(values) - return bindingUtil.blendAlpha({ theme.ActionFillTransparency, values.hover, values.transparency }) + return bindingUtil.blendAlpha({ + buttonTheme.ActionFillTransparency, + values.hover, + values.transparency, + }) end), size = UDim2.new(1, 0, 1, 0), @@ -124,8 +134,8 @@ function TextButton:render() slice = Assets.Slices.RoundedBackground, color = bindingUtil.mapLerp( bindingEnabled, - theme.Enabled.BackgroundColor, - theme.Disabled.BackgroundColor + buttonTheme.Enabled.BackgroundColor, + buttonTheme.Disabled.BackgroundColor ), transparency = self.props.transparency, diff --git a/plugin/src/App/Components/TextInput.lua b/plugin/src/App/Components/TextInput.lua index 61b688d8d..1187296b2 100644 --- a/plugin/src/App/Components/TextInput.lua +++ b/plugin/src/App/Components/TextInput.lua @@ -38,14 +38,18 @@ end function TextInput:render() return Theme.with(function(theme) - theme = theme.TextInput + local textInputTheme = theme.TextInput local bindingHover = bindingUtil.deriveProperty(self.binding, "hover") local bindingEnabled = bindingUtil.deriveProperty(self.binding, "enabled") return e(SlicedImage, { slice = Assets.Slices.RoundedBorder, - color = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.BorderColor, theme.Disabled.BorderColor), + color = bindingUtil.mapLerp( + bindingEnabled, + textInputTheme.Enabled.BorderColor, + textInputTheme.Disabled.BorderColor + ), transparency = self.props.transparency, size = self.props.size or UDim2.new(1, 0, 1, 0), @@ -55,14 +59,18 @@ function TextInput:render() }, { HoverOverlay = e(SlicedImage, { slice = Assets.Slices.RoundedBackground, - color = theme.ActionFillColor, + color = textInputTheme.ActionFillColor, transparency = Roact.joinBindings({ hover = bindingHover:map(function(value) return 1 - value end), transparency = self.props.transparency, }):map(function(values) - return bindingUtil.blendAlpha({ theme.ActionFillTransparency, values.hover, values.transparency }) + return bindingUtil.blendAlpha({ + textInputTheme.ActionFillTransparency, + values.hover, + values.transparency, + }) end), size = UDim2.new(1, 0, 1, 0), zIndex = -1, @@ -72,14 +80,18 @@ function TextInput:render() Size = UDim2.fromScale(1, 1), Text = self.props.text, PlaceholderText = self.props.placeholder, - Font = Enum.Font.GothamMedium, - TextColor3 = bindingUtil.mapLerp(bindingEnabled, theme.Disabled.TextColor, theme.Enabled.TextColor), + FontFace = theme.Font.Main, + TextColor3 = bindingUtil.mapLerp( + bindingEnabled, + textInputTheme.Disabled.TextColor, + textInputTheme.Enabled.TextColor + ), PlaceholderColor3 = bindingUtil.mapLerp( bindingEnabled, - theme.Disabled.PlaceholderColor, - theme.Enabled.PlaceholderColor + textInputTheme.Disabled.PlaceholderColor, + textInputTheme.Enabled.PlaceholderColor ), - TextSize = 18, + TextSize = theme.TextSize.Large, TextEditable = self.props.enabled, ClearTextOnFocus = self.props.clearTextOnFocus, diff --git a/plugin/src/App/Components/Tooltip.lua b/plugin/src/App/Components/Tooltip.lua index 2ab7e19a5..55b056c23 100644 --- a/plugin/src/App/Components/Tooltip.lua +++ b/plugin/src/App/Components/Tooltip.lua @@ -1,4 +1,3 @@ -local TextService = game:GetService("TextService") local HttpService = game:GetService("HttpService") local Rojo = script:FindFirstAncestor("Rojo") @@ -8,6 +7,8 @@ local Packages = Rojo.Packages local Roact = require(Packages.Roact) local Theme = require(Plugin.App.Theme) +local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync) + local BorderedContainer = require(Plugin.App.Components.BorderedContainer) local e = Roact.createElement @@ -21,50 +22,51 @@ local Y_OVERLAP = 10 -- Let the triangle tail piece overlap the target a bit to local TooltipContext = Roact.createContext({}) local function Popup(props) - local textSize = TextService:GetTextSize( - props.Text, - 16, - Enum.Font.GothamMedium, - Vector2.new(math.min(props.parentSize.X, 160), math.huge) - ) + TEXT_PADDING + (Vector2.one * 2) + return Theme.with(function(theme) + local textXSpace = math.min(props.parentSize.X, 120) + local textBounds = Vector2.new( + textXSpace, + getTextBoundsAsync(props.Text, theme.Font.Main, theme.TextSize.Medium, textXSpace).Y + ) + local contentSize = textBounds + TEXT_PADDING + (Vector2.one * 2) - local trigger = props.Trigger:getValue() + local trigger = props.Trigger:getValue() - local spaceBelow = props.parentSize.Y - - (trigger.AbsolutePosition.Y + trigger.AbsoluteSize.Y - Y_OVERLAP + TAIL_SIZE) - local spaceAbove = trigger.AbsolutePosition.Y + Y_OVERLAP - TAIL_SIZE + local spaceBelow = props.parentSize.Y + - (trigger.AbsolutePosition.Y + trigger.AbsoluteSize.Y - Y_OVERLAP + TAIL_SIZE) + local spaceAbove = trigger.AbsolutePosition.Y + Y_OVERLAP - TAIL_SIZE - -- If there's not enough space below, and there's more space above, then show the tooltip above the trigger - local displayAbove = spaceBelow < textSize.Y and spaceAbove > spaceBelow + -- If there's not enough space below, and there's more space above, then show the tooltip above the trigger + local displayAbove = spaceBelow < contentSize.Y and spaceAbove > spaceBelow - local X = math.clamp(props.Position.X - X_OFFSET, 0, props.parentSize.X - textSize.X) - local Y = 0 + local X = math.clamp(props.Position.X - X_OFFSET, 0, props.parentSize.X - contentSize.X) + local Y = 0 - if displayAbove then - Y = math.max(trigger.AbsolutePosition.Y - TAIL_SIZE - textSize.Y + Y_OVERLAP, 0) - else - Y = math.min( - trigger.AbsolutePosition.Y + trigger.AbsoluteSize.Y + TAIL_SIZE - Y_OVERLAP, - props.parentSize.Y - textSize.Y - ) - end + if displayAbove then + Y = math.max(trigger.AbsolutePosition.Y - TAIL_SIZE - contentSize.Y + Y_OVERLAP, 0) + else + Y = math.min( + trigger.AbsolutePosition.Y + trigger.AbsoluteSize.Y + TAIL_SIZE - Y_OVERLAP, + props.parentSize.Y - contentSize.Y + ) + end - return Theme.with(function(theme) return e(BorderedContainer, { position = UDim2.fromOffset(X, Y), - size = UDim2.fromOffset(textSize.X, textSize.Y), + size = UDim2.fromOffset(contentSize.X, contentSize.Y), transparency = props.transparency, }, { Label = e("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromScale(0.5, 0.5), - Size = UDim2.new(1, -TEXT_PADDING.X, 1, -TEXT_PADDING.Y), AnchorPoint = Vector2.new(0.5, 0.5), + Size = UDim2.fromOffset(textBounds.X, textBounds.Y), Text = props.Text, - TextSize = 16, - Font = Enum.Font.GothamMedium, + TextSize = theme.TextSize.Medium, + FontFace = theme.Font.Main, TextWrapped = true, TextXAlignment = Enum.TextXAlignment.Left, + TextYAlignment = Enum.TextYAlignment.Center, TextColor3 = theme.Button.Bordered.Enabled.TextColor, TextTransparency = props.transparency, }), @@ -72,8 +74,8 @@ local function Popup(props) Tail = e("ImageLabel", { ZIndex = 100, Position = if displayAbove - then UDim2.new(0, math.clamp(props.Position.X - X, 6, textSize.X - 6), 1, -1) - else UDim2.new(0, math.clamp(props.Position.X - X, 6, textSize.X - 6), 0, -TAIL_SIZE + 1), + then UDim2.new(0, math.clamp(props.Position.X - X, 6, contentSize.X - 6), 1, -1) + else UDim2.new(0, math.clamp(props.Position.X - X, 6, contentSize.X - 6), 0, -TAIL_SIZE + 1), Size = UDim2.fromOffset(TAIL_SIZE, TAIL_SIZE), AnchorPoint = Vector2.new(0.5, 0), Rotation = if displayAbove then 180 else 0, diff --git a/plugin/src/App/Notifications.lua b/plugin/src/App/Notifications.lua index 682758cbf..5bb581c79 100644 --- a/plugin/src/App/Notifications.lua +++ b/plugin/src/App/Notifications.lua @@ -1,4 +1,3 @@ -local TextService = game:GetService("TextService") local StudioService = game:GetService("StudioService") local Rojo = script:FindFirstAncestor("Rojo") @@ -9,10 +8,10 @@ local Roact = require(Packages.Roact) local Flipper = require(Packages.Flipper) local Log = require(Packages.Log) -local bindingUtil = require(script.Parent.bindingUtil) - local Theme = require(Plugin.App.Theme) local Assets = require(Plugin.Assets) +local bindingUtil = require(Plugin.App.bindingUtil) +local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync) local BorderedContainer = require(Plugin.App.Components.BorderedContainer) local TextButton = require(Plugin.App.Components.TextButton) @@ -86,51 +85,49 @@ function Notification:render() return 1 - value end) - local textBounds = TextService:GetTextSize(self.props.text, 15, Enum.Font.GothamMedium, Vector2.new(350, 700)) - - local actionButtons = {} - local buttonsX = 0 - if self.props.actions then - local count = 0 - for key, action in self.props.actions do - actionButtons[key] = e(TextButton, { - text = action.text, - style = action.style, - onClick = function() - local success, err = pcall(action.onClick, self) - if not success then - Log.warn("Error in notification action: " .. tostring(err)) - end - end, - layoutOrder = -action.layoutOrder, - transparency = transparency, - }) - - buttonsX += TextService:GetTextSize( - action.text, - 18, - Enum.Font.GothamMedium, - Vector2.new(math.huge, math.huge) - ).X + 30 + return Theme.with(function(theme) + local actionButtons = {} + local buttonsX = 0 + if self.props.actions then + local count = 0 + for key, action in self.props.actions do + actionButtons[key] = e(TextButton, { + text = action.text, + style = action.style, + onClick = function() + local success, err = pcall(action.onClick, self) + if not success then + Log.warn("Error in notification action: " .. tostring(err)) + end + end, + layoutOrder = -action.layoutOrder, + transparency = transparency, + }) + + buttonsX += getTextBoundsAsync(action.text, theme.Font.Main, theme.TextSize.Large, math.huge).X + (theme.TextSize.Body * 2) + + count += 1 + end - count += 1 + buttonsX += (count - 1) * 5 end - buttonsX += (count - 1) * 5 - end - - local paddingY, logoSize = 20, 32 - local actionsY = if self.props.actions then 35 else 0 - local contentX = math.max(textBounds.X, buttonsX) - - local size = self.binding:map(function(value) - return UDim2.fromOffset( - (35 + 40 + contentX) * value, - 5 + actionsY + paddingY + math.max(logoSize, textBounds.Y) + local paddingY, logoSize = 20, 32 + local actionsY = if self.props.actions then 35 else 0 + local textXSpace = math.max(250, buttonsX) + 35 + local textBounds = Vector2.new( + textXSpace, + getTextBoundsAsync(self.props.text, theme.Font.Main, theme.TextSize.Body, textXSpace).Y ) - end) + local contentX = math.max(textBounds.X, buttonsX) + + local size = self.binding:map(function(value) + return UDim2.fromOffset( + (35 + 40 + contentX) * value, + 5 + actionsY + paddingY + math.max(logoSize, textBounds.Y) + ) + end) - return Theme.with(function(theme) return e("TextButton", { BackgroundTransparency = 1, Size = size, @@ -147,8 +144,7 @@ function Notification:render() size = UDim2.new(1, 0, 1, 0), }, { Contents = e("Frame", { - Size = UDim2.new(0, 35 + contentX, 1, -paddingY), - Position = UDim2.new(0, 0, 0, paddingY / 2), + Size = UDim2.new(1, 0, 1, 0), BackgroundTransparency = 1, }, { Logo = e("ImageLabel", { @@ -161,14 +157,15 @@ function Notification:render() }), Info = e("TextLabel", { Text = self.props.text, - Font = Enum.Font.GothamMedium, - TextSize = 15, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Body, TextColor3 = theme.Notification.InfoColor, TextTransparency = transparency, TextXAlignment = Enum.TextXAlignment.Left, + TextYAlignment = Enum.TextYAlignment.Top, TextWrapped = true, - Size = UDim2.new(0, textBounds.X, 0, textBounds.Y), + Size = UDim2.new(1, -35, 1, -actionsY), Position = UDim2.fromOffset(35, 0), LayoutOrder = 1, @@ -176,7 +173,7 @@ function Notification:render() }), Actions = if self.props.actions then e("Frame", { - Size = UDim2.new(1, -40, 0, 35), + Size = UDim2.new(1, -40, 0, actionsY), Position = UDim2.new(1, 0, 1, 0), AnchorPoint = Vector2.new(1, 1), BackgroundTransparency = 1, @@ -196,6 +193,8 @@ function Notification:render() Padding = e("UIPadding", { PaddingLeft = UDim.new(0, 17), PaddingRight = UDim.new(0, 15), + PaddingTop = UDim.new(0, paddingY / 2), + PaddingBottom = UDim.new(0, paddingY / 2), }), }), }) diff --git a/plugin/src/App/StatusPages/Confirming.lua b/plugin/src/App/StatusPages/Confirming.lua index df8bf5b49..3f3958a4c 100644 --- a/plugin/src/App/StatusPages/Confirming.lua +++ b/plugin/src/App/StatusPages/Confirming.lua @@ -64,13 +64,13 @@ function ConfirmingPage:render() "Sync changes for project '%s':", self.props.confirmData.serverInfo.projectName or "UNKNOWN" ), - Font = Enum.Font.Gotham, + FontFace = theme.Font.Thin, LineHeight = 1.2, - TextSize = 14, + TextSize = theme.TextSize.Body, TextColor3 = theme.TextColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = self.props.transparency, - Size = UDim2.new(1, 0, 0, 20), + Size = UDim2.new(1, 0, 0, theme.TextSize.Large + 2), BackgroundTransparency = 1, }), diff --git a/plugin/src/App/StatusPages/Connected.lua b/plugin/src/App/StatusPages/Connected.lua index b0bf33d9a..1d089e28f 100644 --- a/plugin/src/App/StatusPages/Connected.lua +++ b/plugin/src/App/StatusPages/Connected.lua @@ -61,12 +61,12 @@ function ChangesViewer:render() Title = e("TextLabel", { Text = "Sync", - Font = Enum.Font.GothamMedium, - TextSize = 17, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Large, TextXAlignment = Enum.TextXAlignment.Left, TextColor3 = theme.TextColor, TextTransparency = self.props.transparency, - Size = UDim2.new(1, -40, 0, 20), + Size = UDim2.new(1, -40, 0, theme.TextSize.Large + 2), Position = UDim2.new(0, 40, 0, 0), BackgroundTransparency = 1, }), @@ -74,13 +74,13 @@ function ChangesViewer:render() Subtitle = e("TextLabel", { Text = DateTime.fromUnixTimestamp(self.props.patchData.timestamp):FormatLocalTime("LTS", "en-us"), TextXAlignment = Enum.TextXAlignment.Left, - Font = Enum.Font.Gotham, - TextSize = 15, + FontFace = theme.Font.Thin, + TextSize = theme.TextSize.Medium, TextColor3 = theme.SubTextColor, TextTruncate = Enum.TextTruncate.AtEnd, TextTransparency = self.props.transparency, - Size = UDim2.new(1, -40, 0, 16), - Position = UDim2.new(0, 40, 0, 20), + Size = UDim2.new(1, -40, 0, theme.TextSize.Medium), + Position = UDim2.new(0, 40, 0, theme.TextSize.Large + 2), BackgroundTransparency = 1, }), @@ -131,8 +131,8 @@ function ChangesViewer:render() }), AppliedText = e("TextLabel", { Text = applied, - Font = Enum.Font.Gotham, - TextSize = 15, + FontFace = theme.Font.Thin, + TextSize = theme.TextSize.Body, TextColor3 = theme.TextColor, TextTransparency = self.props.transparency, Size = UDim2.new(0, 0, 1, 0), @@ -156,8 +156,8 @@ function ChangesViewer:render() }), UnappliedText = e("TextLabel", { Text = unapplied, - Font = Enum.Font.Gotham, - TextSize = 15, + FontFace = theme.Font.Thin, + TextSize = theme.TextSize.Body, TextColor3 = theme.Diff.Warning, TextTransparency = self.props.transparency, Size = UDim2.new(0, 0, 1, 0), @@ -217,13 +217,13 @@ local function ConnectionDetails(props) }, { ProjectName = e("TextLabel", { Text = props.projectName, - Font = Enum.Font.GothamBold, - TextSize = 20, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Large, TextColor3 = theme.ConnectionDetails.ProjectNameColor, TextTransparency = props.transparency, TextXAlignment = Enum.TextXAlignment.Left, - Size = UDim2.new(1, 0, 0, 20), + Size = UDim2.new(1, 0, 0, theme.TextSize.Large), LayoutOrder = 1, BackgroundTransparency = 1, @@ -231,13 +231,13 @@ local function ConnectionDetails(props) Address = e("TextLabel", { Text = props.address, - Font = Enum.Font.Code, - TextSize = 15, + FontFace = theme.Font.Code, + TextSize = theme.TextSize.Medium, TextColor3 = theme.ConnectionDetails.AddressColor, TextTransparency = props.transparency, TextXAlignment = Enum.TextXAlignment.Left, - Size = UDim2.new(1, 0, 0, 15), + Size = UDim2.new(1, 0, 0, theme.TextSize.Medium), LayoutOrder = 2, BackgroundTransparency = 1, @@ -410,8 +410,8 @@ function ConnectedPage:render() Text = e("TextLabel", { BackgroundTransparency = 1, Text = self.changeInfoText, - Font = Enum.Font.Gotham, - TextSize = 15, + FontFace = theme.Font.Thin, + TextSize = theme.TextSize.Body, TextColor3 = if syncWarning then theme.Diff.Warning else theme.Header.VersionColor, TextTransparency = self.props.transparency, TextXAlignment = Enum.TextXAlignment.Right, diff --git a/plugin/src/App/StatusPages/Error.lua b/plugin/src/App/StatusPages/Error.lua index 5d1bcb5eb..f51fb6a40 100644 --- a/plugin/src/App/StatusPages/Error.lua +++ b/plugin/src/App/StatusPages/Error.lua @@ -1,5 +1,3 @@ -local TextService = game:GetService("TextService") - local Rojo = script:FindFirstAncestor("Rojo") local Plugin = Rojo.Plugin local Packages = Rojo.Packages @@ -7,6 +5,7 @@ local Packages = Rojo.Packages local Roact = require(Packages.Roact) local Theme = require(Plugin.App.Theme) +local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync) local TextButton = require(Plugin.App.Components.TextButton) local BorderedContainer = require(Plugin.App.Components.BorderedContainer) @@ -24,43 +23,43 @@ function Error:init() end function Error:render() - return e(BorderedContainer, { - size = Roact.joinBindings({ - containerSize = self.props.containerSize, - contentSize = self.contentSize, - }):map(function(values) - local maximumSize = values.containerSize - maximumSize -= Vector2.new(14, 14) * 2 -- Page padding - maximumSize -= Vector2.new(0, 34 + 10) -- Buttons and spacing - - local outerSize = values.contentSize + ERROR_PADDING * 2 - - return UDim2.new(1, 0, 0, math.min(outerSize.Y, maximumSize.Y)) - end), - transparency = self.props.transparency, - }, { - ScrollingFrame = e(ScrollingFrame, { - size = UDim2.new(1, 0, 1, 0), - contentSize = self.contentSize:map(function(value) - return value + ERROR_PADDING * 2 + return Theme.with(function(theme) + return e(BorderedContainer, { + size = Roact.joinBindings({ + containerSize = self.props.containerSize, + contentSize = self.contentSize, + }):map(function(values) + local maximumSize = values.containerSize + maximumSize -= Vector2.new(14, 14) * 2 -- Page padding + maximumSize -= Vector2.new(0, 34 + 10) -- Buttons and spacing + + local outerSize = values.contentSize + ERROR_PADDING * 2 + + return UDim2.new(1, 0, 0, math.min(outerSize.Y, maximumSize.Y)) end), transparency = self.props.transparency, + }, { + ScrollingFrame = e(ScrollingFrame, { + size = UDim2.new(1, 0, 1, 0), + contentSize = self.contentSize:map(function(value) + return value + ERROR_PADDING * 2 + end), + transparency = self.props.transparency, - [Roact.Change.AbsoluteSize] = function(object) - local containerSize = object.AbsoluteSize - ERROR_PADDING * 2 + [Roact.Change.AbsoluteSize] = function(object) + local containerSize = object.AbsoluteSize - ERROR_PADDING * 2 - local textBounds = TextService:GetTextSize( - self.props.errorMessage, - 16, - Enum.Font.Code, - Vector2.new(containerSize.X, math.huge) - ) + local textBounds = getTextBoundsAsync( + self.props.errorMessage, + theme.Font.Code, + theme.TextSize.Code, + containerSize.X + ) - self.setContentSize(Vector2.new(containerSize.X, textBounds.Y)) - end, - }, { - ErrorMessage = Theme.with(function(theme) - return e("TextBox", { + self.setContentSize(Vector2.new(containerSize.X, textBounds.Y)) + end, + }, { + ErrorMessage = e("TextBox", { [Roact.Event.InputBegan] = function(rbx, input) if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return @@ -71,8 +70,8 @@ function Error:render() Text = self.props.errorMessage, TextEditable = false, - Font = Enum.Font.Code, - TextSize = 16, + FontFace = theme.Font.Code, + TextSize = theme.TextSize.Code, TextColor3 = theme.ErrorColor, TextXAlignment = Enum.TextXAlignment.Left, TextYAlignment = Enum.TextYAlignment.Top, @@ -81,17 +80,17 @@ function Error:render() ClearTextOnFocus = false, BackgroundTransparency = 1, Size = UDim2.new(1, 0, 1, 0), - }) - end), + }), - Padding = e("UIPadding", { - PaddingLeft = UDim.new(0, ERROR_PADDING.X), - PaddingRight = UDim.new(0, ERROR_PADDING.X), - PaddingTop = UDim.new(0, ERROR_PADDING.Y), - PaddingBottom = UDim.new(0, ERROR_PADDING.Y), + Padding = e("UIPadding", { + PaddingLeft = UDim.new(0, ERROR_PADDING.X), + PaddingRight = UDim.new(0, ERROR_PADDING.X), + PaddingTop = UDim.new(0, ERROR_PADDING.Y), + PaddingBottom = UDim.new(0, ERROR_PADDING.Y), + }), }), - }), - }) + }) + end) end local ErrorPage = Roact.Component:extend("ErrorPage") diff --git a/plugin/src/App/StatusPages/NotConnected.lua b/plugin/src/App/StatusPages/NotConnected.lua index e881e7eb1..aabf7640c 100644 --- a/plugin/src/App/StatusPages/NotConnected.lua +++ b/plugin/src/App/StatusPages/NotConnected.lua @@ -27,8 +27,8 @@ local function AddressEntry(props) }, { Host = e("TextBox", { Text = props.host or "", - Font = Enum.Font.Code, - TextSize = 18, + FontFace = theme.Font.Code, + TextSize = theme.TextSize.Large, TextColor3 = theme.AddressEntry.TextColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = props.transparency, @@ -51,8 +51,8 @@ local function AddressEntry(props) Port = e("TextBox", { Text = props.port or "", - Font = Enum.Font.Code, - TextSize = 18, + FontFace = theme.Font.Code, + TextSize = theme.TextSize.Large, TextColor3 = theme.AddressEntry.TextColor, TextTransparency = props.transparency, PlaceholderText = Config.defaultPort, diff --git a/plugin/src/App/StatusPages/Settings/Setting.lua b/plugin/src/App/StatusPages/Settings/Setting.lua index 00c75d86d..e9ea13c20 100644 --- a/plugin/src/App/StatusPages/Settings/Setting.lua +++ b/plugin/src/App/StatusPages/Settings/Setting.lua @@ -1,5 +1,3 @@ -local TextService = game:GetService("TextService") - local Rojo = script:FindFirstAncestor("Rojo") local Plugin = Rojo.Plugin local Packages = Rojo.Packages @@ -9,6 +7,7 @@ local Roact = require(Packages.Roact) local Settings = require(Plugin.Settings) local Assets = require(Plugin.Assets) local Theme = require(Plugin.App.Theme) +local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync) local Checkbox = require(Plugin.App.Components.Checkbox) local Dropdown = require(Plugin.App.Components.Dropdown) @@ -31,10 +30,16 @@ local TAG_TYPES = { }, } -local function getTextBounds(text, textSize, font, lineHeight, bounds) - local textBounds = TextService:GetTextSize(text, textSize, font, bounds) +local function getTextBoundsWithLineHeight( + text: string, + font: Font, + textSize: number, + width: number, + lineHeight: number +) + local textBounds = getTextBoundsAsync(text, font, textSize, width) - local lineCount = textBounds.Y / textSize + local lineCount = math.ceil(textBounds.Y / textSize) local lineHeightAbsolute = textSize * lineHeight return Vector2.new(textBounds.X, lineHeightAbsolute * lineCount - (lineHeightAbsolute - textSize)) @@ -145,7 +150,7 @@ function Setting:render() BackgroundTransparency = 1, }, { Heading = e("Frame", { - Size = UDim2.new(1, 0, 0, 16), + Size = UDim2.new(1, 0, 0, theme.TextSize.Medium), BackgroundTransparency = 1, }, { Layout = e("UIListLayout", { @@ -165,8 +170,8 @@ function Setting:render() else nil, Name = e("TextLabel", { Text = self.props.name, - Font = Enum.Font.GothamBold, - TextSize = 16, + FontFace = theme.Font.Bold, + TextSize = theme.TextSize.Medium, TextColor3 = if self.props.tag and TAG_TYPES[self.props.tag] then getThemeColorFromPath(theme, TAG_TYPES[self.props.tag].color) else settingsTheme.Setting.NameColor, @@ -174,7 +179,7 @@ function Setting:render() TextTransparency = self.props.transparency, RichText = true, - Size = UDim2.new(1, 0, 0, 16), + Size = UDim2.new(1, 0, 0, theme.TextSize.Medium), LayoutOrder = 2, BackgroundTransparency = 1, @@ -183,9 +188,9 @@ function Setting:render() Description = e("TextLabel", { Text = self.props.description, - Font = Enum.Font.Gotham, + FontFace = theme.Font.Main, LineHeight = 1.2, - TextSize = 14, + TextSize = theme.TextSize.Body, TextColor3 = settingsTheme.Setting.DescriptionColor, TextXAlignment = Enum.TextXAlignment.Left, TextTransparency = self.props.transparency, @@ -197,12 +202,12 @@ function Setting:render() inputSize = self.inputSize, }):map(function(values) local offset = values.inputSize.X + 5 - local textBounds = getTextBounds( + local textBounds = getTextBoundsWithLineHeight( self.props.description, - 14, - Enum.Font.Gotham, - 1.2, - Vector2.new(values.containerSize.X - offset, math.huge) + theme.Font.Main, + theme.TextSize.Body, + values.containerSize.X - offset, + 1.2 ) return UDim2.new(1, -offset, 0, textBounds.Y) end), diff --git a/plugin/src/App/StatusPages/Settings/init.lua b/plugin/src/App/StatusPages/Settings/init.lua index 122a9f281..e0f3413ce 100644 --- a/plugin/src/App/StatusPages/Settings/init.lua +++ b/plugin/src/App/StatusPages/Settings/init.lua @@ -30,7 +30,7 @@ local confirmationBehaviors = { "Initial", "Always", "Large Changes", "Unlisted local function Navbar(props) return Theme.with(function(theme) - theme = theme.Settings.Navbar + local navbarTheme = theme.Settings.Navbar return e("Frame", { Size = UDim2.new(1, 0, 0, 46), @@ -40,7 +40,7 @@ local function Navbar(props) Back = e(IconButton, { icon = Assets.Images.Icons.Back, iconSize = 24, - color = theme.BackButtonColor, + color = navbarTheme.BackButtonColor, transparency = props.transparency, position = UDim2.new(0, 0, 0.5, 0), @@ -55,9 +55,9 @@ local function Navbar(props) Text = e("TextLabel", { Text = "Settings", - Font = Enum.Font.Gotham, - TextSize = 18, - TextColor3 = theme.TextColor, + FontFace = theme.Font.Thin, + TextSize = theme.TextSize.Large, + TextColor3 = navbarTheme.TextColor, TextTransparency = props.transparency, Size = UDim2.new(1, 0, 1, 0), @@ -81,185 +81,181 @@ function SettingsPage:render() return layoutOrder end - return Theme.with(function(theme) - theme = theme.Settings - - return Roact.createFragment({ - Navbar = e(Navbar, { - onBack = self.props.onBack, + return Roact.createFragment({ + Navbar = e(Navbar, { + onBack = self.props.onBack, + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), + }), + Content = e(ScrollingFrame, { + size = UDim2.new(1, 0, 1, -47), + position = UDim2.new(0, 0, 0, 47), + contentSize = self.contentSize, + transparency = self.props.transparency, + }, { + ShowNotifications = e(Setting, { + id = "showNotifications", + name = "Show Notifications", + description = "Popup notifications in viewport", transparency = self.props.transparency, layoutOrder = layoutIncrement(), }), - Content = e(ScrollingFrame, { - size = UDim2.new(1, 0, 1, -47), - position = UDim2.new(0, 0, 0, 47), - contentSize = self.contentSize, - transparency = self.props.transparency, - }, { - ShowNotifications = e(Setting, { - id = "showNotifications", - name = "Show Notifications", - description = "Popup notifications in viewport", - transparency = self.props.transparency, - layoutOrder = layoutIncrement(), - }), - SyncReminder = e(Setting, { - id = "syncReminder", - name = "Sync Reminder", - description = "Notify to sync when opening a place that has previously been synced", - transparency = self.props.transparency, - visible = Settings:getBinding("showNotifications"), - layoutOrder = layoutIncrement(), - }), + SyncReminder = e(Setting, { + id = "syncReminder", + name = "Sync Reminder", + description = "Notify to sync when opening a place that has previously been synced", + transparency = self.props.transparency, + visible = Settings:getBinding("showNotifications"), + layoutOrder = layoutIncrement(), + }), - ConfirmationBehavior = e(Setting, { - id = "confirmationBehavior", - name = "Confirmation Behavior", - description = "When to prompt for confirmation before syncing", - transparency = self.props.transparency, - layoutOrder = layoutIncrement(), + ConfirmationBehavior = e(Setting, { + id = "confirmationBehavior", + name = "Confirmation Behavior", + description = "When to prompt for confirmation before syncing", + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), - options = confirmationBehaviors, - }), + options = confirmationBehaviors, + }), - LargeChangesConfirmationThreshold = e(Setting, { - id = "largeChangesConfirmationThreshold", - name = "Confirmation Threshold", - description = "How many modified instances to be considered a large change", - transparency = self.props.transparency, - layoutOrder = layoutIncrement(), - visible = Settings:getBinding("confirmationBehavior"):map(function(value) - return value == "Large Changes" + LargeChangesConfirmationThreshold = e(Setting, { + id = "largeChangesConfirmationThreshold", + name = "Confirmation Threshold", + description = "How many modified instances to be considered a large change", + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), + visible = Settings:getBinding("confirmationBehavior"):map(function(value) + return value == "Large Changes" + end), + input = e(TextInput, { + size = UDim2.new(0, 40, 0, 28), + text = Settings:getBinding("largeChangesConfirmationThreshold"):map(function(value) + return tostring(value) end), - input = e(TextInput, { - size = UDim2.new(0, 40, 0, 28), - text = Settings:getBinding("largeChangesConfirmationThreshold"):map(function(value) - return tostring(value) - end), - transparency = self.props.transparency, - enabled = true, - onEntered = function(text) - local number = tonumber(string.match(text, "%d+")) - if number then - Settings:set("largeChangesConfirmationThreshold", math.clamp(number, 1, 999)) - else - -- Force text back to last valid value - Settings:set( - "largeChangesConfirmationThreshold", - Settings:get("largeChangesConfirmationThreshold") - ) - end - end, - }), - }), - - PlaySounds = e(Setting, { - id = "playSounds", - name = "Play Sounds", - description = "Toggle sound effects", transparency = self.props.transparency, - layoutOrder = layoutIncrement(), + enabled = true, + onEntered = function(text) + local number = tonumber(string.match(text, "%d+")) + if number then + Settings:set("largeChangesConfirmationThreshold", math.clamp(number, 1, 999)) + else + -- Force text back to last valid value + Settings:set( + "largeChangesConfirmationThreshold", + Settings:get("largeChangesConfirmationThreshold") + ) + end + end, }), + }), - CheckForUpdates = e(Setting, { - id = "checkForUpdates", - name = "Check For Updates", - description = "Notify about newer compatible Rojo releases", - transparency = self.props.transparency, - layoutOrder = layoutIncrement(), - }), + PlaySounds = e(Setting, { + id = "playSounds", + name = "Play Sounds", + description = "Toggle sound effects", + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), + }), - CheckForPreleases = e(Setting, { - id = "checkForPrereleases", - name = "Include Prerelease Updates", - description = "Include prereleases when checking for updates", - transparency = self.props.transparency, - layoutOrder = layoutIncrement(), - visible = if string.find(debug.traceback(), "\n[^\n]-user_.-$") == nil - then false -- Must be a local install to allow prerelease checks - else Settings:getBinding("checkForUpdates"), - }), + CheckForUpdates = e(Setting, { + id = "checkForUpdates", + name = "Check For Updates", + description = "Notify about newer compatible Rojo releases", + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), + }), - AutoConnectPlaytestServer = e(Setting, { - id = "autoConnectPlaytestServer", - name = "Auto Connect Playtest Server", - description = "Automatically connect game server to Rojo when playtesting while connected in Edit", - tag = "unstable", - transparency = self.props.transparency, - layoutOrder = layoutIncrement(), - }), + CheckForPreleases = e(Setting, { + id = "checkForPrereleases", + name = "Include Prerelease Updates", + description = "Include prereleases when checking for updates", + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), + visible = if string.find(debug.traceback(), "\n[^\n]-user_.-$") == nil + then false -- Must be a local install to allow prerelease checks + else Settings:getBinding("checkForUpdates"), + }), - OpenScriptsExternally = e(Setting, { - id = "openScriptsExternally", - name = "Open Scripts Externally", - description = "Attempt to open scripts in an external editor", - tag = "unstable", - transparency = self.props.transparency, - layoutOrder = layoutIncrement(), - }), + AutoConnectPlaytestServer = e(Setting, { + id = "autoConnectPlaytestServer", + name = "Auto Connect Playtest Server", + description = "Automatically connect game server to Rojo when playtesting while connected in Edit", + tag = "unstable", + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), + }), - TwoWaySync = e(Setting, { - id = "twoWaySync", - name = "Two-Way Sync", - description = "Editing files in Studio will sync them into the filesystem", - locked = self.props.syncActive, - tag = "unstable", - transparency = self.props.transparency, - layoutOrder = layoutIncrement(), - }), + OpenScriptsExternally = e(Setting, { + id = "openScriptsExternally", + name = "Open Scripts Externally", + description = "Attempt to open scripts in an external editor", + tag = "unstable", + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), + }), - LogLevel = e(Setting, { - id = "logLevel", - name = "Log Level", - description = "Plugin output verbosity level", - tag = "debug", - transparency = self.props.transparency, - layoutOrder = layoutIncrement(), + TwoWaySync = e(Setting, { + id = "twoWaySync", + name = "Two-Way Sync", + description = "Editing files in Studio will sync them into the filesystem", + locked = self.props.syncActive, + tag = "unstable", + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), + }), - options = invertedLevels, - showReset = Settings:getBinding("logLevel"):map(function(value) - return value ~= "Info" - end), - onReset = function() - Settings:set("logLevel", "Info") - end, - }), + LogLevel = e(Setting, { + id = "logLevel", + name = "Log Level", + description = "Plugin output verbosity level", + tag = "debug", + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), - TypecheckingEnabled = e(Setting, { - id = "typecheckingEnabled", - name = "Typechecking", - description = "Toggle typechecking on the API surface", - tag = "debug", - transparency = self.props.transparency, - layoutOrder = layoutIncrement(), - }), + options = invertedLevels, + showReset = Settings:getBinding("logLevel"):map(function(value) + return value ~= "Info" + end), + onReset = function() + Settings:set("logLevel", "Info") + end, + }), - TimingLogsEnabled = e(Setting, { - id = "timingLogsEnabled", - name = "Timing Logs", - description = "Toggle logging timing of internal actions for benchmarking Rojo performance", - tag = "debug", - transparency = self.props.transparency, - layoutOrder = layoutIncrement(), - }), + TypecheckingEnabled = e(Setting, { + id = "typecheckingEnabled", + name = "Typechecking", + description = "Toggle typechecking on the API surface", + tag = "debug", + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), + }), - Layout = e("UIListLayout", { - FillDirection = Enum.FillDirection.Vertical, - SortOrder = Enum.SortOrder.LayoutOrder, + TimingLogsEnabled = e(Setting, { + id = "timingLogsEnabled", + name = "Timing Logs", + description = "Toggle logging timing of internal actions for benchmarking Rojo performance", + tag = "debug", + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), + }), - [Roact.Change.AbsoluteContentSize] = function(object) - self.setContentSize(object.AbsoluteContentSize) - end, - }), + Layout = e("UIListLayout", { + FillDirection = Enum.FillDirection.Vertical, + SortOrder = Enum.SortOrder.LayoutOrder, - Padding = e("UIPadding", { - PaddingLeft = UDim.new(0, 20), - PaddingRight = UDim.new(0, 20), - }), + [Roact.Change.AbsoluteContentSize] = function(object) + self.setContentSize(object.AbsoluteContentSize) + end, }), - }) - end) + + Padding = e("UIPadding", { + PaddingLeft = UDim.new(0, 20), + PaddingRight = UDim.new(0, 20), + }), + }), + }) end return SettingsPage diff --git a/plugin/src/App/Theme.lua b/plugin/src/App/Theme.lua index 42d619425..6e644aca2 100644 --- a/plugin/src/App/Theme.lua +++ b/plugin/src/App/Theme.lua @@ -1,7 +1,6 @@ --[[ - Theming system taking advantage of Roact's new context API. - Doesn't use colors provided by Studio and instead just branches on theme - name. This isn't exactly best practice. + Theming system provided through Roact's context. + Uses Studio colors when possible. ]] -- Studio does not exist outside Roblox Studio, so we'll lazily initialize it @@ -15,6 +14,8 @@ local function getStudio() return _Studio end +local ContentProvider = game:GetService("ContentProvider") + local Rojo = script:FindFirstAncestor("Rojo") local Packages = Rojo.Packages @@ -35,6 +36,27 @@ function StudioProvider:updateTheme() local isDark = studioTheme.Name == "Dark" local theme = strict(studioTheme.Name .. "Theme", { + Font = { + Main = Font.new("rbxasset://fonts/families/Montserrat.json", Enum.FontWeight.Medium, Enum.FontStyle.Normal), + Bold = Font.new("rbxasset://fonts/families/Montserrat.json", Enum.FontWeight.Bold, Enum.FontStyle.Normal), + Thin = Font.new( + "rbxasset://fonts/families/Montserrat.json", + Enum.FontWeight.Regular, + Enum.FontStyle.Normal + ), + Code = Font.new( + "rbxasset://fonts/families/Inconsolata.json", + Enum.FontWeight.Regular, + Enum.FontStyle.Normal + ), + }, + TextSize = { + Body = 15, + Small = 13, + Medium = 16, + Large = 18, + Code = 16, + }, BrandColor = BRAND_COLOR, BackgroundColor = studioTheme:GetColor(Enum.StudioStyleGuideColor.MainBackground), TextColor = studioTheme:GetColor(Enum.StudioStyleGuideColor.MainText), @@ -190,6 +212,13 @@ end function StudioProvider:init() self:updateTheme() + + -- Preload the Fonts so that getTextBoundsAsync won't yield + local fontAssetIds = {} + for _, font in self.state.theme.Font do + table.insert(fontAssetIds, font.Family) + end + pcall(ContentProvider.PreloadAsync, ContentProvider, fontAssetIds) end function StudioProvider:render() diff --git a/plugin/src/App/getTextBoundsAsync.lua b/plugin/src/App/getTextBoundsAsync.lua new file mode 100644 index 000000000..2813903e6 --- /dev/null +++ b/plugin/src/App/getTextBoundsAsync.lua @@ -0,0 +1,41 @@ +local TextService = game:GetService("TextService") + +local Rojo = script:FindFirstAncestor("Rojo") +local Packages = Rojo.Packages + +local Log = require(Packages.Log) + +local params = Instance.new("GetTextBoundsParams") + +local function getTextBoundsAsync( + text: string, + font: Font, + textSize: number, + width: number, + richText: boolean? +): Vector2 + if type(text) ~= "string" then + Log.warn(`Invalid text. Expected string, received {type(text)} instead`) + return Vector2.zero + end + if #text >= 200_000 then + Log.warn(`Invalid text. Exceeds the 199,999 character limit`) + return Vector2.zero + end + + params.Text = text + params.Font = font + params.Size = textSize + params.Width = width + params.RichText = not not richText + + local success, bounds = pcall(TextService.GetTextBoundsAsync, TextService, params) + if not success then + Log.warn(`Failed to get text bounds: {bounds}`) + return Vector2.zero + end + + return bounds +end + +return getTextBoundsAsync