Commit f2367049 authored by Evan Stade's avatar Evan Stade Committed by Commit Bot

Create ImmersiveFocusWatcher for use in Chrome in Mash.

This keeps the immersive reveal going when bubbles anchored to the top
container (like the bookmark bubble) are open, when focus in in the top
container, or when transient children (like the bookmark editor) are
open.

ImmersiveFocusWatcherMus is largely copied from
ImmersiveFocusWatcherClassic, but
1. uses aura::client::TransientWindowClient instead of
   ::wm::TransientWindowManager
2. uses WindowTreeClient::FocusSynchronizer instead of
   ::wm::GetActivationClient
3. Appends |->GetRootWindow()| in a couple places.

Bug: 640365
Change-Id: I46cd0c3d66808d726fea484ff13ceea53dcabd5c
Reviewed-on: https://chromium-review.googlesource.com/1187708
Commit-Queue: Evan Stade <estade@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/master@{#586323}
parent 7f19d899
......@@ -1899,6 +1899,8 @@ jumbo_split_static_library("ui") {
"views/frame/browser_non_client_frame_view_ash.h",
"views/frame/immersive_context_mus.cc",
"views/frame/immersive_context_mus.h",
"views/frame/immersive_focus_watcher_mus.cc",
"views/frame/immersive_focus_watcher_mus.h",
"views/frame/immersive_handler_factory_mus.cc",
"views/frame/immersive_handler_factory_mus.h",
"views/frame/immersive_mode_controller_ash.cc",
......
// 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 "chrome/browser/ui/views/frame/immersive_focus_watcher_mus.h"
#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
#include "ui/aura/client/transient_window_client.h"
#include "ui/aura/mus/focus_synchronizer.h"
#include "ui/aura/mus/window_tree_client.h"
#include "ui/aura/window.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/mus/mus_client.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/public/activation_client.h"
namespace {
// Returns the BubbleDialogDelegateView corresponding to |maybe_bubble| if
// |maybe_bubble| is a bubble.
views::BubbleDialogDelegateView* AsBubbleDialogDelegate(
aura::Window* maybe_bubble) {
if (!maybe_bubble)
return nullptr;
views::Widget* widget = views::Widget::GetWidgetForNativeView(maybe_bubble);
if (!widget)
return nullptr;
return widget->widget_delegate()->AsBubbleDialogDelegate();
}
views::View* GetAnchorView(aura::Window* maybe_bubble) {
views::BubbleDialogDelegateView* bubble_dialog =
AsBubbleDialogDelegate(maybe_bubble);
return bubble_dialog ? bubble_dialog->GetAnchorView() : nullptr;
}
// Returns true if |maybe_transient| is a transient child of |toplevel|.
bool IsWindowTransientChildOf(aura::Window* maybe_transient,
aura::Window* toplevel) {
if (!maybe_transient || !toplevel)
return false;
aura::client::TransientWindowClient* transient_window_client =
aura::client::GetTransientWindowClient();
for (aura::Window* window = maybe_transient; window;
window = transient_window_client->GetTransientParent(window)) {
if (window == toplevel)
return true;
}
return false;
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
// Class which keeps the top-of-window views revealed as long as one of the
// bubbles it is observing is visible. The logic to keep the top-of-window
// views revealed based on the visibility of bubbles anchored to
// children of |ImmersiveFullscreenController::top_container_| is separate from
// the logic related to |ImmersiveFullscreenController::focus_revealed_lock_|
// so that bubbles which are not activatable and bubbles which do not close
// upon deactivation also keep the top-of-window views revealed for the
// duration of their visibility.
class ImmersiveFocusWatcherMus::BubbleObserver : public aura::WindowObserver {
public:
explicit BubbleObserver(ash::ImmersiveFullscreenController* controller);
~BubbleObserver() override;
// Start / stop observing changes to |bubble|'s visibility.
void StartObserving(aura::Window* bubble);
void StopObserving(aura::Window* bubble);
private:
// Updates |revealed_lock_| based on whether any of |bubbles_| is visible.
void UpdateRevealedLock();
// aura::WindowObserver overrides:
void OnWindowVisibilityChanged(aura::Window* window, bool visible) override;
void OnWindowDestroying(aura::Window* window) override;
ash::ImmersiveFullscreenController* controller_;
std::set<aura::Window*> bubbles_;
// Lock which keeps the top-of-window views revealed based on whether any of
// |bubbles_| is visible.
std::unique_ptr<ash::ImmersiveRevealedLock> revealed_lock_;
DISALLOW_COPY_AND_ASSIGN(BubbleObserver);
};
ImmersiveFocusWatcherMus::BubbleObserver::BubbleObserver(
ash::ImmersiveFullscreenController* controller)
: controller_(controller) {}
ImmersiveFocusWatcherMus::BubbleObserver::~BubbleObserver() {
for (aura::Window* bubble : bubbles_)
bubble->RemoveObserver(this);
}
void ImmersiveFocusWatcherMus::BubbleObserver::StartObserving(
aura::Window* bubble) {
if (bubbles_.insert(bubble).second) {
bubble->AddObserver(this);
UpdateRevealedLock();
}
}
void ImmersiveFocusWatcherMus::BubbleObserver::StopObserving(
aura::Window* bubble) {
if (bubbles_.erase(bubble)) {
bubble->RemoveObserver(this);
UpdateRevealedLock();
}
}
void ImmersiveFocusWatcherMus::BubbleObserver::UpdateRevealedLock() {
bool has_visible_bubble = false;
for (aura::Window* bubble : bubbles_) {
if (bubble->IsVisible()) {
has_visible_bubble = true;
break;
}
}
bool was_revealed = controller_->IsRevealed();
if (has_visible_bubble) {
if (!revealed_lock_.get()) {
// Reveal the top-of-window views without animating because it looks
// weird for the top-of-window views to animate and the bubble not to
// animate along with the top-of-window views.
revealed_lock_.reset(controller_->GetRevealedLock(
ash::ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
}
} else {
revealed_lock_.reset();
}
if (!was_revealed && revealed_lock_.get()) {
// Currently, there is no nice way for bubbles to reposition themselves
// whenever the anchor view moves. Tell the bubbles to reposition themselves
// explicitly instead. The hidden bubbles are also repositioned because
// BubbleDialogDelegateView does not reposition its widget as a result of a
// visibility change.
for (aura::Window* bubble : bubbles_)
AsBubbleDialogDelegate(bubble)->OnAnchorBoundsChanged();
}
}
void ImmersiveFocusWatcherMus::BubbleObserver::OnWindowVisibilityChanged(
aura::Window*,
bool visible) {
UpdateRevealedLock();
}
void ImmersiveFocusWatcherMus::BubbleObserver::OnWindowDestroying(
aura::Window* window) {
StopObserving(window);
}
ImmersiveFocusWatcherMus::ImmersiveFocusWatcherMus(
ash::ImmersiveFullscreenController* controller)
: immersive_fullscreen_controller_(controller) {
GetWidget()->GetFocusManager()->AddFocusChangeListener(this);
aura::client::GetTransientWindowClient()->AddObserver(this);
::wm::GetActivationClient(GetWidgetWindow())->AddObserver(this);
RecreateBubbleObserver();
}
ImmersiveFocusWatcherMus::~ImmersiveFocusWatcherMus() {
aura::client::GetTransientWindowClient()->RemoveObserver(this);
GetWidget()->GetFocusManager()->RemoveFocusChangeListener(this);
::wm::GetActivationClient(GetWidgetWindow())->RemoveObserver(this);
}
void ImmersiveFocusWatcherMus::UpdateFocusRevealedLock() {
views::Widget* widget = GetWidget();
views::View* top_container =
immersive_fullscreen_controller_->top_container();
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* native_window = GetWidgetWindow();
aura::Window* active_window = views::MusClient::Get()
->window_tree_client()
->focus_synchronizer()
->active_focus_client_root();
if (GetAnchorView(active_window)) {
// BubbleObserver will already have locked the top-of-window views if the
// bubble is anchored to a child of |top_container|. Don't acquire
// |lock_| here for the sake of simplicity.
// Note: Instead of checking for the existence of the |anchor_view|,
// the existence of the |anchor_widget| is performed to avoid the case
// where the view is already gone (and the widget is still running).
} else {
// The currently active window is not |native_window| and it is not a
// bubble with an anchor view. The top-of-window views should be revealed
// if:
// 1) The active window is a transient child of |native_window|.
// 2) The top-of-window views are already revealed. This restriction
// prevents a transient window opened by the web contents while the
// top-of-window views are hidden from from initiating a reveal.
// The top-of-window views will stay revealed till |native_window| is
// reactivated.
if (immersive_fullscreen_controller_->IsRevealed() &&
IsWindowTransientChildOf(active_window, native_window)) {
hold_lock = true;
}
}
}
if (hold_lock) {
if (!lock_.get()) {
lock_.reset(immersive_fullscreen_controller_->GetRevealedLock(
ash::ImmersiveFullscreenController::ANIMATE_REVEAL_YES));
}
} else {
lock_.reset();
}
}
void ImmersiveFocusWatcherMus::ReleaseLock() {
lock_.reset();
}
views::Widget* ImmersiveFocusWatcherMus::GetWidget() {
return immersive_fullscreen_controller_->widget();
}
aura::Window* ImmersiveFocusWatcherMus::GetWidgetWindow() {
return GetWidget()->GetNativeWindow()->GetRootWindow();
}
void ImmersiveFocusWatcherMus::RecreateBubbleObserver() {
bubble_observer_.reset(new BubbleObserver(immersive_fullscreen_controller_));
const std::vector<aura::Window*> transient_children =
aura::client::GetTransientWindowClient()->GetTransientChildren(
GetWidgetWindow());
for (size_t i = 0; i < transient_children.size(); ++i) {
aura::Window* transient_child = transient_children[i];
views::View* anchor_view = GetAnchorView(transient_child);
if (anchor_view &&
immersive_fullscreen_controller_->top_container()->Contains(
anchor_view))
bubble_observer_->StartObserving(transient_child);
}
}
void ImmersiveFocusWatcherMus::OnWillChangeFocus(views::View* focused_before,
views::View* focused_now) {}
void ImmersiveFocusWatcherMus::OnDidChangeFocus(views::View* focused_before,
views::View* focused_now) {
UpdateFocusRevealedLock();
}
void ImmersiveFocusWatcherMus::OnWindowActivated(
::wm::ActivationChangeObserver::ActivationReason reason,
aura::Window* gaining_active,
aura::Window* losing_active) {
UpdateFocusRevealedLock();
}
void ImmersiveFocusWatcherMus::OnTransientChildWindowAdded(
aura::Window* window,
aura::Window* transient) {
views::View* anchor = GetAnchorView(transient);
if (anchor &&
immersive_fullscreen_controller_->top_container()->Contains(anchor)) {
// Observe the aura::Window because the BubbleDelegateView may not be
// parented to the widget's root view yet so |bubble_delegate->GetWidget()|
// may still return NULL.
bubble_observer_->StartObserving(transient);
}
}
void ImmersiveFocusWatcherMus::OnTransientChildWindowRemoved(
aura::Window* window,
aura::Window* transient) {
bubble_observer_->StopObserving(transient);
}
// 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.
#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_IMMERSIVE_FOCUS_WATCHER_MUS_H_
#define CHROME_BROWSER_UI_VIEWS_FRAME_IMMERSIVE_FOCUS_WATCHER_MUS_H_
#include "ash/public/cpp/immersive/immersive_focus_watcher.h"
#include "ui/aura/client/focus_change_observer.h"
#include "ui/aura/client/transient_window_client_observer.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/wm/public/activation_change_observer.h"
namespace ash {
class ImmersiveFullscreenController;
class ImmersiveRevealedLock;
} // namespace ash
// ImmersiveFocusWatcherMus is responsible for grabbing a reveal lock based on
// activation and/or focus. This implementation grabs a lock if views focus is
// in the top view, a bubble is showing that is anchored to the top view, or
// the focused window is a transient child of the top view's widget.
class ImmersiveFocusWatcherMus
: public ash::ImmersiveFocusWatcher,
public views::FocusChangeListener,
public aura::client::TransientWindowClientObserver,
public ::wm::ActivationChangeObserver {
public:
explicit ImmersiveFocusWatcherMus(
ash::ImmersiveFullscreenController* controller);
~ImmersiveFocusWatcherMus() override;
// ImmersiveFocusWatcher:
void UpdateFocusRevealedLock() override;
void ReleaseLock() override;
private:
class BubbleObserver;
views::Widget* GetWidget();
aura::Window* GetWidgetWindow();
// Recreate |bubble_observer_| and start observing any bubbles anchored to a
// child of |top_container_|.
void RecreateBubbleObserver();
// views::FocusChangeListener overrides:
void OnWillChangeFocus(views::View* focused_before,
views::View* focused_now) override;
void OnDidChangeFocus(views::View* focused_before,
views::View* focused_now) override;
// aura::client::TransientWindowClientObserver overrides:
void OnTransientChildWindowAdded(aura::Window* window,
aura::Window* transient) override;
void OnTransientChildWindowRemoved(aura::Window* window,
aura::Window* transient) override;
// ::wm::ActivationChangeObserver:
void OnWindowActivated(
::wm::ActivationChangeObserver::ActivationReason reason,
aura::Window* gaining_active,
aura::Window* losing_active) override;
ash::ImmersiveFullscreenController* immersive_fullscreen_controller_;
// Lock which keeps the top-of-window views revealed based on the focused view
// and the active widget. Acquiring the lock never triggers a reveal because
// a view is not focusable till a reveal has made it visible.
std::unique_ptr<ash::ImmersiveRevealedLock> lock_;
// Manages bubbles which are anchored to a child of
// |ImmersiveFullscreenController::top_container_|.
std::unique_ptr<BubbleObserver> bubble_observer_;
DISALLOW_COPY_AND_ASSIGN(ImmersiveFocusWatcherMus);
};
#endif // CHROME_BROWSER_UI_VIEWS_FRAME_IMMERSIVE_FOCUS_WATCHER_MUS_H_
......@@ -7,6 +7,7 @@
#include "ash/public/cpp/immersive/immersive_focus_watcher.h"
#include "ash/public/cpp/immersive/immersive_gesture_handler.h"
#include "base/logging.h"
#include "chrome/browser/ui/views/frame/immersive_focus_watcher_mus.h"
ImmersiveHandlerFactoryMus::ImmersiveHandlerFactoryMus() {}
......@@ -15,8 +16,7 @@ ImmersiveHandlerFactoryMus::~ImmersiveHandlerFactoryMus() {}
std::unique_ptr<ash::ImmersiveFocusWatcher>
ImmersiveHandlerFactoryMus::CreateFocusWatcher(
ash::ImmersiveFullscreenController* controller) {
NOTIMPLEMENTED();
return nullptr;
return std::make_unique<ImmersiveFocusWatcherMus>(controller);
}
std::unique_ptr<ash::ImmersiveGestureHandler>
......
......@@ -5,6 +5,8 @@
#ifndef UI_AURA_CLIENT_TRANSIENT_WINDOW_CLIENT_H_
#define UI_AURA_CLIENT_TRANSIENT_WINDOW_CLIENT_H_
#include <vector>
#include "ui/aura/aura_export.h"
namespace aura {
......@@ -29,6 +31,7 @@ class AURA_EXPORT TransientWindowClient {
virtual void RemoveTransientChild(Window* parent, Window* child) = 0;
virtual Window* GetTransientParent(Window* window) = 0;
virtual const Window* GetTransientParent(const Window* window) = 0;
virtual std::vector<Window*> GetTransientChildren(const Window* parent) = 0;
virtual void AddObserver(TransientWindowClientObserver* observer) = 0;
virtual void RemoveObserver(TransientWindowClientObserver* observer) = 0;
......
......@@ -1106,7 +1106,7 @@ bool DesktopNativeWidgetAura::ShouldActivate() const {
}
////////////////////////////////////////////////////////////////////////////////
// DesktopNativeWidgetAura, wmActivationChangeObserver implementation:
// DesktopNativeWidgetAura, wm::ActivationChangeObserver implementation:
void DesktopNativeWidgetAura::OnWindowActivated(
wm::ActivationChangeObserver::ActivationReason reason,
......
......@@ -45,6 +45,15 @@ const aura::Window* TransientWindowController::GetTransientParent(
return window_manager ? window_manager->transient_parent() : nullptr;
}
std::vector<aura::Window*> TransientWindowController::GetTransientChildren(
const aura::Window* parent) {
const TransientWindowManager* window_manager =
TransientWindowManager::GetIfExists(parent);
if (!window_manager)
return {};
return window_manager->transient_children();
}
void TransientWindowController::AddObserver(
aura::client::TransientWindowClientObserver* observer) {
observers_.AddObserver(observer);
......
......@@ -30,6 +30,8 @@ class WM_CORE_EXPORT TransientWindowController
void RemoveTransientChild(aura::Window* parent, aura::Window* child) override;
aura::Window* GetTransientParent(aura::Window* window) override;
const aura::Window* GetTransientParent(const aura::Window* window) override;
std::vector<aura::Window*> GetTransientChildren(
const aura::Window* parent) override;
void AddObserver(
aura::client::TransientWindowClientObserver* observer) override;
void RemoveObserver(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment