mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 06:23:45 +00:00
add selection api to HTMLTextAreaElement
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
// 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 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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user