// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/aura/window_targeter.h" #include "services/ui/public/interfaces/window_tree_constants.mojom.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/client/event_client.h" #include "ui/aura/client/focus_client.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/aura/window_tree_host.h" #include "ui/events/event_target.h" #include "ui/events/event_target_iterator.h" namespace aura { WindowTargeter::WindowTargeter() {} WindowTargeter::~WindowTargeter() {} bool WindowTargeter::SubtreeShouldBeExploredForEvent( Window* window, const ui::LocatedEvent& event) { return SubtreeCanAcceptEvent(window, event) && EventLocationInsideBounds(window, event); } bool WindowTargeter::GetHitTestRects(Window* window, gfx::Rect* hit_test_rect_mouse, gfx::Rect* hit_test_rect_touch) const { DCHECK(hit_test_rect_mouse); DCHECK(hit_test_rect_touch); *hit_test_rect_mouse = *hit_test_rect_touch = window->bounds(); if (ShouldUseExtendedBounds(window)) { hit_test_rect_mouse->Inset(mouse_extend_); hit_test_rect_touch->Inset(touch_extend_); } return true; } std::unique_ptr WindowTargeter::GetExtraHitTestShapeRects(Window* target) const { return nullptr; } void WindowTargeter::SetInsets(const gfx::Insets& mouse_and_touch_extend) { SetInsets(mouse_and_touch_extend, mouse_and_touch_extend); } void WindowTargeter::SetInsets(const gfx::Insets& mouse_extend, const gfx::Insets& touch_extend) { if (mouse_extend_ == mouse_extend && touch_extend_ == touch_extend) return; const gfx::Insets last_mouse_extend_ = mouse_extend_; const gfx::Insets last_touch_extend_ = touch_extend_; mouse_extend_ = mouse_extend; touch_extend_ = touch_extend; OnSetInsets(last_mouse_extend_, last_touch_extend_); } Window* WindowTargeter::GetPriorityTargetInRootWindow( Window* root_window, const ui::LocatedEvent& event) { DCHECK_EQ(root_window, root_window->GetRootWindow()); // Mouse events should be dispatched to the window that processed the // mouse-press events (if any). if (event.IsScrollEvent() || event.IsMouseEvent()) { WindowEventDispatcher* dispatcher = root_window->GetHost()->dispatcher(); if (dispatcher->mouse_pressed_handler()) return dispatcher->mouse_pressed_handler(); } // All events should be directed towards the capture window (if any). Window* capture_window = client::GetCaptureWindow(root_window); if (capture_window) return capture_window; if (event.IsPinchEvent()) { DCHECK_EQ(event.AsGestureEvent()->details().device_type(), ui::GestureDeviceType::DEVICE_TOUCHPAD); WindowEventDispatcher* dispatcher = root_window->GetHost()->dispatcher(); if (dispatcher->touchpad_pinch_handler()) return dispatcher->touchpad_pinch_handler(); } if (event.IsTouchEvent()) { // Query the gesture-recognizer to find targets for touch events. const ui::TouchEvent& touch = *event.AsTouchEvent(); ui::GestureConsumer* consumer = ui::GestureRecognizer::Get()->GetTouchLockedTarget(touch); if (consumer) return static_cast(consumer); } return nullptr; } Window* WindowTargeter::FindTargetInRootWindow(Window* root_window, const ui::LocatedEvent& event) { DCHECK_EQ(root_window, root_window->GetRootWindow()); Window* priority_target = GetPriorityTargetInRootWindow(root_window, event); if (priority_target) return priority_target; if (event.IsTouchEvent()) { // Query the gesture-recognizer to find targets for touch events. const ui::TouchEvent& touch = *event.AsTouchEvent(); // GetTouchLockedTarget() is handled in GetPriorityTargetInRootWindow(). DCHECK(!ui::GestureRecognizer::Get()->GetTouchLockedTarget(touch)); ui::GestureConsumer* consumer = ui::GestureRecognizer::Get()->GetTargetForLocation( event.location_f(), touch.source_device_id()); if (consumer) return static_cast(consumer); // If the initial touch is outside the root window, target the root. if (!root_window->bounds().Contains(event.root_location())) return root_window; } return nullptr; } bool WindowTargeter::ProcessEventIfTargetsDifferentRootWindow( Window* root_window, Window* target, ui::Event* event) { if (root_window->Contains(target)) return false; // |window| is the root window, but |target| is not a descendent of // |window|. So do not allow dispatching from here. Instead, dispatch the // event through the WindowEventDispatcher that owns |target|. Window* new_root = target->GetRootWindow(); DCHECK(new_root); if (event->IsLocatedEvent()) { // The event has been transformed to be in |target|'s coordinate system. // But dispatching the event through the EventProcessor requires the event // to be in the host's coordinate system. So, convert the event to be in // the root's coordinate space, and then to the host's coordinate space by // applying the host's transform. ui::LocatedEvent* located_event = event->AsLocatedEvent(); located_event->ConvertLocationToTarget(target, new_root); WindowTreeHost* window_tree_host = new_root->GetHost(); located_event->UpdateForRootTransform( window_tree_host->GetRootTransform(), window_tree_host->GetRootTransformForLocalEventCoordinates()); } ignore_result(new_root->GetHost()->event_sink()->OnEventFromSource(event)); return true; } ui::EventTarget* WindowTargeter::FindTargetForEvent(ui::EventTarget* root, ui::Event* event) { Window* window = static_cast(root); Window* target = event->IsKeyEvent() ? FindTargetForKeyEvent(window, *event->AsKeyEvent()) : FindTargetForNonKeyEvent(window, event); if (target && !window->parent() && ProcessEventIfTargetsDifferentRootWindow(window, target, event)) { return nullptr; } return target; } ui::EventTarget* WindowTargeter::FindNextBestTarget( ui::EventTarget* previous_target, ui::Event* event) { return nullptr; } Window* WindowTargeter::FindTargetForLocatedEvent(Window* window, ui::LocatedEvent* event) { if (!window->parent()) { Window* target = FindTargetInRootWindow(window, *event); if (target) { window->ConvertEventToTarget(target, event); #if defined(OS_CHROMEOS) if (window->IsRootWindow() && event->HasNativeEvent()) { // If window is root, and the target is in a different host, we need to // convert the native event to the target's host as well. This happens // while a widget is being dragged and when the majority of its bounds // reside in a different display. Setting the widget's bounds at this // point changes the window's root, and the event's target's root, but // the events are still being generated relative to the original // display. crbug.com/714578. ui::LocatedEvent* e = static_cast(event->native_event()); gfx::PointF native_point = e->location_f(); aura::Window::ConvertNativePointToTargetHost(window, target, &native_point); e->set_location_f(native_point); } #endif return target; } } return FindTargetForLocatedEventRecursively(window, event); } bool WindowTargeter::SubtreeCanAcceptEvent( Window* window, const ui::LocatedEvent& event) const { if (!window->IsVisible()) return false; if (window->event_targeting_policy() == ui::mojom::EventTargetingPolicy::NONE || window->event_targeting_policy() == ui::mojom::EventTargetingPolicy::TARGET_ONLY) { return false; } client::EventClient* client = client::GetEventClient(window->GetRootWindow()); if (client && !client->CanProcessEventsWithinSubtree(window)) return false; Window* parent = window->parent(); if (parent && parent->delegate_ && !parent->delegate_->ShouldDescendIntoChildForEventHandling( window, event.location())) { return false; } return true; } bool WindowTargeter::EventLocationInsideBounds( Window* window, const ui::LocatedEvent& event) const { gfx::Rect mouse_rect; gfx::Rect touch_rect; if (!GetHitTestRects(window, &mouse_rect, &touch_rect)) return false; const gfx::Vector2d offset = -window->bounds().OffsetFromOrigin(); mouse_rect.Offset(offset); touch_rect.Offset(offset); gfx::Point point = event.location(); if (window->parent()) Window::ConvertPointToTarget(window->parent(), window, &point); const bool point_in_rect = event.IsTouchEvent() || event.IsGestureEvent() ? touch_rect.Contains(point) : mouse_rect.Contains(point); if (!point_in_rect) return false; auto shape_rects = GetExtraHitTestShapeRects(window); if (!shape_rects) return true; for (const gfx::Rect& shape_rect : *shape_rects) { if (shape_rect.Contains(point)) { return true; } } return false; } bool WindowTargeter::ShouldUseExtendedBounds(const aura::Window* window) const { return true; } void WindowTargeter::OnSetInsets(const gfx::Insets& last_mouse_extend, const gfx::Insets& last_touch_extend) {} Window* WindowTargeter::FindTargetForKeyEvent(Window* window, const ui::KeyEvent& key) { Window* root_window = window->GetRootWindow(); client::FocusClient* focus_client = client::GetFocusClient(root_window); if (!focus_client) return window; Window* focused_window = focus_client->GetFocusedWindow(); if (!focused_window) return window; client::EventClient* event_client = client::GetEventClient(root_window); if (event_client && !event_client->CanProcessEventsWithinSubtree(focused_window)) { focus_client->FocusWindow(nullptr); return nullptr; } return focused_window ? focused_window : window; } Window* WindowTargeter::FindTargetForNonKeyEvent(Window* root_window, ui::Event* event) { if (!event->IsLocatedEvent()) return root_window; return FindTargetForLocatedEvent(root_window, static_cast(event)); } Window* WindowTargeter::FindTargetForLocatedEventRecursively( Window* root_window, ui::LocatedEvent* event) { std::unique_ptr iter = root_window->GetChildIterator(); if (iter) { ui::EventTarget* target = root_window; for (ui::EventTarget* child = iter->GetNextTarget(); child; child = iter->GetNextTarget()) { WindowTargeter* targeter = static_cast(child->GetEventTargeter()); if (!targeter) targeter = this; if (!targeter->SubtreeShouldBeExploredForEvent( static_cast(child), *event)) { continue; } target->ConvertEventToTarget(child, event); target = child; Window* child_target_window = static_cast(targeter->FindTargetForEvent(child, event)); if (child_target_window) return child_target_window; } target->ConvertEventToTarget(root_window, event); } return root_window->CanAcceptEvent(*event) ? root_window : nullptr; } } // namespace aura