slint-ui/slint · error
ShowPopupWindow is invoked on a sub-component
Error message
ShowPopupWindow is invoked on a sub-component
What it means
When generating C++ code for `show-popup-window` (and its `is-open` handling), the generator needs the current sub-component's class name to build a weak mapped handle for the popup's `is-open` setter. If `ctx.current_sub_component()` is None the generator panics: popup invocation is expected to be compiled within a sub-component context.
Solutions
- Ensure the ShowPopupWindow expression is compiled with an EvaluationContext created via `new_sub_component` for the containing sub-component
- If popups can now live at other levels, capture the enclosing component handle explicitly instead of expecting a sub-component
- Report with a reproducing .slint file if hit as a user
Example fix
// before
let self_ty = ident(&ctx.current_sub_component().expect("...").name);
// after
let self_ty = ident(&ctx.current_sub_component().unwrap_or_else(||
panic!("ShowPopupWindow compiled outside sub-component context")).name); Defensive patterns
Strategy: validation
Validate before calling
debug_assert!(ctx.current_sub_component().is_some(), "ShowPopupWindow requires a sub-component context");
Type guard
let self_component = ctx.current_sub_component().ok_or_else(||
panic!("ShowPopupWindow compiled outside sub-component context"))?; Prevention
- Compile popup expressions only within new_sub_component contexts
- Assert the context kind at the top of popup-expression compilation
- Test show-popup-window lowering from every generator entry point
- Keep popup handling symmetric between C++ and Rust generators
When it happens
Trigger: A `ShowPopupWindow` expression is compiled while the EvaluationContext has no current sub-component — e.g. the popup call got moved into a global or root-level context by an earlier pass, or a new code path compiles popup expressions without `new_sub_component`.
Common situations: Slint compiler development: refactoring how popup expressions or `is-open` bindings are lowered; adding a new generator entry point that forgets to establish the sub-component frame.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- there should be a popup menu if we want to show it
- Expression::ObjectAccess: Cannot find a key in an object
- model two-way bindings only exist on sub-components
- there should be a popup menu if we want to show it
- Expression::StructFieldAccess: Cannot find a key in an…
AI-assisted analysis of slint-ui/slint@3a7e700487 (2026-09-16).
Data as JSON: /api/errors/d78aabd350674cd1.
Report an issue: GitHub.
Appendix: source
Thrown at internal/compiler/generator/cpp.rs:5381
let popup_ctx = EvaluationContext::new_sub_component(
ctx.compilation_unit,
popup.item_tree.root,
CppGeneratorContext { global_access: "self->globals".into(), conditional_includes: ctx.generator_state.conditional_includes },
Some(&parent_ctx),
);
let position = compile_expression(&popup.position.borrow(), &popup_ctx);
let close_policy = compile_expression(close_policy, ctx);
let window_kind = if popup.is_tooltip { "slint::cbindgen_private::WindowKind::ToolTip" } else { "slint::cbindgen_private::WindowKind::Popup" };
// Keep the parent's `is-open` property in sync. The setter is passed directly into
// `show_popup`, so there is no extra registration call and no second popup lookup. The
// `false` is delivered when the popup is dropped, which may be long after this frame, so
// we cannot capture a raw `self`. We capture a weak *mapped* handle to the current
// component instance -- the C++ equivalent of Rust's `self_weak`, which for a
// sub-component points at that sub-component itself rather than at the enclosing item
// tree (`self->self_weak`). Popups without `is-open` get a no-op setter.
let is_open_setter = match is_open_args.first() {
Some(llr::Expression::PropertyReference(is_open_ref)) => {
let self_ty = ident(&ctx.current_sub_component().expect("ShowPopupWindow is invoked on a sub-component").name);
let set_is_open = access_member(is_open_ref, ctx).then(|p| format!("{p}.set(is_open)"));
format!(
"[weak = vtable::VWeakMapped<slint::private_api::ItemTreeVTable, const {self_ty}>( \
vtable::VRcMapped<slint::private_api::ItemTreeVTable, const {self_ty}>(self->self_weak.lock().value(), self))] \
(bool is_open) {{ \
auto rc = weak.lock(); \
if (!rc) return; \
[[maybe_unused]] auto self = &**rc; \
{set_is_open}; \
}}"
)
}
_ => "[](bool) {}".to_string(),
};
item_owner(anchor_ref).then_named("anchor_owner", |owner| {
let (_, parent_component) = native_item_from_owner(anchor_ref, ctx, owner);
component_access.then(|component_access| {
let compo_ptr = if compo_path.is_empty() {View on GitHub (pinned to 3a7e700487)