## VDSTools — native macOS controls for Xojo, in pure Xojo

VDSTools 1.0 — native macOS controls for Xojo, in pure Xojo

I have been building a set of AppKit wrappers for my own apps, and it grew
into something worth releasing: 100 classes, written entirely in Xojo
Declares. No plugin, no external framework, no compiled Objective-C to ship
or sign — just code you drop into your project.

27 of them are controls you drop in the IDE and configure in the
Inspector: table and outline views with rich cells, token field, path bar,
combo button, date picker, colour well, switch, level indicator, and the rest.
Fifteen inherit from a Xojo control; twelve host an AppKit object.

Some of what it covers that Xojo does not expose:

  • NSToolbar, including the customisation sheet
  • NSSplitViewController window chrome, and source-list sidebars
  • View-based tables and outlines: checkboxes, two-line cells, progress bars,
    pop-up menus in cells, row reordering with a Numbers-like drag ghost
  • Window tabs and the system Window menu
  • QuickLook thumbnails, the Dock tile, popovers, NSGridView, NSStackView
  • The macOS 26/27 glass effect, and menu image visibility

Documentation: a developer reference in French and English, covering every
class and enumeration — and 56 traps, each with the symptom that sent me
looking in the wrong place first. That part may be the most useful bit.

Demo application: 34 pages, each showing one subject with its code in an
inspector, plus a second window with a page per placeable control and every
option adjustable live. Signed and notarised.

→ VDSTools — macOS natif pour Xojo

Requirements: macOS 15 or later, Xojo 2021r3 or later.

Pricing: €60 incl. VAT for the compiled library, €240 incl. VAT with the
source code — per developer, unlimited applications, royalty-free
distribution. Businesses in the EU outside France are invoiced net of tax.
Running from the IDE is free; a built application without a licence runs, but
carries a small watermark.

Happy to answer questions here, and to hear what is missing.

Wow! Let’s see a sample project.

Wow! Super impressive stuff.

I love the tables. It’s the smoothest implementation that I’ve seen.

1.1 is out.

New: NativeIconButtonControl, the 28th control you drop in the IDE — a
button with an SF Symbol beside its title. Symbol, icon position, hugging,
bezel and control size in the Inspector; a file image or a Picture from code.

It had to be a hosted control rather than a property on the inherited one.
Measured: setImage: on a DesktopButton is accepted, then wiped on the next
pass — image gone, imagePosition back to 0. Xojo reconfigures its own buttons
after Opening and keeps doing it; there is no hook before an inherited
control draws.

Also: ImagePosition and ImageHugsTitle on NativeButton, and a second trap
— with the Push bezel, an Above icon draws the title outside the frame.

101 classes, 58 traps.
Download: https://github.com/Valdemar-VDSC/VDSTools-dist/releases/latest
Documentation: VDSTools — macOS natif pour Xojo

Question about Sidebar.

Some Things are missing:

SelectedItem(Item As SidebarItem, Page As Integer, Title As String)

AddItem(Section, Title, SfSymbol, Color, Collapsed As Boolean = False, Tag As Variant)

Add a Section as a sub item in a Section

Collapse and expand a Section

Do you see a change to add these features to the Sidebar?

Nice idea, I take a look for next release that come later in October :wink:

VDSTools 1.2 — sidebar items, tags, collapsible sections, saved state

This one comes almost entirely from a comment left here on 1.1, so: thank you. Four things were missing from the sidebars, three of them are in. Everything is additive — SelectionChanged is untouched, no existing code has to be rewritten.

A row is an object now. NativeSidebarItem is the handle a sidebar hands back: Tag, Title, Section, Index, Page, IsValid. The link to the bar is weak, so holding handles does not keep a closed window alive.

Tags. Without one, a program identifies a row by its title — which is translated — or by its page number, which moves the moment you insert a row above it. A tag does neither:

mSidebar.AddItem(0, "Invoices", "doc.text", &c007AFF, "invoices")
Var it As NativeSidebarItem = mSidebar.LastItem

On the flat sidebar the tag is set apart, with SetTag and TagAt. An optional Variant on Add would have made every three-argument call ambiguous, since a Variant accepts a Color too.

A selection event that tells you what was selected:

Sub SelectedItem(item As NativeSidebarItem, page As Integer, title As String)
  Select Case item.Tag.StringValue
  Case "invoices"
    ...
  End Select
End Sub

Sections collapse and expand from code, and can start collapsed:

Var sec As Integer = mSidebar.AddSection("Archive", True)   // starts collapsed
mSidebar.ExpandSection(sec)
If mSidebar.SectionExpanded(sec) Then ...

Plus ItemAt, CurrentItem and SelectPage on the way past.

And the state survives a quit. Either AppKit keeps it, under a name of your choosing — mSidebar.AutosaveName = "MainSidebar" — or you keep it yourself, as JSON, to put in a preference or attach to a document: SaveState / RestoreState.

Two traps measured on the way, both now in the reference:

  • Turning a Sub into a Function breaks every existing call. Xojo refuses to ignore a returned value — “You must use the value returned by this function” — so a published method cannot grow a return type without breaking a customer’s code. That is why AddItem stayed a Sub and LastItem gives you the handle.
  • An NSOutlineView restores its autosaved expansion during reloadData, not when you set autosaveName. Set the name after loading the rows and it will save the state faithfully and never once restore it.

102 classes, 798 public methods, 60 documented traps. Pure Xojo — Declares only. macOS 15+, Xojo 2021r3+. Library 60 € TTC, source 240 € TTC — GitHub - Valdemar-VDSC/VDSTools-dist: VDSTools — chrome de fenêtre, barre d'outils et contrôles macOS natifs pour Xojo, en Xojo pur. Documentation et versions distribuées. · GitHub


VDSTools 1.3.0 — what changed since 1.2.0

Two releases since the sidebar one. The first came out of a customer’s request, the second out of using the thing.

Row icons are no longer limited to SF Symbols. A customer was working around it by writing the cell’s imageView.image with declares on a timer, because the library put its symbol back every time a row view was rebuilt. A Picture now works anywhere a symbol did:

mTable.SetRowImage(0, pic)    // and SetNodeImage(node, pic) on the outline

mSidebar.Add("Shared", pic) // or SetImage(index, pic) afterwards

mOutline.SetImage(section, item, pic) // hierarchical sidebar

It survives a refresh, which was the whole point of asking: the image lives in the model, not on a view, and is re-read on every cell build. So it holds through a reload, a sort, a row move, a collapsed section and scrolling — exactly like a symbol name. Image and symbol are mutually exclusive: setting one clears the other, so there are never two icons to arbitrate. A bitmap is not tinted — contentTintColor only means something on a template image — and it is scaled down proportionally, never enlarged.

One asymmetry worth knowing, because it is deliberate: on the sidebars SetImage reloads the list itself, as SetBadge always has; on the table SetRowImage writes to the model and leaves the refresh to you, as SetRowIcon always has. Each class keeps its own rule.

A leak fixed in both sidebars. Cells, their labels, their icons, the badges and the row views were allocated and never released — up to nine objects per cell build, which means on every reload and every time a row scrolled back into view. It is the fix that went into the table in September and was never carried over. Harmless-looking with 18-point symbols; with images, every reload left megabytes behind. If you build AppKit views by hand, the rule is worth stealing: a view added to a parent is released straight after, and a view handed to AppKit goes out autorelease, never release — AppKit retains it after you return.

1.2.1 — a hosted control now follows Enabled. NativeIconButtonControl hosts a real NSButton. Enabled belongs to DesktopCanvas, a subclass cannot override it, and AppKit does not propagate setEnabled: to subviews — so the canvas went dim alone while the button stayed drawn as active and, being the view on top, went on receiving clicks. A button you believed disabled still acted. Fixed, with SetEnabled for when you want it without waiting for a draw. The other twelve hosted controls have the same blind spot; it is written down next to the fix.

102 classes, 804 public methods, 61 documented traps. Pure Xojo — Declares only, no plugin, no external framework, no compiled Objective-C. macOS 15 and later, Xojo 2021r3 and later.