New B4XPreferencesDialog

aeric

Expert
Licensed User
Longtime User
I think we need a new version of B4XPreferencesDialog or a new library.
Currently I hit some limitation when using this library.
I created my own version like changing the switch color and Today button.
I use different json file for localization. If I have 10 languages, each dialog multiply the template by 10.
Currently it is also lack of a file picker by default. Sometimes I want to load an image file path.
I also think the design and theme are a bit difficult to customize.
 

aeric

Expert
Licensed User
Longtime User
ChatGPT:

I think this is a good opportunity to design a new B4X preferences and settings library, rather than continue modifying B4XPreferencesDialog.
The limitations you describe are not just missing features. They point to architectural problems: localization is coupled to dialog templates, customization requires modifying library code, and file selection is not treated as a standard preference type.
Since you're already comfortable with B4X and have experience building reusable libraries, I'd approach this as a reusable, cross-project library with a clean API, configurable appearance, and extensible preference types.

1. The problems we should solve​


1. Localization multiplies templates
Your current approach needs a separate JSON file for each language, and potentially a separate dialog template for each language.
Proposed solution: One template containing stable setting IDs and localization keys. Load the selected language dictionary at runtime, without duplicating the template.

2. Appearance is difficult to customize
You've already modified switch colors and the Today button yourself.
Proposed solution: Expose a theme configuration API for colors, typography, spacing, corner radii, buttons, switches, and other controls, without requiring changes to the library's internal code.

3. No built-in file picker
Sometimes a setting needs to select an image and store its path.
Proposed solution: A first-class file preference with a Browse button, selected-path display, optional image preview, and file-type filters.

4. Preference types and layouts are constrained
A built-in dialog may not accommodate every application's requirements.
Proposed solution: Separate preference definitions, rendering, theme, localization, and value storage so developers can extend one part without rewriting everything.

2. Proposed architecture​

I would build the library around five independent components.
Preferences Definition
JSON schema or programmatic definitions
Localization
Language dictionaries
Theme
Colors and styling
Preferences Renderer
Controls, validation, layout and interactions
Value Provider
Read, update, save and restore preference values
This separation matters because changing a language or theme should not require rebuilding the preference definitions.

Example: one template, ten languages​

The template describes what a setting is, not how its text is translated.
{
"version": 1,
"preferences": [
{
"id": "company_logo",
"type": "file",
"titleKey": "settings.company_logo",
"descriptionKey": "settings.company_logo_help",
"filters": ["png", "jpg", "jpeg", "webp"],
"value": ""
},
{
"id": "dark_mode",
"type": "switch",
"titleKey": "settings.dark_mode",
"value": false
},
{
"id": "start_date",
"type": "date",
"titleKey": "settings.start_date",
"value": ""
}
]
}
Each language has its own dictionary, for example:
{
"settings.company_logo": "Company logo",
"settings.company_logo_help": "Select an image file",
"settings.dark_mode": "Dark mode",
"settings.start_date": "Start date"
}
The same template works with English, Malay, Simplified Chinese, Traditional Chinese, or any other supported language. Switching languages only changes the dictionary used to resolve the keys.

3. Built-in preference types​

I'd start with the commonly needed types and design the renderer so more can be added later.
TypeSuggested behavior
textSingle-line text
multilineMulti-line text
passwordMasked input
numberNumeric input with bounds
switchBoolean toggle with configurable colors
checkboxBoolean checkbox
choiceSingle selection
multichoiceMultiple selections
dateDate selection with customizable Today button
timeTime selection
datetimeDate and time
fileFile picker returning a path or URI, depending on platform
directoryDirectory selection where supported
colorColor selection
sliderNumeric range
labelInformational text
actionExecute a callback or open another screen
customDeveloper-supplied control or renderer
For the file picker, I would also support optional image previews and extension filters. File access differs between Android, iOS and desktop, so the API should distinguish between a filesystem path and a platform-provided URI rather than assume every selected file has a permanent absolute path.

4. Make theme customization a first-class feature​

Instead of patching individual controls, a developer should be able to configure the appearance centrally.
Conceptually:
Dim Theme As PrefsTheme
Theme.Initialize

Theme.PrimaryColor = xui.Color_RGB(40, 120, 210)
Theme.SwitchOnColor = xui.Color_RGB(40, 180, 100)
Theme.SwitchOffColor = xui.Color_RGB(150, 150, 150)
Theme.DialogBackgroundColor = xui.Color_White
Theme.CornerRadius = 12dip

Preferences.SetTheme(Theme)
This is illustrative API design, not existing library code.
The theme should also cover:
  • Light and dark themes.
  • Dialog width, margins, spacing and row heights.
  • Font sizes, title styles and descriptions.
  • Input borders, focus colors and validation errors.
  • Date picker buttons, including Today.
  • Button labels, button order and alignment.
  • Per-preference overrides when a particular control needs a different appearance.
The key design principle is that developers should be able to customize the appearance without forking the library.

5. Recommended development strategy​

I would not try to implement every feature at once.
  1. Phase 1 — Core engine
    Define the preference model, stable IDs, value handling, validation and basic dialog rendering. Keep the API independent of localization and styling.
  2. Phase 2 — Localization and themes
    Add runtime language dictionaries, fallback translations, theme presets and per-control styling.
  3. Phase 3 — File and media preferences
    Implement file selection, directory selection where supported, filters and optional image previews.
  4. Phase 4 — Extensibility
    Add custom preference types, callbacks, grouping, sections and more flexible layouts.
  5. Phase 5 — Compatibility and documentation
    Test on each intended B4X platform, document the API and provide complete runnable examples.
One important decision: I would keep the preference definitions separate from saved settings values. That lets the same definitions be reused for different users or configuration profiles without modifying the template itself.

One question before we settle the design​

Which platforms should the new library support?
1. Target platforms

B4A only — Android first

Cross-platform B4X — B4A, B4J and B4i where feasible

B4J first, with other platforms later

2. Preferred presentation

A dialog, similar to B4XPreferencesDialog

A full settings page or panel

Both, sharing the same preference definitions
Continue with this design
My recommendation is cross-platform B4X with both dialog and page presentations, sharing one preference model. That would make the library useful for far more than replacing your current dialog and avoid locking the architecture into the same limitations you're trying to solve.
 

aeric

Expert
Licensed User
Longtime User

Architecture proposal: A new cross-platform B4X Preferences library​

My recommendation is to build a new library rather than fork B4XPreferencesDialog. The goal should be a reusable settings framework for B4A, B4J and B4i, with one shared preference definition that can render as either a dialog or a full settings page.
The most important architectural decision is this:
The preference model should be independent of the UI, language, theme and storage mechanism.
That separation solves your current problems at the architectural level instead of adding individual patches for localization, switch colors, date-picker buttons and file selection.

1. Library identity and scope​

I'll use B4XPreferences as the working name. It can be renamed later.

B4XPreferences​

A schema-driven, customizable preferences framework for B4X.

B4J
Desktop dialogs and settings panels
B4A
Android dialogs and settings screens
B4i
iOS dialogs and settings screens, subject to native UI constraints
Extensible
Custom controls, validators and platform adapters

Design goals​

  1. One schema for every presentation and supported platform.
  2. Runtime localization using JSON dictionaries, with no language-specific template duplication.
  3. Centralized theme configuration and optional per-control overrides.
  4. Built-in file selection and other common preference types.
  5. Explicit value storage, validation and change notifications.
  6. Minimal dependencies and no mandatory third-party UI framework.
  7. Native platform integrations where needed, with a consistent public API.
A cross-platform library does not require every control to look pixel-identical. It requires consistent behavior and configuration, while respecting platform-specific capabilities.

2. Internal architecture​

I recommend implementing the library as a .b4xlib containing shared B4X classes and optional platform-specific modules. This fits the B4X library model, which supports shared code, platform-specific code and declared dependencies. The official cross-platform guidance also recommends using XUI-compatible views and developing the shared project in B4J first.

1792045546011.png

The modules have clearly defined responsibilities.
ModuleResponsibility
PrefsModelParse and validate definitions; manage preference metadata
PrefsControllerCoordinate values, validation, UI events and lifecycle
PrefsRendererCreate and arrange preference controls
PrefsDialogPresent settings in a modal dialog
PrefsPagePresent settings in a full page or panel
PrefsThemeSupply colors, fonts, dimensions and control styling
PrefsLocalizationResolve translation keys and locale-dependent formatting
PrefsStoreLoad, save, reset and migrate stored values
PrefsValidatorsBuilt-in and custom validation rules
PrefsPlatformBridge platform-specific functions such as file selection
These are proposed module names. We should not create a separate module for every preference type: most controls should be rendered by a shared factory, with dedicated classes only where complexity warrants them.

A critical distinction: model, controller and renderer​

  • Model: What the setting means, its ID, type, constraints and default.
  • Controller: What happens when the user changes it, including validation and persistence.
  • Renderer: How the setting appears on screen.
For example, company_logo remains a file preference regardless of whether it is displayed in a dialog or a settings page. Changing its title translation or background color must not change its value or behavior.

3. Public API proposal​

The public API should feel natural to B4X developers. It should avoid requiring developers to construct a large collection of internal objects for ordinary use.
The following is a proposed API, not a claim that these methods already exist.

A. Define and load preferences​

Support both JSON definitions and programmatic construction. JSON is useful for reusable templates and localization; programmatic construction is useful for dynamic forms.
Private Prefs As B4XPreferences
Private Values As Map

Private Sub InitializePreferences
Prefs.Initialize(Me, "Prefs")

Prefs.LoadDefinition( _
File.ReadString(File.DirAssets, "settings.json"))

Prefs.SetLanguage("en")
Prefs.SetTheme(PrefsTheme.CreateLight)

Values.Initialize
Values.Put("dark_mode", False)
Values.Put("company_logo", "")

Prefs.SetValues(Values)
End Sub
Suggested core methods:
MethodPurpose
Initialize(Callback, EventName)Initialize the component and event prefix
LoadDefinition(JsonText)Load a JSON preference definition
LoadDefinitionFromFile(Dir, Filename)Load a definition from a file
AddPreference(Definition)Add a preference programmatically
RemovePreference(Id)Remove a preference
GetPreference(Id)Retrieve a preference definition
SetValues(Map)Initialize current values
GetValuesReturn a snapshot of current values
GetValue(Id)Get a single value
SetValue(Id, Value)Set a value programmatically
ResetToDefaultsRestore declared defaults
ValidateAllValidate the complete set of values
For dynamic applications, I would also provide methods for adding sections, updating available choice options and enabling or hiding individual preferences at runtime.

B. Show a dialog or render a page​

The same model and controller should drive both presentations.
' Dialog presentation
Prefs.ShowDialog("settings.title", "settings")

' Page presentation
Prefs.CreatePage(RootPanel, "settings")
These are illustrative method signatures; exact parameters will be finalized when the B4X implementation is tested.
The API should support:
  • Modal dialogs with configurable buttons and sizing.
  • Embedded panels that do not create their own window.
  • Full-page layouts suitable for B4XPages.
  • Section headers, grouped preferences and collapsible sections.
  • Save, Cancel and Reset actions.
  • Optional immediate-save behavior.
  • Validation before accepting changes.
Important: CreatePage should render into a supplied container, not force the library to own navigation. B4XPages can manage navigation, while the preferences library manages only the settings UI. This keeps the library usable in projects that do not use B4XPages.



C. Event model​

The event API should make it easy to respond to changes without requiring developers to subclass internal controls.
Sub Prefs_ValueChanged(Id As String, Value As Object)
Log($"${Id} = ${Value}"$)
End Sub

Sub Prefs_ValidationFailed(Id As String, Message As String)
Log($"Invalid ${Id}: ${Message}"$)
End Sub

Sub Prefs_DialogClosed(Confirmed As Boolean, Values As Map)
If Confirmed Then
' Save or apply settings.
End If
End Sub
I recommend these events:
EventWhen it fires
ValueChangedA value changes
BeforeValueChangedOptional validation or veto before a change
ValidationFailedA field or complete form fails validation
BeforeSaveThe user attempts to save
AfterSaveValues have been accepted and persisted, if configured
DialogClosedThe dialog closes with its outcome
ActionClickedAn action-type preference is activated
FileSelectedA file selection completes
Events should have documented ordering and should not fire repeatedly for every internal redraw.
For modal editing, the controller should maintain a draft value map. Pressing Cancel discards changes; confirming applies the draft. For an embedded settings page, the developer should be able to choose either explicit Save/Cancel behavior or immediate application of changes.

4. JSON schema proposal​

I would use a versioned JSON schema with stable IDs and explicit translation keys.
{
"schemaVersion": 1,
"id": "app.settings",
"sections": [
{
"id": "general",
"titleKey": "section.general",
"preferences": [
{
"id": "dark_mode",
"type": "switch",
"titleKey": "setting.dark_mode",
"descriptionKey": "setting.dark_mode.description",
"defaultValue": false
},
{
"id": "company_logo",
"type": "file",
"titleKey": "setting.company_logo",
"defaultValue": "",
"options": {
"selectionType": "file",
"extensions": ["png", "jpg", "jpeg", "webp"],
"preview": true,
"allowClear": true
}
},
{
"id": "items_per_page",
"type": "number",
"titleKey": "setting.items_per_page",
"defaultValue": 20,
"options": {
"min": 5,
"max": 200,
"step": 5
}
}
]
}
]
}
This schema is a starting point, not a final specification. In particular, I would standardize options by type and reject invalid combinations early with useful error messages.

Schema rules​

  1. id is stable and language-independent.
  2. type determines the control and value type.
  3. titleKey, descriptionKey and other text keys are resolved at runtime.
  4. defaultValue defines the initial value when no saved value exists.
  5. options defines type-specific behavior.
  6. Optional visibleWhen and enabledWhen rules allow dependent settings.
  7. Schema versioning enables future migrations.
A preference's ID should not change just because its displayed title changes or its position in the form changes.

5. Localization architecture​

This is one of the most important features for your use case.
I recommend three distinct resources:
  • settings.json — preference definitions.
  • locales/en.json — English translations.
  • locales/ms.json — Malay translations.
Additional dictionaries can provide Simplified Chinese, Traditional Chinese and any future language without duplicating settings.json.
The localization API should provide:
Prefs.SetLanguage("zh-Hant")
Prefs.SetFallbackLanguage("en")

Dim Caption As String = Prefs.Translate("setting.company_logo")
The localization engine should support:
  • Runtime language switching.
  • A configurable fallback language.
  • A missing-key policy, such as returning the key or a readable fallback.
  • Locale-aware dates, numbers and formatting where supported.
  • Localized built-in buttons and validation messages.
  • Application-supplied translations that override library defaults.
The library should ship with a default dictionary for built-in UI labels such as Browse, Clear, Today, OK, Cancel and Reset. Applications should only need to provide their own translations.
For a language switch, the renderer refreshes displayed text without reloading the schema or discarding unsaved values.

6. Theme and styling API​

The theme should be a configuration object, not a collection of hard-coded colors hidden in control creation code.
I recommend separating design tokens from control-specific styles.

Design tokens​

Property groupExamples
ColorsPrimary, surface, background, text, secondary text, border, error
TypographyFont family, title size, body size, label weight
GeometryCorner radius, row height, dialog width, margins, spacing
ControlsSwitch on/off colors, input borders, focus state, checkbox colors
ButtonsPrimary, secondary, destructive, disabled states
LayoutLabel width, control alignment, section spacing
Illustrative usage:
Dim Theme As PrefsTheme
Theme.Initialize

Theme.PrimaryColor = xui.Color_RGB(40, 120, 210)
Theme.AccentColor = xui.Color_RGB(0, 150, 136)
Theme.BackgroundColor = xui.Color_White
Theme.TextColor = xui.Color_Black
Theme.SwitchOnColor = xui.Color_RGB(40, 180, 100)
Theme.CornerRadius = 10dip
Theme.RowSpacing = 8dip

Prefs.SetTheme(Theme)
I would also provide CreateLight, CreateDark and CreateSystem presets, plus a way to override the style of an individual preference.

Two customization levels​

Level 1 — Theme configuration
For most applications. Change the look without writing custom rendering code.

Level 2 — Custom renderers
For developers who need a completely different control, custom layout, or specialized interaction.
A custom renderer should be an extension point, not a requirement for changing ordinary colors and dimensions.
One implementation detail matters: shared B4X code should use XUI-compatible APIs where possible, with small platform-specific adapters for capabilities that differ. The official B4X guidance supports this general strategy, but it does not guarantee that every native control exposes the same styling options on all platforms.



7. File picker design​

File selection deserves its own interface because the meaning of a selected file differs between desktop and mobile platforms.
I would define two related preference types:
  • file — select a file.
  • directory — select a folder where supported.
A file preference could include these options:
OptionPurpose
selectionTypeFile or directory
extensionsRestrict allowed file extensions
multipleAllow multiple selections where supported
previewDisplay an image preview
allowClearAllow the current selection to be removed
titleKeyLocalized picker title
startLocationOptional initial browsing location

File picker API​

' Illustrative API
Prefs.SetValue("company_logo", SelectedPath)
Prefs.GetValue("company_logo")
The picker itself should be opened by the platform adapter, not by the schema parser.
B4J desktop
Use the JavaFX file chooser for file and directory selection. Return a filesystem path.

B4A Android
Use Android's Storage Access Framework or an appropriate system picker. Preserve URI access permissions when necessary.

B4i iOS
Use a document picker or photo picker, depending on the requested media type and required access.
Important distinction: Android and iOS may provide a URI or security-scoped file access rather than a permanent filesystem path. The library must not promise that every selected file can be stored as an absolute path and reopened indefinitely.
For images, I would make the API return a structured selection result internally, containing the URI or path, display name, and access information when applicable. The public API can then expose a simple value for ordinary use and a richer result for advanced use.
For a portable application, an optional copyToAppStorage mode can copy the selected image into application-managed storage. This is often the safest approach for a logo or other image that must remain available later.

8. Value storage and persistence​

The library should not force all applications to use one storage system.
I recommend separating current in-memory values from the persistence adapter.
Preferences controller
Current values and draft changes

Map adapter
Caller-managed values and easy testing
JSON adapter
Portable configuration files
Platform adapter
Platform-specific preference storage
Custom adapter
SQLite or application-defined storage
Suggested interface:
' Illustrative API
Prefs.LoadValues(Store)
Prefs.SaveValues(Store)
Prefs.ResetToDefaults
The first release should include Map-based values and a JSON storage implementation. Platform-native storage and SQLite can follow when their use cases are clear.
Additional requirements:
  • Do not save values automatically unless the selected mode requires it.
  • Keep schema defaults separate from saved values.
  • Preserve unknown stored keys when appropriate, so newer or older application versions do not unnecessarily destroy settings.
  • Provide a schema migration mechanism for renamed, removed or type-changed preferences.
  • Never persist temporary draft changes when the user cancels a dialog.
  • Make file-storage behavior explicit rather than silently copying or moving user files.

9. Validation and dependent settings​

A settings framework becomes significantly more useful when preferences can depend on one another.
For example, an application may enable a logo-path setting only when show_logo is true.
{
"id": "logo_path",
"type": "file",
"titleKey": "setting.logo_path",
"enabledWhen": {
"id": "show_logo",
"equals": true
}
}
For validation, support basic constraints in the schema and custom validation callbacks in code.
Examples include:
  • Required fields.
  • Minimum and maximum numbers.
  • String length limits.
  • Allowed values.
  • File extension checks.
  • Custom application rules.
The framework should distinguish between a control being hidden, disabled, and read-only. These are different behaviors and should not be represented by one ambiguous flag.
For complex dependencies, I would initially support simple equality and boolean expressions rather than embedding a full expression language in JSON.

10. Cross-platform compatibility contract​

We should define what "cross-platform" means before implementation starts.
CapabilityB4AB4JB4i
Shared preference schemaYesYesYes
Localization and themesYesYesYes
Basic text, number, switch and choice fieldsYesYesYes
Dialog presentationPlatform-adaptedPlatform-adaptedPlatform-adapted
Embedded settings pageYesYesYes
File selectionNative adapterNative adapterNative adapter
Directory selectionAdapter-dependentSupported through native chooserAdapter-dependent
Image previewYesYesYes
Custom preference rendererSupported APISupported APISupported API
This is the intended compatibility contract, not a statement that these features have already been implemented or tested.
I would establish a minimum supported version for each B4X platform during development, then test the shared classes against those versions. Avoid relying on undocumented platform behavior.
For packaging, a .b4xlib is a good fit because B4X libraries can contain shared classes, supporting files and platform-specific modules.


11. Repository and packaging proposal​

I recommend developing the library in a shared source repository, with B4J as the first implementation and test environment.

B4XPreferences/
├── Shared/
│ ├── B4XPreferences.bas
│ ├── PrefsModel.bas
│ ├── PrefsController.bas
│ ├── PrefsRenderer.bas
│ ├── PrefsTheme.bas
│ ├── PrefsLocalization.bas
│ ├── PrefsValidators.bas
│ └── PrefsStore.bas
├── Platform/
│ ├── B4A/
│ │ └── PrefsPlatform.bas
│ ├── B4J/
│ │ └── PrefsPlatform.bas
│ └── B4i/
│ └── PrefsPlatform.bas
├── Examples/
│ ├── B4J/
│ ├── B4A/
│ └── B4i/
├── Tests/
│ ├── Schema/
│ ├── Localization/
│ ├── Validation/
│ └── Storage/
├── Docs/
│ ├── API.md
│ ├── Schema.md
│ └── Migration.md
└── B4XPreferences.b4xlib

This is a proposed repository layout. We should keep platform-specific modules limited to actual platform differences; duplicating the controller or schema logic three times would defeat the purpose.

12. Development roadmap and acceptance criteria​


Milestone 1 — Core and B4J prototype
First priority
Implement the schema parser, in-memory values, basic controls, dialog and embedded page.
Acceptance: The same JSON definition renders in both presentations, and changing the presentation does not change the stored values.


Milestone 2 — Localization and themes
Implement language dictionaries, fallback handling, theme presets and per-control overrides.
Acceptance: Switch between ten languages without duplicating the schema; change the switch color and date-picker button styling without modifying library code.


Milestone 3 — File preferences
Implement file selection, filters, image preview and clear-selection behavior.
Acceptance: The selected file can be reopened after restarting the application, subject to the platform's documented file-access rules.


Milestone 4 — B4A and B4i adapters
Implement platform-specific pickers, presentation behavior and any necessary styling adaptations.
Acceptance: The shared schema, value model and localization logic work on all three platforms, with documented platform-specific differences.


Milestone 5 — Release
Finalize the public API, package the .b4xlib, document dependencies and provide runnable examples.
Acceptance: A developer can integrate the library into a fresh project without modifying the library source.

13. Important design decisions to settle​

Before writing the implementation, I would make these decisions explicit.
A. Preference definitions

JSON-first, with programmatic additions

Programmatic definitions first, JSON optional

Equal support for JSON and programmatic definitions

B. Saving behavior

Explicit Save / Cancel by default

Save each change immediately by default

Both modes, configurable per presentation

C. Page layout

Adaptive: dialog on smaller screens, page or panel on larger screens

Explicit dialog and page modes, selected by the application

D. Custom preference types

Callback-based custom renderers

Subclass or interface-based renderer extensions

Simple callbacks for ordinary use, renderer extensions for advanced use
Finalize architecture
My defaults would be JSON-first with programmatic additions, both saving modes, explicit presentation selection, and callback-based custom renderers with a more advanced extension mechanism available later.
The immediate next step should be the B4J core prototype, not the complete library. Once the schema, controller, renderer and theme are working in B4J, we can port the shared code to B4A and B4i and implement the platform-specific file pickers. This minimizes the risk of designing an API around behavior that proves difficult to implement consistently across the three platforms.
 

aeric

Expert
Licensed User
Longtime User

B4XPreferences — Final Architecture and API Specification​

Specification v1.0 · Proposed library design · B4A, B4J and B4i
The architecture is now defined around your four decisions: JSON-first definitions, configurable saving behavior, adaptive presentation, and callback-based custom renderers.
The primary design objective is to make B4XPreferences a reusable framework rather than another dialog implementation. The same preference definition, values, localization dictionaries and theme should work across dialog and page presentations without duplication.

1. Final architectural decisions​

AreaFinal decision
Definition sourceJSON-first; preferences can also be added or modified programmatically
PresentationDialog, embedded page or adaptive mode
Adaptive behaviorChoose a presentation based on available dimensions and application configuration
SavingExplicit Save/Cancel or immediate saving, configurable per instance
LocalizationRuntime JSON dictionaries with fallback language
ThemingGlobal theme, theme presets and per-preference overrides
Custom typesCallback-based custom renderers
StorageIn-memory values separated from pluggable persistence
File selectionBuilt-in file preference with platform-specific adapters
Cross-platform implementationShared B4X logic with thin B4A, B4J and B4i adapters
Initial implementationB4J prototype, then B4A and B4i
DependenciesMinimize dependencies; use XUI-compatible abstractions where practical

Non-negotiable architectural rules​

  1. Changing languages must never require another preference definition.
  2. Switching between dialog and page must not change the value model.
  3. Changing themes must not require editing renderer source code.
  4. A preference ID must remain stable across languages and layouts.
  5. A custom renderer must not require modifying the library's core.
  6. Platform-specific limitations must be reported explicitly rather than hidden behind misleading cross-platform promises.

2. Component architecture​

The library will have a small public facade and a set of internal components.
1792046587754.png

Module responsibilities​

ModuleResponsibility
B4XPreferencesPublic API, configuration and lifecycle
PrefsModelParse, validate and maintain preference definitions
PrefsControllerValues, draft editing, validation and events
PrefsRendererRender and update the visible UI
PrefsDialogModal presentation
PrefsPageEmbedded page or panel presentation
PrefsThemeTheme tokens and style resolution
PrefsLocalizationTranslation dictionaries and fallback
PrefsStoreValue serialization and persistence coordination
PrefsValidatorsBuilt-in and custom validation
PrefsPlatformPlatform-specific file selection and other native operations
These are internal implementation boundaries, not a requirement to expose every class to library users. The public facade should shield developers from internal details.

3. Public API specification​

The following defines the intended API contract. Method signatures are illustrative until verified against actual B4X compiler syntax and platform-specific implementations.

3.1 Initialization and definition management​

Private Prefs As B4XPreferences

Private Sub InitializePreferences
Prefs.Initialize(Me, "Prefs")

Prefs.LoadDefinition( _
File.ReadString(File.DirAssets, "settings.json"))

Prefs.SetLanguage("en")
Prefs.SetTheme(PrefsTheme.CreateLight)

Prefs.SetSaveMode(Prefs.SAVE_EXPLICIT)
End Sub
MethodContract
Initialize(Callback, EventName)Initialize the facade and event prefix
LoadDefinition(JsonText)Parse and validate a JSON definition
LoadDefinitionFromFile(Dir, Filename)Load a definition from a file
AddPreference(Definition)Add a preference programmatically
UpdatePreference(Id, Definition)Update metadata and options for an existing ID
RemovePreference(Id)Remove a definition
GetPreference(Id)Return a definition snapshot
GetPreferenceIdsReturn preference IDs in definition order
AddSection(Definition)Add a section programmatically
RefreshReconcile and redraw the visible UI
Definition rules:
  • Duplicate IDs are rejected within the same settings model.
  • Invalid types, malformed options and unknown required properties produce actionable errors.
  • Adding a definition does not silently overwrite an existing value.
  • Removing a definition does not automatically erase persisted data.
  • A structural change triggers the minimum necessary UI rebuild.

3.2 Value management​

The public value API uses stable preference IDs and B4X-compatible values.
Prefs.SetValue("dark_mode", True)

Dim DarkMode As Boolean
DarkMode = Prefs.GetValue("dark_mode")

Dim Values As Map
Values = Prefs.GetValues
MethodContract
SetValues(Values)Initialize or merge values according to a documented policy
GetValuesReturn a snapshot of current values
GetValue(Id)Return one current value
SetValue(Id, Value)Change a value programmatically
HasValue(Id)Determine whether a current value exists
ResetToDefaultsRestore schema defaults
ResetPreference(Id)Restore one preference's default
GetDefaultValue(Id)Retrieve its default value
Additional rules:
  • GetValues must not expose the controller's mutable internal map directly.
  • SetValue must validate the value type and constraints.
  • Programmatic changes must update the rendered control.
  • A programmatic update must follow the same validation and change-notification rules as a user edit.
  • The library must distinguish between a missing value and a valid empty value.
  • Values that cannot be represented safely in JSON, such as platform-specific file handles, require a documented serialization strategy.

3.3 Presentation API​

Three presentation modes are required:
  • DIALOG: a modal dialog.
  • PAGE: render settings into a supplied container.
  • ADAPTIVE: select a suitable presentation using available dimensions and configuration.
Prefs.SetPresentationMode(Prefs.PRESENTATION_ADAPTIVE)

Prefs.Show
For explicit presentation selection:
Prefs.SetPresentationMode(Prefs.PRESENTATION_DIALOG)
Prefs.Show
For an embedded page:
Prefs.SetPresentationMode(Prefs.PRESENTATION_PAGE)
Prefs.CreatePage(RootPanel)
The actual public method names should remain consistent throughout the implementation. In particular, Show must not silently create a new navigation stack or assume the application uses B4XPages.

Adaptive presentation policy​

Adaptive mode will use available width and explicit application constraints, not platform name alone.
Proposed default behavior:
Available spacePreferred presentation
Narrow container or small windowDialog or compact page, depending on host capability
Wide desktop windowCentered dialog for short forms; page or panel for long forms
Application-owned settings containerEmbedded page if explicitly requested
Platform without a suitable modal presentationUse a supported fallback and report the resolved mode
The initial default width threshold should be configurable, with a provisional value of 600dip. This is a starting point for testing, not a universal usability standard.
Adaptive mode should also consider the preference count and estimated form height. A long form should not be forced into a tiny dialog simply because the display is wide.
The resolved presentation should be observable through a property or event, so applications can adjust surrounding UI if needed.

4. Saving and editing semantics​

The library will support two modes, with explicit mode selection rather than ambiguous implicit behavior.

Explicit mode​

Prefs.SetSaveMode(Prefs.SAVE_EXPLICIT)
  • Editing changes a draft map.
  • Save validates and commits the draft.
  • Cancel discards the draft.
  • Reset should have clearly defined behavior, with confirmation handled by the application or configured UI.
  • Persistence occurs only after a successful commit.

Immediate mode​

Prefs.SetSaveMode(Prefs.SAVE_IMMEDIATE)
  • A valid value change updates the committed value.
  • The configured persistence adapter is asked to save the change.
  • Cancel or closing the screen does not roll back already committed changes.
  • If persistence fails, the library reports the failure and follows a documented recovery policy.
Important: Immediate saving does not mean every keystroke must trigger disk I/O. Text controls may commit on focus loss or a configured debounce interval. The default should balance responsive UI with reasonable persistence behavior.

Transaction model​

The controller should maintain:
  • CommittedValues: current accepted values.
  • DraftValues: temporary values during explicit editing.
  • Defaults: defaults from the definition.
  • PersistedValues: values loaded from storage, tracked only if needed for dirty-state and migration logic.
The controller should expose a dirty-state property, or equivalent method, so the UI can determine whether changes remain unsaved.
The Save operation must validate the entire draft before committing any of it. A validation failure must not partially commit the form.

5. Final JSON definition schema​

The schema uses schemaVersion, id, sections and preferences. All displayed text is resolved through translation keys, with optional literal text reserved for dynamic or developer-generated cases.
{
"schemaVersion": 1,
"id": "app.settings",
"titleKey": "settings.title",
"sections": [
{
"id": "general",
"titleKey": "section.general",
"preferences": [
{
"id": "dark_mode",
"type": "switch",
"titleKey": "setting.dark_mode",
"descriptionKey": "setting.dark_mode.description",
"defaultValue": false
},
{
"id": "company_logo",
"type": "file",
"titleKey": "setting.company_logo",
"defaultValue": "",
"options": {
"selectionType": "file",
"extensions": ["png", "jpg", "jpeg", "webp"],
"preview": true,
"allowClear": true,
"copyToAppStorage": true
}
},
{
"id": "items_per_page",
"type": "number",
"titleKey": "setting.items_per_page",
"defaultValue": 20,
"options": {
"min": 5,
"max": 200,
"step": 5
}
}
]
}
]
}

5.1 Schema contract​

PropertyRequiredMeaning
schemaVersionYesVersion of the definition format
idYesStable identifier for the settings model
titleKeyNoTranslation key for the overall settings title
sectionsYesOrdered collection of settings sections
sections[].idYesStable section ID
sections[].preferencesYesOrdered preferences in that section
preferences[].idYesStable preference ID
preferences[].typeYesBuilt-in or registered custom type
preferences[].titleKeyRecommendedLocalized title
preferences[].descriptionKeyNoLocalized description
preferences[].defaultValueRecommendedDefault value when no stored value exists
preferences[].optionsNoType-specific configuration
preferences[].enabledWhenNoCondition controlling enabled state
preferences[].visibleWhenNoCondition controlling visibility
preferences[].validatorsNoBuilt-in validation constraints

5.2 Initial built-in preference types​

The first stable release should include:
  • text
  • multiline
  • password
  • number
  • switch
  • checkbox
  • choice
  • multichoice
  • date
  • time
  • datetime
  • file
  • directory, where supported
  • color
  • slider
  • label
  • action
  • custom
The core renderer should use a factory/registry so adding a preference type does not require expanding a long conditional block throughout the library.
For types that cannot be implemented consistently across all platforms in the first release, the schema parser should still provide a clear unsupported-type error instead of silently rendering the wrong control.

6. Localization contract​

Language dictionaries are independent of the preference schema.
Files/
├── settings.json
└── locales/
├── en.json
├── ms.json
├── zh-Hans.json
└── zh-Hant.json
Example locales/en.json:
{
"settings.title": "Settings",
"section.general": "General",
"setting.dark_mode": "Dark mode",
"setting.dark_mode.description": "Use a dark appearance",
"setting.company_logo": "Company logo",
"setting.items_per_page": "Items per page",
"common.browse": "Browse",
"common.clear": "Clear",
"common.today": "Today",
"common.save": "Save",
"common.cancel": "Cancel",
"common.reset": "Reset"
}

Localization API​

MethodPurpose
SetLanguage(LanguageCode)Set the active language
SetFallbackLanguage(LanguageCode)Set the fallback language
LoadLanguage(LanguageCode, JsonText)Load or replace a dictionary
LoadLanguageFromFile(LanguageCode, Dir, Filename)Load a dictionary from a file
Translate(Key)Resolve a translation key
RefreshLocalizedTextRefresh currently visible text
Required behavior:
  1. Changing language does not reset values.
  2. Missing translations fall back to the configured fallback language.
  3. Missing fallback translations use a documented policy, preferably displaying the key in development builds.
  4. Built-in button labels and validation messages use the same translation system.
  5. Date and number formatting use platform capabilities or explicit formatting rules.
  6. Language codes and fallback behavior are documented consistently.
The library should not embed application-specific translations in its core. It should supply only default translations for its own built-in UI.

7. Theme API contract​

Theme configuration is separate from the schema and localization.

Theme presets​

  • CreateLight
  • CreateDark
  • CreateSystem

Theme properties​

  • Colors: primary, accent, background, surface, text, secondary text, border, error.
  • Typography: font family, title size, body size, label size and emphasis.
  • Layout: spacing, margins, row height, dialog width and section spacing.
  • Geometry: corner radius and control dimensions.
  • Controls: switch colors, checkbox colors, input border and focus state.
  • Buttons: primary, secondary, destructive and disabled styles.
  • States: focused, disabled, selected, invalid and read-only.

Theme methods​

MethodPurpose
SetTheme(Theme)Apply a theme
GetThemeReturn the active theme
SetPreferenceStyle(Id, Style)Override the style for one preference
ClearPreferenceStyle(Id)Remove a preference-specific override
RefreshThemeReapply style changes to rendered controls
Theme updates must not change the value model or trigger value-change events.
Platform adapters can translate shared design tokens into native control styling. Where a platform does not support a particular styling option, that limitation should be documented and the nearest supported appearance used.

8. File selection contract​

The file preference is a first-class control, not a custom renderer example.
Its options should include:
  • File extension filters.
  • Optional multiple selection.
  • Optional preview for supported image types.
  • Clear-selection support.
  • Initial browsing location where supported.
  • Optional copy into application-managed storage.
  • Localized picker titles and action labels.
The platform adapter owns picker interaction. The controller owns value updates and events.

Cross-platform file value​

The library should distinguish these concepts internally:
  • Path: a filesystem path where one is available.
  • Uri: a platform-provided resource identifier.
  • DisplayName: a human-readable filename.
  • AccessMode: whether the application has persistent access.
  • ManagedPath: an application-owned copy when the copy option is enabled.
The ordinary API may expose a simple path or URI string, but advanced applications need access to the richer result.
For portability, storing an application-managed copy is the recommended mode for assets such as company logos that must remain available across application restarts. The library must not claim that a temporary path or URI is permanently accessible.
The file picker must also support cancellation without changing the previous value.

9. Custom preference types: callback-based renderers​

The extension API should allow developers to register a new type without editing the core renderer.
For example, a developer might create a preference type for a barcode scanner configuration, a printer selector or a custom image selector.

Proposed registration API​

' Illustrative API
Prefs.RegisterCustomType( _
"printer_selector", _
CreatePrinterSelector, _
ReadPrinterSelector, _
WritePrinterSelector, _
ValidatePrinterSelector)
The callback names above are conceptual. The final implementation must use signatures supported by the selected B4X callback mechanism.
The renderer contract needs to cover four operations:
OperationResponsibility
CreateBuild the control in the supplied parent container
ReadRetrieve the current control value
WriteUpdate the control from a supplied value
ValidateCheck the value before it is committed
Additional optional callbacks can handle disposal, localization refresh and theme refresh.

Custom renderer requirements​

  • Receive a preference definition and its current value.
  • Receive the active theme and localization service.
  • Notify the controller when the value changes.
  • Support both dialog and page presentations.
  • Avoid storing the only copy of a value inside the visual control.
  • Release event handlers and resources when the control is removed.
  • Report validation errors using the same mechanism as built-in controls.
Custom renderers must not be able to bypass the controller's commit rules accidentally. The controller remains responsible for deciding when a change becomes accepted.

10. Error handling and compatibility​

The library should define a predictable error contract before implementation.
SituationRequired behavior
Invalid JSONReject the definition and report the parsing location when available
Duplicate preference IDReject the conflicting definition
Unknown built-in typeReport an unsupported type
Missing translationUse fallback policy
Invalid valueReject or mark invalid according to the operation
Picker cancelledPreserve the previous value
Save failureReport the failure; do not report successful persistence
Unsupported platform capabilityReturn a documented error or supported fallback
Removed preferencePreserve stored data unless an explicit migration removes it

Versioning​

There are three separate versioning concerns:
  1. Library version — release compatibility and public API changes.
  2. Schema version — changes to the JSON definition format.
  3. Application settings version — migrations to saved values and preference IDs.
These must not be conflated. Updating a library should not automatically reset a user's settings.

11. Final implementation sequence​

The implementation should proceed in the following order. Each milestone should produce working, testable code rather than merely more interfaces.

01​

Core model and JSON parser
Foundation
Implement PrefsModel, schema validation, stable IDs, sections, defaults, built-in types and programmatic additions.
Exit criteria: Load a JSON definition, reject malformed input, add a preference programmatically and retrieve its metadata.

02​

Value controller and saving
Implement committed values, draft values, default restoration, dirty state, validation and both save modes.
Exit criteria: Save commits valid drafts; Cancel discards drafts; immediate mode updates committed values; validation failure prevents partial commits.

03​

B4J rendering prototype
Implement the public facade, renderer, basic controls, modal dialog and embedded page.
Exit criteria: The same schema and controller work in both presentations without duplicating definitions.

04​

Localization and theming
Implement dictionaries, runtime language switching, fallback resolution, theme presets and per-preference styling.
Exit criteria: Switch between ten languages and change control colors without editing the schema or library source.

05​

Persistence and adaptive presentation
Add Map and JSON adapters, adaptive sizing, explicit presentation overrides, sections and dependent settings.
Exit criteria: Persist and restore values correctly, and resolve the presentation using available dimensions and configuration.

06​

File picker and remaining controls
Add file and directory adapters where supported, image preview, date/time, color, slider and choice controls.
Exit criteria: Picker cancellation preserves the old value, and persisted image selections remain accessible according to the selected storage mode.

07​

Custom renderers and cross-platform ports
Finalize callback registration, port shared code to B4A and B4i, and implement native adapters.
Exit criteria: A custom preference works in both presentations, and shared schema, value and localization tests pass on each supported platform.

08​

Packaging, documentation and release
Package the .b4xlib, document dependencies and compatibility, and publish complete examples for all three platforms.
Exit criteria: A fresh application can use the library without modifying its source.

12. Required test suite​

Before calling the library production-ready, verify at least these cases:
  • Load the same definition with multiple language dictionaries.
  • Switch language while the form contains unsaved changes.
  • Change theme while a dialog or page is visible.
  • Add and remove preferences dynamically.
  • Cancel a dialog after changing multiple values.
  • Fail validation on one field while other fields have valid changes.
  • Simulate a storage failure during an explicit save.
  • Restart the application and restore saved values.
  • Switch between dialog and page without losing values.
  • Test adaptive presentation at narrow and wide window sizes.
  • Cancel file selection and confirm the previous file remains selected.
  • Test missing translations, unknown preference types and schema migrations.
  • Register and remove a custom renderer.
  • Run equivalent behavioral tests on B4J, B4A and B4i.
The implementation should have automated tests for model, validation, localization and persistence wherever possible. Visual behavior and native picker integration will also require platform-specific testing.

13. Final scope for version 1.0​

To keep the first release manageable, I recommend dividing features into three groups.
PriorityFeatures
Required for coreJSON schema, programmatic additions, values, validation, dialog, page, explicit/immediate save modes, localization, themes, text, number, switch, checkbox, choice, date and file
Required for stable releaseJSON persistence, adaptive presentation, reset behavior, error handling, custom renderers, B4A/B4J/B4i compatibility testing
Suitable for later releasesAdvanced expression rules, elaborate nested layouts, remote settings synchronization, complex rich-text controls and specialized platform-only preferences
The first release should prioritize architectural correctness and the controls that eliminate your current limitations. It should not attempt to become a general-purpose form builder.

14. Immediate next action​

Start with Milestone 1: the B4J core model and JSON parser.
Do not begin by designing every visual control. First, create a minimal runnable B4J project that can:
  1. Load the proposed JSON schema.
  2. Validate preference IDs and types.
  3. Add a preference programmatically.
  4. Store and retrieve values by ID.
  5. Keep defaults separate from current values.
  6. Report invalid definitions clearly.
Once that foundation is stable, implement the controller and the first dialog/page renderer against the same model.
This sequence is the lowest-risk way to build a genuinely reusable B4X library. It also prevents the new implementation from inheriting the original problem: a UI component whose internal design makes routine customization increasingly difficult.
 

aeric

Expert
Licensed User
Longtime User
Consider integrating MediaChooser for a cross platform file+ picker.
ChatGPT:

That is worth evaluating for B4XPreferences, particularly because a built-in file/image picker is one of the main limitations you want to solve.
I would treat MediaChooser as a candidate platform adapter, not make it a mandatory dependency of the entire preferences library. That way, B4XPreferences can offer a consistent file preference API while keeping the underlying picker replaceable.
There is also an important distinction: selecting an image from a photo library is not necessarily the same as browsing for an arbitrary file or selecting a directory. We should verify exactly which operations MediaChooser supports on B4A, B4J and B4i before deciding how much of the file-picker requirement it covers.

1. What MediaChooser actually covers​

The official MediaChooser B4X forum thread describes image/video selection and capture, with capture supported on B4A and B4i. It also documents media-copy handling and platform setup requirements.

A later community tutorial explores additional media workflows, but that should not be treated as proof that every feature is available in the official library on all three platforms.

For our purposes, the distinction is:
RequirementProposed approach
Select an image for a logo or avatarEvaluate MediaChooser first
Capture a new photoUse MediaChooser where supported
Select a videoUse MediaChooser where supported
Select arbitrary documents, such as PDF or ZIPVerify the current API; use a generic document-picker adapter if needed
Select a directorySeparate platform-specific implementation
Select files on B4J desktopVerify support or use the native JavaFX file chooser
The official thread also notes that MediaChooser does not support selecting multiple images or videos. That limitation should be considered if we later add a multi-file preference.

2. Update to the B4XPreferences architecture​

I would revise the file-selection architecture to introduce a dedicated PrefsFileProvider abstraction.
1792046853626.png

The preferences controller should not depend directly on MediaChooser. Instead, it requests a selection from the provider and receives a normalized result.
This avoids coupling the entire library to one media library and allows us to change the implementation later without breaking the public API.

Proposed internal result​

' Conceptual result model, not yet implemented.
Type PrefsFileResult ( _
Success As Boolean, _
Path As String, _
Uri As String, _
DisplayName As String, _
MimeType As String)
The exact representation must be checked against B4X's supported type declarations and the actual MediaChooser result type. The key is to normalize the result while preserving platform-specific access information.

3. Revised implementation sequence​

Move file-provider integration earlier in the roadmap, but only after the basic model and controller are working.
  1. Core model and controller: Define the file preference contract and normalized result model.
  2. B4J prototype: Implement ordinary preferences and dialog/page rendering.
  3. Localization and themes: Finish language switching and styling independently of the picker.
  4. File-provider interface: Define selection, cancellation, result normalization and error handling.
  5. MediaChooser integration: Implement and test the B4A/B4i media adapter against the actual library version.
  6. Generic document selection: Add document picking where MediaChooser does not cover the required use case.
  7. B4J file selection: Integrate the JavaFX chooser.
  8. Cross-platform tests: Verify persistence, URI/path handling, cancellation, image previews and restart behavior.
My recommendation: use MediaChooser where it genuinely fits, but do not make it the universal file-picker implementation. The new library should own the stable preference API; platform and media libraries should remain replaceable adapters.
 
Top