MiniGui is a GUI library for MiniLang that helps you build simple desktop
applications quickly. You describe the user interface declaratively in .mson,
keep the application logic in normal MiniLang code, and let MiniGui generate and
compile the glue code for a native Windows application.
In short:
.msondescribes windows, layouts, controls, attributes, and events..mlcontains your application logic and event handlers.tools/minigui.mlis the MiniGui command-line tool.MiniGuiLib/minigui.mlis the runtime library for native Win32 controls.
The minimal hello example is built from examples/hello-gui/app.mson and
examples/hello-gui/app.ml.
MiniGui is its own project and is expected to live next to the MiniLang compiler checkouts:
MiniGui/
MiniGui/
MiniGuiLib/
tools/
examples/
schemas/
tests/
MiniLangCompilerPy/
mlc_win64.py
MiniLangCompilerML/
build/mlc_win64.exe
The Python compiler is currently the faster compiler for MiniGui builds.
MiniGui itself is written in MiniLang. Build the CLI with the Python compiler:
cd C:\Users\nilsk\Desktop\MiniGui\MiniGui
py -3.14 ..\MiniLangCompilerPy\mlc_win64.py .\tools\minigui.ml .\tools\minigui.exe -I . -I ..\MiniLangCompilerPyValidate, generate, and build the hello example:
.\tools\minigui.exe validate .\examples\hello-gui\app.mson
.\tools\minigui.exe generate .\examples\hello-gui\app.mson --output .\examples\hello-gui\build\app.gui.ml
.\tools\minigui.exe build .\examples\hello-gui\app.mson --output .\examples\hello-gui\build\hello-gui.exe --compiler ..\MiniLangCompilerPy\mlc_win64.pyRun the finished application:
.\examples\hello-gui\build\hello-gui.exe.\tools\minigui.exe validate <app.mson>
.\tools\minigui.exe generate <app.mson> --output <generated.gui.ml>
.\tools\minigui.exe build <app.mson> --output <app.exe>Useful options:
--compiler <path>selects the MiniLang compiler.--output <file>sets the generated source or executable output path.--code-behind <file>overrides thecodeBehindvalue from.mson.--generated-dir <dir>chooses the directory for generated files.--include-dir <dir>adds MSON import search paths.--no-compilegenerates.gui.mlwithout compiling an executable.--keep-generatedkeeps generated files afterbuild.
app.mson:
{
"$schema": "../../schemas/minigui.schema.json",
"version": 1,
"namespace": "hello_gui",
"codeBehind": "app.ml",
"application": {
"name": "HelloGui",
"startupWindow": "mainWindow"
},
"windows": [
{
"id": "mainWindow",
"type": "Window",
"properties": {
"title": "MiniGui Hello",
"width": 480,
"height": 220,
"startPosition": "centerScreen"
},
"layout": {
"type": "VerticalStack",
"properties": { "padding": 14, "spacing": 8 },
"children": [
{
"id": "headlineLabel",
"type": "Label",
"properties": { "text": "Welcome to MiniGui" }
},
{
"id": "nameTextBox",
"type": "TextBox",
"properties": { "text": "" },
"events": { "textChanged": "onNameChanged" }
},
{
"id": "greetButton",
"type": "Button",
"properties": { "text": "Greet" },
"events": { "click": "onGreetButtonClick" }
},
{
"id": "resultLabel",
"type": "Label",
"properties": { "text": "" }
}
]
}
}
]
}app.ml:
package hello_gui
import MiniGuiLib.minigui as MiniGui
function onNameChanged(ui, event)
if event.newValue == "" then
MiniGui.Control.setText(ui.resultLabel, "")
end if
return 0
end function
function onGreetButtonClick(ui, event)
name = MiniGui.Control.getText(ui.nameTextBox)
if name == "" then
MiniGui.Control.setText(ui.resultLabel, "Please enter a name.")
else
MiniGui.Control.setText(ui.resultLabel, "Hello, " + name + "!")
end if
return 0
end function
For every control with an id, the generator creates a field on the generated
ui object. In the example above you can access ui.nameTextBox,
ui.greetButton, and ui.resultLabel.
An MSON file is JSON with the .mson extension.
{
"version": 1,
"namespace": "my_app",
"codeBehind": "app.ml",
"resources": {},
"imports": [],
"application": {
"name": "MyApp",
"startupWindow": "mainWindow"
},
"windows": [],
"components": []
}Required fields:
version: currently always1.namespace: the MiniLang package for the generated application.application.name: the application name.application.startupWindow: theidof the startup window.windows: the list of windows.
Optional fields:
codeBehind: MiniLang file containing event handlers.resources: reusable values resolved at generation time.imports: additional.msonfiles.components: reusable UI building blocks.
Layouts group and position controls.
Arranges children from top to bottom.
{
"type": "VerticalStack",
"properties": { "padding": 12, "spacing": 8 },
"children": [
{ "id": "titleLabel", "type": "Label", "properties": { "text": "Title" } },
{ "id": "saveButton", "type": "Button", "properties": { "text": "Save" } }
]
}Arranges children from left to right.
{
"type": "HorizontalStack",
"properties": { "spacing": 8 },
"children": [
{ "id": "firstName", "type": "TextBox", "properties": { "width": "fill", "minWidth": 160 } },
{ "id": "lastName", "type": "TextBox", "properties": { "width": "fill", "minWidth": 160 } }
]
}Distributes children across columns. The columns property sets the column
count.
{
"type": "Grid",
"properties": { "columns": 2, "spacing": 8 },
"children": [
{ "id": "countryComboBox", "type": "ComboBox", "properties": { "items": ["DE", "US"] } },
{ "id": "cityListBox", "type": "ListBox", "properties": { "items": ["Berlin", "Bonn"] } }
]
}Places controls at fixed x and y coordinates.
{
"type": "Canvas",
"children": [
{ "id": "okButton", "type": "Button", "properties": { "text": "OK", "x": 20, "y": 20, "width": 100 } }
]
}Docks children to the edges of the remaining area. Use child dock values
top, bottom, left, right, or fill.
{
"type": "DockPanel",
"properties": { "spacing": 6 },
"children": [
{ "id": "toolbar", "type": "ToolBar", "properties": { "dock": "top", "height": 30, "items": ["Save"] } },
{ "id": "status", "type": "StatusBar", "properties": { "dock": "bottom", "height": 24, "text": "Ready" } },
{ "id": "content", "type": "Panel", "properties": { "dock": "fill" } }
]
}Places children left-to-right and wraps to the next row when the current row is full.
{
"type": "WrapPanel",
"properties": { "spacing": 8 },
"children": [
{ "id": "tagA", "type": "Button", "properties": { "text": "A", "width": 80 } },
{ "id": "tagB", "type": "Button", "properties": { "text": "B", "width": 80 } }
]
}These properties can be used on controls:
text: visible text.width,height: a number,"auto", or"fill".x,y: position inside aCanvas.visible:trueorfalse.enabled:trueorfalse.margin: reserved for layout spacing.horizontalAlignment:"left","center","right","fill", or"stretch".verticalAlignment:"top","center","bottom","fill", or"stretch".alignment: shorthand for horizontal alignment.dock: especially useful with"fill".fill:truesets the width to the available space.resizeMode: runtime resize behavior. Supported values are"scale","none","anchor", and"fill".anchor: edge anchors forresizeMode: "anchor", for example"left,top","right,bottom", or"left,top,right,bottom".minWidth,minHeight,maxWidth,maxHeight: size constraints.tooltip: tooltip text.tabIndex: keyboard navigation order.row,column,rowSpan,columnSpan: reserved for richer grid placement.foreground,background: native text/background styling for standard Win32 controls.borderColor,borderWidth,fontFamily,fontSize,fontWeight: generated styling hooks reserved for richer native rendering.
Example:
{
"id": "nameTextBox",
"type": "TextBox",
"properties": {
"text": "Ada",
"width": "fill",
"minWidth": 220,
"enabled": true,
"visible": true,
"tooltip": "Enter a name",
"tabIndex": 1
}
}MiniGui keeps older generated applications compatible by using
resizeMode: "scale" when no explicit mode is set. That scales a control's
position and size proportionally to its parent. For form-like interfaces, set a
more explicit mode:
none: keep the generated position and size.anchor: keep the control attached to selected parent edges. Anchoring left and right stretches the width; anchoring only right moves the control with the right edge.fill: grow width and height with the parent.
Size constraints are applied again during runtime resize, so minWidth,
minHeight, maxWidth, and maxHeight are not just generation hints.
{
"id": "contentTabs",
"type": "TabControl",
"properties": {
"items": ["Input", "Data"],
"width": "fill",
"height": 420,
"resizeMode": "anchor",
"anchor": "left,top,right,bottom",
"minWidth": 520,
"minHeight": 240
}
}MiniGui currently supports these controls:
All controls can also use focus and blur events. The table lists the
control-specific events in addition to those common focus events.
| Control | Purpose | Important properties | Events |
|---|---|---|---|
Label |
Display text | text |
- |
Button |
Trigger an action | text |
click, clicked |
FilePicker |
Button that opens a native open-file dialog | text, title, filter |
click, clicked |
FolderPicker |
Button that opens a native folder dialog | text, title |
click, clicked |
ColorPicker |
Button that opens a native color dialog | text, title, value |
click, clicked |
TextBox |
Single-line input | text, placeholder |
textChanged, changed, change |
SearchBox |
Single-line search input | text, placeholder, maxLength |
textChanged, changed, change |
TextArea |
Multi-line input | text, placeholder |
textChanged, changed, change |
PasswordBox |
Masked single-line input | text, placeholder, maxLength, readOnly |
textChanged, changed, change, submit, validating, validated |
NumberBox |
Numeric input | minimum, maximum, value, step, readOnly |
textChanged, valueChanged, changed, change |
SpinBox |
Numeric input with native up/down arrows | minimum, maximum, value, step, readOnly |
textChanged, valueChanged, changed, change |
CheckBox |
Boolean choice | text, checked |
click, clicked |
ToggleSwitch |
Boolean toggle control | text, checked |
click, clicked |
RadioButton |
Choice inside a group | text, checked |
click, clicked |
Image |
Bitmap image or image placeholder | source, stretch, text |
click, clicked |
Separator |
Horizontal or vertical separator | orientation |
- |
Splitter |
Draggable horizontal or vertical split marker | orientation, targetBefore, targetAfter |
valueChanged, changed, change |
LinkLabel |
Clickable text link | text, url, visited |
click, clicked |
Panel |
Borderless container | padding, spacing, children |
- |
ScrollViewer |
Scrollable container host | horizontalScroll, verticalScroll, padding, spacing, children |
- |
GroupBox |
Labeled container | text, padding, spacing, children |
- |
ComboBox |
Drop-down selection | items, selectedIndex |
selectionChanged, selected, changed, change |
EditableComboBox |
Editable drop-down selection | items, selectedIndex, text |
selectionChanged, selected, changed, change |
ListBox |
List selection | items, selectedIndex |
selectionChanged, selected, changed, change |
ScrollBar |
Scroll or value control | orientation, minimum, maximum, value, smallStep, largeStep |
scrollChanged, valueChanged, changed, change |
Slider |
Trackbar value control | orientation, minimum, maximum, value, smallStep, largeStep |
scrollChanged, valueChanged, changed, change |
ProgressBar |
Display progress | minimum, maximum, value |
scrollChanged, valueChanged, changed, change |
TabControl |
Tabbed interface | items, selectedIndex, children |
selectionChanged, selected, changed, change |
MenuBar |
Application menu bar | items |
click, clicked |
ContextMenu |
Native right-click popup menu on its parent | items |
click, clicked |
ToolBar |
Toolbar | items |
click, clicked |
StatusBar |
Status line | text |
- |
TreeView |
Tree navigation | items |
selectionChanged, selected, changed, change |
ListView |
List/report view | columns, columnWidths, items, selectedIndex |
selectionChanged, selected, changed, change |
Table |
Table-like list backed by ListView |
columns, columnWidths, items, selectedIndex |
selectionChanged, selected, changed, change |
DataGrid |
Explicit grid/table control backed by report-mode ListView | columns, columnWidths, items, selectedIndex, editable |
selectionChanged, selected, changed, change |
DatePicker |
Date input | text |
textChanged, changed, change |
DateTimePicker |
Date/time input | text |
textChanged, changed, change |
TimePicker |
Time input | text |
textChanged, changed, change |
Calendar |
Month calendar selector | text |
textChanged, changed, change |
These screenshots are cropped from the MiniGui Control Gallery example.
{
"id": "saveButton",
"type": "Button",
"properties": { "text": "Save", "width": 120, "height": 30 },
"events": { "click": "onSaveClick" }
}function onSaveClick(ui, event)
MiniGui.Control.setText(ui.statusBar, "Saved")
return 0
end function
{
"id": "notesTextArea",
"type": "TextArea",
"properties": {
"text": "Notes",
"height": 80,
"width": "fill",
"placeholder": "Enter notes"
},
"events": { "change": "onNotesChanged" }
}function onNotesChanged(ui, event)
MiniGui.Control.setText(ui.statusBar, "Text changed: " + event.newValue)
return 0
end function
{
"id": "passwordBox",
"type": "PasswordBox",
"properties": { "maxLength": 32, "width": "fill" },
"events": { "change": "onPasswordChanged" }
}{
"id": "quantityNumberBox",
"type": "NumberBox",
"properties": { "minimum": 1, "maximum": 99, "value": 3, "step": 1 },
"events": { "valueChanged": "onQuantityChanged" }
}function onQuantityChanged(ui, event)
quantity = MiniGui.Control.getValue(ui.quantityNumberBox)
MiniGui.Control.setText(ui.statusBar, "Quantity: " + quantity)
return 0
end function
{
"id": "advancedCheckBox",
"type": "CheckBox",
"properties": { "text": "Advanced options", "checked": true },
"events": { "click": "onAdvancedClicked" }
}function onAdvancedClicked(ui, event)
enabled = MiniGui.Control.isChecked(ui.advancedCheckBox)
MiniGui.Control.setEnabled(ui.detailsPanel, enabled)
return 0
end function
{
"id": "countryComboBox",
"type": "ComboBox",
"properties": {
"items": ["Germany", "United States", "France"],
"selectedIndex": 0,
"width": "fill"
},
"events": { "selected": "onCountrySelected" }
}function onCountrySelected(ui, event)
selected = MiniGui.Control.getSelectedText(ui.countryComboBox)
MiniGui.Control.setText(ui.resultLabel, "Country: " + selected)
return 0
end function
{
"id": "volumeSlider",
"type": "Slider",
"properties": {
"orientation": "horizontal",
"minimum": 0,
"maximum": 100,
"value": 25,
"smallStep": 5,
"largeStep": 20,
"width": "fill",
"height": 32
},
"events": { "valueChanged": "onVolumeChanged" }
}function onVolumeChanged(ui, event)
MiniGui.Control.setText(ui.volumeLabel, "Volume: " + event.newValue)
MiniGui.Control.setValue(ui.progressBar, event.newValue)
return 0
end function
{
"id": "customerGroup",
"type": "GroupBox",
"properties": {
"text": "Customer",
"height": 120,
"width": "fill",
"padding": 10,
"spacing": 8
},
"children": [
{ "id": "nameTextBox", "type": "TextBox", "properties": { "width": "fill" } },
{ "id": "emailTextBox", "type": "TextBox", "properties": { "width": "fill" } }
]
}{
"id": "tabs",
"type": "TabControl",
"properties": {
"items": ["Input", "Data"],
"selectedIndex": 0,
"height": 220,
"width": "fill"
},
"events": { "selected": "onTabSelected" },
"children": [
{ "id": "inputPanel", "type": "Panel", "properties": { "height": 80 } },
{ "id": "dataPanel", "type": "Panel", "properties": { "height": 80 } }
]
}{
"id": "openFilePicker",
"type": "FilePicker",
"properties": {
"text": "Open file",
"title": "Open file",
"filter": "Text files (*.txt)|*.txt|All files (*.*)|*.*"
},
"events": { "click": "onOpenFile" }
}{
"id": "mainToolBar",
"type": "ToolBar",
"properties": {
"items": ["New", "Open", "Save", "Refresh"],
"height": 30,
"width": "fill"
},
"events": { "click": "onToolbarClick" }
}{
"id": "mainMenu",
"type": "MenuBar",
"properties": {
"items": ["File", "Edit", "View", "Help"],
"height": 24,
"width": "fill"
},
"events": { "clicked": "onMenuClick" }
}{
"id": "rowContextMenu",
"type": "ContextMenu",
"properties": {
"items": ["Open", "Rename", "Delete"]
},
"events": { "clicked": "onContextMenuClick" }
}{
"id": "statusBar",
"type": "StatusBar",
"properties": { "text": "Ready", "height": 24, "width": "fill" }
}{
"type": "Grid",
"properties": { "columns": 2, "spacing": 8 },
"children": [
{
"id": "navigationTree",
"type": "TreeView",
"properties": {
"items": [
{ "text": "Customers", "children": ["Active", "Archived"] },
{ "text": "Orders", "children": ["Open", "Shipped"] },
{ "text": "Reports", "children": ["Revenue", "Inventory"] }
],
"height": 120
},
"events": { "selected": "onTreeSelected" }
},
{
"id": "customerTable",
"type": "DataGrid",
"properties": {
"columns": ["Name", "Role", "Status"],
"columnWidths": [160, 120, 90],
"items": [
["Ada Lovelace", "Analyst", "Active"],
["Grace Hopper", "Compiler", "Active"],
["Margaret Hamilton", "Apollo", "Active"]
],
"selectedIndex": 0,
"editable": true,
"height": 120
},
"events": { "selected": "onTableSelected" }
}
]
}{
"id": "birthDatePicker",
"type": "DatePicker",
"properties": { "width": 150 },
"events": { "changed": "onDateChanged" }
}{
"id": "appointmentTimePicker",
"type": "TimePicker",
"properties": { "width": 120 },
"events": { "changed": "onDateChanged" }
}Event handlers live in the code-behind file and must accept exactly two parameters:
function onSomething(ui, event)
return 0
end function
ui is the generated handle to windows and controls. event contains:
sender: the control that raised the event.eventType: the event name.oldValue: previous value, when available.newValue: new value, when available.cancel: can be set totruein aclosehandler.
Window events:
loadcloseresized
Common control events:
focus: the control received keyboard focus.blur: the control lost keyboard focus.
Selection and value aliases preserve their event name in event.eventType.
For example, a MSON valueChanged handler receives event.eventType == "valueChanged", not the lower-level Win32 event family used internally.
Close-event example:
{
"id": "mainWindow",
"type": "Window",
"events": { "close": "onMainWindowClose" },
"layout": { "type": "VerticalStack", "children": [] }
}function onMainWindowClose(ui, event)
if MiniGui.Control.getText(ui.nameTextBox) == "" then
event.cancel = true
MiniGui.Control.setText(ui.statusBar, "Please enter a name first.")
end if
return 0
end function
The most useful functions from MiniGui.Control:
MiniGui.Control.setText(control, "Text")
text = MiniGui.Control.getText(control)
MiniGui.Control.setEnabled(control, true)
MiniGui.Control.setVisible(control, false)
MiniGui.Control.setReadOnly(control, true)
MiniGui.Control.setMaxLength(control, 32)
MiniGui.Control.setBounds(control, 20, 20, 200, 30)
MiniGui.Control.setPosition(control, 20, 20)
MiniGui.Control.setSize(control, 200, 30)
MiniGui.Control.setLayout(control, "anchor", "left,top,right", 160, -1, -1, -1)
checked = MiniGui.Control.isChecked(control)
MiniGui.Control.setChecked(control, true)
MiniGui.Control.addItem(control, "Item")
MiniGui.Control.setItems(control, ["A", "B", "C"])
MiniGui.Control.clearItems(control)
index = MiniGui.Control.getSelectedIndex(control)
MiniGui.Control.setSelectedIndex(control, 1)
text = MiniGui.Control.getSelectedText(control)
MiniGui.Control.setScrollRange(control, 0, 100)
MiniGui.Control.setScrollSteps(control, 5, 20)
MiniGui.Control.setScrollValue(control, 50)
value = MiniGui.Control.getScrollValue(control)
MiniGui.Control.setValueRange(control, 0, 100)
MiniGui.Control.setValue(control, 35)
value = MiniGui.Control.getValue(control)
MiniGui.Control.setColumnWidths(dataGrid, [160, 120, 90])
MiniGui.Control.setEditable(dataGrid, true)
MiniGui.Control.setCellText(dataGrid, 0, 1, "Updated")
cell = MiniGui.Control.getCellText(dataGrid, 0, 1)
setLayout(control, resizeMode, anchor, minWidth, minHeight, maxWidth, maxHeight) uses -1 for unset size constraints.
setValue and getValue are useful for Slider, ScrollBar, and
ProgressBar, NumberBox, and SpinBox. For ComboBox and ListBox, use
the selection functions.
When a DataGrid is editable, the user can double-click a cell to open an
inline editor. Press Enter or move focus away to commit the value; press Escape
to cancel the edit.
The most useful functions from MiniGui.Events:
MiniGui.Events.bindClick(app, button, onClick, ui)
MiniGui.Events.bindClicked(app, button, onClick, ui)
MiniGui.Events.bindTextChanged(app, textBox, onText, ui)
MiniGui.Events.bindChange(app, control, onChange, ui)
MiniGui.Events.bindChanged(app, control, onChanged, ui)
MiniGui.Events.bindSelectionChanged(app, list, onSelection, ui)
MiniGui.Events.bindSelected(app, list, onSelected, ui)
MiniGui.Events.bindScrollChanged(app, slider, onScroll, ui)
MiniGui.Events.bindValueChanged(app, slider, onValue, ui)
MiniGui.Events.bindFocus(app, control, onFocus, ui)
MiniGui.Events.bindBlur(app, control, onBlur, ui)
MiniGui includes native message boxes. The original show* helpers return the
native Windows result code:
MiniGui.Dialog.showInfo(ui.app, "Saved", "The record was saved.")
MiniGui.Dialog.showWarning(ui.app, "Check input", "Some fields are incomplete.")
MiniGui.Dialog.showError(ui.app, "Error", "The operation failed.")
if MiniGui.Dialog.confirm(ui.app, "Close", "Close without saving?") then
MiniGui.Window.close(ui.mainWindow)
end if
For new code, prefer the high-level helpers. They return stable strings:
ok, cancel, yes, no, retry, abort, ignore, or unknown.
result = MiniGui.Dialog.showMessage(ui.app, "Delete", "Delete this item?", "yesNoCancel", "question")
if result == "yes" then
MiniGui.Control.setText(ui.statusBar, "Deleted")
end if
answer = MiniGui.Dialog.yesNo(ui.app, "Close", "Close without saving?")
retry = MiniGui.Dialog.retryCancel(ui.app, "Network", "Try again?")
MiniGui.Dialog.info(ui.app, "Saved", "The record was saved.")
MiniGui.Dialog.warning(ui.app, "Check input", "Some fields are incomplete.")
MiniGui.Dialog.errorMessage(ui.app, "Error", "The operation failed.")
Native picker helpers are also available:
path = MiniGui.Dialog.pickOpenFile(ui.app, "Open file", "Text files (*.txt)|*.txt|All files (*.*)|*.*")
target = MiniGui.Dialog.pickSaveFile(ui.app, "Save file", "Text files (*.txt)|*.txt|All files (*.*)|*.*")
folder = MiniGui.Dialog.pickFolder(ui.app, "Choose folder")
color = MiniGui.Dialog.pickColor(ui.app, "Choose color", "#336699")
The picker helpers return an empty string when the user cancels. pickColor
returns the fallback color when cancelled.
MiniGui also provides a native MiniGui-based input dialog helper:
name = MiniGui.Dialog.showInput(ui.app, "Customer", "Customer name", "")
if name != "" then
MiniGui.Control.setText(ui.resultLabel, "Customer: " + name)
end if
For custom modal dialogs, create a dialog window and add normal MiniGui controls
to dialog.window:
dialog = MiniGui.Dialog.createCustom(ui.app, "detailsDialog", "Details", 420, 220)
label = MiniGui.Label.create(ui.app, dialog.window, "detailsLabel", "Custom content", 14, 14, 360, 24)
MiniGui.Dialog.showModal(dialog)
Interactive native dialogs and modal input dialogs are intentionally not opened by the automated test suite, because they would block unattended test runs.
Build a distributable ZIP with the MiniGui CLI, runtime library, schema, documentation, and examples:
powershell -NoProfile -ExecutionPolicy Bypass -File .\tools\package-release.ps1 -Version 0.1.0 -Compiler ..\MiniLangCompilerPy\mlc_win64.pyThe script writes dist/MiniGui-<version>.zip and a matching .sha256 file.
Resources are simple values that are resolved while generating MiniLang code.
{
"resources": {
"windowTitle": "MiniGui Control Gallery",
"heading": "MiniGui controls and attributes"
},
"windows": [
{
"id": "mainWindow",
"type": "Window",
"properties": {
"title": { "$resource": "windowTitle" }
},
"layout": {
"type": "VerticalStack",
"children": [
{
"id": "titleLabel",
"type": "Label",
"properties": { "text": { "$resource": "heading" } }
}
]
}
}
]
}imports loads additional .mson files relative to the importing file or from
the --include-dir search paths.
{
"imports": ["resources/common.mson", "components/address-form.mson"]
}Components are reusable UI building blocks:
{
"components": [
{
"name": "AddressForm",
"content": {
"type": "VerticalStack",
"properties": { "spacing": 8 },
"children": [
{ "id": "streetTextBox", "type": "TextBox", "properties": { "width": "fill" } },
{ "id": "cityTextBox", "type": "TextBox", "properties": { "width": "fill" } }
]
}
}
]
}Use the component by its name:
{
"id": "shippingAddress",
"type": "AddressForm"
}When a component is used multiple times, MiniGui prefixes internal IDs with the instance ID.
examples/hello-gui: a small application withTextBox,Button,Label, and event handlers.examples/control-gallery: a test and demo application covering all controls, attributes, layouts, and event binding paths.examples/customer-form: an example for imports, resources, and components.
Build the control gallery:
.\examples\control-gallery\build.ps1The MiniGui test suite builds the CLI, validates the example applications, checks generated code, starts GUI smoke tests, and verifies interactions such as button clicks, slider changes, and resize behavior.
.\tests\minigui\run_minigui_tests.ps1MiniGui uses native interop helpers from the MiniLang compiler:
nativeCallback(fn, "wndproc")nativeBytesPtr(bytes)nativeRawValue(value)nativeValueFromRaw(int)
MiniGuiLib uses these helpers for Win32 windows, window procedures, native
handles, and callback bridges.
To add a new control:
- Extend control metadata and validation in
tools/minigui.ml. - Implement the runtime constructor in
MiniGuiLib/minigui.ml. - Update
schemas/minigui.schema.json. - Extend the control gallery and tests.
The public MSON shape should stay stable: applications describe the UI declaratively while the runtime hides native implementation details.




























