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:

preferences library
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:

preferences library
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:

preferences library
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:

preferences library
-- 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:

main stack script
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.