╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ───────────────╮ │src/ui/widget/thread.zig│ ╰────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │506 { │ │476 - var search_box = try widget.SearchBox.init(allocator, session, " search ", "search", search); │ │507 + var search_box = try widget.SearchBox.init(allocator, session, search_label, "search", search); │ │508 errdefer search_box.deinit(allocator); │ │509 box.getFocus().child_id = search_box.getFocus().id; │ │510 try box.children.put(allocator, search_box.getFocus().id, .{ .widget = .{ .search_box = search_box }, .rect = null, .min_size = widget.SearchBox.min_size });│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │511 } │ │512 │ │482 - if (assignee) |value| { │ │483 - var assignee_box = try widget.SearchBox.init(allocator, session, value.label, "assignee", value.text); │ │484 - errdefer assignee_box.deinit(allocator); │ │485 - try box.children.put(allocator, assignee_box.getFocus().id, .{ .widget = .{ .search_box = assignee_box }, .rect = null, .min_size = widget.SearchBox.min_size });│ │486 - } │ │487 - │ │488 - return .{ .box = box, .tab_ids = .empty, .box_count = box.children.count() }; │ │513 + return .{ .box = box, .tab_ids = .empty }; │ │514 } │ │515 │ │516 pub fn addTab(self: *Header, allocator: std.mem.Allocator, label: []const u8, link: []const u8, view_index: usize) !void { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │561 } │ │562 │ │563 pub fn input(self: *Header, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void { │ │539 - if (self.focusedBox()) |index| { │ │540 - const search_box = self.searchBox(index); │ │541 - // tab, and right-arrow past the last character, step to the next box or the tabs │ │564 + const search_box = self.searchBox(); │ │565 + if (self.box.getFocus().child_id == search_box.getFocus().id) { │ │566 + // tab, and right-arrow past the last character, step to the tabs │ │567 if (key == .tab or (key == .arrow_right and search_box.atEnd())) { │ │543 - root_focus.setFocus(if (index + 1 < self.box_count) self.searchBox(index + 1).getFocus().id else self.tab_ids.keys()[0]);│ │544 - return; │ │545 - } │ │546 - // back-tab, and left-arrow before the first character, step to the previous box │ │547 - if (index > 0 and (key == .back_tab or (key == .arrow_left and search_box.text_input.cursor == 0))) { │ │548 - root_focus.setFocus(self.searchBox(index - 1).getFocus().id); │ │568 + root_focus.setFocus(self.tab_ids.keys()[0]); │ │569 return; │ │570 } │ │571 return search_box.input(allocator, key, root_focus); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────╮ │574 if (key == .back_tab or (key == .arrow_left and current_tab == 0)) { │ │575 // remembered here as well as in build, which may not run between keys│ │576 self.selected_tab = current_tab; │ │557 - root_focus.setFocus(self.searchBox(self.box_count - 1).getFocus().id);│ │577 + root_focus.setFocus(search_box.getFocus().id); │ │578 return; │ │579 } │ │580 if (inp.moveTab(key, current_tab, self.tab_ids.count())) |new_tab| { │ ╰───────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │582 } │ │583 } │ │584 │ │565 - // the typed text when enter should submit it: a box holds focus. the │ │585 + // the typed query when enter should submit it: the box holds focus. the │ │586 // caller owns the returned text. │ │567 - pub fn submittedText(self: *Header, allocator: std.mem.Allocator) !?Submission { │ │568 - const index = self.focusedBox() orelse return null; │ │569 - return .{ .kind = if (index == 0) .search else .assignee, .text = try self.searchBox(index).text(allocator) };│ │587 + pub fn submittedText(self: *Header, allocator: std.mem.Allocator) !?[]u8 { │ │588 + const search_box = self.searchBox(); │ │589 + if (self.box.getFocus().child_id != search_box.getFocus().id) return null; │ │590 + return try search_box.text(allocator); │ │591 } │ │592 │ │572 - // the index of the box holding focus, if one does │ │573 - fn focusedBox(self: *Header) ?usize { │ │574 - const child_id = self.box.getFocus().child_id orelse return null; │ │575 - const index = self.box.children.getIndex(child_id) orelse return null; │ │576 - return if (index < self.box_count) index else null; │ │593 + fn searchBox(self: *Header) *widget.SearchBox { │ │594 + return &self.box.children.values()[search_index].widget.search_box; │ │595 } │ │596 │ │579 - fn searchBox(self: *Header, index: usize) *widget.SearchBox { │ │580 - return &self.box.children.values()[index].widget.search_box; │ │581 - } │ │582 - │ │597 pub fn clearGrid(self: *Header) void { │ │598 self.box.clearGrid(); │ │599 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │839 return null; │ │840 } │ │841 │ │828 - fn listRoute(identity: []const u8, status: Status, label: []const u8, selected: []const u8) ?ui.RoutablePage {│ │829 - return Data.listRoute(identity, status, label, selected); │ │842 + fn listRoute(identity: []const u8, status: Status, selected: []const u8) ?ui.RoutablePage { │ │843 + return Data.listRoute(identity, status, selected); │ │844 } │ │845 │ │846 fn draftsRoute(identity: []const u8) ?ui.RoutablePage { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │859 return if (supports_conflicts) Data.resolveRoute(identity, selected, picks) else null; │ │860 } │ │861 │ │848 - // a list link keeps the filters the page carries: the label, and the │ │849 - // search query and url-encoded assignee when set │ │850 - fn listLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, status: Status, label: []const u8, id: []const u8, search: []const u8, assignee: []const u8) ![]const u8 {│ │851 - var route = listRoute(identity, status, label, id) orelse return error.RouteTooLong; │ │862 + // a list link keeps the search the page carries │ │863 + fn listLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, status: Status, id: []const u8, search: []const u8) ![]const u8 { │ │864 + var route = listRoute(identity, status, id) orelse return error.RouteTooLong; │ │865 if (search.len != 0) route = route.withSearch(search) orelse return error.RouteTooLong; │ │853 - if (assignee.len != 0) route = route.withEncodedAssignee(assignee) orelse return error.RouteTooLong; │ │866 return page_arena.allocator().print("a:{s}", .{try route.toUrl(page_arena)}); │ │855 - } │ │856 - │ │857 - fn labelLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, status: Status, label: []const u8, search: []const u8, assignee: []const u8) ![]const u8 { │ │858 - const encoded = try ui.urlEncodeRef(page_arena.allocator(), label); │ │859 - return listLink(page_arena, identity, status, encoded, "", search, assignee); │ │867 } │ │868 │ │869 fn addToolButton(allocator: std.mem.Allocator, row: *wgt.Box(Widget), label: []const u8, bottom_label: []const u8, action: []const u8) !void { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │897 │ │898 if (supports_forks and self.session.data.current_page.parent() == .fork) { │ │899 // the fork page's own tabs show the diff and commits │ │893 - const route = ui.RoutablePage.repoPatchesRoute(self.data.identity, .open, "", entry.id) orelse return error.RouteTooLong; │ │900 + const route = ui.RoutablePage.repoPatchesRoute(self.data.identity, .open, entry.id) orelse return error.RouteTooLong; │ │901 try addToolButton(allocator, row, "view on target repo", "", try pa.print("a:{s}", .{try route.toUrl(self.session.page_arena)}));│ │902 } else if (supports_forks) { │ │903 if (try Data.diffRoute(self.data.identity, entry)) |route| { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 999 var text_box = try wgt.TextBox.init(allocator, back_label, .{ .border = .single, .round_corners = true, .wrap_kind = .none }); │ │1000 errdefer text_box.deinit(allocator); │ │1001 text_box.getFocus().mode = .all; │ │ 995 - text_box.getFocus().kind = .{ .custom = try listLink(self.session.page_arena, self.data.identity, entryStatus(entry), "", entry.id, self.data.search orelse "", "") };│ │1002 + text_box.getFocus().kind = .{ .custom = try listLink(self.session.page_arena, self.data.identity, entryStatus(entry), entry.id, self.data.search orelse "") }; │ │1003 try inner_box.children.put(allocator, text_box.getFocus().id, .{ .widget = .{ .text_box = text_box }, .rect = null, .min_size = null }); │ │1004 self.title_id = text_box.getFocus().id; │ │1005 } else { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1030 var label_iter = Event.labelIterator(entry.record.event.labels); │ │1031 while (label_iter.next()) |label| { │ │1032 if (label.len == 0) continue; │ │1026 - try items.append(allocator, .{ .text = label, .link = try labelLink(self.session.page_arena, self.data.identity, entryStatus(entry), label, self.data.search orelse "", assigneeFilter(self.data)) });│ │1033 + const search = try srch_thrd.withFilter(self.session.page_arena.allocator(), self.data.search orelse "", srch_thrd.label_prefix, label); │ │1034 + try items.append(allocator, .{ .text = label, .link = try listLink(self.session.page_arena, self.data.identity, entryStatus(entry), "", search) }); │ │1035 } │ │1036 if (items.items.len > 0) { │ │1037 var labels = try WordFlow.init(allocator); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯