╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭──────────────────────────────────────╮ │1427 tag_flow: TagFlow, │ │1428 spacer: Spacer, │ │1429 center: Center, │ │1430 + section_label: SectionLabel,│ │1431 + submit_button: SubmitButton,│ │1432 background: AnsiBackground, │ │1433 home: Home.View, │ │1434 user: User.View, │ ╰──────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2075 │ │2076 // a single-child wrapper that builds the child at its natural size and │ │2077 // positions its grid in the middle of the area granted by the parent │ │2078 +// a bordered label, indented past the content it precedes │ │2079 +pub const SectionLabel = struct { │ │2080 + box: wgt.Box(Widget), │ │2081 + │ │2082 + pub fn init(allocator: std.mem.Allocator, content: []const u8) !SectionLabel { │ │2083 + var box = try wgt.Box(Widget).init(allocator, .{ .border_style = null, .direction = .horiz }); │ │2084 + errdefer box.deinit(allocator); │ │2085 + │ │2086 + { │ │2087 + var gap = try wgt.Text(Widget).init(allocator, " "); │ │2088 + errdefer gap.deinit(allocator); │ │2089 + try box.children.put(allocator, gap.getFocus().id, .{ .widget = .{ .text = gap }, .rect = null, .min_size = null }); │ │2090 + } │ │2091 + │ │2092 + { │ │2093 + var tb = try wgt.TextBox(Widget).init(allocator, content, .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none }); │ │2094 + errdefer tb.deinit(allocator); │ │2095 + tb.getFocus().focusable = true; │ │2096 + try box.children.put(allocator, tb.getFocus().id, .{ .widget = .{ .text_box = tb }, .rect = null, .min_size = null }); │ │2097 + } │ │2098 + │ │2099 + box.getFocus().child_id = box.children.keys()[1]; │ │2100 + return .{ .box = box }; │ │2101 + } │ │2102 + │ │2103 + pub fn deinit(self: *SectionLabel, allocator: std.mem.Allocator) void { │ │2104 + self.box.deinit(allocator); │ │2105 + } │ │2106 + │ │2107 + pub fn build(self: *SectionLabel, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void { │ │2108 + try self.box.build(allocator, constraint, root_focus); │ │2109 + } │ │2110 + │ │2111 + pub fn input(self: *SectionLabel, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void { │ │2112 + _ = self; │ │2113 + _ = allocator; │ │2114 + _ = key; │ │2115 + _ = root_focus; │ │2116 + } │ │2117 + │ │2118 + pub fn clearGrid(self: *SectionLabel) void { │ │2119 + self.box.clearGrid(); │ │2120 + } │ │2121 + │ │2122 + pub fn getGrid(self: SectionLabel) ?Grid { │ │2123 + return self.box.getGrid(); │ │2124 + } │ │2125 + │ │2126 + pub fn getFocus(self: *SectionLabel) *Focus { │ │2127 + return self.box.getFocus(); │ │2128 + } │ │2129 +}; │ │2130 + │ │2131 +// a submit button, indented past its form's content. the web overlay POSTs │ │2132 +// the button's form; terminal hosts detect presses through `buttonId`. │ │2133 +pub const SubmitButton = struct { │ │2134 + box: wgt.Box(Widget), │ │2135 + │ │2136 + pub fn init(allocator: std.mem.Allocator) !SubmitButton { │ │2137 + var box = try wgt.Box(Widget).init(allocator, .{ .border_style = null, .direction = .horiz }); │ │2138 + errdefer box.deinit(allocator); │ │2139 + │ │2140 + { │ │2141 + var gap = try wgt.Text(Widget).init(allocator, " "); │ │2142 + errdefer gap.deinit(allocator); │ │2143 + try box.children.put(allocator, gap.getFocus().id, .{ .widget = .{ .text = gap }, .rect = null, .min_size = null }); │ │2144 + } │ │2145 + │ │2146 + { │ │2147 + const label = "submit"; │ │2148 + var button = try wgt.TextBox(Widget).init(allocator, label, .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none }); │ │2149 + errdefer button.deinit(allocator); │ │2150 + button.getFocus().focusable = true; │ │2151 + // the renderer distinguishes plain clickables from buttons that │ │2152 + // should POST to a server route by this kind. │ │2153 + button.getFocus().kind = .{ .custom = "submit" }; │ │2154 + try box.children.put(allocator, button.getFocus().id, .{ .widget = .{ .text_box = button }, .rect = null, .min_size = .{ .width = label.len + 2, .height = null } });│ │2155 + } │ │2156 + │ │2157 + box.getFocus().child_id = box.children.keys()[1]; │ │2158 + return .{ .box = box }; │ │2159 + } │ │2160 + │ │2161 + pub fn deinit(self: *SubmitButton, allocator: std.mem.Allocator) void { │ │2162 + self.box.deinit(allocator); │ │2163 + } │ │2164 + │ │2165 + pub fn build(self: *SubmitButton, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void { │ │2166 + try self.box.build(allocator, constraint, root_focus); │ │2167 + } │ │2168 + │ │2169 + pub fn input(self: *SubmitButton, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void { │ │2170 + _ = self; │ │2171 + _ = allocator; │ │2172 + _ = key; │ │2173 + _ = root_focus; │ │2174 + } │ │2175 + │ │2176 + pub fn clearGrid(self: *SubmitButton) void { │ │2177 + self.box.clearGrid(); │ │2178 + } │ │2179 + │ │2180 + pub fn getGrid(self: SubmitButton) ?Grid { │ │2181 + return self.box.getGrid(); │ │2182 + } │ │2183 + │ │2184 + pub fn getFocus(self: *SubmitButton) *Focus { │ │2185 + return self.box.getFocus(); │ │2186 + } │ │2187 + │ │2188 + // the button's focus id, for click hit-testing │ │2189 + pub fn buttonId(self: *const SubmitButton) usize { │ │2190 + return self.box.children.keys()[1]; │ │2191 + } │ │2192 +}; │ │2193 + │ │2194 pub const Center = struct { │ │2195 focus: *Focus, │ │2196 grid: ?Grid, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────────╮ │src/ui/Repo/Issues.zig│ ╰──────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │806 // natural height │ │807 fn addSubmitButton(allocator: std.mem.Allocator, box: *wgt.Box(ui.Widget)) !void { │ │808 { │ │809 - var button = try wgt.TextBox(ui.Widget).init(allocator, "submit", .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none }); │ │810 - errdefer button.deinit(allocator); │ │811 - button.getFocus().focusable = true; │ │812 - // the renderer distinguishes plain clickables from buttons that │ │813 - // should POST to a server route by this kind. │ │814 - button.getFocus().kind = .{ .custom = "submit" }; │ │815 - try box.children.put(allocator, button.getFocus().id, .{ .widget = .{ .text_box = button }, .rect = null, .min_size = .{ .width = null, .height = 3 } }); │ │809 + var submit = try ui.SubmitButton.init(allocator); │ │810 + errdefer submit.deinit(allocator); │ │811 + try box.children.put(allocator, submit.getFocus().id, .{ .widget = .{ .submit_button = submit }, .rect = null, .min_size = .{ .width = null, .height = 3 } });│ │812 } │ │813 { │ │814 var spacer = try ui.Spacer.init(allocator); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │846 try addLabel(allocator, &box, "description conflict:"); │ │847 var hunk_index: usize = 0; │ │848 for (desc.chunks, 0..) |*chunk, chunk_index| { │ │853 - if (chunk_index > 0) try addGap(allocator, &box); │ │849 switch (chunk.*) { │ │850 .same => |text| { │ │851 var tb = try wgt.TextBox(ui.Widget).init(allocator, text, .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .word });│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │868 try box.children.put(allocator, tb.getFocus().id, .{ .widget = .{ .text_box = tb }, .rect = null, .min_size = null });│ │869 }, │ │870 .conflict => |hunk| { │ │871 + // a blank row on each side sets the group apart from │ │872 + // the flowing shared and auto-resolved chunks │ │873 + if (chunk_index > 0) try addGap(allocator, &box); │ │874 const name = try std.fmt.allocPrint(aa, "d{d}", .{hunk_index}); │ │875 hunk_index += 1; │ │876 const picked_theirs = data.theirsPicked(name); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │881 resolution_input.getFocus().focusable = true; │ │882 try resolution_input.setContent(allocator, (if (picked_theirs) hunk.theirs else hunk.ours) orelse ""); │ │883 try box.children.put(allocator, resolution_input.getFocus().id, .{ .widget = .{ .text_input = resolution_input }, .rect = null, .min_size = null });│ │884 + if (chunk_index + 1 < desc.chunks.len) try addGap(allocator, &box); │ │885 }, │ │886 } │ │887 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │907 try box.children.put(allocator, resolution_input.getFocus().id, .{ .widget = .{ .text_input = resolution_input }, .rect = null, .min_size = null });│ │908 } │ │909 │ │911 - // a blank row separating adjacent chunks │ │910 + // a blank row setting a conflict group apart from its neighbors │ │911 fn addGap(allocator: std.mem.Allocator, box: *wgt.Box(ui.Widget)) !void { │ │912 var text = try wgt.Text(ui.Widget).init(allocator, " "); │ │913 errdefer text.deinit(allocator); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │914 try box.children.put(allocator, text.getFocus().id, .{ .widget = .{ .text = text }, .rect = null, .min_size = null }); │ │915 } │ │916 │ │918 - // a section label │ │917 fn addLabel(allocator: std.mem.Allocator, box: *wgt.Box(ui.Widget), content: []const u8) !void { │ │920 - var tb = try wgt.TextBox(ui.Widget).init(allocator, content, .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none });│ │921 - errdefer tb.deinit(allocator); │ │922 - tb.getFocus().focusable = true; │ │923 - try box.children.put(allocator, tb.getFocus().id, .{ .widget = .{ .text_box = tb }, .rect = null, .min_size = null }); │ │918 + var label = try ui.SectionLabel.init(allocator, content); │ │919 + errdefer label.deinit(allocator); │ │920 + try box.children.put(allocator, label.getFocus().id, .{ .widget = .{ .section_label = label }, .rect = null, .min_size = null }); │ │921 } │ │922 │ │923 // one side of a conflict: the "use this" link on the left, the version │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │927 errdefer row.deinit(allocator); │ │928 │ │929 { │ │933 - var use = try wgt.TextBox(ui.Widget).init(allocator, "use this", .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none });│ │930 + const use_label = "use this"; │ │931 + var use = try wgt.TextBox(ui.Widget).init(allocator, use_label, .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none }); │ │932 errdefer use.deinit(allocator); │ │933 use.getFocus().focusable = true; │ │934 use.getFocus().kind = .{ .custom = link }; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │935 try row.children.put(allocator, use.getFocus().id, .{ │ │936 .widget = .{ .text_box = use }, │ │937 .rect = null, │ │940 - .min_size = .{ .width = "use this".len + 2, .height = null },│ │941 - .max_size = .{ .width = "use this".len + 2, .height = null },│ │938 + .min_size = .{ .width = use_label.len + 2, .height = null }, │ │939 + .max_size = .{ .width = use_label.len + 2, .height = null }, │ │940 }); │ │941 } │ │942 │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯