Skip to content

MiniLangProject/MiniGui

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MiniGui

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:

  • .mson describes windows, layouts, controls, attributes, and events.
  • .ml contains your application logic and event handlers.
  • tools/minigui.ml is the MiniGui command-line tool.
  • MiniGuiLib/minigui.ml is the runtime library for native Win32 controls.

Screenshot

The minimal hello example is built from examples/hello-gui/app.mson and examples/hello-gui/app.ml.

MiniGui hello example at startup

MiniGui hello example after greeting

Project Layout

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.

Quick Start

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 ..\MiniLangCompilerPy

Validate, 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.py

Run the finished application:

.\examples\hello-gui\build\hello-gui.exe

CLI

.\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 the codeBehind value from .mson.
  • --generated-dir <dir> chooses the directory for generated files.
  • --include-dir <dir> adds MSON import search paths.
  • --no-compile generates .gui.ml without compiling an executable.
  • --keep-generated keeps generated files after build.

A Minimal Application

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.

MSON Structure

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 always 1.
  • namespace: the MiniLang package for the generated application.
  • application.name: the application name.
  • application.startupWindow: the id of 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 .mson files.
  • components: reusable UI building blocks.

Layouts

Layouts group and position controls.

VerticalStack

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" } }
  ]
}

HorizontalStack

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 } }
  ]
}

Grid

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"] } }
  ]
}

Canvas

Places controls at fixed x and y coordinates.

{
  "type": "Canvas",
  "children": [
    { "id": "okButton", "type": "Button", "properties": { "text": "OK", "x": 20, "y": 20, "width": 100 } }
  ]
}

DockPanel

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" } }
  ]
}

WrapPanel

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 } }
  ]
}

Common Attributes

These properties can be used on controls:

  • text: visible text.
  • width, height: a number, "auto", or "fill".
  • x, y: position inside a Canvas.
  • visible: true or false.
  • enabled: true or false.
  • 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: true sets the width to the available space.
  • resizeMode: runtime resize behavior. Supported values are "scale", "none", "anchor", and "fill".
  • anchor: edge anchors for resizeMode: "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
  }
}

Resize Behavior

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
  }
}

Controls

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

Control Screenshot Gallery

These screenshots are cropped from the MiniGui Control Gallery example.

Control Screenshot
Label Label
Button Button
TextBox TextBox
TextArea TextArea
PasswordBox PasswordBox
NumberBox NumberBox
CheckBox CheckBox
RadioButton RadioButton
Image Image
Separator Separator
LinkLabel LinkLabel
Panel Panel
ScrollViewer ScrollViewer
GroupBox GroupBox
ComboBox ComboBox
ListBox ListBox
ScrollBar ScrollBar
Slider Slider
ProgressBar ProgressBar
TabControl TabControl
MenuBar MenuBar
ToolBar ToolBar
StatusBar StatusBar
TreeView TreeView
ListView ListView
Table Table
DatePicker DatePicker

Control Examples

Button

{
  "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

TextBox and TextArea

{
  "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

PasswordBox and NumberBox

{
  "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

CheckBox and RadioButton

{
  "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

ComboBox and ListBox

{
  "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

Slider, ScrollBar, and ProgressBar

{
  "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

Panel and GroupBox

{
  "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" } }
  ]
}

TabControl

{
  "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 } }
  ]
}

ToolBar, MenuBar, ContextMenu, and StatusBar

{
  "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" }
}

TreeView, ListView, DataGrid, and Date Controls

{
  "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" }
}

Events and Code-Behind

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 to true in a close handler.

Window events:

  • load
  • close
  • resized

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

Runtime API

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)

Dialog API

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.

Release Package

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.py

The script writes dist/MiniGui-<version>.zip and a matching .sha256 file.

Resources

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 and Components

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

  • examples/hello-gui: a small application with TextBox, 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.ps1

Tests

The 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.ps1

Compiler Requirements

MiniGui 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.

Extending MiniGui

To add a new control:

  1. Extend control metadata and validation in tools/minigui.ml.
  2. Implement the runtime constructor in MiniGuiLib/minigui.ml.
  3. Update schemas/minigui.schema.json.
  4. 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.

About

A Library for MiniLang for easily creating user interfaces in Windows

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors