╭ Logout.zig ───────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
1 const std = @import("std"); 2 const ui = @import("../../ui.zig"); 3 const xit = @import("xit"); 4 const xitui = xit.xitui; 5 const wgt = xitui.widget; 6 const layout = xitui.layout; 7 const inp = xitui.input; 8 const Grid = xitui.grid.Grid; 9 const Focus = xitui.focus.Focus; 10 11 const Self = @This(); 12 13 pub fn init() Self { 14 return .{}; 15 } 16 17 pub const View = struct { 18 center: ui.Center, 19 data: *const Self, 20 session: *ui.Session, 21 button_id: usize, 22 // focus id of the header's "users" tab; on logout we jump there so the 23 // user isn't stranded on a button that's about to be hidden. 24 users_tab_id: usize, 25 26 const prompt_index: usize = 0; 27 const button_index: usize = 1; 28 29 pub fn init(allocator: std.mem.Allocator, data: *const Self, session: *ui.Session, users_tab_id: usize) !View { 30 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .rounded_corners = true, .direction = .vert }); 31 errdefer box.deinit(allocator); 32 // marks this subtree as an HTML form scope for the web overlay 33 box.getFocus().kind = .{ .custom = switch (session.data.current_page) { 34 .user_repos => |u| try std.fmt.allocPrint(session.page_arena.allocator(), "form:/user/{s}/logout", .{u.name.slice()}), 35 .user_settings, .user_auth => |name| try std.fmt.allocPrint(session.page_arena.allocator(), "form:/user/{s}/logout", .{name.slice()}), 36 else => "form:/logout", 37 } }; 38 39 { 40 var prompt = try wgt.Text(ui.Widget).init(allocator, "are you sure?"); 41 errdefer prompt.deinit(allocator); 42 try box.children.put(allocator, prompt.getFocus().id, .{ 43 .widget = .{ .text = prompt }, 44 .rect = null, 45 .min_size = null, 46 }); 47 } 48 49 var button_id: usize = undefined; 50 { 51 var button = try wgt.TextBox(ui.Widget).init(allocator, "logout", .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none }); 52 errdefer button.deinit(allocator); 53 button.getFocus().focusable = true; 54 // the renderer distinguishes plain clickables from buttons that 55 // should POST to a server route by this kind. 56 button.getFocus().kind = .{ .custom = "submit" }; 57 button_id = button.getFocus().id; 58 try box.children.put(allocator, button.getFocus().id, .{ 59 .widget = .{ .text_box = button }, 60 .rect = null, 61 .min_size = null, 62 }); 63 } 64 65 box.getFocus().child_id = button_id; 66 67 return .{ 68 .center = try ui.Center.init(allocator, .{ .box = box }, .both), 69 .data = data, 70 .session = session, 71 .button_id = button_id, 72 .users_tab_id = users_tab_id, 73 }; 74 } 75 76 pub fn deinit(self: *View, allocator: std.mem.Allocator) void { 77 self.center.deinit(allocator); 78 } 79 80 pub fn build(self: *View, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void { 81 try self.center.build(allocator, constraint, root_focus); 82 } 83 84 pub fn input(self: *View, allocator: std.mem.Allocator, key: inp.Key, root_focus: *Focus) !void { 85 _ = allocator; 86 switch (key) { 87 .enter => { 88 try self.logout(root_focus); 89 return; 90 }, 91 .mouse => |mouse| { 92 if (mouse.action == .press and mouse.action.press == .left) { 93 if (root_focus.children.get(self.button_id)) |entry| { 94 const r = entry.rect; 95 if (mouse.x >= r.x and mouse.y >= r.y and 96 mouse.x < r.x + r.size.width and mouse.y < r.y + r.size.height) 97 { 98 try self.logout(root_focus); 99 return; 100 } 101 } 102 } 103 }, 104 else => {}, 105 } 106 } 107 108 fn logout(self: *View, root_focus: *Focus) !void { 109 self.session.data.user_id = null; 110 // jump focus back to the users tab — the logout button is about to 111 // be hidden by the tab-label swap. 112 root_focus.setFocus(self.users_tab_id); 113 } 114 115 pub fn clearGrid(self: *View) void { 116 self.center.clearGrid(); 117 } 118 119 pub fn getGrid(self: View) ?Grid { 120 return self.center.getGrid(); 121 } 122 123 pub fn getFocus(self: *View) *Focus { 124 return self.center.getFocus(); 125 } 126 127 pub fn getSelectedIndex(self: View) ?usize { 128 _ = self; 129 return 0; 130 } 131 }; 132