-
Notifications
You must be signed in to change notification settings - Fork 286
KeyboardEvent support #1041
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
KeyboardEvent support #1041
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
3964f86
initial keyboard event
nikneym 8895c70
make `toInterface` be aware of `KeyboardEvent`
nikneym 68e237e
add license
nikneym f79f25b
implement `KeyboardEvent` properties and methods
nikneym 8f31fd7
add `KeyboardEvent` tests
nikneym 8d67502
don't expose `DOMErr` function
nikneym 22b4456
correct indentation in tests
nikneym 80851f4
don't inline `keyboardEventKeyIsSet`
nikneym File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,160 @@ | ||
| // Copyright (C) 2023-2024 Lightpanda (Selecy SAS) | ||
| // | ||
| // Francis Bouvier <[email protected]> | ||
| // Pierre Tachoire <[email protected]> | ||
| // | ||
| // 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 log = @import("../../log.zig"); | ||
| const builtin = @import("builtin"); | ||
|
|
||
| const netsurf = @import("../netsurf.zig"); | ||
| const Event = @import("event.zig").Event; | ||
| const JsObject = @import("../env.zig").JsObject; | ||
|
|
||
| // TODO: We currently don't have a UIEvent interface so we skip it in the prototype chain. | ||
| // https://developer.mozilla.org/en-US/docs/Web/API/UIEvent | ||
| const UIEvent = Event; | ||
|
|
||
| // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent | ||
| pub const KeyboardEvent = struct { | ||
| pub const Self = netsurf.KeyboardEvent; | ||
| pub const prototype = *UIEvent; | ||
|
|
||
| pub const ConstructorOptions = struct { | ||
| key: []const u8 = "", | ||
| code: []const u8 = "", | ||
| location: netsurf.KeyboardEventOpts.LocationCode = .standard, | ||
| repeat: bool = false, | ||
| isComposing: bool = false, | ||
| // Currently not supported but we take as argument. | ||
| charCode: u32 = 0, | ||
| // Currently not supported but we take as argument. | ||
| keyCode: u32 = 0, | ||
| // Currently not supported but we take as argument. | ||
| which: u32 = 0, | ||
| ctrlKey: bool = false, | ||
| shiftKey: bool = false, | ||
| altKey: bool = false, | ||
| metaKey: bool = false, | ||
| }; | ||
|
|
||
| pub fn constructor(event_type: []const u8, maybe_options: ?ConstructorOptions) !*netsurf.KeyboardEvent { | ||
| const options: ConstructorOptions = maybe_options orelse .{}; | ||
|
|
||
| var event = try netsurf.keyboardEventCreate(); | ||
| try netsurf.eventSetInternalType(@ptrCast(&event), .keyboard_event); | ||
|
|
||
| try netsurf.keyboardEventInit( | ||
| event, | ||
| event_type, | ||
| .{ | ||
| .key = options.key, | ||
| .code = options.code, | ||
| .location = options.location, | ||
| .repeat = options.repeat, | ||
| .is_composing = options.isComposing, | ||
| .ctrl_key = options.ctrlKey, | ||
| .shift_key = options.shiftKey, | ||
| .alt_key = options.altKey, | ||
| .meta_key = options.metaKey, | ||
| }, | ||
| ); | ||
|
|
||
| return event; | ||
| } | ||
|
|
||
| // Returns the modifier state for given modifier key. | ||
| pub fn _getModifierState(self: *Self, key: []const u8) bool { | ||
| // Chrome and Firefox do case-sensitive match, here we prefer the same. | ||
| if (std.mem.eql(u8, key, "Alt")) { | ||
| return get_altKey(self); | ||
| } | ||
|
|
||
| if (std.mem.eql(u8, key, "AltGraph")) { | ||
| return (get_altKey(self) and get_ctrlKey(self)); | ||
| } | ||
|
|
||
| if (std.mem.eql(u8, key, "Control")) { | ||
| return get_ctrlKey(self); | ||
| } | ||
|
|
||
| if (std.mem.eql(u8, key, "Shift")) { | ||
| return get_shiftKey(self); | ||
| } | ||
|
|
||
| if (std.mem.eql(u8, key, "Meta") or std.mem.eql(u8, key, "OS")) { | ||
| return get_metaKey(self); | ||
| } | ||
|
|
||
| // Special case for IE. | ||
| if (comptime builtin.os.tag == .windows) { | ||
| if (std.mem.eql(u8, key, "Win")) { | ||
| return get_metaKey(self); | ||
| } | ||
| } | ||
|
|
||
| // getModifierState() also accepts a deprecated virtual modifier named "Accel". | ||
| // event.getModifierState("Accel") returns true when at least one of | ||
| // KeyboardEvent.ctrlKey or KeyboardEvent.metaKey is true. | ||
| // | ||
| // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState#accel_virtual_modifier | ||
| if (std.mem.eql(u8, key, "Accel")) { | ||
| return (get_ctrlKey(self) or get_metaKey(self)); | ||
| } | ||
|
|
||
| // TODO: Add support for "CapsLock", "ScrollLock". | ||
| return false; | ||
| } | ||
|
|
||
| // Getters. | ||
|
|
||
| pub fn get_altKey(self: *Self) bool { | ||
| return netsurf.keyboardEventKeyIsSet(self, .alt); | ||
| } | ||
|
|
||
| pub fn get_ctrlKey(self: *Self) bool { | ||
| return netsurf.keyboardEventKeyIsSet(self, .ctrl); | ||
| } | ||
|
|
||
| pub fn get_metaKey(self: *Self) bool { | ||
| return netsurf.keyboardEventKeyIsSet(self, .meta); | ||
| } | ||
|
|
||
| pub fn get_shiftKey(self: *Self) bool { | ||
| return netsurf.keyboardEventKeyIsSet(self, .shift); | ||
| } | ||
|
|
||
| pub fn get_isComposing(self: *Self) bool { | ||
| return self.is_composing; | ||
| } | ||
|
|
||
| pub fn get_location(self: *Self) u32 { | ||
| return self.location; | ||
| } | ||
|
|
||
| pub fn get_key(self: *Self) ![]const u8 { | ||
| return netsurf.keyboardEventGetKey(self); | ||
| } | ||
|
|
||
| pub fn get_repeat(self: *Self) bool { | ||
| return self.repeat; | ||
| } | ||
| }; | ||
|
|
||
| const testing = @import("../../testing.zig"); | ||
| test "Browser: Events.Keyboard" { | ||
| try testing.htmlRunner("events/keyboard.html"); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| <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")); | ||
| testing.expectEqual(true, event.getModifierState("OS")); | ||
| testing.expectEqual(true, event.getModifierState("Accel")); | ||
| </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 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 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 Event); | ||
| testing.expectEqual("w", event.key); | ||
| }); | ||
|
|
||
| document.dispatchEvent(event); | ||
|
|
||
| testing.expectEqual(true, isKeyPress); | ||
| </script> |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.