╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1978 } │ │1979 }; │ │1980 │ │1981 + // the focusable owning each cell, among this panel's own (skipping any │ │1982 + // that belong to a child scroll, whose cells are drawn in their own panel). │ │1983 + // claimed in focus order, so the first focusable over a cell keeps it. │ │1984 + // looking every focusable up for every cell is quadratic in a long list. │ │1985 + const owners = try allocator.alloc(?usize, grid.size.width * grid.size.height); │ │1986 + defer allocator.free(owners); │ │1987 + @memset(owners, null); │ │1988 + { │ │1989 + var it = focus.children.iterator(); │ │1990 + while (it.next()) |entry| { │ │1991 + const child = entry.value_ptr.*; │ │1992 + if (child.focus.mode != .all) continue; │ │1993 + if (excluded.contains(entry.key_ptr.*)) continue; │ │1994 + const r = child.rect; │ │1995 + for (@min(r.y, grid.size.height)..@min(r.y + r.size.height, grid.size.height)) |y| { │ │1996 + for (@min(r.x, grid.size.width)..@min(r.x + r.size.width, grid.size.width)) |x| {│ │1997 + const owner = &owners[y * grid.size.width + x]; │ │1998 + if (owner.* == null) owner.* = entry.key_ptr.*; │ │1999 + } │ │2000 + } │ │2001 + } │ │2002 + } │ │2003 + │ │2004 // the cells are the volatile part of a panel. js replaces this one subtree │ │2005 // wholesale while retaining the panel's native controls and scroll shells. │ │2006 try output.appendSlice(allocator, "<div class=\"grid-cells\">"); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2026 if (x >= r.x and y >= r.y and x < r.x + r.size.width and y < r.y + r.size.height) break :blk true;│ │2027 } │ │2028 break :blk false; │ │2006 - }; │ │2007 - // the focusable cell at (x, y) among this panel's own focusables (skipping │ │2008 - // any that belong to a child scroll, whose cells are drawn in their own │ │2009 - // panel); none for a covered cell. │ │2010 - const cell_id = if (covered_by_scroll) null else blk: { │ │2011 - var iter = focus.children.iterator(); │ │2012 - while (iter.next()) |entry| { │ │2013 - const child = entry.value_ptr.*; │ │2014 - if (child.focus.mode != .all) continue; │ │2015 - if (excluded.contains(entry.key_ptr.*)) continue; │ │2016 - const r = child.rect; │ │2017 - if (x >= r.x and y >= r.y and x < r.x + r.size.width and y < r.y + r.size.height) { │ │2018 - break :blk entry.key_ptr.*; │ │2019 - } │ │2020 - } │ │2021 - break :blk null; │ │2029 }; │ │2030 + // the focusable owning the cell at (x, y); none for a covered cell. │ │2031 + const cell_id = if (covered_by_scroll) null else owners[y * grid.size.width + x]; │ │2032 const fg = if (covered_by_scroll) null else cell.style.fg; │ │2033 const bg = cell.style.bg; │ │2034 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯