Skip to content

UXNib

UXNib loads an interface from a .rsc file. The rest of the design follows from one property: there is no inflation step.

#use <UXKit> // or #import "UXNib.xc"

A GEM resource already contains an OBJECT tree, and a UXView is backed by an OBJECT. Loading a nib means loading that tree and binding a view onto each entry. Nothing is copied or rebuilt, and on GEM the AES walks the resource’s own array directly.

Rocks (macOS) --writes--> app.rsc --load--> OBJECT[] --bind--> UXViewTree

The resource editor is the interface builder. A dialog designed there becomes a live view hierarchy with no conversion, rather than a description that a loader reconstructs.

UXViewTree* tree = UXNib.load((u8*)"app.rsc", 0); // tree 0 of the file

Views are chosen by ob_type. The resource supplies the type, frame, flags and state; your code supplies the behaviour.

Loading gives you a hierarchy. Connecting it to a controller is the other half, and it needs no per-application code:

UXViewTree* tree = UXNib.loadWired((u8*)"app.rsc", 0, (UXDesignable*)controller);

loadWired reaches your controller through UXDesignable and connects the nib’s outlets and actions by name.

Your controller declares, the compiler generates

Section titled “Your controller declares, the compiler generates”
class MainController : Object
{
outlet UXLabel* statusLabel;
outlet UXView* canvas;
void onSave(UXControl* sender) :action { … }
void onQuit(UXControl* sender) :action { … }
}

Declaring any outlet field or :action method auto-conforms the class to UXDesignable, and the compiler generates both method bodies from the decorations:

  • setOutlet(name, value): a checked assignment per outlet, returning false on an unknown name or a type mismatch
  • wireAction(name, control): control.setAction(&self.<method>) per action

There is no reflection beyond what the decorations declare, and the compiler checks every connection. A nib naming an outlet your controller does not have fails at load with a false return, instead of leaving a null field that crashes later.

load · loadWired · loadWiredMem · loadDoc · viewForType · make · registerViewFactory · registerObjectFactory · classOverride

static UXViewTree* load(u8* path, i32 treeIndex)

Loads one tree from a .rsc file and binds views onto it. A resource holds several trees (a dialog, a menu, an about box), addressed by index.

static UXViewTree* loadWired(u8* path, i32 treeIndex, UXDesignable* owner)

load, then connect outlets and actions on owner by name.

static UXViewTree* loadWiredMem(u8* data, i32 len, i32 treeIndex, UXDesignable* owner)

The same from bytes already in memory: a resource compiled into the binary, or fetched rather than read from disk.

static UXViewTree* loadDoc(pointer doc, i32 treeIndex, UXDesignable* owner)

From an already-parsed document, when you load several trees out of one file and do not want to re-read it per tree.

static UXView* viewForType(u16 gtype)

The default type-to-view mapping: G_BUTTON becomes a UXButton, G_FTEXT a UXTextField, and so on.

static void registerViewFactory(pointer fn)

Overrides the mapping, so a resource object can become your view subclass instead of the stock one. A custom widget designed in the editor comes back this way as the class that implements it.

static void registerObjectFactory(pointer fn)

The same for non-view objects named by the nib.

static Object* make(u8* cls)

Instantiates by class name, through the registered factory.

static u8* classOverride(pointer doc, i32 ncl, i32 tree, i32 obj)

The class name a resource records for a particular object, when it wants something other than the default for that type. This is the stored form of “this button is a FancyButton”.

The resource owns the layout; you own the behaviour. Frames, flags and initial state come from the file, so moving a control is an edit in the editor instead of a recompile. Anything you set in code after load is overwritten on the next load, because the file is the source of truth for those fields.

A tree index is positional. Trees are addressed by number, not name, because .rsc does not store tree names. If you delete a tree in the editor, every index after it shifts. Rocks renumbers the links it owns, but you must keep any index hard-coded in your source correct.

  • UXDesignable: the two generated methods, and the outlet / :action decorations
  • UXNibV2: the newer format, with variants per form factor
  • UXViewTree: what a load produces
  • UXView: adoptObject, the binding step