╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Commits.zig│ ╰───────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │814 return std.fmt.allocPrint(page_arena.allocator(), "ai:{s}", .{url}); │ │815 } │ │816 │ │817 +// the "viewing <ref_or_oid> <value>" banner shown above the log. │ │818 +pub const Header = struct { │ │819 + content: []const u8, │ │820 + │ │821 + pub fn init(aa: std.mem.Allocator, ref_or_oid: ui.RoutablePage.RefOrOid, value: []const u8) !Header { │ │822 + return .{ │ │823 + .content = try std.fmt.allocPrint(aa, "viewing {s} {s}", .{ @tagName(ref_or_oid), value }), │ │824 + }; │ │825 + } │ │826 + │ │827 + pub const View = struct { │ │828 + text_box: wgt.TextBox(ui.Widget), │ │829 + data: *const Header, │ │830 + │ │831 + pub fn init(allocator: std.mem.Allocator, data: *const Header, session: *ui.Session) !Header.View { │ │832 + _ = session; │ │833 + var text_box = try wgt.TextBox(ui.Widget).init(allocator, data.content, .{ .border_style = .hidden, .wrap_kind = .none });│ │834 + errdefer text_box.deinit(allocator); │ │835 + return .{ .text_box = text_box, .data = data }; │ │836 + } │ │837 + │ │838 + pub fn deinit(self: *Header.View, allocator: std.mem.Allocator) void { │ │839 + self.text_box.deinit(allocator); │ │840 + } │ │841 + │ │842 + pub fn build(self: *Header.View, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void { │ │843 + // a single line plus the hidden border is 3 rows tall; stretch the box to │ │844 + // the full width by handing the available width down as a min width. │ │845 + try self.text_box.build(allocator, .{ │ │846 + .min_size = .{ .width = constraint.max_size.width, .height = null }, │ │847 + .max_size = constraint.max_size, │ │848 + }, root_focus); │ │849 + } │ │850 + │ │851 + pub fn input(self: *Header.View, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void { │ │852 + _ = self; │ │853 + _ = allocator; │ │854 + _ = key; │ │855 + _ = root_focus; │ │856 + } │ │857 + │ │858 + pub fn clearGrid(self: *Header.View) void { │ │859 + self.text_box.clearGrid(); │ │860 + } │ │861 + │ │862 + pub fn getGrid(self: Header.View) ?Grid { │ │863 + return self.text_box.getGrid(); │ │864 + } │ │865 + │ │866 + pub fn getFocus(self: *Header.View) *Focus { │ │867 + return self.text_box.getFocus(); │ │868 + } │ │869 + }; │ │870 +}; │ │871 + │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ removed ────────────────────────╮ │src/ui/Repo/Commits/SubHeader.zig│ ╰─────────────────────────────────╯ ╭ changed ────────────╮ │src/ui/Repo/Files.zig│ ╰─────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │12 const Grid = xitui.grid.Grid; │ │13 const Focus = xitui.focus.Focus; │ │14 const inp = @import("../input.zig"); │ │15 - │ │16 -pub const SubHeader = @import("Files/SubHeader.zig"); │ │15 │ │16 // how many lines of a file's content one window shows before a "next" link.│ │17 const file_page = 2000; │ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────╮ │61 selected_file: ?[]const u8 = null, │ │62 entries: []const Entry, │ │63 // the "viewing <ref> <value>" banner shown above the listing.│ │66 -sub_header: SubHeader, │ │64 +header: Header, │ │65 │ │66 const Self = @This(); │ │67 │ ╰──────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────╮ │171 .dir = try aa.dupe(u8, dir), │ │172 .selected_file = selected_file, │ │173 .entries = entries, │ │176 - .sub_header = try SubHeader.init(aa, resolved.ref_or_oid, resolved.value),│ │174 + .header = try Header.init(aa, resolved.ref_or_oid, resolved.value), │ │175 }; │ │176 } │ │177 │ ╰───────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │226 .ref_or_oid_value = try aa.dupe(u8, ref_or_oid_value), │ │227 .dir = try aa.dupe(u8, dir), │ │228 .entries = &.{}, │ │231 - .sub_header = try SubHeader.init(aa, ref_or_oid, ref_or_oid_value),│ │229 + .header = try Header.init(aa, ref_or_oid, ref_or_oid_value), │ │230 }; │ │231 } │ │232 │ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────╮ │234 // a vertical stack: the "viewing <ref>" banner on top, then a horizontal │ │235 // split with the file/directory list on the left and a detail pane on the │ │236 // right showing the selected file's contents. │ │239 - box: wgt.Box(ui.Widget), // vert: [sub_header_index] = banner, [content_index] = split│ │237 + box: wgt.Box(ui.Widget), // vert: [header_index] = banner, [content_index] = split │ │238 data: *const Self, │ │239 session: *ui.Session, │ │240 // the list row whose contents the detail pane currently shows. │ ╰───────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────╮ │241 shown_index: ?usize, │ │242 │ │245 - const sub_header_index: usize = 0; │ │243 + const header_index: usize = 0; │ │244 const content_index: usize = 1; │ │245 // indices within the content box (the horizontal split).│ │246 const list_index: usize = 0; │ ╰──────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │257 │ │258 // the ref banner at the top. │ │259 { │ │262 - var sub_header = try SubHeader.View.init(allocator, &data.sub_header, session); │ │263 - errdefer sub_header.deinit(allocator); │ │264 - try outer.children.put(allocator, sub_header.getFocus().id, .{ .widget = .{ .repo_files_sub_header = sub_header }, .rect = null, .min_size = .{ .width = null, .height = 3 } });│ │260 + var header = try Header.View.init(allocator, &data.header, session); │ │261 + errdefer header.deinit(allocator); │ │262 + try outer.children.put(allocator, header.getFocus().id, .{ .widget = .{ .repo_files_header = header }, .rect = null, .min_size = .{ .width = null, .height = 3 } }); │ │263 } │ │264 │ │265 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .horiz }); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │820 return dir[0..slash]; │ │821 } │ │822 │ │823 +// the "viewing <ref_or_oid> <value>" banner shown above the listing. │ │824 +pub const Header = struct { │ │825 + content: []const u8, │ │826 + │ │827 + pub fn init(aa: std.mem.Allocator, ref_or_oid: ui.RoutablePage.RefOrOid, value: []const u8) !Header { │ │828 + return .{ │ │829 + .content = try std.fmt.allocPrint(aa, "viewing {s} {s}", .{ @tagName(ref_or_oid), value }), │ │830 + }; │ │831 + } │ │832 + │ │833 + pub const View = struct { │ │834 + text_box: wgt.TextBox(ui.Widget), │ │835 + data: *const Header, │ │836 + │ │837 + pub fn init(allocator: std.mem.Allocator, data: *const Header, session: *ui.Session) !Header.View { │ │838 + _ = session; │ │839 + var text_box = try wgt.TextBox(ui.Widget).init(allocator, data.content, .{ .border_style = .hidden, .wrap_kind = .none });│ │840 + errdefer text_box.deinit(allocator); │ │841 + return .{ .text_box = text_box, .data = data }; │ │842 + } │ │843 + │ │844 + pub fn deinit(self: *Header.View, allocator: std.mem.Allocator) void { │ │845 + self.text_box.deinit(allocator); │ │846 + } │ │847 + │ │848 + pub fn build(self: *Header.View, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void { │ │849 + // a single line plus the hidden border is 3 rows tall; stretch the box to │ │850 + // the full width by handing the available width down as a min width. │ │851 + try self.text_box.build(allocator, .{ │ │852 + .min_size = .{ .width = constraint.max_size.width, .height = null }, │ │853 + .max_size = constraint.max_size, │ │854 + }, root_focus); │ │855 + } │ │856 + │ │857 + pub fn input(self: *Header.View, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void { │ │858 + _ = self; │ │859 + _ = allocator; │ │860 + _ = key; │ │861 + _ = root_focus; │ │862 + } │ │863 + │ │864 + pub fn clearGrid(self: *Header.View) void { │ │865 + self.text_box.clearGrid(); │ │866 + } │ │867 + │ │868 + pub fn getGrid(self: Header.View) ?Grid { │ │869 + return self.text_box.getGrid(); │ │870 + } │ │871 + │ │872 + pub fn getFocus(self: *Header.View) *Focus { │ │873 + return self.text_box.getFocus(); │ │874 + } │ │875 + }; │ │876 +}; │ │877 + │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯