DVUI

groupBox

A bordered box with a heading, used to group related widgets, also known as a fieldset.

Only valid between Window.beginand Window.end.

Parameters

#
src:std.builtin.SourceLocation
std.builtin.SourceLocation
label_str:[]const u8
[]const u8
opts:Options
Options

Source

Implementation

#
pub fn groupBox(src: std.builtin.SourceLocation, label_str: []const u8, opts: Options) *BoxWidget {
    var options = group_box_defaults.override(.{
        .label = .{ .text = label_str },
    }).override(opts);

    const text_size = options.fontGet().textSize(label_str);

    // Offset top margin and padding to account for label
    options.margin.?.y += @max(text_size.h / 2 - options.borderGet().y / 2, 0);
    options.padding.?.y += @max(text_size.h / 2 - options.borderGet().y / 2, 0);

    const b = widgetAlloc(BoxWidget);
    b.init(src, .{}, options);

    {
        const border = options.borderGet();
        if (border.x != border.y or border.y != border.w or border.w != border.h) {
            options.border = Rect.all(@max(border.x, border.y, border.w, border.h));
            b.data().options.border = options.border;
            dvui.log.err("groupBox {x} requires uniform borders, border width set to {d}", .{ b.data().id, options.border.?.x });
            dvui.Debug.errorOutline(b.data().rectScale().r);
        }
    }

    if (options.backgroundGet()) {
        b.drawBackground();
    }
    const label_padding: Rect = .{ .x = LabelWidget.defaults.paddingGet().x, .w = LabelWidget.defaults.paddingGet().w, .y = 0, .h = 0 };
    const label_rect: Rect = .{
        .x = 0, // Starts at padding.
        .y = -options.paddingGet().y - options.borderGet().y / 2 - text_size.h / 2,
        .w = @min(text_size.w + label_padding.x + label_padding.w, b.data().contentRect().w),
        .h = text_size.h,
    };

    if (!label_rect.empty()) {
        labelNoFmt(@src(), label_str, .{ .align_x = 0.5, .align_y = 0.5 }, options.strip().override(.{
            .rect = label_rect,
            .background = options.background,
            .corners = options.corners,
            .padding = label_padding,
        }));
    }

    // draw the border
    if (b.data().visible() and options.borderGet().nonZero()) {
        const wd = b.data();
        const rs = wd.rectScale().s;
        var path: dvui.Path.Builder = .init(dvui.currentWindow().lifo());
        defer path.deinit();

        const r = wd.borderRectScale().r.insetAll(options.borderGet().x / 2 * rs);
        const cr = options.cornersGet().scale(rs, CornerRect.Physical);
        const left_x = r.x + (options.paddingGet().x + options.borderGet().x / 2) * rs;
        const right_x = @min(
            left_x + text_size.scale(rs, Rect.Physical).w + (label_padding.x + label_padding.w) * rs,
            wd.contentRectScale().r.x + wd.contentRectScale().r.w,
        );
        path.addPoint(.{ .x = left_x, .y = r.y }); // left edge of label

        const tl = dvui.Point.Physical{ .x = r.x + cr.tl.radius(), .y = r.y + cr.tl.radius() };
        path.addArc(tl, cr.tl.radius(), std.math.pi * 1.5, std.math.pi, false);

        const bl = dvui.Point.Physical{ .x = r.x + cr.tl.radius(), .y = r.y + r.h - cr.tl.radius() };
        path.addArc(bl, cr.tl.radius(), std.math.pi, std.math.pi * 0.5, false);

        const br = dvui.Point.Physical{ .x = r.x + r.w - cr.tl.radius(), .y = r.y + r.h - cr.tl.radius() };
        path.addArc(br, cr.tl.radius(), std.math.pi * 0.5, 0, false);

        const tr = dvui.Point.Physical{ .x = r.x + r.w - cr.tl.radius(), .y = r.y + cr.tl.radius() };
        path.addArc(tr, cr.tl.radius(), std.math.pi * 2, std.math.pi * 1.5, false);

        path.addPoint(.{ .x = right_x, .y = r.y }); // right edge of label

        path.build().stroke(.{ .thickness = options.borderGet().x * rs, .color = dvui.themeGet().border });
    }
    return b;
}