Files
Zivro/src/main.zig

198 lines
7.7 KiB
Zig
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 {
const canvas = &ctx.canvas;
const ctrl: bool = dvui.currentWindow().modifiers.control();
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", .{}, .{})) {
canvas.fillRandomGradient() catch |err| {
std.debug.print("Error filling canvas: {}\n", .{err});
};
canvas.pos = .{ .x = 800, .y = 400 };
}
}
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));
{
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,
},
);
{
// Отобразить canvas внутри scroll area.
// ScrollArea сам двигает дочерние виджеты, поэтому margin не нужен.
if (canvas.texture) |texture| {
const img_size = canvas.getScaledSize();
_ = dvui.image(@src(), .{
.source = .{ .texture = texture },
}, .{
.margin = .{ .x = canvas.pos.x, .y = canvas.pos.y },
.min_size_content = .{
.w = img_size.w,
.h = img_size.h,
},
});
// Получить viewport и scroll offset
const viewport_rect = scroll.data().contentRect();
const scroll_current = dvui.Point{ .x = canvas.scroll.viewport.x, .y = canvas.scroll.viewport.y };
const visible_rect = canvas.getVisibleImageRect(viewport_rect, scroll_current);
std.debug.print("Visible image rect: {any}\n", .{visible_rect});
}
// Заблокировать события скролла, если нажат ctrl
if (ctrl) {
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.redrawGradient() catch {};
},
else => {},
}
e.handled = true;
}
},
else => {},
}
}
}
}
scroll.deinit();
dvui.label(@src(), "Canvas", .{}, .{ .gravity_x = 0.5, .gravity_y = 0.0 });
}
overlay.deinit();
}
textured.deinit();
}
right_panel.deinit();
}
back.deinit();
return true;
}