╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2706 .resize => try root.input(allocator, key, root_focus), │ │2707 }, │ │2708 // a scroll tick goes to the pane under the pointer, focusing it first. │ │2709 - // from a text input it moves like the arrows instead, so scrolling up │ │2710 - // from a search box reaches the header. │ │2709 + // from outside any pane (the tabs, a sub-header) or from a text input │ │2710 + // it moves like the arrows instead, so scrolling up from a search box │ │2711 + // reaches the header and stops there. │ │2712 .scroll_up, .scroll_down => |position| { │ │2712 - if (!focusInTextInput(root_focus)) if (scrollUnder(root_focus, position)) |scroll_id| {│ │2713 - if (!focusInside(root_focus, scroll_id)) root_focus.setFocus(scroll_id); │ │2713 + if (focusedPane(root_focus)) |pane| if (scrollUnder(root_focus, position)) |under| { │ │2714 + if (under != pane) root_focus.setFocus(under); │ │2715 }; │ │2716 try root.input(allocator, key, root_focus); │ │2717 }, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────╮ │2744 return found; │ │2745 } │ │2746 │ │2746 -fn focusInTextInput(root_focus: *const Focus) bool { │ │2747 - const focused = root_focus.grandchild_id orelse return false; │ │2748 - const child = root_focus.children.get(focused) orelse return false; │ │2749 - return switch (child.focus.kind) { │ │2750 - .text_input, .text_input_password, .text_area => true, │ │2751 - else => false, │ │2752 - }; │ │2753 -} │ │2754 - │ │2755 -// whether the focused widget is `id` or lies under it │ │2756 -fn focusInside(root_focus: *const Focus, id: usize) bool { │ │2757 - var current = root_focus.grandchild_id orelse return false; │ │2758 - while (true) { │ │2759 - if (current == id) return true; │ │2760 - const child = root_focus.children.get(current) orelse return false; │ │2747 +// the innermost vertically scrolling widget the focus lies in, unless the │ │2748 +// focused widget is a text input │ │2749 +fn focusedPane(root_focus: *const Focus) ?usize { │ │2750 + var current = root_focus.grandchild_id orelse return null; │ │2751 + switch ((root_focus.children.get(current) orelse return null).focus.kind) { │ │2752 + .text_input, .text_input_password, .text_area => return null, │ │2753 + else => {}, │ │2754 + } │ │2755 + while (root_focus.children.get(current)) |child| { │ │2756 + if (child.focus.scroll) |info| if (info.direction != .horiz) return current;│ │2757 current = child.parent_id; │ │2758 } │ │2759 + return null; │ │2760 } │ │2761 │ │2762 // a link to bytes the server serves directly rather than to a page route, so │ ╰──────────────────────────────────────────────────────────────────────────────────────────╯