| // 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/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); | 
 |  | 
 |   // The tooltip icon, despite visually being an icon with no text, actually | 
 |   // opens a bubble whenever the user mouses over it or focuses it, so it's | 
 |   // essentially a text control that hides itself when not in view without | 
 |   // altering the bubble's layout when shown. As such, have it behave like | 
 |   // static text for screenreader users, since that's the role it serves here | 
 |   // anyway. | 
 |   SetAccessibilityProperties(ax::mojom::Role::kStaticText, tooltip_); | 
 | } | 
 |  | 
 | TooltipIcon::~TooltipIcon() { | 
 |   for (auto& observer : observers_) | 
 |     observer.OnTooltipIconDestroying(this); | 
 |   HideBubble(); | 
 | } | 
 |  | 
 | 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 | 
 |   NotifyAccessibilityEvent(ax::mojom::Event::kFocus, true); | 
 | #endif | 
 | } | 
 |  | 
 | void TooltipIcon::OnBlur() { | 
 |   HideBubble(); | 
 | } | 
 |  | 
 | void TooltipIcon::OnGestureEvent(ui::GestureEvent* event) { | 
 |   if (event->type() == ui::ET_GESTURE_TAP) { | 
 |     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()); | 
 |   } | 
 |  | 
 |   for (auto& observer : observers_) | 
 |     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, ImageView) | 
 | END_METADATA | 
 |  | 
 | }  // namespace views |