Skip to content

Commit 0b00c02

Browse files
committed
Now you can reset a single setting to its default value and spawn working correctly
1 parent 25c0795 commit 0b00c02

File tree

6 files changed

+416
-145
lines changed

6 files changed

+416
-145
lines changed

ImExample.nimble

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ backend = "cpp"
88
# Dependencies
99

1010
requires "nim >= 1.6.2"
11-
requires "kdl >= 1.2.4"
11+
requires "kdl >= 2.0.0"
1212
requires "nimgl >= 1.3.2"
1313
requires "stb_image >= 2.5"
14-
requires "imstyle >= 1.0.0"
15-
requires "openurl >= 2.0.3"
14+
requires "imstyle >= 3.0.0"
15+
requires "openurl >= 2.0.4"
1616
requires "tinydialogs >= 1.0.0"
17-
requires "constructor >= 1.1.4"
17+
requires "constructor >= 1.2.0"
1818

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

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

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

5959
# Make desktop file
6060
writeFile(

config.nims

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
switch("backend", "cpp")
22
switch("warning", "HoleEnumConv:off")
33
switch("warning", "ImplicitDefaultValue:off")
4+
switch("threads", "on")
45

56
when defined(Windows):
67
switch("passC", "-static")

main.nim

Lines changed: 28 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import std/[threadpool, strutils, options, os]
1+
import std/[threadpool, strutils, strformat, os]
22

33
import imstyle
44
import openurl
@@ -11,17 +11,11 @@ import src/[settingsmodal, utils, types, icons]
1111
when defined(release):
1212
import resources
1313

14-
# TODO be able to reset single preferences to their default value
15-
# TODO save only the settings values in prefs file
16-
1714
proc getConfigDir(app: App): string =
1815
getConfigDir() / app.config.name
1916

2017
proc drawAboutModal(app: App) =
21-
var center: ImVec2
22-
getCenterNonUDT(center.addr, igGetMainViewport())
23-
igSetNextWindowPos(center, Always, igVec2(0.5f, 0.5f))
24-
18+
igSetNextWindowPos(igGetMainViewport().getCenter(), Always, igVec2(0.5f, 0.5f))
2519
let unusedOpen = true # Passing this parameter creates a close button
2620
if igBeginPopupModal(cstring "About " & app.config.name & "###about", unusedOpen.unsafeAddr, flags = makeFlags(ImGuiWindowFlags.NoResize)):
2721
# Display icon image
@@ -65,22 +59,8 @@ proc drawAboutModal(app: App) =
6559
6660
igEndPopup()
6761
68-
proc drawDialogModal(app: App) =
69-
var center: ImVec2
70-
getCenterNonUDT(center.addr, igGetMainViewport())
71-
igSetNextWindowPos(center, Always, igVec2(0.5f, 0.5f))
72-
73-
if igBeginPopupModal(cstring "External Dialog###dialog", flags = makeFlags(ImGuiWindowFlags.NoResize)):
74-
igText("An external dialog is open, \nclose it to continue using the app.")
75-
76-
# If all spawned threads are finished we can close this popup
77-
if app.checkFlowVarsReady(messageBoxResult):
78-
igCloseCurrentPopup()
79-
80-
igEndPopup()
81-
8262
proc drawMainMenuBar(app: var App) =
83-
var openAbout, openPrefs, openDialog = false
63+
var openAbout, openPrefs, openBlockdialog = false
8464
8565
if igBeginMainMenuBar():
8666
if igBeginMenu("File"):
@@ -94,7 +74,7 @@ proc drawMainMenuBar(app: var App) =
9474
# If a messageBox hasn't been called or if a called messageBox has already been closed
9575
if app.messageBoxResult.isNil or app.messageBoxResult.isReady():
9676
app.messageBoxResult = spawn messageBox(app.config.name, "Hello, earthling. Wanna come with us?", DialogType.YesNo, IconType.Question, Button.Yes)
97-
openDialog = true
77+
openBlockdialog = true
9878
9979
igEndMenu()
10080
@@ -110,34 +90,17 @@ proc drawMainMenuBar(app: var App) =
11090
11191
# See https://github.com/ocornut/imgui/issues/331#issuecomment-751372071
11292
if openPrefs:
113-
echo "TODO"
114-
# app.settingsmodal.cache = app.prefs[settings]
93+
initCache(app.prefs[settings])
11594
igOpenPopup("Settings")
11695
if openAbout:
11796
igOpenPopup("###about")
118-
if openDialog:
119-
igOpenPopup("###dialog")
120-
igOpenPopup("###dialog")
121-
type
122-
SettingType* = enum
123-
stInput # Input text
124-
stCheck # Checkbox
125-
stSlider # Int slider
126-
stFSlider # Float slider
127-
stSpin # Int spin
128-
stFSpin # Float spin
129-
stCombo
130-
stRadio # Radio button
131-
stRGB # Color edit RGB
132-
stRGBA # Color edit RGBA
133-
stSection
134-
stFile # File picker
135-
stFiles # Multiple files picker
136-
stFolder # Folder picker
97+
if openBlockdialog:
98+
igOpenPopup("###blockdialog")
13799
138100
# These modals will only get drawn when igOpenPopup(name) are called, respectly
139101
app.drawAboutModal()
140102
app.drawSettingsmodal()
103+
# app.drawBlockDialogModal()
141104
142105
proc drawMain(app: var App) = # Draw the main window
143106
let viewport = igGetMainViewport()
@@ -166,8 +129,6 @@ proc drawMain(app: var App) = # Draw the main window
166129

167130
igEnd()
168131

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

251-
result.prefs = initKPrefs(
252-
path = (result.getConfigDir() / filename).changeFileExt("kdl"),
253-
default = initPrefs()
254-
)
212+
let path = (result.getConfigDir() / filename).changeFileExt("kdl")
213+
214+
try:
215+
result.prefs = initKPrefs(
216+
path = path,
217+
default = initPrefs()
218+
)
219+
except KdlError:
220+
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)
221+
if m == Button.Yes:
222+
discard tryRemoveFile(path)
223+
result.prefs = initKPrefs(
224+
path = path,
225+
default = initPrefs()
226+
)
227+
else:
228+
raise
255229

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

279253
proc terminate(app: var App) =
254+
sync() # Wait for spawned threads
255+
280256
var x, y, width, height: int32
281257

282258
app.win.getWindowPos(x.addr, y.addr)
@@ -315,6 +291,7 @@ proc main() =
315291
app.initFonts()
316292

317293
# Main loop
294+
# discard app.win.setWindowCloseCallback(closeCallback(, app.config.name))
318295
while not app.win.windowShouldClose:
319296
app.render()
320297

src/settingsmodal.nim

Lines changed: 59 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,24 @@ import nimgl/imgui
66

77
import utils, icons, types
88

9-
proc settingLabel(name: string, setting: Setting[auto]): cstring =
10-
cstring (if setting.display.len == 0: name else: setting.display) & ": "
9+
proc settingLabel(name: string, setting: Setting[auto]): string =
10+
(if setting.display.len == 0: name else: setting.display) & ": "
11+
12+
proc drawSettings(settings: var object, maxLabelWidth: float32): bool =
13+
## Returns wheter or not to open the block dialog (because a file dailog or so was open)
1114

12-
proc drawSettings(settings: var object, maxLabelWidth: float32) =
1315
for name, setting in settings.fieldPairs:
1416
let label = settingLabel(name, setting)
1517
let id = cstring "##" & name
1618
if setting.kind != stSection:
17-
igText(label); igSameLine(0, 0)
18-
if igIsItemHovered() and setting.help.len > 0:
19-
igSetToolTip(cstring setting.help)
20-
21-
igDummy(igVec2(maxLabelWidth - igCalcTextSize(label).x, 0))
19+
igText(cstring label); igSameLine(0, 0)
20+
if igIsItemHovered():
21+
if igIsMouseReleased(ImGuiMouseButton.Right):
22+
igOpenPopup(cstring label)
23+
elif setting.help.len > 0:
24+
igSetToolTip(cstring setting.help)
25+
26+
igDummy(igVec2(maxLabelWidth - igCalcTextSize(cstring label).x, 0))
2227
igSameLine(0, 0)
2328

2429
case setting.kind
@@ -91,44 +96,60 @@ proc drawSettings(settings: var object, maxLabelWidth: float32) =
9196
of stRGBA:
9297
igColorEdit4(id, setting.rgbaCache, makeFlags(setting.rgbaFlags))
9398
of stFile:
94-
let fileCache =
95-
if setting.fileCache.isNil or not setting.fileCache.isReady:
96-
""
97-
else:
98-
^setting.fileCache
99+
if not setting.fileCache.flowvar.isNil and setting.fileCache.flowvar.isReady and (let val = ^setting.fileCache.flowvar; val.len > 0):
100+
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
101+
99102
igPushID(id)
100-
igInputTextWithHint("##input", "No file selected", cstring fileCache, uint fileCache.len, flags = ImGuiInputTextFlags.ReadOnly)
103+
igInputTextWithHint("##input", "No file selected", cstring setting.fileCache.val, uint setting.fileCache.val.len, flags = ImGuiInputTextFlags.ReadOnly)
101104
igSameLine()
102-
if (igIsItemHovered(flags = AllowWhenDisabled) and igIsMouseDoubleClicked(ImGuiMouseButton.Left)) or igButton("Browse " & FA_FolderOpen):
103-
setting.fileCache = spawn openFileDialog("Choose File", getCurrentDir() / "\0", setting.fileFilterPatterns, setting.fileSingleFilterDescription)
105+
if igButton("Browse " & FA_FolderOpen):
106+
setting.fileCache.flowvar = spawn openFileDialog("Choose File", getCurrentDir() / "\0", setting.fileFilterPatterns, setting.fileSingleFilterDescription)
107+
result = true
104108
igPopID()
105109
of stFiles:
106-
let files = setting.filesCache.join(",")
110+
if not setting.filesCache.flowvar.isNil and setting.filesCache.flowvar.isReady and (let val = ^setting.filesCache.flowvar; val.len > 0):
111+
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
112+
113+
let files = setting.filesCache.val.join(";")
107114
igPushID(id)
108115
igInputTextWithHint("##input", "No files selected", cstring files, uint files.len, flags = ImGuiInputTextFlags.ReadOnly)
109116
igSameLine()
110-
if (igIsItemHovered(flags = AllowWhenDisabled) and igIsMouseDoubleClicked(ImGuiMouseButton.Left)) or igButton("Browse " & FA_FolderOpen):
111-
if (let paths = openMultipleFilesDialog("Choose Files", getCurrentDir() / "\0", setting.filesFilterPatterns, setting.filesSingleFilterDescription); paths.len > 0):
112-
setting.filesCache = paths
117+
if igButton("Browse " & FA_FolderOpen):
118+
setting.filesCache.flowvar = spawn openMultipleFilesDialog("Choose Files", getCurrentDir() / "\0", setting.filesFilterPatterns, setting.filesSingleFilterDescription)
119+
result = true
113120
igPopID()
114121
of stFolder:
122+
if not setting.folderCache.flowvar.isNil and setting.folderCache.flowvar.isReady and (let val = ^setting.folderCache.flowvar; val.len > 0):
123+
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
124+
115125
igPushID(id)
116-
igInputTextWithHint("##input", "No folder selected", cstring setting.folderCache, uint setting.folderCache.len, flags = ImGuiInputTextFlags.ReadOnly)
126+
igInputTextWithHint("##input", "No folder selected", cstring setting.folderCache.val, uint setting.folderCache.val.len, flags = ImGuiInputTextFlags.ReadOnly)
117127
igSameLine()
118-
if (igIsItemHovered(flags = AllowWhenDisabled) and igIsMouseDoubleClicked(ImGuiMouseButton.Left)) or igButton("Browse " & FA_FolderOpen):
119-
if (let path = selectFolderDialog("Choose Folder", getCurrentDir() / "\0"); path.len > 0):
120-
setting.folderCache = path
128+
if igButton("Browse " & FA_FolderOpen):
129+
setting.folderCache.flowvar = spawn selectFolderDialog("Choose Folder", getCurrentDir() / "\0")
130+
result = true
121131
igPopID()
122132
of stSection:
123-
igPushID(id)
124-
if igCollapsingHeader(label, makeFlags(setting.sectionFlags)):
125-
igIndent()
133+
if igCollapsingHeader(cstring label, makeFlags(setting.sectionFlags)):
134+
if igIsItemHovered():
135+
if igIsMouseReleased(ImGuiMouseButton.Right):
136+
igOpenPopup(cstring label)
137+
138+
igPushID(id); igIndent()
126139
when setting.content is object:
127-
drawSettings(setting.content, maxLabelWidth)
128-
igUnindent()
129-
igPopID()
140+
result = drawSettings(setting.content, maxLabelWidth)
141+
igUnindent(); igPopID()
142+
else: # When the header is closed
143+
if igIsItemHovered():
144+
if igIsMouseReleased(ImGuiMouseButton.Right):
145+
igOpenPopup(cstring label)
146+
147+
if igBeginPopup(cstring label):
148+
if igSelectable(cstring("Reset " & label[0..^3] #[remove the ": "]# & " to default")):
149+
setting.cacheToDefault()
150+
igEndPopup()
130151

131-
if setting.help.len > 0:
152+
if setting.help.len > 0 and setting.kind != stSection:
132153
igSameLine()
133154
igHelpMarker(setting.help)
134155

@@ -144,7 +165,7 @@ proc calcMaxLabelWidth(settings: object): float32 =
144165
calcMaxLabelWidth(setting.content)
145166
else: 0f
146167
else:
147-
igCalcTextSize(label).x
168+
igCalcTextSize(cstring label).x
148169
if width > result:
149170
result = width
150171
else:
@@ -159,21 +180,21 @@ proc drawSettingsmodal*(app: var App) =
159180
if igBeginPopupModal("Settings", flags = makeFlags(AlwaysAutoResize, HorizontalScrollbar)):
160181
var close = false
161182

162-
# app.settingsmodal.cache must be set to app.prefs[settings] once when opening the modal
163-
drawSettings(app.prefs[settings], app.maxLabelWidth)
183+
if drawSettings(app.prefs[settings], app.maxLabelWidth):
184+
igOpenPopup("###blockdialog")
185+
186+
app.drawBlockDialogModal()
164187

165188
igSpacing()
166189

167190
if igButton("Save"):
168-
echo "TODO: save"
169-
# app.prefs[settings] = app.settingsmodal.cache
191+
app.prefs[settings].save()
170192
igCloseCurrentPopup()
171193

172194
igSameLine()
173195

174196
if igButton("Cancel"):
175-
echo "TODO: cache"
176-
# app.settingsmodal.cache = app.prefs[settings]
197+
initCache(app.prefs[settings])
177198
igCloseCurrentPopup()
178199

179200
igSameLine()

0 commit comments

Comments
 (0)