add a rudimentary "add from device" window
This commit is contained in:
parent
a87a9c104e
commit
65ad8d7786
@ -3,3 +3,7 @@
|
|||||||
```shell
|
```shell
|
||||||
zig build run
|
zig build run
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
* Use downsampling for faster rendering of samples. When viewing many samples use dowsampled versions of data for rendering. Because you either way, you won't be able to see the detail.
|
197
src/app.zig
197
src/app.zig
@ -15,6 +15,8 @@ const clamp = std.math.clamp;
|
|||||||
|
|
||||||
const App = @This();
|
const App = @This();
|
||||||
|
|
||||||
|
const max_channels = 64;
|
||||||
|
|
||||||
const Channel = struct {
|
const Channel = struct {
|
||||||
view_cache: Graph.Cache = .{},
|
view_cache: Graph.Cache = .{},
|
||||||
view_rect: Graph.ViewOptions,
|
view_rect: Graph.ViewOptions,
|
||||||
@ -31,7 +33,7 @@ const Channel = struct {
|
|||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
|
|
||||||
ui: UI,
|
ui: UI,
|
||||||
channels: std.BoundedArray(Channel, 64) = .{},
|
channels: std.BoundedArray(Channel, max_channels) = .{},
|
||||||
ni_daq: NIDaq,
|
ni_daq: NIDaq,
|
||||||
|
|
||||||
shown_window: enum {
|
shown_window: enum {
|
||||||
@ -39,6 +41,11 @@ shown_window: enum {
|
|||||||
add_from_device
|
add_from_device
|
||||||
} = .channels,
|
} = .channels,
|
||||||
|
|
||||||
|
device_filter: NIDaq.BoundedDeviceName = .{},
|
||||||
|
show_voltage_analog_inputs: bool = true,
|
||||||
|
show_voltage_analog_outputs: bool = true,
|
||||||
|
selected_channels: std.BoundedArray([:0]u8, max_channels) = .{},
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator) !App {
|
pub fn init(allocator: std.mem.Allocator) !App {
|
||||||
return App{
|
return App{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
@ -51,7 +58,7 @@ pub fn init(allocator: std.mem.Allocator) !App {
|
|||||||
.max_counter_inputs = 8,
|
.max_counter_inputs = 8,
|
||||||
.max_analog_input_voltage_ranges = 4,
|
.max_analog_input_voltage_ranges = 4,
|
||||||
.max_analog_output_voltage_ranges = 4
|
.max_analog_output_voltage_ranges = 4
|
||||||
}),
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +71,11 @@ pub fn deinit(self: *App) void {
|
|||||||
channel.view_cache.deinit();
|
channel.view_cache.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (self.selected_channels.constSlice()) |channel| {
|
||||||
|
self.allocator.free(channel);
|
||||||
|
}
|
||||||
|
self.selected_channels.len = 0;
|
||||||
|
|
||||||
self.ui.deinit();
|
self.ui.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,9 +292,182 @@ fn showChannelsWindow(self: *App) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn findChannelIndexByName(haystack: []const [:0]const u8, needle: [:0]const u8) ?usize {
|
||||||
|
for (0.., haystack) |i, item| {
|
||||||
|
if (std.mem.eql(u8, item, needle)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
fn showAddFromDeviceWindow(self: *App) !void {
|
fn showAddFromDeviceWindow(self: *App) !void {
|
||||||
const names = try self.ni_daq.listDeviceNames();
|
const window = self.ui.newBoxFromString("Device window");
|
||||||
_ = names;
|
window.size.x = UI.Size.percent(1, 0);
|
||||||
|
window.size.y = UI.Size.percent(1, 0);
|
||||||
|
window.layout_axis = .X;
|
||||||
|
self.ui.pushParent(window);
|
||||||
|
defer self.ui.popParent();
|
||||||
|
|
||||||
|
{
|
||||||
|
const filters_box = self.ui.newBoxFromString("Filters box");
|
||||||
|
filters_box.size.x = UI.Size.percent(0.5, 1);
|
||||||
|
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 self.ni_daq.listDeviceNames()) |device| {
|
||||||
|
const device_box = self.ui.newBoxFromString(device);
|
||||||
|
device_box.flags.insert(.clickable);
|
||||||
|
device_box.size.x = UI.Size.text(2, 1);
|
||||||
|
device_box.size.y = UI.Size.text(2, 1);
|
||||||
|
device_box.setText(.text, device);
|
||||||
|
|
||||||
|
const signal = self.ui.signalFromBox(device_box);
|
||||||
|
if (signal.clicked()) {
|
||||||
|
self.device_filter = try NIDaq.BoundedDeviceName.fromSlice(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const toggle_inputs_box = self.ui.newBoxFromString("Toggle inputs");
|
||||||
|
toggle_inputs_box.flags.insert(.clickable);
|
||||||
|
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");
|
||||||
|
const signal = self.ui.signalFromBox(toggle_inputs_box);
|
||||||
|
if (signal.clicked()) {
|
||||||
|
self.show_voltage_analog_inputs = !self.show_voltage_analog_inputs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const toggle_outputs_box = self.ui.newBoxFromString("Toggle outputs");
|
||||||
|
toggle_outputs_box.flags.insert(.clickable);
|
||||||
|
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");
|
||||||
|
const signal = self.ui.signalFromBox(toggle_outputs_box);
|
||||||
|
if (signal.clicked()) {
|
||||||
|
self.show_voltage_analog_outputs = !self.show_voltage_analog_outputs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const add_button = self.ui.newBoxFromString("Add");
|
||||||
|
add_button.flags.insert(.clickable);
|
||||||
|
add_button.size.x = UI.Size.text(2, 1);
|
||||||
|
add_button.size.y = UI.Size.text(2, 1);
|
||||||
|
add_button.setText(.text, "Add selected");
|
||||||
|
const signal = self.ui.signalFromBox(add_button);
|
||||||
|
if (signal.clicked()) {
|
||||||
|
const selected_devices = self.selected_channels.constSlice();
|
||||||
|
std.debug.print("{s}\n", .{selected_devices});
|
||||||
|
|
||||||
|
for (self.selected_channels.constSlice()) |channel| {
|
||||||
|
self.allocator.free(channel);
|
||||||
|
}
|
||||||
|
self.selected_channels.len = 0;
|
||||||
|
|
||||||
|
self.shown_window = .channels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const channels_box = self.ui.pushScrollbar(self.ui.newKeyFromString("Channels list"));
|
||||||
|
defer self.ui.popScrollbar();
|
||||||
|
channels_box.layout_axis = .Y;
|
||||||
|
channels_box.size.x = UI.Size.percent(1, 0);
|
||||||
|
|
||||||
|
var devices: []const [:0]const u8 = &.{};
|
||||||
|
if (self.device_filter.len > 0) {
|
||||||
|
devices = &.{
|
||||||
|
self.device_filter.buffer[0..self.device_filter.len :0]
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
devices = try self.ni_daq.listDeviceNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (devices) |device| {
|
||||||
|
var ai_voltage_physical_channels: []const [:0]const u8 = &.{};
|
||||||
|
if (self.show_voltage_analog_inputs) {
|
||||||
|
if (try self.ni_daq.checkDeviceAIMeasurementType(device, .Voltage)) {
|
||||||
|
ai_voltage_physical_channels = try self.ni_daq.listDeviceAIPhysicalChannels(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ao_physical_channels: []const [:0]const u8 = &.{};
|
||||||
|
if (self.show_voltage_analog_outputs) {
|
||||||
|
if (try self.ni_daq.checkDeviceAOOutputType(device, .Voltage)) {
|
||||||
|
ao_physical_channels = try self.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();
|
||||||
|
|
||||||
|
const channel_box = self.ui.newBoxFromString(channel);
|
||||||
|
channel_box.flags.insert(.clickable);
|
||||||
|
channel_box.size.x = UI.Size.text(1, 1);
|
||||||
|
channel_box.size.y = UI.Size.text(0.5, 1);
|
||||||
|
channel_box.setText(.text, channel);
|
||||||
|
|
||||||
|
if (findChannelIndexByName(selected_channels_slice, channel) != null) {
|
||||||
|
channel_box.background = srcery.xgray3;
|
||||||
|
}
|
||||||
|
|
||||||
|
const signal = self.ui.signalFromBox(channel_box);
|
||||||
|
if (signal.clicked()) {
|
||||||
|
if (findChannelIndexByName(selected_channels_slice, channel)) |index| {
|
||||||
|
self.allocator.free(self.selected_channels.swapRemove(index));
|
||||||
|
} else {
|
||||||
|
self.selected_channels.appendAssumeCapacity(try self.allocator.dupeZ(u8, channel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (self.selected_device.len > 0) {
|
||||||
|
// const device: [:0]u8 = self.selected_device.buffer[0..self.selected_device.len :0];
|
||||||
|
|
||||||
|
// var ai_voltage_physical_channels: []const [:0]const u8 = &.{};
|
||||||
|
// if (try self.ni_daq.checkDeviceAIMeasurementType(device, .Voltage)) {
|
||||||
|
// ai_voltage_physical_channels = try self.ni_daq.listDeviceAIPhysicalChannels(device);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var ao_physical_channels: []const [:0]const u8 = &.{};
|
||||||
|
// if (try self.ni_daq.checkDeviceAOOutputType(device, .Voltage)) {
|
||||||
|
// ao_physical_channels = try self.ni_daq.listDeviceAOPhysicalChannels(device);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const channels_box = self.ui.newBoxFromString("Channel names");
|
||||||
|
// channels_box.size.x = UI.Size.percent(0.5, 0);
|
||||||
|
// channels_box.size.y = UI.Size.percent(1, 0);
|
||||||
|
// channels_box.layout_axis = .Y;
|
||||||
|
// self.ui.pushParent(channels_box);
|
||||||
|
// defer self.ui.popParent();
|
||||||
|
|
||||||
|
// for (ai_voltage_physical_channels) |channel_name| {
|
||||||
|
// const channel_box = self.ui.newBoxFromString(channel_name);
|
||||||
|
// channel_box.flags.insert(.clickable);
|
||||||
|
// channel_box.size.x = UI.Size.text(1, 0);
|
||||||
|
// channel_box.size.y = UI.Size.text(1, 0);
|
||||||
|
// channel_box.setText(.text, channel_name);
|
||||||
|
|
||||||
|
// const signal = self.ui.signalFromBox(channel_box);
|
||||||
|
// if (signal.clicked()) {
|
||||||
|
// self.selected_device = try NIDaq.BoundedDeviceName.fromSlice(device);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn showToolbar(self: *App) void {
|
fn showToolbar(self: *App) void {
|
||||||
@ -305,7 +490,7 @@ fn showToolbar(self: *App) void {
|
|||||||
.x = UI.Size.text(2, 1),
|
.x = UI.Size.text(2, 1),
|
||||||
.y = UI.Size.percent(1, 1)
|
.y = UI.Size.percent(1, 1)
|
||||||
};
|
};
|
||||||
box.setText("Add from file", .text);
|
box.setText(.text, "Add from file",);
|
||||||
|
|
||||||
const signal = self.ui.signalFromBox(box);
|
const signal = self.ui.signalFromBox(box);
|
||||||
if (signal.clicked()) {
|
if (signal.clicked()) {
|
||||||
@ -329,7 +514,7 @@ fn showToolbar(self: *App) void {
|
|||||||
.x = UI.Size.text(2, 1),
|
.x = UI.Size.text(2, 1),
|
||||||
.y = UI.Size.percent(1, 1)
|
.y = UI.Size.percent(1, 1)
|
||||||
};
|
};
|
||||||
box.setText("Add from device", .text);
|
box.setText(.text, "Add from device");
|
||||||
|
|
||||||
const signal = self.ui.signalFromBox(box);
|
const signal = self.ui.signalFromBox(box);
|
||||||
if (signal.clicked()) {
|
if (signal.clicked()) {
|
||||||
|
@ -11,7 +11,16 @@ const log = std.log.scoped(.ni_daq);
|
|||||||
const max_device_name_size = 255;
|
const max_device_name_size = 255;
|
||||||
const max_task_name_size = 255;
|
const max_task_name_size = 255;
|
||||||
|
|
||||||
pub const BoundedDeviceName = std.BoundedArray(u8, max_device_name_size);
|
const max_channel_name_size = count: {
|
||||||
|
var count: u32 = 0;
|
||||||
|
count += max_device_name_size;
|
||||||
|
count += 1; // '/'
|
||||||
|
count += 2; // 'ai' or 'ao' or 'co' or 'ci'
|
||||||
|
count += 3; // '0' -> '999', I can't imagine this counter being over 1000. What device has over 1000 channels????
|
||||||
|
break :count count;
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const BoundedDeviceName = std.BoundedArray(u8, max_device_name_size + 1); // +1 for null byte
|
||||||
const StringArrayListUnmanaged = std.ArrayListUnmanaged([:0]const u8);
|
const StringArrayListUnmanaged = std.ArrayListUnmanaged([:0]const u8);
|
||||||
|
|
||||||
const NIDaq = @This();
|
const NIDaq = @This();
|
||||||
@ -276,17 +285,7 @@ const DeviceBuffers = struct {
|
|||||||
array_list: StringArrayListUnmanaged,
|
array_list: StringArrayListUnmanaged,
|
||||||
|
|
||||||
fn init(allocator: std.mem.Allocator, capacity: u32) !ChannelNames {
|
fn init(allocator: std.mem.Allocator, capacity: u32) !ChannelNames {
|
||||||
const max_channel_name_size = count: {
|
const buffer_size = capacity * (max_channel_name_size + 2); // +2 for ', ' separator
|
||||||
var count: u32 = 0;
|
|
||||||
count += max_device_name_size;
|
|
||||||
count += 1; // '/'
|
|
||||||
count += 2; // 'ai' or 'ao' or 'co' or 'ci'
|
|
||||||
count += std.math.log10_int(capacity) + 1;
|
|
||||||
|
|
||||||
break :count count;
|
|
||||||
};
|
|
||||||
|
|
||||||
const buffer_size = capacity * (max_channel_name_size + 2);
|
|
||||||
|
|
||||||
const buffer = try allocator.alloc(u8, buffer_size);
|
const buffer = try allocator.alloc(u8, buffer_size);
|
||||||
errdefer allocator.free(buffer);
|
errdefer allocator.free(buffer);
|
||||||
@ -434,13 +433,7 @@ pub fn logDAQmxError(error_code: i32) void {
|
|||||||
|
|
||||||
var msg: [512:0]u8 = .{ 0 } ** 512;
|
var msg: [512:0]u8 = .{ 0 } ** 512;
|
||||||
if (c.DAQmxGetErrorString(error_code, &msg, msg.len) == 0) {
|
if (c.DAQmxGetErrorString(error_code, &msg, msg.len) == 0) {
|
||||||
if (error_code < 0) {
|
log.err("DAQmx ({}): {s}", .{error_code, msg});
|
||||||
log.err("DAQmx ({}): {s}", .{error_code, msg});
|
|
||||||
} else if (!std.mem.startsWith(u8, &msg, "Error code could not be found.")) {
|
|
||||||
// Ignore positive error codes if it could not be found.
|
|
||||||
// This commonly happens when trying to preallocate bytes for buffer and it returns a positive number.
|
|
||||||
log.warn("DAQmx ({}): {s}", .{error_code, msg});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
log.err("DAQmx ({}): Unknown (Buffer too small for message)", .{error_code});
|
log.err("DAQmx ({}): Unknown (Buffer too small for message)", .{error_code});
|
||||||
}
|
}
|
||||||
@ -454,6 +447,14 @@ pub fn checkDAQmxError(error_code: i32, err: anyerror) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn checkDAQmxErrorIgnoreWarnings(error_code: i32, err: anyerror) !void {
|
||||||
|
if (error_code > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try checkDAQmxError(error_code, err);
|
||||||
|
}
|
||||||
|
|
||||||
fn splitCommaDelimitedList(array_list: *std.ArrayListUnmanaged([:0]const u8), buffer: []u8) !void {
|
fn splitCommaDelimitedList(array_list: *std.ArrayListUnmanaged([:0]const u8), buffer: []u8) !void {
|
||||||
const count = std.mem.count(u8, buffer, ",") + 1;
|
const count = std.mem.count(u8, buffer, ",") + 1;
|
||||||
if (count > array_list.capacity) {
|
if (count > array_list.capacity) {
|
||||||
@ -504,6 +505,7 @@ pub fn listDeviceNames(self: *NIDaq) ![]const [:0]const u8 {
|
|||||||
self.clearAllDeviceBuffers();
|
self.clearAllDeviceBuffers();
|
||||||
|
|
||||||
const required_size = c.DAQmxGetSysDevNames(null, 0);
|
const required_size = c.DAQmxGetSysDevNames(null, 0);
|
||||||
|
try checkDAQmxErrorIgnoreWarnings(required_size, error.DAQmxGetSysDevNames);
|
||||||
if (required_size == 0) {
|
if (required_size == 0) {
|
||||||
return self.device_names.items;
|
return self.device_names.items;
|
||||||
}
|
}
|
||||||
@ -560,7 +562,7 @@ fn listDevicePhysicalChannels(
|
|||||||
array_list.clearRetainingCapacity();
|
array_list.clearRetainingCapacity();
|
||||||
|
|
||||||
const required_size = getPhysicalChannels(device, null, 0);
|
const required_size = getPhysicalChannels(device, null, 0);
|
||||||
try checkDAQmxError(required_size, error.GetPhysicalChannels);
|
try checkDAQmxErrorIgnoreWarnings(required_size, error.GetPhysicalChannels);
|
||||||
if (required_size == 0) {
|
if (required_size == 0) {
|
||||||
return array_list.items;
|
return array_list.items;
|
||||||
}
|
}
|
||||||
@ -710,7 +712,7 @@ fn listDeviceVoltageRanges(
|
|||||||
voltage_ranges.clearRetainingCapacity();
|
voltage_ranges.clearRetainingCapacity();
|
||||||
|
|
||||||
const count = getVoltageRanges(device, null, 0);
|
const count = getVoltageRanges(device, null, 0);
|
||||||
try checkDAQmxError(count, error.GetVoltageRanges);
|
try checkDAQmxErrorIgnoreWarnings(count, error.GetVoltageRanges);
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
return voltage_ranges.items;
|
return voltage_ranges.items;
|
||||||
}
|
}
|
||||||
@ -771,7 +773,7 @@ pub fn listDeviceAIMeasurementTypes(self: NIDaq, device: [:0]const u8) !AIMeasur
|
|||||||
_ = self;
|
_ = self;
|
||||||
|
|
||||||
const count = c.DAQmxGetDevAISupportedMeasTypes(device, null, 0);
|
const count = c.DAQmxGetDevAISupportedMeasTypes(device, null, 0);
|
||||||
try checkDAQmxError(count, error.DAQmxGetDevAISupportedMeasTypes);
|
try checkDAQmxErrorIgnoreWarnings(count, error.DAQmxGetDevAISupportedMeasTypes);
|
||||||
assert(count <= result.buffer.len);
|
assert(count <= result.buffer.len);
|
||||||
|
|
||||||
try checkDAQmxError(
|
try checkDAQmxError(
|
||||||
@ -794,7 +796,7 @@ pub fn listDeviceAOOutputTypes(self: NIDaq, device: [:0]const u8) !AOOutputTypeL
|
|||||||
_ = self;
|
_ = self;
|
||||||
|
|
||||||
const count = c.DAQmxGetDevAOSupportedOutputTypes(device, null, 0);
|
const count = c.DAQmxGetDevAOSupportedOutputTypes(device, null, 0);
|
||||||
try checkDAQmxError(count, error.DAQmxGetDevAOSupportedOutputTypes);
|
try checkDAQmxErrorIgnoreWarnings(count, error.DAQmxGetDevAOSupportedOutputTypes);
|
||||||
assert(count <= result.buffer.len);
|
assert(count <= result.buffer.len);
|
||||||
|
|
||||||
try checkDAQmxError(
|
try checkDAQmxError(
|
||||||
|
69
src/ui.zig
69
src/ui.zig
@ -287,13 +287,20 @@ pub const Box = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setText(self: *Box, text: []const u8, font: Assets.FontId) void {
|
pub fn setText(self: *Box, font: Assets.FontId, text: []const u8) void {
|
||||||
self.text = .{
|
self.text = .{
|
||||||
.content = self.allocator.dupe(u8, text) catch return,
|
.content = self.allocator.dupe(u8, text) catch return,
|
||||||
.font = font
|
.font = font
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn setAllocText(self: *Box, font: Assets.FontId, comptime fmt: []const u8, args: anytype) void {
|
||||||
|
self.text = .{
|
||||||
|
.content = std.fmt.allocPrint(self.allocator, fmt, args) catch return,
|
||||||
|
.font = font
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn setFixedX(self: *Box, x: f32) void {
|
pub fn setFixedX(self: *Box, x: f32) void {
|
||||||
self.flags.insert(.fixed_x);
|
self.flags.insert(.fixed_x);
|
||||||
self.fixed_rect.x = x;
|
self.fixed_rect.x = x;
|
||||||
@ -452,6 +459,19 @@ pub fn begin(self: *UI) void {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < self.boxes.len) {
|
||||||
|
const box = &self.boxes.buffer[i];
|
||||||
|
if (box.last_used_frame != self.frame_index) {
|
||||||
|
_ = self.boxes.swapRemove(i);
|
||||||
|
} else {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.frame_index += 1;
|
self.frame_index += 1;
|
||||||
_ = self.frameArena().reset(.retain_capacity);
|
_ = self.frameArena().reset(.retain_capacity);
|
||||||
self.parent_index_stack.len = 0;
|
self.parent_index_stack.len = 0;
|
||||||
@ -489,19 +509,6 @@ pub fn end(self: *UI) void {
|
|||||||
self.popParent();
|
self.popParent();
|
||||||
assert(self.parent_index_stack.len == 0);
|
assert(self.parent_index_stack.len == 0);
|
||||||
|
|
||||||
{
|
|
||||||
var i: usize = 0;
|
|
||||||
while (i < self.boxes.len) {
|
|
||||||
const box = &self.boxes.buffer[i];
|
|
||||||
if (box.last_used_frame != self.frame_index) {
|
|
||||||
_ = self.boxes.swapRemove(i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
var active_box_flags = self.getActiveBoxFlags();
|
var active_box_flags = self.getActiveBoxFlags();
|
||||||
var hover_box_flags: Box.Flags = .{};
|
var hover_box_flags: Box.Flags = .{};
|
||||||
@ -921,12 +928,13 @@ pub fn newBox(self: *UI, key: Key) *Box {
|
|||||||
var box: *Box = undefined;
|
var box: *Box = undefined;
|
||||||
var box_index: BoxIndex = undefined;
|
var box_index: BoxIndex = undefined;
|
||||||
var persistent: Box.Persistent = .{};
|
var persistent: Box.Persistent = .{};
|
||||||
if (self.findBoxByKey(key)) |found_box| {
|
if (self.findBoxIndexByKey(key)) |found_box_index| {
|
||||||
|
const found_box = &self.boxes.buffer[found_box_index];
|
||||||
assert(found_box.last_used_frame < self.frame_index);
|
assert(found_box.last_used_frame < self.frame_index);
|
||||||
|
|
||||||
persistent = found_box.persistent;
|
persistent = found_box.persistent;
|
||||||
box = found_box;
|
box = found_box;
|
||||||
box_index = found_box.index;
|
box_index = found_box_index;
|
||||||
} else {
|
} else {
|
||||||
box = self.boxes.addOneAssumeCapacity();
|
box = self.boxes.addOneAssumeCapacity();
|
||||||
box_index = self.boxes.len - 1;
|
box_index = self.boxes.len - 1;
|
||||||
@ -941,8 +949,9 @@ pub fn newBox(self: *UI, key: Key) *Box {
|
|||||||
.index = box_index
|
.index = box_index
|
||||||
};
|
};
|
||||||
|
|
||||||
if (self.getParent()) |parent| {
|
if (self.getParentIndex()) |parent_index| {
|
||||||
box.parent_index = parent.index;
|
const parent = &self.boxes.buffer[parent_index];
|
||||||
|
box.parent_index = parent_index;
|
||||||
|
|
||||||
if (parent.last_child_index) |last_child_index| {
|
if (parent.last_child_index) |last_child_index| {
|
||||||
const last_child = &self.boxes.buffer[last_child_index];
|
const last_child = &self.boxes.buffer[last_child_index];
|
||||||
@ -958,15 +967,22 @@ pub fn newBox(self: *UI, key: Key) *Box {
|
|||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn findBoxByKey(self: *UI, key: Key) ?*Box {
|
fn findBoxIndexByKey(self: *UI, key: Key) ?BoxIndex {
|
||||||
for (self.boxes.slice()) |*box| {
|
for (0.., self.boxes.slice()) |index, *box| {
|
||||||
if (box.key.eql(key)) {
|
if (box.key.eql(key)) {
|
||||||
return box;
|
return @intCast(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn findBoxByKey(self: *UI, key: Key) ?*Box {
|
||||||
|
if (self.findBoxIndexByKey(key)) |box_index| {
|
||||||
|
return &self.boxes.buffer[box_index];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn signalFromBox(self: *UI, box: *Box) Signal {
|
pub fn signalFromBox(self: *UI, box: *Box) Signal {
|
||||||
var result = Signal{};
|
var result = Signal{};
|
||||||
|
|
||||||
@ -1058,11 +1074,18 @@ fn isBoxActive(self: *UI, key: Key) bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getParent(self: *UI) ?*Box {
|
fn getParentIndex(self: *UI) ?BoxIndex {
|
||||||
const parent_stack: []BoxIndex = self.parent_index_stack.slice();
|
const parent_stack: []BoxIndex = self.parent_index_stack.slice();
|
||||||
|
|
||||||
if (parent_stack.len > 0) {
|
if (parent_stack.len > 0) {
|
||||||
const parent_index = parent_stack[parent_stack.len - 1];
|
return parent_stack[parent_stack.len - 1];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getParent(self: *UI) ?*Box {
|
||||||
|
if (self.getParentIndex()) |parent_index| {
|
||||||
return &self.boxes.buffer[parent_index];
|
return &self.boxes.buffer[parent_index];
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
Loading…
Reference in New Issue
Block a user