// Виджет холста: скролл, текстура, зум по Ctrl+колёсико. const std = @import("std"); const dvui = @import("dvui"); const dvui_ext = @import("dvui_ext.zig"); const Canvas = @import("../Canvas.zig"); const ImageRect = @import("../models/basic_models.zig").ImageRect; pub fn canvasView(canvas: *Canvas, content_rect_scale: dvui.RectScale) void { var textured = dvui_ext.texturedBox(content_rect_scale, dvui.Rect.all(20)); { var overlay = dvui.overlay(@src(), .{ .expand = .both }); { var scroll = dvui.scrollArea( @src(), .{ .scroll_info = &canvas.scroll, .vertical_bar = .auto, .horizontal_bar = .auto, }, .{ .expand = .both, .background = false }, ); { drawCanvasContent(canvas, scroll); handleCanvasZoom(canvas, scroll); } scroll.deinit(); dvui.label(@src(), "Canvas", .{}, .{ .gravity_x = 0.5, .gravity_y = 0.0 }); } overlay.deinit(); } textured.deinit(); } fn drawCanvasContent(canvas: *Canvas, scroll: anytype) void { const natural_scale = if (canvas.native_scaling) 1 else dvui.windowNaturalScale(); const img_size = canvas.getScaledImageSize(); const viewport_rect = scroll.data().contentRect(); const scroll_current = dvui.Point{ .x = canvas.scroll.viewport.x, .y = canvas.scroll.viewport.y }; const viewport_px = dvui.Rect{ .x = viewport_rect.x * natural_scale, .y = viewport_rect.y * natural_scale, .w = viewport_rect.w * natural_scale, .h = viewport_rect.h * natural_scale, }; const scroll_px = dvui.Point{ .x = scroll_current.x * natural_scale, .y = scroll_current.y * natural_scale, }; const changed = canvas.updateVisibleImageRect(viewport_px, scroll_px); if (changed) canvas.requestRedraw(); canvas.processPendingRedraw() catch |err| { std.debug.print("processPendingRedraw error: {}\n", .{err}); }; const content_w_px: u32 = img_size.x + img_size.w; const content_h_px: u32 = img_size.y + img_size.h; const content_w = @as(f32, @floatFromInt(content_w_px)) / natural_scale; const content_h = @as(f32, @floatFromInt(content_h_px)) / natural_scale; var canvas_layer = dvui.overlay( @src(), .{ .min_size_content = .{ .w = content_w, .h = content_h }, .background = false }, ); { if (canvas.texture) |tex| { const vis = canvas._visible_rect orelse ImageRect{ .x = 0, .y = 0, .w = 0, .h = 0 }; const left = @as(f32, @floatFromInt(img_size.x + vis.x)) / natural_scale; const top = @as(f32, @floatFromInt(img_size.y + vis.y)) / natural_scale; _ = dvui.image( @src(), .{ .source = .{ .texture = tex } }, .{ .background = false, .expand = .none, .gravity_x = 0.0, .gravity_y = 0.0, .margin = .{ .x = left, .y = top, .w = canvas.pos.x, .h = canvas.pos.y }, .min_size_content = .{ .w = @as(f32, @floatFromInt(vis.w)) / natural_scale, .h = @as(f32, @floatFromInt(vis.h)) / natural_scale, }, .max_size_content = .{ .w = @as(f32, @floatFromInt(vis.w)) / natural_scale, .h = @as(f32, @floatFromInt(vis.h)) / natural_scale, }, }, ); } } canvas_layer.deinit(); } fn handleCanvasZoom(canvas: *Canvas, scroll: anytype) void { const ctrl = dvui.currentWindow().modifiers.control(); if (!ctrl) return; for (dvui.events()) |*e| { switch (e.evt) { .mouse => |mouse| { const action = mouse.action; if (dvui.eventMatchSimple(e, scroll.data()) and (action == .wheel_x or action == .wheel_y)) { switch (action) { .wheel_y => |y| { canvas.addZoom(y / 1000); canvas.requestRedraw(); }, else => {}, } e.handled = true; } }, else => {}, } } }