╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ─────────────╮ │src/ui/Repo/Issues.zig│ ╰──────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1254 const entry = self.data.selectedIssue() orelse return; │ │1255 const author_email = (try self.session.eventAuthorEmail()) orelse return; │ │1256 │ │1238 - const form = self.issueForm(); │ │1257 + const form = self.issueForm() orelse return; │ │1258 const title_input = &form.children.values()[title_field_index].widget.text_input; │ │1259 const tags_input = &form.children.values()[tags_field_index].widget.text_input; │ │1260 const description_input = &form.children.values()[description_field_index].widget.text_input;│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────╮ │1349 } │ │1350 │ │1351 const button_in_row_index: usize = 1;│ │1333 - const edit_in_row_index: usize = 2; │ │1352 const title_child_index: usize = 0; │ │1353 const tags_child_index: usize = 2; │ │1354 │ ╰───────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1367 return inner.children.getIndex(cid) == tags_child_index and self.tagFlow(index) != null;│ │1368 } │ │1369 │ │1352 - // the open/close button inside the detail frame's tool row. │ │1370 + // the open/close button inside the detail frame's tool row. the full row │ │1371 + // is spacer + open/close + edit; a logged-out session's row has no │ │1372 + // open/close button, and the description page has no row at all. │ │1373 fn statusButton(self: *View, index: usize) ?*wgt.TextBox(ui.Widget) { │ │1374 const row = self.toolRow(index); │ │1355 - if (row.children.count() == 0) return null; │ │1375 + if (row.children.count() != 3) return null; │ │1376 return &row.children.values()[button_in_row_index].widget.text_box; │ │1377 } │ │1378 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──────────╮ │src/ui/Settings.zig│ ╰───────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │16 } │ │17 │ │18 pub const View = struct { │ │19 - center: ui.Center, │ │19 + // own container focus so callers see a stable id while login/logout swap│ │20 + // the shown view │ │21 + focus: *Focus, │ │22 + // the settings controls, shown when logged in │ │23 + authorized: ui.Center, │ │24 + unauthorized: ui.Unauthorized.View, │ │25 session: *ui.Session, │ │26 button_id: usize, │ │27 │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │23 - // the stack switches between two whole views; build() selects one based on │ │24 - // whether a user is logged in. the logged-in view holds the settings │ │25 - // controls (currently just the ANSI toggle, but it will grow). │ │26 - const logged_in_index: usize = 0; │ │27 - const logged_out_index: usize = 1; │ │28 - │ │28 pub fn init(allocator: std.mem.Allocator, session: *ui.Session) !View { │ │30 - var stack = try wgt.Stack(ui.Widget).init(allocator); │ │31 - errdefer stack.deinit(allocator); │ │32 - │ │29 var button_id: usize = undefined; │ │34 - // the logged-in view: the controls shown to a signed-in user. │ │35 - { │ │30 + var authorized = blk: { │ │31 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert });│ │32 errdefer box.deinit(allocator); │ │33 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │46 } │ │47 │ │48 box.getFocus().child_id = button_id; │ │54 - try stack.children.put(allocator, box.getFocus().id, .{ .box = box }); │ │55 - } │ │49 + break :blk try ui.Center.init(allocator, .{ .box = box }); │ │50 + }; │ │51 + errdefer authorized.deinit(allocator); │ │52 │ │57 - // the logged-out view: shown when no user is signed in. │ │58 - { │ │59 - const logged_out_message = │ │60 - \\you must be logged in to see settings. │ │61 - \\ │ │62 - \\here's an emoticon from the early 2000s instead: │ │63 - \\ │ │64 - \\<('o'<) ^( '-' )^ (>'o')> │ │65 - \\ │ │66 - \\hope it helps. │ │67 - ; │ │53 + var unauthorized = try ui.Unauthorized.View.init(allocator); │ │54 + errdefer unauthorized.deinit(allocator); │ │55 │ │69 - var message = try wgt.TextBox(ui.Widget).init(allocator, logged_out_message, .{ .border_style = null, .rounded_corners = false, .wrap_kind = .word });│ │70 - errdefer message.deinit(allocator); │ │71 - try stack.children.put(allocator, message.getFocus().id, .{ .text_box = message }); │ │72 - } │ │73 - │ │56 return .{ │ │75 - .center = try ui.Center.init(allocator, .{ .stack = stack }), │ │57 + .focus = try Focus.create(allocator, .container), │ │58 + .authorized = authorized, │ │59 + .unauthorized = unauthorized, │ │60 .session = session, │ │61 .button_id = button_id, │ │62 }; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │63 } │ │64 │ │65 pub fn deinit(self: *View, allocator: std.mem.Allocator) void { │ │82 - self.center.deinit(allocator); │ │66 + self.focus.destroy(allocator); │ │67 + self.authorized.deinit(allocator); │ │68 + self.unauthorized.deinit(allocator); │ │69 } │ │70 │ │71 pub fn build(self: *View, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void {│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │86 - // select the logged-in view when logged in, else the logged-out view. │ │87 - // decided per-frame since the tty doesn't rebuild the page on login. │ │88 - const logged_in = self.session.data.user_id != null; │ │89 - const stack = &self.center.child.stack; │ │90 - if (logged_in) { │ │91 - const box = &stack.children.values()[logged_in_index].box; │ │72 + // decided per-frame since the tty doesn't rebuild the page on login │ │73 + self.focus.clear(); │ │74 + if (self.session.data.user_id != null) { │ │75 + const box = &self.authorized.child.box; │ │76 const button = &box.children.values()[0].widget.text_box; │ │77 button.box.children.values()[0].widget.text.content = ansiLabel(self.session); │ │78 + try self.authorized.build(allocator, constraint, root_focus); │ │79 + if (self.authorized.getGrid()) |inner_grid| { │ │80 + try self.focus.addChild(allocator, self.authorized.getFocus(), inner_grid.size, 0, 0); │ │81 + } │ │82 + self.focus.child_id = self.authorized.getFocus().id; │ │83 + } else { │ │84 + try self.unauthorized.build(allocator, constraint, root_focus); │ │85 + if (self.unauthorized.getGrid()) |inner_grid| { │ │86 + try self.focus.addChild(allocator, self.unauthorized.getFocus(), inner_grid.size, 0, 0); │ │87 + } │ │88 + self.focus.child_id = self.unauthorized.getFocus().id; │ │89 } │ │95 - stack.getFocus().child_id = stack.children.keys()[if (logged_in) logged_in_index else logged_out_index];│ │96 - try self.center.build(allocator, constraint, root_focus); │ │90 } │ │91 │ │92 pub fn input(self: *View, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────╮ │108 } │ │109 │ │110 pub fn clearGrid(self: *View) void {│ │118 - self.center.clearGrid(); │ │111 + self.authorized.clearGrid(); │ │112 + self.unauthorized.clearGrid(); │ │113 } │ │114 │ │115 pub fn getGrid(self: View) ?Grid { │ ╰─────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │122 - return self.center.getGrid(); │ │116 + if (self.session.data.user_id != null) return self.authorized.getGrid();│ │117 + return self.unauthorized.getGrid(); │ │118 } │ │119 │ │120 pub fn getFocus(self: *View) *Focus { │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯