mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 18:22:45 +00:00 
			
		
		
		
	LibWeb: Update hit_test for CSS Transforms
This now also takes a FloatPoint instead of an IntPoint to avoid excessive rounding when multiple transforms apply on top of each other.
This commit is contained in:
		
							parent
							
								
									a2331e8dd3
								
							
						
					
					
						commit
						48efdaa8c4
					
				
					 7 changed files with 28 additions and 23 deletions
				
			
		|  | @ -138,7 +138,7 @@ bool EventHandler::handle_mousewheel(const Gfx::IntPoint& position, unsigned int | |||
| 
 | ||||
|     // FIXME: Support wheel events in nested browsing contexts.
 | ||||
| 
 | ||||
|     auto result = paint_root()->hit_test(position, Painting::HitTestType::Exact); | ||||
|     auto result = paint_root()->hit_test(position.to_type<float>(), Painting::HitTestType::Exact); | ||||
|     if (result.paintable && result.paintable->handle_mousewheel({}, position, buttons, modifiers, wheel_delta_x, wheel_delta_y)) | ||||
|         return true; | ||||
| 
 | ||||
|  | @ -164,7 +164,7 @@ bool EventHandler::handle_mouseup(const Gfx::IntPoint& position, unsigned button | |||
|     if (m_mouse_event_tracking_layout_node) { | ||||
|         paintable = m_mouse_event_tracking_layout_node->paintable(); | ||||
|     } else { | ||||
|         auto result = paint_root()->hit_test(position, Painting::HitTestType::Exact); | ||||
|         auto result = paint_root()->hit_test(position.to_type<float>(), Painting::HitTestType::Exact); | ||||
|         paintable = result.paintable; | ||||
|     } | ||||
| 
 | ||||
|  | @ -175,7 +175,7 @@ bool EventHandler::handle_mouseup(const Gfx::IntPoint& position, unsigned button | |||
|         // Things may have changed as a consequence of Layout::Node::handle_mouseup(). Hit test again.
 | ||||
|         if (!paint_root()) | ||||
|             return true; | ||||
|         auto result = paint_root()->hit_test(position, Painting::HitTestType::Exact); | ||||
|         auto result = paint_root()->hit_test(position.to_type<float>(), Painting::HitTestType::Exact); | ||||
|         paintable = result.paintable; | ||||
|     } | ||||
| 
 | ||||
|  | @ -222,7 +222,7 @@ bool EventHandler::handle_mousedown(const Gfx::IntPoint& position, unsigned butt | |||
|         if (m_mouse_event_tracking_layout_node) { | ||||
|             paintable = m_mouse_event_tracking_layout_node->paintable(); | ||||
|         } else { | ||||
|             auto result = paint_root()->hit_test(position, Painting::HitTestType::Exact); | ||||
|             auto result = paint_root()->hit_test(position.to_type<float>(), Painting::HitTestType::Exact); | ||||
|             if (!result.paintable) | ||||
|                 return false; | ||||
|             paintable = result.paintable; | ||||
|  | @ -300,7 +300,7 @@ bool EventHandler::handle_mousedown(const Gfx::IntPoint& position, unsigned butt | |||
|         } | ||||
|     } else { | ||||
|         if (button == GUI::MouseButton::Primary) { | ||||
|             auto result = paint_root()->hit_test(position, Painting::HitTestType::TextCursor); | ||||
|             auto result = paint_root()->hit_test(position.to_type<float>(), Painting::HitTestType::TextCursor); | ||||
|             if (result.paintable && result.paintable->layout_node().dom_node()) { | ||||
| 
 | ||||
|                 // See if we want to focus something.
 | ||||
|  | @ -348,7 +348,7 @@ bool EventHandler::handle_mousemove(const Gfx::IntPoint& position, unsigned butt | |||
|     if (m_mouse_event_tracking_layout_node) { | ||||
|         paintable = m_mouse_event_tracking_layout_node->paintable(); | ||||
|     } else { | ||||
|         auto result = paint_root()->hit_test(position, Painting::HitTestType::Exact); | ||||
|         auto result = paint_root()->hit_test(position.to_type<float>(), Painting::HitTestType::Exact); | ||||
|         paintable = result.paintable; | ||||
|         start_index = result.index_in_node; | ||||
|     } | ||||
|  | @ -408,7 +408,7 @@ bool EventHandler::handle_mousemove(const Gfx::IntPoint& position, unsigned butt | |||
|                 return true; | ||||
|         } | ||||
|         if (m_in_mouse_selection) { | ||||
|             auto hit = paint_root()->hit_test(position, Painting::HitTestType::TextCursor); | ||||
|             auto hit = paint_root()->hit_test(position.to_type<float>(), Painting::HitTestType::TextCursor); | ||||
|             if (start_index.has_value() && hit.paintable && hit.paintable->layout_node().dom_node()) { | ||||
|                 m_browsing_context.set_cursor_position(DOM::Position(*hit.paintable->layout_node().dom_node(), *start_index)); | ||||
|                 layout_root()->set_selection_end({ hit.paintable->layout_node(), hit.index_in_node }); | ||||
|  |  | |||
|  | @ -41,7 +41,7 @@ bool Paintable::handle_mousewheel(Badge<EventHandler>, Gfx::IntPoint const&, uns | |||
|     return false; | ||||
| } | ||||
| 
 | ||||
| HitTestResult Paintable::hit_test(Gfx::IntPoint const&, HitTestType) const | ||||
| HitTestResult Paintable::hit_test(Gfx::FloatPoint const&, HitTestType) const | ||||
| { | ||||
|     VERIFY_NOT_REACHED(); | ||||
| } | ||||
|  |  | |||
|  | @ -50,7 +50,7 @@ public: | |||
|     virtual void before_children_paint(PaintContext&, PaintPhase) const { } | ||||
|     virtual void after_children_paint(PaintContext&, PaintPhase) const { } | ||||
| 
 | ||||
|     virtual HitTestResult hit_test(Gfx::IntPoint const&, HitTestType) const; | ||||
|     virtual HitTestResult hit_test(Gfx::FloatPoint const&, HitTestType) const; | ||||
| 
 | ||||
|     virtual bool wants_mouse_events() const { return false; } | ||||
| 
 | ||||
|  |  | |||
|  | @ -526,7 +526,7 @@ void PaintableBox::for_each_child_in_paint_order(Callback callback) const | |||
|     }); | ||||
| } | ||||
| 
 | ||||
| HitTestResult PaintableBox::hit_test(Gfx::IntPoint const& position, HitTestType type) const | ||||
| HitTestResult PaintableBox::hit_test(Gfx::FloatPoint const& position, HitTestType type) const | ||||
| { | ||||
|     if (layout_box().is_initial_containing_block_box()) | ||||
|         return stacking_context()->hit_test(position, type); | ||||
|  | @ -542,7 +542,7 @@ HitTestResult PaintableBox::hit_test(Gfx::IntPoint const& position, HitTestType | |||
|     return result; | ||||
| } | ||||
| 
 | ||||
| HitTestResult PaintableWithLines::hit_test(const Gfx::IntPoint& position, HitTestType type) const | ||||
| HitTestResult PaintableWithLines::hit_test(const Gfx::FloatPoint& position, HitTestType type) const | ||||
| { | ||||
|     if (!layout_box().children_are_inline()) | ||||
|         return PaintableBox::hit_test(position, type); | ||||
|  | @ -552,7 +552,7 @@ HitTestResult PaintableWithLines::hit_test(const Gfx::IntPoint& position, HitTes | |||
|         for (auto& fragment : line_box.fragments()) { | ||||
|             if (is<Layout::Box>(fragment.layout_node()) && static_cast<Layout::Box const&>(fragment.layout_node()).paint_box()->stacking_context()) | ||||
|                 continue; | ||||
|             if (enclosing_int_rect(fragment.absolute_rect()).contains(position)) { | ||||
|             if (fragment.absolute_rect().contains(position)) { | ||||
|                 if (is<Layout::BlockContainer>(fragment.layout_node()) && fragment.layout_node().paintable()) | ||||
|                     return fragment.layout_node().paintable()->hit_test(position, type); | ||||
|                 return { fragment.layout_node().paintable(), fragment.text_index_at(position.x()) }; | ||||
|  |  | |||
|  | @ -115,7 +115,7 @@ public: | |||
|     virtual void before_children_paint(PaintContext&, PaintPhase) const override; | ||||
|     virtual void after_children_paint(PaintContext&, PaintPhase) const override; | ||||
| 
 | ||||
|     virtual HitTestResult hit_test(Gfx::IntPoint const&, HitTestType) const override; | ||||
|     virtual HitTestResult hit_test(Gfx::FloatPoint const&, HitTestType) const override; | ||||
| 
 | ||||
| protected: | ||||
|     explicit PaintableBox(Layout::Box const&); | ||||
|  | @ -164,7 +164,7 @@ public: | |||
|     virtual bool wants_mouse_events() const override { return false; } | ||||
|     virtual bool handle_mousewheel(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y) override; | ||||
| 
 | ||||
|     virtual HitTestResult hit_test(Gfx::IntPoint const&, HitTestType) const override; | ||||
|     virtual HitTestResult hit_test(Gfx::FloatPoint const&, HitTestType) const override; | ||||
| 
 | ||||
| protected: | ||||
|     PaintableWithLines(Layout::BlockContainer const&); | ||||
|  |  | |||
|  | @ -272,8 +272,13 @@ void StackingContext::paint(PaintContext& context) const | |||
|     } | ||||
| } | ||||
| 
 | ||||
| HitTestResult StackingContext::hit_test(Gfx::IntPoint const& position, HitTestType type) const | ||||
| HitTestResult StackingContext::hit_test(Gfx::FloatPoint const& position, HitTestType type) const | ||||
| { | ||||
|     // FIXME: Use the transform origin specified in CSS or SVG
 | ||||
|     auto transform_origin = m_box.paint_box()->absolute_position(); | ||||
|     auto affine_transform = combine_transformations_2d(m_box.computed_values().transformations()); | ||||
|     auto transformed_position = affine_transform.inverse().value_or({}).map(position - transform_origin) + transform_origin; | ||||
| 
 | ||||
|     // NOTE: Hit testing basically happens in reverse painting order.
 | ||||
|     // https://www.w3.org/TR/CSS22/visuren.html#z-index
 | ||||
| 
 | ||||
|  | @ -282,7 +287,7 @@ HitTestResult StackingContext::hit_test(Gfx::IntPoint const& position, HitTestTy | |||
|         auto const& child = *m_children[i]; | ||||
|         if (child.m_box.computed_values().z_index().value_or(0) < 0) | ||||
|             break; | ||||
|         auto result = child.hit_test(position, type); | ||||
|         auto result = child.hit_test(transformed_position, type); | ||||
|         if (result.paintable) | ||||
|             return result; | ||||
|     } | ||||
|  | @ -291,7 +296,7 @@ HitTestResult StackingContext::hit_test(Gfx::IntPoint const& position, HitTestTy | |||
|     // 6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
 | ||||
|     m_box.for_each_in_subtree_of_type<Layout::Box>([&](Layout::Box const& box) { | ||||
|         if (box.is_positioned() && !box.paint_box()->stacking_context()) { | ||||
|             result = box.paint_box()->hit_test(position, type); | ||||
|             result = box.paint_box()->hit_test(transformed_position, type); | ||||
|             if (result.paintable) | ||||
|                 return IterationDecision::Break; | ||||
|         } | ||||
|  | @ -302,7 +307,7 @@ HitTestResult StackingContext::hit_test(Gfx::IntPoint const& position, HitTestTy | |||
| 
 | ||||
|     // 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
 | ||||
|     if (m_box.children_are_inline() && is<Layout::BlockContainer>(m_box)) { | ||||
|         auto result = m_box.paint_box()->hit_test(position, type); | ||||
|         auto result = m_box.paint_box()->hit_test(transformed_position, type); | ||||
|         if (result.paintable) | ||||
|             return result; | ||||
|     } | ||||
|  | @ -310,7 +315,7 @@ HitTestResult StackingContext::hit_test(Gfx::IntPoint const& position, HitTestTy | |||
|     // 4. the non-positioned floats.
 | ||||
|     m_box.for_each_in_subtree_of_type<Layout::Box>([&](Layout::Box const& box) { | ||||
|         if (box.is_floating()) { | ||||
|             result = box.paint_box()->hit_test(position, type); | ||||
|             result = box.paint_box()->hit_test(transformed_position, type); | ||||
|             if (result.paintable) | ||||
|                 return IterationDecision::Break; | ||||
|         } | ||||
|  | @ -321,7 +326,7 @@ HitTestResult StackingContext::hit_test(Gfx::IntPoint const& position, HitTestTy | |||
|     if (!m_box.children_are_inline()) { | ||||
|         m_box.for_each_in_subtree_of_type<Layout::Box>([&](Layout::Box const& box) { | ||||
|             if (!box.is_absolutely_positioned() && !box.is_floating()) { | ||||
|                 result = box.paint_box()->hit_test(position, type); | ||||
|                 result = box.paint_box()->hit_test(transformed_position, type); | ||||
|                 if (result.paintable) | ||||
|                     return IterationDecision::Break; | ||||
|             } | ||||
|  | @ -336,13 +341,13 @@ HitTestResult StackingContext::hit_test(Gfx::IntPoint const& position, HitTestTy | |||
|         auto const& child = *m_children[i]; | ||||
|         if (child.m_box.computed_values().z_index().value_or(0) < 0) | ||||
|             break; | ||||
|         auto result = child.hit_test(position, type); | ||||
|         auto result = child.hit_test(transformed_position, type); | ||||
|         if (result.paintable) | ||||
|             return result; | ||||
|     } | ||||
| 
 | ||||
|     // 1. the background and borders of the element forming the stacking context.
 | ||||
|     if (m_box.paint_box()->absolute_border_box_rect().contains(position.to_type<float>())) { | ||||
|     if (m_box.paint_box()->absolute_border_box_rect().contains(transformed_position)) { | ||||
|         return HitTestResult { | ||||
|             .paintable = m_box.paintable(), | ||||
|         }; | ||||
|  |  | |||
|  | @ -30,7 +30,7 @@ public: | |||
| 
 | ||||
|     void paint_descendants(PaintContext&, Layout::Node&, StackingContextPaintPhase) const; | ||||
|     void paint(PaintContext&) const; | ||||
|     HitTestResult hit_test(Gfx::IntPoint const&, HitTestType) const; | ||||
|     HitTestResult hit_test(Gfx::FloatPoint const&, HitTestType) const; | ||||
| 
 | ||||
|     void dump(int indent = 0) const; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Simon Wanner
						Simon Wanner