Very early prototype of a Lua Only ImGui Library for DCS World. This lets you create complex GUI's for DCS world from only lua devices!
See examples below
Submodules organize this repository into a different git repository so you can easily update and build. This is the suggested install, but requires a git repository already, if you aren't using git (you should) use the standalone instructions.
- Open a git terminal from the root of the project (where the Cockpit folder is).
git submodule add "https://github.com/08jne01/dcs-lua-imgui.git" Cockpit/Scripts/LuaImGuigit submodule update --init --recursive
git clone "https://github.com/08jne01/dcs-lua-imgui.git" LuaImGuicd LuaImGuigit submodule update --init --recursive
- Download Zip from Releases
- Copy LuaImGui folder to Cockpit/Scripts
Requires cmake, ninja and VS Toolchain (usually all included with Visual Studio Install).
-
Open resulting folder in IDE/Command Line of choice, some examples:
- Visual Studio:
Project->Configure LuaImGui- Select
LuaImGui.dllfrom play button dropdown. - Select Release instead of Debug
- Build->Build ALL
- VSCode -> with C++ and Cmake extensions:
- In the command pallet run
CMake: Configure - Select x64-release
- In the command pallet run
CMake: Build
- In the command pallet run
- Command Line - CMake with Ninja (VS Developer Command Prompt):
cmake -G "Ninja" .cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=cl.exe -DCMAKE_C_FLAGS="/MACHINE:X64" -DCMAKE_CXX_FLAGS="/MACHINE:X64" -DCMAKE_CXX_COMPILER=cl.exe -DCMAKE_INSTALL_PREFIX=./dcs-lua-imgui/out/install/x64-release -S. -B./out/build/x64-release -G Ninjacmake --build out/build/x64-release --parallel 30
- Visual Studio:
-
Copy LuaImGui folder to Cockpit/Scripts (if it isn't there)
LuaImGui code execution can be disabled by turning off the menu bar however the direct-x trampolines are still setup and executing. Since LuaImGui is new and the multithreaded nature of DCS can make it not stable with LuaImGui. It's best to disable it completely in production.
This is done by setting the global imgui_disabled to true before calling require on the ImGui.lua.
One useful pattern which is not included by default is a separate ImGui.lua folder which acts as a proxy.
Cockpit/Scripts:
- ConfigurePackage.lua
- ImGui.lua
- LuaImGui:
- ImGui.luaYou can then have the following arrangement:
ConfigurePackage.lua
-- This allows the use of require statements. require uses . instead of / for folder sepparators.
-- Require only loads the lua once saving loading time.
package.path = package.path..";"..LockOn_Options.script_path.."?.lua"
package.path = package.path..";"..LockOn_Options.common_script_path.."?.lua"ImGui.lua
imgui_disabled = false
require( "LuaImGui.ImGui" )Then instead of calling require for the ImGui.lua in LuaImGui folder we call the one in the Cockpit/Scripts. Then the setting imgui_disabled is the same for all instances where ImGui.lua is required. See below an example of how you would now require ImGui.lua.
ExampleDevice.lua
dofile(LockOn_Options.script_path.."ConfigurePackage.lua")
require("ImGui")
-- codeThe really nice thing about this is you can then get the benefits of require by simply calling dofile once for the ConfigurePackage.lua and requiring all other lua files. See below for a better description of the require function.
For aircraft mods you will want to install the LuaImGui folder in Cockpit/Scripts however you may want to change the location (for example export or mission lua contexts). To do this you set the global lua_imgui_path to the path to the LuaImGui folder before calling require on the ImGui.lua.
The path includes the LuaImGui folder itself, for example the default lua_imgui_path (if one is not set) is set to LockOn_Options.script_path.."LuaImGui" which resolves to <aircraft directory>/Cockpit/Scripts/LuaImGui.
- Creating Windows
- Immediate Drawing
- Input
- Control Statements
- Enable/Disable Menu Bar
- Console
- Plotting
- Utility
To draw the imgui you need to add items to the imgui context and you need to call Refresh to update the imgui windows. See below.
-- Set the package path to be the script path. This lets you use require
-- statements for any of your lua files which is better than dofile() because
-- that executes every dofile is called.
-- With require join path folders with a .
-- Some.Path.To.File
-- Will result in Some/Path/To/File.lua being loaded.
-- See require docs here https://www.lua.org/pil/8.1.html
package.path = package.path..";"..LockOn_Options.script_path.."?.lua"
require("LuaImGui.ImGui")
-- Menu Name is button in the bar across the top.
-- Menu Entry name is an entry in that menu.
-- Menus are created automatically as items are assigned to them.
-- Menu Entries are not unique so you can have multiple of the same name,
-- it may result in strange behaviour though.
ImGui.AddItem("Menu", "Menu Entry Name", function()
-- Code goes here
end)
-- ImGui.Refresh() needs to be called in every lua state (device).
-- Every time Refresh is called imgui window will update.
function update()
ImGui.Refresh()
end-- This simply prints Text! to the imgui window
-- Any variable passed to ImGui:Text will automatically
-- be converted to a string using the tostring method.
ImGui:Text("Text!")-- Outside the AddItem code
local some_state = {
hello = "world"
}
ImGui.AddItem("Menu Name", "Menu Entry Name", function()
-- You can capture state.
-- This will show
-- {
-- hello = "world"
-- }
-- Here ImGui.Serialize is a helper function
-- which converts tables and their children to strings.
local s = ImGui.Serialize(some_state)
ImGui:Text(s)
end)-- You can write Tables (not lua tables) to organize your data.
-- The first row is the header this determins the number of columns
-- for the rest of the table, so be sure to make sure it is at least
-- more than the other rows.
ImGui:Table({
{ "Name", "Speed (kts)", "Mass (kg)" },
{ "A-4E", 585, 4469 },
{ "F-100D", 803, 9525 },
{ "Sopwith Camel" }, -- You don't have to have the same number of Columns
})If you don't have the same number of columns as the header the empty ones will be filled with nil.
All these inputs take a value and returns the changed value if input is made.
-- Only need to supply the label for the button.
-- It will return true below the frame after the button is pressed - only one time.
if ImGui:Button("Press This!") then
-- Code in here is ran once only every time a button is pressed
ImGui.Log("Button was Pressed!")
endlocal speed = 0.1 -- speed at which the drag float which change value when dragged.
-- optionally you can supply min and max
value = ImGui:DragFloat("Drag Float Label", value, speed)local step = 0.1 -- size of step for + and - buttons
value = ImGui:InputFloat("Input Float Label", value, step)-- options you wish to choose from
local options = {
"option-a",
"option-b",
"option-c",
}
-- selected option starts at 1 (like lua indices)
selected_option = ImGui:ListBox("List Box Label", selected_option, options)Any ImGui functions which control flow will take a function this is because DCS is multithreaded so LuaImGui has to build a set of commands to send to the Render Thread.
This has the side-effect of requiring that all code within the imgui statements to be executed. So any control flow functions that take a function will execute that function regardless of the state of the control flow.
These statements can be combined recursively and with the Immediate Drawing imgui commands. To create complex graphical structures.
Most control flow functions share the below model.
-- s is usually a string unique (to the current scope)
-- f is a function which takes no parameters
function ImGui:Something(s, f)
ImGui:BeginSomething(s)
f()
ImGui:EndSomething()
endSince it is easy to pass anonymous function around it makes the syntax easy and similar to normal ImGui. See tree below for an example.
Tree Closed
Tree Open
Tree's can be combined recursively (like an imgui element) to make a complex structure.
-- Openable Menu with Indent - You can recursively combine these to make complex structures.
ImGui:Tree("Some Tree", function()
ImGui:Text("Some Hidden Text")
end)This produces an openable menu but unlike Tree there is no indent.
Header Closed
Header Open
-- Open-able Menu without Indent.
ImGui:Header("Some Collapsable Header", function()
ImGui:Text("Some More Hidden Text")
end)This produces a menu with multiple tabs where one tab is displayed at a time depending on what the user selects. Other ImGui elements can be put inside like other control statements allowing for creating complex recursive structures.
Tab 1 Selected
Tab 2 Selected
ImGui:TabBar("Some Tabs", function()
for i=1,5 do
ImGui:TabItem(string.format("Tab %d", i), function()
ImGui:Text(string.format("This is a tab: %d", i))
end)
end
end)ImGui:Window lets you create a floating window from the current window. It will only show if the control code it is in is active (ie tree/header open or window is open). This lets you build complex pop outs which depend on other windows.
ImGui:Header("Popout Window", function()
ImGui:Text("Window Popped Out!")
ImGui:Window("Window!", function()
ImGui:Text("This is a window...")
end)
end)-- This allows the menu bar to be disabled. It also disables all code execution.
-- This would set the menu bar off.
-- Note the . not :
local is_on = false
ImGui.MenuBar(is_on)A console window where you can print messages. You can also clear, use auto-scroll and look at the history.
-- Similar to print_message_to_user except puts it in a neat window you can scroll back up.
-- Note the . not :
ImGui.Log("Message 1")
ImGui.Log("Message 2")
ImGui.Log("Message 3")Plotting is very simple at the moment more features will be added later. Currently it supports:
- Line -
PlotLine - Horizontal Lines -
PlotHLines - Vertical Lines -
PlotVLines
local dx = 1.0 -- space between points
local y_data = {1,2,3,4,5,6,7,8,9} -- y points
local v_lines = { 3.0, 6.0 } -- x coordinates
local h_lines = { 4.0, 8.0 } -- y coordinates
ImGui.AddItem("Plot", "Test Graph", function()
-- Plot is required in order to draw lines.
ImGui:Plot("Plot Name", "x-axis label", "y-axis label", 800, function()
ImGui:PlotHLines("H-Lines", h_lines) -- horizontal lines will be plotted at y values
ImGui:PlotVLines("V-Lines", v_lines) -- vertical lines will be plotted at x values
-- You can have multiple PlotLine
ImGui:PlotLine("Line", dx, y_data) -- line will be plotted with y_data with dx spacing between points
end)
end)This function converts any table (and its metatable) and all its members into a string similar to how a table is defined in lua (not exactly the same). This makes it easy to inspect lua datastructures.
-- note .
-- not :
local s = ImGui.Serialize({
plane = "A-4E",
planet = "Earth",
another_table = {
hello = 5,
world = "something"
}
})
ImGui:Text(s)

















