Skip to content

Contributing

Marco Breveglieri edited this page Jul 21, 2026 · 2 revisions

Contributing

Blinki is open to contributions — bug reports, feature requests, documentation improvements, and code. This page explains the workflow, the coding standards, and the quality bar we maintain.

Blinki is highly experimental and under active development. APIs may change frequently. If you are building something on top of the library, open an issue first to discuss your plans before investing significant effort.


Reporting Bugs & Requesting Features

Use GitHub Issues at github.com/marcobreveglieri/blinki/issues.

Bug reports — include

  • Delphi / RAD Studio version (e.g. "13.1 Florence, 37.0.52831.7315")
  • Windows version and build (run winver)
  • Exact steps to reproduce — ideally a minimal .dpr that demonstrates the issue
  • Expected behaviour vs actual behaviour
  • Screenshot or terminal recording if the bug is visual

Feature requests — include

  • A clear use case ("I need X because Y")
  • Whether you are willing to implement it yourself (we welcome PRs!)
  • Any existing widget or demo that partially covers the scenario

Delphi Coding Standards

The authoritative reference is STYLE_GUIDE.md in the repository root. The summary below covers the most critical points.

Naming

Entity Convention Examples
Classes, records, enums T prefix + PascalCase TMyRecord, TConnectionState
Framework types TTui prefix TTuiWidget, TTuiCanvas, TTuiKeyEvent
Interfaces I or ITui prefix ITuiConsoleBackend
Exception classes E or ETui prefix ETuiRenderError
Instance fields F prefix, strict private FItemCount, FVisible
Method parameters A prefix AValue, ACanvas, ARect
Local variables L prefix LText, LIndex

Formatting

  • Indentation: 2 spaces per level. Never use tabs.
  • Line length: maximum 100 characters.
  • if body always on a new line — never inline after then:
// Correct
if Assigned(FItems) then
  FreeAndNil(FItems);

// Wrong
if Assigned(FItems) then FreeAndNil(FItems);
  • No alignment padding — use exactly one space around := and after :.
  • Declaration order — within each visibility section, keep members alphabetical, grouped by kind (fields → helpers → public methods/properties).

File encoding

Every .pas and .dpr file must be saved as UTF-8 with BOM (EF BB BF) with CRLF line endings. Without the BOM, DCC32 falls back to Windows-1252 and silently corrupts non-ASCII characters (e.g. box-drawing glyphs, Unicode labels). RAD Studio will refuse to compile LF-only files.

When creating files programmatically (e.g. with a script), normalise line endings with:

(Get-Content MyUnit.pas -Raw) -replace '(?<!\r)\n', "`r`n" | Set-Content MyUnit.pas -NoNewline

License banner

Every source file must start with the 22-line ASCII-art banner before the unit/program keyword. The Unit: field must match the file name exactly. See any existing .pas file for the exact format.

Memory and ownership

  • Parent owns children: always pass the parent widget as the first constructor argument. The parent's destructor frees all children automatically.
  • Use FreeAndNil to release and nil a field simultaneously, with the if Assigned guard on its own line:
if Assigned(FItems) then
  FreeAndNil(FItems);
  • Use try..finally to guarantee cleanup on exception.

Other rules

  • Inline var at the point of first use (Delphi 10.3+ syntax); avoid top-of-routine var blocks for local variables.
  • Guard-and-invalidate in every property setter: compare the new value to the current one, exit early if equal, then assign and call Invalidate.
  • Cardinal overflow: when multiplying Cardinal or Integer by a constant ≥ $80000000, promote all factors to Int64 first.
  • No hardcoded colours: always read from Theme.* (e.g. Theme.Primary, Theme.Border) or use TTuiColors constants. Never write $FF5733 inline in a widget's render method.
  • Comments and XML-doc in English: use /// XML-doc on every public type and member. Comment the why, not the what.

Widget contract

Custom widgets must follow the TTuiWidget hook protocol (see Architecture & Advanced Usage → Custom Widgets):

Hook Purpose
DoInit One-time setup after the widget tree is built; call SetFocusable(True) here if needed
DoRender Draw the widget onto the provided TTuiCanvas within ARect
DoHandleEvent Process keyboard / mouse events; return True if consumed
DoApplyTheme React to a theme change; rebuild cached TTuiStyle values
DoTick Drive animations; called every TickMs milliseconds

Build & Test Before Submitting

All CI checks must pass before a pull request can be merged. Run the same checks locally first.

Full build

# From the repository root:
.\do_build.bat

This compiles the library, all smoke tests, all demos, and the unit tests in sequence.

Individual build targets

& "C:\Program Files (x86)\Embarcadero\Studio\37.0\bin\rsvars.bat"

# Library only
msbuild Source\Blinki.dproj /t:Build /p:Config=Release /p:Platform=Win32

# Smoke tests
msbuild Tests\SmokeTests\Blinki.SmokeTests.groupproj /t:Build /p:Config=Debug /p:Platform=Win32

# Unit tests (DUnitX — requires DUnitX in the Unit Search Path)
msbuild Tests\UnitTests\Core\Blinki.UnitTests.Core.dproj /t:Build /p:Config=Debug /p:Platform=Win32
.\Tests\UnitTests\Core\Win32\Debug\Blinki.UnitTests.Core.exe

Note: The unit test runner exits with code 1 if any test fails and 2 on an unhandled exception, so MSBuild or a CI script can detect failures automatically. It requires DUnitX to be present on the search path.

Smoke tests

The 12 smoke-test programs in Tests\SmokeTests\ exercise individual layers of the framework (canvas, layout, widgets, dialogs, FX, emoji, …). Run each .exe and verify it exits without errors or assertion failures.


Pull Request Workflow

1. Fork and create a branch

git checkout -b feature/my-new-widget
# or
git checkout -b fix/button-focus-lost

Branch name conventions:

Prefix Use for
feature/ New widgets, new APIs
fix/ Bug fixes
refactor/ Internal changes with no user-visible effect
docs/ Documentation only
test/ New or improved tests

2. Write code that passes the style guide

  • Run the full build before committing (do_build.bat).
  • Write or update smoke/unit tests when adding new behaviour.
  • Add XML-doc /// comments to every new public member.

3. Commit messages

Use the imperative mood in the subject line, capitalised, no trailing period, 72 characters or fewer:

Add TTuiSpinnerWidget with configurable frame rate
Fix TTuiTextInput cursor off-by-one on wide characters
Refactor TTuiCanvas.Flush to reduce heap allocations
  • Keep each commit atomic: one logical change per commit.
  • Reference the GitHub issue number in the body when applicable: Closes #42.

4. Open the pull request

Push your branch and open a PR against main:

git push origin feature/my-new-widget

In the PR description include:

  • What the change does and why it is needed.
  • A reference to any related GitHub issue.
  • Instructions for how to test / reproduce the new behaviour.
  • Screenshots or recordings for visual changes.

All CI checks (build + tests) must be green before the PR can be merged. A maintainer will review the code for correctness and adherence to the style guide.