1629 lines
56 KiB
Zig
1629 lines
56 KiB
Zig
const std = @import("std");
|
|
const rl = @import("raylib");
|
|
const UI = @import("../ui.zig");
|
|
const App = @import("../app.zig");
|
|
const srcery = @import("../srcery.zig");
|
|
const Platform = @import("../platform/root.zig");
|
|
const RangeF64 = @import("../range.zig").RangeF64;
|
|
const Graph = @import("../graph.zig");
|
|
const Assets = @import("../assets.zig");
|
|
const utils = @import("../utils.zig");
|
|
const NIDaq = @import("../ni-daq/root.zig");
|
|
const UIView = @import("../components/view.zig");
|
|
|
|
const ViewControlsSystem = @import("../components/systems/view_controls.zig");
|
|
|
|
const MainScreen = @This();
|
|
|
|
const log = std.log.scoped(.main_screen);
|
|
const assert = std.debug.assert;
|
|
const remap = utils.remap;
|
|
const Id = App.Id;
|
|
|
|
app: *App,
|
|
view_controls: ViewControlsSystem,
|
|
side_panel: union(enum) {
|
|
project,
|
|
solutions,
|
|
gain_changes,
|
|
statistics_points,
|
|
view_settings: Id,
|
|
// marker: struct {
|
|
// view_id: Id,
|
|
// index: usize
|
|
// },
|
|
// marked_range: struct {
|
|
// view_id: Id,
|
|
// index: usize,
|
|
// }
|
|
} = .project,
|
|
|
|
modal: ?union(enum){
|
|
notes,
|
|
add_proprotional,
|
|
protocol
|
|
} = null,
|
|
|
|
// Protocol modal
|
|
protocol_filename: ?[]u8 = null,
|
|
protocol_file_picker: ?Platform.FilePickerId = null,
|
|
protocol_channel: std.BoundedArray(u8, 128) = .{},
|
|
// frequency_input: UI.TextInputStorage,
|
|
// amplitude_input: UI.TextInputStorage,
|
|
// protocol_error_message: ?[]const u8 = null,
|
|
// protocol_graph_cache: Graph.RenderCache = .{},
|
|
// preview_sample_list_id: App.Id,
|
|
// preview_samples_y_range: RangeF64 = RangeF64.init(0, 0),
|
|
|
|
// Notes modal
|
|
notes_storage: UI.TextInputStorage,
|
|
|
|
// Project settings
|
|
solution_input: UI.TextInputStorage,
|
|
gain_input: UI.TextInputStorage,
|
|
sample_rate_input: UI.TextInputStorage,
|
|
experiment_name: UI.TextInputStorage,
|
|
pipete_solution: UI.TextInputStorage,
|
|
export_location_picker: ?Platform.FilePickerId = null,
|
|
parsed_sample_rate: ?f64 = null,
|
|
|
|
// View settings
|
|
prev_view_settings: ?Id = null, // View ID
|
|
view_name_input: UI.TextInputStorage,
|
|
channel_save_file_picker: ?Platform.FilePickerId = null,
|
|
file_save_file_picker: ?Platform.FilePickerId = null,
|
|
|
|
// Proportional modal
|
|
proportion_view1: ?Id = null, // View ID
|
|
proportion_view2: ?Id = null, // View ID
|
|
|
|
pub fn init(app: *App) !MainScreen {
|
|
const allocator = app.allocator;
|
|
|
|
const self = MainScreen{
|
|
.app = app,
|
|
// .frequency_input = UI.TextInputStorage.init(allocator),
|
|
// .amplitude_input = UI.TextInputStorage.init(allocator),
|
|
.sample_rate_input = UI.TextInputStorage.init(allocator),
|
|
.notes_storage = UI.TextInputStorage.init(allocator),
|
|
.solution_input = UI.TextInputStorage.init(allocator),
|
|
.experiment_name = UI.TextInputStorage.init(allocator),
|
|
.pipete_solution = UI.TextInputStorage.init(allocator),
|
|
.view_name_input = UI.TextInputStorage.init(allocator),
|
|
.gain_input = UI.TextInputStorage.init(allocator),
|
|
.view_controls = ViewControlsSystem.init(app),
|
|
// .preview_sample_list_id = try app.project.addSampleList(allocator)
|
|
};
|
|
|
|
// try self.frequency_input.setText("10");
|
|
// try self.amplitude_input.setText("10");
|
|
|
|
return self;
|
|
}
|
|
|
|
pub fn deinit(self: *MainScreen) void {
|
|
// self.frequency_input.deinit();
|
|
// self.amplitude_input.deinit();
|
|
self.sample_rate_input.deinit();
|
|
self.notes_storage.deinit();
|
|
self.solution_input.deinit();
|
|
self.experiment_name.deinit();
|
|
self.pipete_solution.deinit();
|
|
self.view_name_input.deinit();
|
|
self.gain_input.deinit();
|
|
// self.app.project.removeSampleList(self.preview_sample_list_id);
|
|
|
|
// self.clearProtocolErrorMessage();
|
|
|
|
if (self.protocol_filename) |str| {
|
|
self.app.allocator.free(str);
|
|
}
|
|
}
|
|
|
|
pub fn showProtocolModal(self: *MainScreen) !void {
|
|
var ui = &self.app.ui;
|
|
const ni_daq = &(self.app.ni_daq orelse return);
|
|
// const allocator = self.app.allocator;
|
|
|
|
// const sample_rate = self.app.project.getSampleRate() orelse return;
|
|
// _ = sample_rate;
|
|
|
|
const container = ui.createBox(.{
|
|
.key = ui.keyFromString("Protocol modal"),
|
|
.background = srcery.black,
|
|
.size_x = UI.Sizing.initGrowUpTo(.{ .pixels = 300 }),
|
|
.size_y = UI.Sizing.initGrowUpTo(.{ .pixels = 300 }),
|
|
.layout_direction = .top_to_bottom,
|
|
.padding = UI.Padding.all(ui.rem(1.5)),
|
|
.flags = &.{ .clickable },
|
|
.layout_gap = ui.rem(0.5)
|
|
});
|
|
container.beginChildren();
|
|
defer container.endChildren();
|
|
defer _ = ui.signal(container);
|
|
|
|
if (ui.fileInput(.{
|
|
.key = ui.keyFromString("Protocol filename"),
|
|
.allocator = self.app.allocator,
|
|
.path = self.protocol_filename,
|
|
.file_picker = &self.protocol_file_picker
|
|
})) |filename| {
|
|
if (self.protocol_filename) |str| {
|
|
self.app.allocator.free(str);
|
|
}
|
|
self.protocol_filename = filename;
|
|
}
|
|
|
|
{ // Select channel
|
|
var available_channels = std.ArrayList([]const u8).init(self.app.allocator);
|
|
defer available_channels.deinit();
|
|
|
|
const devices = try ni_daq.listDeviceNames();
|
|
for (devices) |device| {
|
|
if (!try ni_daq.checkDeviceAOOutputType(device, .Voltage)) {
|
|
continue;
|
|
}
|
|
|
|
for (try ni_daq.listDeviceAOPhysicalChannels(device)) |channel| {
|
|
try available_channels.append(channel);
|
|
}
|
|
}
|
|
|
|
|
|
const select = ui.button(ui.keyFromString("Protocol channel"));
|
|
select.borders = UI.Borders.all(.{ .color = srcery.green, .size = 4 });
|
|
if (self.protocol_channel.len == 0) {
|
|
select.setText("Channel: -");
|
|
} else {
|
|
select.setFmtText("Channel: {s}", .{self.protocol_channel.constSlice()});
|
|
}
|
|
|
|
const select_signal = ui.signal(select);
|
|
|
|
if (select_signal.clicked()) {
|
|
select.persistent.open = !select.persistent.open;
|
|
}
|
|
|
|
if (select.persistent.open) {
|
|
const popup = ui.createBox(.{
|
|
.key = ui.keyFromString("Channel popup"),
|
|
.size_x = UI.Sizing.initFixedPixels(ui.rem(10)),
|
|
.size_y = UI.Sizing.initFitChildren(),
|
|
.flags = &.{ .clickable, .scrollable },
|
|
.layout_direction = .top_to_bottom,
|
|
.float_relative_to = select,
|
|
.background = srcery.black,
|
|
.borders = UI.Borders.all(.{ .color = srcery.bright_black, .size = 4 }),
|
|
.draw_on_top = true
|
|
});
|
|
popup.setFloatPosition(0, select.persistent.size.y);
|
|
popup.beginChildren();
|
|
defer popup.endChildren();
|
|
defer _ = ui.signal(popup);
|
|
|
|
for (available_channels.items) |channel| {
|
|
const select_option = ui.textButton(channel);
|
|
select_option.alignment.x = .start;
|
|
select_option.size.x = UI.Sizing.initGrowFull();
|
|
select_option.borders = UI.Borders.all(.{ .color = srcery.hard_black, .size = 2 });
|
|
select_option.background = srcery.black;
|
|
|
|
const signal = ui.signal(select_option);
|
|
if (signal.clicked()) {
|
|
select.persistent.open = false;
|
|
self.protocol_channel.len = 0;
|
|
try self.protocol_channel.appendSlice(channel);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (select_signal.clicked_outside) {
|
|
select.persistent.open = false;
|
|
}
|
|
}
|
|
|
|
{ // Start
|
|
var btn = ui.textButton("Start");
|
|
btn.borders = UI.Borders.all(.{ .color = srcery.hard_black, .size = 4 });
|
|
if (self.protocol_filename != null and self.protocol_channel.len > 0) {
|
|
btn.borders = UI.Borders.all(.{ .color = srcery.green, .size = 4 });
|
|
|
|
if (ui.signal(btn).clicked()) {
|
|
try self.app.startOutput(self.protocol_channel.constSlice(), self.protocol_filename.?);
|
|
self.modal = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
// {
|
|
// const protocol_view = ui.createBox(.{
|
|
// .key = ui.keyFromString("Protocol view"),
|
|
// .size_x = UI.Sizing.initGrowFull(),
|
|
// .size_y = UI.Sizing.initFixedPixels(ui.rem(4)),
|
|
// .borders = UI.Borders.all(.{ .color = srcery.hard_black, .size = 2 })
|
|
// });
|
|
|
|
// if (self.app.project.sample_lists.get(self.preview_sample_list_id)) |sample_list| {
|
|
// const view_rect = Graph.ViewOptions{
|
|
// .x_range = RangeF64.init(0, @floatFromInt(sample_list.getLength())),
|
|
// .y_range = self.preview_samples_y_range
|
|
// };
|
|
// Graph.drawCached(&self.protocol_graph_cache, protocol_view.persistent.size, view_rect, sample_list);
|
|
// if (self.protocol_graph_cache.texture) |texture| {
|
|
// protocol_view.texture = texture.texture;
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// const FormInput = struct {
|
|
// name: []const u8,
|
|
// storage: *UI.TextInputStorage,
|
|
// value: *f32
|
|
// };
|
|
|
|
// var frequency: f32 = 0;
|
|
// var amplitude: f32 = 0;
|
|
|
|
// const form_inputs = &[_]FormInput{
|
|
// .{
|
|
// .name = "Frequency",
|
|
// .storage = &self.frequency_input,
|
|
// .value = &frequency
|
|
// },
|
|
// .{
|
|
// .name = "Amplitude",
|
|
// .storage = &self.amplitude_input,
|
|
// .value = &litude
|
|
// },
|
|
// };
|
|
// var any_input_modified = false;
|
|
|
|
// for (form_inputs) |form_input| {
|
|
// const label = form_input.name;
|
|
// const text_input_storage = form_input.storage;
|
|
|
|
// const row = ui.createBox(.{
|
|
// .key = ui.keyFromString(label),
|
|
// .layout_direction = .left_to_right,
|
|
// .size_x = UI.Sizing.initGrowFull(),
|
|
// .size_y = UI.Sizing.initFixedPixels(ui.rem(1.5))
|
|
// });
|
|
// row.beginChildren();
|
|
// defer row.endChildren();
|
|
|
|
// const label_box = ui.label("{s}", .{ label });
|
|
// label_box.size.x = UI.Sizing.initFixed(.{ .font_size = 5 });
|
|
// label_box.size.y = UI.Sizing.initGrowFull();
|
|
// label_box.alignment.y = .center;
|
|
|
|
// try ui.textInput(.{
|
|
// .key = ui.keyFromString("Text input"),
|
|
// .storage = text_input_storage
|
|
// });
|
|
|
|
// any_input_modified = any_input_modified or text_input_storage.modified;
|
|
// }
|
|
|
|
// if (any_input_modified) {
|
|
// self.clearProtocolErrorMessage();
|
|
|
|
// for (form_inputs) |form_input| {
|
|
// const label = form_input.name;
|
|
// const text_input_storage: *UI.TextInputStorage = form_input.storage;
|
|
|
|
// const number = std.fmt.parseFloat(f32, text_input_storage.buffer.items) catch {
|
|
// try self.setProtocolErrorMessage("ERROR: {s} must be a number", .{ label });
|
|
// continue;
|
|
// };
|
|
|
|
// if (number <= 0) {
|
|
// try self.setProtocolErrorMessage("ERROR: {s} must be positive", .{ label });
|
|
// continue;
|
|
// }
|
|
|
|
// form_input.value.* = number;
|
|
// }
|
|
// }
|
|
|
|
// if (self.protocol_error_message == null and any_input_modified) {
|
|
// if (self.app.project.sample_lists.get(self.preview_sample_list_id)) |sample_list| {
|
|
// var preview_samples: std.ArrayListUnmanaged(f64) = .{};
|
|
// defer preview_samples.deinit(allocator);
|
|
|
|
// try App.Channel.generateSine(&preview_samples, allocator, sample_rate, frequency, amplitude);
|
|
// sample_list.clear(allocator);
|
|
// try sample_list.append(preview_samples.items);
|
|
|
|
// self.preview_samples_y_range = RangeF64.init(-amplitude*1.1, amplitude*1.1);
|
|
// self.protocol_graph_cache.invalidate();
|
|
// }
|
|
// }
|
|
|
|
// if (self.protocol_error_message) |message| {
|
|
// _ = ui.createBox(.{
|
|
// .size_x = UI.Sizing.initGrowFull(),
|
|
// .size_y = UI.Sizing.initFixedText(),
|
|
// .text_color = srcery.red,
|
|
// .align_x = .start,
|
|
// .align_y = .start,
|
|
// .flags = &.{ .wrap_text },
|
|
// .text = message
|
|
// });
|
|
// }
|
|
|
|
// _ = ui.createBox(.{
|
|
// .size_x = UI.Sizing.initGrowFull(),
|
|
// .size_y = UI.Sizing.initGrowFull(),
|
|
// });
|
|
|
|
// {
|
|
// const row = ui.createBox(.{
|
|
// .size_x = UI.Sizing.initGrowFull(),
|
|
// .size_y = UI.Sizing.initFitChildren(),
|
|
// .align_x = .end
|
|
// });
|
|
// row.beginChildren();
|
|
// defer row.endChildren();
|
|
|
|
// const btn = ui.textButton("Confirm");
|
|
// btn.borders = UI.Borders.all(.{ .color = srcery.green, .size = 4 });
|
|
|
|
// if (ui.signal(btn).clicked() or ui.isKeyboardPressed(.key_enter)) {
|
|
// if (self.protocol_error_message == null) {
|
|
// try App.Channel.generateSine(&channel.write_pattern, allocator, sample_rate, frequency, amplitude);
|
|
|
|
// self.app.pushCommand(.{ .start_output = channel_id });
|
|
// self.view_controls.view_protocol_modal = null;
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
|
|
}
|
|
|
|
fn showNotesModal(self: *MainScreen) !void {
|
|
var ui = &self.app.ui;
|
|
|
|
const container = ui.createBox(.{
|
|
.key = ui.keyFromString("Notes modal"),
|
|
.background = srcery.black,
|
|
.size_x = UI.Sizing.initGrowUpTo(.{ .pixels = 400 }),
|
|
.size_y = UI.Sizing.initGrowUpTo(.{ .pixels = 600 }),
|
|
.layout_direction = .top_to_bottom,
|
|
.padding = UI.Padding.all(ui.rem(1.5)),
|
|
.flags = &.{ .clickable },
|
|
.layout_gap = ui.rem(0.5)
|
|
});
|
|
container.beginChildren();
|
|
defer container.endChildren();
|
|
defer _ = ui.signal(container);
|
|
|
|
const label = ui.label("Notes", .{});
|
|
label.font = .{ .variant = .regular_italic, .size = ui.rem(2) };
|
|
label.alignment.x = .center;
|
|
label.size.x = UI.Sizing.initGrowFull();
|
|
|
|
try ui.textInput(.{
|
|
.key = ui.keyFromString("Notes"),
|
|
.storage = &self.notes_storage,
|
|
.size_x = UI.Sizing.initGrowFull(),
|
|
.size_y = UI.Sizing.initGrowFull(),
|
|
.single_line = false
|
|
});
|
|
|
|
if (self.notes_storage.modified) {
|
|
const project = &self.app.project;
|
|
|
|
project.notes.clearRetainingCapacity();
|
|
try project.notes.insertSlice(self.app.allocator, 0, self.notes_storage.buffer.items);
|
|
}
|
|
}
|
|
|
|
fn showProportionalAdd(self: *MainScreen) !void {
|
|
var ui = &self.app.ui;
|
|
|
|
const container = ui.createBox(.{
|
|
.key = ui.keyFromString("Proportional add modal"),
|
|
.background = srcery.black,
|
|
.size_x = UI.Sizing.initGrowUpTo(.{ .pixels = 300 }),
|
|
.size_y = UI.Sizing.initGrowUpTo(.{ .pixels = 200 }),
|
|
.layout_direction = .top_to_bottom,
|
|
.padding = UI.Padding.all(ui.rem(1.5)),
|
|
.flags = &.{ .clickable },
|
|
.layout_gap = ui.rem(0.5)
|
|
});
|
|
container.beginChildren();
|
|
defer container.endChildren();
|
|
defer _ = ui.signal(container);
|
|
|
|
if (self.proportion_view1 != null and self.app.project.views.get(self.proportion_view1.?) == null) {
|
|
self.proportion_view1 = null;
|
|
}
|
|
|
|
if (self.proportion_view2 != null and self.app.project.views.get(self.proportion_view2.?) == null) {
|
|
self.proportion_view2 = null;
|
|
}
|
|
|
|
var arena = std.heap.ArenaAllocator.init(self.app.allocator);
|
|
defer arena.deinit();
|
|
|
|
var available_views = std.ArrayList(struct { view_id: App.Id, name: []const u8 }).init(arena.allocator());
|
|
|
|
{
|
|
var view_iter = self.app.project.views.idIterator();
|
|
while (view_iter.next()) |view_id| {
|
|
const view = self.app.project.views.get(view_id).?;
|
|
if (view.reference != .channel) {
|
|
continue;
|
|
}
|
|
const channel_id = view.reference.channel;
|
|
const channel = self.app.project.channels.get(channel_id).?;
|
|
const view_name = utils.getBoundedStringZ(&channel.name);
|
|
|
|
try available_views.append(.{ .view_id = view_id, .name = view_name });
|
|
}
|
|
}
|
|
|
|
{
|
|
var row = ui.createBox(.{
|
|
.size_x = UI.Sizing.initGrowFull(),
|
|
.size_y = UI.Sizing.initFitChildren(),
|
|
.layout_direction = .left_to_right,
|
|
.layout_gap = ui.rem(0.5),
|
|
});
|
|
row.beginChildren();
|
|
defer row.endChildren();
|
|
|
|
{
|
|
const select = ui.textButton("Select dividend");
|
|
if (self.proportion_view1) |view_id| {
|
|
const view = self.app.project.views.get(view_id).?;
|
|
assert(view.reference == .channel);
|
|
const channel = self.app.project.channels.get(view.reference.channel).?;
|
|
select.setFmtText("Dividend: {s}", .{utils.getBoundedStringZ(&channel.name)});
|
|
}
|
|
|
|
select.borders = UI.Borders.all(.{ .color = srcery.blue, .size = 4 });
|
|
const signal = ui.signal(select);
|
|
if (signal.clicked()) {
|
|
select.persistent.open = !select.persistent.open;
|
|
}
|
|
|
|
if (select.persistent.open) {
|
|
const popup = ui.createBox(.{
|
|
.key = ui.keyFromString("Transform 1 popup"),
|
|
.size_x = UI.Sizing.initFixedPixels(ui.rem(10)),
|
|
.size_y = UI.Sizing.initFitChildren(),
|
|
.flags = &.{ .clickable, .scrollable },
|
|
.layout_direction = .top_to_bottom,
|
|
.float_relative_to = select,
|
|
.background = srcery.black,
|
|
.borders = UI.Borders.all(.{ .color = srcery.bright_black, .size = 4 }),
|
|
.draw_on_top = true
|
|
});
|
|
popup.setFloatPosition(0, select.persistent.size.y);
|
|
popup.beginChildren();
|
|
defer popup.endChildren();
|
|
defer _ = ui.signal(popup);
|
|
|
|
for (available_views.items) |option| {
|
|
const select_option = ui.textButton(option.name);
|
|
select_option.alignment.x = .start;
|
|
select_option.size.x = UI.Sizing.initGrowFull();
|
|
select_option.borders = UI.Borders.all(.{ .color = srcery.hard_black, .size = 2 });
|
|
select_option.background = srcery.black;
|
|
|
|
if (ui.signal(select_option).clicked()) {
|
|
select.persistent.open = false;
|
|
self.proportion_view1 = option.view_id;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (signal.clicked_outside) {
|
|
select.persistent.open = false;
|
|
}
|
|
}
|
|
|
|
{
|
|
const select = ui.textButton("Select divisor");
|
|
if (self.proportion_view2) |view_id| {
|
|
const view = self.app.project.views.get(view_id).?;
|
|
assert(view.reference == .channel);
|
|
const channel = self.app.project.channels.get(view.reference.channel).?;
|
|
select.setFmtText("Divisor: {s}", .{utils.getBoundedStringZ(&channel.name)});
|
|
}
|
|
|
|
select.borders = UI.Borders.all(.{ .color = srcery.blue, .size = 4 });
|
|
const signal = ui.signal(select);
|
|
if (signal.clicked()) {
|
|
select.persistent.open = !select.persistent.open;
|
|
}
|
|
|
|
if (select.persistent.open) {
|
|
const popup = ui.createBox(.{
|
|
.key = ui.keyFromString("Transform 1 popup"),
|
|
.size_x = UI.Sizing.initFixedPixels(ui.rem(10)),
|
|
.size_y = UI.Sizing.initFitChildren(),
|
|
.flags = &.{ .clickable, .scrollable },
|
|
.layout_direction = .top_to_bottom,
|
|
.float_relative_to = select,
|
|
.background = srcery.black,
|
|
.borders = UI.Borders.all(.{ .color = srcery.bright_black, .size = 4 }),
|
|
.draw_on_top = true
|
|
});
|
|
popup.setFloatPosition(0, select.persistent.size.y);
|
|
popup.beginChildren();
|
|
defer popup.endChildren();
|
|
defer _ = ui.signal(popup);
|
|
|
|
for (available_views.items) |option| {
|
|
const select_option = ui.textButton(option.name);
|
|
select_option.alignment.x = .start;
|
|
select_option.size.x = UI.Sizing.initGrowFull();
|
|
select_option.borders = UI.Borders.all(.{ .color = srcery.hard_black, .size = 2 });
|
|
select_option.background = srcery.black;
|
|
|
|
if (ui.signal(select_option).clicked()) {
|
|
select.persistent.open = false;
|
|
self.proportion_view2 = option.view_id;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (signal.clicked_outside) {
|
|
select.persistent.open = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
const btn = ui.textButton("Confirm");
|
|
btn.borders = UI.Borders.all(.{ .color = srcery.hard_black, .size = 4 });
|
|
if (self.proportion_view1 != null and self.proportion_view2 != null) {
|
|
btn.borders = UI.Borders.all(.{ .color = srcery.green, .size = 4 });
|
|
|
|
if (ui.signal(btn).clicked()) {
|
|
self.modal = null;
|
|
const view1 = self.app.project.views.get(self.proportion_view1.?).?;
|
|
const channel1 = self.app.project.channels.get(view1.reference.channel).?;
|
|
const view2 = self.app.project.views.get(self.proportion_view2.?).?;
|
|
const channel2 = self.app.project.channels.get(view2.reference.channel).?;
|
|
|
|
const new_view_id = try self.app.addView(.{ .mirror = channel1.collected_samples_id });
|
|
const new_view = self.app.project.views.get(new_view_id).?;
|
|
new_view.transformation.divider = channel2.collected_samples_id;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// fn setProtocolErrorMessage(self: *MainScreen, comptime fmt: []const u8, args: anytype) !void {
|
|
// self.clearProtocolErrorMessage();
|
|
|
|
// const allocator = self.app.allocator;
|
|
// self.protocol_error_message = try std.fmt.allocPrint(allocator, fmt, args);
|
|
// }
|
|
|
|
// fn clearProtocolErrorMessage(self: *MainScreen) void {
|
|
// const allocator = self.app.allocator;
|
|
|
|
// if (self.protocol_error_message) |msg| {
|
|
// allocator.free(msg);
|
|
// }
|
|
// self.protocol_error_message = null;
|
|
// }
|
|
|
|
fn showProjectSettings(self: *MainScreen) !void {
|
|
var ui = &self.app.ui;
|
|
const frame_allocator = ui.frameAllocator();
|
|
const project = &self.app.project;
|
|
|
|
{
|
|
const label = ui.label("Project", .{});
|
|
label.borders.bottom = .{
|
|
.color = srcery.bright_white,
|
|
.size = 1
|
|
};
|
|
}
|
|
|
|
_ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(1)) });
|
|
|
|
{
|
|
_ = ui.label("Experiment:", .{});
|
|
try ui.textInput(.{
|
|
.key = ui.keyFromString("Experiment name"),
|
|
.storage = &self.experiment_name,
|
|
.initial = project.experiment_name.items,
|
|
.size_x = UI.Sizing.initGrowFull()
|
|
});
|
|
|
|
if (self.experiment_name.modified) {
|
|
project.experiment_name.clearRetainingCapacity();
|
|
try project.experiment_name.insertSlice(self.app.allocator, 0, self.experiment_name.buffer.items);
|
|
}
|
|
}
|
|
|
|
_ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(0.5)) });
|
|
|
|
{
|
|
_ = ui.label("Pipete solution:", .{});
|
|
try ui.textInput(.{
|
|
.key = ui.keyFromString("Pipete solution"),
|
|
.storage = &self.pipete_solution,
|
|
.initial = project.pipete_solution.items,
|
|
.size_x = UI.Sizing.initGrowFull()
|
|
});
|
|
|
|
if (self.pipete_solution.modified) {
|
|
project.pipete_solution.clearRetainingCapacity();
|
|
try project.pipete_solution.insertSlice(self.app.allocator, 0, self.pipete_solution.buffer.items);
|
|
}
|
|
}
|
|
|
|
_ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(0.5)) });
|
|
|
|
{
|
|
_ = ui.label("Solution:", .{});
|
|
|
|
|
|
try ui.textInput(.{
|
|
.key = ui.keyFromString("Solution input"),
|
|
.storage = &self.solution_input,
|
|
.size_x = UI.Sizing.initGrowFull()
|
|
});
|
|
|
|
{
|
|
const row = ui.createBox(.{
|
|
.size_x = UI.Sizing.initFitChildren(),
|
|
.size_y = UI.Sizing.initFitChildren(),
|
|
.layout_direction = .left_to_right,
|
|
.layout_gap = ui.rem(1)
|
|
});
|
|
row.beginChildren();
|
|
defer row.endChildren();
|
|
|
|
{
|
|
const btn = ui.textButton("Append solution");
|
|
btn.borders = UI.Borders.all(.{ .color = srcery.blue, .size = 4 });
|
|
if (ui.signal(btn).clicked()) {
|
|
if (self.solution_input.buffer.items.len > 0) {
|
|
try project.appendSolution(self.app.allocator, self.solution_input.buffer.items);
|
|
}
|
|
self.solution_input.clear();
|
|
}
|
|
}
|
|
|
|
if (project.solutions.items.len > 0) {
|
|
const btn = ui.textButton("View solutions");
|
|
btn.borders = UI.Borders.all(.{ .color = srcery.blue, .size = 4 });
|
|
if (ui.signal(btn).clicked()) {
|
|
self.side_panel = .solutions;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
_ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(0.5)) });
|
|
|
|
{ // Gain
|
|
_ = ui.label("Gain:", .{});
|
|
|
|
const last_gain_change = project.gain_changes.buffer[project.gain_changes.len - 1];
|
|
const current_gain = try ui.numberInput(f64, .{
|
|
.key = ui.keyFromString("Gain input"),
|
|
.storage = &self.gain_input,
|
|
.initial = last_gain_change.gain,
|
|
.width = 400
|
|
});
|
|
|
|
if (!self.gain_input.editing and current_gain != null and current_gain.? != last_gain_change.gain) {
|
|
try project.gain_changes.append(.{
|
|
.gain = current_gain.?,
|
|
.sample = @floatFromInt(project.getSampleTimestamp() orelse 0)
|
|
});
|
|
}
|
|
|
|
{
|
|
const btn = ui.textButton("Show gain changes");
|
|
btn.borders = UI.Borders.all(.{ .color = srcery.blue, .size = 4 });
|
|
if (ui.signal(btn).clicked()) {
|
|
self.side_panel = .gain_changes;
|
|
}
|
|
}
|
|
}
|
|
|
|
_ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(0.5)) });
|
|
|
|
{ // Statistic points
|
|
const row = ui.createBox(.{
|
|
.size_x = UI.Sizing.initFitChildren(),
|
|
.size_y = UI.Sizing.initFitChildren(),
|
|
.layout_direction = .left_to_right,
|
|
.layout_gap = ui.rem(0.5)
|
|
});
|
|
row.beginChildren();
|
|
defer row.endChildren();
|
|
|
|
{
|
|
const btn = ui.textButton("Add statistic point");
|
|
btn.borders = UI.Borders.all(.{ .color = srcery.blue, .size = 4 });
|
|
if (ui.signal(btn).clicked()) {
|
|
const current_sample = project.getSampleTimestamp() orelse 0;
|
|
try project.statistic_points.append(self.app.allocator, current_sample);
|
|
}
|
|
}
|
|
|
|
if (project.statistic_points.items.len > 0) {
|
|
const btn = ui.textButton("View statistic points");
|
|
btn.borders = UI.Borders.all(.{ .color = srcery.blue, .size = 4 });
|
|
if (ui.signal(btn).clicked()) {
|
|
self.side_panel = .statistics_points;
|
|
}
|
|
}
|
|
}
|
|
|
|
_ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(0.5)) });
|
|
|
|
{ // Sample rate
|
|
var placeholder: ?[]const u8 = null;
|
|
if (project.getDefaultSampleRate()) |default_sample_rate| {
|
|
placeholder = try std.fmt.allocPrint(frame_allocator, "{d}", .{ default_sample_rate });
|
|
}
|
|
|
|
_ = ui.label("Sample rate:", .{});
|
|
self.parsed_sample_rate = try ui.numberInput(f64, .{
|
|
.key = ui.keyFromString("Sample rate input"),
|
|
.storage = &self.sample_rate_input,
|
|
.placeholder = placeholder,
|
|
.initial = project.sample_rate,
|
|
.invalid = self.parsed_sample_rate != project.sample_rate,
|
|
.editable = !self.app.isCollectionInProgress(),
|
|
.width = 400
|
|
});
|
|
project.sample_rate = self.parsed_sample_rate;
|
|
|
|
if (project.getAllowedSampleRates()) |allowed_sample_rates| {
|
|
if (project.sample_rate) |selected_sample_rate| {
|
|
if (!allowed_sample_rates.hasInclusive(selected_sample_rate)) {
|
|
project.sample_rate = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(0.5)) });
|
|
|
|
{ // Open notes
|
|
const btn = ui.textButton("Open notes");
|
|
btn.borders = UI.Borders.all(.{ .color = srcery.blue, .size = 4 });
|
|
if (ui.signal(btn).clicked()) {
|
|
self.modal = .notes;
|
|
}
|
|
}
|
|
|
|
_ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(0.5)) });
|
|
|
|
{ // Export folder
|
|
_ = ui.label("Export folder:", .{});
|
|
if (ui.fileInput(.{
|
|
.allocator = self.app.allocator,
|
|
.key = ui.keyFromString("Export location"),
|
|
.path = project.export_location,
|
|
.file_picker = &self.export_location_picker,
|
|
.open_dialog = true,
|
|
.folder = true,
|
|
.size_x = UI.Sizing.initGrowFull()
|
|
})) |path| {
|
|
if (project.export_location) |str| {
|
|
self.app.allocator.free(str);
|
|
}
|
|
project.export_location = path;
|
|
}
|
|
|
|
const btn = ui.textButton("Export");
|
|
if (self.app.isCollectionInProgress() or project.export_location == null) {
|
|
btn.borders = UI.Borders.all(.{ .color = srcery.hard_black, .size = 4 });
|
|
} else {
|
|
btn.borders = UI.Borders.all(.{ .color = srcery.green, .size = 4 });
|
|
if (ui.signal(btn).clicked()) {
|
|
self.app.pushCommand(.export_project);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn showViewSettings(self: *MainScreen, view_id: Id) !void {
|
|
var ui = &self.app.ui;
|
|
|
|
const project = &self.app.project;
|
|
const sample_rate = project.getSampleRate();
|
|
const view = project.views.get(view_id) orelse return;
|
|
|
|
// TODO: Hack
|
|
if (self.prev_view_settings == null or !self.prev_view_settings.?.eql(view_id)) {
|
|
self.view_name_input.clear();
|
|
try self.view_name_input.setText(view.name.constSlice());
|
|
self.prev_view_settings = view_id;
|
|
}
|
|
|
|
{
|
|
const label = ui.label("Settings", .{});
|
|
label.borders.bottom = .{
|
|
.color = srcery.bright_white,
|
|
.size = 1
|
|
};
|
|
|
|
_ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(1)) });
|
|
}
|
|
|
|
{
|
|
_ = ui.label("Name", .{});
|
|
try ui.textInput(.{
|
|
.key = ui.keyFromString("Name input"),
|
|
.storage = &self.view_name_input,
|
|
.initial = view.name.constSlice()
|
|
});
|
|
|
|
if (self.view_name_input.modified) {
|
|
const name = self.view_name_input.textSlice();
|
|
view.name.len = 0;
|
|
if (name.len > view.name.buffer.len) {
|
|
view.name.appendSliceAssumeCapacity(name[0..view.name.buffer.len]);
|
|
} else {
|
|
view.name.appendSliceAssumeCapacity(name);
|
|
}
|
|
}
|
|
}
|
|
|
|
_ = ui.label("Statistics:", .{});
|
|
|
|
switch (view.reference) {
|
|
.channel => |channel_id| {
|
|
const channel = project.channels.get(channel_id).?;
|
|
const channel_name = utils.getBoundedStringZ(&channel.name);
|
|
const channel_type = NIDaq.getChannelType(channel_name);
|
|
|
|
_ = ui.label("Channel: {s}", .{ channel_name });
|
|
|
|
if (channel_type != null) {
|
|
_ = ui.label("Type: {s}", .{ channel_type.?.name() });
|
|
} else {
|
|
_ = ui.label("Type: unknown", .{ });
|
|
}
|
|
},
|
|
.file => |file_id| {
|
|
const file = project.files.get(file_id).?;
|
|
|
|
_ = ui.label("File", .{});
|
|
if (ui.fileInput(.{
|
|
.key = ui.keyFromString("Filename input"),
|
|
.allocator = self.app.allocator,
|
|
.file_picker = &self.file_save_file_picker,
|
|
.path = file.path
|
|
})) |path| {
|
|
self.app.allocator.free(file.path);
|
|
file.path = path;
|
|
self.app.pushCommand(.{ .reload_file = file_id });
|
|
}
|
|
},
|
|
.mirror => |sample_list_id| {
|
|
_ = sample_list_id;
|
|
}
|
|
}
|
|
|
|
const sample_list_id = project.getViewReferenceSampleListId(view_id);
|
|
const sample_list = project.sample_lists.get(sample_list_id).?;
|
|
const sample_count = sample_list.getLength();
|
|
|
|
_ = ui.label("Samples: {d}", .{ sample_count });
|
|
|
|
var duration_str: ?[]const u8 = null;
|
|
if (sample_rate != null) {
|
|
const duration = @as(f64, @floatFromInt(sample_count)) / sample_rate.?;
|
|
if (utils.formatDuration(ui.frameAllocator(), duration, false)) |str| {
|
|
duration_str = str;
|
|
} else |_| {}
|
|
}
|
|
if (duration_str == null) {
|
|
duration_str = std.fmt.allocPrint(ui.frameAllocator(), "{d}", .{ sample_count }) catch null;
|
|
}
|
|
|
|
_ = ui.label("Duration: {s}", .{ duration_str orelse "-" });
|
|
|
|
// var deferred_remove: std.BoundedArray(usize, App.View.max_transforms) = .{};
|
|
|
|
// for (0.., view.transforms.slice()) |i, *_transform| {
|
|
// const transform: *App.Transform = _transform;
|
|
|
|
// const row = ui.createBox(.{
|
|
// .key = UI.Key.initPtr(transform),
|
|
// .size_x = UI.Sizing.initGrowFull(),
|
|
// .size_y = UI.Sizing.initFixedPixels(ui.rem(1.5)),
|
|
// .layout_direction = .left_to_right
|
|
// });
|
|
// row.beginChildren();
|
|
// defer row.endChildren();
|
|
|
|
// if (ui.signal(ui.textButton("Remove")).clicked()) {
|
|
// deferred_remove.appendAssumeCapacity(i);
|
|
// }
|
|
|
|
// {
|
|
// const options = .{
|
|
// .{ .multiply, "Multiply" },
|
|
// .{ .addition, "Addition" },
|
|
// .{ .sliding_window, "Sliding window" },
|
|
// };
|
|
|
|
// const select = ui.button(ui.keyFromString("Transform select"));
|
|
// select.setFmtText("{s}", .{switch (transform.*) {
|
|
// .sliding_window => "Sliding window",
|
|
// .addition => "Addition",
|
|
// .multiply => "Multiply"
|
|
// }});
|
|
|
|
// select.size.y = UI.Sizing.initGrowFull();
|
|
// select.alignment.y = .center;
|
|
// if (ui.signal(select).clicked()) {
|
|
// select.persistent.open = !select.persistent.open;
|
|
// }
|
|
|
|
// if (select.persistent.open) {
|
|
// const popup = ui.createBox(.{
|
|
// .key = ui.keyFromString("Select popup"),
|
|
// .size_x = UI.Sizing.initFixedPixels(ui.rem(10)),
|
|
// .size_y = UI.Sizing.initFitChildren(),
|
|
// .flags = &.{ .clickable, .scrollable },
|
|
// .layout_direction = .top_to_bottom,
|
|
// .float_relative_to = select,
|
|
// .background = srcery.black,
|
|
// .borders = UI.Borders.all(.{ .color = srcery.bright_black, .size = 4 }),
|
|
// .draw_on_top = true
|
|
// });
|
|
// popup.setFloatPosition(0, select.persistent.size.y);
|
|
// popup.beginChildren();
|
|
// defer popup.endChildren();
|
|
|
|
// inline for (options) |option| {
|
|
// const select_option = ui.textButton(option[1]);
|
|
// select_option.alignment.x = .start;
|
|
// select_option.size.x = UI.Sizing.initGrowFull();
|
|
// select_option.borders = UI.Borders.all(.{ .color = srcery.hard_black, .size = 2 });
|
|
// select_option.background = srcery.black;
|
|
|
|
// const signal = ui.signal(select_option);
|
|
// if (signal.clicked()) {
|
|
// select.persistent.open = false;
|
|
// transform.* = switch (option[0]) {
|
|
// .sliding_window => App.Transform{ .sliding_window = sample_rate orelse 0 },
|
|
// .addition => App.Transform{ .addition = 0 },
|
|
// .multiply => App.Transform{ .multiply = 1 },
|
|
// else => unreachable
|
|
// };
|
|
// }
|
|
// }
|
|
|
|
// _ = ui.signal(popup);
|
|
// }
|
|
// }
|
|
|
|
// var input_opts = UI.NumberInputOptions{
|
|
// .key = ui.keyFromString("Sliding window"),
|
|
// .storage = &self.transform_inputs[i],
|
|
// .width = ui.rem(4)
|
|
// // .postfix = if (sample_rate != null) " s" else null,
|
|
// // .display_scalar = sample_rate,
|
|
// };
|
|
|
|
// _ = ui.createBox(.{ .size_x = UI.Sizing.initGrowFull() });
|
|
|
|
// if (transform.* == .sliding_window and sample_rate != null) {
|
|
// input_opts.postfix = " s";
|
|
// input_opts.display_scalar = sample_rate;
|
|
// }
|
|
|
|
// const current_value = switch (transform.*) {
|
|
// .sliding_window => |*v| v,
|
|
// .addition => |*v| v,
|
|
// .multiply => |*v| v
|
|
// };
|
|
// input_opts.initial = current_value.*;
|
|
|
|
// const new_value = try ui.numberInput(f64, input_opts);
|
|
// if (new_value != null) {
|
|
// current_value.* = new_value.?;
|
|
// }
|
|
// }
|
|
|
|
// for (0..deferred_remove.len) |i| {
|
|
// const transform_index = deferred_remove.get(deferred_remove.len - 1 - i);
|
|
// _ = view.transforms.orderedRemove(transform_index);
|
|
// }
|
|
|
|
// if (view.transforms.unusedCapacitySlice().len > 0) {
|
|
// const btn = ui.textButton("Add transform");
|
|
// if (ui.signal(btn).clicked()) {
|
|
// view.transforms.appendAssumeCapacity(.{ .addition = 0 });
|
|
// }
|
|
// }
|
|
|
|
_ = ui.createBox(.{ .size_y = UI.Sizing.initGrowFull() });
|
|
|
|
{
|
|
const btn = ui.textButton("Delete");
|
|
btn.borders = UI.Borders.all(.{ .color = srcery.red, .size = 4 });
|
|
if (ui.signal(btn).clicked()) {
|
|
project.removeView(self.app.allocator, view_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
// fn showMarkedRange(self: *MainScreen, view_id: Id, index: usize) void {
|
|
// var ui = &self.app.ui;
|
|
|
|
// const view = self.app.getView(view_id) orelse return;
|
|
|
|
// const marked_range = view.marked_ranges.get(index);
|
|
|
|
|
|
// {
|
|
// const label = ui.label("Selected range", .{});
|
|
// label.borders.bottom = .{
|
|
// .color = srcery.blue,
|
|
// .size = 1
|
|
// };
|
|
|
|
// _ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(1)) });
|
|
// }
|
|
|
|
// const sample_rate = self.app.project.getSampleRate();
|
|
// if (marked_range.axis == .X and sample_rate != null) {
|
|
// _ = ui.label("From: {d:.3}s", .{ marked_range.range.lower / sample_rate.? });
|
|
// _ = ui.label("To: {d:.3}s", .{ marked_range.range.upper / sample_rate.? });
|
|
// _ = ui.label("Size: {d:.3}s", .{ marked_range.range.size() / sample_rate.? });
|
|
// } else {
|
|
// _ = ui.label("From: {d:.2}", .{ marked_range.range.lower });
|
|
// _ = ui.label("To: {d:.2}", .{ marked_range.range.upper });
|
|
// _ = ui.label("Size: {d:.2}", .{ marked_range.range.size() });
|
|
// }
|
|
|
|
// _ = ui.label("Samples: {d:.2}", .{ marked_range.range.size() });
|
|
|
|
// if (marked_range.axis == .X) {
|
|
// if (marked_range.min) |min| {
|
|
// _ = ui.label("Minimum: {d:.3}", .{ min });
|
|
// } else{
|
|
// _ = ui.label("Minimum: <unknown>", .{});
|
|
// }
|
|
|
|
// if (marked_range.max) |max| {
|
|
// _ = ui.label("Maximum: {d:.3}", .{ max });
|
|
// } else{
|
|
// _ = ui.label("Maximum: <unknown>", .{});
|
|
// }
|
|
|
|
// if (marked_range.average) |average| {
|
|
// _ = ui.label("Average: {d:.3}", .{ average });
|
|
// } else{
|
|
// _ = ui.label("Average: <unknown>", .{});
|
|
// }
|
|
|
|
// if (marked_range.standard_deviation) |standard_deviation| {
|
|
// _ = ui.label("Standard deviation: {d:.3}", .{ standard_deviation });
|
|
// } else{
|
|
// _ = ui.label("Standard deviation: <unknown>", .{});
|
|
// }
|
|
// }
|
|
|
|
// _ = ui.createBox(.{ .size_y = UI.Sizing.initGrowFull() });
|
|
|
|
// {
|
|
// const btn = ui.textButton("Remove");
|
|
// btn.borders = UI.Borders.all(.{ .color = srcery.red, .size = 4 });
|
|
// const signal = ui.signal(btn);
|
|
// if (signal.clicked() or ui.isKeyboardPressed(.key_backspace)) {
|
|
// self.side_panel = .project;
|
|
// _ = view.marked_ranges.swapRemove(index);
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// fn showMarker(self: *MainScreen, view_id: Id, index: usize) void {
|
|
// var ui = &self.app.ui;
|
|
|
|
// const view = self.app.getView(view_id) orelse return;
|
|
|
|
// const marker = view.markers.get(index);
|
|
|
|
// {
|
|
// const label = ui.label("Selected range", .{});
|
|
// label.borders.bottom = .{
|
|
// .color = srcery.blue,
|
|
// .size = 1
|
|
// };
|
|
|
|
// _ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(1)) });
|
|
// }
|
|
|
|
// const sample_rate = self.app.project.getSampleRate();
|
|
|
|
// if (sample_rate != null) {
|
|
// const duration = utils.formatDuration(ui.frameAllocator(), marker / sample_rate.?) catch "";
|
|
// _ = ui.label("Position: {s}", .{ duration });
|
|
// } else {
|
|
// _ = ui.label("Position: {d:.2}", .{ marker });
|
|
// }
|
|
|
|
// }
|
|
|
|
fn showToolbar(self: *MainScreen) void {
|
|
var ui = &self.app.ui;
|
|
|
|
const toolbar = ui.createBox(.{
|
|
.background = srcery.black,
|
|
.layout_direction = .left_to_right,
|
|
.size_x = .{ .fixed = .{ .parent_percent = 1 } },
|
|
.size_y = .{ .fixed = .{ .font_size = 2 } },
|
|
.borders = .{
|
|
.bottom = .{ .color = srcery.hard_black, .size = 4 }
|
|
}
|
|
});
|
|
toolbar.beginChildren();
|
|
defer toolbar.endChildren();
|
|
|
|
{
|
|
var btn = ui.textButton("Start/Stop button");
|
|
btn.borders = UI.Borders.all(.{ .size = 4, .color = srcery.red });
|
|
btn.background = srcery.black;
|
|
btn.size.y = UI.Sizing.initFixed(.{ .parent_percent = 1 });
|
|
btn.padding.top = 0;
|
|
btn.padding.bottom = 0;
|
|
if (ui.signal(btn).clicked()) {
|
|
if (self.app.isCollectionInProgress()) {
|
|
self.app.pushCommand(.stop_collection);
|
|
} else {
|
|
self.app.pushCommand(.start_collection);
|
|
}
|
|
}
|
|
|
|
if (self.app.isCollectionInProgress()) {
|
|
btn.setText("Stop");
|
|
} else {
|
|
btn.setText("Start");
|
|
}
|
|
}
|
|
|
|
{
|
|
const btn = ui.textButton("Add");
|
|
btn.borders = UI.Borders.all(.{ .size = 4, .color = srcery.green });
|
|
const signal = ui.signal(btn);
|
|
if (signal.clicked()) {
|
|
btn.persistent.open = !btn.persistent.open;
|
|
}
|
|
|
|
if (btn.persistent.open) {
|
|
const popup = ui.createBox(.{
|
|
.key = ui.keyFromString("Add popup"),
|
|
.size_x = UI.Sizing.initFixedPixels(ui.rem(10)),
|
|
.size_y = UI.Sizing.initFitChildren(),
|
|
.flags = &.{ .clickable, .scrollable },
|
|
.layout_direction = .top_to_bottom,
|
|
.float_relative_to = btn,
|
|
.background = srcery.black,
|
|
.draw_on_top = true
|
|
});
|
|
popup.setFloatPosition(0, btn.persistent.size.y);
|
|
popup.beginChildren();
|
|
defer popup.endChildren();
|
|
|
|
|
|
{
|
|
const select_btn = ui.textButton("... from file");
|
|
select_btn.alignment.x = .start;
|
|
select_btn.size.x = UI.Sizing.initGrowFull();
|
|
select_btn.background = srcery.hard_black;
|
|
select_btn.borders = UI.Borders.all(.{ .color = srcery.hard_black, .size = 2 });
|
|
if (ui.signal(select_btn).clicked()) {
|
|
self.app.pushCommand(.add_file_from_picker);
|
|
}
|
|
}
|
|
|
|
{
|
|
const select_btn = ui.textButton("... from device");
|
|
select_btn.alignment.x = .start;
|
|
select_btn.size.x = UI.Sizing.initGrowFull();
|
|
select_btn.background = srcery.hard_black;
|
|
select_btn.borders = UI.Borders.all(.{ .color = srcery.hard_black, .size = 2 });
|
|
if (ui.signal(select_btn).clicked()) {
|
|
self.app.screen = .add_channels;
|
|
}
|
|
}
|
|
|
|
{
|
|
const select_btn = ui.textButton("... proportional");
|
|
select_btn.alignment.x = .start;
|
|
select_btn.size.x = UI.Sizing.initGrowFull();
|
|
select_btn.background = srcery.hard_black;
|
|
select_btn.borders = UI.Borders.all(.{ .color = srcery.hard_black, .size = 2 });
|
|
if (ui.signal(select_btn).clicked()) {
|
|
self.app.main_screen.modal = .add_proprotional;
|
|
}
|
|
}
|
|
|
|
// inline for (options) |option| {
|
|
// const select_option = ui.textButton(option[1]);
|
|
// select_option.alignment.x = .start;
|
|
// select_option.size.x = UI.Sizing.initGrowFull();
|
|
// select_option.borders = UI.Borders.all(.{ .color = srcery.hard_black, .size = 2 });
|
|
// select_option.background = srcery.black;
|
|
|
|
// const signal = ui.signal(select_option);
|
|
// if (signal.clicked()) {
|
|
// select.persistent.open = false;
|
|
// transform.* = switch (option[0]) {
|
|
// .sliding_window => App.Transform{ .sliding_window = sample_rate orelse 0 },
|
|
// .addition => App.Transform{ .addition = 0 },
|
|
// .multiply => App.Transform{ .multiply = 1 },
|
|
// else => unreachable
|
|
// };
|
|
// }
|
|
// }
|
|
|
|
_ = ui.signal(popup);
|
|
}
|
|
|
|
if (signal.clicked_outside) {
|
|
btn.persistent.open = false;
|
|
}
|
|
}
|
|
|
|
{
|
|
var btn = ui.textButton("Protocol");
|
|
btn.borders = UI.Borders.all(.{ .size = 4, .color = srcery.hard_black });
|
|
|
|
if (self.app.isCollectionInProgress()) {
|
|
const is_outputing = self.app.isOutputingInProgress();
|
|
|
|
if (is_outputing) {
|
|
btn.borders = UI.Borders.all(.{ .size = 4, .color = srcery.red });
|
|
} else {
|
|
btn.borders = UI.Borders.all(.{ .size = 4, .color = srcery.green });
|
|
}
|
|
|
|
if (ui.signal(btn).clicked()) {
|
|
if (is_outputing) {
|
|
self.app.stopAllOutput();
|
|
} else {
|
|
self.app.main_screen.modal = .protocol;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// {
|
|
// var btn = ui.textButton("Select");
|
|
// if (self.view_controls.selected_tool == .select) {
|
|
// btn.borders = UI.Borders.bottom(.{ .size = 4, .color = srcery.green });
|
|
// }
|
|
|
|
// if (ui.signal(btn).clicked()) {
|
|
// self.view_controls.selected_tool = .select;
|
|
// }
|
|
// }
|
|
|
|
// {
|
|
// var btn = ui.textButton("Marker");
|
|
// if (self.view_controls.selected_tool == .marker) {
|
|
// btn.borders = UI.Borders.bottom(.{ .size = 4, .color = srcery.green });
|
|
// }
|
|
|
|
// if (ui.signal(btn).clicked()) {
|
|
// self.view_controls.selected_tool = .marker;
|
|
// }
|
|
// }
|
|
}
|
|
|
|
fn showSolutions(self: *MainScreen) !void {
|
|
var ui = &self.app.ui;
|
|
const project = &self.app.project;
|
|
_ = &ui;
|
|
|
|
{
|
|
const label = ui.label("Solutions", .{});
|
|
label.borders.bottom = .{
|
|
.color = srcery.white,
|
|
.size = 1
|
|
};
|
|
|
|
_ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(1)) });
|
|
}
|
|
|
|
const sample_rate = project.getSampleRate();
|
|
|
|
for (project.solutions.items) |solution| {
|
|
const row = ui.createBox(.{
|
|
.size_x = UI.Sizing.initGrowFull(),
|
|
.size_y = UI.Sizing.initFitChildren(),
|
|
.layout_direction = .left_to_right,
|
|
.layout_gap = ui.rem(1)
|
|
});
|
|
row.beginChildren();
|
|
defer row.endChildren();
|
|
|
|
if (sample_rate != null) {
|
|
const seconds = @as(f64, @floatFromInt(solution.sample)) / sample_rate.?;
|
|
_ = ui.label("{s}", .{ try utils.formatDuration(ui.frameAllocator(), seconds, false) });
|
|
} else {
|
|
_ = ui.label("{d}", .{ solution.sample });
|
|
}
|
|
|
|
var description = ui.label("{s}", .{ solution.description });
|
|
description.size.x = UI.Sizing.initGrowFull();
|
|
description.flags.insert(.wrap_text);
|
|
}
|
|
|
|
_ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(1)) });
|
|
|
|
{
|
|
const btn = ui.textButton("Close");
|
|
btn.borders = UI.Borders.all(.{ .color = srcery.red, .size = 4 });
|
|
if (ui.signal(btn).clicked()) {
|
|
self.side_panel = .project;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn showStatisticsPoints(self: *MainScreen) !void {
|
|
const ui = &self.app.ui;
|
|
const project = &self.app.project;
|
|
|
|
{
|
|
const label = ui.label("Statistics points", .{});
|
|
label.borders.bottom = .{
|
|
.color = srcery.white,
|
|
.size = 1
|
|
};
|
|
|
|
_ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(1)) });
|
|
}
|
|
|
|
const sample_rate = project.getSampleRate();
|
|
|
|
for (project.statistic_points.items) |sample_timestamp| {
|
|
const row = ui.createBox(.{
|
|
.size_x = UI.Sizing.initGrowFull(),
|
|
.size_y = UI.Sizing.initFitChildren(),
|
|
.layout_direction = .left_to_right,
|
|
.layout_gap = ui.rem(1)
|
|
});
|
|
row.beginChildren();
|
|
defer row.endChildren();
|
|
|
|
if (sample_rate != null) {
|
|
const seconds = @as(f64, @floatFromInt(sample_timestamp)) / sample_rate.?;
|
|
_ = ui.label("{s}", .{ try utils.formatDuration(ui.frameAllocator(), seconds, false) });
|
|
} else {
|
|
_ = ui.label("{d}", .{ sample_timestamp });
|
|
}
|
|
}
|
|
|
|
_ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(1)) });
|
|
|
|
{
|
|
const btn = ui.textButton("Close");
|
|
btn.borders = UI.Borders.all(.{ .color = srcery.red, .size = 4 });
|
|
if (ui.signal(btn).clicked()) {
|
|
self.side_panel = .project;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn showGainChanges(self: *MainScreen) !void {
|
|
const project = &self.app.project;
|
|
var ui = &self.app.ui;
|
|
|
|
{
|
|
const label = ui.label("Gain changes", .{});
|
|
label.borders.bottom = .{
|
|
.color = srcery.white,
|
|
.size = 1
|
|
};
|
|
|
|
_ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(1)) });
|
|
}
|
|
|
|
const sample_rate = project.getSampleRate();
|
|
|
|
var removed_index: ?usize = null;
|
|
|
|
for (0.., project.gain_changes.slice()) |i, *gain_change| {
|
|
const row = ui.createBox(.{
|
|
.key = UI.Key.initPtr(gain_change),
|
|
.size_x = UI.Sizing.initGrowFull(),
|
|
.size_y = UI.Sizing.initFitChildren(),
|
|
.layout_direction = .left_to_right,
|
|
.align_y = .center,
|
|
.layout_gap = ui.rem(1)
|
|
});
|
|
row.beginChildren();
|
|
defer row.endChildren();
|
|
|
|
if (sample_rate != null) {
|
|
const seconds = gain_change.sample / sample_rate.?;
|
|
_ = ui.label("{s}", .{ try utils.formatDuration(ui.frameAllocator(), seconds, false) });
|
|
} else {
|
|
_ = ui.label("{d}", .{ gain_change.sample });
|
|
}
|
|
|
|
var gain_label = ui.label("{d:.3}", .{ gain_change.gain });
|
|
gain_label.size.x = UI.Sizing.initGrowFull();
|
|
|
|
if (i > 0) {
|
|
const btn = ui.textButton("Remove");
|
|
btn.background = srcery.bright_red;
|
|
btn.padding = UI.Padding.all(ui.rem(0.2));
|
|
if (ui.signal(btn).clicked()) {
|
|
removed_index = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (removed_index) |index| {
|
|
project.removeGainChange(index);
|
|
}
|
|
|
|
_ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(1)) });
|
|
|
|
{
|
|
const btn = ui.textButton("Close");
|
|
btn.borders = UI.Borders.all(.{ .color = srcery.red, .size = 4 });
|
|
if (ui.signal(btn).clicked()) {
|
|
self.side_panel = .project;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn showSidePanel(self: *MainScreen) !void {
|
|
var ui = &self.app.ui;
|
|
|
|
const container = ui.createBox(.{
|
|
.size_x = UI.Sizing.initFitChildren(),
|
|
.size_y = UI.Sizing.initGrowFull(),
|
|
.borders = .{
|
|
.right = .{ .color = srcery.hard_black, .size = 4 }
|
|
},
|
|
.layout_direction = .top_to_bottom,
|
|
.padding = UI.Padding.all(ui.rem(1)),
|
|
.layout_gap = ui.rem(0.2)
|
|
});
|
|
container.beginChildren();
|
|
defer container.endChildren();
|
|
|
|
_ = ui.createBox(.{ .size_x = UI.Sizing.initFixedPixels(ui.rem(18)) });
|
|
|
|
switch (self.side_panel) {
|
|
.project => {
|
|
try self.showProjectSettings();
|
|
},
|
|
.solutions => {
|
|
try self.showSolutions();
|
|
},
|
|
.statistics_points => {
|
|
try self.showStatisticsPoints();
|
|
},
|
|
.gain_changes => {
|
|
try self.showGainChanges();
|
|
},
|
|
.view_settings => |view_id| {
|
|
try self.showViewSettings(view_id);
|
|
},
|
|
// .marker => |args| {
|
|
// self.showMarker(args.view_id, args.index);
|
|
// },
|
|
// .marked_range => |args| {
|
|
// self.showMarkedRange(args.view_id, args.index);
|
|
// }
|
|
}
|
|
}
|
|
|
|
pub fn tick(self: *MainScreen) !void {
|
|
var ui = &self.app.ui;
|
|
|
|
if (ui.isCtrlDown() and ui.isKeyboardPressed(.key_z)) {
|
|
self.view_controls.undoLastMove();
|
|
}
|
|
|
|
const root = ui.parentBox().?;
|
|
root.layout_direction = .top_to_bottom;
|
|
|
|
// const was_modal_open = self.modal != null;
|
|
|
|
var maybe_modal_overlay: ?*UI.Box = null;
|
|
if (self.modal != null) {
|
|
const padding = UI.Padding.all(ui.rem(2));
|
|
const modal_overlay = ui.createBox(.{
|
|
.key = ui.keyFromString("Overlay"),
|
|
.float_rect = .{
|
|
.x = 0,
|
|
.y = 0,
|
|
// TODO: This is a hack, UI core should handle this
|
|
.width = root.persistent.size.x - padding.byAxis(.X),
|
|
.height = root.persistent.size.y - padding.byAxis(.Y)
|
|
},
|
|
.background = rl.Color.black.alpha(0.6),
|
|
.flags = &.{ .clickable, .scrollable },
|
|
.padding = padding,
|
|
.align_x = .center,
|
|
.align_y = .center,
|
|
});
|
|
modal_overlay.beginChildren();
|
|
defer modal_overlay.endChildren();
|
|
|
|
switch (self.modal.?) {
|
|
.notes => try self.showNotesModal(),
|
|
.add_proprotional => try self.showProportionalAdd(),
|
|
.protocol => try self.showProtocolModal()
|
|
}
|
|
|
|
if (ui.signal(modal_overlay).clicked()) {
|
|
self.modal = null;
|
|
}
|
|
|
|
maybe_modal_overlay = modal_overlay;
|
|
}
|
|
|
|
self.showToolbar();
|
|
|
|
const ui_view_ctx = UIView.Context{
|
|
.app = self.app,
|
|
.ui = &self.app.ui,
|
|
.view_controls = &self.view_controls
|
|
};
|
|
|
|
{
|
|
const container = ui.createBox(.{
|
|
.size_x = UI.Sizing.initGrowFull(),
|
|
.size_y = UI.Sizing.initGrowFull(),
|
|
.layout_direction = .left_to_right
|
|
});
|
|
container.beginChildren();
|
|
defer container.endChildren();
|
|
|
|
try self.showSidePanel();
|
|
|
|
const scroll_area = ui.beginScrollbar(ui.keyFromString("Channels"));
|
|
defer ui.endScrollbar();
|
|
scroll_area.layout_direction = .top_to_bottom;
|
|
|
|
var view_iter = self.app.project.views.idIterator();
|
|
while (view_iter.next()) |view_id| {
|
|
const view = self.app.getView(view_id);
|
|
_ = try UIView.show(ui_view_ctx, view_id, UI.Sizing.initFixed(.{ .pixels = view.?.height }));
|
|
}
|
|
}
|
|
|
|
self.view_controls.applyCommands();
|
|
|
|
if (maybe_modal_overlay) |modal_overlay| {
|
|
root.bringChildToTop(modal_overlay);
|
|
}
|
|
|
|
if (ui.isKeyboardPressed(.key_escape)) {
|
|
if (self.modal != null) {
|
|
self.modal = null;
|
|
} else if (self.side_panel != .project) {
|
|
self.side_panel = .project;
|
|
} else {
|
|
self.app.should_close = true;
|
|
}
|
|
}
|
|
|
|
// const is_modal_open = self.modal != null;
|
|
// if (!was_modal_open and is_modal_open) {
|
|
// self.protocol_graph_cache.clear();
|
|
// }
|
|
} |