slider
returns true if fraction (0-1) was changed
Parameters
- src:std.builtin.SourceLocation
- std.builtin.SourceLocation
- init_opts:SliderInitOptions
- SliderInitOptions
- opts:Options
- Options
Source
Implementation
pub fn slider(src: std.builtin.SourceLocation, init_opts: SliderInitOptions, opts: Options) bool {
std.debug.assert(init_opts.fraction.* >= 0);
std.debug.assert(init_opts.fraction.* <= 1);
const options = slider_defaults.override(opts);
var b = box(src, .{ .dir = init_opts.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, switch (init_opts.dir) {
.vertical => AccessKit.Orientation.vertical,
.horizontal => AccessKit.Orientation.horizontal,
});
AccessKit.nodeSetNumericValue(ak_node, init_opts.fraction.*);
AccessKit.nodeSetMinNumericValue(ak_node, 0);
AccessKit.nodeSetMaxNumericValue(ak_node, 1);
AccessKit.nodeSetNumericValueStep(ak_node, 0.01);
AccessKit.nodeSetNumericValueJump(ak_node, 0.10);
}
tabIndexSet(b.data().id, options.tab_index, b.data().rectScale().r);
var hovered: bool = false;
var ret = false;
const br = b.data().contentRect();
const knobsize = @min(br.w, br.h);
const track = switch (init_opts.dir) {
.horizontal => Rect{ .x = knobsize / 2, .y = br.h / 2 - 2, .w = br.w - knobsize, .h = 4 },
.vertical => Rect{ .x = br.w / 2 - 2, .y = knobsize / 2, .w = 4, .h = br.h - knobsize },
};
const trackrs = b.widget().screenRectScale(track);
const rs = b.data().contentRectScale();
const evts = events();
for (evts) |*e| {
if (!eventMatch(e, .{ .id = b.data().id, .r = rs.r }))
continue;
switch (e.evt) {
.mouse => |me| {
var p: ?Point.Physical = null;
if (me.action == .focus) {
e.handle(@src(), b.data());
focusWidget(b.data().id, null, e.num);
} else if (me.action == .press and me.button.pointer()) {
// capture
captureMouse(b.data(), e.num);
e.handle(@src(), b.data());
p = me.p;
} else if (me.action == .release and me.button.pointer()) {
// stop capture
captureMouse(null, e.num);
dragEnd();
e.handle(@src(), b.data());
} else if (me.action == .motion and 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);
hovered = true;
}
if (p) |pp| {
var min: f32 = undefined;
var max: f32 = undefined;
switch (init_opts.dir) {
.horizontal => {
min = trackrs.r.x;
max = trackrs.r.x + trackrs.r.w;
},
.vertical => {
min = 0;
max = trackrs.r.h;
},
}
if (max > min) {
const v = if (init_opts.dir == .horizontal) pp.x else (trackrs.r.y + trackrs.r.h - pp.y);
init_opts.fraction.* = (v - min) / (max - min);
init_opts.fraction.* = @max(0, @min(1, init_opts.fraction.*));
ret = true;
}
}
},
.key => |ke| {
if (ke.action == .down or ke.action == .repeat) {
switch (ke.code) {
.left, .down => {
e.handle(@src(), b.data());
init_opts.fraction.* = @max(0, @min(1, init_opts.fraction.* - 0.05));
ret = true;
},
.right, .up => {
e.handle(@src(), b.data());
init_opts.fraction.* = @max(0, @min(1, init_opts.fraction.* + 0.05));
ret = true;
},
else => {},
}
}
},
.text => |te| {
switch (te.action) {
.value => |set| blk: {
e.handle(@src(), b.data());
const value: f32 = std.fmt.parseFloat(f32, set.txt) catch break :blk;
init_opts.fraction.* = std.math.clamp(value, 0.0, 1.0);
},
else => {},
}
},
else => {},
}
}
const perc = @max(0, @min(1, init_opts.fraction.*));
var part = trackrs.r;
switch (init_opts.dir) {
.horizontal => part.w *= perc,
.vertical => {
const h = part.h * (1 - perc);
part.y += h;
part.h = trackrs.r.h - h;
},
}
if (b.data().visible()) {
part.fill(options.cornersGet().scale(trackrs.s, CornerRect.Physical), .{ .color = init_opts.color_bar orelse dvui.themeGet().color(.highlight, .fill), .fade = 1.0 });
}
switch (init_opts.dir) {
.horizontal => {
part.x = part.x + part.w;
part.w = trackrs.r.w - part.w;
},
.vertical => {
part = trackrs.r;
part.h *= (1 - perc);
},
}
if (b.data().visible()) {
part.fill(options.cornersGet().scale(trackrs.s, CornerRect.Physical), .{ .color = options.color(.fill), .fade = 1.0 });
}
const knobRect = switch (init_opts.dir) {
.horizontal => Rect{ .x = (br.w - knobsize) * perc, .w = knobsize, .h = knobsize },
.vertical => Rect{ .y = (br.h - knobsize) * (1 - perc), .w = knobsize, .h = knobsize },
};
const fill_color: Color = if (captured(b.data().id))
options.color(.fill_press)
else if (hovered)
options.color(.fill_hover)
else
options.color(.fill);
var knob: BoxWidget = undefined;
knob.init(@src(), .{ .dir = .horizontal }, .{ .rect = knobRect, .padding = .{}, .margin = .{}, .background = true, .border = Rect.all(1), .corners = .all(100), .color_fill = fill_color });
knob.drawBackground();
if (b.data().id == focusedWidgetId()) {
knob.data().focusBorder();
}
knob.deinit();
if (ret) {
refresh(null, @src(), b.data().id);
}
return ret;
}