╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ───────────────╮ │src/ui/widget/thread.zig│ ╰────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │1737 // indices within a split (the horizontal box inside the stack). │ │1738 const list_index: usize = 0; │ │1739 const detail_index: usize = 1; │ │1740 - // indices within the detail frame (the way back above the detail view).│ │1741 - const detail_back_index: usize = 0; │ │1742 - const detail_view_index: usize = 1; │ │1740 const list_max_width: usize = 35; │ │1741 const detail_min_width: usize = 40; │ │1742 │ ╰──────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2071 var frame = try wgt.Box(Widget).init(allocator, .{ .border = .hidden, .direction = .vert }); │ │2072 errdefer frame.deinit(allocator); │ │2073 frame.getFocus().mode = .mouse; │ │2077 - { │ │2078 - const label = try session.page_arena.allocator().print("← back to {s} list", .{thread_name}); │ │2079 - var back = try wgt.TextBox.init(allocator, label, .{ .border = .single, .round_corners = true, .wrap_kind = .none }); │ │2080 - errdefer back.deinit(allocator); │ │2081 - back.getFocus().mode = .all; │ │2082 - try frame.children.put(allocator, back.getFocus().id, .{ .widget = .{ .text_box = back }, .rect = null, .min_size = null, .hidden = true });│ │2083 - } │ │2074 + try widget.addListBack(allocator, &frame, "← back to " ++ thread_name ++ " list"); │ │2075 var detail_view = try DetailType.init(allocator, data, session, null, .{}); │ │2076 errdefer detail_view.deinit(allocator); │ │2077 frame.getFocus().child_id = detail_view.getFocus().id; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2512 } │ │2513 │ │2514 fn detail(self: *This, index: usize) *DetailType { │ │2524 - return &@field(self.detailFrame(index).children.values()[detail_view_index].widget, Data.detail_widget_name); │ │2525 - } │ │2526 - │ │2527 - // the detail's way back to a list that isn't shown beside it │ │2528 - fn backId(self: *This, index: usize) ?usize { │ │2529 - const frame = self.detailFrame(index); │ │2530 - return if (frame.children.values()[detail_back_index].hidden) null else frame.children.keys()[detail_back_index]; │ │2515 + return &@field(self.detailFrame(index).children.values()[widget.list_pane_index].widget, Data.detail_widget_name);│ │2516 } │ │2517 │ │2518 fn window(self: *This, index: usize) *const Window { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2574 const changed = if (self.detail(index).entry) |current| !std.mem.eql(u8, current.id, entry.id) else true; │ │2575 if (!changed) return; │ │2576 try self.detail(index).setEntry(allocator, entry); │ │2592 - // the way back links to the shown thread's own row, so both hosts │ │2593 - // hand its press and enter to input instead of navigating │ │2594 - const back = &self.detailFrame(index).children.values()[detail_back_index].widget.text_box; │ │2595 - back.getFocus().kind = .{ .custom = try rowLink(self.session.page_arena, self.data, entry.id) }; │ │2577 + widget.setListBackLink(self.detailFrame(index), try rowLink(self.session.page_arena, self.data, entry.id)); │ │2578 } │ │2579 │ │2598 - // show the way back to the list only while the list is hidden │ │2599 - fn showBack(self: *This, index: usize, shown: bool) void { │ │2600 - const frame = self.detailFrame(index); │ │2601 - frame.children.values()[detail_back_index].hidden = !shown; │ │2602 - if (!shown and frame.getFocus().child_id == frame.children.keys()[detail_back_index]) │ │2603 - frame.getFocus().child_id = frame.children.keys()[detail_view_index]; │ │2604 - } │ │2605 - │ │2580 pub fn build(self: *This, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void {│ │2581 self.clearGrid(); │ │2582 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2591 // the list cap below and whether the detail carries a way back │ │2592 const both_panes_fit = if (constraint.max_size.width) |w| w >= list_max_width + detail_min_width else true;│ │2593 try self.refreshDetail(allocator, i); │ │2620 - self.showBack(i, !both_panes_fit); │ │2594 + widget.showListBack(self.detailFrame(i), !both_panes_fit); │ │2595 │ │2596 // the selected list row shows a border (the focused TextBox │ │2597 // upgrades it to a double border itself); the rest stay borderless. │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2699 const i = self.selectedSplitIndex() orelse return; │ │2700 if (self.detailActive(i)) { │ │2701 // the way back to the list, by enter or a click │ │2728 - if (self.backId(i)) |id| { │ │2702 + if (widget.listBackId(self.detailFrame(i))) |id| { │ │2703 if (inp.activated(root_focus, id, key)) return self.focusList(i, root_focus);│ │2704 if (root_focus.grandchild_id == id) return switch (key) { │ │2705 .arrow_left => self.focusList(i, root_focus), │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2711 switch (try self.detail(i).inputWithExit(allocator, key, root_focus)) { │ │2712 .none => {}, │ │2713 .list => self.focusList(i, root_focus), │ │2740 - .header => if (self.backId(i)) |id| root_focus.setFocus(id) else self.focusHeader(root_focus), │ │2714 + .header => if (widget.listBackId(self.detailFrame(i))) |id| root_focus.setFocus(id) else self.focusHeader(root_focus),│ │2715 } │ │2716 } else { │ │2717 try self.listInput(allocator, i, key, root_focus); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │3293 try self.session.navigate(route); │ │3294 } │ │3295 │ │3322 - // enter the detail pane, landing on the way back to the list when it │ │3323 - // has one. selecting the frame's child (then focusing the frame) also │ │3324 - // handles the too-narrow case where the pane isn't laid out yet. │ │3296 + // enter the detail pane, landing on the way back to the list when it shows │ │3297 fn focusDetail(self: *This, index: usize, root_focus: *Focus) void { │ │3326 - if (self.backId(index) == null) { │ │3327 - _ = self.detail(index).focusFirst(root_focus); │ │3328 - return; │ │3329 - } │ │3330 - const frame = self.detailFrame(index); │ │3331 - frame.getFocus().child_id = frame.children.keys()[detail_back_index]; │ │3332 - root_focus.setFocus(frame.getFocus().id); │ │3298 + if (widget.listBackId(self.detailFrame(index)) != null) return widget.focusListBack(self.detailFrame(index), root_focus);│ │3299 + _ = self.detail(index).focusFirst(root_focus); │ │3300 } │ │3301 │ │3302 // return to the list. │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯