DVUI

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_name after 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 = .{} },
            };
        }
    };
}