chromium/ash/display/display_highlight_controller.cc

// Copyright 2020 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/display/display_highlight_controller.h"

#include "ash/accessibility/magnifier/fullscreen_magnifier_controller.h"
#include "ash/display/window_tree_host_manager.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_type.h"
#include "ui/compositor/paint_recorder.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/manager/display_manager_observer.h"
#include "ui/gfx/color_palette.h"
#include "ui/views/border.h"
#include "ui/wm/core/window_animations.h"

namespace ash {

namespace {

constexpr SkColor kHighlightColor = gfx::kGoogleBlue600;
constexpr int kHighlightSizeFactor = 128;

std::unique_ptr<views::Widget> CreateHighlightWidget(
    const display::Display& display) {
  const int64_t display_id = display.id();

  DCHECK_NE(display_id, display::kInvalidDisplayId);

  views::Widget::InitParams params(
      views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET,
      views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
  params.activatable = views::Widget::InitParams::Activatable::kNo;
  params.accept_events = false;
  params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;

  aura::Window* root = Shell::GetRootWindowForDisplayId(display_id);

  params.parent = root->GetChildById(kShellWindowId_ScreenAnimationContainer);
  params.bounds = root->GetBoundsInRootWindow();
  params.name = "DisplayIdentificationHighlight";

  std::unique_ptr<views::Widget> highlight_widget =
      std::make_unique<views::Widget>();

  const int highlight_thickness =
      std::max(params.bounds.width(), params.bounds.height()) /
      kHighlightSizeFactor;

  highlight_widget->Init(std::move(params));

  highlight_widget->GetRootView()->SetBorder(
      views::CreateSolidBorder(highlight_thickness, kHighlightColor));

  auto* window = highlight_widget->GetNativeWindow();
  window->SetId(kShellWindowId_DisplayIdentificationHighlightWindow);
  ::wm::SetWindowVisibilityAnimationTransition(window, ::wm::ANIMATE_NONE);

  FullscreenMagnifierController* magnification_controller =
      Shell::Get()->fullscreen_magnifier_controller();

  // Forces a redraw of full-screen magnification in order to reverse
  // magnification on display highlight window performed in
  // FullscreenMagnifierController::ReDraw(). If redraw is not forced, then the
  // highlight may not show up around the edges of the display properly until
  // the next redraw.
  if (magnification_controller->IsEnabled()) {
    magnification_controller->MoveWindow(
        magnification_controller->GetWindowPosition(), false);
  }

  highlight_widget->Show();

  return highlight_widget;
}

}  // namespace

DisplayHighlightController::DisplayHighlightController() {
  Shell* shell = Shell::Get();
  SessionControllerImpl* session_controller = shell->session_controller();

  session_controller->AddObserver(this);
  shell->display_manager()->AddDisplayManagerObserver(this);

  is_locked_ = session_controller->IsScreenLocked();
}

DisplayHighlightController::~DisplayHighlightController() {
  Shell* shell = Shell::Get();

  shell->display_manager()->RemoveDisplayManagerObserver(this);
  shell->session_controller()->RemoveObserver(this);
}

void DisplayHighlightController::UpdateDisplayIdentificationHighlight() {
  if (selected_display_id_ == display::kInvalidDisplayId) {
    highlight_widget_.reset();
    return;
  }

  display::DisplayManager* display_manager = Shell::Get()->display_manager();

  // If |selected_display_id_| does not correspond to an active display, we
  // cannot display highlights.
  if (!display_manager->IsActiveDisplayId(selected_display_id_)) {
    highlight_widget_.reset();
    return;
  }

  // If there is only one display, we don't need to show a special highlight for
  // it since there is only one place for the user to look.
  if (display_manager->GetNumDisplays() == 1) {
    highlight_widget_.reset();
    return;
  }

  // Don't show a highlight if the device is locked to ensure that the highlight
  // does not appear on the login screen.
  if (is_locked_) {
    highlight_widget_.reset();
    return;
  }

  highlight_widget_ = CreateHighlightWidget(
      display_manager->GetDisplayForId(selected_display_id_));
}

void DisplayHighlightController::OnLockStateChanged(bool locked) {
  is_locked_ = locked;
  UpdateDisplayIdentificationHighlight();
}

void DisplayHighlightController::OnDidApplyDisplayChanges() {
  UpdateDisplayIdentificationHighlight();
}

void DisplayHighlightController::OnDisplaysInitialized() {
  UpdateDisplayIdentificationHighlight();
}

void DisplayHighlightController::SetHighlightedDisplay(int64_t display_id) {
  if (selected_display_id_ != display_id) {
    selected_display_id_ = display_id;
    UpdateDisplayIdentificationHighlight();
  }
}

}  // namespace ash