File: facegaze_bubble_controller.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (130 lines) | stat: -rw-r--r-- 4,545 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// 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/system/accessibility/facegaze_bubble_controller.h"

#include "ash/system/accessibility/facegaze_bubble_view.h"
#include "ash/wm/collision_detection/collision_detection_utils.h"
#include "base/functional/bind.h"
#include "ui/display/screen.h"
#include "ui/events/event.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/widget/widget.h"

namespace ash {

namespace {
constexpr int kBubbleViewOutsets = 25;
constexpr int kMarginFromTopDip = 8;
constexpr base::TimeDelta kShowTimeout = base::Seconds(1);
}  // namespace

FaceGazeBubbleController::FaceGazeBubbleController(
    const base::RepeatingCallback<void()>& on_close_button_clicked)
    : on_close_button_clicked_(std::move(on_close_button_clicked)) {}

FaceGazeBubbleController::~FaceGazeBubbleController() {
  show_timer_.Stop();
  if (widget_ && !widget_->IsClosed()) {
    widget_->CloseNow();
  }
}

void FaceGazeBubbleController::OnViewIsDeleting(views::View* observed_view) {
  if (observed_view != facegaze_bubble_view_) {
    return;
  }

  show_timer_.Stop();
  facegaze_bubble_view_->views::View::RemoveObserver(this);
  facegaze_bubble_view_ = nullptr;
  widget_ = nullptr;
}

void FaceGazeBubbleController::UpdateBubble(const std::u16string& text,
                                            bool is_warning) {
  MaybeInitialize();
  Update(text, is_warning);
  if (!show_timer_.IsRunning()) {
    widget_->Show();
  }
}

void FaceGazeBubbleController::MaybeInitialize() {
  if (widget_) {
    return;
  }

  facegaze_bubble_view_ = new FaceGazeBubbleView(
      base::BindRepeating(&FaceGazeBubbleController::OnMouseEntered,
                          GetWeakPtr()),
      base::BindRepeating(&FaceGazeBubbleController::OnCloseButtonClicked,
                          GetWeakPtr()));
  facegaze_bubble_view_->views::View::AddObserver(this);

  widget_ =
      views::BubbleDialogDelegateView::CreateBubble(facegaze_bubble_view_);
  CollisionDetectionUtils::MarkWindowPriorityForCollisionDetection(
      widget_->GetNativeWindow(),
      CollisionDetectionUtils::RelativePriority::kFaceGazeBubble);
}

void FaceGazeBubbleController::Update(const std::u16string& text,
                                      bool is_warning) {
  if (!facegaze_bubble_view_) {
    return;
  }

  facegaze_bubble_view_->Update(text, is_warning);

  const gfx::Rect primary_work_area =
      display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
  const gfx::Size work_area_size = primary_work_area.size();
  const gfx::Size bubble_size = facegaze_bubble_view_->size();

  // The bubble should be centered at the top of the screen, factoring in other
  // UI elements such as the ChromeVox panel. Note that the work area may not
  // always start at (0, 0) so we need to factor in the starting point of the
  // work area.
  int center = (work_area_size.width() / 2) - (bubble_size.width() / 2) +
               primary_work_area.x();
  int top = primary_work_area.y() + kMarginFromTopDip;
  facegaze_bubble_view_->SetAnchorRect(gfx::Rect(center, top, 0, 0));
}

void FaceGazeBubbleController::OnMouseEntered() {
  widget_->Hide();
  show_timer_.Start(FROM_HERE, kShowTimeout,
                    base::BindRepeating(&FaceGazeBubbleController::OnShowTimer,
                                        GetWeakPtr()));
}

void FaceGazeBubbleController::OnCloseButtonClicked(const ui::Event& event) {
  on_close_button_clicked_.Run();
}

void FaceGazeBubbleController::OnShowTimer() {
  gfx::Point cursor_location =
      display::Screen::GetScreen()->GetCursorScreenPoint();
  // Expand the FaceGazeBubbleView bounds by 25 pixels in each direction.
  // This provides a cushion so that we don't show the UI when the user is
  // trying to click on an element that is a few pixels outside of the original
  // bounds.
  gfx::Rect scaled_bounds = facegaze_bubble_view_->GetBoundsInScreen();
  scaled_bounds.Outset(kBubbleViewOutsets);
  if (scaled_bounds.Contains(cursor_location)) {
    // Though we hide FaceGazeBubble view only if the main content is hovered,
    // we continue to hide it if the mouse is contained by the entire bounds of
    // the view. This is to allow users to click on elements occluded by
    // FaceGazeBubbleView.
    OnMouseEntered();
    return;
  }

  // If the mouse cursor isn't contained by the bubble, then we can show it.
  widget_->Show();
}

}  // namespace ash