Template for making a single-windowed Dear ImGui application in Nim.
(Check ImDemo for a full example)
- Icon font support.
- About modal.
- Preferences system.
- Settings modal.
- AppImage support (Linux).
- Updateable AppImage support (with gh-releases-zsync).
- Simple data resources support (embed files into the binary).
- GitHub workflow for building and uploading the AppImage and
.exe
as assets. - Non-blocking (using
std/threadpool
) native system dialogs usingtinydialogs
.
(To use NimGL in Ubuntu you might need to install some libraries sudo apt install libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev libgl-dev
)
README.md
: Project's description.LICENSE
: Project's license.main.nim
: Application's logic.resources.nim
: To bundle data resources (see Bundling).config.nims
: Nim compile configuration.ImExample.nimble
: Nimble file.assets
:icon.png
,icon.svg
: App icons.style.kdl
: Style (using ImStyle).Cousine-Regular.ttf
,Karla-Regular.ttf
,Roboto-Regular.ttf
,ProggyVector Regular.ttf
: Multiple fonts so you can choose the one you like the most.forkawesome-webfont.ttf
: ForkAwesome icon font (see https://forkaweso.me/).
src
:types.nim
: Type definitions used by other modules.icons.nim
: Helper module with ForkAwesome icons unicode points.utils.nim
: Useful procedures, general types or anything used by more than one module.settingsmodal.nim
: Draw the settings modal
ImTemplate uses ForkAwesome's icon font to be able to display icon in labels, to do it you only need to import icons.nim
(where the unicode points for each icon are defined), browse https://forkaweso.me/Fork-Awesome/icons, choose the one you want and, for example, if you want to use fa-floppy-o
, you will write FA_FloppyO
in a string:
...
# main.nim
import src/icons
if igButton("Open Link " & FA_ExternalLink):
openURL("https://forkaweso.me")
The code is designed to rely on the App
type (defined in utils.nim
), you may want to store anything that your program needs inside it.
type
App* = object
win*: GLFWWindow
config*: Config
prefs*: KdlPrefs[Prefs]
fonts*: array[Config.fonts.len, ptr ImFont]
resources*: Table[string, string]
maxLabelWidth*: float32
messageBoxResult*: FlowVar[Button]
# Add your variables here
...
win
: GLFW window.fonts
: An array containing the loaded fonts fromConfig.fonts
.prefs
: See Prefs.config
: Configuration file (loaded fromconfig.toml
).resources
: Data resources where the key is the filename and the value is the binary data.maxLabelWidth
: This is a value that's used to draw the settingsmodal (see https://github.com/Patitotective/ImTemplate/blob/main/src/settingsmodal.nim)messageBoxResult
: This variable stores the result to a message box dialog opened bytinydialogs
, it uses theFlowVar
type since it's the result of a spawned thread.
The configuration stores data like name and version of the application, it is stored in its type definition in src/configtype.nim
using constructor/defaults
to define the default values:
type
Config* = object
name* = "ImExample"
comment* = "ImExample is a simple Dear ImGui application example"
version* = "2.0.0"
website* = "https://github.com/Patitotective/ImTemplate"
authors* = [
(name: "Patitotective", url: "https://github.com/Patitotective"),
("Cristobal", "mailto:[email protected]"),
("Omar Cornut", "https://github.com/ocornut"),
("Beef, Yard, Rika", ""),
("and the Nim community :]", ""),
("Inu147", ""),
]
categories* = "Utility"
stylePath* = "assets/style.kdl"
iconPath* = "assets/icon.png"
svgIconPath* = "assets/icon.svg"
iconFontPath* = "assets/forkawesome-webfont.ttf"
fonts* = [
font("assets/ProggyVector Regular.ttf", 16f), # Other options are Roboto-Regular.ttf, Cousine-Regular.ttf or Karla-Regular.ttf
font("assets/NotoSansJP-Regular.otf", 16f, GlyphRanges.Japanese),
]
# AppImage
ghRepo* = (user: "Patitotective", repo: "ImTemplate").some
appstreamPath* = ""
# Window
minSize* = (w: 200i32, h: 200i32) # < 0: don't care
name
: App's name.comment
: App's description.version
: App's version.website
: A link where you can find more information about the app.authors
: An array containing information about the authors.categories
: Sequence of registered categories (for the AppImage).stylePath
: App's ImStyle path (using https://github.com/Patitotective/ImStyle).iconPath
: PNG icon path.svgIconPath
: Scalable icon pathiconFontPath
: ForkAwesome's font path.fonts
: An array ofFont
objects containing the font's path, size and range of glyphs for japanese, korean, chinese, etc.ghRepo
: GitHub repo to fetch releases from (if it's some it will generate anAppImage.zsync
file, include it in your releases for AppImage updates).appstreamPath
: Path to the AppStream metadata.minSize
: Window's minimum size, use numbers less than zero to disable a limit.
Using the information from the config object, ImTemplate creates a simple about modal.
The preferences is the data that can change during runtime and that data that you want to store for the future like the position and size of the window, this includes the settings the user can change like the language and theme. The preferences are saved in a KDL file (using kdl/prefs). You just have to provide an object including all the data you want to store as fields:
type
Prefs* {.defaults: {defExported}.} = object
maximized* = false # Was the window maximized when the app was closed?
winpos* = (x: -1i32, y: -1i32) # Window position
winsize* = (w: 600i32, h: 650i32) # Window size
settings* = initSettings()
The settings are preferences that the user can modify through the settings modal.
You can define all the settings' settings (i.e.: combobox, checkbox, input, etc.) through the Settings
object:
type
Os* {.defaults: {}.} = object
file* = fileSetting(display = "Text File", filterPatterns = @["*.txt", "*.nim", "*.kdl", "*.json"])
files* = filesSetting(display = "Multiple files", singleFilterDescription = "Anything", default = @[".bashrc", ".profile"])
folder* = folderSetting(display = "Folder")
Numbers* {.defaults: {}.} = object
spin* = spinSetting(display = "Int Spinner", default = 4, range = 0i32..10i32)
fspin* = fspinSetting(display = "Float Spinner", default = 3.14, range = 0f..10f)
slider* = sliderSetting(display = "Int Slider", default = 40, range = -100i32..100i32)
fslider* = fsliderSetting(display = "Float Slider", default = -2.5, range = -10f..10f)
Colors* {.defaults: {}.} = object
rgb* = rgbSetting(default = [1f, 0f, 0.2f])
rgba* = rgbaSetting(default = [0.4f, 0.7f, 0f, 0.5f], flags = @[AlphaBar, AlphaPreviewHalf])
Sizes* = enum
None, Huge, Big, Medium, Small, Mini
Settings* {.defaults: {}.} = object
input* = inputSetting(display = "Input", default = "Hello World")
input2* = inputSetting(
display = "Custom Input", hint = "Type...",
help = "Has a hint, 10 characters maximum and only accepts on return",
limits = 0..10, flags = @[ImGuiInputTextFlags.EnterReturnsTrue]
)
check* = checkSetting(display = "Checkbox", default = true)
combo* = comboSetting(display = "Combo box", items = Sizes.toSeq, default = None)
radio* = radioSetting(display = "Radio button", items = @[Big, Medium, Small], default = Medium)
os* = sectionSetting(display = "File dialogs", help = "Single file, multiple files and folder pickers", content = initOs())
numbers* = sectionSetting(display = "Spinners and sliders", content = initNumbers())
colors* = sectionSetting(display = "Color pickers", content = initColors())
To build your app you may want to run nimble buildr
task.
You can set the following environment variables to change the building process:
ARCH
: the architecture used to compile the binary, by defaultamd64
.OUTPATH
: the path of the binary file (or exe file on Windows), by default "name-version-arch"FLAGS
: any other flags you want to pass to the compiler, optional.
Note: Unfortunately on Window most of the times Nim binaries are flagged as virus, see nim-lang/Nim#17820.
To bundle your app resources inside the compiled binary, you only need to go to resources.nim
file and define their paths in the resourcesPaths
array.
After that resources
is imported in main.nim
. So when you compile it, it statically reads those files and creates a table with the binary data.
To access them use app.resources["path"]
.
By default this is how resourcesPaths
looks like:
...
const resourcesPaths = @[
config.stylePath,
config.iconPath,
config.iconFontPath,
] & config.fonts.mapIt(it.path) # Add the paths of each font
...
You can publish your application as a binary package with nimble.
To build your app as an AppImage you will need to run nimble buildapp
, it will install the dependencies, compile the app, check for appimagetool
(and install it if its not found in the $PATH
), generate the AppDir
directory and finally build the AppImage.
If you included ghRepo
in the config, it will also generate an AppImage.zsync
file. You should attach this file along with the AppImage
to your GitHub release.
If you included appstreamPath
, it will get copied to AppDir/usr/share/shareinfo/{config.name}.appdata.xml
(see https://docs.appimage.org/packaging-guide/optional/appstream.html).
ImTemplate has a release.yml
workflow that automatically when you publish a release, builds an AppImage and an .exe
file to then upload them as assets to the release.
This can take several minutes.
Apps using this template:
(Contact me if you want your app to be added here).
- Icon Font: https://forkaweso.me (MIT).
- GitHub: https://github.com/Patitotective/ImTemplate.
- Discord: https://discord.gg/U23ZQMsvwc.
Contact me:
- Discord: Patitotective#0127.
- Twitter: @patitotective.
- Email: [email protected].