╭ CloneUrl.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 Key = xitui.input.Key; 8 const Grid = xitui.grid.Grid; 9 const Focus = xitui.focus.Focus; 10 const inp = @import("input.zig"); 11 12 pub const View = struct { 13 box: wgt.Box(ui.Widget), 14 session: *ui.Session, 15 protocols: [2]Protocol, 16 protocol_count: usize, 17 selected: usize = 0, 18 19 const Protocol = struct { 20 name: []const u8, 21 url: []const u8, 22 }; 23 24 pub fn init(allocator: std.mem.Allocator, session: *ui.Session, identity: []const u8) !View { 25 const aa = session.page_arena.allocator(); 26 var protocols: [2]Protocol = undefined; 27 var protocol_count: usize = 0; 28 if (session.data.clone_http_port) |port| { 29 protocols[protocol_count] = .{ 30 .name = "http", 31 .url = try std.fmt.allocPrint(aa, "http://localhost:{d}/repo/{s}", .{ port, identity }), 32 }; 33 protocol_count += 1; 34 } 35 if (session.data.clone_ssh_port) |port| { 36 protocols[protocol_count] = .{ 37 .name = "ssh", 38 .url = try std.fmt.allocPrint(aa, "ssh://localhost:{d}/repo/{s}", .{ port, identity }), 39 }; 40 protocol_count += 1; 41 } 42 if (protocol_count == 0) return error.MissingClonePort; 43 44 var max_url_len: usize = 0; 45 for (protocols[0..protocol_count]) |protocol| max_url_len = @max(max_url_len, protocol.url.len); 46 47 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .horiz }); 48 errdefer box.deinit(allocator); 49 box.getFocus().kind = .{ .custom = "form:" }; 50 51 for (protocols[0..protocol_count]) |protocol| { 52 var label = try wgt.TextBox(ui.Widget).init(allocator, protocol.name, .{ .border_style = .hidden, .wrap_kind = .none }); 53 errdefer label.deinit(allocator); 54 label.getFocus().focusable = true; 55 try box.children.put(allocator, label.getFocus().id, .{ .widget = .{ .text_box = label }, .rect = null, .min_size = .{ .width = protocol.name.len + 2, .height = 3 } }); 56 } 57 58 var url_input = try wgt.TextInput(ui.Widget).init(allocator, .{ 59 .border_style = .single, 60 .label = " clone ", 61 .render_content = session.is_terminal, 62 .visible_width = max_url_len, 63 }); 64 errdefer url_input.deinit(allocator); 65 url_input.getFocus().focusable = true; 66 try url_input.setContent(allocator, protocols[0].url); 67 box.getFocus().child_id = url_input.getFocus().id; 68 try box.children.put(allocator, url_input.getFocus().id, .{ .widget = .{ .text_input = url_input }, .rect = null, .min_size = .{ .width = max_url_len + 2, .height = 3 } }); 69 70 return .{ .box = box, .session = session, .protocols = protocols, .protocol_count = protocol_count }; 71 } 72 73 pub fn minWidth(self: *const View) usize { 74 var width: usize = 0; 75 var max_url_len: usize = 0; 76 for (self.protocols[0..self.protocol_count]) |protocol| { 77 width += protocol.name.len + 2; 78 max_url_len = @max(max_url_len, protocol.url.len); 79 } 80 return width + max_url_len + 2; 81 } 82 83 pub fn deinit(self: *View, allocator: std.mem.Allocator) void { 84 self.box.deinit(allocator); 85 } 86 87 pub fn build(self: *View, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void { 88 self.clearGrid(); 89 90 if (root_focus.grandchild_id) |id| { 91 var clicked: ?usize = null; 92 for (0..self.protocol_count) |i| { 93 if (id == self.protocolLabel(i).getFocus().id) clicked = i; 94 } 95 if (clicked) |selected| { 96 if (selected != self.selected) { 97 self.selected = selected; 98 try self.urlInput().setContent(allocator, self.protocols[selected].url); 99 } 100 root_focus.setFocus(self.urlInput().getFocus().id); 101 } 102 } 103 104 const focused = root_focus.grandchild_id == self.urlInput().getFocus().id; 105 for (0..self.protocol_count) |i| { 106 self.protocolLabel(i).options.border_style = if (i == self.selected) 107 (if (focused) .double else .single) 108 else 109 .hidden; 110 } 111 self.urlInput().options.border_style = .single; 112 self.urlInput().cursor = 0; 113 self.urlInput().scroll_offset = 0; 114 try self.box.build(allocator, constraint, root_focus); 115 116 if (!self.session.is_terminal) { 117 const aa = self.session.arena.allocator(); 118 const id = self.urlInput().getFocus().id; 119 try self.session.text_inputs.put(aa, id, self.urlInput()); 120 try self.session.input_values.put(aa, id, self.protocols[self.selected].url); 121 } 122 } 123 124 pub fn input(self: *View, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void { 125 const next = switch (key) { 126 .arrow_left => if (self.selected > 0) self.selected - 1 else return, 127 .arrow_right => if (self.selected + 1 < self.protocol_count) self.selected + 1 else return, 128 .enter => { 129 if (self.session.is_terminal) self.session.host_request = .{ .show_url = self.protocols[self.selected].url }; 130 return; 131 }, 132 .mouse => |mouse| blk: { 133 for (0..self.protocol_count) |i| { 134 if (inp.leftClickOn(root_focus, self.protocolLabel(i).getFocus().id, mouse)) break :blk i; 135 } 136 if (self.session.is_terminal and !mouse.ctrl and inp.leftClickOn(root_focus, self.urlInput().getFocus().id, mouse)) 137 self.session.host_request = .{ .show_url = self.protocols[self.selected].url }; 138 return; 139 }, 140 else => return, 141 }; 142 self.selected = next; 143 try self.urlInput().setContent(allocator, self.protocols[next].url); 144 } 145 146 fn urlInput(self: *View) *wgt.TextInput(ui.Widget) { 147 return &self.box.children.values()[self.protocol_count].widget.text_input; 148 } 149 150 fn protocolLabel(self: *View, index: usize) *wgt.TextBox(ui.Widget) { 151 return &self.box.children.values()[index].widget.text_box; 152 } 153 154 pub fn clearGrid(self: *View) void { 155 self.box.clearGrid(); 156 } 157 158 pub fn getGrid(self: View) ?Grid { 159 return self.box.getGrid(); 160 } 161 162 pub fn getFocus(self: *View) *Focus { 163 return self.box.getFocus(); 164 } 165 }; 166