Commit f6435d20 authored by ben@chromium.org's avatar ben@chromium.org

Implement reordering of nodes.

Note that this doesn't apply to roots. If an app is embedded and its root node is reordered, there is no notification to the app. I figure this is up to the window manager to expose an API for this type of thing.

R=sky@chromium.org
http://crbug.com/365012

Review URL: https://codereview.chromium.org/327073003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@276768 0039d316-1c4b-4281-b951-d872f2087c98
parent 052e6b15
......@@ -168,6 +168,12 @@ class IViewManagerClientImpl
uint32_t server_change_id,
mojo::Array<view_manager::INodePtr> nodes) OVERRIDE {
}
virtual void OnNodeReordered(
uint32_t node_id,
uint32_t relative_node_id,
view_manager::OrderDirection direction,
uint32_t server_change_id) OVERRIDE {
}
virtual void OnNodeDeleted(uint32_t node, uint32_t server_change_id)
OVERRIDE {
}
......
......@@ -250,6 +250,7 @@
'type': 'static_library',
'sources': [
'services/public/interfaces/view_manager/view_manager.mojom',
'services/public/interfaces/view_manager/view_manager_constants.mojom',
],
'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
'export_dependent_settings': [
......
......@@ -246,52 +246,92 @@ class DestroyViewTreeNodeTransaction : public ViewManagerTransaction {
DISALLOW_COPY_AND_ASSIGN(DestroyViewTreeNodeTransaction);
};
class HierarchyTransaction : public ViewManagerTransaction {
class AddChildTransaction : public ViewManagerTransaction {
public:
enum HierarchyChangeType {
TYPE_ADD,
TYPE_REMOVE
};
HierarchyTransaction(HierarchyChangeType hierarchy_change_type,
Id child_id,
Id parent_id,
ViewManagerSynchronizer* synchronizer)
AddChildTransaction(Id child_id,
Id parent_id,
ViewManagerSynchronizer* synchronizer)
: ViewManagerTransaction(synchronizer),
hierarchy_change_type_(hierarchy_change_type),
child_id_(child_id),
parent_id_(parent_id) {}
virtual ~HierarchyTransaction() {}
virtual ~AddChildTransaction() {}
private:
// Overridden from ViewManagerTransaction:
virtual void DoCommit() OVERRIDE {
switch (hierarchy_change_type_) {
case TYPE_ADD:
service()->AddNode(
parent_id_,
child_id_,
GetAndAdvanceNextServerChangeId(),
ActionCompletedCallback());
break;
case TYPE_REMOVE:
service()->RemoveNodeFromParent(
child_id_,
GetAndAdvanceNextServerChangeId(),
ActionCompletedCallback());
break;
}
service()->AddNode(parent_id_,
child_id_,
GetAndAdvanceNextServerChangeId(),
ActionCompletedCallback());
}
virtual void DoActionCompleted(bool success) OVERRIDE {
// TODO(beng): Failure means either one of the nodes specified didn't exist,
// or we passed the same node id for both params. Roll back?
// TODO(beng): recovery?
}
const HierarchyChangeType hierarchy_change_type_;
const Id child_id_;
const Id parent_id_;
DISALLOW_COPY_AND_ASSIGN(HierarchyTransaction);
DISALLOW_COPY_AND_ASSIGN(AddChildTransaction);
};
class RemoveChildTransaction : public ViewManagerTransaction {
public:
RemoveChildTransaction(Id child_id,
ViewManagerSynchronizer* synchronizer)
: ViewManagerTransaction(synchronizer),
child_id_(child_id) {}
virtual ~RemoveChildTransaction() {}
private:
// Overridden from ViewManagerTransaction:
virtual void DoCommit() OVERRIDE {
service()->RemoveNodeFromParent(
child_id_,
GetAndAdvanceNextServerChangeId(),
ActionCompletedCallback());
}
virtual void DoActionCompleted(bool success) OVERRIDE {
// TODO(beng): recovery?
}
const Id child_id_;
DISALLOW_COPY_AND_ASSIGN(RemoveChildTransaction);
};
class ReorderNodeTransaction : public ViewManagerTransaction {
public:
ReorderNodeTransaction(Id node_id,
Id relative_id,
OrderDirection direction,
ViewManagerSynchronizer* synchronizer)
: ViewManagerTransaction(synchronizer),
node_id_(node_id),
relative_id_(relative_id),
direction_(direction) {}
virtual ~ReorderNodeTransaction() {}
private:
// Overridden from ViewManagerTransaction:
virtual void DoCommit() OVERRIDE {
service()->ReorderNode(node_id_,
relative_id_,
direction_,
GetAndAdvanceNextServerChangeId(),
ActionCompletedCallback());
}
virtual void DoActionCompleted(bool success) OVERRIDE {
// TODO(beng): recovery?
}
const Id node_id_;
const Id relative_id_;
const OrderDirection direction_;
DISALLOW_COPY_AND_ASSIGN(ReorderNodeTransaction);
};
class SetActiveViewTransaction : public ViewManagerTransaction {
......@@ -495,20 +535,23 @@ void ViewManagerSynchronizer::AddChild(Id child_id,
Id parent_id) {
DCHECK(connected_);
pending_transactions_.push_back(
new HierarchyTransaction(HierarchyTransaction::TYPE_ADD,
child_id,
parent_id,
this));
new AddChildTransaction(child_id, parent_id, this));
Sync();
}
void ViewManagerSynchronizer::RemoveChild(Id child_id, Id parent_id) {
DCHECK(connected_);
pending_transactions_.push_back(new RemoveChildTransaction(child_id, this));
Sync();
}
void ViewManagerSynchronizer::Reorder(
Id node_id,
Id relative_node_id,
OrderDirection direction) {
DCHECK(connected_);
pending_transactions_.push_back(
new HierarchyTransaction(HierarchyTransaction::TYPE_REMOVE,
child_id,
parent_id,
this));
new ReorderNodeTransaction(node_id, relative_node_id, direction, this));
Sync();
}
......@@ -638,7 +681,6 @@ void ViewManagerSynchronizer::OnNodeHierarchyChanged(
Id old_parent_id,
Id server_change_id,
mojo::Array<INodePtr> nodes) {
// TODO: deal with |nodes|.
next_server_change_id_ = server_change_id + 1;
BuildNodeTree(this, nodes);
......@@ -652,6 +694,19 @@ void ViewManagerSynchronizer::OnNodeHierarchyChanged(
ViewTreeNodePrivate(old_parent).LocalRemoveChild(node);
}
void ViewManagerSynchronizer::OnNodeReordered(Id node_id,
Id relative_node_id,
OrderDirection direction,
Id server_change_id) {
next_server_change_id_ = server_change_id + 1;
ViewTreeNode* node = GetNodeById(node_id);
ViewTreeNode* relative_node = GetNodeById(relative_node_id);
if (node && relative_node) {
ViewTreeNodePrivate(node).LocalReorder(relative_node, direction);
}
}
void ViewManagerSynchronizer::OnNodeDeleted(Id node_id, Id server_change_id) {
next_server_change_id_ = server_change_id + 1;
......
......@@ -12,6 +12,7 @@
#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
#include "mojo/services/public/cpp/view_manager/view_manager.h"
#include "mojo/services/public/cpp/view_manager/view_manager_types.h"
#include "mojo/services/public/cpp/view_manager/view_tree_node.h"
#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
class SkBitmap;
......@@ -46,6 +47,8 @@ class ViewManagerSynchronizer : public ViewManager,
void AddChild(Id child_id, Id parent_id);
void RemoveChild(Id child_id, Id parent_id);
void Reorder(Id node_id, Id relative_node_id, OrderDirection direction);
// Returns true if the specified node/view was created by this connection.
bool OwnsNode(Id id) const;
bool OwnsView(Id id) const;
......@@ -106,6 +109,10 @@ class ViewManagerSynchronizer : public ViewManager,
Id old_parent_id,
Id server_change_id,
Array<INodePtr> nodes) OVERRIDE;
virtual void OnNodeReordered(Id node_id,
Id relative_node_id,
OrderDirection direction,
Id server_change_id) OVERRIDE;
virtual void OnNodeDeleted(Id node_id, Id server_change_id) OVERRIDE;
virtual void OnNodeViewReplaced(Id node,
Id new_view_id,
......
......@@ -80,6 +80,72 @@ void RemoveChildImpl(ViewTreeNode* child, ViewTreeNode::Children* children) {
}
}
class ScopedOrderChangedNotifier {
public:
ScopedOrderChangedNotifier(ViewTreeNode* node,
ViewTreeNode* relative_node,
OrderDirection direction)
: node_(node),
relative_node_(relative_node),
direction_(direction) {
FOR_EACH_OBSERVER(
ViewTreeNodeObserver,
*ViewTreeNodePrivate(node_).observers(),
OnNodeReordered(node_,
relative_node_,
direction_,
ViewTreeNodeObserver::DISPOSITION_CHANGING));
}
~ScopedOrderChangedNotifier() {
FOR_EACH_OBSERVER(
ViewTreeNodeObserver,
*ViewTreeNodePrivate(node_).observers(),
OnNodeReordered(node_,
relative_node_,
direction_,
ViewTreeNodeObserver::DISPOSITION_CHANGED));
}
private:
ViewTreeNode* node_;
ViewTreeNode* relative_node_;
OrderDirection direction_;
DISALLOW_COPY_AND_ASSIGN(ScopedOrderChangedNotifier);
};
// Returns true if the order actually changed.
bool ReorderImpl(ViewTreeNode::Children* children,
ViewTreeNode* node,
ViewTreeNode* relative,
OrderDirection direction) {
DCHECK(relative);
DCHECK_NE(node, relative);
DCHECK_EQ(node->parent(), relative->parent());
const size_t child_i =
std::find(children->begin(), children->end(), node) - children->begin();
const size_t target_i =
std::find(children->begin(), children->end(), relative) -
children->begin();
if ((direction == ORDER_ABOVE && child_i == target_i + 1) ||
(direction == ORDER_BELOW && child_i + 1 == target_i)) {
return false;
}
ScopedOrderChangedNotifier notifier(node, relative, direction);
const size_t dest_i =
direction == ORDER_ABOVE ?
(child_i < target_i ? target_i : target_i + 1) :
(child_i < target_i ? target_i - 1 : target_i);
children->erase(children->begin() + child_i);
children->insert(children->begin() + dest_i, node);
return true;
}
class ScopedSetActiveViewNotifier {
public:
ScopedSetActiveViewNotifier(ViewTreeNode* node,
......@@ -238,6 +304,24 @@ void ViewTreeNode::RemoveChild(ViewTreeNode* child) {
}
}
void ViewTreeNode::MoveToFront() {
Reorder(parent_->children_.back(), ORDER_ABOVE);
}
void ViewTreeNode::MoveToBack() {
Reorder(parent_->children_.front(), ORDER_BELOW);
}
void ViewTreeNode::Reorder(ViewTreeNode* relative, OrderDirection direction) {
if (!LocalReorder(relative, direction))
return;
if (manager_) {
static_cast<ViewManagerSynchronizer*>(manager_)->Reorder(id_,
relative->id(),
direction);
}
}
bool ViewTreeNode::Contains(ViewTreeNode* child) const {
if (manager_)
CHECK_EQ(ViewTreeNodePrivate(child).view_manager(), manager_);
......@@ -324,6 +408,11 @@ void ViewTreeNode::LocalRemoveChild(ViewTreeNode* child) {
RemoveChildImpl(child, &children_);
}
bool ViewTreeNode::LocalReorder(ViewTreeNode* relative,
OrderDirection direction) {
return ReorderImpl(&parent_->children_, this, relative, direction);
}
void ViewTreeNode::LocalSetActiveView(View* view) {
ScopedSetActiveViewNotifier notifier(this, active_view_, view);
if (active_view_)
......
......@@ -39,6 +39,9 @@ class ViewTreeNodePrivate {
void LocalRemoveChild(ViewTreeNode* child) {
node_->LocalRemoveChild(child);
}
void LocalReorder(ViewTreeNode* relative, OrderDirection direction) {
node_->LocalReorder(relative, direction);
}
void LocalSetActiveView(View* view) {
node_->LocalSetActiveView(view);
}
......
......@@ -250,6 +250,39 @@ void WaitForDestruction(ViewManager* view_manager,
DoRunLoop();
}
class OrderChangeObserver : public ViewTreeNodeObserver {
public:
OrderChangeObserver(ViewTreeNode* node) : node_(node) {
node_->AddObserver(this);
}
virtual ~OrderChangeObserver() {
node_->RemoveObserver(this);
}
private:
// Overridden from ViewTreeNodeObserver:
virtual void OnNodeReordered(ViewTreeNode* node,
ViewTreeNode* relative_node,
OrderDirection direction,
DispositionChangePhase phase) OVERRIDE {
if (phase != ViewTreeNodeObserver::DISPOSITION_CHANGED)
return;
DCHECK_EQ(node, node_);
QuitRunLoop();
}
ViewTreeNode* node_;
DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver);
};
void WaitForOrderChange(ViewManager* view_manager,
ViewTreeNode* node) {
OrderChangeObserver observer(node);
DoRunLoop();
}
// Tracks a node's destruction. Query is_valid() for current state.
class NodeTracker : public ViewTreeNodeObserver {
public:
......@@ -709,5 +742,39 @@ TEST_F(ViewManagerTest, EmbeddingIdentity) {
EXPECT_EQ(kWindowManagerURL, embedded->GetEmbedderURL());
}
TEST_F(ViewManagerTest, Reorder) {
ViewTreeNode* node1 = ViewTreeNode::Create(window_manager());
window_manager()->GetRoots().front()->AddChild(node1);
ViewTreeNode* node11 = ViewTreeNode::Create(window_manager());
node1->AddChild(node11);
ViewTreeNode* node12 = ViewTreeNode::Create(window_manager());
node1->AddChild(node12);
ViewManager* embedded = Embed(window_manager(), node1);
ViewTreeNode* node1_in_embedded = embedded->GetNodeById(node1->id());
{
node11->MoveToFront();
WaitForOrderChange(embedded, embedded->GetNodeById(node11->id()));
EXPECT_EQ(node1_in_embedded->children().front(),
embedded->GetNodeById(node12->id()));
EXPECT_EQ(node1_in_embedded->children().back(),
embedded->GetNodeById(node11->id()));
}
{
node11->MoveToBack();
WaitForOrderChange(embedded, embedded->GetNodeById(node11->id()));
EXPECT_EQ(node1_in_embedded->children().front(),
embedded->GetNodeById(node11->id()));
EXPECT_EQ(node1_in_embedded->children().back(),
embedded->GetNodeById(node12->id()));
}
}
} // namespace view_manager
} // namespace mojo
......@@ -348,6 +348,153 @@ TEST_F(ViewTreeNodeObserverTest, TreeChange_Reparent) {
namespace {
class OrderChangeObserver : public ViewTreeNodeObserver {
public:
struct Change {
ViewTreeNode* node;
ViewTreeNode* relative_node;
OrderDirection direction;
DispositionChangePhase phase;
};
typedef std::vector<Change> Changes;
explicit OrderChangeObserver(ViewTreeNode* observee) : observee_(observee) {
observee_->AddObserver(this);
}
virtual ~OrderChangeObserver() {
observee_->RemoveObserver(this);
}
Changes GetAndClearChanges() {
Changes changes;
changes_.swap(changes);
return changes;
}
private:
// Overridden from ViewTreeNodeObserver:
virtual void OnNodeReordered(ViewTreeNode* node,
ViewTreeNode* relative_node,
OrderDirection direction,
DispositionChangePhase phase) OVERRIDE {
Change change;
change.node = node;
change.relative_node = relative_node;
change.direction = direction;
change.phase = phase;
changes_.push_back(change);
}
ViewTreeNode* observee_;
Changes changes_;
DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver);
};
} // namespace
TEST_F(ViewTreeNodeObserverTest, Order) {
TestViewTreeNode v1, v11, v12, v13;
v1.AddChild(&v11);
v1.AddChild(&v12);
v1.AddChild(&v13);
// Order: v11, v12, v13
EXPECT_EQ(3U, v1.children().size());
EXPECT_EQ(&v11, v1.children().front());
EXPECT_EQ(&v13, v1.children().back());
{
OrderChangeObserver observer(&v11);
// Move v11 to front.
// Resulting order: v12, v13, v11
v11.MoveToFront();
EXPECT_EQ(&v12, v1.children().front());
EXPECT_EQ(&v11, v1.children().back());
OrderChangeObserver::Changes changes = observer.GetAndClearChanges();
EXPECT_EQ(2U, changes.size());
EXPECT_EQ(&v11, changes[0].node);
EXPECT_EQ(&v13, changes[0].relative_node);
EXPECT_EQ(ORDER_ABOVE, changes[0].direction);
EXPECT_EQ(ViewTreeNodeObserver::DISPOSITION_CHANGING, changes[0].phase);
EXPECT_EQ(&v11, changes[1].node);
EXPECT_EQ(&v13, changes[1].relative_node);
EXPECT_EQ(ORDER_ABOVE, changes[1].direction);
EXPECT_EQ(ViewTreeNodeObserver::DISPOSITION_CHANGED, changes[1].phase);
}
{
OrderChangeObserver observer(&v11);
// Move v11 to back.
// Resulting order: v11, v12, v13
v11.MoveToBack();
EXPECT_EQ(&v11, v1.children().front());
EXPECT_EQ(&v13, v1.children().back());
OrderChangeObserver::Changes changes = observer.GetAndClearChanges();
EXPECT_EQ(2U, changes.size());
EXPECT_EQ(&v11, changes[0].node);
EXPECT_EQ(&v12, changes[0].relative_node);
EXPECT_EQ(ORDER_BELOW, changes[0].direction);
EXPECT_EQ(ViewTreeNodeObserver::DISPOSITION_CHANGING, changes[0].phase);
EXPECT_EQ(&v11, changes[1].node);
EXPECT_EQ(&v12, changes[1].relative_node);
EXPECT_EQ(ORDER_BELOW, changes[1].direction);
EXPECT_EQ(ViewTreeNodeObserver::DISPOSITION_CHANGED, changes[1].phase);
}
{
OrderChangeObserver observer(&v11);
// Move v11 above v12.
// Resulting order: v12. v11, v13
v11.Reorder(&v12, ORDER_ABOVE);
EXPECT_EQ(&v12, v1.children().front());
EXPECT_EQ(&v13, v1.children().back());
OrderChangeObserver::Changes changes = observer.GetAndClearChanges();
EXPECT_EQ(2U, changes.size());
EXPECT_EQ(&v11, changes[0].node);
EXPECT_EQ(&v12, changes[0].relative_node);
EXPECT_EQ(ORDER_ABOVE, changes[0].direction);
EXPECT_EQ(ViewTreeNodeObserver::DISPOSITION_CHANGING, changes[0].phase);
EXPECT_EQ(&v11, changes[1].node);
EXPECT_EQ(&v12, changes[1].relative_node);
EXPECT_EQ(ORDER_ABOVE, changes[1].direction);
EXPECT_EQ(ViewTreeNodeObserver::DISPOSITION_CHANGED, changes[1].phase);
}
{
OrderChangeObserver observer(&v11);
// Move v11 below v12.
// Resulting order: v11, v12, v13
v11.Reorder(&v12, ORDER_BELOW);
EXPECT_EQ(&v11, v1.children().front());
EXPECT_EQ(&v13, v1.children().back());
OrderChangeObserver::Changes changes = observer.GetAndClearChanges();
EXPECT_EQ(2U, changes.size());
EXPECT_EQ(&v11, changes[0].node);
EXPECT_EQ(&v12, changes[0].relative_node);
EXPECT_EQ(ORDER_BELOW, changes[0].direction);
EXPECT_EQ(ViewTreeNodeObserver::DISPOSITION_CHANGING, changes[0].phase);
EXPECT_EQ(&v11, changes[1].node);
EXPECT_EQ(&v12, changes[1].relative_node);
EXPECT_EQ(ORDER_BELOW, changes[1].direction);
EXPECT_EQ(ViewTreeNodeObserver::DISPOSITION_CHANGED, changes[1].phase);
}
}
namespace {
typedef std::vector<std::string> Changes;
std::string NodeIdToString(Id id) {
......
......@@ -11,6 +11,7 @@
#include "base/observer_list.h"
#include "mojo/public/cpp/bindings/array.h"
#include "mojo/services/public/cpp/view_manager/view_manager_types.h"
#include "mojo/services/public/interfaces/view_manager/view_manager_constants.mojom.h"
#include "ui/gfx/geometry/rect.h"
namespace mojo {
......@@ -52,6 +53,10 @@ class ViewTreeNode {
void AddChild(ViewTreeNode* child);
void RemoveChild(ViewTreeNode* child);
void Reorder(ViewTreeNode* relative, OrderDirection direction);
void MoveToFront();
void MoveToBack();
bool Contains(ViewTreeNode* child) const;
ViewTreeNode* GetChildById(Id id);
......@@ -76,6 +81,8 @@ class ViewTreeNode {
void LocalDestroy();
void LocalAddChild(ViewTreeNode* child);
void LocalRemoveChild(ViewTreeNode* child);
// Returns true if the order actually changed.
bool LocalReorder(ViewTreeNode* relative, OrderDirection direction);
void LocalSetActiveView(View* view);
void LocalSetBounds(const gfx::Rect& old_bounds, const gfx::Rect& new_bounds);
......
......@@ -9,6 +9,8 @@
#include "base/basictypes.h"
#include "mojo/services/public/cpp/view_manager/view_tree_node.h"
namespace gfx {
class Rect;
}
......@@ -37,6 +39,11 @@ class ViewTreeNodeObserver {
virtual void OnTreeChange(const TreeChangeParams& params) {}
virtual void OnNodeReordered(ViewTreeNode* node,
ViewTreeNode* relative_node,
OrderDirection direction,
DispositionChangePhase phase) {}
virtual void OnNodeDestroy(ViewTreeNode* node,
DispositionChangePhase phase) {}
......
......@@ -4,6 +4,7 @@
import "../geometry/geometry.mojom"
import "../input_events/input_events.mojom"
import "view_manager_constants.mojom"
module mojo.view_manager {
......@@ -72,6 +73,15 @@ interface IViewManager {
RemoveNodeFromParent(uint32 node_id,
uint32 server_change_id) => (bool success);
// Reorders a node in its parent, relative to |relative_node_id| according to
// |direction|.
// Only the connection that created the node's parent can reorder its
// children.
ReorderNode(uint32 node_id,
uint32 relative_node_id,
OrderDirection direction,
uint32 server_change_id) => (bool success);
// Returns the nodes comprising the tree starting at |node_id|. |node_id| is
// the first result in the return value, unless |node_id| is invalid, in which
// case an empty vector is returned. The nodes are visited using a depth first
......@@ -148,6 +158,12 @@ interface IViewManagerClient {
uint32 server_change_id,
INode[] nodes);
// Invoked when the order of nodes within a parent changes.
OnNodeReordered(uint32 node_id,
uint32 relative_node_id,
OrderDirection direction,
uint32 server_change_id);
// Invoked when a node is deleted.
OnNodeDeleted(uint32 node, uint32 server_change_id);
......
// Copyright 2014 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.
module mojo.view_manager {
enum OrderDirection {
ORDER_ABOVE = 1,
ORDER_BELOW,
};
}
......@@ -58,6 +58,13 @@ void Node::Remove(Node* child) {
window_.RemoveChild(&child->window_);
}
void Node::Reorder(Node* child, Node* relative, OrderDirection direction) {
if (direction == ORDER_ABOVE)
window_.StackChildAbove(child->window(), relative->window());
else if (direction == ORDER_BELOW)
window_.StackChildBelow(child->window(), relative->window());
}
const Node* Node::GetRoot() const {
const aura::Window* window = &window_;
while (window && window->parent())
......
......@@ -8,6 +8,7 @@
#include <vector>
#include "base/logging.h"
#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
#include "mojo/services/view_manager/ids.h"
#include "mojo/services/view_manager/view_manager_export.h"
#include "ui/aura/window.h"
......@@ -37,6 +38,8 @@ class MOJO_VIEW_MANAGER_EXPORT Node
void Add(Node* child);
void Remove(Node* child);
void Reorder(Node* child, Node* relative, OrderDirection direction);
aura::Window* window() { return &window_; }
const gfx::Rect& bounds() const { return window_.bounds(); }
......
......@@ -151,6 +151,17 @@ void RootNodeManager::ProcessNodeHierarchyChanged(const Node* node,
}
}
void RootNodeManager::ProcessNodeReorder(const Node* node,
const Node* relative_node,
const OrderDirection direction) {
for (ConnectionMap::iterator i = connection_map_.begin();
i != connection_map_.end(); ++i) {
i->second->ProcessNodeReorder(
node, relative_node, direction, next_server_change_id_,
IsChangeSource(i->first));
}
}
void RootNodeManager::ProcessNodeViewReplaced(const Node* node,
const View* new_view,
const View* old_view) {
......
......@@ -137,6 +137,9 @@ class MOJO_VIEW_MANAGER_EXPORT RootNodeManager : public NodeDelegate {
void ProcessNodeHierarchyChanged(const Node* node,
const Node* new_parent,
const Node* old_parent);
void ProcessNodeReorder(const Node* node,
const Node* relative_node,
const OrderDirection direction);
void ProcessNodeViewReplaced(const Node* node,
const View* new_view_id,
const View* old_view_id);
......
......@@ -26,6 +26,10 @@ std::string RectToString(const gfx::Rect& rect) {
rect.height());
}
std::string DirectionToString(OrderDirection direction) {
return direction == ORDER_ABOVE ? "above" : "below";
}
std::string ChangeToDescription1(const Change& change) {
switch (change.type) {
case CHANGE_TYPE_CONNECTION_ESTABLISHED:
......@@ -55,6 +59,14 @@ std::string ChangeToDescription1(const Change& change) {
NodeIdToString(change.node_id2).c_str(),
NodeIdToString(change.node_id3).c_str());
case CHANGE_TYPE_NODE_REORDERED:
return base::StringPrintf(
"Reordered change_id=%d node=%s relative=%s direction=%s",
static_cast<int>(change.change_id),
NodeIdToString(change.node_id).c_str(),
NodeIdToString(change.node_id2).c_str(),
DirectionToString(change.direction).c_str());
case CHANGE_TYPE_NODE_DELETED:
return base::StringPrintf("NodeDeleted change_id=%d node=%s",
static_cast<int>(change.change_id),
......@@ -119,7 +131,8 @@ Change::Change()
node_id3(0),
view_id(0),
view_id2(0),
event_action(0) {}
event_action(0),
direction(ORDER_ABOVE) {}
Change::~Change() {
}
......@@ -185,6 +198,19 @@ void TestChangeTracker::OnNodeHierarchyChanged(Id node_id,
AddChange(change);
}
void TestChangeTracker::OnNodeReordered(Id node_id,
Id relative_node_id,
OrderDirection direction,
Id server_change_id) {
Change change;
change.type = CHANGE_TYPE_NODE_REORDERED;
change.node_id = node_id;
change.node_id2 = relative_node_id;
change.direction = direction;
change.change_id = server_change_id;
AddChange(change);
}
void TestChangeTracker::OnNodeDeleted(Id node_id, Id server_change_id) {
Change change;
change.type = CHANGE_TYPE_NODE_DELETED;
......
......@@ -23,6 +23,7 @@ enum ChangeType {
CHANGE_TYPE_SERVER_CHANGE_ID_ADVANCED,
CHANGE_TYPE_NODE_BOUNDS_CHANGED,
CHANGE_TYPE_NODE_HIERARCHY_CHANGED,
CHANGE_TYPE_NODE_REORDERED,
CHANGE_TYPE_NODE_DELETED,
CHANGE_TYPE_VIEW_DELETED,
CHANGE_TYPE_VIEW_REPLACED,
......@@ -57,6 +58,7 @@ struct Change {
gfx::Rect bounds2;
int32 event_action;
String creator_url;
OrderDirection direction;
};
// Converts Changes to string descriptions.
......@@ -106,6 +108,10 @@ class TestChangeTracker {
Id old_parent_id,
Id server_change_id,
Array<INodePtr> nodes);
void OnNodeReordered(Id node_id,
Id relative_node_id,
OrderDirection direction,
Id server_change_id);
void OnNodeDeleted(Id node_id, Id server_change_id);
void OnViewDeleted(Id view_id);
void OnNodeViewReplaced(Id node_id, Id new_view_id, Id old_view_id);
......
......@@ -170,6 +170,23 @@ void ViewManagerConnection::ProcessNodeHierarchyChanged(
NodesToINodes(to_send));
}
void ViewManagerConnection::ProcessNodeReorder(const Node* node,
const Node* relative_node,
OrderDirection direction,
Id server_change_id,
bool originated_change) {
if (originated_change ||
!known_nodes_.count(NodeIdToTransportId(node->id())) ||
!known_nodes_.count(NodeIdToTransportId(relative_node->id()))) {
return;
}
client()->OnNodeReordered(NodeIdToTransportId(node->id()),
NodeIdToTransportId(relative_node->id()),
direction,
server_change_id);
}
void ViewManagerConnection::ProcessNodeViewReplaced(
const Node* node,
const View* new_view,
......@@ -267,6 +284,36 @@ bool ViewManagerConnection::CanAddNode(const Node* parent,
return (IsNodeDescendantOfRoots(child) || child->id().connection_id == id_);
}
bool ViewManagerConnection::CanReorderNode(const Node* node,
const Node* relative_node,
OrderDirection direction) const {
if (!node || !relative_node)
return false;
if (node->id().connection_id != id_)
return false;
const Node* parent = node->GetParent();
if (!parent || parent != relative_node->GetParent())
return false;
if (known_nodes_.count(NodeIdToTransportId(parent->id())) == 0)
return false;
std::vector<const Node*> children = parent->GetChildren();
const size_t child_i =
std::find(children.begin(), children.end(), node) - children.begin();
const size_t target_i =
std::find(children.begin(), children.end(), relative_node) -
children.begin();
if ((direction == ORDER_ABOVE && child_i == target_i + 1) ||
(direction == ORDER_BELOW && child_i + 1 == target_i)) {
return false;
}
return true;
}
bool ViewManagerConnection::CanDeleteNode(const NodeId& node_id) const {
return node_id.connection_id == id_;
}
......@@ -553,6 +600,27 @@ void ViewManagerConnection::RemoveNodeFromParent(
callback.Run(success);
}
void ViewManagerConnection::ReorderNode(Id node_id,
Id relative_node_id,
OrderDirection direction,
Id server_change_id,
const Callback<void(bool)>& callback) {
bool success = false;
if (server_change_id == root_node_manager_->next_server_change_id()) {
Node* node = GetNode(NodeIdFromTransportId(node_id));
Node* relative_node = GetNode(NodeIdFromTransportId(relative_node_id));
if (CanReorderNode(node, relative_node, direction)) {
success = true;
RootNodeManager::ScopedChange change(
this, root_node_manager_,
RootNodeManager::CHANGE_TYPE_ADVANCE_SERVER_CHANGE_ID, false);
node->GetParent()->Reorder(node, relative_node, direction);
root_node_manager_->ProcessNodeReorder(node, relative_node, direction);
}
}
callback.Run(success);
}
void ViewManagerConnection::GetNodeTree(
Id node_id,
const Callback<void(Array<INodePtr>)>& callback) {
......
......@@ -86,6 +86,11 @@ class MOJO_VIEW_MANAGER_EXPORT ViewManagerConnection
const Node* old_parent,
Id server_change_id,
bool originated_change);
void ProcessNodeReorder(const Node* node,
const Node* relative_node,
OrderDirection direction,
Id server_change_id,
bool originated_change);
void ProcessNodeViewReplaced(const Node* node,
const View* new_view,
const View* old_view,
......@@ -110,6 +115,9 @@ class MOJO_VIEW_MANAGER_EXPORT ViewManagerConnection
// for this connection.
bool CanRemoveNodeFromParent(const Node* node) const;
bool CanAddNode(const Node* parent, const Node* child) const;
bool CanReorderNode(const Node* node,
const Node* relative_node,
OrderDirection direction) const;
bool CanDeleteNode(const NodeId& node_id) const;
bool CanDeleteView(const ViewId& view_id) const;
bool CanSetView(const Node* node, const ViewId& view_id) const;
......@@ -170,6 +178,11 @@ class MOJO_VIEW_MANAGER_EXPORT ViewManagerConnection
Id node_id,
Id server_change_id,
const Callback<void(bool)>& callback) OVERRIDE;
virtual void ReorderNode(Id node_id,
Id relative_node_id,
OrderDirection direction,
Id server_change_id,
const Callback<void(bool)>& callback) OVERRIDE;
virtual void GetNodeTree(
Id node_id,
const Callback<void(Array<INodePtr>)>& callback) OVERRIDE;
......
......@@ -22,6 +22,7 @@
#include "mojo/services/public/cpp/view_manager/util.h"
#include "mojo/services/public/cpp/view_manager/view_manager_types.h"
#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
#include "mojo/services/view_manager/ids.h"
#include "mojo/services/view_manager/test_change_tracker.h"
#include "mojo/shell/shell_test_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -112,6 +113,19 @@ class ViewManagerProxy : public TestChangeTracker::Delegate {
RunMainLoop();
return result;
}
bool ReorderNode(Id node_id,
Id relative_node_id,
OrderDirection direction,
Id server_change_id) {
changes_.clear();
bool result = false;
view_manager_->ReorderNode(node_id, relative_node_id, direction,
server_change_id,
base::Bind(&ViewManagerProxy::GotResult,
base::Unretained(this), &result));
RunMainLoop();
return result;
}
bool SetView(Id node_id, Id view_id) {
changes_.clear();
bool result = false;
......@@ -305,6 +319,13 @@ class TestViewManagerClientConnection
tracker_.OnNodeHierarchyChanged(node, new_parent, old_parent,
server_change_id, nodes.Pass());
}
virtual void OnNodeReordered(Id node_id,
Id relative_node_id,
OrderDirection direction,
Id server_change_id) OVERRIDE {
tracker_.OnNodeReordered(node_id, relative_node_id, direction,
server_change_id);
}
virtual void OnNodeDeleted(Id node, Id server_change_id) OVERRIDE {
tracker_.OnNodeDeleted(node, server_change_id);
}
......@@ -758,6 +779,84 @@ TEST_F(ViewManagerConnectionTest, NodeHierarchyChangedAddingKnownToUnknown) {
}
}
TEST_F(ViewManagerConnectionTest, ReorderNode) {
Id node1_id = BuildNodeId(1, 1);
Id node2_id = BuildNodeId(1, 2);
Id node3_id = BuildNodeId(1, 3);
Id node4_id = BuildNodeId(1, 4); // Peer to 1,1
Id node5_id = BuildNodeId(1, 5); // Peer to 1,1
Id node6_id = BuildNodeId(1, 6); // Child of 1,2.
Id node7_id = BuildNodeId(1, 7); // Unparented.
Id node8_id = BuildNodeId(1, 8); // Unparented.
ASSERT_TRUE(connection_->CreateNode(node1_id));
ASSERT_TRUE(connection_->CreateNode(node2_id));
ASSERT_TRUE(connection_->CreateNode(node3_id));
ASSERT_TRUE(connection_->CreateNode(node4_id));
ASSERT_TRUE(connection_->CreateNode(node5_id));
ASSERT_TRUE(connection_->CreateNode(node6_id));
ASSERT_TRUE(connection_->CreateNode(node7_id));
ASSERT_TRUE(connection_->CreateNode(node8_id));
ASSERT_TRUE(connection_->AddNode(node1_id, node2_id, 1));
ASSERT_TRUE(connection_->AddNode(node2_id, node6_id, 2));
ASSERT_TRUE(connection_->AddNode(node1_id, node3_id, 3));
ASSERT_TRUE(connection_->AddNode(
NodeIdToTransportId(RootNodeId()), node4_id, 4));
ASSERT_TRUE(connection_->AddNode(
NodeIdToTransportId(RootNodeId()), node5_id, 5));
ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
{
connection_->ReorderNode(node2_id, node3_id, ORDER_ABOVE, 6);
connection2_->DoRunLoopUntilChangesCount(1);
const Changes changes(ChangesToDescription1(connection2_->changes()));
ASSERT_EQ(1u, changes.size());
EXPECT_EQ(
"Reordered change_id=6 node=1,2 relative=1,3 direction=above",
changes[0]);
}
{
connection_->ReorderNode(node2_id, node3_id, ORDER_BELOW, 7);
connection2_->DoRunLoopUntilChangesCount(1);
const Changes changes(ChangesToDescription1(connection2_->changes()));
ASSERT_EQ(1u, changes.size());
EXPECT_EQ(
"Reordered change_id=7 node=1,2 relative=1,3 direction=below",
changes[0]);
}
{
// node2 is already below node3.
EXPECT_FALSE(connection_->ReorderNode(node2_id, node3_id, ORDER_BELOW, 8));
}
{
// node4 & 5 are unknown to connection2_.
EXPECT_FALSE(connection2_->ReorderNode(node4_id, node5_id, ORDER_ABOVE, 8));
}
{
// node6 & node3 have different parents.
EXPECT_FALSE(connection_->ReorderNode(node3_id, node6_id, ORDER_ABOVE, 8));
}
{
// Non-existent node-ids
EXPECT_FALSE(connection_->ReorderNode(BuildNodeId(1, 27),
BuildNodeId(1, 28),
ORDER_ABOVE,
8));
}
{
// node7 & node8 are un-parented.
EXPECT_FALSE(connection_->ReorderNode(node7_id, node8_id, ORDER_ABOVE, 8));
}
}
// Verifies DeleteNode works.
TEST_F(ViewManagerConnectionTest, DeleteNode) {
ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
......
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