瀏覽代碼

docs: rewrite README as a project front page

Replace the build-journal README with a conventional project README:
features, requirements, build/test, quick start, examples, grouped
module reference, signals/ownership/errors, binding notes, generator
and repository layout. The previous detailed version remains in 0418ccb.
Eric Streit 4 天之前
父節點
當前提交
c4954cf883
共有 1 個文件被更改,包括 193 次插入 和 365 次删除
  1. 193 365
      README.md

+ 193 - 365
README.md

@@ -1,212 +1,63 @@
-# m2-GTK4 — GTK4 bindings for GNU Modula-2
-
-Thin `DEFINITION MODULE FOR "C"` bindings of **GTK 4** for `gm2`
-(GCC 16 / GNU Modula-2), plus a small pure-Modula-2 helper layer.
-
-The first slice covers the canonical GTK4 application shape
-(`GtkApplication` + `GtkWindow` + `GtkLabel` / `GtkButton` / `GtkBox`),
-a working `"activate"` / `"clicked"` signal path, and the GLib/GObject
-runtime underneath it.  The second slice adds the common input widgets:
-`GtkEntry`/`GtkEditable`, `GtkCheckButton`, `GtkSwitch`, `GtkScale`
-(`GtkRange`), plus a shared `GtkEnums` module.  The third slice adds the
-layout containers: `GtkGrid`, `GtkStack` + `GtkStackSwitcher`,
-`GtkNotebook`, `GtkScrolledWindow`.  The fourth slice adds actions,
-menus and accelerators: GIO's `GAction` / `GActionMap` /
-`GSimpleAction`, `GMenu` / `GMenuModel` / `GVariant`, plus
-`GtkApplicationWindow` and `GtkPopoverMenuBar`.  The fifth slice switches
-the project to the ISO dialect (`-fiso`) and adds per-connection signal
-closures (`GtkClosures`) and window chrome (`GtkHeaderBar`,
-`GtkMenuButton`, `gtk_window_set_titlebar`).  The sixth slice adds more
-inputs: `GtkSpinButton`, `GtkSearchEntry`, `GtkDropDown` (with
-`GtkStringList` / `GListModel`) and `GtkTextView` + `GtkTextBuffer`.
-The seventh slice adds more containers: `GtkFrame`, `GtkExpander`,
-`GtkPaned`, `GtkOverlay`, `GtkListBox` (+ `GtkListBoxRow`).  The eighth
-slice adds GIO extras: `GListStore` (+ `GtkStringObject`), `GFile`,
-`GtkFileDialog` and `GSettings` (+ `GSettingsSchema`).  The ninth slice
-adds model-backed views: `GtkListView`, `GtkGridView`, `GtkColumnView`
-(+ `GtkColumnViewColumn` / `GtkColumnViewCell`), the selection models
-`GtkSingleSelection` / `GtkNoSelection`, and the
-`GtkSignalListItemFactory` / `GtkListItem` pair.  A
-[`tools/gir2def.py`](#generator-broad-coverage) generator additionally
-emits broad `.def` coverage from GObject-Introspection `.gir` XML.
-A tenth slice adds **libadwaita** (AdwApplication, header/toolbar,
-preferences rows, toast, about/message dialogs) and **GError** handling
-plus an ownership helper layer.
-
-Verified with gm2 16.0.1 (experimental) and GTK 4.18.6 on Linux.
-
-## Status
+# m2-GTK4
 
 
-| Module | C header | Contents |
-|---|---|---|
-| `GLib` | `<glib.h>` | base C types, `GMainLoop`, timeouts/idle sources, memory, strings, clock |
-| `GObject` | `<glib-object.h>` | `ref`/`unref`, `GType`, `g_object_new`, `g_signal_connect_data`, handler disconnect, properties |
-| `Gio` | `<gio/gapplication.h>` | `GApplication` flags, `run`/`quit`/`hold`/`release` |
-| `Gtk` | `<gtk/gtk.h>` | `gtk_init`, library version helpers |
-| `GtkApplication` | `<gtk/gtkapplication.h>` | create/add/remove windows |
-| `GtkWidget` | `<gtk/gtkwidget.h>` | visibility, sensitivity, margins, expand, align, size |
-| `GtkWindow` | `<gtk/gtkwindow.h>` | create, title, size, child, present/close/destroy |
-| `GtkBox` | `<gtk/gtkbox.h>` | orientation, append/prepend/remove, spacing |
-| `GtkLabel` | `<gtk/gtklabel.h>` | text, markup |
-| `GtkButton` | `<gtk/gtkbutton.h>` | label buttons |
-| `GtkEnums` | `<gtk/gtkenums.h>` | orientation, alignment, position constants |
-| `GtkEditable` | `<gtk/gtkeditable.h>` | text get/set, editable, alignment, selection |
-| `GtkEntry` | `<gtk/gtkentry.h>` | placeholder, max length, visibility, progress |
-| `GtkCheckButton` | `<gtk/gtkcheckbutton.h>` | active, label, inconsistent |
-| `GtkSwitch` | `<gtk/gtkswitch.h>` | active / state |
-| `GtkRange` | `<gtk/gtkrange.h>` | numeric value (base of GtkScale) |
-| `GtkScale` | `<gtk/gtkscale.h>` | slider, digits, value position |
-| `GtkGrid` | `<gtk/gtkgrid.h>` | cell attach, spacing, homogeneous |
-| `GtkStack` | `<gtk/gtkstack.h>` | named/titled pages, visible child, transitions |
-| `GtkStackSwitcher` | `<gtk/gtkstackswitcher.h>` | button row that drives a stack |
-| `GtkNotebook` | `<gtk/gtknotebook.h>` | tabbed pages |
-| `GtkScrolledWindow` | `<gtk/gtkscrolledwindow.h>` | scrollable viewport, policy |
-| `GtkApplicationWindow` | `<gtk/gtkapplicationwindow.h>` | app window, window-scoped actions, show-menubar |
-| `GtkPopoverMenuBar` | `<gtk/gtkpopovermenubar.h>` | in-window menu bar from a model |
-| `GtkHeaderBar` | `<gtk/gtkheaderbar.h>` | title widget, pack start/end, title buttons, decoration layout |
-| `GtkMenuButton` | `<gtk/gtkmenubutton.h>` | button that pops up a menu model |
-| `GAction` | `<gio/gaction.h>` | activate, name, enabled, state |
-| `GActionMap` | `<gio/gactionmap.h>` | add / lookup / remove named actions |
-| `GSimpleAction` | `<gio/gsimpleaction.h>` | parameterless and stateful actions |
-| `GMenu` | `<gio/gmenu.h>` | GMenu + GMenuItem builder |
-| `GMenuModel` | `<gio/gmenumodel.h>` | model item count |
-| `GVariant` | `<glib/gvariant.h>` | boolean / string / int32 values |
-| `GtkSpinButton` | `<gtk/gtkspinbutton.h>` | numeric entry, range/increments/digits/wrap |
-| `GtkSearchEntry` | `<gtk/gtksearchentry.h>` | search-styled entry, placeholder |
-| `GListModel` | `<gio/glistmodel.h>` | indexed model interface |
-| `GtkStringList` | `<gtk/gtkstringlist.h>` | simple string model |
-| `GtkDropDown` | `<gtk/gtkdropdown.h>` | choose one item from a model |
-| `GtkTextIter` | `<gtk/gtktextiter.h>` | opaque 80-byte iterator (by address) |
-| `GtkTextBuffer` | `<gtk/gtktextbuffer.h>` | text model for a text view |
-| `GtkTextView` | `<gtk/gtktextview.h>` | multi-line text widget |
-| `GtkFrame` | `<gtk/gtkframe.h>` | labelled border around one child |
-| `GtkExpander` | `<gtk/gtkexpander.h>` | collapsible section |
-| `GtkPaned` | `<gtk/gtkpaned.h>` | two panes with a draggable divider |
-| `GtkOverlay` | `<gtk/gtkoverlay.h>` | stack overlays on a main child |
-| `GtkListBox` | `<gtk/gtklistbox.h>` | vertical row list + `GtkListBoxRow` |
-| `GListStore` | `<gio/gliststore.h>` | in-memory list model |
-| `GtkStringObject` | `<gtk/gtkstringobject.h>` | GObject string wrapper (item type) |
-| `GtkSignalListItemFactory` | `<gtk/gtksignallistitemfactory.h>` | setup/bind/teardown item factory |
-| `GtkListItem` | `<gtk/gtklistitem.h>` | per-row object passed to a factory |
-| `GtkSelectionModel` | `<gtk/gtkselectionmodel.h>` | selection interface for a view |
-| `GtkSingleSelection` | `<gtk/gtksingleselection.h>` | zero/one selected item |
-| `GtkNoSelection` | `<gtk/gtknoselection.h>` | display-only (no selection) |
-| `GtkListView` | `<gtk/gtklistview.h>` | model-driven list |
-| `GtkGridView` | `<gtk/gtkgridview.h>` | model-driven wrapping grid |
-| `GtkColumnView` | `<gtk/gtkcolumnview.h>` | model-driven multi-column table |
-| `GtkColumnViewColumn` | `<gtk/gtkcolumnviewcolumn.h>` | one column |
-| `GtkColumnViewCell` | `<gtk/gtkcolumnviewcell.h>` | column cell (`GtkListItem` subclass) |
-| `GFile` | `<gio/gfile.h>` | path/URI file handle |
-| `GtkFileDialog` | `<gtk/gtkfiledialog.h>` | async native open/save/folder dialog |
-| `GSettings` | `<gio/gsettings.h>` | typed schema keys |
-| `GSettingsSchema` | `<gio/gsettingsschema.h>` | schema lookup / key check |
-| `GError` | `<glib/gerror.h>` | error value (message/domain/code) |
-| `GioError` | `<gio/gioerror.h>` | GIO error domain and codes |
-| `AdwApplication` | `<libadwaita-1/adw-application.h>` | Adwaita application |
-| `AdwApplicationWindow` | `<libadwaita-1/adw-application-window.h>` | Adwaita window |
-| `AdwHeaderBar` | `<libadwaita-1/adw-header-bar.h>` | header bar |
-| `AdwToolbarView` | `<libadwaita-1/adw-toolbar-view.h>` | top/bottom bars + content |
-| `AdwPreferencesPage` | `<libadwaita-1/adw-preferences-page.h>` | page of groups |
-| `AdwPreferencesGroup` | `<libadwaita-1/adw-preferences-group.h>` | titled group of rows |
-| `AdwPreferencesRow` | `<libadwaita-1/adw-preferences-row.h>` | base row (title) |
-| `AdwActionRow` | `<libadwaita-1/adw-action-row.h>` | row with subtitle/prefix/suffix |
-| `AdwSwitchRow` | `<libadwaita-1/adw-switch-row.h>` | row with a switch |
-| `AdwEntryRow` | `<libadwaita-1/adw-entry-row.h>` | row with an entry |
-| `AdwComboRow` | `<libadwaita-1/adw-combo-row.h>` | row with a drop-down |
-| `AdwExpanderRow` | `<libadwaita-1/adw-expander-row.h>` | row that expands |
-| `AdwStatusPage` | `<libadwaita-1/adw-status-page.h>` | placeholder page |
-| `AdwToast` | `<libadwaita-1/adw-toast.h>` | transient notification |
-| `AdwToastOverlay` | `<libadwaita-1/adw-toast-overlay.h>` | toast host |
-| `AdwBanner` | `<libadwaita-1/adw-banner.h>` | dismissible info bar |
-| `AdwClamp` | `<libadwaita-1/adw-clamp.h>` | max-size centred container |
-| `AdwAboutWindow` | `<libadwaita-1/adw-about-window.h>` | about dialog |
-| `AdwMessageDialog` | `<libadwaita-1/adw-message-dialog.h>` | message / confirm dialog |
-| `GtkUtils` (`lib/`) | — | `CStrToM2`, `StrAppend`, `IntToStr`, `Connect`, `SetAccel`, `Unref`, `Free`, `ErrorMessage`, `ClearError` |
-| `GtkClosures` (`lib/`) | — | per-connection signal contexts (`Connect2`/`Connect3`, owned variants) |
-
-## Layout
+GNU Modula-2 bindings for **GTK4** and **libadwaita**, for `gm2`
+(GCC 16 / GNU Modula-2) on Linux.
 
 
-```text
-m2-GTK4/
-  src/        FOR "C" .def bindings (one per GTK/GIO object)
-  lib/        pure-M2 helpers built on the bindings (GtkUtils, GtkClosures)
-  tests/      gm2 smoke tests + run_tests.sh (+ a private GSettings schema)
-  examples/   runnable GTK4 programs (hello, counter, ...)
-  tools/      gir2def.py (GIR -> .def generator) + run_gir_check.sh
-  gen/        generator output (git-ignored)
-  build/      generated binaries (git-ignored)
-```
+`m2-GTK4` is a hand-written set of `DEFINITION MODULE FOR "C"` bindings —
+one module per GTK/GIO/libadwaita class — plus a small pure-Modula-2
+helper layer, a test suite, runnable examples, and a generator that can
+emit broad binding coverage from GObject-Introspection `.gir` XML.
+
+It is compiled in the **ISO dialect** (`-fiso`).
 
 
-## Prerequisites
+Verified with `gm2` 16.0.1 (experimental), GTK 4.18.6 and libadwaita 1.7.6.
 
 
-- `gm2` (tested: 16.0.1 experimental, `~/bin/Modula2/Gm2/bin/gm2`)
-- GTK4 dev files (`pkg-config --cflags --libs gtk4`, tested: 4.18.6)
-- libadwaita dev files (`pkg-config --cflags --libs libadwaita-1`, tested:
-  1.7.6) — optional; enables the `Adw*` modules, `test_adw` and the
-  `adw` example. The build detects them automatically.
+## Features
 
 
-The build uses the **ISO dialect** (`-fiso`). That is required for
-`SYSTEM.CAST` and for `NEW`/`DISPOSE` (which need `ALLOCATE`/`DEALLOCATE`
-from the ISO `Storage` module), both used by `GtkClosures`. `make`
-passes `-fiso` by default.
+- **82 hand-written binding modules** across GLib, GObject, Gio, GTK4 and
+  libadwaita — windows, widgets, inputs, containers, model-backed views,
+  actions/menus/accelerators, dialogs, settings, and more.
+- **Pure-Modula-2 helpers**: `GtkUtils` (string/number conversion,
+  `Connect`, `SetAccel`, ownership and `GError` helpers) and
+  `GtkClosures` (per-connection signal contexts with cleanup).
+- **16 tests** and **11 runnable examples**.
+- **Generator + verifier**: `tools/gir2def.py` turns a `.gir` file into
+  `.def` files; `tools/gir_verify.py` checks every generated identifier
+  against the installed library. Verified clean across 8602 identifiers.
+
+## Requirements
+
+- `gm2` (tested: 16.0.1 experimental)
+- GTK4 development files (`pkg-config --cflags --libs gtk4`, tested 4.18.6)
+- libadwaita development files (`pkg-config --cflags --libs libadwaita-1`,
+  tested 1.7.6) — **optional**; enables the `Adw*` modules, `test_adw`
+  and the `adw` example. The build detects them automatically.
 
 
 ```sh
 ```sh
 export PATH=$HOME/bin/Modula2/Gm2/bin:$PATH
 export PATH=$HOME/bin/Modula2/Gm2/bin:$PATH
 ```
 ```
 
 
-## Build & test
+## Build and test
 
 
 ```sh
 ```sh
-make            # tests + examples -> build/
-./tests/run_tests.sh
-./build/examples/hello
-./build/examples/counter
-./build/examples/inputs
-./build/examples/containers
-./build/examples/actions
-./build/examples/headerbar
-./build/examples/editor
-./build/examples/layouts
-./build/examples/files
-./build/examples/views
-./build/examples/adw
+make                 # build all tests and examples into build/
+./tests/run_tests.sh # run the test suite
 ```
 ```
 
 
-`make check` is `make tests` + the runner script. The GUI tests are
+`make check` is `make tests` plus the runner script. GUI tests are
 skipped automatically when neither `DISPLAY` nor `WAYLAND_DISPLAY` is
 skipped automatically when neither `DISPLAY` nor `WAYLAND_DISPLAY` is
-set. The runner also compiles `tests/schemas/*.gschema.xml` into
-`build/schemas` and points `GSETTINGS_SCHEMA_DIR` at it, so `test_gio`
-can exercise `GSettings` without touching real user settings.
-
-## Generator (broad coverage)
+set. The runner also compiles a private GSettings schema into
+`build/schemas` so `test_gio` can exercise `GSettings` without touching
+real user settings.
 
 
-The hand-written modules in `src/` are the idiomatic API. For breadth,
-`tools/gir2def.py` generates `FOR "C"` `.def` files directly from
-GObject-Introspection `.gir` XML:
+Run an example:
 
 
 ```sh
 ```sh
-make gir                # Gtk-4.0 -> gen/   (301 modules, ~3.7k procedures)
-make gir GIR=Gio-2.0    # any namespace
-make gir-check          # generate + compile/link a sample
+./build/examples/hello
+./build/examples/adw
 ```
 ```
 
 
-It emits one module per class/interface/record (named `<Namespace><Name>`,
-e.g. `GtkButton`), namespace functions in `<Namespace>Functions.def`, and
-enums/bitfields in `<Namespace>Enums.def`. Procedure names are the exact
-GIR `c:identifier`, so the output links straight against the C library.
-All 633 generated modules across Gtk/GLib/Gio/Adw parse under `gm2 -fiso`.
-
-`make gir-verify` closes the loop by checking that every generated
-identifier actually exists as an exported symbol in the installed
-library (via `nm`); it reports 0 unexpected across Gtk, GLib, Gio and
-Adw (a short allowlist covers macros/deprecated no-ops).
-
-See `tools/README.md` for the type mapping and the known limits (value
-structs are opaque, signals/properties are not generated, variadics and
-`GError**` are dropped). Output goes to `gen/` and is meant to be
-cherry-picked, not to replace `src/`.
-
-## Usage
+## Quick start
 
 
 ```modula2
 ```modula2
 MODULE hello ;
 MODULE hello ;
@@ -238,184 +89,161 @@ BEGIN
 END hello.
 END hello.
 ```
 ```
 
 
-Compile a program against the bindings:
+Compile a program against the bindings (the helper objects must be built
+first):
 
 
 ```sh
 ```sh
-gm2 -fiso -Isrc -Ilib myprog.mod build/objs/GtkUtils.o \
-    build/objs/GtkClosures.o -o myprog \
+gm2 -fiso -Isrc -Ilib myprog.mod \
+    build/objs/GtkUtils.o build/objs/GtkClosures.o -o myprog \
     $(pkg-config --cflags --libs gtk4)
     $(pkg-config --cflags --libs gtk4)
 ```
 ```
 
 
-## How it was built, step by step
-
-**Step 0 — feasibility spike.** Before writing anything, the riskiest
-interop points were proved against the real library: creating a GTK4
-window from Modula-2, running the GLib main loop, and invoking a
-Modula-2 procedure from C as a `GSourceFunc`. This fixed the design.
-
-**Step 1 — discover the GNU Modula-2 ↔ C rules.** See the next section.
-These rules decide every line of a binding `.def`.
-
-**Step 2 — GLib and GObject.** Everything else sits on the base types,
-the main loop and the signal machinery, so these came first.
-
-**Step 3 — application and window.** `GtkApplication` + `GApplication`
-drive the loop; `GtkWindow` gives something to show. Using
-`gtk_application_window_new(app)` (not `gtk_window_new()`) is what
-keeps the application alive while the window is open.
-
-**Step 4 — widgets and layout.** `GtkBox`, `GtkLabel`, `GtkButton`, and
-the per-widget `GtkWidget` layout helpers.
-
-**Step 5 — helpers and signal ergonomics.** `GtkUtils.Connect` wraps the
-`g_signal_connect` macro (which has no C symbol) by calling the real
-`g_signal_connect_data`; `CStrToM2` and `IntToStr` handle the
-string/number conversions GTK code always needs.
-
-**Step 6 — tests and examples.** A headless GLib loop test, a
-version/link test, a widget + signal round-trip test, and an
-application test; plus `hello` and `counter` examples.
-
-**Step 7 — input widgets.** `GtkEnums` was extracted as the shared home
-for enum constants, then `GtkEditable`/`GtkEntry`,
-`GtkCheckButton`, `GtkSwitch`, `GtkRange`/`GtkScale` were added,
-with a `test_inputs` round-trip test and the `inputs` example. This
-step also pinned down the real-number mapping (`REAL` = C `double`).
-
-**Step 8 — layout containers.** `GtkGrid`, `GtkStack` +
-`GtkStackSwitcher`, `GtkNotebook` and `GtkScrolledWindow`, with a
-`test_containers` test and the `containers` example (a notebook whose
-pages are a grid, a switcher-driven stack, and a scrolled list).
-
-**Step 9 — actions, menus, accelerators.** GIO's action/menu stack
-(`GAction`, `GActionMap`, `GSimpleAction`, `GMenu`, `GMenuModel`,
-`GVariant`), `GtkApplicationWindow`, and `GtkPopoverMenuBar`, wired
-together in the `actions` example (menu bar + app/win actions +
-Ctrl+N/Q/B accelerators via `GtkUtils.SetAccel`) and checked by
-`test_actions`.
-
-**Step 10 — ISO dialect and per-connection closures.** The project was
-switched to `-fiso`, which provides `SYSTEM.CAST` and intrinsic
-`NEW`/`DISPOSE` (with `Storage`'s `ALLOCATE`/`DEALLOCATE`). That made
-the `GtkClosures` module possible: each signal connection gets its own
-heap context holding the handler and payload, a trampoline forwards the
-call, and a GClosure destroy notify frees the context - and optionally
-the payload - on disconnect or finalize. Covered by `test_closure`.
-
-**Step 11 — header bar and window controls.** `GtkHeaderBar`,
-`GtkMenuButton`, and `gtk_window_set_titlebar()` were added, then
-combined in the `headerbar` example (title widget, a connected "New"
-button packed start, a menu button packed end) and checked by
-`test_headerbar`.
-
-**Step 12 — more inputs.** `GtkSpinButton`, `GtkSearchEntry`,
-`GtkStringList` + `GListModel`, `GtkDropDown`, and `GtkTextView` +
-`GtkTextBuffer` with an opaque 80-byte `GtkTextIter` marshalled by
-address. Checked by `test_inputs2` and shown in the `editor` example
-(search entry, text view, wrap-mode drop-down, padding spin button).
-
-**Step 13 — more containers.** `GtkFrame`, `GtkExpander`, `GtkPaned`,
-`GtkOverlay`, and `GtkListBox` + `GtkListBoxRow` (with
-`GtkSelectionMode` in `GtkEnums`). Checked by `test_containers2` and
-shown in the `layouts` example (a paned split, frame/expander on the
-left, overlay + list box on the right, `row-activated` via
-`GtkClosures.Connect3`).
-
-**Step 14 — GIO extras.** `GListStore` + `GtkStringObject` (a concrete
-list model), `GFile`, the asynchronous `GtkFileDialog`, and `GSettings`
-+ `GSettingsSchema`. Checked by `test_gio` (headless) and
-`test_filedialog`; shown in the `files` example (button -> native open
-dialog -> `open_finish` -> `GFile` basename). The GSettings test uses a
-private schema compiled by the runner.
-
-**Step 15 — model-backed views.** `GtkSignalListItemFactory` +
-`GtkListItem`, the selection models `GtkSingleSelection` /
-`GtkNoSelection`, and the views `GtkListView`, `GtkGridView`,
-`GtkColumnView` (+ `GtkColumnViewColumn`, `GtkColumnViewCell`). Checked
-by `test_views`, which presents a window and asserts (from the main
-loop) that the factory "setup"/"bind" callbacks ran, and shown in the
-`views` example (one store feeding list, grid and column views).
-
-**Step 16 — generator verification.** `tools/gir_verify.py` collects every
-`c:identifier` that `gir2def.py` would emit and checks it against the
-exported symbols of the libraries `pkg-config` links for the namespace
-(symbol-version suffixes stripped). `make gir-verify` reports 0
-unexpected across Gtk/GLib/Gio/Adw, with a small allowlist for GIR-listed
-macros and per-module ABI functions. `tools/run_gir_check.sh` also
-bulk-parses every generated module.
-
-**Step 17 — libadwaita.** Curated `Adw*` modules (application, window,
-header bar, toolbar view, preferences page/group and the action/switch/
-entry/combo/expander rows, status page, toast/overlay, banner, clamp,
-about and message dialogs), checked by `test_adw` and shown in the `adw`
-example. libadwaita is optional in the build.
-
-**Step 18 — GError and ownership.** `GError` (with its public struct
-layout) and `GioError` (domain + codes), plus `GtkUtils.Unref`/`Free`/
-`ErrorMessage`/`ClearError` and a documented ownership discipline.
-Checked by `test_gerror` (a real `GIO_ERROR_NOT_FOUND` from
-`g_file_delete`).
-
-## GNU Modula-2 interop rules learned
-
-1. Declare the module `DEFINITION MODULE FOR "C" Name ;` and list every
-   export in `EXPORT UNQUALIFIED`. Without `EXPORT UNQUALIFIED` the
-   compiler prefixes symbols with `Module_`, and the link fails.
-2. Formal parameters in a **procedure type** must be unnamed:
-   `GSourceFunc = PROCEDURE (ADDRESS) : INTEGER;` (named parameters are
-   a syntax error in gm2's default dialect).
-3. A function result that callers may ignore is declared `: [ T ]`
-   (e.g. `PROCEDURE g_timeout_add (...) : [ guint ];`). Without the
-   brackets, ignoring the result is a hard error. This also works in
-   ordinary modules — `GtkUtils.Connect` uses it.
-4. `ARRAY OF CHAR` maps onto `char *`, so string literals and `CHAR`
-   arrays can be passed straight to `const char *` parameters.
-5. `VAR` record/string parameters map onto `T *`; opaque handles
-   (`GObject *`, `GtkWidget *`, ...) travel as `ADDRESS` (use `NIL` to
-   test for `NULL`).
-6. `BOOLEAN` is not `gboolean`: pass `INTEGER` with `GLib.gTrue` /
-   `GLib.gFalse`, or use `INTEGER` directly.
-7. A top-level Modula-2 procedure is a plain C function pointer; pass
-   it directly to a callback-typed parameter, or use `ADR(proc)` where
-   the binding takes an `ADDRESS`/`GCallback`.
-8. **Real numbers:** gm2 `REAL` is 8 bytes and matches C `double`;
-   gm2 `SHORTREAL` is 4 bytes and matches C `float`; gm2 `LONGREAL` is
-   16 bytes and matches nothing in GTK. Use `REAL` for `gdouble` /
-   `double` (e.g. `GtkRange` values, `GtkScale` ranges).
-9. Beware `*)` and `(*` inside comments — they close/open a comment
-   early. Write `void ptr`, not `void *`, in prose.
-10. gm2 has no pointer arithmetic and its `T(x)` cast requires equal
-    sizes; `CStrToM2` therefore copies with libc `strncpy` instead of
-    walking a `char *`.
-11. `GVariant` values from `g_variant_new_*` are *floating*: ownership
-    transfers to the callee (e.g. `g_simple_action_new_stateful`).
-    Values returned by `g_action_get_state()` / `g_variant_get_*` own a
-    reference and must be released with `g_variant_unref()`.
-12. **ISO dialect.** Compiled with `-fiso`: `CAST(T, expr)` (ISO
-    `SYSTEM`) and `NEW`/`DISPOSE` (with `Storage`'s
-    `ALLOCATE`/`DEALLOCATE`) are available there but not in PIM.
-13. When a parameter is typed as a **procedure type**, pass the procedure
-    directly (`Connect2(b, "clicked", OnClick, ...)`), not `ADR(OnClick)`
-    — gm2 type-checks it. Use `ADR(proc)` only where the parameter is a
-    plain `ADDRESS` / `GCallback`.
-14. A large opaque C struct that is always passed by address (e.g.
-    `GtkTextIter`) can be modelled as a record of `ADDRESS` words sized
-    to `sizeof` it: that gives the right size *and* 64-bit alignment.
-    `GtkTextIter` is 80 bytes, hence `ARRAY [0..9] OF ADDRESS`.
-
-## Roadmap
-
-The original roadmap is complete: GLib/GObject core, widgets, inputs,
-containers, actions/menus/accelerators, header bars, GIO extras,
-model-backed views, and the GIR generator are all in.  On top of that:
-generator verification (`make gir-verify`), curated **libadwaita**, and
-**GError** + ownership helpers.
-
-Possible next steps:
-
-1. Hand-curate more of the generated surface (signals, properties,
-   value-struct records, `GtkCssProvider` styling).
-2. Turn the generator's `ADDRESS` placeholders into cross-module type
-   aliases and `[ ]`/`VAR` refinements using the GIR annotations.
-3. Grow the test suite around the newly generated modules.
-4. Prune/promote useful `gen/` output into `src/` as it proves out.
+## Examples
+
+| Example | Shows |
+|---|---|
+| `hello` | the canonical application + window + label |
+| `counter` | button signal updating a label |
+| `inputs` | entry, check button, switch, scale |
+| `containers` | grid, stack/switcher, notebook, scrolled window |
+| `actions` | menu bar + app/window actions + accelerators |
+| `headerbar` | header bar, menu button, connected button |
+| `editor` | search entry, text view, drop-down, spin button |
+| `layouts` | frame, expander, paned, overlay, list box |
+| `files` | native file dialog → `GFile` |
+| `views` | `GtkListView` / `GtkGridView` / `GtkColumnView` from a store |
+| `adw` | libadwaita preferences app (toolbar view, rows, toast, about) |
+
+## Module reference
+
+### Core runtime
+
+| Module | C header | Contents |
+|---|---|---|
+| `GLib` | `<glib.h>` | base types, main loop, timeouts, memory, clock |
+| `GObject` | `<glib-object.h>` | `ref`/`unref`, `GType`, `g_object_new`, signals, properties |
+| `Gio` | `<gio/gapplication.h>` | `GApplication` flags, run/quit/hold |
+| `GVariant` | `<glib/gvariant.h>` | boolean/string/int32 values, refcount |
+| `GError` | `<glib/gerror.h>` | error value (message/domain/code) |
+| `GioError` | `<gio/gioerror.h>` | GIO error domain and codes |
+| `Gtk` | `<gtk/gtk.h>` | `gtk_init`, version helpers |
+| `GtkEnums` | `<gtk/gtkenums.h>` | orientation, alignment, wrap, selection, position |
+
+### GIO objects and models
+
+| Module | Contents |
+|---|---|
+| `GListModel`, `GListStore` | indexed models; in-memory store |
+| `GtkStringList`, `GtkStringObject` | string model / item |
+| `GMenu`, `GMenuModel`, `GSimpleAction`, `GAction`, `GActionMap` | menus and actions |
+| `GFile`, `GtkFileDialog` | file handles and the native dialog |
+| `GSettings`, `GSettingsSchema` | typed settings and schema lookup |
+
+### GTK widgets and views
+
+| Module | Contents |
+|---|---|
+| `GtkApplication`, `GtkApplicationWindow`, `GtkWindow`, `GtkWidget` | application and windows |
+| `GtkBox`, `GtkGrid`, `GtkFrame`, `GtkExpander`, `GtkPaned`, `GtkOverlay`, `GtkNotebook`, `GtkStack`, `GtkStackSwitcher`, `GtkScrolledWindow` | layout containers |
+| `GtkLabel`, `GtkButton`, `GtkCheckButton`, `GtkSwitch`, `GtkScale`, `GtkRange`, `GtkSpinButton`, `GtkEntry`, `GtkEditable`, `GtkSearchEntry`, `GtkDropDown` | controls |
+| `GtkTextView`, `GtkTextBuffer`, `GtkTextIter` | multi-line text |
+| `GtkHeaderBar`, `GtkMenuButton`, `GtkPopoverMenuBar` | window chrome and menus |
+| `GtkListBox`, `GtkListView`, `GtkGridView`, `GtkColumnView`, `GtkColumnViewColumn`, `GtkColumnViewCell`, `GtkListItem`, `GtkSignalListItemFactory`, `GtkSelectionModel`, `GtkSingleSelection`, `GtkNoSelection` | lists, grids and tables |
+
+### libadwaita
+
+| Module | Contents |
+|---|---|
+| `AdwApplication`, `AdwApplicationWindow` | Adwaita application and window |
+| `AdwHeaderBar`, `AdwToolbarView` | header bar and toolbar view |
+| `AdwPreferencesPage`, `AdwPreferencesGroup`, `AdwPreferencesRow` | preferences layout |
+| `AdwActionRow`, `AdwSwitchRow`, `AdwEntryRow`, `AdwComboRow`, `AdwExpanderRow` | settings rows |
+| `AdwStatusPage`, `AdwBanner`, `AdwClamp` | status/banner/clamp |
+| `AdwToast`, `AdwToastOverlay` | transient notifications |
+| `AdwAboutWindow`, `AdwMessageDialog` | about and message dialogs |
+
+### Helpers (pure Modula-2, in `lib/`)
+
+| Module | Contents |
+|---|---|
+| `GtkUtils` | `CStrToM2`, `StrAppend`, `IntToStr`, `Connect`, `SetAccel`, `Unref`, `Free`, `ErrorMessage`, `ClearError` |
+| `GtkClosures` | `Connect2`/`Connect3` (and owned variants) for per-connection signal state |
+
+## Signals, ownership and errors
+
+`GObject.g_signal_connect_data()` is the real function behind the
+`g_signal_connect()` macro. For the common case use the helpers:
+
+```modula2
+Connect(button, "clicked", ADR(OnClicked), NIL);          (* GtkUtils *)
+Connect2(row, "activated", OnActivate, state);            (* GtkClosures: own state *)
+Connect3(dropdown, "notify::selected", OnSelected, NIL);  (* 3-argument form *)
+```
+
+Modula-2 has no destructors, so follow the C transfer rules:
+
+- GObject handles → `GtkUtils.Unref` (`g_object_unref`);
+- strings from getters (`g_file_get_basename`, `g_settings_get_string`, …)
+  → `GtkUtils.Free` (`g_free`);
+- `GError` values → `GtkUtils.ClearError`, and read them with
+  `GtkUtils.ErrorMessage`;
+- `GVariant` from `g_variant_get_*` / `g_action_get_state` → `g_variant_unref`,
+  while `g_variant_new_*` are floating.
+
+## Dialect and binding notes
+
+The build uses the ISO dialect (`-fiso`). Things worth knowing:
+
+- `CAST` and `NEW`/`DISPOSE` (with `Storage.ALLOCATE`/`DEALLOCATE`) come
+  from ISO, not PIM.
+- `EXPORT UNQUALIFIED` makes the C symbol match the Modula-2 name.
+- Formal parameters in a **procedure type** are unnamed:
+  `GSourceFunc = PROCEDURE (ADDRESS) : INTEGER;`.
+- An ignorable result is declared `: [ T ]`; otherwise ignoring it is an
+  error.
+- gm2 `REAL` is C `double`; `SHORTREAL` is `float`; avoid `LONGREAL`.
+- Pass a procedure **directly** where the parameter is a procedure type,
+  and `ADR(proc)` where the parameter is `ADDRESS`/`GCallback`.
+- `ARRAY OF CHAR` maps to `char *`; `VAR` parameters map to `T *`; opaque
+  handles travel as `ADDRESS`.
+- Inside comments, avoid `*)` and `(*` (write `void ptr`, not `void *`).
+- A large opaque struct always passed by address (e.g. `GtkTextIter`,
+  80 bytes) can be modelled as `ARRAY [0..N] OF ADDRESS`.
+
+## Generator (broad coverage)
+
+For breadth beyond the hand-written modules, `tools/gir2def.py` emits
+`FOR "C"` `.def` files from GObject-Introspection `.gir` XML:
+
+```sh
+make gir                  # Gtk-4.0 -> gen/
+make gir GIR=Adw-1        # any namespace
+make gir-check            # generate, parse every module, link a sample
+make gir-verify           # check every identifier exists in the library
+```
+
+It produces one module per class/interface/record, namespace functions in
+`<Namespace>Functions.def`, and enums in `<Namespace>Enums.def`; procedure
+names are the exact GIR `c:identifier`. Across Gtk/GLib/Gio/Adw, 633
+generated modules parse under `gm2 -fiso` and all 8602 identifiers resolve
+in the installed libraries. Output goes to `gen/` (git-ignored) and is
+meant to be cherry-picked, not to replace `src/`. See `tools/README.md`
+for the type mapping and limits.
+
+## Repository layout
+
+```text
+m2-GTK4/
+  src/        FOR "C" .def bindings (one per GTK/GIO/Adw object)
+  lib/        pure-M2 helpers (GtkUtils, GtkClosures)
+  tests/      gm2 tests + run_tests.sh (+ a private GSettings schema)
+  examples/   runnable GTK4 / libadwaita programs
+  tools/      gir2def.py, gir_verify.py, run_gir_check.sh
+  gen/        generator output (git-ignored)
+  build/      generated binaries (git-ignored)
+```
+
+## License
+
+No license has been chosen yet; there is no `LICENSE` file in this
+repository.