╭ Settings.zig ─────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
1 const std = @import("std"); 2 const ui = @import("../ui.zig"); 3 const inp = @import("./input.zig"); 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 // 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.Center, 24 unauthorized: ui.Unauthorized.View, 25 session: *ui.Session, 26 button_id: usize, 27 28 pub fn init(allocator: std.mem.Allocator, session: *ui.Session) !View { 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); 33 34 // on the web each control posts to a page-scoped form 35 box.getFocus().kind = .{ .custom = "form:ansi" }; 36 37 { 38 var button = try wgt.TextBox(ui.Widget).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().focusable = true; 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.Center.init(allocator, .{ .box = box }); 50 }; 51 errdefer authorized.deinit(allocator); 52 53 var unauthorized = try ui.Unauthorized.View.init(allocator); 54 errdefer unauthorized.deinit(allocator); 55 56 return .{ 57 .focus = try Focus.create(allocator, .container), 58 .authorized = authorized, 59 .unauthorized = unauthorized, 60 .session = session, 61 .button_id = button_id, 62 }; 63 } 64 65 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 } 70 71 pub fn build(self: *View, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void { 72 // decided per-frame since the tty doesn't rebuild the page on login 73 self.focus.clear(); 74 if (self.session.data.user_id != null) { 75 const box = &self.authorized.child.box; 76 const button = &box.children.values()[0].widget.text_box; 77 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; 89 } 90 } 91 92 pub fn input(self: *View, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void { 93 _ = allocator; 94 if (self.session.data.user_id == null) return; // blank/disabled when logged out 95 switch (key) { 96 .enter => try self.toggle(), 97 .mouse => |mouse| { 98 if (inp.leftClickOn(root_focus, self.button_id, mouse)) { 99 try self.toggle(); 100 } 101 }, 102 else => {}, 103 } 104 } 105 106 fn toggle(self: *View) !void { 107 try self.session.push(.toggle_ansi); 108 } 109 110 pub fn clearGrid(self: *View) void { 111 self.authorized.clearGrid(); 112 self.unauthorized.clearGrid(); 113 } 114 115 pub fn getGrid(self: View) ?Grid { 116 if (self.session.data.user_id != null) return self.authorized.getGrid(); 117 return self.unauthorized.getGrid(); 118 } 119 120 pub fn getFocus(self: *View) *Focus { 121 return self.focus; 122 } 123 124 pub fn getSelectedIndex(self: View) ?usize { 125 _ = self; 126 return 0; 127 } 128 }; 129 130 fn ansiLabel(session: *const ui.Session) []const u8 { 131 const label_on = "turn off ANSI art"; 132 const label_off = "turn on ANSI art"; 133 return if (session.data.enable_ansi) label_on else label_off; 134 } 135