mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 15:12:45 +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
					
				
					 9 changed files with 54 additions and 17 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling