| // 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/annotator/annotator_controller.h" |
| |
| #include "ash/annotator/annotation_source_watcher.h" |
| #include "ash/annotator/annotation_tray.h" |
| #include "ash/annotator/annotations_overlay_controller.h" |
| #include "ash/annotator/annotator_metrics.h" |
| #include "ash/capture_mode/capture_mode_controller.h" |
| #include "ash/constants/ash_features.h" |
| #include "ash/projector/projector_metrics.h" |
| #include "ash/public/cpp/annotator/annotations_overlay_view.h" |
| #include "ash/public/cpp/annotator/annotator_tool.h" |
| #include "ash/root_window_controller.h" |
| #include "ash/system/status_area_widget.h" |
| #include "ash/webui/annotator/public/cpp/annotator_client.h" |
| #include "base/check.h" |
| #include "base/feature_list.h" |
| #include "ui/aura/window.h" |
| #include "ui/gfx/geometry/rect_f.h" |
| |
| namespace ash { |
| namespace { |
| AnnotatorMarkerColor GetMarkerColorForMetrics(SkColor color) { |
| switch (color) { |
| case kAnnotatorMagentaPenColor: |
| return AnnotatorMarkerColor::kMagenta; |
| case kAnnotatorBluePenColor: |
| return AnnotatorMarkerColor::kBlue; |
| case kAnnotatorRedPenColor: |
| return AnnotatorMarkerColor::kRed; |
| case kAnnotatorYellowPenColor: |
| return AnnotatorMarkerColor::kYellow; |
| } |
| return AnnotatorMarkerColor::kMaxValue; |
| } |
| |
| AnnotationTray* GetAnnotationTrayForRoot(aura::Window* root) { |
| // It may happen that root is nullptr. This may happen in the event that |
| // the annotation tray is hidden before the canvas finishes its |
| // initialization. |
| if (!root) { |
| return nullptr; |
| } |
| |
| DCHECK(root->IsRootWindow()); |
| |
| // Annotating can end when a display being fullscreen-captured gets removed, |
| // in this case, we don't need to hide the button. |
| if (root->is_destroying()) { |
| return nullptr; |
| } |
| |
| // Can be null while shutting down. |
| auto* root_window_controller = RootWindowController::ForWindow(root); |
| if (!root_window_controller) { |
| return nullptr; |
| } |
| |
| auto* annotation_tray = |
| root_window_controller->GetStatusAreaWidget()->annotation_tray(); |
| DCHECK(annotation_tray); |
| return annotation_tray; |
| } |
| |
| void SetAnnotationTrayVisibility(aura::Window* root, bool visible) { |
| if (auto* annotation_tray = GetAnnotationTrayForRoot(root)) { |
| annotation_tray->SetVisiblePreferred(visible); |
| } |
| } |
| } // namespace |
| |
| AnnotatorController::AnnotatorController() { |
| annotation_source_watcher_ = std::make_unique<AnnotationSourceWatcher>(this); |
| UpdateAnnotationTrayAccessibleName(/*is_annotator_enabled=*/true); |
| } |
| |
| AnnotatorController::~AnnotatorController() { |
| annotation_source_watcher_.reset(); |
| annotations_overlay_controller_.reset(); |
| client_ = nullptr; |
| current_root_ = nullptr; |
| UpdateAnnotationTrayAccessibleName(/*is_annotator_enabled=*/false); |
| } |
| |
| void AnnotatorController::SetAnnotatorTool(const AnnotatorTool& tool) { |
| DCHECK(client_); |
| client_->SetTool(tool); |
| RecordMarkerColorMetrics(GetMarkerColorForMetrics(tool.color)); |
| } |
| |
| void AnnotatorController::ResetTools() { |
| if (annotator_enabled_) { |
| DCHECK(client_); |
| ToggleAnnotatorCanvas(); |
| annotator_enabled_ = false; |
| client_->Clear(); |
| UpdateAnnotationTrayAccessibleName(/*is_annotator_enabled=*/false); |
| } |
| } |
| |
| void AnnotatorController::RegisterView(aura::Window* new_root) { |
| // Make sure the annotator tray is only visible on one root window. |
| // TODO(b/342104047): Remove this check when annotator starts being used |
| // outside of the capture mode. |
| if (current_root_) { |
| UnregisterView(current_root_); |
| } |
| current_root_ = new_root; |
| // Show the tray icon. |
| SetAnnotationTrayVisibility(current_root_, /*visible=*/true); |
| } |
| |
| void AnnotatorController::UnregisterView(aura::Window* window) { |
| DCHECK_EQ(current_root_, window); |
| if (auto* annotation_tray = GetAnnotationTrayForRoot(current_root_)) { |
| annotation_tray->HideAnnotationTray(); |
| } |
| current_root_ = nullptr; |
| } |
| |
| void AnnotatorController::UpdateRootView(aura::Window* new_root) { |
| // Do nothing if the root window is the same. |
| if (new_root == current_root_) { |
| return; |
| } |
| UnregisterView(current_root_); |
| RegisterView(new_root); |
| current_root_ = new_root; |
| if (GetAnnotatorAvailability()) { |
| UpdateTrayEnabledState(); |
| } |
| } |
| |
| void AnnotatorController::EnableAnnotatorTool() { |
| if (!annotator_enabled_ && annotations_overlay_controller_) { |
| ToggleAnnotatorCanvas(); |
| annotator_enabled_ = !annotator_enabled_; |
| UpdateAnnotationTrayAccessibleName(annotator_enabled_); |
| NotifyStateChanged(); |
| // TODO(b/342104047): Decouple from projector metrics. |
| RecordToolbarMetrics(ProjectorToolbar::kMarkerTool); |
| } |
| } |
| |
| void AnnotatorController::DisableAnnotator() { |
| ResetTools(); |
| NotifyStateChanged(); |
| if (current_root_) { |
| UnregisterView(current_root_); |
| } |
| annotations_overlay_controller_.reset(); |
| canvas_initialized_state_.reset(); |
| } |
| |
| void AnnotatorController::CreateAnnotationOverlayForWindow( |
| aura::Window* window, |
| std::optional<gfx::Rect> partial_region_bounds) { |
| annotations_overlay_controller_ = |
| std::make_unique<AnnotationsOverlayController>(window, |
| partial_region_bounds); |
| } |
| |
| void AnnotatorController::UpdateAnnotationTrayAccessibleName( |
| bool is_annotator_enabled) { |
| if (auto* annotation_tray = GetAnnotationTrayForRoot(current_root_)) { |
| annotation_tray->UpdateAccessibleName(is_annotator_enabled); |
| } |
| } |
| |
| void AnnotatorController::CreateAnnotationOverlayForMarkerMode( |
| aura::Window* window) { |
| CreateAnnotationOverlayForWindow(window, std::nullopt); |
| } |
| |
| void AnnotatorController::SetToolClient(AnnotatorClient* client) { |
| client_ = client; |
| } |
| |
| bool AnnotatorController::GetAnnotatorAvailability() const { |
| return canvas_initialized_state_.value_or(false); |
| } |
| |
| void AnnotatorController::OnCanvasInitialized(bool success) { |
| canvas_initialized_state_ = success; |
| UpdateTrayEnabledState(); |
| if (on_canvas_initialized_callback_for_test_) { |
| std::move(on_canvas_initialized_callback_for_test_).Run(); |
| } |
| } |
| |
| void AnnotatorController::ToggleAnnotationTray() { |
| if (auto* annotation_tray = GetAnnotationTrayForRoot(current_root_)) { |
| annotation_tray->ToggleAnnotator(); |
| } |
| } |
| |
| // Callback indicating availability of undo and redo functionalities. |
| void AnnotatorController::OnUndoRedoAvailabilityChanged(bool undo_available, |
| bool redo_available) { |
| // TODO(b/198184362): Reflect undo and redo buttons availability |
| // on the annotator tray. |
| } |
| |
| std::unique_ptr<AnnotationsOverlayView> |
| AnnotatorController::CreateAnnotationsOverlayView() const { |
| return client_->CreateAnnotationsOverlayView(); |
| } |
| |
| void AnnotatorController::AddObserver(AnnotatorObserver* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void AnnotatorController::RemoveObserver(AnnotatorObserver* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| void AnnotatorController::UpdateTrayEnabledState() { |
| if (auto* annotation_tray = GetAnnotationTrayForRoot(current_root_)) { |
| annotation_tray->SetTrayEnabled(GetAnnotatorAvailability()); |
| } |
| } |
| |
| void AnnotatorController::ToggleAnnotatorCanvas() { |
| auto* capture_mode_controller = CaptureModeController::Get(); |
| if (base::FeatureList::IsEnabled(ash::features::kAnnotatorMode) || |
| capture_mode_controller->is_recording_in_progress()) { |
| annotations_overlay_controller_->Toggle(); |
| } |
| } |
| |
| void AnnotatorController::NotifyStateChanged() { |
| for (AnnotatorObserver& observer : observers_) { |
| observer.OnAnnotatorStateChanged(annotator_enabled_); |
| } |
| } |
| |
| } // namespace ash |