unionFieldWidget
Allow the selection of the active union member. returns the tag of the active member.
Parameters
- src:std.builtin.SourceLocation
- std.builtin.SourceLocation
- field_name:[]const u8
- []const u8
- field_value_ptr:anytype
- anytype
- opt:FieldOptions
- FieldOptions
Source
Implementation
pub fn unionFieldWidget(
src: std.builtin.SourceLocation,
field_name: []const u8,
field_value_ptr: anytype,
opt: FieldOptions,
) UnionTagType(@TypeOf(field_value_ptr)) {
const T = @TypeOf(field_value_ptr.*);
if (@typeInfo(T).@"union".tag_type == null) {
@compileError(std.fmt.comptimePrint("union field {s}: Only tagged unions are supported", .{field_name}));
}
if (opt.displayMode() == .none) {
return field_value_ptr.*;
}
const read_only = @typeInfo(@TypeOf(field_value_ptr)).pointer.is_const or opt.displayMode() != .read_write;
var box = dvui.box(src, .{ .dir = .vertical }, .{});
defer box.deinit();
const entries = std.meta.fields(T);
var active_tag = std.meta.activeTag(field_value_ptr.*);
// This loop is being used, rather than @intFromEnum etc as there is no guarantee that
// the enum int values are the same as the field order. i.e. custom enums can be used as tags.
// There should be a better way to do this than converting to string? But UnionField doesn't store the tag.
const choice_names: []const []const u8, var active_choice_num: usize = blk: {
var choice_names: [entries.len][]const u8 = undefined;
var active_choice_num: usize = 0;
inline for (0..entries.len) |i| {
if (std.meta.stringToEnum(@TypeOf(active_tag), entries[i].name).? == std.meta.activeTag(field_value_ptr.*)) {
active_choice_num = i;
}
choice_names[i] = entries[i].name;
}
break :blk .{ &choice_names, active_choice_num };
};
{
var hbox = dvui.box(@src(), .{}, .{});
defer hbox.deinit();
if (read_only) {
switch (active_tag) {
// Don't display active tag, if it will be displayed as a field. i.e. it is not a void union member.
// TODO: It is not strictly just void here. It is really any type that can't be displayed by
// displayField(). Need to add a lookup for displayable types and use that here.
inline else => |t| if (@FieldType(T, @tagName(t)) == void) {
dvui.labelNoFmt(@src(), @tagName(active_tag), .{}, .{ .margin = .{ .y = 4 } });
},
}
} else {
if (dvui.dropdown(@src(), choice_names, .{ .choice = &active_choice_num }, .{}, .{})) {
active_tag = std.meta.stringToEnum(@TypeOf(active_tag), choice_names[active_choice_num]).?; // This should never fail.
}
}
}
return active_tag;
}