| // Copyright 2018 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_preview_view.h" |
| |
| #include "ash/wm/window_mirror_view_pip.h" |
| #include "ash/wm/window_state.h" |
| #include "ash/wm/window_transient_descendant_iterator.h" |
| #include "ash/wm/window_util.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/client/transient_window_client.h" |
| #include "ui/aura/window.h" |
| #include "ui/gfx/geometry/point.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/rect_f.h" |
| #include "ui/gfx/geometry/size_conversions.h" |
| #include "ui/gfx/geometry/size_f.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/wm/core/window_util.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| gfx::Rect GetClientAreaBoundsInScreen(aura::Window* window) { |
| const int inset = window->GetProperty(aura::client::kTopViewInset); |
| if (inset > 0) { |
| gfx::Rect bounds = window->GetBoundsInScreen(); |
| 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(window); |
| if (!widget || !widget->client_view()) |
| return gfx::Rect(); |
| views::View* client_view = widget->client_view(); |
| gfx::Rect bounds = client_view->GetLocalBounds(); |
| views::View::ConvertRectToScreen(client_view, &bounds); |
| return bounds; |
| } |
| |
| } // namespace |
| |
| WindowPreviewView::WindowPreviewView(aura::Window* window, |
| bool trilinear_filtering_on_init) |
| : window_(window), |
| trilinear_filtering_on_init_(trilinear_filtering_on_init) { |
| DCHECK(window); |
| aura::client::GetTransientWindowClient()->AddObserver(this); |
| |
| for (auto* window : GetTransientTreeIterator(window_)) |
| AddWindow(window); |
| } |
| |
| WindowPreviewView::~WindowPreviewView() { |
| for (auto* window : unparented_transient_children_) |
| window->RemoveObserver(this); |
| for (auto entry : mirror_views_) |
| entry.first->RemoveObserver(this); |
| aura::client::GetTransientWindowClient()->RemoveObserver(this); |
| } |
| |
| void WindowPreviewView::RecreatePreviews() { |
| for (auto entry : mirror_views_) |
| entry.second->RecreateMirrorLayers(); |
| } |
| |
| gfx::Size WindowPreviewView::CalculatePreferredSize() const { |
| // The preferred size of this view is the union of all the windows it is made |
| // up of with, scaled to match the ratio of the main window to its mirror |
| // view's preferred size. |
| aura::Window* root = ::wm::GetTransientRoot(window_); |
| DCHECK(root); |
| const gfx::RectF union_rect = GetUnionRect(); |
| gfx::RectF window_bounds(GetClientAreaBoundsInScreen(root)); |
| gfx::SizeF window_size(1.f, 1.f); |
| auto it = mirror_views_.find(root); |
| if (it != mirror_views_.end()) { |
| window_size = gfx::SizeF(it->second->CalculatePreferredSize()); |
| if (window_size.IsEmpty()) |
| return gfx::Size(); // Avoids divide by zero below. |
| } |
| gfx::Vector2dF scale(window_bounds.width() / window_size.width(), |
| window_bounds.height() / window_size.height()); |
| return gfx::ToRoundedSize( |
| gfx::ScaleSize(union_rect.size(), scale.x(), scale.y())); |
| } |
| |
| void WindowPreviewView::Layout() { |
| const gfx::RectF union_rect = GetUnionRect(); |
| if (union_rect.IsEmpty()) |
| return; // Avoids divide by zero below. |
| |
| // Layout the windows in |mirror_view_| by keeping the same ratio of the |
| // original windows to the union of all windows in |mirror_views_|. |
| const gfx::RectF local_bounds = gfx::RectF(GetLocalBounds()); |
| const gfx::Point union_origin = gfx::ToRoundedPoint(union_rect.origin()); |
| |
| gfx::Vector2dF scale(local_bounds.width() / union_rect.width(), |
| local_bounds.height() / union_rect.height()); |
| for (auto entry : mirror_views_) { |
| const gfx::Rect bounds = GetClientAreaBoundsInScreen(entry.first) - |
| union_origin.OffsetFromOrigin(); |
| entry.second->SetBoundsRect( |
| gfx::ScaleToRoundedRect(bounds, scale.x(), scale.y())); |
| } |
| } |
| |
| void WindowPreviewView::OnTransientChildWindowAdded( |
| aura::Window* parent, |
| aura::Window* transient_child) { |
| aura::Window* root = ::wm::GetTransientRoot(window_); |
| if (!::wm::HasTransientAncestor(parent, root) && parent != root) |
| return; |
| |
| if (!transient_child->parent()) { |
| transient_child->AddObserver(this); |
| unparented_transient_children_.emplace(transient_child); |
| return; |
| } |
| |
| AddWindow(transient_child); |
| } |
| |
| void WindowPreviewView::OnTransientChildWindowRemoved( |
| aura::Window* parent, |
| aura::Window* transient_child) { |
| aura::Window* root = ::wm::GetTransientRoot(window_); |
| if (!::wm::HasTransientAncestor(parent, root) && parent != root) |
| return; |
| |
| RemoveWindow(transient_child); |
| } |
| |
| void WindowPreviewView::OnWindowDestroying(aura::Window* window) { |
| RemoveWindow(window); |
| } |
| |
| void WindowPreviewView::OnWindowParentChanged(aura::Window* window, |
| aura::Window* parent) { |
| if (!unparented_transient_children_.contains(window)) |
| return; |
| |
| DCHECK(parent); |
| unparented_transient_children_.erase(window); |
| window->RemoveObserver(this); |
| AddWindow(window); |
| } |
| |
| void WindowPreviewView::AddWindow(aura::Window* window) { |
| DCHECK(!mirror_views_.contains(window)); |
| DCHECK(!unparented_transient_children_.contains(window)); |
| DCHECK(!window->HasObserver(this)); |
| |
| if (window->type() == aura::client::WINDOW_TYPE_POPUP) |
| return; |
| |
| if (!window->HasObserver(this)) |
| window->AddObserver(this); |
| |
| auto* mirror_view = |
| window_util::IsArcPipWindow(window) |
| ? new WindowMirrorViewPip(window, trilinear_filtering_on_init_) |
| : new WindowMirrorView(window, trilinear_filtering_on_init_); |
| mirror_views_[window] = mirror_view; |
| AddChildView(mirror_view); |
| } |
| |
| void WindowPreviewView::RemoveWindow(aura::Window* window) { |
| auto iter = unparented_transient_children_.find(window); |
| if (iter != unparented_transient_children_.end()) { |
| unparented_transient_children_.erase(iter); |
| window->RemoveObserver(this); |
| DCHECK(!mirror_views_.count(window)); |
| return; |
| } |
| |
| auto it = mirror_views_.find(window); |
| if (it == mirror_views_.end()) |
| return; |
| |
| RemoveChildView(it->second); |
| it->first->RemoveObserver(this); |
| mirror_views_.erase(it); |
| } |
| |
| gfx::RectF WindowPreviewView::GetUnionRect() const { |
| gfx::Rect bounds; |
| for (auto entry : mirror_views_) |
| bounds.Union(GetClientAreaBoundsInScreen(entry.first)); |
| return gfx::RectF(bounds); |
| } |
| |
| } // namespace ash |