init
Parameters
- self:*FloatingWindowWidget
- *FloatingWindowWidget
- src:std.builtin.SourceLocation
- std.builtin.SourceLocation
- init_opts:InitOptions
- InitOptions
- opts:Options
- Options
Source
Implementation
pub fn init(self: *FloatingWindowWidget, src: std.builtin.SourceLocation, init_opts: InitOptions, opts: Options) void {
const options = defaults.override(opts);
var box_options = options;
box_options.role = null;
box_options.label = null;
box_options.id_extra = null;
box_options.rect = null; // if the user passes in a rect, don't pass it to the BoxWidget
self.* = .{
// options is really for our embedded BoxWidget, so save them for the
// end of drawBackground()
.options = box_options,
// the floating window itself doesn't have any styling, it comes from
// the embedded BoxWidget
.wd = WidgetData.init(src, .{ .subwindow = true }, .{
.id_extra = opts.id_extra,
// passing options.rect will stop WidgetData.init from calling rectFor
// which is important because we are outside normal layout
.rect = .{},
.role = options.role,
.label = options.label,
.name = options.name,
}),
.init_options = init_opts,
};
// by default we store the rect (only while the window is open)
self.wd.rect = dvui.dataGet(null, self.wd.id, "_rect", Rect) orelse Rect{};
if (self.init_options.rect) |ior| {
// user is storing the rect for us across open/close
self.wd.rect = ior.*;
}
if (dvui.firstFrame(self.wd.id)) {
// Options.rect only affects initial position/size
if (opts.rect) |r| {
self.wd.rect = r;
}
}
if (dvui.dataGet(null, self.wd.id, "_auto_size", @TypeOf(self.auto_size))) |as| {
self.auto_size = as;
} else {
if (self.data().rect.w == 0 and self.wd.rect.h == 0) {
self.autoSize();
}
}
if (dvui.dataGet(null, self.wd.id, "_auto_pos", @TypeOf(self.auto_pos))) |ap| {
self.auto_pos = ap;
} else {
self.auto_pos = (self.wd.rect.x == 0 and self.wd.rect.y == 0);
}
if (dvui.minSizeGet(self.wd.id)) |min_size| {
if (self.auto_size) {
// Track if any of our children called refresh(), and in deinit we
// will turn off auto_size if none of them did.
self.auto_size_refresh_prev_value = dvui.currentWindow().extra_frames_needed;
dvui.currentWindow().extra_frames_needed = 0;
const ms = Size.min(Size.max(min_size, self.options.min_sizeGet()), .cast(dvui.windowRect().size()));
self.wd.rect.w = ms.w;
self.wd.rect.h = ms.h;
}
if (self.auto_pos) {
// only position ourselves once by default
self.auto_pos = false;
const centering: Rect.Natural = self.init_options.center_on orelse dvui.currentWindow().subwindows.current_rect;
self.wd.rect.x = centering.x + (centering.w - self.wd.rect.w) / 2;
self.wd.rect.y = centering.y + (centering.h - self.wd.rect.h) / 2;
if (dvui.snapToPixels()) {
const s = dvui.windowNaturalScale();
self.wd.rect.x = @round(self.wd.rect.x * s) / s;
self.wd.rect.y = @round(self.wd.rect.y * s) / s;
}
if (self.init_options.window_avoid != .none) {
if (self.wd.rect.topLeft().equals(.cast(centering.topLeft()))) {
// if we ended up directly on top, nudge downright a bit
self.wd.rect.x += 24;
self.wd.rect.y += 24;
}
}
if (self.init_options.window_avoid == .nudge) {
const cw = dvui.currentWindow();
// we might nudge onto another window, so have to keep checking until we don't
var nudge = true;
while (nudge) {
nudge = false;
// don't check against subwindows[0] - that's that main window
for (cw.subwindows.stack.items[1..]) |subw| {
if (subw.id != self.wd.id and subw.rect.topLeft().equals(self.data().rect.topLeft())) {
self.wd.rect.x += 24;
self.wd.rect.y += 24;
nudge = true;
}
}
}
}
//std.debug.print("autopos to {}\n", .{self.data().rect});
}
// always make sure we are on the screen
var screen = dvui.windowRect();
// okay if we are off the left or right but still see some
const offleft = self.wd.rect.w - 48;
screen.x -= offleft;
screen.w += offleft + offleft;
// okay if we are off the bottom but still see the top
screen.h += self.wd.rect.h - 24;
self.wd.rect = .cast(dvui.placeOnScreen(screen, .{}, .none, .cast(self.data().rect)));
}
self.data().register();
if (dvui.firstFrame(self.data().id)) {
dvui.focusSubwindow(self.data().id, null);
// write back before we hide ourselves for the first frame
dvui.dataSet(null, self.data().id, "_rect", self.data().rect);
if (self.init_options.rect) |ior| {
// send rect back to user
ior.* = self.data().rect;
}
// need a second frame to fit contents
dvui.refresh(null, @src(), self.data().id);
// hide our first frame so the user doesn't see an empty window or
// jump when we autopos/autosize
self.data().rect.w = 0;
self.data().rect.h = 0;
}
if (dvui.captured(self.data().id)) {
if (dvui.dataGet(null, self.data().id, "_drag_part", DragPart)) |dp| {
self.drag_part = dp;
}
}
dvui.parentSet(self.widget());
// standard subwindow stuff
{
const rs = self.data().rectScale();
self.render_ftb.initReset();
self.prev_windowInfo = dvui.subwindowCurrentSet(self.data().id, .cast(self.data().rect));
dvui.subwindowAdd(self.data().id, self.data().rect, rs.r, self.init_options.modal, if (self.init_options.stay_above_parent_window) self.prev_windowInfo.id else null, true);
dvui.captureMouseMaintain(.{ .id = self.data().id, .rect = rs.r, .subwindow_id = self.data().id });
self.prevClip = dvui.clipGet();
dvui.clipSet(dvui.windowRectPixels()); // break out of whatever clipping we were in
self.prev_scroll = dvui.ScrollContainerWidget.scrollSet(null);
}
// prevents parents from processing key events if focus is inside the floating window
self.prev_last_focus = dvui.lastFocusedIdInFrame();
self.drag_area = self.data().rectScale().r;
if (self.data().accesskit_node()) |ak_node| {
if (self.init_options.modal)
AccessKit.nodeSetModal(ak_node)
else
AccessKit.nodeClearModal(ak_node);
AccessKit.nodeAddAction(ak_node, AccessKit.Action.focus);
}
dvui.toastsShow(self.data().id, .cast(self.data().rect));
}