╭ changed ────────╮ │src/ui/widget.zig│ ╰─────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭────────────────────────────────────────────────────╮ │41 repo_header: ui.Repo.Header.View, │ │42 fork_header: ui.Fork.Header.View, │ │43 search_box: SearchBox, │ │44 + radio: Radio, │ │45 search_header: SearchHeader, │ │46 repo_issues_header: ui.Repo.Issues.Header, │ │47 repo_patches_header: ui.Repo.Patches.Header,│ ╰────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 940 │ │ 941 pub fn getFocus(self: *SearchBox) *Focus { │ │ 942 return self.text_input.getFocus(); │ │ 943 + } │ │ 944 +}; │ │ 945 + │ │ 946 +// a row of bordered options posted as one form field. the selection is the │ │ 947 +// focused option, so a click selects on the web too. │ │ 948 +pub const Radio = struct { │ │ 949 + box: wgt.Box(Widget), │ │ 950 + values: []const []const u8, │ │ 951 + │ │ 952 + pub fn init(allocator: std.mem.Allocator, session: *ui.Session, name: []const u8, values: []const []const u8, initial: []const u8) !Radio { │ │ 953 + var box = try wgt.Box(Widget).init(allocator, .{ .border_style = null, .direction = .horiz }); │ │ 954 + errdefer box.deinit(allocator); │ │ 955 + for (values) |value| { │ │ 956 + var option = try wgt.TextBox.init(allocator, value, .{ .border_style = .single, .round_corners = true, .wrap_kind = .none }); │ │ 957 + errdefer option.deinit(allocator); │ │ 958 + option.getFocus().mode = .all; │ │ 959 + option.getFocus().kind = .{ .custom = try std.fmt.allocPrint(session.page_arena.allocator(), ui.radio_prefix ++ "{s}={s}", .{ name, value }) }; │ │ 960 + if (std.mem.eql(u8, value, initial)) box.getFocus().child_id = option.getFocus().id; │ │ 961 + try box.children.put(allocator, option.getFocus().id, .{ .widget = .{ .text_box = option }, .rect = null, .min_size = .{ .width = try xitui.width.displayWidth(value) + 2, .height = 3 } });│ │ 962 + } │ │ 963 + return .{ .box = box, .values = values }; │ │ 964 + } │ │ 965 + │ │ 966 + pub fn deinit(self: *Radio, allocator: std.mem.Allocator) void { │ │ 967 + self.box.deinit(allocator); │ │ 968 + } │ │ 969 + │ │ 970 + // the focused option's value │ │ 971 + pub fn selected(self: *Radio) []const u8 { │ │ 972 + const index = self.box.children.getIndex(self.box.getFocus().child_id orelse unreachable) orelse unreachable; │ │ 973 + return self.values[index]; │ │ 974 + } │ │ 975 + │ │ 976 + pub fn build(self: *Radio, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void { │ │ 977 + self.clearGrid(); │ │ 978 + const selected_id = self.box.getFocus().child_id; │ │ 979 + for (self.box.children.keys(), self.box.children.values()) |id, *child| { │ │ 980 + child.widget.text_box.options.border_style = if (id == selected_id) .single else .hidden; │ │ 981 + } │ │ 982 + try self.box.build(allocator, constraint, root_focus); │ │ 983 + } │ │ 984 + │ │ 985 + pub fn input(self: *Radio, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void { │ │ 986 + _ = allocator; │ │ 987 + const ids = self.box.children.keys(); │ │ 988 + const index = self.box.children.getIndex(self.box.getFocus().child_id orelse return) orelse return; │ │ 989 + switch (key) { │ │ 990 + .arrow_left => if (index > 0) root_focus.setFocus(ids[index - 1]), │ │ 991 + .arrow_right => if (index + 1 < ids.len) root_focus.setFocus(ids[index + 1]), │ │ 992 + else => {}, │ │ 993 + } │ │ 994 + } │ │ 995 + │ │ 996 + pub fn clearGrid(self: *Radio) void { │ │ 997 + self.box.clearGrid(); │ │ 998 + } │ │ 999 + │ │1000 + pub fn getGrid(self: Radio) ?Grid { │ │1001 + return self.box.getGrid(); │ │1002 + } │ │1003 + │ │1004 + pub fn getFocus(self: *Radio) *Focus { │ │1005 + return self.box.getFocus(); │ │1006 } │ │1007 }; │ │1008 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯