daq-view/src/components/view.zig

320 lines
9.7 KiB
Zig

const std = @import("std");
const App = @import("../app.zig");
const UI = @import("../ui.zig");
const srcery = @import("../srcery.zig");
const RangeF64 = @import("../range.zig").RangeF64;
const utils = @import("../utils.zig");
const NIDaq = @import("../ni-daq/root.zig");
const Assets = @import("../assets.zig");
const rl = @import("raylib");
const constants = @import("../constants.zig");
const Graph = @import("../graph.zig");
const UIViewRuler = @import("./view_ruler.zig");
const ViewControlsSystem = @import("./systems/view_controls.zig");
const Id = App.Id;
const remap = utils.remap;
const assert = std.debug.assert;
pub const ZoomStart = UIViewRuler.ZoomStart;
pub const Context = struct {
ui: *UI,
app: *App,
view_controls: *ViewControlsSystem,
};
pub const Result = struct {
box: *UI.Box,
};
fn createGraphBox(ctx: Context) *UI.Box {
var ui = ctx.ui;
return ui.createBox(.{
.key = ui.keyFromString("Graph"),
.size_x = UI.Sizing.initGrowFull(),
.size_y = UI.Sizing.initGrowFull(),
.background = srcery.black,
.flags = &.{ .clickable, .draggable, .scrollable, .clip_view },
.align_x = .center,
.align_y = .center,
.borders = .{
.bottom = .{ .color = srcery.hard_black, .size = 4 }
}
});
}
fn showGraph(ctx: Context, graph_box: *UI.Box, view_id: Id) void {
var ui = ctx.ui;
const app = ctx.app;
const view = app.getView(view_id).?;
const view_opts = &view.graph_opts;
graph_box.beginChildren();
defer graph_box.endChildren();
const graph_rect = graph_box.rect();
const signal = ui.signal(graph_box);
var sample_value_under_mouse: ?f64 = null;
var sample_index_under_mouse: ?f64 = null;
const mouse_x_range = RangeF64.init(0, graph_rect.width);
const mouse_y_range = RangeF64.init(0, graph_rect.height);
if (signal.hot) {
sample_index_under_mouse = mouse_x_range.remapTo(view_opts.x_range, signal.relative_mouse.x);
sample_value_under_mouse = mouse_y_range.remapTo(view_opts.y_range, signal.relative_mouse.y);
}
// if (ctx.view_controls.selected_tool == .move) {
if (signal.dragged()) {
const x_offset = mouse_x_range.remapTo(RangeF64.init(0, view_opts.x_range.size()), signal.drag.x);
const y_offset = mouse_y_range.remapTo(RangeF64.init(0, view_opts.y_range.size()), signal.drag.y);
ctx.view_controls.pushViewMove(
view_id,
view_opts.x_range.sub(x_offset),
view_opts.y_range.add(y_offset)
);
}
if (signal.scrolled() and sample_index_under_mouse != null and sample_value_under_mouse != null) {
var scale_factor: f64 = 1;
if (signal.scroll.y > 0) {
scale_factor -= constants.zoom_speed;
} else {
scale_factor += constants.zoom_speed;
}
ctx.view_controls.pushViewMove(
view_id,
view_opts.x_range.zoom(sample_index_under_mouse.?, scale_factor),
view_opts.y_range.zoom(sample_value_under_mouse.?, scale_factor)
);
}
if (signal.flags.contains(.middle_clicked)) {
ctx.view_controls.pushViewMove(view_id, view.available_x_range, view.available_y_range);
}
if (signal.flags.contains(.left_released)) {
ctx.view_controls.pushBreakpoint();
}
// } else if (ctx.view_controls.selected_tool == .select) {
// // TODO:
// } else if (ctx.view_controls.selected_tool == .marker) {
// // TODO:
// }
{ // Render graph
const sample_list = app.project.sample_lists.get(view.transformed_samples).?;
Graph.drawCached(&view.graph_cache, graph_box.persistent.size, view_opts.*, sample_list);
if (view.graph_cache.texture) |texture| {
graph_box.texture = texture.texture;
}
}
if (view_opts.x_range.size() == 0 or view_opts.y_range.size() == 0) {
graph_box.setText("<Empty>");
graph_box.text_color = srcery.hard_black;
graph_box.font = .{
.variant = .bold_italic,
.size = ui.rem(3)
};
}
}
fn showToolbar(ctx: Context, view_id: Id) void {
var ui = ctx.ui;
const toolbar = ui.createBox(.{
.layout_direction = .left_to_right,
.background = srcery.hard_black,
.size_x = UI.Sizing.initGrowFull(),
.size_y = UI.Sizing.initFixed(.{ .pixels = ui.rem(2) })
});
toolbar.beginChildren();
defer toolbar.endChildren();
const view = ctx.app.getView(view_id).?;
var view_name: ?[]const u8 = null;
{
const btn = ui.textButton("Settings");
btn.background = srcery.hard_black;
if (ctx.view_controls.isViewSettingsOpen(view_id)) {
btn.borders.bottom = .{
.color = srcery.green,
.size = 4
};
}
if (ui.signal(btn).clicked()) {
ctx.view_controls.toggleViewSettings(view_id);
}
}
switch (view.reference) {
.channel => |channel_id| {
const channel = ctx.app.getChannel(channel_id).?;
const channel_name = utils.getBoundedStringZ(&channel.name);
// const channel_type = NIDaq.getChannelType(channel_name).?;
{
const follow = ui.textButton("Follow");
follow.background = srcery.hard_black;
if (view.follow) {
follow.borders = UI.Borders.bottom(.{
.color = srcery.green,
.size = 4
});
}
if (ui.signal(follow).clicked()) {
view.follow = !view.follow;
}
}
// if (channel_type == .analog_output) {
// const button = ui.button(ui.keyFromString("Output generation"));
// button.texture = Assets.output_generation;
// button.size.y = UI.Sizing.initGrowFull();
// const signal = ui.signal(button);
// if (signal.clicked()) {
// if (ctx.app.isChannelOutputing(channel_id)) {
// ctx.app.pushCommand(.{
// .stop_output = channel_id
// });
// } else {
// ctx.view_controls.view_protocol_modal = view_id;
// }
// }
// var color = rl.Color.white;
// if (ctx.app.isChannelOutputing(channel_id)) {
// color = srcery.red;
// }
// if (signal.active) {
// button.texture_color = color.alpha(0.6);
// } else if (signal.hot) {
// button.texture_color = color.alpha(0.8);
// } else {
// button.texture_color = color;
// }
// }
view_name = channel_name;
},
.file => |file_id| {
const file = ctx.app.getFile(file_id).?;
view_name = std.fs.path.stem(file.path);
},
.mirror => |sample_list_id| {
_ = sample_list_id;
view_name = "Proportional";
}
}
if (view.name.len > 0) {
view_name = view.name.constSlice();
}
if (view_name) |text| {
_ = ui.createBox(.{
.size_x = UI.Sizing.initGrowFull()
});
_ = ui.createBox(.{
.size_x = UI.Sizing.initFixedPixels(ui.rem(1))
});
const label = ui.label("{s}", .{text});
label.size.y = UI.Sizing.initGrowFull();
label.alignment.x = .center;
label.alignment.y = .center;
// TODO:
// label.padding = UI.Padding.horizontal(ui.rem(1));
_ = ui.createBox(.{
.size_x = UI.Sizing.initFixedPixels(ui.rem(1))
});
}
}
pub fn show(ctx: Context, view_id: Id, height: UI.Sizing) !Result {
var ui = ctx.ui;
const view_box = ui.createBox(.{
.key = UI.Key.initUsize(view_id.asInt()),
.layout_direction = .top_to_bottom,
.size_x = UI.Sizing.initGrowFull(),
.size_y = height,
});
view_box.beginChildren();
defer view_box.endChildren();
const result = Result{
.box = view_box
};
showToolbar(ctx, view_id);
const ruler_ctx = UIViewRuler.Context{
.ui = ctx.ui,
.project = &ctx.app.project,
.view_controls = ctx.view_controls
};
var graph_box: *UI.Box = undefined;
var x_ruler: *UI.Box = undefined;
var y_ruler: *UI.Box = undefined;
{
const container = ui.createBox(.{
.layout_direction = .left_to_right,
.size_x = UI.Sizing.initGrowFull(),
.size_y = UI.Sizing.initGrowFull(),
});
container.beginChildren();
defer container.endChildren();
y_ruler = UIViewRuler.createBox(ruler_ctx, ui.keyFromString("Y ruler"), .Y);
graph_box = createGraphBox(ctx);
}
{
const container = ui.createBox(.{
.layout_direction = .left_to_right,
.size_x = UI.Sizing.initGrowFull(),
.size_y = UIViewRuler.ruler_size_y,
});
container.beginChildren();
defer container.endChildren();
_ = ui.createBox(.{
.size_x = UIViewRuler.ruler_size_x,
.size_y = UIViewRuler.ruler_size_y,
.background = srcery.hard_black,
});
x_ruler = UIViewRuler.createBox(ruler_ctx, ui.keyFromString("X ruler"), .X);
}
try UIViewRuler.show(ruler_ctx, x_ruler, graph_box, view_id, .X);
try UIViewRuler.show(ruler_ctx, y_ruler, graph_box, view_id, .Y);
showGraph(ctx, graph_box, view_id);
return result;
}