╭ changed ──────────╮ │src/ui/Settings.zig│ ╰───────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────╮ │10 │ │11 const Self = @This(); │ │12 │ │13 -const label_on = "turn off ANSI art";│ │14 -const label_off = "turn on ANSI art";│ │15 - │ │13 pub fn init() Self { │ │14 return .{}; │ │15 } │ ╰─────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │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; │ │27 │ │28 pub fn init(allocator: std.mem.Allocator, data: *const Self, session: *ui.Session) !View { │ │29 - const logged_in = session.data.user_id != null; │ │30 - │ │29 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .rounded_corners = true, .direction = .vert }); │ │30 errdefer box.deinit(allocator); │ │33 - // when logged in, the toggle posts to a page-scoped /ansi path so the │ │34 - // server sends us back to this page's settings tab; logged out, the │ │35 - // empty action blocks the POST and the input goes to the TUI instead. │ │36 - box.getFocus().kind = .{ .custom = if (!logged_in) "form:" else switch (session.data.current_page) { │ │37 - .user, .user_settings, .user_auth => |name| try std.fmt.allocPrint(session.page_arena.allocator(), "form:/user/{s}/ansi", .{name.slice()}), │ │38 - else => "form:/ansi", │ │39 - } }; │ │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) { │ │36 + box.getFocus().kind = .{ .custom = switch (session.data.current_page) { │ │37 + .user, .user_settings, .user_auth => |name| try std.fmt.allocPrint(session.page_arena.allocator(), "form:/user/{s}/ansi", .{name.slice()}),│ │38 + else => "form:/ansi", │ │39 + } }; │ │40 + } │ │41 │ │42 var button_id: usize = undefined; │ │43 { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │43 - var button = try wgt.TextBox(ui.Widget).init(allocator, labelFor(session), .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none }); │ │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); │ │45 - button.getFocus().focusable = true; │ │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" }; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │54 }); │ │55 } │ │56 │ │57 + // the logged-out message (shown in place of the toggle) │ │58 + { │ │59 + const logged_out_message = │ │60 + \\you must be logged in to see settings :( │ │61 + \\ │ │62 + \\instead, here's an emoticon from the early 2000s: │ │63 + \\<('o'<) ^( '-' )^ (>'o')> │ │64 + \\ │ │65 + \\hope it helps. │ │66 + ; │ │67 + │ │68 + var message = try wgt.TextBox(ui.Widget).init(allocator, logged_out_message, .{ .border_style = null, .rounded_corners = false, .wrap_kind = .word });│ │69 + 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 + }); │ │75 + } │ │76 + │ │77 box.getFocus().child_id = button_id; │ │78 │ │79 return .{ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 89 } │ │ 90 │ │ 91 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). │ │ 95 + const logged_in = self.session.data.user_id != null; │ │ 96 const box = &self.center.child.box; │ │ 73 - if (box.children.values().len > 0) { │ │ 74 - const button = &box.children.values()[button_index].widget.text_box; │ │ 75 - button.box.children.values()[0].widget.text.content = labelFor(self.session); │ │ 76 - } │ │ 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; │ │103 try self.center.build(allocator, constraint, root_focus); │ │104 } │ │105 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │106 pub fn input(self: *View, allocator: std.mem.Allocator, key: inp.Key, root_focus: *Focus) !void {│ │107 _ = allocator; │ │108 + if (self.session.data.user_id == null) return; // blank/disabled when logged out │ │109 switch (key) { │ │110 .enter => try self.toggle(), │ │111 .mouse => |mouse| { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────╮ │147 } │ │148 }; │ │149 │ │123 -fn labelFor(session: *const ui.Session) []const u8 { │ │150 +fn ansiLabel(session: *const ui.Session) []const u8 { │ │151 + const label_on = "turn off ANSI art"; │ │152 + const label_off = "turn on ANSI art"; │ │153 return if (session.data.enable_ansi) label_on else label_off;│ │154 } │ │155 │ ╰──────────────────────────────────────────────────────────────────────╯