blob: ad1a15000a7306adbf90cd0b3c1899c865a04ca4 [file] [log] [blame]
// Copyright 2016 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 "ash/laser/laser_pointer_controller.h"
#include "ash/common/system/chromeos/palette/palette_utils.h"
#include "ash/laser/laser_pointer_view.h"
#include "ash/shell.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_tree_host.h"
#include "ui/display/screen.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
// A point gets removed from the collection if it is older than
// |kPointLifeDurationMs|.
const int kPointLifeDurationMs = 200;
// When no move events are being received we add a new point every
// |kAddStationaryPointsDelayMs| so that points older than
// |kPointLifeDurationMs| can get removed.
const int kAddStationaryPointsDelayMs = 5;
aura::Window* GetCurrentRootWindow() {
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
for (aura::Window* root_window : root_windows) {
if (root_window->ContainsPointInRoot(
root_window->GetHost()->dispatcher()->GetLastMouseLocationInRoot()))
return root_window;
}
return nullptr;
}
} // namespace
LaserPointerController::LaserPointerController()
: stationary_timer_(new base::Timer(
FROM_HERE,
base::TimeDelta::FromMilliseconds(kAddStationaryPointsDelayMs),
base::Bind(&LaserPointerController::AddStationaryPoint,
base::Unretained(this)),
true /* is_repeating */)) {}
LaserPointerController::~LaserPointerController() {
Shell::GetInstance()->RemovePreTargetHandler(this);
}
void LaserPointerController::SetEnabled(bool enabled) {
if (enabled == enabled_)
return;
enabled_ = enabled;
if (enabled_) {
Shell::GetInstance()->AddPreTargetHandler(this);
} else {
laser_pointer_view_.reset();
Shell::GetInstance()->RemovePreTargetHandler(this);
}
}
void LaserPointerController::OnMouseEvent(ui::MouseEvent* event) {
if (!enabled_)
return;
if (event->pointer_details().pointer_type !=
ui::EventPointerType::POINTER_TYPE_PEN)
return;
if (event->type() != ui::ET_MOUSE_DRAGGED &&
event->type() != ui::ET_MOUSE_PRESSED &&
event->type() != ui::ET_MOUSE_RELEASED)
return;
aura::Window* current_window = GetCurrentRootWindow();
if (!current_window) {
DestroyLaserPointerView();
return;
}
// Compute the event coordinate relative to the display it is currently on
// (and not the one the event was captured on).
gfx::Point event_location = event->root_location();
aura::Window* target = static_cast<aura::Window*>(event->target());
aura::Window* event_root = target->GetRootWindow();
aura::Window::ConvertPointToTarget(event_root, current_window,
&event_location);
// Start a new laser session if the mouse is pressed but not pressed over the
// palette.
if (event->type() == ui::ET_MOUSE_PRESSED &&
!PaletteContainsPointInScreen(event_location)) {
DestroyLaserPointerView();
UpdateLaserPointerView(current_window, event_location, event);
}
// Do not update laser if it is in the process of fading away.
if (event->type() == ui::ET_MOUSE_DRAGGED && laser_pointer_view_ &&
!is_fading_away_) {
UpdateLaserPointerView(current_window, event_location, event);
RestartTimer();
}
if (event->type() == ui::ET_MOUSE_RELEASED && laser_pointer_view_ &&
!is_fading_away_) {
is_fading_away_ = true;
UpdateLaserPointerView(current_window, event_location, event);
RestartTimer();
}
}
void LaserPointerController::OnWindowDestroying(aura::Window* window) {
SwitchTargetRootWindowIfNeeded(window);
}
void LaserPointerController::SwitchTargetRootWindowIfNeeded(
aura::Window* root_window) {
if (!root_window) {
DestroyLaserPointerView();
} else if (laser_pointer_view_) {
laser_pointer_view_->ReparentWidget(root_window);
} else if (enabled_) {
laser_pointer_view_.reset(new LaserPointerView(
base::TimeDelta::FromMilliseconds(kPointLifeDurationMs), root_window));
}
}
void LaserPointerController::UpdateLaserPointerView(
aura::Window* current_window,
const gfx::Point& event_location,
ui::MouseEvent* event) {
SwitchTargetRootWindowIfNeeded(current_window);
current_mouse_location_ = event_location;
laser_pointer_view_->AddNewPoint(current_mouse_location_);
event->StopPropagation();
}
void LaserPointerController::DestroyLaserPointerView() {
// |stationary_timer_| should also be stopped so that it does not attempt to
// add points when |laser_pointer_view_| is null.
stationary_timer_->Stop();
if (laser_pointer_view_) {
is_fading_away_ = false;
laser_pointer_view_.reset();
}
}
void LaserPointerController::RestartTimer() {
stationary_timer_repeat_count_ = 0;
if (!stationary_timer_->IsRunning())
stationary_timer_->Reset();
}
void LaserPointerController::AddStationaryPoint() {
if (is_fading_away_)
laser_pointer_view_->UpdateTime();
else
laser_pointer_view_->AddNewPoint(current_mouse_location_);
// We can stop repeating the timer once the mouse has been stationary for
// longer than the life of a point.
if (stationary_timer_repeat_count_ * kAddStationaryPointsDelayMs >=
kPointLifeDurationMs) {
stationary_timer_->Stop();
// Reset the view if the timer expires and the view was in process of fading
// away.
if (is_fading_away_)
DestroyLaserPointerView();
}
stationary_timer_repeat_count_++;
}
} // namespace ash