mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
add FocusEvent, TextEvent and WheelEvent
This commit is contained in:
@@ -817,6 +817,9 @@ pub const JsApis = flattenTypes(&.{
|
||||
@import("../webapi/event/MouseEvent.zig"),
|
||||
@import("../webapi/event/PointerEvent.zig"),
|
||||
@import("../webapi/event/KeyboardEvent.zig"),
|
||||
@import("../webapi/event/FocusEvent.zig"),
|
||||
@import("../webapi/event/WheelEvent.zig"),
|
||||
@import("../webapi/event/TextEvent.zig"),
|
||||
@import("../webapi/event/PromiseRejectionEvent.zig"),
|
||||
@import("../webapi/MessageChannel.zig"),
|
||||
@import("../webapi/MessagePort.zig"),
|
||||
|
||||
22
src/browser/tests/event/focus.html
Normal file
22
src/browser/tests/event/focus.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../testing.js"></script>
|
||||
<script id=default>
|
||||
let event = new FocusEvent('focus');
|
||||
testing.expectEqual('focus', event.type);
|
||||
testing.expectEqual(true, event instanceof FocusEvent);
|
||||
testing.expectEqual(true, event instanceof UIEvent);
|
||||
testing.expectEqual(true, event instanceof Event);
|
||||
testing.expectEqual(null, event.relatedTarget);
|
||||
</script>
|
||||
|
||||
<script id=parameters>
|
||||
let div = document.createElement('div');
|
||||
let focusEvent = new FocusEvent('blur', { relatedTarget: div });
|
||||
testing.expectEqual(div, focusEvent.relatedTarget);
|
||||
</script>
|
||||
|
||||
<script id=createEvent>
|
||||
let evt = document.createEvent('focusevent');
|
||||
testing.expectEqual(true, evt instanceof FocusEvent);
|
||||
testing.expectEqual(true, evt instanceof UIEvent);
|
||||
</script>
|
||||
17
src/browser/tests/event/text.html
Normal file
17
src/browser/tests/event/text.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../testing.js"></script>
|
||||
<script id=createEvent>
|
||||
let evt = document.createEvent('TextEvent');
|
||||
testing.expectEqual(true, evt instanceof TextEvent);
|
||||
testing.expectEqual(true, evt instanceof UIEvent);
|
||||
testing.expectEqual('', evt.data);
|
||||
</script>
|
||||
|
||||
<script id=initTextEvent>
|
||||
let textEvent = document.createEvent('TextEvent');
|
||||
textEvent.initTextEvent('textInput', true, false, window, 'test data');
|
||||
testing.expectEqual('textInput', textEvent.type);
|
||||
testing.expectEqual('test data', textEvent.data);
|
||||
testing.expectEqual(true, textEvent.bubbles);
|
||||
testing.expectEqual(false, textEvent.cancelable);
|
||||
</script>
|
||||
30
src/browser/tests/event/wheel.html
Normal file
30
src/browser/tests/event/wheel.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../testing.js"></script>
|
||||
<script id=default>
|
||||
let event = new WheelEvent('wheel');
|
||||
testing.expectEqual('wheel', event.type);
|
||||
testing.expectEqual(true, event instanceof WheelEvent);
|
||||
testing.expectEqual(true, event instanceof MouseEvent);
|
||||
testing.expectEqual(true, event instanceof UIEvent);
|
||||
testing.expectEqual(0, event.deltaX);
|
||||
testing.expectEqual(0, event.deltaY);
|
||||
testing.expectEqual(0, event.deltaZ);
|
||||
testing.expectEqual(0, event.deltaMode);
|
||||
</script>
|
||||
|
||||
<script id=parameters>
|
||||
let wheelEvent = new WheelEvent('wheel', {
|
||||
deltaX: 10,
|
||||
deltaY: 20,
|
||||
deltaMode: WheelEvent.DOM_DELTA_LINE
|
||||
});
|
||||
testing.expectEqual(10, wheelEvent.deltaX);
|
||||
testing.expectEqual(20, wheelEvent.deltaY);
|
||||
testing.expectEqual(1, wheelEvent.deltaMode);
|
||||
</script>
|
||||
|
||||
<script id=constants>
|
||||
testing.expectEqual(0, WheelEvent.DOM_DELTA_PIXEL);
|
||||
testing.expectEqual(1, WheelEvent.DOM_DELTA_LINE);
|
||||
testing.expectEqual(2, WheelEvent.DOM_DELTA_PAGE);
|
||||
</script>
|
||||
@@ -358,6 +358,16 @@ pub fn createEvent(_: *const Document, event_type: []const u8, page: *Page) !*@i
|
||||
return (try UIEvent.init("", null, page)).asEvent();
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, normalized, "focusevent") or std.mem.eql(u8, normalized, "focusevents")) {
|
||||
const FocusEvent = @import("event/FocusEvent.zig");
|
||||
return (try FocusEvent.init("", null, page)).asEvent();
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, normalized, "textevent") or std.mem.eql(u8, normalized, "textevents")) {
|
||||
const TextEvent = @import("event/TextEvent.zig");
|
||||
return (try TextEvent.init("", null, page)).asEvent();
|
||||
}
|
||||
|
||||
return error.NotSupported;
|
||||
}
|
||||
|
||||
|
||||
91
src/browser/webapi/event/FocusEvent.zig
Normal file
91
src/browser/webapi/event/FocusEvent.zig
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const String = @import("../../../string.zig").String;
|
||||
const Page = @import("../../Page.zig");
|
||||
const js = @import("../../js/js.zig");
|
||||
|
||||
const Event = @import("../Event.zig");
|
||||
const EventTarget = @import("../EventTarget.zig");
|
||||
const UIEvent = @import("UIEvent.zig");
|
||||
|
||||
const FocusEvent = @This();
|
||||
|
||||
_proto: *UIEvent,
|
||||
_related_target: ?*EventTarget = null,
|
||||
|
||||
pub const FocusEventOptions = struct {
|
||||
relatedTarget: ?*EventTarget = null,
|
||||
};
|
||||
|
||||
pub const Options = Event.inheritOptions(
|
||||
FocusEvent,
|
||||
FocusEventOptions,
|
||||
);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*FocusEvent {
|
||||
const arena = try page.getArena(.{ .debug = "FocusEvent" });
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
|
||||
const opts = _opts orelse Options{};
|
||||
|
||||
const event = try page._factory.uiEvent(
|
||||
arena,
|
||||
type_string,
|
||||
FocusEvent{
|
||||
._proto = undefined,
|
||||
._related_target = opts.relatedTarget,
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts, false);
|
||||
return event;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *FocusEvent, shutdown: bool) void {
|
||||
self._proto.deinit(shutdown);
|
||||
}
|
||||
|
||||
pub fn asEvent(self: *FocusEvent) *Event {
|
||||
return self._proto.asEvent();
|
||||
}
|
||||
|
||||
pub fn getRelatedTarget(self: *const FocusEvent) ?*EventTarget {
|
||||
return self._related_target;
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(FocusEvent);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "FocusEvent";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
pub const weak = true;
|
||||
pub const finalizer = bridge.finalizer(FocusEvent.deinit);
|
||||
};
|
||||
|
||||
pub const constructor = bridge.constructor(FocusEvent.init, .{});
|
||||
pub const relatedTarget = bridge.accessor(FocusEvent.getRelatedTarget, null, .{});
|
||||
};
|
||||
|
||||
const testing = @import("../../../testing.zig");
|
||||
test "WebApi: FocusEvent" {
|
||||
try testing.htmlRunner("event/focus.html", .{});
|
||||
}
|
||||
@@ -40,6 +40,7 @@ pub const MouseButton = enum(u8) {
|
||||
pub const Type = union(enum) {
|
||||
generic,
|
||||
pointer_event: *PointerEvent,
|
||||
wheel_event: *@import("WheelEvent.zig"),
|
||||
};
|
||||
|
||||
_type: Type,
|
||||
@@ -123,6 +124,7 @@ pub fn is(self: *MouseEvent, comptime T: type) ?*T {
|
||||
switch (self._type) {
|
||||
.generic => return if (T == MouseEvent) self else null,
|
||||
.pointer_event => |e| return if (T == PointerEvent) e else null,
|
||||
.wheel_event => |e| return if (T == @import("WheelEvent.zig")) e else null,
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
115
src/browser/webapi/event/TextEvent.zig
Normal file
115
src/browser/webapi/event/TextEvent.zig
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const String = @import("../../../string.zig").String;
|
||||
const Page = @import("../../Page.zig");
|
||||
const js = @import("../../js/js.zig");
|
||||
|
||||
const Event = @import("../Event.zig");
|
||||
const UIEvent = @import("UIEvent.zig");
|
||||
|
||||
const TextEvent = @This();
|
||||
|
||||
_proto: *UIEvent,
|
||||
_data: []const u8 = "",
|
||||
|
||||
pub const TextEventOptions = struct {
|
||||
data: ?[]const u8 = null,
|
||||
};
|
||||
|
||||
pub const Options = Event.inheritOptions(
|
||||
TextEvent,
|
||||
TextEventOptions,
|
||||
);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*TextEvent {
|
||||
const arena = try page.getArena(.{ .debug = "TextEvent" });
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
|
||||
const opts = _opts orelse Options{};
|
||||
|
||||
const event = try page._factory.uiEvent(
|
||||
arena,
|
||||
type_string,
|
||||
TextEvent{
|
||||
._proto = undefined,
|
||||
._data = if (opts.data) |str| try arena.dupe(u8, str) else "",
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts, false);
|
||||
return event;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *TextEvent, shutdown: bool) void {
|
||||
self._proto.deinit(shutdown);
|
||||
}
|
||||
|
||||
pub fn asEvent(self: *TextEvent) *Event {
|
||||
return self._proto.asEvent();
|
||||
}
|
||||
|
||||
pub fn getData(self: *const TextEvent) []const u8 {
|
||||
return self._data;
|
||||
}
|
||||
|
||||
pub fn initTextEvent(
|
||||
self: *TextEvent,
|
||||
typ: []const u8,
|
||||
bubbles: bool,
|
||||
cancelable: bool,
|
||||
view: ?*@import("../Window.zig"),
|
||||
data: []const u8,
|
||||
) !void {
|
||||
_ = view; // view parameter is ignored in modern implementations
|
||||
|
||||
const event = self._proto._proto;
|
||||
if (event._event_phase != .none) {
|
||||
// Only allow initialization if event hasn't been dispatched
|
||||
return;
|
||||
}
|
||||
|
||||
const arena = event._arena;
|
||||
event._type_string = try String.init(arena, typ, .{});
|
||||
event._bubbles = bubbles;
|
||||
event._cancelable = cancelable;
|
||||
self._data = try arena.dupe(u8, data);
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(TextEvent);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "TextEvent";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
pub const weak = true;
|
||||
pub const finalizer = bridge.finalizer(TextEvent.deinit);
|
||||
};
|
||||
|
||||
// No constructor - TextEvent is created via document.createEvent('TextEvent')
|
||||
pub const data = bridge.accessor(TextEvent.getData, null, .{});
|
||||
pub const initTextEvent = bridge.function(TextEvent.initTextEvent, .{});
|
||||
};
|
||||
|
||||
const testing = @import("../../../testing.zig");
|
||||
test "WebApi: TextEvent" {
|
||||
try testing.htmlRunner("event/text.html", .{});
|
||||
}
|
||||
@@ -34,6 +34,8 @@ pub const Type = union(enum) {
|
||||
generic,
|
||||
mouse_event: *@import("MouseEvent.zig"),
|
||||
keyboard_event: *@import("KeyboardEvent.zig"),
|
||||
focus_event: *@import("FocusEvent.zig"),
|
||||
text_event: *@import("TextEvent.zig"),
|
||||
};
|
||||
|
||||
pub const UIEventOptions = struct {
|
||||
@@ -83,6 +85,8 @@ pub fn is(self: *UIEvent, comptime T: type) ?*T {
|
||||
return e.is(T);
|
||||
},
|
||||
.keyboard_event => |e| return if (T == @import("KeyboardEvent.zig")) e else null,
|
||||
.focus_event => |e| return if (T == @import("FocusEvent.zig")) e else null,
|
||||
.text_event => |e| return if (T == @import("TextEvent.zig")) e else null,
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
136
src/browser/webapi/event/WheelEvent.zig
Normal file
136
src/browser/webapi/event/WheelEvent.zig
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const String = @import("../../../string.zig").String;
|
||||
const Page = @import("../../Page.zig");
|
||||
const js = @import("../../js/js.zig");
|
||||
|
||||
const Event = @import("../Event.zig");
|
||||
const MouseEvent = @import("MouseEvent.zig");
|
||||
|
||||
const WheelEvent = @This();
|
||||
|
||||
_proto: *MouseEvent,
|
||||
_delta_x: f64,
|
||||
_delta_y: f64,
|
||||
_delta_z: f64,
|
||||
_delta_mode: u32,
|
||||
|
||||
pub const DOM_DELTA_PIXEL: u32 = 0x00;
|
||||
pub const DOM_DELTA_LINE: u32 = 0x01;
|
||||
pub const DOM_DELTA_PAGE: u32 = 0x02;
|
||||
|
||||
pub const WheelEventOptions = struct {
|
||||
deltaX: f64 = 0.0,
|
||||
deltaY: f64 = 0.0,
|
||||
deltaZ: f64 = 0.0,
|
||||
deltaMode: u32 = 0,
|
||||
};
|
||||
|
||||
pub const Options = Event.inheritOptions(
|
||||
WheelEvent,
|
||||
WheelEventOptions,
|
||||
);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*WheelEvent {
|
||||
const arena = try page.getArena(.{ .debug = "WheelEvent" });
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
|
||||
const opts = _opts orelse Options{};
|
||||
|
||||
const event = try page._factory.mouseEvent(
|
||||
arena,
|
||||
type_string,
|
||||
MouseEvent{
|
||||
._type = .{ .wheel_event = undefined },
|
||||
._proto = undefined,
|
||||
._screen_x = opts.screenX,
|
||||
._screen_y = opts.screenY,
|
||||
._client_x = opts.clientX,
|
||||
._client_y = opts.clientY,
|
||||
._ctrl_key = opts.ctrlKey,
|
||||
._shift_key = opts.shiftKey,
|
||||
._alt_key = opts.altKey,
|
||||
._meta_key = opts.metaKey,
|
||||
._button = std.meta.intToEnum(MouseEvent.MouseButton, opts.button) catch return error.TypeError,
|
||||
._related_target = opts.relatedTarget,
|
||||
},
|
||||
WheelEvent{
|
||||
._proto = undefined,
|
||||
._delta_x = opts.deltaX,
|
||||
._delta_y = opts.deltaY,
|
||||
._delta_z = opts.deltaZ,
|
||||
._delta_mode = opts.deltaMode,
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts, false);
|
||||
return event;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *WheelEvent, shutdown: bool) void {
|
||||
self._proto.deinit(shutdown);
|
||||
}
|
||||
|
||||
pub fn asEvent(self: *WheelEvent) *Event {
|
||||
return self._proto.asEvent();
|
||||
}
|
||||
|
||||
pub fn getDeltaX(self: *const WheelEvent) f64 {
|
||||
return self._delta_x;
|
||||
}
|
||||
|
||||
pub fn getDeltaY(self: *const WheelEvent) f64 {
|
||||
return self._delta_y;
|
||||
}
|
||||
|
||||
pub fn getDeltaZ(self: *const WheelEvent) f64 {
|
||||
return self._delta_z;
|
||||
}
|
||||
|
||||
pub fn getDeltaMode(self: *const WheelEvent) u32 {
|
||||
return self._delta_mode;
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(WheelEvent);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "WheelEvent";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
pub const weak = true;
|
||||
pub const finalizer = bridge.finalizer(WheelEvent.deinit);
|
||||
};
|
||||
|
||||
pub const constructor = bridge.constructor(WheelEvent.init, .{});
|
||||
pub const deltaX = bridge.accessor(WheelEvent.getDeltaX, null, .{});
|
||||
pub const deltaY = bridge.accessor(WheelEvent.getDeltaY, null, .{});
|
||||
pub const deltaZ = bridge.accessor(WheelEvent.getDeltaZ, null, .{});
|
||||
pub const deltaMode = bridge.accessor(WheelEvent.getDeltaMode, null, .{});
|
||||
pub const DOM_DELTA_PIXEL = bridge.property(WheelEvent.DOM_DELTA_PIXEL, .{ .template = true });
|
||||
pub const DOM_DELTA_LINE = bridge.property(WheelEvent.DOM_DELTA_LINE, .{ .template = true });
|
||||
pub const DOM_DELTA_PAGE = bridge.property(WheelEvent.DOM_DELTA_PAGE, .{ .template = true });
|
||||
};
|
||||
|
||||
const testing = @import("../../../testing.zig");
|
||||
test "WebApi: WheelEvent" {
|
||||
try testing.htmlRunner("event/wheel.html", .{});
|
||||
}
|
||||
Reference in New Issue
Block a user