diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-12 19:29:35 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-12 19:29:35 +0000 |
commit | f6435d20208dd9f2d56173bbe73dc605aacdea7d (patch) | |
tree | 1c8a97ed6d3eeca4bc49af93fb324eb0c4d20ced /mojo | |
parent | 052e6b15ee2376e12fcb50298eda43fa98f08474 (diff) | |
download | chromium_src-f6435d20208dd9f2d56173bbe73dc605aacdea7d.zip chromium_src-f6435d20208dd9f2d56173bbe73dc605aacdea7d.tar.gz chromium_src-f6435d20208dd9f2d56173bbe73dc605aacdea7d.tar.bz2 |
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
Diffstat (limited to 'mojo')
21 files changed, 693 insertions, 40 deletions
diff --git a/mojo/examples/aura_demo/aura_demo.cc b/mojo/examples/aura_demo/aura_demo.cc index fd87f69..d1caecc 100644 --- a/mojo/examples/aura_demo/aura_demo.cc +++ b/mojo/examples/aura_demo/aura_demo.cc @@ -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 { } diff --git a/mojo/mojo_services.gypi b/mojo/mojo_services.gypi index 1768ccf8..ee73af1 100644 --- a/mojo/mojo_services.gypi +++ b/mojo/mojo_services.gypi @@ -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': [ diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_synchronizer.cc b/mojo/services/public/cpp/view_manager/lib/view_manager_synchronizer.cc index baca5a4..16e2321 100644 --- a/mojo/services/public/cpp/view_manager/lib/view_manager_synchronizer.cc +++ b/mojo/services/public/cpp/view_manager/lib/view_manager_synchronizer.cc @@ -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; diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_synchronizer.h b/mojo/services/public/cpp/view_manager/lib/view_manager_synchronizer.h index 69ebd82..f888787 100644 --- a/mojo/services/public/cpp/view_manager/lib/view_manager_synchronizer.h +++ b/mojo/services/public/cpp/view_manager/lib/view_manager_synchronizer.h @@ -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, diff --git a/mojo/services/public/cpp/view_manager/lib/view_tree_node.cc b/mojo/services/public/cpp/view_manager/lib/view_tree_node.cc index 27848a3..5a6c1d2 100644 --- a/mojo/services/public/cpp/view_manager/lib/view_tree_node.cc +++ b/mojo/services/public/cpp/view_manager/lib/view_tree_node.cc @@ -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_) diff --git a/mojo/services/public/cpp/view_manager/lib/view_tree_node_private.h b/mojo/services/public/cpp/view_manager/lib/view_tree_node_private.h index 8c8cbdd..e3b84d6 100644 --- a/mojo/services/public/cpp/view_manager/lib/view_tree_node_private.h +++ b/mojo/services/public/cpp/view_manager/lib/view_tree_node_private.h @@ -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); } diff --git a/mojo/services/public/cpp/view_manager/tests/view_manager_unittest.cc b/mojo/services/public/cpp/view_manager/tests/view_manager_unittest.cc index 1ce3d0a..1527cd5 100644 --- a/mojo/services/public/cpp/view_manager/tests/view_manager_unittest.cc +++ b/mojo/services/public/cpp/view_manager/tests/view_manager_unittest.cc @@ -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 diff --git a/mojo/services/public/cpp/view_manager/tests/view_tree_node_unittest.cc b/mojo/services/public/cpp/view_manager/tests/view_tree_node_unittest.cc index 89b8293..d2326dc 100644 --- a/mojo/services/public/cpp/view_manager/tests/view_tree_node_unittest.cc +++ b/mojo/services/public/cpp/view_manager/tests/view_tree_node_unittest.cc @@ -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) { diff --git a/mojo/services/public/cpp/view_manager/view_tree_node.h b/mojo/services/public/cpp/view_manager/view_tree_node.h index d3e6ddf..2d9527c 100644 --- a/mojo/services/public/cpp/view_manager/view_tree_node.h +++ b/mojo/services/public/cpp/view_manager/view_tree_node.h @@ -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); diff --git a/mojo/services/public/cpp/view_manager/view_tree_node_observer.h b/mojo/services/public/cpp/view_manager/view_tree_node_observer.h index 116e156..d39f748 100644 --- a/mojo/services/public/cpp/view_manager/view_tree_node_observer.h +++ b/mojo/services/public/cpp/view_manager/view_tree_node_observer.h @@ -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) {} diff --git a/mojo/services/public/interfaces/view_manager/view_manager.mojom b/mojo/services/public/interfaces/view_manager/view_manager.mojom index df40def..e782798 100644 --- a/mojo/services/public/interfaces/view_manager/view_manager.mojom +++ b/mojo/services/public/interfaces/view_manager/view_manager.mojom @@ -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); diff --git a/mojo/services/public/interfaces/view_manager/view_manager_constants.mojom b/mojo/services/public/interfaces/view_manager/view_manager_constants.mojom new file mode 100644 index 0000000..5c73190 --- /dev/null +++ b/mojo/services/public/interfaces/view_manager/view_manager_constants.mojom @@ -0,0 +1,12 @@ +// 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, +}; + +} diff --git a/mojo/services/view_manager/node.cc b/mojo/services/view_manager/node.cc index 208c9c7..cbdd16d 100644 --- a/mojo/services/view_manager/node.cc +++ b/mojo/services/view_manager/node.cc @@ -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()) diff --git a/mojo/services/view_manager/node.h b/mojo/services/view_manager/node.h index 533bc3b..bfee675 100644 --- a/mojo/services/view_manager/node.h +++ b/mojo/services/view_manager/node.h @@ -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(); } diff --git a/mojo/services/view_manager/root_node_manager.cc b/mojo/services/view_manager/root_node_manager.cc index ea6cb2c..0d87d38 100644 --- a/mojo/services/view_manager/root_node_manager.cc +++ b/mojo/services/view_manager/root_node_manager.cc @@ -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) { diff --git a/mojo/services/view_manager/root_node_manager.h b/mojo/services/view_manager/root_node_manager.h index 93988a5..5ade95a 100644 --- a/mojo/services/view_manager/root_node_manager.h +++ b/mojo/services/view_manager/root_node_manager.h @@ -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); diff --git a/mojo/services/view_manager/test_change_tracker.cc b/mojo/services/view_manager/test_change_tracker.cc index 133c171..4260edd 100644 --- a/mojo/services/view_manager/test_change_tracker.cc +++ b/mojo/services/view_manager/test_change_tracker.cc @@ -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; diff --git a/mojo/services/view_manager/test_change_tracker.h b/mojo/services/view_manager/test_change_tracker.h index ef0c002..fbea61d 100644 --- a/mojo/services/view_manager/test_change_tracker.h +++ b/mojo/services/view_manager/test_change_tracker.h @@ -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); diff --git a/mojo/services/view_manager/view_manager_connection.cc b/mojo/services/view_manager/view_manager_connection.cc index 8d4d0a6..0c724bd 100644 --- a/mojo/services/view_manager/view_manager_connection.cc +++ b/mojo/services/view_manager/view_manager_connection.cc @@ -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) { diff --git a/mojo/services/view_manager/view_manager_connection.h b/mojo/services/view_manager/view_manager_connection.h index b9eae90..b2f326b 100644 --- a/mojo/services/view_manager/view_manager_connection.h +++ b/mojo/services/view_manager/view_manager_connection.h @@ -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; diff --git a/mojo/services/view_manager/view_manager_connection_unittest.cc b/mojo/services/view_manager/view_manager_connection_unittest.cc index 4f5d1c9..d9280d8 100644 --- a/mojo/services/view_manager/view_manager_connection_unittest.cc +++ b/mojo/services/view_manager/view_manager_connection_unittest.cc @@ -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))); |