const std = @import("std"); const builtin = @import("builtin"); const dvui = @import("dvui"); const dvui_ext = @import("./ui/dvui_ext.zig"); const SDLBackend = @import("sdl-backend"); const Document = @import("Document.zig"); const WindowContext = @import("WindowContext.zig"); const sdl_c = SDLBackend.c; const Allocator = std.mem.Allocator; const Color = dvui.Color; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const allocator = gpa.allocator(); var backend = try SDLBackend.initWindow(.{ .allocator = allocator, .size = .{ .w = 800.0, .h = 600.0 }, .title = "My DVUI App", .vsync = true, }); defer backend.deinit(); var win = try dvui.Window.init(@src(), allocator, backend.backend(), .{ .theme = switch (backend.preferredColorScheme() orelse .light) { .light => dvui.Theme.builtin.adwaita_light, .dark => dvui.Theme.builtin.adwaita_dark, }, }); defer win.deinit(); var ctx = WindowContext.init(allocator); defer ctx.deinit(); var interrupted = false; main_loop: while (true) { // beginWait coordinates with waitTime below to run frames only when needed const nstime = win.beginWait(interrupted); // marks the beginning of a frame for dvui, can call dvui functions after this try win.begin(nstime); // send all SDL events to dvui for processing try backend.addAllEvents(&win); // if dvui widgets might not cover the whole window, then need to clear // the previous frame's render _ = SDLBackend.c.SDL_SetRenderDrawColor(backend.renderer, 0, 0, 0, 255); _ = SDLBackend.c.SDL_RenderClear(backend.renderer); const keep_running = gui_frame(&ctx); if (!keep_running) break :main_loop; // marks end of dvui frame, don't call dvui functions after this // - sends all dvui stuff to backend for rendering, must be called before renderPresent() const end_micros = try win.end(.{}); // cursor management try backend.setCursor(win.cursorRequested()); try backend.textInputRect(win.textInputRequested()); // render frame to OS try backend.renderPresent(); // waitTime and beginWait combine to achieve variable framerates const wait_event_micros = win.waitTime(end_micros); interrupted = try backend.waitEventTimeout(wait_event_micros); } } fn gui_frame(ctx: *WindowContext) bool { for (dvui.events()) |*e| { if (e.evt == .window and e.evt.window.action == .close) return false; if (e.evt == .app and e.evt.app.action == .quit) return false; } const root = dvui.box( @src(), .{ .dir = .horizontal }, .{ .expand = .both, .background = true, .style = .window }, ); defer root.deinit(); // Левая панель с фиксированной шириной var left_panel = dvui.box(@src(), .{ .dir = .vertical }, .{ .expand = .vertical, .min_size_content = .{ .w = 200 }, .background = true }); { dvui.label(@src(), "Tools", .{}, .{}); if (dvui.button(@src(), "Fill Random Color", .{}, .{})) { ctx.fillRandomColor() catch |err| { std.debug.print("Error filling canvas: {}\n", .{err}); }; } } left_panel.deinit(); // Правая панель - занимает оставшееся пространство const back = dvui.box( @src(), .{ .dir = .horizontal }, .{ .expand = .both, .padding = dvui.Rect.all(12), .background = true }, ); { const fill_color = Color.white.opacity(0.5); var right_panel = dvui.box( @src(), .{ .dir = .vertical }, .{ .expand = .both, .background = true, .padding = dvui.Rect.all(5), .corner_radius = dvui.Rect.all(24), .color_fill = fill_color, }, ); { var textured = dvui_ext.texturedBox(right_panel.data().contentRectScale(), dvui.Rect.all(20)); { const overlay = dvui.overlay(@src(), .{ .expand = .both }); { // Отобразить canvas внутри контейнера if (ctx.canvas_texture) |texture| { _ = dvui.image(@src(), .{ .source = .{ .texture = texture }, }, .{ .margin = .{ .x = ctx.canvas_pos.x, .y = ctx.canvas_pos.y }, .min_size_content = .{ .w = @floatFromInt(ctx.canvas_width), .h = @floatFromInt(ctx.canvas_height), }, }); } dvui.label(@src(), "Canvas", .{}, .{ .gravity_x = 0.5, .gravity_y = 0.0 }); } overlay.deinit(); } textured.deinit(); } right_panel.deinit(); } back.deinit(); return true; }