mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-15 20:33:10 +00:00
LibCore+LibGUI+WindowServer: Make events bubble up through ancestors
With this patch, CEvents no longer stop at the target object, but will bubble up the ancestor chain as long as CEvent::is_accepted() is false. To the set accepted flag, call CEvent::accept(). To clear the accepted flag, call CEvent::ignore(). Events start out in the accepted state, so if you want them to bubble up, you have to call ignore() on them. Using this mechanism, we now ignore non-tabbing keydown events in GWidget, causing them to bubble up through the widget's ancestors. :^)
This commit is contained in:
parent
74c4e62659
commit
fcc3745b02
|
@ -31,8 +31,13 @@ public:
|
||||||
|
|
||||||
unsigned type() const { return m_type; }
|
unsigned type() const { return m_type; }
|
||||||
|
|
||||||
|
bool is_accepted() const { return m_accepted; }
|
||||||
|
void accept() { m_accepted = true; }
|
||||||
|
void ignore() { m_accepted = false; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned m_type { Type::Invalid };
|
unsigned m_type { Type::Invalid };
|
||||||
|
bool m_accepted { true };
|
||||||
};
|
};
|
||||||
|
|
||||||
class CDeferredInvocationEvent : public CEvent {
|
class CDeferredInvocationEvent : public CEvent {
|
||||||
|
|
|
@ -245,7 +245,7 @@ void CEventLoop::pump(WaitMode mode)
|
||||||
#endif
|
#endif
|
||||||
static_cast<CDeferredInvocationEvent&>(event).m_invokee(*receiver);
|
static_cast<CDeferredInvocationEvent&>(event).m_invokee(*receiver);
|
||||||
} else {
|
} else {
|
||||||
receiver->event(event);
|
receiver->dispatch_event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_exit_requested) {
|
if (m_exit_requested) {
|
||||||
|
|
|
@ -134,3 +134,24 @@ void CObject::save_to(JsonObject& json)
|
||||||
json.set("name", name());
|
json.set("name", name());
|
||||||
json.set("parent", String::format("%p", parent()));
|
json.set("parent", String::format("%p", parent()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CObject::is_ancestor_of(const CObject& other) const
|
||||||
|
{
|
||||||
|
if (&other == this)
|
||||||
|
return false;
|
||||||
|
for (auto* ancestor = other.parent(); ancestor; ancestor = ancestor->parent()) {
|
||||||
|
if (ancestor == this)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CObject::dispatch_event(CEvent& e, CObject* stay_within)
|
||||||
|
{
|
||||||
|
ASSERT(!stay_within || stay_within == this || stay_within->is_ancestor_of(*this));
|
||||||
|
auto* target = this;
|
||||||
|
do {
|
||||||
|
target->event(e);
|
||||||
|
target = target->parent();
|
||||||
|
} while (target && target != stay_within && !e.is_accepted());
|
||||||
|
}
|
||||||
|
|
|
@ -48,6 +48,8 @@ public:
|
||||||
template<typename T, typename Callback>
|
template<typename T, typename Callback>
|
||||||
void for_each_child_of_type(Callback callback);
|
void for_each_child_of_type(Callback callback);
|
||||||
|
|
||||||
|
bool is_ancestor_of(const CObject&) const;
|
||||||
|
|
||||||
CObject* parent() { return m_parent; }
|
CObject* parent() { return m_parent; }
|
||||||
const CObject* parent() const { return m_parent; }
|
const CObject* parent() const { return m_parent; }
|
||||||
|
|
||||||
|
@ -71,6 +73,8 @@ public:
|
||||||
|
|
||||||
static IntrusiveList<CObject, &CObject::m_all_objects_list_node>& all_objects();
|
static IntrusiveList<CObject, &CObject::m_all_objects_list_node>& all_objects();
|
||||||
|
|
||||||
|
void dispatch_event(CEvent&, CObject* stay_within = nullptr);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit CObject(CObject* parent = nullptr, bool is_widget = false);
|
explicit CObject(CObject* parent = nullptr, bool is_widget = false);
|
||||||
|
|
||||||
|
|
|
@ -134,8 +134,11 @@ void GAbstractButton::leave_event(CEvent&)
|
||||||
|
|
||||||
void GAbstractButton::keydown_event(GKeyEvent& event)
|
void GAbstractButton::keydown_event(GKeyEvent& event)
|
||||||
{
|
{
|
||||||
if (event.key() == KeyCode::Key_Return)
|
if (event.key() == KeyCode::Key_Return) {
|
||||||
click();
|
click();
|
||||||
|
event.accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
GWidget::keydown_event(event);
|
GWidget::keydown_event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ void GWidget::handle_paint_event(GPaintEvent& event)
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
if (child.relative_rect().intersects(event.rect())) {
|
if (child.relative_rect().intersects(event.rect())) {
|
||||||
GPaintEvent local_event(event.rect().intersected(child.relative_rect()).translated(-child.relative_position()));
|
GPaintEvent local_event(event.rect().intersected(child.relative_rect()).translated(-child.relative_position()));
|
||||||
child.event(local_event);
|
child.dispatch_event(local_event, this);
|
||||||
}
|
}
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
});
|
});
|
||||||
|
@ -231,7 +231,10 @@ void GWidget::keydown_event(GKeyEvent& event)
|
||||||
focus_previous_widget();
|
focus_previous_widget();
|
||||||
else
|
else
|
||||||
focus_next_widget();
|
focus_next_widget();
|
||||||
|
event.accept();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
event.ignore();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GWidget::keyup_event(GKeyEvent&)
|
void GWidget::keyup_event(GKeyEvent&)
|
||||||
|
|
|
@ -191,14 +191,14 @@ void GWindow::event(CEvent& event)
|
||||||
auto window_relative_rect = m_global_cursor_tracking_widget->window_relative_rect();
|
auto window_relative_rect = m_global_cursor_tracking_widget->window_relative_rect();
|
||||||
Point local_point { mouse_event.x() - window_relative_rect.x(), mouse_event.y() - window_relative_rect.y() };
|
Point local_point { mouse_event.x() - window_relative_rect.x(), mouse_event.y() - window_relative_rect.y() };
|
||||||
auto local_event = make<GMouseEvent>((GEvent::Type)event.type(), local_point, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers(), mouse_event.wheel_delta());
|
auto local_event = make<GMouseEvent>((GEvent::Type)event.type(), local_point, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers(), mouse_event.wheel_delta());
|
||||||
m_global_cursor_tracking_widget->event(*local_event);
|
m_global_cursor_tracking_widget->dispatch_event(*local_event, this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (m_automatic_cursor_tracking_widget) {
|
if (m_automatic_cursor_tracking_widget) {
|
||||||
auto window_relative_rect = m_automatic_cursor_tracking_widget->window_relative_rect();
|
auto window_relative_rect = m_automatic_cursor_tracking_widget->window_relative_rect();
|
||||||
Point local_point { mouse_event.x() - window_relative_rect.x(), mouse_event.y() - window_relative_rect.y() };
|
Point local_point { mouse_event.x() - window_relative_rect.x(), mouse_event.y() - window_relative_rect.y() };
|
||||||
auto local_event = make<GMouseEvent>((GEvent::Type)event.type(), local_point, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers(), mouse_event.wheel_delta());
|
auto local_event = make<GMouseEvent>((GEvent::Type)event.type(), local_point, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers(), mouse_event.wheel_delta());
|
||||||
m_automatic_cursor_tracking_widget->event(*local_event);
|
m_automatic_cursor_tracking_widget->dispatch_event(*local_event, this);
|
||||||
if (mouse_event.buttons() == 0)
|
if (mouse_event.buttons() == 0)
|
||||||
m_automatic_cursor_tracking_widget = nullptr;
|
m_automatic_cursor_tracking_widget = nullptr;
|
||||||
return;
|
return;
|
||||||
|
@ -212,7 +212,7 @@ void GWindow::event(CEvent& event)
|
||||||
if (mouse_event.buttons() != 0 && !m_automatic_cursor_tracking_widget)
|
if (mouse_event.buttons() != 0 && !m_automatic_cursor_tracking_widget)
|
||||||
m_automatic_cursor_tracking_widget = result.widget->make_weak_ptr();
|
m_automatic_cursor_tracking_widget = result.widget->make_weak_ptr();
|
||||||
if (result.widget != m_global_cursor_tracking_widget.ptr())
|
if (result.widget != m_global_cursor_tracking_widget.ptr())
|
||||||
return result.widget->event(*local_event);
|
return result.widget->dispatch_event(*local_event, this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +241,7 @@ void GWindow::event(CEvent& event)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& rect : rects)
|
for (auto& rect : rects)
|
||||||
m_main_widget->event(*make<GPaintEvent>(rect));
|
m_main_widget->dispatch_event(*make<GPaintEvent>(rect), this);
|
||||||
|
|
||||||
paint_keybinds();
|
paint_keybinds();
|
||||||
|
|
||||||
|
@ -290,9 +290,9 @@ void GWindow::event(CEvent& event)
|
||||||
if (found_widget != m_keyboard_activation_targets.end()) {
|
if (found_widget != m_keyboard_activation_targets.end()) {
|
||||||
m_keybind_mode = false;
|
m_keybind_mode = false;
|
||||||
auto event = make<GMouseEvent>(GEvent::MouseDown, Point(), 0, GMouseButton::Left, 0, 0);
|
auto event = make<GMouseEvent>(GEvent::MouseDown, Point(), 0, GMouseButton::Left, 0, 0);
|
||||||
found_widget->value->event(*event);
|
found_widget->value->dispatch_event(*event, this);
|
||||||
event = make<GMouseEvent>(GEvent::MouseUp, Point(), 0, GMouseButton::Left, 0, 0);
|
event = make<GMouseEvent>(GEvent::MouseUp, Point(), 0, GMouseButton::Left, 0, 0);
|
||||||
found_widget->value->event(*event);
|
found_widget->value->dispatch_event(*event, this);
|
||||||
} else if (m_entered_keybind.length() >= m_max_keybind_length) {
|
} else if (m_entered_keybind.length() >= m_max_keybind_length) {
|
||||||
m_keybind_mode = false;
|
m_keybind_mode = false;
|
||||||
}
|
}
|
||||||
|
@ -300,9 +300,9 @@ void GWindow::event(CEvent& event)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (m_focused_widget)
|
if (m_focused_widget)
|
||||||
return m_focused_widget->event(event);
|
return m_focused_widget->dispatch_event(event, this);
|
||||||
if (m_main_widget)
|
if (m_main_widget)
|
||||||
return m_main_widget->event(event);
|
return m_main_widget->dispatch_event(event, this);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -315,7 +315,7 @@ void GWindow::event(CEvent& event)
|
||||||
|
|
||||||
m_is_active = event.type() == GEvent::WindowBecameActive;
|
m_is_active = event.type() == GEvent::WindowBecameActive;
|
||||||
if (m_main_widget)
|
if (m_main_widget)
|
||||||
m_main_widget->event(event);
|
m_main_widget->dispatch_event(event, this);
|
||||||
if (m_focused_widget)
|
if (m_focused_widget)
|
||||||
m_focused_widget->update();
|
m_focused_widget->update();
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -178,7 +178,8 @@ void WSWindow::event(CEvent& event)
|
||||||
{
|
{
|
||||||
if (!m_client) {
|
if (!m_client) {
|
||||||
ASSERT(parent());
|
ASSERT(parent());
|
||||||
return parent()->event(event);
|
event.ignore();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_blocked_by_modal_window())
|
if (is_blocked_by_modal_window())
|
||||||
|
|
|
@ -664,11 +664,11 @@ void WSWindowManager::process_event_for_doubleclick(WSWindow& window, WSMouseEve
|
||||||
|
|
||||||
void WSWindowManager::deliver_mouse_event(WSWindow& window, WSMouseEvent& event)
|
void WSWindowManager::deliver_mouse_event(WSWindow& window, WSMouseEvent& event)
|
||||||
{
|
{
|
||||||
window.event(event);
|
window.dispatch_event(event);
|
||||||
if (event.type() == WSEvent::MouseUp) {
|
if (event.type() == WSEvent::MouseUp) {
|
||||||
process_event_for_doubleclick(window, event);
|
process_event_for_doubleclick(window, event);
|
||||||
if (event.type() == WSEvent::MouseDoubleClick)
|
if (event.type() == WSEvent::MouseDoubleClick)
|
||||||
window.event(event);
|
window.dispatch_event(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -703,7 +703,7 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& hovere
|
||||||
|
|
||||||
// FIXME: Now that the menubar has a dedicated window, is this special-casing really necessary?
|
// FIXME: Now that the menubar has a dedicated window, is this special-casing really necessary?
|
||||||
if (!active_window_is_modal() && menubar_rect().contains(event.position())) {
|
if (!active_window_is_modal() && menubar_rect().contains(event.position())) {
|
||||||
m_menu_manager.event(event);
|
m_menu_manager.dispatch_event(event);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -915,7 +915,7 @@ void WSWindowManager::event(CEvent& event)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (m_active_window)
|
if (m_active_window)
|
||||||
return m_active_window->event(event);
|
return m_active_window->dispatch_event(event);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue