init
It's expected to call this when self is undefined
Parameters
- self:*FloatingMenuWidget
- *FloatingMenuWidget
- src:std.builtin.SourceLocation
- std.builtin.SourceLocation
- init_opts:InitOptions
- InitOptions
- opts:Options
- Options
Source
Implementation
pub fn init(self: *FloatingMenuWidget, src: std.builtin.SourceLocation, init_opts: InitOptions, opts: Options) void {
self.* = .{
// the widget itself doesn't have any styling, it comes from the
// embedded MenuWidget
// passing options.rect will stop WidgetData.init from calling
// rectFor/minSizeForChild which is important because we are outside
// normal layout
.wd = .init(src, .{ .subwindow = true }, .{ .id_extra = opts.id_extra, .rect = .{} }),
// get scale from parent
.scale_val = dvui.parentGet().screenRectScale(Rect{}).s / dvui.windowNaturalScale(),
.render_ftb = undefined,
.prev_last_focus = undefined,
.prevClip = undefined,
.menu = undefined,
.scaler = undefined,
.scroll = undefined,
};
const options = defaults.override(opts);
// NOTE: options is really for our embedded ScrollAreaWidget
const avoid: dvui.PlaceOnScreenAvoid = switch (init_opts.avoid) {
.none => .none,
.horizontal => .horizontal,
.vertical => .vertical,
.auto => if (dvui.MenuWidget.current()) |pm| switch (pm.init_opts.dir) {
.horizontal => .vertical,
.vertical => .horizontal,
} else .none,
};
self.data().rect = Rect.fromPoint(.cast(init_opts.from.topLeft()));
if (dvui.minSizeGet(self.data().id)) |_| {
const ms = dvui.minSize(self.data().id, options.min_sizeGet());
self.data().rect = self.data().rect.toSize(ms);
self.data().rect = .cast(dvui.placeOnScreen(dvui.windowRect(), init_opts.from, avoid, .cast(self.data().rect)));
if (dvui.dataGet(null, self.data().id, "_check_focus", void) != null) {
dvui.dataRemove(null, self.data().id, "_check_focus");
if (dvui.MenuWidget.current() == null or !dvui.MenuWidget.current().?.mouse_mode or self.data().rectScale().r.contains(dvui.currentWindow().mouse_pt)) {
dvui.focusSubwindow(self.data().id, null);
}
}
} else {
self.data().rect = .cast(dvui.placeOnScreen(dvui.windowRect(), init_opts.from, avoid, .cast(self.data().rect)));
dvui.dataSet(null, self.data().id, "_check_focus", {});
// need a second frame to fit contents (FocusWindow calls refresh but
// here for clarity)
dvui.refresh(null, @src(), self.data().id);
}
self.data().register();
dvui.parentSet(self.widget());
// standard subwindow stuff
{
const rs = self.data().rectScale();
self.render_ftb.initReset();
self.prev_windowInfo = dvui.subwindowCurrentSet(self.data().id, null);
dvui.subwindowAdd(self.data().id, self.data().rect, rs.r, false, 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.parent_fmw = currentSet(self);
const rs = self.data().rectScale();
const evts = dvui.events();
for (evts) |*e| {
if (!dvui.eventMatch(e, .{ .id = self.data().id, .r = rs.r }))
continue;
if (e.evt == .mouse and e.evt.mouse.action == .focus) {
// focus but let the focus event propagate to widgets
dvui.focusSubwindow(self.data().id, e.num);
}
}
self.scaler.init(@src(), .{ .scale = &self.scale_val }, .{ .expand = .both });
// we are using scroll to do border/background but floating windows
// don't have margin, so turn that off
self.scroll.init(@src(), .{ .horizontal = .none }, options.override(.{ .margin = .{}, .expand = .both }));
if (dvui.MenuWidget.current()) |pm| {
pm.child_popup_rect = rs.r;
}
self.menu.init(@src(), .{ .dir = .vertical, .parentSubwindowId = self.prev_windowInfo.id }, options.strip().override(.{ .role = .none, .expand = .horizontal }));
}