From 8ea5d97c2d60291694f448ffda9b49e01eba41e3 Mon Sep 17 00:00:00 2001 From: Roman Pytkov Date: Mon, 2 Mar 2026 17:19:55 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9D=D0=B0=D1=87=D0=B0=D0=BB=D0=BE=20=D1=84?= =?UTF-8?q?=D0=B8=D0=BA=D1=81=D0=B0=20json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Canvas.zig | 2 +- src/WindowContext.zig | 12 ++++----- src/models/Document.zig | 42 +++++++++++++++---------------- src/persistence/document_json.zig | 14 +++++------ src/random_document.zig | 5 ++-- src/toolbar/Tool.zig | 4 +-- src/toolbar/tools/arc.zig | 2 +- src/toolbar/tools/broken.zig | 2 +- src/toolbar/tools/ellipse.zig | 2 +- src/toolbar/tools/line.zig | 2 +- src/ui/canvas_view.zig | 24 +++++++++--------- src/ui/left_panel.zig | 2 +- 12 files changed, 55 insertions(+), 58 deletions(-) diff --git a/src/Canvas.zig b/src/Canvas.zig index 91090d7..21b9aba 100644 --- a/src/Canvas.zig +++ b/src/Canvas.zig @@ -92,7 +92,7 @@ pub fn exampleReset(self: *Canvas) !void { } pub fn addRandomShapes(self: *Canvas) !void { - try random_document.addRandomShapes(self.document, std.crypto.random); + try random_document.addRandomShapes(self.document, self.allocator, std.crypto.random); self.requestRedraw(); } diff --git a/src/WindowContext.zig b/src/WindowContext.zig index 734d1c8..9e14d05 100644 --- a/src/WindowContext.zig +++ b/src/WindowContext.zig @@ -17,7 +17,7 @@ pub const OpenDocument = struct { pub fn init(allocator: std.mem.Allocator, self: *OpenDocument) void { const default_size = basic_models.Size_f{ .w = 800, .h = 600 }; - self.document = Document.init(allocator, default_size); + self.document = Document.init(default_size); self.cpu_render = CpuRenderEngine.init(allocator, .Squares); self.canvas = Canvas.init( allocator, @@ -38,8 +38,8 @@ pub const OpenDocument = struct { self.selected_object_id = null; } - pub fn deinit(self: *OpenDocument) void { - self.document.deinit(); + pub fn deinit(self: *OpenDocument, allocator: std.mem.Allocator) void { + self.document.deinit(allocator); self.canvas.deinit(); } }; @@ -60,7 +60,7 @@ pub fn init(allocator: std.mem.Allocator) !WindowContext { pub fn deinit(self: *WindowContext) void { for (self.documents.items) |ptr| { - ptr.deinit(); + ptr.deinit(self.allocator); self.allocator.destroy(ptr); } self.documents.deinit(self.allocator); @@ -85,7 +85,7 @@ pub fn addDocument(self: *WindowContext, doc: Document) !void { const ptr = try self.allocator.create(OpenDocument); errdefer self.allocator.destroy(ptr); var doc_mut = doc; - errdefer doc_mut.deinit(); + errdefer doc_mut.deinit(self.allocator); OpenDocument.initWithDocument(self.allocator, ptr, doc_mut); try self.documents.append(self.allocator, ptr); self.active_document_index = self.documents.items.len - 1; @@ -100,7 +100,7 @@ pub fn setActiveDocument(self: *WindowContext, index: usize) void { pub fn closeDocument(self: *WindowContext, index: usize) void { if (index >= self.documents.items.len) return; const open_doc = self.documents.items[index]; - open_doc.deinit(); + open_doc.deinit(self.allocator); self.allocator.destroy(open_doc); _ = self.documents.orderedRemove(index); diff --git a/src/models/Document.zig b/src/models/Document.zig index 603894c..668fbcd 100644 --- a/src/models/Document.zig +++ b/src/models/Document.zig @@ -7,71 +7,69 @@ pub const Object = @import("Object.zig"); const shape = @import("shape/shape.zig"); size: Size_f, -allocator: std.mem.Allocator, objects: std.ArrayList(Object), next_object_id: u64, -pub fn init(allocator: std.mem.Allocator, size: Size_f) Document { +pub fn init(size: Size_f) Document { return .{ .size = size, - .allocator = allocator, .objects = std.ArrayList(Object).empty, .next_object_id = 1, }; } -pub fn deinit(self: *Document) void { - for (self.objects.items) |*obj| obj.deinit(self.allocator); - self.objects.deinit(self.allocator); +pub fn deinit(self: *Document, allocator: std.mem.Allocator) void { + for (self.objects.items) |*obj| obj.deinit(allocator); + self.objects.deinit(allocator); } -pub fn addObject(self: *Document, template: Object) !void { - const obj = try template.clone(self.allocator, &self.next_object_id); - try self.objects.append(self.allocator, obj); +pub fn addObject(self: *Document, allocator: std.mem.Allocator, template: Object) !void { + const obj = try template.clone(allocator, &self.next_object_id); + try self.objects.append(allocator, obj); } /// Добавляет объект в документ: как ребёнка родителя (если id найден), иначе в корень. -pub fn addObjectUnderParentId(self: *Document, parent_id: ?u64, template: Object) !void { +pub fn addObjectUnderParentId(self: *Document, allocator: std.mem.Allocator, parent_id: ?u64, template: Object) !void { if (parent_id) |id| { if (self.findObjectById(id)) |parent| { - try parent.addChild(self.allocator, template, &self.next_object_id); + try parent.addChild(allocator, template, &self.next_object_id); return; } } - try self.addObject(template); + try self.addObject(allocator, template); } -pub fn addShape(self: *Document, parent: ?*Object, shape_kind: Object.ShapeKind) !void { - const obj = try shape.createObject(self.allocator, shape_kind); +pub fn addShape(self: *Document, allocator: std.mem.Allocator, parent: ?*Object, shape_kind: Object.ShapeKind) !void { + const obj = try shape.createObject(allocator, shape_kind); if (parent) |p| { - try p.addChild(self.allocator, obj, &self.next_object_id); + try p.addChild(allocator, obj, &self.next_object_id); } else { - try self.addObject(obj); + try self.addObject(allocator, obj); } } /// Удаляет объект из документа (из корня или из детей родителя). Возвращает true, если объект был найден и удалён. -pub fn removeObject(self: *Document, obj: *Object) bool { +pub fn removeObject(self: *Document, allocator: std.mem.Allocator, obj: *Object) bool { for (self.objects.items, 0..) |*item, i| { if (item == obj) { var removed = self.objects.orderedRemove(i); - removed.deinit(self.allocator); + removed.deinit(allocator); return true; } - if (removeFromChildren(self.allocator, &item.children, obj)) return true; + if (removeFromChildren(allocator, &item.children, obj)) return true; } return false; } /// Удаляет объект по id. Возвращает true, если объект был найден и удалён. -pub fn removeObjectById(self: *Document, obj_id: u64) bool { +pub fn removeObjectById(self: *Document, allocator: std.mem.Allocator, obj_id: u64) bool { for (self.objects.items, 0..) |*item, i| { if (item.id == obj_id) { var removed = self.objects.orderedRemove(i); - removed.deinit(self.allocator); + removed.deinit(allocator); return true; } - if (removeFromChildrenById(self.allocator, &item.children, obj_id)) return true; + if (removeFromChildrenById(allocator, &item.children, obj_id)) return true; } return false; } diff --git a/src/persistence/document_json.zig b/src/persistence/document_json.zig index e837517..87d0b11 100644 --- a/src/persistence/document_json.zig +++ b/src/persistence/document_json.zig @@ -18,12 +18,13 @@ pub fn saveToFile(doc: *const Document, path: []const u8) !void { var buffer: [4096]u8 = undefined; var writer = file.writer(&buffer); - var jw: std.json.Stringify = .{ - .writer = &writer.interface, - .options = .{ .whitespace = .indent_2 }, - }; - try writeDocument(&jw, doc); + try std.json.Stringify.value( + doc, + .{ .whitespace = .indent_2 }, + &writer.interface, + ); + try writer.interface.flush(); } @@ -158,11 +159,10 @@ fn parseDocumentValue(allocator: std.mem.Allocator, value: std.json.Value) !Docu var document = Document{ .size = size, - .allocator = allocator, .objects = std.ArrayList(Object).empty, .next_object_id = 1, }; - errdefer document.deinit(); + errdefer document.deinit(allocator); const objects_val = try getField(obj, "objects"); const objects_arr = try expectArray(objects_val); diff --git a/src/random_document.zig b/src/random_document.zig index 2f0b76f..7ce6609 100644 --- a/src/random_document.zig +++ b/src/random_document.zig @@ -90,17 +90,16 @@ fn randomizeObjectProperties(allocator: std.mem.Allocator, doc_size: *const Size } /// Создаёт в документе случайные фигуры (line, ellipse, broken). -pub fn addRandomShapes(doc: *Document, rng: std.Random) !void { +pub fn addRandomShapes(doc: *Document, allocator: std.mem.Allocator, rng: std.Random) !void { const max_total: usize = 80; var total_count: usize = 0; - const allocator = doc.allocator; const n_root = rng.intRangeLessThan(usize, 6, 15); for (0..n_root) |_| { if (total_count >= max_total) break; var obj = try shape.createObject(allocator, randomShapeKind(rng)); try randomizeObjectProperties(allocator, &doc.size, &obj, rng); - try doc.addObject(obj); + try doc.addObject(allocator, obj); total_count += 1; } diff --git a/src/toolbar/Tool.zig b/src/toolbar/Tool.zig index c2b75bb..838e9d9 100644 --- a/src/toolbar/Tool.zig +++ b/src/toolbar/Tool.zig @@ -13,8 +13,8 @@ pub const ToolContext = struct { pub fn addObject(self: *const ToolContext, template: Document.Object) !void { var obj = template; const local_pos = self.computeLocalPosition(); - try obj.setProperty(self.canvas.document.allocator, .{ .data = .{ .position = local_pos } }); - try self.canvas.document.addObjectUnderParentId(self.selected_object_id, obj); + try obj.setProperty(self.canvas.allocator, .{ .data = .{ .position = local_pos } }); + try self.canvas.document.addObjectUnderParentId(self.canvas.allocator, self.selected_object_id, obj); self.canvas.requestRedraw(); } diff --git a/src/toolbar/tools/arc.zig b/src/toolbar/tools/arc.zig index 93d2720..ee55cc5 100644 --- a/src/toolbar/tools/arc.zig +++ b/src/toolbar/tools/arc.zig @@ -3,7 +3,7 @@ const shape = @import("../../models/shape/shape.zig"); fn onCanvasClick(ctx: *const Tool.ToolContext) !void { const canvas = ctx.canvas; - var obj = shape.createObject(canvas.document.allocator, .arc) catch return; + var obj = shape.createObject(canvas.allocator, .arc) catch return; defer obj.deinit(canvas.allocator); try ctx.addObject(obj); } diff --git a/src/toolbar/tools/broken.zig b/src/toolbar/tools/broken.zig index 6a491c3..149d498 100644 --- a/src/toolbar/tools/broken.zig +++ b/src/toolbar/tools/broken.zig @@ -3,7 +3,7 @@ const shape = @import("../../models/shape/shape.zig"); fn onCanvasClick(ctx: *const Tool.ToolContext) !void { const canvas = ctx.canvas; - var obj = shape.createObject(canvas.document.allocator, .broken) catch return; + var obj = shape.createObject(canvas.allocator, .broken) catch return; defer obj.deinit(canvas.allocator); try ctx.addObject(obj); } diff --git a/src/toolbar/tools/ellipse.zig b/src/toolbar/tools/ellipse.zig index 9364633..e556f9d 100644 --- a/src/toolbar/tools/ellipse.zig +++ b/src/toolbar/tools/ellipse.zig @@ -3,7 +3,7 @@ const shape = @import("../../models/shape/shape.zig"); fn onCanvasClick(ctx: *const Tool.ToolContext) !void { const canvas = ctx.canvas; - var obj = shape.createObject(canvas.document.allocator, .ellipse) catch return; + var obj = shape.createObject(canvas.allocator, .ellipse) catch return; defer obj.deinit(canvas.allocator); try ctx.addObject(obj); } diff --git a/src/toolbar/tools/line.zig b/src/toolbar/tools/line.zig index d833db0..05ee2a7 100644 --- a/src/toolbar/tools/line.zig +++ b/src/toolbar/tools/line.zig @@ -5,7 +5,7 @@ const shape = @import("../../models/shape/shape.zig"); fn onCanvasClick(ctx: *const Tool.ToolContext) !void { const canvas = ctx.canvas; - var obj = shape.createObject(canvas.document.allocator, .line) catch return; + var obj = shape.createObject(canvas.allocator, .line) catch return; defer obj.deinit(canvas.allocator); try ctx.addObject(obj); } diff --git a/src/ui/canvas_view.zig b/src/ui/canvas_view.zig index 5bdf20f..a10259e 100644 --- a/src/ui/canvas_view.zig +++ b/src/ui/canvas_view.zig @@ -417,7 +417,7 @@ fn drawPropertyEditor(canvas: *Canvas, obj: *Document.Object, prop: *const Prope changed = res.changed or changed; } if (changed) { - obj.setProperty(canvas.document.allocator, .{ .data = .{ .position = next } }) catch {}; + obj.setProperty(canvas.allocator, .{ .data = .{ .position = next } }) catch {}; canvas.requestRedraw(); } }, @@ -431,7 +431,7 @@ fn drawPropertyEditor(canvas: *Canvas, obj: *Document.Object, prop: *const Prope subrow.deinit(); if (res.changed) { next = degrees * std.math.pi / 180.0; - obj.setProperty(canvas.document.allocator, .{ .data = .{ .angle = next } }) catch {}; + obj.setProperty(canvas.allocator, .{ .data = .{ .angle = next } }) catch {}; canvas.requestRedraw(); } } @@ -456,28 +456,28 @@ fn drawPropertyEditor(canvas: *Canvas, obj: *Document.Object, prop: *const Prope changed = res.changed or changed; } if (changed) { - obj.setProperty(canvas.document.allocator, .{ .data = .{ .scale = next } }) catch {}; + obj.setProperty(canvas.allocator, .{ .data = .{ .scale = next } }) catch {}; canvas.requestRedraw(); } }, .visible => |v| { var next = v; if (dvui.checkbox(@src(), &next, "Visible", .{})) { - obj.setProperty(canvas.document.allocator, .{ .data = .{ .visible = next } }) catch {}; + obj.setProperty(canvas.allocator, .{ .data = .{ .visible = next } }) catch {}; canvas.requestRedraw(); } }, .opacity => |opacity| { var next = opacity; if (dvui.sliderEntry(@src(), "{d:0.2}", .{ .value = &next, .min = 0.0, .max = 1.0, .interval = 0.01 }, .{ .expand = .horizontal })) { - obj.setProperty(canvas.document.allocator, .{ .data = .{ .opacity = next } }) catch {}; + obj.setProperty(canvas.allocator, .{ .data = .{ .opacity = next } }) catch {}; canvas.requestRedraw(); } }, .locked => |v| { var next = v; if (dvui.checkbox(@src(), &next, "Locked", .{})) { - obj.setProperty(canvas.document.allocator, .{ .data = .{ .locked = next } }) catch {}; + obj.setProperty(canvas.allocator, .{ .data = .{ .locked = next } }) catch {}; canvas.requestRedraw(); } }, @@ -502,7 +502,7 @@ fn drawPropertyEditor(canvas: *Canvas, obj: *Document.Object, prop: *const Prope changed = res.changed or changed; } if (changed) { - obj.setProperty(canvas.document.allocator, .{ .data = .{ .size = next } }) catch {}; + obj.setProperty(canvas.allocator, .{ .data = .{ .size = next } }) catch {}; canvas.requestRedraw(); } }, @@ -527,7 +527,7 @@ fn drawPropertyEditor(canvas: *Canvas, obj: *Document.Object, prop: *const Prope changed = res.changed or changed; } if (changed) { - obj.setProperty(canvas.document.allocator, .{ .data = .{ .radii = next } }) catch {}; + obj.setProperty(canvas.allocator, .{ .data = .{ .radii = next } }) catch {}; canvas.requestRedraw(); } }, @@ -556,7 +556,7 @@ fn drawPropertyEditor(canvas: *Canvas, obj: *Document.Object, prop: *const Prope changed = res.changed or changed; } if (changed) { - obj.setProperty(canvas.document.allocator, .{ .data = .{ .end_point = next } }) catch {}; + obj.setProperty(canvas.allocator, .{ .data = .{ .end_point = next } }) catch {}; canvas.requestRedraw(); } }, @@ -578,7 +578,7 @@ fn drawPropertyEditor(canvas: *Canvas, obj: *Document.Object, prop: *const Prope const res = dvui.textEntryNumber(@src(), T, .{ .value = &next, .min = @as(T, 0.0), .max = @as(T, 100.0) }, .{ .expand = .horizontal }); subrow.deinit(); if (res.changed) { - obj.setProperty(canvas.document.allocator, .{ .data = .{ .thickness = next } }) catch {}; + obj.setProperty(canvas.allocator, .{ .data = .{ .thickness = next } }) catch {}; canvas.requestRedraw(); } } @@ -597,9 +597,9 @@ fn drawColorEditor(canvas: *Canvas, obj: *Document.Object, rgba: u32, is_fill: b )) { const next = colorToRgba(hsv.toColor()); if (is_fill) { - obj.setProperty(canvas.document.allocator, .{ .data = .{ .fill_rgba = next } }) catch {}; + obj.setProperty(canvas.allocator, .{ .data = .{ .fill_rgba = next } }) catch {}; } else { - obj.setProperty(canvas.document.allocator, .{ .data = .{ .stroke_rgba = next } }) catch {}; + obj.setProperty(canvas.allocator, .{ .data = .{ .stroke_rgba = next } }) catch {}; } canvas.requestRedraw(); } diff --git a/src/ui/left_panel.zig b/src/ui/left_panel.zig index 20f97b6..eb93771 100644 --- a/src/ui/left_panel.zig +++ b/src/ui/left_panel.zig @@ -122,7 +122,7 @@ fn objectTree(ctx: *WindowContext) void { } }, .delete => |obj_id| { - _ = doc.removeObjectById(obj_id); + _ = doc.removeObjectById(ctx.allocator, obj_id); if (open_doc.selected_object_id == obj_id) open_doc.selected_object_id = null; open_doc.canvas.requestRedraw();