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 {
|
||||
channels,
|
||||
add_from_device
|
||||
} = .channels,
|
||||
} = .add_from_device,
|
||||
|
||||
shown_modal: ?union(enum) {
|
||||
no_library_error,
|
||||
@ -111,10 +111,13 @@ shown_modal: ?union(enum) {
|
||||
} = null,
|
||||
|
||||
device_filter: NIDaq.BoundedDeviceName = .{},
|
||||
show_voltage_analog_inputs: bool = true,
|
||||
show_voltage_analog_outputs: bool = true,
|
||||
channel_type_filter: ?NIDaq.ChannelType = null,
|
||||
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 {
|
||||
self.* = App{
|
||||
.allocator = allocator,
|
||||
@ -349,6 +352,40 @@ fn findChannelIndexByName(haystack: []const [:0]const u8, needle: [:0]const u8)
|
||||
|
||||
// ------------------------------- 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 {
|
||||
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");
|
||||
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);
|
||||
defer self.ui.popParent();
|
||||
|
||||
@ -572,14 +609,102 @@ fn showChannelsWindow(self: *App) !void {
|
||||
const from_device_button = self.ui.button(.text, "Add from device");
|
||||
from_device_button.background = srcery.green;
|
||||
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 {
|
||||
const ni_daq = &(self.ni_daq orelse return);
|
||||
|
||||
const device_names = try ni_daq.listDeviceNames();
|
||||
|
||||
const window = self.ui.newBoxFromString("Device window");
|
||||
window.size.x = 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");
|
||||
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.layout_axis = .Y;
|
||||
self.ui.pushParent(filters_box);
|
||||
defer self.ui.popParent();
|
||||
|
||||
for (try ni_daq.listDeviceNames()) |device| {
|
||||
const device_box = self.ui.button(.text, device);
|
||||
device_box.size.x = UI.Size.text(2, 1);
|
||||
device_box.size.y = UI.Size.text(2, 1);
|
||||
const device_name_filter = self.ui.clickableBox("Device name filter");
|
||||
const channel_type_filter = self.ui.clickableBox("Channel type filter");
|
||||
|
||||
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);
|
||||
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");
|
||||
toggle_inputs_box.size.x = UI.Size.text(2, 1);
|
||||
toggle_inputs_box.size.y = UI.Size.text(2, 1);
|
||||
toggle_inputs_box.setText(.text, if (self.show_voltage_analog_inputs) "Hide inputs" else "Show inputs");
|
||||
device_name_filter.size.x = UI.Size.percent(1, 1);
|
||||
device_name_filter.size.y = UI.Size.pixels(24, 1);
|
||||
device_name_filter.layout_axis = .X;
|
||||
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");
|
||||
toggle_outputs_box.size.x = UI.Size.text(2, 1);
|
||||
toggle_outputs_box.size.y = UI.Size.text(2, 1);
|
||||
toggle_outputs_box.setText(.text, if (self.show_voltage_analog_outputs) "Hide outputs" else "Show outputs");
|
||||
|
||||
if (self.ui.signalFromBox(toggle_outputs_box).clicked()) {
|
||||
self.show_voltage_analog_outputs = !self.show_voltage_analog_outputs;
|
||||
}
|
||||
}
|
||||
channel_type_filter.size.x = UI.Size.percent(1, 1);
|
||||
channel_type_filter.size.y = UI.Size.pixels(24, 1);
|
||||
channel_type_filter.layout_axis = .X;
|
||||
self.ui.pushParent(channel_type_filter);
|
||||
defer self.ui.popParent();
|
||||
|
||||
{
|
||||
const add_button = self.ui.button(.text, "Add selected");
|
||||
add_button.size.x = UI.Size.text(2, 1);
|
||||
add_button.size.y = UI.Size.text(2, 1);
|
||||
|
||||
if (self.ui.signalFromBox(add_button).clicked()) {
|
||||
const selected_devices = self.selected_channels.constSlice();
|
||||
|
||||
for (selected_devices) |channel| {
|
||||
try self.appendChannelFromDevice(channel);
|
||||
self.ui.pushVerticalAlign();
|
||||
defer self.ui.popVerticalAlign();
|
||||
_ = self.ui.textureBox(Assets.dropdown_arrow, 1);
|
||||
}
|
||||
|
||||
for (selected_devices) |channel| {
|
||||
self.allocator.free(channel);
|
||||
if (self.channel_type_filter) |channeL_type| {
|
||||
_ = 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"));
|
||||
defer self.ui.popScrollbar();
|
||||
const channels_box_container = self.ui.getParentOf(channels_box).?;
|
||||
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 = &.{};
|
||||
if (self.device_filter.len > 0) {
|
||||
@ -667,23 +871,25 @@ fn showAddFromDeviceWindow(self: *App) !void {
|
||||
|
||||
for (devices) |device| {
|
||||
var ai_voltage_physical_channels: []const [:0]const u8 = &.{};
|
||||
if (self.show_voltage_analog_inputs) {
|
||||
if (try ni_daq.checkDeviceAIMeasurementType(device, .Voltage)) {
|
||||
ai_voltage_physical_channels = try ni_daq.listDeviceAIPhysicalChannels(device);
|
||||
}
|
||||
}
|
||||
|
||||
var ao_physical_channels: []const [:0]const u8 = &.{};
|
||||
if (self.show_voltage_analog_outputs) {
|
||||
if (try ni_daq.checkDeviceAOOutputType(device, .Voltage)) {
|
||||
ao_physical_channels = try ni_daq.listDeviceAOPhysicalChannels(device);
|
||||
}
|
||||
}
|
||||
|
||||
inline for (.{ ai_voltage_physical_channels, ao_physical_channels }) |channels| {
|
||||
for (channels) |channel| {
|
||||
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);
|
||||
|
||||
if (findChannelIndexByName(selected_channels_slice, channel) != null) {
|
||||
@ -699,10 +905,42 @@ 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 {
|
||||
|
@ -21,6 +21,8 @@ pub var grab_texture: struct {
|
||||
active: rl.Texture2D,
|
||||
} = undefined;
|
||||
|
||||
pub var dropdown_arrow: rl.Texture2D = undefined;
|
||||
|
||||
pub fn font(font_id: FontId) FontFace {
|
||||
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 }
|
||||
});
|
||||
|
||||
{
|
||||
const grab_ase = try Aseprite.init(allocator, @embedFile("./assets/grab-marker.ase"));
|
||||
defer grab_ase.deinit();
|
||||
|
||||
@ -57,6 +60,18 @@ pub fn init(allocator: std.mem.Allocator) !void {
|
||||
.hot = grab_hot_texture,
|
||||
.active = grab_active_texture
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
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 {
|
||||
|
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,
|
||||
Charge = c.DAQmx_Val_Charge,
|
||||
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) {
|
||||
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_output,
|
||||
counter_input,
|
||||
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 {
|
||||
const slash = std.mem.indexOfScalar(u8, device, '/') orelse return null;
|
||||
pub fn getChannelType(channel_name: []const u8) ?ChannelType {
|
||||
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")) {
|
||||
return ChannelType.analog_input;
|
||||
} 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,
|
||||
percent: f32,
|
||||
text: f32,
|
||||
texture: f32,
|
||||
children_sum,
|
||||
},
|
||||
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 {
|
||||
return Size{
|
||||
.kind = .children_sum,
|
||||
@ -183,6 +191,7 @@ pub const Signal = struct {
|
||||
flags: std.EnumSet(Flag) = .{},
|
||||
drag: Vec2 = .{ .x = 0, .y = 0 },
|
||||
scroll: Vec2 = .{ .x = 0, .y = 0 },
|
||||
hot: bool = false,
|
||||
|
||||
pub fn clicked(self: Signal) bool {
|
||||
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);
|
||||
}
|
||||
|
||||
fn hasClipping(self: *Box) bool {
|
||||
pub fn hasClipping(self: *Box) bool {
|
||||
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;
|
||||
} else if (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) {
|
||||
if (box.text) |text| {
|
||||
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);
|
||||
while (child_iter.next()) |child| {
|
||||
if (child.isPositionFixed(axis)) continue;
|
||||
|
||||
const child_size = getVec2Axis(&child.persistent.size, axis);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
if (self.getParentIndex()) |parent_index| {
|
||||
return &self.boxes.buffer[parent_index];
|
||||
@ -1448,18 +1481,27 @@ pub fn spacer(self: *UI, size: Vec2Size) void {
|
||||
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 {
|
||||
self.pushHorizontalAlign();
|
||||
|
||||
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) });
|
||||
self.pushVerticalAlign();
|
||||
|
||||
const container = self.newBox(UI.Key.initNil());
|
||||
container.size.x = UI.Size.childrenSum(1);
|
||||
@ -1472,9 +1514,7 @@ pub fn pushCenterBox(self: *UI) *Box {
|
||||
pub fn popCenterBox(self: *UI) void {
|
||||
self.popParent();
|
||||
|
||||
self.spacer(.{ .y = UI.Size.percent(1, 0) });
|
||||
self.popParent();
|
||||
|
||||
self.popVerticalAlign();
|
||||
self.popHorizontalAlign();
|
||||
}
|
||||
|
||||
@ -1494,3 +1534,20 @@ pub fn popHorizontalAlign(self: *UI) void {
|
||||
self.spacer(.{ .x = UI.Size.percent(1, 0) });
|
||||
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