123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- // Copyright 2021 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ash/capture_mode/recording_overlay_controller.h"
- #include "ash/capture_mode/capture_mode_controller.h"
- #include "ash/capture_mode/stop_recording_button_tray.h"
- #include "ash/projector/projector_annotation_tray.h"
- #include "ash/public/cpp/capture_mode/recording_overlay_view.h"
- #include "ash/public/cpp/shell_window_ids.h"
- #include "ash/root_window_controller.h"
- #include "ash/shelf/shelf.h"
- #include "ash/system/status_area_widget.h"
- #include "ui/aura/window.h"
- #include "ui/aura/window_targeter.h"
- #include "ui/compositor/layer_type.h"
- #include "ui/gfx/geometry/rect.h"
- #include "ui/views/widget/widget.h"
- #include "ui/wm/core/coordinate_conversion.h"
- #include "ui/wm/core/window_properties.h"
- namespace ash {
- namespace {
- // When recording a non-root window (i.e. kWindow recording source), the overlay
- // is added as a direct child of the window being recorded, and stacked on top
- // of all children. This is so that the overlay contents show up in the
- // recording above everything else.
- //
- // + window_being_recorded
- // |
- // + (Some other child windows hosting contents of the window)
- // |
- // + Recording overlay widget
- //
- // (Note that bottom-most child are the top-most child in terms of z-order).
- //
- // However, when recording the root window (i.e. kFullscreen or kRegion
- // recording sources), the overlay is added as a child of the menu container.
- // The menu container is high enough in terms of z-order, making the overlay on
- // top of most things. However, it's also the same container used by the
- // projector bar (which we want to be on top of the overlay, since it has the
- // button to toggle the overlay off, and we don't want the overlay to block
- // events going to that button). Therefore, the overlay is stacked at the bottom
- // of the menu container's children. See UpdateWidgetStacking() below.
- //
- // + Menu container
- // |
- // + Recording overlay widget
- // |
- // + Projector bar widget
- //
- // TODO(https://crbug.com/1253011): Revise this parenting and z-ordering once
- // the deprecated Projector toolbar is removed and replaced by the shelf-pod
- // based new tools.
- aura::Window* GetWidgetParent(aura::Window* window_being_recorded) {
- return window_being_recorded->IsRootWindow()
- ? window_being_recorded->GetChildById(kShellWindowId_MenuContainer)
- : window_being_recorded;
- }
- // Given the `bounds_in_parent` of the overlay widget, returns the bounds in the
- // correct coordinate system depending on whether the `overlay_window_parent`
- // uses screen coordinates or not.
- gfx::Rect MaybeAdjustOverlayBounds(const gfx::Rect& bounds_in_parent,
- aura::Window* overlay_window_parent) {
- DCHECK(overlay_window_parent);
- if (!overlay_window_parent->GetProperty(wm::kUsesScreenCoordinatesKey))
- return bounds_in_parent;
- gfx::Rect bounds_in_screen = bounds_in_parent;
- wm::ConvertRectToScreen(overlay_window_parent, &bounds_in_screen);
- return bounds_in_screen;
- }
- // Defines a window targeter that will be installed on the overlay widget's
- // window so that we can allow located events over the projector shelf pod or
- // its associated bubble widget to go through and not be consumed by the
- // overlay. This enables the user to interact with the annotation tools while
- // annotating.
- class OverlayTargeter : public aura::WindowTargeter {
- public:
- explicit OverlayTargeter(aura::Window* overlay_window)
- : overlay_window_(overlay_window) {}
- OverlayTargeter(const OverlayTargeter&) = delete;
- OverlayTargeter& operator=(const OverlayTargeter&) = delete;
- ~OverlayTargeter() override = default;
- // aura::WindowTargeter:
- ui::EventTarget* FindTargetForEvent(ui::EventTarget* root,
- ui::Event* event) override {
- if (event->IsLocatedEvent()) {
- auto* root_window = overlay_window_->GetRootWindow();
- auto* status_area_widget =
- RootWindowController::ForWindow(root_window)->GetStatusAreaWidget();
- StopRecordingButtonTray* stop_recording_button =
- status_area_widget->stop_recording_button_tray();
- auto screen_location = event->AsLocatedEvent()->root_location();
- wm::ConvertPointToScreen(root_window, &screen_location);
- Shelf* shelf = RootWindowController::ForWindow(root_window)->shelf();
- // To be able to bring the auto-hidden shelf back even while annotation is
- // active, we expose a slim 1dp region at the edge of the screen in which
- // the shelf is aligned. Events in that region will not be consumed so
- // that they can be used to show the auto-hidden shelf.
- if (!shelf->IsVisible()) {
- gfx::Rect root_window_bounds_in_screen =
- root_window->GetBoundsInScreen();
- const int display_width = root_window_bounds_in_screen.width();
- const int display_height = root_window_bounds_in_screen.height();
- const gfx::Rect shelf_activation_bounds =
- shelf->SelectValueForShelfAlignment(
- gfx::Rect(0, display_height - 1, display_width, 1),
- gfx::Rect(0, 0, 1, display_height),
- gfx::Rect(display_width - 1, 0, 1, display_height));
- if (shelf_activation_bounds.Contains(screen_location))
- return nullptr;
- }
- // To be able to end video recording even while annotation is active,
- // let events over the stop recording button to go through.
- if (stop_recording_button && stop_recording_button->visible_preferred() &&
- stop_recording_button->GetBoundsInScreen().Contains(
- screen_location)) {
- return nullptr;
- }
- ProjectorAnnotationTray* annotations =
- status_area_widget->projector_annotation_tray();
- if (annotations && annotations->visible_preferred()) {
- // Let events over the projector shelf pod to go through.
- if (annotations->GetBoundsInScreen().Contains(screen_location))
- return nullptr;
- // Let events over the projector bubble widget (if shown) to go through.
- views::Widget* bubble_widget = annotations->GetBubbleWidget();
- if (bubble_widget && bubble_widget->IsVisible() &&
- bubble_widget->GetWindowBoundsInScreen().Contains(
- screen_location)) {
- return nullptr;
- }
- // Ensure that the annotator bubble is closed when a press event is
- // triggered.
- if (event->type() == ui::ET_MOUSE_PRESSED ||
- event->type() == ui::ET_TOUCH_PRESSED) {
- annotations->ClickedOutsideBubble();
- }
- }
- }
- return aura::WindowTargeter::FindTargetForEvent(root, event);
- }
- private:
- aura::Window* const overlay_window_;
- };
- } // namespace
- RecordingOverlayController::RecordingOverlayController(
- aura::Window* window_being_recorded,
- const gfx::Rect& initial_bounds_in_parent) {
- DCHECK(window_being_recorded);
- views::Widget::InitParams params(
- views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
- params.name = "RecordingOverlayWidget";
- params.child = true;
- params.parent = GetWidgetParent(window_being_recorded);
- // The overlay hosts transparent contents so actual contents of the window
- // being recorded shows up underneath.
- params.layer_type = ui::LAYER_NOT_DRAWN;
- params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
- params.bounds =
- MaybeAdjustOverlayBounds(initial_bounds_in_parent, params.parent);
- // The overlay window does not receive any events until it's shown and
- // enabled. See |Start()| below.
- params.activatable = views::Widget::InitParams::Activatable::kNo;
- params.accept_events = false;
- overlay_widget_->Init(std::move(params));
- recording_overlay_view_ = overlay_widget_->SetContentsView(
- CaptureModeController::Get()->CreateRecordingOverlayView());
- auto* overlay_window = overlay_widget_->GetNativeWindow();
- overlay_window->SetEventTargeter(
- std::make_unique<OverlayTargeter>(overlay_window));
- UpdateWidgetStacking();
- }
- void RecordingOverlayController::Toggle() {
- is_enabled_ = !is_enabled_;
- if (is_enabled_)
- Start();
- else
- Stop();
- }
- void RecordingOverlayController::SetBounds(const gfx::Rect& bounds_in_parent) {
- overlay_widget_->SetBounds(MaybeAdjustOverlayBounds(
- bounds_in_parent, overlay_widget_->GetNativeWindow()));
- }
- aura::Window* RecordingOverlayController::GetOverlayNativeWindow() {
- return overlay_widget_->GetNativeWindow();
- }
- void RecordingOverlayController::Start() {
- DCHECK(is_enabled_);
- overlay_widget_->GetNativeWindow()->SetEventTargetingPolicy(
- aura::EventTargetingPolicy::kTargetAndDescendants);
- overlay_widget_->Show();
- }
- void RecordingOverlayController::Stop() {
- DCHECK(!is_enabled_);
- overlay_widget_->GetNativeWindow()->SetEventTargetingPolicy(
- aura::EventTargetingPolicy::kNone);
- overlay_widget_->Hide();
- }
- void RecordingOverlayController::UpdateWidgetStacking() {
- auto* overlay_window = overlay_widget_->GetNativeWindow();
- auto* parent = overlay_window->parent();
- DCHECK(parent);
- // See more info in the docs of GetWidgetParent() above.
- if (parent->GetId() == kShellWindowId_MenuContainer)
- parent->StackChildAtBottom(overlay_window);
- else
- parent->StackChildAtTop(overlay_window);
- }
- } // namespace ash
|