chromium/ash/game_dashboard/game_dashboard_main_menu_cursor_handler.cc

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/game_dashboard/game_dashboard_main_menu_cursor_handler.h"

#include "ash/game_dashboard/game_dashboard_context.h"
#include "ash/shell.h"
#include "chromeos/ui/frame/frame_header.h"
#include "ui/wm/core/cursor_manager.h"

namespace ash {

GameDashboardMainMenuCursorHandler::GameDashboardMainMenuCursorHandler(
    GameDashboardContext* context)
    : context_(context), cursor_manager_(Shell::Get()->cursor_manager()) {
  CHECK(context_);
  CHECK(cursor_manager_);
  // Save current state.
  cursor_visibility_to_restore_ = cursor_manager_->IsCursorVisible();
  cursor_to_restore_ = cursor_manager_->GetCursor();
  // Set desired state.
  cursor_manager_->ShowCursor();
  cursor_manager_->SetCursor(ui::mojom::CursorType::kPointer);
  cursor_manager_->LockCursor();
}

GameDashboardMainMenuCursorHandler::~GameDashboardMainMenuCursorHandler() {
  cursor_manager_->UnlockCursor();
  // Restore state.
  cursor_visibility_to_restore_ ? cursor_manager_->ShowCursor()
                                : cursor_manager_->HideCursor();
  cursor_manager_->SetCursor(cursor_to_restore_);
}

void GameDashboardMainMenuCursorHandler::OnMouseEvent(ui::MouseEvent* event) {
  // Propagate the `event` if it's inside the window's
  // `FrameCaptionButtonContainerView`.
  if (!IsEventInWindowFrameHeader(*event)) {
    event->StopPropagation();
    event->SetHandled();
  }
}

bool GameDashboardMainMenuCursorHandler::IsEventInWindowFrameHeader(
    const ui::LocatedEvent& event) {
  // TODO(b/324268128): Update the logic to handle a LaCrOS window's
  // FrameHeader.
  if (auto* frame_header = chromeos::FrameHeader::Get(
          views::Widget::GetWidgetForNativeWindow(context_->game_window()))) {
    const auto* frame_header_view = frame_header->view();
    // For apps that use pointer lock, `event.target()` always returns the
    // window that has acquired the pointer lock. This pretarget handler cannot
    // reliably figure out whether the mouse cursor is located over the game
    // window's frame header or not. As a workaround, the logic is using the
    // event's screen location to determine whether the mouse event is within
    // the painted region of the frame header.
    const auto frame_header_painted_region =
        gfx::Rect(frame_header_view->GetBoundsInScreen().origin(),
                  gfx::Size(frame_header_view->width(),
                            frame_header->GetHeaderHeightForPainting()));
    return frame_header_painted_region.Contains(
        event.target()->GetScreenLocation(event));
  }
  return false;
}

}  // namespace ash