DVUI

hueSlider

Returns true if the hue was changed

hue >= 0 and hue < 360

Parameters

#
src:std.builtin.SourceLocation
std.builtin.SourceLocation
dir:dvui.enums.Direction
dvui.enums.Direction
hue:*f32
*f32
opts:Options
Options

Source

Implementation

#
pub fn hueSlider(src: std.builtin.SourceLocation, dir: dvui.enums.Direction, hue: *f32, opts: Options) bool {
    var fraction = std.math.clamp(hue.* / 360, 0, 1);
    std.debug.assert(fraction >= 0);
    std.debug.assert(fraction <= 1);

    const options = hue_slider_defaults.override(opts);

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

    if (b.data().accesskit_node()) |ak_node| {
        AccessKit.nodeAddAction(ak_node, AccessKit.Action.focus);
        AccessKit.nodeAddAction(ak_node, AccessKit.Action.set_value);
        AccessKit.nodeSetOrientation(ak_node, AccessKit.Orientation.horizontal);
        AccessKit.nodeSetMinNumericValue(ak_node, 0);
        AccessKit.nodeSetMaxNumericValue(ak_node, 359);
        AccessKit.nodeSetNumericValue(ak_node, hue.*);
    }

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

    const br = b.data().contentRect();
    const knobsize: dvui.Size = switch (dir) {
        .horizontal => .{ .w = 10, .h = br.h },
        .vertical => .{ .w = br.w, .h = 10 },
    };
    const track_thickness = 10;
    const track: dvui.Rect = switch (dir) {
        .horizontal => .{ .x = 0, .y = br.h / 2 - track_thickness / 2, .w = br.w, .h = track_thickness },
        .vertical => .{ .x = br.w / 2 - track_thickness / 2, .y = 0, .w = track_thickness, .h = br.h },
    };

    const trackrs = b.widget().screenRectScale(track);

    var ret = false;
    const rect = b.data().contentRect().justSize().outset(if (dir == .vertical) .{ .y = 5, .h = 5 } else .{ .x = 5, .w = 5 });
    const rs = b.widget().screenRectScale(rect);
    const evts = dvui.events();
    for (evts) |*e| {
        if (!dvui.eventMatch(e, .{ .id = b.data().id, .r = 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| {
                    const val = switch (dir) {
                        .horizontal => (pp.x - trackrs.r.x) / trackrs.r.w,
                        .vertical => (pp.y - trackrs.r.y) / trackrs.r.h,
                    };
                    fraction = std.math.clamp(val, 0, 1);
                    ret = true;
                }
            },
            .key => |ke| {
                if (ke.action == .down or ke.action == .repeat) {
                    switch (ke.code) {
                        .left, .up => {
                            e.handle(@src(), b.data());
                            fraction = std.math.clamp(fraction - 0.05, 0, 1);
                            ret = true;
                        },
                        .right, .down => {
                            e.handle(@src(), b.data());
                            fraction = std.math.clamp(fraction + 0.05, 0, 1);
                            ret = true;
                        },
                        else => {},
                    }
                }
            },
            .text => |te| {
                switch (te.action) {
                    .value => |set| {
                        const new_hue = std.fmt.parseFloat(f32, set.txt) catch hue.*;
                        if (new_hue >= 0 and new_hue < 360) {
                            hue.* = new_hue;
                        }
                    },
                    else => {},
                }
            },
            else => {},
        }
    }

    if (ret) {
        hue.* = fraction * 359.99;
        dvui.refresh(null, @src(), b.data().id);
    }

    const uv_offset = comptime 0.5 / @as(f32, @floatFromInt(hue_selector_colors.len));
    const texture = getHueSelectorTexture(dir) catch |err| blk: {
        dvui.logError(@src(), err, "Could not get hue selector texture", .{});
        break :blk null;
    };
    if (texture) |tex| {
        dvui.renderTexture(tex, trackrs, .{
            .corners = options.cornersGet(),
            .uv = .{
                .x = uv_offset,
                .y = if (dir == .vertical) uv_offset else 1 - uv_offset,
                .w = 1 - uv_offset,
                .h = if (dir == .horizontal) uv_offset else 1 - uv_offset,
            },
        }) catch |err| {
            dvui.logError(@src(), err, "Could not render hue selector texture", .{});
        };
    }

    const knobRect = dvui.Rect.fromPoint(switch (dir) {
        .horizontal => .{ .x = (br.w * fraction) - knobsize.w / 2 },
        .vertical => .{ .y = (br.h * fraction) - knobsize.h / 2 },
    }).toSize(knobsize);

    var knob = dvui.box(@src(), .{ .dir = .horizontal }, .{
        .rect = knobRect,
        .padding = .{},
        .margin = .{},
        .background = true,
        .border = .all(1),
        .corners = .all(100),
        .color_fill = (Color.HSV{ .h = hue.* }).toColor(),
    });
    if (b.data().id == dvui.focusedWidgetId()) {
        knob.data().focusBorder();
    }
    knob.deinit();

    return ret;
}