╭ changed ──────────╮ │src/ui/CloneUrl.zig│ ╰───────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │12 pub const View = struct { │ │13 box: wgt.Box(ui.Widget), │ │14 session: *ui.Session, │ │15 - urls: [2][]const u8, │ │15 + protocols: [2]Protocol, │ │16 + protocol_count: usize, │ │17 selected: usize = 0, │ │18 │ │18 - const http_index: usize = 0; │ │19 - const ssh_index: usize = 1; │ │20 - const input_index: usize = 2; │ │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 { │ │23 - const http_port = session.data.clone_http_port orelse return error.MissingClonePort; │ │24 - const ssh_port = session.data.clone_ssh_port orelse return error.MissingClonePort; │ │25 const aa = session.page_arena.allocator(); │ │26 - const urls = [2][]const u8{ │ │27 - try std.fmt.allocPrint(aa, "http://localhost:{d}/repo/{s}", .{ http_port, identity }), │ │28 - try std.fmt.allocPrint(aa, "ssh://localhost:{d}/repo/{s}", .{ ssh_port, identity }), │ │29 - }; │ │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 │ │35 - inline for ([_][]const u8{ "http", "ssh" }) |protocol| { │ │36 - var label = try wgt.TextBox(ui.Widget).init(allocator, protocol, .{ .border_style = .hidden, .wrap_kind = .none }); │ │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; │ │39 - try box.children.put(allocator, label.getFocus().id, .{ .widget = .{ .text_box = label }, .rect = null, .min_size = .{ .width = protocol.len + 2, .height = 3 } }); │ │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, │ │46 - .visible_width = @max(urls[0].len, urls[1].len),│ │62 + .visible_width = max_url_len, │ │63 }); │ │64 errdefer url_input.deinit(allocator); │ │65 url_input.getFocus().focusable = true; │ ╰────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │50 - try url_input.setContent(allocator, urls[0]); │ │66 + try url_input.setContent(allocator, protocols[0].url); │ │67 box.getFocus().child_id = url_input.getFocus().id; │ │52 - try box.children.put(allocator, url_input.getFocus().id, .{ .widget = .{ .text_input = url_input }, .rect = null, .min_size = .{ .width = @max(urls[0].len, urls[1].len) + 2, .height = 3 } });│ │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 │ │54 - return .{ .box = box, .session = session, .urls = urls }; │ │70 + return .{ .box = box, .session = session, .protocols = protocols, .protocol_count = protocol_count }; │ │71 } │ │72 │ │73 pub fn minWidth(self: *const View) usize { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────╮ │58 - return "http".len + "ssh".len + 4 + @max(self.urls[0].len, self.urls[1].len) + 2;│ │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 { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────╮ │88 self.clearGrid(); │ │89 │ │90 if (root_focus.grandchild_id) |id| { │ │69 - const clicked = if (id == self.protocolLabel(http_index).getFocus().id)│ │70 - http_index │ │71 - else if (id == self.protocolLabel(ssh_index).getFocus().id) │ │72 - ssh_index │ │73 - else │ │74 - null; │ │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; │ ╰───────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 78 - try self.urlInput().setContent(allocator, self.urls[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;│ │ 85 - for (0..2) |i| { │ │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 │ ╰───────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │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()); │ │100 - try self.session.input_values.put(aa, id, self.urls[self.selected]); │ │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) { │ │106 - .arrow_left => if (self.selected == ssh_index) http_index else return, │ │107 - .arrow_right => if (self.selected == http_index) ssh_index else return, │ │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 => { │ │109 - if (self.session.is_terminal) self.session.host_request = .{ .clone_url = self.urls[self.selected] }; │ │129 + if (self.session.is_terminal) self.session.host_request = .{ .clone_url = self.protocols[self.selected].url }; │ │130 return; │ │131 }, │ │112 - .mouse => |mouse| if (inp.leftClickOn(root_focus, self.protocolLabel(http_index).getFocus().id, mouse)) │ │113 - http_index │ │114 - else if (inp.leftClickOn(root_focus, self.protocolLabel(ssh_index).getFocus().id, mouse)) │ │115 - ssh_index │ │116 - else if (inp.leftClickOn(root_focus, self.urlInput().getFocus().id, mouse)) { │ │117 - if (self.session.is_terminal) self.session.host_request = .{ .clone_url = self.urls[self.selected] }; │ │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 (inp.leftClickOn(root_focus, self.urlInput().getFocus().id, mouse)) { │ │137 + if (self.session.is_terminal) self.session.host_request = .{ .clone_url = self.protocols[self.selected].url };│ │138 + } │ │139 return; │ │119 - } else return, │ │140 + }, │ │141 else => return, │ │142 }; │ │143 self.selected = next; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯