This source file includes following definitions.
- AsBubbleDelegate
- IsWindowTransientChildOf
- GetEventLocationInScreen
- GetDisplayBoundsInScreen
- StartObserving
- StopObserving
- UpdateRevealedLock
- OnWindowVisibilityChanged
- OnWindowDestroying
- weak_ptr_factory_
- Init
- SetEnabled
- IsEnabled
- IsRevealed
- GetRevealedLock
- SetupForTest
- OnMouseEvent
- OnTouchEvent
- OnGestureEvent
- OnWillChangeFocus
- OnDidChangeFocus
- OnWidgetDestroying
- OnWidgetActivationChanged
- AnimationEnded
- AnimationProgressed
- OnTransientChildAdded
- OnTransientChildRemoved
- LockRevealedState
- UnlockRevealedState
- EnableWindowObservers
- UpdateTopEdgeHoverTimer
- UpdateLocatedEventRevealedLock
- AcquireLocatedEventRevealedLock
- UpdateFocusRevealedLock
- UpdateRevealedLocksForSwipe
- GetAnimationDuration
- MaybeStartReveal
- OnSlideOpenAnimationCompleted
- MaybeEndReveal
- OnSlideClosedAnimationCompleted
- GetSwipeType
- ShouldIgnoreMouseEventAtLocation
- ShouldHandleGestureEvent
- RecreateBubbleManager
#include "ash/wm/immersive_fullscreen_controller.h"
#include <set>
#include "ash/ash_constants.h"
#include "ash/shell.h"
#include "ash/wm/resize_handle_window_targeter.h"
#include "ash/wm/window_state.h"
#include "base/metrics/histogram.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/capture_client.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/gfx/animation/slide_animation.h"
#include "ui/gfx/display.h"
#include "ui/gfx/point.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/screen.h"
#include "ui/views/bubble/bubble_delegate.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/transient_window_manager.h"
#include "ui/wm/core/window_util.h"
#include "ui/wm/public/activation_client.h"
using views::View;
namespace ash {
namespace {
const int kRevealSlowAnimationDurationMs = 400;
const int kRevealFastAnimationDurationMs = 200;
const int kMouseRevealDelayMs = 200;
const int kMouseRevealXThresholdPixels = 3;
const int kSwipeVerticalThresholdMultiplier = 3;
const int kHeightOfDeadRegionAboveTopContainer = 10;
const int kMouseRevealBoundsHeight = 3;
views::BubbleDelegateView* AsBubbleDelegate(aura::Window* maybe_bubble) {
if (!maybe_bubble)
return NULL;
views::Widget* widget = views::Widget::GetWidgetForNativeView(maybe_bubble);
if (!widget)
return NULL;
return widget->widget_delegate()->AsBubbleDelegate();
}
bool IsWindowTransientChildOf(aura::Window* maybe_transient,
aura::Window* toplevel) {
if (!maybe_transient || !toplevel)
return false;
for (aura::Window* window = maybe_transient; window;
window = ::wm::GetTransientParent(window)) {
if (window == toplevel)
return true;
}
return false;
}
gfx::Point GetEventLocationInScreen(const ui::LocatedEvent& event) {
gfx::Point location_in_screen = event.location();
aura::Window* target = static_cast<aura::Window*>(event.target());
aura::client::ScreenPositionClient* screen_position_client =
aura::client::GetScreenPositionClient(target->GetRootWindow());
screen_position_client->ConvertPointToScreen(target, &location_in_screen);
return location_in_screen;
}
gfx::Rect GetDisplayBoundsInScreen(aura::Window* window) {
return Shell::GetScreen()->GetDisplayNearestWindow(window).bounds();
}
}
class ImmersiveFullscreenController::BubbleManager
: public aura::WindowObserver {
public:
explicit BubbleManager(ImmersiveFullscreenController* controller);
virtual ~BubbleManager();
void StartObserving(aura::Window* bubble);
void StopObserving(aura::Window* bubble);
private:
void UpdateRevealedLock();
virtual void OnWindowVisibilityChanged(aura::Window* window,
bool visible) OVERRIDE;
virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
ImmersiveFullscreenController* controller_;
std::set<aura::Window*> bubbles_;
scoped_ptr<ImmersiveRevealedLock> revealed_lock_;
DISALLOW_COPY_AND_ASSIGN(BubbleManager);
};
ImmersiveFullscreenController::BubbleManager::BubbleManager(
ImmersiveFullscreenController* controller)
: controller_(controller) {
}
ImmersiveFullscreenController::BubbleManager::~BubbleManager() {
for (std::set<aura::Window*>::const_iterator it = bubbles_.begin();
it != bubbles_.end(); ++it) {
(*it)->RemoveObserver(this);
}
}
void ImmersiveFullscreenController::BubbleManager::StartObserving(
aura::Window* bubble) {
if (bubbles_.insert(bubble).second) {
bubble->AddObserver(this);
UpdateRevealedLock();
}
}
void ImmersiveFullscreenController::BubbleManager::StopObserving(
aura::Window* bubble) {
if (bubbles_.erase(bubble)) {
bubble->RemoveObserver(this);
UpdateRevealedLock();
}
}
void ImmersiveFullscreenController::BubbleManager::UpdateRevealedLock() {
bool has_visible_bubble = false;
for (std::set<aura::Window*>::const_iterator it = bubbles_.begin();
it != bubbles_.end(); ++it) {
if ((*it)->IsVisible()) {
has_visible_bubble = true;
break;
}
}
bool was_revealed = controller_->IsRevealed();
if (has_visible_bubble) {
if (!revealed_lock_.get()) {
revealed_lock_.reset(controller_->GetRevealedLock(
ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
}
} else {
revealed_lock_.reset();
}
if (!was_revealed && revealed_lock_.get()) {
for (std::set<aura::Window*>::const_iterator it = bubbles_.begin();
it != bubbles_.end(); ++it) {
AsBubbleDelegate(*it)->OnAnchorBoundsChanged();
}
}
}
void ImmersiveFullscreenController::BubbleManager::OnWindowVisibilityChanged(
aura::Window*,
bool visible) {
UpdateRevealedLock();
}
void ImmersiveFullscreenController::BubbleManager::OnWindowDestroying(
aura::Window* window) {
StopObserving(window);
}
ImmersiveFullscreenController::ImmersiveFullscreenController()
: delegate_(NULL),
top_container_(NULL),
widget_(NULL),
native_window_(NULL),
observers_enabled_(false),
enabled_(false),
reveal_state_(CLOSED),
revealed_lock_count_(0),
mouse_x_when_hit_top_in_screen_(-1),
gesture_begun_(false),
animation_(new gfx::SlideAnimation(this)),
animations_disabled_for_test_(false),
weak_ptr_factory_(this) {
}
ImmersiveFullscreenController::~ImmersiveFullscreenController() {
EnableWindowObservers(false);
}
void ImmersiveFullscreenController::Init(Delegate* delegate,
views::Widget* widget,
views::View* top_container) {
delegate_ = delegate;
top_container_ = top_container;
widget_ = widget;
native_window_ = widget_->GetNativeWindow();
native_window_->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
new ResizeHandleWindowTargeter(native_window_, this)));
}
void ImmersiveFullscreenController::SetEnabled(WindowType window_type,
bool enabled) {
if (enabled_ == enabled)
return;
enabled_ = enabled;
EnableWindowObservers(enabled_);
wm::GetWindowState(native_window_)->set_hide_shelf_when_fullscreen(!enabled);
Shell::GetInstance()->UpdateShelfVisibility();
if (enabled_) {
MaybeStartReveal(ANIMATE_NO);
located_event_revealed_lock_.reset();
focus_revealed_lock_.reset();
MaybeEndReveal(ANIMATE_SLOW);
if (reveal_state_ == REVEALED) {
UpdateLocatedEventRevealedLock(NULL);
UpdateFocusRevealedLock();
} else {
widget_->GetFocusManager()->ClearFocus();
}
} else {
top_edge_hover_timer_.Stop();
reveal_state_ = CLOSED;
delegate_->OnImmersiveFullscreenExited();
}
if (enabled_) {
UMA_HISTOGRAM_ENUMERATION("Ash.ImmersiveFullscreen.WindowType",
window_type,
WINDOW_TYPE_COUNT);
}
}
bool ImmersiveFullscreenController::IsEnabled() const {
return enabled_;
}
bool ImmersiveFullscreenController::IsRevealed() const {
return enabled_ && reveal_state_ != CLOSED;
}
ImmersiveRevealedLock* ImmersiveFullscreenController::GetRevealedLock(
AnimateReveal animate_reveal) {
return new ImmersiveRevealedLock(weak_ptr_factory_.GetWeakPtr(),
animate_reveal);
}
void ImmersiveFullscreenController::SetupForTest() {
DCHECK(!enabled_);
animations_disabled_for_test_ = true;
std::vector<gfx::Rect> bounds_in_screen(
delegate_->GetVisibleBoundsInScreen());
DCHECK(!bounds_in_screen.empty());
int bottommost_in_screen = bounds_in_screen[0].bottom();
for (size_t i = 1; i < bounds_in_screen.size(); ++i) {
if (bounds_in_screen[i].bottom() > bottommost_in_screen)
bottommost_in_screen = bounds_in_screen[i].bottom();
}
gfx::Point cursor_pos(0, bottommost_in_screen + 100);
aura::Env::GetInstance()->set_last_mouse_location(cursor_pos);
UpdateLocatedEventRevealedLock(NULL);
}
void ImmersiveFullscreenController::OnMouseEvent(ui::MouseEvent* event) {
if (!enabled_)
return;
if (event->type() != ui::ET_MOUSE_MOVED &&
event->type() != ui::ET_MOUSE_PRESSED &&
event->type() != ui::ET_MOUSE_RELEASED &&
event->type() != ui::ET_MOUSE_CAPTURE_CHANGED) {
return;
}
if (reveal_state_ == SLIDING_OPEN || reveal_state_ == REVEALED) {
top_edge_hover_timer_.Stop();
UpdateLocatedEventRevealedLock(event);
} else if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED) {
UpdateTopEdgeHoverTimer(event);
}
}
void ImmersiveFullscreenController::OnTouchEvent(ui::TouchEvent* event) {
if (!enabled_ || event->type() != ui::ET_TOUCH_PRESSED)
return;
if (!widget_->IsActive())
return;
UpdateLocatedEventRevealedLock(event);
}
void ImmersiveFullscreenController::OnGestureEvent(ui::GestureEvent* event) {
if (!enabled_)
return;
if (!widget_->IsActive())
return;
switch (event->type()) {
case ui::ET_GESTURE_SCROLL_BEGIN:
if (ShouldHandleGestureEvent(GetEventLocationInScreen(*event))) {
gesture_begun_ = true;
}
break;
case ui::ET_GESTURE_SCROLL_UPDATE:
if (gesture_begun_) {
if (UpdateRevealedLocksForSwipe(GetSwipeType(event)))
event->SetHandled();
gesture_begun_ = false;
}
break;
case ui::ET_GESTURE_SCROLL_END:
case ui::ET_SCROLL_FLING_START:
gesture_begun_ = false;
break;
default:
break;
}
}
void ImmersiveFullscreenController::OnWillChangeFocus(
views::View* focused_before,
views::View* focused_now) {
}
void ImmersiveFullscreenController::OnDidChangeFocus(
views::View* focused_before,
views::View* focused_now) {
UpdateFocusRevealedLock();
}
void ImmersiveFullscreenController::OnWidgetDestroying(views::Widget* widget) {
EnableWindowObservers(false);
native_window_ = NULL;
enabled_ = false;
}
void ImmersiveFullscreenController::OnWidgetActivationChanged(
views::Widget* widget,
bool active) {
UpdateFocusRevealedLock();
}
void ImmersiveFullscreenController::AnimationEnded(
const gfx::Animation* animation) {
if (reveal_state_ == SLIDING_OPEN) {
OnSlideOpenAnimationCompleted();
} else if (reveal_state_ == SLIDING_CLOSED) {
OnSlideClosedAnimationCompleted();
}
}
void ImmersiveFullscreenController::AnimationProgressed(
const gfx::Animation* animation) {
delegate_->SetVisibleFraction(animation->GetCurrentValue());
}
void ImmersiveFullscreenController::OnTransientChildAdded(
aura::Window* window,
aura::Window* transient) {
views::BubbleDelegateView* bubble_delegate = AsBubbleDelegate(transient);
if (bubble_delegate &&
bubble_delegate->GetAnchorView() &&
top_container_->Contains(bubble_delegate->GetAnchorView())) {
bubble_manager_->StartObserving(transient);
}
}
void ImmersiveFullscreenController::OnTransientChildRemoved(
aura::Window* window,
aura::Window* transient) {
bubble_manager_->StopObserving(transient);
}
void ImmersiveFullscreenController::LockRevealedState(
AnimateReveal animate_reveal) {
++revealed_lock_count_;
Animate animate = (animate_reveal == ANIMATE_REVEAL_YES) ?
ANIMATE_FAST : ANIMATE_NO;
MaybeStartReveal(animate);
}
void ImmersiveFullscreenController::UnlockRevealedState() {
--revealed_lock_count_;
DCHECK_GE(revealed_lock_count_, 0);
if (revealed_lock_count_ == 0) {
MaybeEndReveal(ANIMATE_FAST);
}
}
void ImmersiveFullscreenController::EnableWindowObservers(bool enable) {
if (observers_enabled_ == enable)
return;
observers_enabled_ = enable;
views::FocusManager* focus_manager = widget_->GetFocusManager();
if (enable) {
widget_->AddObserver(this);
focus_manager->AddFocusChangeListener(this);
Shell::GetInstance()->AddPreTargetHandler(this);
::wm::TransientWindowManager::Get(native_window_)->
AddObserver(this);
RecreateBubbleManager();
} else {
widget_->RemoveObserver(this);
focus_manager->RemoveFocusChangeListener(this);
Shell::GetInstance()->RemovePreTargetHandler(this);
::wm::TransientWindowManager::Get(native_window_)->
RemoveObserver(this);
bubble_manager_.reset();
animation_->Stop();
}
}
void ImmersiveFullscreenController::UpdateTopEdgeHoverTimer(
ui::MouseEvent* event) {
DCHECK(enabled_);
DCHECK(reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED);
if (!top_edge_hover_timer_.IsRunning() &&
!native_window_->Contains(static_cast<aura::Window*>(event->target()))) {
return;
}
if (aura::client::GetCaptureWindow(native_window_))
return;
gfx::Point location_in_screen = GetEventLocationInScreen(*event);
if (ShouldIgnoreMouseEventAtLocation(location_in_screen))
return;
gfx::Rect hit_bounds_in_screen = GetDisplayBoundsInScreen(native_window_);
hit_bounds_in_screen.set_height(kMouseRevealBoundsHeight);
if (!hit_bounds_in_screen.Contains(location_in_screen)) {
top_edge_hover_timer_.Stop();
return;
}
if (top_edge_hover_timer_.IsRunning() &&
abs(location_in_screen.x() - mouse_x_when_hit_top_in_screen_) <=
kMouseRevealXThresholdPixels)
return;
mouse_x_when_hit_top_in_screen_ = location_in_screen.x();
top_edge_hover_timer_.Stop();
top_edge_hover_timer_.Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(kMouseRevealDelayMs),
base::Bind(
&ImmersiveFullscreenController::AcquireLocatedEventRevealedLock,
base::Unretained(this)));
}
void ImmersiveFullscreenController::UpdateLocatedEventRevealedLock(
ui::LocatedEvent* event) {
if (!enabled_)
return;
DCHECK(!event || event->IsMouseEvent() || event->IsTouchEvent());
if (reveal_state_ == CLOSED || reveal_state_ == SLIDING_CLOSED)
return;
if (aura::client::GetCaptureWindow(native_window_))
return;
gfx::Point location_in_screen;
if (event && event->type() != ui::ET_MOUSE_CAPTURE_CHANGED) {
location_in_screen = GetEventLocationInScreen(*event);
} else {
aura::client::CursorClient* cursor_client = aura::client::GetCursorClient(
native_window_->GetRootWindow());
if (!cursor_client->IsMouseEventsEnabled()) {
return;
}
location_in_screen = aura::Env::GetInstance()->last_mouse_location();
}
if ((!event || event->IsMouseEvent()) &&
ShouldIgnoreMouseEventAtLocation(location_in_screen)) {
return;
}
std::vector<gfx::Rect> hit_bounds_in_screen =
delegate_->GetVisibleBoundsInScreen();
bool keep_revealed = false;
for (size_t i = 0; i < hit_bounds_in_screen.size(); ++i) {
if (event && event->type() == ui::ET_MOUSE_MOVED) {
const int kBoundsOffsetY = 8;
hit_bounds_in_screen[i].Inset(0, 0, 0, -kBoundsOffsetY);
}
if (hit_bounds_in_screen[i].Contains(location_in_screen)) {
keep_revealed = true;
break;
}
}
if (keep_revealed)
AcquireLocatedEventRevealedLock();
else
located_event_revealed_lock_.reset();
}
void ImmersiveFullscreenController::AcquireLocatedEventRevealedLock() {
if (!located_event_revealed_lock_.get())
located_event_revealed_lock_.reset(GetRevealedLock(ANIMATE_REVEAL_YES));
}
void ImmersiveFullscreenController::UpdateFocusRevealedLock() {
if (!enabled_)
return;
bool hold_lock = false;
if (widget_->IsActive()) {
views::View* focused_view = widget_->GetFocusManager()->GetFocusedView();
if (top_container_->Contains(focused_view))
hold_lock = true;
} else {
aura::Window* active_window = aura::client::GetActivationClient(
native_window_->GetRootWindow())->GetActiveWindow();
views::BubbleDelegateView* bubble_delegate =
AsBubbleDelegate(active_window);
if (bubble_delegate && bubble_delegate->anchor_widget()) {
} else {
if (IsRevealed() &&
IsWindowTransientChildOf(active_window, native_window_)) {
hold_lock = true;
}
}
}
if (hold_lock) {
if (!focus_revealed_lock_.get())
focus_revealed_lock_.reset(GetRevealedLock(ANIMATE_REVEAL_YES));
} else {
focus_revealed_lock_.reset();
}
}
bool ImmersiveFullscreenController::UpdateRevealedLocksForSwipe(
SwipeType swipe_type) {
if (!enabled_ || swipe_type == SWIPE_NONE)
return false;
DCHECK(widget_->IsActive());
if (reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED) {
if (swipe_type == SWIPE_OPEN && !located_event_revealed_lock_.get()) {
located_event_revealed_lock_.reset(GetRevealedLock(ANIMATE_REVEAL_YES));
return true;
}
} else {
if (swipe_type == SWIPE_CLOSE) {
located_event_revealed_lock_.reset();
focus_revealed_lock_.reset();
if (reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED) {
widget_->GetFocusManager()->ClearFocus();
return true;
}
UpdateLocatedEventRevealedLock(NULL);
UpdateFocusRevealedLock();
}
}
return false;
}
int ImmersiveFullscreenController::GetAnimationDuration(Animate animate) const {
switch (animate) {
case ANIMATE_NO:
return 0;
case ANIMATE_SLOW:
return kRevealSlowAnimationDurationMs;
case ANIMATE_FAST:
return kRevealFastAnimationDurationMs;
}
NOTREACHED();
return 0;
}
void ImmersiveFullscreenController::MaybeStartReveal(Animate animate) {
if (!enabled_)
return;
if (animations_disabled_for_test_)
animate = ANIMATE_NO;
if (reveal_state_ == REVEALED ||
(reveal_state_ == SLIDING_OPEN && animate != ANIMATE_NO)) {
return;
}
RevealState previous_reveal_state = reveal_state_;
reveal_state_ = SLIDING_OPEN;
if (previous_reveal_state == CLOSED) {
delegate_->OnImmersiveRevealStarted();
if (reveal_state_ != SLIDING_OPEN)
return;
}
if (animate == ANIMATE_NO) {
animation_->Reset(1);
OnSlideOpenAnimationCompleted();
} else {
animation_->SetSlideDuration(GetAnimationDuration(animate));
animation_->Show();
}
}
void ImmersiveFullscreenController::OnSlideOpenAnimationCompleted() {
DCHECK_EQ(SLIDING_OPEN, reveal_state_);
reveal_state_ = REVEALED;
delegate_->SetVisibleFraction(1);
UpdateLocatedEventRevealedLock(NULL);
}
void ImmersiveFullscreenController::MaybeEndReveal(Animate animate) {
if (!enabled_ || revealed_lock_count_ != 0)
return;
if (animations_disabled_for_test_)
animate = ANIMATE_NO;
if (reveal_state_ == CLOSED ||
(reveal_state_ == SLIDING_CLOSED && animate != ANIMATE_NO)) {
return;
}
reveal_state_ = SLIDING_CLOSED;
int duration_ms = GetAnimationDuration(animate);
if (duration_ms > 0) {
animation_->SetSlideDuration(duration_ms);
animation_->Hide();
} else {
animation_->Reset(0);
OnSlideClosedAnimationCompleted();
}
}
void ImmersiveFullscreenController::OnSlideClosedAnimationCompleted() {
DCHECK_EQ(SLIDING_CLOSED, reveal_state_);
reveal_state_ = CLOSED;
delegate_->OnImmersiveRevealEnded();
}
ImmersiveFullscreenController::SwipeType
ImmersiveFullscreenController::GetSwipeType(ui::GestureEvent* event) const {
if (event->type() != ui::ET_GESTURE_SCROLL_UPDATE)
return SWIPE_NONE;
if (abs(event->details().scroll_y()) <=
kSwipeVerticalThresholdMultiplier * abs(event->details().scroll_x()))
return SWIPE_NONE;
if (event->details().scroll_y() < 0)
return SWIPE_CLOSE;
else if (event->details().scroll_y() > 0)
return SWIPE_OPEN;
return SWIPE_NONE;
}
bool ImmersiveFullscreenController::ShouldIgnoreMouseEventAtLocation(
const gfx::Point& location) const {
gfx::Rect dead_region = GetDisplayBoundsInScreen(native_window_);
dead_region.set_y(dead_region.y() - kHeightOfDeadRegionAboveTopContainer);
dead_region.set_height(kHeightOfDeadRegionAboveTopContainer);
return dead_region.Contains(location);
}
bool ImmersiveFullscreenController::ShouldHandleGestureEvent(
const gfx::Point& location) const {
DCHECK(widget_->IsActive());
if (reveal_state_ == REVEALED) {
std::vector<gfx::Rect> hit_bounds_in_screen(
delegate_->GetVisibleBoundsInScreen());
for (size_t i = 0; i < hit_bounds_in_screen.size(); ++i) {
if (hit_bounds_in_screen[i].Contains(location))
return true;
}
return false;
}
gfx::Rect hit_bounds_in_screen(GetDisplayBoundsInScreen(native_window_));
hit_bounds_in_screen.set_height(kImmersiveFullscreenTopEdgeInset);
if (hit_bounds_in_screen.Contains(location))
return true;
gfx::Rect screen_bounds =
Shell::GetScreen()->GetDisplayNearestPoint(location).bounds();
return (!screen_bounds.Contains(location) &&
location.y() < hit_bounds_in_screen.y() &&
location.x() >= hit_bounds_in_screen.x() &&
location.x() < hit_bounds_in_screen.right());
}
void ImmersiveFullscreenController::RecreateBubbleManager() {
bubble_manager_.reset(new BubbleManager(this));
const std::vector<aura::Window*> transient_children =
::wm::GetTransientChildren(native_window_);
for (size_t i = 0; i < transient_children.size(); ++i) {
aura::Window* transient_child = transient_children[i];
views::BubbleDelegateView* bubble_delegate =
AsBubbleDelegate(transient_child);
if (bubble_delegate &&
bubble_delegate->GetAnchorView() &&
top_container_->Contains(bubble_delegate->GetAnchorView())) {
bubble_manager_->StartObserving(transient_child);
}
}
}
}