| // Copyright 2016 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/wm/window_mirror_view.h" |
| |
| #include <algorithm> |
| #include <memory> |
| |
| #include "ash/wm/widget_finder.h" |
| #include "ash/wm/window_state.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/window.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/compositor/layer_tree_owner.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/wm/core/window_util.h" |
| |
| namespace ash { |
| namespace wm { |
| namespace { |
| |
| void EnsureAllChildrenAreVisible(ui::Layer* layer) { |
| std::list<ui::Layer*> layers; |
| layers.push_back(layer); |
| while (!layers.empty()) { |
| for (auto* child : layers.front()->children()) |
| layers.push_back(child); |
| layers.front()->SetVisible(true); |
| layers.pop_front(); |
| } |
| } |
| |
| // Removes 1 instance of an element from a multiset. |
| void EraseFromList(std::vector<aura::Window*>* targets, aura::Window* target) { |
| auto it = std::find(targets->begin(), targets->end(), target); |
| if (it != targets->end()) |
| targets->erase(it); |
| } |
| |
| void RemoveTargetWindowFromSource(aura::Window* target, aura::Window* source) { |
| if (!target || !source) |
| return; |
| std::vector<aura::Window*>* target_window_list = |
| source->GetProperty(aura::client::kMirrorWindowList); |
| if (!target_window_list) |
| return; |
| EraseFromList(target_window_list, target); |
| } |
| |
| } // namespace |
| |
| WindowMirrorView::WindowMirrorView(aura::Window* source, |
| bool trilinear_filtering_on_init) |
| : source_(source), |
| trilinear_filtering_on_init_(trilinear_filtering_on_init) { |
| DCHECK(source); |
| } |
| |
| WindowMirrorView::~WindowMirrorView() { |
| // Make sure |source_| has outlived |this|. See crbug.com/681207 |
| DCHECK(source_->layer()); |
| RemoveTargetWindowFromSource(target_, source_); |
| } |
| |
| void WindowMirrorView::RecreateMirrorLayers() { |
| if (layer_owner_) |
| layer_owner_.reset(); |
| |
| InitLayerOwner(); |
| } |
| |
| gfx::Size WindowMirrorView::CalculatePreferredSize() const { |
| return GetClientAreaBounds().size(); |
| } |
| |
| void WindowMirrorView::Layout() { |
| // If |layer_owner_| hasn't been initialized (|this| isn't on screen), no-op. |
| if (!layer_owner_) |
| return; |
| |
| // Position at 0, 0. |
| GetMirrorLayer()->SetBounds(gfx::Rect(GetMirrorLayer()->bounds().size())); |
| |
| gfx::Transform transform; |
| gfx::Rect client_area_bounds = GetClientAreaBounds(); |
| // Scale down if necessary. |
| if (size() != source_->bounds().size()) { |
| const float scale = |
| width() / static_cast<float>(client_area_bounds.width()); |
| transform.Scale(scale, scale); |
| } |
| // Reposition such that the client area is the only part visible. |
| transform.Translate(-client_area_bounds.x(), -client_area_bounds.y()); |
| GetMirrorLayer()->SetTransform(transform); |
| } |
| |
| bool WindowMirrorView::GetNeedsNotificationWhenVisibleBoundsChange() const { |
| return true; |
| } |
| |
| void WindowMirrorView::OnVisibleBoundsChanged() { |
| if (!layer_owner_ && !GetVisibleBounds().IsEmpty()) |
| InitLayerOwner(); |
| } |
| |
| void WindowMirrorView::NativeViewHierarchyChanged() { |
| View::NativeViewHierarchyChanged(); |
| DCHECK(GetWidget()); |
| UpdateSourceWindowProperty(); |
| } |
| |
| void WindowMirrorView::AddedToWidget() { |
| UpdateSourceWindowProperty(); |
| } |
| |
| void WindowMirrorView::RemovedFromWidget() { |
| RemoveTargetWindowFromSource(target_, source_); |
| target_ = nullptr; |
| } |
| |
| void WindowMirrorView::UpdateSourceWindowProperty() { |
| DCHECK(GetWidget()); |
| std::vector<aura::Window*>* target_window_list = |
| source_->GetProperty(aura::client::kMirrorWindowList); |
| |
| // Remove 1 instance of |target_| from the list. |
| if (target_ && target_window_list) |
| EraseFromList(target_window_list, target_); |
| |
| // Set and insert the new target window associated with this mirror view. |
| target_ = GetWidget()->GetNativeWindow(); |
| target_->TrackOcclusionState(); |
| |
| // Allocate new memory for |target_window_list| here because as soon as a |
| // call is made to SetProperty, the previous memory will be deallocated. |
| auto temp_list = |
| target_window_list |
| ? std::make_unique<std::vector<aura::Window*>>(*target_window_list) |
| : std::make_unique<std::vector<aura::Window*>>(); |
| |
| temp_list->push_back(target_); |
| |
| // Set the property to trigger a call to OnWindowPropertyChanged() on all the |
| // window observer. |
| // NOTE: This will deallocate the current property value so make sure new |
| // memory has been allocated for the property value. |
| source_->SetProperty(aura::client::kMirrorWindowList, temp_list.release()); |
| } |
| |
| void WindowMirrorView::InitLayerOwner() { |
| layer_owner_ = ::wm::MirrorLayers(source_, false /* sync_bounds */); |
| |
| SetPaintToLayer(); |
| |
| ui::Layer* mirror_layer = GetMirrorLayer(); |
| layer()->Add(mirror_layer); |
| // This causes us to clip the non-client areas of the window. |
| layer()->SetMasksToBounds(true); |
| |
| // Some extra work is needed when the source window is minimized. |
| if (wm::GetWindowState(source_)->IsMinimized()) { |
| mirror_layer->SetOpacity(1); |
| EnsureAllChildrenAreVisible(mirror_layer); |
| } |
| |
| if (trilinear_filtering_on_init_) { |
| mirror_layer->AddCacheRenderSurfaceRequest(); |
| mirror_layer->AddTrilinearFilteringRequest(); |
| } |
| |
| Layout(); |
| } |
| |
| ui::Layer* WindowMirrorView::GetMirrorLayer() { |
| return layer_owner_->root(); |
| } |
| |
| gfx::Rect WindowMirrorView::GetClientAreaBounds() const { |
| const int inset = source_->GetProperty(aura::client::kTopViewInset); |
| if (inset > 0) { |
| gfx::Rect bounds(source_->bounds().size()); |
| bounds.Inset(0, inset, 0, 0); |
| return bounds; |
| } |
| // The source window may not have a widget in unit tests. |
| views::Widget* widget = views::Widget::GetWidgetForNativeWindow(source_); |
| if (!widget || !widget->client_view()) |
| return gfx::Rect(); |
| views::View* client_view = widget->client_view(); |
| return client_view->ConvertRectToWidget(client_view->GetLocalBounds()); |
| } |
| |
| } // namespace wm |
| } // namespace ash |