blob: 219ee7617aebb5e9f646b707d397cd5617700f0b [file] [log] [blame]
// Copyright 2018 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 "services/ws/topmost_window_observer.h"
#include "services/ws/window_service.h"
#include "services/ws/window_service_delegate.h"
#include "services/ws/window_tree.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_tree_host.h"
#include "ui/events/event.h"
#include "ui/events/gestures/gesture_recognizer.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/wm/core/coordinate_conversion.h"
namespace ws {
TopmostWindowObserver::TopmostWindowObserver(WindowTree* window_tree,
mojom::MoveLoopSource source,
aura::Window* initial_target)
: window_tree_(window_tree),
source_(source),
last_target_(initial_target),
env_(initial_target->env()) {
std::set<ui::EventType> types;
if (source == mojom::MoveLoopSource::MOUSE) {
types.insert(ui::ET_MOUSE_MOVED);
types.insert(ui::ET_MOUSE_DRAGGED);
last_location_ = env_->last_mouse_location();
} else {
types.insert(ui::ET_TOUCH_MOVED);
gfx::PointF point;
ui::GestureRecognizer* gesture_recognizer = env_->gesture_recognizer();
if (gesture_recognizer->GetLastTouchPointForTarget(last_target_, &point))
last_location_ = gfx::Point(point.x(), point.y());
::wm::ConvertPointToScreen(last_target_, &last_location_);
}
env_->AddEventObserver(this, env_, types);
UpdateTopmostWindows();
}
TopmostWindowObserver::~TopmostWindowObserver() {
env_->RemoveEventObserver(this);
if (topmost_)
topmost_->RemoveObserver(this);
if (real_topmost_ && topmost_ != real_topmost_)
real_topmost_->RemoveObserver(this);
}
void TopmostWindowObserver::OnEvent(const ui::Event& event) {
CHECK(event.IsLocatedEvent());
last_target_ = static_cast<aura::Window*>(event.target());
last_location_ = event.AsLocatedEvent()->location();
::wm::ConvertPointToScreen(last_target_, &last_location_);
UpdateTopmostWindows();
}
void TopmostWindowObserver::OnWindowVisibilityChanged(aura::Window* window,
bool visible) {
if (visible)
return;
if (!window->Contains(topmost_) && !window->Contains(real_topmost_))
return;
UpdateTopmostWindows();
}
void TopmostWindowObserver::OnWindowHierarchyChanged(
const HierarchyChangeParams& params) {
UpdateTopmostWindows();
}
void TopmostWindowObserver::OnWindowBoundsChanged(
aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
ui::PropertyChangeReason reason) {
gfx::Rect screen_bounds = new_bounds;
::wm::ConvertRectToScreen(window->parent(), &screen_bounds);
if (!screen_bounds.Contains(last_location_))
UpdateTopmostWindows();
}
void TopmostWindowObserver::UpdateTopmostWindows() {
std::set<aura::Window*> ignore;
ignore.insert(last_target_);
aura::Window* real_topmost = nullptr;
aura::Window* topmost =
window_tree_->window_service()->delegate()->GetTopmostWindowAtPoint(
last_location_, ignore, &real_topmost);
if (topmost == topmost_ && real_topmost == real_topmost_)
return;
// Since |topmost_| and |real_topmost_| could be same, updating observation
// for those windows is really complicated. To simplify the logic, here always
// removes this from the old windows and then adds to the new windows. This
// means removing and adding can happen on the same window when |topmost_| or
// |real_topmost_| are same. See topmost_window_observer_unittest.cc for the
// corner cases of the updates.
if (topmost_)
topmost_->RemoveObserver(this);
if (real_topmost_ && real_topmost_ != topmost_)
real_topmost_->RemoveObserver(this);
topmost_ = topmost;
real_topmost_ = real_topmost;
if (topmost_)
topmost_->AddObserver(this);
if (real_topmost_ && real_topmost_ != topmost_)
real_topmost_->AddObserver(this);
std::vector<aura::Window*> windows;
if (real_topmost_)
windows.push_back(real_topmost_);
if (topmost_ && topmost_ != real_topmost_)
windows.push_back(topmost_);
window_tree_->SendTopmostWindows(windows);
}
} // namespace ws