mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-23 22:58:00 -05:00
Merge pull request #1277 from lightpanda-io/zigdom-ui-events
`UIEvent`, `MouseEvent` and `KeyboardEvent`
This commit is contained in:
@@ -30,6 +30,7 @@ const SlabAllocator = @import("../slab.zig").SlabAllocator;
|
||||
const Page = @import("Page.zig");
|
||||
const Node = @import("webapi/Node.zig");
|
||||
const Event = @import("webapi/Event.zig");
|
||||
const UIEvent = @import("webapi/event/UIEvent.zig");
|
||||
const Element = @import("webapi/Element.zig");
|
||||
const Document = @import("webapi/Document.zig");
|
||||
const EventTarget = @import("webapi/EventTarget.zig");
|
||||
@@ -170,11 +171,11 @@ pub fn eventTarget(self: *Factory, child: anytype) !*@TypeOf(child) {
|
||||
pub fn event(self: *Factory, typ: []const u8, child: anytype) !*@TypeOf(child) {
|
||||
const allocator = self._slab.allocator();
|
||||
|
||||
// Special case: Event has a _type_string field, so we need manual setup
|
||||
const chain = try PrototypeChain(
|
||||
&.{ Event, @TypeOf(child) },
|
||||
).allocate(allocator);
|
||||
|
||||
// Special case: Event has a _type_string field, so we need manual setup
|
||||
const event_ptr = chain.get(0);
|
||||
event_ptr.* = .{
|
||||
._type = unionInit(Event.Type, chain.get(1)),
|
||||
@@ -185,6 +186,25 @@ pub fn event(self: *Factory, typ: []const u8, child: anytype) !*@TypeOf(child) {
|
||||
return chain.get(1);
|
||||
}
|
||||
|
||||
pub fn uiEvent(self: *Factory, typ: []const u8, child: anytype) !*@TypeOf(child) {
|
||||
const allocator = self._slab.allocator();
|
||||
|
||||
const chain = try PrototypeChain(
|
||||
&.{ Event, UIEvent, @TypeOf(child) },
|
||||
).allocate(allocator);
|
||||
|
||||
// Special case: Event has a _type_string field, so we need manual setup
|
||||
const event_ptr = chain.get(0);
|
||||
event_ptr.* = .{
|
||||
._type = unionInit(Event.Type, chain.get(1)),
|
||||
._type_string = try String.init(self._page.arena, typ, .{}),
|
||||
};
|
||||
chain.setMiddle(1, UIEvent.Type);
|
||||
chain.setLeaf(2, child);
|
||||
|
||||
return chain.get(2);
|
||||
}
|
||||
|
||||
pub fn blob(self: *Factory, child: anytype) !*@TypeOf(child) {
|
||||
const allocator = self._slab.allocator();
|
||||
|
||||
|
||||
@@ -576,6 +576,9 @@ pub const JsApis = flattenTypes(&.{
|
||||
@import("../webapi/event/NavigationCurrentEntryChangeEvent.zig"),
|
||||
@import("../webapi/event/PageTransitionEvent.zig"),
|
||||
@import("../webapi/event/PopStateEvent.zig"),
|
||||
@import("../webapi/event/UIEvent.zig"),
|
||||
@import("../webapi/event/MouseEvent.zig"),
|
||||
@import("../webapi/event/KeyboardEvent.zig"),
|
||||
@import("../webapi/MessageChannel.zig"),
|
||||
@import("../webapi/MessagePort.zig"),
|
||||
@import("../webapi/media/MediaError.zig"),
|
||||
|
||||
89
src/browser/tests/event/keyboard.html
Normal file
89
src/browser/tests/event/keyboard.html
Normal file
@@ -0,0 +1,89 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../testing.js"></script>
|
||||
|
||||
<script id=default>
|
||||
let event = new KeyboardEvent("test", { key: "a" });
|
||||
testing.expectEqual(true, event instanceof KeyboardEvent);
|
||||
testing.expectEqual(true, event instanceof Event);
|
||||
|
||||
testing.expectEqual("test", event.type);
|
||||
testing.expectEqual("a", event.key);
|
||||
|
||||
testing.expectEqual(0, event.location);
|
||||
testing.expectEqual(false, event.repeat);
|
||||
testing.expectEqual(false, event.isComposing);
|
||||
|
||||
testing.expectEqual(false, event.ctrlKey);
|
||||
testing.expectEqual(false, event.shiftKey);
|
||||
testing.expectEqual(false, event.metaKey);
|
||||
testing.expectEqual(false, event.altKey);
|
||||
</script>
|
||||
|
||||
<script id=getModifierState>
|
||||
event = new KeyboardEvent("test", {
|
||||
altKey: true,
|
||||
shiftKey: true,
|
||||
metaKey: true,
|
||||
ctrlKey: true,
|
||||
});
|
||||
|
||||
testing.expectEqual(true, event.getModifierState("Alt"));
|
||||
testing.expectEqual(true, event.getModifierState("AltGraph"));
|
||||
testing.expectEqual(true, event.getModifierState("Control"));
|
||||
testing.expectEqual(true, event.getModifierState("Shift"));
|
||||
testing.expectEqual(true, event.getModifierState("Meta"));
|
||||
</script>
|
||||
|
||||
<script id=keyDownListener>
|
||||
event = new KeyboardEvent("keydown", { key: "z" });
|
||||
let isKeyDown = false;
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
isKeyDown = true;
|
||||
|
||||
testing.expectEqual(true, e instanceof KeyboardEvent);
|
||||
testing.expectEqual(true, e instanceof UIEvent);
|
||||
testing.expectEqual(true, e instanceof Event);
|
||||
testing.expectEqual("z", event.key);
|
||||
});
|
||||
|
||||
document.dispatchEvent(event);
|
||||
|
||||
testing.expectEqual(true, isKeyDown);
|
||||
</script>
|
||||
|
||||
<script id=keyUpListener>
|
||||
event = new KeyboardEvent("keyup", { key: "x" });
|
||||
let isKeyUp = false;
|
||||
|
||||
document.addEventListener("keyup", (e) => {
|
||||
isKeyUp = true;
|
||||
|
||||
testing.expectEqual(true, e instanceof KeyboardEvent);
|
||||
testing.expectEqual(true, e instanceof UIEvent);
|
||||
testing.expectEqual(true, e instanceof Event);
|
||||
testing.expectEqual("x", event.key);
|
||||
});
|
||||
|
||||
document.dispatchEvent(event);
|
||||
|
||||
testing.expectEqual(true, isKeyUp);
|
||||
</script>
|
||||
|
||||
<script id=keyPressListener>
|
||||
event = new KeyboardEvent("keypress", { key: "w" });
|
||||
let isKeyPress = false;
|
||||
|
||||
document.addEventListener("keypress", (e) => {
|
||||
isKeyPress = true;
|
||||
|
||||
testing.expectEqual(true, e instanceof KeyboardEvent);
|
||||
testing.expectEqual(true, e instanceof UIEvent);
|
||||
testing.expectEqual(true, e instanceof Event);
|
||||
testing.expectEqual("w", event.key);
|
||||
});
|
||||
|
||||
document.dispatchEvent(event);
|
||||
|
||||
testing.expectEqual(true, isKeyPress);
|
||||
</script>
|
||||
37
src/browser/tests/event/mouse.html
Normal file
37
src/browser/tests/event/mouse.html
Normal file
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../testing.js"></script>
|
||||
<script id=default>
|
||||
let event = new MouseEvent('click');
|
||||
testing.expectEqual('click', event.type);
|
||||
testing.expectEqual(true, event instanceof MouseEvent);
|
||||
testing.expectEqual(true, event instanceof UIEvent);
|
||||
testing.expectEqual(true, event instanceof Event);
|
||||
testing.expectEqual(0, event.clientX);
|
||||
testing.expectEqual(0, event.clientY);
|
||||
testing.expectEqual(0, event.screenX);
|
||||
testing.expectEqual(0, event.screenY);
|
||||
</script>
|
||||
|
||||
<script id=parameters>
|
||||
let new_event = new MouseEvent('click', { 'button': 0, 'clientX': 10, 'clientY': 20, screenX: 200, screenY: 500 });
|
||||
testing.expectEqual(0, new_event.button);
|
||||
testing.expectEqual(10, new_event.x);
|
||||
testing.expectEqual(20, new_event.y);
|
||||
testing.expectEqual(10, new_event.pageX);
|
||||
testing.expectEqual(20, new_event.pageY);
|
||||
testing.expectEqual(200, new_event.screenX);
|
||||
testing.expectEqual(500, new_event.screenY);
|
||||
</script>
|
||||
|
||||
<script id=listener>
|
||||
let me = new MouseEvent('click');
|
||||
testing.expectEqual(true, me instanceof Event);
|
||||
|
||||
var evt = null;
|
||||
document.addEventListener('click', function (e) {
|
||||
evt = e;
|
||||
});
|
||||
document.dispatchEvent(me);
|
||||
testing.expectEqual('click', evt.type);
|
||||
testing.expectEqual(true, evt instanceof MouseEvent);
|
||||
</script>
|
||||
54
src/browser/tests/event/ui.html
Normal file
54
src/browser/tests/event/ui.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../testing.js"></script>
|
||||
|
||||
<script id=uiEventConstructor>
|
||||
const evt = new UIEvent('click', {
|
||||
detail: 5,
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
});
|
||||
testing.expectEqual('click', evt.type);
|
||||
testing.expectEqual(5, evt.detail);
|
||||
testing.expectEqual(true, evt.bubbles);
|
||||
testing.expectEqual(true, evt.cancelable);
|
||||
testing.expectEqual(window, evt.view);
|
||||
</script>
|
||||
|
||||
<script id=uiEventWithView>
|
||||
const evt2 = new UIEvent('mousedown', {
|
||||
detail: 2,
|
||||
view: window
|
||||
});
|
||||
testing.expectEqual('mousedown', evt2.type);
|
||||
testing.expectEqual(2, evt2.detail);
|
||||
testing.expectEqual(window, evt2.view);
|
||||
</script>
|
||||
|
||||
<script id=uiEventDefaults>
|
||||
const evt3 = new UIEvent('focus');
|
||||
testing.expectEqual('focus', evt3.type);
|
||||
testing.expectEqual(0, evt3.detail);
|
||||
testing.expectEqual(false, evt3.bubbles);
|
||||
testing.expectEqual(false, evt3.cancelable);
|
||||
testing.expectEqual(window, evt3.view);
|
||||
</script>
|
||||
|
||||
<script id=uiEventInheritance>
|
||||
const evt4 = new UIEvent('blur', { detail: 1 });
|
||||
testing.expectEqual('blur', evt4.type);
|
||||
testing.expectEqual(Event.NONE, evt4.eventPhase);
|
||||
testing.expectEqual(false, evt4.defaultPrevented);
|
||||
testing.expectEqual(1, evt4.detail);
|
||||
</script>
|
||||
|
||||
<script id=uiEventDetailTypes>
|
||||
const evt5 = new UIEvent('custom', { detail: 0 });
|
||||
const evt6 = new UIEvent('custom', { detail: 100 });
|
||||
testing.expectEqual(0, evt5.detail);
|
||||
testing.expectEqual(100, evt6.detail);
|
||||
</script>
|
||||
|
||||
<script id=uiEventDetailTypes2>
|
||||
const evt7 = new UIEvent('custom', { bubbles: true });
|
||||
testing.expectEqual(0, evt7.detail);
|
||||
</script>
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
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");
|
||||
@@ -60,9 +59,10 @@ pub const Type = union(enum) {
|
||||
navigation_current_entry_change_event: *@import("event/NavigationCurrentEntryChangeEvent.zig"),
|
||||
page_transition_event: *@import("event/PageTransitionEvent.zig"),
|
||||
pop_state_event: *@import("event/PopStateEvent.zig"),
|
||||
ui_event: *@import("event/UIEvent.zig"),
|
||||
};
|
||||
|
||||
const Options = struct {
|
||||
pub const Options = struct {
|
||||
bubbles: bool = false,
|
||||
cancelable: bool = false,
|
||||
composed: bool = false,
|
||||
@@ -210,7 +210,7 @@ pub fn inheritOptions(comptime T: type, comptime additions: anytype) type {
|
||||
|
||||
inline for (t_fields) |field| {
|
||||
if (std.mem.eql(u8, field.name, "_proto")) {
|
||||
const ProtoType = reflect.Struct(field.type);
|
||||
const ProtoType = @typeInfo(field.type).pointer.child;
|
||||
if (@hasDecl(ProtoType, "Options")) {
|
||||
const parent_options = @typeInfo(ProtoType.Options);
|
||||
all_fields = all_fields ++ parent_options.@"struct".fields;
|
||||
|
||||
196
src/browser/webapi/event/KeyboardEvent.zig
Normal file
196
src/browser/webapi/event/KeyboardEvent.zig
Normal file
@@ -0,0 +1,196 @@
|
||||
// Copyright (C) 2023-2025 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 Event = @import("../Event.zig");
|
||||
const UIEvent = @import("UIEvent.zig");
|
||||
const EventTarget = @import("../EventTarget.zig");
|
||||
const Window = @import("../Window.zig");
|
||||
const Page = @import("../../Page.zig");
|
||||
const js = @import("../../js/js.zig");
|
||||
|
||||
const KeyboardEvent = @This();
|
||||
|
||||
_proto: *UIEvent,
|
||||
_key: Key,
|
||||
_ctrl_key: bool,
|
||||
_shift_key: bool,
|
||||
_alt_key: bool,
|
||||
_meta_key: bool,
|
||||
_location: Location,
|
||||
_repeat: bool,
|
||||
_is_composing: bool,
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values
|
||||
pub const Key = union(enum) {
|
||||
// Special Key Values
|
||||
Dead,
|
||||
Undefined,
|
||||
Alt,
|
||||
AltGraph,
|
||||
CapsLock,
|
||||
Control,
|
||||
Fn,
|
||||
FnLock,
|
||||
Hyper,
|
||||
Meta,
|
||||
NumLock,
|
||||
ScrollLock,
|
||||
Shift,
|
||||
Super,
|
||||
Symbol,
|
||||
SymbolLock,
|
||||
standard: []const u8,
|
||||
|
||||
pub fn fromString(allocator: std.mem.Allocator, str: []const u8) !Key {
|
||||
const key_type_info = @typeInfo(Key);
|
||||
inline for (key_type_info.@"union".fields) |field| {
|
||||
if (comptime std.mem.eql(u8, field.name, "standard")) continue;
|
||||
|
||||
if (std.mem.eql(u8, field.name, str)) {
|
||||
return @unionInit(Key, field.name, {});
|
||||
}
|
||||
}
|
||||
|
||||
const duped = try allocator.dupe(u8, str);
|
||||
return .{ .standard = duped };
|
||||
}
|
||||
};
|
||||
|
||||
pub const Location = enum(i32) {
|
||||
DOM_KEY_LOCATION_STANDARD = 0,
|
||||
DOM_KEY_LOCATION_LEFT = 1,
|
||||
DOM_KEY_LOCATION_RIGHT = 2,
|
||||
DOM_KEY_LOCATION_NUMPAD = 3,
|
||||
};
|
||||
|
||||
pub const KeyboardEventOptions = struct {
|
||||
key: []const u8 = "",
|
||||
// TODO: code but it is not baseline.
|
||||
location: i32 = 0,
|
||||
repeat: bool = false,
|
||||
isComposing: bool = false,
|
||||
ctrlKey: bool = false,
|
||||
shiftKey: bool = false,
|
||||
altKey: bool = false,
|
||||
metaKey: bool = false,
|
||||
};
|
||||
|
||||
pub const Options = Event.inheritOptions(
|
||||
KeyboardEvent,
|
||||
KeyboardEventOptions,
|
||||
);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*KeyboardEvent {
|
||||
const opts = _opts orelse Options{};
|
||||
|
||||
const event = try page._factory.uiEvent(
|
||||
typ,
|
||||
KeyboardEvent{
|
||||
._proto = undefined,
|
||||
._key = try Key.fromString(page.arena, opts.key),
|
||||
._location = std.meta.intToEnum(Location, opts.location) catch return error.TypeError,
|
||||
._repeat = opts.repeat,
|
||||
._is_composing = opts.isComposing,
|
||||
._ctrl_key = opts.ctrlKey,
|
||||
._shift_key = opts.shiftKey,
|
||||
._alt_key = opts.altKey,
|
||||
._meta_key = opts.metaKey,
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts);
|
||||
return event;
|
||||
}
|
||||
|
||||
pub fn asEvent(self: *KeyboardEvent) *Event {
|
||||
return self._proto.asEvent();
|
||||
}
|
||||
|
||||
pub fn getAltKey(self: *const KeyboardEvent) bool {
|
||||
return self._alt_key;
|
||||
}
|
||||
|
||||
pub fn getCtrlKey(self: *const KeyboardEvent) bool {
|
||||
return self._ctrl_key;
|
||||
}
|
||||
|
||||
pub fn getIsComposing(self: *const KeyboardEvent) bool {
|
||||
return self._is_composing;
|
||||
}
|
||||
|
||||
pub fn getKey(self: *const KeyboardEvent) []const u8 {
|
||||
return switch (self._key) {
|
||||
.standard => |key| key,
|
||||
else => |x| @tagName(x),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getLocation(self: *const KeyboardEvent) i32 {
|
||||
return @intFromEnum(self._location);
|
||||
}
|
||||
|
||||
pub fn getMetaKey(self: *const KeyboardEvent) bool {
|
||||
return self._meta_key;
|
||||
}
|
||||
|
||||
pub fn getRepeat(self: *const KeyboardEvent) bool {
|
||||
return self._repeat;
|
||||
}
|
||||
|
||||
pub fn getShiftKey(self: *const KeyboardEvent) bool {
|
||||
return self._shift_key;
|
||||
}
|
||||
|
||||
pub fn getModifierState(self: *const KeyboardEvent, str: []const u8, page: *Page) !bool {
|
||||
const key = try Key.fromString(page.arena, str);
|
||||
|
||||
switch (key) {
|
||||
.Alt, .AltGraph => return self._alt_key,
|
||||
.Shift => return self._shift_key,
|
||||
.Control => return self._ctrl_key,
|
||||
.Meta => return self._meta_key,
|
||||
else => return false,
|
||||
}
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(KeyboardEvent);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "KeyboardEvent";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const constructor = bridge.constructor(KeyboardEvent.init, .{});
|
||||
pub const altKey = bridge.accessor(KeyboardEvent.getAltKey, null, .{});
|
||||
pub const ctrlKey = bridge.accessor(KeyboardEvent.getCtrlKey, null, .{});
|
||||
pub const isComposing = bridge.accessor(KeyboardEvent.getIsComposing, null, .{});
|
||||
pub const key = bridge.accessor(KeyboardEvent.getKey, null, .{});
|
||||
pub const location = bridge.accessor(KeyboardEvent.getLocation, null, .{});
|
||||
pub const metaKey = bridge.accessor(KeyboardEvent.getMetaKey, null, .{});
|
||||
pub const repeat = bridge.accessor(KeyboardEvent.getRepeat, null, .{});
|
||||
pub const shiftKey = bridge.accessor(KeyboardEvent.getShiftKey, null, .{});
|
||||
pub const getModifierState = bridge.function(KeyboardEvent.getModifierState, .{});
|
||||
};
|
||||
|
||||
const testing = @import("../../../testing.zig");
|
||||
test "WebApi: KeyboardEvent" {
|
||||
try testing.htmlRunner("event/keyboard.html", .{});
|
||||
}
|
||||
188
src/browser/webapi/event/MouseEvent.zig
Normal file
188
src/browser/webapi/event/MouseEvent.zig
Normal file
@@ -0,0 +1,188 @@
|
||||
// Copyright (C) 2023-2025 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 Event = @import("../Event.zig");
|
||||
const UIEvent = @import("UIEvent.zig");
|
||||
const EventTarget = @import("../EventTarget.zig");
|
||||
const Window = @import("../Window.zig");
|
||||
const Page = @import("../../Page.zig");
|
||||
const js = @import("../../js/js.zig");
|
||||
|
||||
const MouseEvent = @This();
|
||||
|
||||
const MouseButton = enum(u8) {
|
||||
main = 0,
|
||||
auxillary = 1,
|
||||
secondary = 2,
|
||||
fourth = 3,
|
||||
fifth = 4,
|
||||
};
|
||||
|
||||
_proto: *UIEvent,
|
||||
|
||||
_alt_key: bool,
|
||||
_button: MouseButton,
|
||||
// TODO: _buttons
|
||||
_client_x: f64,
|
||||
_client_y: f64,
|
||||
_ctrl_key: bool,
|
||||
_meta_key: bool,
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/relatedTarget
|
||||
_related_target: ?*EventTarget = null,
|
||||
_screen_x: f64,
|
||||
_screen_y: f64,
|
||||
_shift_key: bool,
|
||||
|
||||
pub const MouseEventOptions = struct {
|
||||
screenX: f64 = 0.0,
|
||||
screenY: f64 = 0.0,
|
||||
clientX: f64 = 0.0,
|
||||
clientY: f64 = 0.0,
|
||||
ctrlKey: bool = false,
|
||||
shiftKey: bool = false,
|
||||
altKey: bool = false,
|
||||
metaKey: bool = false,
|
||||
button: i32 = 0,
|
||||
// TODO: buttons
|
||||
relatedTarget: ?*EventTarget = null,
|
||||
};
|
||||
|
||||
pub const Options = Event.inheritOptions(
|
||||
MouseEvent,
|
||||
MouseEventOptions,
|
||||
);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*MouseEvent {
|
||||
const opts = _opts orelse Options{};
|
||||
|
||||
const event = try page._factory.uiEvent(
|
||||
typ,
|
||||
MouseEvent{
|
||||
._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(MouseButton, opts.button) catch return error.TypeError,
|
||||
._related_target = opts.relatedTarget,
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts);
|
||||
return event;
|
||||
}
|
||||
|
||||
pub fn asEvent(self: *MouseEvent) *Event {
|
||||
return self._proto.asEvent();
|
||||
}
|
||||
|
||||
pub fn getAltKey(self: *const MouseEvent) bool {
|
||||
return self._alt_key;
|
||||
}
|
||||
|
||||
pub fn getButton(self: *const MouseEvent) u8 {
|
||||
return @intFromEnum(self._button);
|
||||
}
|
||||
|
||||
pub fn getClientX(self: *const MouseEvent) f64 {
|
||||
return self._client_x;
|
||||
}
|
||||
|
||||
pub fn getClientY(self: *const MouseEvent) f64 {
|
||||
return self._client_y;
|
||||
}
|
||||
|
||||
pub fn getCtrlKey(self: *const MouseEvent) bool {
|
||||
return self._ctrl_key;
|
||||
}
|
||||
|
||||
pub fn getMetaKey(self: *const MouseEvent) bool {
|
||||
return self._meta_key;
|
||||
}
|
||||
|
||||
pub fn getOffsetX(_: *const MouseEvent) f64 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
pub fn getOffsetY(_: *const MouseEvent) f64 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
pub fn getPageX(self: *const MouseEvent) f64 {
|
||||
// this should be clientX + window.scrollX
|
||||
return self._client_x;
|
||||
}
|
||||
|
||||
pub fn getPageY(self: *const MouseEvent) f64 {
|
||||
// this should be clientY + window.scrollY
|
||||
return self._client_y;
|
||||
}
|
||||
|
||||
pub fn getRelatedTarget(self: *const MouseEvent) ?*EventTarget {
|
||||
return self._related_target;
|
||||
}
|
||||
|
||||
pub fn getScreenX(self: *const MouseEvent) f64 {
|
||||
return self._screen_x;
|
||||
}
|
||||
|
||||
pub fn getScreenY(self: *const MouseEvent) f64 {
|
||||
return self._screen_y;
|
||||
}
|
||||
|
||||
pub fn getShiftKey(self: *const MouseEvent) bool {
|
||||
return self._shift_key;
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(MouseEvent);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "MouseEvent";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const constructor = bridge.constructor(MouseEvent.init, .{});
|
||||
pub const altKey = bridge.accessor(getAltKey, null, .{});
|
||||
pub const button = bridge.accessor(getButton, null, .{});
|
||||
pub const clientX = bridge.accessor(getClientX, null, .{});
|
||||
pub const clientY = bridge.accessor(getClientY, null, .{});
|
||||
pub const ctrlKey = bridge.accessor(getCtrlKey, null, .{});
|
||||
pub const metaKey = bridge.accessor(getMetaKey, null, .{});
|
||||
pub const offsetX = bridge.accessor(getOffsetX, null, .{});
|
||||
pub const offsetY = bridge.accessor(getOffsetY, null, .{});
|
||||
pub const pageX = bridge.accessor(getPageX, null, .{});
|
||||
pub const pageY = bridge.accessor(getPageY, null, .{});
|
||||
pub const relatedTarget = bridge.accessor(getRelatedTarget, null, .{});
|
||||
pub const screenX = bridge.accessor(getScreenX, null, .{});
|
||||
pub const screenY = bridge.accessor(getScreenY, null, .{});
|
||||
pub const shiftKey = bridge.accessor(getShiftKey, null, .{});
|
||||
pub const x = bridge.accessor(getClientX, null, .{});
|
||||
pub const y = bridge.accessor(getClientY, null, .{});
|
||||
};
|
||||
|
||||
const testing = @import("../../../testing.zig");
|
||||
test "WebApi: MouseEvent" {
|
||||
try testing.htmlRunner("event/mouse.html", .{});
|
||||
}
|
||||
102
src/browser/webapi/event/UIEvent.zig
Normal file
102
src/browser/webapi/event/UIEvent.zig
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright (C) 2023-2025 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 Event = @import("../Event.zig");
|
||||
const Window = @import("../Window.zig");
|
||||
const Page = @import("../../Page.zig");
|
||||
const js = @import("../../js/js.zig");
|
||||
|
||||
const UIEvent = @This();
|
||||
|
||||
_type: Type,
|
||||
_proto: *Event,
|
||||
_detail: u32 = 0,
|
||||
_view: ?*Window = null,
|
||||
|
||||
pub const Type = union(enum) {
|
||||
generic,
|
||||
mouse_event: *@import("MouseEvent.zig"),
|
||||
keyboard_event: *@import("KeyboardEvent.zig"),
|
||||
};
|
||||
|
||||
pub const UIEventOptions = struct {
|
||||
detail: u32 = 0,
|
||||
view: ?*Window = null,
|
||||
};
|
||||
|
||||
pub const Options = Event.inheritOptions(
|
||||
UIEvent,
|
||||
UIEventOptions,
|
||||
);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*UIEvent {
|
||||
const opts = _opts orelse Options{};
|
||||
|
||||
const event = try page._factory.event(
|
||||
typ,
|
||||
UIEvent{
|
||||
._type = .generic,
|
||||
._proto = undefined,
|
||||
._detail = opts.detail,
|
||||
._view = opts.view orelse page.window,
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts);
|
||||
return event;
|
||||
}
|
||||
|
||||
pub fn populateFromOptions(self: *UIEvent, opts: anytype) void {
|
||||
self._detail = opts.detail;
|
||||
self._view = opts.view;
|
||||
}
|
||||
|
||||
pub fn asEvent(self: *UIEvent) *Event {
|
||||
return self._proto;
|
||||
}
|
||||
|
||||
pub fn getDetail(self: *UIEvent) u32 {
|
||||
return self._detail;
|
||||
}
|
||||
|
||||
// sourceCapabilities not implemented
|
||||
|
||||
pub fn getView(self: *UIEvent, page: *Page) *Window {
|
||||
return self._view orelse page.window;
|
||||
}
|
||||
|
||||
// deprecated `initUIEvent()` not implemented
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(UIEvent);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "UIEvent";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const constructor = bridge.constructor(UIEvent.init, .{});
|
||||
pub const detail = bridge.accessor(UIEvent.getDetail, null, .{});
|
||||
pub const view = bridge.accessor(UIEvent.getView, null, .{});
|
||||
};
|
||||
|
||||
const testing = @import("../../../testing.zig");
|
||||
test "WebApi: UIEvent" {
|
||||
try testing.htmlRunner("event/ui.html", .{});
|
||||
}
|
||||
Reference in New Issue
Block a user