Skip to content

Commit 23b02b3

Browse files
committed
Add url_opener example and bindgen Dart templates
Add a new Flutter example (url_opener_example) demonstrating URL opening across platforms and update bindgen configuration and templates for Dart output. Introduce dart_api_profile.yaml and symbol_map.yaml to drive generation (singleton management, value bridges, naming/formatting and symbol overrides) and simplify/extend config.yaml with API rules and formatters. Revise Jinja templates to emit Dart-friendly wrappers: imports for dart:ui and ffi helpers, NativeHandleWrapper/CNativeApiBindingsMixin integration, string/rect/offset/size bridging, improved enum/alias/type handling, and method/property generation. Also update native packages (cnativeapi/nativeapi) and pubspec to reflect the new bindings and API surface.
1 parent 5b61811 commit 23b02b3

142 files changed

Lines changed: 5156 additions & 157 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bindgen/config.yaml

Lines changed: 13 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -4,131 +4,34 @@ clang_flags:
44
- -std=c++17
55

66
entry_headers:
7-
- packages/cnativeapi/cxx_impl/src/accessibility_manager.h
8-
- packages/cnativeapi/cxx_impl/src/application.h
9-
- packages/cnativeapi/cxx_impl/src/application_event.h
10-
- packages/cnativeapi/cxx_impl/src/autostart.h
11-
- packages/cnativeapi/cxx_impl/src/dialog.h
12-
- packages/cnativeapi/cxx_impl/src/display.h
13-
- packages/cnativeapi/cxx_impl/src/display_event.h
14-
- packages/cnativeapi/cxx_impl/src/display_manager.h
15-
- packages/cnativeapi/cxx_impl/src/foundation/color.h
16-
- packages/cnativeapi/cxx_impl/src/foundation/event.h
17-
- packages/cnativeapi/cxx_impl/src/foundation/event_emitter.h
18-
- packages/cnativeapi/cxx_impl/src/foundation/geometry.h
19-
- packages/cnativeapi/cxx_impl/src/foundation/id_allocator.h
20-
- packages/cnativeapi/cxx_impl/src/foundation/keyboard.h
21-
- packages/cnativeapi/cxx_impl/src/foundation/native_object_provider.h
22-
- packages/cnativeapi/cxx_impl/src/foundation/object_registry.h
23-
- packages/cnativeapi/cxx_impl/src/image.h
24-
- packages/cnativeapi/cxx_impl/src/keyboard_event.h
25-
- packages/cnativeapi/cxx_impl/src/keyboard_monitor.h
26-
- packages/cnativeapi/cxx_impl/src/menu.h
27-
- packages/cnativeapi/cxx_impl/src/menu_event.h
28-
- packages/cnativeapi/cxx_impl/src/message_dialog.h
29-
- packages/cnativeapi/cxx_impl/src/placement.h
30-
- packages/cnativeapi/cxx_impl/src/positioning_strategy.h
31-
- packages/cnativeapi/cxx_impl/src/preferences.h
32-
- packages/cnativeapi/cxx_impl/src/secure_storage.h
33-
- packages/cnativeapi/cxx_impl/src/shortcut.h
34-
- packages/cnativeapi/cxx_impl/src/shortcut_event.h
35-
- packages/cnativeapi/cxx_impl/src/shortcut_manager.h
36-
- packages/cnativeapi/cxx_impl/src/storage.h
37-
- packages/cnativeapi/cxx_impl/src/tray_icon.h
38-
- packages/cnativeapi/cxx_impl/src/tray_icon_event.h
39-
- packages/cnativeapi/cxx_impl/src/tray_manager.h
407
- packages/cnativeapi/cxx_impl/src/url_opener.h
41-
- packages/cnativeapi/cxx_impl/src/window.h
42-
- packages/cnativeapi/cxx_impl/src/window_event.h
43-
- packages/cnativeapi/cxx_impl/src/window_manager.h
44-
- packages/cnativeapi/cxx_impl/src/window_registry.h
45-
46-
include_paths:
47-
- packages/cnativeapi/cxx_impl/src
48-
- packages/cnativeapi/cxx_impl/include
498

509
mapping:
51-
language: dart
5210
output_note: Generated wrapper from C++ API. Calls existing ffigen bindings.
5311

5412
naming:
55-
file_name: snake_case
56-
type_name: pascal_case
57-
enum_name: pascal_case
5813
enum_value_name: camel_case
59-
function_name: camel_case
6014
method_name: camel_case
61-
class_name: pascal_case
6215
field_name: camel_case
63-
param_name: camel_case
64-
constant_name: screaming_snake_case
65-
alias_name: pascal_case
66-
strip_prefixes:
67-
- na_
68-
- NA_
69-
- nativeapi_
70-
- NATIVEAPI_
71-
strip_suffixes:
72-
- _t
73-
- _T
74-
add_prefix: ""
75-
add_suffix: ""
7616

7717
types:
78-
void: void
7918
bool: bool
80-
_Bool: bool
81-
char: int
82-
short: int
83-
int: int
84-
long: int
85-
"long long": int
86-
int8_t: int
87-
int16_t: int
88-
int32_t: int
89-
int64_t: int
90-
int8: int
91-
int16: int
92-
int32: int
93-
int64: int
94-
"unsigned char": int
95-
"unsigned short": int
96-
"unsigned int": int
97-
"unsigned long": int
98-
"unsigned long long": int
99-
uint8_t: int
100-
uint16_t: int
101-
uint32_t: int
102-
uint64_t: int
103-
uint8: int
104-
uint16: int
105-
uint32: int
106-
uint64: int
107-
size_t: int
108-
float: double
109-
double: double
110-
float32: double
111-
float64: double
112-
cstring: "ffi.Pointer<ffi.Char>"
113-
"void*": "ffi.Pointer<ffi.Void>"
114-
"const void*": "ffi.Pointer<ffi.Void>"
115-
"char*": "ffi.Pointer<ffi.Char>"
116-
"const char*": "ffi.Pointer<ffi.Char>"
11719

11820
default_type: dynamic
11921
passthrough_unknown: false
12022

12123
options:
122-
bindings_class: CNativeApiBindings
12324
bindings_import: package:cnativeapi/src/bindings_generated.dart
124-
symbol_overrides:
125-
nativeapi::RunApp: native_run_app
126-
nativeapi::Application::GetInstance: native_application_get_instance
127-
nativeapi::Window::Window: native_window_create
128-
129-
filters:
130-
allowlist_regex: []
131-
denylist_regex: []
132-
exclude_dirs:
133-
- capi
134-
- platform
25+
api_type_rules:
26+
contains:
27+
- match: "&"
28+
type: dynamic
29+
force_instance_methods:
30+
- nativeapi::UrlOpener::IsSupported
31+
formatters:
32+
- name: dart-format-generated
33+
cmd:
34+
- dart
35+
- format
36+
- "{out_dir}/src"
37+
continue_on_error: true

bindgen/spec/dart_api_profile.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
version: 1
2+
language: dart
3+
module_profiles:
4+
singleton_manager:
5+
classes:
6+
- AccessibilityManager
7+
- Application
8+
- DisplayManager
9+
- TrayManager
10+
- WindowManager
11+
native_handle_wrapper:
12+
mixin: CNativeApiBindingsMixin
13+
interface: NativeHandleWrapper
14+
value_bridges:
15+
point: Offset
16+
size: Size
17+
rect: Rect
18+
event_model:
19+
mixin: EventEmitter
20+
conventions:
21+
getter_to_property: true
22+
bool_getter_to_property: true
23+
skip_ctor_dtor_methods: true
24+
string_free_default: free_c_str
25+
string_free_overrides:
26+
native_window_get_title: native_window_free_string

bindgen/spec/symbol_map.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
version: 1
2+
language: dart
3+
explicit:
4+
nativeapi::RunApp: native_run_app
5+
nativeapi::Application::GetInstance: native_application_get_instance
6+
nativeapi::DisplayManager::GetInstance: native_display_manager_get_instance
7+
nativeapi::WindowManager::GetInstance: native_window_manager_get_instance
8+
nativeapi::TrayManager::GetInstance: native_tray_manager_get_instance
9+
defaults:
10+
function: native_{snake(name)}
11+
method: native_{snake(class)}_{snake(method)}

bindgen/template/file/dart.j2

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@
1111
// =============================================================================
1212

1313
import 'dart:ffi' as ffi;
14+
import 'dart:ui' as ui;
15+
import 'package:ffi/ffi.dart' as pkgffi;
16+
import 'package:cnativeapi/cnativeapi.dart';
1417
import '{{ mapping.options.bindings_import }}';
18+
import 'package:nativeapi/src/foundation/cnativeapi_bindings_mixin.dart';
19+
import 'package:nativeapi/src/foundation/native_handle_wrapper.dart';
1520

1621
{%- if has_types %}
1722
{% for item in types %}

bindgen/template/partials/alias.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{%- macro render_alias(item, mapping) -%}
2-
{% set target = item.target.mapped if item.target else 'dynamic' %}typedef {{ item.name }} = {% if '::' in target %}dynamic{% else %}{{ target }}{% endif %};
2+
{% set target = item.target.mapped if item.target else 'dynamic' %}typedef {{ item.name }} = {% if '::' in target or '&' in target %}dynamic{% else %}{{ target }}{% endif %};
33
{%- endmacro -%}

bindgen/template/partials/class.j2

Lines changed: 94 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,109 @@
1-
{%- macro capi_for_method(cls, method, mapping) -%}
2-
{%- set overrides = mapping.options.symbol_overrides if mapping.options and mapping.options.symbol_overrides else {} -%}
3-
{%- set key = ((cls.raw.qualified_name if cls.raw and cls.raw.qualified_name else cls.raw.name) ~ '::' ~ method.raw.name) -%}
4-
{%- if key and overrides.get(key) -%}
5-
{{ overrides.get(key) }}
6-
{%- else -%}
7-
native_{{ cls.raw.name | snake_case }}_{{ method.raw.name | snake_case }}
8-
{%- endif -%}
1+
{%- macro render_method_call(method) -%}
2+
{%- if method.return_bridge == 'string' %}
3+
final cString = bindings.{{ method.call_symbol }}(
4+
{%- if method.needs_instance_handle %}
5+
_nativeHandle{% if method.call_args|length > 0 %},{% endif %}
6+
{%- endif %}
7+
{%- for arg in method.call_args %}
8+
{{ arg }}{% if not loop.last %}, {% endif %}
9+
{%- endfor %}
10+
);
11+
if (cString == ffi.nullptr) return '';
12+
final value = cString.cast<pkgffi.Utf8>().toDartString();
13+
bindings.{{ method.string_free_symbol }}(cString);
14+
return value;
15+
{%- elif method.return_bridge == 'offset' %}
16+
final nativeValue = bindings.{{ method.call_symbol }}(
17+
{%- if method.needs_instance_handle %}
18+
_nativeHandle{% if method.call_args|length > 0 %},{% endif %}
19+
{%- endif %}
20+
{%- for arg in method.call_args %}
21+
{{ arg }}{% if not loop.last %}, {% endif %}
22+
{%- endfor %}
23+
);
24+
return ui.Offset(nativeValue.x, nativeValue.y);
25+
{%- elif method.return_bridge == 'size' %}
26+
final nativeValue = bindings.{{ method.call_symbol }}(
27+
{%- if method.needs_instance_handle %}
28+
_nativeHandle{% if method.call_args|length > 0 %},{% endif %}
29+
{%- endif %}
30+
{%- for arg in method.call_args %}
31+
{{ arg }}{% if not loop.last %}, {% endif %}
32+
{%- endfor %}
33+
);
34+
return ui.Size(nativeValue.width, nativeValue.height);
35+
{%- elif method.return_bridge == 'rect' %}
36+
final nativeValue = bindings.{{ method.call_symbol }}(
37+
{%- if method.needs_instance_handle %}
38+
_nativeHandle{% if method.call_args|length > 0 %},{% endif %}
39+
{%- endif %}
40+
{%- for arg in method.call_args %}
41+
{{ arg }}{% if not loop.last %}, {% endif %}
42+
{%- endfor %}
43+
);
44+
return ui.Rect.fromLTWH(nativeValue.x, nativeValue.y, nativeValue.width, nativeValue.height);
45+
{%- elif method.return_bridge == 'void' %}
46+
bindings.{{ method.call_symbol }}(
47+
{%- if method.needs_instance_handle %}
48+
_nativeHandle{% if method.call_args|length > 0 %},{% endif %}
49+
{%- endif %}
50+
{%- for arg in method.call_args %}
51+
{{ arg }}{% if not loop.last %}, {% endif %}
52+
{%- endfor %}
53+
);
54+
{%- else %}
55+
return bindings.{{ method.call_symbol }}(
56+
{%- if method.needs_instance_handle %}
57+
_nativeHandle{% if method.call_args|length > 0 %},{% endif %}
58+
{%- endif %}
59+
{%- for arg in method.call_args %}
60+
{{ arg }}{% if not loop.last %}, {% endif %}
61+
{%- endfor %}
62+
);
63+
{%- endif %}
964
{%- endmacro -%}
1065

1166
{%- macro render_class(item, mapping) -%}
12-
class {{ item.name }} {
13-
{{ item.name }}(this._bindings, this.handle);
67+
class {{ item.name }} with CNativeApiBindingsMixin implements NativeHandleWrapper<{{ item.handle_alias }}> {
68+
{%- if item.is_singleton %}
69+
static final {{ item.name }} instance = {{ item.name }}._();
70+
71+
late final {{ item.handle_alias }} _nativeHandle;
1472

15-
final {{ mapping.options.bindings_class }} _bindings;
16-
final ffi.Pointer<ffi.Void> handle;
73+
{{ item.name }}._() {
74+
_nativeHandle = bindings.{{ item.singleton_symbol }}();
75+
}
76+
{%- else %}
77+
final {{ item.handle_alias }} _nativeHandle;
78+
79+
{{ item.name }}(this._nativeHandle);
80+
{%- endif %}
81+
82+
@override
83+
{{ item.handle_alias }} get nativeHandle => _nativeHandle;
1784

1885
{%- for method in item.methods %}
19-
{%- if not (method.raw and (method.raw.name == item.raw.name or method.raw.name.startswith('~'))) %}
20-
{% set ret = method.return_type.mapped if method.return_type else 'void' %}{% if '::' in ret %}{% set ret = 'dynamic' %}{% endif %}{{ ret }} {{ method.name }}(
86+
{%- if not method.skip %}
87+
{%- if method.is_property %}
88+
{{ 'static ' if method.static else '' }}{{ method.api_return_type }} get {{ method.property_name }} {
89+
{{ render_method_call(method) }}
90+
}
91+
{%- else %}
92+
{{ 'static ' if method.static else '' }}{{ method.api_return_type }} {{ method.api_name }}(
2193
{%- for param in method.params %}
22-
{% set ptype = param.type.mapped %}{% if '::' in ptype %}dynamic{% else %}{{ ptype }}{% endif %} {{ param.name }}{% if not loop.last %}, {% endif %}
94+
{{ param.api_type }} {{ param.name }}{% if not loop.last %}, {% endif %}
2395
{%- endfor %}
2496
) {
25-
{% if method.return_type and method.return_type.kind != 'void' %}return {% endif %}_bindings.{{ capi_for_method(item, method, mapping) }}(
26-
{%- if not method.static %}
27-
handle{% if method.params|length > 0 %}, {% endif %}
28-
{%- endif %}
29-
{%- for param in method.params %}
30-
{{ param.name }}{% if not loop.last %}, {% endif %}
31-
{%- endfor %}
32-
);
97+
{{ render_method_call(method) }}
3398
}
99+
{%- endif %}
34100

35101
{%- endif %}
36102
{%- endfor %}
103+
104+
@override
105+
void dispose() {
106+
// Generated wrappers are non-owning by default.
107+
}
37108
}
38109
{%- endmacro -%}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{%- macro render_constant(item, mapping) -%}
2-
{% set ctype = item.type.mapped if item.type else 'dynamic' %}const {% if '::' in ctype %}dynamic{% else %}{{ ctype }}{% endif %} {{ item.name }} = {{ item.value }};
2+
{% set ctype = item.type.mapped if item.type else 'dynamic' %}const {% if '::' in ctype or '&' in ctype %}dynamic{% else %}{{ ctype }}{% endif %} {{ item.name }} = {{ item.value }};
33
{%- endmacro -%}

bindgen/template/partials/enum.j2

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1+
{%- macro enum_case_name(name) -%}
2+
{%- if name.startswith('k') and name|length > 1 and name[1].isupper() -%}
3+
{{ name[1:2]|lower }}{{ name[2:] }}
4+
{%- else -%}
5+
{{ name | camel_case }}
6+
{%- endif -%}
7+
{%- endmacro -%}
8+
19
{%- macro render_enum(item, mapping) -%}
210
enum {{ item.name }} {
311
{%- for value in item.values %}
4-
{{ value.name }}{% if not loop.last %},{% endif %}
12+
{{ enum_case_name(value.name) }}{% if not loop.last %},{% endif %}
513
{%- endfor %}
614
}
715
{%- endmacro -%}

0 commit comments

Comments
 (0)