╭ changed ──────────────╮ │src/ui/Repo/Commits.zig│ ╰───────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭────────────────────────────────────────────────╮ │11 const inp = xitui.input; │ │12 const Grid = xitui.grid.Grid; │ │13 const Focus = xitui.focus.Focus; │ │14 + │ │15 +const SubHeader = @import("SubHeader.zig"); │ │16 │ │17 const RefOrOid = ui.RoutablePage.RefOrOid; │ │18 const hex_len = ui.ResolvedRefOrOid.hex_len;│ ╰────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────╮ │51 commits: []const Commit, │ │52 // the first oid of the next page, or null when this is the last page.│ │53 next_start: ?[]const u8, │ │54 +// the "viewing <ref> <value>" banner shown above the log. │ │55 +sub_header: SubHeader, │ │56 │ │57 const Self = @This(); │ │58 │ ╰──────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────╮ │147 .ref_or_oid_value = resolved.value, │ │148 .commits = try aa.dupe(Commit, buf[0..count]), │ │149 .next_start = next_start, │ │150 + .sub_header = try SubHeader.init(aa, resolved.ref_or_oid, resolved.value),│ │151 }; │ │152 } │ │153 │ ╰───────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────╮ │159 .ref_or_oid_value = try aa.dupe(u8, value), │ │160 .commits = &.{}, │ │161 .next_start = null, │ │162 + .sub_header = try SubHeader.init(aa, ref_or_oid, value),│ │163 }; │ │164 } │ │165 │ ╰─────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────╮ │303 } │ │304 │ │305 pub const View = struct { │ │300 - // a horizontal split: the commit list on the left and a diff pane on the │ │301 - // right showing the selected commit's diff │ │302 - box: wgt.Box(ui.Widget), // horiz: [list_index] = list scroll, [diff_index] = diff pane│ │306 + // a vertical stack: the "viewing <ref>" banner on top, then a horizontal │ │307 + // split with the commit list on the left and a diff pane on the right │ │308 + // showing the selected commit's diff. │ │309 + box: wgt.Box(ui.Widget), // vert: [sub_header_index] = banner, [content_index] = split │ │310 data: *const Self, │ │311 session: *ui.Session, │ │312 // the commit whose diff the pane currently shows (index into data.commits). │ ╰────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────╮ │313 diffed_index: ?usize, │ │314 │ │315 + const sub_header_index: usize = 0; │ │316 + const content_index: usize = 1; │ │317 + // indices within the content box (the horizontal split).│ │318 const list_index: usize = 0; │ │319 const diff_index: usize = 1; │ │320 const list_max_width: usize = 40; │ ╰──────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │321 const diff_min_width: usize = 40; │ │322 │ │323 pub fn init(allocator: std.mem.Allocator, data: *const Self, session: *ui.Session) !View { │ │324 + var outer = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert }); │ │325 + errdefer outer.deinit(allocator); │ │326 + │ │327 + // the ref banner at the top. │ │328 + { │ │329 + var sub_header = try SubHeader.View.init(allocator, &data.sub_header, session); │ │330 + errdefer sub_header.deinit(allocator); │ │331 + try outer.children.put(allocator, sub_header.getFocus().id, .{ .widget = .{ .repo_sub_header = sub_header }, .rect = null, .min_size = .{ .width = null, .height = 3 } });│ │332 + } │ │333 + │ │334 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .horiz }); │ │335 errdefer box.deinit(allocator); │ │336 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │364 break :blk2 try wgt.Scroll(ui.Widget).init(allocator, .{ .box = diff_inner }, .{ .direction = .both, .web_native = !session.is_terminal }); │ │365 }; │ │366 errdefer diff_scroll.deinit(allocator); │ │347 - var outer = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = .hidden, .direction = .vert }); │ │348 - errdefer outer.deinit(allocator); │ │349 - try outer.children.put(allocator, diff_scroll.getFocus().id, .{ .widget = .{ .scroll = diff_scroll }, .rect = null, .min_size = null }); │ │350 - break :blk outer; │ │367 + var frame = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = .hidden, .direction = .vert }); │ │368 + errdefer frame.deinit(allocator); │ │369 + try frame.children.put(allocator, diff_scroll.getFocus().id, .{ .widget = .{ .scroll = diff_scroll }, .rect = null, .min_size = null }); │ │370 + break :blk frame; │ │371 }; │ │372 errdefer diff_outer.deinit(allocator); │ │373 try box.children.put(allocator, diff_outer.getFocus().id, .{ .widget = .{ .box = diff_outer }, .rect = null, .min_size = .{ .width = diff_min_width, .height = null } });│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │374 } │ │375 │ │376 box.getFocus().child_id = box.children.keys()[list_index]; │ │377 + try outer.children.put(allocator, box.getFocus().id, .{ .widget = .{ .box = box }, .rect = null, .min_size = null });│ │378 + │ │379 + // focus lives in the split; the banner isn't focusable. │ │380 + outer.getFocus().child_id = outer.children.keys()[content_index]; │ │381 │ │382 return .{ │ │359 - .box = box, │ │383 + .box = outer, │ │384 .data = data, │ │385 .session = session, │ │386 .diffed_index = null, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────╮ │431 │ │432 pub fn deinit(self: *View, allocator: std.mem.Allocator) void { │ │433 self.box.deinit(allocator); │ │434 + } │ │435 + │ │436 + fn contentBox(self: *View) *wgt.Box(ui.Widget) { │ │437 + return &self.box.children.values()[content_index].widget.box;│ │438 } │ │439 │ │440 fn listScroll(self: *View) *wgt.Scroll(ui.Widget) { │ ╰──────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯