Compare commits
10 Commits
fdf6001068
...
be0d0f6bb1
Author | SHA1 | Date | |
---|---|---|---|
be0d0f6bb1 | |||
aa63dab79a | |||
d49dc387fb | |||
0c237eb053 | |||
6adf0b9423 | |||
0edeaefbf0 | |||
eafafbab89 | |||
1428d3d9bf | |||
7de55bde54 | |||
ecea2b513a |
@ -19,6 +19,10 @@
|
||||
.url = "git+https://github.com/lassade/profiler.zig.git#d066d066c36c4eebd494babf15c1cdbd2d512b12",
|
||||
.hash = "122097461acc2064f5f89b85d76d2a02232579864b17604617a333789c892f2d262f",
|
||||
},
|
||||
.@"zig-datetime" = .{
|
||||
.url = "git+https://github.com/frmdstryr/zig-datetime.git#70aebf28fb3e137cd84123a9349d157a74708721",
|
||||
.hash = "122077215ce36e125a490e59ec1748ffd4f6ba00d4d14f7308978e5360711d72d77f",
|
||||
},
|
||||
},
|
||||
|
||||
.paths = .{
|
||||
|
854
src/app.zig
854
src/app.zig
File diff suppressed because it is too large
Load Diff
@ -31,21 +31,15 @@ pub const ViewAxisPosition = struct {
|
||||
}
|
||||
|
||||
fn get(optional_self: *?ViewAxisPosition, project: *App.Project, view_id: Id, axis: UI.Axis) ?f64 {
|
||||
_ = project;
|
||||
|
||||
const self = optional_self.* orelse return null;
|
||||
|
||||
if (self.axis != axis) return null;
|
||||
|
||||
const view = project.views.get(view_id) orelse return null;
|
||||
if (view.sync_controls) {
|
||||
const owner_view = project.views.get(self.view_id).?;
|
||||
if (!owner_view.sync_controls) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
if (!self.view_id.eql(view_id)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return self.position;
|
||||
}
|
||||
@ -159,6 +153,7 @@ const MarkedRangeIterator = struct {
|
||||
}
|
||||
};
|
||||
|
||||
app: *App,
|
||||
project: *App.Project,
|
||||
|
||||
// TODO: Redo
|
||||
@ -166,21 +161,13 @@ undo_stack: CommandFrameArray = .{},
|
||||
last_applied_command: usize = 0,
|
||||
zoom_start: ?ViewAxisPosition = null,
|
||||
cursor: ?ViewAxisPosition = null,
|
||||
view_settings: ?Id = null, // View id
|
||||
view_protocol_modal: ?Id = null, // View id
|
||||
selected_tool: enum { move, select, marker } = .move,
|
||||
show_marked_range: ?struct {
|
||||
view_id: Id,
|
||||
index: usize,
|
||||
} = null,
|
||||
show_marker: ?struct {
|
||||
view_id: Id,
|
||||
index: usize
|
||||
} = null,
|
||||
// view_protocol_modal: ?Id = null, // View id
|
||||
// selected_tool: enum { move, select, marker } = .move,
|
||||
|
||||
pub fn init(project: *App.Project) System {
|
||||
pub fn init(app: *App) System {
|
||||
return System{
|
||||
.project = project
|
||||
.app = app,
|
||||
.project = &app.project
|
||||
};
|
||||
}
|
||||
|
||||
@ -242,39 +229,21 @@ pub fn pushViewMove(self: *System, view_id: Id, x_range: RangeF64, y_range: Rang
|
||||
}
|
||||
}
|
||||
|
||||
var sync_controls = false;
|
||||
if (self.project.views.get(view_id)) |view| {
|
||||
sync_controls = view.sync_controls;
|
||||
}
|
||||
|
||||
var view_ids: std.BoundedArray(Id, constants.max_views) = .{};
|
||||
if (sync_controls) {
|
||||
var iter = self.project.views.idIterator();
|
||||
while (iter.next()) |id| {
|
||||
if (self.project.views.get(id).?.sync_controls) {
|
||||
view_ids.appendAssumeCapacity(id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
view_ids.appendAssumeCapacity(view_id);
|
||||
}
|
||||
|
||||
for (view_ids.constSlice()) |id| {
|
||||
var command: *Command = undefined;
|
||||
if (frame.findCommandByView(id)) |prev_command| {
|
||||
if (frame.findCommandByView(view_id)) |prev_command| {
|
||||
command = prev_command;
|
||||
|
||||
command.move_and_zoom.x = x_range;
|
||||
command.move_and_zoom.y = y_range;
|
||||
} else {
|
||||
const view = self.project.views.get(view_id) orelse continue;
|
||||
if (self.project.views.get(view_id)) |view| {
|
||||
const view_rect = &view.graph_opts;
|
||||
|
||||
command = frame.commands.addOneAssumeCapacity();
|
||||
|
||||
command.* = Command{
|
||||
.move_and_zoom = .{
|
||||
.view_id = id,
|
||||
.view_id = view_id,
|
||||
.before_x = view_rect.x_range,
|
||||
.before_y = view_rect.y_range,
|
||||
.x = x_range,
|
||||
@ -283,7 +252,6 @@ pub fn pushViewMove(self: *System, view_id: Id, x_range: RangeF64, y_range: Rang
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn pushViewMoveAxis(self: *System, view_id: Id, axis: UI.Axis, view_range: RangeF64) void {
|
||||
@ -318,14 +286,15 @@ pub fn applyCommands(self: *System) void {
|
||||
|
||||
pub fn toggleViewSettings(self: *System, view_id: Id) void {
|
||||
if (self.isViewSettingsOpen(view_id)) {
|
||||
self.view_settings = null;
|
||||
self.app.main_screen.side_panel = .project;
|
||||
} else {
|
||||
self.view_settings = view_id;
|
||||
self.app.main_screen.side_panel = .{ .view_settings = view_id };
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isViewSettingsOpen(self: *System, view_id: Id) bool {
|
||||
return self.view_settings != null and self.view_settings.?.eql(view_id);
|
||||
const side_panel = self.app.main_screen.side_panel;
|
||||
return side_panel == .view_settings and side_panel.view_settings.eql(view_id);
|
||||
}
|
||||
|
||||
pub fn setCursor(self: *System, view_id: Id, axis: UI.Axis, position: ?f64) void {
|
||||
@ -345,29 +314,36 @@ pub fn getCursorHoldStart(self: *System, view_id: Id, axis: UI.Axis) ?f64 {
|
||||
}
|
||||
|
||||
pub fn toggleShownMarkedRange(self: *System, view_id: Id, index: usize) void {
|
||||
if (self.show_marked_range) |show_marked_range| {
|
||||
const side_panel = &self.app.main_screen.side_panel;
|
||||
|
||||
if (side_panel.* == .marked_range) {
|
||||
const show_marked_range = side_panel.marked_range;
|
||||
if (show_marked_range.view_id.eql(view_id) and show_marked_range.index == index) {
|
||||
self.show_marked_range = null;
|
||||
side_panel.* = .project;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.show_marked_range = .{
|
||||
.view_id = view_id,
|
||||
.index = index,
|
||||
side_panel.* = .{
|
||||
.marked_range = .{ .index = index, .view_id = view_id }
|
||||
};
|
||||
}
|
||||
|
||||
pub fn toggleShownMarker(self: *System, view_id: Id, index: usize) void {
|
||||
if (self.show_marker) |show_marker| {
|
||||
const side_panel = &self.app.main_screen.side_panel;
|
||||
|
||||
if (side_panel.* == .marker) {
|
||||
const show_marker = side_panel.marker;
|
||||
if (show_marker.view_id.eql(view_id) and show_marker.index == index) {
|
||||
self.show_marker = null;
|
||||
side_panel.* = .project;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.show_marker = .{
|
||||
side_panel.* = .{
|
||||
.marker = .{
|
||||
.view_id = view_id,
|
||||
.index = index,
|
||||
}
|
||||
};
|
||||
}
|
@ -17,8 +17,6 @@ const Id = App.Id;
|
||||
const remap = utils.remap;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const ruler_size = UI.Sizing.initFixed(.{ .pixels = 32 });
|
||||
|
||||
pub const ZoomStart = UIViewRuler.ZoomStart;
|
||||
|
||||
pub const Context = struct {
|
||||
@ -75,7 +73,7 @@ fn showGraph(ctx: Context, graph_box: *UI.Box, view_id: Id) void {
|
||||
sample_value_under_mouse = mouse_y_range.remapTo(view_opts.y_range, signal.relative_mouse.y);
|
||||
}
|
||||
|
||||
if (ctx.view_controls.selected_tool == .move) {
|
||||
// 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);
|
||||
@ -109,21 +107,16 @@ fn showGraph(ctx: Context, graph_box: *UI.Box, view_id: Id) void {
|
||||
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 == .select) {
|
||||
// // TODO:
|
||||
|
||||
} else if (ctx.view_controls.selected_tool == .marker) {
|
||||
// TODO:
|
||||
}
|
||||
// } else if (ctx.view_controls.selected_tool == .marker) {
|
||||
// // TODO:
|
||||
// }
|
||||
|
||||
|
||||
{ // Render graph
|
||||
var sample_list_id = app.project.getViewSampleListId(view_id);
|
||||
if (view.transformed_samples) |transformed_samples_id| {
|
||||
sample_list_id = transformed_samples_id;
|
||||
}
|
||||
|
||||
const sample_list = app.project.sample_lists.get(sample_list_id).?;
|
||||
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;
|
||||
@ -170,19 +163,11 @@ fn showToolbar(ctx: Context, view_id: Id) void {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const btn = ui.textButton("Reset view");
|
||||
btn.background = srcery.hard_black;
|
||||
if (ui.signal(btn).clicked()) {
|
||||
ctx.view_controls.pushViewMove(view_id, view.available_x_range, view.available_y_range);
|
||||
}
|
||||
}
|
||||
|
||||
if (view.reference == .channel) {
|
||||
const channel_id = view.reference.channel;
|
||||
const channel = ctx.app.getChannel(channel_id).?;
|
||||
const channel_name = utils.getBoundedStringZ(&channel.name);
|
||||
const channel_type = NIDaq.getChannelType(channel_name).?;
|
||||
// const channel_type = NIDaq.getChannelType(channel_name).?;
|
||||
|
||||
{
|
||||
const follow = ui.textButton("Follow");
|
||||
@ -198,35 +183,35 @@ fn showToolbar(ctx: Context, view_id: Id) void {
|
||||
}
|
||||
}
|
||||
|
||||
if (channel_type == .analog_output) {
|
||||
const button = ui.button(ui.keyFromString("Output generation"));
|
||||
button.texture = Assets.output_generation;
|
||||
button.size.y = UI.Sizing.initGrowFull();
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
} else if (view.reference == .file) {
|
||||
@ -236,14 +221,8 @@ fn showToolbar(ctx: Context, view_id: Id) void {
|
||||
view_name = std.fs.path.stem(file.path);
|
||||
}
|
||||
|
||||
if (view.sync_controls) {
|
||||
const btn = ui.button(ui.keyFromString("Disable sync"));
|
||||
btn.texture = Assets.cross;
|
||||
btn.size.y = UI.Sizing.initGrowFull();
|
||||
btn.tooltip = "Disable sync controls";
|
||||
if (ui.signal(btn).clicked()) {
|
||||
view.sync_controls = false;
|
||||
}
|
||||
if (view.name.len > 0) {
|
||||
view_name = view.name.constSlice();
|
||||
}
|
||||
|
||||
if (view_name) |text| {
|
||||
@ -314,14 +293,14 @@ pub fn show(ctx: Context, view_id: Id, height: UI.Sizing) !Result {
|
||||
const container = ui.createBox(.{
|
||||
.layout_direction = .left_to_right,
|
||||
.size_x = UI.Sizing.initGrowFull(),
|
||||
.size_y = ruler_size,
|
||||
.size_y = UIViewRuler.ruler_size_y,
|
||||
});
|
||||
container.beginChildren();
|
||||
defer container.endChildren();
|
||||
|
||||
_ = ui.createBox(.{
|
||||
.size_x = ruler_size,
|
||||
.size_y = ruler_size,
|
||||
.size_x = UIViewRuler.ruler_size_x,
|
||||
.size_y = UIViewRuler.ruler_size_y,
|
||||
.background = srcery.hard_black,
|
||||
});
|
||||
|
||||
|
@ -6,6 +6,7 @@ const RangeF64 = @import("../range.zig").RangeF64;
|
||||
const srcery = @import("../srcery.zig");
|
||||
const utils = @import("../utils.zig");
|
||||
const constants = @import("../constants.zig");
|
||||
const Assets = @import("../assets.zig");
|
||||
|
||||
const ViewControlsSystem = @import("./systems/view_controls.zig");
|
||||
|
||||
@ -13,7 +14,8 @@ const Id = App.Id;
|
||||
const remap = utils.remap;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const ruler_size = UI.Sizing.initFixed(.{ .pixels = 32 });
|
||||
pub const ruler_size_x = UI.Sizing.initFixed(.{ .pixels = 32*3 });
|
||||
pub const ruler_size_y = UI.Sizing.initFixed(.{ .pixels = 32 });
|
||||
|
||||
const Ruler = struct {
|
||||
project: *App.Project,
|
||||
@ -57,11 +59,13 @@ const DrawContext = struct {
|
||||
available_range: RangeF64,
|
||||
axis: UI.Axis,
|
||||
rect: rl.Rectangle = .{ .x = 0, .y = 0, .width = 0, .height = 0 },
|
||||
project: *App.Project,
|
||||
|
||||
fn init(axis: UI.Axis, project: *App.Project, view_id: Id) DrawContext {
|
||||
const view = project.views.get(view_id).?;
|
||||
|
||||
return DrawContext{
|
||||
.project = project,
|
||||
.one_unit = switch (axis) {
|
||||
.X => project.getSampleRate(),
|
||||
.Y => 1
|
||||
@ -159,33 +163,43 @@ pub fn createBox(ctx: Context, key: UI.Key, axis: UI.Axis) *UI.Box {
|
||||
});
|
||||
if (axis == .X) {
|
||||
ruler.size.x = UI.Sizing.initGrowFull();
|
||||
ruler.size.y = ruler_size;
|
||||
ruler.size.y = ruler_size_y;
|
||||
} else {
|
||||
ruler.size.x = ruler_size;
|
||||
ruler.size.x = ruler_size_x;
|
||||
ruler.size.y = UI.Sizing.initGrowFull();
|
||||
}
|
||||
|
||||
return ruler;
|
||||
}
|
||||
|
||||
fn drawRulerTicks(_ctx: ?*anyopaque, box: *UI.Box) void {
|
||||
fn formatAxisLabel(allocator: std.mem.Allocator, project: *App.Project, axis: UI.Axis, position: f64) ![]u8 {
|
||||
const sample_rate = project.sample_rate;
|
||||
if (axis == .X and sample_rate != null) {
|
||||
const seconds = position / sample_rate.?;
|
||||
return try utils.formatDuration(allocator, seconds, false);
|
||||
} else {
|
||||
return try std.fmt.allocPrint(allocator, "{d:.3}", .{ position });
|
||||
}
|
||||
}
|
||||
|
||||
fn drawRulerTicks(_ctx: ?*anyopaque, ui: *UI, box: *UI.Box) void {
|
||||
const ctx: *DrawContext = @ptrCast(@alignCast(_ctx));
|
||||
ctx.rect = box.rect();
|
||||
|
||||
ctx.drawLine(ctx.available_range.lower, 1, srcery.yellow);
|
||||
ctx.drawLine(ctx.available_range.upper, 1, srcery.yellow);
|
||||
|
||||
if (ctx.available_range.hasExclusive(0)) {
|
||||
ctx.drawLine(0, 0.75, srcery.yellow);
|
||||
}
|
||||
// if (ctx.available_range.hasExclusive(0)) {
|
||||
// ctx.drawLine(0, 0.75, srcery.yellow);
|
||||
// }
|
||||
|
||||
var one_unit = ctx.one_unit orelse return;
|
||||
var one_unit = ctx.one_unit orelse 5000;
|
||||
|
||||
while (ctx.render_range.size() < 5*one_unit) {
|
||||
while (ctx.render_range.size() < 2*one_unit) {
|
||||
one_unit /= 2;
|
||||
}
|
||||
|
||||
while (ctx.render_range.size() > 50*one_unit) {
|
||||
while (ctx.render_range.size() > 10*one_unit) {
|
||||
one_unit *= 2;
|
||||
}
|
||||
|
||||
@ -194,6 +208,18 @@ fn drawRulerTicks(_ctx: ?*anyopaque, box: *UI.Box) void {
|
||||
|
||||
ctx.drawTicks(ticks_range.lower, ticks_range.upper, one_unit, 0.5, srcery.yellow);
|
||||
ctx.drawTicks(ticks_range.lower + one_unit/2, ticks_range.upper, one_unit, 0.25, srcery.yellow);
|
||||
|
||||
const font = Assets.font(box.font);
|
||||
|
||||
const allocator = ui.frameAllocator();
|
||||
|
||||
{
|
||||
var position = ticks_range.lower + one_unit;
|
||||
while (position < ticks_range.upper - one_unit) : (position += one_unit) {
|
||||
const text = formatAxisLabel(allocator, ctx.project, ctx.axis, position) catch continue;
|
||||
font.drawTextCenter(text, ctx.getPoint(position, 0.8), srcery.bright_white);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn showMouseTooltip(ctx: Context, axis: UI.Axis, view_id: Id, position: f64) void {
|
||||
@ -214,7 +240,7 @@ fn showMouseTooltip(ctx: Context, axis: UI.Axis, view_id: Id, position: f64) voi
|
||||
} else if (axis == .X and sample_rate != null) {
|
||||
const seconds = position / sample_rate.?;
|
||||
const frame_allocator = ui.frameAllocator();
|
||||
_ = ui.label("{s}", .{ utils.formatDuration(frame_allocator, seconds) catch "-" });
|
||||
_ = ui.label("{s}", .{ utils.formatDuration(frame_allocator, seconds, true) catch "-" });
|
||||
|
||||
} else {
|
||||
_ = ui.label("{d:.3}", .{position});
|
||||
@ -303,54 +329,87 @@ pub fn show(ctx: Context, box: *UI.Box, graph_box: *UI.Box, view_id: Id, axis: U
|
||||
_ = showMarkerRect(ui, ruler, RangeF64.init(hold_start.?, cursor.?), marker_color.alpha(0.5), null);
|
||||
}
|
||||
|
||||
{
|
||||
var selected_range_iter = view.iterMarkedRanges(axis);
|
||||
while (selected_range_iter.next()) |selected_range| {
|
||||
var color = srcery.blue;
|
||||
const index = selected_range_iter.index;
|
||||
// {
|
||||
// var selected_range_iter = view.iterMarkedRanges(axis);
|
||||
// while (selected_range_iter.next()) |selected_range| {
|
||||
// var color = srcery.blue;
|
||||
// const index = selected_range_iter.index;
|
||||
|
||||
if (ctx.view_controls.show_marked_range) |show_marked_range| {
|
||||
if (show_marked_range.view_id.eql(view_id) and show_marked_range.index == index) {
|
||||
if (@mod(rl.getTime(), 0.5) < 0.25) {
|
||||
color = utils.shiftColorInHSV(color, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
// const side_panel = ctx.view_controls.app.main_screen.side_panel;
|
||||
// if (side_panel == .marked_range) {
|
||||
// const show_marked_range = side_panel.marked_range;
|
||||
// if (show_marked_range.view_id.eql(view_id) and show_marked_range.index == index) {
|
||||
// if (@mod(rl.getTime(), 0.5) < 0.25) {
|
||||
// color = utils.shiftColorInHSV(color, 0.8);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
showMarkerLine(ui, ruler, selected_range.lower, color);
|
||||
showMarkerLine(ui, ruler, selected_range.upper, color);
|
||||
// showMarkerLine(ui, ruler, selected_range.lower, color);
|
||||
// showMarkerLine(ui, ruler, selected_range.upper, color);
|
||||
|
||||
var hasher = UI.Key.CombineHasher.init();
|
||||
hasher.update(std.mem.asBytes("Marked ranges"));
|
||||
hasher.update(std.mem.asBytes(&view_id));
|
||||
hasher.update(std.mem.asBytes(&axis));
|
||||
hasher.update(std.mem.asBytes(&index));
|
||||
const range_box_key = UI.Key.init(hasher.final());
|
||||
// var hasher = UI.Key.CombineHasher.init();
|
||||
// hasher.update(std.mem.asBytes("Marked ranges"));
|
||||
// hasher.update(std.mem.asBytes(&view_id));
|
||||
// hasher.update(std.mem.asBytes(&axis));
|
||||
// hasher.update(std.mem.asBytes(&index));
|
||||
// const range_box_key = UI.Key.init(hasher.final());
|
||||
|
||||
var range_box = showMarkerRect(ui, ruler, selected_range, color.alpha(0.5), range_box_key);
|
||||
range_box.flags.insert(.clickable);
|
||||
range_box.flags.insert(.draw_hot);
|
||||
range_box.flags.insert(.draw_active);
|
||||
// var range_box = showMarkerRect(ui, ruler, selected_range, color.alpha(0.5), range_box_key);
|
||||
// range_box.flags.insert(.clickable);
|
||||
// range_box.flags.insert(.draw_hot);
|
||||
// range_box.flags.insert(.draw_active);
|
||||
|
||||
range_box.hot_cursor = .mouse_cursor_pointing_hand;
|
||||
if (ctx.view_controls.selected_tool == .select) {
|
||||
const signal = ui.signal(range_box);
|
||||
if (signal.clicked()) {
|
||||
ctx.view_controls.toggleShownMarkedRange(view_id, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// range_box.hot_cursor = .mouse_cursor_pointing_hand;
|
||||
// if (ctx.view_controls.selected_tool == .select) {
|
||||
// const signal = ui.signal(range_box);
|
||||
// if (signal.clicked()) {
|
||||
// ctx.view_controls.toggleShownMarkedRange(view_id, index);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if (axis == .X) {
|
||||
for (0.., view.markers.constSlice()) |i, marker| {
|
||||
const color = srcery.cyan;
|
||||
// for (0.., view.markers.constSlice()) |i, marker| {
|
||||
// const color = srcery.cyan;
|
||||
|
||||
showMarkerLine(ui, ruler, marker, color);
|
||||
showMarkerLine(ui, ruler, marker, color);
|
||||
// showMarkerLine(ui, ruler, marker, color);
|
||||
// showMarkerLine(ui, ruler, marker, color);
|
||||
|
||||
// var hasher = UI.Key.CombineHasher.init();
|
||||
// hasher.update(std.mem.asBytes("Markers"));
|
||||
// hasher.update(std.mem.asBytes(&view_id));
|
||||
// hasher.update(std.mem.asBytes(&axis));
|
||||
// hasher.update(std.mem.asBytes(&i));
|
||||
|
||||
// const view_size = view.graph_opts.x_range.size();
|
||||
// const clickable_width = view_size * 0.01;
|
||||
|
||||
// const clickable = ui.createBox(.{
|
||||
// .key = UI.Key.init(hasher.final()),
|
||||
// .float_rect = ruler.getGraphDrawContext().getRect(marker - clickable_width/2, clickable_width, 0, 1),
|
||||
// .float_relative_to = ruler.graph_box,
|
||||
// .parent = ruler.graph_box,
|
||||
// .flags = &.{ .draw_hot, .draw_active, .clickable },
|
||||
// .hot_cursor = .mouse_cursor_pointing_hand,
|
||||
// });
|
||||
|
||||
// if (ui.signal(clickable).clicked()) {
|
||||
// ctx.view_controls.toggleShownMarker(view_id, i);
|
||||
// }
|
||||
// }
|
||||
|
||||
for (0.., project.gain_changes.slice()) |i, *gain_change| {
|
||||
const color = srcery.bright_orange;
|
||||
|
||||
const sample = gain_change.sample;
|
||||
|
||||
showMarkerLine(ui, ruler, sample, color);
|
||||
showMarkerLine(ui, ruler, sample, color);
|
||||
|
||||
var hasher = UI.Key.CombineHasher.init();
|
||||
hasher.update(std.mem.asBytes("Markers"));
|
||||
hasher.update(std.mem.asBytes("Gain change"));
|
||||
hasher.update(std.mem.asBytes(&view_id));
|
||||
hasher.update(std.mem.asBytes(&axis));
|
||||
hasher.update(std.mem.asBytes(&i));
|
||||
@ -360,21 +419,77 @@ pub fn show(ctx: Context, box: *UI.Box, graph_box: *UI.Box, view_id: Id, axis: U
|
||||
|
||||
const clickable = ui.createBox(.{
|
||||
.key = UI.Key.init(hasher.final()),
|
||||
.float_rect = ruler.getGraphDrawContext().getRect(marker - clickable_width/2, clickable_width, 0, 1),
|
||||
.float_rect = ruler.getGraphDrawContext().getRect(sample - clickable_width/2, clickable_width, 0, 1),
|
||||
.float_relative_to = ruler.graph_box,
|
||||
.parent = ruler.graph_box,
|
||||
.flags = &.{ .draw_hot, .draw_active, .clickable, .draggable },
|
||||
.hot_cursor = .mouse_cursor_pointing_hand,
|
||||
});
|
||||
|
||||
const signal = ui.signal(clickable);
|
||||
if (signal.hot) {
|
||||
const mouse_tooltip = ui.mouseTooltip();
|
||||
mouse_tooltip.beginChildren();
|
||||
defer mouse_tooltip.endChildren();
|
||||
|
||||
_ = ui.label("Gain: {d:.3}", .{ gain_change.gain });
|
||||
}
|
||||
|
||||
const view_range = view.getGraphView(axis);
|
||||
const mouse_range = switch (axis) {
|
||||
.X => RangeF64.init(0, ruler.graph_box.persistent.size.x),
|
||||
.Y => RangeF64.init(0, ruler.graph_box.persistent.size.y)
|
||||
};
|
||||
const mouse_position = switch (axis) {
|
||||
.X => signal.mouse.x - ruler.graph_box.persistent.position.x,
|
||||
.Y => signal.mouse.y - ruler.graph_box.persistent.position.y
|
||||
};
|
||||
|
||||
if (signal.dragged() and i != 0) {
|
||||
project.updateGainChange(i, mouse_range.remapTo(view_range.*, mouse_position));
|
||||
}
|
||||
}
|
||||
|
||||
for (0.., project.statistic_points.items) |i, statistic_point| {
|
||||
const color = srcery.bright_blue;
|
||||
|
||||
const sample: f64 = @floatFromInt(statistic_point);
|
||||
|
||||
showMarkerLine(ui, ruler, sample, color);
|
||||
showMarkerLine(ui, ruler, sample, color);
|
||||
|
||||
var hasher = UI.Key.CombineHasher.init();
|
||||
hasher.update(std.mem.asBytes("Statistic point"));
|
||||
hasher.update(std.mem.asBytes(&view_id));
|
||||
hasher.update(std.mem.asBytes(&axis));
|
||||
hasher.update(std.mem.asBytes(&i));
|
||||
|
||||
const view_size = view.graph_opts.x_range.size();
|
||||
const clickable_width = view_size * 0.01;
|
||||
|
||||
const clickable = ui.createBox(.{
|
||||
.key = UI.Key.init(hasher.final()),
|
||||
.float_rect = ruler.getGraphDrawContext().getRect(sample - clickable_width/2, clickable_width, 0, 1),
|
||||
.float_relative_to = ruler.graph_box,
|
||||
.parent = ruler.graph_box,
|
||||
.flags = &.{ .draw_hot, .draw_active, .clickable },
|
||||
.hot_cursor = .mouse_cursor_pointing_hand,
|
||||
});
|
||||
|
||||
if (ui.signal(clickable).clicked()) {
|
||||
ctx.view_controls.toggleShownMarker(view_id, i);
|
||||
const signal = ui.signal(clickable);
|
||||
if (signal.hot) {
|
||||
const mouse_tooltip = ui.mouseTooltip();
|
||||
mouse_tooltip.beginChildren();
|
||||
defer mouse_tooltip.endChildren();
|
||||
|
||||
_ = ui.label("Statistic point", .{ });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const signal = ui.signal(box);
|
||||
const view_range = view.getGraphView(axis);
|
||||
const mouse_position = switch (axis) {
|
||||
.X => signal.relative_mouse.x,
|
||||
.Y => signal.relative_mouse.y
|
||||
@ -383,7 +498,6 @@ pub fn show(ctx: Context, box: *UI.Box, graph_box: *UI.Box, view_id: Id, axis: U
|
||||
.X => RangeF64.init(0, box.persistent.size.x),
|
||||
.Y => RangeF64.init(0, box.persistent.size.y)
|
||||
};
|
||||
const view_range = view.getGraphView(axis);
|
||||
|
||||
if (signal.hot and view_range.size() > 0) {
|
||||
const mouse_position_on_graph = mouse_range.remapTo(view_range.*, mouse_position);
|
||||
@ -401,7 +515,7 @@ pub fn show(ctx: Context, box: *UI.Box, graph_box: *UI.Box, view_id: Id, axis: U
|
||||
ctx.view_controls.setCursorHoldStart(view_id, axis, cursor);
|
||||
}
|
||||
|
||||
if (ctx.view_controls.selected_tool == .move) {
|
||||
// if (ctx.view_controls.selected_tool == .move) {
|
||||
if (signal.scrolled() and cursor != null) {
|
||||
var scale_factor: f64 = 1;
|
||||
if (signal.scroll.y > 0) {
|
||||
@ -436,27 +550,27 @@ pub fn show(ctx: Context, box: *UI.Box, graph_box: *UI.Box, view_id: Id, axis: U
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (ctx.view_controls.selected_tool == .select) {
|
||||
// } else if (ctx.view_controls.selected_tool == .select) {
|
||||
|
||||
if (cursor != null) {
|
||||
if (ctx.view_controls.getCursorHoldStart(view_id, axis)) |hold_start| {
|
||||
const range = RangeF64.init(
|
||||
@min(hold_start, cursor.?),
|
||||
@max(hold_start, cursor.?),
|
||||
);
|
||||
const hold_start_mouse = view_range.remapTo(mouse_range, range.lower);
|
||||
const hold_end_mouse = view_range.remapTo(mouse_range, range.upper);
|
||||
const mouse_move_distance = @abs(hold_end_mouse - hold_start_mouse);
|
||||
if (signal.flags.contains(.left_released) and mouse_move_distance > 5) {
|
||||
_ = ctx.project.appendMarkedRange(view_id, axis, range);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (ctx.view_controls.selected_tool == .marker) {
|
||||
if (cursor != null and signal.flags.contains(.left_released) and axis == .X) {
|
||||
view.markers.append(cursor.?) catch {};
|
||||
}
|
||||
}
|
||||
// if (cursor != null) {
|
||||
// if (ctx.view_controls.getCursorHoldStart(view_id, axis)) |hold_start| {
|
||||
// const range = RangeF64.init(
|
||||
// @min(hold_start, cursor.?),
|
||||
// @max(hold_start, cursor.?),
|
||||
// );
|
||||
// const hold_start_mouse = view_range.remapTo(mouse_range, range.lower);
|
||||
// const hold_end_mouse = view_range.remapTo(mouse_range, range.upper);
|
||||
// const mouse_move_distance = @abs(hold_end_mouse - hold_start_mouse);
|
||||
// if (signal.flags.contains(.left_released) and mouse_move_distance > 5) {
|
||||
// _ = ctx.project.appendMarkedRange(view_id, axis, range);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } else if (ctx.view_controls.selected_tool == .marker) {
|
||||
// if (cursor != null and signal.flags.contains(.left_released) and axis == .X) {
|
||||
// view.markers.append(cursor.?) catch {};
|
||||
// }
|
||||
// }
|
||||
|
||||
if (signal.flags.contains(.left_released)) {
|
||||
ctx.view_controls.setCursorHoldStart(view_id, axis, null);
|
||||
|
@ -30,7 +30,7 @@ pub const ViewOptions = struct {
|
||||
pub const RenderCache = struct {
|
||||
const Key = struct {
|
||||
options: ViewOptions,
|
||||
drawn_x_range: RangeF64
|
||||
drawn_x_range: RangeF64,
|
||||
};
|
||||
|
||||
texture: ?rl.RenderTexture2D = null,
|
||||
@ -48,6 +48,14 @@ pub const RenderCache = struct {
|
||||
self.key = null;
|
||||
}
|
||||
|
||||
pub fn invalidateRange(self: *RenderCache, x_range: RangeF64) void {
|
||||
if (self.key) |key| {
|
||||
if (key.drawn_x_range.intersectPositive(x_range).isPositive()) {
|
||||
self.invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(self: RenderCache, rect: rl.Rectangle) void {
|
||||
if (self.texture) |texture| {
|
||||
const source = rl.Rectangle{
|
||||
@ -323,7 +331,7 @@ pub fn drawCached(cache: *RenderCache, render_size: Vec2, options: ViewOptions,
|
||||
|
||||
const cache_key = RenderCache.Key{
|
||||
.options = options,
|
||||
.drawn_x_range = RangeF64.init(0, @max(@as(f64, @floatFromInt(sample_list.getLength())) - 1, 0)).intersectPositive(options.x_range)
|
||||
.drawn_x_range = RangeF64.init(0, @max(@as(f64, @floatFromInt(sample_list.getLength())) - 1, 0)).intersectPositive(options.x_range),
|
||||
};
|
||||
|
||||
if (cache.key != null and std.meta.eql(cache.key.?, cache_key)) {
|
||||
|
11
src/main.zig
11
src/main.zig
@ -118,8 +118,12 @@ pub fn main() !void {
|
||||
|
||||
if (app_config_dir.openFile("config.bin", .{})) |save_file| {
|
||||
defer save_file.close();
|
||||
|
||||
app.loadProject(save_file) catch |e| {
|
||||
log.err("Failed to load project: {}", .{e});
|
||||
if (@errorReturnTrace()) |stack_trace| {
|
||||
std.debug.dumpStackTrace(stack_trace.*);
|
||||
}
|
||||
};
|
||||
} else |e| switch (e) {
|
||||
error.FileNotFound => {},
|
||||
@ -174,6 +178,13 @@ pub fn main() !void {
|
||||
}
|
||||
|
||||
{
|
||||
if (app.isCollectionInProgress()) {
|
||||
app.stopCollection();
|
||||
}
|
||||
if (app.isOutputingInProgress()) {
|
||||
app.stopAllOutput();
|
||||
}
|
||||
|
||||
const save_file = try app_config_dir.createFile("config.bin", .{});
|
||||
defer save_file.close();
|
||||
try app.saveProject(save_file);
|
||||
|
@ -97,12 +97,12 @@ pub fn tick(self: *Screen) !void {
|
||||
ai_voltage_physical_channels = try ni_daq.listDeviceAIPhysicalChannels(device);
|
||||
}
|
||||
|
||||
var ao_physical_channels: []const [:0]const u8 = &.{};
|
||||
if (try ni_daq.checkDeviceAOOutputType(device, .Voltage)) {
|
||||
ao_physical_channels = try ni_daq.listDeviceAOPhysicalChannels(device);
|
||||
}
|
||||
// var ao_physical_channels: []const [:0]const u8 = &.{};
|
||||
// if (try ni_daq.checkDeviceAOOutputType(device, .Voltage)) {
|
||||
// ao_physical_channels = try ni_daq.listDeviceAOPhysicalChannels(device);
|
||||
// }
|
||||
|
||||
inline for (.{ ai_voltage_physical_channels, ao_physical_channels }) |channels| {
|
||||
inline for (.{ ai_voltage_physical_channels }) |channels| {
|
||||
for (channels) |channel| {
|
||||
const channel_button = ui.textButton(channel);
|
||||
channel_button.background = srcery.black;
|
||||
|
File diff suppressed because it is too large
Load Diff
24
src/ui.zig
24
src/ui.zig
@ -519,7 +519,7 @@ pub const Box = struct {
|
||||
|
||||
pub const Draw = struct {
|
||||
ctx: ?*anyopaque = null,
|
||||
do: *const fn(ctx: ?*anyopaque, box: *Box) void
|
||||
do: *const fn(ctx: ?*anyopaque, ui: *UI, box: *Box) void
|
||||
};
|
||||
|
||||
const max_wrapped_lines = 64;
|
||||
@ -1828,7 +1828,7 @@ fn drawBox(self: *UI, box: *Box, on_top_pass: ?bool) void {
|
||||
}
|
||||
|
||||
if (box.draw) |box_draw| {
|
||||
box_draw.do(box_draw.ctx, box);
|
||||
box_draw.do(box_draw.ctx, self, box);
|
||||
}
|
||||
|
||||
const alignment_x_coeff = box.alignment.x.getCoefficient();
|
||||
@ -2502,7 +2502,8 @@ pub const FileInputOptions = struct {
|
||||
file_picker: *?Platform.FilePickerId,
|
||||
open_dialog: bool = true,
|
||||
folder: bool = false,
|
||||
path: ?[]const u8 = null
|
||||
path: ?[]const u8 = null,
|
||||
size_x: ?Sizing = null
|
||||
};
|
||||
|
||||
pub fn mouseTooltip(self: *UI) *Box {
|
||||
@ -2649,7 +2650,7 @@ pub fn textInput(self: *UI, opts: TextInputOptions) !void {
|
||||
const storage_text = &storage.buffer;
|
||||
|
||||
if (opts.initial != null and container.created) {
|
||||
storage_text.clearAndFree();
|
||||
storage.clear();
|
||||
try storage_text.appendSlice(opts.initial.?);
|
||||
}
|
||||
|
||||
@ -2778,8 +2779,9 @@ pub fn textInput(self: *UI, opts: TextInputOptions) !void {
|
||||
storage.editing = true;
|
||||
}
|
||||
|
||||
var stop_editing = false;
|
||||
if (self.isKeyActiveAny() and !container_signal.active) {
|
||||
storage.editing = false;
|
||||
stop_editing = true;
|
||||
}
|
||||
|
||||
// Text input controls
|
||||
@ -2951,7 +2953,7 @@ pub fn textInput(self: *UI, opts: TextInputOptions) !void {
|
||||
}
|
||||
|
||||
if (self.isKeyboardPressed(.key_escape)) {
|
||||
storage.editing = false;
|
||||
stop_editing = true;
|
||||
}
|
||||
|
||||
if (self.isKeyboardPressed(.key_enter)) {
|
||||
@ -2974,9 +2976,15 @@ pub fn textInput(self: *UI, opts: TextInputOptions) !void {
|
||||
}
|
||||
|
||||
if (!opts.editable) {
|
||||
storage.editing = false;
|
||||
stop_editing = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (stop_editing) {
|
||||
storage.editing = false;
|
||||
storage.cursor_start = 0;
|
||||
storage.cursor_stop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn numberInput(self: *UI, T: type, opts: NumberInputOptions) !?T {
|
||||
@ -3071,7 +3079,7 @@ pub fn fileInput(self: *UI, opts: FileInputOptions) ?[]u8 {
|
||||
|
||||
const container = self.createBox(.{
|
||||
.key = opts.key,
|
||||
.size_x = Sizing.initGrowUpTo(.{ .pixels = 200 }),
|
||||
.size_x = opts.size_x orelse Sizing.initGrowUpTo(.{ .pixels = 200 }),
|
||||
.size_y = Sizing.initFixed(Unit.initPixels(self.rem(1))),
|
||||
.flags = &.{ .clickable, .clip_view, .draw_hot, .draw_active },
|
||||
.background = srcery.bright_white,
|
||||
|
@ -86,8 +86,12 @@ pub inline fn dumpErrorTrace() void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn formatDuration(allocator: std.mem.Allocator, total_seconds: f64) ![]u8 {
|
||||
pub fn formatDuration(allocator: std.mem.Allocator, total_seconds: f64, high_accuracy: bool) ![]u8 {
|
||||
const seconds = @mod(total_seconds, @as(f64, @floatFromInt(std.time.s_per_min)));
|
||||
const minutes = @divFloor(total_seconds, std.time.s_per_min);
|
||||
if (high_accuracy) {
|
||||
return try std.fmt.allocPrint(allocator, "{d:.0}m {d:.5}s", .{ minutes, seconds });
|
||||
} else {
|
||||
return try std.fmt.allocPrint(allocator, "{d:.0}m {d:.3}s", .{ minutes, seconds });
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user