Reflex C++


Reflex

Reflex is the portable C++ framework for building graphical multimedia applications and audio plugins, featuring:

  • Layered modular architecture with strict dependencies and minimal external requirements
  • Uniform object model with intrusive references, lightweight views, and data-driven properties
  • Unified structured data system including JSON, XML, RIFF, and PropertySheet formats
  • Cross-platform abstraction layer for desktop apps, mobile apps, and audio plugins
  • Native real-time audio and plugin support including VST and AU
  • GPU-driven rendering and UI framework with layout, styling, animation, and events
  • Hot-reloadable stylesheets, scripting, and integrated developer tooling
  • Designed for high performance with small binaries, low memory usage, and deterministic runtime behaviour

What it's not

Reflex is not a DSP library or an audio-processing toolkit. It provides low-level abstraction over platform and plug-in APIs, but does not include DSP algorithms, synthesis engines, processing graphs, or other domain-specific audio layers. Those systems are expected to be built on top when needed.

Framework vs library

The most common way to use Reflex is as a framework. Reflex Creator template projects and the Bootstrap namespace provide the standard starting point for applications, with state/view separation, startup wiring, UI initialisation, and project scaffolding already in place.

Due to its modular nature, Reflex may also be used as a library. Lower-level modules can be embedded into existing host architectures and initialised independently, allowing integration into other frameworks such as JUCE.

Organisation

Reflex is organised into a set of small, focused sub-libraries with strict one-directional dependencies. Each directory contains a 'require.h' that aggregates the headers of its dependencies.

The source tree is divided into two major parts, reflex and reflex_ext.

reflex/

The core, canonical framework layer containing the fundamental primitives and primary APIs of Reflex.

Reflex: Core object model, containers, and reference system. Lightweight primitives forming the foundation for all Reflex modules.

System: Cross-platform OS abstraction (files, memory, threading, timing, SIMD detection).

SIMD: Portable SIMD layer exposing unified vector types over platform intrinsics.

Data: Structured data system (PropertySet), serialization, formats (JSON/XML/RIFF), encoding, compression, hashing.

File: File IO utilities, path handling, virtual file system, and shared resource management.

GLX: UI framework: layout, styling, events, and animation system.

VM: Scripting system: VM, compiler, runtime, module loading, and C++ bindings.

reflex_ext/

Contains higher-level extension modules, helper systems, widgets, tooling, and application scaffolding built on top of reflex/.

Async: Task system with worker threads, scheduling, and HTTP utilities.

File: Extensions to file system (e.g. monolithic archive format, extra utilities).

GLX: Additional UI widgets and helpers built on GLX.

IDE: Integrated debugging, live editing and hot-reload for stylesheets and scripts.

Bootstrap: Application scaffolding, lifecycle management, VFS setup, and project template foundation.

Fundamentals & Key Concepts

Reflex is built around a small set of core concepts which are used consistently throughout the framework. It is imperative to understand these concepts before diving deeper into the framework:

Object model and lifetime: Reflex > Object

Intrusive hierarchies and traversal: Reflex > Intrusive

Containers, views, and allocation: Reflex > Containers

Strings and text processing: Reflex > String

Keys and properties: Reflex > Data > PropertySet

Initialisation and globals: Reflex > Initialisation

Getting Started

A good starting point is the examples/Notes project. It is a small but complete working application demonstrating the separation between UI and application logic, recommended project structure, and typical usage of Reflex modules across GLX, Data, and Bootstrap.

Once familiar with the example, create your own project using Reflex Project Creator (bin/tools), which sets up the required modules, entry point, and Bootstrap scaffolding automatically.

From there, the natural development path is:

  • Build your UI using Reflex::GLX - layout, styling, and events
  • Manage structured state with Data::PropertySet and the serialization helpers
  • Follow the patterns established in the Notes example for state/view separation and lifecycle management

Value Types

Output


Reflex > Containers

Reflex provides a compact suite of container types designed around predictable behaviour, explicit allocation control, contiguous storage, and lightweight non-owning views.

Design Principles

  • Runtime allocator model rather than allocator template parameters
  • Explicit allocation policies for deterministic memory behaviour
  • Zero-copy views and slicing where possible
  • Contiguous storage preferred for iteration efficiency and cache locality
  • Minimal and internally consistent APIs

Core Containers

Array

Array<T> is the primary dynamic container in Reflex.

It provides a contiguous dynamically-sized buffer with explicit allocation control and fast iteration.

CString and WString are typedefs of Array<char> and Array<wchar_t>.

  • Push / Insert / Remove operations
  • Explicit allocation policies such as kAllocateOver, kAllocateExact, and kAllocateNone
  • Runtime allocator selection without affecting the container type itself
  • Contiguous storage

Array<Int32> values;
values.Push(10);
values.Push(20);

Strings

CString and WString are specialised Array types with guaranteed null termination after all modifications.

This allows direct interoperability with traditional C APIs while preserving the Array API model.


CString text = "hello";
FILE * file = fopen(path.GetData(), "r");

Unlike generic Array<T>, string containers always maintain an additional null terminator internally.

Views

ArrayView<T> provides a lightweight non-owning slice into contiguous memory.

CString::View and WString::View are typedefs of ArrayView<char> and ArrayView<wchar_t>.

Views are passed by value, never allocate, and are commonly used for substring operations, tokenization, temporary references, and zero-copy APIs.

  • Zero-allocation slicing
  • Efficient substring and buffer operations
  • Lightweight function arguments and return values
  • Does not own memory

auto [a, b] = Splice(str, 5);
CString copy = a; // promote to owning string if needed

The referenced memory must remain valid for the lifetime of the view.

Sequence

Sequence<TKey, TValue> is a contiguous associative container where keys are not required to be unique.

It is useful when insertion order matters or when multiple entries may share the same identifier.

  • Supports repeated keys
  • Contiguous storage
  • Fast iteration
  • Shares the same allocator model as Array

Map

Map<TKey, TValue> is a simplified unique-key associative container built on top of Sequence.

  • Unique keys
  • Linear contiguous storage
  • Simple map[key] syntax

Queue

Queue<T> is a fixed-size SPSC (single producer / single consumer) ring buffer.

It is designed for real-time and multi-threaded systems such as audio processing pipelines.

  • Lock-free and wait-free operations
  • Zero allocations after creation
  • Deterministic runtime behaviour

Allocation Model

Runtime Allocators

Reflex containers use allocator objects at runtime rather than allocator template parameters.

This allows containers to move across API and module boundaries without allocator-dependent types.

Allocation Policies

Write operations such as Push() and Insert() accept explicit allocation policies.

Common policies include:

  • kAllocateOver - grow capacity automatically
  • kAllocateExact - resize exactly as requested
  • kAllocateNone - forbid allocation

Iteration

Reflex containers support range-based iteration:


for (auto & value : array)
{
}

Reverse iteration helpers are also provided via rbegin() and rend().

Functions

ToRegion, ToView

Object Types

Allocation, Allocator

Value Types

Array, ArrayRegion, ArrayView, Map, Queue, Sequence


Reflex > Initialisation

Reflex uses a controlled runtime initialisation model. Some systems, including allocators, modules, null-instances, and global state, are not safe to use before Reflex has initialised.

Static Initialisation

Avoid global objects which allocate memory or depend on Reflex runtime state.

Allocating types such as Array, CString, WString, Reference, TRef, and many Object-derived types may depend on systems which are not yet initialised during static construction.

Recommended Patterns

  • Use constexpr CString::View or WString::View for global string constants
  • Use The<T>::Acquire() for singleton-style global objects
  • Use AcquireProperty<T>(Bootstrap::global, "id") for shared application state
  • Use Reflex::Detail::Module when explicit module initialisation order is required

Allocating Globals

If a non-trivial global object is unavoidable, ensure the default allocator is instantiated in the same translation unit before the global object.


REFLEX_INSTANTIATE_DEFAULT_ALLOCATOR;
CString g_name = "example";

In general, prefer explicit initialisation over global construction.


Reflex > Intrusive

Reflex provides a small set of intrusive container primitives: Item, List, and Node.

These form the structural foundation of many higher-level framework systems including GLX::Object, GLX::Style, property hierarchies, and other tree-based APIs.

Most applications will not implement these classes directly, however you will interact with them implicitly when iterating over children, siblings, or hierarchical structures throughout the framework.

Intrusive Containers

An intrusive container stores linkage directly inside the object itself rather than inside external wrapper nodes.

Objects therefore remain allocated exactly where they were created while simultaneously participating in lists or hierarchies.

This model provides:

  • No wrapper-node allocations
  • Stable object addresses and references
  • Lightweight traversal and iteration
  • Efficient parent/child hierarchies
  • A unified structural model across the framework

Reflex uses intrusive structures extensively for UI trees, style trees, property hierarchies, and related systems.

Core Types

List

List<TYPE, RETAIN, BASE> is a doubly-linked intrusive list.

The list itself does not allocate nodes - linkage exists inside the contained objects.

Common operations include:

  • GetFirst()
  • GetLast()
  • GetNumItems()
  • Range-based iteration

for (auto & item : list)
{
}

Item

Item represents a single element participating inside a List.

Objects which should appear in intrusive lists inherit from Item.

Common navigation helpers include:

  • GetPrev()
  • GetNext()

Node

Node combines both Item and List to form a hierarchical tree node.

A Node may simultaneously:

  • Exist as a child inside another Node
  • Contain child Nodes of its own

This forms the basis of Reflex hierarchical systems such as UI trees and style trees.


for (auto & child : object)
{
	// ...
}

Ownership and Retention

Depending on template configuration, intrusive lists may optionally retain contained objects automatically.

Many higher-level Reflex systems therefore combine intrusive hierarchy management with the Object reference counting system.

The exact ownership behaviour depends on the RETAIN template parameter and the surrounding framework subsystem.

Iteration

Intrusive lists and nodes support standard range-based iteration:


for (auto & child : object)
{
}

Reverse iteration helpers are also available via rbegin() and rend().

Value Types

Item, List, Node


Reflex > Notifications

Reflex provides two lightweight notification primitives that together cover the majority of notification and change-tracking use cases. Both are intentionally minimal and give explicit control over notification semantics, lifetime, and cost.

State & Monitor

Pull-based change tracking.

Extremely lightweight and allocation-free. Clients explicitly poll a State for changes using a State::Monitor. Best suited for high-frequency or frame-based updates where control and predictability are critical, as well as multi-threaded scenarios.

Signal

Push-based notification.

Observers register callbacks (including lambdas) and are notified synchronously when the signal is emitted. Listener lifetime is managed automatically via reference-counted handles, making the client passive and convenient. Typically the caller will store the object received CreateListener via a Reference to keep it alive.

Signal is generally more expensive than State and should be avoided for very frequent notifications.

Usage

  • Use State when changes occur often, when polling is already part of the update loop, when minimal overhead is required, or when notifications may cross thread boundaries.
  • Use Signal in single-thread scenarios when events are infrequent, when callbacks improve clarity, or when automatic listener lifetime management is desired.

Value Types

Monitor, Signal, State


Reflex > Object

Reflex makes a primary, explicit distinction between object-types and value-types. Object types derive from Reflex::Object, are shared, reference counted, typically heap-based (although they can also be instantiated on the stack), and non-copyable by default.

Value types (non-Objects such as UInt32 or Array) are lightweight stack-based primitives. To promote a value into an object so it can participate in reference counting, heap allocation, dynamic properties, or generic object APIs, use ObjectOf<TYPE>.

Reflex::Object provides two foundational capabilities:

  • Deterministic lifetime management via intrusive reference counting
  • A generic interface for attaching and querying typed properties dynamically

Object Lifetime

Reflex uses an intrusive reference counting model built directly into Object.

Unlike std::shared_ptr, COM, or Objective-C, there are no external control blocks or hidden allocations - the reference count lives inside the object itself.

Core Principles

  • All reference-counted types derive from Object
  • The reference count is stored inside the object itself
  • Newly created objects always start with a reference count of zero
  • Every Release() must therefore correspond to a previous Retain()
  • Ownership semantics are primarily expressed through Reference<T> and TRef<T>

Reference <TYPE>

Reference<T> is the primary ownership wrapper for Reflex objects. It retains on construction, releases on destruction, and guarantees a valid object reference over its lifetime.

See Reflex::Reference for full semantics and usage.

TRef <TYPE>

TRef<T> is the lightweight non-owning counterpart to Reference<T>. It does not retain or release, but still follows the Reflex convention of representing a valid object reference rather than a nullable pointer.

See Reflex::TRef for full semantics and usage.

Object Creation

Reflex objects should never be created with raw new/delete directly.

Instead, object creation is performed through the Reflex helper APIs:


auto a = New<MyClass>(args...);
auto b = Make<MyClass>(args...);
  • New<T>() returns a TRef<T>
  • Make<T>() returns a Reference<T>
  • If T is abstract, New<T>() automatically resolves through T::Create()

Make<T>() is generally the most convenient form when immediate ownership is required.

AutoRelease Helpers

AutoRelease() provides a shorthand for constructing temporary Reference<T> wrappers.

The following examples are equivalent:


Reference<HttpConnection> conn = HttpConnection::Create(...);
auto conn = AutoRelease(HttpConnection::Create(...));
auto conn = Make<HttpConnection>(...);

Stack-Allocated Objects

Unlike traditional COM-style systems, Reflex objects may be instantiated directly on the stack.


Data::PropertySet node;

Stack objects ignore Retain() and Release() calls and are destroyed normally via scope lifetime.

This flexibility is useful and efficient for temporaries and embedded members, however ownership must remain explicit: any retained reference must not outlive the stack object it refers to. For example, storing a Reference or TRef to a stack object beyond its lifetime is invalid:


Data::PropertySet node;
auto props = New<Data::PropertySet>();
props->SetProperty("child", node); // invalid after node leaves scope

Null Instances

Reference<T> and TRef<T> default construct using the type's null-instance rather than nullptr.

If a type does not define a null-instance, the wrappers cannot be default-constructed, enforcing validity at compile time.

User-defined object types therefore often require explicit construction:


Reference <View> view1 = New<View>();
Reference <View> view2 = kNewObject;	//shorthand helper

TRef<T> also supports construction using kNoValue for deferred assignment, however this should only be used sparingly.

Manual Retain/Release

Object exposes Retain() and Release(), however direct usage is uncommon.

If manual lifetime management is required:

  • New objects begin with refcount = 0
  • Every Release() must correspond to a previous Retain()
  • Double-release will assert
  • Retaining in constructors and releasing in destructors is the standard pattern

Circular References

Reflex is not a garbage collected system. Circular references will leak unless explicitly broken:


auto a = New<Data::PropertySet>();
auto b = New<Data::PropertySet>();
a->SetProperty("foo", b);
b->SetProperty("foo", a);

Some advanced systems inside Reflex (such as Reflex::VM) provide explicit cycle-breaking mechanisms, however application code should generally avoid circular ownership through design.

Best Practices

  • Prefer Reference<T> for ownership
  • Use TRef<T> for temporary non-owning references
  • Avoid manual Retain()/Release() where possible
  • Avoid circular ownership relationships
  • Prefer clear ownership hierarchies

ObjectOf<T>

ObjectOf<T> wraps a value type inside an Object, allowing values to participate in reference counting, dynamic properties, and generic object-based APIs.

Generic Properties

Reflex::Object defines a virtual interface for attaching, querying, and removing typed properties dynamically via Object::SetProperty, Object::QueryProperty, and Object::UnsetProperty.

This allows sub-objects and arbitrary typed values to be attached to objects at runtime, reducing the need for deep subclassing and enabling highly data-driven systems.

Properties are identified by both id AND type, rather than by id alone. This complements C++'s strong typing model by allowing multiple typed views of the same conceptual property.

Important: Object Does Not Implement the Interface

Reflex::Object itself does not provide a concrete property implementation. Calling SetProperty() on a derived type which does not implement the property callbacks will silently discard the property.

Typically, dynamic properties are implemented through Data::PropertySet, which fully implements the property system for arbitrary types.

Many primary framework classes (such as GLX::Object) derive from Data::PropertySet and therefore support dynamic properties automatically.

For more information on dynamic properties, see Data::PropertySet.

Functions

AcquireProperty, AutoRelease, GetAbstractProperty, GetProperty, Make, New, SetAbstractProperty, UnsetAbstractProperty

Object Types

Object

Value Types

ConstReference, ConstTRef, ObjectOf, Reference, TRef


Reflex > String

In Reflex, strings are arrays.

CString and WString are typedefs of Array<char> and Array<wchar_t> respectively, while CString::View and WString::View are typedefs of ArrayView<char> and ArrayView<wchar_t>.

Because of this, most string utilities operate generically on Array and ArrayView rather than on dedicated string-only types.

Functions such as Split, Splice, Search, Replace, Trim, and tokenization helpers therefore work uniformly across strings, byte arrays, and other contiguous sequences.

String Storage

CString and WString are specialised Array types which guarantee null termination after all modifications.

This allows safe interoperability with traditional APIs expecting const char* or const wchar_t* buffers.


CString text = "hello";
SomeAPI(text.GetData());

Generic Array<T> containers do not provide this guarantee.

Views

Many string operations return lightweight non-owning views rather than allocating new strings.

This allows substring and tokenization operations to occur without copying or heap allocation.


CString str = "HelloWorld";
auto [a, b] = Splice(str, 5);

In this example, both a and b reference memory owned by str.

If str is modified or destroyed, the views immediately become invalid.

This behaviour is intentional and allows high-performance zero-copy APIs throughout the framework.

Ownership

When ownership of a substring or slice is required, the view must be explicitly copied into an owning string:


CString str = "HelloWorld";
auto [a, b] = Splice(str, 5);

CString owned = b;

Explicit copying keeps ownership semantics visible and avoids hidden allocations in performance-sensitive code paths.

Generic Sequence Utilities

Because strings are simply specialised arrays, most helper functions are implemented generically for contiguous sequences rather than specifically for text.

This keeps the API surface small and consistent while avoiding duplicate implementations for strings, byte buffers, and related container types.

Iteration

Strings and views support standard range-based iteration:


for (auto c : text)
{
}

Reverse iteration helpers are also available via rbegin() and rend().

Typedefs

CString, CString::View, WString, WString::View

Functions

Join, Left, Lowercase, Merge, Mid, RawStringCopy, RawStringLength, Remove, Replace, ReverseSearch, ReverseSplice, Right, Search, Splice, Split, ToCString, ToFloat32, ToFloat64, ToInt32, ToInt64, ToUInt32, ToUInt64, ToWString, Uppercase


Reflex > Types

Typedefs

Key32

Value Types

Address, Float32, Float64, Function, Idx, Int16, Int32, Int64, Int8, Key, NullType, Pair, Point, Rect, Size, Tuple, UInt16, UInt32, UInt64, UInt8, WChar


Reflex > Async

Async is a small util library built on top of the low-level System::Task and System::Thread primitives, providing high-level and convenient access to asynchronous operations. The API is designed to simplify correct async usage while keeping control over execution, lifetime, and thread safety explicit.

It covers the most common async use cases, including background execution with progress reporting and cancellation, HTTP requests, safe transfer of payloads across thread boundaries, and lifetime management of running tasks.

Lifetime

Async tasks are reference-counted. Clients typically hold a Reference <Async::Task>. Destroying this reference automatically requests cancellation of the task (assuming one-owner).

For custom workers, task cancellation is cooperative. Worker implementations must explicitly observe and respect the task’s run flag via calling Cancelled() and if true, return early. In this case typically you should also ctx.SetResult(false) to indicate failure.

Observation

Progress and completion are commonly monitored via polling using clocks created with CreateClock() or CreatePeriodic().

In UI code, do not use Async::CreateClock or Async::CreatePeriodic. Use the GLX equivalents (GLX::CreateAnimationClock, GLX::CreatePeriodicClock, or GLX::Object::OnClock), which guarantee a valid UI context for each callback.

Examples


//start a background task
auto task_ref = Make<Async::Worker>([](Async::Worker::Context & ctx)
{
	auto result = New<Data::UInt32Property>(); //use whatever payload type is suitable

	UInt n = 0;
	while (!ctx.Cancelled() && n < 100)
	{
		System::SuspendThread(10);             //emulate work

		ctx.SetProgress(Float(n) / 99.0f);     //publish progress for UI

		n++;
	}

	result->value = n;

	ctx.SetResult(n == 100, result);
});

// monitor progress
m_clock_ref = Async::CreatePeriodicClock(0.1f, [task_ref]()	//!by capturing task_ref, the Task is kept alive as long as needed
{
	switch (task_ref->GetStatus())
	{
	case Async::Task::kStatusCompleted:
		if (auto result = Cast<Data::UInt32Property>(task_ref->GetResult()))	//use DynamicCast if your worker returns different types
		{
			//do something with result
		}
		break;

	default:
		break;
	}
});

Reusable UI "Await" helper


void AttachAwait(GLX::Object & object, Key32 id, TRef <Async::Task> task, const Function <void(bool ok, Reflex::Object & result)> & callback)
{
	GLX::AttachPeriodicClock(object, id, 0.25f, 0.25f, [&object, id, task = AutoRelease(task), callback]()
	{
		auto status = task->GetStatus();

		if (status == Async::Task::kStatusPending)
		{
			return;		//exit early, no callback
		}
		else
		{
			Reflex::Detail::WeakRef <GLX::Object> weakref(object);	//object could die during callback, but we need to access again on last line

			callback(status == Async::Task::kStatusCompleted, task->GetResult());

			GLX::DetachClock(weakref.Load(), id);	//also releases the task, because was captured in a Reference
		}
	});
}

Functions

CreateClock, CreatePeriodicClock

Enums

Status

Object Types

Task, Worker

Value Types

Context


Reflex > Data

The Reflex::Data namespace provides the core infrastructure for representing, transforming, and persisting structured data in a platform-independent way.

It sits beneath higher-level systems and is used throughout Reflex wherever data needs to be serialized, stored, transmitted, hashed, or converted between representations.

Data covers three main concerns: structured data representation, format-level serialization, and low-level data transformation utilities.

  • Structured data: PropertySet, generic properties, and reflection-friendly containers used across the engine.
  • Serialization & formats: Pluggable Format implementations for JSON, XML, RIFF, Reflex PropertySheet, and binary PropertySet.
  • Data transformation: Compression, hashing, and string encoding utilities (e.g. EncodeUTF8).

You should expect to use the Data namespace extensively in application logic. When used correctly, it removes the need to roll your own data containers, serialization layers, encoding utilities, or hashing functions, as these concerns are already handled in a consistent and well-integrated way across Reflex.


Reflex > Data > Compression

Globals

kLZ4

Functions

Compress, Decompress

Object Types

CompressionAlgorithm, DecompressionAlgorithm


Reflex > Data > Encoding

Functions

BytesToHex, DecodeUCS2, DecodeUTF8, DecodeUrlSegment, EncodeUCS2, EncodeUTF8, EncodeUrlSegment, HexToBytes, IsHttps, MakeUrl, SplitUrl, SplitUrlResource

Value Types

Url


Reflex > Data > Format

Data::Format defines the interface for encoding and decoding structured data. A given Format implements how to serialize a PropertySet into bytes and how to reconstruct it back into a PropertySet.

Reflex provides several global, constant Format instances covering the common persistence use cases.

Common Formats

kPropertySetFormat

  • Recommended binary format
  • Fast, compact, and supports the full range of Reflex property types (ints, floats, arrays, nested sets, blobs, colours, points, etc)

kPropertySheetFormat

  • Human-readable text format
  • Similar to JSON but with additional type info support (e.g. a sheet can encode various int formats)

kJsonFormat

  • Standard JSON
  • Ideal for interoperability with web APIs and external tooling. Limited to JSON-compatible types

kReflexXmlFormat

  • Supports a sub-set of XML
  • Only attributes and nested elements (ignores text content between tags)

Typical Usage

Encoding


Data::PropertySet data;

Data::SetInt32(data, "version", 1);

auto child = Data::AcquirePropertySet(data, "child");
Data::SetFloat32Array(child, "values", {1.0f, 2.0f});

auto blob = Data::EncodePropertySet(Data::kPropertySetFormat, data);

File::Save(path, blob);

Decoding


auto bytes = File::Open(path);

Data::PropertySet root = Data::DecodePropertySet(Data::kPropertySetFormat, bytes);
if (root)
{
	auto v = Data::GetInt32(root, "version");
	auto sub = Data::GetPropertySet(root, "child");
	auto arr = Data::GetFloat32Array(sub, "values");
}

Choosing a Format

  • Use kPropertySetFormat for application data, presets, configs, or anything Reflex-internal.
  • Use kPropertySheetFormat when you need editable text files or want structured types without JSON’s restrictions.
  • Use kJsonFormat for web communication and external file formats.

Globals

kBinaryFormat, kJsonFormat, kPropertySetFormat, kPropertySheetFormat, kReflexMarkupFormat, kReflexXmlFormat, kRiffFormat

Functions

CopyPropertySet, DecodePropertySet, EncodePropertySet, ResetPropertySet

Object Types

Format, SerializableFormat


Reflex > Data > Hash

Reflex provides a small set of non-cryptographic hash functions for use in lookup keys, content identifiers, and integrity checks.

These are designed for speed and practicality, not for cryptographic security.

All functions operate on Archive::View and return an Archive (for SHA) or UInt32/UInt64.

Functions

CRC32, FNV1a32, FNV1a64, SHA1, SHA256


Reflex > Data > PropertySet

Data::PropertySet is Reflex’s generic container for structured, tree-based data.

PropertySet allows arbitrary data to be attached to objects at runtime, without modifying class definitions or introducing ad-hoc subclasses.

Instead of encoding every variation in a type hierarchy, behavior and state can be composed dynamically by attaching properties as needed.


object.SetProperty("hover_time", 0.0f);
object.SetProperty("user_data", some_ref);

This bridges the performance and safety of C++ with the flexibility typically associated with dynamic languages. It enables rapid iteration, late-bound features, and data-driven behavior while remaining fully type-aware and debuggable.

Generic property system

PropertySet implements the root Reflex::Object property interface (OnSetProperty / OnUnsetProperty / OnQueryProperty).

This provides a uniform, generic property mechanism across the entire framework.

Properties are identified by id AND type, rather than id alone.

This complements C++'s strong typing model by allowing multiple, type-safe views of the same conceptual property without collapsing everything into a single loosely-typed value.


ps.SetProperty("my_id", 1);
ps.SetProperty("my_id", "string");

The example above creates two distinct properties:

  • An Int32 property with id "my_id".
  • A CString property with id "my_id".

Under the hood, this (id + type) pair is represented by Reflex::Address.

Structured and hierarchical data

PropertySet supports hierarchical data by allowing nested PropertySet instances.

This makes it suitable for representing structured trees of runtime state, configuration, or metadata when needed.

Unlike rigid schemas, PropertySet allows structure to evolve organically as systems interact.

Persistence and serialization

PropertySet is serializable, via the Format system.

This makes it suitable for storing presets, state snapshots, configuration files, and interchange data.

For persistent data, values should be written using the Data::SetXXX family of functions, which define the set of interoperable, format-safe property types. Using the Reflex::Data:: suite of property accessors also avoids template bloat occuring from use of the Reflex:: templated ones.


Data::SetFloat32(ps, "gain", 0.75f);
Data::SetCString(ps, "name", "Preset A");

PropertySet can be serialized using any Data::Format implementation:


auto blob = Data::EncodePropertySet(Data::kPropertySetFormat, ps);
//or
auto json = Data::DecodePropertySet(Data::kJsonFormat, ps);

Note that when writing text-based formats (JSON, Reflex PropertySheets) you need to register the key-strings, via Data::AcquireKeyMap and Data::RegisterKey.

Summary

  • PropertySet enables dynamic, runtime data attachment without subclassing.
  • It is a core mechanism for flexible, data-driven behavior in Reflex.
  • Properties are identified by (id, type), allowing rich runtime composition.
  • Serialization is supported via pluggable formats.

Use Iterate<TYPE>() to iterate over all properties of TYPE

Each item will have a [key,value] where key is Reflex::Address (comprising of the property id and type_id) and value a Reference <TYPE>


for (auto & [adr, ref] : test.Iterate<Data::CStringProperty>())
{
	output.Log(id.value, ref->value);
}

Typedefs

Float32Property, Float64Property, Int32Property, Int64Property, KeyMap, UInt32Property, UInt64Property

Functions

AcquireKeyMap, AcquirePropertySet, AcquirePropertySetArray, AddPropertySet, Assimilate, GetBool, GetFloat32, GetFloat64, GetInt32, GetInt64, GetKey, GetKey32, GetKeyMap, GetPropertySet, GetPropertySetArray, GetUInt32, GetUInt64, GetUInt8, Merge, RegisterKey, SetBinary, SetBool, SetCString, SetFloat32, SetFloat64, SetInt32, SetInt64, SetKey32, SetPropertySet, SetUInt32, SetUInt64, SetUInt8, SetWString, UnsetBinary, UnsetBool, UnsetCString, UnsetFloat32, UnsetFloat64, UnsetInt32, UnsetInt64, UnsetKey32, UnsetPropertySet, UnsetPropertySetArray, UnsetUInt32, UnsetUInt64, UnsetUInt8, UnsetWString

Object Types

ArchiveObject, ObjectArray, PropertySet

Value Types

PropertyIterator


Reflex > Data > Serialization

Reflex serialization provides a lightweight, explicit way to encode and decode strongly-typed data to and from a binary stream.

It is designed for deterministic layouts, low overhead, and full control over what is written.

Unlike PropertySet serialization (which targets structured, schema-light data), this API is intended for **compact, ordered, binary serialization** of known data layouts.

Archive

Data::Archive is a byte container used as the backing store for serialization.

It is a simple typedef over Array<UInt8>, and can be treated as a writable or readable byte stream.


Data::Archive outstream;

To read from an archive, use Data::Archive::View, which maintains a read cursor without copying data:


Data::Archive::View instream = outstream;

Archive::View performs deserialization directly from the underlying byte buffer. For simple value types, data is read by copying from the current stream position and advancing an internal read cursor. This avoids additional heap allocations and minimizes intermediate copying, making the operation suitable for performance-sensitive code paths.

Writing data (Serialize)

Use Data::Serialize to append strongly-typed values to an archive in sequence.


Data::Serialize(stream, 1, 2.0f);
Data::SerializeUTF8(stream, L"wide");

Values are written in the order provided. The resulting stream is compact and contains no metadata beyond what is required to decode variable-length data (such as strings).

Reading data (Deserialize)

Use Data::Deserialize to read values back in the same order they were written.


auto [i, f] = Data::Deserialize<Int32, Float32>(stream);
auto wstring = Data::DeserializeUTF8(stream);

Deserialization advances the read cursor stored in the Archive::View.

The template parameters define the expected types and enforce strong typing at the call site.

Ordering and determinism

Serialization is order-dependent.

The reader must deserialize values in the exact order and type they were written.

This explicitness is intentional:

  • No reflection or schema lookup.
  • No runtime type ambiguity.
  • No hidden allocations.

This makes the system well suited for file formats, IPC, network packets, caches, and other performance-sensitive data paths.

Strings and encoding

String serialization is explicit.

Use SerializeUTF8 / DeserializeUTF8 to encode wide strings into UTF-8 byte sequences.

Encoding is handled deterministically and is independent of platform wchar size.

Relationship to PropertySet

Use direct serialization when the data layout is known and fixed.

Use PropertySet serialization (EncodePropertySet) when flexibility, flexible version evolution, or dynamic composition is required.

Both systems share the same Data namespace but target different problem spaces.

Relationship to Pack / Unpack

Data::Pack and Data::Unpack provide a lower-level mechanism for converting a single value into a binary representation and restoring it again.

These functions operate on types that have a well-defined, “toll-free” binary representation - meaning the value can be viewed as a contiguous block of bytes without transformation. This typically includes:

  • Integral and floating-point types.
  • POD and trivially copyable types.
  • Arrays of such types.

UInt32 t;
Data::Archive::View view = Data::Pack(t);

auto restored = Data::Unpack<UInt32>(view);

For types with a compile-time known binary size (for example, UInt32 is always 4 bytes), Pack / Unpack and Serialize / Deserialize produce the same binary representation. Internally, Serialize uses Pack / Unpack for these cases.

The distinction becomes important for variable-sized data.

Serialize is designed for writing *sequences* of values to a stream. As such, it includes any additional information required to reconstruct the data when reading, such as encoding the length of strings or arrays before their contents.

In contrast, Pack produces only the binary representation of the value itself. For example, when packing a string, the length is not prepended, as the size of the binary view implicitly defines the data extent.

Summary

  • Data::Archive is a simple byte stream for serialization.
  • Serialize / Deserialize write and read strongly-typed values in order.
  • The API is explicit, deterministic, and allocation-aware.
  • String encoding is handled explicitly via UTF-8 helpers.
  • PropertySet serialization serves higher-level, structured data needs.

Typedefs

Archive, Archive::View

Functions

Deserialize, DeserializePropertySet, DeserializeUCS2, DeserializeUTF8, Pack, ReadLine, Serialize, SerializePropertySet, SerializeUCS2, SerializeUTF8, Unpack, WriteLine


Reflex > File

The Reflex::File namespace provides a unified, cross-platform interface for working with files, paths, and shared file-backed resources.

It is built on top of the System layer and is intended to be the primary API for file and resource access in application code.

File covers three closely related concerns: path manipulation, file I/O, and virtualised/shared resource access.

  • Path utilities: helpers for common path operations such as SplitFilename, CheckExtension, ResolveExistingPath, and related functions.
  • File I/O: high-level helpers for opening, saving, and reading files (e.g. File::Open, File::Save, File::ReadLine), avoiding direct interaction with low-level file handles.
  • Virtual filesystems and resources: abstractions for unifying real, bundled, and embedded data sources.

Reflex > File > IO

Functions

Copy, CreateMemoryReader, CreateMemoryWriter, GetRemainder, Open, Peek, ReadBytes, ReadLine, ReadValue, Save, WriteBytes, WriteLine, WriteValue

Object Types

PersistentPropertySet, ResourcePool, VirtualFileSystem


Reflex > File > Path

Globals

kPathDelimiter

Functions

CheckExtension, CorrectExtension, CorrectStrokes, CorrectTrailingStroke, Delete, DeleteDirectoryContent, DeletePath, Exists, GetSystemPath, GetVolumes, IsDirectory, MakeDirectory, MakePath, MakeRelativePath, RemoveDuplicateStrokes, RemoveTrailingStroke, Rename, ResolveExistingFolder, ResolveRelativePath, SplitExtension, SplitFilename


Reflex > GLX

GLX is Reflex's UI framework. It combines a retained object tree, a declarative layout system, stylesheet-based rendering, an event model, and time-based animation helpers into one consistent API surface.

Most GLX work falls into a small set of concepts which are used repeatedly across the framework:

  • Every visible element is a GLX::Object in a parent -> child hierarchy
  • Parents control layout flow, while children declare how they participate in that layout
  • Visual appearance is defined in stylesheets and render layers rather than hard-coded drawing logic
  • Input and application behavior are driven through bubbling events and delegate bindings
  • Animated behavior is built from state transitions, explicit animations, and clocks

Key Guides

See the following guides before diving into specific widgets or helpers:

Object tree and layout: Layout

Stylesheets, layers, and states: Styling

Input dispatch and custom behavior: Events

Time-based transitions, clocks, and procedural updates: Animation

Minimal Example


auto panel = New<GLX::Object>();
GLX::SetFlow(panel, GLX::kFlowY);

auto title = New<GLX::Label>(L"Overview");
auto body = New<GLX::Object>();
auto button = New<GLX::Button>(L"Run");

GLX::AddInline(panel, title);
GLX::AddInlineFlex(panel, body);
GLX::AddFloat(body, button, GLX::kAlignmentTopRight);
GLX::BindClick(button, [](){});

Object Types

WindowClient


Reflex > GLX > Animation

GLX animation covers two closely related systems:

  • Animation objects that interpolate values or states over time
  • Clocks that execute UI callbacks on the GLX update thread

In many cases the simplest animation is declarative. A stylesheet can define @State variants and a transition time, and code only needs to push or clear the relevant state.

State-Based Transitions

For hover, selected, inactive, and similar UI feedback, prefer stylesheet-driven transitions first.


Button:
{
	transition: 0.25;

	@State hover:
	{
		bg: Fill(colour: 228);
	};
}

This keeps simple UI motion in the style layer rather than in imperative code.

Running Explicit Animations

When code needs to decide target values dynamically, create an animation object and run it against a property or state on a target object.


auto fade = GLX::CreateColourPropertyAnimation("colour", GLX::kWhite, GLX::kBlack);
GLX::Run(object, "colour", 0.25f, fade);

Common helpers in this module include CreateStateAnimation, CreateColourPropertyAnimation, CreateMarginPropertyAnimation, and CreateCallbackAnimation.

Clocks

Use clocks when you need procedural updates over time rather than interpolation between two values.

  • CreateAnimationClock / AttachAnimationClock for frame-based UI callbacks
  • CreatePeriodicClock / AttachPeriodicClock for lower-frequency periodic work
  • DetachClock to stop an attached clock

This is commonly used for polling async task status, updating drag visuals, driving custom canvas effects, or other time-based UI behavior that is not well described as a simple property tween.

Choosing the Right Approach

  • Use stylesheet transition + @State for simple visual feedback
  • Use explicit animation objects when code determines the end value or state
  • Use clocks for continuous procedural behavior or periodic observation

Functions

AttachAnimationClock, AttachPeriodicClock, CreateAnimationClock, CreateCallbackAnimation, CreateColourPropertyAnimation, CreateFloatPropertyAnimation, CreateInterpolatedAnimation, CreateLogarithmicAnimation, CreateMarginPropertyAnimation, CreateMaxBoundsAnimation, CreateOpacityAnimation, CreatePeriodicClock, CreatePointPropertyAnimation, CreatePositionAnimation, CreateSizePropertyAnimation, CreateStateAnimation, CreateWaitAnimation, DetachClock, Enter, Exit, Run, Stop

Enums

Easing

Object Types

Animation, ContainerAnimation, InterpolatedAnimation, Multi, PlayList


Reflex > GLX > Events

The GLX Event System provides a unified way to send and receive input and custom messages across all UI objects.

Events are lightweight, dynamically extensible objects that bubble up through the view hierarchy until handled or consumed.

Fundamentals

GLX::Event derives from Data::PropertySet, allowing arbitrary name-value pairs to be attached dynamically.

It also has a Key32 id member for fast event type comparisons.

GLX::Object::Emit delivers an event to the target object, then bubbles it up through its parent hierarchy until a handler traps or consumes it.

Use GLX::Object::ProcessEvent when you want to dispatch an event directly to a single object without bubbling.

It is the callers responsiblity to ensure the event is retained before calling Emit or ProcessEvent.

Emitting Events

The low-level approach is to create and populate an Event instance manually and call Emit() on the target object.


auto e = Make<Event>("MyCustomEvent");
Data::SetBool(e, "selected", true);
my_view->Emit(e);

A more concise helper exists:


GLX::Emit(*this, "MyCustomEvent", "selected", true);

This ensures compile-time checking that arguments are provided in name-value pairs and that names decay to Key32.

Receiving Events

The lowest-level way to respond to events is by overriding bool GLX::Object::OnEvent(GLX::Object & src, GLX::Event & e).


bool MyView::OnEvent(GLX::Object & src, GLX::Event & e)
{
	switch (e.id.value)
	{
	case GLX::kMouseDown:
		// handle click
		if (GLX::GetClickFlags(e) & GLX::kClickFlagDbl)
		{
		}
		return true; // trap event

	case K32("MyCustomEvent"):
		// handle custom event
		return true;
	}

	return GLX::Object::OnEvent(src, e); // Always forward to base
}

Combined with the dispatch helpers, it's common to use if/else checks instead of a switch:


if (e.id == GLX::kMouseDown)
{
	return true;
}
else if (auto menu = GLX::GetMenu(e))
{
	menu->AddItem(L"Option 1");
	return true;
}

Always forward unhandled events to the base OnEvent implementation, or delegates and default behavior will not run.

Delegate-Based Binding

GLX supports inline delegate binding to avoid subclassing.


auto btn = GLX::AddInline(*this, GLX::Init(New<GLX::Button>(L"Click Me"), m_button_style));

GLX::BindClick(btn, []()
{
	// Handle click
});

BindEvent and BindEventVoid provide binding to a specific event id, while SetEventDelegate allows full forwarding of all events.

As BindEvent uses the event id also for the delegate id, each usage of BindEvent with the same event id will replace any previous delegate for that event id. To attach multiple event handlers use SetEventDelegate with different ids.

Event Helpers

Some useful helpers for dispatching events include...


UInt8 GetClickFlags(const Event & e);   //Returns click state flags.
bool IsLeftClick(const Event & e);      //True if left mouse button was used.
bool IsRightClick(const Event & e);     //True if right mouse button was used.
bool IsDoubleClick(const Event & e);    //True if a double click was detected.
Point GetMouseDelta(const Event & e);   //Mouse drag or wheel delta.
UInt8 GetModifierKeys(const Event & e); //Modifier key flags.
KeyCode GetKeyCode(const Event & e);    //Key code from key events.
WChar GetKeyCharacter(const Event & e);    //Character from key events.
TRef<Menu> GetMenu(Event & e);          //Returns Menu from kMenuOpen event.
TRef<Menu> GetMenu(Event & e, Key32 context); //Same, filtered by context.
Transaction GetTransactionStage(const Event & e); //Retrieve transaction stage.
TRef<Object> GetDragSource(Event & e);  //Retrieve drag source.

Example: Custom Event End-to-End


// Emit a custom event with a property
GLX::Emit(*this, K32("VolumeChanged"), "value", 0.75f);

// Handle it in a parent view
bool MyView::OnEvent(GLX::Object & src, GLX::Event & e)
{
	if (e.id == K32("VolumeChanged"))
	{
		auto v = Data::GetFloat32(e, "value");
		ApplyVolume(v);
		return true;
	}
	return Base::OnEvent(src, e);
}

Typedefs

KeyCode, ModifierKeys

Globals

kCharacter, kFocus, kKeyDown, kKeyUp, kLoseFocus, kMouseDown, kMouseDrag, kMouseEnter, kMouseLeave, kMouseUp, kMouseWheel, kTransaction

Functions

BindClick, BindEvent, BindEventVoid, Emit, EnableMouse, EnableMouseCapture, FocusBranch, GetClickFlags, GetKeyCharacter, GetKeyCode, GetModifierKeys, GetMousePosition, IsDoubleClick, IsLeftClick, IsRightClick, QueryAntecedent, RedirectFocus, ScaleDelta, Send, SetEventDelegate, TransformPosition, UnbindEvent

Enums

ClickFlags, TransactionStage

Object Types

Event


Reflex > GLX > Events > Drag & Drop

GLX drag and drop passes a Reflex::Object payload through the event system. The payload can be any Object, including GLX::Object, Data::PropertySet, ObjectOf <TYPE>, or any custom object-type.


auto payload = Make<Data::PropertySet>();
Data::SetWString(payload, "path", L"example.txt");
GLX::StartDragDrop(payload, GLX::kMouseCursorPointer, GLX::kMouseCursorBlock);

Initiating a drag

To receive kMouseDrag and kMouseUp events on an object, first enable mouse capture:


GLX::EnableMouseCapture(object, true);

The typical pattern is to watch kMouseDrag and use ExceedsDragThreshold(GetMouseDelta(e)) to decide when the pointer movement is large enough to begin a drag operation.


bool MyView::OnEvent(GLX::Object & src, GLX::Event & e)
{
	if (e.id == GLX::kMouseDrag)
	{
		if (GLX::ExceedsDragThreshold(GLX::GetMouseDelta(e)))
		{
			GLX::StartDragDrop(Make<MyDragData>(), GLX::kMouseCursorPointer, GLX::kMouseCursorBlock);
			return true;
		}
	}

	return Base::OnEvent(src, e);
}

Accepting drops

A potential drop target becomes active by handling kDragDropTender and returning true. Once a target accepts kDragDropTender, it will then receive kDragDropEnter, kDragDropLeave, and kDragDropReceive events for that drag.


bool MyView::OnEvent(GLX::Object & src, GLX::Event & e)
{
	switch (e.id.value)
	{
	case GLX::kDragDropTender:
		return GLX::QueryDragDropData<MyDragData>(e);

	case GLX::kDragDropEnter:
		src.SetState("dragover");
		return true;

	case GLX::kDragDropLeave:
		src.ClearState("dragover");
		return true;

	case GLX::kDragDropReceive:
		if (auto data = GLX::QueryDragDropData<MyDragData>(e))
		{
			ConsumeDrop(*data);
			return true;
		}
		return false;
	}

	return Base::OnEvent(src, e);
}

If you want to prevent this object and its parents from accepting the current drag, you can intercept the tender event, clear its id, and then allow it to continue upward as a non-drag event:


case GLX::kDragDropTender:
	e.id = kNullKey;	//change the Event id to effectively hide the event from parents
	return false;	//return false as this object doesnt want to receive the drop

Reading drag data

QueryDragDropData<TYPE>(e) is the standard typed helper. Internally it performs a DynamicCast on the object stored in the event's drag payload.


struct CustomDragData : public Reflex::Object
{
	REFLEX_OBJECT(CustomDragData, Reflex::Object);

	Array <WString> filepaths;
};

template <class TYPE> inline TYPE * Reflex::GLX::QueryDragDropData(GLX::Event & e)
{
	return DynamicCast<TYPE>(GetDragDropData(e));
}

If you use a custom payload type, make it object-castable with REFLEX_OBJECT so DynamicCast can recognise it.

When checking against multiple possible payload types, it is slightly more efficient to fetch the payload once and then test it manually:


auto drag_data = GLX::GetDragDropData(e);

if (auto custom = DynamicCast<CustomDragData>(drag_data))
{
}
else if (auto generic = DynamicCast<Data::PropertySet>(drag_data))
{
}

Cursor feedback

The drag API takes two cursors: the cursor shown while hovering an accepting target, and the cursor shown when the current target does not accept the drag. A simple setup is:


GLX::StartDragDrop(data, GLX::kMouseCursorPointer, GLX::kMouseCursorBlock);

For richer UI, a common pattern is to hide the OS drag cursor and render your own drag-preview object in the window foreground. Start the drag with invisible cursors:


GLX::StartDragDrop(data, GLX::kMouseCursorInvisible, GLX::kMouseCursorInvisible);

A typical custom-cursor implementation uses a global begin-listener to construct the preview from the drag payload type, attaches that object to the window foreground, disables mouse handling on it, and updates its position every frame.

A key rule is to exclude the visual cursor from hit-testing. Use GLX::EnableMouse(cursor, false, true) so it ignores mouse-over events.


m_drag_drop_visualiser = GLX::CreateDragDropBeginListener([this](Reflex::Object & drag_data)
{
	auto origin = GLX::Core::desktop->GetMouseOver();
	TRef <GLX::WindowClient> window = origin->GetWindow();

	//create drag cursor
	auto cursor = New<GLX::Object>();
	GLX::SetText(cursor, GLX::GetText(origin));
	cursor->SetStyle(GLX::FindStyle(origin, "DragCursor"));
	GLX::EnableMouse(cursor, false, true);	//ensure cursor, or any children on cursor, do not doesnt intercept mouse (see EnableMouse for details)
	GLX::Enter(cursor, GLX::kEnterAnimationFade);
	GLX::AddAbsolute(window->GetForeground(), cursor, window->GetMousePosition());

	//attach callbacks to window, so if window is destroyed they wont be called
	SetAbstractProperty(window, "dragdrop_clock", GLX::CreateAnimationClock([window, cursor](Float)
	{
		cursor->SetPosition(window->GetMousePosition());
	}));
	auto run_opacity_animation = [cursor](GLX::Object & drop_target)
	{
		auto fade = GLX::CreateOpacityAnimation("opacity", 0.75f, 0.9f);
		if (IsNull(drop_target)) fade->Flip();
		GLX::Run(cursor, "opacity", 0.25f, fade);
	};
	run_opacity_animation(Null<GLX::Object>());	//call now to apply initial fade to cursor

	SetAbstractProperty(window, "dragdrop_target", GLX::CreateDragDropTargetListener([this, run_opacity_animation](GLX::Object & drop_target)
	{
		m_drop_target.Load()->ClearState("dragover");	//using Reflex::Detail::WeakRef to cover case drop_target might be stack-based
		m_drop_target.Store(drop_target);
		drop_target.SetState("dragover");

		run_opacity_animation(drop_target);
	}));
	SetAbstractProperty(window, "dragdrop_end", GLX::CreateDragDropEndListener([this, window, cursor]()
	{
		GLX::Exit(cursor, true);	//detach cursor with fade

		m_drop_target.Clear();

		UnsetAbstractProperty(window, "dragdrop_clock");
		UnsetAbstractProperty(window, "dragdrop_target");
		UnsetAbstractProperty(window, "dragdrop_end");	//important always delete containing lambda last
	}));
});

The above is stil a fairly basic solution, if you support multiple payload types, a convenient pattern is to register a small factory per drag-data type and choose the preview object in CreateDragDropBeginListener. This lets file drags, object drags, and generic property payloads each supply their own visual cursor

In a multi-instance plugin environment, take care to do this once, otherwise each window would create a mousecursor.

Notes

  • The drag payload is any Reflex::Object; Data::PropertySet is a convenient generic choice.
  • kDragDropTender is the gatekeeper event. If it does not return true, the target will not receive the rest of the drag lifecycle.
  • The 'examples/Drag & Drop Demo' project shows a complete working pattern, including moving UI objects between rows and restoring the source when the drop is not accepted.

Globals

kDragDropEnter, kDragDropLeave, kDragDropReceive, kDragDropReceiveExternal, kDragDropTender

Functions

CancelDragDrop, CreateDragDropBeginListener, CreateDragDropEndListener, CreateDragDropTargetListener, ExceedsDragThreshold, GetDragDropData, QueryDragDropData, StartDragDrop


Reflex > GLX > Layout

GLX layout is handled by a layout model attached to each Object. By default every Object uses the standard layout model (kStandardLayout), which supports common “box layout” behaviors: inline flow, inline flex, float (pin), stretch (fill), plus absolute positioning.

You typically don’t write layout code directly. Instead:

  • Set the parent’s flow (how it arranges inline children) via SetFlow.
  • Set a positioning mode for each child (how the parent treats that child) via the AddXXX or EnableXXX helpers.
  • Optionally enable auto-fit on the parent (size-to-content).

A useful mental model:

  • Parents control *flow*.
  • Children control *positioning*.

Layout Models

Each Object owns a layout model (GLX::Detail::LayoutModel), which can be changed via Object::SetLayoutModel.

The following standard models are provided:

  • kStandardLayout: single-line inline flow, plus float/overlay, and absolute positioning.
  • kStandardLayoutWrapped: identical behavior, but inline children wrap onto new rows/columns when space runs out.

Custom layout models can be implemented by deriving from GLX::Detail::LayoutModel, but this is a secondary API. Most UIs should use the standard model and helpers described below.

Parent Flow

The parent controls how *inline* children are arranged. This includes the primary axis, optional inversion, and optional centering of the inline group.

  • GLX::SetFlow(parent, GLX::kFlowX): inline children flow left -> right.
  • GLX::SetFlow(parent, GLX::kFlowY): inline children flow top -> bottom.
  • GLX::kFlowInvert: reverse the flow direction.
  • GLX::kFlowCenter: center the inline group along the primary axis.

auto panel = New<GLX::Object>();
GLX::SetFlow(panel, GLX::kFlowY);                        //vertical stack
//GLX::SetFlow(panel, GLX::kFlowY | GLX::kFlowCenter);   //stack centered as a group

Flow only affects children in *inline* mode. Float and absolute children ignore flow entirely.

Auto-fit (size to content)

Auto-fit is enabled by default. This means an object will grow its content size to accommodate its children.

A child’s contribution to content size is derived from:

  • Its style size / max properties.
  • Its render layers (e.g. Text layers naturally size to text).
  • Its own children, if auto-fit is enabled on that child.

GLX::EnableAutoFit(panel, true, true);		//fit width + height to children
//GLX::EnableAutoFit(panel, true, false);	//fit width only

Auto-fit is commonly disabled on large scrolling containers, but useful for popups, pills, menus, and small panels.

Child Positioning Modes

Each child has a positioning mode that is interpreted by its parent’s layout model.

In the standard model, this is set either:

  • When adding the child via AddInline / AddFloat / AddAbsolute
  • Explicitly via EnableInline / EnableFloat / EnableAbsolute.

The AddXXX helpers are convenience functions that:

  • Configure the child’s positioning mode (via the EnableXXX helpers)
  • Attach the child to the parent (via Object::SetParent).

As positioning mode and position are child-properties, they persist when changing the parent:


auto parent = New<GLX::Object>();
auto another_parent = New<GLX::Object>();
auto child = New<GLX::Object>();

GLX::AddFloat(parent, child, GLX::kAlignmentTopRight);

child->Detach();
child->SetParent(another_parent); //child is still pinned top-right on the new parent

GLX::EnableFloat(child, GLX::kAlignmentBottomLeft);	//now moved to bottom left

Inline Positioning

Inline children participate in the parent’s flow. They are laid out sequentially along the flow axis.

The Orientation parameter controls placement on the *orthogonal axis* (perpendicular to flow):

  • kOrientationNear: near edge (top or left)
  • kOrientationCenter: centered on the cross axis
  • kOrientationFar: far edge (bottom or right)
  • kOrientationFit: stretch to available cross-axis space (default)

auto row = New<GLX::Object>();
GLX::SetFlow(row, GLX::kFlowX);

auto icon = New<GLX::Object>();
auto label = New<GLX::Label>(L"Settings");

GLX::AddInline(row, icon, GLX::kOrientationCenter);  //y centered (ortho axis) on row
GLX::AddInline(row, label);                          //fit to row height

Inline Flex

Using flex allows the child to expand along the *primary axis* to consume remaining space.

Conceptually similar to “flex: 1”.


auto header = New<GLX::Object>();
GLX::SetFlow(header, GLX::kFlowX);

auto title = New<GLX::Label>(L"Project");
auto spacer = New<GLX::Object>();
auto close = New<GLX::Button>(L"X");

GLX::AddInline(header, title, GLX::kOrientationCenter);
GLX::AddInlineFlex(header, spacer);
GLX::AddInline(header, close, GLX::kOrientationCenter);

The remaining space is distributed across all children with flex enabled evenly.

To achieve an exact 50% / 50% horizontal split, enable flex on both children and disable horizontal auto-fit so their size is not influenced by content:


auto container = New<GLX::Object>();
GLX::SetFlow(header, GLX::kFlowX);

auto left = New<GLX::Label>(L"Left");
auto right = New<GLX::Button>(L"Right");

GLX::EnableAutoFit(left, false, true);    //Important: prevent content from affecting width
GLX::EnableAutoFit(right, false, true);

GLX::AddInlineFlex(container, left);
GLX::AddInlineFlex(container, right);

Float Positioning

Float positions a child relative to the parent’s bounds but removes it from inline flow. Floated children do not affect layout of inline siblings.

Float positioning can be specified using:

  • Alignment enum (single value)
  • Two orientations (x, y)

auto view = New<GLX::Object>();

auto badge = New<GLX::Label>(L"NEW");
GLX::AddFloat(view, badge, GLX::kAlignmentTopRight);

auto spinner = New<GLX::Object>();
GLX::AddFloat(view, spinner, GLX::kAlignmentCenter);

auto sticky_footer = New<GLX::Object>();
GLX::AddFloat(view, sticky_footer, GLX::kOrientationFit, GLX::kOrientationFar);	//note use of 2nd variant with Orientation enum

Orientation values in float mode mean:

  • Near: anchor to start edge
  • Center: center on that axis
  • Far: anchor to end edge
  • Fit: stretch along that axis

Stretch (fill parent)

Stretch is a convenience mode for the most common float case: filling the parent on both axes.

It is shorthand for enabling float with kOrientationFit on X and Y.


GLX::AddStretch(parent, child);	//same as GLX::AddFloat(parent, child, GLX::kOrientationFit, GLX::kOrientationFit)

Absolute (explicit coordinates)

Absolute places a child at an explicit position in the parent’s coordinate space.


auto canvas = New<GLX::Object>();
auto node = New<GLX::Object>();
GLX::AddAbsolute(canvas, node, {120.0f, 80.0f});

Genuine use-cases for absolute positioning are very rare, and should generally be avoided.

The majority of cases where you may think you need absolute positioning are likely covered by use of GLX::Scroller / GLX::Zoomable or custom drawing.

Furthermore, typical genuine cases for absolute positioning will require a custom layout model, to ensure the child positions are updated in response to the parent size (during the Align phase)

Standard Layout Variants

When used correctly the standard layout model will cover most use cases in typical UI layouts. The additional standard layout variants cover most remaining use cases:

kStandardLayoutWrapped

To allow inline children to wrap, set the parent’s layout model to kStandardLayoutWrapped.

Child APIs remain unchanged.

Typical use cases:

  • Tag strips / pill lists
  • Wrapping toolbars
  • Flow-style grids without custom layout code

Typedefs

Point, Rect, Size

Functions

AddAbsolute, AddFloat, AddInline, AddInlineFlex, AddStretch, BranchContains, CalculateAbs, CalculateAbsoluteRect, CalculateRelativeRect, EnableAutoFit, GetBounds, LookupBranchIndex, LookupChildAtIndex, LookupIndex, QueryChildById, QueryElementById, SetFlow

Enums

Alignment, FlowFlags, Orientation

Object Types

Object

Value Types

Range


Reflex > GLX > Styling

GLX supports a powerful visual styling system designed to clearly separate presentation from layout. Layout logic is implemented in C++ or Reflex VM code, while stylesheets remain no-code, making them accessible to designers without impacting development.

Stylesheets avoid the unpredictable inheritance rules of CSS while still enabling reuse via includes, states, and the inherit: property.

The unique bg/fg layer concept allows highly rich designs to be expressed almost entirely through vector primitives. With a wide set of predefined render layers, you can build fully resizable, vectorised UIs that eliminate reliance on pre-baked bitmaps and bloated binaries. Layers support advanced anti-aliasing for crisp visuals at any resolution.

Dynamic property binding (&property) further extends styles into the runtime, powering fluid, data-driven animations and live effects.

Stylesheets

Load a stylesheet from disk or resources using RetrieveStyleSheet.

A StyleSheet is itself a Style, so you typically apply it to your root view.


auto sheet = GLX::RetrieveStyleSheet(L":res:MyApp/styles.txt");
view->SetStyle(sheet);

Recommended: use Bootstrap::SetStyleSheet for hot-reload during development.


Bootstrap::SetStyleSheet(view, L":res:MyApp/styles.txt");

Style lifecycle and OnSetStyle

Whenever a style is set or hot-reloaded, the framework calls OnSetStyle(const Style & style) on that object.

This is the right place to style sub-objects so they also update on hot-reload.


void MyApp::View::OnSetStyle(const GLX::Style & style)
{
	auto header_style = style["Header"];
	auto tab_style = header_style["Tab"];

	m_header->SetStyle(header_style);
	for (auto & tab : m_header) tab->SetStyle(tab_style);
}

Common Properties

Styles support a core set of properties that define sizing, spacing, colors, layers, and state transitions.

Margin properties

The 'margin' and 'padding' properties accept 1, 2, or 4 float values.

By default, values use Reflex ordering:


margin: 24;           // all edges
margin: 16,8;         // width, height
margin: 12,8,12,8;    // left, top, right, bottom

You can opt into CSS-style ordering:


#option margin_syntax css
margin: 24;           // all edges
margin: 16,8;         // vertical, horizontal
margin: 12,8,12,8;    // top, right, bottom, left

Margins define spacing outside the object's bounds, while padding defines spacing inside, around its content.

While margin and padding are set on Style's, most layers also accept an 'indent' property. e.g:


bg: Fill(indent: 8,12; corner: 4);

Size properties

'size' and 'max' accept 1 or 2 floats:


size: 64;        // 64 x 64
size: 200,48;    // width 200, height 48
max: 400,200;    // maximum size

'size' sets the minimum content size, while 'max' constrains it.

Color properties

The 'color' (or 'colour') property sets the "pen" colour, whereas 'bg_color' (or 'bg_colour') defines a rectangular fill colour. Colors always use 0-255 ranges for components, including the alpha channel.


colour: 128;                // greyscale
colour: 128,128;            // greyscale + alpha
colour: 255,128,0;          // RGB
bg_colour: 0,128,255,192;   // RGBA

You can also set colour via hex codes:


colour: $FF0000;

These map to normalized (0.0f - 1.0f) floats.

Most layers also support a colour property, which is multiplied by the "pen" colour.


bg: Border(width: 2; corner: 4; colour: 255,0,0);

Opacity

The 'opacity' property is a normalized single float value (0.0 - 1.0), applied multiplicatively to all layers and children of an object.


opacity: 0.75;

Layers

Each object supports two layer arrays: 'bg' (drawn before children) and 'fg' (drawn after children). Typically use bg, but for cases where rendering "on top of" content is required, use fg (e.g. InnerShadow).

Both properties take an array of layers. Layers can be freely combined, but the draw order is strictly in the order specified.


bg: Fill(colour: 240), Border(colour: 0, width: 1);
fg: Text(value: &label, font: Small, colour: 0);

Grouping layers

Some layers (such as Mask and Align) are grouping layers that include a 'content' property, which is itself a nested array of layers:


bg:
Mask
(
	mask: Fill(corner: 16);

	content:
	Image(source: artwork; fit: cover),
	Border(corner: 16; width: 2);
);

Transition time

The 'transition' property combined with @State selection is the simplest way to do a basic animation. It sets the blending time in seconds when switching between states.


transition: 0.25; // quarter second blend

States

States are style variants that override properties when active. They appear as @State blocks inside a style and are applied in top-to-bottom order, so later states override earlier ones when both are active.

Push and clear states in code with Object::SetState(Key32) and Object::ClearState(Key32). Hover and focus are the only built-in states and are managed by GLX::Object automatically.


Button:
{
	transition: 0.25;
	size: 64;

	@State hover:
	{
		bg: Fill(colour: 236);
	};

	@State selected:
	{
		bg: Fill(colour: 80,176,240);
		fg: Text(colour: 255);
	};

	@State inactive:
	{
		fg: Text(colour: 128);
	};
}

Nested states let you specify combinations explicitly.


Button:
{
	@State inactive:
	{
		@State selected:
		{
			bg: Fill(colour: 96,120,140);
		};
	};
}

The Select, SelectChildren, SelectBranch helper functions set/unset the "selected" id. Activate(object, false) pushes "inactive" and disables input on that object.

Includes and Resources

Stylesheets can include other sheets for reuse and structure.


include: ["common.txt", "controls.txt"];

Stylesheets can define resources for use in layers like Text, Image, TextEdit.


@Font app_font:
{
	path: ":res:MyApp/fonts/Inter-Regular.ttf";
	size: 14;
};

@Bitmap logo:
{
	path: ":res:MyApp/bitmaps/logo.png"; // png,bmp supported; jpg mostly supported
};

Header:
{
	fg: Text(font: app_font; colour: 32; value: &title);
	bg: Image(source: logo; fit: contain);
}

Resources can be declared at the root of the sheet for global access or nested inside a style for specificity.

Inheritance and Aliasing

Stylesheets support reuse and composition through the inherit property and the special @Alias directive.

Using inherit, a style copies all properties from another style before applying its own overrides. This allows you to define a base style and then extend or specialize it with minimal duplication:


BigButton:
{
	inherit: Button;
	size: 128;
};

The inherit property accepts either a single style ID, or a path to a nested sub-style. For example, to inherit from a sub-style defined inside another style:


inherit: Dialog > Header;

Additionally, the @Alias style variant lets you reuse a style as a named property of another style. Unlike inherit, aliasing does not copy values, and instead directly references the source definition:


Popup:
{
	inherit: Button;
	@Alias menu; // alias a menu property defined previously
};

This makes aliasing useful when you want multiple styles to share the same reference (for example, to the same menu definition or resource) rather than duplicating properties.

Dynamic Binding

Any style property value can bind to a code-side variable by using &name. This allows live updates and animations without reapplying styles.

Commonly used for Text value or colours.


// Stylesheet
Flashing:
{
	size: 64;
	bg: Fill(corner: 12; colour: &dynamic_colour);
}

// C++
View::View()
{
	m_flashing = GLX::Init(New<GLX::Object>(), style["Flashing"]);
	SetProperty(m_flashing, "dynamic_color", New<GLX::ColorObject>(GLX::kWhite));
	GLX::AddFloat(*this, m_flashing, GLX::kAlignmentCenter);
	EnableOnClock(); // enable OnClock(delta) callback
}

void View::OnSetStyle(const GLX::Style & style)
{
	m_flashing->SetStyle(style["Flashing"]);
}

void View::OnClock(Float delta_seconds)
{
	auto colour = QueryProperty<GLX::ColorObject>(m_flashing, "dynamic_color");
	colour->r = /* update channel over time */;
	colour->g = /* update channel over time */;
	colour->b = /* update channel over time */;
	m_flashing->Redraw(); // trigger layer repaint
}

Patterns and tips

  • Use OnSetStyle to style sub-objects so hot-reload propagates automatically.
  • Order @State blocks from least to most dominant: hover - selected - inactive. Use nested states for combinations.
  • Prefer binding (&name) for any property you want to animate or update at runtime.
  • Use hierarchical lookup style["Section"]["Title"] instead of duplicating properties.
  • Put common primitives in included sheets to keep app sheets small and focused.

Typedefs

Colour, MouseCursor, Points

Functions

Activate, ActivateBranch, ClearText, FindStyle, GetClip, GetOpacity, GetText, IsActive, IsSelected, RGB, Rescale, RetrieveStyleSheet, Rotate, Select, SelectBranch, SelectChildren, SetBounds, SetClip, SetOnStyle, SetOpacity, SetState, SetText, ToggleState, Translate, UnsetBounds, UnsetClip, UnsetOpacity

Object Types

Style, StyleSheet

Value Types

Margin


Reflex > GLX > Styling > Canvas

Reflex GLX provides three canvas entry points for custom, code-driven drawing, ordered from simplest and fastest to most advanced:

  • SetCanvas for monochrome geometry, using Array<Point> output.
  • SetColourCanvas for multi-colour geometry, using Array<ColourPoint> output.
  • SetGraphicCanvas for advanced cases where you need direct access to graphics, images, fonts, or a custom Graphic that can change every frame.

In stylesheets, these correspond to:

  • bg: Canvas();
  • bg: ColourCanvas();
  • bg: GraphicCanvas();

Execution model

These canvas callbacks are not immediate-mode draw calls in the traditional sense. They are not invoked every frame just because the object is visible.

Instead, the callback prepares geometry for the renderer, effectively building VBO data that can be reused until something changes.

To request a new canvas build, call Redraw() on the object. For example:


m_object.Redraw();

Other state changes that already imply a visual refresh, such as Update(), Realign(), and related layout-reaccommodation paths, also schedule a redraw.

For animated content, a typical pattern is to trigger redraws from OnClock(Float):


void MyView::OnClock(Float dt)
{
	Redraw();
}

Choosing the right API

Prefer Canvas over ColourCanvas over GraphicCanvas, in that order, for performance and simplicity.

Use Canvas() when you are drawing simple monochrome paths and shapes.

Use ColourCanvas() when your geometry needs per-point colour.

Use GraphicCanvas() only when you need the extra power geometry plus textures (eg image/font) composition (which are non primary APIs).

Style usage

Canvas bindings are usually declared in styles with an optional id. Use the plain form when there is only one binding for the object:


bg: Canvas();

When you have multiple canvas bindings on the same object, give each one an id and bind them explicitly in code:


bg: Canvas(id: logo), Canvas(id: clouds);

SetCanvas(m_obj, {.id = K32("logo")}, draw_logo);
SetCanvas(m_obj, {.id = K32("clouds")}, draw_clouds);

The same pattern applies to colour and graphic canvases:


SetColourCanvas(m_obj, {}, draw_coloured_geometry);
SetGraphicCanvas(m_obj, {}, draw_advanced_graphic);

For Canvas and ColourCanvas, do not clear ctx.output. Append to it only. The system may batch multiple canvases together and compact them into a single VBO.

Simple Demo

A minimal Canvas demo can draw a star shape by appending a closed path to ctx.output:


void StarView::OnSetStyle(const GLX::Style & style)
{
	GLX::SetCanvas(*this, {}, [](GLX::CanvasContext & ctx)
	{
		auto cx = ctx.size.w * 0.5f;
		auto cy = ctx.size.h * 0.5f;
		auto outer = Min(ctx.size.w, ctx.size.h) * 0.42f;
		auto inner = outer * 0.45f;
		GLX::Point star[10];

		for (UInt i = 0; i < 10; i++)
		{
			auto a = -kPif * 0.5f + kPif * 0.2f * Float(i);
			auto r = (i & 1) ? inner : outer;
			star[i] = { cx + Cos(a) * r, cy + Sin(a) * r };
		}

		GLX::AddPath(ctx.output, star, true, 2.0f, GLX::kPathJoinRound, GLX::kPathCapRound);
	});
}

This demo uses SetCanvas because the geometry is monochrome. As with most layers, it will be drawn at the style colour, modulated by its own colour property. Use ColourCanvas only when you need per-point colour, and keep GraphicCanvas for the advanced cases where you need the full Graphic pipeline.

Example project

See examples/Custom Drawing for a practical demonstration of the Canvas API in use.

Typedefs

ColourPoints

Functions

AddDottedLine, AddEllipseFill, AddEllipseOutline, AddPath, AddPointsWithColour, AddPolygonFill, AddRectFill, AddRectOutline, AddRoundedFill, AddRoundedOutline, AddRoundedTriangleFill, AddRoundedTriangleOutline, AddTriangleFill, AddTriangleOutline, SetCanvas, SetColourCanvas, SetGraphicCanvas, UnsetCanvas

Value Types

CanvasContext, ColourCanvasContext, GraphicCanvasContext


Reflex > GLX > Styling > SVG

Reflex provides native SVG support, treating vector assets as first-class resources alongside bitmaps. SVGs can be imported and used in stylesheets via the @SVG resource declaration, or decoded in code for custom drawing via the geometry pipeline.

SVGs in Stylesheets

SVG usage in stylesheets is consistent with @Bitmap resources and the Image layer. Declare an SVG resource, then reference it in a style property.

Importing an SVG

Declare an SVG resource using the @SVG type:


@SVG my_svg_id:
{
	path: "assets/some.svg";
};

Then use it as an Image source:


bg: Image(source: my_svg_id; fit: contain; anchor: center);

SVG Size

@SVG also supports a size property. If the SVG file contains intrinsic dimensions, those will be used by default. However, some SVGs do not specify a size, in which case you must set one explicitly using standard size syntax:


@SVG my_svg_id:
{
	path: "assets/some.svg";
	size: 128;
};

Because SVGs are vector-based, size does not affect rendering quality in the way it does for bitmaps. However, the initial size is important as it affects the initial geometry preparation, including corner steps and other tessellation details.

Multi-Icon SVG Sets

Reflex supports icon sets where multiple icons are defined in a single SVG file using the `<symbol>` tag with unique IDs. Declare the SVG resource once, then select individual icons using the `>` frame selector - the same syntax used for bitmap sprite frames.


@SVG icons:
{
	path: "assets/icons.svg";
};

Reference a specific icon by its symbol ID:


bg: Image(source: icons > circle_icon; fit: contain; anchor: center);

As per-standard stylesheet syntax, if the symbol ID contains characters that are not a single token (such as a dash), wrap it in single quotes:


bg: Image(source: icons > 'circle-icon'; fit: contain; anchor: center);

Advanced Usage: SVG in Code

The examples/SVG Demo project demonstrates parsing an SVG file and coupling it with custom drawing. The basic workflow is:

  • Load the SVG file into memory.
  • Decode it using the XML decoder into a PropertySet.
  • Use InspectSVG and DecodeSVG to convert it into geometry suitable for a VBO.
  • Render using SetColourCanvas (as DecodeSVG produces Array<GLX::ColourPoint>).

C++ Example


void ViewImpl::OnUpdate()
{
	auto xml_bytes = File::Open(m_path_to_svg);
	auto xml = Make<Data::PropertySet>(Data::DecodePropertySet(Data::kReflexXmlFormat, xml_bytes));

	if (auto svgs = GLX::Detail::InspectSVG(xml))
	{
		GLX::SetColourCanvas(m_icon, {}, {[xml, svg = svgs.GetFirst()](GLX::ColourCanvasContext & ctx)
		{
			GLX::Detail::DecodeSVG(ctx.output, svg, size);
		}});
	}
	else
	{
		GLX::UnsetCanvas(m_icon, {});
	}
}

void ViewImpl::OnSetStyle(const GLX::Style & style)
{
	m_icon.SetStyle(style["Icon"]);
}

Note: In the example, capturing 'xml' as a Reference (produced by Make) in the lambda is critical to keep the PropertySet alive for the lifetime of the canvas binding.

Due to the complexity of the SVG format, decoding is expensive and should be avoided per-frame. In the example, the size_z guard in the example ensures the geometry is only re-decoded when the target size actually changes.

Stylesheet for Code-Driven SVG


Icon:
{
	bg: ColourCanvas();		//use Canvas() for monochrome geometry, ColourCanvas() for colour-point geometry
};

Summary

  • Use @SVG to declare SVG resources, with the same Image layer syntax as @Bitmap.
  • Set size explicitly for SVGs that lack intrinsic dimensions; initial size affects geometry preparation.
  • Use the > frame selector with symbol IDs to reference individual icons from multi-icon SVG files.
  • Single-quote symbol IDs that contain dashes or other non-token characters.
  • For custom drawing, decode via the XML pipeline and use InspectSVG / DecodeSVG to produce ColourPoint geometry.
  • Render code-driven SVGs with SetColourCanvas.

Reflex > GLX > Widgets

Widgets are the higher-level interactive controls built on top of GLX::Object and a small set of reusable behaviours.

Most of them are event-driven composites rather than standalone rendering systems: they emit requests such as select, open, load, remove, and transaction updates, and they usually apply named substyles to internal parts such as header, body, footer, item, tab, prev, or next.

A recurring pattern in this module is that visual feedback comes from ordinary GLX state flags such as selected, hover, open, and reorder, so stylesheet state handling is a big part of how these controls are customised.

Many of the classes here are also intended to be assembled together. Form, Selector, Button, and Menu all act as building blocks for larger widgets rather than being isolated controls.

Functions

CloseContextMenu, OpenContextMenu

Enums

SelectionMode

Object Types

AbstractList, AbstractViewBar, AbstractViewPort, Form, Label, List, Menu, Popup, RangeBar, RotarySlider, Scroller, Selector, Split, TabGroup, TextArea, VirtualList, Zoomable


Reflex > SIMD

The Reflex::SIMD namespace provides a high-performance, cross-platform abstraction for Single Instruction, Multiple Data (SIMD) operations. It allows for data-parallel processing of 4-element vectors using platform-specific hardware acceleration (such as SSE, AVX, or NEON) through a unified C++ interface.

Core Types

The library centers around the TypeV4<T> template, which represents a vector of four elements. Specialized typedefs are provided for common data types:

  • FloatV4: A vector of 4 Float32 values.
  • IntV4: A vector of 4 Int32 values.
  • BoolV4: A mask type (using kBooleanTrue or kBooleanFalse) used for conditional SIMD logic.

Initialization and Access

Vectors can be initialized via broadcasting a single value, specifying four distinct values, or loading from memory.


using namespace Reflex::SIMD;

FloatV4 a(1.0f);                    // Broadcast: [1.0, 1.0, 1.0, 1.0]
FloatV4 b(1.0f, 2.0f, 3.0f, 4.0f);   // Explicit:  [1.0, 2.0, 3.0, 4.0]

Float32 data[4] = { 5.0f, 6.0f, 7.0f, 8.0f };
FloatV4 c = LoadUnaligned(data);     // Load from memory

Accessing individual elements can be done via the [] operator or by retrieving a Quad<T> structure. Use GetFirst() to efficiently grab the element at index 0.

Mathematical Operations

Standard arithmetic operators are overloaded to perform component-wise operations across all four lanes simultaneously.

  • Arithmetic: +, -, *, / and MulAdd(a, b, c) (computes a * b + c).
  • Comparison: ==, !=, >, >=, <, <= (returns a BoolV4 mask).
  • Common Math: Abs, Sign, Min, Max, SquareRoot, Reciprocal.
  • Advanced: Exp2, Log2, and LinearInterpolate(t, a, b).

Conditional Logic (Masking)

Because SIMD lanes cannot branch independently, conditional logic is handled via masking and the Select function.


// Choose between 'a' and 'b' based on a condition
BoolV4 mask = a > b;
FloatV4 result = Select(mask, a, b); // result[i] = mask[i] ? a[i] : b[i]

You can also analyze the state of a mask using these helpers:

  • Any(mask): Returns true if at least one lane is true.
  • Full(mask): Returns true if all four lanes are true.
  • Empty(mask): Returns true if no lanes are true.
  • Count(mask): Returns the number of true lanes (0-4).

Shuffling and Transposition

The library provides tools to reorder data within vectors or between two different vectors.


// Reorder elements within a vector
auto swapped = Shuffle<1, 0, 3, 2>(v);

// Interleave values from two vectors
auto lo = InterleaveLo(a, b);

// Transpose a 4x4 matrix represented by four vectors
Transpose(row0, row1, row2, row3);

Horizontal Operations

While most operations are vertical (lane-to-lane), some operations combine values across a single vector:


Float32 total = Sum(my_v4); // Adds all 4 components into a single scalar

Conversion

Explicit conversion between integer and floating-point vectors is required:

  • ToFloatV4(IntV4): Converts integers to floats.
  • ToIntV4(FloatV4): Converts floats to integers (rounding).
  • Truncate(FloatV4): Converts floats to integers using truncation toward zero.

Typedefs

BoolV4, FloatV4, IntV4

Functions

Abs, And, Any, ClipNormal, Count, Empty, Exp, Exp2, Full, GetFlags, GetFree, Invert, Log, Log2, Max, Min, Modulo, Not, Or, Pow, Reciprocal, RoundDown, RoundNearest, Select, SelectNot, Sign, SquareRoot, operator!=, operator&, operator*, operator+, operator-, operator/, operator<, operator<=, operator==, operator>, operator>=, operator|

Enums

Boolean

Value Types

TypeV4


Reflex > System

Reflex is designed so that application code is completely platform-agnostic. When developing with Reflex, there is no need to interact with OS-specific APIs directly - all system functionality is abstracted and implemented in a fully cross-platform way.

The System namespace is responsible for this abstraction layer. It provides the low-level, platform-independent primitives that Reflex builds upon.

Design intent

The System namespace is not intended to be the primary API used by application code.

Instead:

  • Higher-level namespaces (Data, File, GLX etc.) are built on top of System.
  • These higher-level APIs provide safer, more convenient, and more expressive interfaces.
  • System exists to unify OS behavior and expose consistent primitives across all supported platforms.

Typical usage

In most cases, you should use the higher-level APIs rather than System directly.

For example, instead of working with System::FileHandle, use the File namespace for more convenient helper functions,


//Low-level (usually unnecessary)
auto file_handle = Make<System::FileHandle>(path);
Array <UInt8> bytes1(file_handle.GetSize());
auto bytes_read = file_handle->Read(bytes1.GetData(), bytes1.GetSize());
bytes1.SetSize(bytes_read);

//Recommended
auto bytes2 = File::Open(path);

When to use System directly

Direct use of the System namespace is appropriate in advanced or specialized cases, such as:

  • When you need lower-level control than higher-level APIs expose.
  • When implementing new subsystems or extending Reflex itself.
  • When performance, lifetime, or resource management must be handled explicitly.

In these cases, System provides a stable, cross-platform foundation without leaking OS-specific concepts into application code.

Custom Implementations

As most System APIs are defined as pure-abstract interfaces, the System layer acts as an extensibility point as well as an OS abstraction. This allows you to provide alternate implementations (for example, a FileHandle that reads from an in-memory blob, a cloud stream, or a virtual file system) and pass them directly to other APIs operating on the System primitives.

Typedefs

ColourPoint, fPoint, fRect, fSize

Functions

Delete, Exists, GetElapsedTime, GetNumProcessor, GetOperatingSystemVersion, GetPath, GetSystemID, GetTime, IsDirectory, MakeDirectory, Open, Rename

Enums

KeyCode, ModifierKeys, MouseCursor, Path, Response

Object Types

DirectoryIterator, DiskIterator, DynamicLibrary, FileHandle, HttpConnection, Process, Renderer, Renderer::Canvas, Renderer::Graphic, Task, Thread, Window

Value Types

Colour, ReceiveDataFn, ReceiveHeaderFn


API Reference


Reflex

Reflex::CString


using CString = Array <char>;

Reflex::CString::View


using CString::View = ArrayView <char>;

Reflex::Key32

Key32 is Reflex's 32-bit identifier type. It is used extensively throughout the framework anywhere a compact, comparable id is needed.

Common uses include property ids, property addresses, GLX event ids, style ids, and other lightweight keys used to identify behavior or data without storing strings at runtime.

When constructed from a string literal or const char source in normal code, the hash is typically produced at compile time via the consteval constructor path. This makes Key32 convenient to write and very cheap to compare.


Data::SetFloat32(object, "gain", 0.75f);
if (e.id == MakeKey32("MyEvent"))
{
}
auto button_style = style["Button"];

Key32 is also useful when you need to branch on received text without doing repeated string comparisons:


switch (MakeKey32(some_received_string))
{
case MakeKey32("one"):
	break;

case MakeKey32("two"):
	break;
}

Because Key32 stores only the hash value, reverse lookup to the original text is not available unless you explicitly maintain that mapping elsewhere, for example in a KeyMap.


using Key32 = Key <UInt32>;

Reflex::WString


using WString = Array <WChar>;

Reflex::WString::View


using WString::View = ArrayView <WChar>;

Reflex::AcquireProperty


Reference <TYPE> AcquireProperty(Object& object, Key32 id);
Reference <TYPE> AcquireProperty(Object& object, Key32 id, VARGS...);

Reflex::AutoRelease


Reference <TYPE> AutoRelease(TYPE& object);

Reflex::GetAbstractProperty


TRef <Object> GetAbstractProperty(Object& object, Key32 id);
ConstTRef <Object> GetAbstractProperty(const Object& object, Key32 id);

Reflex::GetProperty


ConstTRef <TYPE> GetProperty(Object& object, Key32 id);

Reflex::Join


Array <TYPE> Join(const VARGS...& ...);

Varadic function concatenating 'args' returning an Array.

  • Input args can be Array <TYPE>, ArrayView <TYPE> or TYPE.
  • In the case of CString / WString, null terminated const char * can also be passed as an argument.

Reflex::Left


ArrayView <TYPE> Left(const ArrayView <TYPE>& view, UInt32 position);

Reflex::Lowercase


void Lowercase(const TYPE& string);
char Lowercase(char character);
WChar Lowercase(WChar character);

Reflex::Make


Reference <TYPE> Make(VARGS... args);

Reflex::Merge


void Merge(const ArrayView <TYPE>& view, const ArrayView <TYPE>& delimiter);

Reflex::Mid


ArrayView <TYPE> Mid(const ArrayView <TYPE>& view, UInt32 position);
ArrayView <TYPE> Mid(const ArrayView <TYPE>& view, UInt32 position, UInt32 length);

Reflex::New


TRef <TYPE> New(VARGS... args);

Reflex::RawStringCopy


UInt32 RawStringCopy(const TYPE* from, TYPE* to, UInt32 capacity);

Reflex::RawStringLength


UInt32 RawStringLength(const TYPE* string);
UInt32 RawStringLength(const TYPE* string, UInt32 capacity);

Reflex::Remove


void Remove(Array <TYPE>& array, const TYPE& element_or_array);

Reflex::Replace


Array <TYPE> Replace(const ArrayView <TYPE>& view, const TYPE& element_or_array);

Reflex::ReverseSearch


Idx ReverseSearch(const ArrayView <TYPE>& view, const TYPE& element_or_array);

Reflex::ReverseSplice


Pair < ArrayView <TYPE> , ArrayView <TYPE> > ReverseSplice(const ArrayView <TYPE>& view, UInt32 position);

Reflex::Right


ArrayView <TYPE> Right(const ArrayView <TYPE>& view, UInt32 position);

Reflex::Search


Idx Search(const ArrayView <TYPE>& view, const TYPE& element_or_array);

Reflex::SetAbstractProperty


void SetAbstractProperty(Object& object, Key32 id, TRef <Object> property);

Reflex::Splice


Pair < ArrayView <TYPE> , ArrayView <TYPE> > Splice(const ArrayView <TYPE>& view, UInt32 position);

Reflex::Split


Array < ArrayView <TYPE> > Split(const ArrayView <TYPE>& view, const TYPE& delimiter);

Reflex::ToCString


CString ToCString(const WString::View& string);
CString ToCString(const WString& string);
CString ToCString(UInt32 value);
CString ToCString(UInt64 value);
CString ToCString(Int32 value);
CString ToCString(Int64 value);
CString ToCString(Float32 value, UInt32 precision, bool discard_zeros);
CString ToCString(Float64 value, UInt32 precision, bool discard_zeros);

Converts a value to a CString.

  • string: Source string to convert.
  • value: Numeric value to convert.
  • precision: Number of digits after the decimal point (floating-point overloads).
  • discard_zeros: If true, trailing fractional zeros are removed.
  • return: Resulting CString.

Supported conversions include:

  • WString and WString::View -> CString
  • Signed and unsigned integers -> CString
  • Floating-point values -> CString

Floating-point overloads format the value using the specified precision and optional trailing-zero trimming.

Reflex::ToFloat32


Float32 ToFloat32(const CString::View& string);
Float32 ToFloat32(const WString::View& string);

Converts a string to a Float32.

  • string: Source string to convert.
  • return: Parsed 32-bit floating-point value.

Supported conversions include:

  • CString::View -> Float32
  • WString::View -> Float32

The string is interpreted as a base-10 floating-point value. Behaviour is undefined if the string does not contain a valid numeric representation.

Reflex::ToFloat64


Float64 ToFloat64(const CString::View& string);
Float64 ToFloat64(const WString::View& string);

Converts a string to a Float32.

  • string: Source string to convert.
  • return: Parsed 64-bit floating-point value.

Supported conversions include:

  • CString::View -> Float64
  • WString::View -> Float64

The string is interpreted as a base-10 floating-point value. Behaviour is undefined if the string does not contain a valid numeric representation.

Reflex::ToInt32


Int32 ToInt32(const CString::View& string);
Int32 ToInt32(const WString::View& string);

Reflex::ToInt64


Int64 ToInt64(const CString::View& string);
Int64 ToInt64(const WString::View& string);

Reflex::ToRegion


ArrayRegion <TYPE> ToRegion(const ArrayRegion <TYPE>& value);
ArrayRegion <TYPE> ToRegion(Allocation <TYPE>& allocation);
ArrayRegion <TYPE> ToRegion(Array <TYPE>& value);

Reflex::ToUInt32


UInt32 ToUInt32(const CString::View& string);
UInt32 ToUInt32(const WString::View& string);

Converts a string to a UInt32.

  • string: Source string to convert.
  • return: Parsed unsigned 32-bit integer value.

Supported conversions include:

  • CString::View -> UInt32
  • WString::View -> UInt32

The string is interpreted as a base-10 integer. Behaviour is undefined if the string does not contain a valid unsigned integer representation.

Reflex::ToUInt64


UInt64 ToUInt64(const CString::View& string);
UInt64 ToUInt64(const WString::View& string);

Converts a string to a UInt32.

  • string: Source string to convert.
  • return: Parsed unsigned 64-bit integer value.

Supported conversions include:

  • CString::View -> UInt64
  • WString::View -> UInt64

The string is interpreted as a base-10 integer. Behaviour is undefined if the string does not contain a valid unsigned integer representation.

Reflex::ToView


ArrayView <TYPE> ToView(const ArrayView <TYPE>& value);
ArrayView <TYPE> ToView(const ArrayRegion <TYPE>& value);
ArrayView <TYPE> ToView(const TYPE& value);
ArrayView <TYPE> ToView(const Allocation <TYPE>& allocation);
ArrayView <TYPE> ToView(const Array <TYPE>& value);

Reflex::ToWString


WString ToWString(const CString::View& string);
WString ToWString(const CString& string);
WString ToWString(UInt32 value);
WString ToWString(UInt64 value);
WString ToWString(Int32 value);
WString ToWString(Int64 value);
WString ToWString(Float32 value, UInt32 precision, bool discard_zeros);
WString ToWString(Float64 value, UInt32 precision, bool discard_zeros);

Converts a value to a WString.

  • string: Source string to convert.
  • value: Numeric value to convert.
  • precision: Number of digits after the decimal point (floating-point overloads).
  • discard_zeros: If true, trailing fractional zeros are removed.
  • return: Resulting WString.

Supported conversions include:

  • CString and CString::View -> WString
  • Signed and unsigned integers -> WString
  • Floating-point values -> WString

Floating-point overloads format the value using the specified precision and optional trailing-zero trimming.

Reflex::UnsetAbstractProperty


void UnsetAbstractProperty(Object& object, Key32 id);

Reflex::Uppercase


void Uppercase(const TYPE& string);
char Uppercase(char character);
WChar Uppercase(WChar character);

Reflex::Address

Reflex::Address is the internal key used to identify a property address. It combines the property id and property type into a single value.

You do not usually use it directly in normal code, because the type portion is typically selected implicitly via the property template or typed accessor you call.

Lower-level object property callbacks such as Object::OnSetProperty receive a Reflex::Address to describe which typed property is being accessed.


Key32 Address::id;

UInt32 Address::type_id;

template Reflex::Array <TYPE>

Type-safe dynamic contiguous container.

Array stores elements in contiguous memory with explicit size and capacity management. It supports value types and object types, with optimized paths for raw-copyable items.

Storage and Allocation

Array owns its storage through an Allocator reference.

  • Capacity grows on demand via Allocate(...) or automatically via push/append operations.
  • Over-allocation mode is supported to reduce reallocations during growth.
  • Compact() reallocates to current size.

Clear() destroys active elements and keeps capacity. The destructor clears elements and releases owned memory.

Size and Mutation

Array supports SetSize(...), Expand(...), Shrink(...), Push(...), Pop(), Insert(...), Remove(...), and Append(...).

For non-trivial element types, constructors and destructors are called correctly during growth, shrink, and relocation.

For raw-constructible/copyable element types, operations use bulk memory paths for performance.

Null-Terminated Mode

If TYPE is marked null-terminated, Array keeps an additional trailing terminator element that is maintained after each mutation.

This mode enables string-like usage from the same container type while preserving explicit size.

Access and Iteration

Array provides indexed access, first/last accessors, direct data pointer access, and forward/reverse iteration.

Bounds are validated with assertions for index-based operations.

Usage Example


Array<int> values;
values.Push(10);
values.Push(20);
values.Insert(1, 15);
values.Remove(0);

for (auto & v : values)
{
	// v = 15, then 20
}

void Array::Allocate(UInt32 capacity);

UInt32 Array::GetCapacity();

void Array::Compact();

void Array::Clear();

TYPE& Array::Push();
TYPE& Array::Push(const TYPE& value);

void Array::Pop();

TYPE& Array::Insert(UInt32 idx);
TYPE& Array::Insert(UInt32 idx, const TYPE& value);

void Array::SetSize(UInt32 size);

UInt32 Array::GetSize();

TYPE& Array::operator[](UInt32 idx);
const TYPE& Array::operator[](UInt32 idx);

bool Array::operator bool();

TYPE* Array::GetData();
const TYPE* Array::GetData();

void Array::Append(const ArrayView <TYPE>& values);

bool Array::operator<(const ArrayView <TYPE>& value);

bool Array::operator==(const ArrayView <TYPE>& value);

bool Array::operator!=(const ArrayView <TYPE>& value);

template Reflex::ArrayRegion <TYPE>


TYPE* ArrayRegion::data;

UInt32 ArrayRegion::size;

template Reflex::ArrayView <TYPE>


const TYPE* ArrayView::data;

UInt32 ArrayView::size;

template Reflex::ConstReference <TYPE>

template Reflex::ConstTRef <TYPE>

Reflex::Float32

Reflex::Float64

template Reflex::Function <RTN(VARGS...)>

Type-erased callable wrapper with semantics similar to std::function, but always safe to invoke.

An empty Function is implicitly bound to a null function that returns a default-constructed value. As a result, calling an unset Function is well-defined and never undefined behavior.

When the return type is not default-constructible, the function must be bound at construction and cannot exist in the empty (null-bound) state. This is enforced at compile time.

Clear() resets the function to the same null-bound state. This operation is likewise unavailable for non-default-constructible return types.

The function converts to bool, evaluating to true when a callable is bound and false when in the null-bound state.


Function <Int32(Int32)> fn;

output.Log(True(fn));     //logs false

auto v = fn(10);             //safe, returns default int (0)

fn = [](Int32 x)
{
	return x * 2;
};

output.Log(True(fn));    //logs true

fn.Clear();                 //resets to null function

void Function::Clear();

RTN Function::Invoke(VARGS...);

RTN Function::operator()(VARGS...);

Reflex::Idx

Lightweight integral index type used to represent either a valid position within an indexable container or an invalid/unset state.

Idx is typically used as the return type for search and lookup operations. A special sentinel value represents an invalid or not-found index.

  • Integral type, cheap to copy and compare.
  • Intended for indexing into contiguous or indexable data structures.
  • Casts to bool to indicate set/valid (true) or unset/invalid (false).

if (Idx idx = Search(array, value))
{
	auto & item = array[idx.value];
}

UInt32 Idx::value;

bool Idx::operator bool();

Reflex::Int16

Reflex::Int32

Reflex::Int64

Reflex::Int8

template Reflex::Item <TYPE>

Intrusive list node base type.

Item provides link storage and membership operations for objects that participate in List.

Membership Model

An Item belongs to at most one List at a time.

Attaching an item automatically detaches it from any current list before linking into the target list.

Destroying an attached item unlinks it safely from its list.

Positioning APIs

Item supports:

  • Attach(list) to append at the end.
  • InsertBefore(item) and InsertAfter(item) for relative insertion.
  • SendBottom() and SendTop() for reordering within the same list.
  • SetIndex(idx) to move to a target list position.
  • Detach() to remove from current list.

GetPrev(), GetNext(), and GetList() expose neighborhood and ownership context.

Lifecycle Hooks

OnAttach() and OnDetach() can be implemented by derived item types.

These hooks are invoked during normal attach/detach operations and allow custom side effects.

Usage Example


struct Entry : public Item<Entry>
{
	using Item::Attach;
	using Item::Detach;
	using Item::InsertBefore;

	void OnAttach() { /* became linked */ }
	void OnDetach() { /* became unlinked */ }
};

List<Entry> entries;
Entry x, y;
x.Attach(entries);
y.InsertBefore(x);
y.Detach();

List <TYPE>* Item::GetList();
const List <TYPE>* Item::GetList();

Item <TYPE>* Item::GetPrev();
const Item <TYPE>* Item::GetPrev();

Item <TYPE>* Item::GetNext();
const Item <TYPE>* Item::GetNext();

template Reflex::Key <TYPE>


TYPE Key::value;

template Reflex::List <TYPE>

Intrusive doubly-linked list container.

List manages ordering and traversal of items that derive from Item<TYPE,...>. The list does not allocate per-node wrappers; link fields live inside each item.

Ownership and Retention

Item can be configured with RETAIN = true or false.

  • With RETAIN = true, attaching an item retains it and detaching releases it.
  • With RETAIN = false, list linkage is non-retaining.
  • List itself stores links only and does not create or destroy item objects.

Core Operations

List exposes GetFirst(), GetLast(), GetNumItem(), Empty(), and boolean conversion.

Item-side APIs (Attach/InsertBefore/InsertAfter/Detach) update list links and counts atomically with respect to list invariants.

List destruction clears membership by detaching all linked items.

Iteration and Safety

Forward and reverse iterators are provided.

In debug builds, iterator dereference validates modification state to catch invalid iteration across structural mutation.

SafeIterate(...) is available for mutation-tolerant traversal patterns.


UInt32 List::GetNumItem();

bool List::Empty();

Item <TYPE>* List::GetFirst();
const Item <TYPE>* List::GetFirst();

Item <TYPE>* List::GetLast();
const Item <TYPE>* List::GetLast();

template Reflex::Map <KEY,VALUE>


void Map::Clear();

TYPE1* Map::Search(const TYPE1& key, TYPE2* fallback);
const TYPE2* Map::Search(const TYPE1& key, const TYPE2* fallback);

TYPE2& Map::Set(const TYPE1& key, const TYPE2& value);

void Map::Unset(const TYPE1& key);

TYPE2& Map::operator[](const TYPE1& idx);
const TYPE2& Map::operator[](const TYPE1& idx);

State::Monitor

Lightweight observer used to detect changes in a State via polling.

A Monitor is connected to a State and records the last observed change count. Calling Poll() reports whether the associated State has changed since the previous poll.

Lifetime Management

State::Monitor does not manage the lifetime of the observed State. It is the caller’s responsibility to ensure that the State remains alive for the duration of the Monitor’s lifetime.

For cases where strong lifetime coupling is required, clients would typically wrap a Monitor together with an owning reference to the State’s owner.


struct RetainingMonitor
{
	RetainingMonitor(Object & owner, State & owner_state)
		: m_owner(owner)
		, m_monitor(owner_state)
	{
	}
	Reference <Object> m_owner; // retains state owner
	State::Monitor m_monitor;
};

void Monitor::Connect(const State& state);

void Monitor::Reconnect();

void Monitor::Disconnect();

bool Monitor::Poll();

template Reflex::Node <TYPE>

Hierarchical intrusive node built from Item + List.

Node combines sibling linkage (as an Item in a parent list) and child ownership/view (as a List of children), enabling tree structures without external wrapper nodes.

Tree Semantics

Each node can have:

  • Zero or one parent (via Item membership).
  • Zero or more children (via inherited List interface).

Attach(parent) links a node as a child of parent.

GetParent() returns the parent node or null for roots.

Traversal Helpers

Node provides parent and branch traversal utilities.

  • ParentRange / ConstParentRange iterate upward through ancestors.
  • BranchIterator / ConstBranchIterator iterate descendants in depth-first order.

LookupBranchIndex(root, node) returns the top-level child index beneath root that contains node.

BranchContains(parent, child) returns true if child lies in parent's ancestry chain.

Usage Example


struct TreeItem  : public Node<WidgetNode>
{
	using Node::Attach;
};

TreeItem root, child, grandchild;
child.Attach(root);
grandchild.Attach(child);

bool inside = BranchContains(root, grandchild);

Reflex::NullType

template Reflex::ObjectOf <TYPE>

ObjectOf<T> wraps a value type inside a Reflex::Object so that it can participate in object-based APIs without designing a dedicated object class.

This is commonly useful when you want to heap-allocate a plain value, store it in a Reference, attach it as a typed property, pass it through generic object channels, or return it from async/task-style APIs.


auto offset = Make<ObjectOf<GLX::Point>>();
object.SetProperty("offset", New<ObjectOf<GLX::Point>>({10, 20}));
auto payload = New<ObjectOf<Function<void()>>>(callback);

The wrapped value is stored in the public .value member.

A useful detail is that ObjectOf<T> automatically provides a null-instance when T is trivially constructible. This makes wrappers such as ObjectOf<bool>, ObjectOf<UInt32>, ObjectOf<Key32>, and similar simple value types integrate cleanly with Reference<T> and TRef<T> default construction.

Reflex::Output


void Output::Log(VARGS... args);

void Output::Warn(VARGS... args);

void Output::Error(VARGS... args);

template Reflex::Pair <TYPE1,TYPE2>

template Reflex::Point <TYPE>


TYPE Point::x;

TYPE Point::y;

template Reflex::Queue <TYPE,SIZE>

Lock-free single-producer single-consumer queue.

Queue is a ring-buffer queue designed for SPSC workloads. It uses atomic read/write positions and does not take locks.

Concurrency Model

Queue is intended for one producer thread and one consumer thread.

  • Producer uses Push(...).
  • Consumer uses Pop(...) or Flush(...).
  • Coordination is performed with atomic indices.

SIZE must be a power of two.

Capacity and Wrap

For fixed-size queues, storage is TYPE m_data[SIZE] and index wrap uses bitmasking.

Push(...) fails and returns false when full.

Pop(...) fails and returns false when empty.

Flush Behavior

Flush(value) reads the latest queued value and advances read position to write position, effectively dropping older pending entries.

Flush() clears all pending entries without returning a value.

Usage Example


Queue<int, 1024> q;

// producer thread
q.Push(42);

// consumer thread
int v = 0;
if (q.Pop(v))
{
	// consumed value
}

void Queue::Push(const TYPE& value);
void Queue::Push(TYPE temp);

bool Queue::Pop(TYPE& out);

bool Queue::Flush(TYPE& last_out);

template Reflex::Rect <TYPE>


Point <TYPE> Rect::origin;

Size <TYPE> Rect::size;

template Reflex::Reference <TYPE>

Reference<T> is the primary ownership wrapper for Reflex objects.

It automatically retains on construction and releases on destruction, giving deterministic RAII-style lifetime management while guaranteeing a valid object reference over its lifetime.

Reference<T> default-constructs to the type's null-instance rather than nullptr. If a type does not define a null-instance, Reference<T> cannot be default-constructed.


Reference<MyClass> ref = New<MyClass>();
ref->DoSomething();

This is broadly equivalent to manual Retain()/Release() management:


MyClass * ptr = New<MyClass>();
ptr->Retain();
ptr->DoSomething();
ptr->Release();

In practice, Reference<T> is safer, clearer, and strongly preferred over manual retain/release handling.

template Reflex::Sequence <KEY,VALUE>


Idx Sequence::Search(const KEY& key);

void Sequence::Clear();

TYPE2* Sequence::SearchValue(const TYPE1& key, TYPE2* fallback);
const TYPE2* Sequence::SearchValue(const TYPE1& key, const TYPE2* fallback);

TYPE2& Sequence::Set(const TYPE1& key, const TYPE2& value);

TYPE2& Sequence::Insert(const TYPE1& key, const TYPE2& value);

TYPE2& Sequence::Acquire(const TYPE1& key, VARGS... ...);

void Sequence::Remove(UInt32 idx);
void Sequence::Remove(UInt32 idx, UInt32 n);

UInt32 Sequence::GetSize();

TYPE2& Sequence::operator[](UInt32 idx);
const TYPE2& Sequence::operator[](UInt32 idx);

template Reflex::Signal <VARGS...>

Type-safe push-based notification primitive.

Signal represents an event that can be emitted with a fixed argument signature. Observers register callbacks (listeners) which are invoked synchronously when the signal is notified.

Signals manage listener registration internally and support multiple independent listeners per signal.

Lifetime Management

Signal stores listeners in an intrusive list and does not retain them. Listener objects are owned by the client.

CreateListener(...) returns an Object representing a listener handle. As notifications are delivered only while this handle remains alive, the receiver would typically store the received Object in a Reference. When the handle is destroyed, the listener automatically disconnects and will no longer be called.

This design guarantees that Notify(...) never calls into a listener that has already been destroyed, without requiring the signal to manage listener lifetimes or the client to explicitly disconnect.

Signal and listener lifetimes are fully independent: it is safe for a Signal to be destroyed before its listeners, and for listeners to be destroyed before the Signal.

Dispatch Safety

At the start of a notification, the current listener list is copied into a temporary dispatch buffer using strong references. This guarantees safe iteration in the presence of re-entrancy and list mutation.

  • Listener attachment and removal during Notify(...) is always safe.
  • Re-entrant calls to Notify(...) are supported.
  • The dispatch set is stable for the duration of the call.

Listeners added while a Notify(...) call is in progress will not receive that notification, and will be invoked starting from the next Notify(...).

Listener removal during dispatch takes effect immediately: if a listener is detached or released such that it is only retained by the internal dispatch buffer, it is skipped and will not be invoked.

Signal::Mute

Supports temporary muting via Signal::Mute.

If a signal is muted, notifications are suppressed until the mute scope is released. Nested mutes are supported.

Usage Example


struct DataStream
	: public Signal<const Packet &>
{
	using Signal<const Packet &>::CreateListener;

	void Receive(const Packet & packet)
	{
		Notify(packet);
	}
};

m_client.SetProperty(K32("listener"), data_stream.CreateListener([](const Packet & packet)
{
	// process incoming data
}));

//disconnect
m_client.UnsetProperty<Reflex::Object>(K32("listener"));

template Reflex::Size <TYPE>


TYPE Size::h;

TYPE Size::w;

Reflex::State

Lightweight change-tracking primitive for pull-based notification.

State represents a mutable condition that can be marked as changed. Observers do not receive callbacks; instead, they poll for changes using a State::Monitor.

State is intended to be used as a base class. Its primary mutation method, Notify(), is protected to enforce strict semantics: only the state owner may publish its own changes. External code can observe state transitions via State::Monitor, but cannot trigger them directly.


struct MyObject :
	public Reflex::Object,
	public Reflex::State
{
	void SetSomething()
	{
		State::Notify();
	}
};

Each call to Notify() increments an internal change counter. Clients use State::Monitor instances to poll for changes since the last observation.

template Reflex::TRef <TYPE>

TRef<T> is Reflex's lightweight non-owning object reference.

Unlike Reference<T>, TRef<T> does not retain or release. Its role is primarily semantic: it expresses temporary access to a valid object without taking ownership.

Like Reference<T>, TRef<T> binds to the type's null-instance rather than nullptr, so it follows the Reflex convention of valid object references instead of nullable smart pointers.


Reference<MyClass> owner = New<MyClass>();
TRef<MyClass> handle = owner;
handle->DoSomething();

Typical uses include:

  • Return values from Create() factory functions
  • Getter return values where ownership remains with the parent object
  • Function parameters indicating the receiver will retain the object

template Reflex::Tuple <VARGS...>


TYPE1 Tuple::a;

TYPE2 Tuple::b;

VARGS... Tuple::c (etc);

Reflex::UInt16

Reflex::UInt32

Reflex::UInt64

Reflex::UInt8

Reflex::WChar

template Reflex::Allocation <TYPE>

Inherits from Object

Fixed-size contiguous object allocation.

Allocation stores a contiguous block of elements with a size chosen at creation time. Unlike Array, Allocation does not track capacity or support growth. The size can only stay the same or be reduced via Shrink(...).

Because the allocation size cannot increase after creation, Allocation is suitable for sharing fixed-size primitive buffers across multiple threads when callers need stable storage and a stable element count.

This does not provide atomic read/write synchronization. Concurrent access still requires the usual external synchronization when multiple threads may read and write the same elements.

Allocation provides indexed access, direct data pointer access, and begin/end iteration in a compact object-owned buffer.


const UInt32 Allocation::size;

void Allocation::Shrink(UInt32 n);

TYPE& Allocation::operator[](UInt32 idx);
const TYPE& Allocation::operator[](UInt32 idx);

TYPE* Allocation::GetData();
const TYPE* Allocation::GetData();

Reflex::Allocator

Inherits from Object

Reflex::Object

Reflex makes a primary, explicit distinction between object-types and value-types. Object types derive from Reflex::Object, are shared, reference counted, typically heap-based (although they can also be instantiated on the stack), and non-copyable by default.

Value types (non-Objects such as UInt32 or Array) are lightweight stack-based primitives. To promote a value into an object so it can participate in reference counting, heap allocation, dynamic properties, or generic object APIs, use ObjectOf<TYPE>.

Reflex::Object provides two foundational capabilities:

  • Deterministic lifetime management via intrusive reference counting
  • A generic interface for attaching and querying typed properties dynamically

Object Lifetime

Reflex uses an intrusive reference counting model built directly into Object.

Unlike std::shared_ptr, COM, or Objective-C, there are no external control blocks or hidden allocations - the reference count lives inside the object itself.

Core Principles

  • All reference-counted types derive from Object
  • The reference count is stored inside the object itself
  • Newly created objects always start with a reference count of zero
  • Every Release() must therefore correspond to a previous Retain()
  • Ownership semantics are primarily expressed through Reference<T> and TRef<T>

Reference <TYPE>

Reference<T> is the primary ownership wrapper for Reflex objects. It retains on construction, releases on destruction, and guarantees a valid object reference over its lifetime.

See Reflex::Reference for full semantics and usage.

TRef <TYPE>

TRef<T> is the lightweight non-owning counterpart to Reference<T>. It does not retain or release, but still follows the Reflex convention of representing a valid object reference rather than a nullable pointer.

See Reflex::TRef for full semantics and usage.

Object Creation

Reflex objects should never be created with raw new/delete directly.

Instead, object creation is performed through the Reflex helper APIs:


auto a = New<MyClass>(args...);
auto b = Make<MyClass>(args...);
  • New<T>() returns a TRef<T>
  • Make<T>() returns a Reference<T>
  • If T is abstract, New<T>() automatically resolves through T::Create()

Make<T>() is generally the most convenient form when immediate ownership is required.

AutoRelease Helpers

AutoRelease() provides a shorthand for constructing temporary Reference<T> wrappers.

The following examples are equivalent:


Reference<HttpConnection> conn = HttpConnection::Create(...);
auto conn = AutoRelease(HttpConnection::Create(...));
auto conn = Make<HttpConnection>(...);

Stack-Allocated Objects

Unlike traditional COM-style systems, Reflex objects may be instantiated directly on the stack.


Data::PropertySet node;

Stack objects ignore Retain() and Release() calls and are destroyed normally via scope lifetime.

This flexibility is useful and efficient for temporaries and embedded members, however ownership must remain explicit: any retained reference must not outlive the stack object it refers to. For example, storing a Reference or TRef to a stack object beyond its lifetime is invalid:


Data::PropertySet node;
auto props = New<Data::PropertySet>();
props->SetProperty("child", node); // invalid after node leaves scope

Null Instances

Reference<T> and TRef<T> default construct using the type's null-instance rather than nullptr.

If a type does not define a null-instance, the wrappers cannot be default-constructed, enforcing validity at compile time.

User-defined object types therefore often require explicit construction:


Reference <View> view1 = New<View>();
Reference <View> view2 = kNewObject;	//shorthand helper

TRef<T> also supports construction using kNoValue for deferred assignment, however this should only be used sparingly.

Manual Retain/Release

Object exposes Retain() and Release(), however direct usage is uncommon.

If manual lifetime management is required:

  • New objects begin with refcount = 0
  • Every Release() must correspond to a previous Retain()
  • Double-release will assert
  • Retaining in constructors and releasing in destructors is the standard pattern

Circular References

Reflex is not a garbage collected system. Circular references will leak unless explicitly broken:


auto a = New<Data::PropertySet>();
auto b = New<Data::PropertySet>();
a->SetProperty("foo", b);
b->SetProperty("foo", a);

Some advanced systems inside Reflex (such as Reflex::VM) provide explicit cycle-breaking mechanisms, however application code should generally avoid circular ownership through design.

Best Practices

  • Prefer Reference<T> for ownership
  • Use TRef<T> for temporary non-owning references
  • Avoid manual Retain()/Release() where possible
  • Avoid circular ownership relationships
  • Prefer clear ownership hierarchies

ObjectOf<T>

ObjectOf<T> wraps a value type inside an Object, allowing values to participate in reference counting, dynamic properties, and generic object-based APIs.

Generic Properties

Reflex::Object defines a virtual interface for attaching, querying, and removing typed properties dynamically via Object::SetProperty, Object::QueryProperty, and Object::UnsetProperty.

This allows sub-objects and arbitrary typed values to be attached to objects at runtime, reducing the need for deep subclassing and enabling highly data-driven systems.

Properties are identified by both id AND type, rather than by id alone. This complements C++'s strong typing model by allowing multiple typed views of the same conceptual property.

Important: Object Does Not Implement the Interface

Reflex::Object itself does not provide a concrete property implementation. Calling SetProperty() on a derived type which does not implement the property callbacks will silently discard the property.

Typically, dynamic properties are implemented through Data::PropertySet, which fully implements the property system for arbitrary types.

Many primary framework classes (such as GLX::Object) derive from Data::PropertySet and therefore support dynamic properties automatically.

For more information on dynamic properties, see Data::PropertySet.


void Object::RetainSt();

void Object::ReleaseSt();

void Object::RetainMt();

void Object::ReleaseMt();

void Object::UnsetProperty(Key32 id);

void Object::SetProperty(Key32 id, TYPE& property);

TYPE* Object::QueryProperty(Key32 id, TYPE& property);

Allocator* Object::GetAllocator();

Reflex > Async

Task::Status


kStatusPending
kStausFailed
kStatusCompleted

Async::CreateClock


TRef <Object> CreateClock(const Function <void()>& callback);

Async::CreatePeriodicClock


TRef <Object> CreatePeriodicClock(Float32 interval, const Function <void()>& callback);

Worker::Context


bool Context::Cancelled();

void Context::SetProgress(Float32 value_normalized);

void Context::SetResult(bool result, TRef <Object> payload);

Async::Task

Inherits from System::Thread


void Task::Cancel();

Float32 Task::GetProgress();

Task::Status Task::GetStatus();

TRef <Object> Task::GetResult();

Async::Worker

Inherits from System::Thread


Reflex > Data

Data::kBinaryFormat


const kBinaryFormat kBinaryFormat;

Data::kJsonFormat

While Reflex’s native formats (text-based PropertySheet and the binary PropertySet format) provide better type support, performance, and integration with the Reflex pipeline, JSON remains the natural choice when exchanging data with external systems - especially web services, scripting environments, and tools that expect standard JSON encoding.

Reflex::Data provides full support for mapping JSON to/from PropertySet, allowing seamless interchange while retaining the benefits of PropertySet’s typed accessors and structured hierarchy.

Decoding JSON into a PropertySet

Use DecodePropertySet with the Data::kJsonFormat format to parse a JSON blob into a standard PropertySet:


Data::Archive json_blob = File::Open(path);
auto json = Data::DecodePropertySet(Data::kJSON, json_blob);

Once decoded, use the dynamic property system to access the properties

For readability/convienence and to minimise template bloat, Data implements helpers for common property types:


auto f = Data::GetFloat32(json, "my_key");
auto s = Data::GetCString(json, "my_string");
auto sub = Data::GetPropertySet(json, "sub_node");

Nested structure, typed values, and optional presence checks behave exactly like any other PropertySet.

Array Properties

Use the Data::Get...Array helpers to retrieve arrays, including Data::GetPropertySetArray for object arrays.


auto items = Data::GetFloat32Array(json, "numbers");
for (auto & i : items)
{
}

Root-level JSON arrays

PropertySet is a map, not an array.

To read a JSON document whose root is an array, use:


auto items = Data::GetPropertySetArray(json, {});  //or kNullKey for constexpr hash of ""
for (auto & i : items)
{
}

Each entry in the returned array is a PropertySet representing one array element.

JSON decoding options

JSON decoding supports configurable options, such as:

  • Decoding all strings as WString instead of CString.
  • Decoding numbers as 64-bit float/int types instead of 32-bit defaults.

These are controlled via the optional Decode options structure passed to `DecodePropertySet`.

Encoding PropertySet to JSON

Encoding follows the same Format interface as all other Data formats, but one key rule applies:

JSON keys must be strings.

PropertySet keys are integer IDs, so each ID must be mapped to a string name.

Registering keys

Before setting values on a JSON PropertySet for encoding, acquire a key map on the root:


Data::PropertySet json;
auto keymap = Data::AcquireKeyMap(json);   // only needed on the root

Then register each string key once:


auto my_key = Data::RegisterKey(keymap, "my_key");
Data::SetFloat32(json, my_key, 22.0f);

After registration, the same integer ID is used for subsequent sets - no repeated string hashing.

Encoding to JSON


auto blob = Data::EncodePropertySet(Data::kJSON, json);
File::Save(path, blob);

The resulting `Data::Archive` contains a UTF-8 JSON document.

Summary

  • Use DecodePropertySet / EncodePropertySet with `kJSON` for full JSON↔PropertySet interoperability.
  • Use typed accessors (GetFloat32, GetCString, GetPropertySet, etc.) exactly as with native PropertySet formats.
  • JSON keys must be string-mapped via AcquireKeyMap / RegisterKey.
  • Root arrays are accessed using `GetPropertySetArray(json, kNullKey)`.
  • JSON decoding/encoding offers flexible type options for strings and numeric widths.

const kJsonFormat kJsonFormat;

Data::kLZ4


const kLZ4 kLZ4;

Data::kPropertySetFormat


const kPropertySetFormat kPropertySetFormat;

Data::kPropertySheetFormat


const kPropertySheetFormat kPropertySheetFormat;

Data::kReflexMarkupFormat


const kReflexMarkupFormat kReflexMarkupFormat;

Data::kReflexXmlFormat

Reflex XML support is for interoperability (eg SVG, simple config).

XML is decoded into a PropertySet tree where:

  • Each XML element is a PropertySet
  • Child elements live in a PropertySetArray stored at the root/empty key (kNullKey)
  • The element tag name is stored in a string property "tag"
  • Attributes (and any other element values) are stored as string properties on the element PropertySet

Decoding XML into a PropertySet


Data::Archive blob = File::Open(path);
auto xml = Data::DecodePropertySet(Data::kReflexXmlFormat, blob);

The returned PropertySet is the root element.

Reading the root tag


if (Data::GetXmlTag(*xml) == "svg")
{
}

XML documents have a single root element.

Iterating child elements

Child elements are stored as a PropertySetArray at kNullKey.


for (auto & child : Data::GetXmlNodes(xml))
{
	auto tag = Data::GetXmlTag(*child);
}

Reading attributes / properties

XML attribute values decode as strings and are stored directly on the node PropertySet.

Use the dynamic property iterator (or your string helpers) to read them.


auto keymap = Data::GetKeyMap(xml);
for (auto & child : Data::GetXmlNodes(xml))
{
	for (auto & [adr, prop] : child->Iterate<Data::CStringProperty>())
	{
		auto name  = Data::GetKey(keymap, adr.id);
		auto value = prop->value;
	}
}

Note: "tag" is also a string property on the node. Skip it if you only want attributes.

Building XML trees (for encoding)

To build an XML structure, create a root PropertySet and attach child nodes via the kNullKey array.

Adding / accessing the child-node array


Data::PropertySet root;
auto nodes = Data::AcquireXmlNodes(root);   // = AcquirePropertySetArray(root, kNullKey)

Adding a child element


auto rect = Data::AddXmlNode(*nodes, "rect");  // creates a PropertySet + sets ktag
Data::SetCString(rect, "x", "2");
Data::SetCString(rect, "y", "3");
Data::SetCString(rect, "width", "10");
Data::SetCString(rect, "height", "10");

To add grandchildren, call AcquireXmlNodes on the child and keep going.


auto g = Data::AddXmlNode(*nodes, "g");
Data::SetCString(g, "transform", "translate(12 12)");
auto g_nodes = Data::AcquireXmlNodes(*g);
auto path = Data::AddXmlNode(*g_nodes, "path");
Data::SetCString(path, "d", "M0 0 L10 0");

Encoding to XML


auto blob = Data::EncodePropertySet(Data::kReflexXmlFormat, root);
File::Save(path, blob);

Practical notes / constraints

  • All XML values are strings (attributes and any stored element values). Convert manually if you need numeric types.
  • Child elements are always in the kNullKey PropertySetArray (AcquireXmlNodes / GetXmlNodes).
  • Tag name lives in the `ktag` CString on each node (GetXmlTag).
  • If your XML subset is “icons/SVG-like”, you can usually ignore namespaces, DTD, entities, etc.

Summary

  • Decode with DecodePropertySet(kReflexXmlFormat, ...)
  • Root element is a PropertySet; tag via GetXmlTag.
  • Children via GetXmlNodes(node) (stored at kNullKey).
  • Build trees with AcquireXmlNodes + AddXmlNode.
  • Attributes are string properties on the node PropertySet.

const kReflexXmlFormat kReflexXmlFormat;

Data::kRiffFormat


const kRiffFormat kRiffFormat;

Data::Archive


using Archive = Array <UInt8>;

Data::Archive::View


using Archive::View = ArrayView <UInt8>;

Data::Float32Property


using Float32Property = ObjectOf <Float32>;

Data::Float64Property


using Float64Property = ObjectOf <Float64>;

Data::Int32Property


using Int32Property = ObjectOf <Int32>;

Data::Int64Property


using Int64Property = ObjectOf <Int64>;

Data::KeyMap


using KeyMap = ObjectOf < Map < Key <UInt32> , Array <char> > >;

Data::UInt32Property


using UInt32Property = ObjectOf <UInt32>;

Data::UInt64Property


using UInt64Property = ObjectOf <UInt64>;

Data::AcquireKeyMap


TRef < ObjectOf < Map < Key <UInt32> , Array <char> > > > AcquireKeyMap(PropertySet& root);

Data::AcquirePropertySet


TRef <PropertySet> AcquirePropertySet(PropertySet& propertyset, Key32 id);
TRef <PropertySet> AcquirePropertySet(PropertySet& propertyset, const ArrayView < Key <UInt32> >& path);

Data::AcquirePropertySetArray


TRef < ObjectArray <PropertySet> > AcquirePropertySetArray(PropertySet& propertyset, Key32 id);

Data::AddPropertySet


TRef <PropertySet> AddPropertySet(ObjectArray <PropertySet>& array);

Data::Assimilate


void Assimilate(PropertySet& target, const PropertySet& source);

Data::BytesToHex


CString BytesToHex(const Archive::View& bytes);

Data::CRC32


UInt32 CRC32(const Archive::View& bytes, UInt32 previous);

Computes a standard 32-bit CRC (Cyclic Redundancy Check) using the IEEE 802.3 polynomial. Intended primarily for error detection or compatibility with external systems that use CRC32.

  • bytes: Binary data to process.
  • previous: Optional seed value. Can be used to continue hashing from a previous result.
  • return: 32-bit CRC value.

Note, if 'previous' is non-zero, the function will continue from that value, allowing incremental CRC accumulation over multiple buffers. This is useful for streaming or chunked data validation.


UInt32 crc = CRC32(chunk1);
crc = CRC32(chunk2, crc);
crc = CRC32(chunk3, crc);

Data::Compress


Archive Compress(const CompressionAlgorithm& algorithm, const Archive::View& bytes);

Data::CopyPropertySet


PropertySet CopyPropertySet(const Format& format, const PropertySet& propertyset);

Data::DecodePropertySet


PropertySet DecodePropertySet(const Format& format, const Archive::View& bytes, UInt32 options);

Data::DecodeUCS2


void DecodeUCS2(WString& output, const Archive::View& bytes);
WString DecodeUCS2(const Archive::View& bytes);

Data::DecodeUTF8


void DecodeUTF8(WString& output, const Archive::View& bytes);
WString DecodeUTF8(const Archive::View& bytes);

Data::DecodeUrlSegment


CString DecodeUrlSegment(const CString::View& view);

Data::Decompress


Archive Decompress(const DecompressionAlgorithm& algorithm, const Archive::View& bytes);

Data::Deserialize


void Deserialize(Archive::View& stream, VARGS...& ...);
TYPE Deserialize(Archive::View& stream);
Tuple <VARGS...> Deserialize(Archive::View& stream);

Restores one or more values from a binary [code Archive::View], in the same order they were previously stored. Two forms are provided for flexibility.

  • stream: Source archive view to decode from.
  • VARGS...: The types to restore, either as references or template parameters.
  • return: Either void (for reference-based restore), or a [code Tuple] containing the restored values.

Restore by references


void Deserialize(Archive::View & stream, VARGS & ...)

Restores values in-place by writing into references.


Int32 a;
CString b;
Restore(stream, a, b);

Restore by return


template <class ...VARGS> Tuple<VARGS...> Deserialize(Archive::View & stream)

Restores values and returns them as a [code Tuple]. If only one type is requested, the tuple decays to that type automatically.


auto [id, name] = Deserialize<Int32,CString>(in);
CString str = Deserialize<CString>(in); // returns single value directly

The input [code stream] is advanced as each value is decoded. The number and order of types must match what was passed to [code Store].

Data::DeserializePropertySet


void DeserializePropertySet(Archive::View& stream, const SerializableFormat& format, PropertySet& out);

Data::DeserializeUCS2


void DeserializeUCS2(Archive::View& stream, WString& string_out);
WString DeserializeUCS2(Archive::View& stream);

Data::DeserializeUTF8


void DeserializeUTF8(Archive::View& stream, WString& string_out);
WString DeserializeUTF8(Archive::View& stream);

Data::EncodePropertySet


Archive EncodePropertySet(const Format& format, const PropertySet& propertyset);

Data::EncodeUCS2


void EncodeUCS2(Archive& output, const WString::View& string);
Archive EncodeUCS2(const WString::View& string);

Data::EncodeUTF8


void EncodeUTF8(Archive& output, const WString::View& string);
Archive EncodeUTF8(const WString::View& string);

Data::EncodeUrlSegment


CString EncodeUrlSegment(const CString::View& view);
CString EncodeUrlSegment(const WString::View& view);

Data::FNV1a32


UInt32 FNV1a32(const Archive::View& bytes, UInt32 previous);

Data::FNV1a64


UInt64 FNV1a64(const Archive::View& bytes, UInt64 previous);

Computes a fast non-cryptographic hash using the FNV-1a algorithm. Suitable for hashing structured binary data (strings, file names, serialized values).

  • bytes: Binary data to hash
  • seed: Optional seed value. Can be used to continue hashing from a previous result
  • return: 64-bit hash value

auto file = Make<System::FileHandle>(path_to_file);
auto h64 = FNV1a64({});
while (auto chunk = File::ReadBytes(file, kMaxUInt16))
{

}

Data::GetBool


bool GetBool(const Object& propertyset, Key32 id, bool fallback_opt);

Data::GetFloat32


Float32 GetFloat32(const Object& propertyset, Key32 id, Float32 fallback_opt);

Data::GetFloat64


Float64 GetFloat64(const Object& propertyset, Key32 id, Float64 fallback_opt);

Data::GetInt32


Int32 GetInt32(const Object& propertyset, Key32 id, Int32 fallback_opt);

Data::GetInt64


Int64 GetInt64(const Object& propertyset, Key32 id, Int64 fallback_opt);

Data::GetKey


CString::View GetKey(const KeyMap& keymap, Key32 key);

Data::GetKey32


Key32 GetKey32(const Object& propertyset, Key32 id, Key32 fallback_opt);

Data::GetKeyMap


ConstTRef < ObjectOf < Map < Key <UInt32> , Array <char> > > > GetKeyMap(const PropertySet& root);

Data::GetPropertySet


ConstTRef <PropertySet> GetPropertySet(const Object& propertyset, Key32 id);
ConstTRef <PropertySet> GetPropertySet(const PropertySet& propertyset, const ArrayView < Key <UInt32> >& path);

Data::GetPropertySetArray


ArrayView < ConstReference <PropertySet> > GetPropertySetArray(const PropertySet& propertyset, Key32 id);

Data::GetUInt32


UInt32 GetUInt32(const Object& propertyset, Key32 id, UInt32 fallback_opt);

Data::GetUInt64


UInt64 GetUInt64(const Object& propertyset, Key32 id, UInt64 fallback_opt);

Data::GetUInt8


UInt8 GetUInt8(const Object& propertyset, Key32 id, UInt8 fallback_opt);

Data::HexToBytes


Archive HexToBytes(const CString::View& hex);

Data::IsHttps


bool IsHttps(const CString::View& url);

Data::MakeUrl


CString MakeUrl(const Url& url);

Data::Merge


PropertySet Merge(const PropertySet& a, const PropertySet& b);

Data::Pack


Archive::View Pack(const TYPE& value);

Converts a value into a binary view without transformation.

Pack operates only on *raw-packable types* - types whose binary representation is stable, contiguous, and platform-independent.

  • value: The value to pack. TYPE must be a raw-packable type.
  • return: An Archive::View referencing the binary representation of the value.

If TYPE is not raw-packable, compilation will fail with an error such as “Non raw-packable type”.

Pack performs no encoding, metadata emission, or length prefixing. The size of the returned view is determined entirely by the value’s binary representation.


UInt32 u32 = 42;
auto view = Data::Pack(u32);      //OK

CString value;                    //typedef of Array<char>
auto view = Data::Pack(value);    //OK

WString value;
auto view = Data::Pack(value);    //ERROR wchar_t size is platform dependent, use Data::EncodeUTF8 instead

This struct is raw-packable:


struct Foo
{
	UInt32 a;
	CString b;
};

Foo foo;
auto view = Data::Pack(foo);	//OK

Whereas this struct is not:


struct Foo
{
	UInt32 a;
	WString b;
};

Foo foo;
auto view = Data::Pack(foo);	//ERROR

Data::ReadLine


bool ReadLine(Archive::View& stream, WString& line_out);
bool ReadLine(Archive::View& stream, CString& line_out);

Reads a single line of text from a memory stream and decodes it as either raw ASCII (CString overload) or UTF-8 (WString overload).

  • stream: An Archive::View containing text data.
  • line_out: Output buffer receiving the decoded line. CString reads raw ASCII bytes; WString decodes from UTF-8.
  • return: true if a line was read (including empty lines); false only when no more lines are available (end-of-file).

//read entire ASCII text file
Data::Archive archive = File::Open(path);
Data::Archive::View stream = archive;	//use ToView for shorthand
CString buffer;
while (Data::ReadLine(stream, buffer))
{
	//process line
}

Data::RegisterKey


Key32 RegisterKey(KeyMap& keymap, const CString::View& string);

Data::ResetPropertySet


void ResetPropertySet(const Format& format, PropertySet& propertyset);

Data::SHA1


Archive SHA1(const Archive::View& bytes);

Computes a 160-bit SHA-1 hash of the input data. Suitable for content identification or integrity checks where a stronger hash than CRC32 is required.

  • bytes: Binary data to hash.
  • return: 160-bit SHA-1 hash result, typically used for content-based identification.

Note, SHA-1 is not considered secure for cryptographic purposes but remains useful for non-security-critical fingerprinting or deduplication.


Archive hash = SHA1(fileData);

Data::SHA256


Archive SHA256(const Archive::View& bytes);

Computes a 256-bit SHA-2 (SHA-256) hash of the input data. Provides strong collision resistance for security-sensitive applications such as signing, verification, or integrity validation.

  • bytes: Binary data to hash.
  • return: 256-bit SHA-256 hash result.

Note, SHA-256 is suitable for cryptographic use cases, including secure hashing of content, signatures, and tokens.


Archive hash = SHA256(fileData);

Data::Serialize


void Serialize(Archive& stream, const VARGS...& ...);

Serializes one or more values into a binary [code Archive]. Supports any combination of raw, structured, or user-defined types with appropriate [code Encode] overloads.

  • stream: Destination archive to append serialized data into.
  • VARGS...: One or more values to serialize. Can be of any supported type.
  • return: (void) The archive is modified in-place.

This function is the core of Reflex's structured serialization system. It can encode basic types, arrays, containers, strings, and custom structures, in order.


Archive out;
Serialize(out, Int32(42), CString("example"), my_object);

The data can later be recovered using [code Restore] with the same types and order.


Archive::View in = out;
Int32 a;
CString b;
MyType c;
Deserialize(in, a, b, c);

Data::SerializePropertySet


void SerializePropertySet(Archive& stream, const SerializableFormat& format, const PropertySet& in);

Data::SerializeUCS2


void SerializeUCS2(Archive& stream, const WString::View& string);

Data::SerializeUTF8


void SerializeUTF8(Archive& stream, const WString::View& string);

Data::SetBinary


void SetBinary(Object& propertyset, Key32 id, const Archive::View& value);

Data::SetBool


void SetBool(Object& propertyset, Key32 id, bool value);

Data::SetCString


void SetCString(Object& propertyset, Key32 id, const CString::View& value);

Data::SetFloat32


void SetFloat32(Object& propertyset, Key32 id, Float32 value);

Data::SetFloat64


void SetFloat64(Object& propertyset, Key32 id, Float64 value);

Data::SetInt32


void SetInt32(Object& propertyset, Key32 id, Int32 value);

Data::SetInt64


void SetInt64(Object& propertyset, Key32 id, Int64 value);

Data::SetKey32


void SetKey32(Object& propertyset, Key32 id, Key32 value);

Data::SetPropertySet


void SetPropertySet(Object& propertyset, Key32 id, TRef <PropertySet> value);

Data::SetUInt32


void SetUInt32(Object& propertyset, Key32 id, UInt32 value);

Data::SetUInt64


void SetUInt64(Object& propertyset, Key32 id, UInt64 value);

Data::SetUInt8


void SetUInt8(Object& propertyset, Key32 id, UInt8 value);

Data::SetWString


void SetWString(Object& propertyset, Key32 id, const WString::View& value);

Data::SplitUrl


Url SplitUrl(const CString::View& url);

Data::SplitUrlResource


Pair < ArrayView <char> , ArrayView <char> > SplitUrlResource(const CString::View& url);

Data::Unpack


void Unpack(const Archive::View& bytes, TYPE& output);
TYPE Unpack(const Archive::View& bytes);

Restores a value from a binary view, previously produced by Pack.

Unpack operates only on *raw-packable types* and reconstructs the value by reading its binary representation.

  • ref: An Archive::View referencing the binary data to unpack.
  • value: Destination for the unpacked value (overload 1).
  • return: The unpacked value (overload 2).

TYPE must match the type used when packing. If TYPE is not raw-packable, compilation will fail with an error similar to "Non raw-packable type".

Unpack performs no decoding, validation, or metadata processing. It simply interprets the referenced bytes as TYPE.

Unpack assumes the binary layout in ref exactly matches TYPE. Mismatched types or incompatible layouts result in undefined behavior.

Use Unpack only with data produced by Pack or when the binary layout is known and stable.


UInt32 original = 42;
auto view = Data::Pack(original);

UInt32 restored;
Data::Unpack(view, restored);	//OK

UInt32 restored = Data::Unpack<UInt32>(view);	//OK

struct Foo
{
	UInt32 a;
	CString b;
};

Foo foo;
auto view = Data::Pack(foo);

Foo restored = Data::Unpack<Foo>(view);

auto restored = Data::Unpack<WString>(view);	//FAIL WString size is platform dependent

Data::UnsetBinary


void UnsetBinary(Object& propertyset, Key32 id);

Data::UnsetBool


void UnsetBool(Object& propertyset, Key32 id);

Data::UnsetCString


void UnsetCString(Object& propertyset, Key32 id);

Data::UnsetFloat32


void UnsetFloat32(Object& propertyset, Key32 id);

Data::UnsetFloat64


void UnsetFloat64(Object& propertyset, Key32 id);

Data::UnsetInt32


void UnsetInt32(Object& propertyset, Key32 id);

Data::UnsetInt64


void UnsetInt64(Object& propertyset, Key32 id);

Data::UnsetKey32


void UnsetKey32(Object& propertyset, Key32 id);

Data::UnsetPropertySet


void UnsetPropertySet(Object& propertyset, Key32 id);

Data::UnsetPropertySetArray


void UnsetPropertySetArray(PropertySet& propertyset, Key32 id);

Data::UnsetUInt32


void UnsetUInt32(Object& propertyset, Key32 id);

Data::UnsetUInt64


void UnsetUInt64(Object& propertyset, Key32 id);

Data::UnsetUInt8


void UnsetUInt8(Object& propertyset, Key32 id);

Data::UnsetWString


void UnsetWString(Object& propertyset, Key32 id);

Data::WriteLine


void WriteLine(Archive& stream, const WString::View& line);
void WriteLine(Archive& stream, const CString::View& line);

Writes a single line of text to a Data::Archive (byte array), encoding the input as either raw ASCII (CString::View overload) or UTF-8 (WString::View overload).

  • stream: Byte array to append the encoded line.
  • line: The line to write. CString::View is written as raw ASCII bytes; WString::View is encoded to UTF-8.

A line terminator is automatically appended. The input string should not include a newline.


Data::Archive stream;

//write plain ASCII
Data::WriteLine(stream, "first line");
Data::WriteLine(stream, "second line");

//write multibyte UTF-8
Data::WriteLine(stream, L"UTF-8 text");
Data::WriteLine(stream, L"日本語");

template PropertySet::PropertyIterator <TYPE>

Data::Url


CString Url::domain;

CString Url::fragment;

UInt16 Url::port;

CString Url::resource;

CString Url::scheme;

Pair < Array <char> , Array <char> > Url::user;

Data::ArchiveObject

Inherits from Object

Data::CompressionAlgorithm

Inherits from DecompressionAlgorithm


UInt32 CompressionAlgorithm::GetMaxCompressedSize(UInt32 size);

UInt32 CompressionAlgorithm::Compress(const Archive::View& input, UInt8* output);

Data::DecompressionAlgorithm

Inherits from Object


bool DecompressionAlgorithm::Decompress(const Archive::View& input, Archive& output);

Data::Format

Inherits from Object

Data::Format defines the interface for encoding and decoding structured data. A given Format implements how to serialize a PropertySet into bytes and how to reconstruct it back into a PropertySet.

Reflex provides several global, constant Format instances covering the common persistence use cases.

Common Formats

kPropertySetFormat

  • Recommended binary format
  • Fast, compact, and supports the full range of Reflex property types (ints, floats, arrays, nested sets, blobs, colours, points, etc)

kPropertySheetFormat

  • Human-readable text format
  • Similar to JSON but with additional type info support (e.g. a sheet can encode various int formats)

kJsonFormat

  • Standard JSON
  • Ideal for interoperability with web APIs and external tooling. Limited to JSON-compatible types

kReflexXmlFormat

  • Supports a sub-set of XML
  • Only attributes and nested elements (ignores text content between tags)

Typical Usage

Encoding


Data::PropertySet data;

Data::SetInt32(data, "version", 1);

auto child = Data::AcquirePropertySet(data, "child");
Data::SetFloat32Array(child, "values", {1.0f, 2.0f});

auto blob = Data::EncodePropertySet(Data::kPropertySetFormat, data);

File::Save(path, blob);

Decoding


auto bytes = File::Open(path);

Data::PropertySet root = Data::DecodePropertySet(Data::kPropertySetFormat, bytes);
if (root)
{
	auto v = Data::GetInt32(root, "version");
	auto sub = Data::GetPropertySet(root, "child");
	auto arr = Data::GetFloat32Array(sub, "values");
}

Choosing a Format

  • Use kPropertySetFormat for application data, presets, configs, or anything Reflex-internal.
  • Use kPropertySheetFormat when you need editable text files or want structured types without JSON’s restrictions.
  • Use kJsonFormat for web communication and external file formats.

void Format::Reset(PropertySet& propertyset);

bool Format::Encode(Archive& out, const PropertySet& data);

bool Format::Decode(PropertySet& out, const Archive::View& data, UInt32 options);

template Data::ObjectArray <TYPE>

Inherits from Object

Data::PropertySet

Inherits from Object

Data::PropertySet is Reflex’s generic container for structured, tree-based data.

PropertySet allows arbitrary data to be attached to objects at runtime, without modifying class definitions or introducing ad-hoc subclasses.

Instead of encoding every variation in a type hierarchy, behavior and state can be composed dynamically by attaching properties as needed.


object.SetProperty("hover_time", 0.0f);
object.SetProperty("user_data", some_ref);

This bridges the performance and safety of C++ with the flexibility typically associated with dynamic languages. It enables rapid iteration, late-bound features, and data-driven behavior while remaining fully type-aware and debuggable.

Generic property system

PropertySet implements the root Reflex::Object property interface (OnSetProperty / OnUnsetProperty / OnQueryProperty).

This provides a uniform, generic property mechanism across the entire framework.

Properties are identified by id AND type, rather than id alone.

This complements C++'s strong typing model by allowing multiple, type-safe views of the same conceptual property without collapsing everything into a single loosely-typed value.


ps.SetProperty("my_id", 1);
ps.SetProperty("my_id", "string");

The example above creates two distinct properties:

  • An Int32 property with id "my_id".
  • A CString property with id "my_id".

Under the hood, this (id + type) pair is represented by Reflex::Address.

Structured and hierarchical data

PropertySet supports hierarchical data by allowing nested PropertySet instances.

This makes it suitable for representing structured trees of runtime state, configuration, or metadata when needed.

Unlike rigid schemas, PropertySet allows structure to evolve organically as systems interact.

Persistence and serialization

PropertySet is serializable, via the Format system.

This makes it suitable for storing presets, state snapshots, configuration files, and interchange data.

For persistent data, values should be written using the Data::SetXXX family of functions, which define the set of interoperable, format-safe property types. Using the Reflex::Data:: suite of property accessors also avoids template bloat occuring from use of the Reflex:: templated ones.


Data::SetFloat32(ps, "gain", 0.75f);
Data::SetCString(ps, "name", "Preset A");

PropertySet can be serialized using any Data::Format implementation:


auto blob = Data::EncodePropertySet(Data::kPropertySetFormat, ps);
//or
auto json = Data::DecodePropertySet(Data::kJsonFormat, ps);

Note that when writing text-based formats (JSON, Reflex PropertySheets) you need to register the key-strings, via Data::AcquireKeyMap and Data::RegisterKey.

Summary

  • PropertySet enables dynamic, runtime data attachment without subclassing.
  • It is a core mechanism for flexible, data-driven behavior in Reflex.
  • Properties are identified by (id, type), allowing rich runtime composition.
  • Serialization is supported via pluggable formats.

Use Iterate<TYPE>() to iterate over all properties of TYPE

Each item will have a [key,value] where key is Reflex::Address (comprising of the property id and type_id) and value a Reference <TYPE>


for (auto & [adr, ref] : test.Iterate<Data::CStringProperty>())
{
	output.Log(id.value, ref->value);
}

bool PropertySet::Empty();

bool PropertySet::operator bool();

PropertySet::PropertyIterator <TYPE> PropertySet::Iterate();

Data::SerializableFormat

Inherits from Format


void SerializableFormat::Serialize(Archive& stream, const PropertySet& propertyset);

void SerializableFormat::Deserialize(Archive::View& stream, PropertySet& propertyset);

Reflex > File

File::kPathDelimiter

The standard path separator used by Reflex. This is an alias of System::kPathDelimiter and represents the canonical delimiter for all paths handled within Reflex APIs.

Reflex paths must always use this delimiter, regardless of the underlying OS. The System layer will translate paths when interacting with native APIs, ensuring portability across platforms. Any path provided by a Reflex::System API is guaranteed to use kPathDelimiter.

Although the current value is a forward slash ('/'), this should not be relied upon for future compatibility. All path construction should reference File::kPathDelimiter explicitly.

Trailing delimiter rules

Folder paths in Reflex typically include the trailing delimiter.

For example, when calling File::List(path), folder entries in the returned list will include the trailing separator.

Constructing a valid file path

Use Join(...) or manually insert File::kPathDelimiter when building paths:


auto user_docs_path = File::GetSystemPath(File::kPathUserDocuments);   //includes trailing stroke
auto path = Join(user_docs_path, L"sub_folder", File::kPathDelimiter, L"sub_sub_folder", File::kPathDelimiter);

This ensures consistent behaviour across all platforms, correct interoperation with the VirtualFileSystem, and predictable results when listing or resolving paths.


const kPathDelimiter kPathDelimiter;

File::CheckExtension


bool CheckExtension(const WString::View& path, const WString::View& extension);

File::Copy


bool Copy(const WString& from, const WString& to);
bool Copy(System::FileHandle& from, System::FileHandle& to, UInt32 chunksize_opt);

File::CorrectExtension


WString CorrectExtension(const WString::View& path, const WString::View& extension);

File::CorrectStrokes


WString CorrectStrokes(const WString::View& path);

File::CorrectTrailingStroke


WString CorrectTrailingStroke(const WString::View& path);

File::CreateMemoryReader


TRef <System::FileHandle> CreateMemoryReader(ConstTRef <Data::ArchiveObject> data);

File::CreateMemoryWriter


TRef <System::FileHandle> CreateMemoryWriter(TRef <Data::ArchiveObject> data);

File::Delete


bool Delete(const WString& path);

Deletes a file or directory. To delete a directory, the directory must be empty.

This is an alias of System::Delete.

File::DeleteDirectoryContent


void DeleteDirectoryContent(const WString& path);

Deletes all content of a directory, recursively, but does not the directory itself.

File::DeletePath


void DeletePath(const WString& path);

Deletes a directory, including all content.

File::Exists


bool Exists(const WString& path);

Returns true is path is a file or directory/folder.

This is an alias of System::Exists.

File::GetRemainder


UInt64 GetRemainder(const System::FileHandle& file_handle);

File::GetSystemPath


WString GetSystemPath(System::Path path);

Returns the actual location of a system defined 'special' path.

Alias of System::GetPath.

File::GetVolumes


Array < Pair < Array <WChar> , Array <WChar> > > GetVolumes();

File::IsDirectory


bool IsDirectory(const WString& path);

Returns true is path is a directory/folder.

This is an alias of System::IsDirectory.

File::MakeDirectory


bool MakeDirectory(const WString& path);

Creates a directory (folder) at the given path. Returns true on success, false on failure.

This is an alias of System::MakeDirectory.

File::MakePath


void MakePath(const WString& path);

File::MakeRelativePath


WString MakeRelativePath(const WString::View& base_dir, const WString::View& path);

Computes a relative path from 'base_dir' to 'path' using purely lexical (string) path comparison.

  • base_dir: Base path used as the “from” location
  • path: Target path to express relative to base_dir
  • return: A relative path using ".." segments and System::kPathDelimiter. If either path cannot be split into components, returns path unchanged.

The result is formed by:

  • finding the longest common prefix of the path components of base_dir to path
  • emitting ".." for each remaining component in base_dir
  • then appending the remaining components of path
  • This is a lexical operation; it does not access the filesystem (i.e. does not resolve symlinks, current working directory, or check path existence).
  • Input is assumed to be a valid Reflex directory path, i.e. terminated with System::kPathDelimiter

auto rel = Reflex::File::MakeRelativePath(L"/a/b/c/", L"/a/b/d/e.txt");
// rel == "../d/e.txt"

auto rel = Reflex::File::MakeRelativePath(L"/project/assets/", L"/project/assets/textures/wood.png");
// rel == "textures/wood.png"

File::Open


Data::Archive Open(const WString& path);

Reads the entire contents of a file into a Data::Archive. Open is a convenience wrapper around System::FileHandle for cases where a file needs to be loaded into memory at once.

  • filename: Path to the file to read.
  • return: A Data::Archive containing the complete file contents. Empty if the file cannot be opened or read.

The archive is sized to the file length, and the file is read in a single operation.


auto bytes = File::Open(filename);
if (bytes.GetSize() > 4)
{
	Data::Archive::View stream = bytes;
	if (Data::Deserialize<UInt32>(stream) == kMagicBytes)
	{
		//deserialize remaining data according to the expected format
	}
}

This helper is suitable for loading small–medium resources, configuration files, text files, and binary assets that fit comfortably in memory.

File::Peek


Data::Archive Peek(System::FileHandle& file_handle, UInt32 bytes);

File::ReadBytes


Data::Archive ReadBytes(System::FileHandle& file_handle);
Data::Archive ReadBytes(System::FileHandle& file_handle, UInt32 bytes);

Reads raw bytes from a file into a Data::Archive. Two overloads are provided: one that reads all remaining bytes, and one that reads up to a specified number of bytes.

Overload 1

  • file_handle: The file to read from. The read position advances as bytes are consumed.
  • return: A Data::Archive containing all remaining bytes from the current file position to end of file.

Overload 2

  • file_handle: The file to read from.
  • num_bytes: Maximum number of bytes to read. If fewer bytes remain in the file, only the remaining bytes are returned.
  • return: A Data::Archive containing the requested byte range (or fewer, if EOF is reached).

auto handle = Make<System::FileHandle>(path);

if (handle->GetSize() > 32)
{
	handle->SetPosition(16);
	auto region = File::ReadBytes(handle, 16);
	//use region
}

File::ReadLine


bool ReadLine(System::FileHandle& file_handle, CString& line_out);
bool ReadLine(System::FileHandle& file_handle, WString& line_out);

Reads a single line of text from the file and decodes it as either raw ASCII (CString overload) or UTF-8 (WString overload).

  • file_handle: An open file handle positioned at the current read offset.
  • line_out: Output buffer receiving the decoded line. CString reads raw ASCII bytes; WString decodes from UTF-8.
  • return: true if a line was read (including empty lines); false only when no more lines are available (end-of-file).

//read entire ASCII text file
auto file_handle = Make<System::FileHandle>(path);
CString buffer;
while (File::ReadLine(file_handle, buffer))
{
	//process line
}

File::ReadValue


bool ReadValue(System::FileHandle& file_handle, TYPE& value_out);
TYPE ReadValue(System::FileHandle& file_handle);

Reads a fixed-size value from the file into a raw-packable type. This is a low-level binary read intended for POD / trivially copyable types with a stable binary layout.

  • file_handle: File to read from. The read position is advanced by sizeof(TYPE).
  • inout: Destination value. On success it is overwritten with the bytes read. On failure it is unchanged or partially overwritten depending on underlying read behavior.
  • return: true if exactly sizeof(TYPE) bytes were read, otherwise (insufficient bytes) false.

TYPE must be raw-packable (compile-time enforced). Non raw-packable types will fail to compile.


auto file_handle = Make<System::FileHandle>(path);
UInt32 a = 0;
if (File::ReadValue(*file_handle, a))
{
	//use a
}

auto b = File::ReadValue<Float32>(*file_handle);

File::RemoveDuplicateStrokes


WString RemoveDuplicateStrokes(const WString::View& path);

File::RemoveTrailingStroke


WString::View RemoveTrailingStroke(const WString::View& path);

File::Rename


bool Rename(const WString& from, const WString& to);

File::ResolveExistingFolder


WString::View ResolveExistingFolder(const WString::View& path);

File::ResolveRelativePath


WString ResolveRelativePath(const WString::View& path);

File::Save


bool Save(const WString& filename, const Data::Archive::View& data);

Writes the contents of a byte buffer to a file. Save is the inverse of File::Open: instead of loading the entire file into an Archive, it writes an Archive (or view of one) out to disk (typically).

  • filename: Destination path to write.
  • data: The byte range to write. Typically an Archive or Archive::View produced by serialization or binary packing.
  • return: true on success, false if the file could not be opened or all bytes could not be written.

Save performs a single write of the provided data range. No additional formatting or metadata is added.


Data::Archive stream;
Data::Serialize(stream, 123, 4.5f);
Data::SerializeUTF8(stream, L"hello");
if (!File::Save(L"output.bin", out))
{
	//handle save failure
}

File::SplitExtension


Pair < ArrayView <WChar> , ArrayView <WChar> > SplitExtension(const WString::View& path);

File::SplitFilename


Pair < ArrayView <WChar> , ArrayView <WChar> > SplitFilename(const WString::View& path);

File::WriteBytes


UInt32 WriteBytes(System::FileHandle& file_handle, const Data::Archive::View& bytes);

Writes the given byte view to the file at the current cursor position.

  • file_handle: Open file handle to write into.
  • bytes: Byte view to write.
  • return: Number of bytes actually written.

A short write (return < bytes.GetSize()) indicates an I/O failure, full disk, or write restrictions.

File::WriteLine


void WriteLine(System::FileHandle& file_handle, const CString::View& line);
void WriteLine(System::FileHandle& file_handle, const WString::View& line);

Writes a single line of text to the file, encoding the input as either raw ASCII (CString::View overload) or UTF-8 (WString::View overload).

  • file_handle: An open file handle positioned at the current write offset.
  • line: The line to write. CString::View is written as raw ASCII bytes; WString::View is encoded to UTF-8.

A line terminator is automatically appended. The input string should not include a newline.


auto file_handle = Make<System::FileHandle>(path);

//write plain ASCII
File::WriteLine(file_handle, "first line");
File::WriteLine(file_handle, "second line");

//write multibyte UTF-8
File::WriteLine(file_handle, L"UTF-8 text");
File::WriteLine(file_handle, L"日本語");

File::WriteValue


bool WriteValue(System::FileHandle& file_handle, TYPE value);

Writes a single raw value directly to the file. TYPE must be a raw-packable type - i.e., a type whose binary representation is stable, contiguous, and platform-independent.

  • file_handle: The file to write to.
  • in: The value to write. Written as its raw binary representation.
  • return: true if the full value was written; false if the write failed or reached EOF.

WriteValue performs no encoding or metadata emission. The bytes of the value are written exactly as they exist in memory.


auto f = Make<System::FileHandle>(path, File::kWrite);

UInt32 counter = 123;
File::WriteValue(f, counter);      //writes 4 bytes

struct Foo
{
	UInt32 a;
	Float32 b;
};
Foo foo = { 10, 2.5f };
File::WriteValue(f, foo);          //OK if Foo is trivially copyable

File::PersistentPropertySet

Inherits from Data::PropertySet

PersistentPropertySet brings together Data::PropertySet and Data::Format to create a PropertySet that can be stored and restored.

It is used primarily by the Bootstrap layer for the prefs object and the app session object, and it is the underlying mechanism behind the Bootstrap::Streamable persistence system used by the app, views, and other clients.

You can subscribe to changes by creating a listener using the Signal::CreateListener pattern. The receiver must store the returned object to keep the connection alive.

See also

Data::Format, Signal

File::ResourcePool

Inherits from Object

ResourcePool is the higher-level shared resource cache built on top of the file layer.

It resolves resources by address or path, ensures the underlying file is decoded only once, and then keeps the in-memory representation available for reuse across the application.

In practice this is used extensively by GLX for stylesheets, fonts, bitmaps, and other UI resources, but it was originally designed around audio/sample loading and is equally well proven there.

ResourcePool typically sits above File::VirtualFileSystem, which handles the lower-level path resolution.

File::VirtualFileSystem

Inherits from Object

VirtualFileSystem is the lower-level path resolution layer used to map a path onto a concrete file source.

Rather than being limited to disk files, it works through attachable locator handlers for different domains and storage types. This allows special paths such as ":res:Project/styles.glx" to resolve through the appropriate handler instead of directly through the OS filesystem.

In practice this is used for embedded resources, monolithic containers, web-backed content, and other custom storage schemes. Most application code uses higher-level APIs on top of it rather than talking to the VirtualFileSystem directly.


Reflex > GLX

GLX::kCharacter


const kCharacter kCharacter;

GLX::kDragDropEnter


const kDragDropEnter kDragDropEnter;

GLX::kDragDropLeave


const kDragDropLeave kDragDropLeave;

GLX::kDragDropReceive


const kDragDropReceive kDragDropReceive;

GLX::kDragDropReceiveExternal


const kDragDropReceiveExternal kDragDropReceiveExternal;

GLX::kDragDropTender


const kDragDropTender kDragDropTender;

GLX::kFocus


const kFocus kFocus;

GLX::kKeyDown


const kKeyDown kKeyDown;

GLX::kKeyUp


const kKeyUp kKeyUp;

GLX::kLoseFocus


const kLoseFocus kLoseFocus;

GLX::kMouseDown


const kMouseDown kMouseDown;

GLX::kMouseDrag


const kMouseDrag kMouseDrag;

GLX::kMouseEnter


const kMouseEnter kMouseEnter;

GLX::kMouseLeave


const kMouseLeave kMouseLeave;

GLX::kMouseUp


const kMouseUp kMouseUp;

GLX::kMouseWheel


const kMouseWheel kMouseWheel;

GLX::kTransaction

"Transaction" / kTransaction is a standard event used by interactive widgets to report value changes in a structured begin -> perform -> end sequence. It is emitted during continuous user interactions such as dragging, scrolling, typing, and other adjustments where you typically want live updates plus a single undo/commit at the end.

Widgets that emit transactions include RotarySlider, RangeBar, and TextArea.

The transaction stage is stored on the event and can be read using GLX::GetTransactionStage(e).

  • kTransactionStageBegin: interaction started (e.g. mouse down, drag begin, edit begin)
  • kTransactionStagePerform: value changed during the interaction (may be emitted many times)
  • kTransactionStageEnd: interaction finished (e.g. mouse up, drag end, edit end)
  • kTransactionStageCancel: interaction aborted (e.g. escape, focus loss, cancelled drag)

Event properties

The following properties may be attached to the event:

  • stage: UInt8 (TransactionStage). Always set.
  • index: UInt32. Optional. Used when a widget reports multiple lanes/handles/fields.
  • value: Float32. Optional. The current value for the interaction step.
  • modifiers: UInt8. Modifier key state at emit time (System::GetModifierKeys()).

index and value are optional; some widgets emit only the stage (and modifiers) and expect the receiver to query widget state directly.

Handling Transactions

Typical handling is to bind to GLX::kTransaction, switch on the stage, apply live updates on kTransactionStagePerform, and push a single undo/commit on kTransactionStageEnd.


GLX::BindEvent(widget, GLX::kTransaction, [](GLX::Object & src, GLX::Event & e)
{
	auto stage = GLX::GetTransactionStage(e);

	switch (stage)
	{
		case GLX::kTransactionStageBegin:
		{
			//optional: capture initial state for undo / preview
			break;
		}

		case GLX::kTransactionStagePerform:
		{
			//live update (engine/app state)
			//optional: use GLX::GetIndex(e) / GLX::GetValue(e) if provided
			break;
		}

		case GLX::kTransactionStageEnd:
		{
			//commit once (e.g. add undo step)
			break;
		}

		case GLX::kTransactionStageCancel:
		{
			//optional: revert to initial state
			break;
		}

		default: break;
	}

	return true;
});

Emission

Transactions are typically emitted via the helper EmitTransaction

Notes

  • kTransactionStagePerform may be emitted at high frequency; handlers should be lightweight.
  • If index/value are not present, read the current state from src (or your model) instead.
  • Use modifiers to support alternate modes (fine adjust, snapping, multi-select, etc.).

const kTransaction kTransaction;

GLX::FlowFlags


kFlowX
kFlowY
kFlowInvert
kFlowCenter

GLX::Orientation


kOrientationNear
kOrientationCenter
kOrientationFar
kOrientationFit

GLX::Alignment


kAlignmentTopLeft
kAlignmentTop
kAlignmentTopRight
kAlignmentLeft
kAlignmentCenter
kAlignmentRight
kAlignmentBottomLeft
kAlignmentBottom
kAlignmentBottomRight

GLX::ClickFlags


kClickFlagRmb
kClickFlagDbl

InterpolatedAnimation::Easing


kLinear
kEaseIn2x
kEaseIn3x
kEaseOut2x
kEaseOut3x
kEaseInOutCos
kEaseInOut2x

GLX::TransactionStage


kTransactionStageNone
kTransactionStageBegin
kTransactionStagePerform
kTransactionStageEnd
kTransactionStageCancel

AbstractList::SelectionMode


kSelectionModeSingle
kSelectionModeMulti
kSelectionModeMultiToggle

GLX::Colour


using Colour = System::Colour;

GLX::ColourPoints


using ColourPoints = Array < Tuple < Point <Float32> ,System::Colour> >;

GLX::KeyCode


using KeyCode = System::KeyCode;

GLX::ModifierKeys

Convenience alias of System::ModifierKeys. Use GLX::kModifierKeyPrimary to identify Ctrl (Windows) or Cmd (macOS).


if (GLX::GetModifierKeys(e) & GLX::kModifierKeyShift)
{
	//handle shift
}

using ModifierKeys = System::ModifierKeys;

GLX::MouseCursor


using MouseCursor = System::MouseCursor;

GLX::Point


using Point = System::fPoint;

GLX::Points


using Points = Array < Point <Float32> >;

GLX::Rect


using Rect = System::fRect;

GLX::Size


using Size = System::fSize;

GLX::Activate


void Activate(Object& object, bool state);

GLX::ActivateBranch


void ActivateBranch(Object& object, bool state);

GLX::AddAbsolute


TRef <Object> AddAbsolute(Object& parent, Object& child);
TRef <Object> AddAbsolute(Object& parent, Object& child, Point position);

Places the child at a fixed position within the parent's content rect, without using the layout system.

This function should generally not be used in application code, typical use-cases are for building complex widgets (such as ViewPort).

The real power of the layout system comes from using inline and float modes, which provide dynamic, responsive layouts. AddAbsolute bypasses those mechanisms and should be considered a low-level escape hatch.

GLX::AddDottedLine


void AddDottedLine(Points& points, Point from, Point to, Size pixel_size);

GLX::AddEllipseFill


void AddEllipseFill(Points& points, const Rect& rect, Float32 start, Float32 sweep);

GLX::AddEllipseOutline


void AddEllipseOutline(Points& points, const Rect& rect, Size width, Float32 start, Float32 sweep);

GLX::AddFloat


TRef <Object> AddFloat(Object& parent, Object& child, Orientation x_position, Orientation y_position);
TRef <Object> AddFloat(Object& parent, Object& child, Alignment alignment);

Adds child to parent with float positioning, independent of the inline flow. The childs is positioned either by specifying Orientation values for both axes, or by using a single Alignment value as shorthand.

In the shorthand form, the alignment parameters selects one of nine standard positions (top-left, top etc...)

In the full form, Orientation gives fine-grained control along X and Y:

  • kOrientationNear/Center/Far position the child relative to the parent on that axis,
  • kOrientationFit stretches the child to fit that axis.

Common patterns include:

  • x=Fit, y=Fit: full stretch overlay across the parent.
  • x=Far, y=Fit: sticky footer that stays pinned to the bottom edge.
  • alignment=TopRight: floating badge in the top right corner.
  • alignment=Center: centered modal or dialog.

AddFloat does not consume inline space - it layers the child over the parent's content area. Combine with margin and parent padding for refined placement.

GLX::AddInline


TRef <Object> AddInline(Object& parent, Object& child, Orientation ortho_position);

Adds child to parent with inline positioning (horizontal or vertical depending on parent layout). The child is positioned on the parents ortho-axis according to the orientation paremeter.

  • parent: Container receiving the child.
  • child: Element to insert into the inline sequence.
  • orientation: kOrientationNear/Center/Far/Fit along the ortho axis (default kOrientationFit)

GLX::AddInlineFlex


TRef <Object> AddInlineFlex(Object& parent, Object& child, Orientation ortho_position);

Adds child to parent with inline positioning, similar to AddInline, but the child expands to fill available space along the main axis. When multiple flex children are present, the available space is distributed evenly among them.

  • parent: Container receiving the child.
  • child: Element to insert into the inline sequence.
  • orientation: kOrientationNear/Center/Far/Fit along the ortho axis (default kOrientationFit).

GLX::AddPath


void AddPath(Points& points, const ArrayView < Point <Float32> >& path, bool& closed);

GLX::AddPointsWithColour


void AddPointsWithColour(ColourPoints& colour_points, const ArrayView < Point <Float32> >& colour, const Colour& input);

GLX::AddPolygonFill


void AddPolygonFill(Points& points, const ArrayView < Point <Float32> >& input);

GLX::AddRectFill


void AddRectFill(Points& points, const Rect& rect);
void AddRectFill(ColourPoints& colour_points, const Colour& colour, const Rect& rect);

GLX::AddRectOutline


void AddRectOutline(Points& points, const Rect& rect, const Margin& width, Size pixel_size);
void AddRectOutline(ColourPoints& colour_points, const Colour& colour, const Rect& rect, const Margin& width, Size pixel_size);

GLX::AddRoundedFill


void AddRoundedFill(Points& points, const Rect& rect, const Size* corners[4], Float32 corner_step);
void AddRoundedFill(Points& points, const Rect& rect, const Margin& corners, Float32 corner_step);

GLX::AddRoundedOutline


void AddRoundedOutline(Points& points, const Rect& rect, const Margin& width, const Size* corners[4], Float32 corner_step);
void AddRoundedOutline(Points& points, const Rect& rect, const Margin& width, const Margin& corners, Float32 corner_step);

GLX::AddRoundedTriangleFill


void AddRoundedTriangleFill(Points& points, const Rect& rect, Float32 corner, Alignment direction, Size pixel_size);

GLX::AddRoundedTriangleOutline


void AddRoundedTriangleOutline(Points& points, const Rect& rect, Float32 width, Float32 corner, Alignment direction, Alignment pixel_size);

GLX::AddStretch


TRef <Object> AddStretch(Object& parent, Object& child);

Shorthand for AddFloat with both axes set to kOrientationFit.

Adds the child stretched to fill the entire parent's content area. Useful for overlays, background layers, or full-size panels where the child should always match the parent's size.

GLX::AddTriangleFill


void AddTriangleFill(Points& points, const Rect& rect, Alignment direction);

GLX::AddTriangleOutline


void AddTriangleOutline(Points& points, const Rect& rect, Float32 width, Alignment direction, Size pixel_size);

GLX::AttachAnimationClock


void AttachAnimationClock(Object& object, Key32 id, const Function <void(Float32)>& callback);

GLX::AttachPeriodicClock


void AttachPeriodicClock(Object& object, Key32 id, Float32 delay, Float32 interval, const Function <void()>& callback);

GLX::BindClick


TRef <Object> BindClick(Object& object, const Function <void()>& callback);

GLX::BindEvent


void BindEvent(Object& object, Key32 event_id, const Function <bool(Object&, Event&)>& callback);

Attaches a delegate to the specified object which filters and forwards only events matching the given event-id to the supplied callback.

  • object: Target object that emits events.
  • event_id: Event ID (e.g. GLX::kMouseDown).
  • callback: Function called when the event occurs.

BindEvent replaces any previous delegate with the same event_id on this object.

The callback should return true if the event is fully handled, to stop propagation, or false to allow further delegates to receive it.

The following example shows how to intercept and trap mouse clicks. (BindClick is a shorthand for this).


GLX::BindEvent(button, GLX::Button::kMouseDown, [](GLX::Object & src, GLX::Event & e)
{
	Log("Button clicked!");
	return true; // stop propagation
});

GLX::BindEventVoid


void BindEventVoid(Object& object, Key32 event_id, const Function <void()>& callback);

Convenience version of BindEvent for simple handlers.

  • object: Target GLX::Object that emits the event.
  • event_id: Event id to listen for.
  • callback: Function(void) executed when the event triggers.

Invokes the supplied callback when the specified event-id occurs, and always traps the event.

The callback receives no parameters and does not see the source object or event payload.

GLX::BranchContains


bool BranchContains(const Object& parent, const Object& child);

GLX::CalculateAbs


Pair < Point <Float32> , Size <Float32> > CalculateAbs(const Object& object);
Pair < Point <Float32> , Size <Float32> > CalculateAbs(const Object& parent, const Object& object);

GLX::CalculateAbsoluteRect


Rect CalculateAbsoluteRect(const Object& object);

GLX::CalculateRelativeRect


Rect CalculateRelativeRect(const Object& parent, const Object& object);

GLX::CancelDragDrop


void CancelDragDrop();

GLX::ClearText


void ClearText(Object& object, Key32 id_opt);

GLX::CloseContextMenu


void CloseContextMenu();

GLX::CreateAnimationClock


TRef <Object> CreateAnimationClock(const Function <void(Float32)>& callback);

GLX::CreateCallbackAnimation


TRef <Animation> CreateCallbackAnimation(const Function <void(Object&)>& callback);

GLX::CreateColourPropertyAnimation


TRef <InterpolatedAnimation> CreateColourPropertyAnimation(Key32 property_id, const Colour& from, const Colour& to);

GLX::CreateDragDropBeginListener


TRef <Object> CreateDragDropBeginListener(const Function <void(Object&)>& callback);

GLX::CreateDragDropEndListener


TRef <Object> CreateDragDropEndListener(const Function <void()>& callback);

GLX::CreateDragDropTargetListener


TRef <Object> CreateDragDropTargetListener(const Function <void(Object&)>& callback);

GLX::CreateFloatPropertyAnimation


TRef <InterpolatedAnimation> CreateFloatPropertyAnimation(Key32 property_id, Float32 from, Float32 to);

GLX::CreateInterpolatedAnimation


TRef <InterpolatedAnimation> CreateInterpolatedAnimation(const Function <void(Object&, Float32)>& callback);

GLX::CreateLogarithmicAnimation


TRef <Animation> CreateLogarithmicAnimation(Float32 from, Float32 to, const Function <void(Object&, Float32)>& callback, Float32 decay_factor);

GLX::CreateMarginPropertyAnimation


TRef <InterpolatedAnimation> CreateMarginPropertyAnimation(Key32 property_id, const Margin& from, const Margin& to);

GLX::CreateMaxBoundsAnimation


TRef <Animation> CreateMaxBoundsAnimation(Key32 bounds_id, bool yaxis, Float32 from, Float32 to);

GLX::CreateOpacityAnimation


TRef <InterpolatedAnimation> CreateOpacityAnimation(Object& target, Key32 id, Float32 from, Float32 to);

GLX::CreatePeriodicClock


TRef <Object> CreatePeriodicClock(Float32 delay, Float32 interval, const Function <void()>& callback);

GLX::CreatePointPropertyAnimation


TRef <InterpolatedAnimation> CreatePointPropertyAnimation(Key32 property_id, Point from, Point to);

GLX::CreatePositionAnimation


TRef <InterpolatedAnimation> CreatePositionAnimation(bool y, Float32 from, Float32 to);

GLX::CreateSizePropertyAnimation


TRef <InterpolatedAnimation> CreateSizePropertyAnimation(Key32 property_id, Size from, Size to);

GLX::CreateStateAnimation


TRef <Animation> CreateStateAnimation(Key32 state);

GLX::CreateWaitAnimation


TRef <InterpolatedAnimation> CreateWaitAnimation();

GLX::DetachClock


void DetachClock(Object& object, Key32 id);

GLX::Emit


bool Emit(Object& src, Key32 id, VARGS... id_value_pairs);

Posts a custom event upward from the specified object. This is the recommended helper for emitting events without manually constructing an Event object.

  • src: The object emitting the event. The event is dispatched upward from this object.
  • id: The Key32 event identifier.
  • id_value_pairs: A variadic list of key/value pairs written into the event’s data payload. Keys are identifiers; values may be any supported data type.
  • return: boolean indicating whether the event was trapped or not

Emit(object, "my_event", "value", 1.0f);

The call above is equivalent to:


auto e = Make<Event>("my_event");
Data::SetFloat(e, "value", 1.0f);
object.Emit(e);

This helper is intended for simple transactional or notification-style events. For advanced cases (preconfigured payloads or reuse), construct and emit an Event explicitly.

GLX::EnableAutoFit


void EnableAutoFit(Object& object, bool x, bool y);

GLX::EnableMouse


void EnableMouse(Object& object, bool enable, bool intercept);

Controls how an object participates in mouse hit-testing.

  • Object obj: target object
  • bool enable: allows mouse events on the target (default true)
  • bool intercept: prevents children from receiving mouse events (default false)

Common combinations:


//obj receives mouse events, if a child doesnt have priorty (default behaviour)
GLX::EnableMouse(obj, true);

//obj receives all mouse events, stealing from children
GLX::EnableMouse(obj, true, true);

//obj is excluded from hit-testing, but its children can still receive mouse events normally
GLX::EnableMouse(obj, false)

//the object and its entire subtree are invisible to the mouse
GLX::EnableMouse(obj, false, true)

GLX::EnableMouseCapture


void EnableMouseCapture(Object& object, bool enable, bool incremental);

GLX::Enter


void Enter(Object& object, UInt8 flags);

GLX::ExceedsDragThreshold


bool ExceedsDragThreshold(Point drag, Float32 sens);

GLX::Exit


void Exit(Object& object, bool detach, UInt8 or_flags_opt);

GLX::FindStyle


ConstTRef <Style> FindStyle(const Object& object, Key32 id);
ConstTRef <Style> FindStyle(const Style& style, Key32 id);

Finds a style by path relative to 'start'.

Resolution order:

  • 1. Direct child of 'start'.
  • 2. If not found, perform a fallback search "upwards" through the stylesheet (older-siblings and parents).

Returns null if no match is found.

GLX::FocusBranch


void FocusBranch(Object& branch_root);

GLX::GetBounds


const Pair < Size <Float32> , Size <Float32> >& GetBounds(const Object& object, Key32 id);

GLX::GetClickFlags


UInt8 GetClickFlags(const Event& e);

GLX::GetClip


Pair <bool,bool> GetClip(const Object& object, Key32 id);

GLX::GetDragDropData


TRef <Object> GetDragDropData(Event& e);

GLX::GetKeyCharacter


WChar GetKeyCharacter(const Event& e);

GLX::GetKeyCode


KeyCode GetKeyCode(const Event& e);

GLX::GetModifierKeys


UInt8 GetModifierKeys(const Event& e);

GLX::GetMousePosition


Point GetMousePosition(const Object& object);

GLX::GetOpacity


Float32 GetOpacity(const Object& object, Key32 id);

GLX::GetText


WString::View GetText(const Object& object, Key32 id_opt);

GLX::IsActive


bool IsActive(const Object& object);

GLX::IsDoubleClick


bool IsDoubleClick(const Event& e);

GLX::IsLeftClick


bool IsLeftClick(const Event& e);

GLX::IsRightClick


bool IsRightClick(const Event& e);

GLX::IsSelected


bool IsSelected(const Object& object);

GLX::LookupBranchIndex


Idx LookupBranchIndex(const Object& parent, const Object& child);

GLX::LookupChildAtIndex


TRef <Object> LookupChildAtIndex(Object& parent, UInt32 idx);

GLX::LookupIndex


Idx LookupIndex(const Object& child);

GLX::OpenContextMenu


Reference <Menu> OpenContextMenu(Object& src, Key32 context_opt, Key32 style_opt);

Attaches a Menu widget to the window foreground.

See Menu for more details.

GLX::QueryAntecedent


const Event* QueryAntecedent(const Event& e, Key32 id, const Event* fallback);

GLX::QueryChildById


Object* QueryChildById(Object& parent, Key32 id, Object* fallback);

Searches 'object' for the first direct child (i.e. no recursion) with the given 'id'.

GLX::QueryDragDropData


TYPE* QueryDragDropData(Event& e);

GLX::QueryElementById


Object* QueryElementById(Object& object, Key32 id, Object* fallback);

Searches 'object' recursively for the first child with the given 'id'.

GLX::RGB


Colour RGB(UInt8 grey);
Colour RGB(UInt8 grey, UInt8 alpha);
Colour RGB(UInt8 red, UInt8 green, UInt8 blue);
Colour RGB(UInt8 red, UInt8 green, UInt8 blue, UInt8 alpha);

GLX::RedirectFocus


void RedirectFocus(Object& branch_root, Object& object);

GLX::Rescale


void Rescale(const ArrayRegion < Point <Float32> >& points, Size scale);
void Rescale(const ArrayRegion < Tuple < Point <Float32> ,System::Colour> >& colour_points, Size scale);

GLX::RetrieveStyleSheet


ConstTRef <StyleSheet> RetrieveStyleSheet(const WString::View& path, const Data::PropertySet& options_opt);

GLX::Rotate


void Rotate(const ArrayRegion < Point <Float32> >& points, Point origin, Float32 angle_normalised);
void Rotate(const ArrayRegion < Tuple < Point <Float32> ,System::Colour> >& colour_points, Point origin, Float32 angle_normalised);

GLX::Run


void Run(Object& target, Key32 id, TRef <Animation> animation);
void Run(Object& target, Key32 id, Float32 time, TRef <Animation> animation);
void Run(Object& target, Key32 id, Float32 time, InterpolatedAnimation::Easing easing, TRef <InterpolatedAnimation> animation);

GLX::ScaleDelta


Point ScaleDelta(const Object& object, Point window_coordinates_delta);

GLX::Select


void Select(Object& object, bool select);

Sets or clears the GLX::kSelectedState ("selected") on the target object

GLX::SelectBranch


void SelectBranch(Object& object, bool select);

GLX::SelectChildren


void SelectChildren(Object& object, bool select);

GLX::Send


bool Send(Object& src, Key32 id, VARGS... id_value_pairs);

Sends a custom event directly to the specified object without propagating it up the object hierarchy.

  • src: The object receiving the event.
  • id: The Key32 event identifier.
  • id_value_pairs: A variadic list of key/value pairs written into the event’s data payload. Keys are identifiers; values may be any supported data type.
  • return: boolean indicating whether the event was trapped or not

Send(object, "my_event", "value", 1.0f);

The call above is equivalent to:


auto e = Make<Event>("my_event");
Data::SetFloat(e, "value", 1.0f);
object.ProcessEvent(e);

This helper is intended for simple transactional or notification-style events. For advanced cases (preconfigured payloads or reuse), construct an Event explicitly.

GLX::SetBounds


void SetBounds(Object& object, Key32 id, const Size& min, const Size& max);

GLX::SetCanvas


void SetCanvas(Object& object, Key32 id, const Function <void(GLX::CanvasContext&)>& callback);

GLX::SetClip


void SetClip(Object& object, Key32 id, bool x, bool y);

GLX::SetColourCanvas


void SetColourCanvas(Object& object, Key32 id, const Function <void(GLX::ColourCanvasContext&)>& callback);

GLX::SetEventDelegate


void SetEventDelegate(Object& object, Key32 delegate_id, const Function <bool(Object&, Event&)>& callback);

Attaches a delegate to the specified object which forwards all events to the supplied callback.

  • object: Target object that emits events.
  • delegate_id: a unique id for this handler
  • callback: Function called when any event occurs.

SetEventDelegate replaces any previous delegate with the same delegate_id on this object.

The callback should return true if the event is fully handled, to stop propagation. Return false to allow further delegates to receive it.

The following example shows how to intercept and trap mouse clicks. (BindClick is a shorthand for this).


GLX::SetEventDelegate(button, "my_delegate", [](GLX::Object & src, GLX::Event & e)
{
	if (e.id == GLX::kMouseDown)
	{
		Log("Button clicked!");
		return true; // stop propagation of mouse down
	}
	return false;	//allow propogation of other events
});

GLX::SetFlow


void SetFlow(Object& object, FlowFlags flags);

GLX::SetGraphicCanvas


void SetGraphicCanvas(Object& object, Key32 id, const Function <void(GLX::GraphicCanvasContext&)>& callback);

GLX::SetOnStyle


void SetOnStyle(Object& object, const Function <void(const Style &)>& callback, Key32 delegate_id_opt);

Attaches a delegate which invokes the supplied callback whenever the object's style is changed.

This is a callback-based alternative to overriding the GLX::Object::OnSetStyle method, which can be used to apply sub-styles to child elements when not sub-classing GLX::Object.

  • object: target object
  • callback: callback to be called every time the object's style is set/changed.
  • delegate_id_opt: identifies the delegate instance (so it can be replaced/cleared deterministically).

auto container = New<GLX::Object>();
auto button = GLX::AddFloat(container, New<GLX::Button>("Click Me"));
GLX::SetOnStyle(container, [button](const GLX::Style & style)
{
	button->SetStyle(style["button"]);
});

GLX::SetOpacity


void SetOpacity(Object& object, Key32 id, Float32 opacity);

GLX::SetState


void SetState(Object& object, Key32 state, bool value);

GLX::SetText


void SetText(Object& object, const WString& value, Key32 id_opt);

GLX::StartDragDrop


void StartDragDrop(TRef <Object> data, MouseCursor dragover, MouseCursor block);

GLX::Stop


void Stop(Object& target, Key32 id);

GLX::ToggleState


bool ToggleState(Object& object, Key32 state);

GLX::TransformPosition


Point TransformPosition(const Object& object, Point window_coordinates_position);

GLX::Translate


void Translate(const ArrayRegion < Point <Float32> >& points, Point offset);
void Translate(const ArrayRegion < Tuple < Point <Float32> ,System::Colour> >& colour_points, Point offset);

GLX::UnbindEvent


void UnbindEvent(Object& object, Key32 event_id);

GLX::UnsetBounds


void UnsetBounds(Object& object, Key32 id);

GLX::UnsetCanvas


void UnsetCanvas(Object& object, Key32 id);

GLX::UnsetClip


void UnsetClip(Object& object, Key32 id);

GLX::UnsetOpacity


void UnsetOpacity(Object& object, Key32 id);

GLX::CanvasContext

GLX::ColourCanvasContext

GLX::GraphicCanvasContext

GLX::Margin


Size Margin::far;

Size Margin::near;

GLX::Range


Float32 Range::length;

Float32 Range::start;

GLX::AbstractList

Inherits from Object

AbstractList is the shared selection and navigation base for list-like widgets.

It handles click, double-click, drag-start, keyboard navigation, Ctrl+A / Ctrl+D selection helpers, and Delete / Backspace remove requests, while derived classes provide item storage and visual updates.

The key events to know are ListSelect, ListLoad, ListStartDrag, and ListRequestRemove. Selection mode can be single, multi, or toggle.


void AbstractList::SetSelectionMode(AbstractList::SelectionMode mode);

UInt32 AbstractList::GetNumItem();

void AbstractList::SelectAll();

void AbstractList::SelectNone();

bool AbstractList::Select(UInt32 idx, bool multi);

void AbstractList::Deselect(UInt32 idx);

bool AbstractList::SelectNext(bool extend);

bool AbstractList::SelectPrev(bool extend);

void AbstractList::EnumerateSelection(UInt32 start, UInt32 range, const Function <void(UInt idx, UInt n)>& callback);

void AbstractList::Reveal(UInt32 idx);

GLX::AbstractViewBar

Inherits from Object

GLX::AbstractViewPort

Inherits from Object


void AbstractViewPort::SetContent(TRef <Object> content, Key32 style_id_opt);

TRef <Object> AbstractViewPort::GetContent();
ConstTRef <Object> AbstractViewPort::GetContent();

void AbstractViewPort::InvertScrollAxis(bool invert);

TRef <Object> AbstractViewPort::CreateListener(const Function <void()>& callback);

void AbstractViewPort::SetMinView(Size size);

Size AbstractViewPort::GetMinView();

Size AbstractViewPort::GetExtent();

void AbstractViewPort::SetView(const Rect& view);

const Rect& AbstractViewPort::GetView();

Size AbstractViewPort::GetPixelsPerUnit();

void AbstractViewPort::StartScroll(bool yaxis, Float32 offset);

void AbstractViewPort::StopScroll(bool yaxis);

void AbstractViewPort::Reveal(bool yaxis, Float32 offset, Float32 range, Float32 padding, bool animate);

void AbstractViewPort::EnableAutoScroll(Float32 amount, bool scoped);

void AbstractViewPort::DisableAutoScroll();

ConstTRef <Object> AbstractViewPort::GetBody();

TRef <AbstractViewBar> AbstractViewPort::GetViewBar(bool yaxis);

GLX::Animation

Inherits from Object

GLX animation covers two closely related systems:

  • Animation objects that interpolate values or states over time
  • Clocks that execute UI callbacks on the GLX update thread

In many cases the simplest animation is declarative. A stylesheet can define @State variants and a transition time, and code only needs to push or clear the relevant state.

State-Based Transitions

For hover, selected, inactive, and similar UI feedback, prefer stylesheet-driven transitions first.


Button:
{
	transition: 0.25;

	@State hover:
	{
		bg: Fill(colour: 228);
	};
}

This keeps simple UI motion in the style layer rather than in imperative code.

Running Explicit Animations

When code needs to decide target values dynamically, create an animation object and run it against a property or state on a target object.


auto fade = GLX::CreateColourPropertyAnimation("colour", GLX::kWhite, GLX::kBlack);
GLX::Run(object, "colour", 0.25f, fade);

Common helpers in this module include CreateStateAnimation, CreateColourPropertyAnimation, CreateMarginPropertyAnimation, and CreateCallbackAnimation.

Clocks

Use clocks when you need procedural updates over time rather than interpolation between two values.

  • CreateAnimationClock / AttachAnimationClock for frame-based UI callbacks
  • CreatePeriodicClock / AttachPeriodicClock for lower-frequency periodic work
  • DetachClock to stop an attached clock

This is commonly used for polling async task status, updating drag visuals, driving custom canvas effects, or other time-based UI behavior that is not well described as a simple property tween.

Choosing the Right Approach

  • Use stylesheet transition + @State for simple visual feedback
  • Use explicit animation objects when code determines the end value or state
  • Use clocks for continuous procedural behavior or periodic observation

void Animation::SetTime(Float32 time);

void Animation::SetTarget(Object& target);

void Animation::Play();

GLX::ContainerAnimation

Inherits from Animation


void ContainerAnimation::Clear();

void ContainerAnimation::Add(Animation& animation);

GLX::Event

Inherits from Data::PropertySet


Key32 Event::id;

TRef <Event> Event::Clone();

GLX::Form

Inherits from Object

Simple non-interactive container widget composed of two sub-objects: header and body.

The header is laid out inline, followed by the body with inline-flex. The default layout direction is vertical (GLX::kFlowY).

Styling

Applies the header style block to the header object and the body style block to the body object.


FormExample:
{
	size: 200,300;

	header:
	{
		size: 32;
		bg_colour: 255,0,0;
	};

	body:
	{
		padding: 8;
		bg_colour: 0,255,0;
	};
}

const TRef <Object> Form::body;

const TRef <Label> Form::header;

GLX::InterpolatedAnimation

Inherits from Animation

GLX::Label

Inherits from Object

Label is the lightweight text-holding widget.

It stores a Text property under a chosen property id, so it can be used both for ordinary labels and for small value displays embedded inside larger widgets.

GLX::List

Inherits from AbstractList

List is the concrete child-backed implementation of AbstractList.

Selection is reflected directly through each child item's selected state, and it also adds optional drag-reordering through the ListReorder transaction event sequence.

Use it when you already have concrete child objects for each row and want the list to manage selection, reveal, focus, and reorder interaction.

GLX::Menu

Inherits from Scroller

The Menu widget provides a standard floating popup menu, typically used for context menus and drop-down menus.

It is built on top of Scroller, so it supports a single scrollable content region and automatically shows scrollbars when needed.

Menus are typically not opened directly. For most cases, use OpenContextMenu which attaches the menu to the window foreground (always-on-top) and triggers a MenuOpen event to allow menu population by the target object and its parents.

Alternatively, use the Popup widget for simple option-enumeration widgets.


auto menu = GLX::OpenContextMenu(*this);

GLX::BindClick(menu->AddItem(L"Option 1"), []()
{
	//handle option
});

menu->AddSeparator();

auto sub = menu->AddSubMenu(L"More");
GLX::BindClick(sub->AddItem(L"Option 2"), [](){});

Styling

Menu inherits Scroller, so implements the same style-schema (body, content, x, y). Additionally, Menu applies item styles by applying these ids:

  • item: applied to items added via AddItem(label).
  • separator: applied to separators added via AddSeparator().
  • folder: applied to submenu “folder” items added via AddSubMenu(label).

style


menu:
{
	size: 256,0;	//min width

	bg: Shadow(width: 16; indent: -8; color: 0,32),Fill(corner: 4);

	content:
	{
		padding: 4;
	};

	item:
	{
		bg: Text(font: MyFont; value: &value; indent: 8,4; color: 0);
	};

	//y: see Scroller
};

Opening Menus

Typically use OpenContextMenu to create and attach a menu instance.

When the menu is attached, the menu emits Menu::kMenuOpen on src, with the created menu stored as an event property. This allows src and its parents to intercept the open and add items.

To fully support context-menus created on child objects, populate the menu in a seperate handler from creation, e.g:


bool SubView::OnEvent(GLX::Object & src, GLX::Event & e)
{
	if (e.id == GLX::kMouseDown && (GLX::GetClickFlags(e) & GLX::kClickFlagRmb))
	{
		GLX::OpenContextMenu(*this);
		return true;
	}
	else if (auto menu = GLX::GetMenu(e)) //e.id == Menu::kMenuOpen, reads "menu" property
	{
		GLX::BindClick(menu->AddItem(L"Option 1"), []()
		{
			//handle option
		});

		//return true; //optional: trap to prevent parents adding items
	}

	return GLX::Object::OnEvent(src, e);
}

If you don’t need to support interception of menus from child objects, you can open and populate together:


bool SubView::OnEvent(GLX::Object & src, GLX::Event & e)
{
	if (GLX::IsRightClick(e))	//helper
	{
		auto menu = GLX::OpenContextMenu(*this);

		GLX::BindClick(menu->AddItem(L"Option 1"), []()
		{
			//handle option
		});

		return true;
	}

	return GLX::Object::OnEvent(src, e);
}

Handling selection

Binding a click handler on every item (BindClick) is often convenient, but for some cases you may prefer a unified handler.

Menu emits Menu::kMenuSelect whenever an item is selected, allowing you to handle all selections in one place. This is useful for large menus, or for simple option/enumeration menus where you only need the selected index.

In this case, use the GetIndex and GetItem helpers to read the index and source item properties from the event:


GLX::BindEvent(menu, GLX::kMenuSelect, [](GLX::Object & src, GLX::Event & e)
{
	UInt idx = GLX::GetIndex(e);
	auto item = GLX::GetItem(e);

	//handle selection (idx / item)
	return true;
});

void Menu::Clear();

TRef <Object> Menu::AddItem(const WString::View& label);
TRef <Object> Menu::AddItem(TRef <Object> item);

TRef <Object> Menu::AddSeparator();
TRef <Object> Menu::AddSeparator(TRef <Object> item);

TRef <Object> Menu::AddSubMenu(const WString::View& label);
TRef <Object> Menu::AddSubMenu(TRef <Object> item);

TRef <Object> Menu::GetParentItem();

bool Menu::OpenSubMenu(Object& item);

GLX::Multi

Inherits from ContainerAnimation

GLX::Object

Inherits from Data::PropertySet


void Object::SetParent(Object& child);

void Object::Clear();

void Object::InsertBefore(Object& child);

void Object::InsertAfter(Object& child);

void Object::Detach();

void Object::SendBottom();

void Object::SendTop();

void Object::SetMouseCursor(MouseCursor mousecursor);

MouseCursor Object::GetMouseCursor();

void Object::SetStyle(const Style& style);

ConstTRef <Style> Object::GetStyle();

void Object::ClearState(Key32 state);

void Object::SetState(Key32 state);

bool Object::CheckState(Key32 state);

void Object::Focus();

bool Object::ProcessEvent(Object& src, Event& e);

bool Object::Emit(Event& e);

void Object::Accommodate();

void Object::Realign();

void Object::Update();

void Object::OnAttachWindow();

void Object::OnDetachWindow();

void Object::OnClock(Float32 delta);

void Object::OnUpdate();

void Object::OnSetStyle(const Style& style);

bool Object::OnEvent(Object& src, Event& e);

GLX::PlayList

Inherits from ContainerAnimation


void PlayList::EnableLoop(bool enable);

GLX::Popup

Inherits from Object

The Popup widget is a lightweight “click-to-open” menu control. When clicked, it opens a floating Menu anchored to the Popup’s screen position (typically displayed underneath the widget if there is space, otherwise positioned to fit).

Popup is built around Menu: it opens a Menu instance and reuses Menu’s item model, styling, and selection events.

See the GLX::Menu documentation for details on populating a menu (items, sub-menus etc) and handling selection events.


GLX::BindEvent(m_popup, GLX::Popup::kMenuOpen, [](GLX::Object & src, GLX::Event & e)
{
	auto menu = GLX::GetMenu(e);	//get handle to Menu created by the Popup

	GLX::BindClick(menu->AddItem(L"Option 1"), []()
	{
		//handle option
	});

	return true;
});

Opening behaviour

When the Popup is clicked, it opens a Menu and then forwards the menu’s Menu::kMenuOpen event to the Popup itself (emitting upward). This allows the Popup, or any of its parents, to intercept the open and populate the menu.

Typical patterns:

  • Bind directly on the Popup (recommended for self-contained controls).
  • Populate from a parent OnEvent override (recommended when you want parents to extend or override the menu, or share population code).

Example: populate from a parent handler


bool ViewImpl::OnEvent(GLX::Object & src, GLX::Event & e)
{
	if (src == m_popup && e.id == GLX::Popup::kMenuOpen)
	{
		auto menu = GLX::GetMenu(e);

		menu->AddItem(L"Parent option");

		return true;	//typically trap to prevent ancestors adding items
	}

	return GLX::Object::OnEvent(src, e);
}

Styling

Popup itself is a widget (Object) that opens a Menu; menu styling is controlled via the Menu style schema. Define a menu sub-style that implements the Menu style schema.

Assuming you have a 'menu' defined previously in your stylesheet, a typical Popup style might look like this:


Popup:
{
	color: 0;
	bg: Border(),Text(font: FontID; value: &value; indent: 16,8);	//&value set in code via GLX::SetText

	@Alias menu;   //re-use an existing 'menu' style
}

As selecting a menu item will typically lead to a state change, the typical pattern is to display the current value in OnUpdate, for example:


void ViewImpl::OnUpdate()
{
	Key32 mode = app->GetMode();                //some state property

	GLX::SetText(m_popup, GetModeString(mode)); //GetModeString is some helper to stringify enum values
}

GLX::RangeBar

Inherits from AbstractViewBar

Emits a "Transaction" event, see GLX::kTransaction.

GLX::RotarySlider

Inherits from Object

RotarySlider is the base numeric drag control used by knob-like widgets such as DragEdit.

It exposes krange and kvalue properties, emits transaction-style updates while dragging, supports keyboard stepping, and provides built-in reset behaviour via right click or double click when those actions are not otherwise handled.


void RotarySlider::SetSensitivity(Float32 pixels);

bool RotarySlider::SetRange(Float32 min, Float32 max, Float32 step);

Pair <Range,Float32> RotarySlider::GetRange();

void RotarySlider::SetDefault(Float32 value);

Float32 RotarySlider::GetDefault();

void RotarySlider::Reset();

bool RotarySlider::SetValue(Float32 value);

Float32 RotarySlider::GetValue();

GLX::Scroller

Inherits from AbstractViewPort

The Scroller widget provides a scrollable container.

It hosts a single content object and automatically manages x and y scrollbars which appear dynamically when content exceeds the visible region.

Its often used with the List and VirtualList widgets for the content. It also forms the basis for the standard Menu implementation.


auto scroller = New<GLX::Scroller>();
auto content = New<GLX::Object>();

// Set content size (normally comes from content layout)
GLX::SetBounds(content, {}, { 512.0f, 1024.0f });

scroller->SetContent(content);

Once the content is assigned, ViewPort automatically calculates the visible region and shows the relevant scrollbars as needed.

Styling

Scroller applies the following sub-styles:

  • body: background and container styling.
  • content: applied to the hosted content object.
  • x: horizontal scrollbar.
  • y: vertical scrollbar.

Example style block:


List:
{
	clip: true;  //alternatively you can set on body (for slightly different clipping behaviour)

	content:
	{
		bg: Tile(stride: 32; axis: y; content: Line(position: bottom; colour: 128; pattern: dashed));
	};

	y:
	{
		size: 32;
		bg:
		Fill(colour: 0,32),
		Bar(range: &range; region: &region; content: Border(colour: 0,64; corner: 4));
	};
};

The 'Bar' layer is particularly useful for scrollbar styling.

By binding to 'range' and 'region' (published by the scrollbars), you can draw the visible "trackbar" area that represents the current view region within the total content.

Layout Behaviour

By default, Scroller applies kFlowY to its own flow.

The flow direction determines how scrollbars are arranged.

  • kFlowY: Y-bar floats over content (right side), X-bar appears inline at the bottom, reducing vertical content space.
  • kFlowX: Y-bar appears inline at the right (reducing width), X-bar floats over content at the bottom.

The flow direction of the content (not the ViewPort itself) affects navigation behaviour (mouse wheel, keyboard PageUp/Down, Home/End).

Tips & Tricks

  • use Reveal to automatically scroll to particular region (see also List and VirtualList Reveal)
  • use CreateListener to receive notifications on scrolling
  • in your stylesheet, use fg: InnerShadow with the same colour of the content colour as a quick way to blend out content at the edges

GLX::Selector

Inherits from Object

Selector is a one-panel-at-a-time content switcher.

Panels are registered up front, then SelectPanel swaps the active content and emits a selection event with both the selected item and index. Depending on style, the content swap can be animated.

EnableContentAutoFit is useful when the selected panel should determine the container size from its real content rather than from style constraints alone.


void Selector::EnableContentAutoFit(bool enable);

void Selector::Clear();

void Selector::AddPanel(TRef <Object> item, Key32 style_id_opt);

void Selector::RemovePanel(UInt32 idx);

UInt32 Selector::GetNumPanel();

TRef <Object> Selector::GetPanel(UInt32 idx);

void Selector::SelectPanel(UInt32 idx);

Idx Selector::GetCurrentIndex();

GLX::Split

Inherits from Object

Split is a thin GLX::Object wrapper around SplitBehaviour for resizable split layouts.

The public API stays intentionally small: you typically set, clear, or query the split size for child items and let the attached behaviour handle the interaction details.

GLX::Style

Inherits from Data::PropertySet


const Key32 Style::id;

void Style::SetParent(Style& child);

void Style::Clear();

void Style::InsertBefore(Style& child);

void Style::InsertAfter(Style& child);

void Style::Detach();

void Style::Attach(Style& child);

GLX::StyleSheet

Inherits from Style


const Key32 StyleSheet::path;

GLX::TabGroup

Inherits from Form

Container widget that extends GLX::Form to provide a tab-based layout.

TabGroup uses a GLX::Selector as its body and populates the form header with tab buttons. Selecting a tab activates the corresponding panel in the underlying selector.

Panels are added with 'AddPanel(label, content, style_id, tab_style_id)'. The 'style_id' is applied to the panel in the selector body, and the 'tab_style_id' is applied to the corresponding tab in the header.

By default, tabs use the header > tab style, and panels use the body > content style.

Behaviour

Clicking a tab selects the matching panel.

Keyboard navigation is also supported: Tab moves to the next tab and shift+Tab moves to the previous.

The underlying selector can animate panel changes by setting the body > animate property to true.

Events

TabGroup does not define any custom event IDs of its own.

Selection is driven by the underlying GLX::Selector, and TabGroup forwards the selector's panel-selection notification.

To observe selection changes, either listen on the selector returned by `GetSelector()`, or handle `GLX::Selector::kSelectPanel` on the TabGroup itself.

Styling

Extends the Form style schema.

  • header: extended with a 'tab' style entry used as the default tab style, plus an align_content property (align_content: [near|far|center|fit];)
  • body: applied to the Selector body

The header > align_content property controls how tabs are positioned in the header, via the standard orientation keys (near|far|center|fit)


TabGroup
{
	header:
	{
		align_content: center;
	};
};

In the simple case, all tabs use the same 'header > tab' style and all panels use 'body > content'.


TabGroup:
{
	fg: InnerShadow(width: 0,3,0,0; colour: 0,32);

	header:
	{
		align_content: center;

		bg_colour: 128;
		fg: Line(position: bottom; colour: 200);

		tab:
		{
			transition: 0.25;

			bg:
			[
				Text(indent: 8,4; font: Medium; value: &value; colour: kTextColour)
			];

			@State hover:
			{
				bg:
				[
					Fill(indent: 2,4; colour: 240,128; corner: 4),
					Text(indent: 8,4; font: Medium; value: &value; colour: 0)
				];
			};

			@State selected:
			{
				bg:
				[
					Fill(indent: 2,4; colour: 240; corner: 4),
					Text(indent: 8,4; font: Medium; value: &value; colour: 0)
				];
			};
		};
	};

	body:
	{
		animate: true;		//GLX::Selector property for ease/in animation

		content:
		{
			bg: Fill(colour: 252);
		};
	};
};

To style each tab/panel individually, define additional styles under header and pass their ids through the tab_style_id parameter of 'AddPanel(...)'.


tabs.AddPanel(L"Overview", overview_panel, "OverviewPanel", "OverviewTab");
tabs.AddPanel(L"Logs", logs_panel, "LogsPanel", "LogsTab");
tabs.AddPanel(L"Settings", settings_panel, "SettingsPanel", "SettingsTab");

TabGroup:
{
	header:
	{
		align_content: fit;

		@SVG TabIcons:
		{
			path: "icons/tabs.svg";
		};

		OverviewTab:
		{
			size: 36;

			bg:
			[
				Image(source: TabIcons > overview; fit: contain; anchor: top; indent: 8,0; colour: 96),
				Text(font: Medium; value: &value; anchor: bottom; colour: 96)
			];
		};

		LogsTab:
		{
			size: 36;

			bg:
			[
				Image(source: TabIcons > logs; fit: contain; anchor: left; indent: 8,0; colour: 96),
				Text(indent: 30,4; font: Medium; value: &value; colour: 96)
			];
		};

		SettingsTab:
		{
			size: 36;

			bg:
			[
				Image(source: TabIcons > settings; fit: contain; anchor: left; indent: 8,0; colour: 96),
				Text(indent: 30,4; font: Medium; value: &value; colour: 96)
			];
		};
	};

	body:
	{
		animate: true;

		OverviewPanel:
		{
			bg_colour: 200;
		};
	};
};

void TabGroup::Clear();

TRef <Object> TabGroup::AddPanel(const WString::View& label, TRef <Object> content, Key32 style_id, Key32 tab_style_id);

void TabGroup::RemovePanel(UInt32 idx);

TRef <Selector> TabGroup::GetSelector();
ConstTRef <Selector> TabGroup::GetSelector();

GLX::TextArea

Inherits from Scroller

TextArea is a scrollable wrapper around TextEditBehaviour.

It owns a content object with a Text property, installs the editing behaviour for that property, and exposes simple ClearText, SetText, and GetText helpers.

The constructor's multi_line flag changes both text flow and scrolling setup, so single-line and multi-line usage are configured differently from the start.


void TextArea::ClearText();

void TextArea::SetText(const WString& label);

WString::View TextArea::GetText();

GLX::VirtualList

Inherits from AbstractList

VirtualList is the large-data counterpart to List.

Instead of owning one child object per row, it materialises only the currently visible items and repopulates them through a callback as the viewport moves. Selection is tracked separately and re-applied when rows come back into view.

This makes it a better fit for long or dynamic datasets, but note that GetItem only returns objects that are currently visible.


void VirtualList::SetPopulateCallback(const Function <void(UInt start, ArrayRegion <Reference<GLX::Object>> items, const GLX::Style & style)>& callback);

void VirtualList::ClearItems();

void VirtualList::SetNumItem(UInt32 n, bool force_refresh);

void VirtualList::Rebuild();

TRef <Object> VirtualList::GetItem(UInt32 idx);

GLX::WindowClient

Inherits from Object

GLX::Zoomable

Inherits from AbstractViewPort


Reflex > SIMD

SIMD::Boolean


kBooleanFalse
kBooleanTrue

SIMD::BoolV4


using BoolV4 = TypeV4 <Boolean>;

SIMD::FloatV4


using FloatV4 = TypeV4 <Float32>;

SIMD::IntV4


using IntV4 = TypeV4 <Int32>;

SIMD::Abs


FloatV4 Abs(const FloatV4& value);
IntV4 Abs(const IntV4& value);

SIMD::And


BoolV4 And(const BoolV4& a, const BoolV4& b);

SIMD::Any


bool Any(const BoolV4& value);

SIMD::ClipNormal


FloatV4 ClipNormal(const FloatV4& value);

SIMD::Count


Int32 Count(const BoolV4& value);

SIMD::Empty


bool Empty(const BoolV4& value);

SIMD::Exp


FloatV4 Exp(const FloatV4& value);

SIMD::Exp2


FloatV4 Exp2(const FloatV4& value);

SIMD::Full


bool Full(const BoolV4& value);

SIMD::GetFlags


Int32 GetFlags(const BoolV4& value);

SIMD::GetFree


UInt32 GetFree(const BoolV4& value);

SIMD::Invert


FloatV4 Invert(const FloatV4& value);

SIMD::Log


FloatV4 Log(const FloatV4& value);

SIMD::Log2


FloatV4 Log2(const FloatV4& value);

SIMD::Max


FloatV4 Max(const FloatV4& a, const FloatV4& b);
IntV4 Max(const IntV4& a, const IntV4& b);

SIMD::Min


FloatV4 Min(const FloatV4& a, const FloatV4& b);
IntV4 Min(const IntV4& a, const IntV4& b);

SIMD::Modulo


FloatV4 Modulo(const FloatV4& a, const FloatV4& b);

SIMD::Not


BoolV4 Not(const BoolV4& a);

SIMD::Or


BoolV4 Or(const BoolV4& a, const BoolV4& b);

SIMD::Pow


FloatV4 Pow(const FloatV4& x, const FloatV4& y);

SIMD::Reciprocal


FloatV4 Reciprocal(const FloatV4& value);

SIMD::RoundDown


FloatV4 RoundDown(const FloatV4& value);

SIMD::RoundNearest


FloatV4 RoundNearest(const FloatV4& value);

SIMD::Select


TypeV4 <TYPE> Select(const BoolV4& mask, const TypeV4 <TYPE>& true_value, const TypeV4 <TYPE>& false_value);
TypeV4 <TYPE> Select(const BoolV4& mask, const TypeV4 <TYPE>& true_value);

SIMD::SelectNot


TYPE SelectNot(const BoolV4& a, const TYPE& b);

SIMD::Sign


FloatV4 Sign(const FloatV4& value);

SIMD::SquareRoot


FloatV4 SquareRoot(const FloatV4& x);

SIMD::operator!=


BoolV4 operator!=(const FloatV4& a, const FloatV4& b);
BoolV4 operator!=(const IntV4& a, const IntV4& b);
BoolV4 operator!=(const BoolV4& a, const BoolV4& b);

SIMD::operator&


IntV4 operator&(const IntV4& a, const IntV4& b);

SIMD::operator*


FloatV4 operator*(const FloatV4& a, const FloatV4& b);
IntV4 operator*(const IntV4& a, const IntV4& b);

SIMD::operator+


FloatV4 operator+(const FloatV4& a, const FloatV4& b);
IntV4 operator+(const IntV4& a, const IntV4& b);

SIMD::operator-


FloatV4 operator-(const FloatV4& value);
FloatV4 operator-(const FloatV4& a, const FloatV4& b);
IntV4 operator-(const IntV4& a, const IntV4& b);

SIMD::operator/


FloatV4 operator/(const FloatV4& a, const FloatV4& b);

SIMD::operator<


BoolV4 operator<(const FloatV4& a, const FloatV4& b);
BoolV4 operator<(const IntV4& a, const IntV4& b);

SIMD::operator<=


BoolV4 operator<=(const FloatV4& a, const FloatV4& b);

SIMD::operator==


BoolV4 operator==(const FloatV4& a, const FloatV4& b);
BoolV4 operator==(const IntV4& a, const IntV4& b);
BoolV4 operator==(const BoolV4& a, const BoolV4& b);

SIMD::operator>


BoolV4 operator>(const FloatV4& a, const FloatV4& b);
BoolV4 operator>(const IntV4& a, const IntV4& b);

SIMD::operator>=


BoolV4 operator>=(const FloatV4& a, const FloatV4& b);

SIMD::operator|


IntV4 operator|(const IntV4& a, const IntV4& b);

template SIMD::TypeV4 <TYPE>


TYPE TypeV4::data;

TypeV4 <TYPE>& TypeV4::operator=(const TypeV4 <TYPE>& value);
TypeV4 <TYPE>& TypeV4::operator=(TYPE value);

void TypeV4::Set(TYPE value);
void TypeV4::Set(TYPE a, TYPE b, TYPE c, TYPE d);

TypeV4 <TYPE>& TypeV4::operator+=(const TypeV4 <TYPE>& value);

TypeV4 <TYPE>& TypeV4::operator-=(const TypeV4 <TYPE>& value);

TypeV4 <TYPE>& TypeV4::operator*=(const TypeV4 <TYPE>& value);

TypeV4 <TYPE>& TypeV4::operator/=(const TypeV4 <TYPE>& value);

TYPE& TypeV4::operator[](UInt32 idx);
const TYPE& TypeV4::operator[](UInt32 idx);

TYPE* TypeV4::GetData();
const TYPE* TypeV4::GetData();

TYPE TypeV4::ReadFirst();

Reflex > System

System::Path


kPathTemp
kPathDesktop
kPathApplicationData
kPathUserData
kPathUserDocuments

HttpConnection::Response


kResponseAborted
kResponseNoConnection
kResponseOK
kResponsePartialContent
kResponseMovedPermanently
kResponseFound
kResponseBadRequest
kResponseUnauthorized
kResponseForbidden
kResponseNotFound
kResponseInternalServerError
kResponseServiceUnavailable

System::MouseCursor


kMouseCursorInvisible
kMouseCursorArrow
kMouseCursorWait
kMouseCursorMove
kMouseCursorLeftRight
kMouseCursorTopBottom
kMouseCursorTopLeftBottomRight
kMouseCursorBottomLeftTopRight
kMouseCursorPointer
kMouseCursorDrag
kMouseCursorText
kMouseCursorBlock
kMouseCursorZoom
kNumMouseCursor

System::KeyCode


kKeyCodeNull
kKeyCodeF1
kKeyCodeF12
kKeyCodeTab
kKeyCodeEnter
kKeyCodeEscape
kKeyCodeSpace
kKeyCodeBackspace
kKeyCodeInsert
kKeyCodeDelete
kKeyCodeHome
kKeyCodeEnd
kKeyCodePageUp
kKeyCodePageDown
kKeyCodeUp
kKeyCodeDown
kKeyCodeLeft
kKeyCodeRight
kKeyCodeNumericDivide
kKeyCodeNumericMultiply
kKeyCodeNumericMinus
kKeyCodeNumericPlus
kKeyCode1
kKeyCode0
kKeyCodeMinus
kKeyCodePlus
kKeyCodeSlash
kKeyCodeA
kKeyCodeZ
kKeyCodeBracketOpen
kKeyCodeBracketClose
kNumKeyCode

System::ModifierKeys


kModifierKeyShift
kModifierKeyCtrl
kModifierKeyAlt
kModifierKeySystem

System::ColourPoint


using ColourPoint = Tuple < Point <Float32> ,Colour>;

System::fPoint


using fPoint = Point <Float32>;

System::fRect


using fRect = Rect <Float32>;

System::fSize


using fSize = Size <Float32>;

System::Delete


bool Delete(const WString& path);

System::Exists


bool Exists(const WString& path);

System::GetElapsedTime


Float64 GetElapsedTime();

System::GetNumProcessor


UInt32 GetNumProcessor();

System::GetOperatingSystemVersion


CString GetOperatingSystemVersion();

System::GetPath


WString GetPath(Path path_id);

System::GetSystemID


UInt64 GetSystemID();

System::GetTime


UInt64 GetTime();

System::IsDirectory


bool IsDirectory(const WString& path);

System::MakeDirectory


bool MakeDirectory(const WString& path);

System::Open


bool Open(const WString& path);

System::Rename


bool Rename(const WString& from, const WString& to);

System::Colour


Float32 Colour::a;

Float32 Colour::b;

Float32 Colour::g;

Float32 Colour::r;

HttpConnection::ReceiveDataFn

HttpConnection::ReceiveHeaderFn

System::DirectoryIterator

Inherits from Object

System::DiskIterator

Inherits from Object

System::DynamicLibrary

Inherits from Object

System::FileHandle

Inherits from Object


bool FileHandle::Status();

bool FileHandle::IsWriteable();

UInt64 FileHandle::GetSize();

void FileHandle::SetPosition(UInt64 position);

UInt64 FileHandle::GetPosition();

UInt32 FileHandle::Read(void* bytes, UInt32 buffer_capacity);

UInt32 FileHandle::Write(const void* bytes, UInt32 size);

System::HttpConnection

Inherits from Object


void HttpConnection::SetTimeout(Float32 connection, Float32 transfer);

HttpConnection::Response HttpConnection::Request(const CString::View& method, const CString::View& resource, const ArrayView < Pair < Array <char> , Array <char> > >& headers, const ArrayView <UInt8>& body, const HttpConnection::ReceiveHeaderFn& receive_header, const HttpConnection::ReceiveDataFn& receive_data);

System::Process

Inherits from Thread

System::Renderer

Inherits from Object

System::Renderer::Canvas

Inherits from Object

System::Renderer::Graphic

Inherits from Object

System::Task

Inherits from Object

System::Thread

Inherits from Task

System::Window

Inherits from Object