Skip to content

Commit 6da0f55

Browse files
authored
Associate PopupWindows with an ID for their active popup (#6693)
Popups are stored in a HashMap and are assigned an ID so popup.close(); closes the correct popup and so a single PopupWindow cannot be opened multiple times
1 parent d30dfc0 commit 6da0f55

File tree

11 files changed

+460
-75
lines changed

11 files changed

+460
-75
lines changed

api/cpp/include/slint_window.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,18 +106,23 @@ class WindowAdapterRc
106106
}
107107

108108
template<typename Component, typename Parent, typename PosGetter>
109-
void show_popup(const Parent *parent_component, PosGetter pos,
110-
cbindgen_private::PopupClosePolicy close_policy,
111-
cbindgen_private::ItemRc parent_item) const
109+
uint32_t show_popup(const Parent *parent_component, PosGetter pos,
110+
cbindgen_private::PopupClosePolicy close_policy,
111+
cbindgen_private::ItemRc parent_item) const
112112
{
113113
auto popup = Component::create(parent_component);
114114
cbindgen_private::Point p = pos(popup);
115115
auto popup_dyn = popup.into_dyn();
116-
cbindgen_private::slint_windowrc_show_popup(&inner, &popup_dyn, p, close_policy,
117-
&parent_item);
116+
return cbindgen_private::slint_windowrc_show_popup(&inner, &popup_dyn, p, close_policy,
117+
&parent_item);
118118
}
119119

120-
void close_popup() const { cbindgen_private::slint_windowrc_close_popup(&inner); }
120+
void close_popup(uint32_t popup_id) const
121+
{
122+
if (popup_id > 0) {
123+
cbindgen_private::slint_windowrc_close_popup(&inner, popup_id);
124+
}
125+
}
121126

122127
template<std::invocable<RenderingState, GraphicsAPI> F>
123128
std::optional<SetRenderingNotifierError> set_rendering_notifier(F callback) const

internal/backends/qt/qt_window.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ cpp! {{
165165
void *parent_window = p->rust_window;
166166
bool inside = rect().contains(event->pos());
167167
bool close_on_click = rust!(Slint_mouseReleaseEventPopup [parent_window: &QtWindow as "void*", inside: bool as "bool"] -> bool as "bool" {
168-
let close_policy = parent_window.close_policy();
168+
let close_policy = parent_window.top_close_policy();
169169
close_policy == PopupClosePolicy::CloseOnClick || (close_policy == PopupClosePolicy::CloseOnClickOutside && !inside)
170170
});
171171
if (close_on_click) {
@@ -182,7 +182,7 @@ cpp! {{
182182
});
183183
if (parent_of_popup_to_close) {
184184
rust!(Slint_mouseReleaseEventClosePopup [parent_of_popup_to_close: &QtWindow as "void*"] {
185-
parent_of_popup_to_close.close_popup();
185+
parent_of_popup_to_close.close_top_popup();
186186
});
187187
}
188188
}
@@ -1746,12 +1746,12 @@ impl QtWindow {
17461746
timer_event();
17471747
}
17481748

1749-
fn close_popup(&self) {
1750-
WindowInner::from_pub(&self.window).close_popup();
1749+
fn close_top_popup(&self) {
1750+
WindowInner::from_pub(&self.window).close_top_popup();
17511751
}
17521752

1753-
fn close_policy(&self) -> PopupClosePolicy {
1754-
WindowInner::from_pub(&self.window).close_policy()
1753+
fn top_close_policy(&self) -> PopupClosePolicy {
1754+
WindowInner::from_pub(&self.window).top_close_policy()
17551755
}
17561756

17571757
fn window_state_event(&self) {

internal/compiler/generator/cpp.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,6 +1969,17 @@ fn generate_sub_component(
19691969
));
19701970
}
19711971

1972+
for (i, _) in component.popup_windows.iter().enumerate() {
1973+
target_struct.members.push((
1974+
field_access,
1975+
Declaration::Var(Var {
1976+
ty: ident("mutable uint32_t"),
1977+
name: format_smolstr!("popup_id_{}", i),
1978+
..Default::default()
1979+
}),
1980+
));
1981+
}
1982+
19721983
for (prop1, prop2) in &component.two_way_bindings {
19731984
init.push(format!(
19741985
"slint::private_api::Property<{ty}>::link_two_way(&{p1}, &{p2});",
@@ -3613,15 +3624,28 @@ fn compile_builtin_function_call(
36133624
let position = compile_expression(&popup.position.borrow(), &popup_ctx);
36143625
let close_policy = compile_expression(close_policy, ctx);
36153626
format!(
3616-
"{window}.show_popup<{popup_window_id}>({component_access}, [=](auto self) {{ return {position}; }}, {close_policy}, {{ {parent_component} }})"
3627+
"{window}.close_popup({component_access}->popup_id_{popup_index}); {component_access}->popup_id_{popup_index} = {window}.show_popup<{popup_window_id}>({component_access}, [=](auto self) {{ return {position}; }}, {close_policy}, {{ {parent_component} }})"
36173628
)
36183629
} else {
36193630
panic!("internal error: invalid args to ShowPopupWindow {:?}", arguments)
36203631
}
36213632
}
36223633
BuiltinFunction::ClosePopupWindow => {
3623-
let window = access_window_field(ctx);
3624-
format!("{window}.close_popup()")
3634+
if let [llr::Expression::NumberLiteral(popup_index), llr::Expression::PropertyReference(parent_ref)] = arguments {
3635+
let mut parent_ctx = ctx;
3636+
let mut component_access = "self".into();
3637+
3638+
if let llr::PropertyReference::InParent { level, .. } = parent_ref {
3639+
for _ in 0..level.get() {
3640+
component_access = format!("{}->parent", component_access);
3641+
parent_ctx = parent_ctx.parent.as_ref().unwrap().ctx;
3642+
}
3643+
};
3644+
let window = access_window_field(ctx);
3645+
format!("{window}.close_popup({component_access}->popup_id_{popup_index})")
3646+
} else {
3647+
panic!("internal error: invalid args to ClosePopupWindow {:?}", arguments)
3648+
}
36253649
}
36263650
BuiltinFunction::SetSelectionOffsets => {
36273651
if let [llr::Expression::PropertyReference(pr), from, to] = arguments {

internal/compiler/generator/rust.rs

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,9 @@ fn generate_sub_component(
10011001
sub_component_types.push(sub_component_id);
10021002
}
10031003

1004+
let popup_id_names =
1005+
component.popup_windows.iter().enumerate().map(|(i, _)| internal_popup_id(i));
1006+
10041007
for (prop1, prop2) in &component.two_way_bindings {
10051008
let p1 = access_member(prop1, &ctx);
10061009
let p2 = access_member(prop2, &ctx);
@@ -1116,6 +1119,7 @@ fn generate_sub_component(
11161119
struct #inner_component_id {
11171120
#(#item_names : sp::#item_types,)*
11181121
#(#sub_component_names : #sub_component_types,)*
1122+
#(#popup_id_names : ::core::cell::Cell<sp::Option<::core::num::NonZeroU32>>,)*
11191123
#(#declared_property_vars : sp::Property<#declared_property_types>,)*
11201124
#(#declared_callbacks : sp::Callback<(#(#declared_callbacks_types,)*), #declared_callbacks_ret>,)*
11211125
#(#repeated_element_names : sp::Repeater<#repeated_element_components>,)*
@@ -1819,6 +1823,12 @@ fn inner_component_id(component: &llr::SubComponent) -> proc_macro2::Ident {
18191823
format_ident!("Inner{}", ident(&component.name))
18201824
}
18211825

1826+
fn internal_popup_id(index: usize) -> proc_macro2::Ident {
1827+
let mut name = index.to_string();
1828+
name.insert_str(0, "popup_id_");
1829+
ident(&name)
1830+
}
1831+
18221832
fn global_inner_name(g: &llr::GlobalComponent) -> TokenStream {
18231833
if g.is_builtin {
18241834
let i = ident(&g.name);
@@ -2660,27 +2670,51 @@ fn compile_builtin_function_call(
26602670

26612671
let close_policy = compile_expression(close_policy, ctx);
26622672
let window_adapter_tokens = access_window_adapter_field(ctx);
2673+
let popup_id_name = internal_popup_id(*popup_index as usize);
26632674
quote!({
26642675
let popup_instance = #popup_window_id::new(#component_access_tokens.self_weak.get().unwrap().clone()).unwrap();
26652676
let popup_instance_vrc = sp::VRc::map(popup_instance.clone(), |x| x);
26662677
#popup_window_id::user_init(popup_instance_vrc.clone());
26672678
let position = { let _self = popup_instance_vrc.as_pin_ref(); #position };
2668-
sp::WindowInner::from_pub(#window_adapter_tokens.window()).show_popup(
2669-
&sp::VRc::into_dyn(popup_instance.into()),
2670-
position,
2671-
#close_policy,
2672-
#parent_component
2673-
)
2679+
if let Some(current_id) = #component_access_tokens.#popup_id_name.take() {
2680+
sp::WindowInner::from_pub(#window_adapter_tokens.window()).close_popup(current_id);
2681+
}
2682+
#component_access_tokens.#popup_id_name.set(Some(
2683+
sp::WindowInner::from_pub(#window_adapter_tokens.window()).show_popup(
2684+
&sp::VRc::into_dyn(popup_instance.into()),
2685+
position,
2686+
#close_policy,
2687+
#parent_component
2688+
))
2689+
);
26742690
})
26752691
} else {
26762692
panic!("internal error: invalid args to ShowPopupWindow {:?}", arguments)
26772693
}
26782694
}
26792695
BuiltinFunction::ClosePopupWindow => {
2680-
let window_adapter_tokens = access_window_adapter_field(ctx);
2681-
quote!(
2682-
sp::WindowInner::from_pub(#window_adapter_tokens.window()).close_popup()
2683-
)
2696+
if let [Expression::NumberLiteral(popup_index), Expression::PropertyReference(parent_ref)] =
2697+
arguments
2698+
{
2699+
let mut parent_ctx = ctx;
2700+
let mut component_access_tokens = quote!(_self);
2701+
if let llr::PropertyReference::InParent { level, .. } = parent_ref {
2702+
for _ in 0..level.get() {
2703+
component_access_tokens =
2704+
quote!(#component_access_tokens.parent.upgrade().unwrap().as_pin_ref());
2705+
parent_ctx = parent_ctx.parent.as_ref().unwrap().ctx;
2706+
}
2707+
}
2708+
let window_adapter_tokens = access_window_adapter_field(ctx);
2709+
let popup_id_name = internal_popup_id(*popup_index as usize);
2710+
quote!(
2711+
if let Some(current_id) = #component_access_tokens.#popup_id_name.take() {
2712+
sp::WindowInner::from_pub(#window_adapter_tokens.window()).close_popup(current_id);
2713+
}
2714+
)
2715+
} else {
2716+
panic!("internal error: invalid args to ClosePopupWindow {:?}", arguments)
2717+
}
26842718
}
26852719
BuiltinFunction::SetSelectionOffsets => {
26862720
if let [llr::Expression::PropertyReference(pr), from, to] = arguments {

internal/compiler/llr/lower_expression.rs

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,7 @@ pub fn lower_expression(
122122
lower_show_popup(arguments, ctx)
123123
}
124124
tree_Expression::BuiltinFunctionReference(BuiltinFunction::ClosePopupWindow, _) => {
125-
// FIXME: right now, `popup.close()` will close any visible popup, as the popup argument is ignored
126-
llr_Expression::BuiltinFunctionCall {
127-
function: BuiltinFunction::ClosePopupWindow,
128-
arguments: vec![],
129-
}
125+
lower_close_popup(arguments, ctx)
130126
}
131127
tree_Expression::BuiltinFunctionReference(f, _) => {
132128
let mut arguments =
@@ -399,6 +395,38 @@ fn lower_show_popup(args: &[tree_Expression], ctx: &ExpressionContext) -> llr_Ex
399395
}
400396
}
401397

398+
fn lower_close_popup(args: &[tree_Expression], ctx: &ExpressionContext) -> llr_Expression {
399+
if let [tree_Expression::ElementReference(e)] = args {
400+
let popup_window = e.upgrade().unwrap();
401+
let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
402+
let parent_component = pop_comp
403+
.parent_element
404+
.upgrade()
405+
.unwrap()
406+
.borrow()
407+
.enclosing_component
408+
.upgrade()
409+
.unwrap();
410+
let popup_list = parent_component.popup_windows.borrow();
411+
let (popup_index, popup) = popup_list
412+
.iter()
413+
.enumerate()
414+
.find(|(_, p)| Rc::ptr_eq(&p.component, &pop_comp))
415+
.unwrap();
416+
let item_ref = lower_expression(
417+
&tree_Expression::ElementReference(Rc::downgrade(&popup.parent_element)),
418+
ctx,
419+
);
420+
421+
llr_Expression::BuiltinFunctionCall {
422+
function: BuiltinFunction::ClosePopupWindow,
423+
arguments: vec![llr_Expression::NumberLiteral(popup_index as _), item_ref],
424+
}
425+
} else {
426+
panic!("invalid arguments to ShowPopupWindow");
427+
}
428+
}
429+
402430
pub fn lower_animation(a: &PropertyAnimation, ctx: &ExpressionContext<'_>) -> Animation {
403431
fn lower_animation_element(a: &ElementRc, ctx: &ExpressionContext<'_>) -> llr_Expression {
404432
llr_Expression::Struct {

0 commit comments

Comments
 (0)