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
This commit is contained in:
Brandon Fowler 2024-11-04 03:17:55 -05:00 committed by GitHub
parent d30dfc0175
commit 6da0f55b05
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 459 additions and 74 deletions

View file

@ -106,18 +106,23 @@ public:
}
template<typename Component, typename Parent, typename PosGetter>
void show_popup(const Parent *parent_component, PosGetter pos,
uint32_t show_popup(const Parent *parent_component, PosGetter pos,
cbindgen_private::PopupClosePolicy close_policy,
cbindgen_private::ItemRc parent_item) const
{
auto popup = Component::create(parent_component);
cbindgen_private::Point p = pos(popup);
auto popup_dyn = popup.into_dyn();
cbindgen_private::slint_windowrc_show_popup(&inner, &popup_dyn, p, close_policy,
return cbindgen_private::slint_windowrc_show_popup(&inner, &popup_dyn, p, close_policy,
&parent_item);
}
void close_popup() const { cbindgen_private::slint_windowrc_close_popup(&inner); }
void close_popup(uint32_t popup_id) const
{
if (popup_id > 0) {
cbindgen_private::slint_windowrc_close_popup(&inner, popup_id);
}
}
template<std::invocable<RenderingState, GraphicsAPI> F>
std::optional<SetRenderingNotifierError> set_rendering_notifier(F callback) const

View file

@ -165,7 +165,7 @@ cpp! {{
void *parent_window = p->rust_window;
bool inside = rect().contains(event->pos());
bool close_on_click = rust!(Slint_mouseReleaseEventPopup [parent_window: &QtWindow as "void*", inside: bool as "bool"] -> bool as "bool" {
let close_policy = parent_window.close_policy();
let close_policy = parent_window.top_close_policy();
close_policy == PopupClosePolicy::CloseOnClick || (close_policy == PopupClosePolicy::CloseOnClickOutside && !inside)
});
if (close_on_click) {
@ -182,7 +182,7 @@ cpp! {{
});
if (parent_of_popup_to_close) {
rust!(Slint_mouseReleaseEventClosePopup [parent_of_popup_to_close: &QtWindow as "void*"] {
parent_of_popup_to_close.close_popup();
parent_of_popup_to_close.close_top_popup();
});
}
}
@ -1746,12 +1746,12 @@ impl QtWindow {
timer_event();
}
fn close_popup(&self) {
WindowInner::from_pub(&self.window).close_popup();
fn close_top_popup(&self) {
WindowInner::from_pub(&self.window).close_top_popup();
}
fn close_policy(&self) -> PopupClosePolicy {
WindowInner::from_pub(&self.window).close_policy()
fn top_close_policy(&self) -> PopupClosePolicy {
WindowInner::from_pub(&self.window).top_close_policy()
}
fn window_state_event(&self) {

View file

@ -1969,6 +1969,17 @@ fn generate_sub_component(
));
}
for (i, _) in component.popup_windows.iter().enumerate() {
target_struct.members.push((
field_access,
Declaration::Var(Var {
ty: ident("mutable uint32_t"),
name: format_smolstr!("popup_id_{}", i),
..Default::default()
}),
));
}
for (prop1, prop2) in &component.two_way_bindings {
init.push(format!(
"slint::private_api::Property<{ty}>::link_two_way(&{p1}, &{p2});",
@ -3613,15 +3624,28 @@ fn compile_builtin_function_call(
let position = compile_expression(&popup.position.borrow(), &popup_ctx);
let close_policy = compile_expression(close_policy, ctx);
format!(
"{window}.show_popup<{popup_window_id}>({component_access}, [=](auto self) {{ return {position}; }}, {close_policy}, {{ {parent_component} }})"
"{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} }})"
)
} else {
panic!("internal error: invalid args to ShowPopupWindow {:?}", arguments)
}
}
BuiltinFunction::ClosePopupWindow => {
if let [llr::Expression::NumberLiteral(popup_index), llr::Expression::PropertyReference(parent_ref)] = arguments {
let mut parent_ctx = ctx;
let mut component_access = "self".into();
if let llr::PropertyReference::InParent { level, .. } = parent_ref {
for _ in 0..level.get() {
component_access = format!("{}->parent", component_access);
parent_ctx = parent_ctx.parent.as_ref().unwrap().ctx;
}
};
let window = access_window_field(ctx);
format!("{window}.close_popup()")
format!("{window}.close_popup({component_access}->popup_id_{popup_index})")
} else {
panic!("internal error: invalid args to ClosePopupWindow {:?}", arguments)
}
}
BuiltinFunction::SetSelectionOffsets => {
if let [llr::Expression::PropertyReference(pr), from, to] = arguments {

View file

@ -1001,6 +1001,9 @@ fn generate_sub_component(
sub_component_types.push(sub_component_id);
}
let popup_id_names =
component.popup_windows.iter().enumerate().map(|(i, _)| internal_popup_id(i));
for (prop1, prop2) in &component.two_way_bindings {
let p1 = access_member(prop1, &ctx);
let p2 = access_member(prop2, &ctx);
@ -1116,6 +1119,7 @@ fn generate_sub_component(
struct #inner_component_id {
#(#item_names : sp::#item_types,)*
#(#sub_component_names : #sub_component_types,)*
#(#popup_id_names : ::core::cell::Cell<sp::Option<::core::num::NonZeroU32>>,)*
#(#declared_property_vars : sp::Property<#declared_property_types>,)*
#(#declared_callbacks : sp::Callback<(#(#declared_callbacks_types,)*), #declared_callbacks_ret>,)*
#(#repeated_element_names : sp::Repeater<#repeated_element_components>,)*
@ -1819,6 +1823,12 @@ fn inner_component_id(component: &llr::SubComponent) -> proc_macro2::Ident {
format_ident!("Inner{}", ident(&component.name))
}
fn internal_popup_id(index: usize) -> proc_macro2::Ident {
let mut name = index.to_string();
name.insert_str(0, "popup_id_");
ident(&name)
}
fn global_inner_name(g: &llr::GlobalComponent) -> TokenStream {
if g.is_builtin {
let i = ident(&g.name);
@ -2660,27 +2670,51 @@ fn compile_builtin_function_call(
let close_policy = compile_expression(close_policy, ctx);
let window_adapter_tokens = access_window_adapter_field(ctx);
let popup_id_name = internal_popup_id(*popup_index as usize);
quote!({
let popup_instance = #popup_window_id::new(#component_access_tokens.self_weak.get().unwrap().clone()).unwrap();
let popup_instance_vrc = sp::VRc::map(popup_instance.clone(), |x| x);
#popup_window_id::user_init(popup_instance_vrc.clone());
let position = { let _self = popup_instance_vrc.as_pin_ref(); #position };
if let Some(current_id) = #component_access_tokens.#popup_id_name.take() {
sp::WindowInner::from_pub(#window_adapter_tokens.window()).close_popup(current_id);
}
#component_access_tokens.#popup_id_name.set(Some(
sp::WindowInner::from_pub(#window_adapter_tokens.window()).show_popup(
&sp::VRc::into_dyn(popup_instance.into()),
position,
#close_policy,
#parent_component
)
))
);
})
} else {
panic!("internal error: invalid args to ShowPopupWindow {:?}", arguments)
}
}
BuiltinFunction::ClosePopupWindow => {
if let [Expression::NumberLiteral(popup_index), Expression::PropertyReference(parent_ref)] =
arguments
{
let mut parent_ctx = ctx;
let mut component_access_tokens = quote!(_self);
if let llr::PropertyReference::InParent { level, .. } = parent_ref {
for _ in 0..level.get() {
component_access_tokens =
quote!(#component_access_tokens.parent.upgrade().unwrap().as_pin_ref());
parent_ctx = parent_ctx.parent.as_ref().unwrap().ctx;
}
}
let window_adapter_tokens = access_window_adapter_field(ctx);
let popup_id_name = internal_popup_id(*popup_index as usize);
quote!(
sp::WindowInner::from_pub(#window_adapter_tokens.window()).close_popup()
if let Some(current_id) = #component_access_tokens.#popup_id_name.take() {
sp::WindowInner::from_pub(#window_adapter_tokens.window()).close_popup(current_id);
}
)
} else {
panic!("internal error: invalid args to ClosePopupWindow {:?}", arguments)
}
}
BuiltinFunction::SetSelectionOffsets => {
if let [llr::Expression::PropertyReference(pr), from, to] = arguments {

View file

@ -122,11 +122,7 @@ pub fn lower_expression(
lower_show_popup(arguments, ctx)
}
tree_Expression::BuiltinFunctionReference(BuiltinFunction::ClosePopupWindow, _) => {
// FIXME: right now, `popup.close()` will close any visible popup, as the popup argument is ignored
llr_Expression::BuiltinFunctionCall {
function: BuiltinFunction::ClosePopupWindow,
arguments: vec![],
}
lower_close_popup(arguments, ctx)
}
tree_Expression::BuiltinFunctionReference(f, _) => {
let mut arguments =
@ -399,6 +395,38 @@ fn lower_show_popup(args: &[tree_Expression], ctx: &ExpressionContext) -> llr_Ex
}
}
fn lower_close_popup(args: &[tree_Expression], ctx: &ExpressionContext) -> llr_Expression {
if let [tree_Expression::ElementReference(e)] = args {
let popup_window = e.upgrade().unwrap();
let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
let parent_component = pop_comp
.parent_element
.upgrade()
.unwrap()
.borrow()
.enclosing_component
.upgrade()
.unwrap();
let popup_list = parent_component.popup_windows.borrow();
let (popup_index, popup) = popup_list
.iter()
.enumerate()
.find(|(_, p)| Rc::ptr_eq(&p.component, &pop_comp))
.unwrap();
let item_ref = lower_expression(
&tree_Expression::ElementReference(Rc::downgrade(&popup.parent_element)),
ctx,
);
llr_Expression::BuiltinFunctionCall {
function: BuiltinFunction::ClosePopupWindow,
arguments: vec![llr_Expression::NumberLiteral(popup_index as _), item_ref],
}
} else {
panic!("invalid arguments to ShowPopupWindow");
}
}
pub fn lower_animation(a: &PropertyAnimation, ctx: &ExpressionContext<'_>) -> Animation {
fn lower_animation_element(a: &ElementRc, ctx: &ExpressionContext<'_>) -> llr_Expression {
llr_Expression::Struct {

View file

@ -28,6 +28,7 @@ use alloc::rc::{Rc, Weak};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::cell::{Cell, RefCell};
use core::num::NonZeroU32;
use core::pin::Pin;
use euclid::num::Zero;
use vtable::VRcMapped;
@ -384,6 +385,8 @@ enum PopupWindowLocation {
/// This structure defines a graphical element that is designed to pop up from the surrounding
/// UI content, for example to show a context menu.
struct PopupWindow {
/// The ID of the associated popup.
popup_id: NonZeroU32,
/// The location defines where the pop up is rendered.
location: PopupWindowLocation,
/// The component that is responsible for providing the popup content.
@ -434,6 +437,7 @@ pub struct WindowInner {
/// Stack of currently active popups
active_popups: RefCell<Vec<PopupWindow>>,
next_popup_id: Cell<NonZeroU32>,
had_popup_on_press: Cell<bool>,
close_requested: Callback<(), CloseRequestResponse>,
click_state: ClickState,
@ -495,6 +499,7 @@ impl WindowInner {
last_ime_text: Default::default(),
cursor_blinker: Default::default(),
active_popups: Default::default(),
next_popup_id: Cell::new(NonZeroU32::MIN),
had_popup_on_press: Default::default(),
close_requested: Default::default(),
click_state: ClickState::default(),
@ -510,7 +515,7 @@ impl WindowInner {
/// Associates this window with the specified component. Further event handling and rendering, etc. will be
/// done with that component.
pub fn set_component(&self, component: &ItemTreeRc) {
self.close_popup();
self.close_all_popups();
self.focus_item.replace(Default::default());
self.mouse_input_state.replace(Default::default());
self.modifiers.replace(Default::default());
@ -580,7 +585,7 @@ impl WindowInner {
self.had_popup_on_press.set(!self.active_popups.borrow().is_empty());
}
let close_policy = self.close_policy();
let close_policy = self.top_close_policy();
let mut mouse_inside_popup = false;
mouse_input_state = if let Some(mut event) =
@ -644,12 +649,12 @@ impl WindowInner {
if (mouse_inside_popup && released_event && self.had_popup_on_press.get())
|| (!mouse_inside_popup && pressed_event)
{
self.close_popup();
self.close_top_popup();
}
}
PopupClosePolicy::CloseOnClickOutside => {
if !mouse_inside_popup && pressed_event {
self.close_popup();
self.close_top_popup();
}
}
PopupClosePolicy::NoAutoClose => {}
@ -971,14 +976,15 @@ impl WindowInner {
.map_or(ColorScheme::Unknown, |x| x.color_scheme())
}
/// Show a popup at the given position relative to the item
/// Show a popup at the given position relative to the item and returns its ID.
/// The returned ID will always be non-zero.
pub fn show_popup(
&self,
popup_componentrc: &ItemTreeRc,
position: Point,
close_policy: PopupClosePolicy,
parent_item: &ItemRc,
) {
) -> NonZeroU32 {
let position = parent_item.map_to_window(
parent_item.geometry().origin + LogicalPoint::from_untyped(position).to_vector(),
);
@ -1046,6 +1052,9 @@ impl WindowInner {
height_property.set(size.height_length());
};
let popup_id = self.next_popup_id.get();
self.next_popup_id.set(self.next_popup_id.get().checked_add(1).unwrap());
let location = match parent_window_adapter
.internal(crate::InternalToken)
.and_then(|x| x.create_popup(LogicalRect::new(position, size)))
@ -1069,17 +1078,18 @@ impl WindowInner {
};
self.active_popups.borrow_mut().push(PopupWindow {
popup_id,
location,
component: popup_componentrc.clone(),
close_policy,
});
popup_id
}
/// Removes any active popup.
/// TODO: this function should take a component ref as parameter, to close a specific popup - i.e. when popup menus create a hierarchy of popups.
pub fn close_popup(&self) {
if let Some(current_popup) = self.active_popups.borrow_mut().pop() {
match current_popup.location {
// Close the popup associated with the given popup window.
fn close_popup_impl(&self, current_popup: &PopupWindow) {
match &current_popup.location {
PopupWindowLocation::ChildWindow(offset) => {
// Refresh the area that was previously covered by the popup.
let popup_region = crate::properties::evaluate_no_tracking(|| {
@ -1099,10 +1109,34 @@ impl WindowInner {
}
}
}
/// Removes the popup matching the given ID.
pub fn close_popup(&self, popup_id: NonZeroU32) {
let mut active_popups = self.active_popups.borrow_mut();
let maybe_index = active_popups.iter().position(|popup| popup.popup_id == popup_id);
if let Some(popup_index) = maybe_index {
self.close_popup_impl(&active_popups[popup_index]);
active_popups.remove(popup_index);
}
}
/// Returns the close policy of the active popup. PopupClosePolicy::NoAutoClose if there is no active popup.
pub fn close_policy(&self) -> PopupClosePolicy {
/// Close all active popups.
pub fn close_all_popups(&self) {
for popup in self.active_popups.take() {
self.close_popup_impl(&popup);
}
}
/// Close the top-most popup.
pub fn close_top_popup(&self) {
if let Some(popup) = self.active_popups.borrow_mut().pop() {
self.close_popup_impl(&popup);
}
}
/// Returns the close policy of the top-most popup. PopupClosePolicy::NoAutoClose if there is no active popup.
pub fn top_close_policy(&self) -> PopupClosePolicy {
self.active_popups
.borrow()
.last()
@ -1361,7 +1395,7 @@ pub mod ffi {
WindowInner::from_pub(window_adapter.window()).set_component(component)
}
/// Show a popup.
/// Show a popup and return its ID. The returned ID will always be non-zero.
#[no_mangle]
pub unsafe extern "C" fn slint_windowrc_show_popup(
handle: *const WindowAdapterRcOpaque,
@ -1369,20 +1403,24 @@ pub mod ffi {
position: crate::graphics::Point,
close_policy: PopupClosePolicy,
parent_item: &ItemRc,
) {
) -> NonZeroU32 {
let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
WindowInner::from_pub(window_adapter.window()).show_popup(
return WindowInner::from_pub(window_adapter.window()).show_popup(
popup,
position,
close_policy,
parent_item,
);
}
/// Close the current popup
/// Close the popup by the given ID.
#[no_mangle]
pub unsafe extern "C" fn slint_windowrc_close_popup(handle: *const WindowAdapterRcOpaque) {
pub unsafe extern "C" fn slint_windowrc_close_popup(
handle: *const WindowAdapterRcOpaque,
popup_id: NonZeroU32,
) {
let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
WindowInner::from_pub(window_adapter.window()).close_popup();
WindowInner::from_pub(window_adapter.window()).close_popup(popup_id);
}
/// C binding to the set_rendering_notifier() API of Window

View file

@ -42,6 +42,7 @@ use once_cell::unsync::{Lazy, OnceCell};
use smol_str::{SmolStr, ToSmolStr};
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::num::NonZeroU32;
use std::{pin::Pin, rc::Rc};
pub const SPECIAL_PROPERTY_INDEX: &str = "$index";
@ -411,6 +412,8 @@ pub struct ItemTreeDescription<'id> {
Vec<(NamedReference, Expression)>,
)>,
timers: Vec<FieldOffset<Instance<'id>, Timer>>,
/// Map of element IDs to their active popup's ID
popup_ids: std::cell::RefCell<HashMap<SmolStr, NonZeroU32>>,
/// The collection of compiled globals
compiled_globals: Option<Rc<CompiledGlobalCollection>>,
@ -1341,6 +1344,7 @@ pub(crate) fn generate_item_tree<'id>(
compiled_globals,
change_trackers,
timers,
popup_ids: std::cell::RefCell::new(HashMap::new()),
#[cfg(feature = "highlight")]
type_loader: std::cell::OnceCell::new(),
#[cfg(feature = "highlight")]
@ -2330,6 +2334,8 @@ impl<'a, 'id> InstanceRef<'a, 'id> {
/// Show the popup at the given location
pub fn show_popup(
element: ElementRc,
instance: InstanceRef,
popup: &object_tree::PopupWindow,
pos_getter: impl FnOnce(InstanceRef<'_, '_>) -> i_slint_core::graphics::Point,
close_policy: PopupClosePolicy,
@ -2355,14 +2361,30 @@ pub fn show_popup(
let instance_ref = compo_box.borrow_instance();
pos_getter(instance_ref)
};
close_popup(element.clone(), instance, parent_window_adapter.clone());
instance.description.popup_ids.borrow_mut().insert(
element.borrow().id.clone(),
WindowInner::from_pub(parent_window_adapter.window()).show_popup(
&vtable::VRc::into_dyn(inst),
pos,
close_policy,
parent_item,
),
);
}
pub fn close_popup(
element: ElementRc,
instance: InstanceRef,
parent_window_adapter: WindowAdapterRc,
) {
if let Some(current_id) =
instance.description.popup_ids.borrow_mut().remove(&element.borrow().id)
{
WindowInner::from_pub(parent_window_adapter.window()).close_popup(current_id);
}
}
pub fn update_timers(instance: InstanceRef) {
let ts = instance.description.original.timers.borrow();
for (desc, offset) in ts.iter().zip(&instance.description.timers) {

View file

@ -614,6 +614,8 @@ fn call_builtin_function(
.expect("Invalid internal enumeration representation for close policy");
crate::dynamic_item_tree::show_popup(
popup_window,
component,
popup,
|instance_ref| {
let comp = ComponentInstance::InstanceRef(instance_ref);
@ -633,7 +635,7 @@ fn call_builtin_function(
);
Value::Void
} else {
panic!("internal error: argument to SetFocusItem must be an element")
panic!("internal error: argument to ShowPopupWindow must be an element")
}
}
BuiltinFunction::ClosePopupWindow => {
@ -644,9 +646,18 @@ fn call_builtin_function(
}
};
component.access_window(|window| window.close_popup());
if let Expression::ElementReference(popup_window) = &arguments[0] {
let popup_window = popup_window.upgrade().unwrap();
crate::dynamic_item_tree::close_popup(
popup_window,
component,
component.window_adapter(),
);
Value::Void
} else {
panic!("internal error: argument to ClosePopupWindow must be an element")
}
}
BuiltinFunction::SetSelectionOffsets => {
if arguments.len() != 3 {

View file

@ -263,6 +263,33 @@ instance.set_result("".into());
slint_testing::send_mouse_click(&instance, 20., 310.);
assert_eq!(instance.get_result(), "Root");
instance.set_result("".into());
// open both popups
slint_testing::send_mouse_click(&instance, 380., 10.);
// close popup1
slint_testing::send_mouse_click(&instance, 150., 210. + 40.);
assert_eq!(instance.get_result(), "C1");
instance.set_result("".into());
// popup2 is still open and can be closed
slint_testing::send_mouse_click(&instance, 40., 210. + 40.);
assert_eq!(instance.get_result(), "C2");
instance.set_result("".into());
// open both popups
slint_testing::send_mouse_click(&instance, 380., 10.);
// close popup2
slint_testing::send_mouse_click(&instance, 40., 210. + 40.);
assert_eq!(instance.get_result(), "C2");
instance.set_result("".into());
// popup1 is still open
slint_testing::send_mouse_click(&instance, 210., 90.);
assert_eq!(instance.get_result(), "P1");
instance.set_result("".into());
```
```cpp
@ -343,6 +370,33 @@ instance.set_result("");
slint_testing::send_mouse_click(&instance, 20., 310.);
assert_eq(instance.get_result(), "Root");
instance.set_result("");
// open both popups
slint_testing::send_mouse_click(&instance, 380., 10.);
// close popup1
slint_testing::send_mouse_click(&instance, 150., 210. + 40.);
assert_eq(instance.get_result(), "C1");
instance.set_result("");
// popup2 is still open and can be closed
slint_testing::send_mouse_click(&instance, 40., 210. + 40.);
assert_eq(instance.get_result(), "C2");
instance.set_result("");
// open both popups
slint_testing::send_mouse_click(&instance, 380., 10.);
// close popup2
slint_testing::send_mouse_click(&instance, 40., 210. + 40.);
assert_eq(instance.get_result(), "C2");
instance.set_result("");
// popup1 is still open
slint_testing::send_mouse_click(&instance, 210., 90.);
assert_eq(instance.get_result(), "P1");
instance.set_result("");
```
*/

View file

@ -0,0 +1,169 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
import { Button } from "std-widgets.slint";
component PopupContainer inherits Rectangle {
width: 400px;
height: 300px;
in-out property <int> popup_click_count;
callback first_reached;
callback second_reached;
property <length> popup_x;
property <bool> first_sent: false;
property <bool> second_sent: false;
public function show(second: bool) {
popup_x = !second ? 0px : 200px;
popup.show();
}
public function close() {
popup.close();
}
popup := PopupWindow {
x: popup_x;
y: 100px;
width: 200px;
height: 200px;
close-policy: no-auto-close;
Rectangle {
background: red;
}
TouchArea {
clicked => {
popup_click_count += 1;
if (popup_x == 0px) {
if (!first_sent) {
first_reached();
first_sent = true;
}
} else {
if (!second_sent ) {
second_reached();
second_sent = true;
}
}
}
}
}
}
export component TestCase inherits Window {
width: 400px;
height: 400px;
in-out property <int> popup_click_count;
VerticalLayout {
alignment: start;
HorizontalLayout {
Button {
text: "Open";
clicked => {
cnt1.show(false);
}
}
}
}
cnt1 := PopupContainer {
x: 0px;
y: 100px;
popup_click_count <=> popup_click_count;
first_reached => {
cnt1.show(true);
}
second_reached => {
cnt2.show(false);
}
}
cnt2 := PopupContainer {
x: 0px;
y: 100px;
popup_click_count <=> popup_click_count;
first_reached => {
cnt2.close();
}
}
}
/*
```rust
#[allow(unused)]
use slint::{platform::WindowEvent, platform::PointerEventButton, LogicalPosition};
let instance = TestCase::new().unwrap();
// open on the left
slint_testing::send_mouse_click(&instance, 20., 10.);
// use left popup to open on the right
slint_testing::send_mouse_click(&instance, 10., 210.);
assert_eq!(instance.get_popup_click_count(), 1);
// no longer open on the left
slint_testing::send_mouse_click(&instance, 10., 210.);
assert_eq!(instance.get_popup_click_count(), 1);
// popup is now open on the right
// open a second popup on the left in the second container
slint_testing::send_mouse_click(&instance, 210., 210.);
assert_eq!(instance.get_popup_click_count(), 2);
// second popup is still open on the left
// close the second popup
slint_testing::send_mouse_click(&instance, 10., 210.);
assert_eq!(instance.get_popup_click_count(), 3);
// first popup is still open as multiple of the same "PopupWindow" across different component instances is fine
slint_testing::send_mouse_click(&instance, 210., 210.);
assert_eq!(instance.get_popup_click_count(), 4);
```
```cpp
auto handle = TestCase::create();
TestCase &instance = *handle;
// open on the left
slint_testing::send_mouse_click(&instance, 20., 10.);
// use left popup to open on the right
slint_testing::send_mouse_click(&instance, 10., 210.);
assert_eq(instance.get_popup_click_count(), 1);
// no longer open on the left
slint_testing::send_mouse_click(&instance, 10., 210.);
assert_eq(instance.get_popup_click_count(), 1);
// popup is now open on the right
// open a second popup on the left in the second container
slint_testing::send_mouse_click(&instance, 210., 210.);
assert_eq(instance.get_popup_click_count(), 2);
// open on the left from the second container
slint_testing::send_mouse_click(&instance, 380., 10.);
// second popup is still open on the left
// close the second popup
slint_testing::send_mouse_click(&instance, 10., 210.);
assert_eq(instance.get_popup_click_count(), 3);
// first popup is still open as multiple of the same "PopupWindow" across different component instances is fine
slint_testing::send_mouse_click(&instance, 210., 210.);
assert_eq(instance.get_popup_click_count(), 4);
```
*/

View file

@ -1358,8 +1358,8 @@ fn set_preview_factory(
callback: Box<dyn Fn(ComponentInstance)>,
behavior: LoadBehavior,
) {
// Ensure that the popup is closed as it is related to the old factory
i_slint_core::window::WindowInner::from_pub(ui.window()).close_popup();
// Ensure that any popups are closed as they are related to the old factory
i_slint_core::window::WindowInner::from_pub(ui.window()).close_all_popups();
let factory = slint::ComponentFactory::new(move |ctx: FactoryContext| {
let instance = compiled.create_embedded(ctx).unwrap();