mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
Merge pull request #1533 from lightpanda-io/element_positions
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
e2e-test / browser fetch (push) Has been cancelled
zig-test / zig test using v8 in debug mode (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
nightly build / build-linux-x86_64 (push) Has been cancelled
nightly build / build-linux-aarch64 (push) Has been cancelled
nightly build / build-macos-aarch64 (push) Has been cancelled
nightly build / build-macos-x86_64 (push) Has been cancelled
wpt / web platform tests json output (push) Has been cancelled
wpt / perf-fmt (push) Has been cancelled
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
e2e-test / browser fetch (push) Has been cancelled
zig-test / zig test using v8 in debug mode (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
nightly build / build-linux-x86_64 (push) Has been cancelled
nightly build / build-linux-aarch64 (push) Has been cancelled
nightly build / build-macos-aarch64 (push) Has been cancelled
nightly build / build-macos-x86_64 (push) Has been cancelled
wpt / web platform tests json output (push) Has been cancelled
wpt / perf-fmt (push) Has been cancelled
Add various element position properties
This commit is contained in:
@@ -106,6 +106,7 @@ _element_rel_lists: Element.RelListLookup = .empty,
|
||||
_element_shadow_roots: Element.ShadowRootLookup = .empty,
|
||||
_node_owner_documents: Node.OwnerDocumentLookup = .empty,
|
||||
_element_assigned_slots: Element.AssignedSlotLookup = .empty,
|
||||
_element_scroll_positions: Element.ScrollPositionLookup = .empty,
|
||||
|
||||
/// Lazily-created inline event listeners (or listeners provided as attributes).
|
||||
/// Avoids bloating all elements with extra function fields for rare usage.
|
||||
|
||||
116
src/browser/tests/element/position.html
Normal file
116
src/browser/tests/element/position.html
Normal file
@@ -0,0 +1,116 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../testing.js"></script>
|
||||
|
||||
<div id="test1">Test Element</div>
|
||||
<div id="test2">Another Element</div>
|
||||
|
||||
<script id="clientDimensions">
|
||||
{
|
||||
const test1 = $('#test1');
|
||||
|
||||
// clientWidth/Height - default is 5px in dummy layout
|
||||
testing.expectEqual('number', typeof test1.clientWidth);
|
||||
testing.expectEqual('number', typeof test1.clientHeight);
|
||||
testing.expectTrue(test1.clientWidth >= 0);
|
||||
testing.expectTrue(test1.clientHeight >= 0);
|
||||
|
||||
// clientTop/Left should be 0 (no borders in dummy layout)
|
||||
testing.expectEqual(0, test1.clientTop);
|
||||
testing.expectEqual(0, test1.clientLeft);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="scrollDimensions">
|
||||
{
|
||||
const test1 = $('#test1');
|
||||
|
||||
// In dummy layout, scroll dimensions equal client dimensions (no overflow)
|
||||
testing.expectEqual(test1.clientWidth, test1.scrollWidth);
|
||||
testing.expectEqual(test1.clientHeight, test1.scrollHeight);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="scrollPosition">
|
||||
{
|
||||
const test1 = $('#test1');
|
||||
|
||||
// Initial scroll position should be 0
|
||||
testing.expectEqual(0, test1.scrollTop);
|
||||
testing.expectEqual(0, test1.scrollLeft);
|
||||
|
||||
// Setting scroll position
|
||||
test1.scrollTop = 50;
|
||||
testing.expectEqual(50, test1.scrollTop);
|
||||
|
||||
test1.scrollLeft = 25;
|
||||
testing.expectEqual(25, test1.scrollLeft);
|
||||
|
||||
// Negative values should be clamped to 0
|
||||
test1.scrollTop = -10;
|
||||
testing.expectEqual(0, test1.scrollTop);
|
||||
|
||||
test1.scrollLeft = -5;
|
||||
testing.expectEqual(0, test1.scrollLeft);
|
||||
|
||||
// Each element has independent scroll position
|
||||
const test2 = $('#test2');
|
||||
testing.expectEqual(0, test2.scrollTop);
|
||||
testing.expectEqual(0, test2.scrollLeft);
|
||||
|
||||
test2.scrollTop = 100;
|
||||
testing.expectEqual(100, test2.scrollTop);
|
||||
testing.expectEqual(0, test1.scrollTop); // test1 should still be 0
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="offsetDimensions">
|
||||
{
|
||||
const test1 = $('#test1');
|
||||
|
||||
// offsetWidth/Height should be numbers
|
||||
testing.expectEqual('number', typeof test1.offsetWidth);
|
||||
testing.expectEqual('number', typeof test1.offsetHeight);
|
||||
testing.expectTrue(test1.offsetWidth >= 0);
|
||||
testing.expectTrue(test1.offsetHeight >= 0);
|
||||
|
||||
// Should equal client dimensions
|
||||
testing.expectEqual(test1.clientWidth, test1.offsetWidth);
|
||||
testing.expectEqual(test1.clientHeight, test1.offsetHeight);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="offsetPosition">
|
||||
{
|
||||
const test1 = $('#test1');
|
||||
const test2 = $('#test2');
|
||||
|
||||
// offsetTop/Left should be calculated from tree position
|
||||
// These values are based on the heuristic layout engine
|
||||
const top1 = test1.offsetTop;
|
||||
const left1 = test1.offsetLeft;
|
||||
const top2 = test2.offsetTop;
|
||||
const left2 = test2.offsetLeft;
|
||||
|
||||
// Position values should be numbers
|
||||
testing.expectEqual('number', typeof top1);
|
||||
testing.expectEqual('number', typeof left1);
|
||||
testing.expectEqual('number', typeof top2);
|
||||
testing.expectEqual('number', typeof left2);
|
||||
|
||||
// Siblings should have different positions (either different x or y)
|
||||
testing.expectTrue(top1 !== top2 || left1 !== left2);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="offsetVsBounding">
|
||||
{
|
||||
const test1 = $('#test1');
|
||||
|
||||
// offsetTop/Left should match getBoundingClientRect
|
||||
const rect = test1.getBoundingClientRect();
|
||||
testing.expectEqual(rect.y, test1.offsetTop);
|
||||
testing.expectEqual(rect.x, test1.offsetLeft);
|
||||
testing.expectEqual(rect.width, test1.offsetWidth);
|
||||
testing.expectEqual(rect.height, test1.offsetHeight);
|
||||
}
|
||||
</script>
|
||||
@@ -49,6 +49,12 @@ pub const RelListLookup = std.AutoHashMapUnmanaged(*Element, *collections.DOMTok
|
||||
pub const ShadowRootLookup = std.AutoHashMapUnmanaged(*Element, *ShadowRoot);
|
||||
pub const AssignedSlotLookup = std.AutoHashMapUnmanaged(*Element, *Html.Slot);
|
||||
|
||||
pub const ScrollPosition = struct {
|
||||
x: u32 = 0,
|
||||
y: u32 = 0,
|
||||
};
|
||||
pub const ScrollPositionLookup = std.AutoHashMapUnmanaged(*Element, ScrollPosition);
|
||||
|
||||
pub const Namespace = enum(u8) {
|
||||
html,
|
||||
svg,
|
||||
@@ -1027,6 +1033,82 @@ pub fn getClientRects(self: *Element, page: *Page) ![]DOMRect {
|
||||
return ptr[0..1];
|
||||
}
|
||||
|
||||
pub fn getScrollTop(self: *Element, page: *Page) u32 {
|
||||
const pos = page._element_scroll_positions.get(self) orelse return 0;
|
||||
return pos.y;
|
||||
}
|
||||
|
||||
pub fn setScrollTop(self: *Element, value: i32, page: *Page) !void {
|
||||
const gop = try page._element_scroll_positions.getOrPut(page.arena, self);
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = .{};
|
||||
}
|
||||
gop.value_ptr.y = @intCast(@max(0, value));
|
||||
}
|
||||
|
||||
pub fn getScrollLeft(self: *Element, page: *Page) u32 {
|
||||
const pos = page._element_scroll_positions.get(self) orelse return 0;
|
||||
return pos.x;
|
||||
}
|
||||
|
||||
pub fn setScrollLeft(self: *Element, value: i32, page: *Page) !void {
|
||||
const gop = try page._element_scroll_positions.getOrPut(page.arena, self);
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = .{};
|
||||
}
|
||||
gop.value_ptr.x = @intCast(@max(0, value));
|
||||
}
|
||||
|
||||
pub fn getScrollHeight(self: *Element, page: *Page) !f64 {
|
||||
// In our dummy layout engine, content doesn't overflow
|
||||
return self.getClientHeight(page);
|
||||
}
|
||||
|
||||
pub fn getScrollWidth(self: *Element, page: *Page) !f64 {
|
||||
// In our dummy layout engine, content doesn't overflow
|
||||
return self.getClientWidth(page);
|
||||
}
|
||||
|
||||
pub fn getOffsetHeight(self: *Element, page: *Page) !f64 {
|
||||
if (!try self.checkVisibility(page)) {
|
||||
return 0.0;
|
||||
}
|
||||
const dims = try self.getElementDimensions(page);
|
||||
return dims.height;
|
||||
}
|
||||
|
||||
pub fn getOffsetWidth(self: *Element, page: *Page) !f64 {
|
||||
if (!try self.checkVisibility(page)) {
|
||||
return 0.0;
|
||||
}
|
||||
const dims = try self.getElementDimensions(page);
|
||||
return dims.width;
|
||||
}
|
||||
|
||||
pub fn getOffsetTop(self: *Element, page: *Page) !f64 {
|
||||
if (!try self.checkVisibility(page)) {
|
||||
return 0.0;
|
||||
}
|
||||
return calculateDocumentPosition(self.asNode());
|
||||
}
|
||||
|
||||
pub fn getOffsetLeft(self: *Element, page: *Page) !f64 {
|
||||
if (!try self.checkVisibility(page)) {
|
||||
return 0.0;
|
||||
}
|
||||
return calculateSiblingPosition(self.asNode());
|
||||
}
|
||||
|
||||
pub fn getClientTop(_: *Element) f64 {
|
||||
// Border width - in our dummy layout, we don't apply borders to layout
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
pub fn getClientLeft(_: *Element) f64 {
|
||||
// Border width - in our dummy layout, we don't apply borders to layout
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Calculates document position by counting all nodes that appear before this one
|
||||
// in tree order, but only traversing the "left side" of the tree.
|
||||
//
|
||||
@@ -1502,6 +1584,16 @@ pub const JsApi = struct {
|
||||
pub const checkVisibility = bridge.function(Element.checkVisibility, .{});
|
||||
pub const clientWidth = bridge.accessor(Element.getClientWidth, null, .{});
|
||||
pub const clientHeight = bridge.accessor(Element.getClientHeight, null, .{});
|
||||
pub const clientTop = bridge.accessor(Element.getClientTop, null, .{});
|
||||
pub const clientLeft = bridge.accessor(Element.getClientLeft, null, .{});
|
||||
pub const scrollTop = bridge.accessor(Element.getScrollTop, Element.setScrollTop, .{});
|
||||
pub const scrollLeft = bridge.accessor(Element.getScrollLeft, Element.setScrollLeft, .{});
|
||||
pub const scrollHeight = bridge.accessor(Element.getScrollHeight, null, .{});
|
||||
pub const scrollWidth = bridge.accessor(Element.getScrollWidth, null, .{});
|
||||
pub const offsetTop = bridge.accessor(Element.getOffsetTop, null, .{});
|
||||
pub const offsetLeft = bridge.accessor(Element.getOffsetLeft, null, .{});
|
||||
pub const offsetWidth = bridge.accessor(Element.getOffsetWidth, null, .{});
|
||||
pub const offsetHeight = bridge.accessor(Element.getOffsetHeight, null, .{});
|
||||
pub const getClientRects = bridge.function(Element.getClientRects, .{});
|
||||
pub const getBoundingClientRect = bridge.function(Element.getBoundingClientRect, .{});
|
||||
pub const getElementsByTagName = bridge.function(Element.getElementsByTagName, .{});
|
||||
|
||||
Reference in New Issue
Block a user