From bc2c76e5e2c4e0ac6bb7c9bd83a4953a8edd52bb Mon Sep 17 00:00:00 2001 From: Kenneth Loeffler Date: Wed, 6 Nov 2024 15:59:03 +0000 Subject: [PATCH 1/4] Use 7.5.0-prealpha for master branch version, not 7.4.4

in #989, we changed Rojo's version number on the master branch to 7.4.4. This is a little odd, because 7.4.4 is already released, is diverged from the master branch, and we are not working towards 7.4.4 on the master branch. If we're going to spend time on this, I think we should use a more appropriate version number.

This PR changes the version number to 7.5.0-prealpha, since Rojo's master branch is currently undergoing development towards 7.5.0. We will most likely not be making a release of this version - the only intent is better clarity for those running Rojo's latest master.

--- Cargo.lock | 2 +- Cargo.toml | 2 +- plugin/Version.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 908ff793..3e61f7c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1752,7 +1752,7 @@ dependencies = [ [[package]] name = "rojo" -version = "7.4.4" +version = "7.5.0-prealpha" dependencies = [ "anyhow", "backtrace", diff --git a/Cargo.toml b/Cargo.toml index 700f6e47..9798e670 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rojo" -version = "7.4.4" +version = "7.5.0-prealpha" rust-version = "1.70.0" authors = ["Lucien Greathouse "] description = "Enables professional-grade development tools for Roblox developers" diff --git a/plugin/Version.txt b/plugin/Version.txt index c74107f7..9b648123 100644 --- a/plugin/Version.txt +++ b/plugin/Version.txt @@ -1 +1 @@ -7.4.4 \ No newline at end of file +7.5.0-prealpha \ No newline at end of file From 80c406f19697af9992d2808e50fe927e4865f303 Mon Sep 17 00:00:00 2001 From: Kenneth Loeffler Date: Fri, 8 Nov 2024 08:40:32 +0000 Subject: [PATCH 2/4] Fix returning NoProjectFound for any project load error (#985) In #917, we accidentally changed ServeSession::new's project loading logic so that it always returns `ServeSession::ProjectNotFound` if the load fails for any reason. This PR fixes this so that it returns the right error when there is an error loading the project, and moves the `NoProjectFound` error to `project::Error`, since I think it makes more sense there. --- src/project.rs | 28 ++++++++++++++++++++++------ src/serve_session.rs | 18 ++---------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/project.rs b/src/project.rs index b9573427..a83088c0 100644 --- a/src/project.rs +++ b/src/project.rs @@ -10,10 +10,7 @@ use memofs::Vfs; use serde::{Deserialize, Serialize}; use thiserror::Error; -use crate::{ - glob::Glob, resolution::UnresolvedValue, snapshot::SyncRule, - snapshot_middleware::default_sync_rules, -}; +use crate::{glob::Glob, resolution::UnresolvedValue, snapshot::SyncRule}; static PROJECT_FILENAME: &str = "default.project.json"; @@ -24,6 +21,13 @@ pub struct ProjectError(#[from] Error); #[derive(Debug, Error)] enum Error { + #[error( + "Rojo requires a project file, but no project file was found in path {}\n\ + See https://rojo.space/docs/ for guides and documentation.", + .path.display() + )] + NoProjectFound { path: PathBuf }, + #[error("The folder for the provided project cannot be used as a project name: {}\n\ Consider setting the `name` field on this project.", .path.display())] FolderNameInvalid { path: PathBuf }, @@ -222,7 +226,13 @@ impl Project { fuzzy_project_location: &Path, ) -> Result, ProjectError> { if let Some(project_path) = Self::locate(fuzzy_project_location) { - let contents = vfs.read(&project_path).map_err(Error::from)?; + let contents = vfs.read(&project_path).map_err(|e| match e.kind() { + io::ErrorKind::NotFound => Error::NoProjectFound { + path: project_path.to_path_buf(), + }, + _ => return e.into(), + })?; + Ok(Some(Self::load_from_slice(&contents, project_path, None)?)) } else { Ok(None) @@ -236,7 +246,13 @@ impl Project { fallback_name: Option<&str>, ) -> Result { let project_path = project_file_location.to_path_buf(); - let contents = vfs.read(&project_path).map_err(Error::from)?; + let contents = vfs.read(&project_path).map_err(|e| match e.kind() { + io::ErrorKind::NotFound => Error::NoProjectFound { + path: project_path.to_path_buf(), + }, + _ => return e.into(), + })?; + Ok(Self::load_from_slice( &contents, project_path, diff --git a/src/serve_session.rs b/src/serve_session.rs index ddb4dc21..25317d74 100644 --- a/src/serve_session.rs +++ b/src/serve_session.rs @@ -3,7 +3,7 @@ use std::{ collections::HashSet, io, net::IpAddr, - path::{Path, PathBuf}, + path::Path, sync::{Arc, Mutex, MutexGuard}, time::Instant, }; @@ -109,14 +109,7 @@ impl ServeSession { log::debug!("Loading project file from {}", project_path.display()); - let root_project = match Project::load_exact(&vfs, &project_path, None) { - Ok(project) => project, - Err(_) => { - return Err(ServeSessionError::NoProjectFound { - path: project_path.to_path_buf(), - }) - } - }; + let root_project = Project::load_exact(&vfs, &project_path, None)?; let mut tree = RojoTree::new(InstanceSnapshot::new()); @@ -226,13 +219,6 @@ impl ServeSession { #[derive(Debug, Error)] pub enum ServeSessionError { - #[error( - "Rojo requires a project file, but no project file was found in path {}\n\ - See https://rojo.space/docs/ for guides and documentation.", - .path.display() - )] - NoProjectFound { path: PathBuf }, - #[error(transparent)] Io { #[from] From 8c33100d7a7fcbb7418c73c6373e9dadecbf1258 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Sat, 9 Nov 2024 04:05:57 -0800 Subject: [PATCH 3/4] Use FontFace and consistent text sizing (#988) --- CHANGELOG.md | 2 + plugin/src/App/Components/Checkbox.lua | 10 +- plugin/src/App/Components/CodeLabel.lua | 37 +- plugin/src/App/Components/Dropdown.lua | 33 +- plugin/src/App/Components/Header.lua | 6 +- .../Components/PatchVisualizer/ChangeList.lua | 20 +- .../PatchVisualizer/DisplayValue.lua | 12 +- .../Components/PatchVisualizer/DomLabel.lua | 16 +- .../App/Components/PatchVisualizer/init.lua | 4 +- .../Components/StringDiffVisualizer/init.lua | 13 +- .../Components/TableDiffVisualizer/Array.lua | 8 +- .../TableDiffVisualizer/Dictionary.lua | 16 +- plugin/src/App/Components/Tag.lua | 85 ++--- plugin/src/App/Components/TextButton.lua | 42 ++- plugin/src/App/Components/TextInput.lua | 30 +- plugin/src/App/Components/Tooltip.lua | 72 ++-- plugin/src/App/Notifications.lua | 93 +++-- plugin/src/App/StatusPages/Confirming.lua | 6 +- plugin/src/App/StatusPages/Connected.lua | 38 +- plugin/src/App/StatusPages/Error.lua | 89 +++-- plugin/src/App/StatusPages/NotConnected.lua | 8 +- .../src/App/StatusPages/Settings/Setting.lua | 37 +- plugin/src/App/StatusPages/Settings/init.lua | 340 +++++++++--------- plugin/src/App/Theme.lua | 35 +- plugin/src/App/getTextBoundsAsync.lua | 41 +++ 25 files changed, 597 insertions(+), 496 deletions(-) create mode 100644 plugin/src/App/getTextBoundsAsync.lua diff --git a/CHANGELOG.md b/CHANGELOG.md index fbc01f68..088a9669 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 0d37fd86..c4572e93 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 0cdba8e9..1951106b 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 a5dda6f2..72629c18 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 ca6acecf..81e2f46a 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 2f10d458..fd6f83cc 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 f7e772b9..94106af6 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 a842dcbc..689f8143 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 a87d499b..3e600dca 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 e4203c21..a826c649 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 febd07e9..4e9a3daf 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 033956c3..db3697bd 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 9fed3d33..6e6d4c4f 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 bd643c79..f4243c1b 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 61b688d8..1187296b 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 2ab7e19a..55b056c2 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) - - 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 - - -- 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 - - local X = math.clamp(props.Position.X - X_OFFSET, 0, props.parentSize.X - textSize.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 - 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 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 < contentSize.Y and spaceAbove > spaceBelow + + 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 - 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 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 682758cb..5bb581c7 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)) + 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, + }) - 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) - buttonsX += TextService:GetTextSize( - action.text, - 18, - Enum.Font.GothamMedium, - Vector2.new(math.huge, math.huge) - ).X + 30 + 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 df8bf5b4..3f3958a4 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 b0bf33d9..1d089e28 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 5d1bcb5e..f51fb6a4 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 + 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 + 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 UDim2.new(1, 0, 0, math.min(outerSize.Y, maximumSize.Y)) end), transparency = self.props.transparency, - - [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) - ) - - self.setContentSize(Vector2.new(containerSize.X, textBounds.Y)) - end, }, { - ErrorMessage = Theme.with(function(theme) - return e("TextBox", { + 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 + + local textBounds = getTextBoundsAsync( + self.props.errorMessage, + theme.Font.Code, + theme.TextSize.Code, + containerSize.X + ) + + 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 e881e7eb..aabf7640 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 00c75d86..e9ea13c2 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 122a9f28..e0f3413c 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, + + 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, - }, { - ShowNotifications = e(Setting, { - id = "showNotifications", - name = "Show Notifications", - description = "Popup notifications in viewport", - transparency = self.props.transparency, - layoutOrder = layoutIncrement(), - }), + 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(), - }), - - CheckForUpdates = e(Setting, { - id = "checkForUpdates", - name = "Check For Updates", - description = "Notify about newer compatible Rojo releases", - 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"), - }), - - 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(), - }), - - 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(), - }), - - 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(), - }), - - LogLevel = e(Setting, { - id = "logLevel", - name = "Log Level", - description = "Plugin output verbosity level", - 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") + 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, }), - - TypecheckingEnabled = e(Setting, { - id = "typecheckingEnabled", - name = "Typechecking", - description = "Toggle typechecking on the API surface", - tag = "debug", - transparency = self.props.transparency, - layoutOrder = layoutIncrement(), - }), - - 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(), - }), - - Layout = e("UIListLayout", { - FillDirection = Enum.FillDirection.Vertical, - SortOrder = Enum.SortOrder.LayoutOrder, - - [Roact.Change.AbsoluteContentSize] = function(object) - self.setContentSize(object.AbsoluteContentSize) - end, - }), - - Padding = e("UIPadding", { - PaddingLeft = UDim.new(0, 20), - PaddingRight = UDim.new(0, 20), - }), }), - }) - end) + + PlaySounds = e(Setting, { + id = "playSounds", + name = "Play Sounds", + description = "Toggle sound effects", + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), + }), + + CheckForUpdates = e(Setting, { + id = "checkForUpdates", + name = "Check For Updates", + description = "Notify about newer compatible Rojo releases", + 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"), + }), + + 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(), + }), + + 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(), + }), + + 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(), + }), + + LogLevel = e(Setting, { + id = "logLevel", + name = "Log Level", + description = "Plugin output verbosity level", + 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, + }), + + TypecheckingEnabled = e(Setting, { + id = "typecheckingEnabled", + name = "Typechecking", + description = "Toggle typechecking on the API surface", + tag = "debug", + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), + }), + + 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(), + }), + + Layout = e("UIListLayout", { + FillDirection = Enum.FillDirection.Vertical, + SortOrder = Enum.SortOrder.LayoutOrder, + + [Roact.Change.AbsoluteContentSize] = function(object) + self.setContentSize(object.AbsoluteContentSize) + 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 42d61942..6e644aca 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 00000000..2813903e --- /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 From b7d3394464e0ffb350b9c8481399cc5845c10f07 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Sun, 10 Nov 2024 15:53:58 -0800 Subject: [PATCH 4/4] Plugin dev ux improvements (#992) Co-authored-by: kennethloeffler --- .dir-locals.el | 2 ++ .github/workflows/release.yml | 2 +- .gitignore | 4 ++-- .vscode/extensions.json | 8 +++++++ .vscode/settings.json | 4 ++++ CONTRIBUTING.md | 17 +++++++++++++ build.rs | 24 ++++++++++--------- ...efault.project.json => plugin.project.json | 16 ++++++------- plugin/run-tests.server.lua | 2 +- plugin/test | 4 ---- plugin/test-place.project.json | 2 +- plugin/watch-build.sh | 2 -- scripts/unit-test-plugin.sh | 2 ++ scripts/watch-build-plugin.sh | 1 + 14 files changed, 60 insertions(+), 30 deletions(-) create mode 100644 .dir-locals.el create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json rename plugin/default.project.json => plugin.project.json (50%) delete mode 100644 plugin/test delete mode 100644 plugin/watch-build.sh create mode 100644 scripts/unit-test-plugin.sh create mode 100644 scripts/watch-build-plugin.sh diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 00000000..25e8e6c1 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,2 @@ +((nil . ((eglot-luau-rojo-project-path . "plugin.project.json") + (eglot-luau-rojo-sourcemap-enabled . 't)))) \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a1718858..1441c04a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: version: 'v0.3.0' - name: Build Plugin - run: rojo build plugin --output Rojo.rbxm + run: rojo build plugin.project.json --output Rojo.rbxm - name: Upload Plugin to Release env: diff --git a/.gitignore b/.gitignore index 88cdf551..b1a5efe9 100644 --- a/.gitignore +++ b/.gitignore @@ -10,8 +10,8 @@ /*.rbxl /*.rbxlx -# Test places for the Roblox Studio Plugin -/plugin/*.rbxlx +# Sourcemap for the Rojo plugin (for better intellisense) +/sourcemap.json # Roblox Studio holds 'lock' files on places *.rbxl.lock diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..6ffebea2 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "JohnnyMorganz.luau-lsp", + "JohnnyMorganz.stylua", + "Kampfkarren.selene-vscode", + "rust-lang.rust-analyzer" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..f6578386 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "luau-lsp.sourcemap.rojoProjectFile": "plugin.project.json", + "luau-lsp.sourcemap.autogenerate": true +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8f3efd19..7eccb0ee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,6 +16,23 @@ You'll want these tools to work on Rojo: * Latest stable Rust compiler * Latest stable [Rojo](https://github.com/rojo-rbx/rojo) * [Foreman](https://github.com/Roblox/foreman) +* [Luau Language Server](https://github.com/JohnnyMorganz/luau-lsp) (Only needed if working on the Studio plugin.) + +When working on the Studio plugin, we recommend using this command to automatically rebuild the plugin when you save a change: + +*(Make sure you've enabled the Studio setting to reload plugins on file change!)* + +```bash +bash scripts/watch-build-plugin.sh +``` + +You can also run the plugin's unit tests with the following: + +*(Make sure you have `run-in-roblox` installed first!)* + +```bash +bash scripts/unit-test-plugin.sh +``` ## Documentation Documentation impacts way more people than the individual lines of code we write. diff --git a/build.rs b/build.rs index 9f9a606b..e423b61b 100644 --- a/build.rs +++ b/build.rs @@ -41,12 +41,12 @@ fn snapshot_from_fs_path(path: &Path) -> io::Result { fn main() -> Result<(), anyhow::Error> { let out_dir = env::var_os("OUT_DIR").unwrap(); - let root_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap(); - let plugin_root = PathBuf::from(root_dir).join("plugin"); + let root_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); + let plugin_dir = root_dir.join("plugin"); let our_version = Version::parse(env::var_os("CARGO_PKG_VERSION").unwrap().to_str().unwrap())?; let plugin_version = - Version::parse(fs::read_to_string(plugin_root.join("Version.txt"))?.trim())?; + Version::parse(fs::read_to_string(plugin_dir.join("Version.txt"))?.trim())?; assert_eq!( our_version, plugin_version, @@ -54,14 +54,16 @@ fn main() -> Result<(), anyhow::Error> { ); let snapshot = VfsSnapshot::dir(hashmap! { - "default.project.json" => snapshot_from_fs_path(&plugin_root.join("default.project.json"))?, - "fmt" => snapshot_from_fs_path(&plugin_root.join("fmt"))?, - "http" => snapshot_from_fs_path(&plugin_root.join("http"))?, - "log" => snapshot_from_fs_path(&plugin_root.join("log"))?, - "rbx_dom_lua" => snapshot_from_fs_path(&plugin_root.join("rbx_dom_lua"))?, - "src" => snapshot_from_fs_path(&plugin_root.join("src"))?, - "Packages" => snapshot_from_fs_path(&plugin_root.join("Packages"))?, - "Version.txt" => snapshot_from_fs_path(&plugin_root.join("Version.txt"))?, + "default.project.json" => snapshot_from_fs_path(&root_dir.join("plugin.project.json"))?, + "plugin" => VfsSnapshot::dir(hashmap! { + "fmt" => snapshot_from_fs_path(&plugin_dir.join("fmt"))?, + "http" => snapshot_from_fs_path(&plugin_dir.join("http"))?, + "log" => snapshot_from_fs_path(&plugin_dir.join("log"))?, + "rbx_dom_lua" => snapshot_from_fs_path(&plugin_dir.join("rbx_dom_lua"))?, + "src" => snapshot_from_fs_path(&plugin_dir.join("src"))?, + "Packages" => snapshot_from_fs_path(&plugin_dir.join("Packages"))?, + "Version.txt" => snapshot_from_fs_path(&plugin_dir.join("Version.txt"))?, + }), }); let out_path = Path::new(&out_dir).join("plugin.bincode"); diff --git a/plugin/default.project.json b/plugin.project.json similarity index 50% rename from plugin/default.project.json rename to plugin.project.json index 424f107e..83265250 100644 --- a/plugin/default.project.json +++ b/plugin.project.json @@ -3,25 +3,25 @@ "tree": { "$className": "Folder", "Plugin": { - "$path": "src" + "$path": "plugin/src" }, "Packages": { - "$path": "Packages", + "$path": "plugin/Packages", "Log": { - "$path": "log" + "$path": "plugin/log" }, "Http": { - "$path": "http" + "$path": "plugin/http" }, "Fmt": { - "$path": "fmt" + "$path": "plugin/fmt" }, "RbxDom": { - "$path": "rbx_dom_lua" + "$path": "plugin/rbx_dom_lua" } }, "Version": { - "$path": "Version.txt" + "$path": "plugin/Version.txt" } } -} \ No newline at end of file +} diff --git a/plugin/run-tests.server.lua b/plugin/run-tests.server.lua index 1e2130f5..0ab3cc05 100644 --- a/plugin/run-tests.server.lua +++ b/plugin/run-tests.server.lua @@ -1,6 +1,6 @@ local ReplicatedStorage = game:GetService("ReplicatedStorage") -local TestEZ = require(ReplicatedStorage.Packages.TestEZ) +local TestEZ = require(ReplicatedStorage.Packages:WaitForChild("TestEZ", 10)) local Rojo = ReplicatedStorage.Rojo diff --git a/plugin/test b/plugin/test deleted file mode 100644 index caeb8874..00000000 --- a/plugin/test +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -rojo build test-place.project.json -o TestPlace.rbxlx -run-in-roblox --script run-tests.server.lua --place TestPlace.rbxlx \ No newline at end of file diff --git a/plugin/test-place.project.json b/plugin/test-place.project.json index 70875137..f4da9815 100644 --- a/plugin/test-place.project.json +++ b/plugin/test-place.project.json @@ -5,7 +5,7 @@ "ReplicatedStorage": { "Rojo": { - "$path": "default.project.json" + "$path": "../plugin.project.json" }, "Packages": { diff --git a/plugin/watch-build.sh b/plugin/watch-build.sh deleted file mode 100644 index 91ce6f89..00000000 --- a/plugin/watch-build.sh +++ /dev/null @@ -1,2 +0,0 @@ -# Continously build the rojo plugin into the local plugin directory on Windows -rojo build plugin/default.project.json -o $LOCALAPPDATA/Roblox/Plugins/Rojo.rbxm --watch diff --git a/scripts/unit-test-plugin.sh b/scripts/unit-test-plugin.sh new file mode 100644 index 00000000..c7ff7c79 --- /dev/null +++ b/scripts/unit-test-plugin.sh @@ -0,0 +1,2 @@ +rojo build plugin/test-place.project.json -o TestPlace.rbxl +run-in-roblox --script plugin/run-tests.server.lua --place TestPlace.rbxl \ No newline at end of file diff --git a/scripts/watch-build-plugin.sh b/scripts/watch-build-plugin.sh new file mode 100644 index 00000000..c21397f2 --- /dev/null +++ b/scripts/watch-build-plugin.sh @@ -0,0 +1 @@ +rojo build plugin.project.json --plugin Rojo.rbxm --watch \ No newline at end of file