How-To Guide
⏱ ~20 minutes
Building a Preferences System
Almost every app needs to remember something between sessions — a window size, a last-used folder, a font size. This guide shows you how to build a clean, cross-platform preferences system in HyperXTalk using a dedicated preferences stack and custom properties. No file parsing, no JSON — just the stack engine doing what it does best.
What you'll cover
- Why a separate preferences stack is the right approach
- Opening or creating the preferences file cross-platform
- Getting and setting preference values
- Saving and closing the preferences stack cleanly
- Wiring it all together in your app's open and close handlers
The Approach
HyperXTalk stacks can store arbitrary data as custom properties — key/value pairs that are saved with the stack file. This makes a stack an ideal preferences store: you get free serialisation, automatic cross-platform file handling, and no parsing code to write.
The idea is simple: create a dedicated invisible stack just to hold preferences. When your app opens, load the preferences stack. Read and write values using get the and set the. When done, save and close it. The OS takes care of putting the file in the right place via specialFolderPath:
-- macOS: ~/Library/Preferences/MyApp/myapp.prefs
-- Windows: C:\Users\...\AppData\Roaming\MyApp\myapp.prefs
-- Linux: ~/.MyApp/myapp.prefs
Opening the Preferences Stack
The PreferencesOpen command takes your app name and a preferences file name. It builds the correct path for the current platform, creates the folder if needed, and either loads the existing preferences stack or creates a new one:
local sPreferencesA
command PreferencesOpen applicationName, preferencesName
put preferencesName into sPreferencesA["fileName"]
if (the platform = "MacOS") then
put specialFolderPath("preferences") & "/" & applicationName into sPreferencesA["pathToFolder"]
else if (the platform = "Win32") then
put specialFolderPath("0x001A") & "/" & applicationName into sPreferencesA["pathToFolder"]
else
put specialFolderPath("home") & "/." & applicationName into sPreferencesA["pathToFolder"]
end if
if (there is not a folder sPreferencesA["pathToFolder"]) then
create folder sPreferencesA["pathToFolder"]
end if
put sPreferencesA["pathToFolder"] & "/" & preferencesName into sPreferencesA["pathToFile"]
if (there is not a file sPreferencesA["pathToFile"]) then
create invisible stack preferencesName
save stack preferencesName as sPreferencesA["pathToFile"]
else
go invisible to stack sPreferencesA["pathToFile"] in a new window
end if
end PreferencesOpen
💡
The stack is opened invisible so the user never sees it. It lives in memory as a normal stack — you just never show it.
Getting a Value
Reading a preference is just reading a custom property from the preferences stack. The PreferencesGet function takes a key name and returns its value:
function PreferencesGet pKey, pDefault
local tValue
if (sPreferencesA["pathToFile"] is not empty) and (there is a stack sPreferencesA["pathToFile"]) then
put the pKey of stack sPreferencesA["pathToFile"] into tValue
end if
if tValue is empty then
return pDefault
end if
return tValue
end PreferencesGet
Pass a default value as the second argument — if the preference hasn't been set yet, the default is returned instead:
local tTheme, tFontSize
put PreferencesGet("theme", "light") into tTheme
put PreferencesGet("fontSize", 12) into tFontSize
Setting a Value
PreferencesSet writes a custom property to the stack. The value is held in memory until you explicitly save — which gives you control over when disk writes happen:
command PreferencesSet pKey, pValue
if (sPreferencesA["pathToFile"] is not empty) and (there is a stack sPreferencesA["pathToFile"]) then
set the pKey of stack sPreferencesA["pathToFile"] to pValue
end if
end PreferencesSet
Call it whenever a preference changes:
PreferencesSet "theme", "dark"
PreferencesSet "fontSize", 14
PreferencesSet "lastFolder", tChosenFolder
Saving and Closing
You have three options depending on what you need:
-- Save without closing (good for mid-session saves)
command PreferencesSave
if (sPreferencesA["pathToFile"] is not empty) and (there is a stack sPreferencesA["pathToFile"]) then
save stack sPreferencesA["pathToFile"]
end if
end PreferencesSave
-- Close without saving (discard changes)
command PreferencesClose
if (sPreferencesA["pathToFile"] is not empty) and (there is a stack sPreferencesA["pathToFile"]) then
lock messages
delete stack sPreferencesA["pathToFile"]
unlock messages
end if
end PreferencesClose
-- Save then close (use this in closeStack)
command PreferencesSaveAndClose
if (sPreferencesA["pathToFile"] is not empty) and (there is a stack sPreferencesA["pathToFile"]) then
save stack sPreferencesA["pathToFile"]
lock messages
delete stack sPreferencesA["pathToFile"]
unlock messages
end if
end PreferencesSaveAndClose
💡
delete stack removes the stack from memory — it does not delete the file. lock messages prevents the engine sending a closeStack message during deletion, which could cause issues if your closeStack handler also calls the preferences library.
Using It in Your App
In your main stack script, open the preferences in openStack, apply them to your UI, and save them in closeStack:
on openStack
local tFontSize, tShowToolbar
PreferencesOpen "MyApp", "myapp.prefs"
put PreferencesGet("fontSize", 12) into tFontSize
put PreferencesGet("showToolbar", true) into tShowToolbar
-- Apply to UI
set the textSize of field "Editor" to tFontSize
set the toolbarVisible of toolbar "mainBar" to tShowToolbar
end openStack
on closeStack
-- Save current state before closing
PreferencesSet "fontSize", the textSize of field "Editor"
PreferencesSet "showToolbar", the toolbarVisible of toolbar "mainBar"
PreferencesSaveAndClose
end closeStack
The Full Preferences Library
Here is the complete preferences library. Save it as its own stack file and include it with your app:
preferences library stack script
local sPreferencesA
command PreferencesOpen applicationName, preferencesName
put preferencesName into sPreferencesA["fileName"]
if (the platform = "MacOS") then
put specialFolderPath("preferences") & "/" & applicationName into sPreferencesA["pathToFolder"]
else if (the platform = "Win32") then
put specialFolderPath("0x001A") & "/" & applicationName into sPreferencesA["pathToFolder"]
else
put specialFolderPath("home") & "/." & applicationName into sPreferencesA["pathToFolder"]
end if
if (there is not a folder sPreferencesA["pathToFolder"]) then
create folder sPreferencesA["pathToFolder"]
end if
put sPreferencesA["pathToFolder"] & "/" & preferencesName into sPreferencesA["pathToFile"]
if (there is not a file sPreferencesA["pathToFile"]) then
create invisible stack preferencesName
save stack preferencesName as sPreferencesA["pathToFile"]
else
go invisible to stack sPreferencesA["pathToFile"] in a new window
end if
end PreferencesOpen
function PreferencesGet pKey, pDefault
local tValue
if (sPreferencesA["pathToFile"] is not empty) and (there is a stack sPreferencesA["pathToFile"]) then
put the pKey of stack sPreferencesA["pathToFile"] into tValue
end if
if tValue is empty then return pDefault
return tValue
end PreferencesGet
command PreferencesSet pKey, pValue
if (sPreferencesA["pathToFile"] is not empty) and (there is a stack sPreferencesA["pathToFile"]) then
set the pKey of stack sPreferencesA["pathToFile"] to pValue
end if
end PreferencesSet
command PreferencesSave
if (sPreferencesA["pathToFile"] is not empty) and (there is a stack sPreferencesA["pathToFile"]) then
save stack sPreferencesA["pathToFile"]
end if
end PreferencesSave
command PreferencesClose
if (sPreferencesA["pathToFile"] is not empty) and (there is a stack sPreferencesA["pathToFile"]) then
lock messages
delete stack sPreferencesA["pathToFile"]
unlock messages
end if
end PreferencesClose
command PreferencesSaveAndClose
if (sPreferencesA["pathToFile"] is not empty) and (there is a stack sPreferencesA["pathToFile"]) then
save stack sPreferencesA["pathToFile"]
lock messages
delete stack sPreferencesA["pathToFile"]
unlock messages
end if
end PreferencesSaveAndClose
🟢
You can store any value a custom property supports — strings, numbers, booleans, even arrays. The stack engine handles serialisation automatically.