Compare commits

...

2 Commits

Author SHA1 Message Date
e3588f6836 add button to channels window 2025-02-21 02:10:23 +02:00
cf85b00084 add crappy dropdowns 2025-02-20 01:30:22 +02:00
5 changed files with 453 additions and 97 deletions

View File

@ -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");
const signal = self.ui.signalFromBox(device_box);
if (signal.clicked()) {
self.device_filter = try NIDaq.BoundedDeviceName.fromSlice(device);
}
}
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();
{
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");
dropdown.setFixedPosition(
device_name_filter.persistent.position.add(.{ .x = 0, .y = device_name_filter.persistent.size.y })
);
if (self.ui.signalFromBox(toggle_inputs_box).clicked()) {
self.show_voltage_analog_inputs = !self.show_voltage_analog_inputs;
}
}
{
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);
{
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;
}
}
{
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);
if (self.ui.signalFromBox(device_box).clicked()) {
self.device_filter.len = 0;
self.show_device_filter_dropdown = false;
}
}
for (selected_devices) |channel| {
self.allocator.free(channel);
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_name);
self.show_device_filter_dropdown = false;
}
self.selected_channels.len = 0;
}
}
self.shown_window = .channels;
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;
}
}
}
{
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();
{
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) });
}
{
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();
{
self.ui.pushVerticalAlign();
defer self.ui.popVerticalAlign();
_ = self.ui.textureBox(Assets.dropdown_arrow, 1);
}
if (self.channel_type_filter) |channeL_type| {
_ = self.ui.label(.text, channeL_type.name());
} else {
_ = self.ui.label(.text, "All");
}
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);
}
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);
}
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 {

View File

@ -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,29 +36,42 @@ 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();
{
const grab_ase = try Aseprite.init(allocator, @embedFile("./assets/grab-marker.ase"));
defer grab_ase.deinit();
const grab_normal_image = grab_ase.getTagImage(grab_ase.getTag("normal") orelse return error.TagNotFound);
defer grab_normal_image.unload();
const grab_normal_texture = rl.loadTextureFromImage(grab_normal_image);
errdefer grab_normal_texture.unload();
const grab_normal_image = grab_ase.getTagImage(grab_ase.getTag("normal") orelse return error.TagNotFound);
defer grab_normal_image.unload();
const grab_normal_texture = rl.loadTextureFromImage(grab_normal_image);
errdefer grab_normal_texture.unload();
const grab_hot_image = grab_ase.getTagImage(grab_ase.getTag("hot") orelse return error.TagNotFound);
defer grab_hot_image.unload();
const grab_hot_texture = rl.loadTextureFromImage(grab_hot_image);
errdefer grab_hot_texture.unload();
const grab_hot_image = grab_ase.getTagImage(grab_ase.getTag("hot") orelse return error.TagNotFound);
defer grab_hot_image.unload();
const grab_hot_texture = rl.loadTextureFromImage(grab_hot_image);
errdefer grab_hot_texture.unload();
const grab_active_image = grab_ase.getTagImage(grab_ase.getTag("active") orelse return error.TagNotFound);
defer grab_active_image.unload();
const grab_active_texture = rl.loadTextureFromImage(grab_active_image);
errdefer grab_active_texture.unload();
const grab_active_image = grab_ase.getTagImage(grab_ase.getTag("active") orelse return error.TagNotFound);
defer grab_active_image.unload();
const grab_active_texture = rl.loadTextureFromImage(grab_active_image);
errdefer grab_active_texture.unload();
grab_texture = .{
.normal = grab_normal_texture,
.hot = grab_hot_texture,
.active = grab_active_texture
};
grab_texture = .{
.normal = grab_normal_texture,
.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 {

Binary file not shown.

View File

@ -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")) {

View File

@ -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();
}
@ -1493,4 +1533,21 @@ pub fn pushHorizontalAlign(self: *UI) void {
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();
}