Skip to content

Commit

Permalink
Consolidate focus handling a bit more in the new FocusController.
Browse files Browse the repository at this point in the history
There are now three phases of focus change:

- input phase (from an input event, window disposition change or API call)
- query phase (determination of what window should be focused)
- exec phase (state is updated and events dispatched).

Secondly, adds activation support and events. Currently passing BasicActivation and ActivationEvents test cases. I don't think we have a use case to support parent (hierarchy) changes to activation, so I am not supporting this for now as it makes the tests considerably more baroque.

http://crbug.com/162100
R=sky@chromium.org
Review URL: https://codereview.chromium.org/11412173

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@169580 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
ben@chromium.org committed Nov 27, 2012
1 parent 751ec7a commit 75d8fad
Show file tree
Hide file tree
Showing 4 changed files with 272 additions and 114 deletions.
36 changes: 31 additions & 5 deletions ui/views/corewm/base_focus_rules.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "ui/views/corewm/base_focus_rules.h"

#include "ui/aura/root_window.h"
#include "ui/aura/window.h"

namespace views {
Expand All @@ -22,8 +23,8 @@ BaseFocusRules::~BaseFocusRules() {
// BaseFocusRules, FocusRules implementation:

bool BaseFocusRules::CanActivateWindow(aura::Window* window) {
// TODO(beng):
return true;
return !window ||
(window->IsVisible() && window->parent() == window->GetRootWindow());
}

bool BaseFocusRules::CanFocusWindow(aura::Window* window) {
Expand All @@ -32,8 +33,16 @@ bool BaseFocusRules::CanFocusWindow(aura::Window* window) {
}

aura::Window* BaseFocusRules::GetActivatableWindow(aura::Window* window) {
// TODO(beng):
return window;
// BasicFocusRules considers only direct children of the RootWindow as
// activatable.
aura::Window* parent = window->parent();
aura::Window* activatable = window;
aura::RootWindow* root_window = window->GetRootWindow();
while (parent != root_window) {
activatable = parent;
parent = parent->parent();
}
return activatable;
}

aura::Window* BaseFocusRules::GetFocusableWindow(aura::Window* window) {
Expand All @@ -45,7 +54,24 @@ aura::Window* BaseFocusRules::GetFocusableWindow(aura::Window* window) {
aura::Window* BaseFocusRules::GetNextActivatableWindow(aura::Window* ignore) {
DCHECK(ignore);

// TODO(beng):
// Can be called from the RootWindow's destruction, which has a NULL parent.
if (!ignore->parent())
return NULL;

// In the basic scenarios handled by BasicFocusRules, the pool of activatable
// windows is limited to the |ignore|'s siblings.
const aura::Window::Windows& siblings = ignore->parent()->children();
DCHECK(!siblings.empty());

for (aura::Window::Windows::const_reverse_iterator rit = siblings.rbegin();
rit != siblings.rend();
++rit) {
aura::Window* cur = *rit;
if (cur == ignore)
continue;
if (CanActivateWindow(cur))
return cur;
}
return NULL;
}

Expand Down
145 changes: 98 additions & 47 deletions ui/views/corewm/focus_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,49 @@

#include "ui/views/corewm/focus_controller.h"

#include "base/auto_reset.h"
#include "ui/aura/env.h"
#include "ui/views/corewm/focus_change_event.h"
#include "ui/views/corewm/focus_rules.h"

namespace views {
namespace corewm {
namespace {

// Updates focused window state and dispatches changing/changed events.
void DispatchEventsAndUpdateState(ui::EventDispatcher* dispatcher,
int changing_event_type,
int changed_event_type,
aura::Window** state,
aura::Window* new_state,
ui::EventTarget** event_dispatch_target) {
int result = ui::ER_UNHANDLED;
{
base::AutoReset<ui::EventTarget*> reset(event_dispatch_target, *state);
FocusChangeEvent changing_event(changing_event_type);
result = dispatcher->ProcessEvent(*state, &changing_event);
}
DCHECK(!(result & ui::ER_CONSUMED))
<< "Focus and Activation events cannot be consumed";

*state = new_state;

{
base::AutoReset<ui::EventTarget*> reset(event_dispatch_target, *state);
FocusChangeEvent changed_event(changed_event_type);
dispatcher->ProcessEvent(*state, &changed_event);
}
}

} // namespace

////////////////////////////////////////////////////////////////////////////////
// FocusController, public:

FocusController::FocusController(FocusRules* rules)
: active_window_(NULL),
focused_window_(NULL),
event_dispatch_target_(NULL),
rules_(rules) {
DCHECK(rules);
FocusChangeEvent::RegisterEventTypes();
Expand All @@ -27,19 +57,8 @@ FocusController::~FocusController() {
aura::Env::GetInstance()->RemoveObserver(this);
}

void FocusController::SetFocusedWindow(aura::Window* window) {
DCHECK(rules_->CanFocusWindow(window));
// TODO(beng): dispatch changing events.
FocusChangeEvent changing_event(
FocusChangeEvent::focus_changing_event_type());
int result = ProcessEvent(focused_window_, &changing_event);
if (result & ui::ER_CONSUMED)
return;

focused_window_ = window;

FocusChangeEvent changed_event(FocusChangeEvent::focus_changed_event_type());
ProcessEvent(focused_window_, &changed_event);
void FocusController::FocusWindow(aura::Window* window) {
SetFocusedWindow(window);
}

////////////////////////////////////////////////////////////////////////////////
Expand All @@ -56,16 +75,15 @@ void FocusController::RemoveObserver(
}

void FocusController::ActivateWindow(aura::Window* window) {
// TODO(beng):
SetActiveWindow(rules_->GetActivatableWindow(window));
}

void FocusController::DeactivateWindow(aura::Window* window) {
// TODO(beng):
SetActiveWindow(rules_->GetNextActivatableWindow(window));
}

aura::Window* FocusController::GetActiveWindow() {
// TODO(beng):
return NULL;
return active_window_;
}

bool FocusController::OnWillFocusWindow(aura::Window* window,
Expand All @@ -85,11 +103,8 @@ ui::EventResult FocusController::OnKeyEvent(ui::KeyEvent* event) {
}

ui::EventResult FocusController::OnMouseEvent(ui::MouseEvent* event) {
// TODO(beng): GetFocusableWindow().
if (event->type() == ui::ET_MOUSE_PRESSED) {
SetFocusedWindow(rules_->GetFocusableWindow(
static_cast<aura::Window*>(event->target())));
}
if (event->type() == ui::ET_MOUSE_PRESSED)
WindowFocusedFromInputEvent(static_cast<aura::Window*>(event->target()));
return ui::ER_UNHANDLED;
}

Expand All @@ -102,11 +117,9 @@ ui::EventResult FocusController::OnTouchEvent(ui::TouchEvent* event) {
}

ui::EventResult FocusController::OnGestureEvent(ui::GestureEvent* event) {
// TODO(beng): GetFocusableWindow().
if (event->type() == ui::ET_GESTURE_BEGIN &&
event->details().touch_points() == 1) {
SetFocusedWindow(rules_->GetFocusableWindow(
static_cast<aura::Window*>(event->target())));
WindowFocusedFromInputEvent(static_cast<aura::Window*>(event->target()));
}
return ui::ER_UNHANDLED;
}
Expand All @@ -119,34 +132,20 @@ void FocusController::OnWindowVisibilityChanging(aura::Window* window,
// We need to process this change in VisibilityChanging while the window is
// still visible, since focus events cannot be dispatched to invisible
// windows.
// TODO(beng): evaluate whether or not the visibility restriction is worth
// enforcing for events that aren't user-input.
if (window->Contains(focused_window_))
SetFocusedWindow(rules_->GetNextFocusableWindow(window));
if (!visible)
WindowLostFocusFromDispositionChange(window);
}

if (window->Contains(active_window_)) {
// TODO(beng): Reset active window.
}
void FocusController::OnWindowDestroying(aura::Window* window) {
WindowLostFocusFromDispositionChange(window);
}

void FocusController::OnWindowDestroyed(aura::Window* window) {
window->RemoveObserver(this);

if (window->Contains(focused_window_))
SetFocusedWindow(rules_->GetNextFocusableWindow(window));

if (window->Contains(active_window_)) {
// TODO(beng): Reset active window.
}
}

void FocusController::OnWillRemoveWindow(aura::Window* window) {
if (window->Contains(focused_window_))
SetFocusedWindow(rules_->GetNextFocusableWindow(window));

if (window->Contains(active_window_)) {
// TODO(beng): Reset active window.
}
WindowLostFocusFromDispositionChange(window);
}

void FocusController::OnWindowInitialized(aura::Window* window) {
Expand All @@ -157,8 +156,60 @@ void FocusController::OnWindowInitialized(aura::Window* window) {
// FocusController, ui::EventDispatcher implementation:

bool FocusController::CanDispatchToTarget(ui::EventTarget* target) {
// TODO(beng):
return true;
return target == event_dispatch_target_;
}

////////////////////////////////////////////////////////////////////////////////
// FocusController, private:

void FocusController::SetFocusedWindow(aura::Window* window) {
DCHECK(rules_->CanFocusWindow(window));
if (window)
DCHECK_EQ(window, rules_->GetFocusableWindow(window));
DispatchEventsAndUpdateState(
this,
FocusChangeEvent::focus_changing_event_type(),
FocusChangeEvent::focus_changed_event_type(),
&focused_window_,
window,
&event_dispatch_target_);
}

void FocusController::SetActiveWindow(aura::Window* window) {
DCHECK(rules_->CanActivateWindow(window));
if (window)
DCHECK_EQ(window, rules_->GetActivatableWindow(window));
DispatchEventsAndUpdateState(
this,
FocusChangeEvent::activation_changing_event_type(),
FocusChangeEvent::activation_changed_event_type(),
&active_window_,
window,
&event_dispatch_target_);
}

void FocusController::WindowLostFocusFromDispositionChange(
aura::Window* window) {
// Activation adjustments are handled first in the event of a disposition
// changed. If an activation change is necessary, focus is reset as part of
// that process so there's no point in updating focus independently.
if (window->Contains(active_window_)) {
aura::Window* next_activatable = rules_->GetNextActivatableWindow(window);
SetActiveWindow(next_activatable);
SetFocusedWindow(next_activatable);
} else if (window->Contains(focused_window_)) {
// Active window isn't changing, but focused window might be.
SetFocusedWindow(rules_->GetNextFocusableWindow(window));
}
}

void FocusController::WindowFocusedFromInputEvent(aura::Window* window) {
aura::Window* activatable = rules_->GetActivatableWindow(window);
SetActiveWindow(activatable);

// This handles switching focus via input events within the active window.
SetFocusedWindow(rules_->GetFocusableWindow(
activatable->Contains(window) ? window : activatable));
}

} // namespace corewm
Expand Down
21 changes: 20 additions & 1 deletion ui/views/corewm/focus_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class VIEWS_EXPORT FocusController : public aura::client::ActivationClient,
// TODO(beng): FocusClient
// Sets the focused window, resulting in focus change events being sent.
// Must only be called with valid focusable windows per FocusRules.
void SetFocusedWindow(aura::Window* window);
void FocusWindow(aura::Window* window);
aura::Window* focused_window() { return focused_window_; }

private:
Expand All @@ -70,6 +70,7 @@ class VIEWS_EXPORT FocusController : public aura::client::ActivationClient,
// Overridden from aura::WindowObserver:
virtual void OnWindowVisibilityChanging(aura::Window* window,
bool visible) OVERRIDE;
virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE;

Expand All @@ -79,9 +80,27 @@ class VIEWS_EXPORT FocusController : public aura::client::ActivationClient,
// Overridden from ui::EventDispatcher:
virtual bool CanDispatchToTarget(ui::EventTarget* target) OVERRIDE;

// Internal implementations that set the focused/active windows, fire events
// etc. These functions must be called with valid focusable/activatable
// windows.
void SetFocusedWindow(aura::Window* window);
void SetActiveWindow(aura::Window* window);

// Called when a window's disposition changed such that it and its hierarchy
// are no longer focusable/activatable. The system must determine what window
// to focus next based on rules.
void WindowLostFocusFromDispositionChange(aura::Window* window);

// Called when an attempt is made to focus or activate a window via an input
// event targeted at that window. Rules determine the best focusable window
// for the input window.
void WindowFocusedFromInputEvent(aura::Window* window);

aura::Window* active_window_;
aura::Window* focused_window_;

ui::EventTarget* event_dispatch_target_;

scoped_ptr<FocusRules> rules_;

DISALLOW_COPY_AND_ASSIGN(FocusController);
Expand Down
Loading

0 comments on commit 75d8fad

Please sign in to comment.