diff --git a/src/app.zig b/src/app.zig index 67d2bed..286266b 100644 --- a/src/app.zig +++ b/src/app.zig @@ -775,6 +775,7 @@ pub const File = struct { }; pub const View = struct { + pub const max_markers = 32; pub const max_transforms = 16; pub const BoundedTransformsArray = std.BoundedArray(Transform, max_transforms); @@ -880,6 +881,7 @@ pub const View = struct { graph_opts: Graph.ViewOptions = .{}, sync_controls: bool = false, marked_ranges: std.BoundedArray(MarkedRange, 32) = .{}, + markers: std.BoundedArray(f64, max_markers) = .{}, transforms: BoundedTransformsArray = .{}, // Runtime diff --git a/src/components/systems/view_controls.zig b/src/components/systems/view_controls.zig index f986dcc..b9a6eb6 100644 --- a/src/components/systems/view_controls.zig +++ b/src/components/systems/view_controls.zig @@ -170,11 +170,15 @@ view_settings: ?Id = null, // View id view_fullscreen: ?Id = null, // View id view_protocol_modal: ?Id = null, // View id show_ruler: bool = false, -selected_tool: enum { move, select } = .move, +selected_tool: enum { move, select, marker } = .move, show_marked_range: ?struct { view_id: Id, index: usize, } = null, +show_marker: ?struct { + view_id: Id, + index: usize +} = null, pub fn init(project: *App.Project) System { return System{ @@ -362,4 +366,18 @@ pub fn toggleShownMarkedRange(self: *System, view_id: Id, index: usize) void { .view_id = view_id, .index = index, }; +} + +pub fn toggleShownMarker(self: *System, view_id: Id, index: usize) void { + if (self.show_marker) |show_marker| { + if (show_marker.view_id.eql(view_id) and show_marker.index == index) { + self.show_marker = null; + return; + } + } + + self.show_marker = .{ + .view_id = view_id, + .index = index, + }; } \ No newline at end of file diff --git a/src/components/view.zig b/src/components/view.zig index 1ea64d0..98da3b9 100644 --- a/src/components/view.zig +++ b/src/components/view.zig @@ -32,14 +32,10 @@ pub const Result = struct { box: *UI.Box, }; -fn showGraph(ctx: Context, view_id: Id) *UI.Box { +fn createGraphBox(ctx: Context) *UI.Box { var ui = ctx.ui; - const app = ctx.app; - const view = app.getView(view_id).?; - const view_opts = &view.graph_opts; - - const graph_box = ui.createBox(.{ + return ui.createBox(.{ .key = ui.keyFromString("Graph"), .size_x = UI.Sizing.initGrowFull(), .size_y = UI.Sizing.initGrowFull(), @@ -51,6 +47,16 @@ fn showGraph(ctx: Context, view_id: Id) *UI.Box { .bottom = .{ .color = srcery.hard_black, .size = 4 } } }); +} + +fn showGraph(ctx: Context, graph_box: *UI.Box, view_id: Id) void { + var ui = ctx.ui; + const app = ctx.app; + + const view = app.getView(view_id).?; + const view_opts = &view.graph_opts; + + graph_box.beginChildren(); defer graph_box.endChildren(); @@ -105,6 +111,9 @@ fn showGraph(ctx: Context, view_id: Id) *UI.Box { } } else if (ctx.view_controls.selected_tool == .select) { // TODO: + + } else if (ctx.view_controls.selected_tool == .marker) { + // TODO: } @@ -129,8 +138,6 @@ fn showGraph(ctx: Context, view_id: Id) *UI.Box { .size = ui.rem(3) }; } - - return graph_box; } fn showToolbar(ctx: Context, view_id: Id) void { @@ -280,7 +287,8 @@ pub fn show(ctx: Context, view_id: Id, height: UI.Sizing) !Result { showToolbar(ctx, view_id); if (!ctx.app.project.show_rulers) { - _ = showGraph(ctx, view_id); + const graph_box = createGraphBox(ctx); + showGraph(ctx, graph_box, view_id); } else { const ruler_ctx = UIViewRuler.Context{ @@ -304,7 +312,7 @@ pub fn show(ctx: Context, view_id: Id, height: UI.Sizing) !Result { y_ruler = UIViewRuler.createBox(ruler_ctx, ui.keyFromString("Y ruler"), .Y); - graph_box = showGraph(ctx, view_id); + graph_box = createGraphBox(ctx); } { @@ -335,6 +343,7 @@ pub fn show(ctx: Context, view_id: Id, height: UI.Sizing) !Result { try UIViewRuler.show(ruler_ctx, x_ruler, graph_box, view_id, .X); try UIViewRuler.show(ruler_ctx, y_ruler, graph_box, view_id, .Y); + showGraph(ctx, graph_box, view_id); } return result; diff --git a/src/components/view_ruler.zig b/src/components/view_ruler.zig index 26813b5..6ccd10f 100644 --- a/src/components/view_ruler.zig +++ b/src/components/view_ruler.zig @@ -304,7 +304,6 @@ pub fn show(ctx: Context, box: *UI.Box, graph_box: *UI.Box, view_id: Id, axis: U } { - var selected_range_iter = view.iterMarkedRanges(axis); while (selected_range_iter.next()) |selected_range| { var color = srcery.blue; @@ -322,6 +321,7 @@ pub fn show(ctx: Context, box: *UI.Box, graph_box: *UI.Box, view_id: Id, axis: U showMarkerLine(ui, ruler, selected_range.upper, color); var hasher = UI.Key.CombineHasher.init(); + hasher.update(std.mem.asBytes("Marked ranges")); hasher.update(std.mem.asBytes(&view_id)); hasher.update(std.mem.asBytes(&axis)); hasher.update(std.mem.asBytes(&index)); @@ -341,6 +341,37 @@ pub fn show(ctx: Context, box: *UI.Box, graph_box: *UI.Box, view_id: Id, axis: U } } } + + if (axis == .X) { + for (0.., view.markers.constSlice()) |i, marker| { + const color = srcery.cyan; + + showMarkerLine(ui, ruler, marker, color); + showMarkerLine(ui, ruler, marker, color); + + var hasher = UI.Key.CombineHasher.init(); + hasher.update(std.mem.asBytes("Markers")); + hasher.update(std.mem.asBytes(&view_id)); + hasher.update(std.mem.asBytes(&axis)); + hasher.update(std.mem.asBytes(&i)); + + const view_size = view.graph_opts.x_range.size(); + const clickable_width = view_size * 0.01; + + const clickable = ui.createBox(.{ + .key = UI.Key.init(hasher.final()), + .float_rect = ruler.getGraphDrawContext().getRect(marker - clickable_width/2, clickable_width, 0, 1), + .float_relative_to = ruler.graph_box, + .parent = ruler.graph_box, + .flags = &.{ .draw_hot, .draw_active, .clickable }, + .hot_cursor = .mouse_cursor_pointing_hand, + }); + + if (ui.signal(clickable).clicked()) { + ctx.view_controls.toggleShownMarker(view_id, i); + } + } + } } const signal = ui.signal(box); @@ -421,6 +452,10 @@ pub fn show(ctx: Context, box: *UI.Box, graph_box: *UI.Box, view_id: Id, axis: U } } } + } else if (ctx.view_controls.selected_tool == .marker) { + if (cursor != null and signal.flags.contains(.left_released) and axis == .X) { + view.markers.append(cursor.?) catch {}; + } } if (signal.flags.contains(.left_released)) { diff --git a/src/screens/main_screen.zig b/src/screens/main_screen.zig index 3b1ef42..bf7d993 100644 --- a/src/screens/main_screen.zig +++ b/src/screens/main_screen.zig @@ -626,6 +626,34 @@ fn showMarkedRange(self: *MainScreen, view_id: Id, index: usize) void { } } +fn showMarker(self: *MainScreen, view_id: Id, index: usize) void { + var ui = &self.app.ui; + + const view = self.app.getView(view_id) orelse return; + + const marker = view.markers.get(index); + + { + const label = ui.label("Selected range", .{}); + label.borders.bottom = .{ + .color = srcery.blue, + .size = 1 + }; + + _ = ui.createBox(.{ .size_y = UI.Sizing.initFixedPixels(ui.rem(1)) }); + } + + const sample_rate = self.app.project.getSampleRate(); + + if (sample_rate != null) { + const duration = utils.formatDuration(ui.frameAllocator(), marker / sample_rate.?) catch ""; + _ = ui.label("Position: {s}", .{ duration }); + } else { + _ = ui.label("Position: {d:.2}", .{ marker }); + } + +} + fn showToolbar(self: *MainScreen) void { var ui = &self.app.ui; @@ -679,7 +707,7 @@ fn showToolbar(self: *MainScreen) void { btn.borders = UI.Borders.bottom(.{ .size = 4, .color = srcery.green }); } - if (ui.signal(btn).clicked() or ui.isKeyboardPressed(.key_one)) { + if (ui.signal(btn).clicked()) { self.view_controls.selected_tool = .move; } } @@ -690,10 +718,21 @@ fn showToolbar(self: *MainScreen) void { btn.borders = UI.Borders.bottom(.{ .size = 4, .color = srcery.green }); } - if (ui.signal(btn).clicked() or ui.isKeyboardPressed(.key_two)) { + if (ui.signal(btn).clicked()) { self.view_controls.selected_tool = .select; } } + + { + var btn = ui.textButton("Marker"); + if (self.view_controls.selected_tool == .marker) { + btn.borders = UI.Borders.bottom(.{ .size = 4, .color = srcery.green }); + } + + if (ui.signal(btn).clicked()) { + self.view_controls.selected_tool = .marker; + } + } } pub fn showSidePanel(self: *MainScreen) !void { @@ -712,9 +751,11 @@ pub fn showSidePanel(self: *MainScreen) !void { container.beginChildren(); defer container.endChildren(); - _ = ui.createBox(.{ .size_x = UI.Sizing.initFixedPixels(ui.rem(12)) }); + _ = ui.createBox(.{ .size_x = UI.Sizing.initFixedPixels(ui.rem(18)) }); - if (self.view_controls.show_marked_range) |show_marked_range| { + if (self.view_controls.show_marker) |marker| { + self.showMarker(marker.view_id, marker.index); + } else if (self.view_controls.show_marked_range) |show_marked_range| { self.showMarkedRange(show_marked_range.view_id, show_marked_range.index); } else if (self.view_controls.view_settings) |view_id| { try self.showViewSettings(view_id); diff --git a/src/ui.zig b/src/ui.zig index ac8b546..b6967ac 100644 --- a/src/ui.zig +++ b/src/ui.zig @@ -510,6 +510,8 @@ pub const Box = struct { draggable, draw_hot, draw_active, + // TODO: Add a way to specify relative to which box should clipping occur. + // Useful for floating boxes clip_view };