mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 14:33:47 +00:00
add selection api to HTMLInputElement
This commit is contained in:
@@ -2933,16 +2933,7 @@ pub fn handleKeydown(self: *Page, target: *Node, event: *Event) !void {
|
|||||||
|
|
||||||
// Handle printable characters
|
// Handle printable characters
|
||||||
if (key.isPrintable()) {
|
if (key.isPrintable()) {
|
||||||
// if the input is selected, replace the content.
|
try input.innerInsert(key.asString(), self);
|
||||||
if (input._selected) {
|
|
||||||
const new_value = try self.arena.dupe(u8, key.asString());
|
|
||||||
try input.setValue(new_value, self);
|
|
||||||
input._selected = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const current_value = input.getValue();
|
|
||||||
const new_value = try std.mem.concat(self.arena, u8, &.{ current_value, key.asString() });
|
|
||||||
try input.setValue(new_value, self);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3011,18 +3002,7 @@ pub fn insertText(self: *Page, v: []const u8) !void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the input is selected, replace the existing value
|
try input.innerInsert(v, self);
|
||||||
if (input._selected) {
|
|
||||||
const new_value = try self.arena.dupe(u8, v);
|
|
||||||
try input.setValue(new_value, self);
|
|
||||||
input._selected = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Or append the value
|
|
||||||
const current_value = input.getValue();
|
|
||||||
const new_value = try std.mem.concat(self.arena, u8, &.{ current_value, v });
|
|
||||||
return input.setValue(new_value, self);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (html_element.is(Element.Html.TextArea)) |textarea| {
|
if (html_element.is(Element.Html.TextArea)) |textarea| {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const Node = @import("Node.zig");
|
|||||||
/// https://w3c.github.io/selection-api/
|
/// https://w3c.github.io/selection-api/
|
||||||
const Selection = @This();
|
const Selection = @This();
|
||||||
|
|
||||||
const SelectionDirection = enum { backward, forward, none };
|
pub const SelectionDirection = enum { backward, forward, none };
|
||||||
|
|
||||||
_range: ?*Range = null,
|
_range: ?*Range = null,
|
||||||
_direction: SelectionDirection = .none,
|
_direction: SelectionDirection = .none,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const Node = @import("../../Node.zig");
|
|||||||
const Element = @import("../../Element.zig");
|
const Element = @import("../../Element.zig");
|
||||||
const HtmlElement = @import("../Html.zig");
|
const HtmlElement = @import("../Html.zig");
|
||||||
const Form = @import("Form.zig");
|
const Form = @import("Form.zig");
|
||||||
|
const Selection = @import("../../Selection.zig");
|
||||||
|
|
||||||
const Input = @This();
|
const Input = @This();
|
||||||
|
|
||||||
@@ -74,9 +75,12 @@ _value: ?[]const u8 = null,
|
|||||||
_checked: bool = false,
|
_checked: bool = false,
|
||||||
_checked_dirty: bool = false,
|
_checked_dirty: bool = false,
|
||||||
_input_type: Type = .text,
|
_input_type: Type = .text,
|
||||||
_selected: bool = false,
|
|
||||||
_indeterminate: bool = false,
|
_indeterminate: bool = false,
|
||||||
|
|
||||||
|
_selection_start: u32 = 0,
|
||||||
|
_selection_end: u32 = 0,
|
||||||
|
_selection_direction: Selection.SelectionDirection = .none,
|
||||||
|
|
||||||
pub fn asElement(self: *Input) *Element {
|
pub fn asElement(self: *Input) *Element {
|
||||||
return self._proto._proto;
|
return self._proto._proto;
|
||||||
}
|
}
|
||||||
@@ -255,8 +259,120 @@ pub fn setRequired(self: *Input, required: bool, page: *Page) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select(self: *Input) void {
|
pub fn select(self: *Input) !void {
|
||||||
self._selected = true;
|
const len = if (self._value) |v| @as(u32, @intCast(v.len)) else 0;
|
||||||
|
try self.setSelectionRange(0, len, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selectionAvailable(self: *const Input) bool {
|
||||||
|
switch (self._input_type) {
|
||||||
|
.text, .search, .url, .tel, .password => return true,
|
||||||
|
else => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const HowSelected = union(enum) { partial: struct { u32, u32 }, full, none };
|
||||||
|
|
||||||
|
fn howSelected(self: *const Input) HowSelected {
|
||||||
|
if (!self.selectionAvailable()) return .none;
|
||||||
|
const value = self._value orelse return .none;
|
||||||
|
|
||||||
|
if (self._selection_start == self._selection_end) return .none;
|
||||||
|
if (self._selection_start == 0 and self._selection_end == value.len) return .full;
|
||||||
|
return .{ .partial = .{ self._selection_start, self._selection_end } };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn innerInsert(self: *Input, str: []const u8, page: *Page) !void {
|
||||||
|
const arena = page.arena;
|
||||||
|
|
||||||
|
switch (self.howSelected()) {
|
||||||
|
.full => {
|
||||||
|
// if the input is fully selected, replace the content.
|
||||||
|
const new_value = try arena.dupe(u8, str);
|
||||||
|
try self.setValue(new_value, page);
|
||||||
|
self._selection_start = 1;
|
||||||
|
self._selection_end = 1;
|
||||||
|
self._selection_direction = .none;
|
||||||
|
},
|
||||||
|
.partial => |range| {
|
||||||
|
// if the input is partially selected, replace the selected content.
|
||||||
|
const current_value = self.getValue();
|
||||||
|
const before = current_value[0..range[0]];
|
||||||
|
const remaining = current_value[range[1]..];
|
||||||
|
|
||||||
|
const new_value = try std.mem.concat(
|
||||||
|
arena,
|
||||||
|
u8,
|
||||||
|
&.{ before, str, remaining },
|
||||||
|
);
|
||||||
|
try self.setValue(new_value, page);
|
||||||
|
|
||||||
|
const new_pos = range[0] + str.len;
|
||||||
|
self._selection_start = @intCast(new_pos);
|
||||||
|
self._selection_end = @intCast(new_pos);
|
||||||
|
self._selection_direction = .none;
|
||||||
|
},
|
||||||
|
.none => {
|
||||||
|
// if the input is not selected, just insert at cursor.
|
||||||
|
const current_value = self.getValue();
|
||||||
|
const new_value = try std.mem.concat(arena, u8, &.{ current_value, str });
|
||||||
|
try self.setValue(new_value, page);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getSelectionDirection(self: *const Input) []const u8 {
|
||||||
|
return @tagName(self._selection_direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getSelectionStart(self: *const Input) !?u32 {
|
||||||
|
if (!self.selectionAvailable()) return null;
|
||||||
|
return self._selection_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setSelectionStart(self: *Input, value: u32) !void {
|
||||||
|
if (!self.selectionAvailable()) return error.InvalidStateError;
|
||||||
|
self._selection_start = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getSelectionEnd(self: *const Input) !?u32 {
|
||||||
|
if (!self.selectionAvailable()) return null;
|
||||||
|
return self._selection_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setSelectionEnd(self: *Input, value: u32) !void {
|
||||||
|
if (!self.selectionAvailable()) return error.InvalidStateError;
|
||||||
|
self._selection_end = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setSelectionRange(self: *Input, selection_start: u32, selection_end: u32, selection_dir: ?[]const u8) !void {
|
||||||
|
if (!self.selectionAvailable()) return error.InvalidStateError;
|
||||||
|
|
||||||
|
const direction = blk: {
|
||||||
|
if (selection_dir) |sd| {
|
||||||
|
break :blk std.meta.stringToEnum(Selection.SelectionDirection, sd) orelse .none;
|
||||||
|
} else break :blk .none;
|
||||||
|
};
|
||||||
|
|
||||||
|
const value = self._value orelse {
|
||||||
|
self._selection_start = 0;
|
||||||
|
self._selection_end = 0;
|
||||||
|
self._selection_direction = .none;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const len_u32: u32 = @intCast(value.len);
|
||||||
|
var start: u32 = if (selection_start > len_u32) len_u32 else selection_start;
|
||||||
|
const end: u32 = if (selection_end > len_u32) len_u32 else selection_end;
|
||||||
|
|
||||||
|
// If end is less than start, both are equal to end.
|
||||||
|
if (end < start) {
|
||||||
|
start = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
self._selection_direction = direction;
|
||||||
|
self._selection_start = start;
|
||||||
|
self._selection_end = end;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getForm(self: *Input, page: *Page) ?*Form {
|
pub fn getForm(self: *Input, page: *Page) ?*Form {
|
||||||
@@ -352,6 +468,11 @@ pub const JsApi = struct {
|
|||||||
pub const form = bridge.accessor(Input.getForm, null, .{});
|
pub const form = bridge.accessor(Input.getForm, null, .{});
|
||||||
pub const indeterminate = bridge.accessor(Input.getIndeterminate, Input.setIndeterminate, .{});
|
pub const indeterminate = bridge.accessor(Input.getIndeterminate, Input.setIndeterminate, .{});
|
||||||
pub const select = bridge.function(Input.select, .{});
|
pub const select = bridge.function(Input.select, .{});
|
||||||
|
|
||||||
|
pub const selectionStart = bridge.accessor(Input.getSelectionStart, Input.setSelectionStart, .{});
|
||||||
|
pub const selectionEnd = bridge.accessor(Input.getSelectionEnd, Input.setSelectionEnd, .{});
|
||||||
|
pub const selectionDirection = bridge.accessor(Input.getSelectionDirection, null, .{});
|
||||||
|
pub const setSelectionRange = bridge.function(Input.setSelectionRange, .{ .dom_exception = true });
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Build = struct {
|
pub const Build = struct {
|
||||||
@@ -422,7 +543,9 @@ pub const Build = struct {
|
|||||||
clone._value = source._value;
|
clone._value = source._value;
|
||||||
clone._checked = source._checked;
|
clone._checked = source._checked;
|
||||||
clone._checked_dirty = source._checked_dirty;
|
clone._checked_dirty = source._checked_dirty;
|
||||||
clone._selected = source._selected;
|
clone._selection_direction = source._selection_direction;
|
||||||
|
clone._selection_start = source._selection_start;
|
||||||
|
clone._selection_end = source._selection_end;
|
||||||
clone._indeterminate = source._indeterminate;
|
clone._indeterminate = source._indeterminate;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user