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
10 changes: 10 additions & 0 deletions backend/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,16 @@ pub fn objcModule(dep: *std.Build.Dependency, opts: Options) ?*std.Build.Module
return objc.module("objc");
}

/// `viewports` for a backend without OS windows besides the main one (`src/viewports_none.zig`):
/// dvui's own, its testing backend, the web. An app that builds on either kind imports it as
/// `viewports_none` and takes the backend's own `viewports` where it has one. Needs only dvui,
/// so nothing of SDL is fetched for it.
pub fn viewportsNoneModule(dep: *std.Build.Dependency, dvui_mod: *std.Build.Module) *std.Build.Module {
const m = dep.builder.createModule(.{ .root_source_file = dep.builder.path("src/viewports_none.zig") });
m.addImport("dvui", dvui_mod);
return m;
}

/// zigwin32, from this package's pin, for an app's own Windows code. Null on the configure pass
/// that fetches it.
pub fn win32Module(dep: *std.Build.Dependency) ?*std.Build.Module {
Expand Down
44 changes: 23 additions & 21 deletions backend/src/SDLBackend.zig
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ const dvui = @import("dvui");

const GpuRenderer = @import("GpuRenderer.zig");
pub const viewport_map = @import("viewport_map.zig");
/// The OS windows besides the main one an app draws with (`Viewport`).
pub const viewports = @import("viewports.zig");
/// What this backend keeps count of as it runs (`health`).
pub const Health = @import("Health.zig");

Expand Down Expand Up @@ -94,7 +96,7 @@ window_geometry: WindowGeometry = .{},
// Set by `initWindow` and `initWindowSecondary` for use by eventual child window.
init_opts_save: ?InitOptions = null,
/// The OS windows besides this one that show part of its frame (`Viewport`).
viewports: [max_viewports]?Viewport = @splat(null),
viewport_slots: [max_viewports]?Viewport = @splat(null),
/// Where a held pointer is read while one is held (`PointerPin`, `heldPoint`).
pointer_pin: PointerPin = .none,
/// macOS: the main window may have come in front of a viewport's window — it took focus, was
Expand Down Expand Up @@ -915,7 +917,7 @@ fn noteMainForward(self: *SDLBackend, target: ?*c.SDL_Window, event_type: u32) v
};
if (event_type == c.SDL_EVENT_WINDOW_FOCUS_GAINED) self.main_focused_once = true;
if (!over) return;
for (&self.viewports) |*slot| if (slot.*) |*v| {
for (&self.viewport_slots) |*slot| if (slot.*) |*v| {
v.over_main = true;
};
}
Expand Down Expand Up @@ -1172,7 +1174,7 @@ fn openViewport(self: *SDLBackend, at: viewport_map.Rect, title_text: [:0]const
const carry = role != .window;
const menu = role == .menu;
if (!viewportsAvailable()) return null;
const slot = for (self.viewports, 0..) |v, i| {
const slot = for (self.viewport_slots, 0..) |v, i| {
if (v == null) break i;
} else return null;
const b = viewport_map.band(slot);
Expand Down Expand Up @@ -1220,8 +1222,8 @@ fn openViewport(self: *SDLBackend, at: viewport_map.Rect, title_text: [:0]const
// where SDL makes it a child window that moves with its parent, and the windows that left
// the main one stay where they are when it moves (`plans/POPOUT_WINDOWS_PLAN.md`, decision 2).
if (comptime builtin.os.tag != .macos) _ = c.SDL_SetWindowParent(window, self.window);
self.viewports[slot] = .{ .window = window, .band = b, .anchor = anchor, .density = d, .screen = placed.screen, .frame = placed.frame, .passive = carry and !menu, .overlay = role == .overlay, .menu = menu };
const vp = &self.viewports[slot].?;
self.viewport_slots[slot] = .{ .window = window, .band = b, .anchor = anchor, .density = d, .screen = placed.screen, .frame = placed.frame, .passive = carry and !menu, .overlay = role == .overlay, .menu = menu };
const vp = &self.viewport_slots[slot].?;
// Dressed by `viewportOpenMenu`, and no hit test: nothing in it moves or resizes it.
if (menu) return vp;
if (role == .overlay) {
Expand Down Expand Up @@ -1325,13 +1327,13 @@ pub fn viewportClose(self: *SDLBackend, vp: *Viewport) void {
// What rides on it lets go first (`Viewport.ride`): SDL destroys a window's children with it,
// and the slot of each still holds its window. The app moves a dialog left behind back over
// the main window (`Popout`).
for (&self.viewports) |*slot| {
for (&self.viewport_slots) |*slot| {
if (slot.*) |*other| if (other != vp and other.ride != null and other.ride == vp.window) {
_ = c.SDL_SetWindowParent(other.window, null);
other.ride = null;
};
}
for (&self.viewports) |*slot| {
for (&self.viewport_slots) |*slot| {
if (slot.*) |*v| if (v == vp) {
// Holding the keyboard as it goes — a float that merged back into the main window, or
// came back on a command, while its window was the one in front — it hands it back to
Expand All @@ -1352,7 +1354,7 @@ pub fn viewportClose(self: *SDLBackend, vp: *Viewport) void {
break;
};
}
for (self.viewports) |v| if (v != null) return;
for (self.viewport_slots) |v| if (v != null) return;
_ = c.SDL_ResetHint(c.SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH);
}

Expand Down Expand Up @@ -1623,7 +1625,7 @@ pub fn viewportPresent(_: *SDLBackend, vp: *Viewport, target: ?dvui.TextureTarge
}

fn viewportOf(self: *SDLBackend, window: *c.SDL_Window) ?*Viewport {
for (&self.viewports) |*slot| {
for (&self.viewport_slots) |*slot| {
if (slot.*) |*v| if (v.window == window) return v;
}
return null;
Expand Down Expand Up @@ -1666,7 +1668,7 @@ fn heldAcrossWindows(self: *SDLBackend, event: c.SDL_Event) bool {
c.SDL_EVENT_MOUSE_BUTTON_DOWN, c.SDL_EVENT_MOUSE_BUTTON_UP => if (event.button.which == c.SDL_TOUCH_MOUSEID) return false,
else => return false,
}
const any = for (self.viewports) |v| {
const any = for (self.viewport_slots) |v| {
if (v != null) break true;
} else false;
if (!any) return false;
Expand Down Expand Up @@ -1700,7 +1702,7 @@ fn heldPoint(self: *SDLBackend) dvui.Point.Physical {
var mh: c_int = 0;
_ = c.SDL_GetWindowSize(self.window, &mw, &mh);
const over_main = gx >= origin.x and gy >= origin.y and gx < origin.x + @as(f32, @floatFromInt(mw)) and gy < origin.y + @as(f32, @floatFromInt(mh));
for (&self.viewports) |*slot| {
for (&self.viewport_slots) |*slot| {
const vp = if (slot.*) |*v| v else continue;
if (vp.passive or vp.see_through) continue;
// In a fullscreen Space of its own, a window is where the OS put it, and under nothing:
Expand Down Expand Up @@ -1964,7 +1966,7 @@ pub fn deinit(self: *SDLBackend) void {
WindowGeometry.save(self);
}
// Viewports are claimed on this window's device: given back before it goes.
for (&self.viewports) |*slot| {
for (&self.viewport_slots) |*slot| {
if (slot.*) |*v| self.viewportClose(v);
}
self.gpu.destroy();
Expand All @@ -1988,7 +1990,7 @@ pub fn renderPresent(self: *SDLBackend) void {
var transacted: [max_viewports]bool = @splat(false);
var any = false;
if (comptime builtin.os.tag == .macos) {
for (&self.viewports, 0..) |*slot, i| {
for (&self.viewport_slots, 0..) |*slot, i| {
const vp = if (slot.*) |*v| v else continue;
const first = !vp.shown and vp.pending != null;
const reshaped = vp.passive and !vp.overlay and (vp.carry_radius != vp.carry_radius_shown or vp.carry_alpha != vp.carry_alpha_shown or vp.carry_size_shown[0] != vp.screen.w or vp.carry_size_shown[1] != vp.screen.h);
Expand Down Expand Up @@ -2018,7 +2020,7 @@ pub fn renderPresent(self: *SDLBackend) void {
}
}
defer if (comptime builtin.os.tag == .macos) if (any) {
for (&self.viewports, transacted) |*slot, t| {
for (&self.viewport_slots, transacted) |*slot, t| {
if (!t) continue;
const vp = if (slot.*) |*v| v else continue;
fizzy_native_viewport_presented(cocoaWindow(vp.window));
Expand All @@ -2033,7 +2035,7 @@ pub fn renderPresent(self: *SDLBackend) void {
// drawables back slowly while the main window goes through a Space transition, and waiting on
// them every frame halved the move's rate with one open.
const main_moving = platform.window.windowMovingItself(self.window);
for (&self.viewports, 0..) |*slot, i| {
for (&self.viewport_slots, 0..) |*slot, i| {
const vp = if (slot.*) |*v| v else continue;
const target = vp.pending orelse continue;
vp.pending = null;
Expand All @@ -2058,7 +2060,7 @@ pub fn renderPresent(self: *SDLBackend) void {
self.gpu.present(self.clear_window_on_begin, builtin.os.tag == .windows and self.inLiveResize());
// Shown once there is a frame in it (or empty, above). Not made key: the window it came out of
// keeps the keyboard until the viewport is clicked.
for (&self.viewports, first_frame, map_empty) |*slot, show, empty| {
for (&self.viewport_slots, first_frame, map_empty) |*slot, show, empty| {
if (!show and !empty) continue;
const vp = if (slot.*) |*v| v else continue;
if (!vp.mapped) {
Expand All @@ -2081,11 +2083,11 @@ pub fn renderPresent(self: *SDLBackend) void {
// activates, which brings the main window in front of them (`fizzy_macos_watch_first_activation`).
if (comptime builtin.os.tag == .macos) {
fizzy_macos_watch_first_activation();
if (fizzy_macos_took_first_activation() != 0) for (&self.viewports) |*slot| if (slot.*) |*v| {
if (fizzy_macos_took_first_activation() != 0) for (&self.viewport_slots) |*slot| if (slot.*) |*v| {
v.over_main = true;
};
const main_ns = cocoaWindow(self.window);
for (&self.viewports) |*slot| {
for (&self.viewport_slots) |*slot| {
const vp = if (slot.*) |*v| v else continue;
if (!vp.over_main) continue;
vp.over_main = false;
Expand All @@ -2098,7 +2100,7 @@ pub fn renderPresent(self: *SDLBackend) void {
// when the stacking may have changed.
if (self.stack_stale) {
self.stack_stale = false;
for (&self.viewports) |*slot| {
for (&self.viewport_slots) |*slot| {
const vp = if (slot.*) |*v| v else continue;
vp.under_main = vp.mapped and !vp.passive and !vp.menu and fizzy_macos_viewport_under_main(cocoaWindow(vp.window), main_ns) != 0;
}
Expand Down Expand Up @@ -2176,7 +2178,7 @@ pub fn begin(self: *SDLBackend, arena: std.mem.Allocator) !void {
// A float's window standing still on its way into or out of full screen: its picture has
// taken this frame's step (`begin_hook`), and the float follows it there — as it did the
// window's moves — and back to the window as the way ends.
if (comptime builtin.os.tag == .macos) for (&self.viewports) |*slot| {
if (comptime builtin.os.tag == .macos) for (&self.viewport_slots) |*slot| {
const vp = if (slot.*) |*v| v else continue;
if (vp.picture_at[0] != 0 or vp.picture_at[1] != 0 or platform.window.windowSpacePicture(vp.window) != null)
viewportFollowWindow(vp);
Expand Down Expand Up @@ -2227,7 +2229,7 @@ fn mainHeld(self: *SDLBackend) bool {
// Its own way into or out of full screen is drawn whatever AppKit says of it on the way.
if (platform.window.windowMovingItself(self.window)) return false;
if (c.SDL_GetWindowFlags(self.window) & c.SDL_WINDOW_OCCLUDED != 0) return true;
for (&self.viewports) |*slot| {
for (&self.viewport_slots) |*slot| {
const vp = if (slot.*) |*v| v else continue;
if (vp.shown and platform.window.windowMovingItself(vp.window)) return true;
}
Expand Down
44 changes: 42 additions & 2 deletions backend/src/platform/window.zig
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,46 @@ pub const WindowGlass = extern struct {
feather: f64,
};

/// A window's Liquid Glass this frame (`liquidGlassLook`, `viewports.windowGlass`), as an app
/// says it: what `core.glass_look.window` says at the window's opacity, the window's colour, and the clearing
/// bevel `core.glass_look.band` gives its size.
pub const WindowGlassLook = struct {
under_variant: i32,
under_style: i32,
over_variant: i32,
over_style: i32,
frost: f32,
blur: f32,
glass: f32,
fill: dvui.Color,
/// The colour's opacity in the body…
fill_opacity: f32,
/// …and over all of it, the bevel too.
top_fill: f32,
/// Points, each.
radius: f32,
clear: f32,
feather: f32,

/// As `fizzy_macos_window_liquid_glass_look` reads it.
pub fn native(self: WindowGlassLook) WindowGlass {
return .{
.under_variant = self.under_variant,
.under_style = self.under_style,
.over_variant = self.over_variant,
.over_style = self.over_style,
.frost = self.frost,
.blur = self.blur,
.glass = self.glass,
.fill = .{ @as(f64, @floatFromInt(self.fill.r)) / 255, @as(f64, @floatFromInt(self.fill.g)) / 255, @as(f64, @floatFromInt(self.fill.b)) / 255, self.fill_opacity },
.top_fill = self.top_fill,
.radius = self.radius,
.clear = self.clear,
.feather = self.feather,
};
}
};

/// Make one of fizzy's titled windows (`raw_ptr`, its `NSWindow`) a window of Liquid Glass — over
/// the clear lens a body of frost, the plain blur (the vibrancy it wore before,
/// `ns_visual_effect_material`) and the window's colour, fading out across a clearing bevel along
Expand Down Expand Up @@ -259,7 +299,7 @@ pub fn setStyle(win: *dvui.Window) void {
// Not a menu's or a dialog's: dressed once as it opens (`fizzy_macos_viewport_menu`), it
// never goes full screen, and this made it a full-screen primary window over the
// auxiliary one it was opened as, every frame.
for (&win.backend.impl.viewports) |*slot| {
for (&win.backend.impl.viewport_slots) |*slot| {
const vp = if (slot.*) |*v| v else continue;
if (vp.passive or vp.menu) continue;
const ns = c.SDL_GetPointerProperty(c.SDL_GetWindowProperties(vp.window), c.SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, null) orelse continue;
Expand Down Expand Up @@ -383,7 +423,7 @@ pub fn setBackground(win: *dvui.Window, color: dvui.Color) void {
// Every float's own window too, and each opened from now on (`SDLBackend.viewportGlass`):
// side by side, and each in a Space of its own, they are one app's windows.
win.backend.impl.window_skin = color;
for (&win.backend.impl.viewports) |*slot| {
for (&win.backend.impl.viewport_slots) |*slot| {
const vp = if (slot.*) |*v| v else continue;
if (vp.passive) continue;
const ns = c.SDL_GetPointerProperty(c.SDL_GetWindowProperties(vp.window), c.SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, null) orelse continue;
Expand Down
Loading
Loading