#include "ash/wm/window_mirror_view.h"
#include <algorithm>
#include <memory>
#include "ash/wm/desks/desks_util.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_occlusion_tracker.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_tree_owner.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/window_util.h"
namespace ash {
namespace {
void EnsureAllChildrenAreVisible(ui::Layer* layer) {
for (auto* child : layer->children())
EnsureAllChildrenAreVisible(child);
layer->SetVisible(true);
layer->SetOpacity(1);
}
}
WindowMirrorView::WindowMirrorView(aura::Window* source,
bool trilinear_filtering_on_init,
bool show_non_client_view)
: source_(source),
trilinear_filtering_on_init_(trilinear_filtering_on_init),
show_non_client_view_(show_non_client_view) {
source_->AddObserver(this);
DCHECK(source);
}
WindowMirrorView::~WindowMirrorView() {
if (source_) {
DCHECK(source_->layer());
source_->RemoveObserver(this);
}
}
void WindowMirrorView::RecreateMirrorLayers() {
if (layer_owner_)
layer_owner_.reset();
InitLayerOwner();
}
void WindowMirrorView::OnWindowDestroying(aura::Window* window) {
DCHECK_EQ(source_, window);
if (source_ == window) {
source_->RemoveObserver(this);
source_ = nullptr;
}
}
gfx::Size WindowMirrorView::CalculatePreferredSize() const {
return show_non_client_view_ ? source_->bounds().size()
: GetClientAreaBounds().size();
}
void WindowMirrorView::Layout() {
if (!layer_owner_ || !source_)
return;
GetMirrorLayer()->SetBounds(gfx::Rect(GetMirrorLayer()->bounds().size()));
if (show_non_client_view_) {
GetMirrorLayer()->SetTransform(gfx::Transform());
return;
}
gfx::Transform transform;
gfx::Rect client_area_bounds = GetClientAreaBounds();
if (size() != source_->bounds().size()) {
const float scale_x =
width() / static_cast<float>(client_area_bounds.width());
const float scale_y =
height() / static_cast<float>(client_area_bounds.height());
transform.Scale(scale_x, scale_y);
}
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::AddedToWidget() {
target_ = GetWidget()->GetNativeWindow();
target_->TrackOcclusionState();
if (source_) {
force_occlusion_tracker_visible_ =
std::make_unique<aura::WindowOcclusionTracker::ScopedForceVisible>(
source_);
} else {
force_occlusion_tracker_visible_.reset();
}
}
void WindowMirrorView::RemovedFromWidget() {
target_ = nullptr;
}
ui::Layer* WindowMirrorView::GetMirrorLayerForTesting() {
return GetMirrorLayer();
}
void WindowMirrorView::InitLayerOwner() {
layer_owner_ = wm::MirrorLayers(source_, false);
layer_owner_->root()->SetOpacity(1.f);
SetPaintToLayer();
ui::Layer* mirror_layer = GetMirrorLayer();
layer()->Add(mirror_layer);
layer()->SetMasksToBounds(true);
if (window_util::IsMinimizedOrTucked(source_) ||
!desks_util::BelongsToActiveDesk(source_)) {
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 {
DCHECK(!show_non_client_view_);
const int inset = source_->GetProperty(aura::client::kTopViewInset);
if (inset > 0) {
gfx::Rect bounds(source_->bounds().size());
bounds.Inset(gfx::Insets::TLBR(inset, 0, 0, 0));
return bounds;
}
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());
}
}