Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/cpp/cbindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,7 @@ fn gen_corelib(
"slint_windowrc_set_component",
"slint_windowrc_show_popup",
"slint_windowrc_close_popup",
"slint_windowrc_create_popup_window_adapter",
"slint_windowrc_set_rendering_notifier",
"slint_windowrc_request_redraw",
"slint_windowrc_on_close_requested",
Expand Down
25 changes: 24 additions & 1 deletion api/cpp/include/private/slint_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,16 @@ class WindowAdapterRc
cbindgen_private::PopupClosePolicy close_policy,
cbindgen_private::ItemRc parent_item) const
{
auto popup = Component::create(parent_component);
using SharedGlobals = decltype(parent_component->globals);
SharedGlobals _own_globals = nullptr;
if (auto _popup_adapter = create_popup_window_adapter()) {
_own_globals = parent_component->globals->clone_with_window_adapter(*_popup_adapter);
}
if (!_own_globals) {
_own_globals = parent_component->globals;
}

auto popup = Component::create(parent_component, _own_globals);
auto p = pos(popup);
auto popup_dyn = popup.into_dyn();
auto id = cbindgen_private::slint_windowrc_show_popup(&inner, &popup_dyn, p, close_policy,
Expand All @@ -181,6 +190,20 @@ class WindowAdapterRc
}
}

/// Try to create a window adapter for a popup window.
/// Returns std::nullopt if the backend renders popups as child windows.
std::optional<WindowAdapterRc> create_popup_window_adapter() const
{
cbindgen_private::WindowAdapterRcOpaque raw_result;
if (cbindgen_private::slint_windowrc_create_popup_window_adapter(&inner, &raw_result)) {
std::optional<WindowAdapterRc> result;
result.emplace(raw_result); // clone: refcount = 2
cbindgen_private::slint_windowrc_drop(&raw_result); // drop original: refcount = 1
return result;
}
return std::nullopt;
}

template<typename Component, typename SharedGlobals, typename InitFn>
uint32_t show_popup_menu(
SharedGlobals *globals, LogicalPosition pos, cbindgen_private::ItemRc context_menu_rc,
Expand Down
10 changes: 2 additions & 8 deletions internal/backends/qt/qt_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2227,18 +2227,12 @@ impl WindowAdapterInternal for QtWindow {
self.tree_structure_changed.replace(true);
}

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

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

let popup_ptr = popup_window.widget_ptr();
let pos = qttypes::QPoint { x: geometry.origin.x as _, y: geometry.origin.y as _ };
let widget_ptr = self.widget_ptr();
cpp! {unsafe [widget_ptr as "QWidget*", popup_ptr as "QWidget*", pos as "QPoint", size as "QSize"] {
cpp! {unsafe [widget_ptr as "QWidget*", popup_ptr as "QWidget*"] {
popup_ptr->setParent(widget_ptr, Qt::Popup);
popup_ptr->setGeometry(QRect(pos + widget_ptr->mapToGlobal(QPoint(0,0)), size));
popup_ptr->show();
}};
Some(popup_window as _)
}
Expand Down
58 changes: 52 additions & 6 deletions internal/compiler/generator/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,7 @@ pub fn generate(
));

let mut init_global = Vec::new();
let mut clone_constructor_global_inits = Vec::new();

for (idx, glob) in llr.globals.iter_enumerated() {
if !glob.must_generate() {
Expand All @@ -837,6 +838,8 @@ pub fn generate(
Declaration::TypeAlias(TypeAlias { old_name: ident(&glob.name), new_name: ident(name) })
}));

clone_constructor_global_inits.push(format!("{name}(source.{name})"));

globals_struct.members.push((
Access::Public,
Declaration::Var(Var {
Expand All @@ -859,6 +862,42 @@ pub fn generate(
}),
));

// Build initializer-list string for the clone_with_window_adapter constructor
{
let global_inits = std::iter::once("root_weak(source.root_weak)".to_string())
.chain(clone_constructor_global_inits)
.collect::<Vec<_>>()
.join(", ");
let init_list =
if global_inits.is_empty() { String::new() } else { format!(" : {global_inits}") };

// A private constructor for cloning with a different window adapter
globals_struct.members.push((
Access::Private,
Declaration::Function(Function {
name: globals_struct.name.clone(),
is_constructor_or_destructor: true,
signature: format!(
"(const {SHARED_GLOBAL_CLASS}& source, const slint::private_api::WindowAdapterRc& adapter){init_list}"
),
statements: Some(vec!["m_window.emplace(adapter);".into()]),
..Default::default()
}),
));

globals_struct.members.push((
Access::Public,
Declaration::Function(Function {
name: "clone_with_window_adapter".into(),
signature: format!("(const slint::private_api::WindowAdapterRc& adapter) const -> {SHARED_GLOBAL_CLASS}*"),
statements: Some(vec![format!(
"return new {SHARED_GLOBAL_CLASS}(*this, adapter);"
)]),
..Default::default()
}),
));
}

file.declarations.push(Declaration::Struct(globals_struct));

if let Some(popup_menu) = &llr.popup_menu {
Expand Down Expand Up @@ -1406,7 +1445,7 @@ fn generate_item_tree(
sub_tree: &llr::ItemTree,
root: &llr::CompilationUnit,
parent_ctx: Option<&ParentScope>,
is_popup_menu: bool,
is_popup: bool,
item_tree_class_name: SmolStr,
field_access: Access,
file: &mut File,
Expand Down Expand Up @@ -1833,7 +1872,7 @@ fn generate_item_tree(
"self->self_weak = vtable::VWeak(self_rc).into_dyn();".into(),
];

if is_popup_menu {
if is_popup {
create_code.push("self->globals = globals;".into());
create_parameters.push("const SharedGlobals *globals".into());
} else if parent_ctx.is_none() {
Expand All @@ -1857,7 +1896,8 @@ fn generate_item_tree(
create_code.push("self->m_globals.root_weak = self->self_weak;".into());
}

let global_access = if parent_ctx.is_some() { "parent->globals" } else { "self->globals" };
let global_access =
if !is_popup && parent_ctx.is_some() { "parent->globals" } else { "self->globals" };
create_code.extend([
format!(
"slint::private_api::register_item_tree(&self_rc.into_dyn(), {global_access}->m_window);",
Expand All @@ -1867,7 +1907,7 @@ fn generate_item_tree(

// Repeaters run their user_init() code from Repeater::ensure_updated() after update() initialized model_data/index.
// And in PopupWindow this is also called by the runtime
if parent_ctx.is_none() && !is_popup_menu {
if parent_ctx.is_none() && !is_popup {
create_code.push("self->user_init();".to_string());
// initialize the Window in this point to be consistent with Rust
create_code.push("self->window();".to_string())
Expand Down Expand Up @@ -2004,7 +2044,7 @@ fn generate_sub_component(
&popup.item_tree,
root,
Some(&parent_ctx),
false,
true,
component_id,
Access::Public,
file,
Expand Down Expand Up @@ -4436,7 +4476,13 @@ fn compile_builtin_function_call(
let position = compile_expression(&popup.position.borrow(), &popup_ctx);
let close_policy = compile_expression(close_policy, ctx);
component_access.then(|component_access| format!(
"{window}.close_popup({component_access}->popup_id_{popup_index}); {component_access}->popup_id_{popup_index} = {window}.template show_popup<{popup_window_id}>(&*({component_access}), [=](auto self) {{ return {position}; }}, {close_policy}, {{ {parent_component} }})"
// Use a block statement to create own globals and popup instance
"{window}.close_popup({component_access}->popup_id_{popup_index}); \
{component_access}->popup_id_{popup_index} = \
{window}.template show_popup<{popup_window_id}>(&*({component_access}), \
[=](auto self) {{ return {position}; }}, \
{close_policy}, \
{{ {parent_component} }})"
))
} else {
panic!("internal error: invalid args to ShowPopupWindow {arguments:?}")
Expand Down
35 changes: 30 additions & 5 deletions internal/compiler/generator/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,23 @@ fn generate_shared_globals(
_self
}

// Clone the SharedGlobals struct but use a different window adapter. This is for example used for popup windows, because they need access to the globals, but need their own window adapter
#[allow(dead_code)]
#pub_token fn clone_with_window_adapter(&self, window_adapter: sp::WindowAdapterRc) -> sp::Rc<Self> {
Comment thread
Murmele marked this conversation as resolved.
let _self = sp::Rc::new(Self {
#(#global_names : self.#global_names.clone(),)*
#(#from_library_global_names : self.#from_library_global_names.clone(),)*
window_adapter: ::core::default::Default::default(),
root_item_tree_weak: self.root_item_tree_weak.clone(),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this also need to passed in

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can do. It is only required to init the window_adapter, but since we initialized already, self.window_adapter.get_or_try_init will never call the init branch

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem here is that we need the global to create the item, but on the other side we need the global to create the popup item

#(#library_shared_globals_names: self.#library_shared_globals_names.clone(),)*
});
_self.window_adapter
.set(window_adapter)
.map_err(|_| ())
.expect("The window adapter should not be initialized before this call");
_self
}

fn window_adapter_impl(&self) -> sp::Rc<dyn sp::WindowAdapter> {
sp::Rc::clone(self.window_adapter_ref().unwrap())
}
Expand Down Expand Up @@ -841,7 +858,7 @@ fn generate_sub_component(
root,
Some(&ParentScope::new(&ctx, None)),
None,
false,
true,
)
})
.chain(component.menu_item_trees.iter().map(|tree| {
Expand Down Expand Up @@ -1716,7 +1733,7 @@ fn generate_item_tree(
root: &llr::CompilationUnit,
parent_ctx: Option<&ParentScope>,
index_property: Option<llr::PropertyIdx>,
is_popup_menu: bool,
is_popup: bool,
) -> TokenStream {
let sub_comp = generate_sub_component(sub_tree.root, root, parent_ctx, index_property, true);
let inner_component_id = self::inner_component_id(&root.sub_components[sub_tree.root]);
Expand All @@ -1729,14 +1746,14 @@ fn generate_item_tree(
})
.collect::<Vec<_>>();

let globals = if is_popup_menu {
let globals = if is_popup {
quote!(globals)
} else if parent_ctx.is_some() {
quote!(parent.upgrade().unwrap().globals.get().unwrap().clone())
} else {
quote!(SharedGlobals::new(sp::VRc::downgrade(&self_dyn_rc)))
};
let globals_arg = is_popup_menu.then(|| quote!(globals: sp::Rc<SharedGlobals>));
let globals_arg = is_popup.then(|| quote!(globals: sp::Rc<SharedGlobals>));

let embedding_function = if parent_ctx.is_some() {
quote!(todo!("Components written in Rust can not get embedded yet."))
Expand Down Expand Up @@ -3286,7 +3303,15 @@ fn compile_builtin_function_call(
let popup_id_name = internal_popup_id(*popup_index as usize);
component_access_tokens.then(|component_access_tokens| quote!({
let parent_item = #parent_item;
let popup_instance = #popup_window_id::new(#component_access_tokens.self_weak.get().unwrap().clone()).unwrap();
// Use the newly created window adapter if we are able to create one. Otherwise use the parent's one
let globals = if let Some(window_adapter) = sp::WindowInner::from_pub(#window_adapter_tokens.window()).create_popup_window_adapter() {
Comment thread
Murmele marked this conversation as resolved.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess a similar change in the C++ and interpreter code will be required.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep there I have to add as well

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

let globals = #component_access_tokens.globals.get().unwrap().clone_with_window_adapter(window_adapter);
globals
} else {
#component_access_tokens.globals.get().unwrap().clone()
};

let popup_instance = #popup_window_id::new(#component_access_tokens.self_weak.get().unwrap().clone(), globals).unwrap();
let popup_instance_vrc = sp::VRc::map(popup_instance.clone(), |x| x);
let position = { let _self = popup_instance_vrc.as_pin_ref(); #position };
if let Some(current_id) = #component_access_tokens.#popup_id_name.take() {
Expand Down
84 changes: 59 additions & 25 deletions internal/core/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
#![warn(missing_docs)]
//! Exposed Window API
use crate::api::{
CloseRequestResponse, LogicalPosition, PhysicalPosition, PhysicalSize, PlatformError, Window,
WindowPosition, WindowSize,
CloseRequestResponse, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize,
PlatformError, Window, WindowPosition, WindowSize,
};
use crate::graphics::Color;
use crate::input::{
Expand Down Expand Up @@ -178,12 +178,12 @@ pub trait WindowAdapterInternal: core::any::Any {
}

/// Create a window for a popup.
///
/// `geometry` is the location of the popup in the window coordinate
/// This function will create only the window adapter but does not show the popup it self
/// Use this window adapter to create a new popup window and show it with `show_popup()`
///
/// If this function return None (the default implementation), then the
/// popup will be rendered within the window itself.
fn create_popup(&self, _geometry: LogicalRect) -> Option<Rc<dyn WindowAdapter>> {
fn create_popup_window_adapter(&self) -> Option<Rc<dyn WindowAdapter>> {
None
}

Expand Down Expand Up @@ -1338,6 +1338,13 @@ impl WindowInner {
.collect()
});
}
/// Create a new popup window adapter
/// This window adapter can be used on a popup component and shown with show_popup()
pub fn create_popup_window_adapter(&self) -> Option<Rc<dyn WindowAdapter>> {
self.window_adapter()
.internal(crate::InternalToken)
.and_then(|s| s.create_popup_window_adapter())
}

/// Show a popup at the given position relative to the `parent_item` and returns its ID.
/// The returned ID will always be non-zero.
Expand Down Expand Up @@ -1433,28 +1440,35 @@ impl WindowInner {
self.window_adapter()
};

let mut popup_window_adapter = None;
ItemTreeRc::borrow_pin(popup_componentrc)
.as_ref()
.window_adapter(false, &mut popup_window_adapter);
let popup_window_adapter =
popup_window_adapter.expect("It must be there because we set the global");

// If a popup can be created it is at TopLevel, otherwise it is a ChildWindow
// of the current window
let location = match parent_window_adapter
.internal(crate::InternalToken)
.and_then(|x| x.create_popup(LogicalRect::new(position, size)))
{
None => {
let clip = LogicalRect::new(
LogicalPoint::new(0.0 as crate::Coord, 0.0 as crate::Coord),
self.window_adapter().size().to_logical(self.scale_factor()).to_euclid(),
);
let rect = popup::place_popup(
popup::Placement::Fixed(LogicalRect::new(position, size)),
&Some(clip),
);
self.window_adapter().request_redraw();
PopupWindowLocation::ChildWindow(rect.origin)
}
Some(window_adapter) => {
WindowInner::from_pub(window_adapter.window()).set_component(popup_componentrc);
PopupWindowLocation::TopLevel(window_adapter)
}
let location = if Rc::ptr_eq(&parent_window_adapter, &popup_window_adapter) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking this could potentially be a problem if we had a popup child of another popup and one (custom) platform would only support one layer of popup. But maybe there is no such thing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean a mix of ChildWindow and TopLevelWindow?

let clip = LogicalRect::new(
LogicalPoint::new(0.0 as crate::Coord, 0.0 as crate::Coord),
self.window_adapter().size().to_logical(self.scale_factor()).to_euclid(),
);
let rect = popup::place_popup(
popup::Placement::Fixed(LogicalRect::new(position, size)),
&Some(clip),
);
self.window_adapter().request_redraw();
PopupWindowLocation::ChildWindow(rect.origin)
} else {
WindowInner::from_pub(popup_window_adapter.window()).set_component(popup_componentrc);
popup_window_adapter.window().set_position(LogicalPosition::from_euclid(position));
popup_window_adapter
.window()
.set_size(WindowSize::Logical(LogicalSize::from_euclid(size)));

popup_window_adapter.set_visible(true).expect("Unable to show popup");
PopupWindowLocation::TopLevel(popup_window_adapter)
};

let focus_item = self
Expand Down Expand Up @@ -1952,6 +1966,26 @@ pub mod ffi {
}
}

/// Create a popup window adapter. Returns true if a new adapter was created and written to result.
/// Returns false if the backend does not support top-level popups.
/// This can be used to set the correct window adapter on a popup component before showing it.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn slint_windowrc_create_popup_window_adapter(
handle: *const WindowAdapterRcOpaque,
result: *mut WindowAdapterRcOpaque,
) -> bool {
unsafe {
let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
match WindowInner::from_pub(window_adapter.window()).create_popup_window_adapter() {
Some(wa) => {
core::ptr::write(result as *mut Rc<dyn WindowAdapter>, wa);
true
}
None => false,
}
}
}

/// Close the popup by the given ID.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn slint_windowrc_close_popup(
Expand Down
Loading
Loading