blob: 7d333a027ed0d005d82344138c03739d25ced2ba [file] [log] [blame]
// Copyright 2015 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 "services/ui/ws/server_window_drawn_tracker.h"
#include "services/ui/ws/server_window.h"
#include "services/ui/ws/server_window_drawn_tracker_observer.h"
namespace ui {
namespace ws {
ServerWindowDrawnTracker::ServerWindowDrawnTracker(
ServerWindow* window,
ServerWindowDrawnTrackerObserver* observer)
: window_(window), observer_(observer), drawn_(window->IsDrawn()) {
AddObservers();
}
ServerWindowDrawnTracker::~ServerWindowDrawnTracker() {
RemoveObservers();
}
void ServerWindowDrawnTracker::SetDrawn(ServerWindow* ancestor, bool drawn) {
// If |windows_| is empty when this code runs, that means |window_| has been
// destroyed. So set |window_| to nullptr, but make sure the right value is
// sent to OnDrawnStateChanged().
ServerWindow* window = window_;
if (windows_.empty())
window_ = nullptr;
if (drawn == drawn_)
return;
drawn_ = drawn;
observer_->OnDrawnStateChanged(ancestor, window, drawn);
}
void ServerWindowDrawnTracker::AddObservers() {
if (!window_)
return;
for (ServerWindow* v = window_; v; v = v->parent()) {
v->AddObserver(this);
windows_.insert(v);
}
}
void ServerWindowDrawnTracker::RemoveObservers() {
for (ServerWindow* window : windows_)
window->RemoveObserver(this);
windows_.clear();
}
void ServerWindowDrawnTracker::OnWindowDestroying(ServerWindow* window) {
if (!drawn_)
return;
observer_->OnDrawnStateWillChange(window->parent(), window_, false);
}
void ServerWindowDrawnTracker::OnWindowDestroyed(ServerWindow* window) {
// As windows are removed before being destroyed, resulting in
// OnWindowHierarchyChanged() and us removing ourself as an observer, the only
// window we should ever get notified of destruction on is |window_|.
DCHECK_EQ(window, window_);
RemoveObservers();
SetDrawn(nullptr, false);
}
void ServerWindowDrawnTracker::OnWillChangeWindowHierarchy(
ServerWindow* window,
ServerWindow* new_parent,
ServerWindow* old_parent) {
bool new_is_drawn = new_parent && new_parent->IsDrawn();
if (new_is_drawn) {
for (ServerWindow* w = window_; new_is_drawn && w != old_parent;
w = w->parent()) {
new_is_drawn = w->visible();
}
}
if (drawn_ != new_is_drawn) {
observer_->OnDrawnStateWillChange(new_is_drawn ? nullptr : old_parent,
window_, new_is_drawn);
}
}
void ServerWindowDrawnTracker::OnWindowHierarchyChanged(
ServerWindow* window,
ServerWindow* new_parent,
ServerWindow* old_parent) {
RemoveObservers();
AddObservers();
const bool is_drawn = window_->IsDrawn();
SetDrawn(is_drawn ? nullptr : old_parent, is_drawn);
}
void ServerWindowDrawnTracker::OnWillChangeWindowVisibility(
ServerWindow* window) {
bool will_change = false;
if (drawn_) {
// If |window_| is currently drawn, then any change of visibility of the
// windows will toggle the drawn status.
will_change = true;
} else {
// If |window| is currently visible, then it's becoming invisible, and so
// |window_| will remain not drawn.
if (window->visible()) {
will_change = false;
} else {
bool is_drawn = (window->GetRoot() == window) ||
(window->parent() && window->parent()->IsDrawn());
if (is_drawn) {
for (ServerWindow* w = window_; is_drawn && w != window;
w = w->parent())
is_drawn = w->visible();
}
will_change = drawn_ != is_drawn;
}
}
if (will_change) {
bool new_is_drawn = !drawn_;
observer_->OnDrawnStateWillChange(new_is_drawn ? nullptr : window->parent(),
window_, new_is_drawn);
}
}
void ServerWindowDrawnTracker::OnWindowVisibilityChanged(ServerWindow* window) {
const bool is_drawn = window_->IsDrawn();
SetDrawn(is_drawn ? nullptr : window->parent(), is_drawn);
}
} // namespace ws
} // namespace ui