496 lines
12 KiB
Luau
Executable File
496 lines
12 KiB
Luau
Executable File
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
local RunService = game:GetService("RunService")
|
|
local AssetService = game:GetService("AssetService")
|
|
|
|
local G = require(ReplicatedStorage:WaitForChild("ImGui"))
|
|
|
|
local BaseEnvironment = getfenv()
|
|
setfenv(1, setmetatable({}, {
|
|
__index = function(_, Key)
|
|
local Value = G[Key]
|
|
if Value ~= nil then
|
|
return Value
|
|
end
|
|
return BaseEnvironment[Key]
|
|
end,
|
|
}))
|
|
|
|
local MainScale = 1.0
|
|
local RenderScale = 1.0
|
|
local RenderRate = 0 -- 0 = every frame (tile redraws are limited by the redraw budget instead)
|
|
local ThumbnailSize = 420
|
|
local FontSize = 13.0
|
|
local FontNames = { "Cousine-Regular", "DroidSans", "Karla-Regular", "ProggyTiny", "Roboto-Medium" }
|
|
|
|
local State = {
|
|
ClearColor = ImVec4(0.45, 0.55, 0.60, 1.00),
|
|
ShowDemoWindow = true,
|
|
ShowAnotherWindow = false,
|
|
Float = 0.0,
|
|
Counter = 0,
|
|
}
|
|
|
|
local Spinner = {
|
|
Show = true,
|
|
Speed = 1.0,
|
|
Angle = 0.0,
|
|
Color = ImVec4(0.80, 0.65, 0.97, 1.0),
|
|
}
|
|
|
|
ImGui.CreateContext()
|
|
|
|
local IO = ImGui.GetIO()
|
|
IO.ConfigFlags = bit32.bor(IO.ConfigFlags, ImGuiConfigFlags.NavEnableKeyboard, ImGuiConfigFlags.DockingEnable)
|
|
|
|
-- catppuccin mocha mauve https://catppuccin.com/palette
|
|
local function StyleColorsCatppuccinMocha(Style)
|
|
local function Hex(Value, Alpha)
|
|
return ImVec4(
|
|
bit32.extract(Value, 16, 8) / 255,
|
|
bit32.extract(Value, 8, 8) / 255,
|
|
bit32.extract(Value, 0, 8) / 255,
|
|
Alpha or 1.0
|
|
)
|
|
end
|
|
|
|
local Crust, Mantle, Base = 0x11111b, 0x181825, 0x1e1e2e
|
|
local Surface0, Surface1, Surface2 = 0x313244, 0x45475a, 0x585b70
|
|
local Overlay0, Overlay2, Text = 0x6c7086, 0x9399b2, 0xcdd6f4
|
|
local Mauve, Lavender = 0xcba6f7, 0xb4befe
|
|
|
|
local Entries = {
|
|
{ "Text", Text },
|
|
{ "TextDisabled", Overlay0 },
|
|
|
|
{ "WindowBg", Base },
|
|
{ "ChildBg", Base, 0.0 },
|
|
{ "PopupBg", Mantle, 0.98 },
|
|
{ "Border", Surface1 },
|
|
{ "BorderShadow", Crust, 0.0 },
|
|
|
|
{ "FrameBg", Surface0 },
|
|
{ "FrameBgHovered", Surface1 },
|
|
{ "FrameBgActive", Surface2 },
|
|
|
|
{ "TitleBg", Mantle },
|
|
{ "TitleBgActive", Crust },
|
|
{ "TitleBgCollapsed", Mantle, 0.75 },
|
|
{ "MenuBarBg", Mantle },
|
|
|
|
{ "ScrollbarBg", Mantle },
|
|
{ "ScrollbarGrab", Surface1 },
|
|
{ "ScrollbarGrabHovered", Surface2 },
|
|
{ "ScrollbarGrabActive", Overlay0 },
|
|
|
|
{ "CheckMark", Mauve },
|
|
{ "SliderGrab", Mauve, 0.8 },
|
|
{ "SliderGrabActive", Mauve },
|
|
|
|
{ "Button", Surface0 },
|
|
{ "ButtonHovered", Surface1 },
|
|
{ "ButtonActive", Mauve, 0.6 },
|
|
|
|
{ "Header", Surface0 },
|
|
{ "HeaderHovered", Surface1 },
|
|
{ "HeaderActive", Mauve, 0.5 },
|
|
|
|
{ "Separator", Surface1 },
|
|
{ "SeparatorHovered", Mauve, 0.7 },
|
|
{ "SeparatorActive", Mauve },
|
|
|
|
{ "ResizeGrip", Mauve, 0.2 },
|
|
{ "ResizeGripHovered", Mauve, 0.6 },
|
|
{ "ResizeGripActive", Mauve, 0.9 },
|
|
|
|
{ "InputTextCursor", Text },
|
|
|
|
{ "Tab", Surface0 },
|
|
{ "TabHovered", Surface2 },
|
|
{ "TabSelected", Surface1 },
|
|
{ "TabSelectedOverline", Mauve },
|
|
{ "TabDimmed", Mantle },
|
|
{ "TabDimmedSelected", Surface0 },
|
|
{ "TabDimmedSelectedOverline", Overlay0 },
|
|
|
|
{ "DockingPreview", Mauve, 0.7 },
|
|
{ "DockingEmptyBg", Crust },
|
|
|
|
{ "PlotLines", Overlay2 },
|
|
{ "PlotLinesHovered", Mauve },
|
|
{ "PlotHistogram", Mauve, 0.8 },
|
|
{ "PlotHistogramHovered", Lavender },
|
|
|
|
{ "TableHeaderBg", Surface0 },
|
|
{ "TableBorderStrong", Surface1 },
|
|
{ "TableBorderLight", Surface0 },
|
|
{ "TableRowBg", Base, 0.0 },
|
|
{ "TableRowBgAlt", Surface0, 0.3 },
|
|
|
|
{ "TextLink", Mauve },
|
|
{ "TextSelectedBg", Mauve, 0.35 },
|
|
{ "TreeLines", Surface2 },
|
|
{ "DragDropTarget", Mauve, 0.9 },
|
|
|
|
{ "NavCursor", Lavender },
|
|
{ "NavWindowingHighlight", Lavender, 0.7 },
|
|
{ "NavWindowingDimBg", Crust, 0.5 },
|
|
{ "ModalWindowDimBg", Crust, 0.6 },
|
|
}
|
|
|
|
for _, Entry in ipairs(Entries) do
|
|
local Name, Color, Alpha = Entry[1], Entry[2], Entry[3]
|
|
Style.Colors[ImGuiCol[Name]] = Hex(Color, Alpha)
|
|
end
|
|
end
|
|
|
|
local Style = ImGui.GetStyle()
|
|
StyleColorsCatppuccinMocha(Style)
|
|
Style:ScaleAllSizes(MainScale)
|
|
Style.FontScaleDpi = MainScale
|
|
|
|
IO.Fonts:AddFontDefault()
|
|
for _, Name in ipairs(FontNames) do
|
|
IO.Fonts:AddFontFromFileTTF("fonts/" .. Name .. ".ttf", FontSize)
|
|
end
|
|
|
|
ImGui_ImplRoblox.Init(nil, RenderScale)
|
|
ImGui_ImplRoblox.SetRenderRate(RenderRate)
|
|
-- ImGui_ImplRoblox.SetBusyRenderRate(30) -- Optional: lower redraw rate while scrolling/resizing/moving windows
|
|
-- ImGui_ImplRoblox.SetProfiling(true) -- MicroProfiler labels; turn off when not profiling
|
|
-- ImGui_ImplRoblox.SetTextProxy(true) -- Optional: hidden TextBox for OS typing (any layout), paste and copy/cut
|
|
|
|
local function ProfileBegin(Name)
|
|
if ImGui_ImplRoblox.IsProfiling() then
|
|
debug.profilebegin(Name)
|
|
end
|
|
end
|
|
|
|
local function ProfileEnd()
|
|
if ImGui_ImplRoblox.IsProfiling() then
|
|
debug.profileend()
|
|
end
|
|
end
|
|
|
|
|
|
local EmbedFlags = {
|
|
None = 0,
|
|
NoClip = 1, -- Don't clip to the window's content area (still moves/hides/stacks with the window)
|
|
NoBackground = 2, -- No FrameBg behind the Frame
|
|
NoBorder = 4, -- No style border
|
|
NoPadding = 8, -- No FramePadding inside the Frame (UIPadding)
|
|
NoRounding = 16, -- No FrameRounding corners (UICorner)
|
|
}
|
|
|
|
G.ImGuiEmbedFlags = EmbedFlags
|
|
|
|
local Embeds = {}
|
|
|
|
local function HasFlag(Flags, Flag)
|
|
return bit32.band(Flags, Flag) ~= 0
|
|
end
|
|
|
|
local function CreateEmbed(Id)
|
|
local Frame = Instance.new("Frame")
|
|
Frame.Name = tostring(Id)
|
|
Frame.BackgroundTransparency = 1
|
|
Frame.BorderSizePixel = 0
|
|
Frame.Visible = false
|
|
|
|
local Padding = Instance.new("UIPadding")
|
|
Padding.Parent = Frame
|
|
|
|
local Corner = Instance.new("UICorner")
|
|
Corner.Parent = Frame
|
|
|
|
return { Frame = Frame, Padding = Padding, Corner = Corner }
|
|
end
|
|
|
|
function ImGui.Embed(Id, Size, Flags, Init)
|
|
Flags = Flags or EmbedFlags.None
|
|
|
|
local Window = ImGui.GetCurrentWindow()
|
|
local GuiId = Window:GetID(Id)
|
|
|
|
local Embed = Embeds[GuiId]
|
|
if not Embed then
|
|
Embed = CreateEmbed(Id)
|
|
Embeds[GuiId] = Embed
|
|
|
|
if Init then
|
|
Init(Embed.Frame)
|
|
end
|
|
end
|
|
|
|
if Window.SkipItems then
|
|
return Embed.Frame
|
|
end
|
|
|
|
local EmbedStyle = ImGui.GetStyle()
|
|
local ItemSize = ImGui.CalcItemSize(
|
|
Size or ImVec2(0, 0),
|
|
ImGui.CalcItemWidth(),
|
|
ImGui.GetFrameHeight() * 4
|
|
)
|
|
local CursorPos = Window.DC.CursorPos
|
|
local BoundingBox = ImRect(CursorPos.x, CursorPos.y, CursorPos.x + ItemSize.x, CursorPos.y + ItemSize.y)
|
|
|
|
ImGui.ItemSize(ItemSize)
|
|
if not ImGui.ItemAdd(BoundingBox, GuiId) then
|
|
return Embed.Frame
|
|
end
|
|
|
|
local Rounding = HasFlag(Flags, EmbedFlags.NoRounding) and 0 or EmbedStyle.FrameRounding
|
|
local ShowBorder = not HasFlag(Flags, EmbedFlags.NoBorder)
|
|
|
|
if not HasFlag(Flags, EmbedFlags.NoBackground) then
|
|
ImGui.RenderFrame(
|
|
BoundingBox.Min,
|
|
BoundingBox.Max,
|
|
ImGui.GetColorU32(ImGuiCol.FrameBg),
|
|
ShowBorder,
|
|
Rounding
|
|
)
|
|
elseif ShowBorder then
|
|
ImGui.RenderFrameBorder(BoundingBox.Min, BoundingBox.Max, Rounding)
|
|
end
|
|
|
|
local Scale = ImGui_ImplRoblox.GetRenderScale()
|
|
local PaddingX, PaddingY = 0, 0
|
|
|
|
if not HasFlag(Flags, EmbedFlags.NoPadding) then
|
|
PaddingX = EmbedStyle.FramePadding.x / Scale
|
|
PaddingY = EmbedStyle.FramePadding.y / Scale
|
|
end
|
|
|
|
if Embed.PaddingX ~= PaddingX or Embed.PaddingY ~= PaddingY then
|
|
Embed.PaddingX, Embed.PaddingY = PaddingX, PaddingY
|
|
|
|
local Padding = Embed.Padding
|
|
Padding.PaddingLeft = UDim.new(0, PaddingX)
|
|
Padding.PaddingRight = UDim.new(0, PaddingX)
|
|
Padding.PaddingTop = UDim.new(0, PaddingY)
|
|
Padding.PaddingBottom = UDim.new(0, PaddingY)
|
|
end
|
|
|
|
if Embed.Rounding ~= Rounding then
|
|
Embed.Rounding = Rounding
|
|
Embed.Corner.CornerRadius = UDim.new(0, Rounding / Scale)
|
|
end
|
|
|
|
ImGui_ImplRoblox.SetOverlay(
|
|
Embed.Frame,
|
|
BoundingBox.Min.x,
|
|
BoundingBox.Min.y,
|
|
ItemSize.x,
|
|
ItemSize.y,
|
|
not HasFlag(Flags, EmbedFlags.NoClip)
|
|
)
|
|
|
|
return Embed.Frame
|
|
end
|
|
|
|
function ImGui.DestroyEmbed(Id)
|
|
local GuiId = ImGui.GetCurrentWindow():GetID(Id)
|
|
local Embed = Embeds[GuiId]
|
|
|
|
if Embed then
|
|
Embed.Frame:Destroy()
|
|
Embeds[GuiId] = nil
|
|
end
|
|
end
|
|
|
|
|
|
local Decal = {
|
|
Show = true,
|
|
Input = ImGui.NewTextBuffer("", 32),
|
|
Status = "Enter a decal or image id",
|
|
Loading = false,
|
|
Texture = nil,
|
|
Width = 0,
|
|
Height = 0,
|
|
Viewer = ExampleImageViewerData(),
|
|
}
|
|
|
|
local function LoadDecal(Id)
|
|
Decal.Loading = true
|
|
Decal.Status = "Loading " .. Id .. "..."
|
|
|
|
task.spawn(function()
|
|
local Uri = string.format("rbxthumb://type=Asset&id=%d&w=%d&h=%d", Id, ThumbnailSize, ThumbnailSize)
|
|
local Success, Result = pcall(AssetService.CreateEditableImageAsync, AssetService, Content.fromUri(Uri))
|
|
|
|
if Success then
|
|
local Size = Result.Size
|
|
local Pixels = Result:ReadPixelsBuffer(Vector2.zero, Size)
|
|
Result:Destroy()
|
|
|
|
if Decal.Texture then
|
|
ImGui_ImplRoblox.DestroyTexture(Decal.Texture)
|
|
end
|
|
|
|
Decal.Texture = ImGui_ImplRoblox.CreateTexture(Pixels, Size.X, Size.Y)
|
|
Decal.Width, Decal.Height = Size.X, Size.Y
|
|
Decal.Viewer.ViewReset = true
|
|
Decal.Status = string.format("Thumbnail %d: %dx%d", Id, Size.X, Size.Y)
|
|
else
|
|
Decal.Status = "Failed to load thumbnail: " .. tostring(Result)
|
|
end
|
|
|
|
Decal.Loading = false
|
|
end)
|
|
end
|
|
|
|
local function ShowDecalViewer()
|
|
if not Decal.Show then
|
|
return
|
|
end
|
|
|
|
Decal.Show = ImGui.Begin("Decal Viewer", Decal.Show)
|
|
|
|
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 12)
|
|
local EnterPressed = ImGui.InputTextWithHint(
|
|
"##id",
|
|
"decal id",
|
|
Decal.Input,
|
|
32,
|
|
ImGuiInputTextFlags.CharsDecimal + ImGuiInputTextFlags.EnterReturnsTrue
|
|
)
|
|
|
|
ImGui.SameLine()
|
|
ImGui.BeginDisabled(Decal.Loading)
|
|
if (ImGui.Button("Load") or EnterPressed) and not Decal.Loading then
|
|
local Id = tonumber(ImGui.TextBufferToString(Decal.Input))
|
|
if Id and Id == math.floor(Id) then
|
|
LoadDecal(Id)
|
|
else
|
|
Decal.Status = "Not a valid id"
|
|
end
|
|
end
|
|
ImGui.EndDisabled()
|
|
|
|
ImGui.SameLine()
|
|
ImGui.TextDisabled(Decal.Status)
|
|
|
|
if Decal.Texture then
|
|
ExampleImageViewer_DrawOptions(Decal.Viewer)
|
|
|
|
local Available = ImGui.GetContentRegionAvail()
|
|
ExampleImageViewer_DrawCanvas(
|
|
Decal.Viewer,
|
|
ImVec2(math.max(Available.x, 50), math.max(Available.y, 50)),
|
|
Decal.Texture,
|
|
Decal.Width,
|
|
Decal.Height
|
|
)
|
|
end
|
|
|
|
ImGui.End()
|
|
end
|
|
|
|
local function ShowHelloWindow()
|
|
ImGui.Begin("Hello, world!")
|
|
|
|
ImGui.Text("This is some useful text.")
|
|
|
|
local _
|
|
_, State.ShowDemoWindow = ImGui.Checkbox("Demo Window", State.ShowDemoWindow)
|
|
_, State.ShowAnotherWindow = ImGui.Checkbox("Another Window", State.ShowAnotherWindow)
|
|
|
|
State.Float = ImGui.SliderFloat("float", State.Float, 0.0, 1.0)
|
|
ImGui.ColorEdit3("clear color", State.ClearColor)
|
|
|
|
if ImGui.Button("Button") then
|
|
State.Counter += 1
|
|
end
|
|
ImGui.SameLine()
|
|
ImGui.Text("counter = %d", State.Counter)
|
|
|
|
ImGui.Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0 / IO.Framerate, IO.Framerate)
|
|
|
|
ImGui.End()
|
|
end
|
|
|
|
local function ShowAnotherWindow()
|
|
if not State.ShowAnotherWindow then
|
|
return
|
|
end
|
|
|
|
State.ShowAnotherWindow = ImGui.Begin("Another Window", State.ShowAnotherWindow)
|
|
|
|
ImGui.Text("Hello from another window!")
|
|
if ImGui.Button("Close Me") then
|
|
State.ShowAnotherWindow = false
|
|
end
|
|
|
|
ImGui.End()
|
|
end
|
|
|
|
local function CreateSpinnerViewport(Container)
|
|
local Viewport = Instance.new("ViewportFrame")
|
|
Viewport.Size = UDim2.fromScale(1, 1)
|
|
Viewport.BackgroundTransparency = 1
|
|
Viewport.Parent = Container
|
|
|
|
local Camera = Instance.new("Camera")
|
|
Camera.CFrame = CFrame.lookAt(Vector3.new(0, 2, 6), Vector3.zero)
|
|
Camera.Parent = Viewport
|
|
Viewport.CurrentCamera = Camera
|
|
|
|
local Part = Instance.new("Part")
|
|
Part.Name = "Spinner"
|
|
Part.Size = Vector3.new(2, 2, 2)
|
|
Part.Parent = Viewport
|
|
end
|
|
|
|
local function ShowViewportWindow()
|
|
if not Spinner.Show then
|
|
return
|
|
end
|
|
|
|
Spinner.Show = ImGui.Begin("Viewport", Spinner.Show)
|
|
|
|
Spinner.Speed = ImGui.SliderFloat("speed", Spinner.Speed, 0.0, 5.0)
|
|
ImGui.ColorEdit3("color", Spinner.Color)
|
|
|
|
local Frame = ImGui.Embed("viewport", ImVec2(-1, 220), EmbedFlags.None, CreateSpinnerViewport)
|
|
|
|
Spinner.Angle += Spinner.Speed * IO.DeltaTime
|
|
|
|
local Part = Frame.ViewportFrame.Spinner
|
|
Part.CFrame = CFrame.Angles(0.4, Spinner.Angle, 0)
|
|
Part.Color = Color3.new(Spinner.Color.x, Spinner.Color.y, Spinner.Color.z)
|
|
|
|
ImGui.End()
|
|
end
|
|
|
|
RunService.Stepped:Connect(function()
|
|
ProfileBegin("ImGui frame")
|
|
ImGui_ImplRoblox.NewFrame()
|
|
|
|
ProfileBegin("ImGui.NewFrame")
|
|
ImGui.NewFrame()
|
|
ProfileEnd()
|
|
|
|
ProfileBegin("ImGui UI")
|
|
ImGui.DockSpaceOverViewport(0, nil, ImGuiDockNodeFlags.PassthruCentralNode)
|
|
|
|
if State.ShowDemoWindow then
|
|
-- For reference: https://github.com/4DBug/imgui-luau/blob/master/imgui_demo.lua
|
|
ImGui.ShowDemoWindow()
|
|
end
|
|
|
|
ShowHelloWindow()
|
|
ShowAnotherWindow()
|
|
ShowDecalViewer()
|
|
ShowViewportWindow()
|
|
ProfileEnd()
|
|
|
|
ProfileBegin("ImGui.Render")
|
|
ImGui.EndFrame()
|
|
ImGui.Render()
|
|
ProfileEnd()
|
|
|
|
ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData())
|
|
ProfileEnd()
|
|
end)
|