-
Notifications
You must be signed in to change notification settings - Fork 40
/
Copy pathFloatingWidget.zig
117 lines (92 loc) · 3.62 KB
/
FloatingWidget.zig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
const std = @import("std");
const dvui = @import("../dvui.zig");
const Event = dvui.Event;
const Options = dvui.Options;
const Rect = dvui.Rect;
const RectScale = dvui.RectScale;
const Size = dvui.Size;
const Widget = dvui.Widget;
const WidgetData = dvui.WidgetData;
const FloatingWidget = @This();
pub var defaults: Options = .{
.name = "Floating",
};
prev_rendering: bool = undefined,
wd: WidgetData = undefined,
prev_windowId: u32 = 0,
prevClip: Rect = Rect{},
scale_val: f32 = undefined,
scaler: dvui.ScaleWidget = undefined,
/// FloatingWidget is a subwindow to show any temporary floating thing.
/// It doesn't focus itself (as a subwindow), and whether it is shown or not is
/// entirely up to the calling code.
///
/// Don't put menus or menuItems in a floating widget because those depend on
/// focus to work. FloatingMenu is made for that.
///
/// Use FloatingWindowWidget for a floating window that the user can change
/// size, move around, and adjust stacking.
pub fn init(src: std.builtin.SourceLocation, opts_in: Options) FloatingWidget {
var self = FloatingWidget{};
// get scale from parent
self.scale_val = dvui.parentGet().screenRectScale(Rect{}).s / dvui.windowNaturalScale();
var opts = opts_in;
if (opts.min_size_content) |msc| {
opts.min_size_content = msc.scale(self.scale_val);
}
// passing options.rect will stop WidgetData.init from calling
// rectFor/minSizeForChild which is important because we are outside
// normal layout
self.wd = WidgetData.init(src, .{ .subwindow = true }, defaults.override(opts).override(.{ .rect = opts.rect orelse .{} }));
self.prev_rendering = dvui.renderingSet(false);
return self;
}
pub fn install(self: *FloatingWidget) !void {
dvui.parentSet(self.widget());
self.prev_windowId = dvui.subwindowCurrentSet(self.wd.id, null).id;
const rs = self.wd.rectScale();
try dvui.subwindowAdd(self.wd.id, self.wd.rect, rs.r, false, self.prev_windowId);
dvui.captureMouseMaintain(self.wd.id);
try self.wd.register();
// clip to just our window (using clipSet since we are not inside our parent)
self.prevClip = dvui.clipGet();
dvui.clipSet(rs.r);
self.scaler = dvui.ScaleWidget.init(@src(), self.scale_val, .{ .expand = .both });
try self.scaler.install();
}
pub fn widget(self: *FloatingWidget) Widget {
return Widget.init(self, data, rectFor, screenRectScale, minSizeForChild, processEvent);
}
pub fn data(self: *FloatingWidget) *WidgetData {
return &self.wd;
}
pub fn rectFor(self: *FloatingWidget, id: u32, min_size: Size, e: Options.Expand, g: Options.Gravity) Rect {
_ = id;
return dvui.placeIn(self.wd.contentRect().justSize(), min_size, e, g);
}
pub fn screenRectScale(self: *FloatingWidget, rect: Rect) RectScale {
return self.wd.contentRectScale().rectToRectScale(rect);
}
pub fn minSizeForChild(self: *FloatingWidget, s: Size) void {
self.wd.minSizeMax(self.wd.options.padSize(s));
}
pub fn processEvent(self: *FloatingWidget, e: *Event, bubbling: bool) void {
// no normal events, just forward close_popup
switch (e.evt) {
.close_popup => {
self.wd.parent.processEvent(e, true);
},
else => {},
}
// otherwise don't bubble events
_ = bubbling;
}
pub fn deinit(self: *FloatingWidget) void {
self.scaler.deinit();
self.wd.minSizeSetAndRefresh();
// outside normal layout, don't call minSizeForChild or self.wd.minSizeReportToParent();
dvui.parentReset(self.wd.id, self.wd.parent);
_ = dvui.subwindowCurrentSet(self.prev_windowId, null);
dvui.clipSet(self.prevClip);
_ = dvui.renderingSet(self.prev_rendering);
}