daq-view/src/screens/main_screen.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 = &amplitude
// },
// };
// 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();
// }
}