Skip to content

Commit

Permalink
Add support for multiple popups
Browse files Browse the repository at this point in the history
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
  • Loading branch information
BrandonXLF committed Oct 31, 2024
1 parent 5bd6c4b commit 6e0beea
Show file tree
Hide file tree
Showing 9 changed files with 248 additions and 105 deletions.
2 changes: 1 addition & 1 deletion api/cpp/include/slint_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class WindowAdapterRc
&parent_item);
}

void close_popup() const { cbindgen_private::slint_windowrc_close_popup(&inner); }
void close_popup(uint32_t popup_id) const { cbindgen_private::slint_windowrc_close_popup(&inner, popup_id); }

template<std::invocable<RenderingState, GraphicsAPI> F>
std::optional<SetRenderingNotifierError> set_rendering_notifier(F callback) const
Expand Down
23 changes: 14 additions & 9 deletions internal/backends/qt/qt_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ cpp! {{
void *parent_of_popup_to_close = nullptr;
if (auto p = dynamic_cast<const SlintWidget*>(parent())) {
void *parent_window = p->rust_window;
bool close_on_click = rust!(Slint_mouseReleaseEventPopup [parent_window: &QtWindow as "void*"] -> bool as "bool" {
let close_policy = parent_window.close_policy();
bool close_on_click = rust!(Slint_mouseReleaseEventPopup [rust_window: &QtWindow as "void*", parent_window: &QtWindow as "void*"] -> bool as "bool" {
let close_policy = parent_window.close_policy(*rust_window.popup_id.borrow());

close_policy == PopupClosePolicy::CloseOnClick || close_policy == PopupClosePolicy::CloseOnClickOutside
});
Expand All @@ -178,8 +178,8 @@ cpp! {{
rust_window.mouse_event(MouseEvent::Released{ position, button, click_count: 0 })
});
if (parent_of_popup_to_close) {
rust!(Slint_mouseReleaseEventClosePopup [parent_of_popup_to_close: &QtWindow as "void*"] {
parent_of_popup_to_close.close_popup();
rust!(Slint_mouseReleaseEventClosePopup [rust_window: &QtWindow as "void*", parent_of_popup_to_close: &QtWindow as "void*"] {
parent_of_popup_to_close.close_popup(*rust_window.popup_id.borrow());
});
}
}
Expand Down Expand Up @@ -1624,6 +1624,9 @@ pub struct QtWindow {
tree_structure_changed: RefCell<bool>,

color_scheme: OnceCell<Pin<Box<Property<ColorScheme>>>>,

// ID of the popup this window represents, if any
popup_id: RefCell<u32>
}

impl Drop for QtWindow {
Expand Down Expand Up @@ -1658,6 +1661,7 @@ impl QtWindow {
cache: Default::default(),
tree_structure_changed: RefCell::new(false),
color_scheme: Default::default(),
popup_id: Default::default()
}
});
let widget_ptr = rc.widget_ptr();
Expand Down Expand Up @@ -1743,12 +1747,12 @@ impl QtWindow {
timer_event();
}

fn close_popup(&self) {
WindowInner::from_pub(&self.window).close_popup();
fn close_popup(&self, popup: u32) {
WindowInner::from_pub(&self.window).close_popup(popup);
}

fn close_policy(&self) -> PopupClosePolicy {
WindowInner::from_pub(&self.window).close_policy()
fn close_policy(&self, popup: u32) -> PopupClosePolicy {
WindowInner::from_pub(&self.window).close_policy(popup)
}

fn window_state_event(&self) {
Expand Down Expand Up @@ -2021,8 +2025,9 @@ impl WindowAdapterInternal for QtWindow {
self.tree_structure_changed.replace(true);
}

fn create_popup(&self, geometry: LogicalRect) -> Option<Rc<dyn WindowAdapter>> {
fn create_popup(&self, popup_id: u32, geometry: LogicalRect) -> Option<Rc<dyn WindowAdapter>> {
let popup_window = QtWindow::new();
popup_window.popup_id.replace(popup_id);

let size = qttypes::QSize { width: geometry.width() as _, height: geometry.height() as _ };

Expand Down
30 changes: 27 additions & 3 deletions internal/compiler/generator/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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("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});",
Expand Down Expand Up @@ -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}, self->popup_id_{popup_index}, [=](auto self) {{ return {position}; }}, {close_policy}, {{ {parent_component} }})"
)
} else {
panic!("internal error: invalid args to ShowPopupWindow {:?}", arguments)
}
}
BuiltinFunction::ClosePopupWindow => {
let window = access_window_field(ctx);
format!("{window}.close_popup()")
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({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 {
Expand Down
55 changes: 45 additions & 10 deletions internal/compiler/generator/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,10 @@ 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);
Expand Down Expand Up @@ -1116,6 +1120,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<u32>,)*
#(#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>,)*
Expand Down Expand Up @@ -1819,6 +1824,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);
Expand Down Expand Up @@ -2660,27 +2671,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 };
sp::WindowInner::from_pub(#window_adapter_tokens.window()).show_popup(
&sp::VRc::into_dyn(popup_instance.into()),
position,
#close_policy,
#parent_component
)
let current_id = #component_access_tokens.#popup_id_name.take();
if current_id > 0 {
sp::WindowInner::from_pub(#window_adapter_tokens.window()).close_popup(current_id);
}
#component_access_tokens.#popup_id_name.replace(
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 => {
let window_adapter_tokens = access_window_adapter_field(ctx);
quote!(
sp::WindowInner::from_pub(#window_adapter_tokens.window()).close_popup()
)
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!(
let current_id = #component_access_tokens.#popup_id_name.take();
if current_id > 0 {
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 {
Expand Down
41 changes: 36 additions & 5 deletions internal/compiler/llr/lower_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -399,6 +395,41 @@ 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 {
Expand Down
Loading

0 comments on commit 6e0beea

Please sign in to comment.