VDSTools

Native macOS window chrome, toolbar and controls, in pure Xojo — no plugin, no compiled Objective-C. Target: macOS 15 and later.

101classes
774public methods
8subfolders
15+macOS

The model

Three ideas carry the whole library. Understanding them saves reading the rest in order.

The owner registry

A callback from the Objective-C runtime receives nothing but a bare pointer. Cocoa.RegisterOwner maps that pointer to the Xojo object, in a dictionary of WeakRef that purges itself as it is read. OwnerOf finds it again.

One target, not five subclasses

A single object created at runtime carries itemAction: and validateToolbarItem:. AppKit queries the target before validating an item: clicks and dimming work without one line of compiled code.

Host inside, not beside

An AppKit view goes inside the view of a DesktopCanvas serving as an anchor. Xojo carries on placing the anchor, autoresizing makes the rest follow: no geometry to recalculate.

Compatibility

The target is macOS 15, but several settings appeared later — or earlier, and are worth noting. Each one is guarded by Cocoa.Responds or by a nil ClassRef: on an earlier system the call is without effect, never an error.

SinceWhat depends on it
macOS 10.10NativeStatusItem — everything goes through button, the item accessors are deprecated
macOS 10.13NativeLevelIndicator.SetBands (fillColor), NativeSegmentedControl.SetDistribution
macOS 10.15NativeSwitch, NativeColorSampler
macOS 11NativeFilePanel.SetAllowedExtensions (UTType), NativeButton.SetDestructive, NativeAlert.SetDestructiveButton, window title and subtitle, toolbar styles
macOS 13NativeComboButton, NativeColorWell.Styles.Minimal and .Expanded
macOS 14NativeButton.BezelStyles.Automatic, NativePopover.SetFullSizeContent, nearly all of NativeMenu — section headers, badges, palettes, selection modes; subtitles wait for 14.4
macOS 26NativeGlassEffectView, NativeButton.BezelStyles.Glass, badge and background tint of toolbar items
macOS 27NativeMenu.ImageVisibility — without it, item symbols disappear —, NativeGlassEffectView.Interactive

Pitfalls

The ones that cost dearly. Almost every one has a symptom that points somewhere other than the cause.

Xojo wipes an inherited button's image, forever

Measured on 17 September 2026 on a DesktopButton placed in a window. setImage: is received — right after the call, [button image] returns the image and imagePosition the value set. On the NEXT click, before touching anything: image gone, imagePosition back to 0. Nothing had touched it in between.

Neither the cell nor the bezel is to blame: a plain NSButtonCell, a Push bezel, both of which draw an icon perfectly outside Xojo. XOJButton overrides no drawing method — only events. But the framework itself calls setImage:, setImagePosition: and setButtonType:, the last of which resets image and bezel, and it does so AFTER Opening and on every pass.

What does not work: Timer.CallLater(0) to set the image later, setNeedsDisplay: to force the drawing. There is no hook before an inherited control is drawn.

What works: HOSTING rather than inheriting — NativeIconButtonControl. The button is then created by the library, which Xojo does not know and never reconfigures.

An icon above the title falls outside the frame, with the Push bezel

Found by rendering, on macOS 27. With ImagePosition = Above (or Below) and the Push bezel — the ordinary button's — AppKit draws the icon inside the frame and the title OUTSIDE, below the pill. Tried at 32, 40, 46, 52 and 60 points tall: the bezel keeps its own fixed height and the text stays outside, at every height.

The cause is not missing room: the Push bezel has a height of its own, which the control's frame does not command. fittingSize gives no warning — it returns 24 points tall for Above just as for a button with no image.

What works: a bezel that agrees to grow — FlexiblePush (2) or SmallSquare (10), both verified. Icon and title then fit inside the frame, stacked. With Leading, Trailing, Left or Right, the Push bezel is perfectly fine.

The dragged row vanishes: AppKit's image is not enough

Found frame by frame on macOS 27, in two recordings. The image: for a view-based table, the header says AppKit builds it from NSTableCellView's draggingImageComponents — its text field and image. Plain NSView cells provide almost nothing: a white title, then nothing at all. The selection: in Gap style, the grabbed row is hidden for the whole drag; what is being moved is visible nowhere. And over the middle of a row, AppKit proposes DropOn; refusing it leaves no destination.

What works, and mirrors Numbers: Regular style, so the original row stays in place; photograph the rows as early as pasteboardWriterForRow: with cacheDisplayInRect: on the table; place a ghost view in the table — those photos stacked in an opaque rounded card with an accent-colour border, the drop shadow carried by an outer view that clips nothing (a plain translucent copy blended into macOS 27's white background) — that validateDrop: moves on every motion from draggingLocation, and that draggingSession:endedAtPoint: removes; empty AppKit's image with setDraggingFrame:contents:; retarget DropOn to DropAbove with setDropRow:dropOperation:.

On macOS 27, a white field on a white window

The field has not lost its border: it has lost its contrast. Measured on a 27.0 system in light appearance, windowBackgroundColor is now 255/255/255 — exactly the white of textBackgroundColor, the field's interior. Only a line of about 12 % grey remains, which the eye no longer sees on a scaled-down screenshot. Square and Rounded are then drawn identically, and every field is affected: DesktopTextField as much as NativeTextField, it is the same NSTextField.

Two intuitive remedies fail, measured too: setting a backgroundColor on a bezeled field is ignored, and dropping the bezel for a plain line gives a hard stroke, bright white in dark mode. What works is the System Settings layout: put the fields in a group box, whose grey makes the white stand out — and an NSBox is drawn on macOS 15 too, with no version test. Mind the empty title: it still reserves 12 points at the top of the box. Increase contrast, in the accessibility settings, strengthens borders; that is the user's choice, not the application's.

On macOS 27, menu icons disappear without an error

The code has not changed, the symbol is set through setImage:, and the menu opens without its icon. NSMenuItem's header says it plainly: from macOS 27 on, AppKit “determines the visibility of menu item images, and will typically hide images”. The new preferredImageVisibility property is Automatic on creation — measured on a 27.0 system.

The remedy is one setting, Visible, behind a selector test. NativeMenu sets it by default: a symbol passed as an argument is an explicit request, and handing it back to the system is still possible with ImageVisibility = Automatic. The pitfall only concerns items with an image: a palette menu's swatches have none.

Testing for a CLASS method: two idioms, two different arguments

respondsToSelector: is a MESSAGE sent to an object: it searches the class of that object. For a class method you therefore send it to the class itself. Send it to the metaclass and it searches the meta-metaclass, and never finds anything.

The runtime function class_respondsToSelector(cls, sel) is the other idiom: it looks at the instance methods of the class handed to it, so for a class method you must pass the metaclass. Both are correct; mixing them is not.

The symptom is silent and misleading: a feature reports itself unavailable on a machine that supports it, with no error anywhere. Observed on the macOS 15 cursors, reported absent on a 15.7.9 system.

Never derive one pane's geometry from another's

Under NSSplitViewController, two full-height panes have the same height — in steady state. AppKit does not resize them at the same instant, and a full-screen toggle is enough to show it: laying the inspector's content out from the height reported by the detail pane's resize event gives a wrong frame.

The symptom misleads twice over. Too tall on the way in: since AppKit's origin is at the BOTTOM, the content leaves through the top of the pane and the panel looks empty — which reads as a drawing bug, not as a frame that is too large. Too short on the way back: the content ends up stuck at the bottom. And nothing shows in steady state, only during the transition.

Each pane must be read from its own bounds. The value received in the event serves only as a fallback for the very first pass, before the controller has laid the pane out — at construction, its bounds are still those of its factory.

The customisation palette goes through the same delegate

toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar: serves two purposes: populating the bar, and building the customisation sheet. Returning the same NSToolbarItem in both cases confuses the item displayed in the sheet with the one in the bar: dragging it to the bar does nothing, without the slightest error. The header states it — each toolbar receives its own distinct copies and each time this method is called a new instance, and the palette is built through that same method.

Since NSToolbarItem is NSCopying, return an autoreleased copy when willBeInserted is NO, and the real instance when it is YES: live property changes on an item already placed then carry on working.

Two orderings decide the rest. autosavesConfiguration must be YES BEFORE setToolbar: — that is the moment AppKit reads the saved configuration back; set afterwards, it only writes, and the bar returns to the default set at every open. And displayMode and the style must be set before attaching, as starting values: set afterwards, they overwrite the mode the user has just chosen in the sheet every time.

The autosave key is NSToolbar Configuration <identifier> in the application's own preferences — read from a real one, not guessed. No API resets it: it has to be deleted and the default identifiers re-inserted.

NSOutlineView compares the pointers you hand it

The data source returns one “item” per node, and NSOutlineView keeps those objects and compares them to know what is expanded and what is selected. Returning a new object on every call — even an equivalent one — loses the expanded state and the selection on every refresh.

So the keys must be stable and retained: one NSString per node, tied to its number — never to its row, which changes as soon as something is inserted or moved —, cached in a Dictionary, with the reverse table to find the node again from the pointer. A key stays retained until the tree is cleared, even for a removed node: AppKit may still hold it during the removal animation. And setOutlineTableColumn: designates the column that carries the triangles: without it, AppKit puts them where it likes and the indentation does not follow the text.

A node identifier must not be a row number

Returning the row index as identifier is tempting: as long as you only append, nothing moves and everything works. But removing or inserting in the middle shifts every row above — the identifiers the caller kept, the parent table, and the keys handed to NSOutlineView. Expansion and selection then jump from one node to another, a child hangs off the wrong parent, and nothing raises.

The cure is a type of its own, not a more stable integer: a never-reused number kept as an Integer would have compiled everywhere and matched the row until the first removal. NativeOutlineNode makes the compiler refuse every place that confused the two. And since an index AppKit does not know raises an Objective-C exception, animation is only attempted on a parent that is shown and expanded; elsewhere, the tree is reloaded.

A folder is not necessarily a folder

FolderItem.IsFolder returns True for an application, an .mpkg, a Photos library, an Xcode project, an RTFD document. macOS calls that a package and it alone knows: you have to ask NSURLIsPackageKey, NSURLIsApplicationKey and NSURLContentTypeKey. A modern .pkg, for its part, is not a folder at all — it is a flat file.

Two pitfalls in the detail. The order of the questions: an alias to an application is an alias first, an application is a package before it is a folder. And you must resolve the symbolic links/Applications/Safari.app is one, and would otherwise pass for a plain alias. Finally, read the keys rather than copying them out: NSURLIsApplicationKey is in fact _NSURLIsApplicationKey, with a leading underscore that nothing announces.

RealityKit is out of reach from Xojo

RealityKit has replaced SceneKit since macOS 26, but it exposes no view class to Objective-C: neither ARView, nor RealityView, nor Entity, nor ModelEntity. Verified at runtime — the framework's only ObjC classes are Swift internals with mangled names (_TtC10RealityKit…). Its public headers cover nothing but Metal shaders.

Since Xojo speaks only Objective-C, RealityKit cannot be reached. SceneKit therefore remains the only workable route to 3D in Xojo, deprecated though it is — and that will have to be known the day Apple withdraws it: the fallback will be the QuickLook thumbnail, static.

The size asked of QLThumbnailImageCreate is a BOX, and 3D wants 4:3

The size parameter is not a scaling instruction but a box the thumbnail has to fit into. The 3D generator returns 4:3: any box narrower than that ratio makes it fail without saying anything. Measured on an STL — 840 × 340 returns Nil, 400 × 267 too, whereas 512 × 512 returns 512 × 384 and 400 × 300 returns 400 × 300.

So we ask for a square, which contains any natural ratio, and let the view scale it. Beyond 1024 the generator caps itself. The trap is a nasty one because the failure is silent and looks like “this format is not handled”: you fall back to the icon believing QuickLook cannot manage it.

Closing a QLPreviewView twice ends the process in silence

close is final — the view accepts nothing afterwards — but nothing stops you calling it a second time, and that makes the application vanish with no message and no trace. The case arrives quickly as soon as two paths close: one on the page change, the other before rebuilding.

So an internal flag is needed, and it must be checked in close as well as by the caller. Available() must return False after closing, failing which a careful caller will believe itself allowed to close again.

LIVE preview or THUMBNAIL: 3D makes the choice compulsory

A QLPreviewView installs an NSRemoteView served by SceneKitQLPreviewExtension — another process, which moreover outlives its client and accumulates. On a 3D document, that destroys the process's drag and drop for good: nothing can be dragged into the application any more, and the Finder even refuses to lift a file. Neither close, nor destroying the view, nor changing page does anything about it — only quitting the application. Images, PDFs, text and audio are unharmed.

The thumbnail is the way out: QLThumbnailImageCreate goes through SceneKitQLThumbnailExtension, the one the Finder uses constantly. It returns an image — no out-of-process view in the window, no process left behind — with the full 3D rendering. Deprecated since 10.15 but still present and synchronous (43 ms on an STL), where the modern API demands a block and a callback off the main thread. It returns Nil on folders, applications and unknown formats: you then fall back to the Finder icon, and the breakage of the day it disappears is already provided for.

A class outside AppKit does not exist until you load its framework

QLPreviewView belongs to QuickLookUI, under the Quartz umbrella — which Xojo never links. Verified: a binary that does not link Quartz does not find the class, objc_getClass returns Nil. So an explicit dlopen is needed before any ClassRef.

And on the full path: dlopen("Quartz") fails, as does dlopen("AppKit") for that matter — the short names Xojo accepts in a Lib are not the ones dlopen resolves. A pleasant surprise, on the other hand: NSURL already conforms to the QLPreviewItem protocol, there is no runtime class to build.

A Xojo keyword as an enumeration member, and the error blames the uses

Soft is a keyword — the one from Soft Declare — and Xojo ignores case: Soft=1 in a #tag Enum is a “Syntax error”. The symptom is misleading to the point of being disabling: Xojo reports every line that writes MyEnum.Soft and declares the enumeration block sound, as long as one use remains to blame.

The error only lands on the declaration once every reference has been removed. The rule to draw from it: when a construct rigorously identical to code that compiles is refused, it is an identifier — and you must go and read the declaration it names, not the line the compiler points at.

addRow: raises if the root row is not compound

Setting a simple predicate on an NSPredicateEditor reduces it to a single row, with no compound root. The next addRow: does not stay inert: it raises an NSRangeException in -[NSRuleEditor _insertNewRowAtIndex:ofType:withParentRow:] — and an Objective-C exception ends the Xojo process.

The remedy costs nothing: wrap the predicate in an AND with a single sub-condition. Measured — the string returned by predicateFormat is identical, but the editor gets its compound row back. rowTypeForRow(0) lets you detect the dangerous state before acting; an empty editor, for its part, accepts addRow: without trouble.

An NSTableHeaderView with a zero frame is installed, and invisible

Hiding a table header means setHeaderView:nil — the NSScrollView then folds away the area it was reserving for it. Bringing it back by building a fresh header works too, provided you give it a height: with a zero frame it really is installed, the scroll view restores its area… at a height of zero.

So nothing is visible, with not the slightest error to say so. Better to keep the original header — retained, since setHeaderView:nil releases it — than to guess a height that depends on the system and the table style.

The menu checkmark image is SHARED

paletteMenuWithColors:titles:selectionHandler: leaves the swatches with the ordinary state image: NSMenuCheckmark, 18 × 17 points. Drawn over a swatch of about 13 points, it overflows and ends up clipped — you see a white arc instead of a checkmark.

And resizing it in place is out of the question: two distinct palettes return the same object, and so does an ordinary menu item. A fresh checkmark has to be built per item — an SF symbol at the wanted size, in template mode so that it takes its colour from the background.

AppKit has no vertical alignment - you place the frame

An NSTextField centres its text in the frame it is given, and that is all: no property says “at the top” or “at the bottom”. The vertical alignment of a cell is therefore obtained by placing the frame of the control inside the cell, not by setting anything.

Two consequences. Since the origin of a view that is not flipped is at the bottom left, “at the top” means the largest y — the opposite of intuition. And the autoresizing mask must be limited to the width: letting the height follow would undo the placement just calculated.

A style is not a control: where to put the macOS 26 edge effect

NSScrollEdgeEffectStyle has neither frame nor view: it is a style object, and the SDK accepts it on two properties only — preferredScrollEdgeEffectStyle of NSTitlebarAccessoryViewController and of NSSplitViewItemAccessoryViewController. Making it a class of the library would have been padding.

So it lives on NativeWindowChrome, which owns the window. SetScrollEdgeEffect returns the number of accessories actually styled: zero is not an error but the observation that there is none to style — the window has a title bar accessory only when the RTF format bar is open.

The table's double-click and a cell's editing aim at the same gesture

The double-click action of an NSTableView and the start of editing of an NSTextField housed in a cell both answer the second click. On an editable column they fight over the mouse, and nothing in the API says so.

DoubleClickAction = False disarms the action — a nil SEL passed to setDoubleAction:, and AppKit sends nothing more. Same logic for the look: BezeledEditableCells chooses between a field that only reveals itself on typing (the Finder rename) and a field that announces itself as editable straight away. Neither is objectively right, hence the option rather than an imposed choice.

The tag of an NSControl, to find the cell that acts

When a checkbox or a menu housed in a table fires its action, the callback receives nothing but the control — no row, no column. The tag, a free NSInteger on every NSControl, carries the flat index of the cell there, the same as the storage: row x columns + column.

You get back to both through an integer division and a modulo. An encoding like row x 1000 + column would work too, but would impose an arbitrary limit; reusing the storage index imposes none and leaves only one piece of arithmetic to check.

An editable field warns you by TWO paths

The action of an NSTextField fires on Return; controlTextDidEndEditing: fires on the loss of focus. Both are needed — one very often clicks elsewhere without validating — but they then both arrive for a single change.

The answer is not to choose: it is to do nothing when the value has not really changed. The guard makes both paths idempotent and the event fires only once.

An Objective-C exception cannot be caught from Xojo

predicateWithFormat: raises an NSInvalidArgumentException on a malformed string — verified. A Xojo Try...Catch does not intercept it: the exception passes straight through and ends the process. This is not an error you handle, it is a crash.

Consequence for the shape of the API: SetPredicateFormat must receive only strings produced by PredicateFormat, never user input. The method says so in its own comment rather than leaving it to be discovered.

An NSSet has no order - “the first item” does not exist

collectionView:didSelectItemsAtIndexPaths: returns an NSSet, not an array. Taking “the first” index from it makes no sense: anyObject is the only honest access when you want just one.

The same caution applies to selectionIndexPaths. As soon as multiple selection is allowed, exposing a single “selected index” is a simplification that has to be owned out loud.

An NSCollectionViewItem does not need a nib

Verified in Objective-C before relying on it: setView: on a fresh NSCollectionViewItem works, and loadView is never called. It is the trick NativeWindowChrome already uses for its NSViewController, and it avoids the whole detour through an interface file.

A corollary not to forget: an item whose view is set by hand draws no selection. You have to paint it yourself from didSelect / didDeselect, writing the colour at the moment of the click so that it is resolved in the current appearance.

The IDE silently restores an anchoring the chrome forbids

Under NSSplitViewController, the rule is: the size through the Xojo API — the only gesture that lays out the children of a DesktopPagePanel again —, the position through AppKit. For the second to hold, the top-level views of the detail must have LockRight and LockBottom at False. With LockTop and LockBottom at True, Xojo takes the view to be stretched from the top to the bottom of the window and puts it back at Top = 0 — under the toolbar — after the AppKit placement.

The trap is that no line of code changes: stretching a control to the bottom edge of the container in the designer is enough for the IDE to lock the anchoring, without a word. The symptom is unmistakable — the page title overlaps the window title, the toolbar labels are crossed by the content. It can be checked in the .xojo_window, on the header of the top-level block, before any nested Begin. Putting LockBottom back to False does not lose the height: the DetailResized event recalculates it on every pass.

Xojo is case-insensitive: an identifier that looks like a keyword is the keyword

isa is the IsA operator. sub, iF, dO, tO likewise. And it is not restricted to keywords: a parameter named color masks the Color type, so that Color.RGB(0,0,0) becomes a member lookup on that parameter.

A syntax error on the FIRST statement of a method stops the parsing of the whole class: its members then become “non-existent” as seen from other classes, and its constructor is reported with a wrong number of arguments. Five errors for a single bad name.

The retain is the whole subject

NSToolbar.delegate, NSToolbarItem.target, NSControl.target, NSTableView.dataSource, NSPopover.delegate: all of them weak, zeroing references. ObjCClass.CreateInstance therefore returns Retain(init(alloc(cls))).

Without that retain the instance dies at once, the weak reference empties, and no callback ever fires. The symptom reads as “the runtime refuses weak references to a dynamically created class”. It does not refuse them; the object had simply been deallocated.

The size through the Xojo API, the position through AppKit

Writing DetailPanel.Width = w through the Xojo property is the only gesture that lays out the children of the DesktopPagePanel again. Then correct the position with PlaceView, in AppKit coordinates. Never read the Xojo geometry under a split view controller — it stays out of date.

And remove LockRight / LockBottom from the top-level views: that lock is what tells Xojo “this control is mine, I resize it”. Otherwise Xojo lays out as well, against the width of the WINDOW, and you see two layout passes.

reloadData deselects

Four ways to refresh a list, and taking the wrong one manufactures bugs. Calling it from the selection-changed notification empties the selection just before the event reads the index — the symptom reads as “the click does nothing”.

And a plain setNeedsDisplay: does NOT make an overridden getter such as isEmphasized be read again: only a real reload does, hence ReloadPreservingSelection.

NSToolbarItemGroup: the order of the wiring is everything

AppKit routes a group's click to the subitem, never to the group. But writing target/action on a subitem that is ALREADY assembled ends the process at the first real layout pass, with no exception and no Closing event.

The sequence that works: setSubitems: with an empty array → wire each subitem → setSubitems: with the full list.

ObjCBlock has been supplied by Xojo since 2019r2

New ObjCBlock(theDelegate As Object) then .Handle. The Delegate takes the declared arguments of the block, without the block pointer at the front; built on an instance method, it carries the object with it, so the Cocoa registry is not needed.

For an asynchronous API, the block, its delegate AND the owning object must be held in PROPERTIES until the callback — never in local variables.

sizeToFit exists only on NSControl

Calling it on a plain NSView raises an unknown selector. NativeSlider.Handle returns a composite host view as soon as there are icons: NativeControlHost.Center therefore makes the call conditional on Cocoa.Responds.

NSTextAlignment does not have the same values on every architecture

The SDK switches on TARGET_ABI_USES_IOS_VALUES: on Apple Silicon, Centre = 1 and Right = 2; on Intel, the other way round. Hence the #If TargetARM in the badges and in NativeTextField.SetAlignment.

SDK 26 renamed the whole of NSBezelStyle

RoundedPush, RegularSquareFlexiblePush, TexturedRoundedToolbar, RoundRectAccessoryBarAction, RecessedAccessoryBar, InlineBadge. The old names are now no more than deprecated aliases.

And setButtonType: RESETS the bezel and the image to values proper to the type: to be called BEFORE setting BezelStyle, never after.

Three APIs where the value is not yet where you look for it

comboBoxSelectionDidChange:stringValue is not up to date yet, AppKit copies the chosen item over AFTER the callback: read objectValueOfSelectedItem. clickedPathItem — valid ONLY while the action is being sent. NSAlert.suppressionButton — to be read AFTER RunModal.

NSAlert: the order of addition, not the order on screen

The first button added is the default button, hence the rightmost one, the one Return triggers. With a destructive action the keyboard equivalent must therefore be moved — an irreversible action must never fire by reflex.

And NSAlert has no notion of “destructive”: it is its buttons that have one, buttons being an array of NSButton.

The RTF bar does not belong to the editor, but to the window

usesInspectorBar installs an NSTitlebarAccessoryViewController on the window — an __NSInspectorBarView view, 28 points, under the toolbar and across the full width — and shrinks the content view by as much. So it cannot be confined to a frame: it is at window level, exactly as in TextEdit.

Any layout that positions its own views must be redone after the call, otherwise the bar covers the content instead of pushing it. And contrary to what one readily assumes, neither the scroll view nor the order of the calls changes anything — verified in Objective-C without a scroll view, set before setDocumentView:, then outside a window: the accessory is installed in all three cases. For a bar inside the frame another route is needed: the component's own bar, or the ruler accessory below.

The ruler accessory crashes if the client is not set first

It is the only native way to house a bar inside the text area: the accessory becomes a subview of the NSRulerView, hence of the scroll view, without touching the window. But setAccessoryView: raises an exception if the ruler's clientView has not been set beforehand — “you must set the client view of the ruler before you can have an accessory view”. This is not a call without effect, it is a crash.

And the graduated ruler comes with it: 60 points measured for 28 of accessory. For a bar on its own in a frame, the one in NativeRichTextEditor remains preferable.

A formatting command goes to the first responder

changeFont: and changeColor: do not aim at the control you think: the Font panel and the Colours panel send them to the first responder. Without a prior makeFirstResponder, the command goes nowhere, with no error.

Auto Layout and frame placement: what sorts itself out, and what does not

NSGridView and NSStackView work in Auto Layout, whereas the whole library places by frame. Verified in Objective-C: built with initWithFrame:, both keep translatesAutoresizingMaskIntoConstraints at YES — so they place by frame like the rest. And addRowWithViews:, like addArrangedSubview:, sets that flag to NO on the views handed to them: the caller has nothing to adjust.

Two reservations. The class factory stackViewWithViews:, for its part, returns a stack at NO — it would ignore its frame; hence initWithFrame: in the constructor. And fittingSize is expressed in alignment rectangles: the frame of an NSButton overflows its own by a few points, so a grid of buttons fitted to the pixel looks clipped — Refit takes an extra for that.

Two runtime classes with the same name: the selector disappears without a word

ObjCClass reuses a class that is already registered — this is deliberate, and it is what prevents proliferation from one window to the next. But if two different classes ask for the same name, the second inherits from the first and its AddMethod is a silent no-op: its target never answers its selector.

The symptom points nowhere. A menu opens, an item is chosen, popUpMenuPositioningItem: returns YES — an item WAS indeed chosen — and nothing happens: the action went into the void for want of a responder. Since then, AddMethod checks on a reused class that the selector really is there, and raises otherwise. It happened once, between NativeMenuToolbarItem and NativeMenu, both on “VDSMenuTarget”.

The palette menu cannot be pulled down on its own

A menu with a Palette presentationStyle must be the submenu of an item belonging to an ordinary menu. The header is clear: it can be neither opened by popUpMenuPositioningItem:, nor attached directly to a pop-up menu or a toolbar item. AddColorPalette therefore imposes that shape — you give it the title of the carrying item, not a menu of its own.

Good news on the other hand: its selection handler is optional. You pass Nil and read selectedItems afterwards, which spares you having to build an Objective-C block. And selectionMode acts only inside a GROUP — the items between two separators.

A Boolean event that is not implemented returns False

That detail dictates the name of a library's events. An Event WillDrag() As Boolean would have forbidden every drag until the caller wrote it — the opposite of what one expects from a default.

Hence CancelDrag, inverted: returning True cancels, and writing nothing lets it through. NativeDropView, by contrast, keeps Dropped in the direct sense, because a drop has to be handled to mean anything.

A programmatic selection warns the delegate

selectRowIndexes:byExtendingSelection: fires tableViewSelectionDidChange: exactly like a click — NativeSidebar.SelectPage is therefore enough to make the page follow, without pushing it a second time.

With one measured reservation: AppKit does not notify an unchanged selection. Aiming at the row that is already selected produces no event — which is correct, nothing moved, but a caller that relied on the event to synchronise itself needs to know.

The origin of a view that is not flipped is at the bottom left

The frame of an NSDraggingItem is expressed in the coordinates of the view that opens the session. Leaving it at (0,0) does not put the thumbnail “at the start”: it sticks it in the bottom left corner, far from the cursor.

For it to follow the mouse: convertPoint:fromView: with Nil — which means “from the window's coordinates” — on the point returned by locationInWindow, then an offset of half a thumbnail to centre it. Checkable without a mouse: a view at (100,50), a cursor at (180,110) in the window, and you must read (80,60).

A CGPoint by value reads as two Doubles

The drag source selectors — hitTest:, draggingSession:endedAtPoint:operation: — declare a CGPoint passed by value. Two consecutive Doubles occupy exactly the same registers as the structure: the Xojo delegate can therefore receive them separately, without declaring a structure.

Verified in Objective-C rather than assumed: a method installed with the encoding "@@:{CGPoint=dd}" but implemented as (id, SEL, double, double) does receive the right values.

What is dropped is not necessarily a file

A FolderItem coming from a drop knows whether it is a folder — IsFolder. Writing “file” for everything is a convenience, not a limit of drag and drop.

With one nuance: a package — .app, .pkgis a directory, so IsFolder counts it among the folders. To treat it as a document, which is what the Finder does, you have to look at its extension. The icon returned by NSWorkspace.iconForFile:, for its part, already shows the difference.

Two ways to read an exported constant, and they are not interchangeable

dlsym returns the address of the symbol. For an NSString constant — toolbar item identifier, pasteboard type — the symbol contains a pointer to the object: it has to be dereferenced, slot.Ptr(0). For a symbol declared as an array, such as _NSConcreteGlobalBlock, the address is the object: dereferencing would give anything at all.

Hence Cocoa.ExportedString for the first case, and the direct reading for the second. Both run in the library, three lines apart.

What the system does not retain for you

An NSStatusItem disappears as soon as the variable holding it goes out of scope: the menu bar does not retain it. And Remove is not optional — without it, the extra stays in the bar until the end of the process, even once the Xojo object is destroyed.

Same family as asynchronous blocks and action targets: as soon as AppKit holds only a weak reference, or none, a PROPERTY is what is needed, never a local.

An NSOutlineView IS an NSTableView

The chain reads at runtime: NSOutlineView → NSTableView → NSControl → NSView. An outline is therefore not a control next to the table, it is a table that indents its first column. Writing two independent classes costs the cell code twice, and the second always ends up the poorer: the outline had neither checkbox, nor menu, nor editable cell, nor colour, nor alignment, nor header — nothing AppKit forbade, only what the duplication had left out.

Making it inherit removed ten twin implementations and left only ten overrides: the data source selectors, the column that carries the disclosure triangles, clearing, the refusal to sort, the visible row ↔ storage row translation, row adding turned into node adding, and three relays that also raise the node event. The same kinship holds elsewhere in the library: NativeSidebar and NativeOutlineSidebar descend from NativeSidebarBase, and four toolbar items from NativeToolbarItem.

Xojo dispatches virtually from Super.Constructor

A subclass override is indeed reached from the base class's constructor: the object carries its real class from allocation onwards — the Java model, not C++. That is what allows a single constructor to build what is common and let the subclass supply its differences. One condition: the override must touch only its parameters, the subclass's properties not being set at that instant.

And it must be observed, not assumed. A silent fallback to the base implementation raises nothing: you get a control that simply draws nothing, a symptom too quiet for anyone to trace back to its cause. A Cocoa.Responds(mDelegate, "<selector added by the override>") just after construction, followed by a System.DebugLog on failure, turns the assumption into an observation for the price of one call.

A fallback is not “do nothing”

Everything dating from macOS 26 is set behind a Cocoa.Responds — the SELECTOR queried, never the version number. But the next question matters just as much: what do you do when it is absent? For a border shape or a tint prominence, doing nothing is right — the control keeps its look, and the application is simply less refined. For placeholderStrings, doing nothing would leave the field with no placeholder at all: that is no longer a degradation, it is a regression. So the FIRST of the placeholders is set there.

The rule reads in one sentence: the fallback must give the best available behaviour, not the absence of behaviour. A selector test that guards against the crash while breaking the feature has merely moved the defect.

A property of NSView belongs to every class

prefersCompactControlSizeMetrics (macOS 26) is declared on NSView, not on a particular control: it therefore holds for the twenty-one classes the library wraps. Copying it into each would be the same line twenty times over to correct the day it changes. It is set ONCE, as a shared method of NativeControlHost, which takes the Handle of any control.

The same reasoning for the enumerations: NSControlBorderShape serves NSButton and NSSegmentedControl, NSTintProminence serves NSButton and NSSlider. Both live in NativeControlHost and the classes refer to them — the same reason an outline inherits from a table rather than copying it.

Read the SDK, and probe rather than assume

The thresholds of NSLevelIndicator are documented in one direction only — “values above the warning threshold”. There is no documented way to invert them: any other direction is painted by hand through fillColor.

And when a setting “changes nothing” on screen: push an absurd value and rebuild ONCE. If it moves, the code path is alive and the adjustments were too subtle; if not, the binary that is running is not the code you edited.

Core

The bridge to the Objective-C runtime, and the machinery that houses an NSView in a Xojo window.

Core

Cocoa

module

Base module: declarations, NSRect / NSSize / NSEdgeInsets structures, string conversion, and the owner registry that lets a Shared callback — which receives nothing but a bare pointer — find its Xojo object again.

Methods

Sub AddSubview(parent As Ptr, child As Ptr)
Function Alloc(cls As Ptr) As Ptr
Sub ApplyColor(tv As Ptr, c As Color)
Function Autorelease(obj As Ptr) As Ptr
Gives up ownership of an object WITHOUT destroying it right away: the pool releases it at the end of the current loop pass. This is what you need when handing an object to AppKit — it will retain it for itself, but AFTER you return.An immediate release would destroy the object before AppKit took it; doing nothing leaks it. Autorelease is the only one of the three that is right. Returns the object, so it can be written at the end of an expression: Return Autorelease(v).
Function BuildRichTextEditor(hostView As Ptr, width As Double, height As Double) As Ptr
Creates an NSScrollView + NSTextView (rich text) and adds it to the host view (the DesktopCanvas's NSView). Returns the pointer to the NSTextView (keep it to drive the formatting).
Function ClassRef(name As String) As Ptr
Function ConvertFontTrait(fm As Ptr, font As Ptr, trait As UInteger) As Ptr
Function DataWithContentsOfFile(path As String) As Ptr
Sub ExportRTF(tv As Ptr, file As FolderItem)
Function FontOfSelection(tv As Ptr) As Ptr
Function FromNSStr(p As Ptr) As String
NSString * -> Xojo String, WITHOUT CFStringRef.A return declared as CFStringRef makes Xojo take ownership of the object and release it. But -[NSString description] returns self without incrementing the count: every call on a string belonging to AppKit over-releases it, and the breakage shows up later, elsewhere. So we go through UTF8String, which transfers no ownership. The return of a CString declare is not implicitly converted in an extension method call: it has to be assigned first.
Sub ImportRTF(tv As Ptr, file As FolderItem)
Function InitWithFrame(obj As Ptr, frame As NSRect) As Ptr
Function NSColorRGBA(r As Double, g As Double, b As Double, a As Double) As Ptr
Function MacOSVersionAtLeast(major As Integer, minor As Integer = 0) As Boolean
Operating system version, read once only. To be used for presentation only: to decide whether an API exists, Responds() is safer — a selector that is present is a proof, a version number a guess.
Function MainQueue() As Ptr
GCD's main queue.dispatch_get_main_queue() is not a function but a macro naming the exported _dispatch_main_q structure: you read the symbol's ADDRESS, and that address IS the queue. The opposite nuance to ExportedString, where the symbol holds a pointer that must be dereferenced.
Sub PerformOnMainThread(callback As Ptr, context As Ptr)
Runs a callback on the interface thread, later, without waiting.Apple's completion blocks arrive on a service queue, not on the interface thread, and the Xojo runtime is not built to be woken from a thread it does not know. The block stores a pointer, raises a flag, and comes back through here. callback is an AddressOf on a Shared method taking a Ptr.
Function Responds(obj As Ptr, selectorName As String) As Boolean
Does the object respond to this selector? This is THE way to test for the presence of an API: it observes instead of assuming.
Function ExportedString(symbolName As String) As String
Reads an NSString constant exported by AppKit — toolbar item identifier, pasteboard type — rather than copying out its value. They do not all follow the same convention, and a copied string becomes wrong without warning the day Apple changes it.NUANCE: dlsym returns the ADDRESS of the symbol, and the symbol CONTAINS a pointer to the NSString — hence the slot.Ptr(0) dereference. For a symbol declared as an array, on the contrary, the address IS the object.
Function NSStr(s As String) As Ptr
Function OwnerOf(handle As Ptr) As Object
Finds the Xojo object that owns an ObjC instance created at runtime. Used from the Shared callback methods, which receive nothing but the ObjC “self”.
Sub RegisterOwner(handle As Ptr, owner As Object)
Maps an ObjC instance (delegate, dataSource, target…) to its Xojo owner. The reference is weak: registering does not keep the Xojo object alive.
Sub ReplaceRangeWithRTF(tv As Ptr, range As NSRange, data As Ptr)
Function RTFFromRange(tv As Ptr, range As NSRange) As Ptr
Function SelectedRange(tv As Ptr) As NSRange
Sub SetAllowsUndo(tv As Ptr, flag As Boolean)
Sub SetAutoresizingMask(view As Ptr, mask As UInteger)
Sub SetDocumentView(scroll As Ptr, view As Ptr)
Sub SetEditable(tv As Ptr, flag As Boolean)
Sub SetFontRange(tv As Ptr, font As Ptr, range As NSRange)
Sub SetHasVerticalScroller(scroll As Ptr, flag As Boolean)
Sub SetRichText(tv As Ptr, flag As Boolean)
Sub SetTextColorRange(tv As Ptr, color As Ptr, range As NSRange)
Sub SetViewFrame(view As Ptr, frame As NSRect)
Puts back a frame read with ViewFrame. Needed when Xojo REPLACES a view and misplaces the one it has just created — toggling Password on a DesktopTextField destroys the view, creates another, and that one lands at coordinates which are not its own.Always the same rule: size through the Xojo API, position through AppKit.
Function SharedFontManager() As Ptr
Function TextLength(tv As Ptr) As UInteger
Sub ToggleTrait(tv As Ptr, mask As UInteger)
mask : NSItalicFontMask=1, NSBoldFontMask=2
Function TraitsOfFont(fm As Ptr, font As Ptr) As UInteger
Sub UnderlineSelection(tv As Ptr)
Sub UnregisterOwner(handle As Ptr)
Function ViewFrame(view As Ptr) As NSRect
A view's REAL frame, to compare against what Xojo believes it set. A difference means the view was moved behind one of their backs.y counts from the bottom in AppKit and from the top in Xojo: a difference in y is not necessarily an anomaly, one in x or in the width is.
Function WriteDataToFile(data As Ptr, path As String) As Boolean
Core

NativeColor

class

The 54 SEMANTIC colours of macOS, the ones that carry a role rather than a hue. They are the only ones that follow dark mode, the accent tint the user picked and the accessibility settings — any hard-coded RGB value ignores all of it. One performSelector: rather than fifty hand-typed Declares, which would be fifty chances of a typo.

Methods

Shared Function Handle(kind As Kinds) As Ptr
The LIVE NSColor, to hand straight to AppKit. It stays dynamic: it resolves at every draw, so it follows an appearance change with nothing to reset. This is the form to prefer as soon as the colour goes to a view. Returns Nil when the colour does not exist on this system.
Shared Function Value(kind As Kinds, fallback As Color = &c000000) As Color
The RESOLVED colour, for drawing on the Xojo side.It is a SNAPSHOT: the value is resolved in the current appearance and will not move again. Switching to dark mode does not update it — read it again and redraw. A limit of the conversion, not of the implementation: a Xojo Color is three bytes, it cannot carry anything dynamic. Alpha is preserved, and it must be: the secondary to quinary labels, the separator and the five fills are translucent by construction; in Xojo, Alpha is 0 for opaque and 255 for transparent.
Shared Function Available(kind As Kinds) As Boolean
Availability read BY SELECTOR, not inferred from a version number — the doctrine of the rest of the library. The palette runs from 10.8 to 14.0: quinaryLabelColor in 11.0, systemCyanColor in 12.0, textInsertionPointColor and the five fills in 14.0.
Shared Function SelectorName(kind As Kinds) As String
The colour's AppKit name. Useful on screen and in logs: it says exactly which API is in play, which a translated enum name would not.
Core

ObjCClass

class

Objective-C class factory at runtime. Unlike implementations that suffix _1, _2, this one asks objc_getClass first and reuses a class that is already registered; AddMethod then becomes ineffective. Deterministic, and no proliferation of classes from one window to the next.

Constructor

Sub Constructor(className As String, superClassName As String = "NSObject")

Methods

Sub AddMethod(selectorName As String, handle As Ptr, signature As String)
Grafts a Xojo implementation (AddressOf of a Shared method) onto the class. The signature is the ObjC type encoding: return type, then "@:" (self and _cmd), then one character per argument.q=Int64 d=Double @=id c/B=BOOL v=void see the Objective-C Runtime Programming Guide, “Type Encodings”.
Sub AddProtocol(protocolName As String)
Declares formal conformance. Optional: AppKit asks respondsToSelector: in any case. Without effect if the protocol is not visible to the runtime.
Function CreateInstance() As Ptr
Returns a RETAINED instance. The retain is not a precaution, it is the condition for working at all: AppKit's delegate, dataSource and target are weak zeroing references. Without a strong owner, the instance is deallocated at once, the weak reference goes back to Nil, and no callback ever arrives. The caller is responsible for the matching ReleaseInstance.
Function Handle() As Ptr
The Class pointer, for a manual alloc/init (view subclasses, for example).
Function IsNew() As Boolean
False if the class already existed and was reused as it was.
Shared Sub ReleaseInstance(obj As Ptr)
Function Responds(selectorName As String) As Boolean
Consistency check: does the class really expose the grafted selector?
Shared Function MetaClassOf(cls As Ptr) As Ptr
The metaclass carries the CLASS methods: it is the one to ask to know whether a factory of the +itemWithFoo: kind exists on this version.
Shared Function SelectorFor(selectorName As String) As Ptr
Core

NativeControlHost

class

Houses an AppKit view inside the view of a Xojo control serving as an anchor — an empty DesktopCanvas, typically. That is what avoids the whole question of geometry: Xojo carries on placing the anchor, the autoresizing mask makes the AppKit view follow, and there is nothing to resynchronise.

Methods

Shared Sub Center(anchor As DesktopUIControl, view As Ptr)
Same principle as Fill, but the view keeps its own size and stays centred. For a control with an intrinsic size — an NSSwitch, for example, which it would make no sense to stretch.
Shared Sub Detach(view As Ptr)
Shared Function MakeFlippedContainer(width As Double, height As Double) As Ptr
Like MakeContainer, but FLIPPED: y counts from the top, as in the rest of the library and as in the Xojo designer.isFlipped is not settable, it has to be overridden — hence the class created at runtime. ObjCClass reuses a class that is already registered, so several callers can ask for it without duplicating it.
Shared Function MakeContainer(width As Double, height As Double) As Ptr
A bare NSView, for the places that accept ONLY ONE view where several would be needed: the accessory view of a file panel or an alert, the contentView of an NSGlassEffectView.The view is RETAINED: it is up to the caller to keep it in a property and give it back through Cocoa.ObjCClass.ReleaseInstance. AppKit coordinates, origin at the bottom — including for what is added to it.
Shared Sub SetFrame(view As Ptr, x As Double, y As Double, width As Double, height As Double)
Sets the frame of an AppKit view without adding it anywhere — to prepare a view destined for a container that will place it itself: the contentView of an NSGlassEffectView, the accessory view of an NSAlert.AppKit coordinates, so origin AT THE BOTTOM, unless the parent view is flipped — which is the case for a NativePopover's, not for the others.
Shared Sub Fill(anchor As DesktopUIControl, view As Ptr)
Places an AppKit view IN the view of a Xojo control serving as an anchor — an empty DesktopCanvas, typically — and lets it fill it entirely.Hosting INSIDE the anchor rather than beside it avoids the whole geometry problem: Xojo carries on placing and sizing the anchor, and the autoresizing mask makes the AppKit view follow. No position is calculated, so there is nothing to resynchronise on resize.
Shared Sub SetCompactMetrics(view As Ptr, compact As Boolean)
macOS 26: asks a view — and everything it contains — for the system's COMPACT metrics. It is a property of NSView, so valid for any wrapped control: hence its place here rather than copied into every class.
Shared Sub SetSelectionAnchor(view As Ptr, x As Double, y As Double, width As Double, height As Double)
macOS 15. States WHERE the selection is inside the view: the system places the keyboard context menu and popovers there instead of at the centre. Only applies to views built by MakeFlippedContainer — a system class cannot receive the method without being subclassed.
Shared Sub ClearSelectionAnchor(view As Ptr)
No more selection: the system returns to the centre of the view.

Enumerations

BorderShapesAutomatic=0Capsule=1RoundedRectangle=2Circle=3
TintProminencesAutomatic=0None=1Primary=2Secondary=3
Core

VDSLicence

class

The licence check. Running from the IDE is free: the check sits behind #If DebugBuild, so it is not even compiled under the IDE. A BUILT application without a valid licence still runs, but every window that uses VDSTools carries a notice — chrome, a control dropped in the IDE, or a hosted AppKit view. One exception: the publisher's demo application, recognised by its bundle IDENTIFIER, with no key — a key placed in a distributed application can be read from its binary and used as is elsewhere.

Methods

Shared Sub SetLicence(licensee As String, key As String)
To be called in App.Opening, BEFORE any window is dressed. The name must be copied EXACTLY as it appears on the licence: it goes into the code's computation, and one extra space is enough for it to be refused. Two empty strings = no licence.
Shared Function Licensed() As Boolean · LicensedTo · StatusText · Notice
The state, the licensee's name, and texts ready to show in an “About” box.
Shared Function Inspect(licensee As String, key As String, ByRef majorCovered As Integer, ByRef reason As String) As Boolean
Checks a key without installing it, and says WHY it is refused — length, prefix, signature. It is what the generator uses.
Shared Function MakeLicence(licensee As String, majorCovered As Integer) As String
Builds a key — the generator's business, not the application's. A 33-character key, VDS01-XXXXXX-…: HMAC-SHA256 truncated to 120 bits, in base32 without the letters I, L, O and U.
Shared Sub ApplyWatermark(window As Ptr)
The watermark of unlicensed builds: a light badge with a border, bold black text, bottom right. One per window, also placed by MarkHost, which the library calls from view hosting and from the Opening of the controls dropped in the IDE.The background is translucent through its LAYER, not through the view's alpha, or the text would fade with it. The view returns Nil from hitTest:: it takes no clicks.

Window

The NSSplitViewController that takes ownership of the window.

Window

NativeWindowChrome

class

Installs an NSSplitViewController as the window's contentViewController: sidebar, detail pane, collapsible inspector, native title and subtitle, safe area under the toolbar.

Constructor

Sub Constructor(win As DesktopWindow)

Methods

Function DetailHeight() As Double
Function DetailWidth() As Double
Function HasInspector() As Boolean
Sub Install(sidebarView As Ptr, sidebarWidth As Double, minWidth As Double = 180, maxWidth As Double = 340)
Hands the window's contentViewController to an NSSplitViewController: Xojo's contentView becomes the view of the detail pane, and the view passed as an argument that of the sidebar pane.What this arrangement gives for free: vibrancy and animated collapsing of the sidebar pane, the system “toggle” toolbar item through the responder chain, automatic toolbar breaking, and safe-area handling (so no inset to compensate under the titlebar).
Sub InstallInspector(inspectorView As Ptr, width As Double, minWidth As Double = 200, maxWidth As Double = 420)
A third pane, on the right. inspectorWithViewController: (macOS 14+) brings the same system treatment as the sidebar: animated collapsing, and above all the NSToolbarToggleInspectorItem / NSToolbarInspectorTrackingSeparatorItem identifiers, which then work without a line of code.
Function IsInspectorCollapsed() As Boolean
Function IsSidebarCollapsed() As Boolean
Shared Function MakeTitleView(title As String, subtitle As String = "") As Ptr
Title + subtitle stacked, to be set as the VIEW of a toolbar item.Why: NSWindow.title is placed by AppKit at the HEAD of the bar, hence on the sidebar side. To align it with the start of the detail — which is what the system applications do — an item is needed, placed by hand just after the tracking separator. In that case the system title is hidden (chrome.TitleVisible = False) so as not to show it twice.
Sub PlaceView(v As Ptr, x As Double, y As Double, w As Double, h As Double)
Placement in AppKit coordinates: origin at the BOTTOM left, Xojo's contentView not being “flipped”. To be used for the POSITION only — the size must go through the Xojo property, see the DetailResized event.
Sub Relayout()
Two stages, and the order matters: 1) the SIZE is announced to the window (event), which writes it through the Xojo properties — the only gesture that triggers the layout of a PagePanel's child controls; 2) the window then corrects the POSITION with PlaceView, the Xojo geometry being wrong under NSSplitViewController. The top-level views of the detail must have LockRight and LockBottom at False in the designer, failing which Xojo lays them out on its side and you see two passes follow one another.
Function SafeAreaTop() As Double
Height taken by the titlebar and the toolbar ABOVE the detail.With FullHeightSidebar, the detail pane rises to the top of the window: without this offset, the content passes under the toolbar. safeAreaInsets (macOS 11+) gives the exact value, which varies with the bar style and the display mode — hard-coding it would be wrong as soon as the style changes.
Function ScrollEdgeEffectAvailable() As Boolean
macOS 26. The style CLASS is queried, not the version number: a selector that is present is a proof, a version number a guess.
Function SetScrollEdgeEffect(style As ScrollEdgeStyles) As Integer
NSScrollEdgeEffectStyle (macOS 26): the way the content fades under a title bar or a pane when scrolling — soft edge, or hard cut.This is NOT a control but a style object, and it goes only on ACCESSORY controllers: those of the title bar and those of the panes. So it is applied to all the window's title accessories — including the one AppKit installs for the RTF format bar. Returns the NUMBER of accessories actually styled: zero is not an error, it means there is none to style for the moment.
Function SplitViewHandle() As Ptr
Shared Function SystemIdentifier(symbolName As String) As String
Reads an NSToolbarItemIdentifier constant exported by AppKit rather than guessing its value — they do not all follow the same convention. The machinery lives in the Cocoa module: it also serves the pasteboard types of drag and drop.
Sub ToggleInspector()
Sub ToggleSidebar()
The programmatic equivalent of the system toolbar item.
Function AddTopAccessory(area As SplitAreas, view As Ptr) As Ptr
macOS 26. An accessory bar at the top of ONE given pane — the same family as the titlebar accessory the RTF bar puts on the whole window, but confined to the targeted pane. Returns the created controller's pointer, to keep for removing or hiding it.
Function AddBottomAccessory(area As SplitAreas, view As Ptr) As Ptr
The symmetric one, below the pane's content.
Sub RemoveAccessory(controller As Ptr)
Goes through removeFromParentViewController, which the header recommends: no need to find the index in the top or bottom list.
Sub SetAccessoryHidden(controller As Ptr, hidden As Boolean)
Collapses the accessory to zero height without removing it — reversible without rebuilding the view.
Sub SetAllowsOverlay(area As SplitAreas, allow As Boolean)
macOS 26, automaticallyAdjustsSafeAreaInsets. Other panes may then overlay this one, its safeAreaInsets following that overlay.

Properties

FullHeightSidebar As Boolean
True: the sidebar pane rises behind the titlebar and the toolbar starts only at the divider — this is the arrangement of the system applications, and the condition for NSWindow.title to fall on the detail side.
Subtitle As String
A secondary line under the title, in grey (macOS 11+). Empty = no line.
Title As String
The title of the WINDOW, shown in bold in the unified bar. Nothing to do with the Label or Title of a NativeToolbarItem: it is this title/subtitle pair that the system applications show.
TitleVisible As Boolean
False hides both title AND subtitle, so that a custom view can take their place. That is what the demo used to do — and it is precisely what stopped the system presentation from being seen.

Events

  • Event DetailResized(w As Double, h As Double)

Enumerations

SplitAreasSidebar=0Detail=1Inspector=2
ScrollEdgeStylesAutomatic=0SoftEdge=1HardEdge=2
Window

NativeWindowTabs

class

Window tabs — “Show Tab Bar”, “Merge All Windows”. Xojo exposes none of it. Two conditions, both stated by the header: tabbingMode “should be set before a window is shown”, and two windows group together only if they share the same tabbingIdentifier — left empty, AppKit derives it from the window's class, so two different classes will never group.

Methods

Shared Sub Configure(w As DesktopWindow, mode As Modes, identifier As String = "")
To be called BEFORE Show. The identifier is what allows two windows to become tabs of one another.
Shared Function UserPreference() As Preferences
The system “Prefer tabs” setting: Manual, Always or InFullScreen. It decides what AppKit does with a window in Automatic mode.
Shared Sub AddTab(host As DesktopWindow, tab As DesktopWindow)
Adds a window as a tab of another, bypassing the system setting.
Shared Sub ToggleTabBar(w As DesktopWindow)
Shows or hides the tab bar — the “Show Tab Bar” item of the View menu.
Shared Sub ToggleOverview(w As DesktopWindow)
The animated overview, “Show All Tabs”.
Shared Sub MergeAll(w As DesktopWindow)
Gathers every compatible window into tabs of a single one.
Shared Sub MoveToNewWindow(w As DesktopWindow)
Detaches the current tab into its own window.
Shared Sub SelectNext(w As DesktopWindow) · SelectPrevious
Navigation from tab to tab.
Shared Function TabCount(w As DesktopWindow) As Integer · TabBarVisible
tabGroup is created ON DEMAND and returned through a WEAK reference: read it again on every call rather than holding it.

Enumerations

ModesAutomaticPreferredDisallowed
PreferencesManualAlwaysInFullScreen
Window

NativeWindowStyle

class

Four NSWindow properties Xojo does not expose, and which make up most of a floating panel, a HUD or a pinned window: stacking level, opacity, behaviour towards spaces and Mission Control, and mouse dragging.

Methods

Shared Sub SetLevel(w As DesktopWindow, level As Levels)
The stacking level. The enum values are MEASURED, not copied: the header does not spell them out, it defines them through kCGNormalWindowLevel and friends, computed by CGWindowLevelForKey(). Measured at runtime on macOS 15.7.9 — Normal 0, Floating 3, ModalPanel 8, MainMenu 24, Status 25, PopUpMenu 101, ScreenSaver 1000.Submenu and TornOffMenu are 3 as well, exactly Floating: they are not included, they would be three names for one value.
Shared Sub SetLevelRaw(w As DesktopWindow, value As Integer)
Shared Function LevelRaw(w As DesktopWindow) As Integer
As a number, because NSWindowLevel is declared NS_TYPED_EXTENSIBLE_ENUM: any value is legitimate, and « Floating + 1 » is a common idiom to sit just above the palettes.
Shared Sub SetAlpha(w As DesktopWindow, value As Double)
Shared Function Alpha(w As DesktopWindow) As Double
The opacity of the WHOLE window, chrome included, from 0 to 1. The value is clamped to that range: AppKit accepts anything, but a window at −3 disappears without saying why.
Shared Sub SetCollectionBehavior(w As DesktopWindow, behaviors() As Behaviors)
Shared Sub SetCollectionBehavior(w As DesktopWindow, behavior As Behaviors)
Behaviour towards spaces, Exposé and full screen.ORDER MATTERS: the header states that the default behaviour DEPENDS on the level — Managed and ParticipatesInCycle when the level is Normal, Transient and IgnoresCycle otherwise. Set the level first. And « at most one per group »: at most one of Managed/Transient/Stationary, at most one of ParticipatesInCycle/IgnoresCycle, at most one of the three FullScreen bits — nothing checks it, neither AppKit nor this class.
Shared Function CollectionBehaviorRaw(w As DesktopWindow) As Integer
Shared Function HasBehavior(w As DesktopWindow, behavior As Behaviors) As Boolean
The raw mask, and the readable way to test one bit. Default being zero, testing it against the mask would always be false: HasBehavior then answers the question as it is actually asked — is the mask empty?
Shared Sub SetMovable(w As DesktopWindow, movable As Boolean)
Shared Function Movable(w As DesktopWindow) As Boolean
macOS 10.6. When false, the server stops dragging the window by its title bar or background.Two surprises written into the header: the window stays RESIZABLE if it was, and its frame remains settable programmatically; and the system stops moving it on a display reconfiguration, which can leave it off screen.
Shared Sub SetMovableByBackground(w As DesktopWindow, value As Boolean)
Shared Function MovableByBackground(w As DesktopWindow) As Boolean
Drag the window from anywhere in its background.Ignored without a word on a non-movable window — « setMovableByWindowBackground:YES is ignored on a window that returns NO from -isMovable ». Setting Movable false then this true does nothing at all.

Sidebar

Two data models, two classes, one common base.

Sidebar

NativeSidebarBase

class

A common base extracted once both bars existed. Carries the vibrancy, the building of the cells, the badges, the blue or grey selection. The metrics differ (an outline view indents): those are parameters, not constants.

Methods

Sub Refit()
The source-list style adds a horizontal margin BEYOND the column width: the view becomes wider than its visible area and the selection overflows. To be called AFTER layout — sizeLastColumnToFit does nothing while the dimensions are not established.

Properties

EmphasizedSelection As Boolean
True: blue selection. False: grey, like a list without focus. NOT ReloadList: reloadData DESELECTS. isEmphasized is queried at drawing time, so a simple redraw is enough — and it preserves the selection.
Width As Double read-only
The pane's real width, read from the view. Read-only: it is the container that fixes it, not the sidebar.
Sidebar

NativeSidebar

class inherits from NativeSidebarBase

A flat list on NSTableView in source-list style. Three natures of row: item, separator, section header — headers are not selectable and PageForRow ignores them.

Constructor

Sub Constructor()

Methods

Sub Add(title As String, sfSymbol As String)
Sub Add(title As String, sfSymbol As String, iconColor As Color)
Sub AddSection(title As String)
Section header: small grey capitals, not selectable. Kind 2 in mKinds — 0 = item, 1 = separator, 2 = header.
Sub AddSeparator()
Function BuildSidebarView(width As Double, height As Double, topInset As Double = 0) As Ptr
Builds the sidebar view ALONE — vibrancy + source-list table + footer. No NSSplitView, no styleMask, no contentView: the container is the caller's business — NativeWindowChrome mounts this view in an NSSplitViewItem.
Function Count() As Integer
Sub Insert(index As Integer, title As String, sfSymbol As String)
Function PageForTitle(title As String) As Integer
Aiming at a page by its TITLE rather than by a number: a caller that hard-codes “page 15” is wrong as soon as an entry is inserted above it.The comparison is made with “=”, and that is deliberate: in Xojo the equality of two Strings ignores CASE — the same insensitivity that makes “isa” BE the IsA operator. To compare strictly, StrComp(a, b, 1) would be needed.
Function RowForPage(page As Integer) As Integer
The exact inverse of PageForRow.
Function SelectPage(page As Integer) As Boolean
VERIFIED rather than assumed: selectRowIndexes:byExtendingSelection: DOES warn the delegate — tableViewSelectionDidChange: fires just as on a click. An application listening to SelectionChanged therefore sees the page arrive on its own; there is no need to push it a second time.Except when the row is ALREADY selected: AppKit does not notify an unchanged selection. That is correct — nothing moved —, but a caller relying on the event to synchronise itself needs to know.
Function PageForRow(row As Integer) As Integer
The rank of the row among the SELECTABLE rows, separators excluded. Saves the caller hard-coding the position of the separators — arithmetic that breaks as soon as one is added.
Sub Remove(index As Integer)
Sub SetBadge(index As Integer, text As String)
A badge counter on an entry, Mail-style. An empty string = no badge.
Sub SetFooter(title As String, sfSymbol As String)
A special row pinned AT THE BOTTOM (separator + clickable icon/label). To be called BEFORE BuildSidebarView. The click fires the FooterClicked event.

Properties

SelectedIndex As Integer

Events

  • Event FooterClicked()
  • Event SelectionChanged(index As Integer)
Sidebar

NativeOutlineSidebar

class inherits from NativeSidebarBase

A hierarchy on NSOutlineView, with collapsible sections. Every row is represented by a retained, cached NSString: the view retains and compares those pointers, so the same one must come back for the same row.

Constructor

Sub Constructor()

Methods

Function AddSection(title As String) As Integer
Returns the index of the section, to be passed to AddItem afterwards.
Sub AddItem(section As Integer, title As String, sfSymbol As String = "", iconColor As Color = &c8E8E93)
Function BuildSidebarView(width As Double, height As Double) As Ptr
Hierarchical sidebar: NSOutlineView in source-list style, in an NSScrollView, on an NSVisualEffectView. The sections are collapsible “group rows”; the triangle appears on hover, as in the Finder.
Sub CollapseAll()
Sub ExpandAll()
Function ItemCount(section As Integer) As Integer
Sub SetBadge(section As Integer, item As Integer, text As String)
A badge counter on an entry. An empty string = no badge.
Function SectionCount() As Integer

Properties

SelectedPage As Integer read-only
The rank of the selected row among ALL the items, sections excluded: the caller thus gets back a page index just as with NativeSidebar.

Events

  • Event SelectionChanged(page As Integer)

Toolbar

NSToolbar and its item types, without a line of compiled Objective-C.

Toolbar

NativeToolbar

class

The NSToolbar and its delegate. Every item must be added before Attach: setToolbar: is the moment when AppKit asks for the default list.

Constructor

Sub Constructor(identifier As String)

Methods

Sub AddItem(item As NativeToolbarItem)
The DEFAULT set: what the bar shows as long as the user has customised nothing, and the bottom row of the customisation sheet.
Sub AddAvailableItem(item As NativeToolbarItem)
The CATALOGUE: the item is offered in the customisation sheet without being placed in the bar. AppKit can only build what the delegate announces in toolbarAllowedItemIdentifiers:, hence the registration here.
Sub Attach(w As DesktopWindow)
Sub Attach(windowHandle As Ptr)
Every item must have been added BEFORE: it is at the setToolbar: moment that AppKit asks toolbarDefaultItemIdentifiers: in order to populate the bar.
Sub SetItemIdentifiers(identifiers() As String)
macOS 15. Sets the bar's CONTENT in one assignment, AppKit doing the diff — animated insertions and removals, without rebuilding. The header warns: “will override any customizations the user has made”. On a customisable, autosaving bar, this call wipes out the user's arrangement.
Function ItemIdentifiers() As String()
What the bar shows RIGHT NOW, the user's customisation included — unlike the default set, which does not move.
Sub RemoveItem(identifier As String)
macOS 15. Removes ONE item by its identifier, where RemoveAll is all or nothing. Two points from the header: if several items share the identifier, the FIRST one goes, and the change propagates immediately to every toolbar with the same identifier. The item leaves the bar, not the catalogue — so it is still offered in the palette.
Sub RemoveAll()
Empties the bar. Useful for rebuilding it in another style — item groups, for example, are refused by the Preference style.
Sub ResetConfiguration()
Clears the saved customisation and puts the default set back. AppKit exposes nothing for this: the configuration is written to the preferences under “NSToolbar Configuration <identifier>”, and removing it is not enough — the live bar keeps its items, they have to be set again.
Sub RunCustomizationPalette()
Opens the customisation palette; requires AllowsUserCustomization = True.
Function Handle() As Ptr
Function Item(identifier As String) As NativeToolbarItem
Sub ValidateVisibleItems()

Properties

AllowsDisplayModeCustomization As Boolean
macOS 15. The “Icon and Text / Icon Only / Text Only” part of the contextual menu and of the sheet. INDEPENDENT of AllowsUserCustomization, which governs only the order of the items. Default YES for an application linked on macOS 15 — so active without being asked for.
AllowsUserCustomization As Boolean
AutosavesConfiguration As Boolean
At True, AppKit itself writes to the preferences whatever the user changes — item order, removed items, display mode — and reads it back the next time a bar with the same identifier is built. To be set BEFORE Attach: the configuration is read at setToolbar: time. Default False.
DisplayMode As DisplayModes
Visible As Boolean
Style As ToolbarStyles
Applicable before or after Attach: ApplyStyle is replayed on attaching.

Events

  • Event ItemPressed(item As NativeToolbarItem)

Enumerations

DisplayModesDefaultIconAndLabelIconOnlyLabelOnly
ToolbarStylesAutomaticExpandedPreferenceUnifiedUnifiedCompact
Toolbar

NativeToolbarItem

class

An NSToolbarItem. The constructor takes an optional Objective-C class name, which lets subclasses instantiate their own while inheriting everything else.

Constructor

Sub Constructor(identifier As String, objcClassName As String = "NSToolbarItem")

Methods

Shared Function FlexibleSpaceItem() As NativeToolbarItem
System identifier: AppKit builds the item itself, the delegate returns Nil.
Shared Function ContainerItem(identifier As String, container As DesktopContainer) As NativeToolbarItem
An item whose view is that of a DesktopContainer. If the container implements ToolbarItemContainer, its SetEnabled is called on every state change: AppKit does not know how to dim the controls of a custom view.
Shared Function SystemItem(identifier As String) As NativeToolbarItem
An identifier supplied by AppKit (sidebar toggle, automatic breaking, spaces): the delegate returns Nil and AppKit builds and then drives the item itself.
Sub WireSubItems(target As Ptr, action As Ptr)
Without effect for a plain item; NativeToolbarItemGroup overrides it.
Function SubItems() As NativeToolbarItem()
Empty for a plain item; NativeToolbarItemGroup overrides it.
Function Handle() As Ptr
The underlying NSToolbarItem. Nil for system identifiers (spaces).
Function IsSystemItem() As Boolean
Function ItemIdentifier() As String
Function RespondsToClicks() As Boolean
False for the spaces and the tracking separator: no target/action on those.
Sub ClearBadge()
Function BadgeSupported() As Boolean
macOS 26+. Lets the caller adapt its interface rather than offer a setting without effect.
Sub SetBadge(count As Integer)
A numbered badge (macOS 26+).
Sub SetBadge(text As String)
A text badge, or a plain dot if the text is empty (macOS 26+).
Sub SetIcon(sfSymbolName As String, accessibilityDescription As String = "")
Sub SetView(view As Ptr)
A custom view (title field, bespoke control…).The item then stops asking the bar for a target/action: when the view is an NSControl, AppKit passes the item's target/action DOWN ONTO it and overwrites the control's own target — its event then never fires again. A custom view handles its own interaction.
Shared Function SpaceItem() As NativeToolbarItem
Shared Function TrackingSeparatorToolbarItem(identifier As String, splitView As Ptr, dividerIndex As Integer) As NativeToolbarItem
Toolbar break aligned on an NSSplitView divider (macOS 11+).

Properties

AutoValidates As Boolean
False = the Enabled state set by hand is authoritative, AppKit does not revalidate. True (AppKit's default) = validateToolbarItem: is queried periodically; NativeToolbar answers it by returning Enabled, so the result is the same.
IsHidden As Boolean
Navigational As Boolean
Puts the item in the navigation area, on the left (macOS 11+).
VisibilityPriority As Integer
The higher the value, the more the item resists the shrinking of the bar.
Bordered As Boolean
Enabled As Boolean
Title As String
Text SHOWN INSIDE the button, beside or instead of the icon (macOS 10.15+). Not to be confused with Label, which is the caption UNDER the item.
BackgroundTintColor As Color
macOS 26+. Silently ignored below: the selector is queried rather than the version number — present or absent, that is a fact.
Style As ItemStyles
macOS 26+: Plain, or Prominent for an item brought forward, “Done”-style.
Label As String
ToolTip As String

Enumerations

ItemStylesPlainProminent
Toolbar

NativeToolbarItemGroup

class inherits from NativeToolbarItem

An item group, rendered as segments or as a pull-down menu according to ControlRepresentations.

Constructor

Sub Constructor(identifier As String)

Methods

Sub AddItem(item As NativeToolbarItem)
The subitems are retained on the Xojo side: setSubitems: guarantees the survival of the ObjC objects, not of our wrappers.
Sub WireSubItems(target As Ptr, action As Ptr)
AppKit routes a group's click to its SUBITEMS: without a target/action on them, the group is inert. But setting one on them once they already belong to the group ends the process at the first layout pass — reproduced twice.The hypothesis tested here: the group copies its subitems on assembly, and forming a weak reference (NSToolbarItem.target is one) to an instance of a runtime-created class during that copy is what breaks. So we wire them while they belong to nobody, then reassemble.
Function SubItems() As NativeToolbarItem()
Accessor: the subitems belong to the group, not to the bar. NEVER write target/action to them — that is what made AppKit crash at the first layout pass.

Properties

ControlRepresentation As ControlRepresentations
Collapsed = a single button that pulls down a menu; Expanded = all visible.
SelectedIndex As Integer
SelectionMode As SelectionModes

Enumerations

ControlRepresentationsAutomaticExpandedCollapsed
SelectionModesSelectOneSelectAnyMomentary
Toolbar

NativeMenuToolbarItem

class inherits from NativeToolbarItem

NSMenuToolbarItem: an item that pulls down a menu.

Constructor

Sub Constructor(identifier As String)

Methods

Sub AddMenuItem(title As String, tag As Integer = -1)
Adds an entry to the pull-down menu. The tag comes back in the MenuItemSelected event; if it is omitted, the rank of the entry is used.
Sub AddSeparator()

Properties

ShowsIndicator As Boolean
False = the button does not show the menu chevron.

Events

  • Event MenuItemSelected(title As String, tag As Integer)
Toolbar

NativeSearchToolbarItem

class inherits from NativeToolbarItem

NSSearchToolbarItem: the search field that expands and contracts according to the room available.

Constructor

Sub Constructor(identifier As String)

Methods

Sub BeginSearchInteraction()
Expands the field and gives it the focus.
Function SearchFieldHandle() As Ptr
The NSSearchField hosted by the item.

Properties

PlaceholderText As String
PreferredWidth As Double
Text As String

Events

  • Event SearchEnded()
  • Event SearchStarted()
  • Event TextChanged(text As String)
Toolbar

NativeSharingToolbarItem

class inherits from NativeToolbarItem

NSSharingServicePickerToolbarItem: the system share button.

Constructor

Sub Constructor(identifier As String)

Methods

Sub SetFiles(paths() As String)
The files offered for sharing. Copied on the Xojo side, read again on every click.
Toolbar

ToolbarItemContainer

interface

An interface to be carried by a DesktopContainer used as an item view: AppKit does not know how to propagate enabling to a custom view, so it is up to the container to do it.

Methods

Sub SetEnabled(enabled As Boolean)
Called when the toolbar item hosting this container changes state: it is up to the container to propagate enabling or dimming to its own controls, AppKit not knowing how to do it for a custom view.

Controls

One AppKit control per class, set in a DesktopCanvas serving as an anchor — or in the view of a toolbar item.

Controls

NativeButton

class

NSButton and the whole range of bezelStyle: the help circle, the disclosure triangle, the counter badge, the accessory-bar capsule.

Constructor

Sub Constructor(title As String, style As BezelStyles = BezelStyles.Push)

Methods

Function Handle() As Ptr
Sub Refit()
To be called again after changing the title, the style or the symbol: the ideal size of a button depends on its bezel as much as on its content.
Sub SetButtonType(type_ As ButtonTypes)
The TYPE decides the behaviour on click — momentary, toggle, checkbox, radio — where the bezel decides only the drawing. The two are independent: a checkbox keeps a bezel, a push button can behave as a toggle. The constructor sets MomentaryPushIn, the ordinary button.Careful: setButtonType: RESETS the bezel and the image to values proper to the type. To be called BEFORE setting BezelStyle, never after.
Sub SetBordered(bordered As Boolean)
Without a border, a symbol button becomes a plain clickable icon: that is what is wanted in a command bar that already carries its own background.
Sub SetControlSize(size As ControlSizes)
Shared Function FontSizeForControlSize(size As ControlSizes) As Double
The font size the system pairs with this control size — measured on macOS 15.7.9: Regular 13, Small 11, Mini 9, Large 13. AppKit does NOT link the two properties: setControlSize: only affects metrics and never changes a control's font. To make a label follow the control size, set FontSize yourself from this value.Large is identical to Regular. ExtraLarge returns 12 on a system older than macOS 26: a meaningless fallback, not to be used before checking against the macOS 26 SDK.
Sub SetDefault(isDefault As Boolean)
The default button is not a style: it is the one that answers Return. AppKit then tints it with the accent colour, of its own accord.
Sub SetDestructive(destructive As Boolean)
macOS 11+. The button turns red, like “Delete” in an alert. On an earlier system the call is simply without effect.
Sub SetSymbol(symbolName As String, accessibilityDescription As String = "")
An SF symbol rather than a title — indispensable for round bezels (Circular) where a word would not fit.
Sub SetImage(item As FolderItem)
An image from DISK, when the subject does not exist as a symbol — an application logo, say.
Sub SetPicture(source As Picture)
A Xojo Picture, for whatever the application draws itself. CopyOSHandle returns an NSImage you OWN — “Copy” in the name — and the button retains it on its side: it is released once set.

Properties

ImagePosition As ImagePositions
Where the icon sits relative to the title. NSCellImagePosition, read from NSCell.h — the values follow no visual order: Below is 4 and Above 5. Leading and Trailing follow the writing direction; Left and Right never move.MEASURED: fittingSize IGNORES Above and Below — it returns the same height as with no image, 24 points. The height is yours to set.
ImageHugsTitle As Boolean
True places the icon next to the title and centres the pair — almost always what you want. False pins it against the frame edge and centres the title in what remains: the icon then looks stranded away from the word.
BezelStyle As BezelStyles
State As Integer
NSControlStateValue: Off = 0, On = 1, Mixed = -1. Only meaningful with a stateful type — Switch, Radio, PushOnPushOff, OnOff.
Enabled As Boolean
Title As String
BorderShape As NativeControlHost.BorderShapes
macOS 26. The SELECTOR is queried, not the version number: on an earlier system the control keeps its look instead of raising.
TintProminence As NativeControlHost.TintProminences
macOS 26. The SELECTOR is queried, not the version number: on an earlier system the control keeps its look instead of raising.

Events

  • Event Pressed()

Enumerations

BezelStylesAutomatic=0Push=1FlexiblePush=2Disclosure=5ShadowlessSquare=6Circular=7TexturedSquare=8HelpButton=9SmallSquare=10Toolbar=11AccessoryBarAction=12AccessoryBar=13PushDisclosure=14Badge=15Glass=16
ButtonTypesMomentaryLight=0PushOnPushOff=1Toggle=2Switch=3Radio=4MomentaryChange=5OnOff=6MomentaryPushIn=7Accelerator=8MultiLevelAccelerator=9
ControlSizesRegular=0Small=1Mini=2Large=3ExtraLarge=4
Controls

NativeComboButton

class

NSComboButton (macOS 13+): a main action and a menu. The menu items carry their own target and action, independent of the main action.

Constructor

Sub Constructor(title As String, style As Styles = Styles.Split)

In Split style, the arrow is a separate zone: clicking the title fires the action, clicking the arrow opens the menu. In Unified, a single segment: since the action is set, a click fires it and the menu appears only on press and hold — the header says so, and this class always sets its action.

Methods

Function Handle() As Ptr
Sub AddItem(title As String)
The header is explicit: the menu items carry THEIR OWN target and action, independent of the button's main action. The tag carries the index, the only way to know which one was chosen.
Sub RemoveAllItems()
Function ItemCount() As Integer
Empties the menu and resets the counter: each item's tag is its rank, and items added afterwards would otherwise carry shifted indexes.
Sub SetItems(ParamArray titles() As String)
Sub SetSymbol(symbolName As String, accessibilityDescription As String = "")

Properties

Style As Styles
Title As String

Events

  • Event ItemChosen(index As Integer, title As String)
  • Event Pressed()

Enumerations

StylesSplit=0Unified=1
Controls

NativePopupButton

class

NSPopUpButton, pop-up (the selection is shown) or pull-down (the first item serves as a fixed title, like an “Action” button).

Constructor

Sub Constructor(pullsDown As Boolean = False, style As NativeButton.BezelStyles = NativeButton.BezelStyles.Push)

Methods

Function Handle() As Ptr
Sub AddItem(title As String)
Sub AddSeparator()
Function Count() As Integer
Sub RemoveAllItems()
Sub SetItems(ParamArray titles() As String)
UsesItemFromMenu As Boolean
macOS 15. On a PULL-DOWN, YES makes the first item serve as the title and hides it from the list; NO leaves the button carrying its own title, which is empty until one is set. The setter calls synchronizeTitleAndSelectedItem: AppKit does NOT re-derive the title when going back to YES, and without it, unticking then re-ticking leaves the button permanently silent.
AltersStateOfSelectedItem As Boolean
macOS 15. YES ticks the chosen item. “This property is ignored for pull-down buttons”: a pull-down has no selected item to tick.

Properties

BezelStyle As NativeButton.BezelStyles
SelectedIndex As Integer
SelectedTitle As String read-only

Events

  • Event Changed(index As Integer)
Controls

NativeSwitch

class

NSSwitch (10.15+). Unlike a switch redrawn by hand, it follows dark mode, the accent colour and the system animation without anyone attending to it.

Constructor

Sub Constructor(onState As Boolean = False)

Methods

Function Handle() As Ptr

Properties

Value As Boolean
NSControlStateValue: Off = 0, On = 1 (Mixed = -1, not applicable here).

Events

  • Event Changed(value As Boolean)
Controls

NativeStepper

class

NSStepper — the two little arrows. Xojo has DesktopUpDownArrows, but with no adjustable step, no wrapping and no auto-repeat.

Constructor

Sub Constructor(minimum As Double = 0, maximum As Double = 100, increment As Double = 1, value As Double = 0)

Methods

Function Handle() As Ptr
Sub SetBehaviour(autorepeat As Boolean, wraps As Boolean)
autorepeat: holding the arrow runs through the values. wraps: past the maximum, it starts again from the minimum — useful for hours.

Properties

Value As Double

Events

  • Event Changed(value As Double)
Controls

NativeTextField

class

NSTextField. Two bezels only, square and rounded; the third useful case, the “flat” field, is obtained through SetBordered(False, False).

Constructor

Sub Constructor(placeholder As String = "", style As Bezels = Bezels.Square)

Methods

Function Handle() As Ptr
Sub SetAlignment(alignment As Alignments)
NSTextAlignment is not the same number on every architecture: the SDK switches on TARGET_ABI_USES_IOS_VALUES. On Apple Silicon, Centre = 1 and Right = 2; on Intel, the other way round.
Sub SetBordered(bordered As Boolean, drawsBackground As Boolean = True)
Sub SetEditable(editable As Boolean, selectable As Boolean = True)
Sub SetPlaceholders(ParamArray items() As String)
macOS 26: SEVERAL placeholders, which the field cycles through one after another — “Search for a package”, then “Search for a component”…The fallback is not “do nothing”: on an earlier system we set the FIRST one. A field that lost every placeholder would be a regression, not a degradation.
AllowsWritingTools As Boolean
macOS 15.2, default YES. To be set to NO on a field that holds no prose: an identifier, a path, a licence key.
AllowsWritingToolsAffordance As Boolean
macOS 15.4, default NO. The button the system places in the field. Only meaningful if the previous one is true.
Sub SetContentType(kind As ContentTypes)
NSTextContentType — a semantic hint about what the field EXPECTS, not access to anything. It is what lets the system offer, above the field, the one-time code received by SMS or iMessage: the app never reads the message, only the system does. Telephone, Email and URL have no visible effect without data the system already knows — it is a hint, not a rendering.
Shared Function SymbolFor(kind As ContentTypes) As String
The exported AppKit symbol name for a given value. Public and shared: NativeTextFieldControl uses it too, rather than duplicating the 43 cases.

Properties

Bezel As Bezels
setBezelStyle: has an effect only on a bordered field: without a border there is no bezel to draw.On macOS 27, Square and Rounded are drawn identically — same rounded corners, same line of about 12 % grey, measured pixel by pixel on a 27.0 system. The setting stays applied and keeps its meaning on macOS 15. See the pitfall “a white field on a white window”.
Placeholder As String
Text As String

Events

  • Event Accepted(text As String)
  • Event Changed(text As String)

Enumerations

ContentTypesNone=0Username=1Password=2OneTimeCode=3NewPassword=4Name=5TelephoneNumber=24EmailAddress=25URL=26BirthdateYear=43
AlignmentsLeading=0Center=1Trailing=2
BezelsSquare=0Rounded=1
Controls

NativeComboBox

class

NSComboBox in internal-list mode. It is not an NSPopUpButton: the field stays editable, so SelectedIndex is −1 as soon as the typing leaves the list — that is normal.

Constructor

Sub Constructor(placeholder As String = "")

We stay in “internal list” mode (usesDataSource = NO): the items live in the control, with no dataSource to hold.

Methods

Function Handle() As Ptr
Sub AddItem(value As String)
Function Count() As Integer
Sub RemoveAllItems()
Sub SetCompletes(completes As Boolean)
The typing completes itself on the first item that starts the same way, like Mail's address field.
Sub SetItems(ParamArray values() As String)
Sub SetVisibleItems(count As Integer)
The number of rows shown before the list scrolls. 5 by default.

Properties

Placeholder As String
SelectedIndex As Integer
−1 when the typed text matches no item — the normal case for a combo box, which accepts free values.
Text As String

Events

  • Event Accepted(text As String)
  • Event Changed(text As String)
  • Event SelectionChanged(index As Integer, value As String)
Controls

NativeSearchField

class

NSSearchField: the magnifier, the clear cross, the recent-searches menu and the keyboard behaviour come with it.

Constructor

Sub Constructor(placeholder As String = "")

Methods

Function Handle() As Ptr
Function RecentSearches() As String()
Sub SetLiveSearch(live As Boolean)
By default the action fires only on validation. In live mode, it fires on every keystroke — handy for filtering a list as you type.
Sub SetRecentsMenu(maximumRecents As Integer = 10)
Without a template menu, the magnifier stays silent. AppKit itself replaces the items marked with a special tag; we merely supply the mould.
Sub SetPlaceholders(ParamArray items() As String)
macOS 26: SEVERAL placeholders, which the field cycles through one after another — “Search for a package”, then “Search for a component”…The fallback is not “do nothing”: on an earlier system we set the FIRST one. A field that lost every placeholder would be a regression, not a degradation.

Properties

Placeholder As String
Text As String

Events

  • Event Changed(text As String)
Controls

NativeLevelIndicator

class

NSLevelIndicator: capacity gauge, relevancy indicator or rating stars. AppKit knows only two thresholds, and only in the “above = bad” direction; any other direction goes through SetBands.

Constructor

Sub Constructor(style As Styles = Styles.DiscreteCapacity, minimum As Double = 0, maximum As Double = 10, value As Double = 5)

Methods

Function Handle() As Ptr
Sub SetEditable(editable As Boolean)
Makes the gauge editable with the mouse — indispensable for a rating.
Sub SetBands(thresholds() As Double, colors() As Color)
Generalises SetThresholds to any number of bands.thresholds must be increasing, and colors must have one element more: the value takes colors(i) as soon as it is <= thresholds(i), the last colour covering everything beyond the last threshold. The reading direction therefore rests solely on the order of the colours — red first for a battery gauge, green first for a disk fill. SetBands(Array(2.0, 4.0, 7.0), Array(red, orange, yellow, green))
Sub SetThresholds(warning As Double, critical As Double, inverted As Boolean = False)
Two semantics, and only one is native.NORMAL (inverted = False): ABOVE the warning threshold the gauge turns yellow, above the critical threshold red. This is AppKit's behaviour, documented as such in the header — “values above the warning threshold”. Suits a disk fill: the higher, the worse. INVERTED (inverted = True): BELOW the warning threshold yellow, below the critical one red — the direction of a battery gauge. AppKit cannot do it, so this is nothing but a particular case of SetBands with three bands.
Sub SetTicks(count As Integer, major As Integer = 0)

Properties

Value As Double

Events

  • Event Changed(value As Double)

Enumerations

StylesRelevancyContinuousCapacityDiscreteCapacityRating
Controls

NativeColorWell

class

NSColorWell and, since macOS 13, its three presentations — two of which open a picker in a popover instead of the big system panel.

Constructor

Sub Constructor(initialColor As Color = &c007AFF, style As Styles = Styles.Standard)

Methods

Function Handle() As Ptr

Properties

Style As Styles
colorWellStyle dates from macOS 13; below that, the well keeps the classic presentation without anyone having to worry about it.
Value As Color
MaximumLinearExposure As Double
macOS 26. The SELECTOR is queried, not the version number: on an earlier system the control keeps its look instead of raising. Beyond 1, the well allows HDR colours to be chosen: the value is the maximum linear exposure accepted.

Events

  • Event Changed(value As Color)

Enumerations

StylesStandard=0Minimal=1Expanded=2
Controls

NativeColorSampler

class

NSColorSampler (10.15+): the system eyedropper, the one that magnifies the pixel under the cursor. Its only method takes an Objective-C block.

Constructor

Sub Constructor()

Its only method takes an Objective-C BLOCK. Xojo has built one since 2019r2 with the framework's ObjCBlock class: you give it a Delegate, it returns a Handle to pass to the Declare. Nothing to assemble in memory.

Methods

Function Handle() As Ptr
Function Show() As Boolean
Returns False if the eyedropper is not available — system too old. The sampling is ASYNCHRONOUS: Show returns at once, the Picked event arrives later. The instance must therefore survive until then, in a property of the window.

Events

  • Event Picked(value As Color, cancelled As Boolean)

Delegate

  • Delegate Sub SamplerHandler(nsColor As Ptr)
Controls

NativeDatePicker

class

NSDatePicker. Xojo does have a DesktopDatePicker, but not the ClockAndCalendar style — the real calendar with its clock — nor the range mode, nor the fine choice of the elements shown.

Constructor

Sub Constructor(style As Styles = Styles.TextFieldAndStepper)

Methods

Function Handle() As Ptr
Sub SetBordered(bezeled As Boolean, bordered As Boolean = False)
Sub SetElements(dateElements As DateElements, timeElements As TimeElements)
NSDatePickerElementFlags is a BIT FIELD, not a plain enumeration: the two groups combine. YearMonthDay is 224 and already contains YearMonth (192); HourMinuteSecond (14) contains HourMinute (12).
Sub SetRange(minimum As DateTime, maximum As DateTime)
Passing Nil on both sides removes the bounds.

Properties

Duration As Double
In Range mode, the duration in seconds covered from Value onwards; 0 in OneDate mode.
Mode As Modes
Style As Styles
Value As DateTime

Events

  • Event Changed(value As DateTime, duration As Double)

Enumerations

DateElementsNone=0YearMonth=192YearMonthDay=224
ModesOneDate=0DateRange=1
StylesTextFieldAndStepper=0ClockAndCalendar=1TextField=2
TimeElementsNone=0HourMinute=12HourMinuteSecond=14
Controls

NativeOutlineView

class inherits from NativeTableView

A columned outline — the Finder's hierarchical table in list mode. AppKit derives NSOutlineView from NSTableView: this class therefore inherits from NativeTableView and adds nothing but the hierarchy — checkboxes, menus, editable cells, gauges, colours, alignments and header come from the base class. A node is an object, NativeOutlineNode, whose number never changes: it stays valid whatever is inserted, removed or moved elsewhere, and that is what lets insertion, removal and moves animate. AddFolder populates from disk without entering packages. Sorting is refused: the storage order is the sibling order. NativeOutlineSidebar wraps the same NSOutlineView for a source list: two levels, one column, no header.

Constructor

Sub Constructor(width As Double = 600, height As Double = 300)

The model, since 14 September 2026. The outline holds the nodes in storage order, their parents (Nil for the root) and a number → row table rebuilt after every structural change. The keys handed to AppKit are tied to the node's number, never to its row: they stay right whatever moves. The former model returned the row index as identifier, and only appending could animate.

Building

Function AddNode(parent As NativeOutlineNode, ParamArray cells() As String) As NativeOutlineNode
Function AddNodeArray(parent As NativeOutlineNode, cells() As String) As NativeOutlineNode
Adds a node as the last child of parentNil for the root — and returns it: that is what you keep, and pass as the parent of its children.No animation: the form used to build a tree before showing it. AddNodeArray is public because a ParamArray does not carry from one method to another. And AddRow, inherited from the table, now creates a top-level node instead of growing the storage with no node.
Function AddFolder(parent As NativeOutlineNode, item As FolderItem, depth As Integer = 3) As NativeOutlineNode
Populates the tree from disk. The kind comes from NativeFileKind: an application or a Photos library is a folder to the file system, but we do not go into it — the Finder does not either.Writes three columns: name, kind, size. The following ones stay empty — that is where a checkbox goes to compose a payload.

Changing, animated

Function InsertNode(parent As NativeOutlineNode, index As Integer, cells() As String, animation As NativeTableView.Animations = SlideDown) As NativeOutlineNode
Function AddNodeAnimated(parent As NativeOutlineNode, cells() As String, animation As NativeTableView.Animations = FadeSlideDown) As NativeOutlineNode
Inserts a node at position index among parent's children; out of bounds, last. AddNodeAnimated is the same in last position.Storage follows sibling order: the row is placed just before the row of the sibling it precedes. The index handed to AppKit is the final position — the insertObjects:atIndexes: semantics the header adopts.
Sub RemoveNode(node As NativeOutlineNode, animation As NativeTableView.Animations = SlideUp)
Removes the node and all its descendants. The other nodes keep their number; only the removed ones become invalid, and every method then ignores them without raising.
Function MoveNode(node As NativeOutlineNode, newParent As NativeOutlineNode, index As Integer) As Boolean
Moves the node with its descendants to position index among newParent's children — final position, counted without the node. Returns False, doing nothing, for a move into its own descendants.Proven before being written: 180,000 random operations — insertions, removals, moves — without a single discrepancy between this model, a reference tree and the view AppKit rebuilds from the indexes it is sent alone, row formatting included.
When animating is not safe
An index AppKit does not know raises an Objective-C exception, which stops a Xojo application. AppKit knows the children only of a parent that is shown and expanded, and nothing before its first load. Outside those cases, the tree is reloaded instead — without loss: the keys being tied to the number, expansion and selection survive.

Dragging nodes

AllowsRowReordering As Boolean
Inherited from the table, and valid for the outline since 14 September 2026, through its own data source methods: outlineView:pasteboardWriterForItem:, validateDrop:proposedItem:proposedChildIndex: and acceptDrop:item:childIndex:. You drop between two nodes, or onto a node to put it inside, last — the proposed index is then -1.Dropping into its own descendants is refused while hovering, and the cursor shows it. Several nodes drop as a block, in on-screen order: each is placed in turn just before an anchor — the first non-dragged sibling after the drop point —, and a node whose ancestor is also dragged travels with it. Proven on 14,407 random drops against an independent computation — remove the block, reinsert it at once —, without a single discrepancy. The dragged type is specific to outlines, and a drag coming from another view is ignored: a node number only means something in its own tree.
Event NodesMoved(nodes() As NativeOutlineNode, newParent As NativeOutlineNode, index As Integer)
The dropped nodes, in on-screen order, their new parent — Nil for the root — and the position of the first. The counterpart of RowsMoved; the block stays selected, and a drop onto a collapsed node expands it.

Walking

Function Contains(node As NativeOutlineNode) As Boolean
Function NodeCount() As Integer
Function ParentOf(node As NativeOutlineNode) As NativeOutlineNode
Function ChildCount(parent As NativeOutlineNode) As Integer
Function ChildAt(parent As NativeOutlineNode, index As Integer) As NativeOutlineNode
Function IndexOf(node As NativeOutlineNode) As Integer
Nil means the root. Contains is true for a node of this outline that has not been removed; ParentOf also returns Nil for a foreign node, and Contains decides. IndexOf gives the index InsertNode and MoveNode expect.
Function RowOf(node As NativeOutlineNode) As Integer
Function NodeAt(row As Integer) As NativeOutlineNode
The link to the inherited row methods, which take a storage row.The row changes as soon as something is inserted, removed or moved elsewhere: do not keep it, keep the node.

Cells, selection and expansion, by node

Function NodeCell(node, column) As String · Sub SetNodeCell(node, column, value)
Function NodeChecked(node, column) As Boolean · Sub SetNodeChecked(node, column, checked)
Sub SetNodeIcon(node, symbolName) · Sub SetNodeSubtitle(node, column, subtitle)
Sub SetNodeBold(node, column, bold) · Sub SetNodeTextColor(node, column, textColor)
Sub SetNodeBackground(node, column, backColor) · Sub ReloadNode(node)
The node version of each inherited row method: RowOf does the translation. ReloadNode redraws a node already on screen — after a SetNodeIcon, for instance.
Sub SelectNode(node) · Function SelectedNode() As NativeOutlineNode
Function SelectedNodes() As NativeOutlineNode() · Sub ScrollToNode(node)
Sub ExpandNode(node, withChildren As Boolean = False) · Sub CollapseNode(node, withChildren As Boolean = False)
Function IsNodeExpanded(node) As Boolean · Sub ExpandAll() · Sub CollapseAll()
SelectNode(Nil) deselects. A node under a collapsed parent has no row on screen: it cannot be selected.

Events

Event NodeSelectionChanged(node As NativeOutlineNode)
Event NodeCellChanged(node As NativeOutlineNode, column As Integer, value As String)
Event NodeDoubleClicked(node As NativeOutlineNode)
Raised in addition to the table's, which give a storage row. Prefer these: a row changes as soon as something is inserted or moved.
Controls

NativeOutlineNode

class

An outline node — a handle, not a row. NativeOutlineView returns one on every add and expects one wherever a node must be designated. Its number never changes: it stays valid after an insertion, removal or move elsewhere in the tree.

Why a type of its own

A stable number kept as an Integer would have compiled everywhere, and matched the row… until the first removal, then silently broken everything. With a type of its own, the compiler points at every place to revisit. The link to the tree is weak: keeping nodes does not keep the tree alive.

Methods

Function Id() As Integer
A number specific to the tree, never reused — not even after RemoveAllRows. To compare two nodes, Is is enough: the tree always returns the same object for the same node.
Function IsValid() As Boolean
False if the node was removed, if the tree was cleared, or if it no longer exists.
Function Tree() As NativeOutlineView
Function BelongsTo(tree As NativeOutlineView) As Boolean
The node's tree, through a weak link; Nil if it is gone.
Sub Constructor(owner As NativeOutlineView, nodeId As Integer)
Reserved to the tree. A hand-made node, even with an existing number, is not recognised: Contains requires the object the tree returned.
Controls

NativePathControl

class

NSPathControl: the Finder's path bar. Every component is clickable, the control accepts drag and drop, and in editable PopUp style it opens a filtered NSOpenPanel itself.

Constructor

Sub Constructor(style As Styles = Styles.Standard)

Methods

Function Handle() As Ptr
Sub SetAllowedTypes(ParamArray types() As String)
Extensions or UTIs. Used by the NSOpenPanel the control opens by itself in editable PopUp style; with no list, everything is accepted.
Sub SetAllowedTypeArray(types() As String)
The array form, for NativePathBarControl. An empty array sets nil: for the header, an empty list would allow nothing.
Sub SetEditable(editable As Boolean)
Combined with the PopUp style, adds the “Choose…” entry that opens the panel.
Sub SetPlaceholder(text As String)

Properties

Path As String
Style As Styles
Value As FolderItem

Events

  • Event Changed(item As FolderItem, path As String)
  • Event DoubleClicked(item As FolderItem, path As String)

Enumerations

StylesStandard=0PopUp=2
Controls

NativeSegmentedControl

class

NSSegmentedControl with the eight styles, the four tracking modes and the distribution. Careful: the NSSegmentStyle enumeration has a hole at 7Separated is 8.

Constructor

Sub Constructor(labels() As String, selectOne As Boolean = True)

Methods

Function Handle() As Ptr
Function IsSelected(index As Integer) As Boolean
Only meaningful in SelectAny mode: elsewhere, SelectedIndex is enough.
Sub Refit()
To be called again after a change of style, symbol or width: the ideal size depends on all three.
Sub SetDistribution(distribution As Distributions)
macOS 10.13+. Decides the fate of the remaining space: left empty, shared out, or segments equalised. Without effect below.
Sub SetSelected(index As Integer, selected As Boolean)
The counterpart of IsSelected, for ticking several segments in SelectAny.
Sub SetSymbol(index As Integer, symbolName As String, accessibilityDescription As String = "")
An SF symbol instead of — or beside — the label. A symbol that cannot be found simply leaves the segment as it is.
Sub SetWidth(index As Integer, width As Double)
0 makes the segment dynamic: the distribution then decides.

Properties

SelectedIndex As Integer
Style As Styles
TrackingMode As TrackingModes
BorderShape As NativeControlHost.BorderShapes
macOS 26. The SELECTOR is queried, not the version number: on an earlier system the control keeps its look instead of raising.

Events

  • Event Changed(index As Integer)

Enumerations

DistributionsFit=0Fill=1FillEqually=2FillProportionally=3
StylesAutomatic=0Rounded=1TexturedRounded=2RoundRect=3TexturedSquare=4Capsule=5SmallSquare=6Separated=8
TrackingModesSelectOne=0SelectAny=1Momentary=2MomentaryAccelerator=3
Controls

NativeSlider

class

NSSlider, optionally framed by two SF symbols in a host view — the Preview or Photos pattern. Handle then returns the host view, SliderHandle the slider.

Constructor

Sub Constructor(minimum As Double = 0, maximum As Double = 100, value As Double = 50, width As Double = 120, leadingSymbol As String = "", trailingSymbol As String = "")

If SF symbols are supplied, the slider is framed by two small images in a host view — the Preview or Photos pattern. Handle then returns that host view, SliderHandle the slider itself.

Methods

Function Handle() As Ptr
The view to hand to the item: icons + slider together where applicable.
Sub SetTicks(count As Integer, valuesOnly As Boolean = False)
Tick marks under the slider. valuesOnly forces the values to align on them.
Sub SetTooltip(text As String)
Function SliderHandle() As Ptr

Properties

Value As Double
TintProminence As NativeControlHost.TintProminences
macOS 26. The SELECTOR is queried, not the version number: on an earlier system the control keeps its look instead of raising.
NeutralValue As Double
macOS 26. The SELECTOR is queried, not the version number: on an earlier system the control keeps its look instead of raising. The ORIGIN of the fill, not a bound: a balance slider fills from the centre rather than from the left.

Events

  • Event Changed(value As Double)
Controls

NativeProgressIndicator

class

NSProgressIndicator, bar or spinner, determinate or not.

Constructor

Sub Constructor(indeterminate As Boolean = True, width As Double = 120)

Methods

Function Handle() As Ptr
Sub StartAnimation()
Sub StopAnimation()

Properties

Value As Double
0 to 100 by default (minValue/maxValue unchanged).
Controls

NativeTokenField

class

NSTokenField: the token field of Mail's “To:”. Its objectValue is not a string but an NSArray — the whole difference with the NSTextField it inherits from. Completion draws on a supplied list, served by the delegate.

Constructor

Sub Constructor(placeholder As String = "")

NSTokenField inherits from NSTextField, but its objectValue is NOT a string: it is an NSArray. Reading stringValue returns only the flattened representation, separated by the tokenizing character set.

Methods

Function Handle() As Ptr
Sub SetCompletions(ParamArray candidates() As String)
The list completion draws on. Empty = no completion.
Sub SetTokenizingCharacter(separator As String = ",")
The character that validates a token. A comma by default in AppKit; the semicolon is common for addresses.
Sub SetTokens(ParamArray values() As String)
Sub SetTokenArray(values() As String)
Sub SetCompletionArray(candidates() As String)
The array forms of SetTokens and SetCompletions: a ParamArray cannot be passed from one method to another, and NativeTokenFieldControl forwards lists read from the Inspector.
Function Tokens() As String()
objectValue is an NSArray, NOT a string — that is the whole difference with the NSTextField that NSTokenField inherits from. Reading stringValue would return only the flattened version, glued back together by the tokenizing character.

Properties

Placeholder As String
Style As Styles

Events

  • Event Changed(tokens() As String)

Enumerations

StylesStandard=0None=1Rounded=2Squared=3PlainSquared=4
Controls

NativeTextView

class

NSTextView in its NSScrollView: a real rich-text editor. ShowFormatBar turns on usesInspectorBarthe system RTF bar, the most complete one: font and size shown, strikethrough, background colour, and already localised — but it sits on the window, not on the editor. The same commands are also exposed one by one, so that a bar of your own can be composed.

Constructor

Sub Constructor(width As Double = 520, height As Double = 260)

The scroll view serves scrolling and the RULER. It is NOT needed for the format bar — see ShowFormatBar, which is not what one assumes.

Methods

Function Handle() As Ptr
The SCROLL VIEW — that is what is hosted, not the text view. Same convention as NativeSlider with its host view.
Sub LoadRTF(file As FolderItem)
Replaces the whole content. The RTF primitives already live in the Cocoa module: no need to redeclare them here.
Sub SaveRTF(file As FolderItem)
Sub SetAdaptiveDarkMode(adaptive As Boolean)
macOS 10.14+. Without it, an RTF document whose colours are hard-coded stays unreadable in dark mode: the system does not remap them.
Sub SetBorder(kind As Borders)
When the editor is housed in a frame that already draws its border, the scroll view's is redundant.
Sub SetEditable(editable As Boolean, richText As Boolean = True)
Sub SetOptions(spellChecking As Boolean = True, linkDetection As Boolean = True, importsGraphics As Boolean = True, fontPanel As Boolean = True)
importsGraphics allows images to be dropped into the text; fontPanel connects the editor to the system Font panel (Cmd-T).
Sub SetWritingTools(behavior As WritingTools)
macOS 15+. None (-1) excludes the view from the writing tools; Complete asks for inline rewriting, Limited the overlay panel.
Sub SetRulerAccessory(view As Ptr)
The THIRD route, and the only one that houses a bar INSIDE the text area: the ruler's accessory view. Verified in Objective-C — the accessory becomes a subview of the NSRulerView, hence of the scroll view; no window accessory is created and the content view does not move.TRAP: setAccessoryView: RAISES AN EXCEPTION if the ruler's clientView has not been set — “you must set the client view of the ruler before you can have an accessory view”. This is not a no-op, it is a crash. Also worth knowing: the graduated ruler comes WITH it. In the trial, a 60-point ruler for 28 of accessory. For a bar on its own in a frame, NativeRichTextEditor's home-made bar remains preferable.
Sub ShowFindBar(visible As Boolean)
TextEdit's search bar, Cmd-F. incrementalSearching highlights as you type.
Sub ShowFormatBar(visible As Boolean)
THE TextEdit RTF bar: font, size, bold/italic/underline, text and background colours. It is usesInspectorBar, which appeared in 10.7.BUT IT DOES NOT BELONG TO THE EDITOR. Verified in Objective-C: AppKit installs an NSTitlebarAccessoryViewController on the WINDOW — an __NSInspectorBarView view, 28 points, layoutAttribute 4, hence under the toolbar and across the full width — and SHRINKS the content view by as much. Turning it off removes the accessory and gives the 28 points back. Three consequences: 1) the bar cannot be confined to a frame: it is at window level, exactly as in TextEdit; 2) any layout that positions its own views must be redone after the call, otherwise the bar covers the content instead of pushing it; 3) neither the scroll view nor the order of the calls changes anything at all — tested without a scroll view, set before setDocumentView:, and outside a window: the accessory is installed in all three cases.
Sub ShowRuler(visible As Boolean)
The graduated ruler, with tab stops and indents. It shares the area with the format bar: both can coexist, one above the other.
Sub ChangeSize(bigger As Boolean)
The “Bigger” / “Smaller” actions of the Format menu: it is the NSFontManager that carries them out, according to the TAG of the object handed to it.
Function IsBold() As Boolean
Function IsItalic() As Boolean
Sub SetAlignment(alignment As Alignments)
Native NSText actions: they already know which paragraphs to touch.
Sub ShowColorPanel()
The panel sends changeColor: to the first responder: it is the NSTextView that applies the colour, there is nothing to copy out.
Sub ShowFontPanel()
Sub ToggleBold()
Sub ToggleBullets()
The only command in the bar with NO native equivalent: AppKit exposes only orderFrontListPanel:, a panel, not a toggle. So “•⇥” is prefixed to each paragraph of the selection, or removed.
Sub ToggleItalic()
Sub ToggleUnderline()
A native action, this one: NSText supplies it directly.
Function TextViewHandle() As Ptr
The NSTextView itself, to be passed to the Cocoa module's primitives — ApplyColor, SetTextColorRange, TextLength.
Sub Highlight(scheme As HighlightSchemes = HighlightSchemes.Default)
macOS 15. The Notes highlighter. The colour is NOT passed as an argument: “The sender should be a menu item with a representedObject of type NSTextHighlightColorScheme” — so a throwaway menu item is built to carry it. Calling the same colour again removes the highlight.
Function HighlightSupported() As Boolean
To ADAPT the interface, not to guard the call.
Function WritingToolsActive() As Boolean
macOS 15. Are the tools rewriting the text RIGHT NOW? That is the moment to suspend an autosave or a content analysis, which would work on an intermediate state.
Sub SetAllowedWritingToolsResults(options As Integer)
macOS 15. What the tools are allowed to RETURN. A bit field whose values imply one another: List and Table imply RichText, and PresentationIntent (macOS 26) implies all three. An editor that cannot display a table has every interest in not being offered one.The highlight LOOK — textHighlightAttributes — is deliberately NOT exposed. Tried and withdrawn: the background set that way paints over the glyphs and hides the text, with two colours as with one; forcing a redisplay makes it worse, drawTextHighlightBackgroundForTextRange: being a drawing method that cannot be called outside its pass. Highlight's named schemes do work.
Sub ShowWritingTools()
macOS 15.2. Sends showWritingTools: straight to the view, without depending on the first responder.

Properties

Text As String
The BARE text: the attributes are lost. To keep the formatting, go through SaveRTF.

Events

  • Event Changed()

Enumerations

WritingToolsResultsDefault=0PlainText=1RichText=2List=4Table=8PresentationIntent=16
BordersNone=0Line=1Bezel=2Groove=3
AlignmentsLeft=0Center=1Right=2Justified=3
WritingToolsNone=-1Automatic=0Complete=1Limited=2
Controls

NativeRichTextEditor

class

The complete resource editor: a rounded frame, a grey command bar, the typing area, a rule, a footer with its button. Built entirely on NativeBox — hence on real NSColors, which follow dark mode on their own, where a CGColor set in a layer stays frozen. ShowCommandBar makes it exclusive of the system's native bar, which belongs to the window.

Constructor

Sub Constructor(width As Double = 620, height As Double = 320)

Everything is built in NSBox and not in layers: an NSBox paints with REAL NSColors, so it follows dark mode on its own. A CGColor set in a CALayer is frozen the moment it is written and never updates.

Methods

Function Editor() As NativeTextView
The text view, for everything the bar does not cover: Text, LoadRTF, SaveRTF, ShowRuler, and the native ShowFormatBar.
Function Handle() As Ptr
Sub ShowCommandBar(visible As Boolean)
The component's bar and the system's native bar are ALTERNATIVES, not layers: the native one sits on the window (see NativeTextView.ShowFormatBar), this one lives in the frame. Showing both gives two rows of commands that do the same thing.
Sub SetFooter(text As String, buttonTitle As String = "")
The footer: an explanation on the left, a button on the right. Passing an empty title shows no button.

Events

  • Event Changed()
  • Event CommandUsed(index As Integer)
  • Event FooterPressed()
Controls

NativeDropView

class

A view that receives drag and drop — the file well Xojo does not have. The view is itself the destination, so there is no separate target object. The other direction is handled by NativeDragSource.

Constructor

Sub Constructor(width As Double = 300, height As Double = 140, accepts As Kinds = Kinds.FilesAndText)

Here the VIEW is itself the destination: no separate target object, so the Cocoa registry maps the view pointer directly to the Xojo object.

Methods

Function Handle() As Ptr
Sub SetAccepts(kinds As Kinds)
Registers the accepted types again. unregisterDraggedTypes first: otherwise the old types would remain in addition to the new ones, and the view would carry on accepting what has just been refused it.

Properties

Operation As Operations
The operation announced to the system: it decides the CURSOR the user sees — the “+” of a copy, the curved arrow of an alias.

Events

  • Event Dropped(files() As FolderItem, text As String) As Boolean
  • Event Entered(hasFiles As Boolean, hasText As Boolean) As Boolean
  • Event Exited()

Enumerations

KindsFilesOnly=0TextOnly=1FilesAndText=2
OperationsNone=0Copy=1Link=2Generic=4Move=16
Controls

NativeDragSource

class

A view that emits a drag — to the Finder or another application. Three overrides, none decorative: an empty mouseDown: (without it, NSResponder passes the event up and the view never follows the gesture), mouseDragged: (where the session starts, because beginDraggingSessionWithItems:event:source: requires an NSEvent), and hitTest: returning Self — since NSControl intercepts mouseDown:, a plain label set in the well would break the drag.

Constructor

Sub Constructor(width As Double = 220, height As Double = 130)

Three overrides, and none is decorative — each answers a measured fact:

mouseDown:

NSView does not implement it: without an override, NSResponder passes the event UP to the nextResponder and the view never follows the gesture. This empty body is therefore a deliberate sink. mouseDragged: that is where the session starts — beginDraggingSession… requires an NSEvent, there is no dragging without a gesture. hitTest:

Methods

Sub AddContent(view As Ptr)
The well draws nothing of itself: you put in it whatever you want shown — a NativeImageView, a NativeBox, a label. hitTest: guarantees that the content will not steal the gesture, whatever it is.
Function Handle() As Ptr
Sub SetFiles(files() As FolderItem)
What the drag CARRIES. Replaces any previous content.
Sub SetText(value As String)

Properties

Operation As UInteger
What the SOURCE allows. The destination chooses within that set: offering Copy alone means forbidding a move.
PreviewSide As Double
StackMultipleItems As Boolean

Events

  • Event Ended(operation As UInteger)
  • Event CancelDrag() As Boolean

Constants

  • kFormationStack = 4
  • OperationCopy = 1
  • OperationLink = 2
  • OperationMove = 16
Controls

NativeImageView

class

NSImageView. Xojo has DesktopImageViewer, but without the scaling modes, without a frame, without content tint and without SF symbols. ShowFile shows the content if it is an image and otherwise falls back to the Finder icon — which exists for everything: folder, application, unknown document.

Constructor

Sub Constructor(width As Double = 96, height As Double = 96, scaling As Scalings = Scalings.ProportionallyUpOrDown)

Methods

Sub Clear()
Function Handle() As Ptr
Sub ShowFile(item As FolderItem)
A preview of the content if it is an image, otherwise the Finder icon — which exists for EVERYTHING: folder, application, unknown document. initWithContentsOfFile returns Nil for a folder as for a text file, hence the fallback.
Sub ShowFinderIcon(item As FolderItem)
The icon the Finder shows for this particular item — not the one for its type. A folder, a package and an application each have their own.
Function ShowThumbnail(item As FolderItem, maxSide As Double = 512) As Boolean
The REAL QuickLook thumbnail — the first page of a PDF, the image itself, the 3D rendering of an STL — and not the icon of the type.WHY THIS ROUTE, and not QLPreviewView: a LIVE preview installs an NSRemoteView served by SceneKitQLPreviewExtension, and displaying a 3D model then destroys the process's drag and drop FOR GOOD — measured, nothing restores it but quitting. The thumbnail, for its part, goes through SceneKitQLThumbnailExtension, the one the Finder uses constantly: it returns an IMAGE, puts no view in our window, and leaves no process behind it. QLThumbnailImageCreate has been deprecated since 10.15 but is still present, and it is SYNCHRONOUS — 43 ms on an STL — where the modern API demands a block and a callback off the main thread. The day it disappears, it will return Nil and we will fall back to the icon: the breakage is already provided for. ONE SIDE ONLY, and it is measured: the size asked for is a BOX the thumbnail has to fit into, and the 3D generator returns 4:3. A box narrower than that ratio makes it fail without a word — 840 x 340 returns Nil, 512 x 512 returns 512 x 384. So we ask for a SQUARE, which contains any natural ratio, and let the view scale it. Beyond 1024, the generator caps itself.
Sub ShowSymbol(symbolName As String, accessibilityDescription As String = "")
Sub SetTint(useTint As Boolean, tint As Color = &c000000)
10.14+, and acts only on “template” images — an SF symbol, typically.

Properties

FrameStyle As Frames
Scaling As Scalings

Enumerations

FramesNone=0Photo=1GrayBezel=2Groove=3Button=4
ScalingsProportionallyDown=0AxesIndependently=1None=2ProportionallyUpOrDown=3
Controls

NativeTableView

class

A view-based NSTableView: every cell is a real view, not a drawing — hence the checkboxes, menus, editable fields and gauges you can put in it, everything DesktopListBox cannot do. Background, text colour and bold are set per cell, and sorting carries them along with their row. Sorting itself goes through sortDescriptorPrototype: the arrow is drawn by AppKit. An SF Symbols icon is set per row at the start of the first column (SetRowIcon). It is also the base class of NativeOutlineView: its protected methods — data source, hierarchical column, clearing, sorting, storage primitives, visible row ↔ storage row translation (ModelRow, VisibleRow), event relays — are provided there to be overridden. Those two translations make the edited cell, single and multiple selection, scrolling and row reloading right for an outline; and MoveRowModel moves the whole row, formatting included — the first version lost background, colour, bold, alignments and subtitles.

Constructor

Sub Constructor(width As Double = 480, height As Double = 240, viewClass As String = "NSTableView", dsClass As String = "VDSTableDS")

NativeSidebarBase also wraps an NSTableView, but for a source list: one column, no header, no sorting. The two do not overlap.

The LAST TWO parameters are reserved for subclasses. AppKit derives NSOutlineView from NSTableView: NativeOutlineView does the same, and therefore has only its view class and the name of its data source to supply.

Methods

Sub AddColumn(title As String, width As Double = 140, sortable As Boolean = False, kind As CellKinds = CellKinds.Text, minWidth As Double = 40)
The columns BEFORE the rows: the storage is flat — index = row × number of columns + column —, adding one afterwards would shift everything. Rather than corrupt silently, we clear.
Sub AddRow(ParamArray cells() As String)
The convenient form. The work is in AddRowArray: a ParamArray cannot be passed from one method to another, and a subclass needs the array form — AddNode simply adds a parent to it.
Function CellAt(row As Integer, column As Integer) As String
Function CellChecked(row As Integer, column As Integer) As Boolean
A ticked box has no separate storage: it is “1” in the same grid of strings as everything else. One single source of truth.
Function ColumnCount() As Integer
Function Handle() As Ptr
The view to SET: the NSScrollView, not the table — the table is its documentView and has no useful size of its own.
Sub Reload()
Sub RemoveAllRows()
Function RowCount() As Integer
Sub ScrollTo(row As Integer)
Sub SelectRow(row As Integer)
Sub SetCell(row As Integer, column As Integer, value As String)
Sub SetCellBackground(row As Integer, column As Integer, backColor As Color)
A background that is SET does not follow dark mode: it is a colour chosen by the application, not a system colour. To be reserved for meaning — a late item in red, an agreement in green — never for decoration.
Sub ClearCellBackground(row As Integer, column As Integer)
Sub SetCellTextColor(row As Integer, column As Integer, textColor As Color)
Sub SetCellBold(row As Integer, column As Integer, bold As Boolean)
Sub SetCellChecked(row As Integer, column As Integer, checked As Boolean)
Sub SetCellAlignment(row As Integer, column As Integer, alignment As Alignments)
Overrides the column for THIS cell. Useful where a value changes nature within a single column — a number among text, a total at the bottom of a list.
Sub ClearCellAlignment(row As Integer, column As Integer)
Returns the cell to its column's alignment.
Sub SetCellVerticalAlignment(row As Integer, column As Integer, alignment As VerticalAlignments)
Sub ClearCellVerticalAlignment(row As Integer, column As Integer)
Sub SetColumnVerticalAlignment(column As Integer, alignment As VerticalAlignments)
AppKit offers NOTHING for this: an NSTextField centres its text in the frame it is given, and that is all. Vertical alignment is therefore done by placing the FRAME in the cell, not by setting a property.
Sub SetColumnAlignment(column As Integer, alignment As Alignments)
Sub SetColumnChoices(column As Integer, ParamArray items() As String)
The choices of a Popup column. Stored joined by a line break: an array of arrays does not lend itself to the text format of a Xojo class.
Sub SortBy(column As Integer, ascending As Boolean = True)
The default sort: ALPHABETICAL and case-insensitive, since the model stores nothing but text. To sort numbers or dates, SortChanged must be intercepted and return True.The STYLES follow the rows: a red background set on “late” must stay with it, not stay in third place in the table. An OUTLINE refuses: its rows are nodes, and the parent table designates nodes by their INDEX. Permuting them would break the parent-child link in silence.
Function TableHandle() As Ptr
Sub SetRowIcon(row As Integer, symbolName As String)
A system symbol at the start of the FIRST column. The name is the SF Symbols one — “folder”, “doc.text” — and an empty string removes it.

Properties

SelectedRow As Integer read-only
For an OUTLINE, the visible row depends on what is expanded: CurrentRow then returns the node, which is the stable identity.
BezeledEditableCells As Boolean
DoubleClickAction As Boolean
EDITING a cell traditionally starts on the second click, and the table's double-click action aims at the same gesture. So the two fight over the mouse on an editable column: disengaging the action hands editing back. A nil SEL disarms cleanly: AppKit sends nothing more.
AlternatingRowColors As Boolean
GridMask As UInteger
AllowsMultipleSelection As Boolean
RowHeight As Double
Style As Styles
setStyle: exists only from macOS 11 onwards: on an earlier system the table keeps its original look rather than raising.
ShowsHeader As Boolean
Hiding a header means setting Nil: AppKit then folds away the area the NSScrollView was reserving for it. Bringing it back means PUTTING BACK the one that was set aside at the start. Building a fresh one works too, BUT it has to be given a height: an NSTableHeaderView with a zero frame really is installed, and the scroll view restores its area — at a height of zero. So you see “nothing”, with not the slightest error to say so.

Events

  • Event CellChanged(row As Integer, column As Integer, value As String)
  • Event RowDoubleClicked(row As Integer)
  • Event SelectionChanged(row As Integer)
  • Event SortChanged(column As Integer, ascending As Boolean) As Boolean

Enumerations

AlignmentsLeft=0Center=1Right=2
VerticalAlignmentsTop=0Middle=1Bottom=2
CellKindsText=0Editable=1Checkbox=2Popup=3Level=4
StylesAutomatic=0FullWidth=1Inset=2SourceList=3Plain=4

Constants

  • GridNone = 0
  • GridVertical = 1
  • GridHorizontal = 2

Row animations

Sub InsertRow(index As Integer, cells() As String, animation As Animations = Animations.SlideDown)
Inserts a row AT AN INDEX and animates it, where AddRow appends and forces a full reload.THE MODEL FIRST, AppKit after: insertRowsAtIndexes: immediately asks the data source for the new row; if the store is not already updated, the table reads a row that does not exist.
Sub RemoveRow(index As Integer, animation As Animations = Animations.SlideUp)
Removes a row and animates it.The header notes that indexes passed to removeRowsAtIndexes: refer to the DISPLAYED state, not the final one. No consequence for a single row, but it would matter for several.
Sub MoveRow(fromIndex As Integer, toIndex As Integer)
Moves a row. No animation option, and that is not an oversight: moveRowAtIndex:toIndex: accepts none — the view is neither destroyed nor recreated, the same one simply changes position, and the move animates itself.
Sub BeginUpdates()
Sub EndUpdates()
macOS 10.7. Several changes animate TOGETHER between the two, the starting state being the one before BeginUpdates. Calls nest.Unnecessary for a single change — the header says so — but essential as soon as there are two, otherwise each animates on its own and the result jumps.
Sub ReloadRow(row As Integer)
macOS 10.6. Redraws ONE row instead of reloading the whole table: selection, scrolling and the other rows' views are preserved. Works for an outline too.
Enum Animations
None, Fade, Gap, the four slides and the four fade combinations.The slide options are NOT flags, despite appearances. Measured: SlideUp 16, SlideDown 32, SlideLeft 48, SlideRight 64 — and SlideUp Or SlideLeft gives 48, that is SlideLeft. It is a FIELD, and the header confirms it: « only one option from this group may be specified at a time ». Only the fade truly combines, hence the ready-made combinations rather than an addition that would silently give the wrong value.

Two-line cell

Sub SetCellSubtitle(row As Integer, column As Integer, subtitle As String)
Function CellSubtitle(row As Integer, column As Integer) As String
The second line of a CellKinds.Subtitle cell — the Mail-list pattern: a title, and below it a smaller, paler line. Kept in its own parallel array, like every other per-cell property, rather than encoding two values in one string — which would make CellAt return « title + separator + subtitle ».It needs height: two lines do not fit in a standard 22-point row. Below RowHeight = 34 the subtitle is clipped.

Columns and selection

Function SelectedRows() As Integer()
Sub SelectRows(rows() As Integer, extend As Boolean = False)
Function IsRowSelected(row As Integer) As Boolean
AllowsMultipleSelection already existed, but nothing let you READ the selection — a multiple selection you can switch on without being able to read it is half a feature. SelectedRow only returns the current row.Walking the NSIndexSet is bounded by the row count rather than compared against NSNotFound. That one is NSIntegerMax — 9223372036854775807 — which a Double cannot represent: it rounds to …808, the comparison would never be true and the loop would never end. Verified, not assumed.
AllowsColumnReordering As Boolean
AllowsColumnResizing As Boolean
Drag columns to reorder them, resize them by dragging between headers. Both default to True in AppKit, and this class keeps that default.A column only resizes if it allows it for itself — see -[NSTableColumn setResizingMask:]. And programmatic changes remain possible whatever the setting.
Sub SetIntercellSpacing(horizontal As Double, vertical As Double)
Function IntercellSpacing() As Cocoa.NSSize
The spacing between cells. AppKit's default is 3 × 2.The height ADDS to RowHeight: a row's real rectangle is rowHeight plus intercellSpacing.height, as the header states about rectOfRow:.
Event ColumnClicked(column As Integer)
Event ColumnDragged(column As Integer)
A click on a header, distinct from sorting, and the end of a column drag.
Event ShouldRefuseColumnReorder(column As Integer, toColumn As Integer) As Boolean
The veto. It asks what to REFUSE, not what to allow, and the inversion is deliberate: a RaiseEvent with no handler returns False, so asking the question the other way round would forbid every move by default. Inverted, no handler means everything is allowed — like AppKit when the delegate method is not implemented.toColumn is -1 on the FIRST call, when the column is grabbed and before any destination exists. The header says so: refusing at that moment forbids moving that column at all.

Row dragging

AllowsRowReordering As Boolean
Move a row by dragging it with the mouse. The drop calls MoveRow, so the move animation serves here with no extra line of code. Also valid for NativeOutlineView since 14 September 2026, through its own data source methods — see its card.As in Numbers: the original row stays in place and selected, a ghost — a copy of the grabbed rows on a card with an accent-colour border and a pronounced drop shadow — follows the cursor inside the table, and AppKit draws the insertion line (Regular feedback style). Non-contiguous rows are packed together in the ghost.The ghost is a real view placed in the table, made of photos taken when the rows are grabbed; AppKit's drag image is emptied. The Gap style, used at first, hid the grabbed row: the selection vanished and an empty hole remained. A drop proposed on a row is retargeted above it.
Event RowsMoved(sources() As Integer, firstRow As Integer)
Raised after a successful drop: the ORIGINAL indices in ascending order, and where the block landed. A non-contiguous selection gives several at once.The drop arithmetic is the one delicate spot. AppKit gives the insertion index in the coordinate space BEFORE the move, and each removed row shifts everything after it by one — with a multiple selection those shifts compound. The algorithm is not reasoned but verified: simulated over the 1757 cases formed by every size up to seven rows, every selection subset and every drop point, compared against an independently computed result. Zero divergence.
Controls

NativeCollectionView

class

A grid of items — NSCollectionView and its flow layout. Xojo has nothing of the sort. Every item is an NSCollectionViewItem, normally loaded from a nib: here its view is set for it, loadView is then never called, and the nib becomes unnecessary. The items are cached rather than recycled — a deliberate trade-off, see the pitfalls.

Constructor

Sub Constructor(width As Double = 600, height As Double = 300, itemWidth As Double = 104, itemHeight As Double = 104)

VERIFIED rather than assumed: an NSCollectionViewItem accepts having its view SET, and loadView is then never called. It is the same trick NativeWindowChrome uses for its NSViewController, and it does away with nibs entirely.

Methods

Sub AddFile(item As FolderItem)
The Finder icon accounts for THIS particular item — folder, application, document — and not for its type. It is what the user sees.
Sub AddItem(title As String, symbolName As String = "")
Function Count() As Integer
Function Handle() As Ptr
The view to SET: the NSScrollView, not the grid — the grid is its documentView and resizes itself.
Sub Reload()
Sub RemoveAllItems()
Sub SetItemSize(width As Double, height As Double)
Sub SetSpacing(betweenItems As Double, betweenLines As Double)

Properties

HorizontalScrolling As Boolean
AllowsMultipleSelection As Boolean

Events

  • Event SelectionChanged(index As Integer)
Controls

NativePredicateEditor

class

macOS's rule builder — “Name contains … AND Size > …” —, the very component of the Finder's search window. Xojo has neither this interface nor the predicate that comes out of it. You declare typed fields, Build derives the row templates from them, and PredicateFormat returns the textual form to save.

Constructor

Sub Constructor(width As Double = 560, height As Double = 180)

The editor GROWS with the rules added: so it lives in an NSScrollView, without which the bottom rows fall outside the frame.

Methods

Sub AddField(keyPath As String, kind As Kinds = Kinds.Text)
The field is designated by its KEY PATH — “name”, “size”. That is what appears in the first menu of each rule, and what will be found again in the predicate produced.
Sub Build()
One row template per TYPE, not per field: AppKit expects to be given all the left-hand expressions sharing the same type and the same operators in a single NSPredicateEditorRowTemplate.
Sub AddRow()
addRow: RAISES an NSRangeException inside AppKit — verified — if the root row is not COMPOUND. The editor falls into that state as soon as a simple predicate is set on it, and the “add” button then becomes a mine. So the root is restored before adding.
Function Handle() As Ptr
The view to SET: the NSScrollView, the editor growing with its rules.
Function PredicateFormat() As String
The TEXTUAL form of the predicate — “name CONTAINS "a" AND size > 100”. It is what gets saved, and the only thing that can be read back.
Function RowCount() As Integer
Sub SetPredicateFormat(format As String)
DANGER, and it is real: predicateWithFormat: raises an NSInvalidArgumentException on a malformed string — verified —, and an Objective-C exception CANNOT be caught from Xojo: it ends the process. Pass only strings here that you produced yourself, typically read back from PredicateFormat.

Properties

NestingMode As NestingModes
RowHeight As Double

Events

  • Event Changed(predicateFormat As String)

Enumerations

KindsText=0Number=1Date=2YesNo=3
NestingModesSingle=0List=1Compound=2

Placeable controls

These are not built in code: they are dragged from the library and set in the Inspector. Fifteen inherit from a native Xojo control, grab at opening the real NSView it already created, through Ptr(Me.Handle), and apply what Xojo does not expose. The other twelve host: they descend from DesktopCanvas and lodge a library object — the table and the outline, and ten AppKit controls.

Worth knowing when placing one of these by hand in a .xojo_window. DesktopImageViewer, DesktopProgressBar and DesktopProgressWheel are the only types that write PanelIndex on top of TabPanelIndex. Omitted, it defaults to 0: the control belongs to panel zero and draws on top of EVERY page, at its own coordinates — which looks like a layout bug on the page it invades. PanelIndex is the 0-based twin of the 1-based TabPanelIndex. Those three types also carry AllowTabStop, not TabStop.

Placeable controls

NativeButtonControl

class

Inherits DesktopButtonNSButton. A native Xojo button enriched in place. Dragged from the library and set in the Inspector like any DesktopButton — but with the NSButton settings Xojo does not expose.

Properties

ControlSize As NativeButton.ControlSizes
AppKit control size. The font is reapplied by hand: DesktopButton already sets an explicit one before Opening, which prevents the automatic size adjustment.
Bordered As Boolean
Button border (setBordered:).
ShowsBorderOnlyOnHover As Boolean
The border appears only while the mouse is inside.
Symbol As String
The NAME of an SF Symbol — square.and.arrow.up — not a file: a symbol follows the text size, dark mode and accentuation, which no placed image does. Empty = no icon. An unknown name leaves the button as it is.
ImagePosition As NativeButton.ImagePositions
Where the icon sits, settable in the Inspector. Leading by default.
ImageHugsTitle As Boolean
Icon next to the title, the pair centred. True by default.
Placeable controls

NativeIconButtonControl

class

Hosted: lodges a NativeButton — an ICON button you drop in the IDE. The only one of the button controls that hosts rather than inherits, and not as a matter of taste: on an inherited DesktopButton, Xojo WIPES the image.

Properties

Caption As String
The title, as on a Xojo button.
SymbolName As String
The NAME of an SF Symbol — square.and.arrow.up. Empty removes the icon; an unknown name leaves the button as it is. A file image or a Picture go through SetImage and SetPicture.
ImagePosition As NativeButton.ImagePositions
Where the icon sits. Leading by default.Above and Below need ANOTHER BEZEL: with Push, the title is drawn outside the frame — measured from 32 to 60 points tall. FlexiblePush and SmallSquare contain both.
ImageHugsTitle As Boolean
Icon next to the title, the pair centred. True by default. False pins the icon against the frame edge.
BezelStyle As NativeButton.BezelStyles
The frame drawing, down to the help circle and the capsule.
ControlSize As NativeButton.ControlSizes
The system control size. The button is re-centred on every change.

Methods

Function Inner() As NativeButton
The hosted object, for everything the Inspector does not expose.
Sub SetImage(item As FolderItem) · Sub SetPicture(source As Picture)
An image from disk or a Xojo Picture, instead of the symbol.

Events

Event Pressed()
Placeable controls

NativeCheckBoxControl

class

Inherits DesktopCheckBoxNSButton. A native checkbox, with AppKit's real third state.

Properties

ControlSize As NativeButton.ControlSizes
AppKit control size. The font is reapplied by hand: DesktopButton already sets an explicit one before Opening, which prevents the automatic size adjustment.
MixedState As Boolean
Indeterminate state (allowsMixedState + state = -1): neither on nor off. Not to be confused with the Inspector's VisualState, which only drives the editor preview — Value overrides it at runtime.
Placeable controls

NativeRadioButtonControl

class

Inherits DesktopRadioButtonNSButton. A native radio button with a settable control size.

Properties

ControlSize As NativeButton.ControlSizes
AppKit control size. The font is reapplied by hand: DesktopButton already sets an explicit one before Opening, which prevents the automatic size adjustment.
Placeable controls

NativeTextFieldControl

class

Inherits DesktopTextFieldNSTextField. A native text field carrying the content-type hint and the Writing Tools flags.

Properties

Sub ReapplyNativeSettings()
Call after any change to Password. Toggling Password makes Xojo DESTROY the view and CREATE another, and Opening is not raised on the new one: everything this class had set disappears with no error and no warning.Measured on a live object — the Handle pointer changes on every toggle, three toggles giving three distinct views; contentType returned one-time-code before and none after. Xojo offers no way to intercept a write to an inherited property, so the re-apply cannot be automatic.And the call must be DEFERRED — Timer.CallLater, not an immediate call: asking for the Handle before Xojo has finished installing the replacement view forces its creation OUTSIDE its panel, and the field ends up placed at window level.
EchosBullets As Boolean
The bullets of a password field. A CELL property, which NSSecureTextField does not forward, and which exists only while Password is True — otherwise the cell is a plain XOJTextFieldCell that does not answer the selector.
ContentType As NativeTextField.ContentTypes
NSTextContentType semantic hint: what the field EXPECTS. It is what lets the system offer a code received by SMS. The lookup table is shared with NativeTextField.SymbolFor, not duplicated.
AllowsWritingTools As Boolean
macOS 15.2.
AllowsWritingToolsAffordance As Boolean
macOS 15.4. Only applies when AllowsWritingTools is true.
Placeable controls

NativeTextAreaControl

class

Inherits DesktopTextAreaNSTextView. A native text area. Note: DesktopTextArea is composite — an NSScrollView hosting the real NSTextView — and nothing guarantees which of the two Handle returns. The class finds out by selector instead of assuming.

Properties

ContinuousSpellChecking As Boolean
Spell checking as you type.
AutomaticLinkDetection As Boolean
Only fires on text TYPED afterwards, not on text already present.
UsesFindBar As Boolean
TextEdit's find bar (Cmd-F), with incremental searching.
Placeable controls

NativePopupMenuControl

class

Inherits DesktopPopupMenuNSPopUpButton. A native pop-up menu, with the two macOS 15 NSPopUpButton settings.

Properties

AltersStateOfSelectedItem As Boolean
Ticks the selected item. Ignored on a pull-down button.
UsesItemFromMenu As Boolean
Only applies to a PULL-DOWN button — DesktopPopupMenu stays pop-up style. Reapplies synchronizeTitleAndSelectedItem, without which unticking then re-ticking leaves the button silent.
Placeable controls

NativeComboBoxControl

class

Inherits DesktopComboBoxNSComboBox. A native combo box. NSComboBox.completes is deliberately not wrapped: Xojo already exposes it as AllowAutoComplete.

Properties

VisibleItems As Integer
Rows shown before scrolling. Zero = AppKit's own setting kept.
ItemHeight As Double
Row height. Zero = AppKit's own setting kept.
ButtonBordered As Boolean
Border of the arrow button.
Placeable controls

NativeSliderControl

class

Inherits DesktopSliderNSSlider. A native slider with the macOS 26 neutral value.

Properties

NeutralValue As Double
macOS 26. The fill starts from this value rather than from the minimum — a centred equaliser, for instance.
Placeable controls

NativeGroupBoxControl

class

Inherits DesktopGroupBoxNSBox. A native group box whose title can finally change size.

Properties

TitleFontSize As Double
DesktopGroupBox applies Bold/FontSize to its CONTENT, never to its own title — which stayed at the system size regardless.
Placeable controls

NativeImageViewerControl

class

Inherits DesktopImageViewerNSImageView. A native image viewer, in template mode.

Properties

Template As Boolean
NSImage.isTemplate: automatic recolouring by the system, black on light and white on dark. The setting belongs to the IMAGE: reassigning Image afterwards builds a new NSImage and loses the flag.
Placeable controls

NativeProgressBarControl

class

Inherits DesktopProgressBarNSProgressIndicator. A native progress bar, with the animation start and stop Xojo does not expose.

Methods

Sub ReapplyNativeSettings()
Call after any change to Indeterminate. The toggle makes Xojo DESTROY the view and create another, and Opening is not raised on the new one: ControlSize and DisplayedWhenStopped are lost with no error.All fifteen placeable controls went through the same test, with Password on a text field as the positive control. Enabled, Visible and Width recreate the view on none of them; Indeterminate and Password are the only two cases found.
Sub StartAnimation()
Without a way to stop, DisplayedWhenStopped would never get a chance to matter.
Sub StopAnimation()

Properties

ControlSize As NativeButton.ControlSizes
A “small” bar is noticeably thinner than the regular one.
DisplayedWhenStopped As Boolean
When false, the indicator vanishes as soon as it stops animating.
Placeable controls

NativeProgressWheelControl

class

Inherits DesktopProgressWheelNSProgressIndicator. A native spinner. DisplayedWhenStopped matters most here: a wheel left on screen when stopped is a visual artefact, not information.

Methods

Sub StartAnimation()
Sub StopAnimation()

Properties

ControlSize As NativeButton.ControlSizes
DisplayedWhenStopped As Boolean
When false, the wheel disappears at rest.
Placeable controls

NativeTabPanelControl

class

The real NSTabView sleeping under DesktopTabPanel. Checked at runtime, not assumed: the view Xojo places is of class XOJTabView, lineage XOJTabViewNSTabViewNSView. The main gain is one word: Xojo can only put the tabs at the top, AppKit also puts them left, bottom, right — or nowhere.

Properties

TabPosition As TabPositions
macOS 10.12. None, Top, Left, Bottom, Right. The one thing Xojo cannot do.
BorderType As BorderTypes
Respected only when TabPosition is None — the header is categorical, and measuring all fifteen combinations goes further: the property always stores the value it was given and reads back faithfully, but tabViewType, the one that drives the drawing, stays …Bezel as soon as the position is not None. The three borders only differ at position None, where tabViewType takes NoTabsNoBorder, NoTabsLine or NoTabsBezel.Reading the property back therefore does NOT reveal that the setting is inert. A UI exposing BorderType should grey it out away from that position.
ControlSize As NativeButton.ControlSizes
Subsumes Xojo's SmallTabs, which has only two states: Small does the same, with three more sizes.
AllowsTruncatedLabels As Boolean · DrawsBackground As Boolean
The second applies only to a borderless tab view — “only relevant for borderless tab view type”.

Methods

Sub SelectNext() · SelectPrevious() · SelectFirst() · SelectLast()
AppKit's four navigation actions, which Xojo does not expose: otherwise you compute the next index and handle the bounds yourself.
Function ContentRect() As Cocoa.NSRect · Function MinimumSize() As Cocoa.NSSize
The area actually available for a page, and the size below which the tabs no longer fit. AppKit coordinates, origin at the bottom.

Xojo places the children, AppKit moves the tabs. Setting TabPosition to a side changes the NSTabView's content area, but the controls placed inside are laid out by Xojo, at coordinates computed for tabs AT THE TOP — it does not reposition them. On a page fitted to the pixel, everything shifts. ContentRect returns the real area so you can place things yourself.

What Xojo already covers, and is therefore not wrapped: the label font, through FontName, FontSize, Bold, Italic and Underline. Nor the legacy tabViewType: it combines position and border, and the header itself recommends the two separate properties.

A detail that costs one compile: the IDE writes Value into the Begin block — the initially selected tab — but DesktopTabPanel does not expose it at runtime. The member is called SelectedPanelIndex. A serialised property is therefore not necessarily readable from code.

Placeable controls

NativeScrollBarControl

class

The real NSScroller under DesktopScrollbarXOJScrollerNSScrollerNSControl, checked at runtime. Legacy or overlay bar, light or dark knob, reduced size: four settings AppKit exposes and Xojo passes over in silence.

Properties

ScrollerStyle As ScrollerStyles
Legacy always takes up its space; Overlay sits on top of the content and fades when idle. Setting it deliberately contradicts the person's global preference, which PreferredScrollerStyle lets you read first.
KnobStyle As KnobStyles
Dark and Light apply ONLY to an overlay bar: a legacy one takes the system colours. It is the setting that makes a bar readable over a dark background painted by hand. Measured by rendering the bar into a bitmap and comparing the bytes — reading the property back is always faithful and tells you nothing: with the legacy style all three values produce a pixel-identical image.On an overlay bar Dark is also identical to Default: the enum has three values for two renderings. A UI exposing KnobStyle should grey it out with the legacy style.
ControlSize As NativeButton.ControlSizes
Since NSScroller is an NSControl, it takes the system sizes: a mini bar is noticeably thinner.
KnobProportion As Double
The share of the track the knob occupies, from 0 to 1. Xojo writes this property too — that is how it sizes the knob from PageStep: a value set by hand holds only until the next refresh.

Shared methods

Shared Function WidthForControlSize(size As NativeButton.ControlSizes, style As ScrollerStyles) As Double
The width AppKit would give such a bar. Use it to size the anchor rather than hard-coding 16: the value changes with the style.
Shared Function PreferredScrollerStyle() As ScrollerStyles · OverlayScrollersSupported() As Boolean
The person's choice in System Settings, and the question the header asks: are overlay scrollers possible here? To adapt an interface, not to guard a call.

No PanelIndex on this type: DesktopImageViewer, DesktopProgressBar and DesktopProgressWheel remain the only ones that write it. Read off the ViewBehavior an IDE wrote for an existing subclass of this very control, not deduced.

Placeable controls

NativeLabelControl

class

The real NSTextField under DesktopLabelXOJStaticTextNSTextFieldNSControl, read with object_getClass on a live label. Xojo gives you its text, font and colour; it gives you nothing of what decides HOW the text is cut when it does not fit.

Properties

LineBreakMode As LineBreakModes
The most glaring gap. Xojo lets the text be cut at the end and nothing else; AppKit truncates at the head (…wxyz), the middle (ab…yz), the tail (abcd…), clips without an ellipsis, or wraps by word or by character. macOS 10.10, on NSControl, which forwards to its cell.
MaximumNumberOfLines As Integer
macOS 10.11. Zero means no limit, and is the default.A trap written into the header: past that count the text is CLIPPED, and only truncated with an ellipsis if TruncatesLastVisibleLine is set. Its value also changes what FittingSize returns.
TruncatesLastVisibleLine As Boolean
A CELL property, which NSTextField does not forward. Honoured only under WordWrapping or CharWrapping — the header is categorical. Set under any other mode it does nothing, and still reads back faithfully.Same mechanism as the tab border and the knob style: a UI exposing it should grey it out away from the two wrapping modes.
AllowsDefaultTighteningForTruncation As Boolean
macOS 10.11. Tightens inter-character spacing slightly before giving up and truncating — which spares you an on a word that overflowed by two points.
AllowsExpansionToolTips As Boolean
macOS 10.8. The bubble that shows the full text on hover when it is cut, the one the Finder puts on an over-long file name. The system only builds it when the text is actually truncated.
Bordered As Boolean
A one-point frame around the label. Xojo only has Transparent, which acts on the background alone.
DrawsBackground As Boolean
Governs the background. BackgroundColor paints nothing without it.These notes first assumed an overlap with Xojo's Transparent. That is wrong, and measured: on a fresh label, Transparent is False while drawsBackground is False too — if either governed the other, a non-transparent label would paint its background. The two flags are independent.
BackgroundColor As Color
The background colour, Xojo → Cocoa conversion included: in Xojo, Alpha is 0 for opaque and 255 for transparent, the reverse of the Cocoa convention.

Methods

Sub SetViewOrigin(x As Double, y As Double)
Puts the frame origin back, in AppKit coordinates — so y counts from the BOTTOM of the parent view. Writing Width at runtime on a DesktopPagePanel child moves the view, and Xojo does not notice: it keeps reporting the original Left and Top.Measured: before, Xojo 230, 100 · 460 × 90 and AppKit 28, 589 · 464 × 90, agreeing to within the inset; after Width = 700, Xojo still says 230, 100 while AppKit moved to 3, 641. The remedy is three steps: read ViewFrame, write the size, put SetViewOrigin back.
Function ViewFrame() As Cocoa.NSRect
The view's REAL frame, to compare against the Left, Top, Width and Height Xojo believes it set. If the two diverge, something moved the view behind one of their backs, and no screenshot will say so for you.y counts from the bottom in AppKit and from the top in Xojo: a difference in y is not necessarily an anomaly, whereas one in x or in the width is.
Function FittingSize() As Cocoa.NSSize
The size it would take to show everything, WITHOUT touching the frame.sizeToFit is deliberately not exposed: it resizes the view behind Xojo's back, and Xojo keeps its own Width and Height and restores them at the next refresh. Measure, then write Me.Width.
Function SingleLineMode() As Boolean
Read-only diagnostic: the real value of usesSingleLineMode, the AppKit counterpart of what Xojo calls Multiline. Reading it says which of the two governs, instead of assuming a correspondence.
Function ReadsDrawsBackground() As Boolean
Same use for drawsBackground against Xojo's Transparent.
Placeable controls

NativeTableViewControl

class

A NativeTableView you drag from the IDE's tool bar, like any control: row height, header, multiple selection, draggable columns and the rest are set in the Inspector, and the events are implemented directly on it.

It hosts instead of inheriting, and it has no choice. Fifteen placeable controls inherit a Xojo control whose view IS already the AppKit control wanted. For a table that is impossible: the sweep of XojoFramework's 82 classes showed XOJListboxView descends from a bare NSViewDesktopListBox is not an NSTableView, Xojo draws it itself. So this control descends from DesktopCanvas and lodges a table inside. Hosting happens in Paint, the only moment the control is really on screen: asking for its Handle earlier would create its view OUTSIDE its panel, hence visible on every page. The table itself exists well before — it builds its own views with no host — which is what lets you fill it from the window's Opening.

Methods

Function Table() As NativeTableView
The table itself, for everything this control does not forward.NativeTableView has some forty public methods; forwarding them all would be as much code to maintain twice, diverging at the first addition. Forwarded are the ones used on every table — columns, rows, cells, selection — and the rest goes through Table, with no detour and no loss.
Sub AddColumn(…)
Sub AddRow(ParamArray cells() As String)
Sub SetCell(…)
Function CellAt(…) As String
Sub RemoveAllRows()
Sub Reload()
Function RowCount() As Integer
Function ColumnCount() As Integer
Sub SelectRow(row As Integer)
SelectedRow As Integer
What is forwarded. AddRow goes through AddRowArray: a ParamArray does not carry from one method to another, and this control does not inherit the table — it hosts it.
Event SelectionChanged · CellChanged · RowDoubleClicked · SortChanged · RowsMoved
The table's events, re-raised by the control: whoever drops it implements them without knowing there is a table inside.
Placeable controls

NativeOutlineViewControl

class

A NativeOutlineView you drag from the IDE's tool bar. It inherits NativeTableViewControl; MakeTable lodges the outline and also re-raises its node events.

An outline is a table that indents

Everything separating it from its parent fits in MakeTable: it lodges a NativeOutlineView instead of a NativeTableView, and re-raises its three node events. The rest is inherited: the deferred hosting in Paint, the Inspector properties, the table's events. A node is a NativeOutlineNode, no longer a row number: inserting, removing and moving animate.

Methods

Function Tree() As NativeOutlineView
The same instance as Table, but typed: whatever is not forwarded goes through it — Tree.SetNodeIcon, Tree.NodeCell, Tree.ExpandNode.
Function AddNode(parent As NativeOutlineNode, ParamArray cells() As String) As NativeOutlineNode
Function InsertNode(parent, index, cells(), animation = SlideDown) As NativeOutlineNode
Function AddNodeAnimated(parent, cells(), animation = FadeSlideDown) As NativeOutlineNode
Sub RemoveNode(node, animation = SlideUp)
Function MoveNode(node, newParent, index) As Boolean
Function AddFolder(parent, item As FolderItem, depth As Integer = 3) As NativeOutlineNode
Function ParentOf(node) · ChildCount(parent) · ChildAt(parent, index) · IndexOf(node)
Function SelectedNode() · Sub SelectNode(node) · Sub ExpandAll() · Sub CollapseAll()
What is forwarded: building, removing, moving, walking, selecting. Nil as the parent means the root.
Event NodeSelectionChanged · NodeCellChanged · NodeDoubleClicked · NodesMoved
Re-raised from the outline, with the node.The table's events — SelectionChanged, CellChanged, RowDoubleClicked — coexist and give a storage row. Prefer these: a row changes as soon as something is inserted or moved.
Placeable controls

NativeSwitchControl

class

An NSSwitch you drag from the IDE's tool bar. Hosted like the table: it descends from DesktopCanvas and lodges a NativeSwitch.

Mounted with Center, as in MainWindow: a switch has an intrinsic size, stretching it would make no sense. It is re-centred on every control-size change.

Methods

Function Inner() As NativeSwitch
The hosted object, for whatever is not forwarded.Named Inner in all ten hosted controls: a name taken from the class would have given SearchField or SegmentedControl, class names from Xojo's old API that would mask the type at call sites.
Value As Boolean
ControlSize As NativeButton.ControlSizes
Set in the Inspector.Setting Value from code does not raise ValueChanged: AppKit sends its action only on the user's gesture.
Event ValueChanged(value As Boolean)
The hosted class's Changed, under Xojo's API 2 name.
Placeable controls

NativeStepperControl

class

An NSStepper — the two little arrows — you drop in the IDE. What DesktopUpDownArrows does not do: an adjustable increment, wrapping past the bounds, repeat while the click is held.

Mounted with Center. The bounds and increment change after construction thanks to NativeStepper.SetRange, added for this control: the hosted class set them only on creation.

Methods

Function Inner() As NativeStepper
The hosted object.
Value · MinimumValue · MaximumValue · Increment As Double
Autorepeat · ValueWraps As Boolean
ControlSize As NativeButton.ControlSizes
Set in the Inspector. ValueWraps makes the value start again from the minimum past the maximum.
Event ValueChanged(value As Double)
The hosted class's Changed.
Placeable controls

NativeSegmentedButtonControl

class

An NSSegmentedControl you drop in the IDE. Its name is Xojo's, DesktopSegmentedButton: NativeSegmentedControlControl would read badly.

Mounted with Fill, not with Center as in MainWindow. AppKit's header says the distribution spreads the available space: Fill, the default, stretches the segments to fill it, Fit leaves the rest empty. Centred at its ideal size, the control would have no space to distribute; mounted with Fill, the width given in the IDE is kept.

Methods

Function Inner() As NativeSegmentedControl
The hosted object.
Labels As String
The labels in a single field, separated by semicolons: “Day;Week;Month”.Changeable after construction thanks to NativeSegmentedControl.SetLabels, added for this control.
SelectedIndex As Integer
Style As NativeSegmentedControl.Styles
TrackingMode As NativeSegmentedControl.TrackingModes
Distribution As NativeSegmentedControl.Distributions
BorderShape As NativeControlHost.BorderShapes
ControlSize As NativeButton.ControlSizes
Set in the Inspector.BorderShape acts only from macOS 26 on.
Function SegmentCount() As Integer
Function IsSelected(index As Integer) As Boolean
Sub SetSelected(index As Integer, selected As Boolean)
Sub SetSymbol(index As Integer, symbolName As String, accessibilityDescription As String = "")
Sub SetWidth(index As Integer, width As Double)
Forwarded. In SelectAny tracking, several segments can be selected at once.
Event SelectionChanged(index As Integer)
The hosted class's Changed.
Placeable controls

NativeLevelIndicatorControl

class

An NSLevelIndicator you drop in the IDE: continuous or discrete capacity gauge, relevancy indicator, or rating stars.

Mounted with Fill: the track follows the control's width. Style and bounds change after construction thanks to NativeLevelIndicator.SetStyle and SetRange, added for this control. SetRange pushes the colour-band thresholds past the new maximum: otherwise a raised maximum would bring them back into the track.

Methods

Function Inner() As NativeLevelIndicator
The hosted object.
Style As NativeLevelIndicator.Styles
Value · MinimumValue · MaximumValue As Double
Editable As Boolean
Set in the Inspector. Editable, the indicator is set by clicking: that is what makes a Rating a rating.
WarningValue · CriticalValue As Double
InvertedThresholds As Boolean
TickMarks · MajorTickMarks As Integer
Two zero thresholds give AppKit's default look. InvertedThresholds puts the warning at the bottom: a battery rather than a disk.
Event ValueChanged(value As Double)
The hosted class's Changed.
Placeable controls

NativeSearchFieldControl

class

An NSSearchField you drop in the IDE: magnifier, clear button, recent-searches menu.

LiveSearch does not take AppKit's default. The header describes three behaviours: by default, an action on each keystroke “after sufficient amount of time so we don't interfere with typing”; with sendsWholeSearchString, only on Return or a click on the magnifier. This control offers two clear choices: immediate sending, or validation. Mounted with Fill.

Methods

Function Inner() As NativeSearchField
The hosted object.
Text · Placeholder As String
LiveSearch As Boolean
MaximumRecents As Integer
Set in the Inspector.MaximumRecents at zero removes the menu template — and the header says so, without a template recent searches are no longer tracked. The hosted class could set it, not remove it.
Function RecentSearches() As String()
Forwarded. SetPlaceholders is not: a ParamArray does not carry from one method to another; it stays reachable through Inner.
Event TextChanged(text As String)
The hosted class's Changed, under Xojo's API 2 name.
Placeable controls

NativeColorWellControl

class

An NSColorWell you drop in the IDE. Since macOS 13, three presentations: Standard opens the system colour panel, Minimal and Expanded a popover picker.

The value is an sRGB snapshot: a Xojo Color is three bytes and carries neither colour space nor exposure. Mounted with Fill, as in MainWindow.

Methods

Function Inner() As NativeColorWell
The hosted object.
Value As Color
Style As NativeColorWell.Styles
Set in the Inspector.
MaximumLinearExposure As Double
macOS 26. AppKit's header: any value under 1 is ignored, and from 2 on the picked colour may carry a linear exposure.
Event ValueChanged(value As Color)
The hosted class's Changed.
Placeable controls

NativeDatePickerControl

class

An NSDatePicker you drop in the IDE. What DesktopDateTimePicker does not do: the calendar with its clock, range selection, fine choice of the displayed elements.

Mounted with Center, and re-centred on every style or element change: the calendar and the text field do not have the same size. MainWindow mounted the calendar with Fill; Center keeps it at its own size, without guessing the control's height.

Methods

Function Inner() As NativeDatePicker
The hosted object.
Style As NativeDatePicker.Styles
Mode As NativeDatePicker.Modes
DateElements As NativeDatePicker.DateElements
TimeElements As NativeDatePicker.TimeElements
Bezeled · Bordered As Boolean
Set in the Inspector.
Value As DateTime
Duration As Double
Sub SetRange(minimum As DateTime, maximum As DateTime)
From code only: a DateTime cannot be set in the Inspector, and the control starts from today's date.Duration is the range length in seconds, in DateRange mode.
Event ValueChanged(value As DateTime, duration As Double)
The hosted class's Changed.
Placeable controls

NativeTokenFieldControl

class

An NSTokenField — the token field of Mail's “To:” — dropped in the IDE. Hosted: no Xojo class is backed by NSTokenField.

Two lists in the Inspector, separated by semicolons: TokenList, the initial tokens, and Completions, the list completion draws on. That semicolon is an entry separator, not the tokenizing character, which is set on its own. Standard equals Rounded: AppKit's header says so of NSTokenStyleDefault, and adds that this may change. Mounted with Fill: a two-line height lets the tokens wrap.

Methods

Function Inner() As NativeTokenField
The hosted object.
TokenList · Placeholder · Completions As String
Style As NativeTokenField.Styles
TokenizingCharacter As String
Set in the Inspector.Read back, TokenList returns the committed tokens, joined by semicolons. An empty TokenizingCharacter keeps AppKit's comma.
Function Tokens() As String()
Sub SetTokens(values() As String)
The tokens as they are. The text being typed is not part of them yet: it becomes a token on the tokenizing character, on Return or when focus is lost.An array and not a ParamArray, which would not pass through to the hosted class. A token containing a semicolon can only go through here.
Event TokensChanged(tokens() As String)
The hosted class's Changed.
Placeable controls

NativePathBarControl

class

An NSPathControl — the Finder's path bar — dropped in the IDE. Hosted: no Xojo class is backed by it. NativePathControlControl would read badly, and Xojo has no control whose name to borrow.

Clicking does not navigate. PathSelected reports the clicked component, read while the action is sent — clickedPathItem is valid only then —, and the displayed path does not change: setting it is up to the caller. A dropped file, or one chosen in the PopUp style's panel, raises PathSelected too; the header states that clickedPathItem is then nil, and the hosted class falls back on the control's URL.

Methods

Function Inner() As NativePathControl
The hosted object.
Path · Placeholder · AllowedTypes As String
Style As NativePathControl.Styles
Editable As Boolean
Set in the Inspector.Two styles: NavigationBar, value 1, has been deprecated since macOS 10.7. AppKit makes the bar editable by default; this control starts from False. And for the header, an empty type list allows nothing — nil allows everything, and that is what an empty field sets.
Value As FolderItem
From code only: a FolderItem cannot be set in the Inspector.
Event PathSelected(item As FolderItem, path As String)
Event PathDoubleClicked(item As FolderItem, path As String)
The hosted class's two events.DoubleClicked is already a DesktopCanvas event, with coordinates: the bar's one therefore takes another name.
Placeable controls

NativeComboButtonControl

class

An NSComboButton (macOS 13) — a main action and a menu — dropped in the IDE. Hosted: no Xojo class is backed by it. DesktopBevelButton does carry a menu, but the probe read its view: XOJBevelButton < NSView, which Xojo draws itself.

Split or Unified. In Split, the title raises Pressed and the arrow opens the menu. In Unified, a single segment: AppKit's header says that, the action being set, a click fires it and the menu appears only on press and hold. Mounted with Center, and re-centred on every title, symbol or size change. Before macOS 13, nothing is placed, without error.

Methods

Function Inner() As NativeComboButton
The hosted object.
Caption · Items · SymbolName As String
Style As NativeComboButton.Styles
ControlSize As NativeButton.ControlSizes
Set in the Inspector.Items fits in one field, separated by semicolons; changing it rebuilds the whole menu through NativeComboButton.RemoveAllItems, added for this control. Caption and not Title: that is Xojo's API 2 name.
Function ItemCount() As Integer
The number of menu items.
Event Pressed()
Event MenuItemSelected(index As Integer, title As String)
Each menu item carries its own target; its tag is its rank, and that is what MenuItemSelected reports.

Surfaces

What is drawn behind the content, over the window, or beside it.

Surfaces

NativePopover

class

NSPopover: the bubble anchored to a control, with its arrow. Its content view is flippedy counts from the top, as everywhere else in the library.

Constructor

Sub Constructor(width As Double = 260, height As Double = 160)

A popover REQUIRES an NSViewController. There is no need to subclass one: a bare NSViewController given a view with setView: is enough, which stops loadView being called.

Methods

Sub AddView(view As Ptr, x As Double, y As Double, width As Double, height As Double)
y counts from the TOP: the content view is flipped on purpose.
Sub Close()
Function ContentView() As Ptr
The view in which to house the content. Flipped: y from the top.
Function Handle() As Ptr
Sub SetContentSize(width As Double, height As Double)
Resizes the bubble AND its content view: both must follow, otherwise the content ends up clipped or floating.
Sub SetFullSizeContent(fullSize As Boolean)
macOS 14+: the content also occupies the arrow's area. Without effect below.
Sub Show(anchor As DesktopUIControl, edge As Edges = Edges.MinY)
Anchors the bubble to a Xojo control: its view serves as the positioning view, its bounds as the anchoring rectangle.

Properties

Animates As Boolean
Behavior As Behaviors
IsShown As Boolean read-only

Events

  • Event DidClose()
  • Event DidShow()
  • Event WillClose()

Enumerations

BehaviorsApplicationDefined=0Transient=1Semitransient=2
EdgesMinX=0MinY=1MaxX=2MaxY=3
Surfaces

NativeAlert

class

NSAlert. Two things MessageDialog cannot do: house a view of your own in the alert, and offer “Do not ask me again”.

Constructor

Sub Constructor(messageText As String, informativeText As String = "")

Methods

Function Handle() As Ptr
Function AddButton(title As String) As Integer
Returns the index of the button, 0 for the first one added. CAREFUL: it is the order of ADDITION, not the order on screen — the first one added is the default button, hence the RIGHTMOST one.With no call at all, NSAlert sets a single “OK” button.
Function RunModal() As Integer
Returns the 0-based index of the button, in order of addition; -1 if the alert could not be built.An alert MODAL to the application, and therefore blocking: the call returns only on the click. For the form attached to a window, see RunSheet.
Sub RunSheet(parent As DesktopWindow)
Presented as a SHEET, attached to the window — the correct form on macOS for a question that concerns a document.It is asynchronous: RunSheet returns immediately and the Completed event arrives later. Two consequences, and both are traps: 1. the block AND its delegate are kept in properties, never in locals; 2. the NativeAlert instance must survive until the callback — so a property of the window, never a Var in the click handler.
Sub SetAccessoryView(view As Ptr, width As Double = 0, height As Double = 0)
Any NSView at all — hence the Handle of any control. If a size is supplied, it is applied first: NSAlert sizes its panel from the view's frame, without renegotiating it.
Sub SetCancelButton(index As Integer)
Esc triggers this button. AppKit already does it on its own for a button titled “Annuler” or “Cancel”; to be specified as soon as the label leaves those two words.
Sub SetDefaultButton(index As Integer)
Moves the Return key. Indispensable with a destructive button: Apple's rules want the irreversible action NEVER to be the one triggered by reflex, yet AddButton makes the first one added the default button. So the keyboard equivalent is taken away from all the others.
Sub SetDestructiveButton(index As Integer, destructive As Boolean = True)
NSAlert itself has no notion of “destructive”: it is its BUTTONS that have one, since buttons is an array of NSButton. That is how the Finder gets its red “Delete”.hasDestructiveAction dates from macOS 11; below that, the call is without effect and the button stays grey.
Sub SetHelp(shown As Boolean, anchor As String = "")
Sub SetSuppression(shown As Boolean, title As String = "")
The “Do not ask me again” box. It is up to the application to remember the choice: AppKit only displays it and reports its state.
Sub SetSymbol(symbolName As String, accessibilityDescription As String = "")
Replaces the application icon with an SF symbol. Passing "" restores it.

Properties

InformativeText As String
MessageText As String
Style As Styles
Suppressed As Boolean read-only
Valid only after RunModal.

Events

  • Event Completed(buttonIndex As Integer)
  • Event HelpRequested() As Boolean

Delegate

  • Delegate Sub SheetHandler(response As Integer)

Enumerations

StylesWarning=0Informational=1Critical=2
Surfaces

NativeGlassEffectView

class

NSGlassEffectView (macOS 26): Liquid Glass. The header guarantees only the contentView — arbitrary subviews have no defined z-order with respect to the effect.

Constructor

Sub Constructor(width As Double = 200, height As Double = 120, style As Styles = Styles.Regular)

Handle returns Nil below macOS 26; it is up to the caller to fall back on something else.

Methods

Function Available() As Boolean
Shared Function InteractiveSupported() As Boolean
To ADAPT the interface: Interactive is already inert below macOS 27, where the view exists without knowing this setting.
Function Handle() As Ptr
Sub SetContentView(view As Ptr)
Sub SetTint(useTint As Boolean, tint As Color = &c000000)
Passing False puts tintColor back to Nil: the glass returns to its neutral tint.

Properties

CornerRadius As Double
Interactive As Boolean
macOS 27, effectIsInteractive. The header: to be enabled for glass used as the background or container of controls — it then responds to hover and click. Default NO: glass carrying buttons stays static until asked.
Style As Styles

Enumerations

StylesRegular=0Clear=1
Surfaces

NativeGlassEffectContainerView

class

NSGlassEffectContainerView (macOS 26) — a class distinct from NativeGlassEffectView: it does not draw a pane but groups several. The header states three effects: it raises the z-order of contentView's descendants, merges those similar and close enough, and batches them for performance.

Constructor

Sub Constructor(width As Double = 200, height As Double = 120)

Handle returns Nil below macOS 26; the caller must fall back to something else.

Methods

Function Available() As Boolean
Function Handle() As Ptr
Sub SetContentView(view As Ptr)
The panes to merge go as DESCENDANTS of this view, not directly in the container.

Properties

Spacing As Double
The proximity at which panes begin merging. Zero by default: batching, with no visible merge and no distortion of merely neighbouring views.
Surfaces

NativeBackgroundExtensionView

class

macOS 26. It extends its content out to its own bounds. The intended use is precise: a view that overflows the safe area, under the title bar, the sidebar or the inspector. The content itself stays inside the safe area — it is its edges that the system stretches to fill the rest. Available() queries the class, not the version number; Handle returns Nil before macOS 26.

Constructor

Sub Constructor(width As Double = 400, height As Double = 240)

The intended use is precise — placing a view that overflows the safe area, under the title bar, the sidebar or the inspector. The content itself stays INSIDE the safe area; it is its edges that the system stretches and blurs to fill the rest. So you get an image that seems to pass under the toolbar without really being clipped by it.

Handle returns Nil before macOS 26: it is up to the caller to fall back on something else, typically the content view set on its own.

Methods

Function Available() As Boolean
True only if the class REALLY exists in the system in use. The class is queried, not the version number: a selector that is present is a proof, a version number a guess.
Function Handle() As Ptr
Sub SetContentView(view As Ptr)
The view to extend. It becomes a subview of the extension and, by default, is placed in the safe area — see AutomaticPlacement.
Function ContentView() As Ptr

Properties

AutomaticPlacement As Boolean
At True — the default —, the system itself places the content view in the safe area. At False, it is up to the caller to set the frame or the constraints, and the extension effect fills what remains around it.
Surfaces

NativeMenu

class

NSMenu with everything macOS 14 added to it: section headers, SF symbols, subtitles (14.4), typed badges — Updates, NewItems, Alerts carry a label localised by the system, where the plain badge shows only the number — selection modes, and the palette menus, the row of coloured swatches of the Finder tags.

Constructor

Sub Constructor(title As String = "")

Methods

Function AddItem(title As String, symbolName As String = "", subtitle As String = "") As Integer
Returns the index of the item, which serves as the tag and comes back in the Chosen event.
Function AddSectionHeader(title As String) As Integer
macOS 14: a real section header, not selectable and drawn as such. Below that, we fall back on a disabled item — not identical, but readable, and with no test for the caller to write.
Sub AddSeparator()
Function AddSubmenu(title As String, submenu As NativeMenu, symbolName As String = "") As Integer
The submenu is RETAINED by the item; keeping the Xojo object alive remains the caller's business, otherwise its destructor will release the NSMenu under the item.
Function AddColorPalette(title As String, colors() As Color, titles() As String, checkSize As Double = 10) As Integer
macOS 14: the row of coloured swatches, like the Finder tags.Two constraints the header states and that cannot be worked around: a palette menu must be the SUBMENU of an item of an ordinary menu, and it can be neither pulled down by PopUp nor attached to a pop-up menu. Hence the signature: you give the title of the carrying item, not the menu itself. The factory's selection handler is optional: you pass Nil and read SelectedIndexes on the submenu — which spares you having to build an Objective-C block.
Function PaletteSelection(index As Integer) As Integer()
The indexes selected in the palette carried by the given item.
Function Handle() As Ptr
Function PopUp(anchor As DesktopUIControl, dx As Double = 0, dy As Double = 0) As Boolean
Pulls the menu down under a Xojo control and returns True if an item was chosen. The call is SYNCHRONOUS: it returns only when the menu closes, which allows the selection to be read just afterwards.AppKit coordinates, origin at the BOTTOM: (0,0) is the bottom-left corner of the anchor, so the menu opens just below it.
Sub RemoveAll()
Function SelectedIndexes() As Integer()
macOS 14, and only in SelectOne or SelectAny selection mode: the ticked items of one and the same selection group.
Sub SetBadge(index As Integer, count As Integer, kind As Badges = Badges.Plain)
macOS 14. The three named types carry a label localised by the system — “3 updates”, “2 new items” — where Plain shows only the number. Without effect below 14.
Sub SetBadgeText(index As Integer, text As String)
Sub SetEnabled(index As Integer, enabled As Boolean)
Sub SetIndent(index As Integer, level As Integer)
Sub SetImageVisibility(index As Integer, visibility As ImageVisibilities)
The same for ONE item.
Shared Function ImageVisibilitySupported() As Boolean
To ADAPT the interface: on an earlier system, images show anyway.
Sub SetState(index As Integer, state As Integer)
NSControlStateValue: Off = 0, On = 1, Mixed = -1.
Sub SetSymbol(index As Integer, symbolName As String)
Sub AttachTo(control As DesktopUIControl) · AttachTo(view As Ptr)
Makes this menu the CONTEXT menu. The overload on a view is needed as soon as a view is PLACED in the anchoring control: the right-click reaches the view on top, not the Xojo control's own.
Shared Sub ShowForSelection(control As DesktopUIControl) · ShowForSelection(view As Ptr)
macOS 15. Shows the view's context menu where the selection is — what the system key does, Ctrl-Return by default. Nothing has to be written for that key to work: the header is categorical, “Most applications should not override this method”. The system walks the responder chain up to NSApplication, which sends showContextMenuForSelection:, and NSView's default implementation shows the menu returned by menuForEvent: — hence the one AttachTo set. This method only triggers the same thing programmatically.Placement: NSView uses selectionAnchorRect. A view that does not implement it — which is the case for the views created here — sees the menu appear at the CENTRE of its bounds; AppKit's text views put it on the selection.
Shared Function ContextMenuKeySupported() As Boolean
Only useful to hide a button where it would do nothing: the key itself needs no test.
Sub AddWritingToolsItems()
macOS 15.2. “Each call returns an array of newly allocated instances”: fresh items, safe to place without sharing them — unlike the palette checkmark image.
Sub AutomaticallyInsertsWritingToolsItems(yes As Boolean)
macOS 15.2. Applies ONLY when the menu is used as a context menu — “if used as a context menu”. A menu pulled down by PopUp is not one.

Properties

Count As Integer read-only
PresentationStyle As Styles
macOS 14. In Palette, the menu presents itself as a compact row — but it can then NOT be pulled down by PopUp nor attached to a pop-up menu: it must be the submenu of an item of an ordinary menu.
SelectionMode As SelectionModes
macOS 14. Acts only on the items of one and the same selection GROUP — that is to say, with no separator or header between them.
ImageVisibility As ImageVisibilities
macOS 27. NSMenuItem's header: from macOS 27 on, AppKit decides the visibility of menu item images and “will typically hide images”. Visible by default in this class, not Automatic as in AppKit: a symbol passed to AddItem is an explicit request. Applies to existing and future items; Automatic hands the decision back to the system.The header warns that even Visible may be overridden “in some cases”. A palette menu's swatches are not affected: measured on macOS 27.0, their items have no image.

Events

  • Event Chosen(index As Integer, title As String)

Enumerations

BadgesPlain=0Updates=1NewItems=2Alerts=3
ImageVisibilitiesAutomatic=0Visible=1Hidden=2
SelectionModesAutomatic=0SelectOne=1SelectAny=2
StylesRegular=0Palette=1
Surfaces

NativeVisualEffectView

class

NSVisualEffectView, the system's translucent background — the one of the sidebar and the inspector, used internally from the start without being exposed. The materials are semantic: you ask for “sidebar” or “window background”, never for a colour, and the system decides according to the appearance, reduced transparency and the place in the window. Careful with Emphasized: the header warns that few materials change their look — it is on Selection that it shows.

Constructor

Sub Constructor(width As Double = 260, height As Double = 200, material As Materials = Materials.WindowBackground, blending As Blendings = Blendings.BehindWindow)

The materials are SEMANTIC: you ask for “sidebar” or “window background”, not for a colour. The system decides the rendering according to the appearance, the reduced-transparency setting and the position in the window. The old Light and Dark have been deprecated since 10.14 and are not exposed here.

Methods

Sub AddView(view As Ptr, x As Double, y As Double, width As Double, height As Double)
AppKit coordinates, origin at the BOTTOM: an NSVisualEffectView is not flipped. To count from the top, set a view from NativeControlHost.MakeFlippedContainer first and add the content to it.
Function Handle() As Ptr
Sub SetContentView(view As Ptr)
A single view, filling the whole surface and following resizes.
Sub SetCornerRadius(radius As Double)
Goes through the layer, and it is safe here: a RADIUS is not a colour. CGColors set in a layer are frozen at the moment of writing and do not follow dark mode — a geometry does.

Properties

Blending As Blendings
BehindWindow lets the desktop and the windows behind show through; WithinWindow blends only with what is behind the view WITHIN the window. The first has an effect only if the window itself is translucent.
Emphasized As Boolean
10.12+. Used to signal that an associated view has the keyboard focus. CAREFUL, the header is explicit: “Some, but not all, materials change their look when emphasized.” On most materials — Sidebar, WindowBackground, Popover… — the effect is nil or imperceptible. It is on SELECTION that it shows: the material goes from the accent tint to grey, exactly like a selected row in a list that loses the focus.
Material As Materials
State As States
FollowsWindowActiveState is the system behaviour: the material goes out when the window moves to the background.

Enumerations

BlendingsBehindWindow=0WithinWindow=1
MaterialsTitlebar=3Selection=4Menu=5Popover=6Sidebar=7HeaderView=10Sheet=11WindowBackground=12HUDWindow=13FullScreenUI=15ToolTip=17ContentBackground=18UnderWindowBackground=21UnderPageBackground=22
StatesFollowsWindowActiveState=0Active=1Inactive=2
Surfaces

NativeSceneView

class

A live 3D model — turned with the mouse, lit, animated — rendered entirely in the process: SCNView is an ordinary NSView. It is the only way to show 3D without QLPreviewView, whose out-of-process view destroys drag and drop. Show tries sceneWithURL: then goes through ModelIO, which adds STL; CanShow says in advance whether the format is SceneKit's business.

Constructor

Sub Constructor(width As Double = 420, height As Double = 170)

WHY NOT QLPreviewView, which would do the same thing in one line: it installs an NSRemoteView served by SceneKitQLPreviewExtension, and displaying a 3D model then destroys the process's drag and drop FOR GOOD — measured, nothing restores it but quitting the application. SCNView is an ordinary NSView: nothing leaves the process, apart from the parsing of the file.

WHY NOT RealityKit, which has replaced SceneKit since macOS 26: it exposes NO view class to Objective-C — neither ARView, nor RealityView, nor Entity. Verified at runtime: the framework's only ObjC classes are Swift internals with mangled names. RealityKit can only be driven from Swift, hence not from Xojo. SceneKit is deprecated but remains, to this day, the only workable route.

Methods

Function Available() As Boolean
Sub Clear()
Function Handle() As Ptr
Function Item() As FolderItem
Shared Function CanShow(item As FolderItem) As Boolean
“Handles” would have been the natural name — it is a RESERVED WORD in Xojo, the one for menu handlers. And case makes no difference.The formats SceneKit opens by itself or through ModelIO. A LIST, and that is owned: SceneKit offers no way to ask “can you read this?” without attempting the read, which would cost a full load.
Function Show(item As FolderItem) As Boolean
TWO routes, and the second catches what the first misses: sceneWithURL: knows SceneKit's native formats, ModelIO opens others — STL among them. We try the direct one, then the bridge.

Properties

CameraControl As Boolean
Lets the user turn, zoom and move the model with the mouse.
RendersContinuously As Boolean
Redraws continuously, at the screen's rate: needed for an ANIMATION contained in the file. Costly, hence optional.
Surfaces

NativeBox

class

NSBox: group frame, custom container and — under-used — a separator, horizontal or vertical according to its proportions. Its virtue comes down to one point: it paints with real NSColors, so it follows dark mode on its own, where a CGColor set in a CALayer is frozen at the moment of writing.

Constructor

Sub Constructor(width As Double = 300, height As Double = 200, kind As Kinds = Kinds.Custom)

Its virtue comes down to one point: an NSBox paints with REAL NSColors. So it follows dark mode and increased contrast on its own, where a CGColor set in a CALayer is frozen the moment it is written and is never updated. That is why NativeRichTextEditor is built in NSBox.

Methods

Sub AddView(view As Ptr, x As Double, y As Double, width As Double, height As Double)
Adds to the content. If UseFlippedContent has been called, y counts from the TOP; otherwise it is the AppKit frame, origin at the bottom.
Function ContentView() As Ptr
The view in which to house the content: the one that was set, or the one NSBox manages.
Function Handle() As Ptr
Shared Function Separator(x As Double, y As Double, width As Double, height As Double) As NativeBox
A separator: horizontal or vertical according to its proportions, and it takes the system tint without anyone naming it. A one-point-thick view painted by hand would have neither the right colour nor the right behaviour in dark mode.
Sub SetColors(fill As Color, border As Color, borderWidth As Double = 1, cornerRadius As Double = 0)
Has an effect only with the Custom type: the other types draw themselves.
Sub SetContentView(view As Ptr)
Sub SetSystemColors(fillName As String = "controlBackgroundColor", borderName As String = "separatorColor", borderWidth As Double = 1, cornerRadius As Double = 6)
The right way: SEMANTIC colours, alive, that follow the appearance. A name unknown to the system is ignored rather than painting black.
Sub SetTitle(title As String, position As TitlePositions = TitlePositions.AtTop)
Sub SetTransparent(transparent As Boolean)
Neither background nor border, but the title and the layout remain: the way to group without drawing anything.
Sub UseFlippedContent(width As Double, height As Double)
Replaces the content view with a FLIPPED view: y then counts from the top, as in the Xojo designer and as everywhere else here.

Enumerations

KindsPrimary=0Separator=2Custom=4
TitlePositionsNone=0AboveTop=1AtTop=2BelowTop=3AboveBottom=4AtBottom=5BelowBottom=6

Layout

The only two controls that do away with hard-coded coordinates: you describe, AppKit computes.

Layout

NativeGridView

class

NSGridView (10.12+): a grid in Auto Layout. You describe rows, AppKit computes positions and column widths. A hidden row or column shrinks to zero and hides its views — the clean way to make an option disappear without dismantling the grid.

Constructor

Sub Constructor(columnSpacing As Double = 12, rowSpacing As Double = 8)

Two facts verified in Objective-C before writing this: 1) built with initWithFrame:, the grid keeps translatesAutoresizingMaskIntoConstraints at YES — so it places by frame like all the rest of the library; 2) addRowWithViews: sets that same flag to NO on the views handed to it. The caller has nothing to do: the grid takes charge of its children.

Methods

Function AddRow(ParamArray views() As Ptr) As Integer
Returns the index of the row created. For an empty cell, pass EmptyCell — AppKit uses it as a marker, a Nil in the array would not do.
Function CellView(row As Integer, column As Integer) As Ptr
Shared Function EmptyCell() As Ptr
The empty-cell marker, to be passed to AddRow. It is a CLASS property of NSGridCell, not an instance to create.
Function FittingSize() As Cocoa.NSSize
The natural size computed by Auto Layout. CAREFUL: it is expressed in ALIGNMENT rectangles — the frame of an NSButton overflows its own by a few points. A grid of buttons therefore deserves a little margin.
Function Handle() As Ptr
Sub MergeCells(column As Integer, columnCount As Integer, row As Integer, rowCount As Integer)
Merges a rectangular block — a title running across the full width, for example. The cell kept is the one at the top left of the block.
Sub Refit(padding As Double = 0)
Fits the frame to the natural size. The extra compensates for the overflow of the alignment rectangles when the grid contains buttons.
Sub SetColumnHidden(column As Integer, hidden As Boolean)
A hidden column shrinks to zero AND hides its views: it is the clean way to make an option disappear without dismantling the grid.
Sub SetColumnWidth(column As Integer, width As Double)
Passing SizeForContent returns the column to automatic fitting.
Sub SetPlacement(horizontal As Placements, vertical As Placements)
The grid's DEFAULT placement. Rows, columns and cells can override it; “Inherited” on a cell refers back to the level above.
Sub SetRowAlignment(alignment As RowAlignments)
FirstBaseline aligns the baselines of a row: it is what makes a label and a field look as though they sit on the same line, where a vertical centring offsets them visibly.
Sub SetRowHeight(row As Integer, height As Double)
Sub SetRowHidden(row As Integer, hidden As Boolean)
Sub SetRowPadding(row As Integer, top As Double, bottom As Double)
The total space between two rows is the bottomPadding of the first, plus rowSpacing, plus the topPadding of the second.
Sub SetSpacing(columnSpacing As Double, rowSpacing As Double)
Shared Function SizeForContent() As Double
NSGridViewSizeForContent, the “fit yourself to the content” sentinel. It is an exported CGFloat equal to FLT_MIN: we READ it rather than copy a literal, AppKit's comparison being an exact equality.

Properties

ColumnCount As Integer read-only
RowCount As Integer read-only

Enumerations

PlacementsInherited=0None=1Leading=2Trailing=3Center=4Fill=5
RowAlignmentsInherited=0None=1FirstBaseline=2LastBaseline=3
Layout

NativeStackView

class

NSStackView: a row or a column that distributes itself, with six distribution modes and visibility priorities — it is the mechanism of the bars that thin out when room runs short.

Constructor

Sub Constructor(orientation As Orientations = Orientations.Horizontal, spacing As Double = 8)

CONSTRUCTION TRAP, verified in Objective-C: the class factory stackViewWithViews: returns a stack whose translatesAutoresizingMaskIntoConstraints is NO — impossible to place by frame, it would ignore its own. initWithFrame: leaves it at YES, and is therefore the only route compatible with the rest of the library.

Methods

Sub AddView(view As Ptr)
addArrangedSubview: — the stack takes charge of placing the view and sets its translatesAutoresizingMaskIntoConstraints to NO, as NSGridView does. Nothing to adjust on the caller's side.
Sub AddViews(ParamArray views() As Ptr)
Function FittingSize() As Cocoa.NSSize
The natural size, expressed in ALIGNMENT rectangles: the frame of an NSButton overflows its own by a few points. A stack of buttons therefore needs an extra — see Refit.
Function Handle() As Ptr
Sub InsertView(view As Ptr, index As Integer)
Sub Refit(padding As Double = 0)
Sub RemoveView(view As Ptr)
Removes the view from the layout AND from the hierarchy: removeArrangedSubview: alone would leave it a subview, hence drawn but no longer placed.
Sub SetCustomSpacing(afterView As Ptr, spacing As Double)
A particular spacing AFTER a given view — that is how groups are separated within one stack without slipping an empty view in.
Sub SetDetachesHiddenViews(detaches As Boolean)
When a hidden view is detached, the stack closes up as if it did not exist. Otherwise it keeps its place, empty.
Sub SetInsets(top As Double, leading As Double, bottom As Double, trailing As Double)
Sub SetVisibilityPriority(view As Ptr, priority As Double)
Below 1000, the stack is allowed to detach the view when room runs short — it is the mechanism of the bars that thin out as they shrink. MustHold = 1000, DetachOnlyIfNecessary = 900, NotVisible = 0.

Properties

Alignment As Alignments
Alignment on the CROSS axis: for a horizontal stack, CenterY, Top, Bottom or FirstBaseline; for a vertical one, CenterX, Leading or Trailing. An alignment taken on the wrong axis is simply ignored.
Distribution As Distributions
Orientation As Orientations
Spacing As Double

Enumerations

AlignmentsTop=3Bottom=4Leading=5Trailing=6CenterX=9CenterY=10FirstBaseline=12
DistributionsGravityAreas=-1Fill=0FillEqually=1FillProportionally=2EqualSpacing=3EqualCentering=4
OrientationsHorizontal=0Vertical=1

System

What aims at no window: the system menu bar, and the file panels.

System

NativeStatusItem

class

NSStatusItem: an extra in the menu bar. Xojo cannot place one at all. Since 10.10 everything goes through button — the item's own title, image, target and action are deprecated.

Constructor

Sub Constructor(title As String = "", symbolName As String = "")

Variable length: the item fits its content. NSSquareStatusItemLength (-2) would keep it square, at the height of the bar.

Methods

Sub AddMenuItem(title As String)
A menu item carries its own target and its own tag, as in NativeComboButton: it is the tag that says which one was chosen.
Sub AddSeparator()
Function Handle() As Ptr
Sub Remove()
Without this call, the extra stays in the menu bar until the end of the process, even if the Xojo object has gone.
Sub SetSymbol(symbolName As String, accessibilityDescription As String = "")

Properties

Title As String
Visible As Boolean

Events

  • Event Clicked(index As Integer, title As String)
System

NativeFilePanel

class

NSSavePanel and NSOpenPanel together, since the second inherits from the first. What the Xojo dialogs do not have: an accessory view — the format options under the name field, as in Preview's export.

Constructor

Sub Constructor(mode As Modes = Modes.Save)

The two panels share a class here because they share almost everything: NSOpenPanel inherits from NSSavePanel.

Methods

Function Handle() As Ptr
Function RunModal() As Boolean
Returns True if the user validated. NSModalResponseOK is 1 — not to be confused with NSAlert's 1000, which counts from NSAlertFirstButtonReturn.
Sub SetAccessoryView(view As Ptr, width As Double = 0, height As Double = 0)
Any NSView at all — hence the Handle of any control in the library. The panel sizes itself on the frame supplied.
Sub SetAllowedExtensions(ParamArray extensions() As String)
allowedContentTypes wants UTTypes (macOS 11+), not strings. We build them from extensions; an extension unknown to the system returns Nil and is simply ignored.
Sub SetOpenOptions(chooseFiles As Boolean = True, chooseDirectories As Boolean = False, multipleSelection As Boolean = False)
Without effect on a save panel: these settings exist only on NSOpenPanel.
Sub SetTexts(title As String = "", message As String = "", prompt As String = "")
prompt is the label of the validation button. Passing "" leaves the system's default text, already localised — better not to translate it yourself.
Function Values() As FolderItem()
The plural is only meaningful when opening with multiple selection; elsewhere the array contains at most one element.
Sub ShowContentTypes(visible As Boolean)
macOS 15. The FORMAT menu under the name field, drawn and populated by AppKit from the allowed types — what had to be built by hand in an accessory view. Without effect when opening, or if no type was declared.
Function ChosenContentType() As String
The type chosen, as a uniform identifier. To be read AFTER validation.

Properties

Directory As FolderItem
FileName As String
The name proposed in the field. Not applicable to an open panel.
Value As FolderItem read-only
Valid only after an accepted RunModal.

Enumerations

ModesSave=0Open=1
System

NativeFileKind

class

Recognises what the Finder shows and Xojo ignores: an application, an installer package, a Photos library are folders to the file system — FolderItem.IsFolder returns True — but documents to the user. Classify returns the kind, TypeIdentifier the type identifier (com.apple.photos.library), and Describe the Finder's label, translated by the system.

Methods

Shared Function Classify(item As FolderItem) As Kinds
What the Finder sees, and Xojo does not say: an application, an installer package, a Photos library are FOLDERS to the file system — FolderItem.IsFolder returns True — but documents to the user. The distinction is called a “package”, and only macOS knows it.The order of the questions matters: an alias to an application is an alias first, and an application is a package before it is a folder.
Shared Function Describe(item As FolderItem) As String
The Finder's “kind”, translated by the system: “Application”, “Installer package”, “Photos Library”, “Folder”. It is not made up here — the translation is macOS's own.
Shared Function IsApplication(item As FolderItem) As Boolean
Shared Function IsPackage(item As FolderItem) As Boolean
True for everything the Finder presents as a document although it is a folder: application, installer package, Photos library, Xcode project, RTFD document…
Shared Function TypeIdentifier(item As FolderItem) As String
The uniform type identifier: “com.apple.application-bundle”, “com.apple.photos.library”, “public.folder”. It is what allows a given application's library to be recognised.

Enumerations

KindsMissing=0PlainFile=1Folder=2AliasFile=3Application=4InstallerPackage=5Package=6
System

NativeQuickLook

class

QLPreviewView: the Finder preview in a view of your own — PDF, image, audio, video, text. Xojo has nothing for this. Preview(aFile) is enough; Available() says whether the framework could be loaded, and Handle returns Nil otherwise. It can safely be placed under a drop area: a QLPreviewView registers no drag type — verified — and therefore cannot fight the view above it for the drop. The well then becomes the preview of whatever is dropped on it.

Constructor

Sub Constructor(width As Double = 320, height As Double = 320, style As Styles = Styles.Normal)

ONE PECULIARITY, and it is decisive: QLPreviewView does not belong to AppKit but to QuickLookUI, which Xojo never links. Without an explicit load, the class simply does not exist — verified: a binary that does not link Quartz does not find QLPreviewView. Hence the dlopen, on the FULL PATH: the short name “Quartz” fails.

Methods

Sub Close()
RELEASES the preview and its resources — it is the only gesture that does.The header is categorical: without close, “your application leaks”. And since shouldCloseWithWindow is True by default, close only happens by itself when the WINDOW closes: in a single-window application, that means “on quitting”. A live preview therefore holds its QuickLook service for the whole session. CAREFUL, and it is irreversible: once closed, the view ACCEPTS NO further item. To preview again, another one has to be built — hence Available(), which will return False afterwards.
Function Available() As Boolean
The view obtained is queried, not the version number: QuickLookUI has existed since 10.7, but its loading can fail for other reasons. A CLOSED view is no longer usable either, and says so here.
Sub Clear()
Function Handle() As Ptr
Function Item() As FolderItem
Function Preview(item As FolderItem) As Boolean
VERIFIED: NSURL ALREADY conforms to the QLPreviewItem protocol — its previewItemURL returns the URL itself. So there is no runtime class to build, contrary to what reading the header leads you to fear.
Sub Refresh()
To be called when the FILE has changed under the preview: setting the same item again would not be enough, QuickLook having no reason to read it afresh.

Properties

Autostarts As Boolean
Starts documents that play — audio, video — by themselves.
CloseWithWindow As Boolean
At True, the preview closes with the window that carries it, which saves having to think about it. At False, it is up to the Destructor to see to it.

Enumerations

StylesNormal=0Compact=1
System

NativeWindowMenu

class

The system Window menu, and the window commands that go with it. Once the menu is designated by Install, AppKit keeps it up to date ITSELF: it lists every window that opens, removes it on closing, ticks the front one and switches on a click. Xojo exposes none of this.

Methods

Shared Sub Install(menu As DesktopMenuItem)
Designates this item's submenu as NSApplication.windowsMenu. You pass the menu-bar ITEM, not the menu: on the ObjC side, the submenu of an NSMenuItem is the NSMenu expected. The parameterless Handle is checked with Cocoa.Responds(item, "submenu") before use — the parameterised form the reference documents belongs to MenuItem, the old name, and no overload accepts it.
Shared Function Installed() As Boolean
Observe rather than assume: if the menu could not be designated, the list will never fill and nothing else would say so.
Shared Sub Exclude(w As DesktopWindow, hidden As Boolean)
A utility window — palette, detached inspector — has no business in the list. It is a property of the WINDOW, not of the menu.
Shared Sub BringToFront(w As DesktopWindow)
makeKeyAndOrderFront: and not Show: on a window that is already visible, Show does not bring it forward and does not give it the keyboard focus.
Shared Sub BringAllToFront()
The “Bring All to Front” action of the Window menu.
Shared Function Front() As DesktopWindow
The KEY window according to AppKit, which is not App.Window(0): Xojo's list follows the order of creation, not the stacking order.
Shared Sub PerformClose()
performClose: and not a forced close: the system selector consults the window's delegate — windowShouldClose: may refuse —, animates the closing and beeps when the window has no close box.
Shared Sub ToggleFullScreen()
Native full screen, the one behind the green button, and not Xojo's FullScreen, which merely enlarges the window. Without effect if the window does not carry its full-screen button.
System

NativeDockTile

class

The application's icon in the Dock: the counter badge, and the ability to draw a view of your own in it — progress gauge, document thumbnail, state. Xojo has nothing for this.

Methods

Shared Sub SetBadge(text As String)
The red badge — Mail's “3”. An empty string removes it. AppKit truncates an over-long label itself: no need to count characters.
Shared Function Badge() As String
What the badge carries, an empty string when there is none.
Shared Sub SetContentView(view As Ptr)
The icon becomes a VIEW, and you draw what you like in it. Nil restores the application icon.
Shared Sub Display()
To be called after EVERY change to the content view: the Dock does not redraw of its own accord.
Shared Function Size() As Cocoa.NSSize
The usable size of the view, in points. It FOLLOWS the Dock size setting: hard-coding it would give a wrong gauge as soon as the user changes it.
Shared Sub ShowsApplicationBadge(visible As Boolean)
Distinct from the text badge: this setting decides whether the application icon is inset into the custom view. With no custom view, it has nothing to do.
System

NativeHaptics

class

The small tap of the Force Touch trackpad, the one felt when a drag snaps to a guide. The class tests neither the hardware nor the settings: the header says defaultPerformer returns the performer appropriate “for the current input device, accessibility settings and user preferences”. With no haptic trackpad, or if the user has turned the feedback off, the call does nothing — it is the system's decision to make.

Methods

Shared Sub Perform(pattern As Patterns = Patterns.Alignment, when As Timing = Timing.Default)
Careful with the timing: Timing.Default means DrawCompleted, so the tap waits for the next drawing pass. To feel it on the click, pass Timing.Now.
Shared Function Available() As Boolean
To be used only to ADAPT the interface, never to guard the call: Perform is already without effect in that case.

Enumerations

PatternsGeneric=0Alignment=1LevelChange=2
TimingDefault=0Now=1DrawCompleted=2
System

NativeCursor

class

The cursors introduced with macOS 15: column and row resize, frame handle, magnifiers. The directions passed are not decorative — they say which ways the drag is still possible, so that a column already at its minimum width shows a cursor that points only one way.

Methods

Shared Sub SetColumnResize(directions As Horizontal = Horizontal.All) · SetRowResize
The cursors of the Finder in column view and of a table.
Shared Sub SetFrameResize(position As FramePositions, directions As FrameDirections = FrameDirections.All)
The cursor of a frame HANDLE: you give it the corner or edge grabbed, and the ways still possible.
Shared Sub SetZoom(zoomIn As Boolean)
The “plus” and “minus” magnifiers.
Shared Sub Restore()
Pops ONE cursor. The setters go through push and not set: a cursor set with set is overwritten at the next hover over a control. AppKit's stack has no “restore everything”, so one Restore per push.
Shared Function Available() As Boolean
To ADAPT the interface only: the methods above are already without effect on an earlier system.

Enumerations

HorizontalLeft=1Right=2All=3
VerticalUp=1Down=2All=3
FramePositionsTop=1Left=2TopLeft=3Bottom=4BottomLeft=6Right=8TopRight=9BottomRight=12
FrameDirectionsInward=1Outward=2All=3
System

NativePasteboard

class

The general pasteboard seen through the access alert of macOS 15.4 — the one that warns the user an application has just read their pasteboard.

Methods

Shared Function AccessBehavior() As Behaviors
macOS 15.4. What the system does when the application reads the pasteboard PROGRAMMATICALLY: ask, always allow, always deny. The user sets it per application in System Settings. Two nuances the header states: as long as an application has NEVER triggered the alert, it reports Default — it is the first access that flips the state to Ask, so reading Default does not mean “no alert will come”; and this setting does NOT touch what follows from a user gesture — a drag and drop, a Cmd-V never ask anything.
Shared Function ChangeCount() As Integer
The change counter, incremented on every write by anyone. It is the useful counterpart of the previous one: reading it does not count as access to the content, so it triggers no alert. An application that wants to enable a “Paste” button queries this counter instead of reading the pasteboard.
Sub Constructor() · Function Detect(ParamArray patterns() As Patterns) As Boolean
macOS 15.4. Asks whether the FIRST pasteboard item matches one of the patterns. The whole point is in one sentence of the header — “without notifying the person using the app”: you learn what is there without reading, hence without triggering the alert. The callback returns only the patterns RECOGNISED, never the values: getting those would mean reading, and that the system reports. Returns False if the call could not be made — the event will then never arrive, and that also covers a detection already in flight: one at a time.
Shared Function DetectionAvailable() As Boolean
To ADAPT the interface: Detect already returns False otherwise.
Shared Function Available() As Boolean
To ADAPT the interface only: AccessBehavior already returns Default on an earlier system.

Events

  • Event Detected(patterns() As Patterns, failed As Boolean)

The completion block arrives on a service queue, NOT on the interface thread. It therefore only retains the NSSet and raises a flag, then calls Cocoa.PerformOnMainThread: Detected is raised from the main thread, where strings can safely be built. That retain is what forces uniqueness — two detections in flight would fight over the same pointer from two threads.

Enumerations

PatternsProbableWebURLProbableWebSearchNumberLinkPhoneNumberEmailAddressPostalAddressCalendarEventShipmentTrackingNumberFlightNumberMoneyAmount
BehaviorsDefault=0Ask=1AlwaysAllow=2AlwaysDeny=3
System

NativeSymbolEffect

class

Animates an SF symbol: bounce, pulse, draw-on… A shared utility, not a control — the methods are Shared and apply to any NSImageView. Two limits checked in the headers: the effects live in Symbols.framework rather than AppKit, hence an explicit load; and on AppKit addSymbolEffect: exists ONLY on NSImageView, unlike UIKit where buttons accept it too.

Methods

Shared Sub Play(imageView As Ptr, kind As Effects)
The image must be a real SF symbol: on an ordinary image the effect has nothing to animate.
Shared Sub RemoveAll(imageView As Ptr)
Indefinite effects — pulse, variable colour, rotate — run until this call; one-shot ones stop by themselves.
Shared Function Available(kind As Effects) As Boolean
Each effect is a distinct class, introduced in a different version. Ask the class, never the system number.

Enumerations

EffectsPulse=0Bounce=1VariableColor=2Scale=3Appear=4Disappear=5Wiggle=6Rotate=7Breathe=8DrawOn=9DrawOff=10

Availability: the first six since macOS 14, Wiggle/Rotate/Breathe since macOS 15, DrawOn/DrawOff since macOS 26.

System

NativeLoginItem

class

Registers the app — or a service it carries — to open at login, through SMAppService (macOS 13). Replaces the removed SMLoginItemSetEnabled, and writing straight into the login preferences, which was never an API. The ServiceManagement framework is not linked by a Xojo app, so the class loads it itself.

Factories

Shared Function MainApp() As NativeLoginItem
The app itself. The common case, and the only one needing nothing more than a normal .app bundle.
Shared Function LoginItem(bundleIdentifier As String) As NativeLoginItem
A helper app shipped under Contents/Library/LoginItems.
Shared Function Agent(plistName As String) As NativeLoginItem
A LaunchAgent under Contents/Library/LaunchAgents: inside the session, with the logged-in person's rights.
Shared Function Daemon(plistName As String) As NativeLoginItem
A LaunchDaemon under Contents/Library/LaunchDaemons: as root, outside the session. Registering one asks for an administrator authentication.

Methods

Function Register(ByRef errorMessage As String) As Boolean
True means the request succeeded, not necessarily that the service is active: the status that follows may be RequiresApproval.
Function Unregister(ByRef errorMessage As String) As Boolean
No effect and no error if the service was not registered.
Function Status() As Statuses
The state as the system sees it. Re-read it after every action: the person may have changed everything in System Settings meanwhile.
Shared Sub OpenSystemSettings()
Opens System Settings › General › Login Items & Extensions. The only move available when the status is RequiresApproval.
Shared Function Available() As Boolean
macOS 13. Before that, there is nothing to fall back on.
Function Handle() As Ptr

Enumerations

StatusesUnavailable=-1NotRegistered=0Enabled=1RequiresApproval=2NotFound=3

The system refuses to register an executable it cannot identify: an unsigned app, or one launched outside its .app bundle, makes Register return False with an explicit message. When debugging, what gets registered is the .debug.app bundle, not the shipped app. statusForLegacyURL: is not wrapped: it only serves migration from the pre-13 era.

System

NativeWorkspace

class

The service through which an application talks to the Finder. For now a single thing, the one most often missing: « Reveal in Finder » — the Finder comes to the front and opens a window with the files ALREADY SELECTED. No Xojo equivalent: FolderItem.Open LAUNCHES the file, and opening the parent folder leaves the user hunting by eye.

Methods

Shared Function Reveal(files() As FolderItem) As Boolean
macOS 10.6, activateFileViewerSelectingURLs:. Several files at once: the Finder selects them all and opens one window per folder involved — which no parent-folder workaround reproduces.The return says « the request reached the Finder », not « the Finder succeeded »: the selector returns nothing, the header declares it void. What Reveal does check is that at least one file EXISTS — without which the call goes nowhere and nothing visible happens.
Shared Function Reveal(file As FolderItem) As Boolean
The single-file case, which is the common one.
System

NativeUserNotification

class

System notifications through UNUserNotificationCenter (macOS 10.14), replacing NSUserNotification, deprecated and silently inert on recent systems. Everything here is asynchronous: each request goes out, and the answer arrives later as an event.

Methods

Function RequestAuthorization(alerts As Boolean = True, sounds As Boolean = True, badges As Boolean = False, provisional As Boolean = False) As Boolean
The system dialog only appears ONCE in the app's lifetime; afterwards the call returns the recorded answer without showing anything. provisional asks for quiet authorisation: no dialog at all, and notifications land silently in the centre.
Sub RequestSettings()
Reads the authorisation state without asking anyone. Answered by SettingsRead.
Function Send(identifier As String, title As String, body As String, subtitle As String = "", delaySeconds As Double = 0, withSound As Boolean = True, categoryIdentifier As String = "") As Boolean
A zero delaySeconds means "right now": no trigger is attached. The same identifier REPLACES the previous notification — that is how you update a progress without stacking ten banners.
Sub RequestPendingNotifications() · Sub RequestDeliveredNotifications()
What is scheduled, and what is still visible in the centre. Answered by PendingRead and DeliveredRead.
Sub RemovePendingRequests(ParamArray identifiers() As String) · Sub RemoveAllPendingNotifications()
Sub RemoveDeliveredNotifications(ParamArray identifiers() As String) · Sub RemoveAllDeliveredNotifications()
Sub SetBadgeCount(value As Integer)
macOS 13. A badge set by the notification centre, so subject to the "badges" authorisation. For a badge that does not depend on it, see NativeDockTile.
Sub AddResponseCategory(category As NativeNotificationCategory)
Declares a set of buttons — and optionally a text field — to the system. Before sending the notification that uses it: declaring afterwards fixes nothing. A category with the same identifier is replaced, not duplicated.
Sub RemoveAllResponseCategories()
setNotificationCategories: replaces the whole set on each call instead of adding to it; the list is therefore kept here, and that is what gets cleared.
Shared Sub SetForegroundPresentation(banner As Boolean = True, inList As Boolean = True, sound As Boolean = True, badge As Boolean = False)
What the system shows when a notification arrives while the app is active. All False amounts to showing nothing — macOS's behaviour without a delegate, exactly what this class fixes. A shared setting: the centre is a singleton.
Shared Sub OpenNotificationSettings()
Opens System Settings › Notifications, at the app's pane. The move to offer when the status is Authorized but AlertStyle is None: no API changes that setting on behalf of the person who chose it.
Shared Function Available() As Boolean

Read-only properties

AlertStyle As AlertStyles
Valid after SettingsRead. This setting, not the authorisation status, decides whether a banner appears. An app can be authorised with an alert style of None: the system accepts notifications, files them in the centre, and never shows anything.
ShowsAlerts As DeviceSettings · ShowsInNotificationCenter As DeviceSettings · PlaysSounds As DeviceSettings · ShowsBadges As DeviceSettings

Events

Event UserResponded(identifier As String, kind As Actions, actionIdentifier As String, textResponse As String)
The notification was opened or dismissed. actionIdentifier carries the raw value; kind classifies it, by reading the exported constants rather than copying their values.
Event AuthorizationAnswered(granted As Boolean, errorMessage As String)
Event SettingsRead(status As AuthorizationStatuses)
Event NotificationSent(identifier As String, errorMessage As String)
Event PendingRead(identifiers() As String) · Event DeliveredRead(identifiers() As String)

Enumerations

ActionsOpened=0Dismissed=1Custom=2
AlertStylesUnknown=-1None=0Banner=1Alert=2
DeviceSettingsUnknown=-1NotSupported=0Disabled=1Enabled=2
AuthorizationStatusesUnavailable=-1NotDetermined=0Denied=1Authorized=2Provisional=3Ephemeral=4

Xojo offers nothing of the sort on macOS: its whole "Notifications" section — Notification, TimeIntervalNotification, CalendarNotification, RemoteNotification and the response classes, driven by MobileNotifications — is marked “Project Types: Mobile, Operating Systems: iOS”, and none of those names exists in the 2026 R2.1 desktop framework. This class therefore duplicates nothing. It does adopt its naming, deliberately: Send, RequestPendingNotifications, OpenNotificationSettings, the NotificationSent and UserResponded events, the AlertStyle, ShowsAlerts, ShowsInNotificationCenter, PlaysSounds, ShowsBadges settings and the AlertStyles, DeviceSettings, AuthorizationStatuses enumerations, right down to the response categories — AddResponseCategory and the NativeNotification… classes. Three deliberate departures: AuthorizationAnswered carries grant, refusal and error where Xojo splits AuthorizationSucceeded and Error; RequestSettings has no counterpart; and the enumerations gain a sentinel Xojo does not have.

The other reason to see nothing, this one beyond the code's reach: "authorised" does not mean "visible". If the alert style is None, the system accepts everything and shows nothing. RequestSettings says so, OpenNotificationSettings leads to the right pane, and no API changes that setting on behalf of the person who chose it.

The delegate is installed by default, and the header says why: “The method will be called on the delegate only if the application is in the foreground. If the method is not implemented […] the notification will not be presented.” Without it, a notification posted while the app is active goes out, is accepted, files itself in the centre — and never shows, with no error to say so. The Constructor therefore builds an ObjC class at runtime, the way NativeControlHost does, and SetForegroundPresentation decides what appears.

Completion blocks arrive on a service queue, NOT on the interface thread. Each block therefore only retains the raw result, then calls Cocoa.PerformOnMainThread: every event of this class is raised from the main thread. Consequence: the instance must live in a property, never in a local variable — the system may call its blocks after it has been destroyed.

Response buttons go through AddResponseCategory: a NativeNotificationCategory carrying NativeNotificationButtons and, if wanted, a NativeNotificationTextField. UserResponded then reports the chosen action's identifier and, for a field, the typed text. One reservation remains for a click that LAUNCHED the app: the delegate must be in place before launch completes, so the instance created in App.Opening.

System

NativeNotificationAction

class

Base class for a notification action, modelled on Xojo's iOS NotificationResponseAction: you do not instantiate it, you take NativeNotificationButton or NativeNotificationTextField.

Properties

Identifier As String
What UserResponded will report. This, not the caption, is what tells actions apart.
Caption As String
The button text, as shown.
BringToForeground As Boolean
Brings the app to the front. Without it the action is handled without the window moving — the whole point of an interactive notification.
Destructive As Boolean
Red button. Cosmetic, but it is the convention.
AuthenticationRequired As Boolean
Requires Touch ID, the watch or the password before acting.
SystemImageName As String
An SF symbol on the button (UNNotificationActionIcon, macOS 12). Ignored on earlier systems, and absent from Xojo's iOS class: an addition here.

Methods

Shared Function Available() As Boolean
Loads UserNotifications.framework along the way: a category may be built before any NativeUserNotification instance exists.
Function Build() As Ptr
Builds the UNNotificationAction. Called by NativeNotificationCategory, overridden by NativeNotificationTextField.

ORDER MATTERS: macOS truncates the action list when space runs short, so put the most useful first.

System

NativeNotificationButton

class

A plain button on a notification. Inherits NativeNotificationAction, from which it takes all its behaviour — modelled on Xojo's NotificationResponseButton.

Constructor

Sub Constructor(caption As String, identifier As String)
Yes/No buttons are two instances, each with its own identifier — that is what UserResponded reports.
System

NativeNotificationTextField

class

A text field on the notification, with its send button: UNTextInputNotificationAction. Same argument order as Xojo's NotificationResponseTextField.

Constructor and properties

Sub Constructor(caption As String, hint As String, sendCaption As String, identifier As String)
ButtonCaption As String · Hint As String
The send button's label, and the empty field's placeholder.

What the person typed arrives in the textResponse parameter of the UserResponded event, and nowhere else: the reply does not travel through the notification's content.

System

NativeNotificationCategory

class

The group of buttons and fields a notification can carry: UNNotificationCategory, modelled on Xojo's NotificationResponseCategory. The link to a notification is one string, the identifier.

Constructor, properties, method

Sub Constructor(identifier As String)
That identifier is what you pass to Send as categoryIdentifier.
Actions() As NativeNotificationAction
The buttons and fields, in display order.
CustomDismiss As Boolean
Raises UserResponded even when the notification is merely DISMISSED. Without it, a dismissal goes unnoticed.
HiddenPreviewsBody As String · HiddenPreviewShowsTitle As Boolean · HiddenPreviewShowsSubtitle As Boolean
What stays visible when previews are hidden — lock screen, "Show previews" set to "Never".
Function Build() As Ptr

A category only takes effect once declared through NativeUserNotification.AddResponseCategory, and before the notification that uses it is sent: declaring afterwards fixes nothing. The system keeps them on the app's behalf, not the instance's.

WHAT YOU WILL NOT SEE STRAIGHT AWAY: with the Banners style the buttons only appear once you hover the notification and expand it; with Alerts, immediately. That setting belongs to the person, not the app. AllowInCarPlay and AllowAnnouncement, exposed by Xojo's iOS class, are marked API_UNAVAILABLE(macos) and are therefore not wrapped.