910e62b5创建于 1月15日历史提交
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/bubble/tooltip_icon.h"

#include "base/observer_list.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/color/color_id.h"
#include "ui/color/color_provider.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/bubble/info_bubble.h"
#include "ui/views/controls/focus_ring.h"
#include "ui/views/controls/highlight_path_generator.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/mouse_watcher_view_host.h"
#include "ui/views/style/platform_style.h"

namespace views {

TooltipIcon::TooltipIcon(const std::u16string& tooltip, int tooltip_icon_size)
    : tooltip_(tooltip),
      tooltip_icon_size_(tooltip_icon_size),

      bubble_(nullptr) {
  SetFocusBehavior(PlatformStyle::kDefaultFocusBehavior);
  set_suppress_default_focus_handling();
  FocusRing::Install(this);
  SetBorder(CreateEmptyBorder(
      LayoutProvider::Get()->GetInsetsMetric(INSETS_VECTOR_IMAGE_BUTTON)));
  InstallCircleHighlightPathGenerator(this);

  // Setting the accessible role to kTooltip allows the tooltip icon to be
  // announced by screen readers when it receives focus although it essentially
  // acts as a static text label.
  GetViewAccessibility().SetRole(ax::mojom::Role::kTooltip);
  GetViewAccessibility().SetName(tooltip_);
}

TooltipIcon::~TooltipIcon() {
  observers_.Notify(&Observer::OnTooltipIconDestroying, this);
  HideBubble();
}

void TooltipIcon::SetBubbleWidth(int preferred_width) {
  preferred_width_ = preferred_width;
  OnPropertyChanged(&preferred_width_, PropertyEffects::kPreferredSizeChanged);
}

int TooltipIcon::GetBubbleWidth() const {
  return preferred_width_;
}

void TooltipIcon::SetAnchorPointArrow(BubbleBorder::Arrow arrow) {
  anchor_point_arrow_ = arrow;
  OnPropertyChanged(&anchor_point_arrow_, PropertyEffects::kPaint);
}

BubbleBorder::Arrow TooltipIcon::GetAnchorPointArrow() const {
  return anchor_point_arrow_;
}

void TooltipIcon::OnMouseEntered(const ui::MouseEvent& event) {
  mouse_inside_ = true;
  show_timer_.Start(FROM_HERE, base::Milliseconds(150), this,
                    &TooltipIcon::ShowBubble);
}

void TooltipIcon::OnMouseExited(const ui::MouseEvent& event) {
  show_timer_.Stop();
}

bool TooltipIcon::OnMousePressed(const ui::MouseEvent& event) {
  // Swallow the click so that the parent doesn't process it.
  return true;
}

void TooltipIcon::OnFocus() {
  ShowBubble();
#if BUILDFLAG(IS_WIN)
  // Tooltip text does not announce on Windows; crbug.com/1245470
  NotifyAccessibilityEventDeprecated(ax::mojom::Event::kFocus, true);
#endif
}

void TooltipIcon::OnBlur() {
  HideBubble();
}

void TooltipIcon::OnGestureEvent(ui::GestureEvent* event) {
  if (event->type() == ui::EventType::kGestureTap) {
    ShowBubble();
    event->SetHandled();
  }
}

void TooltipIcon::OnThemeChanged() {
  ImageView::OnThemeChanged();
  SetDrawAsHovered(false);
}

void TooltipIcon::MouseMovedOutOfHost() {
  if (IsMouseHovered()) {
    mouse_watcher_->Start(GetWidget()->GetNativeWindow());
    return;
  }

  mouse_inside_ = false;
  HideBubble();
}

void TooltipIcon::AddObserver(Observer* observer) {
  observers_.AddObserver(observer);
}

void TooltipIcon::RemoveObserver(Observer* observer) {
  observers_.RemoveObserver(observer);
}

void TooltipIcon::SetDrawAsHovered(bool hovered) {
  SetImage(ui::ImageModel::FromVectorIcon(
      vector_icons::kInfoOutlineIcon,
      GetColorProvider()->GetColor(hovered ? ui::kColorHelpIconActive
                                           : ui::kColorHelpIconInactive),
      tooltip_icon_size_));
}

void TooltipIcon::ShowBubble() {
  if (bubble_) {
    return;
  }

  SetDrawAsHovered(true);

  bubble_ = new InfoBubble(this, anchor_point_arrow_, tooltip_);
  bubble_->set_preferred_width(preferred_width_);
  // When shown due to a gesture event, close on deactivate (i.e. don't use
  // "focusless").
  bubble_->SetCanActivate(!mouse_inside_);

  bubble_->Show();
  observation_.Observe(bubble_->GetWidget());

  if (mouse_inside_) {
    View* frame = bubble_->GetWidget()->non_client_view()->frame_view();
    mouse_watcher_ = std::make_unique<MouseWatcher>(
        std::make_unique<MouseWatcherViewHost>(frame, gfx::Insets()), this);
    mouse_watcher_->Start(GetWidget()->GetNativeWindow());
  }

  observers_.Notify(&Observer::OnTooltipBubbleShown, this);
}

void TooltipIcon::HideBubble() {
  if (bubble_) {
    bubble_->Hide();
  }
}

void TooltipIcon::OnWidgetDestroyed(Widget* widget) {
  DCHECK(observation_.IsObservingSource(widget));
  observation_.Reset();

  SetDrawAsHovered(false);
  mouse_watcher_.reset();
  bubble_ = nullptr;
}

BEGIN_METADATA(TooltipIcon)
ADD_PROPERTY_METADATA(int, BubbleWidth)
ADD_PROPERTY_METADATA(BubbleBorder::Arrow, AnchorPointArrow)
END_METADATA

}  // namespace views