help_bubble_factory_views.cc 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/user_education/views/help_bubble_factory_views.h"
  5. #include <memory>
  6. #include "base/logging.h"
  7. #include "base/memory/ptr_util.h"
  8. #include "components/user_education/common/help_bubble_params.h"
  9. #include "components/user_education/common/user_education_class_properties.h"
  10. #include "components/user_education/views/help_bubble_delegate.h"
  11. #include "components/user_education/views/help_bubble_view.h"
  12. #include "third_party/abseil-cpp/absl/types/optional.h"
  13. #include "ui/base/interaction/element_identifier.h"
  14. #include "ui/base/interaction/element_tracker.h"
  15. #include "ui/views/accessible_pane_view.h"
  16. #include "ui/views/bubble/bubble_dialog_delegate_view.h"
  17. #include "ui/views/interaction/element_tracker_views.h"
  18. #include "ui/views/view_utils.h"
  19. namespace user_education {
  20. DEFINE_FRAMEWORK_SPECIFIC_METADATA(HelpBubbleViews)
  21. DEFINE_FRAMEWORK_SPECIFIC_METADATA(HelpBubbleFactoryViews)
  22. HelpBubbleViews::HelpBubbleViews(HelpBubbleView* help_bubble_view)
  23. : help_bubble_view_(help_bubble_view) {
  24. DCHECK(help_bubble_view);
  25. DCHECK(help_bubble_view->GetWidget());
  26. scoped_observation_.Observe(help_bubble_view->GetWidget());
  27. }
  28. HelpBubbleViews::~HelpBubbleViews() {
  29. // Needs to be called here while we still have access to HelpBubbleViews-
  30. // specific logic.
  31. Close();
  32. }
  33. bool HelpBubbleViews::ToggleFocusForAccessibility() {
  34. // // If the bubble isn't present or can't be meaningfully focused, stop.
  35. if (!help_bubble_view_)
  36. return false;
  37. // If the focus isn't in the help bubble, focus the help bubble.
  38. // Note that if is_focus_in_ancestor_widget is true, then anchor both exists
  39. // and has a widget, so anchor->GetWidget() will always be valid.
  40. if (!help_bubble_view_->IsFocusInHelpBubble()) {
  41. help_bubble_view_->GetWidget()->Activate();
  42. help_bubble_view_->RequestFocus();
  43. return true;
  44. }
  45. auto* const anchor = help_bubble_view_->GetAnchorView();
  46. if (!anchor)
  47. return false;
  48. bool set_focus = false;
  49. if (anchor->IsAccessibilityFocusable()) {
  50. #if BUILDFLAG(IS_MAC)
  51. // Mac does not automatically pass activation on focus, so we have to do it
  52. // manually.
  53. anchor->GetWidget()->Activate();
  54. #else
  55. // Focus the anchor. We can't request focus for an accessibility-only view
  56. // until we turn on keyboard accessibility for its focus manager.
  57. anchor->GetFocusManager()->SetKeyboardAccessible(true);
  58. #endif
  59. anchor->RequestFocus();
  60. set_focus = true;
  61. } else if (views::IsViewClass<views::AccessiblePaneView>(anchor)) {
  62. // An AccessiblePaneView can receive focus, but is not necessarily itself
  63. // accessibility focusable. Use the built-in functionality for focusing
  64. // elements of AccessiblePaneView instead.
  65. #if BUILDFLAG(IS_MAC)
  66. // Mac does not automatically pass activation on focus, so we have to do it
  67. // manually.
  68. anchor->GetWidget()->Activate();
  69. #else
  70. // You can't focus an accessible pane if it's already in accessibility
  71. // mode, so avoid doing that; the SetPaneFocus() call will go back into
  72. // accessibility navigation mode.
  73. anchor->GetFocusManager()->SetKeyboardAccessible(false);
  74. #endif
  75. set_focus =
  76. static_cast<views::AccessiblePaneView*>(anchor)->SetPaneFocus(nullptr);
  77. }
  78. return set_focus;
  79. }
  80. void HelpBubbleViews::OnAnchorBoundsChanged() {
  81. if (help_bubble_view_)
  82. help_bubble_view_->OnAnchorBoundsChanged();
  83. }
  84. gfx::Rect HelpBubbleViews::GetBoundsInScreen() const {
  85. return help_bubble_view_
  86. ? help_bubble_view_->GetWidget()->GetWindowBoundsInScreen()
  87. : gfx::Rect();
  88. }
  89. ui::ElementContext HelpBubbleViews::GetContext() const {
  90. return help_bubble_view_
  91. ? views::ElementTrackerViews::GetContextForView(help_bubble_view_)
  92. : ui::ElementContext();
  93. }
  94. bool HelpBubbleViews::AcceleratorPressed(const ui::Accelerator& accelerator) {
  95. if (CanHandleAccelerators()) {
  96. ToggleFocusForAccessibility();
  97. return true;
  98. }
  99. return false;
  100. }
  101. bool HelpBubbleViews::CanHandleAccelerators() const {
  102. return help_bubble_view_ && help_bubble_view_->GetWidget() &&
  103. help_bubble_view_->GetWidget()->IsActive();
  104. }
  105. void HelpBubbleViews::MaybeResetAnchorView() {
  106. if (!help_bubble_view_)
  107. return;
  108. auto* const anchor_view = help_bubble_view_->GetAnchorView();
  109. if (!anchor_view)
  110. return;
  111. anchor_view->SetProperty(kHasInProductHelpPromoKey, false);
  112. }
  113. void HelpBubbleViews::CloseBubbleImpl() {
  114. if (!help_bubble_view_)
  115. return;
  116. scoped_observation_.Reset();
  117. MaybeResetAnchorView();
  118. help_bubble_view_->GetWidget()->Close();
  119. help_bubble_view_ = nullptr;
  120. }
  121. void HelpBubbleViews::OnWidgetDestroying(views::Widget* widget) {
  122. scoped_observation_.Reset();
  123. MaybeResetAnchorView();
  124. help_bubble_view_ = nullptr;
  125. NotifyBubbleClosed();
  126. }
  127. HelpBubbleFactoryViews::HelpBubbleFactoryViews(
  128. const HelpBubbleDelegate* delegate)
  129. : delegate_(delegate) {
  130. DCHECK(delegate_);
  131. }
  132. HelpBubbleFactoryViews::~HelpBubbleFactoryViews() = default;
  133. std::unique_ptr<HelpBubble> HelpBubbleFactoryViews::CreateBubble(
  134. ui::TrackedElement* element,
  135. HelpBubbleParams params) {
  136. views::View* const anchor_view =
  137. element->AsA<views::TrackedElementViews>()->view();
  138. anchor_view->SetProperty(kHasInProductHelpPromoKey, true);
  139. auto result = base::WrapUnique(new HelpBubbleViews(
  140. new HelpBubbleView(delegate_, anchor_view, std::move(params))));
  141. for (const auto& accelerator :
  142. delegate_->GetPaneNavigationAccelerators(element)) {
  143. result->bubble_view()->GetFocusManager()->RegisterAccelerator(
  144. accelerator, ui::AcceleratorManager::HandlerPriority::kNormalPriority,
  145. result.get());
  146. }
  147. return result;
  148. }
  149. bool HelpBubbleFactoryViews::CanBuildBubbleForTrackedElement(
  150. const ui::TrackedElement* element) const {
  151. return element->IsA<views::TrackedElementViews>();
  152. }
  153. } // namespace user_education