| // 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_) { | |
| return; | |
| } | |
| DCHECK(client_); | |
| ToggleAnnotatorCanvas(); | |
| annotator_enabled_ = false; | |
| client_->Clear(); | |
| UpdateAnnotationTrayAccessibleName(/*is_annotator_enabled=*/false); | |
| NotifyStateChanged(); | |
| } | |
| 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(); | |
| 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 |