| // Copyright 2021 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/dictation_bubble_controller.h" |
| |
| #include "ash/display/window_tree_host_manager.h" |
| #include "ash/public/cpp/accessibility_controller_enums.h" |
| #include "ash/shell.h" |
| #include "ash/system/accessibility/dictation_bubble_view.h" |
| #include "ash/wm/collision_detection/collision_detection_utils.h" |
| #include "ui/base/ime/text_input_client.h" |
| #include "ui/views/view.h" |
| #include "ui/views/widget/widget.h" |
| |
| namespace ash { |
| |
| DictationBubbleController::DictationBubbleController() { |
| ui::InputMethod* input_method = |
| Shell::Get()->window_tree_host_manager()->input_method(); |
| if (!input_method_observer_.IsObservingSource(input_method)) |
| input_method_observer_.Observe(input_method); |
| } |
| |
| DictationBubbleController::~DictationBubbleController() { |
| input_method_observer_.Reset(); |
| if (widget_ && !widget_->IsClosed()) |
| widget_->CloseNow(); |
| } |
| |
| void DictationBubbleController::UpdateBubble( |
| bool visible, |
| DictationBubbleIconType icon, |
| const std::optional<std::u16string>& text, |
| const std::optional<std::vector<DictationBubbleHintType>>& hints) { |
| MaybeInitialize(); |
| Update(icon, text, hints); |
| visible ? widget_->Show() : widget_->Hide(); |
| |
| for (Observer& observer : observers_) { |
| observer.OnBubbleUpdated(); |
| } |
| } |
| |
| void DictationBubbleController::OnCaretBoundsChanged( |
| const ui::TextInputClient* client) { |
| if (!client || client->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || |
| !dictation_bubble_view_ || !dictation_bubble_view_->GetVisible()) { |
| return; |
| } |
| |
| const gfx::Rect new_caret_bounds = client->GetCaretBounds(); |
| if (new_caret_bounds == dictation_bubble_view_->GetAnchorRect()) |
| return; |
| |
| // Update the position of `dictation_bubble_view_` to match the current caret |
| // location. |
| dictation_bubble_view_->SetAnchorRect(new_caret_bounds); |
| } |
| |
| void DictationBubbleController::OnViewIsDeleting(views::View* observed_view) { |
| if (observed_view != dictation_bubble_view_) |
| return; |
| dictation_bubble_view_->views::View::RemoveObserver(this); |
| dictation_bubble_view_ = nullptr; |
| widget_ = nullptr; |
| } |
| |
| void DictationBubbleController::MaybeInitialize() { |
| if (widget_) |
| return; |
| |
| dictation_bubble_view_ = new DictationBubbleView(); |
| dictation_bubble_view_->views::View::AddObserver(this); |
| |
| widget_ = |
| views::BubbleDialogDelegateView::CreateBubble(dictation_bubble_view_); |
| widget_->SetZOrderLevel(ui::ZOrderLevel::kFloatingUIElement); |
| CollisionDetectionUtils::MarkWindowPriorityForCollisionDetection( |
| widget_->GetNativeWindow(), |
| CollisionDetectionUtils::RelativePriority::kDictationBubble); |
| } |
| |
| void DictationBubbleController::Update( |
| DictationBubbleIconType icon, |
| const std::optional<std::u16string>& text, |
| const std::optional<std::vector<DictationBubbleHintType>>& hints) { |
| DCHECK(dictation_bubble_view_); |
| DCHECK(widget_); |
| |
| // Update `dictation_bubble_view_`. |
| dictation_bubble_view_->Update(icon, text, hints); |
| |
| // Update the bounds to fit entirely within the screen. |
| gfx::Rect new_bounds = widget_->GetWindowBoundsInScreen(); |
| gfx::Rect display_bounds = |
| display::Screen::GetScreen()->GetDisplayMatching(new_bounds).bounds(); |
| new_bounds.AdjustToFit(display_bounds); |
| |
| // Update the preferred bounds based on other system windows. |
| gfx::Rect resting_bounds = CollisionDetectionUtils::AvoidObstacles( |
| display::Screen::GetScreen()->GetDisplayNearestWindow( |
| widget_->GetNativeWindow()), |
| new_bounds, CollisionDetectionUtils::RelativePriority::kDictationBubble); |
| widget_->SetBounds(resting_bounds); |
| } |
| |
| void DictationBubbleController::AddObserver(Observer* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void DictationBubbleController::RemoveObserver(Observer* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| } // namespace ash |