diff --git a/default.project.json b/default.project.json
index dca8bb7..b882aa6 100644
--- a/default.project.json
+++ b/default.project.json
@@ -1,60 +1,6 @@
{
"name": "iris",
"tree": {
- "$className": "DataModel",
-
- "StarterPlayer": {
- "StarterPlayerScripts": {
- "Client": {
- "$path": "src/client"
- }
- }
- },
-
- "Workspace": {
- "$properties": {
- "FilteringEnabled": true
- },
- "Baseplate": {
- "$className": "Part",
- "$properties": {
- "Anchored": true,
- "Color": [
- 0.38823,
- 0.37254,
- 0.38823
- ],
- "Locked": true,
- "Position": [
- 0,
- -10,
- 0
- ],
- "Size": [
- 512,
- 20,
- 512
- ]
- }
- }
- },
- "Lighting": {
- "$properties": {
- "Ambient": [
- 0,
- 0,
- 0
- ],
- "Brightness": 2,
- "GlobalShadows": true,
- "Outlines": false,
- "Technology": "Voxel"
- }
- },
- "SoundService": {
- "$properties": {
- "RespectFilteringEnabled": true
- }
- }
+ "$path": "lib"
}
}
\ No newline at end of file
diff --git a/dev.project.json b/dev.project.json
new file mode 100644
index 0000000..7faa8bf
--- /dev/null
+++ b/dev.project.json
@@ -0,0 +1,18 @@
+{
+ "name": "iris",
+ "tree": {
+ "$className": "DataModel",
+
+ "ReplicatedStorage": {
+ "Iris": {
+ "$path": "default.project.json"
+ }
+ },
+
+ "StarterPlayer": {
+ "StarterPlayerScripts": {
+ "$path": "src/client"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/client/Iris/API.lua b/lib/API.lua
similarity index 100%
rename from src/client/Iris/API.lua
rename to lib/API.lua
diff --git a/src/client/Iris/Internal.lua b/lib/Internal.lua
similarity index 100%
rename from src/client/Iris/Internal.lua
rename to lib/Internal.lua
diff --git a/src/client/Iris/Types.lua b/lib/Types.lua
similarity index 100%
rename from src/client/Iris/Types.lua
rename to lib/Types.lua
diff --git a/src/client/Iris/config.lua b/lib/config.lua
similarity index 97%
rename from src/client/Iris/config.lua
rename to lib/config.lua
index a72eeda..52ef839 100644
--- a/src/client/Iris/config.lua
+++ b/lib/config.lua
@@ -1,242 +1,242 @@
-local TemplateConfig = {
- colorDark = { -- Dear, ImGui default dark
- TextColor = Color3.fromRGB(255, 255, 255),
- TextTransparency = 0,
- TextDisabledColor = Color3.fromRGB(128, 128, 128),
- TextDisabledTransparency = 0,
-
- BorderColor = Color3.fromRGB(110, 110, 125),
- -- Dear ImGui uses 110, 110, 125
- -- The Roblox window selection highlight is 67, 191, 254
- BorderActiveColor = Color3.fromRGB(160, 160, 175), -- does not exist in Dear ImGui
-
- BorderTransparency = 0,
- BorderActiveTransparency = 0,
- -- BorderTransparency will be problematic for non UIStroke border implimentations
- -- is not implimented because of this
-
- WindowBgColor = Color3.fromRGB(15, 15, 15),
- WindowBgTransparency = 0.072,
-
- ScrollbarGrabColor = Color3.fromRGB(128, 128, 128),
- ScrollbarGrabTransparency = 0,
-
- TitleBgColor = Color3.fromRGB(10, 10, 10),
- TitleBgTransparency = 0,
- TitleBgActiveColor = Color3.fromRGB(41, 74, 122),
- TitleBgActiveTransparency = 0,
- TitleBgCollapsedColor = Color3.fromRGB(0, 0, 0),
- TitleBgCollapsedTransparency = 0.5,
-
- MenubarBgColor = Color3.fromRGB(36, 36, 36),
- MenubarBgTransparency = 0,
-
- FrameBgColor = Color3.fromRGB(41, 74, 122),
- FrameBgTransparency = 0.46,
- FrameBgHoveredColor = Color3.fromRGB(66, 150, 250),
- FrameBgHoveredTransparency = 0.46,
- FrameBgActiveColor = Color3.fromRGB(66, 150, 250),
- FrameBgActiveTransparency = 0.33,
-
- ButtonColor = Color3.fromRGB(66, 150, 250),
- ButtonTransparency = 0.6,
- ButtonHoveredColor = Color3.fromRGB(66, 150, 250),
- ButtonHoveredTransparency = 0,
- ButtonActiveColor = Color3.fromRGB(15, 135, 250),
- ButtonActiveTransparency = 0,
-
- SliderGrabColor = Color3.fromRGB(66, 150, 250),
- SliderGrabTransparency = 0,
- SliderGrabActiveColor = Color3.fromRGB(117, 138, 204),
- SliderGrabActiveTransparency = 0,
-
- HeaderColor = Color3.fromRGB(66, 150, 250),
- HeaderTransparency = 0.69,
- HeaderHoveredColor = Color3.fromRGB(66, 150, 250),
- HeaderHoveredTransparency = 0.2,
- HeaderActiveColor = Color3.fromRGB(66, 150, 250),
- HeaderActiveTransparency = 0,
-
- SelectionImageObjectColor = Color3.fromRGB(255, 255, 255),
- SelectionImageObjectTransparency = 0.8,
- SelectionImageObjectBorderColor = Color3.fromRGB(255, 255, 255),
- SelectionImageObjectBorderTransparency = 0,
-
- TableBorderStrongColor = Color3.fromRGB(79, 79, 89),
- TableBorderStrongTransparency = 0,
- TableBorderLightColor = Color3.fromRGB(59, 59, 64),
- TableBorderLightTransparency = 0,
- TableRowBgColor = Color3.fromRGB(0, 0, 0),
- TableRowBgTransparency = 1,
- TableRowBgAltColor = Color3.fromRGB(255, 255, 255),
- TableRowBgAltTransparency = 0.94,
-
- NavWindowingHighlightColor = Color3.fromRGB(255, 255, 255),
- NavWindowingHighlightTransparency = 0.3,
- NavWindowingDimBgColor = Color3.fromRGB(204, 204, 204),
- NavWindowingDimBgTransparency = 0.65,
-
- SeparatorColor = Color3.fromRGB(110, 110, 128),
- SeparatorTransparency = 0.5,
-
- CheckMarkColor = Color3.fromRGB(66, 150, 250),
- CheckMarkTransparency = 0,
- },
- colorLight = { -- Dear, ImGui default light
- TextColor = Color3.fromRGB(0, 0, 0),
- TextTransparency = 0,
- TextDisabledColor = Color3.fromRGB(153, 153, 153),
- TextDisabledTransparency = 0,
-
- BorderColor = Color3.fromRGB(64, 64, 64),
- -- Dear ImGui uses 0, 0, 0, 77
- -- The Roblox window selection highlight is 67, 191, 254
- BorderActiveColor = Color3.fromRGB(64, 64, 64), -- does not exist in Dear ImGui
-
- -- BorderTransparency = 0.5,
- -- BorderTransparency will be problematic for non UIStroke border implimentations
- -- will not be implimented because of this
-
- WindowBgColor = Color3.fromRGB(240, 240, 240),
- WindowBgTransparency = 0,
-
- TitleBgColor = Color3.fromRGB(245, 245, 245),
- TitleBgTransparency = 0,
- TitleBgActiveColor = Color3.fromRGB(209, 209, 209),
- TitleBgActiveTransparency = 0,
- TitleBgCollapsedColor = Color3.fromRGB(255, 255, 255),
- TitleBgCollapsedTransparency = 0.5,
-
- MenubarBgColor = Color3.fromRGB(219, 219, 219),
- MenubarBgTransparency = 0,
-
- ScrollbarGrabColor = Color3.fromRGB(96, 96, 96),
- ScrollbarGrabTransparency = 0,
-
- FrameBgColor = Color3.fromRGB(255, 255, 255),
- FrameBgTransparency = 0.6,
- FrameBgHoveredColor = Color3.fromRGB(66, 150, 250),
- FrameBgHoveredTransparency = 0.6,
- FrameBgActiveColor = Color3.fromRGB(66, 150, 250),
- FrameBgActiveTransparency = 0.33,
-
- ButtonColor = Color3.fromRGB(66, 150, 250),
- ButtonTransparency = 0.6,
- ButtonHoveredColor = Color3.fromRGB(66, 150, 250),
- ButtonHoveredTransparency = 0,
- ButtonActiveColor = Color3.fromRGB(15, 135, 250),
- ButtonActiveTransparency = 0,
-
- HeaderColor = Color3.fromRGB(66, 150, 250),
- HeaderTransparency = 0.31,
- HeaderHoveredColor = Color3.fromRGB(66, 150, 250),
- HeaderHoveredTransparency = 0.2,
- HeaderActiveColor = Color3.fromRGB(66, 150, 250),
- HeaderActiveTransparency = 0,
-
- SliderGrabColor = Color3.fromRGB(61, 133, 224),
- SliderGrabTransparency = 0,
- SliderGrabActiveColor = Color3.fromRGB(66, 150, 250),
- SliderGrabActiveTransparency = 0,
-
- SelectionImageObjectColor = Color3.fromRGB(0, 0, 0),
- SelectionImageObjectTransparency = 0.8,
- SelectionImageObjectBorderColor = Color3.fromRGB(0, 0, 0),
- SelectionImageObjectBorderTransparency = 0,
-
- TableBorderStrongColor = Color3.fromRGB(145, 145, 163),
- TableBorderStrongTransparency = 0,
- TableBorderLightColor = Color3.fromRGB(173, 173, 189),
- TableBorderLightTransparency = 0,
- TableRowBgColor = Color3.fromRGB(0, 0, 0),
- TableRowBgTransparency = 1,
- TableRowBgAltColor = Color3.fromRGB(77, 77, 77),
- TableRowBgAltTransparency = 0.91,
-
- NavWindowingHighlightColor = Color3.fromRGB(179, 179, 179),
- NavWindowingHighlightTransparency = 0.3,
- NavWindowingDimBgColor = Color3.fromRGB(51, 51, 51),
- NavWindowingDimBgTransparency = 0.8,
-
- SeparatorColor = Color3.fromRGB(99, 99, 99),
- SeparatorTransparency = 0.38,
-
- CheckMarkColor = Color3.fromRGB(66, 150, 250),
- CheckMarkTransparency = 0,
- },
-
- sizeDefault = { -- Dear, ImGui default
- ItemWidth = UDim.new(1, 0),
- ContentWidth = UDim.new(0.65, 0),
-
- WindowPadding = Vector2.new(8, 8),
- WindowResizePadding = Vector2.new(6, 6),
- FramePadding = Vector2.new(4, 3),
- ItemSpacing = Vector2.new(8, 4),
- ItemInnerSpacing = Vector2.new(4, 4),
- CellPadding = Vector2.new(4, 2),
- DisplaySafeAreaPadding = Vector2.new(0, 0),
- SeparatorTextPadding = Vector2.new(20, 3),
- IndentSpacing = 21,
-
- TextFont = Font.fromEnum(Enum.Font.Code),
- TextSize = 13,
- FrameBorderSize = 0,
- FrameRounding = 0,
- GrabRounding = 0,
- WindowRounding = 0, -- these don't actually work but it's nice to have them.
- WindowBorderSize = 1,
- WindowTitleAlign = Enum.LeftRight.Left,
- PopupBorderSize = 1,
- PopupRounding = 0,
- ScrollbarSize = 7,
- GrabMinSize = 10,
- SeparatorTextBorderSize = 3,
- },
- sizeClear = { -- easier to read and manuveure
- ItemWidth = UDim.new(1, 0),
- ContentWidth = UDim.new(0.65, 0),
-
- WindowPadding = Vector2.new(12, 8),
- WindowResizePadding = Vector2.new(8, 8),
- FramePadding = Vector2.new(6, 4),
- ItemSpacing = Vector2.new(8, 8),
- ItemInnerSpacing = Vector2.new(8, 8),
- CellPadding = Vector2.new(4, 4),
- DisplaySafeAreaPadding = Vector2.new(8, 8),
- SeparatorTextPadding = Vector2.new(24, 6),
- IndentSpacing = 25,
-
- TextFont = Font.fromEnum(Enum.Font.Ubuntu),
- TextSize = 15,
- FrameBorderSize = 1,
- FrameRounding = 4,
- GrabRounding = 4,
- WindowRounding = 4,
- WindowBorderSize = 1,
- WindowTitleAlign = Enum.LeftRight.Center,
- PopupBorderSize = 1,
- PopupRounding = 4,
- ScrollbarSize = 9,
- GrabMinSize = 14,
- SeparatorTextBorderSize = 4,
- },
-
- utilityDefault = {
- UseScreenGUIs = true,
- IgnoreGuiInset = false,
- Parent = nil,
- RichText = false,
- DisableWidget = false,
- DisplayOrderOffset = 127,
- ZIndexOffset = 0,
-
- MouseDoubleClickTime = 0.30, -- Time for a double-click, in seconds.
- MouseDoubleClickMaxDist = 6.0, -- Distance threshold to stay in to validate a double-click, in pixels.
-
- HoverColor = Color3.fromRGB(255, 255, 0),
- HoverTransparency = 0.1,
- },
-}
-
-return TemplateConfig
+local TemplateConfig = {
+ colorDark = { -- Dear, ImGui default dark
+ TextColor = Color3.fromRGB(255, 255, 255),
+ TextTransparency = 0,
+ TextDisabledColor = Color3.fromRGB(128, 128, 128),
+ TextDisabledTransparency = 0,
+
+ BorderColor = Color3.fromRGB(110, 110, 125),
+ -- Dear ImGui uses 110, 110, 125
+ -- The Roblox window selection highlight is 67, 191, 254
+ BorderActiveColor = Color3.fromRGB(160, 160, 175), -- does not exist in Dear ImGui
+
+ BorderTransparency = 0,
+ BorderActiveTransparency = 0,
+ -- BorderTransparency will be problematic for non UIStroke border implimentations
+ -- is not implimented because of this
+
+ WindowBgColor = Color3.fromRGB(15, 15, 15),
+ WindowBgTransparency = 0.072,
+
+ ScrollbarGrabColor = Color3.fromRGB(128, 128, 128),
+ ScrollbarGrabTransparency = 0,
+
+ TitleBgColor = Color3.fromRGB(10, 10, 10),
+ TitleBgTransparency = 0,
+ TitleBgActiveColor = Color3.fromRGB(41, 74, 122),
+ TitleBgActiveTransparency = 0,
+ TitleBgCollapsedColor = Color3.fromRGB(0, 0, 0),
+ TitleBgCollapsedTransparency = 0.5,
+
+ MenubarBgColor = Color3.fromRGB(36, 36, 36),
+ MenubarBgTransparency = 0,
+
+ FrameBgColor = Color3.fromRGB(41, 74, 122),
+ FrameBgTransparency = 0.46,
+ FrameBgHoveredColor = Color3.fromRGB(66, 150, 250),
+ FrameBgHoveredTransparency = 0.46,
+ FrameBgActiveColor = Color3.fromRGB(66, 150, 250),
+ FrameBgActiveTransparency = 0.33,
+
+ ButtonColor = Color3.fromRGB(66, 150, 250),
+ ButtonTransparency = 0.6,
+ ButtonHoveredColor = Color3.fromRGB(66, 150, 250),
+ ButtonHoveredTransparency = 0,
+ ButtonActiveColor = Color3.fromRGB(15, 135, 250),
+ ButtonActiveTransparency = 0,
+
+ SliderGrabColor = Color3.fromRGB(66, 150, 250),
+ SliderGrabTransparency = 0,
+ SliderGrabActiveColor = Color3.fromRGB(117, 138, 204),
+ SliderGrabActiveTransparency = 0,
+
+ HeaderColor = Color3.fromRGB(66, 150, 250),
+ HeaderTransparency = 0.69,
+ HeaderHoveredColor = Color3.fromRGB(66, 150, 250),
+ HeaderHoveredTransparency = 0.2,
+ HeaderActiveColor = Color3.fromRGB(66, 150, 250),
+ HeaderActiveTransparency = 0,
+
+ SelectionImageObjectColor = Color3.fromRGB(255, 255, 255),
+ SelectionImageObjectTransparency = 0.8,
+ SelectionImageObjectBorderColor = Color3.fromRGB(255, 255, 255),
+ SelectionImageObjectBorderTransparency = 0,
+
+ TableBorderStrongColor = Color3.fromRGB(79, 79, 89),
+ TableBorderStrongTransparency = 0,
+ TableBorderLightColor = Color3.fromRGB(59, 59, 64),
+ TableBorderLightTransparency = 0,
+ TableRowBgColor = Color3.fromRGB(0, 0, 0),
+ TableRowBgTransparency = 1,
+ TableRowBgAltColor = Color3.fromRGB(255, 255, 255),
+ TableRowBgAltTransparency = 0.94,
+
+ NavWindowingHighlightColor = Color3.fromRGB(255, 255, 255),
+ NavWindowingHighlightTransparency = 0.3,
+ NavWindowingDimBgColor = Color3.fromRGB(204, 204, 204),
+ NavWindowingDimBgTransparency = 0.65,
+
+ SeparatorColor = Color3.fromRGB(110, 110, 128),
+ SeparatorTransparency = 0.5,
+
+ CheckMarkColor = Color3.fromRGB(66, 150, 250),
+ CheckMarkTransparency = 0,
+ },
+ colorLight = { -- Dear, ImGui default light
+ TextColor = Color3.fromRGB(0, 0, 0),
+ TextTransparency = 0,
+ TextDisabledColor = Color3.fromRGB(153, 153, 153),
+ TextDisabledTransparency = 0,
+
+ BorderColor = Color3.fromRGB(64, 64, 64),
+ -- Dear ImGui uses 0, 0, 0, 77
+ -- The Roblox window selection highlight is 67, 191, 254
+ BorderActiveColor = Color3.fromRGB(64, 64, 64), -- does not exist in Dear ImGui
+
+ -- BorderTransparency = 0.5,
+ -- BorderTransparency will be problematic for non UIStroke border implimentations
+ -- will not be implimented because of this
+
+ WindowBgColor = Color3.fromRGB(240, 240, 240),
+ WindowBgTransparency = 0,
+
+ TitleBgColor = Color3.fromRGB(245, 245, 245),
+ TitleBgTransparency = 0,
+ TitleBgActiveColor = Color3.fromRGB(209, 209, 209),
+ TitleBgActiveTransparency = 0,
+ TitleBgCollapsedColor = Color3.fromRGB(255, 255, 255),
+ TitleBgCollapsedTransparency = 0.5,
+
+ MenubarBgColor = Color3.fromRGB(219, 219, 219),
+ MenubarBgTransparency = 0,
+
+ ScrollbarGrabColor = Color3.fromRGB(96, 96, 96),
+ ScrollbarGrabTransparency = 0,
+
+ FrameBgColor = Color3.fromRGB(255, 255, 255),
+ FrameBgTransparency = 0.6,
+ FrameBgHoveredColor = Color3.fromRGB(66, 150, 250),
+ FrameBgHoveredTransparency = 0.6,
+ FrameBgActiveColor = Color3.fromRGB(66, 150, 250),
+ FrameBgActiveTransparency = 0.33,
+
+ ButtonColor = Color3.fromRGB(66, 150, 250),
+ ButtonTransparency = 0.6,
+ ButtonHoveredColor = Color3.fromRGB(66, 150, 250),
+ ButtonHoveredTransparency = 0,
+ ButtonActiveColor = Color3.fromRGB(15, 135, 250),
+ ButtonActiveTransparency = 0,
+
+ HeaderColor = Color3.fromRGB(66, 150, 250),
+ HeaderTransparency = 0.31,
+ HeaderHoveredColor = Color3.fromRGB(66, 150, 250),
+ HeaderHoveredTransparency = 0.2,
+ HeaderActiveColor = Color3.fromRGB(66, 150, 250),
+ HeaderActiveTransparency = 0,
+
+ SliderGrabColor = Color3.fromRGB(61, 133, 224),
+ SliderGrabTransparency = 0,
+ SliderGrabActiveColor = Color3.fromRGB(66, 150, 250),
+ SliderGrabActiveTransparency = 0,
+
+ SelectionImageObjectColor = Color3.fromRGB(0, 0, 0),
+ SelectionImageObjectTransparency = 0.8,
+ SelectionImageObjectBorderColor = Color3.fromRGB(0, 0, 0),
+ SelectionImageObjectBorderTransparency = 0,
+
+ TableBorderStrongColor = Color3.fromRGB(145, 145, 163),
+ TableBorderStrongTransparency = 0,
+ TableBorderLightColor = Color3.fromRGB(173, 173, 189),
+ TableBorderLightTransparency = 0,
+ TableRowBgColor = Color3.fromRGB(0, 0, 0),
+ TableRowBgTransparency = 1,
+ TableRowBgAltColor = Color3.fromRGB(77, 77, 77),
+ TableRowBgAltTransparency = 0.91,
+
+ NavWindowingHighlightColor = Color3.fromRGB(179, 179, 179),
+ NavWindowingHighlightTransparency = 0.3,
+ NavWindowingDimBgColor = Color3.fromRGB(51, 51, 51),
+ NavWindowingDimBgTransparency = 0.8,
+
+ SeparatorColor = Color3.fromRGB(99, 99, 99),
+ SeparatorTransparency = 0.38,
+
+ CheckMarkColor = Color3.fromRGB(66, 150, 250),
+ CheckMarkTransparency = 0,
+ },
+
+ sizeDefault = { -- Dear, ImGui default
+ ItemWidth = UDim.new(1, 0),
+ ContentWidth = UDim.new(0.65, 0),
+
+ WindowPadding = Vector2.new(8, 8),
+ WindowResizePadding = Vector2.new(6, 6),
+ FramePadding = Vector2.new(4, 3),
+ ItemSpacing = Vector2.new(8, 4),
+ ItemInnerSpacing = Vector2.new(4, 4),
+ CellPadding = Vector2.new(4, 2),
+ DisplaySafeAreaPadding = Vector2.new(0, 0),
+ SeparatorTextPadding = Vector2.new(20, 3),
+ IndentSpacing = 21,
+
+ TextFont = Font.fromEnum(Enum.Font.Code),
+ TextSize = 13,
+ FrameBorderSize = 0,
+ FrameRounding = 0,
+ GrabRounding = 0,
+ WindowRounding = 0, -- these don't actually work but it's nice to have them.
+ WindowBorderSize = 1,
+ WindowTitleAlign = Enum.LeftRight.Left,
+ PopupBorderSize = 1,
+ PopupRounding = 0,
+ ScrollbarSize = 7,
+ GrabMinSize = 10,
+ SeparatorTextBorderSize = 3,
+ },
+ sizeClear = { -- easier to read and manuveure
+ ItemWidth = UDim.new(1, 0),
+ ContentWidth = UDim.new(0.65, 0),
+
+ WindowPadding = Vector2.new(12, 8),
+ WindowResizePadding = Vector2.new(8, 8),
+ FramePadding = Vector2.new(6, 4),
+ ItemSpacing = Vector2.new(8, 8),
+ ItemInnerSpacing = Vector2.new(8, 8),
+ CellPadding = Vector2.new(4, 4),
+ DisplaySafeAreaPadding = Vector2.new(8, 8),
+ SeparatorTextPadding = Vector2.new(24, 6),
+ IndentSpacing = 25,
+
+ TextFont = Font.fromEnum(Enum.Font.Ubuntu),
+ TextSize = 15,
+ FrameBorderSize = 1,
+ FrameRounding = 4,
+ GrabRounding = 4,
+ WindowRounding = 4,
+ WindowBorderSize = 1,
+ WindowTitleAlign = Enum.LeftRight.Center,
+ PopupBorderSize = 1,
+ PopupRounding = 4,
+ ScrollbarSize = 9,
+ GrabMinSize = 14,
+ SeparatorTextBorderSize = 4,
+ },
+
+ utilityDefault = {
+ UseScreenGUIs = true,
+ IgnoreGuiInset = false,
+ Parent = nil,
+ RichText = false,
+ DisableWidget = false,
+ DisplayOrderOffset = 127,
+ ZIndexOffset = 0,
+
+ MouseDoubleClickTime = 0.30, -- Time for a double-click, in seconds.
+ MouseDoubleClickMaxDist = 6.0, -- Distance threshold to stay in to validate a double-click, in pixels.
+
+ HoverColor = Color3.fromRGB(255, 255, 0),
+ HoverTransparency = 0.1,
+ },
+}
+
+return TemplateConfig
diff --git a/src/client/Iris/demoWindow.lua b/lib/demoWindow.lua
similarity index 97%
rename from src/client/Iris/demoWindow.lua
rename to lib/demoWindow.lua
index 89653ad..80b3815 100644
--- a/src/client/Iris/demoWindow.lua
+++ b/lib/demoWindow.lua
@@ -1,1048 +1,1048 @@
-local Types = require(script.Parent.Types)
-
-return function(Iris: Types.Iris)
- local showMainWindow = Iris.State(true)
- local showRecursiveWindow = Iris.State(false)
- local showRuntimeInfo = Iris.State(false)
- local showStyleEditor = Iris.State(false)
- local showWindowlessDemo = Iris.State(false)
- local showMainMenuBarWindow = Iris.State(false)
-
- -- stylua: ignore start
- local function helpMarker(helpText)
- Iris.PushConfig({ TextColor = Iris._config.TextDisabledColor })
- local text = Iris.Text({ "(?)" })
- Iris.PopConfig()
-
- Iris.PushConfig({ ContentWidth = UDim.new(0, 350) })
- if text.hovered() then
- Iris.Tooltip({ helpText })
- end
- Iris.PopConfig()
- end
-
- -- shows each widgets functionality
- local widgetDemos = {
- Basic = function()
- Iris.Tree({ "Basic" })
- Iris.SeparatorText({ "Basic" })
-
- local radioButtonState = Iris.State(1)
- Iris.Button({ "Button" })
- Iris.SmallButton({ "SmallButton" })
- Iris.Text({ "Text" })
- Iris.TextWrapped({ string.rep("Text Wrapped ", 5) })
- Iris.TextColored({ "Colored Text", Color3.fromRGB(255, 128, 0) })
- Iris.Text({ `Rich Text: bold text italic text underline text strikethrough text red text bigger text`, true, nil, true })
- Iris.SameLine()
- Iris.RadioButton({ "Index '1'", 1 }, { index = radioButtonState })
- Iris.RadioButton({ "Index 'two'", "two" }, { index = radioButtonState })
- if Iris.RadioButton({ "Index 'false'", false }, { index = radioButtonState }).active() == false then
- if Iris.SmallButton({ "Select last" }).clicked() then
- radioButtonState:set(false)
- end
- end
- Iris.End()
- Iris.Text({ "The Index is: " .. tostring(radioButtonState.value) })
-
- Iris.SeparatorText({ "Inputs" })
-
- Iris.InputNum({})
- Iris.DragNum({})
- Iris.SliderNum({})
-
- Iris.End()
- end,
-
- Tree = function()
- Iris.Tree({ "Trees" })
- Iris.Tree({ "Tree using SpanAvailWidth", [Iris.Args.Tree.SpanAvailWidth] = true })
- helpMarker("SpanAvailWidth determines if the Tree is selectable from its entire with, or only the text area")
- Iris.End()
-
- local tree1 = Iris.Tree({ "Tree with Children" })
- Iris.Text({ "Im inside the first tree!" })
- Iris.Button({ "Im a button inside the first tree!" })
- Iris.Tree({ "Im a tree inside the first tree!" })
- Iris.Text({ "I am the innermost text!" })
- Iris.End()
- Iris.End()
-
- Iris.Checkbox({ "Toggle above tree" }, { isChecked = tree1.state.isUncollapsed })
-
- Iris.End()
- end,
-
- CollapsingHeader = function()
- Iris.Tree({ "Collapsing Headers" })
- Iris.CollapsingHeader({ "A header" })
- Iris.Text({ "This is under the first header!" })
- Iris.End()
-
- local secondHeader = Iris.State(true)
- Iris.CollapsingHeader({ "Another header" }, { isUncollapsed = secondHeader })
- if Iris.Button({ "Shhh... secret button!" }).clicked() then
- secondHeader:set(true)
- end
- Iris.End()
- Iris.End()
- end,
-
- Group = function()
- Iris.Tree({ "Groups" })
- Iris.SameLine()
- Iris.Group()
- Iris.Text({ "I am in group A" })
- Iris.Button({ "Im also in A" })
- Iris.End()
-
- Iris.Separator()
-
- Iris.Group()
- Iris.Text({ "I am in group B" })
- Iris.Button({ "Im also in B" })
- Iris.Button({ "Also group B" })
- Iris.End()
- Iris.End()
- Iris.End()
- end,
-
- Indent = function()
- Iris.Tree({ "Indents" })
- Iris.Text({ "Not Indented" })
- Iris.Indent()
- Iris.Text({ "Indented" })
- Iris.Indent({ 7 })
- Iris.Text({ "Indented by 7 more pixels" })
- Iris.End()
-
- Iris.Indent({ -7 })
- Iris.Text({ "Indented by 7 less pixels" })
- Iris.End()
- Iris.End()
- Iris.End()
- end,
-
- Input = function()
- Iris.Tree({ "Input" })
- local NoField, NoButtons, Min, Max, Increment, Format = Iris.State(false), Iris.State(false), Iris.State(0), Iris.State(100), Iris.State(1), Iris.State("%d")
-
- Iris.PushConfig({ ContentWidth = UDim.new(1, -120) })
- local InputNum = Iris.InputNum({
- "Input Number",
- -- [Iris.Args.InputNum.NoField] = NoField.value,
- [Iris.Args.InputNum.NoButtons] = NoButtons.value,
- [Iris.Args.InputNum.Min] = Min.value,
- [Iris.Args.InputNum.Max] = Max.value,
- [Iris.Args.InputNum.Increment] = Increment.value,
- [Iris.Args.InputNum.Format] = { Format.value },
- })
- Iris.PopConfig()
- Iris.Text({ "The Value is: " .. InputNum.number.value })
- if Iris.Button({ "Randomize Number" }).clicked() then
- InputNum.number:set(math.random(1, 99))
- end
- local NoFieldCheckbox = Iris.Checkbox({ "NoField" }, { isChecked = NoField })
- local NoButtonsCheckbox = Iris.Checkbox({ "NoButtons" }, { isChecked = NoButtons })
- if NoFieldCheckbox.checked() and NoButtonsCheckbox.isChecked.value == true then
- NoButtonsCheckbox.isChecked:set(false)
- end
- if NoButtonsCheckbox.checked() and NoFieldCheckbox.isChecked.value == true then
- NoFieldCheckbox.isChecked:set(false)
- end
-
- Iris.PushConfig({ ContentWidth = UDim.new(1, -120) })
- Iris.InputVector2({ "InputVector2" })
- Iris.InputVector3({ "InputVector3" })
- Iris.InputUDim({ "InputUDim" })
- Iris.InputUDim2({ "InputUDim2" })
- local UseFloats = Iris.State(false)
- local UseHSV = Iris.State(false)
- local sharedColor = Iris.State(Color3.new())
- local transparency = Iris.State(0)
- Iris.SliderNum({ "Transparency", 0.01, 0, 1 }, { number = transparency })
- Iris.InputColor3({ "InputColor3", UseFloats:get(), UseHSV:get() }, { color = sharedColor })
- Iris.InputColor4({ "InputColor4", UseFloats:get(), UseHSV:get() }, { color = sharedColor, transparency = transparency })
- Iris.SameLine()
- Iris.Text({ sharedColor:get():ToHex() })
- Iris.Checkbox({ "Use Floats" }, { isChecked = UseFloats })
- Iris.Checkbox({ "Use HSV" }, { isChecked = UseHSV })
- Iris.End()
-
- Iris.PopConfig()
-
- Iris.Separator()
-
- Iris.SameLine()
- Iris.Text({ "Slider Numbers" })
- helpMarker("ctrl + click slider number widgets to input a number")
- Iris.End()
- Iris.PushConfig({ ContentWidth = UDim.new(1, -120) })
- Iris.SliderNum({ "Slide Int", 1, 1, 8 })
- Iris.SliderNum({ "Slide Float", 0.01, 0, 100 })
- Iris.SliderNum({ "Small Numbers", 0.001, -2, 1, "%f radians" })
- Iris.SliderNum({ "Odd Ranges", 0.001, -math.pi, math.pi, "%f radians" })
- Iris.SliderNum({ "Big Numbers", 1e4, 1e5, 1e7 })
- Iris.SliderNum({ "Few Numbers", 1, 0, 3 })
- Iris.PopConfig()
-
- Iris.Separator()
-
- Iris.SameLine()
- Iris.Text({ "Drag Numbers" })
- helpMarker("ctrl + click or double click drag number widgets to input a number, hold shift/alt while dragging to increase/decrease speed")
- Iris.End()
- Iris.PushConfig({ ContentWidth = UDim.new(1, -120) })
- Iris.DragNum({ "Drag Int" })
- Iris.DragNum({ "Slide Float", 0.001, -10, 10 })
- Iris.DragNum({ "Percentage", 1, 0, 100, "%d %%" })
- Iris.PopConfig()
- Iris.End()
- end,
-
- InputText = function()
- Iris.Tree({ "Input Text" })
- Iris.PushConfig({ ContentWidth = UDim.new(0, 250) })
- local InputText = Iris.InputText({ "Input Text Test", [Iris.Args.InputText.TextHint] = "Input Text here" })
- Iris.PopConfig()
- Iris.Text({ "The text is: " .. InputText.text.value })
- Iris.End()
- end,
-
- MultiInput = function()
- Iris.Tree({"Multi-Component Input"})
-
- local sharedVector2 = Iris.State(Vector2.new())
- local sharedVector3 = Iris.State(Vector3.new())
- local sharedUDim = Iris.State(UDim.new())
- local sharedUDim2 = Iris.State(UDim2.new())
- local sharedColor3 = Iris.State(Color3.new())
- local SharedRect = Iris.State(Rect.new(0, 0, 0, 0))
-
- Iris.SeparatorText({"Input"})
-
- Iris.InputVector2({}, {number = sharedVector2})
- Iris.InputVector3({}, {number = sharedVector3})
- Iris.InputUDim({}, {number = sharedUDim})
- Iris.InputUDim2({}, {number = sharedUDim2})
- Iris.InputRect({}, {number = SharedRect})
-
- Iris.SeparatorText({"Drag"})
-
- Iris.DragVector2({}, {number = sharedVector2})
- Iris.DragVector3({}, {number = sharedVector3})
- Iris.DragUDim({}, {number = sharedUDim})
- Iris.DragUDim2({}, {number = sharedUDim2})
- Iris.DragRect({}, {number = SharedRect})
-
- Iris.SeparatorText({"Slider"})
-
- Iris.SliderVector2({}, {number = sharedVector2})
- Iris.SliderVector3({}, {number = sharedVector3})
- Iris.SliderUDim({}, {number = sharedUDim})
- Iris.SliderUDim2({}, {number = sharedUDim2})
- Iris.SliderRect({}, {number = SharedRect})
-
- Iris.SeparatorText({"Color"})
-
- Iris.InputColor3({}, {color = sharedColor3})
- Iris.InputColor4({}, {color = sharedColor3})
-
- Iris.End()
- end,
-
- Tooltip = function()
- Iris.PushConfig({ ContentWidth = UDim.new(0, 250) })
- Iris.Tree({ "Tooltip" })
- if Iris.Text({ "Hover over me to reveal a tooltip" }).hovered() then
- Iris.Tooltip({ "I am some helpful tooltip text" })
- end
- local dynamicText = Iris.State("Hello ")
- local numRepeat = Iris.State(1)
- if Iris.InputNum({ "# of repeat", 1, 1, 50 }, { number = numRepeat }).numberChanged() then
- dynamicText:set(string.rep("Hello ", numRepeat:get()))
- end
- if Iris.Checkbox({ "Show dynamic text tooltip" }).isChecked.value then
- Iris.Tooltip({ dynamicText:get() })
- end
- Iris.End()
- Iris.PopConfig()
- end,
-
- Selectable = function()
- Iris.Tree({ "Selectable" })
- local sharedIndex = Iris.State(2)
- Iris.Selectable({ "Selectable #1", 1 }, { index = sharedIndex })
- Iris.Selectable({ "Selectable #2", 2 }, { index = sharedIndex })
- if Iris.Selectable({ "Double click Selectable", 3, true }, { index = sharedIndex }).doubleClicked() then
- sharedIndex:set(3)
- end
- Iris.Selectable({ "Impossible to select", 4, true }, { index = sharedIndex })
- if Iris.Button({ "Select last" }).clicked() then
- sharedIndex:set(4)
- end
- Iris.Selectable({ "Independent Selectable" })
- Iris.End()
- end,
-
- Combo = function()
- Iris.Tree({ "Combo" })
- Iris.PushConfig({ ContentWidth = UDim.new(1, -120) })
- local sharedComboIndex = Iris.State("No Selection")
- Iris.SameLine()
- local NoPreview = Iris.Checkbox({ "No Preview" })
- local NoButton = Iris.Checkbox({ "No Button" })
- if NoPreview.checked() and NoButton.isChecked.value == true then
- NoButton.isChecked:set(false)
- end
- if NoButton.checked() and NoPreview.isChecked.value == true then
- NoPreview.isChecked:set(false)
- end
- Iris.End()
- Iris.Combo({ "Basic Usage", NoButton.isChecked:get(), NoPreview.isChecked:get() }, { index = sharedComboIndex })
- Iris.Selectable({ "Select 1", "One" }, { index = sharedComboIndex })
- Iris.Selectable({ "Select 2", "Two" }, { index = sharedComboIndex })
- Iris.Selectable({ "Select 3", "Three" }, { index = sharedComboIndex })
- Iris.End()
-
- Iris.ComboArray({ "Using ComboArray" }, { index = "No Selection" }, { "Red", "Green", "Blue" })
-
- local sharedComboIndex2 = Iris.State("7 AM")
- Iris.Combo({ "Combo with Inner widgets" }, { index = sharedComboIndex2 })
- Iris.Tree({ "Morning Shifts" })
- Iris.Selectable({ "Shift at 7 AM", "7 AM" }, { index = sharedComboIndex2 })
- Iris.Selectable({ "Shift at 11 AM", "11 AM" }, { index = sharedComboIndex2 })
- Iris.Selectable({ "Shist at 3 PM", "3 PM" }, { index = sharedComboIndex2 })
- Iris.End()
- Iris.Tree({ "Night Shifts" })
- Iris.Selectable({ "Shift at 6 PM", "6 PM" }, { index = sharedComboIndex2 })
- Iris.Selectable({ "Shift at 9 PM", "9 PM" }, { index = sharedComboIndex2 })
- Iris.End()
- Iris.End()
-
- local ComboEnum = Iris.ComboEnum({ "Using ComboEnum" }, { index = Enum.UserInputState.Begin }, Enum.UserInputState)
- Iris.Text({ "Selected: " .. ComboEnum.index:get().Name })
- Iris.PopConfig()
- Iris.End()
- end,
- }
- local widgetDemosOrder = { "Basic", "Tree", "CollapsingHeader", "Group", "Indent", "Input", "MultiInput", "InputText", "Tooltip", "Selectable", "Combo" }
-
- local function recursiveTree()
- local theTree = Iris.Tree({ "Recursive Tree" })
- if theTree.state.isUncollapsed.value then
- recursiveTree()
- end
- Iris.End()
- end
-
- local function recursiveWindow(parentCheckboxState)
- Iris.Window({ "Recursive Window" }, { size = Iris.State(Vector2.new(175, 100)), isOpened = parentCheckboxState })
- local theCheckbox = Iris.Checkbox({ "Recurse Again" })
- Iris.End()
- if theCheckbox.isChecked.value then
- recursiveWindow(theCheckbox.isChecked)
- end
- end
-
- -- shows list of runtime widgets and states, including IDs. shows other info about runtime and can show widgets/state info in depth.
- local function runtimeInfo()
- local runtimeInfoWindow = Iris.Window({ "Runtime Info" }, { isOpened = showRuntimeInfo })
- local lastVDOM = Iris.Internal._lastVDOM
- local states = Iris.Internal._states
-
- local numSecondsDisabled = Iris.State(3)
- local rollingDT = Iris.State(0)
- local lastT = Iris.State(os.clock())
-
- Iris.SameLine()
- Iris.InputNum({ "", [Iris.Args.InputNum.Format] = "%d Seconds", [Iris.Args.InputNum.Max] = 10 }, { number = numSecondsDisabled })
- if Iris.Button({ "Disable" }).clicked() then
- Iris.Disabled = true
- task.delay(numSecondsDisabled:get(), function()
- Iris.Disabled = false
- end)
- end
- Iris.End()
-
- local t = os.clock()
- local dt = t - lastT.value
- rollingDT.value += (dt - rollingDT.value) * 0.2
- lastT.value = t
- Iris.Text({ string.format("Average %.3f ms/frame (%.1f FPS)", rollingDT.value * 1000, 1 / rollingDT.value) })
-
- Iris.Text({
- string.format("Window Position: (%d, %d), Window Size: (%d, %d)", runtimeInfoWindow.position.value.X, runtimeInfoWindow.position.value.Y, runtimeInfoWindow.size.value.X, runtimeInfoWindow.size.value.Y),
- })
-
- Iris.SameLine()
- Iris.Text({ "Enter an ID to learn more about it." })
- helpMarker("every widget and state has an ID which Iris tracks to remember which widget is which. below lists all widgets and states, with their respective IDs")
- Iris.End()
-
- Iris.PushConfig({ ItemWidth = UDim.new(1, -150) })
- local enteredText = Iris.InputText({ "ID field" }, { text = Iris.State(runtimeInfoWindow.ID) }).text.value
- Iris.PopConfig()
-
- Iris.Indent()
- local enteredWidget = lastVDOM[enteredText]
- local enteredState = states[enteredText]
- if enteredWidget then
- Iris.Table({ 1, [Iris.Args.Table.RowBg] = false })
- Iris.Text({ string.format('The ID, "%s", is a widget', enteredText) })
- Iris.NextRow()
-
- Iris.Text({ string.format("Widget is type: %s", enteredWidget.type) })
- Iris.NextRow()
-
- Iris.Tree({ "Widget has Args:" }, { isUncollapsed = Iris.State(true) })
- for i, v in enteredWidget.arguments do
- Iris.Text({ i .. " - " .. tostring(v) })
- end
- Iris.End()
- Iris.NextRow()
-
- if enteredWidget.state then
- Iris.Tree({ "Widget has State:" }, { isUncollapsed = Iris.State(true) })
- for i, v in enteredWidget.state do
- Iris.Text({ i .. " - " .. tostring(v.value) })
- end
- Iris.End()
- end
- Iris.End()
- elseif enteredState then
- Iris.Table({ 1, [Iris.Args.Table.RowBg] = false })
- Iris.Text({ string.format('The ID, "%s", is a state', enteredText) })
- Iris.NextRow()
-
- Iris.Text({ string.format("Value is type: %s, Value = %s", typeof(enteredState.value), tostring(enteredState.value)) })
- Iris.NextRow()
-
- Iris.Tree({ "state has connected widgets:" }, { isUncollapsed = Iris.State(true) })
- for i, v in enteredState.ConnectedWidgets do
- Iris.Text({ i .. " - " .. v.type })
- end
- Iris.End()
- Iris.NextRow()
-
- Iris.Text({ string.format("state has: %d connected functions", #enteredState.ConnectedFunctions) })
- Iris.End()
- else
- Iris.Text({ string.format('The ID, "%s", is not a state or widget', enteredText) })
- end
- Iris.End()
-
- if Iris.Tree({ "Widgets" }).isUncollapsed.value then
- local widgetCount = 0
- local widgetStr = ""
- for _, v in lastVDOM do
- widgetCount += 1
- widgetStr ..= "\n" .. v.ID .. " - " .. v.type
- end
-
- Iris.Text({ "Number of Widgets: " .. widgetCount })
-
- Iris.Text({ widgetStr })
- end
- Iris.End()
- if Iris.Tree({ "States" }).isUncollapsed.value then
- local stateCount = 0
- local stateStr = ""
- for i, v in states do
- stateCount += 1
- stateStr ..= "\n" .. i .. " - " .. tostring(v.value)
- end
-
- Iris.Text({ "Number of States: " .. stateCount })
-
- Iris.Text({ stateStr })
- end
- Iris.End()
- Iris.End()
- end
-
- local function recursiveMenu()
- -- stylua: ignore start
- if Iris.Menu({ "Recursive" }).state.isOpened.value then
- Iris.MenuItem({ "New", Enum.KeyCode.N, Enum.ModifierKey.Ctrl })
- Iris.MenuItem({ "Open", Enum.KeyCode.O, Enum.ModifierKey.Ctrl })
- Iris.MenuItem({ "Save", Enum.KeyCode.S, Enum.ModifierKey.Ctrl })
- Iris.Separator()
- Iris.MenuToggle({ "Autosave" })
- Iris.MenuToggle({ "Checked" })
- Iris.Separator()
- Iris.Menu({ "Options" })
- Iris.MenuItem({ "Red" })
- Iris.MenuItem({ "Yellow" })
- Iris.MenuItem({ "Green" })
- Iris.MenuItem({ "Blue" })
- Iris.Separator()
- recursiveMenu()
- Iris.End()
- end
- Iris.End()
- -- stylua: ignore end
-
- end
-
- local function mainMenuBar()
- Iris.MenuBar()
- Iris.Menu({ "File" })
- Iris.MenuItem({ "New", Enum.KeyCode.N, Enum.ModifierKey.Ctrl })
- Iris.MenuItem({ "Open", Enum.KeyCode.O, Enum.ModifierKey.Ctrl })
- Iris.MenuItem({ "Save", Enum.KeyCode.S, Enum.ModifierKey.Ctrl })
- recursiveMenu()
- Iris.MenuItem({ "Quit", Enum.KeyCode.Q, Enum.ModifierKey.Alt })
- Iris.End()
-
- Iris.Menu({ "Examples" })
- Iris.MenuToggle({ "Recursive Window" }, { isChecked = showRecursiveWindow })
- Iris.MenuToggle({ "Windowless" }, { isChecked = showWindowlessDemo })
- Iris.MenuToggle({ "Main Menu Bar" }, { isChecked = showMainMenuBarWindow })
- Iris.End()
-
- Iris.Menu({ "Tools" })
- Iris.MenuToggle({ "Runtime Info" }, { isChecked = showRuntimeInfo })
- Iris.MenuToggle({ "Style Editor" }, { isChecked = showStyleEditor })
- Iris.End()
- Iris.End()
- end
-
- local function mainMenuBarExample()
- -- local screenSize = Iris.Internal._rootWidget.Instance.PseudoWindowScreenGui.AbsoluteSize
- -- Iris.Window(
- -- {[Iris.Args.Window.NoBackground] = true, [Iris.Args.Window.NoTitleBar] = true, [Iris.Args.Window.NoMove] = true, [Iris.Args.Window.NoResize] = true},
- -- {size = Iris.State(screenSize), position = Iris.State(Vector2.new(0, 0))}
- -- )
-
- mainMenuBar()
-
- --Iris.End()
- end
-
- -- allows users to edit state
- local styleEditor
- do
- styleEditor = function()
- local selectedPanel = Iris.State(1)
-
- local styleList = {
- {
- "Sizing",
- function()
- local UpdatedConfig = Iris.State({})
-
- if Iris.Button({ "Update Config" }).clicked() then
- Iris.UpdateGlobalConfig(UpdatedConfig:get())
- UpdatedConfig:set({})
- end
-
- local UDims = {
- { "ItemWidth", nil, UDim.new(), UDim.new(1, 200) },
- { "ContentWidth", nil, UDim.new(), UDim.new(1, 200) }
- }
- for _, vUDim in UDims do
- local Input = Iris.SliderUDim({ table.unpack(vUDim) }, { number = Iris.WeakState(Iris._config[vUDim[1]]) })
- if Input.numberChanged() then
- UpdatedConfig:get()[vUDim[1]] = Input.number:get()
- end
- end
-
- local Vector2s = {
- { "WindowPadding", nil, Vector2.zero, Vector2.one * 20 },
- { "WindowResizePadding", nil, Vector2.zero, Vector2.one * 20 },
- { "FramePadding", nil, Vector2.zero, Vector2.one * 20 },
- { "ItemSpacing", nil, Vector2.zero, Vector2.one * 20 },
- { "ItemInnerSpacing", nil, Vector2.zero, Vector2.one * 20 },
- { "CellPadding", nil, Vector2.zero, Vector2.one * 20 },
- { "DisplaySafeAreaPadding", nil, Vector2.zero, Vector2.one * 20 },
- }
- for _, vVector2 in Vector2s do
- local Input = Iris.SliderVector2({ table.unpack(vVector2) }, { number = Iris.WeakState(Iris._config[vVector2[1]]) })
- if Input.numberChanged() then
- UpdatedConfig:get()[vVector2[1]] = Input.number:get()
- end
- end
-
- local Numbers = {
- { "TextSize", 1, 4, 20 },
- { "FrameBorderSize", 0.1, 0, 1 },
- { "FrameRounding", 1, 0, 12 },
- { "GrabRounding", 1, 0, 12 },
- { "WindowBorderSize", 0.1, 0, 1 },
- { "PopupBorderSize", 0.1, 0, 1 },
- { "PopupRounding", 1, 0, 12 },
- { "ScrollbarSize", 1, 0, 20 },
- { "GrabMinSize", 1, 0, 20 },
- }
- for _, vNumber in Numbers do
- local Input = Iris.SliderNum({ table.unpack(vNumber) }, { number = Iris.WeakState(Iris._config[vNumber[1]]) })
- if Input.numberChanged() then
- UpdatedConfig:get()[vNumber[1]] = Input.number:get()
- end
- end
-
- local Enums = {
- "WindowTitleAlign",
- -- "TextFont"
- }
- for _, vEnum in Enums do
- local Input = Iris.ComboEnum({ vEnum }, { index = Iris.WeakState(Iris._config[vEnum]) }, Iris._config[vEnum].EnumType)
- if Input.closed() then
- Iris.UpdateGlobalConfig({ [vEnum] = Input.index:get() })
- end
- end
- end,
- },
- {
- "Colors",
- function()
- local UpdatedConfig = Iris.State({})
-
- if Iris.Button({ "Update Config" }).clicked() then
- Iris.UpdateGlobalConfig(UpdatedConfig:get())
- UpdatedConfig:set({})
- end
-
- local color3s = { "BorderColor", "BorderActiveColor" }
-
- for _, vColor in color3s do
- local Input = Iris.InputColor3({ vColor }, { color = Iris.WeakState(Iris._config[vColor]) })
- if Input.numberChanged() then
- Iris.UpdateGlobalConfig({ [vColor] = Input.color:get() })
- end
- end
-
- local color4s = {
- "Text",
- "TextDisabled",
- "WindowBg",
- "ScrollbarGrab",
- "TitleBg",
- "TitleBgActive",
- "TitleBgCollapsed",
- "MenubarBg",
- "FrameBg",
- "FrameBgHovered",
- "FrameBgActive",
- "Button",
- "ButtonHovered",
- "ButtonActive",
- "SliderGrab",
- "SliderGrabActive",
- "Header",
- "HeaderHovered",
- "HeaderActive",
- "SelectionImageObject",
- "SelectionImageObjectBorder",
- "TableBorderStrong",
- "TableBorderLight",
- "TableRowBg",
- "TableRowBgAlt",
- "NavWindowingHighlight",
- "NavWindowingDimBg",
- "Separator",
- "CheckMark",
- }
-
- for _, vColor in color4s do
- local Input = Iris.InputColor4({ vColor }, {
- color = Iris.WeakState(Iris._config[vColor .. "Color"]),
- transparency = Iris.WeakState(Iris._config[vColor .. "Transparency"]),
- })
- if Input.numberChanged() then
- UpdatedConfig:get()[vColor .. "Color"] = Input.color:get()
- UpdatedConfig:get()[vColor .. "Transparency"] = Input.transparency:get()
- end
- end
- end,
- },
- }
-
- local window = Iris.Window({ "Style Editor" }, { isOpened = showStyleEditor })
- Iris.Text({ `Clicked close: {window.closed()}` })
- Iris.Text({ "Customize the look of Iris in realtime." })
- Iris.SameLine()
- if Iris.SmallButton({ "Light Theme" }).clicked() then
- Iris.UpdateGlobalConfig(Iris.TemplateConfig.colorLight)
- end
- if Iris.SmallButton({ "Dark Theme" }).clicked() then
- Iris.UpdateGlobalConfig(Iris.TemplateConfig.colorDark)
- end
- Iris.End()
-
- Iris.SameLine()
- if Iris.SmallButton({ "Classic Size" }).clicked() then
- Iris.UpdateGlobalConfig(Iris.TemplateConfig.sizeDefault)
- end
- if Iris.SmallButton({ "Larger Size" }).clicked() then
- Iris.UpdateGlobalConfig(Iris.TemplateConfig.sizeClear)
- end
- Iris.End()
-
- if Iris.SmallButton({ "Reset Everything" }).clicked() then
- Iris.UpdateGlobalConfig(Iris.TemplateConfig.colorDark)
- Iris.UpdateGlobalConfig(Iris.TemplateConfig.sizeDefault)
- end
- Iris.Separator()
-
- Iris.SameLine()
- for i, v in ipairs(styleList) do
- Iris.RadioButton({ v[1], i }, { index = selectedPanel })
- end
- Iris.End()
-
- styleList[selectedPanel:get()][2]()
- Iris.End()
- end
- end
-
- local function widgetEventInteractivity()
- Iris.CollapsingHeader({ "Widget Event Interactivity" })
- local clickCount = Iris.State(0)
- if Iris.Button({ "Click to increase Number" }).clicked() then
- clickCount:set(clickCount:get() + 1)
- end
- Iris.Text({ "The Number is: " .. clickCount:get() })
-
- Iris.Separator()
-
- local showEventText = Iris.State(false)
- local selectedEvent = Iris.State("clicked")
- Iris.SameLine()
- Iris.RadioButton({ "clicked", "clicked" }, { index = selectedEvent })
- Iris.RadioButton({ "rightClicked", "rightClicked" }, { index = selectedEvent })
- Iris.RadioButton({ "doubleClicked", "doubleClicked" }, { index = selectedEvent })
- Iris.RadioButton({ "ctrlClicked", "ctrlClicked" }, { index = selectedEvent })
- Iris.End()
- Iris.SameLine()
-
- if Iris.Button({ selectedEvent:get() .. " to reveal text" })[selectedEvent:get()]() then
- showEventText:set(not showEventText:get())
- end
- if showEventText:get() then
- Iris.Text({ "Here i am!" })
- end
-
- Iris.End()
-
- Iris.Separator()
-
- local showTextTimer = Iris.State(0)
- Iris.SameLine()
- if Iris.Button({ "Click to show text for 20 frames" }).clicked() then
- showTextTimer:set(20)
- end
- if showTextTimer:get() > 0 then
- Iris.Text({ "Here i am!" })
- end
- Iris.End()
- showTextTimer:set(math.max(0, showTextTimer:get() - 1))
- Iris.Text({ "Text Timer: " .. showTextTimer:get() })
-
- local checkbox0 = Iris.Checkbox({ "Event-tracked checkbox" })
- Iris.Indent()
- Iris.Text({ "unchecked: " .. tostring(checkbox0.unchecked()) })
- Iris.Text({ "checked: " .. tostring(checkbox0.checked()) })
- Iris.End()
- Iris.SameLine()
- if Iris.Button({ "Hover over me" }).hovered() then
- Iris.Text({ "The button is hovered" })
- end
- Iris.End()
- Iris.End()
- end
-
- local function widgetStateInteractivity()
- Iris.CollapsingHeader({ "Widget State Interactivity" })
- local checkbox0 = Iris.Checkbox({ "Widget-Generated State" })
- Iris.Text({ `isChecked: {checkbox0.state.isChecked.value}\n` })
-
- local checkboxState0 = Iris.State(false)
- local checkbox1 = Iris.Checkbox({ "User-Generated State" }, { isChecked = checkboxState0 })
- Iris.Text({ `isChecked: {checkbox1.state.isChecked.value}\n` })
-
- local checkbox2 = Iris.Checkbox({ "Widget Coupled State" })
- local checkbox3 = Iris.Checkbox({ "Coupled to above Checkbox" }, { isChecked = checkbox2.state.isChecked })
- Iris.Text({ `isChecked: {checkbox3.state.isChecked.value}\n` })
-
- local checkboxState1 = Iris.State(false)
- local _checkbox4 = Iris.Checkbox({ "Widget and Code Coupled State" }, { isChecked = checkboxState1 })
- local Button0 = Iris.Button({ "Click to toggle above checkbox" })
- if Button0.clicked() then
- checkboxState1:set(not checkboxState1:get())
- end
- Iris.Text({ `isChecked: {checkboxState1.value}\n` })
-
- local checkboxState2 = Iris.State(true)
- local checkboxState3 = Iris.ComputedState(checkboxState2, function(newValue)
- return not newValue
- end)
- local _checkbox5 = Iris.Checkbox({ "ComputedState (dynamic coupling)" }, { isChecked = checkboxState2 })
- local _checkbox5 = Iris.Checkbox({ "Inverted of above checkbox" }, { isChecked = checkboxState3 })
- Iris.Text({ `isChecked: {checkboxState3.value}\n` })
-
- Iris.End()
- end
-
- local function dynamicStyle()
- Iris.CollapsingHeader({ "Dynamic Styles" })
- local colorH = Iris.State(0)
- Iris.SameLine()
- if Iris.Button({ "Change Color" }).clicked() then
- colorH:set(math.random())
- end
- Iris.Text({ "Hue: " .. math.floor(colorH:get() * 255) })
- helpMarker("Using PushConfig with a changing value, this can be done with any config field")
- Iris.End()
- Iris.PushConfig({ TextColor = Color3.fromHSV(colorH:get(), 1, 1) })
- Iris.Text({ "Text with a unique and changable color" })
- Iris.PopConfig()
- Iris.End()
- end
-
- local function tablesDemo()
- local showTablesTree = Iris.State(false)
-
- Iris.CollapsingHeader({ "Tables & Columns" }, { isUncollapsed = showTablesTree })
- if showTablesTree.value == false then
- -- optimization to skip code which draws GUI which wont be seen.
- -- its a trade off because when the tree becomes opened widgets will all have to be generated again.
- -- Dear ImGui utilizes the same trick, but its less useful here because the Retained mode Backend
- Iris.End()
- else
- Iris.SameLine()
- Iris.Text({ "Table using NextRow and NextColumn syntax:" })
- helpMarker("calling Iris.NextRow() in the outer loop, and Iris.NextColumn()in the inner loop")
- Iris.End()
- Iris.Table({ 3 })
- for i = 1, 4 do
- Iris.NextRow()
- for i2 = 1, 3 do
- Iris.NextColumn()
- Iris.Text({ `Row: {i}, Column: {i2}` })
- end
- end
- Iris.End()
-
- Iris.Text({ "" })
-
- Iris.SameLine()
- Iris.Text({ "Table using NextColumn only syntax:" })
- helpMarker("only calling Iris.NextColumn() in the inner loop, the result is identical")
- Iris.End()
-
- Iris.Table({ 2 })
- for i = 1, 4 do
- for i2 = 1, 2 do
- Iris.NextColumn()
- Iris.Text({ `Row: {i}, Column: {i2}` })
- end
- end
- Iris.End()
-
- Iris.Separator()
-
- local TableRowBg = Iris.State(false)
- local TableBordersOuter = Iris.State(false)
- local TableBordersInner = Iris.State(true)
- local TableUseButtons = Iris.State(true)
- local TableNumRows = Iris.State(3)
-
- Iris.Text({ "Table with Customizable Arguments" })
- Iris.Table({
- 4,
- [Iris.Args.Table.RowBg] = TableRowBg.value,
- [Iris.Args.Table.BordersOuter] = TableBordersOuter.value,
- [Iris.Args.Table.BordersInner] = TableBordersInner.value,
- })
- for i = 1, TableNumRows:get() do
- for i2 = 1, 4 do
- Iris.NextColumn()
- if TableUseButtons.value then
- Iris.Button({ `Month: {i}, Week: {i2}` })
- else
- Iris.Text({ `Month: {i}, Week: {i2}` })
- end
- end
- end
- Iris.End()
-
- Iris.Checkbox({ "RowBg" }, { isChecked = TableRowBg })
- Iris.Checkbox({ "BordersOuter" }, { isChecked = TableBordersOuter })
- Iris.Checkbox({ "BordersInner" }, { isChecked = TableBordersInner })
- Iris.SameLine()
- Iris.RadioButton({ "Buttons", true }, { index = TableUseButtons })
- Iris.RadioButton({ "Text", false }, { index = TableUseButtons })
- Iris.End()
- Iris.InputNum({
- "Number of rows",
- [Iris.Args.InputNum.Min] = 0,
- [Iris.Args.InputNum.Max] = 100,
- [Iris.Args.InputNum.Format] = "%d",
- }, { number = TableNumRows })
-
- Iris.End()
- end
- end
-
- local function layoutDemo()
- Iris.CollapsingHeader({ "Widget Layout" })
- Iris.Tree({ "Content Width" })
- local value = Iris.State(50)
- local index = Iris.State(Enum.Axis.X)
-
- Iris.Text({ "The Content Width is a size property which determines the width of input fields." })
- Iris.SameLine()
- Iris.Text({ "By default the value is UDim.new(0.65, 0)" })
- helpMarker("This is the default value from Dear ImGui.\nIt is 65% of the window width.")
- Iris.End()
- Iris.Text({ "This works well, but sometimes we know how wide elements are going to be and want to maximise the space." })
- Iris.Text({ "Therefore, we can use Iris.PushConfig() to change the width" })
-
- Iris.Separator()
-
- Iris.SameLine()
- Iris.Text({ "Content Width = 150 pixels" })
- helpMarker("UDim.new(0, 150)")
- Iris.End()
- Iris.PushConfig({ ContentWidth = UDim.new(0, 150) })
- Iris.DragNum({ "number", 1, 0, 100 }, { number = value })
- Iris.ComboEnum({ "axis" }, { index = index }, Enum.Axis)
- Iris.PopConfig()
-
- Iris.SameLine()
- Iris.Text({ "Content Width = 50% window width" })
- helpMarker("UDim.new(0.5, 0)")
- Iris.End()
- Iris.PushConfig({ ContentWidth = UDim.new(0.5, 0) })
- Iris.DragNum({ "number", 1, 0, 100 }, { number = value })
- Iris.ComboEnum({ "axis" }, { index = index }, Enum.Axis)
- Iris.PopConfig()
-
- Iris.SameLine()
- Iris.Text({ "Content Width = -150 pixels from the right side" })
- helpMarker("UDim.new(1, -150)")
- Iris.End()
- Iris.PushConfig({ ContentWidth = UDim.new(1, -150) })
- Iris.DragNum({ "number", 1, 0, 100 }, { number = value })
- Iris.InputEnum({ "axis" }, { index = index }, Enum.Axis)
- Iris.PopConfig()
- Iris.End()
- Iris.End()
- end
-
- -- showcases how widgets placed outside of a window are placed inside root
- local function windowlessDemo()
- Iris.PushConfig({ ItemWidth = UDim.new(0, 150) })
- Iris.SameLine()
- Iris.TextWrapped({ "Windowless widgets" })
- helpMarker("Widgets which are placed outside of a window will appear on the top left side of the screen.")
- Iris.End()
- Iris.Button({})
- Iris.Tree({})
- Iris.InputText({})
- Iris.End()
- Iris.PopConfig()
- end
-
- -- main demo window
- return function()
- local NoTitleBar = Iris.State(false)
- local NoBackground = Iris.State(false)
- local NoCollapse = Iris.State(false)
- local NoClose = Iris.State(true)
- local NoMove = Iris.State(false)
- local NoScrollbar = Iris.State(false)
- local NoResize = Iris.State(false)
- local NoNav = Iris.State(false)
- local NoMenu = Iris.State(false)
-
- if showMainWindow.value == false then
- Iris.Checkbox({ "Open main window" }, { isChecked = showMainWindow })
- return
- end
-
- Iris.Window({
- "Iris Demo Window",
- [Iris.Args.Window.NoTitleBar] = NoTitleBar.value,
- [Iris.Args.Window.NoBackground] = NoBackground.value,
- [Iris.Args.Window.NoCollapse] = NoCollapse.value,
- [Iris.Args.Window.NoClose] = NoClose.value,
- [Iris.Args.Window.NoMove] = NoMove.value,
- [Iris.Args.Window.NoScrollbar] = NoScrollbar.value,
- [Iris.Args.Window.NoResize] = NoResize.value,
- [Iris.Args.Window.NoNav] = NoNav.value,
- [Iris.Args.Window.NoMenu] = NoMenu.value,
- }, { size = Iris.State(Vector2.new(600, 550)), position = Iris.State(Vector2.new(100, 25)), isOpened = showMainWindow })
-
- mainMenuBar()
-
- Iris.Text({ "Iris says hello. (" .. Iris.Internal._version .. ")" })
-
- Iris.CollapsingHeader({ "Window Options" })
- Iris.Table({ 3, false, false, false })
- Iris.NextColumn()
- Iris.Checkbox({ "NoTitleBar" }, { isChecked = NoTitleBar })
- Iris.NextColumn()
- Iris.Checkbox({ "NoBackground" }, { isChecked = NoBackground })
- Iris.NextColumn()
- Iris.Checkbox({ "NoCollapse" }, { isChecked = NoCollapse })
- Iris.NextColumn()
- Iris.Checkbox({ "NoClose" }, { isChecked = NoClose })
- Iris.NextColumn()
- Iris.Checkbox({ "NoMove" }, { isChecked = NoMove })
- Iris.NextColumn()
- Iris.Checkbox({ "NoScrollbar" }, { isChecked = NoScrollbar })
- Iris.NextColumn()
- Iris.Checkbox({ "NoResize" }, { isChecked = NoResize })
- Iris.NextColumn()
- Iris.Checkbox({ "NoNav" }, { isChecked = NoNav })
- Iris.NextColumn()
- Iris.Checkbox({ "NoMenu" }, { isChecked = NoMenu })
- Iris.End()
- Iris.End()
-
- -- stylua: ignore end
-
- widgetEventInteractivity()
-
- widgetStateInteractivity()
-
- Iris.CollapsingHeader({ "Recursive Tree" })
- recursiveTree()
- Iris.End()
-
- dynamicStyle()
-
- Iris.Separator()
-
- Iris.CollapsingHeader({ "Widgets" })
- for _, name in widgetDemosOrder do
- widgetDemos[name]()
- end
- Iris.End()
-
- tablesDemo()
-
- layoutDemo()
- Iris.End()
-
- if showRecursiveWindow.value then
- recursiveWindow(showRecursiveWindow)
- end
- if showRuntimeInfo.value then
- runtimeInfo()
- end
- if showStyleEditor.value then
- styleEditor()
- end
- if showWindowlessDemo.value then
- windowlessDemo()
- end
-
- if showMainMenuBarWindow.value then
- mainMenuBarExample()
- end
- end
-end
+local Types = require(script.Parent.Types)
+
+return function(Iris: Types.Iris)
+ local showMainWindow = Iris.State(true)
+ local showRecursiveWindow = Iris.State(false)
+ local showRuntimeInfo = Iris.State(false)
+ local showStyleEditor = Iris.State(false)
+ local showWindowlessDemo = Iris.State(false)
+ local showMainMenuBarWindow = Iris.State(false)
+
+ -- stylua: ignore start
+ local function helpMarker(helpText)
+ Iris.PushConfig({ TextColor = Iris._config.TextDisabledColor })
+ local text = Iris.Text({ "(?)" })
+ Iris.PopConfig()
+
+ Iris.PushConfig({ ContentWidth = UDim.new(0, 350) })
+ if text.hovered() then
+ Iris.Tooltip({ helpText })
+ end
+ Iris.PopConfig()
+ end
+
+ -- shows each widgets functionality
+ local widgetDemos = {
+ Basic = function()
+ Iris.Tree({ "Basic" })
+ Iris.SeparatorText({ "Basic" })
+
+ local radioButtonState = Iris.State(1)
+ Iris.Button({ "Button" })
+ Iris.SmallButton({ "SmallButton" })
+ Iris.Text({ "Text" })
+ Iris.TextWrapped({ string.rep("Text Wrapped ", 5) })
+ Iris.TextColored({ "Colored Text", Color3.fromRGB(255, 128, 0) })
+ Iris.Text({ `Rich Text: bold text italic text underline text strikethrough text red text bigger text`, true, nil, true })
+ Iris.SameLine()
+ Iris.RadioButton({ "Index '1'", 1 }, { index = radioButtonState })
+ Iris.RadioButton({ "Index 'two'", "two" }, { index = radioButtonState })
+ if Iris.RadioButton({ "Index 'false'", false }, { index = radioButtonState }).active() == false then
+ if Iris.SmallButton({ "Select last" }).clicked() then
+ radioButtonState:set(false)
+ end
+ end
+ Iris.End()
+ Iris.Text({ "The Index is: " .. tostring(radioButtonState.value) })
+
+ Iris.SeparatorText({ "Inputs" })
+
+ Iris.InputNum({})
+ Iris.DragNum({})
+ Iris.SliderNum({})
+
+ Iris.End()
+ end,
+
+ Tree = function()
+ Iris.Tree({ "Trees" })
+ Iris.Tree({ "Tree using SpanAvailWidth", [Iris.Args.Tree.SpanAvailWidth] = true })
+ helpMarker("SpanAvailWidth determines if the Tree is selectable from its entire with, or only the text area")
+ Iris.End()
+
+ local tree1 = Iris.Tree({ "Tree with Children" })
+ Iris.Text({ "Im inside the first tree!" })
+ Iris.Button({ "Im a button inside the first tree!" })
+ Iris.Tree({ "Im a tree inside the first tree!" })
+ Iris.Text({ "I am the innermost text!" })
+ Iris.End()
+ Iris.End()
+
+ Iris.Checkbox({ "Toggle above tree" }, { isChecked = tree1.state.isUncollapsed })
+
+ Iris.End()
+ end,
+
+ CollapsingHeader = function()
+ Iris.Tree({ "Collapsing Headers" })
+ Iris.CollapsingHeader({ "A header" })
+ Iris.Text({ "This is under the first header!" })
+ Iris.End()
+
+ local secondHeader = Iris.State(true)
+ Iris.CollapsingHeader({ "Another header" }, { isUncollapsed = secondHeader })
+ if Iris.Button({ "Shhh... secret button!" }).clicked() then
+ secondHeader:set(true)
+ end
+ Iris.End()
+ Iris.End()
+ end,
+
+ Group = function()
+ Iris.Tree({ "Groups" })
+ Iris.SameLine()
+ Iris.Group()
+ Iris.Text({ "I am in group A" })
+ Iris.Button({ "Im also in A" })
+ Iris.End()
+
+ Iris.Separator()
+
+ Iris.Group()
+ Iris.Text({ "I am in group B" })
+ Iris.Button({ "Im also in B" })
+ Iris.Button({ "Also group B" })
+ Iris.End()
+ Iris.End()
+ Iris.End()
+ end,
+
+ Indent = function()
+ Iris.Tree({ "Indents" })
+ Iris.Text({ "Not Indented" })
+ Iris.Indent()
+ Iris.Text({ "Indented" })
+ Iris.Indent({ 7 })
+ Iris.Text({ "Indented by 7 more pixels" })
+ Iris.End()
+
+ Iris.Indent({ -7 })
+ Iris.Text({ "Indented by 7 less pixels" })
+ Iris.End()
+ Iris.End()
+ Iris.End()
+ end,
+
+ Input = function()
+ Iris.Tree({ "Input" })
+ local NoField, NoButtons, Min, Max, Increment, Format = Iris.State(false), Iris.State(false), Iris.State(0), Iris.State(100), Iris.State(1), Iris.State("%d")
+
+ Iris.PushConfig({ ContentWidth = UDim.new(1, -120) })
+ local InputNum = Iris.InputNum({
+ "Input Number",
+ -- [Iris.Args.InputNum.NoField] = NoField.value,
+ [Iris.Args.InputNum.NoButtons] = NoButtons.value,
+ [Iris.Args.InputNum.Min] = Min.value,
+ [Iris.Args.InputNum.Max] = Max.value,
+ [Iris.Args.InputNum.Increment] = Increment.value,
+ [Iris.Args.InputNum.Format] = { Format.value },
+ })
+ Iris.PopConfig()
+ Iris.Text({ "The Value is: " .. InputNum.number.value })
+ if Iris.Button({ "Randomize Number" }).clicked() then
+ InputNum.number:set(math.random(1, 99))
+ end
+ local NoFieldCheckbox = Iris.Checkbox({ "NoField" }, { isChecked = NoField })
+ local NoButtonsCheckbox = Iris.Checkbox({ "NoButtons" }, { isChecked = NoButtons })
+ if NoFieldCheckbox.checked() and NoButtonsCheckbox.isChecked.value == true then
+ NoButtonsCheckbox.isChecked:set(false)
+ end
+ if NoButtonsCheckbox.checked() and NoFieldCheckbox.isChecked.value == true then
+ NoFieldCheckbox.isChecked:set(false)
+ end
+
+ Iris.PushConfig({ ContentWidth = UDim.new(1, -120) })
+ Iris.InputVector2({ "InputVector2" })
+ Iris.InputVector3({ "InputVector3" })
+ Iris.InputUDim({ "InputUDim" })
+ Iris.InputUDim2({ "InputUDim2" })
+ local UseFloats = Iris.State(false)
+ local UseHSV = Iris.State(false)
+ local sharedColor = Iris.State(Color3.new())
+ local transparency = Iris.State(0)
+ Iris.SliderNum({ "Transparency", 0.01, 0, 1 }, { number = transparency })
+ Iris.InputColor3({ "InputColor3", UseFloats:get(), UseHSV:get() }, { color = sharedColor })
+ Iris.InputColor4({ "InputColor4", UseFloats:get(), UseHSV:get() }, { color = sharedColor, transparency = transparency })
+ Iris.SameLine()
+ Iris.Text({ sharedColor:get():ToHex() })
+ Iris.Checkbox({ "Use Floats" }, { isChecked = UseFloats })
+ Iris.Checkbox({ "Use HSV" }, { isChecked = UseHSV })
+ Iris.End()
+
+ Iris.PopConfig()
+
+ Iris.Separator()
+
+ Iris.SameLine()
+ Iris.Text({ "Slider Numbers" })
+ helpMarker("ctrl + click slider number widgets to input a number")
+ Iris.End()
+ Iris.PushConfig({ ContentWidth = UDim.new(1, -120) })
+ Iris.SliderNum({ "Slide Int", 1, 1, 8 })
+ Iris.SliderNum({ "Slide Float", 0.01, 0, 100 })
+ Iris.SliderNum({ "Small Numbers", 0.001, -2, 1, "%f radians" })
+ Iris.SliderNum({ "Odd Ranges", 0.001, -math.pi, math.pi, "%f radians" })
+ Iris.SliderNum({ "Big Numbers", 1e4, 1e5, 1e7 })
+ Iris.SliderNum({ "Few Numbers", 1, 0, 3 })
+ Iris.PopConfig()
+
+ Iris.Separator()
+
+ Iris.SameLine()
+ Iris.Text({ "Drag Numbers" })
+ helpMarker("ctrl + click or double click drag number widgets to input a number, hold shift/alt while dragging to increase/decrease speed")
+ Iris.End()
+ Iris.PushConfig({ ContentWidth = UDim.new(1, -120) })
+ Iris.DragNum({ "Drag Int" })
+ Iris.DragNum({ "Slide Float", 0.001, -10, 10 })
+ Iris.DragNum({ "Percentage", 1, 0, 100, "%d %%" })
+ Iris.PopConfig()
+ Iris.End()
+ end,
+
+ InputText = function()
+ Iris.Tree({ "Input Text" })
+ Iris.PushConfig({ ContentWidth = UDim.new(0, 250) })
+ local InputText = Iris.InputText({ "Input Text Test", [Iris.Args.InputText.TextHint] = "Input Text here" })
+ Iris.PopConfig()
+ Iris.Text({ "The text is: " .. InputText.text.value })
+ Iris.End()
+ end,
+
+ MultiInput = function()
+ Iris.Tree({"Multi-Component Input"})
+
+ local sharedVector2 = Iris.State(Vector2.new())
+ local sharedVector3 = Iris.State(Vector3.new())
+ local sharedUDim = Iris.State(UDim.new())
+ local sharedUDim2 = Iris.State(UDim2.new())
+ local sharedColor3 = Iris.State(Color3.new())
+ local SharedRect = Iris.State(Rect.new(0, 0, 0, 0))
+
+ Iris.SeparatorText({"Input"})
+
+ Iris.InputVector2({}, {number = sharedVector2})
+ Iris.InputVector3({}, {number = sharedVector3})
+ Iris.InputUDim({}, {number = sharedUDim})
+ Iris.InputUDim2({}, {number = sharedUDim2})
+ Iris.InputRect({}, {number = SharedRect})
+
+ Iris.SeparatorText({"Drag"})
+
+ Iris.DragVector2({}, {number = sharedVector2})
+ Iris.DragVector3({}, {number = sharedVector3})
+ Iris.DragUDim({}, {number = sharedUDim})
+ Iris.DragUDim2({}, {number = sharedUDim2})
+ Iris.DragRect({}, {number = SharedRect})
+
+ Iris.SeparatorText({"Slider"})
+
+ Iris.SliderVector2({}, {number = sharedVector2})
+ Iris.SliderVector3({}, {number = sharedVector3})
+ Iris.SliderUDim({}, {number = sharedUDim})
+ Iris.SliderUDim2({}, {number = sharedUDim2})
+ Iris.SliderRect({}, {number = SharedRect})
+
+ Iris.SeparatorText({"Color"})
+
+ Iris.InputColor3({}, {color = sharedColor3})
+ Iris.InputColor4({}, {color = sharedColor3})
+
+ Iris.End()
+ end,
+
+ Tooltip = function()
+ Iris.PushConfig({ ContentWidth = UDim.new(0, 250) })
+ Iris.Tree({ "Tooltip" })
+ if Iris.Text({ "Hover over me to reveal a tooltip" }).hovered() then
+ Iris.Tooltip({ "I am some helpful tooltip text" })
+ end
+ local dynamicText = Iris.State("Hello ")
+ local numRepeat = Iris.State(1)
+ if Iris.InputNum({ "# of repeat", 1, 1, 50 }, { number = numRepeat }).numberChanged() then
+ dynamicText:set(string.rep("Hello ", numRepeat:get()))
+ end
+ if Iris.Checkbox({ "Show dynamic text tooltip" }).isChecked.value then
+ Iris.Tooltip({ dynamicText:get() })
+ end
+ Iris.End()
+ Iris.PopConfig()
+ end,
+
+ Selectable = function()
+ Iris.Tree({ "Selectable" })
+ local sharedIndex = Iris.State(2)
+ Iris.Selectable({ "Selectable #1", 1 }, { index = sharedIndex })
+ Iris.Selectable({ "Selectable #2", 2 }, { index = sharedIndex })
+ if Iris.Selectable({ "Double click Selectable", 3, true }, { index = sharedIndex }).doubleClicked() then
+ sharedIndex:set(3)
+ end
+ Iris.Selectable({ "Impossible to select", 4, true }, { index = sharedIndex })
+ if Iris.Button({ "Select last" }).clicked() then
+ sharedIndex:set(4)
+ end
+ Iris.Selectable({ "Independent Selectable" })
+ Iris.End()
+ end,
+
+ Combo = function()
+ Iris.Tree({ "Combo" })
+ Iris.PushConfig({ ContentWidth = UDim.new(1, -120) })
+ local sharedComboIndex = Iris.State("No Selection")
+ Iris.SameLine()
+ local NoPreview = Iris.Checkbox({ "No Preview" })
+ local NoButton = Iris.Checkbox({ "No Button" })
+ if NoPreview.checked() and NoButton.isChecked.value == true then
+ NoButton.isChecked:set(false)
+ end
+ if NoButton.checked() and NoPreview.isChecked.value == true then
+ NoPreview.isChecked:set(false)
+ end
+ Iris.End()
+ Iris.Combo({ "Basic Usage", NoButton.isChecked:get(), NoPreview.isChecked:get() }, { index = sharedComboIndex })
+ Iris.Selectable({ "Select 1", "One" }, { index = sharedComboIndex })
+ Iris.Selectable({ "Select 2", "Two" }, { index = sharedComboIndex })
+ Iris.Selectable({ "Select 3", "Three" }, { index = sharedComboIndex })
+ Iris.End()
+
+ Iris.ComboArray({ "Using ComboArray" }, { index = "No Selection" }, { "Red", "Green", "Blue" })
+
+ local sharedComboIndex2 = Iris.State("7 AM")
+ Iris.Combo({ "Combo with Inner widgets" }, { index = sharedComboIndex2 })
+ Iris.Tree({ "Morning Shifts" })
+ Iris.Selectable({ "Shift at 7 AM", "7 AM" }, { index = sharedComboIndex2 })
+ Iris.Selectable({ "Shift at 11 AM", "11 AM" }, { index = sharedComboIndex2 })
+ Iris.Selectable({ "Shist at 3 PM", "3 PM" }, { index = sharedComboIndex2 })
+ Iris.End()
+ Iris.Tree({ "Night Shifts" })
+ Iris.Selectable({ "Shift at 6 PM", "6 PM" }, { index = sharedComboIndex2 })
+ Iris.Selectable({ "Shift at 9 PM", "9 PM" }, { index = sharedComboIndex2 })
+ Iris.End()
+ Iris.End()
+
+ local ComboEnum = Iris.ComboEnum({ "Using ComboEnum" }, { index = Enum.UserInputState.Begin }, Enum.UserInputState)
+ Iris.Text({ "Selected: " .. ComboEnum.index:get().Name })
+ Iris.PopConfig()
+ Iris.End()
+ end,
+ }
+ local widgetDemosOrder = { "Basic", "Tree", "CollapsingHeader", "Group", "Indent", "Input", "MultiInput", "InputText", "Tooltip", "Selectable", "Combo" }
+
+ local function recursiveTree()
+ local theTree = Iris.Tree({ "Recursive Tree" })
+ if theTree.state.isUncollapsed.value then
+ recursiveTree()
+ end
+ Iris.End()
+ end
+
+ local function recursiveWindow(parentCheckboxState)
+ Iris.Window({ "Recursive Window" }, { size = Iris.State(Vector2.new(175, 100)), isOpened = parentCheckboxState })
+ local theCheckbox = Iris.Checkbox({ "Recurse Again" })
+ Iris.End()
+ if theCheckbox.isChecked.value then
+ recursiveWindow(theCheckbox.isChecked)
+ end
+ end
+
+ -- shows list of runtime widgets and states, including IDs. shows other info about runtime and can show widgets/state info in depth.
+ local function runtimeInfo()
+ local runtimeInfoWindow = Iris.Window({ "Runtime Info" }, { isOpened = showRuntimeInfo })
+ local lastVDOM = Iris.Internal._lastVDOM
+ local states = Iris.Internal._states
+
+ local numSecondsDisabled = Iris.State(3)
+ local rollingDT = Iris.State(0)
+ local lastT = Iris.State(os.clock())
+
+ Iris.SameLine()
+ Iris.InputNum({ "", [Iris.Args.InputNum.Format] = "%d Seconds", [Iris.Args.InputNum.Max] = 10 }, { number = numSecondsDisabled })
+ if Iris.Button({ "Disable" }).clicked() then
+ Iris.Disabled = true
+ task.delay(numSecondsDisabled:get(), function()
+ Iris.Disabled = false
+ end)
+ end
+ Iris.End()
+
+ local t = os.clock()
+ local dt = t - lastT.value
+ rollingDT.value += (dt - rollingDT.value) * 0.2
+ lastT.value = t
+ Iris.Text({ string.format("Average %.3f ms/frame (%.1f FPS)", rollingDT.value * 1000, 1 / rollingDT.value) })
+
+ Iris.Text({
+ string.format("Window Position: (%d, %d), Window Size: (%d, %d)", runtimeInfoWindow.position.value.X, runtimeInfoWindow.position.value.Y, runtimeInfoWindow.size.value.X, runtimeInfoWindow.size.value.Y),
+ })
+
+ Iris.SameLine()
+ Iris.Text({ "Enter an ID to learn more about it." })
+ helpMarker("every widget and state has an ID which Iris tracks to remember which widget is which. below lists all widgets and states, with their respective IDs")
+ Iris.End()
+
+ Iris.PushConfig({ ItemWidth = UDim.new(1, -150) })
+ local enteredText = Iris.InputText({ "ID field" }, { text = Iris.State(runtimeInfoWindow.ID) }).text.value
+ Iris.PopConfig()
+
+ Iris.Indent()
+ local enteredWidget = lastVDOM[enteredText]
+ local enteredState = states[enteredText]
+ if enteredWidget then
+ Iris.Table({ 1, [Iris.Args.Table.RowBg] = false })
+ Iris.Text({ string.format('The ID, "%s", is a widget', enteredText) })
+ Iris.NextRow()
+
+ Iris.Text({ string.format("Widget is type: %s", enteredWidget.type) })
+ Iris.NextRow()
+
+ Iris.Tree({ "Widget has Args:" }, { isUncollapsed = Iris.State(true) })
+ for i, v in enteredWidget.arguments do
+ Iris.Text({ i .. " - " .. tostring(v) })
+ end
+ Iris.End()
+ Iris.NextRow()
+
+ if enteredWidget.state then
+ Iris.Tree({ "Widget has State:" }, { isUncollapsed = Iris.State(true) })
+ for i, v in enteredWidget.state do
+ Iris.Text({ i .. " - " .. tostring(v.value) })
+ end
+ Iris.End()
+ end
+ Iris.End()
+ elseif enteredState then
+ Iris.Table({ 1, [Iris.Args.Table.RowBg] = false })
+ Iris.Text({ string.format('The ID, "%s", is a state', enteredText) })
+ Iris.NextRow()
+
+ Iris.Text({ string.format("Value is type: %s, Value = %s", typeof(enteredState.value), tostring(enteredState.value)) })
+ Iris.NextRow()
+
+ Iris.Tree({ "state has connected widgets:" }, { isUncollapsed = Iris.State(true) })
+ for i, v in enteredState.ConnectedWidgets do
+ Iris.Text({ i .. " - " .. v.type })
+ end
+ Iris.End()
+ Iris.NextRow()
+
+ Iris.Text({ string.format("state has: %d connected functions", #enteredState.ConnectedFunctions) })
+ Iris.End()
+ else
+ Iris.Text({ string.format('The ID, "%s", is not a state or widget', enteredText) })
+ end
+ Iris.End()
+
+ if Iris.Tree({ "Widgets" }).isUncollapsed.value then
+ local widgetCount = 0
+ local widgetStr = ""
+ for _, v in lastVDOM do
+ widgetCount += 1
+ widgetStr ..= "\n" .. v.ID .. " - " .. v.type
+ end
+
+ Iris.Text({ "Number of Widgets: " .. widgetCount })
+
+ Iris.Text({ widgetStr })
+ end
+ Iris.End()
+ if Iris.Tree({ "States" }).isUncollapsed.value then
+ local stateCount = 0
+ local stateStr = ""
+ for i, v in states do
+ stateCount += 1
+ stateStr ..= "\n" .. i .. " - " .. tostring(v.value)
+ end
+
+ Iris.Text({ "Number of States: " .. stateCount })
+
+ Iris.Text({ stateStr })
+ end
+ Iris.End()
+ Iris.End()
+ end
+
+ local function recursiveMenu()
+ -- stylua: ignore start
+ if Iris.Menu({ "Recursive" }).state.isOpened.value then
+ Iris.MenuItem({ "New", Enum.KeyCode.N, Enum.ModifierKey.Ctrl })
+ Iris.MenuItem({ "Open", Enum.KeyCode.O, Enum.ModifierKey.Ctrl })
+ Iris.MenuItem({ "Save", Enum.KeyCode.S, Enum.ModifierKey.Ctrl })
+ Iris.Separator()
+ Iris.MenuToggle({ "Autosave" })
+ Iris.MenuToggle({ "Checked" })
+ Iris.Separator()
+ Iris.Menu({ "Options" })
+ Iris.MenuItem({ "Red" })
+ Iris.MenuItem({ "Yellow" })
+ Iris.MenuItem({ "Green" })
+ Iris.MenuItem({ "Blue" })
+ Iris.Separator()
+ recursiveMenu()
+ Iris.End()
+ end
+ Iris.End()
+ -- stylua: ignore end
+
+ end
+
+ local function mainMenuBar()
+ Iris.MenuBar()
+ Iris.Menu({ "File" })
+ Iris.MenuItem({ "New", Enum.KeyCode.N, Enum.ModifierKey.Ctrl })
+ Iris.MenuItem({ "Open", Enum.KeyCode.O, Enum.ModifierKey.Ctrl })
+ Iris.MenuItem({ "Save", Enum.KeyCode.S, Enum.ModifierKey.Ctrl })
+ recursiveMenu()
+ Iris.MenuItem({ "Quit", Enum.KeyCode.Q, Enum.ModifierKey.Alt })
+ Iris.End()
+
+ Iris.Menu({ "Examples" })
+ Iris.MenuToggle({ "Recursive Window" }, { isChecked = showRecursiveWindow })
+ Iris.MenuToggle({ "Windowless" }, { isChecked = showWindowlessDemo })
+ Iris.MenuToggle({ "Main Menu Bar" }, { isChecked = showMainMenuBarWindow })
+ Iris.End()
+
+ Iris.Menu({ "Tools" })
+ Iris.MenuToggle({ "Runtime Info" }, { isChecked = showRuntimeInfo })
+ Iris.MenuToggle({ "Style Editor" }, { isChecked = showStyleEditor })
+ Iris.End()
+ Iris.End()
+ end
+
+ local function mainMenuBarExample()
+ -- local screenSize = Iris.Internal._rootWidget.Instance.PseudoWindowScreenGui.AbsoluteSize
+ -- Iris.Window(
+ -- {[Iris.Args.Window.NoBackground] = true, [Iris.Args.Window.NoTitleBar] = true, [Iris.Args.Window.NoMove] = true, [Iris.Args.Window.NoResize] = true},
+ -- {size = Iris.State(screenSize), position = Iris.State(Vector2.new(0, 0))}
+ -- )
+
+ mainMenuBar()
+
+ --Iris.End()
+ end
+
+ -- allows users to edit state
+ local styleEditor
+ do
+ styleEditor = function()
+ local selectedPanel = Iris.State(1)
+
+ local styleList = {
+ {
+ "Sizing",
+ function()
+ local UpdatedConfig = Iris.State({})
+
+ if Iris.Button({ "Update Config" }).clicked() then
+ Iris.UpdateGlobalConfig(UpdatedConfig:get())
+ UpdatedConfig:set({})
+ end
+
+ local UDims = {
+ { "ItemWidth", nil, UDim.new(), UDim.new(1, 200) },
+ { "ContentWidth", nil, UDim.new(), UDim.new(1, 200) }
+ }
+ for _, vUDim in UDims do
+ local Input = Iris.SliderUDim({ table.unpack(vUDim) }, { number = Iris.WeakState(Iris._config[vUDim[1]]) })
+ if Input.numberChanged() then
+ UpdatedConfig:get()[vUDim[1]] = Input.number:get()
+ end
+ end
+
+ local Vector2s = {
+ { "WindowPadding", nil, Vector2.zero, Vector2.one * 20 },
+ { "WindowResizePadding", nil, Vector2.zero, Vector2.one * 20 },
+ { "FramePadding", nil, Vector2.zero, Vector2.one * 20 },
+ { "ItemSpacing", nil, Vector2.zero, Vector2.one * 20 },
+ { "ItemInnerSpacing", nil, Vector2.zero, Vector2.one * 20 },
+ { "CellPadding", nil, Vector2.zero, Vector2.one * 20 },
+ { "DisplaySafeAreaPadding", nil, Vector2.zero, Vector2.one * 20 },
+ }
+ for _, vVector2 in Vector2s do
+ local Input = Iris.SliderVector2({ table.unpack(vVector2) }, { number = Iris.WeakState(Iris._config[vVector2[1]]) })
+ if Input.numberChanged() then
+ UpdatedConfig:get()[vVector2[1]] = Input.number:get()
+ end
+ end
+
+ local Numbers = {
+ { "TextSize", 1, 4, 20 },
+ { "FrameBorderSize", 0.1, 0, 1 },
+ { "FrameRounding", 1, 0, 12 },
+ { "GrabRounding", 1, 0, 12 },
+ { "WindowBorderSize", 0.1, 0, 1 },
+ { "PopupBorderSize", 0.1, 0, 1 },
+ { "PopupRounding", 1, 0, 12 },
+ { "ScrollbarSize", 1, 0, 20 },
+ { "GrabMinSize", 1, 0, 20 },
+ }
+ for _, vNumber in Numbers do
+ local Input = Iris.SliderNum({ table.unpack(vNumber) }, { number = Iris.WeakState(Iris._config[vNumber[1]]) })
+ if Input.numberChanged() then
+ UpdatedConfig:get()[vNumber[1]] = Input.number:get()
+ end
+ end
+
+ local Enums = {
+ "WindowTitleAlign",
+ -- "TextFont"
+ }
+ for _, vEnum in Enums do
+ local Input = Iris.ComboEnum({ vEnum }, { index = Iris.WeakState(Iris._config[vEnum]) }, Iris._config[vEnum].EnumType)
+ if Input.closed() then
+ Iris.UpdateGlobalConfig({ [vEnum] = Input.index:get() })
+ end
+ end
+ end,
+ },
+ {
+ "Colors",
+ function()
+ local UpdatedConfig = Iris.State({})
+
+ if Iris.Button({ "Update Config" }).clicked() then
+ Iris.UpdateGlobalConfig(UpdatedConfig:get())
+ UpdatedConfig:set({})
+ end
+
+ local color3s = { "BorderColor", "BorderActiveColor" }
+
+ for _, vColor in color3s do
+ local Input = Iris.InputColor3({ vColor }, { color = Iris.WeakState(Iris._config[vColor]) })
+ if Input.numberChanged() then
+ Iris.UpdateGlobalConfig({ [vColor] = Input.color:get() })
+ end
+ end
+
+ local color4s = {
+ "Text",
+ "TextDisabled",
+ "WindowBg",
+ "ScrollbarGrab",
+ "TitleBg",
+ "TitleBgActive",
+ "TitleBgCollapsed",
+ "MenubarBg",
+ "FrameBg",
+ "FrameBgHovered",
+ "FrameBgActive",
+ "Button",
+ "ButtonHovered",
+ "ButtonActive",
+ "SliderGrab",
+ "SliderGrabActive",
+ "Header",
+ "HeaderHovered",
+ "HeaderActive",
+ "SelectionImageObject",
+ "SelectionImageObjectBorder",
+ "TableBorderStrong",
+ "TableBorderLight",
+ "TableRowBg",
+ "TableRowBgAlt",
+ "NavWindowingHighlight",
+ "NavWindowingDimBg",
+ "Separator",
+ "CheckMark",
+ }
+
+ for _, vColor in color4s do
+ local Input = Iris.InputColor4({ vColor }, {
+ color = Iris.WeakState(Iris._config[vColor .. "Color"]),
+ transparency = Iris.WeakState(Iris._config[vColor .. "Transparency"]),
+ })
+ if Input.numberChanged() then
+ UpdatedConfig:get()[vColor .. "Color"] = Input.color:get()
+ UpdatedConfig:get()[vColor .. "Transparency"] = Input.transparency:get()
+ end
+ end
+ end,
+ },
+ }
+
+ local window = Iris.Window({ "Style Editor" }, { isOpened = showStyleEditor })
+ Iris.Text({ `Clicked close: {window.closed()}` })
+ Iris.Text({ "Customize the look of Iris in realtime." })
+ Iris.SameLine()
+ if Iris.SmallButton({ "Light Theme" }).clicked() then
+ Iris.UpdateGlobalConfig(Iris.TemplateConfig.colorLight)
+ end
+ if Iris.SmallButton({ "Dark Theme" }).clicked() then
+ Iris.UpdateGlobalConfig(Iris.TemplateConfig.colorDark)
+ end
+ Iris.End()
+
+ Iris.SameLine()
+ if Iris.SmallButton({ "Classic Size" }).clicked() then
+ Iris.UpdateGlobalConfig(Iris.TemplateConfig.sizeDefault)
+ end
+ if Iris.SmallButton({ "Larger Size" }).clicked() then
+ Iris.UpdateGlobalConfig(Iris.TemplateConfig.sizeClear)
+ end
+ Iris.End()
+
+ if Iris.SmallButton({ "Reset Everything" }).clicked() then
+ Iris.UpdateGlobalConfig(Iris.TemplateConfig.colorDark)
+ Iris.UpdateGlobalConfig(Iris.TemplateConfig.sizeDefault)
+ end
+ Iris.Separator()
+
+ Iris.SameLine()
+ for i, v in ipairs(styleList) do
+ Iris.RadioButton({ v[1], i }, { index = selectedPanel })
+ end
+ Iris.End()
+
+ styleList[selectedPanel:get()][2]()
+ Iris.End()
+ end
+ end
+
+ local function widgetEventInteractivity()
+ Iris.CollapsingHeader({ "Widget Event Interactivity" })
+ local clickCount = Iris.State(0)
+ if Iris.Button({ "Click to increase Number" }).clicked() then
+ clickCount:set(clickCount:get() + 1)
+ end
+ Iris.Text({ "The Number is: " .. clickCount:get() })
+
+ Iris.Separator()
+
+ local showEventText = Iris.State(false)
+ local selectedEvent = Iris.State("clicked")
+ Iris.SameLine()
+ Iris.RadioButton({ "clicked", "clicked" }, { index = selectedEvent })
+ Iris.RadioButton({ "rightClicked", "rightClicked" }, { index = selectedEvent })
+ Iris.RadioButton({ "doubleClicked", "doubleClicked" }, { index = selectedEvent })
+ Iris.RadioButton({ "ctrlClicked", "ctrlClicked" }, { index = selectedEvent })
+ Iris.End()
+ Iris.SameLine()
+
+ if Iris.Button({ selectedEvent:get() .. " to reveal text" })[selectedEvent:get()]() then
+ showEventText:set(not showEventText:get())
+ end
+ if showEventText:get() then
+ Iris.Text({ "Here i am!" })
+ end
+
+ Iris.End()
+
+ Iris.Separator()
+
+ local showTextTimer = Iris.State(0)
+ Iris.SameLine()
+ if Iris.Button({ "Click to show text for 20 frames" }).clicked() then
+ showTextTimer:set(20)
+ end
+ if showTextTimer:get() > 0 then
+ Iris.Text({ "Here i am!" })
+ end
+ Iris.End()
+ showTextTimer:set(math.max(0, showTextTimer:get() - 1))
+ Iris.Text({ "Text Timer: " .. showTextTimer:get() })
+
+ local checkbox0 = Iris.Checkbox({ "Event-tracked checkbox" })
+ Iris.Indent()
+ Iris.Text({ "unchecked: " .. tostring(checkbox0.unchecked()) })
+ Iris.Text({ "checked: " .. tostring(checkbox0.checked()) })
+ Iris.End()
+ Iris.SameLine()
+ if Iris.Button({ "Hover over me" }).hovered() then
+ Iris.Text({ "The button is hovered" })
+ end
+ Iris.End()
+ Iris.End()
+ end
+
+ local function widgetStateInteractivity()
+ Iris.CollapsingHeader({ "Widget State Interactivity" })
+ local checkbox0 = Iris.Checkbox({ "Widget-Generated State" })
+ Iris.Text({ `isChecked: {checkbox0.state.isChecked.value}\n` })
+
+ local checkboxState0 = Iris.State(false)
+ local checkbox1 = Iris.Checkbox({ "User-Generated State" }, { isChecked = checkboxState0 })
+ Iris.Text({ `isChecked: {checkbox1.state.isChecked.value}\n` })
+
+ local checkbox2 = Iris.Checkbox({ "Widget Coupled State" })
+ local checkbox3 = Iris.Checkbox({ "Coupled to above Checkbox" }, { isChecked = checkbox2.state.isChecked })
+ Iris.Text({ `isChecked: {checkbox3.state.isChecked.value}\n` })
+
+ local checkboxState1 = Iris.State(false)
+ local _checkbox4 = Iris.Checkbox({ "Widget and Code Coupled State" }, { isChecked = checkboxState1 })
+ local Button0 = Iris.Button({ "Click to toggle above checkbox" })
+ if Button0.clicked() then
+ checkboxState1:set(not checkboxState1:get())
+ end
+ Iris.Text({ `isChecked: {checkboxState1.value}\n` })
+
+ local checkboxState2 = Iris.State(true)
+ local checkboxState3 = Iris.ComputedState(checkboxState2, function(newValue)
+ return not newValue
+ end)
+ local _checkbox5 = Iris.Checkbox({ "ComputedState (dynamic coupling)" }, { isChecked = checkboxState2 })
+ local _checkbox5 = Iris.Checkbox({ "Inverted of above checkbox" }, { isChecked = checkboxState3 })
+ Iris.Text({ `isChecked: {checkboxState3.value}\n` })
+
+ Iris.End()
+ end
+
+ local function dynamicStyle()
+ Iris.CollapsingHeader({ "Dynamic Styles" })
+ local colorH = Iris.State(0)
+ Iris.SameLine()
+ if Iris.Button({ "Change Color" }).clicked() then
+ colorH:set(math.random())
+ end
+ Iris.Text({ "Hue: " .. math.floor(colorH:get() * 255) })
+ helpMarker("Using PushConfig with a changing value, this can be done with any config field")
+ Iris.End()
+ Iris.PushConfig({ TextColor = Color3.fromHSV(colorH:get(), 1, 1) })
+ Iris.Text({ "Text with a unique and changable color" })
+ Iris.PopConfig()
+ Iris.End()
+ end
+
+ local function tablesDemo()
+ local showTablesTree = Iris.State(false)
+
+ Iris.CollapsingHeader({ "Tables & Columns" }, { isUncollapsed = showTablesTree })
+ if showTablesTree.value == false then
+ -- optimization to skip code which draws GUI which wont be seen.
+ -- its a trade off because when the tree becomes opened widgets will all have to be generated again.
+ -- Dear ImGui utilizes the same trick, but its less useful here because the Retained mode Backend
+ Iris.End()
+ else
+ Iris.SameLine()
+ Iris.Text({ "Table using NextRow and NextColumn syntax:" })
+ helpMarker("calling Iris.NextRow() in the outer loop, and Iris.NextColumn()in the inner loop")
+ Iris.End()
+ Iris.Table({ 3 })
+ for i = 1, 4 do
+ Iris.NextRow()
+ for i2 = 1, 3 do
+ Iris.NextColumn()
+ Iris.Text({ `Row: {i}, Column: {i2}` })
+ end
+ end
+ Iris.End()
+
+ Iris.Text({ "" })
+
+ Iris.SameLine()
+ Iris.Text({ "Table using NextColumn only syntax:" })
+ helpMarker("only calling Iris.NextColumn() in the inner loop, the result is identical")
+ Iris.End()
+
+ Iris.Table({ 2 })
+ for i = 1, 4 do
+ for i2 = 1, 2 do
+ Iris.NextColumn()
+ Iris.Text({ `Row: {i}, Column: {i2}` })
+ end
+ end
+ Iris.End()
+
+ Iris.Separator()
+
+ local TableRowBg = Iris.State(false)
+ local TableBordersOuter = Iris.State(false)
+ local TableBordersInner = Iris.State(true)
+ local TableUseButtons = Iris.State(true)
+ local TableNumRows = Iris.State(3)
+
+ Iris.Text({ "Table with Customizable Arguments" })
+ Iris.Table({
+ 4,
+ [Iris.Args.Table.RowBg] = TableRowBg.value,
+ [Iris.Args.Table.BordersOuter] = TableBordersOuter.value,
+ [Iris.Args.Table.BordersInner] = TableBordersInner.value,
+ })
+ for i = 1, TableNumRows:get() do
+ for i2 = 1, 4 do
+ Iris.NextColumn()
+ if TableUseButtons.value then
+ Iris.Button({ `Month: {i}, Week: {i2}` })
+ else
+ Iris.Text({ `Month: {i}, Week: {i2}` })
+ end
+ end
+ end
+ Iris.End()
+
+ Iris.Checkbox({ "RowBg" }, { isChecked = TableRowBg })
+ Iris.Checkbox({ "BordersOuter" }, { isChecked = TableBordersOuter })
+ Iris.Checkbox({ "BordersInner" }, { isChecked = TableBordersInner })
+ Iris.SameLine()
+ Iris.RadioButton({ "Buttons", true }, { index = TableUseButtons })
+ Iris.RadioButton({ "Text", false }, { index = TableUseButtons })
+ Iris.End()
+ Iris.InputNum({
+ "Number of rows",
+ [Iris.Args.InputNum.Min] = 0,
+ [Iris.Args.InputNum.Max] = 100,
+ [Iris.Args.InputNum.Format] = "%d",
+ }, { number = TableNumRows })
+
+ Iris.End()
+ end
+ end
+
+ local function layoutDemo()
+ Iris.CollapsingHeader({ "Widget Layout" })
+ Iris.Tree({ "Content Width" })
+ local value = Iris.State(50)
+ local index = Iris.State(Enum.Axis.X)
+
+ Iris.Text({ "The Content Width is a size property which determines the width of input fields." })
+ Iris.SameLine()
+ Iris.Text({ "By default the value is UDim.new(0.65, 0)" })
+ helpMarker("This is the default value from Dear ImGui.\nIt is 65% of the window width.")
+ Iris.End()
+ Iris.Text({ "This works well, but sometimes we know how wide elements are going to be and want to maximise the space." })
+ Iris.Text({ "Therefore, we can use Iris.PushConfig() to change the width" })
+
+ Iris.Separator()
+
+ Iris.SameLine()
+ Iris.Text({ "Content Width = 150 pixels" })
+ helpMarker("UDim.new(0, 150)")
+ Iris.End()
+ Iris.PushConfig({ ContentWidth = UDim.new(0, 150) })
+ Iris.DragNum({ "number", 1, 0, 100 }, { number = value })
+ Iris.ComboEnum({ "axis" }, { index = index }, Enum.Axis)
+ Iris.PopConfig()
+
+ Iris.SameLine()
+ Iris.Text({ "Content Width = 50% window width" })
+ helpMarker("UDim.new(0.5, 0)")
+ Iris.End()
+ Iris.PushConfig({ ContentWidth = UDim.new(0.5, 0) })
+ Iris.DragNum({ "number", 1, 0, 100 }, { number = value })
+ Iris.ComboEnum({ "axis" }, { index = index }, Enum.Axis)
+ Iris.PopConfig()
+
+ Iris.SameLine()
+ Iris.Text({ "Content Width = -150 pixels from the right side" })
+ helpMarker("UDim.new(1, -150)")
+ Iris.End()
+ Iris.PushConfig({ ContentWidth = UDim.new(1, -150) })
+ Iris.DragNum({ "number", 1, 0, 100 }, { number = value })
+ Iris.InputEnum({ "axis" }, { index = index }, Enum.Axis)
+ Iris.PopConfig()
+ Iris.End()
+ Iris.End()
+ end
+
+ -- showcases how widgets placed outside of a window are placed inside root
+ local function windowlessDemo()
+ Iris.PushConfig({ ItemWidth = UDim.new(0, 150) })
+ Iris.SameLine()
+ Iris.TextWrapped({ "Windowless widgets" })
+ helpMarker("Widgets which are placed outside of a window will appear on the top left side of the screen.")
+ Iris.End()
+ Iris.Button({})
+ Iris.Tree({})
+ Iris.InputText({})
+ Iris.End()
+ Iris.PopConfig()
+ end
+
+ -- main demo window
+ return function()
+ local NoTitleBar = Iris.State(false)
+ local NoBackground = Iris.State(false)
+ local NoCollapse = Iris.State(false)
+ local NoClose = Iris.State(true)
+ local NoMove = Iris.State(false)
+ local NoScrollbar = Iris.State(false)
+ local NoResize = Iris.State(false)
+ local NoNav = Iris.State(false)
+ local NoMenu = Iris.State(false)
+
+ if showMainWindow.value == false then
+ Iris.Checkbox({ "Open main window" }, { isChecked = showMainWindow })
+ return
+ end
+
+ Iris.Window({
+ "Iris Demo Window",
+ [Iris.Args.Window.NoTitleBar] = NoTitleBar.value,
+ [Iris.Args.Window.NoBackground] = NoBackground.value,
+ [Iris.Args.Window.NoCollapse] = NoCollapse.value,
+ [Iris.Args.Window.NoClose] = NoClose.value,
+ [Iris.Args.Window.NoMove] = NoMove.value,
+ [Iris.Args.Window.NoScrollbar] = NoScrollbar.value,
+ [Iris.Args.Window.NoResize] = NoResize.value,
+ [Iris.Args.Window.NoNav] = NoNav.value,
+ [Iris.Args.Window.NoMenu] = NoMenu.value,
+ }, { size = Iris.State(Vector2.new(600, 550)), position = Iris.State(Vector2.new(100, 25)), isOpened = showMainWindow })
+
+ mainMenuBar()
+
+ Iris.Text({ "Iris says hello. (" .. Iris.Internal._version .. ")" })
+
+ Iris.CollapsingHeader({ "Window Options" })
+ Iris.Table({ 3, false, false, false })
+ Iris.NextColumn()
+ Iris.Checkbox({ "NoTitleBar" }, { isChecked = NoTitleBar })
+ Iris.NextColumn()
+ Iris.Checkbox({ "NoBackground" }, { isChecked = NoBackground })
+ Iris.NextColumn()
+ Iris.Checkbox({ "NoCollapse" }, { isChecked = NoCollapse })
+ Iris.NextColumn()
+ Iris.Checkbox({ "NoClose" }, { isChecked = NoClose })
+ Iris.NextColumn()
+ Iris.Checkbox({ "NoMove" }, { isChecked = NoMove })
+ Iris.NextColumn()
+ Iris.Checkbox({ "NoScrollbar" }, { isChecked = NoScrollbar })
+ Iris.NextColumn()
+ Iris.Checkbox({ "NoResize" }, { isChecked = NoResize })
+ Iris.NextColumn()
+ Iris.Checkbox({ "NoNav" }, { isChecked = NoNav })
+ Iris.NextColumn()
+ Iris.Checkbox({ "NoMenu" }, { isChecked = NoMenu })
+ Iris.End()
+ Iris.End()
+
+ -- stylua: ignore end
+
+ widgetEventInteractivity()
+
+ widgetStateInteractivity()
+
+ Iris.CollapsingHeader({ "Recursive Tree" })
+ recursiveTree()
+ Iris.End()
+
+ dynamicStyle()
+
+ Iris.Separator()
+
+ Iris.CollapsingHeader({ "Widgets" })
+ for _, name in widgetDemosOrder do
+ widgetDemos[name]()
+ end
+ Iris.End()
+
+ tablesDemo()
+
+ layoutDemo()
+ Iris.End()
+
+ if showRecursiveWindow.value then
+ recursiveWindow(showRecursiveWindow)
+ end
+ if showRuntimeInfo.value then
+ runtimeInfo()
+ end
+ if showStyleEditor.value then
+ styleEditor()
+ end
+ if showWindowlessDemo.value then
+ windowlessDemo()
+ end
+
+ if showMainMenuBarWindow.value then
+ mainMenuBarExample()
+ end
+ end
+end
diff --git a/src/client/Iris/init.lua b/lib/init.lua
similarity index 96%
rename from src/client/Iris/init.lua
rename to lib/init.lua
index c8fcc4f..b038376 100644
--- a/src/client/Iris/init.lua
+++ b/lib/init.lua
@@ -1,421 +1,421 @@
---!optimize 2
-local Types = require(script.Types)
-
---[=[
- @class Iris
-
- Iris; contains the all user-facing functions and properties.
- A set of internal functions can be found in `Iris.Internal` (only use unless you understand).
-]=]
-local Iris = {} :: Types.Iris
-local Internal: Types.Internal = require(script.Internal)(Iris)
-
---[=[
- @prop Disabled boolean
- @within Iris
-
- While Iris.Disabled is true, execution of Iris and connected functions will be paused.
- The widgets are not destroyed, they are just frozen so no changes will happen to them.
-]=]
-Iris.Disabled = false
-
---[=[
- @prop Args table
- @within Iris
-
- Provides a list of every possible Argument for each type of widget to it's index.
- For instance, `Iris.Args.Window.NoResize`.
- The Args table is useful for using widget Arguments without remembering their order.
- ```lua
- Iris.Window({"My Window", [Iris.Args.Window.NoResize] = true})
- ```
-]=]
-Iris.Args = {}
-
---[=[
- @ignore
- @prop Events table
- @within Iris
-
- -todo: work out what this is used for.
-]=]
-Iris.Events = {}
-
---[=[
- @function Init
- @within Iris
- @param parentInstance Instance | nil -- instance which Iris will place UI in. defaults to [PlayerGui] if unspecified
- @param eventConnection RBXScriptSignal | () -> {} | nil
- @return Iris
-
- Initializes Iris and begins rendering. May only be called once.
- By default, Iris will create its widgets under the PlayerGui and use the Heartbeat event.
-]=]
-function Iris.Init(parentInstance: BasePlayerGui?, eventConnection: (RBXScriptSignal | () -> ())?): Types.Iris
- if parentInstance == nil then
- -- coalesce to playerGui
- parentInstance = game:GetService("Players").LocalPlayer:WaitForChild("PlayerGui")
- end
- if eventConnection == nil then
- -- coalesce to Heartbeat
- eventConnection = game:GetService("RunService").Heartbeat
- end
- Internal.parentInstance = parentInstance :: BasePlayerGui
- assert(Internal._started == false, "Iris.Init can only be called once.")
- Internal._started = true
-
- Internal._generateRootInstance()
- Internal._generateSelectionImageObject()
-
- -- spawns the connection to call `Internal._cycle()` within.
- task.spawn(function()
- if typeof(eventConnection) == "function" then
- while true do
- eventConnection()
- Internal._cycle()
- end
- elseif eventConnection ~= nil then
- eventConnection:Connect(function()
- Internal._cycle()
- end)
- end
- end)
-
- return Iris
-end
-
---[=[
- @within Iris
- @method Connect
- @param callback function -- the callback containg the Iris code.
-
- Allows users to connect a function which will execute every Iris cycle, (cycle is determined by the callback or event passed to Iris.Init or default to Heartbeat).
- Multiple callbacks can be added to Iris from many different scripts or modules.
-]=]
-function Iris:Connect(callback: () -> ()) -- this uses method syntax for no reason.
- if Internal._started == false then
- warn("Iris:Connect() was called before calling Iris.Init(), the connected function will never run")
- end
- table.insert(Internal._connectedFunctions, callback)
-end
-
---[=[
- @function Append
- @within Iris
-
- Allows the caller to insert any Roblox Instance into Iris. The parent can either be determined by the `_config.Parent`
- property or by the current parent widget from the stack.
-]=]
-function Iris.Append(userInstance: GuiObject)
- local parentWidget: Types.Widget = Internal._GetParentWidget()
- local widgetInstanceParent: GuiObject
- if Internal._config.Parent then
- widgetInstanceParent = Internal._config.Parent :: any
- else
- widgetInstanceParent = Internal._widgets[parentWidget.type].ChildAdded(parentWidget, { type = "userInstance" } :: Types.Widget)
- end
- userInstance.Parent = widgetInstanceParent
-end
-
---[=[
- @function End
- @within Iris
-
- This function marks the end of any widgets which contain children. For example:
- ```lua
- -- Widgets placed here **will not** be inside the tree
- Iris.Tree({"My First Tree"})
- -- Widgets placed here **will** be inside the tree
- Iris.End()
- -- Widgets placed here **will not** be inside the tree
- ```
- :::caution Caution: Error
- Seeing the error `Callback has too few calls to Iris.End()` or `Callback has too many calls to Iris.End()`?
- Using the wrong amount of `Iris.End()` calls in your code will lead to an error. Each widget called which might have children should be paired with a call to `Iris.End()`, **Even if the Widget doesnt currently have any children**.
- :::
-]=]
-function Iris.End()
- if Internal._stackIndex == 1 then
- error("Callback has too many calls to Iris.End()", 2)
- end
- Internal._IDStack[Internal._stackIndex] = nil
- Internal._stackIndex -= 1
-end
-
---[[
- ------------------------
- [SECTION] Config
- ------------------------
-]]
-
---[=[
- @function ForceRefresh
- @within Iris
-
- Destroys and regenerates all instances used by Iris. Useful if you want to propogate state changes.
- :::caution Caution: Performance
- Because this function Deletes and Initializes many instances, it may cause **performance issues** when used with many widgets.
- In **no** case should it be called every frame.
- :::
-]=]
-function Iris.ForceRefresh()
- Internal._globalRefreshRequested = true
-end
-
---[=[
- @function UpdateGlobalConfig
- @within Iris
- @param deltaStyle table -- a table containing the changes in style ex: `{ItemWidth = UDim.new(0, 100)}`
-
- Allows callers to customize the config which **every** widget will inherit from.
- It can be used along with Iris.TemplateConfig to easily swap styles, ex: ```Iris.UpdateGlobalConfig(Iris.TemplateConfig.colorLight) -- use light theme```
- :::caution Caution: Performance
- this function internally calls [Iris.ForceRefresh] so that style changes are propogated, it may cause **performance issues** when used with many widgets.
- In **no** case should it be called every frame.
- :::
-]=]
-function Iris.UpdateGlobalConfig(deltaStyle: { [string]: any })
- for index, style in deltaStyle do
- Internal._rootConfig[index] = style
- end
- Iris.ForceRefresh()
-end
-
---[=[
- @function PushConfig
- @within Iris
- @param deltaStyle table -- a table containing the changes in style ex: `{ItemWidth = UDim.new(0, 100)}`
-
- Allows callers to cascade a style, meaning that styles may be locally and hierarchically applied.
- Each call to Iris.PushConfig must be paired with a call to [Iris.PopConfig].
- For example:
- ```lua
- Iris.PushConfig({TextColor = Color3.fromRGB(128, 0, 256)})
- Iris.Text({"Colored Text!"})
- Iris.PopConfig()
- ```
-]=]
-function Iris.PushConfig(deltaStyle: { [string]: any })
- local ID = Iris.State(-1)
- if ID.value == -1 then
- ID:set(deltaStyle)
- else
- -- compare tables
- if Internal._deepCompare(ID:get(), deltaStyle) == false then
- -- refresh local
- Internal._localRefreshActive = true
- ID:set(deltaStyle)
- end
- end
-
- Internal._config = setmetatable(deltaStyle, {
- __index = Internal._config,
- }) :: any
-end
-
---[=[
- @function PopConfig
- @within Iris
-
- Ends a PushConfig style.
- Each call to [Iris.PushConfig] must be paired with a call to Iris.PopConfig.
-]=]
-function Iris.PopConfig()
- Internal._localRefreshActive = false
- Internal._config = getmetatable(Internal._config :: any).__index
-end
-
---[=[
- @prop TemplateConfig table
- @within Iris
-
- TemplateConfig provides a table of default styles and configurations which you may apply to your UI.
-]=]
-Iris.TemplateConfig = require(script.config)
-Iris.UpdateGlobalConfig(Iris.TemplateConfig.colorDark) -- use colorDark and sizeDefault themes by default
-Iris.UpdateGlobalConfig(Iris.TemplateConfig.sizeDefault)
-Iris.UpdateGlobalConfig(Iris.TemplateConfig.utilityDefault)
-Internal._globalRefreshRequested = false -- UpdatingGlobalConfig changes this to true, leads to Root being generated twice.
-
---[[
- --------------------
- [SECTION] ID
- --------------------
-]]
-
---[=[
- @function PushId
- @within Iris
- @param id Types.ID -- custom id.
-
- Sets the id discriminator for the next widgets. Use [Iris.PopId] to remove it.
-]=]
-function Iris.PushId(id: Types.ID)
- assert(typeof(id) == "string", "Iris expected Iris.PushId id to PushId to be a string.")
-
- Internal._pushedId = tostring(id)
-end
-
---[=[
- @function PopId
- @within Iris
-
- Removes the id discriminator set by [Iris.PushId].
-]=]
-function Iris.PopId()
- Internal._pushedId = nil
-end
-
---[=[
- @function SetNextWidgetId
- @within Iris
- @param id Types.ID -- custom id.
-
- Sets the id for the next widget. Useful for using [Iris.Append] on the same widget.
- ```lua
- Iris.SetNextWidgetId("demo_window")
- Iris.Window({ "Window" })
- Iris.Text({ "Text one placed here." })
- Iris.End()
-
- -- later in the code
-
- Iris.SetNextWidgetId("demo_window")
- Iris.Window()
- Iris.Text({ "Text two placed here." })
- Iris.End()
-
- -- both text widgets will be placed under the same window despite being called separately.
- ```
-]=]
-function Iris.SetNextWidgetID(id: Types.ID)
- Internal._nextWidgetId = id
-end
-
---[[
- -----------------------
- [SECTION] State
- -----------------------
-]]
-
---[=[
- @function State
- @within Iris
- @param initialValue any -- The initial value for the state
-
- Constructs a new state object, subsequent ID calls will return the same object
- :::info
- Iris.State allows you to create "references" to the same value while inside your UI drawing loop.
- For example:
- ```lua
- Iris:Connect(function()
- local myNumber = 5;
- myNumber = myNumber + 1
- Iris.Text({"The number is: " .. myNumber})
- end)
- ```
- This is problematic. Each time the function is called, a new myNumber is initialized, instead of retrieving the old one.
- The above code will always display 6.
- ***
- Iris.State solves this problem:
- ```lua
- Iris:Connect(function()
- local myNumber = Iris.State(5)
- myNumber:set(myNumber:get() + 1)
- Iris.Text({"The number is: " .. myNumber})
- end)
- ```
- In this example, the code will work properly, and increment every frame.
- :::
-]=]
-function Iris.State(initialValue: any): Types.State
- local ID: Types.ID = Internal._getID(2)
- if Internal._states[ID] then
- return Internal._states[ID]
- end
- Internal._states[ID] = {
- value = initialValue,
- ConnectedWidgets = {},
- ConnectedFunctions = {},
- } :: any
- setmetatable(Internal._states[ID], Internal.StateClass)
- return Internal._states[ID]
-end
-
---[=[
- @function State
- @within Iris
- @param initialValue any -- The initial value for the state
-
- Constructs a new state object, subsequent ID calls will return the same object, except all widgets connected to the state are discarded, the state reverts to the passed initialValue
-]=]
-function Iris.WeakState(initialValue: any): Types.State
- local ID: Types.ID = Internal._getID(2)
- if Internal._states[ID] then
- if #Internal._states[ID].ConnectedWidgets == 0 then
- Internal._states[ID] = nil
- else
- return Internal._states[ID]
- end
- end
- Internal._states[ID] = {
- value = initialValue,
- ConnectedWidgets = {},
- ConnectedFunctions = {},
- } :: any
- setmetatable(Internal._states[ID], Internal.StateClass)
- return Internal._states[ID]
-end
-
---[=[
- @function ComputedState
- @within Iris
- @param firstState State -- State to bind to.
- @param onChangeCallback function -- callback which should return a value transformed from the firstState value
-
- Constructs a new State object, but binds its value to the value of another State.
- :::info
- A common use case for this constructor is when a boolean State needs to be inverted:
- ```lua
- Iris.ComputedState(otherState, function(newValue)
- return not newValue
- end)
- ```
- :::
-]=]
-function Iris.ComputedState(firstState: Types.State, onChangeCallback: (firstState: any) -> any): Types.State
- local ID: Types.ID = Internal._getID(2)
-
- if Internal._states[ID] then
- return Internal._states[ID]
- else
- Internal._states[ID] = {
- value = onChangeCallback(firstState.value),
- ConnectedWidgets = {},
- ConnectedFunctions = {},
- } :: any
- firstState:onChange(function(newValue: any)
- Internal._states[ID]:set(onChangeCallback(newValue))
- end)
- setmetatable(Internal._states[ID], Internal.StateClass)
- return Internal._states[ID]
- end
-end
-
---[=[
- @function ShowDemoWindow
- @within Iris
-
- ShowDemoWindow is a function which creates a Demonstration window. this window contains many useful utilities for coders,
- and serves as a refrence for using each part of the library. Ideally, the DemoWindow should always be available in your UI.
- It is the same as any other callback you would connect to Iris using [Iris.Connect]
- ```lua
- Iris:Connect(Iris.ShowDemoWindow)
- ```
-]=]
-Iris.ShowDemoWindow = require(script.demoWindow)(Iris)
-
-require(script.widgets)(Internal)
-require(script.API)(Iris)
-
-return Iris
+--!optimize 2
+local Types = require(script.Types)
+
+--[=[
+ @class Iris
+
+ Iris; contains the all user-facing functions and properties.
+ A set of internal functions can be found in `Iris.Internal` (only use unless you understand).
+]=]
+local Iris = {} :: Types.Iris
+local Internal: Types.Internal = require(script.Internal)(Iris)
+
+--[=[
+ @prop Disabled boolean
+ @within Iris
+
+ While Iris.Disabled is true, execution of Iris and connected functions will be paused.
+ The widgets are not destroyed, they are just frozen so no changes will happen to them.
+]=]
+Iris.Disabled = false
+
+--[=[
+ @prop Args table
+ @within Iris
+
+ Provides a list of every possible Argument for each type of widget to it's index.
+ For instance, `Iris.Args.Window.NoResize`.
+ The Args table is useful for using widget Arguments without remembering their order.
+ ```lua
+ Iris.Window({"My Window", [Iris.Args.Window.NoResize] = true})
+ ```
+]=]
+Iris.Args = {}
+
+--[=[
+ @ignore
+ @prop Events table
+ @within Iris
+
+ -todo: work out what this is used for.
+]=]
+Iris.Events = {}
+
+--[=[
+ @function Init
+ @within Iris
+ @param parentInstance Instance | nil -- instance which Iris will place UI in. defaults to [PlayerGui] if unspecified
+ @param eventConnection RBXScriptSignal | () -> {} | nil
+ @return Iris
+
+ Initializes Iris and begins rendering. May only be called once.
+ By default, Iris will create its widgets under the PlayerGui and use the Heartbeat event.
+]=]
+function Iris.Init(parentInstance: BasePlayerGui?, eventConnection: (RBXScriptSignal | () -> ())?): Types.Iris
+ if parentInstance == nil then
+ -- coalesce to playerGui
+ parentInstance = game:GetService("Players").LocalPlayer:WaitForChild("PlayerGui")
+ end
+ if eventConnection == nil then
+ -- coalesce to Heartbeat
+ eventConnection = game:GetService("RunService").Heartbeat
+ end
+ Internal.parentInstance = parentInstance :: BasePlayerGui
+ assert(Internal._started == false, "Iris.Init can only be called once.")
+ Internal._started = true
+
+ Internal._generateRootInstance()
+ Internal._generateSelectionImageObject()
+
+ -- spawns the connection to call `Internal._cycle()` within.
+ task.spawn(function()
+ if typeof(eventConnection) == "function" then
+ while true do
+ eventConnection()
+ Internal._cycle()
+ end
+ elseif eventConnection ~= nil then
+ eventConnection:Connect(function()
+ Internal._cycle()
+ end)
+ end
+ end)
+
+ return Iris
+end
+
+--[=[
+ @within Iris
+ @method Connect
+ @param callback function -- the callback containg the Iris code.
+
+ Allows users to connect a function which will execute every Iris cycle, (cycle is determined by the callback or event passed to Iris.Init or default to Heartbeat).
+ Multiple callbacks can be added to Iris from many different scripts or modules.
+]=]
+function Iris:Connect(callback: () -> ()) -- this uses method syntax for no reason.
+ if Internal._started == false then
+ warn("Iris:Connect() was called before calling Iris.Init(), the connected function will never run")
+ end
+ table.insert(Internal._connectedFunctions, callback)
+end
+
+--[=[
+ @function Append
+ @within Iris
+
+ Allows the caller to insert any Roblox Instance into Iris. The parent can either be determined by the `_config.Parent`
+ property or by the current parent widget from the stack.
+]=]
+function Iris.Append(userInstance: GuiObject)
+ local parentWidget: Types.Widget = Internal._GetParentWidget()
+ local widgetInstanceParent: GuiObject
+ if Internal._config.Parent then
+ widgetInstanceParent = Internal._config.Parent :: any
+ else
+ widgetInstanceParent = Internal._widgets[parentWidget.type].ChildAdded(parentWidget, { type = "userInstance" } :: Types.Widget)
+ end
+ userInstance.Parent = widgetInstanceParent
+end
+
+--[=[
+ @function End
+ @within Iris
+
+ This function marks the end of any widgets which contain children. For example:
+ ```lua
+ -- Widgets placed here **will not** be inside the tree
+ Iris.Tree({"My First Tree"})
+ -- Widgets placed here **will** be inside the tree
+ Iris.End()
+ -- Widgets placed here **will not** be inside the tree
+ ```
+ :::caution Caution: Error
+ Seeing the error `Callback has too few calls to Iris.End()` or `Callback has too many calls to Iris.End()`?
+ Using the wrong amount of `Iris.End()` calls in your code will lead to an error. Each widget called which might have children should be paired with a call to `Iris.End()`, **Even if the Widget doesnt currently have any children**.
+ :::
+]=]
+function Iris.End()
+ if Internal._stackIndex == 1 then
+ error("Callback has too many calls to Iris.End()", 2)
+ end
+ Internal._IDStack[Internal._stackIndex] = nil
+ Internal._stackIndex -= 1
+end
+
+--[[
+ ------------------------
+ [SECTION] Config
+ ------------------------
+]]
+
+--[=[
+ @function ForceRefresh
+ @within Iris
+
+ Destroys and regenerates all instances used by Iris. Useful if you want to propogate state changes.
+ :::caution Caution: Performance
+ Because this function Deletes and Initializes many instances, it may cause **performance issues** when used with many widgets.
+ In **no** case should it be called every frame.
+ :::
+]=]
+function Iris.ForceRefresh()
+ Internal._globalRefreshRequested = true
+end
+
+--[=[
+ @function UpdateGlobalConfig
+ @within Iris
+ @param deltaStyle table -- a table containing the changes in style ex: `{ItemWidth = UDim.new(0, 100)}`
+
+ Allows callers to customize the config which **every** widget will inherit from.
+ It can be used along with Iris.TemplateConfig to easily swap styles, ex: ```Iris.UpdateGlobalConfig(Iris.TemplateConfig.colorLight) -- use light theme```
+ :::caution Caution: Performance
+ this function internally calls [Iris.ForceRefresh] so that style changes are propogated, it may cause **performance issues** when used with many widgets.
+ In **no** case should it be called every frame.
+ :::
+]=]
+function Iris.UpdateGlobalConfig(deltaStyle: { [string]: any })
+ for index, style in deltaStyle do
+ Internal._rootConfig[index] = style
+ end
+ Iris.ForceRefresh()
+end
+
+--[=[
+ @function PushConfig
+ @within Iris
+ @param deltaStyle table -- a table containing the changes in style ex: `{ItemWidth = UDim.new(0, 100)}`
+
+ Allows callers to cascade a style, meaning that styles may be locally and hierarchically applied.
+ Each call to Iris.PushConfig must be paired with a call to [Iris.PopConfig].
+ For example:
+ ```lua
+ Iris.PushConfig({TextColor = Color3.fromRGB(128, 0, 256)})
+ Iris.Text({"Colored Text!"})
+ Iris.PopConfig()
+ ```
+]=]
+function Iris.PushConfig(deltaStyle: { [string]: any })
+ local ID = Iris.State(-1)
+ if ID.value == -1 then
+ ID:set(deltaStyle)
+ else
+ -- compare tables
+ if Internal._deepCompare(ID:get(), deltaStyle) == false then
+ -- refresh local
+ Internal._localRefreshActive = true
+ ID:set(deltaStyle)
+ end
+ end
+
+ Internal._config = setmetatable(deltaStyle, {
+ __index = Internal._config,
+ }) :: any
+end
+
+--[=[
+ @function PopConfig
+ @within Iris
+
+ Ends a PushConfig style.
+ Each call to [Iris.PushConfig] must be paired with a call to Iris.PopConfig.
+]=]
+function Iris.PopConfig()
+ Internal._localRefreshActive = false
+ Internal._config = getmetatable(Internal._config :: any).__index
+end
+
+--[=[
+ @prop TemplateConfig table
+ @within Iris
+
+ TemplateConfig provides a table of default styles and configurations which you may apply to your UI.
+]=]
+Iris.TemplateConfig = require(script.config)
+Iris.UpdateGlobalConfig(Iris.TemplateConfig.colorDark) -- use colorDark and sizeDefault themes by default
+Iris.UpdateGlobalConfig(Iris.TemplateConfig.sizeDefault)
+Iris.UpdateGlobalConfig(Iris.TemplateConfig.utilityDefault)
+Internal._globalRefreshRequested = false -- UpdatingGlobalConfig changes this to true, leads to Root being generated twice.
+
+--[[
+ --------------------
+ [SECTION] ID
+ --------------------
+]]
+
+--[=[
+ @function PushId
+ @within Iris
+ @param id Types.ID -- custom id.
+
+ Sets the id discriminator for the next widgets. Use [Iris.PopId] to remove it.
+]=]
+function Iris.PushId(id: Types.ID)
+ assert(typeof(id) == "string", "Iris expected Iris.PushId id to PushId to be a string.")
+
+ Internal._pushedId = tostring(id)
+end
+
+--[=[
+ @function PopId
+ @within Iris
+
+ Removes the id discriminator set by [Iris.PushId].
+]=]
+function Iris.PopId()
+ Internal._pushedId = nil
+end
+
+--[=[
+ @function SetNextWidgetId
+ @within Iris
+ @param id Types.ID -- custom id.
+
+ Sets the id for the next widget. Useful for using [Iris.Append] on the same widget.
+ ```lua
+ Iris.SetNextWidgetId("demo_window")
+ Iris.Window({ "Window" })
+ Iris.Text({ "Text one placed here." })
+ Iris.End()
+
+ -- later in the code
+
+ Iris.SetNextWidgetId("demo_window")
+ Iris.Window()
+ Iris.Text({ "Text two placed here." })
+ Iris.End()
+
+ -- both text widgets will be placed under the same window despite being called separately.
+ ```
+]=]
+function Iris.SetNextWidgetID(id: Types.ID)
+ Internal._nextWidgetId = id
+end
+
+--[[
+ -----------------------
+ [SECTION] State
+ -----------------------
+]]
+
+--[=[
+ @function State
+ @within Iris
+ @param initialValue any -- The initial value for the state
+
+ Constructs a new state object, subsequent ID calls will return the same object
+ :::info
+ Iris.State allows you to create "references" to the same value while inside your UI drawing loop.
+ For example:
+ ```lua
+ Iris:Connect(function()
+ local myNumber = 5;
+ myNumber = myNumber + 1
+ Iris.Text({"The number is: " .. myNumber})
+ end)
+ ```
+ This is problematic. Each time the function is called, a new myNumber is initialized, instead of retrieving the old one.
+ The above code will always display 6.
+ ***
+ Iris.State solves this problem:
+ ```lua
+ Iris:Connect(function()
+ local myNumber = Iris.State(5)
+ myNumber:set(myNumber:get() + 1)
+ Iris.Text({"The number is: " .. myNumber})
+ end)
+ ```
+ In this example, the code will work properly, and increment every frame.
+ :::
+]=]
+function Iris.State(initialValue: any): Types.State
+ local ID: Types.ID = Internal._getID(2)
+ if Internal._states[ID] then
+ return Internal._states[ID]
+ end
+ Internal._states[ID] = {
+ value = initialValue,
+ ConnectedWidgets = {},
+ ConnectedFunctions = {},
+ } :: any
+ setmetatable(Internal._states[ID], Internal.StateClass)
+ return Internal._states[ID]
+end
+
+--[=[
+ @function State
+ @within Iris
+ @param initialValue any -- The initial value for the state
+
+ Constructs a new state object, subsequent ID calls will return the same object, except all widgets connected to the state are discarded, the state reverts to the passed initialValue
+]=]
+function Iris.WeakState(initialValue: any): Types.State
+ local ID: Types.ID = Internal._getID(2)
+ if Internal._states[ID] then
+ if #Internal._states[ID].ConnectedWidgets == 0 then
+ Internal._states[ID] = nil
+ else
+ return Internal._states[ID]
+ end
+ end
+ Internal._states[ID] = {
+ value = initialValue,
+ ConnectedWidgets = {},
+ ConnectedFunctions = {},
+ } :: any
+ setmetatable(Internal._states[ID], Internal.StateClass)
+ return Internal._states[ID]
+end
+
+--[=[
+ @function ComputedState
+ @within Iris
+ @param firstState State -- State to bind to.
+ @param onChangeCallback function -- callback which should return a value transformed from the firstState value
+
+ Constructs a new State object, but binds its value to the value of another State.
+ :::info
+ A common use case for this constructor is when a boolean State needs to be inverted:
+ ```lua
+ Iris.ComputedState(otherState, function(newValue)
+ return not newValue
+ end)
+ ```
+ :::
+]=]
+function Iris.ComputedState(firstState: Types.State, onChangeCallback: (firstState: any) -> any): Types.State
+ local ID: Types.ID = Internal._getID(2)
+
+ if Internal._states[ID] then
+ return Internal._states[ID]
+ else
+ Internal._states[ID] = {
+ value = onChangeCallback(firstState.value),
+ ConnectedWidgets = {},
+ ConnectedFunctions = {},
+ } :: any
+ firstState:onChange(function(newValue: any)
+ Internal._states[ID]:set(onChangeCallback(newValue))
+ end)
+ setmetatable(Internal._states[ID], Internal.StateClass)
+ return Internal._states[ID]
+ end
+end
+
+--[=[
+ @function ShowDemoWindow
+ @within Iris
+
+ ShowDemoWindow is a function which creates a Demonstration window. this window contains many useful utilities for coders,
+ and serves as a refrence for using each part of the library. Ideally, the DemoWindow should always be available in your UI.
+ It is the same as any other callback you would connect to Iris using [Iris.Connect]
+ ```lua
+ Iris:Connect(Iris.ShowDemoWindow)
+ ```
+]=]
+Iris.ShowDemoWindow = require(script.demoWindow)(Iris)
+
+require(script.widgets)(Internal)
+require(script.API)(Iris)
+
+return Iris
diff --git a/src/client/Iris/widgets/Button.lua b/lib/widgets/Button.lua
similarity index 100%
rename from src/client/Iris/widgets/Button.lua
rename to lib/widgets/Button.lua
diff --git a/src/client/Iris/widgets/Checkbox.lua b/lib/widgets/Checkbox.lua
similarity index 100%
rename from src/client/Iris/widgets/Checkbox.lua
rename to lib/widgets/Checkbox.lua
diff --git a/src/client/Iris/widgets/Combo.lua b/lib/widgets/Combo.lua
similarity index 100%
rename from src/client/Iris/widgets/Combo.lua
rename to lib/widgets/Combo.lua
diff --git a/src/client/Iris/widgets/Format.lua b/lib/widgets/Format.lua
similarity index 100%
rename from src/client/Iris/widgets/Format.lua
rename to lib/widgets/Format.lua
diff --git a/src/client/Iris/widgets/Input.lua b/lib/widgets/Input.lua
similarity index 100%
rename from src/client/Iris/widgets/Input.lua
rename to lib/widgets/Input.lua
diff --git a/src/client/Iris/widgets/Menu.lua b/lib/widgets/Menu.lua
similarity index 100%
rename from src/client/Iris/widgets/Menu.lua
rename to lib/widgets/Menu.lua
diff --git a/src/client/Iris/widgets/RadioButton.lua b/lib/widgets/RadioButton.lua
similarity index 100%
rename from src/client/Iris/widgets/RadioButton.lua
rename to lib/widgets/RadioButton.lua
diff --git a/src/client/Iris/widgets/Root.lua b/lib/widgets/Root.lua
similarity index 100%
rename from src/client/Iris/widgets/Root.lua
rename to lib/widgets/Root.lua
diff --git a/src/client/Iris/widgets/Table.lua b/lib/widgets/Table.lua
similarity index 100%
rename from src/client/Iris/widgets/Table.lua
rename to lib/widgets/Table.lua
diff --git a/src/client/Iris/widgets/Text.lua b/lib/widgets/Text.lua
similarity index 100%
rename from src/client/Iris/widgets/Text.lua
rename to lib/widgets/Text.lua
diff --git a/src/client/Iris/widgets/Tree.lua b/lib/widgets/Tree.lua
similarity index 100%
rename from src/client/Iris/widgets/Tree.lua
rename to lib/widgets/Tree.lua
diff --git a/src/client/Iris/widgets/Window.lua b/lib/widgets/Window.lua
similarity index 100%
rename from src/client/Iris/widgets/Window.lua
rename to lib/widgets/Window.lua
diff --git a/src/client/Iris/widgets/init.lua b/lib/widgets/init.lua
similarity index 100%
rename from src/client/Iris/widgets/init.lua
rename to lib/widgets/init.lua
diff --git a/model.project.json b/model.project.json
deleted file mode 100644
index b90f981..0000000
--- a/model.project.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "Iris",
- "tree": {
- "$path": "src/client/Iris"
- }
-}
\ No newline at end of file
diff --git a/src/client/helloIris.client.lua b/src/client/helloIris.client.lua
index f424c38..d66c6f1 100644
--- a/src/client/helloIris.client.lua
+++ b/src/client/helloIris.client.lua
@@ -1,4 +1,4 @@
-local StarterPlayerScripts = game.StarterPlayer.StarterPlayerScripts
-local Iris = require(StarterPlayerScripts.Client.Iris).Init()
+local ReplicatedStorage = game:GetService("ReplicatedStorage")
+local Iris = require(ReplicatedStorage.Iris).Init()
Iris:Connect(Iris.ShowDemoWindow)
diff --git a/wally.toml b/wally.toml
index fc886c5..ae5b64b 100644
--- a/wally.toml
+++ b/wally.toml
@@ -10,11 +10,5 @@ realm = "shared"
registry = "https://github.com/upliftgames/wally-index"
exclude = ["**"]
-include = ["src/client/Iris", "src/client/Iris/*", "model.project.json", "wally.toml"]
-private = false
-
-[dependencies]
-
-[server-dependencies]
-
-[dev-dependencies]
+include = ["default.project.json", "lib", "lib/**", "LICENSE.txt", "wally.toml"]
+private = false
\ No newline at end of file