Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Qt: Fix event forwarding for native popups #7609

Merged
merged 2 commits into from
Feb 11, 2025
Merged
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
63 changes: 29 additions & 34 deletions internal/backends/qt/qt_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ use i_slint_core::item_rendering::{
use i_slint_core::item_tree::{ItemTreeRc, ItemTreeRef};
use i_slint_core::items::{
self, ColorScheme, FillRule, ImageRendering, ItemRc, ItemRef, Layer, LineCap, MouseCursor,
Opacity, PointerEventButton, PopupClosePolicy, RenderingResult, TextOverflow, TextStrokeStyle,
TextWrap,
Opacity, PointerEventButton, RenderingResult, TextOverflow, TextStrokeStyle, TextWrap,
};
use i_slint_core::layout::Orientation;
use i_slint_core::lengths::{
Expand Down Expand Up @@ -107,7 +106,7 @@ cpp! {{
void paintEvent(QPaintEvent *) override {
if (!rust_window)
return;
auto painter = std::unique_ptr<QPainter>(new QPainter(this));
auto painter = std::unique_ptr<QPainter>(new QPainter(this));
painter->setClipRect(rect());
painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
QPainterPtr *painter_ptr = &painter;
Expand All @@ -129,11 +128,32 @@ cpp! {{
});
}

/// If this window is a PopupWindow and the mouse event is outside of the popup, then adjust the event to map to the parent window
/// Returns the position and the rust_window to which we need to deliver the event
std::tuple<QPoint, void*> adjust_mouse_event_to_popup_parent(QMouseEvent *event) {
auto pos = event->pos();
if (auto p = dynamic_cast<const SlintWidget*>(parent()); p && !rect().contains(pos)) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QPoint eventPos = event->globalPosition().toPoint();
#else
QPoint eventPos = event->globalPos();
#endif
while (auto pp = dynamic_cast<const SlintWidget*>(p->parent())) {
if (p->rect().contains(p->mapFromGlobal(eventPos)))
break;
p = pp;
}
return { p->mapFromGlobal(eventPos), p->rust_window };
} else {
return { pos, rust_window };
}
}

void mousePressEvent(QMouseEvent *event) override {
isMouseButtonDown = true;
auto [pos, rust_window] = adjust_mouse_event_to_popup_parent(event);
if (!rust_window)
return;
isMouseButtonDown = true;
QPoint pos = event->pos();
int button = event->button();
rust!(Slint_mousePressEvent [rust_window: &QtWindow as "void*", pos: qttypes::QPoint as "QPoint", button: u32 as "int" ] {
let position = LogicalPoint::new(pos.x as _, pos.y as _);
Expand All @@ -159,45 +179,20 @@ cpp! {{
}
isMouseButtonDown = false;

void *parent_of_popup_to_close = nullptr;
int popup_id_to_close = 0;
if (auto p = dynamic_cast<const SlintWidget*>(parent())) {
while (auto pp = dynamic_cast<const SlintWidget*>(p->parent())) {
p = pp;
}
void *parent_window = p->rust_window;
bool inside = rect().contains(event->pos());
popup_id_to_close = rust!(Slint_mouseReleaseEventPopup [parent_window: &QtWindow as "void*", inside: bool as "bool"] -> u32 as "int" {
let active_popups = WindowInner::from_pub(&parent_window.window).active_popups();
if let Some(popup) = active_popups.last() {
if popup.close_policy == PopupClosePolicy::CloseOnClick || (popup.close_policy == PopupClosePolicy::CloseOnClickOutside && !inside) {
return popup.popup_id.get();
}
}
0
});
if (popup_id_to_close) {
parent_of_popup_to_close = parent_window;
}
}

QPoint pos = event->pos();
auto [pos, rust_window] = adjust_mouse_event_to_popup_parent(event);
if (!rust_window)
return;
int button = event->button();
rust!(Slint_mouseReleaseEvent [rust_window: &QtWindow as "void*", pos: qttypes::QPoint as "QPoint", button: u32 as "int" ] {
let position = LogicalPoint::new(pos.x as _, pos.y as _);
let button = from_qt_button(button);
rust_window.mouse_event(MouseEvent::Released{ position, button, click_count: 0 })
});
if (popup_id_to_close) {
rust!(Slint_mouseReleaseEventClosePopup [parent_of_popup_to_close: &QtWindow as "void*", popup_id_to_close: std::num::NonZeroU32 as "int"] {
WindowInner::from_pub(&parent_of_popup_to_close.window).close_popup(popup_id_to_close);
});
}
}
void mouseMoveEvent(QMouseEvent *event) override {
auto [pos, rust_window] = adjust_mouse_event_to_popup_parent(event);
if (!rust_window)
return;
QPoint pos = event->pos();
rust!(Slint_mouseMoveEvent [rust_window: &QtWindow as "void*", pos: qttypes::QPoint as "QPoint"] {
let position = LogicalPoint::new(pos.x as _, pos.y as _);
rust_window.mouse_event(MouseEvent::Moved{position})
Expand Down
36 changes: 29 additions & 7 deletions internal/core/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -594,21 +594,40 @@ impl WindowInner {
crate::input::process_delayed_event(&window_adapter, mouse_input_state);
}

// Try to get the root window in case `self` is the popup itself (to get the active_popups list)
let mut root_adapter = None;
ItemTreeRc::borrow_pin(&self.component()).as_ref().window_adapter(false, &mut root_adapter);
let root_adapter = root_adapter.unwrap_or_else(|| window_adapter.clone());
let active_popups = &WindowInner::from_pub(root_adapter.window()).active_popups;
let native_popup_index = active_popups.borrow().iter().position(|p| {
if let PopupWindowLocation::TopLevel(wa) = &p.location {
Rc::ptr_eq(wa, &window_adapter)
} else {
false
}
});

if pressed_event {
self.had_popup_on_press.set(!self.active_popups.borrow().is_empty());
self.had_popup_on_press.set(!active_popups.borrow().is_empty());
}

let mut popup_to_close = self.active_popups.borrow().last().and_then(|popup| {
let mut popup_to_close = active_popups.borrow().last().and_then(|popup| {
let mouse_inside_popup = || {
if let PopupWindowLocation::ChildWindow(coordinates) = &popup.location {
event.position().map_or(true, |pos| {
event.position().is_none_or(|pos| {
ItemTreeRc::borrow_pin(&popup.component)
.as_ref()
.item_geometry(0)
.contains(pos - coordinates.to_vector())
})
} else {
false
native_popup_index.is_some_and(|idx| idx == active_popups.borrow().len() - 1)
&& event.position().is_none_or(|pos| {
ItemTreeRc::borrow_pin(&self.component())
.as_ref()
.item_geometry(0)
.contains(pos)
})
}
};
match popup.close_policy {
Expand All @@ -628,18 +647,21 @@ impl WindowInner {
{
let mut item_tree = self.component.borrow().upgrade();
let mut offset = LogicalPoint::default();
for popup in self.active_popups.borrow().iter().rev() {
for (idx, popup) in active_popups.borrow().iter().enumerate().rev() {
item_tree = None;
if let PopupWindowLocation::ChildWindow(coordinates) = &popup.location {
let geom = ItemTreeRc::borrow_pin(&popup.component).as_ref().item_geometry(0);
let mouse_inside_popup = event
.position()
.map_or(true, |pos| geom.contains(pos - coordinates.to_vector()));
.is_none_or(|pos| geom.contains(pos - coordinates.to_vector()));
if mouse_inside_popup {
item_tree = Some(popup.component.clone());
offset = *coordinates;
break;
}
} else if native_popup_index.is_some_and(|i| i == idx) {
item_tree = self.component.borrow().upgrade();
break;
}

if !popup.is_menu {
Expand Down Expand Up @@ -683,7 +705,7 @@ impl WindowInner {
self.mouse_input_state.set(mouse_input_state);

if let Some(popup_id) = popup_to_close {
self.close_popup(popup_id);
WindowInner::from_pub(root_adapter.window()).close_popup(popup_id);
}

crate::properties::ChangeTracker::run_change_handlers();
Expand Down
Loading