Compare commits

...

10 Commits

11 changed files with 1580 additions and 1064 deletions

View File

@ -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 = .{

File diff suppressed because it is too large Load Diff

View File

@ -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,
}
};
}

View File

@ -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,
});

View File

@ -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);

View File

@ -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)) {

View File

@ -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);

View 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

View File

@ -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,

View File

@ -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 });
}
}