Giter Site home page Giter Site logo

studiowidgets's Introduction

Studio Widgets

A set of GUI elements to use in Roblox Plugins hosted in PluginGUIs. Widgets have a standard "Studio" look & feel.
ย 

Overview

With PluginGuis, your RobloxPlugins can create GUIs hosted in dockable widgets (as opposed to being hosted in 3d viewport).

We encourage plugin developers to use this library so that your GUIs have a standardized look & feel: colors, spacing, layout, etc.

We will keep these libraries up to date as Studio look & feel changes (e.g. automatic support for Dark Theme, when that happens).

Please contribute!

We will work hard to keep this library up to date, bug-free, etc.

That said, we have a small team and many competing priorities, so your efforts to improve this library are welcome and invited. Feel free to fork the repository and make fixes and improvements as you see fit. We will be happy to merge in any updates that fit within our vision of the library.

Coding conventions

Class and function names are CamelCase, starting with caps. Variable and member names are CamelCase, starting with lowercase. Members and methods of classes that begin with '_' are considered "private": should not be read or written outside the class.

Files

CollapsibleTitledSection.lua

A "Section" containing one or more widgets, with titlebar. Title bar includes rotating arrow widget which can be used to collapse/expand the section.

CollapsibleTitledSection

local collapse = CollapsibleTitledSection.new(
	"suffix", -- name suffix of the gui object
	"titleText", -- the text displayed beside the collapsible arrow
	true, -- have the content frame auto-update its size?
	true, -- minimizable?
	false -- minimized by default?
)

-- put things we want to be "collapsed" under the frame returned by the :GetContentsFrame() method
local label = Instance.new("TextLabel")
label.Text = "Peekaboo!"
label.Size = UDim2.new(0, 60, 0, 20)
label.BackgroundTransparency = 1
label.BorderSizePixel = 0
label.Parent = collapse:GetContentsFrame()

-- set the parent of the collapse object by setting the parent of the frame returned by the :GetSectionFrame() method
collapse:GetSectionFrame().Parent = widgetGui

CustomTextButton.lua

A text button contained in an image (rounded rect). Button and frame highlight appropriately on hover and click.

CustomTextButton

local button = CustomTextButton.new(
	"button", -- name of the gui object
	"labelText" -- the text displayed on the button
)

-- use the :getButton() method to return the ImageButton gui object
local buttonObject = button:GetButton()
buttonObject.Size = UDim2.new(0, 70, 0, 25)

buttonObject.MouseButton1Click:Connect(function()
	print("I was clicked!")
end)

buttonObject.Parent = widgetGui

GuiUtilities.lua

Grab bag of functions and definitions used by the rest of the code: colors, spacing, etc.

ImageButtonWithText.lua

A button comprising an image above text. Button highlights appropriately on hover and click. ImageButtonWithText

local button = ImageButtonWithText.new(
	"imgButton", -- name of the gui object
	1,  -- sets the sorting order for use with a UIGridStyleLayout object
	"rbxassetid://924320031", -- the asset id of the image
	"text", -- button text 
	UDim2.new(0, 100, 0, 100), -- button size
	UDim2.new(0, 70, 0, 70), -- image size
	UDim2.new(0, 15, 0, 15), -- image position
	UDim2.new(0, 60, 0, 20), -- text size
	UDim2.new(0, 20, 0, 80) -- text position
)

-- use the :getButton() method to return an ImageButton gui object
local buttonObject = button:getButton()

buttonObject.MouseButton1Click:Connect(function()
	-- use the :setSelected() method to highlight the button
	-- use the :getSelected() method to return a boolean that defines if the button is selected or not
	button:setSelected(not button:getSelected())
end)

buttonObject.Parent = widgetGui

LabeledCheckbox.lua

A widget comprising a text label and a checkbox. Can be configured in normal or "small" sizing. Layout and spacing change depending on size.

LabeledCheckbox

local checkbox = LabeledCheckbox.new(
	"suffix", -- name suffix of gui object
	"labelText", -- text beside the checkbox
	false, -- initial value
	false -- initially disabled?
)

-- get/set current value of the checkbox
checkbox:SetValue(true)
print(checkbox:GetValue())

-- disables and forces a checkbox value
checkbox:DisableWithOverrideValue(false)
if (checkbox:GetDisabled()) then
	checkbox:SetDisabled(false)
end

-- return the label or button frames
print(checkbox:GetLabel())
print(checkbox:GetButton())

-- fire function when checkbox value changes
checkbox:SetValueChangedFunction(function(newValue)
	print(newValue);
end)

-- use :GetFrame() to set the parent of the LabeledCheckbox
checkbox:GetFrame().Parent = widgetGui

LabeledMultiChoice.lua

A widget comprising a top-level label and a family of radio buttons. Exactly one radio button is always selected. Buttons are in a grid layout and will adjust to flood-fill parent. Height updates based on content.

LabeledMultiChoice

-- each choice must have an Id and Text
local choices = {
	{Id = "choice1", Text = "a"},
	{Id = "choice2", Text = "b"},
	{Id = "choice3", Text = "c"}
}

local multiChoice = LabeledMultiChoice.new(
	"suffix", -- name suffix of gui object
	"labelText", -- title text of the multi choice
	choices, -- choices array
	1 -- the starting index of the selection (in this case choice 1)
)

-- get/set selection index
multiChoice:SetSelectedIndex(3) 
print(multiChoice:GetSelectedIndex())

-- fire function when index value changes
multiChoice:SetValueChangedFunction(function(newIndex)
	print(choices[newIndex].Id, choices[newIndex].Text)
end)

-- use :GetFrame() to set the parent of the LabeledMultiChoice
multiChoice:GetFrame().Parent = widgetGui

LabeledSlider.lua

A widget comprising a label and a slider control.

LabeledSlider

-- note: the slider is clamped between [0, intervals]
local slider = LabeledSlider.new(
	"suffix", -- name suffix of gui object
	"labelText", -- title text of the multi choice
	100, -- how many intervals to split the slider into
	50 -- the starting value of the slider
)

-- get/set values
slider:SetValue(0)
print(slider:GetValue())

-- fire function when slider value changes
slider:SetValueChangedFunction(function(newValue)
	print(newValue)
end)

-- use :GetFrame() to set the parent of the LabeledSlider
slider:GetFrame().Parent = widgetGui

LabeledTextInput.lua

A widget comprising a label and text edit control.

LabeledTextInput

local input = LabeledTextInput.new(
	"suffix", -- name suffix of gui object
	"labelText", -- title text of the multi choice
	"Hello world!" -- default value
)

-- set/get graphemes which is essentially text character limit but grapemes measure things like emojis too
input:SetMaxGraphemes(20)
input:GetMaxGraphemes()

-- set/get values methods
input:SetValue("Hello world again...")
print(input:GetValue())

-- fire function when input value changes
input:SetValueChangedFunction(function(newValue)
	print(newValue)
end)

-- use :GetFrame() to set the parent of the LabeledTextInput
input:GetFrame().Parent = widgetGui

RbxGui.lua

Helper functions to support the slider control.

StatefulImageButton.lua

An image button with "on" and "off" states.

StatefulImageButton

local button = StatefulImageButton.new(
	"imgButton", -- name of the gui object
	"rbxassetid://924320031", -- image asset id
	UDim2.new(0, 100, 0, 100) -- size of the button
)

-- set if the StatefulImageButton is selected or not
local selected = false
button:setSelected(selected)

-- use the :getButton() method to return the ImageButton gui object
local buttonObject = button:getButton()
buttonObject.MouseButton1Click:Connect(function()
	selected = not selected
	button:setSelected(selected)
end)
buttonObject.Parent = widgetGui

VerticallyScalingListFrame.lua

A frame that contains a list of sub-widgets. Will grow to accomodate size of children.

local listFrame = VerticallyScalingListFrame.new(
	"suffix" -- name suffix of gui object
)

local label = Instance.new("TextLabel")
label.Text = "labelText"
label.Size = UDim2.new(0, 60, 0, 20)
label.BackgroundTransparency = 1
label.BorderSizePixel = 0
local label2 = label:Clone()
local label3 = label:Clone()

-- fire function when the listFrame resizes
listFrame:SetCallbackOnResize(function()
	print("Frame was resized!")
end)

-- add a gui element to the VerticallyScalingListFrame
listFrame:AddChild(label)
listFrame:AddChild(label2)
listFrame:AddChild(label3)

-- add padding to the VerticallyScalingListFrame
listFrame:AddBottomPadding()

-- use :GetFrame() to set the parent of the VerticallyScalingListFrame
listFrame:GetFrame().Parent = widgetGui

VerticalScrollingFrame.lua

A frame that holds sub-widgets and gives the user the ability to scroll through them over a fixed space.

VerticalScrollingFrame

local choices = {
	{Id = "choice1", Text = "a"},
	{Id = "choice2", Text = "b"},
	{Id = "choice3", Text = "c"}
}

local scrollFrame = ScrollingFrame.new("suffix")

local listFrame = VerticallyScalingListFrame.new("suffix")
local collapse = CollapsibleTitledSection.new("suffix", "titleText", true, true, true)
local multiChoice = LabeledMultiChoice.new("suffix", "labelText", choices, 1)
local multiChoice2 = LabeledMultiChoice.new("suffix", "labelText", choices, 2)

multiChoice:GetFrame().Parent = collapse:GetContentsFrame()
multiChoice2:GetFrame().Parent = collapse:GetContentsFrame()
listFrame:AddChild(collapse:GetSectionFrame()) -- add child to expanding VerticallyScalingListFrame

local collapse = CollapsibleTitledSection.new("suffix", "titleText", true, false, false)
local multiChoice = LabeledMultiChoice.new("suffix", "labelText", choices, 1)
local multiChoice2 = LabeledMultiChoice.new("suffix", "labelText", choices, 2)

multiChoice:GetFrame().Parent = collapse:GetContentsFrame()
multiChoice2:GetFrame().Parent = collapse:GetContentsFrame()
listFrame:AddChild(collapse:GetSectionFrame()) -- add child to expanding VerticallyScalingListFrame

listFrame:AddBottomPadding() -- add padding to VerticallyScalingListFrame

listFrame:GetFrame().Parent = scrollFrame:GetContentFrame() -- scroll content will be the VerticallyScalingListFrame
scrollFrame:GetSectionFrame().Parent = widgetGui -- set the section parent

Bringing the project into studio

The easiest way to bring the project into studio is to use the HttpService to pull the contents directly from this github project into module scripts. After enabling the http service from Game Settings the following code can be run in the command bar.

local http = game:GetService("HttpService")
local req = http:GetAsync("https://api.github.com/repos/Roblox/StudioWidgets/contents/src")
local json = http:JSONDecode(req)

local targetFolder = Instance.new("Folder")
targetFolder.Name = "StudioWidgets"
targetFolder.Parent = game.Workspace

for i = 1, #json do
	local file = json[i]
	if (file.type == "file") then
		local name = file.name:sub(1, #file.name-4)
		local module = targetFolder:FindFirstChild(name) or Instance.new("ModuleScript")
		module.Name = name
		module.Source = http:GetAsync(file.download_url)
		module.Parent = targetFolder
	end
end

License

Available under the Apache 2.0 license. See LICENSE for details.

studiowidgets's People

Contributors

alts-alt avatar dougbankspersonal avatar egomoose avatar fieryevent avatar lucaswolschick avatar lukadev-0 avatar quackbox avatar undermywheel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

studiowidgets's Issues

src/LabeledTextInput.lua Full-width Textbox

With all widgets aiming to replicate the Properties panel, I found the default behavior of LabeledTextInput to be breaking the full-width consistency. I think all developers will benefit from the following change.

Current:

textBoxWrapperFrame.Size = UDim2.new(0, kTextInputWidth, 0.6, 0)
Proposed:
textBoxWrapperFrame.Size = UDim2.new(1,-(GuiUtilities.StandardLineElementLeftMargin+GuiUtilities.StandardLineLabelLeftMargin), 0.6, 0)

Visual result:
image

As this removes the kTextInputWidth variable from the equation, line 10 would become obsolete:

local kTextInputWidth = 100

LabeledMultiChoice.new won't work properly

I'm trying to get something to work so I can copy the the LabeledMultiChoice from the PluginGuiService, unfortunately, I have no luck. If I try a local plugin in my plugins folder, it prints out 16:40:56.495 - forrobloxosthingy.lua:19: attempt to index nil with 'new' and as a installed published plugin prints out 16:55:02.143 - Plugin_4087104785.Script:19: attempt to index nil with 'new'. If that happens, then it is likely that saving a local plugin not in my plugins folder or pcall(function() won't work too. If you want to download the code, click here.

Thanks.
Plus judging by the attempt to index nil with 'new', it might have been removed.

CollapsibleTitledSection Should Collapse / Expand when double-clicked on any position within the TitleBar

The TitledSection element in Studio's Properties panel will collapse / expand when the developer double-clicks any position within the elements title bar. The CollapsibleTitledSection of the StudioWidgets library does not come with this functionality and instead requires developers to accurately click the small arrow icon. Adding this behavior will be a great improvement to the elements usability.

src/labeledcheckbox.lua error when using LabeledMultiChoice.new()

When using the code: LabeledMultiChoice.new("LabeledMultiChoice", "LabeledMultiChoice", {'Labeled', 'Multi', 'Choice'}, 1) I get the error Workspace.SubWidgets.Modules.StudioWidgets.LabeledCheckbox:46: attempt to concatenate local 'nameSuffix' (a nil value). The labelled multi choice does not appear.

Here's the full output:

17:41:23.428 - Workspace.SubWidgets.Modules.StudioWidgets.LabeledCheckbox:46: attempt to concatenate local 'nameSuffix' (a nil value)
17:41:23.428 - Stack Begin
17:41:23.428 - Script 'Workspace.SubWidgets.Modules.StudioWidgets.LabeledCheckbox', Line 46 - field new
17:41:23.429 - Script 'Workspace.SubWidgets.Modules.StudioWidgets.LabeledRadioButton', Line 22 - field new
17:41:23.429 - Script 'Workspace.SubWidgets.Modules.StudioWidgets.LabeledMultiChoice', Line 112 - method _AddRadioButton
17:41:23.430 - Script 'Workspace.SubWidgets.Modules.StudioWidgets.LabeledMultiChoice', Line 102 - method _MakeRadioButtons

LabeledSlider UI is glitchy

The LabeledSlider UI is pretty glitchy when you drag the slider around.

Personally, I fixed this by assigning a Heartbeat connection to the element. It's not a great or elegant solution, but it fixed the issue for me. I assume that the sliderValue.Changed method is firing 1 frame after the change is actually being made, thus causing the issue.

For the sake of example, my solution is shown below. Notice that this also includes a new sliderValue.Changed connection that replaces the current one. Again, not a great solution, but it works for my current project:

	
	local lastVal = nil
	
	local function Recalc()
		if (lastVal == self._value) then return end
		lastVal = self._value
		local scale = (lastVal - 1) / (sliderIntervals - 1)
		self._preThumbImage.Size = UDim2.new(scale, 0, 1, 0)
		self._postThumbImage.Size = UDim2.new(1 - scale, 0, 1, 0)
		self._postThumbImage.Position = UDim2.new(scale, 0, 0, 0)
		self._thumb.Position = UDim2.new(scale, 0, 0.5, 0)
	end
	
	local heartbeat = game:GetService("RunService").Heartbeat:Connect(Recalc)
	frame:GetPropertyChangedSignal("Parent"):Connect(function()
		if (not frame.Parent) then
			heartbeat:Disconnect()
		elseif (not heartbeat.Connected) then
			heartbeat = game:GetService("RunService").Heartbeat:Connect(Recalc)
		end
	end)

	sliderValue.Changed:connect(function()
		self._value = sliderValue.Value
		if self._valueChangedFunction then 
			self._valueChangedFunction(self._value)
		end
	end)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.