Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ jobs:
zig build -Dshape=studio -Dzon-layout=true --summary all
- name: Build the replay app
run: zig build replay --summary all
- name: Build the dvui app
run: zig build dvui --summary all
93 changes: 74 additions & 19 deletions dvui/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
//! through `dvui.osWindow` — nothing of fizzy the editor, and nothing but dvui in the app's code.
//!
//! On fizzy's backend each `dvui.osWindow` is a floating window in the one frame, shown in an OS
//! window of its own that looks just as the floating window does in the main window: dvui's header
//! and close button, no OS title bar. The OS moves it by its header; it resizes itself from its
//! edges, as a floating window does, and its window follows. The backend does all of it
//! (`backend/src/os_windows.zig` in fizzy). On dvui's own SDL3 backend (`-Ddvui-backend=sdl3`)
//! `dvui.osWindow` opens a `dvui.Window` of its own instead, as dvui does anywhere.
//!
//! dvui's demo is a floating window, and stays one: in the main window, as in dvui's own example.
//! window of its own: the OS's title bar and buttons in the theme's colour, as on the main window,
//! and the floating window's content below. The OS moves, resizes, zooms and closes it; on macOS it
//! is an ordinary window, with a full screen of its own. The backend does all of it
//! (`backend/src/os_windows.zig` in fizzy). Every other floating window, dvui's demo among them,
//! stays in the main window, as in dvui's own example. On dvui's own SDL3 backend
//! (`-Ddvui-backend=sdl3`) `dvui.osWindow` opens a `dvui.Window` of its own instead.
const std = @import("std");
const dvui = @import("dvui");

Expand All @@ -23,8 +22,10 @@ pub const main = dvui.App.main;
pub const panic = dvui.App.panic;
pub const std_options: std.Options = .{ .logFn = dvui.App.logFn };

/// An OS window of the app's own.
/// A window of the app's own: an in-app floating window, which can be dragged out into an OS window
/// of its own on fizzy's backend, or a `dvui.osWindow`.
const Window = struct {
os: bool,
open: bool = true,
title_buf: [32:0]u8 = @splat(0),
title_len: usize = 0,
Expand All @@ -34,59 +35,113 @@ const Window = struct {
return self.title_buf[0..self.title_len :0];
}
};
var windows: [4]?Window = @splat(null);
var windows: [6]?Window = @splat(null);
var made: u32 = 0;
var started = false;

fn frame() !dvui.App.Result {
// dvui's demo and two OS windows to begin with.
// dvui's demo, in the main window, to begin with. An OS window opens only when one is asked
// for ("New window"), or when a floating window is dragged out of the app's windows.
if (!started) {
started = true;
dvui.Examples.show_demo_window = true;
newWindow();
newWindow();
}
mainWindow();
// dvui's demo first: a window opens over those opened before it, and the demo, centred and much
// the larger, hid the app's own windows opened under it.
dvui.Examples.demo(.lite);
for (&windows, 0..) |*slot, i| if (slot.*) |*w| {
if (!w.open) {
slot.* = null;
continue;
}
osWindow(w, i);
if (w.os) osWindow(w, i) else floatWindow(w, i);
};
dvui.Examples.demo(.lite);
return .ok;
}

fn mainWindow() void {
// No fill of its own: the window's colour is the backend's, under what the window is made of
// (`materialPicker`), the theme's window colour at the transparency asked.
var box = dvui.box(@src(), .{}, .{ .expand = .both, .padding = .all(16) });
defer box.deinit();
dvui.labelNoFmt(@src(), "dvui's own widgets on fizzy's backend.", .{}, .{ .font = .theme(.title) });
dvui.labelNoFmt(@src(), "Each window opened here is a dvui.osWindow.", .{}, .{});
if (dvui.button(@src(), "New window", .{}, .{ .margin = .{ .y = 12 } })) newWindow();
dvui.labelNoFmt(@src(), "A window opens in the app, and drags out into one of its own; an OS window opens as one.", .{}, .{});
{
var row = dvui.box(@src(), .{ .dir = .horizontal }, .{ .margin = .{ .y = 12 } });
defer row.deinit();
if (dvui.button(@src(), "New window", .{}, .{})) newWindow(false);
if (dvui.button(@src(), "New OS window", .{}, .{})) newWindow(true);
}
if (!dvui.Examples.show_demo_window) {
if (dvui.button(@src(), "Show the dvui demo", .{}, .{})) dvui.Examples.show_demo_window = true;
}
if (comptime @hasDecl(dvui.backend, "window_material")) materialPicker();
}

/// What the app's windows are made of, on fizzy's backend: opaque, clear, blurred, frosted or
/// Liquid Glass, each falling back to what the OS can show, and how transparent. The colour is the
/// theme's window colour (dvui's theme picker).
fn materialPicker() void {
const B = dvui.backend;
var row = dvui.box(@src(), .{ .dir = .horizontal }, .{ .margin = .{ .y = 12 } });
defer row.deinit();
const names = [_][]const u8{ "Opaque", "Clear", "Blur", "Frost", "Liquid Glass" };
var choice: usize = @backingInt(B.window_material.kind);
if (dvui.dropdown(@src(), &names, .{ .choice = &choice }, .{}, .{ .min_size_content = .{ .w = 140 }, .gravity_y = 0.5 })) {
B.window_material.kind = @fromBackingInt(@as(u8, @intCast(choice)));
// Opaque, the window's colour covers it whole: half, to begin with.
if (B.window_material.kind != .none and B.window_material.opacity >= 1) B.window_material.opacity = 0.5;
}
dvui.labelNoFmt(@src(), "Transparency", .{}, .{ .gravity_y = 0.5, .margin = .{ .x = 12 } });
var transparency = if (B.window_material.kind == .none) 0 else 1 - B.window_material.opacity;
if (dvui.slider(@src(), .{ .fraction = &transparency }, .{ .min_size_content = .{ .w = 160, .h = 20 }, .gravity_y = 0.5 })) {
// Transparent at all, it is made of something: clear, where it was opaque.
if (B.window_material.kind == .none and transparency > 0) B.window_material.kind = .transparent;
// Glass keeps a little of the window's colour, the backend's floor: all the way, it would
// be no glass. Only a clear window goes all the way.
const floor: f32 = if (comptime @hasDecl(B, "material_min_opacity")) switch (B.window_material.kind) {
.blur, .frost, .liquid => B.material_min_opacity,
else => 0,
} else 0;
B.window_material.opacity = @max(1 - transparency, floor);
}
}

fn newWindow() void {
fn newWindow(os: bool) void {
const slot = for (&windows) |*slot| {
if (slot.* == null) break slot;
} else return;
made += 1;
slot.* = .{};
slot.* = .{ .os = os };
const w = &slot.*.?;
const t: []const u8 = std.fmt.bufPrint(&w.title_buf, "Window {d}", .{made}) catch "";
const t: []const u8 = std.fmt.bufPrint(&w.title_buf, "{s} {d}", .{ if (os) "OS window" else "Window", made }) catch "";
w.title_len = t.len;
}

/// An in-app floating window: on fizzy's backend, dragged by its header past the app's windows it
/// tears off into an OS window of its own (its close button, `open_flag`, is what lets it).
fn floatWindow(w: *Window, i: usize) void {
var float = dvui.floatingWindow(@src(), .{ .open_flag = &w.open, .window_avoid = .nudge }, .{
.id_extra = i,
.min_size_content = .{ .w = 220, .h = 120 },
});
defer float.deinit();
float.dragAreaSet(dvui.windowHeader(w.title(), "", &w.open));
windowContent(w);
}

fn osWindow(w: *Window, i: usize) void {
var os = dvui.osWindow(@src(), .{
.title = w.title(),
.size = .{ .w = 280, .h = 180 },
.min_size = .{ .w = 220, .h = 120 },
}, .{ .id_extra = i, .open_flag = &w.open });
defer os.deinit();
windowContent(w);
}

fn windowContent(w: *Window) void {
var box = dvui.box(@src(), .{}, .{ .expand = .both, .padding = .all(12) });
defer box.deinit();
dvui.label(@src(), "Clicked {d} times", .{w.count}, .{});
Expand Down
Loading