@@ -62,8 +62,9 @@ export setupGuiFrames :: () {
6262 guiState.hbox!({
6363 guiState.inputText!({ name="bgPath", labelLeft="path", tooltip="Absolute path, or relative to the .hotparticles file (Right-click for recent)", weight=1 }),
6464 guiState.buttons!({ buttons={
65- {name="bgChoose", image="iconFolder", tooltip="Browse files"},
66- {name="bgReload", image="iconReload", tooltip="Reload background"},
65+ {name="bgChoose", image="iconFolder", tooltip="Browse files"},
66+ {name="bgRelative", image="iconRelativeDirectory", tooltip="Toggle relative path"},
67+ {name="bgReload", image="iconReload", tooltip="Reload background"},
6768 }}),
6869 }),
6970 guiState.hbox!({
@@ -119,8 +120,9 @@ export setupGuiFrames :: () {
119120 guiState.hbox!({
120121 guiState.inputText!({ name="texturePath", labelLeft="path", tooltip="Absolute path, or relative to the .hotparticles file (Right-click for recent)", weight=1 }),
121122 guiState.buttons!({ buttons={
122- {name="chooseTexture", image="iconFolder", tooltip="Browse files"},
123- {name="reloadTexture", image="iconReload", tooltip="Reload texture"},
123+ {name="textureChoose", image="iconFolder", tooltip="Browse files"},
124+ {name="textureRelative", image="iconRelativeDirectory", tooltip="Toggle relative path"},
125+ {name="textureReload", image="iconReload", tooltip="Reload texture"},
124126 }}),
125127 }),
126128 guiState.hbox!({
@@ -349,8 +351,9 @@ export setupGuiFrames :: () {
349351 guiState.hbox!({
350352 guiState.inputText!({ name="shaderPath", labelLeft="path", tooltip="Absolute path, or relative to the .hotparticles file", weight=1 }),
351353 guiState.buttons!({ buttons={
352- {name="chooseShader", image="iconFolder", tooltip="Browse files"},
353- {name="reloadShader", image="iconReload", tooltip="Reload shader"},
354+ {name="shaderChoose", image="iconFolder", tooltip="Browse files"},
355+ {name="shaderRelative", image="iconRelativeDirectory", tooltip="Toggle relative path"},
356+ {name="shaderReload", image="iconReload", tooltip="Reload shader"},
354357 }}),
355358 }),
356359 }),
@@ -453,11 +456,16 @@ export setupGuiFrames :: () {
453456 guiState.frame!({ name="preferences", width=DIALOG_WIDTH, modal=true, layout=.FLOATING, active=false,
454457 guiState.text!({ text="Preferences", size=3 }),
455458
456- guiState.separator!({ thick=false }),
459+ guiState.separator!({ thick=true }),
457460 guiState.section!({ label="Max buffer size",
458461 guiState.inputText!({ name="preferences_maxBufferSize", tooltip="Default: 16000" }),
459462 }),
460463
464+ guiState.separator!({ thick=true }),
465+ guiState.section!({ label="File browser",
466+ guiState.checkbox!({ name="preferences_preferRelativePaths", label="Prefer relative paths", tooltip="Returned paths will be relative whenever possible" }),
467+ }),
468+
461469 guiState.separator!({ thick=false }),
462470 guiState.section!({ label="Max recent",
463471 guiState.hbox!({
@@ -466,7 +474,7 @@ export setupGuiFrames :: () {
466474 }),
467475 }),
468476
469- guiState.separator!({ thick=false }),
477+ guiState.separator!({ thick=true }),
470478 guiState.section!({ label="Undo history",
471479 guiState.inputText!({ name="preferences_maxChanges", tooltip="Default: 200" }),
472480 }),
@@ -605,11 +613,16 @@ local fileDialog_isSave = false
605613local fileDialog_dir = ""
606614local fileDialog_filename = ""
607615local fileDialog_filenamePattern = ""
608- local fileDialog_onHighlight: = cast((path :Path)) NULL
609- local fileDialog_onChoose: = cast((path :Path)) NULL
616+ local fileDialog_onHighlight: = cast((pathObj:Path, pathAbsoluteObj :Path)) NULL
617+ local fileDialog_onChoose: = cast((pathObj:Path, pathAbsoluteObj :Path)) NULL
610618local fileDialog_onClose: = cast(()) NULL
611619
612- export showFileDialog :: (title:string, dir,filename:string, onHighlight:(path:Path), onChoose:(path:Path), onClose:(), filenamePattern="", isSave=false, floating=false) {
620+ export showFileDialog :: (title:string, dir,filename:string,
621+ onHighlight: (pathObj:Path, pathAbsoluteObj:Path),
622+ onChoose: (pathObj:Path, pathAbsoluteObj:Path),
623+ onClose: (),
624+ filenamePattern="", isSave=false, floating=false
625+ ) {
613626 fileDialog_isSave = isSave
614627 fileDialog_dir = dir
615628 fileDialog_filename = filename
@@ -1019,10 +1032,10 @@ export setupGuiCallbacks :: () {
10191032 if not ok filename = ""
10201033
10211034 showFileDialog("Choose texture", dir, filename,
1022- onHighlight = (pathObj:Path) {
1035+ onHighlight = (pathObj:Path, pathAbsoluteObj:Path ) {
10231036 textureBrowserPreviewImage = NULL
10241037
1025- local _, dir, filename = pathObj .getDirectoryAndFilename!()
1038+ local _, dir, filename = pathAbsoluteObj .getDirectoryAndFilename!()
10261039 if not connectToRemoteDirectory(dir) return
10271040 defer disconnectFromRemoteDirectory()
10281041
@@ -1036,9 +1049,9 @@ export setupGuiCallbacks :: () {
10361049 local filter = project.pixelateTextures ? LG.FilterMode.NEAREST : LG.FilterMode.LINEAR
10371050 textureBrowserPreviewImage.setFilter!(filter, filter)
10381051 },
1039- onChoose = (pathObj:Path) {
1040- local ok, dir, filename = pathObj .getDirectoryAndFilename!()
1041- assert(ok)
1052+ onChoose = (pathObj:Path, pathAbsoluteObj:Path ) {
1053+ local ok, dir, filename = pathAbsoluteObj .getDirectoryAndFilename!()
1054+ if not ok return -- @UX: Show error message.
10421055
10431056 if not connectToRemoteDirectory(dir) {
10441057 setErrorText("Could not access folder '%s'", dir)
@@ -1064,6 +1077,15 @@ export setupGuiCallbacks :: () {
10641077 )
10651078 }
10661079
1080+ guiState.onAction.bgRelative = (buttons:gui.Buttons, buttonIndex:int) {
1081+ local project = app.projects[app.currentProjectIndex]
1082+ local ok, path = toggleRelativePath(project, project.bgPath, [*] () -> (success:bool, path:string) {
1083+ local ok, path = getBackgroundFullPath(project)
1084+ return ok, path
1085+ })
1086+ if ok setBackgroundPath(project, path)
1087+ }
1088+
10671089 guiState.onAction.bgReload = (buttons:gui.Buttons, buttonIndex:int) {
10681090 local project = app.projects[app.currentProjectIndex]
10691091 if project.bgPath updateBackgroundTexture(project)
@@ -1361,7 +1383,7 @@ export setupGuiCallbacks :: () {
13611383 })
13621384 }
13631385
1364- guiState.onAction.chooseTexture = (buttons:gui.Buttons, buttonIndex:int) {
1386+ guiState.onAction.textureChoose = (buttons:gui.Buttons, buttonIndex:int) {
13651387 local project, system = getCurrentProjectAndSystem()
13661388 local pathObj = Path(system.texturePath)
13671389 local ok, dir = pathObj.getDirectory!()
@@ -1383,10 +1405,10 @@ export setupGuiCallbacks :: () {
13831405 if not ok filename = ""
13841406
13851407 showFileDialog("Choose texture", dir, filename,
1386- onHighlight = (pathObj:Path) {
1408+ onHighlight = (pathObj:Path, pathAbsoluteObj:Path ) {
13871409 textureBrowserPreviewImage = NULL
13881410
1389- local _, dir, filename = pathObj .getDirectoryAndFilename!()
1411+ local _, dir, filename = pathAbsoluteObj .getDirectoryAndFilename!()
13901412 if not connectToRemoteDirectory(dir) return
13911413 defer disconnectFromRemoteDirectory()
13921414
@@ -1400,9 +1422,9 @@ export setupGuiCallbacks :: () {
14001422 local filter = project.pixelateTextures ? LG.FilterMode.NEAREST : LG.FilterMode.LINEAR
14011423 textureBrowserPreviewImage.setFilter!(filter, filter)
14021424 },
1403- onChoose = (pathObj:Path) {
1404- local ok, dir, filename = pathObj .getDirectoryAndFilename!()
1405- assert(ok)
1425+ onChoose = (pathObj:Path, pathAbsoluteObj:Path ) {
1426+ local ok, dir, filename = pathAbsoluteObj .getDirectoryAndFilename!()
1427+ if not ok return -- @UX: Show error message.
14061428
14071429 if not connectToRemoteDirectory(dir) {
14081430 setErrorText("Could not access folder '%s'", dir)
@@ -1428,7 +1450,16 @@ export setupGuiCallbacks :: () {
14281450 )
14291451 }
14301452
1431- guiState.onAction.reloadTexture = (buttons:gui.Buttons, buttonIndex:int) {
1453+ guiState.onAction.textureRelative = (buttons:gui.Buttons, buttonIndex:int) {
1454+ local project, system = getCurrentProjectAndSystem()
1455+ local ok, path = toggleRelativePath(project, system.texturePath, [*] () -> (success:bool, path:string) {
1456+ local ok, path = getTextureFullPath(project, system)
1457+ return ok, path
1458+ })
1459+ if ok setTexturePath(project, system, path)
1460+ }
1461+
1462+ guiState.onAction.textureReload = (buttons:gui.Buttons, buttonIndex:int) {
14321463 local project, system = getCurrentProjectAndSystem()
14331464 if system.texturePath updateParticleTexture(project, system)
14341465 }
@@ -2184,75 +2215,87 @@ export setupGuiCallbacks :: () {
21842215 )
21852216
21862217 -- Shader.
2187- guiState.onRefresh.shaderPath = (input:gui.InputText) {
2188- local project, system = getCurrentProjectAndSystem()
2189- input.value = system.shaderPath
2190- }
2191- guiState.onAction.shaderPath = (input:gui.InputText) {
2192- local project, system = getCurrentProjectAndSystem()
2193- setShaderPath(project, system, Path(trim(input.value)).toString!())
2194- }
2195- guiState.onOption.shaderPath = (input:gui.InputText, _:int) {
2196- showRecentFilesContextMenu("Recent shaders", app.recentShaders, (path:string) {
2218+ do {
2219+ guiState.onRefresh.shaderPath = (input:gui.InputText) {
21972220 local project, system = getCurrentProjectAndSystem()
2198- setShaderPath(project, system, path)
2199- })
2200- }
2221+ input.value = system.shaderPath
2222+ }
2223+ guiState.onAction.shaderPath = (input:gui.InputText) {
2224+ local project, system = getCurrentProjectAndSystem()
2225+ setShaderPath(project, system, Path(trim(input.value)).toString!())
2226+ }
2227+ guiState.onOption.shaderPath = (input:gui.InputText, _:int) {
2228+ showRecentFilesContextMenu("Recent shaders", app.recentShaders, (path:string) {
2229+ local project, system = getCurrentProjectAndSystem()
2230+ setShaderPath(project, system, path)
2231+ })
2232+ }
22012233
2202- guiState.onAction.chooseShader = (buttons:gui.Buttons, buttonIndex:int) {
2203- local project, system = getCurrentProjectAndSystem()
2204- local pathObj = Path(system.shaderPath)
2205- local ok, dir = pathObj.getDirectory!()
2234+ guiState.onAction.shaderChoose = (buttons:gui.Buttons, buttonIndex:int) {
2235+ local project, system = getCurrentProjectAndSystem()
2236+ local pathObj = Path(system.shaderPath)
2237+ local ok, dir = pathObj.getDirectory!()
22062238
2207- if not ok {
2208- dir = getSaveDirectory().."/projects"
2239+ if not ok {
2240+ dir = getSaveDirectory().."/projects"
22092241
2210- } else {
2211- local dirObj = Path(dir)
2242+ } else {
2243+ local dirObj = Path(dir)
22122244
2213- if not dirObj.isAbsolute {
2214- ok, dir = Path(project.path).getDirectory!()
2215- dirObj.prepend!(ok ? dir : getSaveDirectory().."/projects")
2216- dir = dirObj.toString!()
2245+ if not dirObj.isAbsolute {
2246+ ok, dir = Path(project.path).getDirectory!()
2247+ dirObj.prepend!(ok ? dir : getSaveDirectory().."/projects")
2248+ dir = dirObj.toString!()
2249+ }
22172250 }
2218- }
22192251
2220- local ^ok, filename = Path(system.shaderPath).getFilename!()
2221- if not ok filename = ""
2252+ local ^ok, filename = Path(system.shaderPath).getFilename!()
2253+ if not ok filename = ""
22222254
2223- showFileDialog("Choose shader", dir, filename,
2224- onHighlight = (pathObj:Path) {},
2225- onChoose = (pathObj:Path) {
2226- local ok, dir, filename = pathObj.getDirectoryAndFilename!()
2227- assert(ok)
2255+ showFileDialog("Choose shader", dir, filename,
2256+ onHighlight = (pathObj:Path, pathAbsoluteObj:Path) {
2257+ -- @UX: Preview shader.
2258+ },
2259+ onChoose = (pathObj:Path, pathAbsoluteObj:Path) {
2260+ local ok, dir, filename = pathAbsoluteObj.getDirectoryAndFilename!()
2261+ if not ok return -- @UX: Show error message.
22282262
2229- if not connectToRemoteDirectory(dir) {
2230- setErrorText("Could not access folder '%s'", dir)
2231- return
2232- }
2233- defer disconnectFromRemoteDirectory()
2263+ if not connectToRemoteDirectory(dir) {
2264+ setErrorText("Could not access folder '%s'", dir)
2265+ return
2266+ }
2267+ defer disconnectFromRemoteDirectory()
22342268
2235- addRecent(app.recentFolders, dir, app.maxRecentFolders)
2269+ addRecent(app.recentFolders, dir, app.maxRecentFolders)
22362270
2237- local shader = pcall_newShader(filename)
2238- if shader == NULL {
2239- setErrorText("'%s' is not a valid shader file", filename)
2240- return
2241- }
2271+ local shader = pcall_newShader(filename)
2272+ if shader == NULL {
2273+ setErrorText("'%s' is not a valid shader file", filename)
2274+ return
2275+ }
22422276
2243- local project, system = getCurrentProjectAndSystem()
2244- setShaderPath(project, system, pathObj.toString!())
2245- popPanel()
2246- },
2247- onClose = () {}
2248- )
2249- }
2277+ local project, system = getCurrentProjectAndSystem()
2278+ setShaderPath(project, system, pathObj.toString!())
2279+ popPanel()
2280+ },
2281+ onClose = () {}
2282+ )
2283+ }
22502284
2251- guiState.onAction.reloadShader = (buttons:gui.Buttons, buttonIndex:int) {
2252- local project, system = getCurrentProjectAndSystem()
2253- if system.shaderPath updateParticleShader(project, system)
2285+ guiState.onAction.shaderRelative = (buttons:gui.Buttons, buttonIndex:int) {
2286+ local project, system = getCurrentProjectAndSystem()
2287+ local ok, path = toggleRelativePath(project, system.shaderPath, [*] () -> (success:bool, path:string) {
2288+ local ok, path = getShaderFullPath(project, system)
2289+ return ok, path
2290+ })
2291+ if ok setShaderPath(project, system, path)
2292+ }
2293+
2294+ guiState.onAction.shaderReload = (buttons:gui.Buttons, buttonIndex:int) {
2295+ local project, system = getCurrentProjectAndSystem()
2296+ if system.shaderPath updateParticleShader(project, system)
2297+ }
22542298 }
2255- --
22562299
22572300 guiState.onRefresh.customDataSystem = (input:gui.InputText) {
22582301 local project, system = getCurrentProjectAndSystem()
@@ -2271,7 +2314,7 @@ export setupGuiCallbacks :: () {
22712314 }
22722315
22732316 --==============================================================
2274- --= Save/open project
2317+ --= File dialog
22752318 --==============================================================
22762319 do {
22772320 local Item :: struct { isDir=false, name="" }
@@ -2426,6 +2469,19 @@ export setupGuiCallbacks :: () {
24262469 guiState.refreshRecursively!("fileDialog_directory")
24272470 }
24282471
2472+ local makeRelativeOrClone :: (pathObj:Path) -> Path {
2473+ if not app.preferRelativePaths or fileDialog_isSave return pathObj.clone!()
2474+
2475+ local project = app.projects[app.currentProjectIndex]
2476+ if not project.path return pathObj.clone!()
2477+
2478+ local _, dir = Path(project.path).getDirectory!()
2479+ local ok, pathRelativeObj = pathObj.getRelativeTo!(dir)
2480+ if ok return pathRelativeObj
2481+
2482+ return pathObj.clone!()
2483+ }
2484+
24292485 guiState.onAction.fileDialog_items = (buttons:gui.Buttons, buttonIndex:int) {
24302486 local item = cast(Item) buttons.buttons[buttonIndex].value
24312487
@@ -2435,7 +2491,7 @@ export setupGuiCallbacks :: () {
24352491
24362492 local pathObj = Path(dirCurrent)
24372493 insert(pathObj.path, item.name)
2438- fileDialog_onHighlight(pathObj)
2494+ fileDialog_onHighlight(makeRelativeOrClone(pathObj), pathObj)
24392495
24402496 } elseif item.name == ".." {
24412497 local dirObj = Path(dirCurrent)
@@ -2476,7 +2532,7 @@ export setupGuiCallbacks :: () {
24762532 }
24772533 }
24782534
2479- fileDialog_onChoose(pathObj)
2535+ fileDialog_onChoose(makeRelativeOrClone(pathObj), pathObj)
24802536 }
24812537 }
24822538
@@ -3675,6 +3731,14 @@ export setupGuiCallbacks :: () {
36753731 guiState.refreshRecursively!(frame)
36763732 }
36773733
3734+ guiState.onRefresh.preferences_preferRelativePaths = (checkbox:gui.Checkbox) {
3735+ checkbox.checked = app.preferRelativePaths
3736+ }
3737+ guiState.onAction.preferences_preferRelativePaths = (checkbox:gui.Checkbox, _:int) {
3738+ app.preferRelativePaths = checkbox.checked
3739+ scheduleSaveWorkspace()
3740+ }
3741+
36783742 guiState.onRefresh.preferences_maxBufferSize = (input:gui.InputText) {
36793743 input.value = format("%.0f", app.maxBufferSize)
36803744 }
0 commit comments