diff --git a/plugin/src/Components/App.lua b/plugin/src/Components/App.lua new file mode 100644 index 00000000..12aac48d --- /dev/null +++ b/plugin/src/Components/App.lua @@ -0,0 +1,15 @@ +local Roact = require(script:FindFirstAncestor("Rojo").Roact) + +local ConnectPanel = require(script.Parent.ConnectPanel) + +local e = Roact.createElement + +local App = Roact.Component:extend("App") + +function App:render() + return e("ScreenGui", nil, { + ConnectPanel = e(ConnectPanel), + }) +end + +return App \ No newline at end of file diff --git a/plugin/src/Components/ConnectPanel.lua b/plugin/src/Components/ConnectPanel.lua new file mode 100644 index 00000000..59c8b500 --- /dev/null +++ b/plugin/src/Components/ConnectPanel.lua @@ -0,0 +1,177 @@ +local Roact = require(script:FindFirstAncestor("Rojo").Roact) + +local FitList = require(script.Parent.FitList) +local FitText = require(script.Parent.FitText) + +local e = Roact.createElement + +local ConnectPanel = Roact.Component:extend("ConnectPanel") + +function ConnectPanel:init() + self.labelSizes = {} + self.labelSize, self.setLabelSize = Roact.createBinding(Vector2.new()) + + self:setState({ + address = "localhost", + port = "34872", + }) +end + +function ConnectPanel:updateLabelSize(name, size) + self.labelSizes[name] = size + + local x = 0 + local y = 0 + + for _, size in pairs(self.labelSizes) do + x = math.max(x, size.X) + y = math.max(y, size.Y) + end + + self.setLabelSize(Vector2.new(x, y)) +end + +function ConnectPanel:render() + return e(FitList, { + containerProps = { + BackgroundColor3 = Color3.fromRGB(8, 8, 8), + BorderColor3 = Color3.fromRGB(64, 64, 64), + Position = UDim2.new(0.5, 0, 0, 0), + AnchorPoint = Vector2.new(0.5, 0), + }, + layoutProps = { + Padding = UDim.new(0, 8), + }, + paddingProps = { + PaddingTop = UDim.new(0, 8), + PaddingBottom = UDim.new(0, 8), + PaddingLeft = UDim.new(0, 8), + PaddingRight = UDim.new(0, 8), + }, + }, { + Address = e(FitList, { + containerProps = { + LayoutOrder = 1, + BackgroundTransparency = 1, + }, + layoutProps = { + FillDirection = Enum.FillDirection.Horizontal, + Padding = UDim.new(0, 8), + }, + }, { + Label = e(FitText, { + Kind = "TextLabel", + LayoutOrder = 1, + BackgroundTransparency = 1, + TextXAlignment = Enum.TextXAlignment.Left, + Font = Enum.Font.SourceSans, + TextSize = 16, + Text = "Address", + TextColor3 = Color3.fromRGB(245, 245, 245), + [Roact.Change.AbsoluteSize] = function(rbx) + self:updateLabelSize("address", rbx.AbsoluteSize) + end, + }, { + Sizing = e("UISizeConstraint", { + MinSize = self.labelSize, + }), + }), + + Input = e("TextBox", { + LayoutOrder = 2, + Size = UDim2.new(0, 300, 0, 20), + Font = Enum.Font.SourceSans, + ClearTextOnFocus = false, + TextXAlignment = Enum.TextXAlignment.Left, + TextSize = 16, + Text = self.state.address, + [Roact.Change.Text] = function(rbx) + self:setState({ + address = rbx.Text, + }) + end, + TextColor3 = Color3.fromRGB(245, 245, 245), + BackgroundColor3 = Color3.fromRGB(8, 8, 8), + BorderColor3 = Color3.fromRGB(64, 64, 64), + }), + }), + + Port = e(FitList, { + containerProps = { + LayoutOrder = 2, + BackgroundTransparency = 1, + }, + layoutProps = { + FillDirection = Enum.FillDirection.Horizontal, + Padding = UDim.new(0, 8), + }, + }, { + Label = e(FitText, { + Kind = "TextLabel", + LayoutOrder = 1, + BackgroundTransparency = 1, + TextXAlignment = Enum.TextXAlignment.Left, + Font = Enum.Font.SourceSans, + TextSize = 16, + Text = "Port", + TextColor3 = Color3.fromRGB(245, 245, 245), + [Roact.Change.AbsoluteSize] = function(rbx) + self:updateLabelSize("port", rbx.AbsoluteSize) + end, + }, { + Sizing = e("UISizeConstraint", { + MinSize = self.labelSize, + }), + }), + + Input = e("TextBox", { + LayoutOrder = 2, + Size = UDim2.new(0, 300, 0, 20), + Font = Enum.Font.SourceSans, + ClearTextOnFocus = false, + TextXAlignment = Enum.TextXAlignment.Left, + TextSize = 16, + Text = self.state.port, + [Roact.Change.Text] = function(rbx) + self:setState({ + port = rbx.Text, + }) + end, + TextColor3 = Color3.fromRGB(245, 245, 245), + BackgroundColor3 = Color3.fromRGB(8, 8, 8), + BorderColor3 = Color3.fromRGB(64, 64, 64), + }), + }), + + Buttons = e(FitList, { + containerProps = { + LayoutOrder = 3, + BackgroundTransparency = 1, + }, + layoutProps = { + FillDirection = Enum.FillDirection.Horizontal, + Padding = UDim.new(0, 8), + }, + }, { + e(FitText, { + Kind = "TextButton", + LayoutOrder = 1, + BackgroundColor3 = Color3.fromRGB(16, 16, 16), + BorderColor3 = Color3.fromRGB(64, 64, 64), + TextColor3 = Color3.fromRGB(245, 245, 245), + Text = "Start", + }), + + e(FitText, { + Kind = "TextButton", + LayoutOrder = 2, + BackgroundColor3 = Color3.fromRGB(16, 16, 16), + BorderColor3 = Color3.fromRGB(64, 64, 64), + TextColor3 = Color3.fromRGB(245, 245, 245), + Text = "Cancel", + }), + }) + }) +end + +return ConnectPanel \ No newline at end of file diff --git a/plugin/src/Components/FitList.lua b/plugin/src/Components/FitList.lua new file mode 100644 index 00000000..d7a1c8c0 --- /dev/null +++ b/plugin/src/Components/FitList.lua @@ -0,0 +1,63 @@ +local Roact = require(script:FindFirstAncestor("Rojo").Roact) + +local e = Roact.createElement + +local function merge(...) + local output = {} + + for i = 1, select("#", ...) do + local source = select(i, ...) + + if source ~= nil then + for key, value in pairs(source) do + output[key] = value + end + end + end + + return output +end + +local FitList = Roact.Component:extend("FitList") + +function FitList:init() + self.sizeBinding, self.setSize = Roact.createBinding(UDim2.new()) +end + +function FitList:render() + local containerProps = self.props.containerProps + local layoutProps = self.props.layoutProps + local paddingProps = self.props.paddingProps + + local padding + if paddingProps ~= nil then + padding = e("UIPadding", paddingProps) + end + + local children = merge(self.props[Roact.Children], { + ["$Layout"] = e("UIListLayout", merge({ + SortOrder = Enum.SortOrder.LayoutOrder, + [Roact.Change.AbsoluteContentSize] = function(instance) + local size = instance.AbsoluteContentSize + + if paddingProps ~= nil then + size = size + Vector2.new( + paddingProps.PaddingLeft.Offset + paddingProps.PaddingRight.Offset, + paddingProps.PaddingTop.Offset + paddingProps.PaddingBottom.Offset) + end + + self.setSize(UDim2.new(0, size.X, 0, size.Y)) + end, + }, layoutProps)), + + ["$Padding"] = padding, + }) + + local fullContainerProps = merge(containerProps, { + Size = self.sizeBinding, + }) + + return e("Frame", fullContainerProps, children) +end + +return FitList \ No newline at end of file diff --git a/plugin/src/Components/FitText.lua b/plugin/src/Components/FitText.lua new file mode 100644 index 00000000..76524e03 --- /dev/null +++ b/plugin/src/Components/FitText.lua @@ -0,0 +1,69 @@ +local TextService = game:GetService("TextService") + +local Roact = require(script:FindFirstAncestor("Rojo").Roact) + +local e = Roact.createElement + +local None = newproxy(false) +local function merge(...) + local output = {} + + for i = 1, select("#", ...) do + local source = select(i, ...) + + if source ~= nil then + for key, value in pairs(source) do + if value == None then + output[key] = nil + else + output[key] = value + end + end + end + end + + return output +end + +local FitText = Roact.Component:extend("FitText") + +function FitText:init() + self.sizeBinding, self.setSize = Roact.createBinding(UDim2.new()) +end + +function FitText:render() + local kind = self.props.Kind + + local containerProps = merge(self.props, { + Kind = None, + Padding = None, + Size = self.sizeBinding + }) + + return e(kind, containerProps) +end + +function FitText:didMount() + self:updateTextMeasurements() +end + +function FitText:didUpdate() + self:updateTextMeasurements() +end + +function FitText:updateTextMeasurements() + local padding = self.props.Padding or Vector2.new(0, 0) + + local text = self.props.Text or "" + local font = self.props.Font or Enum.Font.Legacy + local textSize = self.props.TextSize or 12 + + local measuredText = TextService:GetTextSize(text, textSize, font, Vector2.new(9e6, 9e6)) + local totalSize = UDim2.new( + 0, padding.X * 2 + measuredText.X, + 0, padding.Y * 2 + measuredText.Y) + + self.setSize(totalSize) +end + +return FitText \ No newline at end of file diff --git a/plugin/src/init.server.lua b/plugin/src/init.server.lua index 8f18ea53..b956cbf2 100644 --- a/plugin/src/init.server.lua +++ b/plugin/src/init.server.lua @@ -2,11 +2,18 @@ if not plugin then return end +local Roact = require(script.Parent.Roact) + +Roact.setGlobalConfig({ + elementTracing = true, +}) + local Session = require(script.Session) local Config = require(script.Config) local Version = require(script.Version) local Logging = require(script.Logging) local DevSettings = require(script.DevSettings) +local App = require(script.Components.App) local function showUpgradeMessage(lastVersion) local message = ( @@ -53,25 +60,29 @@ local function main() local toolbar = plugin:CreateToolbar("Rojo " .. displayedVersion) + Roact.mount(Roact.createElement(App), game:GetService("CoreGui"), "Rojo UI") + local currentSession - -- TODO: More robust session tracking to handle errors -- TODO: Icon! - toolbar:CreateButton("Connect", "Connect to Rojo Session", "") - .Click:Connect(function() - checkUpgrade() + local connectButton = toolbar:CreateButton("Connect", "Connect to Rojo Session", "") + connectButton.ClickableWhenViewportHidden = true - if currentSession ~= nil then - Logging.warn("A session is already running!") - return - end + connectButton.Click:Connect(function() + connectButton:SetActive(false) + checkUpgrade() - Logging.info("Started new session.") - currentSession = Session.new(function() - Logging.info("Session terminated.") - currentSession = nil - end) + if currentSession ~= nil then + Logging.warn("A session is already running!") + return + end + + Logging.info("Started new session.") + currentSession = Session.new(function() + Logging.info("Session terminated.") + currentSession = nil end) + end) end main() \ No newline at end of file