DVUI

valueSaturationBox

Returns true if the color was changed

Parameters

#
src:std.builtin.SourceLocation
std.builtin.SourceLocation
hsv:*Color.HSV
*Color.HSV
opts:Options
Options

Source

Implementation

#
pub fn valueSaturationBox(src: std.builtin.SourceLocation, hsv: *Color.HSV, opts: Options) bool {
    const options = value_saturation_box_defaults.override(opts);

    var b = dvui.box(src, .{ .dir = .horizontal }, options);
    defer b.deinit();

    // Accessibility TODO: Needs 2d-slider support from AccessKit. It's not possible to attach
    // a horizontal and vertical value to a single widget.

    dvui.tabIndexSet(b.data().id, options.tab_index, b.data().rectScale().r);

    const rs = b.data().contentRectScale();

    const texture = getValueSaturationTexture(hsv.h) catch |err| blk: {
        dvui.logError(@src(), err, "Could not get value saturation texture", .{});
        break :blk null;
    };
    if (texture) |tex| {
        dvui.renderTexture(tex, rs, .{
            .corners = options.cornersGet(),
            .uv = .{ .x = 0.25, .y = 0.25, .w = 0.5, .h = 0.5 },
        }) catch |err| {
            dvui.logError(@src(), err, "Could not render value saturation texture", .{});
        };
    }

    const mouse_rect = b.data().contentRect().justSize().outsetAll(5);
    const mouse_rs = b.widget().screenRectScale(mouse_rect);

    var changed = false;
    const evts = dvui.events();
    for (evts) |*e| {
        if (!dvui.eventMatch(e, .{ .id = b.data().id, .r = mouse_rs.r }))
            continue;

        switch (e.evt) {
            .mouse => |me| {
                var p: ?dvui.Point.Physical = null;
                if (me.action == .focus) {
                    e.handle(@src(), b.data());
                    dvui.focusWidget(b.data().id, null, e.num);
                } else if (me.action == .press and me.button.pointer()) {
                    // capture
                    dvui.captureMouse(b.data(), e.num);
                    e.handle(@src(), b.data());
                    p = me.p;
                } else if (me.action == .release and me.button.pointer()) {
                    // stop capture
                    dvui.captureMouse(null, e.num);
                    dvui.dragEnd();
                    e.handle(@src(), b.data());
                } else if (me.action == .motion and dvui.captured(b.data().id)) {
                    // handle only if we have capture
                    e.handle(@src(), b.data());
                    p = me.p;
                } else if (me.action == .position) {
                    dvui.cursorSet(.arrow);
                }

                if (p) |pp| {
                    hsv.s = std.math.clamp((pp.x - rs.r.x) / rs.r.w, 0, 1);
                    hsv.v = std.math.clamp(1 - (pp.y - rs.r.y) / rs.r.h, 0, 1);
                    changed = true;
                }
            },
            .key => |ke| {
                if (ke.action == .down or ke.action == .repeat) {
                    switch (ke.code) {
                        .left => {
                            e.handle(@src(), b.data());
                            hsv.s = std.math.clamp(hsv.s - 0.05, 0, 1);
                            changed = true;
                        },
                        .right => {
                            e.handle(@src(), b.data());
                            hsv.s = std.math.clamp(hsv.s + 0.05, 0, 1);
                            changed = true;
                        },
                        .up => {
                            e.handle(@src(), b.data());
                            // hsv.v is inverted, up is positive
                            hsv.v = std.math.clamp(hsv.v + 0.05, 0, 1);
                            changed = true;
                        },
                        .down => {
                            e.handle(@src(), b.data());
                            // hsv.v is inverted, down is negative
                            hsv.v = std.math.clamp(hsv.v - 0.05, 0, 1);
                            changed = true;
                        },
                        else => {},
                    }
                }
            },
            else => {},
        }
    }

    const br = b.data().contentRect();
    const current_point: dvui.Point = .{ .x = br.w * hsv.s, .y = br.h * (1 - hsv.v) };

    var indicator = dvui.box(@src(), .{ .dir = .horizontal }, .{
        .rect = dvui.Rect.fromPoint(current_point).toSize(.all(10)).offsetNeg(.all(5)),
        .padding = .{},
        .margin = .{},
        .background = true,
        .border = .all(1),
        .corners = .all(100),
        .color_fill = hsv.toColor(),
    });
    if (b.data().id == dvui.focusedWidgetId()) {
        indicator.data().focusBorder();
    }
    indicator.deinit();

    return changed;
}