╭ 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 stack: wgt.Stack(ui.Widget), 20 session: *ui.Session, 21 button_id: usize, 22 23 const authorized_index = 0; 24 const unauthorized_index = 1; 25 26 pub fn init(allocator: std.mem.Allocator, session: *ui.Session) !View { 27 var stack = try wgt.Stack(ui.Widget).init(allocator); 28 errdefer stack.deinit(allocator); 29 var button_id: usize = undefined; 30 { 31 var authorized = blk: { 32 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert }); 33 errdefer box.deinit(allocator); 34 35 // on the web each control posts to a page-scoped form 36 box.getFocus().kind = .{ .custom = "form:ansi" }; 37 38 { 39 var button = try wgt.TextBox.init(allocator, ansiLabel(session), .{ .border_style = .single, .round_corners = true, .wrap_kind = .none }); 40 errdefer button.deinit(allocator); 41 // the renderer distinguishes plain clickables from buttons that 42 // should POST to a server route by this kind. 43 button.getFocus().kind = .{ .custom = "submit" }; 44 button.getFocus().mode = .all; 45 button_id = button.getFocus().id; 46 try box.children.put(allocator, button.getFocus().id, .{ .widget = .{ .text_box = button }, .rect = null, .min_size = null }); 47 } 48 49 box.getFocus().child_id = button_id; 50 break :blk try ui.widget.Center.init(allocator, .{ .box = box }); 51 }; 52 errdefer authorized.deinit(allocator); 53 try stack.children.put(allocator, authorized.getFocus().id, .{ .center = authorized }); 54 } 55 { 56 var unauthorized = try ui.Unauthorized.View.init(allocator); 57 errdefer unauthorized.deinit(allocator); 58 try stack.children.put(allocator, unauthorized.getFocus().id, .{ .unauthorized = unauthorized }); 59 } 60 61 return .{ 62 .stack = stack, 63 .session = session, 64 .button_id = button_id, 65 }; 66 } 67 68 pub fn deinit(self: *View, allocator: std.mem.Allocator) void { 69 self.stack.deinit(allocator); 70 } 71 72 pub fn build(self: *View, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void { 73 // decided per-frame since the tty doesn't rebuild the page on login 74 const index: usize = if (self.session.data.user_id != null) authorized_index else unauthorized_index; 75 self.stack.getFocus().child_id = self.stack.children.keys()[index]; 76 if (self.session.data.user_id != null) { 77 const box = &self.stack.children.values()[authorized_index].center.child.box; 78 const button = &box.children.values()[0].widget.text_box; 79 try button.setContent(allocator, ansiLabel(self.session)); 80 } 81 try self.stack.build(allocator, constraint, root_focus); 82 } 83 84 pub fn input(self: *View, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void { 85 _ = allocator; 86 if (self.session.data.user_id == null) return; // blank/disabled when logged out 87 switch (key) { 88 .enter => try self.toggle(), 89 .mouse => |mouse| { 90 if (inp.leftClickOn(root_focus, self.button_id, mouse)) { 91 try self.toggle(); 92 } 93 }, 94 else => {}, 95 } 96 } 97 98 fn toggle(self: *View) !void { 99 try self.session.push(.toggle_ansi); 100 } 101 102 pub fn clearGrid(self: *View) void { 103 self.stack.clearGrid(); 104 } 105 106 pub fn getGrid(self: View) ?Grid { 107 return self.stack.getGrid(); 108 } 109 110 pub fn getFocus(self: *View) *Focus { 111 return self.stack.getFocus(); 112 } 113 114 pub fn atTop(self: View) bool { 115 _ = self; 116 return true; 117 } 118 }; 119 120 fn ansiLabel(session: *const ui.Session) []const u8 { 121 const label_on = "turn off ANSI art"; 122 const label_off = "turn on ANSI art"; 123 return if (session.data.enable_ansi) label_on else label_off; 124 } 125