╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Logout.zig ───────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
4 const xit = @import("xit"); 5 const xitui = xit.xitui; 6 const wgt = xitui.widget; 7 const layout = xitui.layout; 8 const Key = xitui.input.Key; 9 const Grid = xitui.grid.Grid; 10 const Focus = xitui.focus.Focus; 11 12 const Self = @This(); 13 14 pub fn init() Self { 15 return .{}; 16 } 17 18 pub const View = struct { 19 center: ui.widget.Center, 20 data: *const Self, 21 session: *ui.Session, 22 ansi_id: usize, 23 logout_id: usize, 24 25 const ansi_index: usize = 0; 26 27 pub fn init(allocator: std.mem.Allocator, data: *const Self, session: *ui.Session) !View { 28 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .round_corners = true, .direction = .vert }); 29 errdefer box.deinit(allocator); 30 31 const ansi_id = try addButton(allocator, &box, "form:ansi", ansiLabel(session)); 32 33 { 34 var gap = try wgt.Text.init(allocator, " "); 35 errdefer gap.deinit(allocator); 36 try box.children.put(allocator, gap.getFocus().id, .{ 37 .widget = .{ .text = gap }, 38 .rect = null, 39 .min_size = null, 40 }); 41 } 42 43 const logout_id = try addButton(allocator, &box, "form:logout", "logout"); 44 45 box.getFocus().child_id = box.children.keys()[ansi_index]; 46 47 return .{ 48 .center = try ui.widget.Center.init(allocator, .{ .box = box }), 49 .data = data, 50 .session = session, 51 .ansi_id = ansi_id, 52 .logout_id = logout_id, 53 }; 54 } 55 56 // each button sits in its own form box so the web renderer posts it to its own route 57 fn addButton(allocator: std.mem.Allocator, parent: *wgt.Box(ui.Widget), form: []const u8, label: []const u8) !usize { 58 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert }); 59 errdefer box.deinit(allocator); 60 box.getFocus().kind = .{ .custom = form }; 61 62 var button = try wgt.TextBox.init(allocator, label, .{ .border_style = .single, .round_corners = true, .wrap_kind = .none }); 63 errdefer button.deinit(allocator); 64 button.getFocus().mode = .all; 65 // the renderer distinguishes plain clickables from buttons that 66 // should POST to a server route by this kind. 67 button.getFocus().kind = .{ .custom = "submit" }; 68 const button_id = button.getFocus().id; 69 try box.children.put(allocator, button_id, .{ 70 .widget = .{ .text_box = button }, 71 .rect = null, 72 .min_size = null, 73 }); 74 box.getFocus().child_id = button_id; 75 76 try parent.children.put(allocator, box.getFocus().id, .{ 77 .widget = .{ .box = box }, 78 .rect = null, 79 .min_size = null, 80 }); 81 return button_id; 82 } 83 84 pub fn deinit(self: *View, allocator: std.mem.Allocator) void { 85 self.center.deinit(allocator); 86 } 87 88 pub fn build(self: *View, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void { 89 const ansi_box = &self.center.child.box.children.values()[ansi_index].widget.box; 90 try ansi_box.children.values()[0].widget.text_box.setContent(allocator, ansiLabel(self.session)); 91 try self.center.build(allocator, constraint, root_focus); 92 } 93 94 pub fn input(self: *View, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void { 95 _ = allocator; 96 const focused_id = if (self.atTop()) self.ansi_id else self.logout_id; 97 switch (key) { 98 .arrow_up => root_focus.setFocus(self.ansi_id), 99 .arrow_down => root_focus.setFocus(self.logout_id), 100 .enter => try self.activate(focused_id), 101 .mouse => |mouse| if (inp.leftClickOn(root_focus, focused_id, mouse)) try self.activate(focused_id), 102 else => {}, 103 } 104 } 105 106 fn activate(self: *View, button_id: usize) !void { 107 if (button_id == self.ansi_id) { 108 try self.session.push(.toggle_ansi); 109 return; 110 } 111 self.session.logOut(); 112 // leave the auth tab for the page it belongs to, where the web's 113 // /logout redirect also lands 114 try self.session.navigate(self.session.data.current_page.pageRoot()); 115 } 116 117 pub fn clearGrid(self: *View) void { 118 self.center.clearGrid(); 119 } 120 121 pub fn getGrid(self: View) ?Grid { 122 return self.center.getGrid(); 123 } 124 125 pub fn getFocus(self: *View) *Focus { 126 return self.center.getFocus(); 127 } 128 129 pub fn atTop(self: View) bool { 130 return self.center.child.box.focus.child_id == self.center.child.box.children.keys()[ansi_index]; 131 } 132 }; 133 134 fn ansiLabel(session: *const ui.Session) []const u8 { 135 return if (session.data.enable_ansi) "turn off ANSI art" else "turn on ANSI art"; 136 } 137