blob: 93552daa5c1727e135dd408072b3b089d427596a [file] [log] [blame]
// Copyright (c) 2012 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 "ui/wm/core/shadow_controller.h"
#include <utility>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/scoped_observer.h"
#include "base/stl_util.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/env.h"
#include "ui/aura/env_observer.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
#include "ui/base/class_property.h"
#include "ui/base/ui_base_types.h"
#include "ui/compositor/layer.h"
#include "ui/wm/core/shadow.h"
#include "ui/wm/core/window_util.h"
#include "ui/wm/public/activation_client.h"
using std::make_pair;
DEFINE_UI_CLASS_PROPERTY_TYPE(::wm::Shadow*);
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(::wm::Shadow, kShadowLayerKey, nullptr);
namespace wm {
namespace {
constexpr ShadowElevation kInactiveNormalShadowElevation =
ShadowElevation::MEDIUM;
ShadowElevation GetDefaultShadowElevationForWindow(aura::Window* window) {
switch (window->type()) {
case aura::client::WINDOW_TYPE_NORMAL:
case aura::client::WINDOW_TYPE_PANEL:
return kInactiveNormalShadowElevation;
case aura::client::WINDOW_TYPE_MENU:
case aura::client::WINDOW_TYPE_TOOLTIP:
return ShadowElevation::SMALL;
default:
break;
}
return ShadowElevation::NONE;
}
// Returns the ShadowElevation for |window|, converting |DEFAULT| to the
// appropriate ShadowElevation.
ShadowElevation GetShadowElevationConvertDefault(aura::Window* window) {
ShadowElevation elevation = window->GetProperty(kShadowElevationKey);
return elevation == ShadowElevation::DEFAULT
? GetDefaultShadowElevationForWindow(window)
: elevation;
}
ShadowElevation GetShadowElevationForActiveState(aura::Window* window) {
ShadowElevation elevation = window->GetProperty(kShadowElevationKey);
if (elevation != ShadowElevation::DEFAULT)
return elevation;
if (IsActiveWindow(window))
return ShadowController::kActiveNormalShadowElevation;
return GetDefaultShadowElevationForWindow(window);
}
// Returns the shadow style to be applied to |losing_active| when it is losing
// active to |gaining_active|. |gaining_active| may be of a type that hides when
// inactive, and as such we do not want to render |losing_active| as inactive.
ShadowElevation GetShadowElevationForWindowLosingActive(
aura::Window* losing_active,
aura::Window* gaining_active) {
if (gaining_active && GetHideOnDeactivate(gaining_active)) {
if (base::ContainsValue(GetTransientChildren(losing_active),
gaining_active))
return ShadowController::kActiveNormalShadowElevation;
}
return kInactiveNormalShadowElevation;
}
} // namespace
// ShadowController::Impl ------------------------------------------------------
// Real implementation of the ShadowController. ShadowController observes
// ActivationChangeObserver, which are per ActivationClient, where as there is
// only a single Impl (as it observes all window creation by way of an
// EnvObserver).
class ShadowController::Impl :
public aura::EnvObserver,
public aura::WindowObserver,
public base::RefCounted<Impl> {
public:
// Returns the singleton instance, destroyed when there are no more refs.
static Impl* GetInstance();
// aura::EnvObserver override:
void OnWindowInitialized(aura::Window* window) override;
// aura::WindowObserver overrides:
void OnWindowPropertyChanged(aura::Window* window,
const void* key,
intptr_t old) override;
void OnWindowBoundsChanged(aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
ui::PropertyChangeReason reason) override;
void OnWindowDestroyed(aura::Window* window) override;
private:
friend class base::RefCounted<Impl>;
friend class ShadowController;
Impl();
~Impl() override;
// Forwarded from ShadowController.
void OnWindowActivated(ActivationReason reason,
aura::Window* gained_active,
aura::Window* lost_active);
// Checks if |window| is visible and contains a property requesting a shadow.
bool ShouldShowShadowForWindow(aura::Window* window) const;
// Updates the shadow for windows when activation changes.
void HandleWindowActivationChange(aura::Window* gaining_active,
aura::Window* losing_active);
// Shows or hides |window|'s shadow as needed (creating the shadow if
// necessary).
void HandlePossibleShadowVisibilityChange(aura::Window* window);
// Creates a new shadow for |window| and stores it with the |kShadowLayerKey|
// key.
// The shadow's bounds are initialized and it is added to the window's layer.
void CreateShadowForWindow(aura::Window* window);
ScopedObserver<aura::Window, aura::WindowObserver> observer_manager_;
static Impl* instance_;
DISALLOW_COPY_AND_ASSIGN(Impl);
};
// static
ShadowController::Impl* ShadowController::Impl::instance_ = NULL;
// static
ShadowController::Impl* ShadowController::Impl::GetInstance() {
if (!instance_)
instance_ = new Impl();
return instance_;
}
void ShadowController::Impl::OnWindowInitialized(aura::Window* window) {
observer_manager_.Add(window);
HandlePossibleShadowVisibilityChange(window);
}
void ShadowController::Impl::OnWindowPropertyChanged(aura::Window* window,
const void* key,
intptr_t old) {
if (key == kShadowElevationKey) {
if (window->GetProperty(kShadowElevationKey) ==
static_cast<ShadowElevation>(old))
return;
} else if (key == aura::client::kShowStateKey) {
if (window->GetProperty(aura::client::kShowStateKey) ==
static_cast<ui::WindowShowState>(old)) {
return;
}
} else {
return;
}
HandlePossibleShadowVisibilityChange(window);
}
void ShadowController::Impl::OnWindowBoundsChanged(
aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
ui::PropertyChangeReason reason) {
Shadow* shadow = GetShadowForWindow(window);
if (shadow)
shadow->SetContentBounds(gfx::Rect(new_bounds.size()));
}
void ShadowController::Impl::OnWindowDestroyed(aura::Window* window) {
window->ClearProperty(kShadowLayerKey);
observer_manager_.Remove(window);
}
void ShadowController::Impl::OnWindowActivated(ActivationReason reason,
aura::Window* gained_active,
aura::Window* lost_active) {
if (gained_active) {
Shadow* shadow = GetShadowForWindow(gained_active);
if (shadow)
shadow->SetElevation(GetShadowElevationForActiveState(gained_active));
}
if (lost_active) {
Shadow* shadow = GetShadowForWindow(lost_active);
if (shadow &&
GetShadowElevationConvertDefault(lost_active) ==
kInactiveNormalShadowElevation) {
shadow->SetElevation(
GetShadowElevationForWindowLosingActive(lost_active, gained_active));
}
}
}
bool ShadowController::Impl::ShouldShowShadowForWindow(
aura::Window* window) const {
ui::WindowShowState show_state =
window->GetProperty(aura::client::kShowStateKey);
if (show_state == ui::SHOW_STATE_FULLSCREEN ||
show_state == ui::SHOW_STATE_MAXIMIZED) {
return false;
}
return static_cast<int>(GetShadowElevationConvertDefault(window)) > 0;
}
void ShadowController::Impl::HandlePossibleShadowVisibilityChange(
aura::Window* window) {
const bool should_show = ShouldShowShadowForWindow(window);
Shadow* shadow = GetShadowForWindow(window);
if (shadow) {
shadow->SetElevation(GetShadowElevationForActiveState(window));
shadow->layer()->SetVisible(should_show);
} else if (should_show && !shadow) {
CreateShadowForWindow(window);
}
}
void ShadowController::Impl::CreateShadowForWindow(aura::Window* window) {
Shadow* shadow = new Shadow();
window->SetProperty(kShadowLayerKey, shadow);
shadow->Init(GetShadowElevationForActiveState(window));
shadow->SetContentBounds(gfx::Rect(window->bounds().size()));
shadow->layer()->SetVisible(ShouldShowShadowForWindow(window));
window->layer()->Add(shadow->layer());
}
ShadowController::Impl::Impl()
: observer_manager_(this) {
aura::Env::GetInstance()->AddObserver(this);
}
ShadowController::Impl::~Impl() {
DCHECK_EQ(instance_, this);
aura::Env::GetInstance()->RemoveObserver(this);
instance_ = NULL;
}
// ShadowController ------------------------------------------------------------
Shadow* ShadowController::GetShadowForWindow(aura::Window* window) {
return window->GetProperty(kShadowLayerKey);
}
ShadowController::ShadowController(ActivationClient* activation_client)
: activation_client_(activation_client), impl_(Impl::GetInstance()) {
// Watch for window activation changes.
activation_client_->AddObserver(this);
}
ShadowController::~ShadowController() {
activation_client_->RemoveObserver(this);
}
void ShadowController::OnWindowActivated(ActivationReason reason,
aura::Window* gained_active,
aura::Window* lost_active) {
impl_->OnWindowActivated(reason, gained_active, lost_active);
}
} // namespace wm