add undo history to channel movements
This commit is contained in:
parent
859c36e93a
commit
de2941c5bf
@ -17,17 +17,78 @@ const assert = std.debug.assert;
|
|||||||
const remap = utils.remap;
|
const remap = utils.remap;
|
||||||
|
|
||||||
const zoom_speed = 0.1;
|
const zoom_speed = 0.1;
|
||||||
|
const ruler_size = UI.Sizing.initFixed(.{ .pixels = 32 });
|
||||||
|
|
||||||
|
const ChannelCommand = struct {
|
||||||
|
channel: *ChannelView,
|
||||||
|
updated_at_ns: i128,
|
||||||
|
action: union(enum) {
|
||||||
|
move_and_zoom: struct {
|
||||||
|
before_x: RangeF64,
|
||||||
|
before_y: RangeF64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
app: *App,
|
app: *App,
|
||||||
fullscreen_channel: ?*App.ChannelView = null,
|
fullscreen_channel: ?*ChannelView = null,
|
||||||
|
|
||||||
axis_zoom: ?struct {
|
axis_zoom: ?struct {
|
||||||
channel: *App.ChannelView,
|
channel: *ChannelView,
|
||||||
axis: UI.Axis,
|
axis: UI.Axis,
|
||||||
start: f64
|
start: f64,
|
||||||
} = null,
|
} = null,
|
||||||
|
|
||||||
fn showChannelViewGraph(self: *MainScreen, channel_view: *ChannelView) !void {
|
// TODO: Redo
|
||||||
|
channel_undo_stack: std.BoundedArray(ChannelCommand, 100) = .{},
|
||||||
|
|
||||||
|
fn pushChannelMoveCommand(self: *MainScreen, channel_view: *ChannelView, x_range: RangeF64, y_range: RangeF64) void {
|
||||||
|
const now_ns = std.time.nanoTimestamp();
|
||||||
|
var undo_stack = &self.channel_undo_stack;
|
||||||
|
var push_new_command = true;
|
||||||
|
|
||||||
|
if (undo_stack.len > 0) {
|
||||||
|
const top_command = &undo_stack.buffer[undo_stack.len - 1];
|
||||||
|
if (now_ns - top_command.updated_at_ns < std.time.ns_per_ms * 250) {
|
||||||
|
top_command.updated_at_ns = now_ns;
|
||||||
|
push_new_command = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var view_rect = &channel_view.view_rect;
|
||||||
|
|
||||||
|
if (push_new_command) {
|
||||||
|
if (undo_stack.unusedCapacitySlice().len == 0) {
|
||||||
|
_ = undo_stack.orderedRemove(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
undo_stack.appendAssumeCapacity(ChannelCommand{
|
||||||
|
.channel = channel_view,
|
||||||
|
.updated_at_ns = now_ns,
|
||||||
|
.action = .{
|
||||||
|
.move_and_zoom = .{
|
||||||
|
.before_x = view_rect.x_range,
|
||||||
|
.before_y = view_rect.y_range,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
view_rect.x_range = x_range;
|
||||||
|
view_rect.y_range = y_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pushChannelMoveCommandAxis(self: *MainScreen, channel_view: *ChannelView, axis: UI.Axis, view_range: RangeF64) void {
|
||||||
|
if (axis == .X) {
|
||||||
|
const view_rect = &channel_view.view_rect;
|
||||||
|
self.pushChannelMoveCommand(channel_view, view_range, view_rect.y_range);
|
||||||
|
} else {
|
||||||
|
const view_rect = &channel_view.view_rect;
|
||||||
|
self.pushChannelMoveCommand(channel_view, view_rect.x_range, view_range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn showChannelViewGraph(self: *MainScreen, channel_view: *ChannelView) *UI.Box {
|
||||||
var ui = &self.app.ui;
|
var ui = &self.app.ui;
|
||||||
|
|
||||||
const samples = self.app.getChannelSamples(channel_view);
|
const samples = self.app.getChannelSamples(channel_view);
|
||||||
@ -62,8 +123,11 @@ fn showChannelViewGraph(self: *MainScreen, channel_view: *ChannelView) !void {
|
|||||||
const x_offset = mouse_x_range.remapTo(RangeF64.init(0, view_rect.x_range.size()), signal.drag.x);
|
const x_offset = mouse_x_range.remapTo(RangeF64.init(0, view_rect.x_range.size()), signal.drag.x);
|
||||||
const y_offset = mouse_y_range.remapTo(RangeF64.init(0, view_rect.y_range.size()), signal.drag.y);
|
const y_offset = mouse_y_range.remapTo(RangeF64.init(0, view_rect.y_range.size()), signal.drag.y);
|
||||||
|
|
||||||
view_rect.x_range = view_rect.x_range.sub(x_offset);
|
self.pushChannelMoveCommand(
|
||||||
view_rect.y_range = view_rect.y_range.add(y_offset);
|
channel_view,
|
||||||
|
view_rect.x_range.sub(x_offset),
|
||||||
|
view_rect.y_range.add(y_offset)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (signal.scrolled() and sample_index_under_mouse != null and sample_value_under_mouse != null) {
|
if (signal.scrolled() and sample_index_under_mouse != null and sample_value_under_mouse != null) {
|
||||||
@ -74,14 +138,23 @@ fn showChannelViewGraph(self: *MainScreen, channel_view: *ChannelView) !void {
|
|||||||
scale_factor += zoom_speed;
|
scale_factor += zoom_speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
view_rect.x_range = view_rect.x_range.zoom(sample_index_under_mouse.?, scale_factor);
|
self.pushChannelMoveCommand(
|
||||||
view_rect.y_range = view_rect.y_range.zoom(sample_value_under_mouse.?, scale_factor);
|
channel_view,
|
||||||
|
view_rect.x_range.zoom(sample_index_under_mouse.?, scale_factor),
|
||||||
|
view_rect.y_range.zoom(sample_value_under_mouse.?, scale_factor)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signal.flags.contains(.middle_clicked)) {
|
||||||
|
self.pushChannelMoveCommand(channel_view, channel_view.x_range, channel_view.y_range);
|
||||||
}
|
}
|
||||||
|
|
||||||
Graph.drawCached(&channel_view.view_cache, graph_box.persistent.size, view_rect.*, samples);
|
Graph.drawCached(&channel_view.view_cache, graph_box.persistent.size, view_rect.*, samples);
|
||||||
if (channel_view.view_cache.texture) |texture| {
|
if (channel_view.view_cache.texture) |texture| {
|
||||||
graph_box.texture = texture.texture;
|
graph_box.texture = texture.texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return graph_box;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getLineOnRuler(
|
fn getLineOnRuler(
|
||||||
@ -271,6 +344,158 @@ fn showRulerTicks(self: *MainScreen, channel_view: *ChannelView, axis: UI.Axis)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn addRulerPlaceholder(self: *MainScreen, key: UI.Key, axis: UI.Axis) *UI.Box {
|
||||||
|
var ui = &self.app.ui;
|
||||||
|
|
||||||
|
var ruler = ui.createBox(.{
|
||||||
|
.key = key,
|
||||||
|
.background = srcery.hard_black,
|
||||||
|
.flags = &.{ .clip_view, .clickable, .scrollable },
|
||||||
|
.hot_cursor = .mouse_cursor_pointing_hand
|
||||||
|
});
|
||||||
|
if (axis == .X) {
|
||||||
|
ruler.size.x = UI.Sizing.initGrowFull();
|
||||||
|
ruler.size.y = ruler_size;
|
||||||
|
} else {
|
||||||
|
ruler.size.x = ruler_size;
|
||||||
|
ruler.size.y = UI.Sizing.initGrowFull();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ruler;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn showRuler(self: *MainScreen, ruler: *UI.Box, graph_box: *UI.Box, channel_view: *ChannelView, axis: UI.Axis) void {
|
||||||
|
var ui = &self.app.ui;
|
||||||
|
|
||||||
|
ruler.beginChildren();
|
||||||
|
defer ruler.endChildren();
|
||||||
|
|
||||||
|
self.showRulerTicks(channel_view, axis);
|
||||||
|
|
||||||
|
const signal = ui.signal(ruler);
|
||||||
|
const mouse_position = switch (axis) {
|
||||||
|
.X => signal.relative_mouse.x,
|
||||||
|
.Y => signal.relative_mouse.y
|
||||||
|
};
|
||||||
|
const mouse_range = switch (axis) {
|
||||||
|
.X => RangeF64.init(0, ruler.persistent.size.x),
|
||||||
|
.Y => RangeF64.init(0, ruler.persistent.size.y)
|
||||||
|
};
|
||||||
|
const view_range = channel_view.getViewRange(axis);
|
||||||
|
const mouse_position_on_graph = mouse_range.remapTo(view_range.*, mouse_position);
|
||||||
|
|
||||||
|
var zoom_start: ?f64 = null;
|
||||||
|
var zoom_end: ?f64 = null;
|
||||||
|
|
||||||
|
var is_zooming: bool = false;
|
||||||
|
if (self.axis_zoom) |axis_zoom| {
|
||||||
|
is_zooming = axis_zoom.channel == channel_view and axis_zoom.axis == axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signal.hot) {
|
||||||
|
const mouse_tooltip = ui.mouseTooltip();
|
||||||
|
mouse_tooltip.beginChildren();
|
||||||
|
defer mouse_tooltip.endChildren();
|
||||||
|
|
||||||
|
if (channel_view.getSampleRange(axis).hasInclusive(mouse_position_on_graph)) {
|
||||||
|
_ = ui.label("{d:.3}", .{mouse_position_on_graph});
|
||||||
|
}
|
||||||
|
|
||||||
|
zoom_start = mouse_position_on_graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signal.flags.contains(.left_pressed)) {
|
||||||
|
self.axis_zoom = .{
|
||||||
|
.axis = axis,
|
||||||
|
.start = mouse_position_on_graph,
|
||||||
|
.channel = channel_view
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_zooming) {
|
||||||
|
zoom_start = self.axis_zoom.?.start;
|
||||||
|
zoom_end = mouse_position_on_graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zoom_start != null) {
|
||||||
|
_ = ui.createBox(.{
|
||||||
|
.background = srcery.green,
|
||||||
|
.float_rect = getLineOnRuler(channel_view, ruler, axis, zoom_start.?, 0, 1),
|
||||||
|
.float_relative_to = ruler,
|
||||||
|
});
|
||||||
|
|
||||||
|
_ = ui.createBox(.{
|
||||||
|
.background = srcery.green,
|
||||||
|
.float_rect = getLineOnRuler(channel_view, graph_box, axis, zoom_start.?, 0, 1),
|
||||||
|
.float_relative_to = graph_box,
|
||||||
|
.parent = graph_box
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zoom_end != null) {
|
||||||
|
_ = ui.createBox(.{
|
||||||
|
.background = srcery.green,
|
||||||
|
.float_rect = getLineOnRuler(channel_view, ruler, axis, zoom_end.?, 0, 1),
|
||||||
|
.float_relative_to = ruler,
|
||||||
|
});
|
||||||
|
|
||||||
|
_ = ui.createBox(.{
|
||||||
|
.background = srcery.green,
|
||||||
|
.float_rect = getLineOnRuler(channel_view, graph_box, axis, zoom_end.?, 0, 1),
|
||||||
|
.float_relative_to = graph_box,
|
||||||
|
.parent = graph_box
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zoom_start != null and zoom_end != null) {
|
||||||
|
_ = ui.createBox(.{
|
||||||
|
.background = srcery.green.alpha(0.5),
|
||||||
|
.float_relative_to = ruler,
|
||||||
|
.float_rect = getRectOnRuler(
|
||||||
|
channel_view,
|
||||||
|
ruler,
|
||||||
|
axis,
|
||||||
|
zoom_start.?,
|
||||||
|
zoom_end.? - zoom_start.?,
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signal.scrolled()) {
|
||||||
|
var scale_factor: f64 = 1;
|
||||||
|
if (signal.scroll.y > 0) {
|
||||||
|
scale_factor -= zoom_speed;
|
||||||
|
} else {
|
||||||
|
scale_factor += zoom_speed;
|
||||||
|
}
|
||||||
|
const new_view_range = view_range.zoom(mouse_position_on_graph, scale_factor);
|
||||||
|
self.pushChannelMoveCommandAxis(channel_view, axis, new_view_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_zooming and signal.flags.contains(.left_released)) {
|
||||||
|
if (zoom_start != null and zoom_end != null) {
|
||||||
|
const zoom_start_mouse = view_range.remapTo(mouse_range, zoom_start.?);
|
||||||
|
const zoom_end_mouse = view_range.remapTo(mouse_range, zoom_end.?);
|
||||||
|
const mouse_move_distance = @abs(zoom_end_mouse - zoom_start_mouse);
|
||||||
|
if (mouse_move_distance > 5) {
|
||||||
|
var new_view_range = RangeF64.init(
|
||||||
|
@min(zoom_start.?, zoom_end.?),
|
||||||
|
@max(zoom_start.?, zoom_end.?)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (axis == .Y) {
|
||||||
|
new_view_range = new_view_range.flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.pushChannelMoveCommandAxis(channel_view, axis, new_view_range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.axis_zoom = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn showChannelView(self: *MainScreen, channel_view: *ChannelView, height: UI.Sizing) !void {
|
fn showChannelView(self: *MainScreen, channel_view: *ChannelView, height: UI.Sizing) !void {
|
||||||
var ui = &self.app.ui;
|
var ui = &self.app.ui;
|
||||||
|
|
||||||
@ -286,14 +511,12 @@ fn showChannelView(self: *MainScreen, channel_view: *ChannelView, height: UI.Siz
|
|||||||
const show_ruler = true;
|
const show_ruler = true;
|
||||||
|
|
||||||
if (!show_ruler) {
|
if (!show_ruler) {
|
||||||
try self.showChannelViewGraph(channel_view);
|
_ = self.showChannelViewGraph(channel_view);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const x_ruler_size = UI.Sizing.initFixed(.{ .pixels = 32 });
|
var graph_box: *UI.Box = undefined;
|
||||||
const y_ruler_size = UI.Sizing.initFixed(.{ .pixels = 32 });
|
|
||||||
|
|
||||||
var y_ruler: *UI.Box = undefined;
|
|
||||||
var x_ruler: *UI.Box = undefined;
|
var x_ruler: *UI.Box = undefined;
|
||||||
|
var y_ruler: *UI.Box = undefined;
|
||||||
|
|
||||||
{
|
{
|
||||||
const container = ui.createBox(.{
|
const container = ui.createBox(.{
|
||||||
@ -304,31 +527,24 @@ fn showChannelView(self: *MainScreen, channel_view: *ChannelView, height: UI.Siz
|
|||||||
container.beginChildren();
|
container.beginChildren();
|
||||||
defer container.endChildren();
|
defer container.endChildren();
|
||||||
|
|
||||||
y_ruler = ui.createBox(.{
|
y_ruler = self.addRulerPlaceholder(ui.keyFromString("Y ruler"), .Y);
|
||||||
.key = ui.keyFromString("Y ruler"),
|
|
||||||
.size_x = y_ruler_size,
|
|
||||||
.size_y = UI.Sizing.initGrowFull(),
|
|
||||||
.background = srcery.hard_black,
|
|
||||||
.flags = &.{ .clickable, .clip_view, .scrollable },
|
|
||||||
.hot_cursor = .mouse_cursor_pointing_hand
|
|
||||||
});
|
|
||||||
|
|
||||||
try self.showChannelViewGraph(channel_view);
|
graph_box = self.showChannelViewGraph(channel_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const container = ui.createBox(.{
|
const container = ui.createBox(.{
|
||||||
.layout_direction = .left_to_right,
|
.layout_direction = .left_to_right,
|
||||||
.size_x = UI.Sizing.initGrowFull(),
|
.size_x = UI.Sizing.initGrowFull(),
|
||||||
.size_y = x_ruler_size,
|
.size_y = ruler_size,
|
||||||
});
|
});
|
||||||
container.beginChildren();
|
container.beginChildren();
|
||||||
defer container.endChildren();
|
defer container.endChildren();
|
||||||
|
|
||||||
const fullscreen = ui.createBox(.{
|
const fullscreen = ui.createBox(.{
|
||||||
.key = ui.keyFromString("Fullscreen toggle"),
|
.key = ui.keyFromString("Fullscreen toggle"),
|
||||||
.size_y = x_ruler_size,
|
.size_x = ruler_size,
|
||||||
.size_x = y_ruler_size,
|
.size_y = ruler_size,
|
||||||
.background = srcery.hard_black,
|
.background = srcery.hard_black,
|
||||||
.hot_cursor = .mouse_cursor_pointing_hand,
|
.hot_cursor = .mouse_cursor_pointing_hand,
|
||||||
.flags = &.{ .draw_hot, .draw_active, .clickable },
|
.flags = &.{ .draw_hot, .draw_active, .clickable },
|
||||||
@ -343,141 +559,19 @@ fn showChannelView(self: *MainScreen, channel_view: *ChannelView, height: UI.Siz
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
x_ruler = ui.createBox(.{
|
x_ruler = self.addRulerPlaceholder(ui.keyFromString("X ruler"), .X);
|
||||||
.key = ui.keyFromString("X ruler"),
|
|
||||||
.size_y = x_ruler_size,
|
|
||||||
.size_x = UI.Sizing.initGrowFull(),
|
|
||||||
.background = srcery.hard_black,
|
|
||||||
.flags = &.{ .clip_view, .clickable, .scrollable },
|
|
||||||
.hot_cursor = .mouse_cursor_pointing_hand
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ruler_desciptions = .{
|
self.showRuler(x_ruler, graph_box, channel_view, .X);
|
||||||
.{ x_ruler, .X },
|
self.showRuler(y_ruler, graph_box, channel_view, .Y);
|
||||||
.{ y_ruler, .Y }
|
|
||||||
};
|
|
||||||
|
|
||||||
inline for (ruler_desciptions) |ruler_desc| {
|
|
||||||
const ruler = ruler_desc[0];
|
|
||||||
const axis: UI.Axis = ruler_desc[1];
|
|
||||||
|
|
||||||
ruler.beginChildren();
|
|
||||||
defer ruler.endChildren();
|
|
||||||
|
|
||||||
self.showRulerTicks(channel_view, axis);
|
|
||||||
|
|
||||||
const signal = ui.signal(ruler);
|
|
||||||
const mouse_position = switch (axis) {
|
|
||||||
.X => signal.relative_mouse.x,
|
|
||||||
.Y => signal.relative_mouse.y
|
|
||||||
};
|
|
||||||
const mouse_range = switch (axis) {
|
|
||||||
.X => RangeF64.init(0, ruler.persistent.size.x),
|
|
||||||
.Y => RangeF64.init(0, ruler.persistent.size.y)
|
|
||||||
};
|
|
||||||
const view_range = channel_view.getViewRange(axis);
|
|
||||||
const mouse_position_on_graph = mouse_range.remapTo(view_range.*, mouse_position);
|
|
||||||
|
|
||||||
var zoom_start: ?f64 = null;
|
|
||||||
var zoom_end: ?f64 = null;
|
|
||||||
|
|
||||||
var is_zooming: bool = false;
|
|
||||||
if (self.axis_zoom) |axis_zoom| {
|
|
||||||
is_zooming = axis_zoom.channel == channel_view and axis_zoom.axis == axis;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signal.hot) {
|
|
||||||
const mouse_tooltip = ui.mouseTooltip();
|
|
||||||
mouse_tooltip.beginChildren();
|
|
||||||
defer mouse_tooltip.endChildren();
|
|
||||||
|
|
||||||
if (channel_view.getSampleRange(axis).hasInclusive(mouse_position_on_graph)) {
|
|
||||||
_ = ui.label("{d:.3}", .{mouse_position_on_graph});
|
|
||||||
}
|
|
||||||
|
|
||||||
zoom_start = mouse_position_on_graph;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signal.flags.contains(.left_pressed)) {
|
|
||||||
self.axis_zoom = .{
|
|
||||||
.axis = axis,
|
|
||||||
.start = mouse_position_on_graph,
|
|
||||||
.channel = channel_view
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_zooming) {
|
|
||||||
zoom_start = self.axis_zoom.?.start;
|
|
||||||
zoom_end = mouse_position_on_graph;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zoom_start != null) {
|
|
||||||
_ = ui.createBox(.{
|
|
||||||
.background = srcery.green,
|
|
||||||
.float_rect = getLineOnRuler(channel_view, ruler, axis, zoom_start.?, 0, 1),
|
|
||||||
.float_relative_to = ruler,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zoom_end != null) {
|
|
||||||
_ = ui.createBox(.{
|
|
||||||
.background = srcery.green,
|
|
||||||
.float_rect = getLineOnRuler(channel_view, ruler, axis, zoom_end.?, 0, 1),
|
|
||||||
.float_relative_to = ruler,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zoom_start != null and zoom_end != null) {
|
|
||||||
_ = ui.createBox(.{
|
|
||||||
.background = srcery.green.alpha(0.5),
|
|
||||||
.float_relative_to = ruler,
|
|
||||||
.float_rect = getRectOnRuler(
|
|
||||||
channel_view,
|
|
||||||
ruler,
|
|
||||||
axis,
|
|
||||||
zoom_start.?,
|
|
||||||
zoom_end.? - zoom_start.?,
|
|
||||||
0,
|
|
||||||
1
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signal.scrolled()) {
|
|
||||||
var scale_factor: f64 = 1;
|
|
||||||
if (signal.scroll.y > 0) {
|
|
||||||
scale_factor -= zoom_speed;
|
|
||||||
} else {
|
|
||||||
scale_factor += zoom_speed;
|
|
||||||
}
|
|
||||||
view_range.* = view_range.zoom(mouse_position_on_graph, scale_factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_zooming and signal.flags.contains(.left_released)) {
|
|
||||||
if (zoom_start != null and zoom_end != null) {
|
|
||||||
const zoom_start_mouse = view_range.remapTo(mouse_range, zoom_start.?);
|
|
||||||
const zoom_end_mouse = view_range.remapTo(mouse_range, zoom_end.?);
|
|
||||||
const mouse_move_distance = @abs(zoom_end_mouse - zoom_start_mouse);
|
|
||||||
if (mouse_move_distance > 5) {
|
|
||||||
view_range.lower = @min(zoom_start.?, zoom_end.?);
|
|
||||||
view_range.upper = @max(zoom_start.?, zoom_end.?);
|
|
||||||
|
|
||||||
if (axis == .Y) {
|
|
||||||
view_range.* = view_range.flip();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.axis_zoom = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tick(self: *MainScreen) !void {
|
pub fn tick(self: *MainScreen) !void {
|
||||||
var ui = &self.app.ui;
|
var ui = &self.app.ui;
|
||||||
|
|
||||||
if (rl.isKeyPressed(.key_escape)) {
|
if (ui.isKeyboardPressed(.key_escape)) {
|
||||||
if (self.fullscreen_channel != null) {
|
if (self.fullscreen_channel != null) {
|
||||||
self.fullscreen_channel = null;
|
self.fullscreen_channel = null;
|
||||||
} else {
|
} else {
|
||||||
@ -485,6 +579,18 @@ pub fn tick(self: *MainScreen) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ui.isCtrlDown() and ui.isKeyboardPressed(.key_z)) {
|
||||||
|
if (self.channel_undo_stack.popOrNull()) |command| {
|
||||||
|
switch (command.action) {
|
||||||
|
.move_and_zoom => |args| {
|
||||||
|
const view_rect = &command.channel.view_rect;
|
||||||
|
view_rect.x_range = args.before_x;
|
||||||
|
view_rect.y_range = args.before_y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const root = ui.parentBox().?;
|
const root = ui.parentBox().?;
|
||||||
root.layout_direction = .top_to_bottom;
|
root.layout_direction = .top_to_bottom;
|
||||||
|
|
||||||
@ -538,8 +644,6 @@ pub fn tick(self: *MainScreen) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (self.fullscreen_channel) |channel| {
|
if (self.fullscreen_channel) |channel| {
|
||||||
try self.showChannelView(channel, UI.Sizing.initGrowFull());
|
try self.showChannelView(channel, UI.Sizing.initGrowFull());
|
||||||
|
|
||||||
|
29
src/ui.zig
29
src/ui.zig
@ -636,7 +636,8 @@ pub const BoxOptions = struct {
|
|||||||
float_rect: ?Rect = null,
|
float_rect: ?Rect = null,
|
||||||
scientific_number: ?f64 = null,
|
scientific_number: ?f64 = null,
|
||||||
scientific_precision: ?u32 = null,
|
scientific_precision: ?u32 = null,
|
||||||
float_relative_to: ?*Box = null
|
float_relative_to: ?*Box = null,
|
||||||
|
parent: ?*UI.Box = null
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const root_box_key = Key.initString(0, "$root$");
|
pub const root_box_key = Key.initString(0, "$root$");
|
||||||
@ -1430,7 +1431,7 @@ pub fn createBox(self: *UI, opts: BoxOptions) *Box {
|
|||||||
box.setFloatRect(rect);
|
box.setFloatRect(rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.parentBox()) |parent| {
|
if (opts.parent orelse self.parentBox()) |parent| {
|
||||||
box.tree.parent_index = parent.tree.index;
|
box.tree.parent_index = parent.tree.index;
|
||||||
|
|
||||||
if (parent.tree.last_child_index) |last_child_index| {
|
if (parent.tree.last_child_index) |last_child_index| {
|
||||||
@ -1842,6 +1843,30 @@ pub fn isKeyActive(self: *UI, key: Key) bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn isKeyboardPressed(self: *UI, key: rl.KeyboardKey) bool {
|
||||||
|
const key_u32: u32 = @intCast(@intFromEnum(key));
|
||||||
|
|
||||||
|
for (0.., self.events.slice()) |i, _event| {
|
||||||
|
const event: Event = _event;
|
||||||
|
if (event == .key_pressed and event.key_pressed == key_u32) {
|
||||||
|
_ = self.events.swapRemove(i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn isShiftDown(self: *UI) bool {
|
||||||
|
_ = self;
|
||||||
|
return rl.isKeyDown(rl.KeyboardKey.key_left_shift) or rl.isKeyDown(rl.KeyboardKey.key_right_shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn isCtrlDown(self: *UI) bool {
|
||||||
|
_ = self;
|
||||||
|
return rl.isKeyDown(rl.KeyboardKey.key_left_control) or rl.isKeyDown(rl.KeyboardKey.key_right_control);
|
||||||
|
}
|
||||||
|
|
||||||
// --------------------------------- Widgets ----------------------------------------- //
|
// --------------------------------- Widgets ----------------------------------------- //
|
||||||
|
|
||||||
pub fn mouseTooltip(self: *UI) *Box {
|
pub fn mouseTooltip(self: *UI) *Box {
|
||||||
|
Loading…
Reference in New Issue
Block a user