Первая версия тулбара
This commit is contained in:
0
assets/icons/.gitkeep
Normal file
0
assets/icons/.gitkeep
Normal file
@@ -9,11 +9,13 @@ const Size_i = basic_models.Size_i;
|
|||||||
const Point2_f = @import("models/basic_models.zig").Point2_f;
|
const Point2_f = @import("models/basic_models.zig").Point2_f;
|
||||||
const Color = dvui.Color;
|
const Color = dvui.Color;
|
||||||
|
|
||||||
|
const Toolbar = @import("Toolbar.zig");
|
||||||
const Canvas = @This();
|
const Canvas = @This();
|
||||||
|
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
document: *Document,
|
document: *Document,
|
||||||
render_engine: RenderEngine,
|
render_engine: RenderEngine,
|
||||||
|
toolbar: Toolbar,
|
||||||
texture: ?dvui.Texture = null,
|
texture: ?dvui.Texture = null,
|
||||||
pos: dvui.Point = dvui.Point{ .x = 400, .y = 400 },
|
pos: dvui.Point = dvui.Point{ .x = 400, .y = 400 },
|
||||||
scroll: dvui.ScrollInfo = .{
|
scroll: dvui.ScrollInfo = .{
|
||||||
@@ -30,15 +32,17 @@ cursor_document_point: ?Point2_f = null,
|
|||||||
/// true — рисовать документ (render), false — пример (gradient/squares).
|
/// true — рисовать документ (render), false — пример (gradient/squares).
|
||||||
draw_document: bool = true,
|
draw_document: bool = true,
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, document: *Document, engine: RenderEngine) Canvas {
|
pub fn init(allocator: std.mem.Allocator, document: *Document, engine: RenderEngine, toolbar: Toolbar) Canvas {
|
||||||
return .{
|
return .{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.document = document,
|
.document = document,
|
||||||
.render_engine = engine,
|
.render_engine = engine,
|
||||||
|
.toolbar = toolbar,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Canvas) void {
|
pub fn deinit(self: *Canvas) void {
|
||||||
|
self.toolbar.deinit();
|
||||||
if (self.texture) |texture| {
|
if (self.texture) |texture| {
|
||||||
dvui.Texture.destroyLater(texture);
|
dvui.Texture.destroyLater(texture);
|
||||||
self.texture = null;
|
self.texture = null;
|
||||||
|
|||||||
36
src/Toolbar.zig
Normal file
36
src/Toolbar.zig
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
//! Структура тулбара инструментов. Жизненный цикл совпадает с Canvas.
|
||||||
|
|
||||||
|
const tool_interface = @import("tool_interface.zig");
|
||||||
|
|
||||||
|
const Toolbar = @This();
|
||||||
|
|
||||||
|
/// Описание одного инструмента для тулбара.
|
||||||
|
pub const ToolDescriptor = struct {
|
||||||
|
name: []const u8,
|
||||||
|
/// Иконка в формате TVG (байты).
|
||||||
|
icon_tvg: []const u8,
|
||||||
|
/// Реализация интерфейса инструмента (своя для каждого инструмента в tools/).
|
||||||
|
implementation: *const tool_interface.Tool,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Вертикальный тулбар инструментов.
|
||||||
|
tools: []const ToolDescriptor,
|
||||||
|
selected_index: usize,
|
||||||
|
|
||||||
|
pub fn init(tools_list: []const ToolDescriptor) Toolbar {
|
||||||
|
return .{
|
||||||
|
.tools = tools_list,
|
||||||
|
.selected_index = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(_: *Toolbar) void {}
|
||||||
|
|
||||||
|
pub fn currentDescriptor(self: *const Toolbar) ?*const ToolDescriptor {
|
||||||
|
if (self.tools.len == 0) return null;
|
||||||
|
return &self.tools[self.selected_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select(self: *Toolbar, index: usize) void {
|
||||||
|
if (index < self.tools.len) self.selected_index = index;
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ const RenderEngine = @import("render/RenderEngine.zig").RenderEngine;
|
|||||||
const Document = @import("models/Document.zig");
|
const Document = @import("models/Document.zig");
|
||||||
const random_document = @import("models/random_document.zig");
|
const random_document = @import("models/random_document.zig");
|
||||||
const basic_models = @import("models/basic_models.zig");
|
const basic_models = @import("models/basic_models.zig");
|
||||||
|
const tools = @import("tools.zig");
|
||||||
|
const Toolbar = @import("Toolbar.zig");
|
||||||
|
|
||||||
const WindowContext = @This();
|
const WindowContext = @This();
|
||||||
|
|
||||||
@@ -17,7 +19,12 @@ pub const OpenDocument = struct {
|
|||||||
const default_size = basic_models.Size_f{ .w = 800, .h = 600 };
|
const default_size = basic_models.Size_f{ .w = 800, .h = 600 };
|
||||||
self.document = Document.init(allocator, default_size);
|
self.document = Document.init(allocator, default_size);
|
||||||
self.cpu_render = CpuRenderEngine.init(allocator, .Squares);
|
self.cpu_render = CpuRenderEngine.init(allocator, .Squares);
|
||||||
self.canvas = Canvas.init(allocator, &self.document, (&self.cpu_render).renderEngine());
|
self.canvas = Canvas.init(
|
||||||
|
allocator,
|
||||||
|
&self.document,
|
||||||
|
(&self.cpu_render).renderEngine(),
|
||||||
|
Toolbar.init(&tools.default_tools),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *OpenDocument) void {
|
pub fn deinit(self: *OpenDocument) void {
|
||||||
|
|||||||
3
src/icons.zig
Normal file
3
src/icons.zig
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
const dvui = @import("dvui");
|
||||||
|
|
||||||
|
pub const line = dvui.entypo.line_graph;
|
||||||
14
src/tool_interface.zig
Normal file
14
src/tool_interface.zig
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
//! Общий интерфейс инструмента. Реализации живут в каталоге tools/.
|
||||||
|
|
||||||
|
const Point2_f = @import("models/basic_models.zig").Point2_f;
|
||||||
|
|
||||||
|
/// Контекст вызова: холст и точка в координатах документа.
|
||||||
|
pub const ToolContext = struct {
|
||||||
|
canvas: *anyopaque,
|
||||||
|
document_point: Point2_f,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Интерфейс инструмента: один метод — клик по холсту в позиции курсора.
|
||||||
|
pub const Tool = struct {
|
||||||
|
onClick: *const fn (*const ToolContext) void,
|
||||||
|
};
|
||||||
13
src/tools.zig
Normal file
13
src/tools.zig
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
//! Список инструментов по умолчанию для тулбара. Реализации — в каталоге tools/.
|
||||||
|
|
||||||
|
const Toolbar = @import("Toolbar.zig");
|
||||||
|
const line = @import("tools/line.zig");
|
||||||
|
const icons = @import("icons.zig");
|
||||||
|
|
||||||
|
pub const default_tools = [_]Toolbar.ToolDescriptor{
|
||||||
|
.{
|
||||||
|
.name = "Line",
|
||||||
|
.icon_tvg = icons.line,
|
||||||
|
.implementation = &line.tool,
|
||||||
|
},
|
||||||
|
};
|
||||||
21
src/tools/line.zig
Normal file
21
src/tools/line.zig
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
//! Инструмент «Линия»: создаёт линию в позиции клика.
|
||||||
|
|
||||||
|
const Canvas = @import("../Canvas.zig");
|
||||||
|
const tool_interface = @import("../tool_interface.zig");
|
||||||
|
const shape = @import("../models/shape/shape.zig");
|
||||||
|
|
||||||
|
fn onClick(ctx: *const tool_interface.ToolContext) void {
|
||||||
|
const canvas: *Canvas = @alignCast(@ptrCast(ctx.canvas));
|
||||||
|
var obj = shape.createObject(canvas.document.allocator, .line) catch return;
|
||||||
|
obj.setProperty(canvas.document.allocator, .{ .data = .{ .position = ctx.document_point } }) catch {
|
||||||
|
obj.deinit(canvas.document.allocator);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
canvas.document.addObject(obj) catch {
|
||||||
|
obj.deinit(canvas.document.allocator);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
canvas.requestRedraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const tool = tool_interface.Tool{ .onClick = onClick };
|
||||||
@@ -3,6 +3,7 @@ const dvui = @import("dvui");
|
|||||||
const dvui_ext = @import("dvui_ext.zig");
|
const dvui_ext = @import("dvui_ext.zig");
|
||||||
const Canvas = @import("../Canvas.zig");
|
const Canvas = @import("../Canvas.zig");
|
||||||
const Rect_i = @import("../models/basic_models.zig").Rect_i;
|
const Rect_i = @import("../models/basic_models.zig").Rect_i;
|
||||||
|
const tool_interface = @import("../tool_interface.zig");
|
||||||
|
|
||||||
pub fn canvasView(canvas: *Canvas, content_rect_scale: dvui.RectScale) void {
|
pub fn canvasView(canvas: *Canvas, content_rect_scale: dvui.RectScale) void {
|
||||||
var textured = dvui_ext.texturedBox(content_rect_scale, dvui.Rect.all(20));
|
var textured = dvui_ext.texturedBox(content_rect_scale, dvui.Rect.all(20));
|
||||||
@@ -25,6 +26,7 @@ pub fn canvasView(canvas: *Canvas, content_rect_scale: dvui.RectScale) void {
|
|||||||
}
|
}
|
||||||
scroll.deinit();
|
scroll.deinit();
|
||||||
|
|
||||||
|
drawToolbar(canvas);
|
||||||
dvui.label(@src(), "Canvas", .{}, .{ .gravity_x = 0.5, .gravity_y = 0.0 });
|
dvui.label(@src(), "Canvas", .{}, .{ .gravity_x = 0.5, .gravity_y = 0.0 });
|
||||||
}
|
}
|
||||||
overlay.deinit();
|
overlay.deinit();
|
||||||
@@ -159,10 +161,49 @@ fn handleCanvasMouse(canvas: *Canvas, scroll: anytype) void {
|
|||||||
};
|
};
|
||||||
const doc_pt = canvas.contentPointToDocument(content_pt, natural_scale);
|
const doc_pt = canvas.contentPointToDocument(content_pt, natural_scale);
|
||||||
canvas.cursor_document_point = if (canvas.isContentPointOnDocument(content_pt, natural_scale)) doc_pt else null;
|
canvas.cursor_document_point = if (canvas.isContentPointOnDocument(content_pt, natural_scale)) doc_pt else null;
|
||||||
if (canvas.cursor_document_point) |point|
|
if (canvas.cursor_document_point) |point| {
|
||||||
std.debug.print("cursor_document_point: {}\n", .{point});
|
if (canvas.toolbar.currentDescriptor()) |desc| {
|
||||||
|
var ctx = tool_interface.ToolContext{
|
||||||
|
.canvas = canvas,
|
||||||
|
.document_point = point,
|
||||||
|
};
|
||||||
|
desc.implementation.onClick(&ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn drawToolbar(canvas: *Canvas) void {
|
||||||
|
const tools_list = canvas.toolbar.tools;
|
||||||
|
if (tools_list.len == 0) return;
|
||||||
|
|
||||||
|
var bar = dvui.box(
|
||||||
|
@src(),
|
||||||
|
.{ .dir = .vertical },
|
||||||
|
.{
|
||||||
|
.gravity_x = 0.0,
|
||||||
|
.gravity_y = 0.0,
|
||||||
|
.margin = dvui.Rect{ .x = 8, .y = 8 },
|
||||||
|
.padding = dvui.Rect.all(6),
|
||||||
|
.corner_radius = dvui.Rect.all(8),
|
||||||
|
.background = true,
|
||||||
|
.color_fill = dvui.Color.black.opacity(0.2),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
{
|
||||||
|
for (tools_list, 0..) |*tool_desc, i| {
|
||||||
|
const is_selected = (canvas.toolbar.selected_index == i);
|
||||||
|
const opts: dvui.Options = .{
|
||||||
|
.id_extra = i,
|
||||||
|
.color_fill = if (is_selected) dvui.Color.transparent else undefined,
|
||||||
|
};
|
||||||
|
if (dvui.buttonIcon(@src(), tool_desc.name, tool_desc.icon_tvg, .{}, .{}, opts)) {
|
||||||
|
canvas.toolbar.select(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bar.deinit();
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user