sequoia_elements/
popup.rs1use sequoia_core::Edges;
2use sequoia_reactive::Signal;
3use sequoia_view::{EventCtx, OverlayEntry, OverlayId, PaintCtx};
4
5#[derive(Clone)]
6pub struct PopupState {
7 open: Signal<bool>,
8 entry: Signal<Option<OverlayId>>,
9 margin: Edges,
10}
11
12impl PopupState {
13 pub fn new(open: Signal<bool>) -> Self {
14 Self {
15 open,
16 entry: Signal::new(None),
17 margin: Edges::ZERO,
18 }
19 }
20
21 pub fn is_open(&self) -> bool {
22 self.open.get_untracked()
23 }
24
25 pub fn entry_id(&self) -> Option<OverlayId> {
26 self.entry.get_untracked()
27 }
28
29 pub fn open(&self) {
30 self.open.set(true);
31 }
32
33 pub fn close(&self) {
34 self.open.set(false);
35 }
36
37 pub fn toggle(&self) {
38 self.open.set(!self.open.get_untracked());
39 }
40
41 pub fn subscribe(&self) {
42 let _ = self.open.get();
43 }
44
45 pub fn sync_show(&self, ctx: &mut PaintCtx, make: impl FnOnce() -> OverlayEntry) {
46 if !self.open.get_untracked() {
47 if let Some(id) = self.entry.get_untracked() {
48 ctx.hide_overlay(id);
49 self.entry.set(None);
50 }
51 return;
52 }
53 if let Some(id) = self.entry.get_untracked() {
54 if !ctx.has_overlay(id) {
55 self.entry.set(None);
56 self.open.set(false);
57 }
58 return;
59 }
60 let id = ctx.show_overlay(make());
61 self.entry.set(Some(id));
62 }
63
64 pub fn toggle_in(&self, ctx: &mut EventCtx) {
65 if self.open.get_untracked() {
66 self.close_in(ctx);
67 } else {
68 self.open();
69 }
70 ctx.request_focus();
71 ctx.handled();
72 }
73
74 pub fn close_in(&self, ctx: &mut EventCtx) -> bool {
75 let was = self.open.get_untracked();
76 self.open.set(false);
77 if let Some(id) = self.entry.get_untracked() {
78 ctx.hide_overlay(id);
79 self.entry.set(None);
80 }
81 if was {
82 ctx.handled();
83 }
84 was
85 }
86
87 pub fn forget(&self) {
88 self.entry.set(None);
89 }
90
91 pub fn margin(mut self, margin: impl Into<Edges>) -> Self {
92 let m = margin.into();
93 self.margin = Edges::only(
94 sanitize_margin(m.left),
95 sanitize_margin(m.top),
96 sanitize_margin(m.right),
97 sanitize_margin(m.bottom),
98 );
99 self
100 }
101
102 pub fn margin_xy(self, x: f32, y: f32) -> Self {
103 self.margin(Edges::xy(x, y))
104 }
105
106 pub fn margin_x(mut self, x: f32) -> Self {
107 let x = sanitize_margin(x);
108 self.margin.left = x;
109 self.margin.right = x;
110 self
111 }
112
113 pub fn margin_y(mut self, y: f32) -> Self {
114 let y = sanitize_margin(y);
115 self.margin.top = y;
116 self.margin.bottom = y;
117 self
118 }
119
120 pub fn margin_left(mut self, v: f32) -> Self {
121 self.margin.left = sanitize_margin(v);
122 self
123 }
124
125 pub fn margin_right(mut self, v: f32) -> Self {
126 self.margin.right = sanitize_margin(v);
127 self
128 }
129
130 pub fn margin_top(mut self, v: f32) -> Self {
131 self.margin.top = sanitize_margin(v);
132 self
133 }
134
135 pub fn margin_bottom(mut self, v: f32) -> Self {
136 self.margin.bottom = sanitize_margin(v);
137 self
138 }
139}
140
141fn sanitize_margin(v: f32) -> f32 {
142 if v.is_finite() { v.max(0.0) } else { 0.0 }
143}