diff --git a/src/browser/webapi/element/html/TextArea.zig b/src/browser/webapi/element/html/TextArea.zig
index e9a1dcb1..b0b0ae1c 100644
--- a/src/browser/webapi/element/html/TextArea.zig
+++ b/src/browser/webapi/element/html/TextArea.zig
@@ -16,6 +16,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
+const std = @import("std");
const js = @import("../../../js/js.zig");
const Page = @import("../../../Page.zig");
@@ -23,12 +24,17 @@ const Node = @import("../../Node.zig");
const Element = @import("../../Element.zig");
const HtmlElement = @import("../Html.zig");
const Form = @import("Form.zig");
+const Selection = @import("../../Selection.zig");
const TextArea = @This();
_proto: *HtmlElement,
_value: ?[]const u8 = null,
+_selection_start: u32 = 0,
+_selection_end: u32 = 0,
+_selection_direction: Selection.SelectionDirection = .none,
+
pub fn asElement(self: *TextArea) *Element {
return self._proto._proto;
}
@@ -109,6 +115,108 @@ pub fn setRequired(self: *TextArea, required: bool, page: *Page) !void {
}
}
+pub fn select(self: *TextArea) !void {
+ const len = if (self._value) |v| @as(u32, @intCast(v.len)) else 0;
+ try self.setSelectionRange(0, len, null);
+}
+
+const HowSelected = union(enum) { partial: struct { u32, u32 }, full, none };
+
+fn howSelected(self: *const TextArea) HowSelected {
+ 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: *TextArea, str: []const u8, page: *Page) !void {
+ const arena = page.arena;
+
+ switch (self.howSelected()) {
+ .full => {
+ // if the text area 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 text area 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 text area 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 TextArea) []const u8 {
+ return @tagName(self._selection_direction);
+}
+
+pub fn getSelectionStart(self: *const TextArea) u32 {
+ return self._selection_start;
+}
+
+pub fn setSelectionStart(self: *TextArea, value: u32) void {
+ self._selection_start = value;
+}
+
+pub fn getSelectionEnd(self: *const TextArea) u32 {
+ return self._selection_end;
+}
+
+pub fn setSelectionEnd(self: *TextArea, value: u32) void {
+ self._selection_end = value;
+}
+
+pub fn setSelectionRange(self: *TextArea, selection_start: u32, selection_end: u32, selection_dir: ?[]const u8) !void {
+ 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: *TextArea, page: *Page) ?*Form {
const element = self.asElement();