#include "ash/fast_ink/laser/laser_pointer_controller.h"
#include <memory>
#include "ash/fast_ink/fast_ink_pointer_controller.h"
#include "ash/fast_ink/laser/laser_pointer_view.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/shell.h"
#include "ash/system/palette/palette_utils.h"
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "ui/display/screen.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/types/event_type.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/coordinate_conversion.h"
namespace ash {
namespace {
const int kPointLifeDurationMs = 200;
const int kAddStationaryPointsDelayMs = 16;
}
class LaserPointerController::ScopedLockedHiddenCursor {
public:
ScopedLockedHiddenCursor() : cursor_manager_(Shell::Get()->cursor_manager()) {
DCHECK(cursor_manager_);
cursor_manager_->HideCursor();
cursor_manager_->LockCursor();
}
~ScopedLockedHiddenCursor() {
cursor_manager_->UnlockCursor();
}
private:
const raw_ptr<wm::CursorManager> cursor_manager_;
};
LaserPointerController::LaserPointerController() {
Shell::Get()->AddPreTargetHandler(this);
}
LaserPointerController::~LaserPointerController() {
Shell::Get()->RemovePreTargetHandler(this);
}
void LaserPointerController::AddObserver(LaserPointerObserver* observer) {
observers_.AddObserver(observer);
}
void LaserPointerController::RemoveObserver(LaserPointerObserver* observer) {
observers_.RemoveObserver(observer);
}
void LaserPointerController::OnWindowBoundsChanged(
aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
ui::PropertyChangeReason reason) {
DCHECK_EQ(window, root_window_observation_.GetSource());
DestroyPointerView();
}
void LaserPointerController::OnWindowDestroyed(aura::Window* window) {
DCHECK_EQ(window, root_window_observation_.GetSource());
root_window_observation_.Reset();
}
void LaserPointerController::SetEnabled(bool enabled) {
if (enabled == is_enabled())
return;
FastInkPointerController::SetEnabled(enabled);
if (!enabled) {
DestroyPointerView();
scoped_locked_hidden_cursor_.reset();
}
NotifyStateChanged(enabled);
}
views::View* LaserPointerController::GetPointerView() const {
return laser_pointer_view_widget_
? laser_pointer_view_widget_->GetContentsView()
: nullptr;
}
void LaserPointerController::CreatePointerView(
base::TimeDelta presentation_delay,
aura::Window* root_window) {
laser_pointer_view_widget_ = LaserPointerView::Create(
base::Milliseconds(kPointLifeDurationMs), presentation_delay,
base::Milliseconds(kAddStationaryPointsDelayMs),
Shell::GetContainer(root_window, kShellWindowId_OverlayContainer));
DCHECK(!root_window_observation_.IsObserving());
root_window_observation_.Observe(root_window);
}
void LaserPointerController::UpdatePointerView(ui::TouchEvent* event) {
LaserPointerView* laser_pointer_view = GetLaserPointerView();
if (IsPointerInExcludedWindows(event)) {
DestroyPointerView();
return;
}
if (event->type() != ui::EventType::kTouchCancelled) {
scoped_locked_hidden_cursor_.reset();
laser_pointer_view->AddNewPoint(event->root_location_f(),
event->time_stamp());
}
if (event->type() == ui::EventType::kTouchReleased ||
event->type() == ui::EventType::kTouchCancelled) {
laser_pointer_view->FadeOut(base::BindOnce(
&LaserPointerController::DestroyPointerView, base::Unretained(this)));
}
}
void LaserPointerController::UpdatePointerView(ui::MouseEvent* event) {
LaserPointerView* laser_pointer_view = GetLaserPointerView();
if (event->type() == ui::EventType::kMouseMoved) {
if (IsPointerInExcludedWindows(event)) {
DestroyPointerView();
scoped_locked_hidden_cursor_.reset();
return;
}
if (!scoped_locked_hidden_cursor_) {
scoped_locked_hidden_cursor_ =
std::make_unique<ScopedLockedHiddenCursor>();
}
}
laser_pointer_view->AddNewPoint(event->root_location_f(),
event->time_stamp());
if (event->type() == ui::EventType::kMouseReleased) {
laser_pointer_view->FadeOut(base::BindOnce(
&LaserPointerController::DestroyPointerView, base::Unretained(this)));
}
}
void LaserPointerController::DestroyPointerView() {
FastInkPointerController::DestroyPointerView();
root_window_observation_.Reset();
laser_pointer_view_widget_.reset();
}
bool LaserPointerController::CanStartNewGesture(ui::LocatedEvent* event) {
aura::Window* target = static_cast<aura::Window*>(event->target());
gfx::Point screen_point = event->location();
wm::ConvertPointToScreen(target, &screen_point);
if (palette_utils::PaletteContainsPointInScreen(screen_point))
return false;
return FastInkPointerController::CanStartNewGesture(event);
}
bool LaserPointerController::ShouldProcessEvent(ui::LocatedEvent* event) {
if (event->type() == ui::EventType::kMousePressed ||
event->type() == ui::EventType::kMouseReleased) {
return false;
}
return FastInkPointerController::ShouldProcessEvent(event);
}
void LaserPointerController::NotifyStateChanged(bool enabled) {
for (LaserPointerObserver& observer : observers_)
observer.OnLaserPointerStateChanged(enabled);
}
LaserPointerView* LaserPointerController::GetLaserPointerView() const {
return static_cast<LaserPointerView*>(GetPointerView());
}
}