開発
UIngは、Crystalでネイティブアプリケーションを構築するための、小さく持続可能な基盤です。 変更を加えるときは、安定性、クロスプラットフォームの動作、既存のライフタイム規則を 維持してください。
APIレベル
| レベル | 定義場所 | 例 | 用途 |
|---|---|---|---|
| 高レベル | src/uing/*.cr | button.on_clicked { } | オブジェクト指向のアプリケーションAPI |
| 低レベル | src/uing/lib_ui/lib_ui.cr | UIng::LibUI.new_button | libui-ngへの直接バインディング |
高レベルAPIはWindow、Label、Buttonなどの基本コントロールに加え、Tableや
Areaなどの高度なコントロールも提供します。通常のアプリケーションコードでは
こちらを使います。UIng::LibUIは、基盤となるC APIへの直接アクセスと、高レベル
ラッパーの実装に使用します。
低レベルバインディングとネイティブライブラリ
低レベルバインディングは、当初
crystal_libで生成していました。
生成後にもLibC::Intから高レベルのBoolへの変換など、多くの手動調整が必要だったため、
現在はAIの支援も利用しながら直接保守しています。
コンポーネントを追加するときは、既存コントロールのコールバックとライフタイムの
パターンに従います。ネイティブのlibui-ngは
kojix2/libui-ngのGitHub Actionsでビルドされ、
UIng固有の拡張はdevブランチで保守されます。Windowsではcomctl32.manifestにより
Common Controls v6を選択し、OS標準コントロールに新しい視覚スタイルを適用します。
メモリ安全性
CrystalのGC付きランタイムとネイティブC APIを安全に接続するため、UIngは次の仕組みを 使用します。
- コントロールレジストリがネイティブ側の破棄までラッパーを保持し、親への参照が ネイティブのコントロールツリーを反映します。
- ネイティブコードから呼び出される可能性がある間、Box化したコールバックを保持します。
AreaとTableの拡張ハンドラ構造体は、ネイティブ互換の基本ハンドラと一緒に コールバックデータを格納します。静的なトランポリンがそのデータを復元してCrystalの クロージャを呼び出します。- コールバック中だけ借用されるラッパーは、コールバック終了時に無効化されます。
利用者向けの所有権と解放規則は ランタイムとライフタイムを参照してください。
低レベルコンテキストでのクロージャ
libui-ngの多くのコールバックはdataポインタを受け取るため、UIngはこれを使って
Crystalのクロージャを保持・復元します。ネイティブ構造体へ直接格納される関数ポインタの
一部には、この引数がありません。その場合はハンドラ構造体へBox化したデータを追加し、
C互換のトランポリンから拡張構造体へキャストして利用します。TableとAreaがこの
パターンを使用します。
生成AIの利用
GitHub Actions、複雑なサンプル、メモリ安全性のレビュー、パッチ適用版libui-ngの開発に 生成AIを利用しています。UIngは反復的な実装と、生成コードに対する人間の行単位レビュー から始まりました。現在の人間の作業は、全体設計、ネイティブGUIの目視確認、実際の利用を 通じた改善点の発見に重点を置いています。
コントリビューション
- リポジトリをforkしてPull Requestを送ってください。
- 再現手順、プラットフォーム、Crystalのバージョンを添えてバグを報告してください。
- 表示動作を変更するときは、クロスプラットフォームのサンプルも追加・更新してください。
- UIngに関する記事や利用報告も歓迎します。