╭ changed ─────────────╮ │src/ui/Repo/Issues.zig│ ╰──────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭───────────────────────────────────────────────────────────────────────────────╮ │653 return self.stackSelectedIndex() == tags_view_index; │ │654 } │ │655 │ │629 - fn newViewActive(self: *View) bool { │ │656 + fn formViewActive(self: *View) bool { │ │657 if (self.headerActive()) return false; │ │631 - return self.stackSelectedIndex() == new_view_index; │ │658 + return self.stackSelectedIndex() == form_view_index; │ │659 } │ │660 │ │661 // the selected issue's index, or null when a window-navigation row is│ ╰───────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │680 if (self.header().getSelectedIndex()) |index| { │ │681 const stack = self.viewStack(); │ │682 stack.getFocus().child_id = stack.children.keys()[index]; │ │656 - self.session.data.current_page = (if (index == viewIndex(self.data.view) and self.data.selected_id.len != 0)│ │683 + self.session.data.current_page = (if (index == form_view_index and self.data.view == .edit) │ │684 + ui.RoutablePage.repoIssuesEditRoute(self.data.identity, self.data.selected_id) │ │685 + else if (index == viewIndex(self.data.view) and self.data.selected_id.len != 0) │ │686 ui.RoutablePage.repoIssuesRoute(self.data.identity, .open, self.data.tag, self.data.selected_id) │ │687 else if (index == tags_view_index) │ │688 ui.RoutablePage.repoIssuesTagsRoute(self.data.identity, self.data.tag) │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │660 - else if (index == new_view_index) │ │689 + else if (index == form_view_index) │ │690 ui.RoutablePage.repoIssuesNewRoute(self.data.identity) │ │691 else │ │692 ui.RoutablePage.repoIssuesRoute(self.data.identity, splitStatus(index), self.data.tag, "")) orelse self.session.data.current_page;│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │740 // refresh the form inputs' entries in the session's focus-id -> input │ │741 // map with this frame's addresses, so the web/wasm form handling can │ │742 // find them by focus id │ │714 - const form = self.newForm(); │ │743 + const form = self.issueForm(); │ │744 const inputs_arena = self.session.arena.allocator(); │ │745 for (form.children.values()) |*child| switch (child.widget) { │ │746 .text_input => |*ti| try self.session.text_inputs.put(inputs_arena, ti.getFocus().id, ti),│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │747 else => {}, │ │748 }; │ │749 + │ │750 + // on an edit page, register the issue's content as the inputs' │ │751 + // page-constant initial values, which the web overlay renders │ │752 + // into them. │ │753 + if (self.data.view == .edit) { │ │754 + if (self.data.selectedIssue()) |entry| { │ │755 + const fields = form.children.values(); │ │756 + try self.session.input_values.put(inputs_arena, fields[title_field_index].widget.text_input.getFocus().id, entry.issue.title); │ │757 + try self.session.input_values.put(inputs_arena, fields[tags_field_index].widget.text_input.getFocus().id, entry.issue.tags); │ │758 + try self.session.input_values.put(inputs_arena, fields[description_field_index].widget.text_input.getFocus().id, entry.issue.description);│ │759 + } │ │760 + } │ │761 │ │762 try self.box.build(allocator, constraint, root_focus); │ │763 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │808 try row.children.put(allocator, button.getFocus().id, .{ .widget = .{ .text_box = button }, .rect = null, .min_size = .{ .width = action.len + 2, .height = null } });│ │809 } │ │810 │ │811 + // the edit button links to the issue's edit page. │ │812 + { │ │813 + var button = try wgt.TextBox(ui.Widget).init(allocator, "edit", .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none }); │ │814 + errdefer button.deinit(allocator); │ │815 + button.getFocus().focusable = true; │ │816 + const route = ui.RoutablePage.repoIssuesEditRoute(self.data.identity, entry.id) orelse return error.RouteTooLong; │ │817 + button.getFocus().kind = .{ .custom = try std.fmt.allocPrint(pa, "a:{s}", .{try route.toUrl(self.session.page_arena)}) }; │ │818 + try row.children.put(allocator, button.getFocus().id, .{ .widget = .{ .text_box = button }, .rect = null, .min_size = .{ .width = "edit".len + 2, .height = null } });│ │819 + } │ │820 + │ │821 row.getFocus().child_id = row.children.keys()[button_in_row_index]; │ │822 } │ │823 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────╮ │888 self.tagsViewInput(key, root_focus); │ │889 return; │ │890 } │ │840 - if (self.newViewActive()) { │ │841 - try self.newViewInput(allocator, key, root_focus);│ │891 + if (self.formViewActive()) { │ │892 + try self.formInput(allocator, key, root_focus); │ │893 return; │ │894 } │ │895 const i = self.selectedSplitIndex() orelse return; │ ╰───────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │936 } │ │937 │ │938 fn detailInput(self: *View, allocator: std.mem.Allocator, index: usize, key: Key, root_focus: *Focus) !void {│ │888 - if (self.statusButtonFocused(index)) { │ │889 - try self.statusButtonInput(allocator, index, key, root_focus); │ │939 + if (self.toolRowFocused(index)) { │ │940 + try self.toolRowInput(allocator, index, key, root_focus); │ │941 } else if (self.titleFocused(index)) { │ │942 try self.titleInput(index, key, root_focus); │ │943 } else if (self.tagsFocused(index)) { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │947 } │ │948 } │ │949 │ │899 - // the open/close button: enter or a click flips the issue's status; │ │900 - // arrows cross to the neighboring widgets. │ │901 - fn statusButtonInput(self: *View, allocator: std.mem.Allocator, index: usize, key: Key, root_focus: *Focus) !void { │ │950 + // the tool row: enter or a click on the open/close button flips the │ │951 + // issue's status; the edit button is an a: link the host follows. │ │952 + // arrows cross between the buttons and to the neighboring widgets. │ │953 + fn toolRowInput(self: *View, allocator: std.mem.Allocator, index: usize, key: Key, root_focus: *Focus) !void { │ │954 + const row = self.toolRow(index); │ │955 + const on_edit = if (row.getFocus().child_id) |cid| row.children.getIndex(cid) == edit_in_row_index else false; │ │956 switch (key) { │ │903 - .arrow_left => try self.focusList(index, root_focus), │ │957 + .arrow_left => if (on_edit) root_focus.setFocus(row.children.keys()[button_in_row_index]) else try self.focusList(index, root_focus),│ │958 + .arrow_right => if (!on_edit) root_focus.setFocus(row.children.keys()[edit_in_row_index]), │ │959 .arrow_up => self.focusHeader(root_focus), │ │960 .arrow_down => self.focusTitle(index, root_focus), │ │906 - .enter => try self.toggleIssueStatus(allocator, index), │ │961 + .enter => if (!on_edit) try self.toggleIssueStatus(allocator, index), │ │962 .mouse => |mouse| switch (mouse.action) { │ │963 .scroll => |dir| { │ │964 const sc = self.detailScroll(index); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │977 const sc = self.detailScroll(index); │ │978 switch (key) { │ │979 .arrow_left => try self.focusList(index, root_focus), │ │925 - .arrow_up => self.focusStatusButton(index, root_focus), │ │980 + .arrow_up => self.focusToolRow(index, root_focus), │ │981 .arrow_down => if (self.tagFlow(index) != null) try self.focusTags(index, root_focus) else try self.focusDescription(index, root_focus),│ │982 .mouse => |mouse| switch (mouse.action) { │ │983 .scroll => |dir| { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯