Skip to content

Commit

Permalink
Now you can reset a single setting to its default value and spawn wor…
Browse files Browse the repository at this point in the history
…king correctly
  • Loading branch information
Patitotective committed Dec 9, 2023
1 parent 25c0795 commit 0b00c02
Show file tree
Hide file tree
Showing 6 changed files with 416 additions and 145 deletions.
12 changes: 6 additions & 6 deletions ImExample.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ backend = "cpp"
# Dependencies

requires "nim >= 1.6.2"
requires "kdl >= 1.2.4"
requires "kdl >= 2.0.0"
requires "nimgl >= 1.3.2"
requires "stb_image >= 2.5"
requires "imstyle >= 1.0.0"
requires "openurl >= 2.0.3"
requires "imstyle >= 3.0.0"
requires "openurl >= 2.0.4"
requires "tinydialogs >= 1.0.0"
requires "constructor >= 1.1.4"
requires "constructor >= 1.2.0"

import std/[strformat, options]
import src/types
Expand All @@ -32,7 +32,7 @@ let args = &"--app:gui --out:{outPath} --cpu:{arch} -d:configPath={configPath} {

task buildr, "Build the application for release":
exec "nimble install -d -y"
exec &"nim cpp -d:release {args} main.nim"
exec &"nim c -d:release {args} main.nim"

const desktop = """
[Desktop Entry]
Expand All @@ -54,7 +54,7 @@ task buildapp, "Build the AppImage":
# Compile applicaiton executable
if not existsDir("AppDir"): mkDir("AppDir")
exec "nimble install -d -y"
exec &"nim cpp -d:release -d:appimage {args} --out:AppDir/AppRun main.nim"
exec &"nim c -d:release -d:appimage {args} --out:AppDir/AppRun main.nim"

# Make desktop file
writeFile(
Expand Down
1 change: 1 addition & 0 deletions config.nims
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
switch("backend", "cpp")
switch("warning", "HoleEnumConv:off")
switch("warning", "ImplicitDefaultValue:off")
switch("threads", "on")

when defined(Windows):
switch("passC", "-static")
Expand Down
79 changes: 28 additions & 51 deletions main.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import std/[threadpool, strutils, options, os]
import std/[threadpool, strutils, strformat, os]

import imstyle
import openurl
Expand All @@ -11,17 +11,11 @@ import src/[settingsmodal, utils, types, icons]
when defined(release):
import resources

# TODO be able to reset single preferences to their default value
# TODO save only the settings values in prefs file

proc getConfigDir(app: App): string =
getConfigDir() / app.config.name

proc drawAboutModal(app: App) =
var center: ImVec2
getCenterNonUDT(center.addr, igGetMainViewport())
igSetNextWindowPos(center, Always, igVec2(0.5f, 0.5f))

igSetNextWindowPos(igGetMainViewport().getCenter(), Always, igVec2(0.5f, 0.5f))
let unusedOpen = true # Passing this parameter creates a close button
if igBeginPopupModal(cstring "About " & app.config.name & "###about", unusedOpen.unsafeAddr, flags = makeFlags(ImGuiWindowFlags.NoResize)):
# Display icon image
Expand Down Expand Up @@ -65,22 +59,8 @@ proc drawAboutModal(app: App) =

igEndPopup()

proc drawDialogModal(app: App) =
var center: ImVec2
getCenterNonUDT(center.addr, igGetMainViewport())
igSetNextWindowPos(center, Always, igVec2(0.5f, 0.5f))

if igBeginPopupModal(cstring "External Dialog###dialog", flags = makeFlags(ImGuiWindowFlags.NoResize)):
igText("An external dialog is open, \nclose it to continue using the app.")

# If all spawned threads are finished we can close this popup
if app.checkFlowVarsReady(messageBoxResult):
igCloseCurrentPopup()

igEndPopup()

proc drawMainMenuBar(app: var App) =
var openAbout, openPrefs, openDialog = false
var openAbout, openPrefs, openBlockdialog = false

if igBeginMainMenuBar():
if igBeginMenu("File"):
Expand All @@ -94,7 +74,7 @@ proc drawMainMenuBar(app: var App) =
# If a messageBox hasn't been called or if a called messageBox has already been closed
if app.messageBoxResult.isNil or app.messageBoxResult.isReady():
app.messageBoxResult = spawn messageBox(app.config.name, "Hello, earthling. Wanna come with us?", DialogType.YesNo, IconType.Question, Button.Yes)
openDialog = true
openBlockdialog = true

igEndMenu()

Expand All @@ -110,34 +90,17 @@ proc drawMainMenuBar(app: var App) =

# See https://github.com/ocornut/imgui/issues/331#issuecomment-751372071
if openPrefs:
echo "TODO"
# app.settingsmodal.cache = app.prefs[settings]
initCache(app.prefs[settings])
igOpenPopup("Settings")
if openAbout:
igOpenPopup("###about")
if openDialog:
igOpenPopup("###dialog")
igOpenPopup("###dialog")
type
SettingType* = enum
stInput # Input text
stCheck # Checkbox
stSlider # Int slider
stFSlider # Float slider
stSpin # Int spin
stFSpin # Float spin
stCombo
stRadio # Radio button
stRGB # Color edit RGB
stRGBA # Color edit RGBA
stSection
stFile # File picker
stFiles # Multiple files picker
stFolder # Folder picker
if openBlockdialog:
igOpenPopup("###blockdialog")

# These modals will only get drawn when igOpenPopup(name) are called, respectly
app.drawAboutModal()
app.drawSettingsmodal()
# app.drawBlockDialogModal()

proc drawMain(app: var App) = # Draw the main window
let viewport = igGetMainViewport()
Expand Down Expand Up @@ -166,8 +129,6 @@ proc drawMain(app: var App) = # Draw the main window

igEnd()

app.drawDialogModal()

proc render(app: var App) = # Called in the main loop
# Poll and handle events (inputs, window resize, etc.)
glfwPollEvents() # Use glfwWaitEvents() to only draw on events (more efficient)
Expand Down Expand Up @@ -248,10 +209,23 @@ proc initApp(): App =
when defined(release): "prefs"
else: "prefs_dev"

result.prefs = initKPrefs(
path = (result.getConfigDir() / filename).changeFileExt("kdl"),
default = initPrefs()
)
let path = (result.getConfigDir() / filename).changeFileExt("kdl")

try:
result.prefs = initKPrefs(
path = path,
default = initPrefs()
)
except KdlError:
let m = messageBox(result.config.name, &"Corrupt preferences file {path}.\nYou cannot continue using the app until it is fixed.\nYou may fix it manually or do you want to delete it and reset its content? You cannot undo this action", DialogType.OkCancel, IconType.Error, Button.No)
if m == Button.Yes:
discard tryRemoveFile(path)
result.prefs = initKPrefs(
path = path,
default = initPrefs()
)
else:
raise

template initFonts(app: var App) =
# Merge ForkAwesome icon font
Expand All @@ -277,6 +251,8 @@ template initFonts(app: var App) =
io.fonts.igAddFontFromMemoryTTF(app.res(app.config.iconFontPath), font.size, config.unsafeAddr, iconFontGlyphRanges[0].unsafeAddr)

proc terminate(app: var App) =
sync() # Wait for spawned threads

var x, y, width, height: int32

app.win.getWindowPos(x.addr, y.addr)
Expand Down Expand Up @@ -315,6 +291,7 @@ proc main() =
app.initFonts()

# Main loop
# discard app.win.setWindowCloseCallback(closeCallback(, app.config.name))
while not app.win.windowShouldClose:
app.render()

Expand Down
97 changes: 59 additions & 38 deletions src/settingsmodal.nim
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,24 @@ import nimgl/imgui

import utils, icons, types

proc settingLabel(name: string, setting: Setting[auto]): cstring =
cstring (if setting.display.len == 0: name else: setting.display) & ": "
proc settingLabel(name: string, setting: Setting[auto]): string =
(if setting.display.len == 0: name else: setting.display) & ": "

proc drawSettings(settings: var object, maxLabelWidth: float32): bool =
## Returns wheter or not to open the block dialog (because a file dailog or so was open)

proc drawSettings(settings: var object, maxLabelWidth: float32) =
for name, setting in settings.fieldPairs:
let label = settingLabel(name, setting)
let id = cstring "##" & name
if setting.kind != stSection:
igText(label); igSameLine(0, 0)
if igIsItemHovered() and setting.help.len > 0:
igSetToolTip(cstring setting.help)

igDummy(igVec2(maxLabelWidth - igCalcTextSize(label).x, 0))
igText(cstring label); igSameLine(0, 0)
if igIsItemHovered():
if igIsMouseReleased(ImGuiMouseButton.Right):
igOpenPopup(cstring label)
elif setting.help.len > 0:
igSetToolTip(cstring setting.help)

igDummy(igVec2(maxLabelWidth - igCalcTextSize(cstring label).x, 0))
igSameLine(0, 0)

case setting.kind
Expand Down Expand Up @@ -91,44 +96,60 @@ proc drawSettings(settings: var object, maxLabelWidth: float32) =
of stRGBA:
igColorEdit4(id, setting.rgbaCache, makeFlags(setting.rgbaFlags))
of stFile:
let fileCache =
if setting.fileCache.isNil or not setting.fileCache.isReady:
""
else:
^setting.fileCache
if not setting.fileCache.flowvar.isNil and setting.fileCache.flowvar.isReady and (let val = ^setting.fileCache.flowvar; val.len > 0):
setting.fileCache = (val: val, flowvar: nil) # Here we set flowvar to nil because once we acquire it's value it's not neccessary until it's spawned again

igPushID(id)
igInputTextWithHint("##input", "No file selected", cstring fileCache, uint fileCache.len, flags = ImGuiInputTextFlags.ReadOnly)
igInputTextWithHint("##input", "No file selected", cstring setting.fileCache.val, uint setting.fileCache.val.len, flags = ImGuiInputTextFlags.ReadOnly)
igSameLine()
if (igIsItemHovered(flags = AllowWhenDisabled) and igIsMouseDoubleClicked(ImGuiMouseButton.Left)) or igButton("Browse " & FA_FolderOpen):
setting.fileCache = spawn openFileDialog("Choose File", getCurrentDir() / "\0", setting.fileFilterPatterns, setting.fileSingleFilterDescription)
if igButton("Browse " & FA_FolderOpen):
setting.fileCache.flowvar = spawn openFileDialog("Choose File", getCurrentDir() / "\0", setting.fileFilterPatterns, setting.fileSingleFilterDescription)
result = true
igPopID()
of stFiles:
let files = setting.filesCache.join(",")
if not setting.filesCache.flowvar.isNil and setting.filesCache.flowvar.isReady and (let val = ^setting.filesCache.flowvar; val.len > 0):
setting.filesCache = (val: val, flowvar: nil) # Here we set flowvar to nil because once we acquire it's value it's not neccessary until it's spawned again

let files = setting.filesCache.val.join(";")
igPushID(id)
igInputTextWithHint("##input", "No files selected", cstring files, uint files.len, flags = ImGuiInputTextFlags.ReadOnly)
igSameLine()
if (igIsItemHovered(flags = AllowWhenDisabled) and igIsMouseDoubleClicked(ImGuiMouseButton.Left)) or igButton("Browse " & FA_FolderOpen):
if (let paths = openMultipleFilesDialog("Choose Files", getCurrentDir() / "\0", setting.filesFilterPatterns, setting.filesSingleFilterDescription); paths.len > 0):
setting.filesCache = paths
if igButton("Browse " & FA_FolderOpen):
setting.filesCache.flowvar = spawn openMultipleFilesDialog("Choose Files", getCurrentDir() / "\0", setting.filesFilterPatterns, setting.filesSingleFilterDescription)
result = true
igPopID()
of stFolder:
if not setting.folderCache.flowvar.isNil and setting.folderCache.flowvar.isReady and (let val = ^setting.folderCache.flowvar; val.len > 0):
setting.folderCache = (val: val, flowvar: nil) # Here we set flowvar to nil because once we acquire it's value it's not neccessary until it's spawned again

igPushID(id)
igInputTextWithHint("##input", "No folder selected", cstring setting.folderCache, uint setting.folderCache.len, flags = ImGuiInputTextFlags.ReadOnly)
igInputTextWithHint("##input", "No folder selected", cstring setting.folderCache.val, uint setting.folderCache.val.len, flags = ImGuiInputTextFlags.ReadOnly)
igSameLine()
if (igIsItemHovered(flags = AllowWhenDisabled) and igIsMouseDoubleClicked(ImGuiMouseButton.Left)) or igButton("Browse " & FA_FolderOpen):
if (let path = selectFolderDialog("Choose Folder", getCurrentDir() / "\0"); path.len > 0):
setting.folderCache = path
if igButton("Browse " & FA_FolderOpen):
setting.folderCache.flowvar = spawn selectFolderDialog("Choose Folder", getCurrentDir() / "\0")
result = true
igPopID()
of stSection:
igPushID(id)
if igCollapsingHeader(label, makeFlags(setting.sectionFlags)):
igIndent()
if igCollapsingHeader(cstring label, makeFlags(setting.sectionFlags)):
if igIsItemHovered():
if igIsMouseReleased(ImGuiMouseButton.Right):
igOpenPopup(cstring label)

igPushID(id); igIndent()
when setting.content is object:
drawSettings(setting.content, maxLabelWidth)
igUnindent()
igPopID()
result = drawSettings(setting.content, maxLabelWidth)
igUnindent(); igPopID()
else: # When the header is closed
if igIsItemHovered():
if igIsMouseReleased(ImGuiMouseButton.Right):
igOpenPopup(cstring label)

if igBeginPopup(cstring label):
if igSelectable(cstring("Reset " & label[0..^3] #[remove the ": "]# & " to default")):
setting.cacheToDefault()
igEndPopup()

if setting.help.len > 0:
if setting.help.len > 0 and setting.kind != stSection:
igSameLine()
igHelpMarker(setting.help)

Expand All @@ -144,7 +165,7 @@ proc calcMaxLabelWidth(settings: object): float32 =
calcMaxLabelWidth(setting.content)
else: 0f
else:
igCalcTextSize(label).x
igCalcTextSize(cstring label).x
if width > result:
result = width
else:
Expand All @@ -159,21 +180,21 @@ proc drawSettingsmodal*(app: var App) =
if igBeginPopupModal("Settings", flags = makeFlags(AlwaysAutoResize, HorizontalScrollbar)):
var close = false

# app.settingsmodal.cache must be set to app.prefs[settings] once when opening the modal
drawSettings(app.prefs[settings], app.maxLabelWidth)
if drawSettings(app.prefs[settings], app.maxLabelWidth):
igOpenPopup("###blockdialog")

app.drawBlockDialogModal()

igSpacing()

if igButton("Save"):
echo "TODO: save"
# app.prefs[settings] = app.settingsmodal.cache
app.prefs[settings].save()
igCloseCurrentPopup()

igSameLine()

if igButton("Cancel"):
echo "TODO: cache"
# app.settingsmodal.cache = app.prefs[settings]
initCache(app.prefs[settings])
igCloseCurrentPopup()

igSameLine()
Expand Down
Loading

0 comments on commit 0b00c02

Please sign in to comment.