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(""); 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; }