╭ changed ──────────╮ │src/ui/Settings.zig│ ╰───────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │16 } │ │17 │ │18 pub const View = struct { │ │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.widget.Center, │ │24 - unauthorized: ui.Unauthorized.View, │ │19 + stack: wgt.Stack(ui.Widget), │ │20 session: *ui.Session, │ │21 button_id: usize, │ │22 + │ │23 + const authorized_index = 0; │ │24 + const unauthorized_index = 1; │ │25 │ │26 pub fn init(allocator: std.mem.Allocator, session: *ui.Session) !View { │ │27 + var stack = try wgt.Stack(ui.Widget).init(allocator); │ │28 + errdefer stack.deinit(allocator); │ │29 var button_id: usize = undefined; │ │30 - var authorized = blk: { │ │31 - var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert }); │ │32 - errdefer box.deinit(allocator); │ │30 + { │ │31 + var authorized = blk: { │ │32 + var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert }); │ │33 + errdefer box.deinit(allocator); │ │34 │ │34 - // on the web each control posts to a page-scoped form │ │35 - box.getFocus().kind = .{ .custom = "form:ansi" }; │ │35 + // on the web each control posts to a page-scoped form │ │36 + box.getFocus().kind = .{ .custom = "form:ansi" }; │ │37 │ │37 - { │ │38 - var button = try wgt.TextBox.init(allocator, ansiLabel(session), .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none }); │ │39 - errdefer button.deinit(allocator); │ │40 - // the renderer distinguishes plain clickables from buttons that │ │41 - // should POST to a server route by this kind. │ │42 - button.getFocus().kind = .{ .custom = "submit" }; │ │43 - button.getFocus().mode = .all; │ │44 - button_id = button.getFocus().id; │ │45 - try box.children.put(allocator, button.getFocus().id, .{ .widget = .{ .text_box = button }, .rect = null, .min_size = null }); │ │46 - } │ │47 - │ │48 - box.getFocus().child_id = button_id; │ │49 - break :blk try ui.widget.Center.init(allocator, .{ .box = box }); │ │50 - }; │ │51 - errdefer authorized.deinit(allocator); │ │38 + { │ │39 + var button = try wgt.TextBox.init(allocator, ansiLabel(session), .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none });│ │40 + errdefer button.deinit(allocator); │ │41 + // the renderer distinguishes plain clickables from buttons that │ │42 + // should POST to a server route by this kind. │ │43 + button.getFocus().kind = .{ .custom = "submit" }; │ │44 + button.getFocus().mode = .all; │ │45 + button_id = button.getFocus().id; │ │46 + try box.children.put(allocator, button.getFocus().id, .{ .widget = .{ .text_box = button }, .rect = null, .min_size = null }); │ │47 + } │ │48 │ │53 - var unauthorized = try ui.Unauthorized.View.init(allocator); │ │54 - errdefer unauthorized.deinit(allocator); │ │49 + box.getFocus().child_id = button_id; │ │50 + break :blk try ui.widget.Center.init(allocator, .{ .box = box }); │ │51 + }; │ │52 + errdefer authorized.deinit(allocator); │ │53 + try stack.children.put(allocator, authorized.getFocus().id, .{ .center = authorized }); │ │54 + } │ │55 + { │ │56 + var unauthorized = try ui.Unauthorized.View.init(allocator); │ │57 + errdefer unauthorized.deinit(allocator); │ │58 + try stack.children.put(allocator, unauthorized.getFocus().id, .{ .unauthorized = unauthorized }); │ │59 + } │ │60 │ │61 return .{ │ │57 - .focus = try Focus.create(allocator, .container), │ │58 - .authorized = authorized, │ │59 - .unauthorized = unauthorized, │ │62 + .stack = stack, │ │63 .session = session, │ │64 .button_id = button_id, │ │65 }; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │66 } │ │67 │ │68 pub fn deinit(self: *View, allocator: std.mem.Allocator) void { │ │66 - self.focus.destroy(allocator); │ │67 - self.authorized.deinit(allocator); │ │68 - self.unauthorized.deinit(allocator); │ │69 + self.stack.deinit(allocator); │ │70 } │ │71 │ │72 pub fn build(self: *View, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void {│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │73 // decided per-frame since the tty doesn't rebuild the page on login │ │73 - self.focus.clear(); │ │74 + const index: usize = if (self.session.data.user_id != null) authorized_index else unauthorized_index;│ │75 + self.stack.getFocus().child_id = self.stack.children.keys()[index]; │ │76 if (self.session.data.user_id != null) { │ │75 - const box = &self.authorized.child.box; │ │77 + const box = &self.stack.children.values()[authorized_index].center.child.box; │ │78 const button = &box.children.values()[0].widget.text_box; │ │79 try button.setContent(allocator, 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; │ │80 } │ │81 + try self.stack.build(allocator, constraint, root_focus); │ │82 } │ │83 │ │84 pub fn input(self: *View, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────╮ │100 } │ │101 │ │102 pub fn clearGrid(self: *View) void {│ │111 - self.authorized.clearGrid(); │ │112 - self.unauthorized.clearGrid(); │ │103 + self.stack.clearGrid(); │ │104 } │ │105 │ │106 pub fn getGrid(self: View) ?Grid { │ ╰─────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │116 - if (self.session.data.user_id != null) return self.authorized.getGrid();│ │117 - return self.unauthorized.getGrid(); │ │107 + return self.stack.getGrid(); │ │108 } │ │109 │ │110 pub fn getFocus(self: *View) *Focus { │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────╮ │121 - return self.focus; │ │111 + return self.stack.getFocus();│ │112 } │ │113 │ │114 pub fn atTop(self: View) bool { │ ╰──────────────────────────────────────────╯