Merge pull request #1271 from lightpanda-io/zigdom-event-opts-inherit

Inherit Prototype Event Options
This commit is contained in:
Karl Seguin
2025-12-12 07:26:10 +08:00
committed by GitHub
10 changed files with 184 additions and 85 deletions

View File

@@ -18,6 +18,7 @@
const std = @import("std");
const js = @import("../js/js.zig");
const reflect = @import("../reflect.zig");
const Page = @import("../Page.zig");
const EventTarget = @import("EventTarget.zig");
@@ -195,6 +196,54 @@ pub fn composedPath(self: *Event, page: *Page) ![]const *EventTarget {
return path;
}
pub fn populateFromOptions(self: *Event, opts: anytype) void {
self._bubbles = opts.bubbles;
self._cancelable = opts.cancelable;
self._composed = opts.composed;
}
pub fn inheritOptions(comptime T: type, comptime additions: anytype) type {
var all_fields: []const std.builtin.Type.StructField = &.{};
if (@hasField(T, "_proto")) {
const t_fields = @typeInfo(T).@"struct".fields;
inline for (t_fields) |field| {
if (std.mem.eql(u8, field.name, "_proto")) {
const ProtoType = reflect.Struct(field.type);
if (@hasDecl(ProtoType, "Options")) {
const parent_options = @typeInfo(ProtoType.Options);
all_fields = all_fields ++ parent_options.@"struct".fields;
}
}
}
}
const additions_info = @typeInfo(additions);
all_fields = all_fields ++ additions_info.@"struct".fields;
return @Type(.{
.@"struct" = .{
.layout = .auto,
.fields = all_fields,
.decls = &.{},
.is_tuple = false,
},
});
}
pub fn populatePrototypes(self: anytype, opts: anytype) void {
const T = @TypeOf(self.*);
if (@hasField(T, "_proto")) {
populatePrototypes(self._proto, opts);
}
if (@hasDecl(T, "populateFromOptions")) {
T.populateFromOptions(self, opts);
}
}
pub const JsApi = struct {
pub const bridge = js.Bridge(Event);

View File

@@ -26,23 +26,21 @@ const CompositionEvent = @This();
_proto: *Event,
_data: []const u8 = "",
pub const InitOptions = struct {
const CompositionEventOptions = struct {
data: ?[]const u8 = null,
bubbles: bool = false,
cancelable: bool = false,
};
pub fn init(typ: []const u8, opts_: ?InitOptions, page: *Page) !*CompositionEvent {
const opts = opts_ orelse InitOptions{};
const Options = Event.inheritOptions(CompositionEvent, CompositionEventOptions);
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*CompositionEvent {
const opts = opts_ orelse Options{};
const event = try page._factory.event(typ, CompositionEvent{
._proto = undefined,
._data = if (opts.data) |str| try page.dupeString(str) else "",
});
event._proto._bubbles = opts.bubbles;
event._proto._cancelable = opts.cancelable;
Event.populatePrototypes(event, opts);
return event;
}

View File

@@ -29,25 +29,26 @@ _proto: *Event,
_detail: ?js.Object = null,
_arena: Allocator,
pub const InitOptions = struct {
const CustomEventOptions = struct {
detail: ?js.Object = null,
bubbles: bool = false,
cancelable: bool = false,
};
pub fn init(typ: []const u8, opts_: ?InitOptions, page: *Page) !*CustomEvent {
const Options = Event.inheritOptions(CustomEvent, CustomEventOptions);
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*CustomEvent {
const arena = page.arena;
const opts = opts_ orelse InitOptions{};
const opts = opts_ orelse Options{};
const event = try page._factory.event(typ, CustomEvent{
._arena = arena,
._proto = undefined,
._detail = if (opts.detail) |detail| try detail.persist() else null,
});
event._proto._bubbles = opts.bubbles;
event._proto._cancelable = opts.cancelable;
const event = try page._factory.event(
typ,
CustomEvent{
._arena = arena,
._proto = undefined,
._detail = if (opts.detail) |detail| try detail.persist() else null,
},
);
Event.populatePrototypes(event, opts);
return event;
}

View File

@@ -33,33 +33,34 @@ _column_number: u32 = 0,
_error: ?js.Object = null,
_arena: Allocator,
pub const InitOptions = struct {
pub const ErrorEventOptions = struct {
message: ?[]const u8 = null,
filename: ?[]const u8 = null,
lineno: u32 = 0,
colno: u32 = 0,
@"error": ?js.Object = null,
bubbles: bool = false,
cancelable: bool = false,
};
pub fn init(typ: []const u8, opts_: ?InitOptions, page: *Page) !*ErrorEvent {
const Options = Event.inheritOptions(ErrorEvent, ErrorEventOptions);
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*ErrorEvent {
const arena = page.arena;
const opts = opts_ orelse InitOptions{};
const opts = opts_ orelse Options{};
const event = try page._factory.event(typ, ErrorEvent{
._arena = arena,
._proto = undefined,
._message = if (opts.message) |str| try arena.dupe(u8, str) else "",
._filename = if (opts.filename) |str| try arena.dupe(u8, str) else "",
._line_number = opts.lineno,
._column_number = opts.colno,
._error = if (opts.@"error") |err| try err.persist() else null,
});
event._proto._bubbles = opts.bubbles;
event._proto._cancelable = opts.cancelable;
const event = try page._factory.event(
typ,
ErrorEvent{
._arena = arena,
._proto = undefined,
._message = if (opts.message) |str| try arena.dupe(u8, str) else "",
._filename = if (opts.filename) |str| try arena.dupe(u8, str) else "",
._line_number = opts.lineno,
._column_number = opts.colno,
._error = if (opts.@"error") |err| try err.persist() else null,
},
);
Event.populatePrototypes(event, opts);
return event;
}

View File

@@ -29,27 +29,28 @@ _data: ?js.Object = null,
_origin: []const u8 = "",
_source: ?*Window = null,
pub const InitOptions = struct {
const MessageEventOptions = struct {
data: ?js.Object = null,
origin: ?[]const u8 = null,
source: ?*Window = null,
bubbles: bool = false,
cancelable: bool = false,
};
pub fn init(typ: []const u8, opts_: ?InitOptions, page: *Page) !*MessageEvent {
const opts = opts_ orelse InitOptions{};
const Options = Event.inheritOptions(MessageEvent, MessageEventOptions);
const event = try page._factory.event(typ, MessageEvent{
._proto = undefined,
._data = if (opts.data) |d| try d.persist() else null,
._origin = if (opts.origin) |str| try page.arena.dupe(u8, str) else "",
._source = opts.source,
});
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*MessageEvent {
const opts = opts_ orelse Options{};
event._proto._bubbles = opts.bubbles;
event._proto._cancelable = opts.cancelable;
const event = try page._factory.event(
typ,
MessageEvent{
._proto = undefined,
._data = if (opts.data) |d| try d.persist() else null,
._origin = if (opts.origin) |str| try page.arena.dupe(u8, str) else "",
._source = opts.source,
},
);
Event.populatePrototypes(event, opts);
return event;
}

View File

@@ -30,26 +30,37 @@ _proto: *Event,
_from: *NavigationHistoryEntry,
_navigation_type: ?NavigationType,
pub const EventInit = struct {
const NavigationCurrentEntryChangeEventOptions = struct {
from: *NavigationHistoryEntry,
navigationType: ?[]const u8 = null,
};
const Options = Event.inheritOptions(
NavigationCurrentEntryChangeEvent,
NavigationCurrentEntryChangeEventOptions,
);
pub fn init(
typ: []const u8,
init_obj: EventInit,
opts: Options,
page: *Page,
) !*NavigationCurrentEntryChangeEvent {
const navigation_type = if (init_obj.navigationType) |nav_type_str|
const navigation_type = if (opts.navigationType) |nav_type_str|
std.meta.stringToEnum(NavigationType, nav_type_str)
else
null;
return page._factory.event(typ, NavigationCurrentEntryChangeEvent{
._proto = undefined,
._from = init_obj.from,
._navigation_type = navigation_type,
});
const event = try page._factory.event(
typ,
NavigationCurrentEntryChangeEvent{
._proto = undefined,
._from = opts.from,
._navigation_type = navigation_type,
},
);
Event.populatePrototypes(event, opts);
return event;
}
pub fn asEvent(self: *NavigationCurrentEntryChangeEvent) *Event {

View File

@@ -25,18 +25,28 @@ const Page = @import("../../Page.zig");
// https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent
const PageTransitionEvent = @This();
const EventInit = struct {
persisted: ?bool = null,
};
_proto: *Event,
_persisted: bool,
pub fn init(typ: []const u8, init_obj: EventInit, page: *Page) !*PageTransitionEvent {
return page._factory.event(typ, PageTransitionEvent{
._proto = undefined,
._persisted = init_obj.persisted orelse false,
});
const PageTransitionEventOptions = struct {
persisted: ?bool = false,
};
const Options = Event.inheritOptions(PageTransitionEvent, PageTransitionEventOptions);
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PageTransitionEvent {
const opts = _opts orelse Options{};
const event = try page._factory.event(
typ,
PageTransitionEvent{
._proto = undefined,
._persisted = opts.persisted orelse false,
},
);
Event.populatePrototypes(event, opts);
return event;
}
pub fn asEvent(self: *PageTransitionEvent) *Event {

View File

@@ -25,20 +25,28 @@ const Page = @import("../../Page.zig");
// https://developer.mozilla.org/en-US/docs/Web/API/PopStateEvent
const PopStateEvent = @This();
const EventOptions = struct {
state: ?[]const u8 = null,
};
_proto: *Event,
_state: ?[]const u8,
pub fn init(typ: []const u8, _options: ?EventOptions, page: *Page) !*PopStateEvent {
const options = _options orelse EventOptions{};
const PopStateEventOptions = struct {
state: ?[]const u8 = null,
};
return page._factory.event(typ, PopStateEvent{
._proto = undefined,
._state = options.state,
});
const Options = Event.inheritOptions(PopStateEvent, PopStateEventOptions);
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PopStateEvent {
const opts = _opts orelse Options{};
const event = try page._factory.event(
typ,
PopStateEvent{
._proto = undefined,
._state = opts.state,
},
);
Event.populatePrototypes(event, opts);
return event;
}
pub fn asEvent(self: *PopStateEvent) *Event {

View File

@@ -25,12 +25,28 @@ _total: usize = 0,
_loaded: usize = 0,
_length_computable: bool = false,
pub fn init(typ: []const u8, total: usize, loaded: usize, page: *Page) !*ProgressEvent {
return page._factory.event(typ, ProgressEvent{
._proto = undefined,
._total = total,
._loaded = loaded,
});
const ProgressEventOptions = struct {
total: usize = 0,
loaded: usize = 0,
lengthComputable: bool = false,
};
const Options = Event.inheritOptions(ProgressEvent, ProgressEventOptions);
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*ProgressEvent {
const opts = _opts orelse Options{};
const event = try page._factory.event(
typ,
ProgressEvent{
._proto = undefined,
._total = opts.total,
._loaded = opts.loaded,
},
);
Event.populatePrototypes(event, opts);
return event;
}
pub fn asEvent(self: *ProgressEvent) *Event {

View File

@@ -57,7 +57,11 @@ pub fn dispatch(self: *XMLHttpRequestEventTarget, comptime event_type: DispatchT
};
const progress = progress_ orelse Progress{};
const event = try ProgressEvent.init(typ, progress.total, progress.loaded, page);
const event = try ProgressEvent.init(
typ,
.{ .total = progress.total, .loaded = progress.loaded },
page,
);
return page._event_manager.dispatchWithFunction(
self.asEventTarget(),