add checkbox to toggle view sync controls

This commit is contained in:
Rokas Puzonas 2025-04-08 21:36:19 +03:00
parent 772f35fee7
commit 8d1cad16b3
7 changed files with 237 additions and 168 deletions

View File

@ -250,6 +250,7 @@ pub const View = struct {
height: f32 = 300,
follow: bool = false,
graph_opts: Graph.ViewOptions = .{},
sync_controls: bool = false,
// Runtime
graph_cache: Graph.Cache = .{},
@ -359,6 +360,9 @@ pub const Project = struct {
self.sample_rate = null;
}
const show_rulers_u8 = try readInt(reader, u8);
self.show_rulers = show_rulers_u8 == 1;
{ // Channels
const channel_count = try readInt(reader, u32);
for (0..channel_count) |_| {
@ -421,6 +425,8 @@ pub const Project = struct {
view.graph_opts.x_range = try readRangeF64(reader);
view.graph_opts.y_range = try readRangeF64(reader);
const sync_controls = try readInt(reader, u8);
view.sync_controls = sync_controls == 1;
}
}
@ -433,13 +439,21 @@ pub const Project = struct {
pub fn save(self: *Project) !void {
const save_location = self.save_location orelse return error.NoSaveLocation;
const f = try std.fs.cwd().createFile(save_location, .{});
var save_tmp_location: std.BoundedArray(u8, std.fs.max_path_bytes) = .{};
save_tmp_location.appendSliceAssumeCapacity(save_location);
save_tmp_location.appendSliceAssumeCapacity("-tmp");
const dir = std.fs.cwd();
{
const f = try dir.createFile(save_tmp_location.slice(), .{});
defer f.close();
const writer = f.writer();
try writeInt(writer, u8, file_format_version);
try writeFloat(writer, f64, self.sample_rate orelse 0);
try writeInt(writer, u8, @intFromBool(self.show_rulers));
{ // Channels
try writeInt(writer, u32, @intCast(self.channels.count()));
@ -483,10 +497,14 @@ pub const Project = struct {
try writeRangeF64(writer, view.graph_opts.x_range);
try writeRangeF64(writer, view.graph_opts.y_range);
try writeInt(writer, u8, @intFromBool(view.sync_controls));
}
}
}
try std.fs.rename(dir, save_tmp_location.slice(), dir, save_location);
}
fn writeRangeF64(writer: anytype, range: RangeF64) !void {
try writeFloat(writer, f64, range.lower);
try writeFloat(writer, f64, range.upper);
@ -1083,7 +1101,6 @@ pub fn loadFile(self: *App, id: Id) !void {
const samples = try readFileF64(self.allocator, samples_file);
file.samples = samples;
if (samples.len > 0) {
file.min_sample = samples[0];
file.max_sample = samples[0];
@ -1255,6 +1272,11 @@ pub fn loadView(self: *App, id: Id) !void {
self.refreshViewAvailableXYRanges(id);
if (view.graph_opts.x_range.size() == 0) {
view.graph_opts.x_range = view.available_x_range;
}
if (view.graph_opts.y_range.size() == 0) {
view.graph_opts.y_range = view.available_y_range;
}
}

View File

@ -48,6 +48,7 @@ pub var dropdown_arrow: rl.Texture2D = undefined;
pub var fullscreen: rl.Texture2D = undefined;
pub var output_generation: rl.Texture2D = undefined;
pub var checkbox_mark: rl.Texture2D = undefined;
pub var cross: rl.Texture2D = undefined;
pub fn font(font_id: FontId) FontFace {
var found_font: ?LoadedFont = null;
@ -123,6 +124,7 @@ pub fn init(allocator: std.mem.Allocator) !void {
fullscreen = try loadTextureFromAseprite(allocator, @embedFile("./assets/fullscreen-icon.ase"));
output_generation = try loadTextureFromAseprite(allocator, @embedFile("./assets/output-generation-icon.ase"));
checkbox_mark = try loadTextureFromAseprite(allocator, @embedFile("./assets/checkbox-mark.ase"));
cross = try loadTextureFromAseprite(allocator, @embedFile("./assets/cross.ase"));
}
fn loadTextureFromAseprite(allocator: std.mem.Allocator, memory: []const u8) !rl.Texture {

View File

@ -30,12 +30,13 @@ pub const ViewAxisPosition = struct {
}
}
fn get(optional_self: *?ViewAxisPosition, view_id: Id, axis: UI.Axis) ?f64 {
fn get(optional_self: *?ViewAxisPosition, project: *App.Project, view_id: Id, axis: UI.Axis) ?f64 {
const self = optional_self.* orelse return null;
if (self.axis != axis) return null;
if (!constants.sync_view_controls) {
const view = project.views.get(view_id) orelse return null;
if (!view.sync_controls) {
if (!self.view_id.eql(view_id)) {
return null;
}
@ -152,6 +153,8 @@ fn lastCommandFrame(self: *System) ?*CommandFrame {
}
pub fn pushViewMove(self: *System, view_id: Id, x_range: RangeF64, y_range: RangeF64) void {
var frame: *CommandFrame = undefined;
{
var push_new_command = true;
@ -174,12 +177,19 @@ pub fn pushViewMove(self: *System, view_id: Id, x_range: RangeF64, y_range: Rang
}
}
var sync_controls = false;
if (self.project.views.get(view_id)) |view| {
sync_controls = view.sync_controls;
}
var view_ids: std.BoundedArray(Id, constants.max_views) = .{};
if (constants.sync_view_controls) {
if (sync_controls) {
var iter = self.project.views.idIterator();
while (iter.next()) |id| {
if (self.project.views.get(id).?.sync_controls) {
view_ids.appendAssumeCapacity(id);
}
}
} else {
view_ids.appendAssumeCapacity(view_id);
}
@ -192,7 +202,7 @@ pub fn pushViewMove(self: *System, view_id: Id, x_range: RangeF64, y_range: Rang
command.action.move_and_zoom.x = x_range;
command.action.move_and_zoom.y = y_range;
} else {
const view = self.project.views.get(id) orelse return;
const view = self.project.views.get(view_id) orelse continue;
const view_rect = &view.graph_opts;
command = frame.commands.addOneAssumeCapacity();
@ -264,7 +274,7 @@ pub fn setCursor(self: *System, view_id: Id, axis: UI.Axis, position: ?f64) void
}
pub fn getCursor(self: *System, view_id: Id, axis: UI.Axis) ?f64 {
return ViewAxisPosition.get(&self.cursor, view_id, axis);
return ViewAxisPosition.get(&self.cursor, self.project, view_id, axis);
}
pub fn setZoomStart(self: *System, view_id: Id, axis: UI.Axis, position: ?f64) void {
@ -272,5 +282,5 @@ pub fn setZoomStart(self: *System, view_id: Id, axis: UI.Axis, position: ?f64) v
}
pub fn getZoomStart(self: *System, view_id: Id, axis: UI.Axis) ?f64 {
return ViewAxisPosition.get(&self.zoom_start, view_id, axis);
return ViewAxisPosition.get(&self.zoom_start, self.project,view_id, axis);
}

View File

@ -117,29 +117,15 @@ fn showGraph(ctx: Context, view_id: Id) *UI.Box {
return graph_box;
}
pub fn show(ctx: Context, view_id: Id, height: UI.Sizing) !Result {
fn showToolbar(ctx: Context, view_id: Id) void {
var ui = ctx.ui;
const view_box = ui.createBox(.{
.key = UI.Key.initUsize(view_id.asInt()),
.layout_direction = .top_to_bottom,
.size_x = UI.Sizing.initGrowFull(),
.size_y = height,
});
view_box.beginChildren();
defer view_box.endChildren();
const result = Result{
.box = view_box
};
const toolbar = ui.createBox(.{
.layout_direction = .left_to_right,
.background = srcery.hard_black,
.size_x = UI.Sizing.initGrowFull(),
.size_y = UI.Sizing.initFixed(.{ .pixels = ui.rem(2) })
});
{
toolbar.beginChildren();
defer toolbar.endChildren();
@ -227,6 +213,16 @@ pub fn show(ctx: Context, view_id: Id, height: UI.Sizing) !Result {
view_name = std.fs.path.stem(file.path);
}
if (view.sync_controls) {
const btn = ui.button(ui.keyFromString("Disable sync"));
btn.texture = Assets.cross;
btn.size.y = UI.Sizing.initGrowFull();
btn.tooltip = "Disable sync controls";
if (ui.signal(btn).clicked()) {
view.sync_controls = false;
}
}
if (view_name) |text| {
_ = ui.createBox(.{
.size_x = UI.Sizing.initGrowFull()
@ -238,7 +234,25 @@ pub fn show(ctx: Context, view_id: Id, height: UI.Sizing) !Result {
label.alignment.y = .center;
label.padding = UI.Padding.horizontal(ui.rem(1));
}
}
}
pub fn show(ctx: Context, view_id: Id, height: UI.Sizing) !Result {
var ui = ctx.ui;
const view_box = ui.createBox(.{
.key = UI.Key.initUsize(view_id.asInt()),
.layout_direction = .top_to_bottom,
.size_x = UI.Sizing.initGrowFull(),
.size_y = height,
});
view_box.beginChildren();
defer view_box.endChildren();
const result = Result{
.box = view_box
};
showToolbar(ctx, view_id);
if (!ctx.app.project.show_rulers) {
_ = showGraph(ctx, view_id);

View File

@ -1,9 +1,7 @@
pub const max_files = 32;
pub const max_channels = 32;
pub const max_views = 64;
// UI
pub const sync_view_controls = true;
pub const zoom_speed = 0.1;

View File

@ -272,6 +272,11 @@ pub fn showSidePanel(self: *MainScreen) !void {
_ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(1)) });
_ = ui.checkbox(.{
.value = &view.sync_controls,
.label = "Sync controls"
});
var sample_count: ?usize = null;
switch (view.reference) {
.channel => |channel_id| {
@ -313,7 +318,6 @@ pub fn showSidePanel(self: *MainScreen) !void {
_ = ui.label("Duration: {s}", .{ duration_str });
}
} else {
{
const label = ui.label("Project", .{});
@ -356,13 +360,11 @@ pub fn showSidePanel(self: *MainScreen) !void {
}
}
{ // Show ruler checkbox
_ = ui.checkbox(.{
.value = &project.show_rulers,
.label = "Ruler"
});
}
}
}
pub fn tick(self: *MainScreen) !void {

View File

@ -515,6 +515,7 @@ pub const Box = struct {
draw: ?Draw = null,
visual_hot: bool = false,
visual_active: bool = false,
tooltip: ?[]const u8 = null,
// Variables that you probably shouldn't be touching
last_used_frame: u64 = 0,
@ -699,6 +700,10 @@ pub const Box = struct {
}
}
fn hasChildren(self: *const Box) bool {
return self.tree.first_child_index != null;
}
pub fn bringChildToTop(self: *Box, child: *Box) void {
self.removeChild(child);
self.appendChild(child);
@ -942,6 +947,22 @@ pub fn begin(self: *UI) void {
pub fn end(self: *UI) void {
const mouse_tooltip = self.getBoxByKey(mouse_tooltip_box_key).?;
// Add mouse tooltip to hot item
if (!mouse_tooltip.hasChildren() and self.hot_box_key != null) {
const box = self.getBoxByKey(self.hot_box_key.?).?;
if (box.tooltip) |tooltip| {
mouse_tooltip.beginChildren();
defer mouse_tooltip.endChildren();
_ = self.createBox(.{
.size_x = Sizing.initFixed(.text),
.size_y = Sizing.initFixed(.text),
.text = tooltip
});
}
}
const root_box = self.parentBox().?;
root_box.endChildren();
@ -1632,7 +1653,7 @@ pub fn draw(self: *UI) void {
self.drawBox(root_box);
if (mouse_tooltip.tree.first_child_index != null) {
if (mouse_tooltip.hasChildren()) {
self.drawBox(mouse_tooltip);
}
}