Compare commits
2 Commits
7301c68b7e
...
e3588f6836
Author | SHA1 | Date | |
---|---|---|---|
e3588f6836 | |||
cf85b00084 |
326
src/app.zig
326
src/app.zig
@ -102,7 +102,7 @@ task_pool: TaskPool,
|
|||||||
shown_window: enum {
|
shown_window: enum {
|
||||||
channels,
|
channels,
|
||||||
add_from_device
|
add_from_device
|
||||||
} = .channels,
|
} = .add_from_device,
|
||||||
|
|
||||||
shown_modal: ?union(enum) {
|
shown_modal: ?union(enum) {
|
||||||
no_library_error,
|
no_library_error,
|
||||||
@ -111,10 +111,13 @@ shown_modal: ?union(enum) {
|
|||||||
} = null,
|
} = null,
|
||||||
|
|
||||||
device_filter: NIDaq.BoundedDeviceName = .{},
|
device_filter: NIDaq.BoundedDeviceName = .{},
|
||||||
show_voltage_analog_inputs: bool = true,
|
channel_type_filter: ?NIDaq.ChannelType = null,
|
||||||
show_voltage_analog_outputs: bool = true,
|
|
||||||
selected_channels: std.BoundedArray([:0]u8, max_channels) = .{},
|
selected_channels: std.BoundedArray([:0]u8, max_channels) = .{},
|
||||||
|
|
||||||
|
last_hot_channel: ?[:0]const u8 = null,
|
||||||
|
show_device_filter_dropdown: bool = false,
|
||||||
|
show_channel_type_filter_dropdown: bool = false,
|
||||||
|
|
||||||
pub fn init(self: *App, allocator: std.mem.Allocator) !void {
|
pub fn init(self: *App, allocator: std.mem.Allocator) !void {
|
||||||
self.* = App{
|
self.* = App{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
@ -349,6 +352,40 @@ fn findChannelIndexByName(haystack: []const [:0]const u8, needle: [:0]const u8)
|
|||||||
|
|
||||||
// ------------------------------- GUI -------------------------------------------- //
|
// ------------------------------- GUI -------------------------------------------- //
|
||||||
|
|
||||||
|
const Row = struct {
|
||||||
|
name: []const u8,
|
||||||
|
value: []const u8
|
||||||
|
};
|
||||||
|
|
||||||
|
fn showLabelRows(self: *App, rows: []const Row) void {
|
||||||
|
{
|
||||||
|
const name_column = self.ui.newBoxFromString("Names");
|
||||||
|
name_column.layout_axis = .Y;
|
||||||
|
name_column.size.y = UI.Size.childrenSum(1);
|
||||||
|
name_column.size.x = UI.Size.childrenSum(1);
|
||||||
|
self.ui.pushParent(name_column);
|
||||||
|
defer self.ui.popParent();
|
||||||
|
|
||||||
|
for (rows) |row| {
|
||||||
|
_ = self.ui.label(.text, row.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const value_column = self.ui.newBoxFromString("Values");
|
||||||
|
value_column.layout_axis = .Y;
|
||||||
|
value_column.size.y = UI.Size.childrenSum(1);
|
||||||
|
value_column.size.x = UI.Size.percent(1, 0);
|
||||||
|
self.ui.pushParent(value_column);
|
||||||
|
defer self.ui.popParent();
|
||||||
|
|
||||||
|
for (rows) |row| {
|
||||||
|
const label = self.ui.label(.text, row.value);
|
||||||
|
label.flags.insert(.text_wrapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn showChannelViewSlider(self: *App, view_rect: *Graph.ViewOptions, sample_count: f32) void {
|
fn showChannelViewSlider(self: *App, view_rect: *Graph.ViewOptions, sample_count: f32) void {
|
||||||
const min_visible_samples = 1; // sample_count*0.02;
|
const min_visible_samples = 1; // sample_count*0.02;
|
||||||
|
|
||||||
@ -554,7 +591,7 @@ fn showChannelsWindow(self: *App) !void {
|
|||||||
{
|
{
|
||||||
const prompt_box = self.ui.newBoxFromString("Add prompt");
|
const prompt_box = self.ui.newBoxFromString("Add prompt");
|
||||||
prompt_box.size.x = UI.Size.percent(1, 0);
|
prompt_box.size.x = UI.Size.percent(1, 0);
|
||||||
prompt_box.size.y = UI.Size.percent(1, 1);
|
prompt_box.size.y = UI.Size.pixels(200, 1);
|
||||||
self.ui.pushParent(prompt_box);
|
self.ui.pushParent(prompt_box);
|
||||||
defer self.ui.popParent();
|
defer self.ui.popParent();
|
||||||
|
|
||||||
@ -572,14 +609,102 @@ fn showChannelsWindow(self: *App) !void {
|
|||||||
const from_device_button = self.ui.button(.text, "Add from device");
|
const from_device_button = self.ui.button(.text, "Add from device");
|
||||||
from_device_button.background = srcery.green;
|
from_device_button.background = srcery.green;
|
||||||
if (self.ui.signalFromBox(from_device_button).clicked()) {
|
if (self.ui.signalFromBox(from_device_button).clicked()) {
|
||||||
log.debug("TODO: Not implemented", .{});
|
self.shown_window = .add_from_device;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn showChannelInfoPanel(self: *App, hot_channel: ?[:0]const u8) !void {
|
||||||
|
const ni_daq = &(self.ni_daq orelse return);
|
||||||
|
|
||||||
|
var device_buff: NIDaq.BoundedDeviceName = .{};
|
||||||
|
var hot_device: ?[:0]const u8 = null;
|
||||||
|
if (hot_channel) |channel| {
|
||||||
|
if (NIDaq.getDeviceNameFromChannel(channel)) |device| {
|
||||||
|
device_buff.appendSliceAssumeCapacity(device);
|
||||||
|
device_buff.buffer[device_buff.len] = 0;
|
||||||
|
hot_device = device_buff.buffer[0..device_buff.len :0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const info_box = self.ui.newBoxFromString("Info box");
|
||||||
|
info_box.layout_axis = .Y;
|
||||||
|
info_box.size.y = UI.Size.percent(1, 0);
|
||||||
|
info_box.size.x = UI.Size.percent(1, 0);
|
||||||
|
self.ui.pushParent(info_box);
|
||||||
|
defer self.ui.popParent();
|
||||||
|
|
||||||
|
if (hot_channel) |channel| {
|
||||||
|
_ = self.ui.label(.text, "Channel properties");
|
||||||
|
|
||||||
|
const channel_info = self.ui.newBoxFromString("Channel info");
|
||||||
|
channel_info.layout_axis = .X;
|
||||||
|
channel_info.size.y = UI.Size.childrenSum(1);
|
||||||
|
channel_info.size.x = UI.Size.percent(1, 0);
|
||||||
|
self.ui.pushParent(channel_info);
|
||||||
|
defer self.ui.popParent();
|
||||||
|
|
||||||
|
var rows: std.BoundedArray(Row, 16) = .{};
|
||||||
|
|
||||||
|
rows.appendAssumeCapacity(Row{
|
||||||
|
.name = "Name",
|
||||||
|
.value = channel
|
||||||
|
});
|
||||||
|
|
||||||
|
var channel_type_name: []const u8 = "unknown";
|
||||||
|
if (NIDaq.getChannelType(channel)) |channel_type| {
|
||||||
|
channel_type_name = channel_type.name();
|
||||||
|
// rows.appendAssumeCapacity(Row{
|
||||||
|
// .name = "Type",
|
||||||
|
// .value = channel_type_name
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.appendAssumeCapacity(Row{
|
||||||
|
.name = "Type",
|
||||||
|
.value = channel_type_name
|
||||||
|
});
|
||||||
|
|
||||||
|
self.showLabelRows(rows.constSlice());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ui.spacer(.{ .y = UI.Size.pixels(16, 0) });
|
||||||
|
|
||||||
|
if (hot_device) |device| {
|
||||||
|
_ = self.ui.label(.text, "Device properties");
|
||||||
|
|
||||||
|
const device_info = self.ui.newBoxFromString("Device info");
|
||||||
|
device_info.layout_axis = .X;
|
||||||
|
device_info.size.y = UI.Size.childrenSum(1);
|
||||||
|
device_info.size.x = UI.Size.percent(1, 0);
|
||||||
|
self.ui.pushParent(device_info);
|
||||||
|
defer self.ui.popParent();
|
||||||
|
|
||||||
|
var rows: std.BoundedArray(Row, 16) = .{};
|
||||||
|
|
||||||
|
if (ni_daq.listDeviceAIMeasurementTypes(device)) |measurement_types| {
|
||||||
|
rows.appendAssumeCapacity(Row{
|
||||||
|
.name = "Measurement types",
|
||||||
|
.value = try std.fmt.allocPrint(device_info.allocator, "{} types", .{measurement_types.len})
|
||||||
|
});
|
||||||
|
} else |e| {
|
||||||
|
log.err("ni_daq.listDeviceAIMeasurementTypes(): {}", .{ e });
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.appendAssumeCapacity(Row{
|
||||||
|
.name = "Foo",
|
||||||
|
.value = "bar"
|
||||||
|
});
|
||||||
|
|
||||||
|
self.showLabelRows(rows.constSlice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn showAddFromDeviceWindow(self: *App) !void {
|
fn showAddFromDeviceWindow(self: *App) !void {
|
||||||
const ni_daq = &(self.ni_daq orelse return);
|
const ni_daq = &(self.ni_daq orelse return);
|
||||||
|
|
||||||
|
const device_names = try ni_daq.listDeviceNames();
|
||||||
|
|
||||||
const window = self.ui.newBoxFromString("Device window");
|
const window = self.ui.newBoxFromString("Device window");
|
||||||
window.size.x = UI.Size.percent(1, 0);
|
window.size.x = UI.Size.percent(1, 0);
|
||||||
window.size.y = UI.Size.percent(1, 0);
|
window.size.y = UI.Size.percent(1, 0);
|
||||||
@ -589,72 +714,151 @@ fn showAddFromDeviceWindow(self: *App) !void {
|
|||||||
|
|
||||||
{
|
{
|
||||||
const filters_box = self.ui.newBoxFromString("Filters box");
|
const filters_box = self.ui.newBoxFromString("Filters box");
|
||||||
filters_box.size.x = UI.Size.percent(0.5, 1);
|
filters_box.size.x = UI.Size.percent(0.5, 0);
|
||||||
filters_box.size.y = UI.Size.percent(1, 0);
|
filters_box.size.y = UI.Size.percent(1, 0);
|
||||||
filters_box.layout_axis = .Y;
|
filters_box.layout_axis = .Y;
|
||||||
self.ui.pushParent(filters_box);
|
self.ui.pushParent(filters_box);
|
||||||
defer self.ui.popParent();
|
defer self.ui.popParent();
|
||||||
|
|
||||||
for (try ni_daq.listDeviceNames()) |device| {
|
const device_name_filter = self.ui.clickableBox("Device name filter");
|
||||||
const device_box = self.ui.button(.text, device);
|
const channel_type_filter = self.ui.clickableBox("Channel type filter");
|
||||||
device_box.size.x = UI.Size.text(2, 1);
|
|
||||||
device_box.size.y = UI.Size.text(2, 1);
|
if (self.show_device_filter_dropdown) {
|
||||||
|
const dropdown = self.ui.clickableBox("Device name dropdown");
|
||||||
|
dropdown.size.x = UI.Size.percent(1, 1);
|
||||||
|
dropdown.size.y = UI.Size.childrenSum(1);
|
||||||
|
dropdown.layout_axis = .Y;
|
||||||
|
dropdown.background = srcery.xgray2;
|
||||||
|
self.ui.pushParent(dropdown);
|
||||||
|
defer self.ui.popParent();
|
||||||
|
|
||||||
|
dropdown.setFixedPosition(
|
||||||
|
device_name_filter.persistent.position.add(.{ .x = 0, .y = device_name_filter.persistent.size.y })
|
||||||
|
);
|
||||||
|
|
||||||
|
{
|
||||||
|
const device_box = self.ui.button(.text, "All");
|
||||||
|
device_box.size.x = UI.Size.percent(1, 1);
|
||||||
|
device_box.size.y = UI.Size.text(0.5, 1);
|
||||||
|
device_box.flags.insert(.text_left_align);
|
||||||
|
|
||||||
|
if (self.ui.signalFromBox(device_box).clicked()) {
|
||||||
|
self.device_filter.len = 0;
|
||||||
|
self.show_device_filter_dropdown = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (device_names) |device_name| {
|
||||||
|
const device_box = self.ui.button(.text, device_name);
|
||||||
|
device_box.size.x = UI.Size.percent(1, 1);
|
||||||
|
device_box.size.y = UI.Size.text(0.5, 1);
|
||||||
|
device_box.flags.insert(.text_left_align);
|
||||||
|
|
||||||
const signal = self.ui.signalFromBox(device_box);
|
const signal = self.ui.signalFromBox(device_box);
|
||||||
if (signal.clicked()) {
|
if (signal.clicked()) {
|
||||||
self.device_filter = try NIDaq.BoundedDeviceName.fromSlice(device);
|
self.device_filter = try NIDaq.BoundedDeviceName.fromSlice(device_name);
|
||||||
|
self.show_device_filter_dropdown = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.show_channel_type_filter_dropdown) {
|
||||||
|
const dropdown = self.ui.clickableBox("Channel type dropdown");
|
||||||
|
dropdown.size.x = UI.Size.percent(1, 1);
|
||||||
|
dropdown.size.y = UI.Size.childrenSum(1);
|
||||||
|
dropdown.layout_axis = .Y;
|
||||||
|
dropdown.background = srcery.xgray2;
|
||||||
|
self.ui.pushParent(dropdown);
|
||||||
|
defer self.ui.popParent();
|
||||||
|
|
||||||
|
dropdown.setFixedPosition(
|
||||||
|
channel_type_filter.persistent.position.add(.{ .x = 0, .y = channel_type_filter.persistent.size.y })
|
||||||
|
);
|
||||||
|
|
||||||
|
{
|
||||||
|
const device_box = self.ui.button(.text, "All");
|
||||||
|
device_box.size.x = UI.Size.percent(1, 1);
|
||||||
|
device_box.size.y = UI.Size.text(0.5, 1);
|
||||||
|
device_box.flags.insert(.text_left_align);
|
||||||
|
|
||||||
|
if (self.ui.signalFromBox(device_box).clicked()) {
|
||||||
|
self.channel_type_filter = null;
|
||||||
|
self.show_channel_type_filter_dropdown = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (&[_]NIDaq.ChannelType{ NIDaq.ChannelType.analog_input, NIDaq.ChannelType.analog_output }) |channel_type| {
|
||||||
|
const device_box = self.ui.button(.text, channel_type.name());
|
||||||
|
device_box.size.x = UI.Size.percent(1, 1);
|
||||||
|
device_box.size.y = UI.Size.text(0.5, 1);
|
||||||
|
device_box.flags.insert(.text_left_align);
|
||||||
|
|
||||||
|
if (self.ui.signalFromBox(device_box).clicked()) {
|
||||||
|
self.channel_type_filter = channel_type;
|
||||||
|
self.show_channel_type_filter_dropdown = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const toggle_inputs_box = self.ui.button(.text, "Toggle inputs");
|
device_name_filter.size.x = UI.Size.percent(1, 1);
|
||||||
toggle_inputs_box.size.x = UI.Size.text(2, 1);
|
device_name_filter.size.y = UI.Size.pixels(24, 1);
|
||||||
toggle_inputs_box.size.y = UI.Size.text(2, 1);
|
device_name_filter.layout_axis = .X;
|
||||||
toggle_inputs_box.setText(.text, if (self.show_voltage_analog_inputs) "Hide inputs" else "Show inputs");
|
self.ui.pushParent(device_name_filter);
|
||||||
|
defer self.ui.popParent();
|
||||||
|
|
||||||
if (self.ui.signalFromBox(toggle_inputs_box).clicked()) {
|
{
|
||||||
self.show_voltage_analog_inputs = !self.show_voltage_analog_inputs;
|
self.ui.pushVerticalAlign();
|
||||||
|
defer self.ui.popVerticalAlign();
|
||||||
|
_ = self.ui.textureBox(Assets.dropdown_arrow, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (self.device_filter.len > 0) {
|
||||||
|
_ = self.ui.label(.text, self.device_filter.constSlice());
|
||||||
|
} else {
|
||||||
|
_ = self.ui.label(.text, "All");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.ui.signalFromBox(device_name_filter).clicked()) {
|
||||||
|
self.show_device_filter_dropdown = !self.show_device_filter_dropdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ui.spacer(.{ .x = UI.Size.percent(1, 0) });
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const toggle_outputs_box = self.ui.button(.text, "Toggle outputs");
|
channel_type_filter.size.x = UI.Size.percent(1, 1);
|
||||||
toggle_outputs_box.size.x = UI.Size.text(2, 1);
|
channel_type_filter.size.y = UI.Size.pixels(24, 1);
|
||||||
toggle_outputs_box.size.y = UI.Size.text(2, 1);
|
channel_type_filter.layout_axis = .X;
|
||||||
toggle_outputs_box.setText(.text, if (self.show_voltage_analog_outputs) "Hide outputs" else "Show outputs");
|
self.ui.pushParent(channel_type_filter);
|
||||||
|
defer self.ui.popParent();
|
||||||
if (self.ui.signalFromBox(toggle_outputs_box).clicked()) {
|
|
||||||
self.show_voltage_analog_outputs = !self.show_voltage_analog_outputs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
const add_button = self.ui.button(.text, "Add selected");
|
self.ui.pushVerticalAlign();
|
||||||
add_button.size.x = UI.Size.text(2, 1);
|
defer self.ui.popVerticalAlign();
|
||||||
add_button.size.y = UI.Size.text(2, 1);
|
_ = self.ui.textureBox(Assets.dropdown_arrow, 1);
|
||||||
|
|
||||||
if (self.ui.signalFromBox(add_button).clicked()) {
|
|
||||||
const selected_devices = self.selected_channels.constSlice();
|
|
||||||
|
|
||||||
for (selected_devices) |channel| {
|
|
||||||
try self.appendChannelFromDevice(channel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (selected_devices) |channel| {
|
if (self.channel_type_filter) |channeL_type| {
|
||||||
self.allocator.free(channel);
|
_ = self.ui.label(.text, channeL_type.name());
|
||||||
|
} else {
|
||||||
|
_ = self.ui.label(.text, "All");
|
||||||
}
|
}
|
||||||
self.selected_channels.len = 0;
|
|
||||||
|
|
||||||
self.shown_window = .channels;
|
if (self.ui.signalFromBox(channel_type_filter).clicked()) {
|
||||||
|
self.show_channel_type_filter_dropdown = !self.show_channel_type_filter_dropdown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hot_channel: ?[:0]const u8 = self.last_hot_channel;
|
||||||
{
|
{
|
||||||
const channels_box = self.ui.pushScrollbar(self.ui.newKeyFromString("Channels list"));
|
const channels_box = self.ui.pushScrollbar(self.ui.newKeyFromString("Channels list"));
|
||||||
defer self.ui.popScrollbar();
|
defer self.ui.popScrollbar();
|
||||||
|
const channels_box_container = self.ui.getParentOf(channels_box).?;
|
||||||
channels_box.layout_axis = .Y;
|
channels_box.layout_axis = .Y;
|
||||||
channels_box.size.x = UI.Size.percent(1, 0);
|
//channels_box.size.x = UI.Size.childrenSum(1);
|
||||||
|
channels_box_container.size.x = UI.Size.percent(1, 0);
|
||||||
|
|
||||||
var devices: []const [:0]const u8 = &.{};
|
var devices: []const [:0]const u8 = &.{};
|
||||||
if (self.device_filter.len > 0) {
|
if (self.device_filter.len > 0) {
|
||||||
@ -667,23 +871,25 @@ fn showAddFromDeviceWindow(self: *App) !void {
|
|||||||
|
|
||||||
for (devices) |device| {
|
for (devices) |device| {
|
||||||
var ai_voltage_physical_channels: []const [:0]const u8 = &.{};
|
var ai_voltage_physical_channels: []const [:0]const u8 = &.{};
|
||||||
if (self.show_voltage_analog_inputs) {
|
|
||||||
if (try ni_daq.checkDeviceAIMeasurementType(device, .Voltage)) {
|
if (try ni_daq.checkDeviceAIMeasurementType(device, .Voltage)) {
|
||||||
ai_voltage_physical_channels = try ni_daq.listDeviceAIPhysicalChannels(device);
|
ai_voltage_physical_channels = try ni_daq.listDeviceAIPhysicalChannels(device);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var ao_physical_channels: []const [:0]const u8 = &.{};
|
var ao_physical_channels: []const [:0]const u8 = &.{};
|
||||||
if (self.show_voltage_analog_outputs) {
|
|
||||||
if (try ni_daq.checkDeviceAOOutputType(device, .Voltage)) {
|
if (try ni_daq.checkDeviceAOOutputType(device, .Voltage)) {
|
||||||
ao_physical_channels = try ni_daq.listDeviceAOPhysicalChannels(device);
|
ao_physical_channels = try ni_daq.listDeviceAOPhysicalChannels(device);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
inline for (.{ ai_voltage_physical_channels, ao_physical_channels }) |channels| {
|
inline for (.{ ai_voltage_physical_channels, ao_physical_channels }) |channels| {
|
||||||
for (channels) |channel| {
|
for (channels) |channel| {
|
||||||
const selected_channels_slice = self.selected_channels.constSlice();
|
const selected_channels_slice = self.selected_channels.constSlice();
|
||||||
|
|
||||||
|
if (self.channel_type_filter) |channel_type_filter| {
|
||||||
|
if (NIDaq.getChannelType(channel) != channel_type_filter) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const channel_box = self.ui.button(.text, channel);
|
const channel_box = self.ui.button(.text, channel);
|
||||||
|
|
||||||
if (findChannelIndexByName(selected_channels_slice, channel) != null) {
|
if (findChannelIndexByName(selected_channels_slice, channel) != null) {
|
||||||
@ -699,12 +905,44 @@ fn showAddFromDeviceWindow(self: *App) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (signal.hot) {
|
||||||
|
hot_channel = channel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const left_panel = self.ui.newBox(UI.Key.initNil());
|
||||||
|
left_panel.layout_axis = .Y;
|
||||||
|
left_panel.size.y = UI.Size.percent(1, 0);
|
||||||
|
left_panel.size.x = UI.Size.percent(1, 0);
|
||||||
|
self.ui.pushParent(left_panel);
|
||||||
|
defer self.ui.popParent();
|
||||||
|
|
||||||
|
try self.showChannelInfoPanel(hot_channel);
|
||||||
|
|
||||||
|
const add_button = self.ui.button(.text, "Add");
|
||||||
|
if (self.ui.signalFromBox(add_button).clicked()) {
|
||||||
|
for (self.selected_channels.constSlice()) |channel_name| {
|
||||||
|
try self.appendChannelFromDevice(channel_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.shown_window = .channels;
|
||||||
|
for (self.selected_channels.constSlice()) |channel| {
|
||||||
|
self.allocator.free(channel);
|
||||||
|
}
|
||||||
|
self.selected_channels.len = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hot_channel != null) {
|
||||||
|
self.last_hot_channel = hot_channel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn showToolbar(self: *App) void {
|
fn showToolbar(self: *App) void {
|
||||||
const toolbar = self.ui.newBoxFromString("Toolbar");
|
const toolbar = self.ui.newBoxFromString("Toolbar");
|
||||||
toolbar.background = rl.Color.green;
|
toolbar.background = rl.Color.green;
|
||||||
|
@ -21,6 +21,8 @@ pub var grab_texture: struct {
|
|||||||
active: rl.Texture2D,
|
active: rl.Texture2D,
|
||||||
} = undefined;
|
} = undefined;
|
||||||
|
|
||||||
|
pub var dropdown_arrow: rl.Texture2D = undefined;
|
||||||
|
|
||||||
pub fn font(font_id: FontId) FontFace {
|
pub fn font(font_id: FontId) FontFace {
|
||||||
return fonts.get(font_id);
|
return fonts.get(font_id);
|
||||||
}
|
}
|
||||||
@ -34,6 +36,7 @@ pub fn init(allocator: std.mem.Allocator) !void {
|
|||||||
.text = FontFace{ .font = default_font, .line_height = 1.2 }
|
.text = FontFace{ .font = default_font, .line_height = 1.2 }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
{
|
||||||
const grab_ase = try Aseprite.init(allocator, @embedFile("./assets/grab-marker.ase"));
|
const grab_ase = try Aseprite.init(allocator, @embedFile("./assets/grab-marker.ase"));
|
||||||
defer grab_ase.deinit();
|
defer grab_ase.deinit();
|
||||||
|
|
||||||
@ -59,6 +62,18 @@ pub fn init(allocator: std.mem.Allocator) !void {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const dropdown_arrow_ase = try Aseprite.init(allocator, @embedFile("./assets/dropdown-arrow.ase"));
|
||||||
|
defer dropdown_arrow_ase.deinit();
|
||||||
|
|
||||||
|
const dropdown_image = dropdown_arrow_ase.getFrameImage(0);
|
||||||
|
defer dropdown_image.unload();
|
||||||
|
|
||||||
|
dropdown_arrow = rl.loadTextureFromImage(dropdown_image);
|
||||||
|
assert(rl.isTextureReady(dropdown_arrow));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn loadFont(ttf_data: []const u8, font_size: u32) !rl.Font {
|
fn loadFont(ttf_data: []const u8, font_size: u32) !rl.Font {
|
||||||
var codepoints: std.BoundedArray(i32, 128) = .{};
|
var codepoints: std.BoundedArray(i32, 128) = .{};
|
||||||
for (0..95) |i| {
|
for (0..95) |i| {
|
||||||
|
BIN
src/assets/dropdown-arrow.ase
Normal file
BIN
src/assets/dropdown-arrow.ase
Normal file
Binary file not shown.
@ -225,9 +225,46 @@ pub const AIMeasurementType = enum(i32) {
|
|||||||
TEDS_Sensor = c.DAQmx_Val_TEDS_Sensor,
|
TEDS_Sensor = c.DAQmx_Val_TEDS_Sensor,
|
||||||
Charge = c.DAQmx_Val_Charge,
|
Charge = c.DAQmx_Val_Charge,
|
||||||
Power = c.DAQmx_Val_Power,
|
Power = c.DAQmx_Val_Power,
|
||||||
_
|
_,
|
||||||
|
|
||||||
|
pub fn name(self: AIMeasurementType) []const u8 {
|
||||||
|
return switch (self) {
|
||||||
|
.Voltage => "Voltage",
|
||||||
|
.VoltageRMS => "Voltage RMS",
|
||||||
|
.Current => "Current",
|
||||||
|
.CurrentRMS => "Current RMS",
|
||||||
|
.Voltage_CustomWithExcitation => "Voltage (custom excitation)",
|
||||||
|
.Bridge => "Bridge",
|
||||||
|
.Freq_Voltage => "Frequency (frequency to voltage converter)",
|
||||||
|
.Resistance => "Resistance",
|
||||||
|
.Temp_TC => "Temperature (thermocouple)",
|
||||||
|
.Temp_Thrmstr => "Temperature (thermistor)",
|
||||||
|
.Temp_RTD => "Temperature (RTD)",
|
||||||
|
.Temp_BuiltInSensor => "Temperature (built-in)",
|
||||||
|
.Strain_Gage => "Strain",
|
||||||
|
.Rosette_Strain_Gage => "Strain (Rosette strain gage)",
|
||||||
|
.Position_LVDT => "Position (LVDT)",
|
||||||
|
.Position_RVDT => "Position (RVDT)",
|
||||||
|
.Position_EddyCurrentProximityProbe => "Position (eddy current proximity probe)",
|
||||||
|
.Accelerometer => "Acceleration",
|
||||||
|
.Acceleration_Charge => "Acceleration (charge-based)",
|
||||||
|
.Acceleration_4WireDCVoltage => "Acceleration (4 wire DC voltage based)",
|
||||||
|
.Velocity_IEPESensor => "Velocity (IEPE Sensor)",
|
||||||
|
.Force_Bridge => "Force (bridge-based)",
|
||||||
|
.Force_IEPESensor => "Force (IEPE Sensor)",
|
||||||
|
.Pressure_Bridge => "Pressure (bridge-based)",
|
||||||
|
.SoundPressure_Microphone => "Sound pressure (microphone)",
|
||||||
|
.Torque_Bridge => "Torque (bridge-based)",
|
||||||
|
.TEDS_Sensor => "TEDS",
|
||||||
|
.Charge => "Charge",
|
||||||
|
.Power => "Power source",
|
||||||
|
_ => "Unknown"
|
||||||
};
|
};
|
||||||
const AIMeasurementTypeList = std.BoundedArray(AIMeasurementType, @typeInfo(AIMeasurementType).Enum.fields.len);
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const max_ai_measurement_type_list_len = @typeInfo(AIMeasurementType).Enum.fields.len;
|
||||||
|
pub const AIMeasurementTypeList = std.BoundedArray(AIMeasurementType, max_ai_measurement_type_list_len);
|
||||||
|
|
||||||
pub const AOOutputType = enum(i32) {
|
pub const AOOutputType = enum(i32) {
|
||||||
Voltage = c.DAQmx_Val_Voltage,
|
Voltage = c.DAQmx_Val_Voltage,
|
||||||
@ -632,17 +669,26 @@ pub fn listDeviceCIPhysicalChannels(self: *NIDaq, device: [:0]const u8) ![]const
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChannelType = enum {
|
pub const ChannelType = enum {
|
||||||
analog_input,
|
analog_input,
|
||||||
analog_output,
|
analog_output,
|
||||||
counter_input,
|
counter_input,
|
||||||
counter_output,
|
counter_output,
|
||||||
|
|
||||||
|
pub fn name(self: ChannelType) []const u8 {
|
||||||
|
return switch (self) {
|
||||||
|
.analog_input => "Analog input",
|
||||||
|
.analog_output => "Analog output",
|
||||||
|
.counter_input => "Counter input",
|
||||||
|
.counter_output => "Counter output",
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn getChannelType(device: [:0]const u8) ?ChannelType {
|
pub fn getChannelType(channel_name: []const u8) ?ChannelType {
|
||||||
const slash = std.mem.indexOfScalar(u8, device, '/') orelse return null;
|
const slash = std.mem.indexOfScalar(u8, channel_name, '/') orelse return null;
|
||||||
|
|
||||||
const afterSlash = device[(slash+1)..];
|
const afterSlash = channel_name[(slash+1)..];
|
||||||
if (std.mem.startsWith(u8, afterSlash, "ai")) {
|
if (std.mem.startsWith(u8, afterSlash, "ai")) {
|
||||||
return ChannelType.analog_input;
|
return ChannelType.analog_input;
|
||||||
} else if (std.mem.startsWith(u8, afterSlash, "ao")) {
|
} else if (std.mem.startsWith(u8, afterSlash, "ao")) {
|
||||||
|
85
src/ui.zig
85
src/ui.zig
@ -60,6 +60,7 @@ pub const Size = struct {
|
|||||||
pixels: f32,
|
pixels: f32,
|
||||||
percent: f32,
|
percent: f32,
|
||||||
text: f32,
|
text: f32,
|
||||||
|
texture: f32,
|
||||||
children_sum,
|
children_sum,
|
||||||
},
|
},
|
||||||
strictness: f32 = 1,
|
strictness: f32 = 1,
|
||||||
@ -85,6 +86,13 @@ pub const Size = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn texture(scale: f32, strictness: f32) Size {
|
||||||
|
return Size{
|
||||||
|
.kind = .{ .texture = scale },
|
||||||
|
.strictness = strictness
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn childrenSum(strictness: f32) Size {
|
pub fn childrenSum(strictness: f32) Size {
|
||||||
return Size{
|
return Size{
|
||||||
.kind = .children_sum,
|
.kind = .children_sum,
|
||||||
@ -183,6 +191,7 @@ pub const Signal = struct {
|
|||||||
flags: std.EnumSet(Flag) = .{},
|
flags: std.EnumSet(Flag) = .{},
|
||||||
drag: Vec2 = .{ .x = 0, .y = 0 },
|
drag: Vec2 = .{ .x = 0, .y = 0 },
|
||||||
scroll: Vec2 = .{ .x = 0, .y = 0 },
|
scroll: Vec2 = .{ .x = 0, .y = 0 },
|
||||||
|
hot: bool = false,
|
||||||
|
|
||||||
pub fn clicked(self: Signal) bool {
|
pub fn clicked(self: Signal) bool {
|
||||||
return self.flags.contains(.left_clicked) or self.flags.contains(.right_clicked);
|
return self.flags.contains(.left_clicked) or self.flags.contains(.right_clicked);
|
||||||
@ -413,7 +422,7 @@ pub const Box = struct {
|
|||||||
return self.flags.contains(flag);
|
return self.flags.contains(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hasClipping(self: *Box) bool {
|
pub fn hasClipping(self: *Box) bool {
|
||||||
return self.view_offset.equals(Vec2.zero()) == 0;
|
return self.view_offset.equals(Vec2.zero()) == 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -791,6 +800,18 @@ fn calcLayoutStandaloneSize(self: *UI, box: *Box, axis: Axis) void {
|
|||||||
computed_size.* = fixed_size;
|
computed_size.* = fixed_size;
|
||||||
} else if (size.kind == .pixels) {
|
} else if (size.kind == .pixels) {
|
||||||
computed_size.* = size.kind.pixels;
|
computed_size.* = size.kind.pixels;
|
||||||
|
} else if (size.kind == .texture) {
|
||||||
|
if (box.texture) |texture| {
|
||||||
|
var texture_size: f32 = 0;
|
||||||
|
if (axis == .X) {
|
||||||
|
texture_size = @floatFromInt(texture.width);
|
||||||
|
} else if (axis == .Y) {
|
||||||
|
texture_size = @floatFromInt(texture.height);
|
||||||
|
}
|
||||||
|
computed_size.* = size.kind.texture * texture_size;
|
||||||
|
} else {
|
||||||
|
computed_size.* = 0;
|
||||||
|
}
|
||||||
} else if (size.kind == .text) {
|
} else if (size.kind == .text) {
|
||||||
if (box.text) |text| {
|
if (box.text) |text| {
|
||||||
const font = Assets.font(text.font);
|
const font = Assets.font(text.font);
|
||||||
@ -900,6 +921,8 @@ fn calcLayoutEnforceConstraints(self: *UI, box: *Box, axis: Axis) void {
|
|||||||
|
|
||||||
var child_iter = self.iterChildrenByParent(box);
|
var child_iter = self.iterChildrenByParent(box);
|
||||||
while (child_iter.next()) |child| {
|
while (child_iter.next()) |child| {
|
||||||
|
if (child.isPositionFixed(axis)) continue;
|
||||||
|
|
||||||
const child_size = getVec2Axis(&child.persistent.size, axis);
|
const child_size = getVec2Axis(&child.persistent.size, axis);
|
||||||
|
|
||||||
if (child_size.* > max_child_size) {
|
if (child_size.* > max_child_size) {
|
||||||
@ -1266,6 +1289,8 @@ pub fn signalFromBox(self: *UI, box: *Box) Signal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.hot = self.isKeyHot(box.key);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1305,6 +1330,14 @@ fn getParentIndex(self: *UI) ?BoxIndex {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getParentOf(self: *UI, box: *Box) ?*Box {
|
||||||
|
if (box.parent_index) |index| {
|
||||||
|
return &self.boxes.buffer[index];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getParent(self: *UI) ?*Box {
|
pub fn getParent(self: *UI) ?*Box {
|
||||||
if (self.getParentIndex()) |parent_index| {
|
if (self.getParentIndex()) |parent_index| {
|
||||||
return &self.boxes.buffer[parent_index];
|
return &self.boxes.buffer[parent_index];
|
||||||
@ -1448,18 +1481,27 @@ pub fn spacer(self: *UI, size: Vec2Size) void {
|
|||||||
box.size = size;
|
box.size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn label(self: *UI, font: Assets.FontId, text: []const u8) *Box {
|
||||||
|
const box = self.newBoxFromString(text);
|
||||||
|
box.size.x = UI.Size.text(1, 1);
|
||||||
|
box.size.y = UI.Size.text(0.5, 1);
|
||||||
|
box.setText(font, text);
|
||||||
|
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn textureBox(self: *UI, texture: rl.Texture2D, scale: f32) *Box {
|
||||||
|
const box = self.newBox(Key.initNil());
|
||||||
|
box.size.x = UI.Size.texture(scale, 1);
|
||||||
|
box.size.y = UI.Size.texture(scale, 1);
|
||||||
|
box.texture = texture;
|
||||||
|
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn pushCenterBox(self: *UI) *Box {
|
pub fn pushCenterBox(self: *UI) *Box {
|
||||||
self.pushHorizontalAlign();
|
self.pushHorizontalAlign();
|
||||||
|
self.pushVerticalAlign();
|
||||||
const vertical_align = self.newBox(UI.Key.initNil());
|
|
||||||
vertical_align.layout_axis = .Y;
|
|
||||||
vertical_align.size = .{
|
|
||||||
.x = UI.Size.childrenSum(1),
|
|
||||||
.y = UI.Size.percent(1, 0),
|
|
||||||
};
|
|
||||||
self.pushParent(vertical_align);
|
|
||||||
|
|
||||||
self.spacer(.{ .y = UI.Size.percent(1, 0) });
|
|
||||||
|
|
||||||
const container = self.newBox(UI.Key.initNil());
|
const container = self.newBox(UI.Key.initNil());
|
||||||
container.size.x = UI.Size.childrenSum(1);
|
container.size.x = UI.Size.childrenSum(1);
|
||||||
@ -1472,9 +1514,7 @@ pub fn pushCenterBox(self: *UI) *Box {
|
|||||||
pub fn popCenterBox(self: *UI) void {
|
pub fn popCenterBox(self: *UI) void {
|
||||||
self.popParent();
|
self.popParent();
|
||||||
|
|
||||||
self.spacer(.{ .y = UI.Size.percent(1, 0) });
|
self.popVerticalAlign();
|
||||||
self.popParent();
|
|
||||||
|
|
||||||
self.popHorizontalAlign();
|
self.popHorizontalAlign();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1494,3 +1534,20 @@ pub fn popHorizontalAlign(self: *UI) void {
|
|||||||
self.spacer(.{ .x = UI.Size.percent(1, 0) });
|
self.spacer(.{ .x = UI.Size.percent(1, 0) });
|
||||||
self.popParent();
|
self.popParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pushVerticalAlign(self: *UI) void {
|
||||||
|
const vertical_align = self.newBox(UI.Key.initNil());
|
||||||
|
vertical_align.layout_axis = .Y;
|
||||||
|
vertical_align.size = .{
|
||||||
|
.x = UI.Size.childrenSum(1),
|
||||||
|
.y = UI.Size.percent(1, 0),
|
||||||
|
};
|
||||||
|
self.pushParent(vertical_align);
|
||||||
|
|
||||||
|
self.spacer(.{ .y = UI.Size.percent(1, 0) });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn popVerticalAlign(self: *UI) void {
|
||||||
|
self.spacer(.{ .y = UI.Size.percent(1, 0) });
|
||||||
|
self.popParent();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user