StructOptions
Creates a default set of field options for a struct or union.
An optional default value can be provided to init, and used whenever the struct or union must be created. e.g. from setting an optional to Not Null.
Field options can be overridden after creation directly through the field_options member. Use .remove() and/or .put().
Parameters
- Struct:type
- type
Functions
- init
Initialize and display only the fields provided.
- initWithDefaults
Initialize struct options with default options for all fields.
- initWithDisplayFn
Use a custom display function to display this struct.
- forFieldName
Helper for setting
for_field_nameafter construction.- defaultFieldOption
Return a default value for a field if no default for that field has been supplied through
Constants
Source
Implementation
pub fn StructOptions(Struct: type) type {
switch (@typeInfo(Struct)) {
.@"struct", .@"union" => {},
else => @compileError(std.fmt.comptimePrint("StructOptions(T) requires Struct or Union, but received a {s}.", .{@typeName(Struct)})),
}
return struct {
const Self = @This();
pub const StructOptionsT = std.EnumMap(std.meta.FieldEnum(StructT), FieldOptions);
// Type of struct or union these options belong to
pub const StructT = Struct;
// display options for each field to be displayed
field_options: StructOptionsT,
// A default value to be used whenever an instance of this type is created
default_value: ?StructT = null,
// Display the struct using this function, instead of the default struct_ui function.
customDisplayFn: ?*const fn ([]const u8, *anyopaque, bool, *dvui.Alignment) void = null,
// If set, this struct_option will only apply to fields with this name
for_field_name: ?[]const u8 = null,
/// Initialize and display only the fields provided.
/// options: field options for all the fields to be displayed.
/// default_value: An optional default value to be used whenever an instance
/// of this type needs ot be created.
///
/// Example Usage - Do not display the .a field and display all other fields as sliders.
/// ```
/// const color_options: dvui.struct_ui.StructOptions(dvui.Color) = .init(.{
/// .r = .{ .number = .{ .min = 0, .max = 255, .widget_type = .slider } },
/// .g = .{ .number = .{ .min = 0, .max = 255, .widget_type = .slider } },
/// .b = .{ .number = .{ .min = 0, .max = 255, .widget_type = .slider } },
/// }, .{ .r = 127, .g = 127, .b = 127, .a = 255 });
/// ```
pub fn init(
options: std.enums.EnumFieldStruct(
StructOptionsT.Key,
?StructOptionsT.Value,
@as(?StructOptionsT.Value, null),
),
comptime default_value: ?StructT,
) Self {
return .{
.field_options = .init(options),
.default_value = default_value,
};
}
/// Initialize struct options with default options for all fields.
/// Overrides for these defaults are specified in options.
///
/// options: field options for all the fields to be displayed.
/// default_value: An optional default value to be used whenever an instance
/// of this type needs ot be created.
/// Used with the same syntax as .init, with the only difference being that this initializer
/// creates default field options for any fields not provided in options.
///
/// Example Usage - Display .r, .g, .b, .a as default text entry boxes.
/// `const color_options: dvui.struct_ui.StructOptions(dvui.Color) = .init(.{}, null);`
pub fn initWithDefaults(comptime options: std.enums.EnumFieldStruct(
StructOptionsT.Key,
?StructOptionsT.Value,
@as(?StructOptionsT.Value, null),
), comptime default_value: ?StructT) Self {
comptime var field_options: StructOptionsT = .{};
comptime {
for (0..field_options.values.len) |i| {
const key = StructOptionsT.Indexer.keyForIndex(i);
const field_name = @tagName(key);
if (@field(options, field_name)) |*v| {
field_options.put(key, v.*);
} else {
const type_info = @typeInfo(@FieldType(StructT, field_name));
// Skip creating default field options for any pointer fields that can't be displayed.
if (type_info == .pointer and !canDisplayPtr(type_info.pointer)) continue;
field_options.put(key, defaultFieldOption(@FieldType(StructT, field_name)));
}
}
}
return .{
.field_options = field_options,
.default_value = default_value,
};
}
/// Use a custom display function to display this struct.
pub fn initWithDisplayFn(
// Display the struct using this function, instead of the default struct_ui function.
customDisplayFn: *const fn (field_name: []const u8, field_value_ptr: *anyopaque, read_only: bool, *dvui.Alignment) void,
comptime default_value: ?StructT,
) Self {
return .{
.field_options = .init(.{}),
.customDisplayFn = customDisplayFn,
.default_value = default_value,
};
}
/// Helper for setting `for_field_name` after construction.
/// If `for_field_name` is set, these options will only apply to fields
/// field with that field name.
///
/// Useful for dealing with common struct such as dvui.Point where you want
/// to display different fields of the same type using different widgets.
///
/// NOTE: Ordering is important. If there are multiple options for the same struct type
/// order the field_name variants before the generic struct options.
pub fn forFieldName(self: Self, field_name: []const u8) Self {
// This should be rarely used, so this is fine. But if we add more of these
// options, move to using an init_opts struct, rather than this builder pattern.
var result = self;
result.for_field_name = field_name;
return result;
}
/// Return a default value for a field if no default for that field has been supplied through
/// StructOptions.
pub fn defaultFieldOption(FieldType: type) FieldOptions {
return switch (@typeInfo(FieldType)) {
.int, .float => .{ .number = .{} },
.bool => .{ .boolean = .{} },
// For arrays, pointers and optionals, field_options are set for the child type.
.pointer => |ptr| if (ptr.size == .slice and ptr.child == u8)
.{
.text = .{ .display = if (ptr.is_const or ptr.sentinel_ptr != null) .read_only else .read_write },
}
else
defaultFieldOption(ptr.child),
.optional => |opt| defaultFieldOption(opt.child),
.array => |arr| if (arr.child == u8) .{ .text = .{ .display = .read_only } } else defaultFieldOption(arr.child),
else => .{ .standard = .{} },
};
}
};
}