╭ changed ──────────╮ │src/ui/Settings.zig│ ╰───────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │20 session: *ui.Session, │ │21 button_id: usize, │ │22 │ │23 - // the box holds the toggle button and the logged-out message; build() shows │ │24 - // exactly one of them based on whether a user is logged in. │ │25 - const button_index: usize = 0; │ │26 - const message_index: usize = 1; │ │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 │ │29 pub fn init(allocator: std.mem.Allocator, data: *const Self, session: *ui.Session) !View { │ │29 - var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .rounded_corners = true, .direction = .vert }); │ │30 - errdefer box.deinit(allocator); │ │31 - // on the web the toggle is an overlay form posting to a page-scoped /ansi │ │32 - // path; only mark it a form when logged in so a logged-out page stays │ │33 - // blank (the web rebuilds per request, so this is correct there; the tty │ │34 - // ignores the form kind and toggles in-process via input). │ │35 - if (session.data.user_id != null) { │ │30 + var stack = try wgt.Stack(ui.Widget).init(allocator); │ │31 + errdefer stack.deinit(allocator); │ │32 + │ │33 + var button_id: usize = undefined; │ │34 + // the logged-in view: the controls shown to a signed-in user. │ │35 + { │ │36 + var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert }); │ │37 + errdefer box.deinit(allocator); │ │38 + │ │39 + // on the web each control posts to a page-scoped form │ │40 box.getFocus().kind = .{ .custom = switch (session.data.current_page) { │ │41 .user, .user_settings, .user_auth => |name| try std.fmt.allocPrint(session.page_arena.allocator(), "form:/user/{s}/ansi", .{name.slice()}),│ │42 else => "form:/ansi", │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │43 } }; │ │40 - } │ │44 │ │42 - var button_id: usize = undefined; │ │43 - { │ │44 - var button = try wgt.TextBox(ui.Widget).init(allocator, ansiLabel(session), .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none }); │ │45 - errdefer button.deinit(allocator); │ │46 - // the renderer distinguishes plain clickables from buttons that │ │47 - // should POST to a server route by this kind. │ │48 - button.getFocus().kind = .{ .custom = "submit" }; │ │49 - button_id = button.getFocus().id; │ │50 - try box.children.put(allocator, button.getFocus().id, .{ │ │51 - .widget = .{ .text_box = button }, │ │52 - .rect = null, │ │53 - .min_size = null, │ │54 - }); │ │45 + { │ │46 + var button = try wgt.TextBox(ui.Widget).init(allocator, ansiLabel(session), .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none });│ │47 + errdefer button.deinit(allocator); │ │48 + // the renderer distinguishes plain clickables from buttons that │ │49 + // should POST to a server route by this kind. │ │50 + button.getFocus().kind = .{ .custom = "submit" }; │ │51 + button.getFocus().focusable = true; │ │52 + button_id = button.getFocus().id; │ │53 + try box.children.put(allocator, button.getFocus().id, .{ .widget = .{ .text_box = button }, .rect = null, .min_size = null }); │ │54 + } │ │55 + │ │56 + box.getFocus().child_id = button_id; │ │57 + try stack.children.put(allocator, box.getFocus().id, .{ .box = box }); │ │58 } │ │59 │ │57 - // the logged-out message (shown in place of the toggle) │ │60 + // the logged-out view: shown when no user is signed in. │ │61 { │ │62 const logged_out_message = │ │60 - \\you must be logged in to see settings :( │ │63 + \\you must be logged in to see settings. │ │64 + \\ │ │65 + \\here's an emoticon from the early 2000s instead: │ │66 \\ │ │62 - \\instead, here's an emoticon from the early 2000s: │ │67 \\<('o'<) ^( '-' )^ (>'o')> │ │68 \\ │ │69 \\hope it helps. │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │71 │ │72 var message = try wgt.TextBox(ui.Widget).init(allocator, logged_out_message, .{ .border_style = null, .rounded_corners = false, .wrap_kind = .word });│ │73 errdefer message.deinit(allocator); │ │70 - try box.children.put(allocator, message.getFocus().id, .{ │ │71 - .widget = .{ .text_box = message }, │ │72 - .rect = null, │ │73 - .min_size = null, │ │74 - }); │ │74 + try stack.children.put(allocator, message.getFocus().id, .{ .text_box = message }); │ │75 } │ │76 │ │77 - box.getFocus().child_id = button_id; │ │78 - │ │77 return .{ │ │80 - .center = try ui.Center.init(allocator, .{ .box = box }, .both), │ │78 + .center = try ui.Center.init(allocator, .{ .stack = stack }, .both), │ │79 .data = data, │ │80 .session = session, │ │81 .button_id = button_id, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 87 } │ │ 88 │ │ 89 pub fn build(self: *View, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void {│ │ 92 - // show the toggle when logged in, else the message. decided per-frame │ │ 93 - // since the tty doesn't rebuild the page on login. a zero max height │ │ 94 - // hides the inactive one (it builds to nothing). │ │ 90 + // select the logged-in view when logged in, else the logged-out view. │ │ 91 + // decided per-frame since the tty doesn't rebuild the page on login. │ │ 92 const logged_in = self.session.data.user_id != null; │ │ 96 - const box = &self.center.child.box; │ │ 97 - const button = &box.children.values()[button_index].widget.text_box; │ │ 98 - if (logged_in) button.box.children.values()[0].widget.text.content = ansiLabel(self.session); │ │ 99 - button.getFocus().focusable = logged_in; │ │100 - const hidden = layout.MaybeSize{ .width = null, .height = 0 }; │ │101 - box.children.values()[button_index].max_size = if (logged_in) null else hidden; │ │102 - box.children.values()[message_index].max_size = if (logged_in) hidden else null; │ │ 93 + const stack = &self.center.child.stack; │ │ 94 + if (logged_in) { │ │ 95 + const box = &stack.children.values()[logged_in_index].box; │ │ 96 + const button = &box.children.values()[0].widget.text_box; │ │ 97 + button.box.children.values()[0].widget.text.content = ansiLabel(self.session); │ │ 98 + } │ │ 99 + stack.getFocus().child_id = stack.children.keys()[if (logged_in) logged_in_index else logged_out_index]; │ │100 try self.center.build(allocator, constraint, root_focus); │ │101 } │ │102 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │121 } │ │122 } │ │123 │ │127 - // enqueue the toggle; the host drains it (applying + persisting) this frame.│ │124 fn toggle(self: *View) !void { │ │125 try self.session.push(.toggle_ansi); │ │126 } │ ╰──────────────────────────────────────────────────────────────────────────────────────╯