Commit 4e72e787 authored by ben@chromium.org's avatar ben@chromium.org

Attempt at cleaning up a bunch of shutdown issues.

1. Defers command buffer destruction until after the native viewport client has had a chance to cleanup.
2. Destroy the compositor before quitting the view manager message loop.
3. Make various NodeObservers properly unhook themselves as node/view observers during destruction and view swapping.
4. Adds a "ViewManagerDisconnected" method to ViewManagerDelegate to allow the application to exit when the connection to the view manager is closed. This is necessary in addition to root monitoring because for some connections (e.g. the root node->window manager) the root node is not destroyed. I'm not entirely happy with imposing this burden on application developers but I will try and tidy this up later.
5. Instantiate nested AtExitManagers only in a static build. In the component build we can use the shell's.
6. Adds a callback to notify the ViewManagerInitServiceImpl when the NativeViewport is destroyed. The VMISI uses this callback to close its connection to the view manager, which triggers the view manager to shut down.

Since this still crashes, I can't test this yet.

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

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@282683 0039d316-1c4b-4281-b951-d872f2087c98
parent 782a8e94
......@@ -152,6 +152,10 @@ class AuraDemo : public ApplicationDelegate,
window_tree_host_->Show();
}
virtual void OnViewManagerDisconnected(
view_manager::ViewManager* view_manager) OVERRIDE {
base::MessageLoop::current()->Quit();
}
// WindowTreeHostMojoDelegate:
virtual void CompositorContentsChanged(const SkBitmap& bitmap) OVERRIDE {
......
......@@ -205,6 +205,12 @@ class Browser : public ApplicationDelegate,
root_->SetFocus();
CreateWidget(root_);
}
virtual void OnViewManagerDisconnected(
view_manager::ViewManager* view_manager) OVERRIDE {
DCHECK_EQ(view_manager_, view_manager);
view_manager_ = NULL;
base::MessageLoop::current()->Quit();
}
// views::TextfieldController:
virtual bool HandleKeyEvent(views::Textfield* sender,
......@@ -232,6 +238,10 @@ class Browser : public ApplicationDelegate,
else if (gained_focus == root_)
focus_client->FocusWindow(widget_->GetNativeView());
}
virtual void OnNodeDestroyed(view_manager::Node* node) OVERRIDE {
DCHECK_EQ(root_, node);
node->RemoveObserver(this);
}
scoped_ptr<ViewsInit> views_init_;
......
......@@ -39,8 +39,9 @@ class SampleApp : public ApplicationDelegate, public NativeViewportClient {
virtual void OnCreated() OVERRIDE {
}
virtual void OnDestroyed() OVERRIDE {
virtual void OnDestroyed(const mojo::Callback<void()>& callback) OVERRIDE {
base::MessageLoop::current()->Quit();
callback.Run();
}
virtual void OnBoundsChanged(RectPtr bounds) OVERRIDE {
......
......@@ -5,6 +5,7 @@
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_number_conversions.h"
#include "mojo/public/cpp/application/application_connection.h"
#include "mojo/public/cpp/application/application_delegate.h"
......@@ -97,6 +98,9 @@ class EmbeddedApp : public ApplicationDelegate,
roots_[root->id()] = root;
ProcessPendingNodeColor(root->id());
}
virtual void OnViewManagerDisconnected(ViewManager* view_manager) OVERRIDE {
base::MessageLoop::current()->Quit();
}
// Overridden from ViewObserver:
virtual void OnViewInputEvent(View* view, const EventPtr& event) OVERRIDE {
......
......@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/message_loop/message_loop.h"
#include "mojo/examples/html_viewer/blink_platform_impl.h"
#include "mojo/examples/html_viewer/html_document_view.h"
#include "mojo/public/cpp/application/application_connection.h"
......@@ -77,6 +78,10 @@ class HTMLViewer : public ApplicationDelegate,
document_view_->AttachToNode(root);
MaybeLoad();
}
virtual void OnViewManagerDisconnected(
view_manager::ViewManager* view_manager) OVERRIDE {
base::MessageLoop::current()->Quit();
}
void MaybeLoad() {
if (document_view_ && response_.get())
......
......@@ -93,6 +93,12 @@ class Keyboard : public ApplicationDelegate,
root->SetActiveView(view_manager::View::Create(view_manager));
CreateWidget(root);
}
virtual void OnViewManagerDisconnected(
view_manager::ViewManager* view_manager) OVERRIDE {
DCHECK_EQ(view_manager_, view_manager);
view_manager_ = NULL;
base::MessageLoop::current()->Quit();
}
// KeyboardDelegate:
virtual void OnKeyPressed(int key_code, int event_flags) OVERRIDE {
......
......@@ -297,6 +297,12 @@ class MediaViewer : public ApplicationDelegate,
request->response_details.Pass());
}
}
virtual void OnViewManagerDisconnected(
view_manager::ViewManager* view_manager) OVERRIDE {
DCHECK_EQ(view_manager_, view_manager);
view_manager_ = NULL;
base::MessageLoop::current()->Quit();
}
// Overridden from ControlPanel::Delegate:
virtual void ButtonPressed(ControlPanel::ControlType type) OVERRIDE {
......
......@@ -4,6 +4,7 @@
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/stringprintf.h"
#include "mojo/examples/window_manager/window_manager.mojom.h"
#include "mojo/public/cpp/application/application_connection.h"
......@@ -94,6 +95,9 @@ class NestingApp : public ApplicationDelegate,
NavigateChild();
}
virtual void OnViewManagerDisconnected(ViewManager* view_manager) OVERRIDE {
base::MessageLoop::current()->Quit();
}
// Overridden from ViewObserver:
virtual void OnViewInputEvent(View* view, const EventPtr& event) OVERRIDE {
......
......@@ -55,7 +55,7 @@ class PepperContainerApp: public ApplicationDelegate,
plugin_instance_.reset();
}
virtual void OnDestroyed() OVERRIDE {
virtual void OnDestroyed(const mojo::Callback<void()>& callback) OVERRIDE {
ppapi::ProxyAutoLock lock;
if (plugin_instance_) {
......@@ -64,6 +64,7 @@ class PepperContainerApp: public ApplicationDelegate,
}
base::MessageLoop::current()->Quit();
callback.Run();
}
virtual void OnBoundsChanged(RectPtr bounds) OVERRIDE {
......
......@@ -4,6 +4,7 @@
#include <algorithm>
#include "base/message_loop/message_loop.h"
#include "base/strings/string_tokenizer.h"
#include "mojo/examples/media_viewer/media_viewer.mojom.h"
#include "mojo/public/cpp/application/application_connection.h"
......@@ -166,6 +167,10 @@ class PNGViewer : public ApplicationDelegate,
if (!bitmap_.isNull())
DrawBitmap();
}
virtual void OnViewManagerDisconnected(
view_manager::ViewManager* view_manager) OVERRIDE {
base::MessageLoop::current()->Quit();
}
void DrawBitmap() {
if (!content_view_)
......
......@@ -48,8 +48,10 @@ class SampleApp : public ApplicationDelegate, public NativeViewportClient {
virtual void OnCreated() MOJO_OVERRIDE {
}
virtual void OnDestroyed() MOJO_OVERRIDE {
virtual void OnDestroyed(
const mojo::Callback<void()>& callback) MOJO_OVERRIDE {
RunLoop::current()->Quit();
callback.Run();
}
virtual void OnBoundsChanged(RectPtr bounds) MOJO_OVERRIDE {
......
......@@ -18,7 +18,6 @@
#include "mojo/services/public/cpp/view_manager/view_event_dispatcher.h"
#include "mojo/services/public/cpp/view_manager/view_manager.h"
#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
#include "mojo/services/public/cpp/view_manager/view_observer.h"
#include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
#include "mojo/services/public/interfaces/launcher/launcher.mojom.h"
#include "mojo/services/public/interfaces/navigation/navigation.mojom.h"
......@@ -189,7 +188,6 @@ class RootLayoutManager : public NodeObserver {
class WindowManager : public ApplicationDelegate,
public DebugPanel::Delegate,
public ViewObserver,
public ViewManagerDelegate,
public ViewEventDispatcher {
public:
......@@ -280,23 +278,27 @@ class WindowManager : public ApplicationDelegate,
view_manager_ = view_manager;
view_manager_->SetEventDispatcher(this);
Node* node = Node::Create(view_manager);
Node* node = Node::Create(view_manager_);
root->AddChild(node);
node->SetBounds(gfx::Rect(root->bounds().size()));
content_node_id_ = node->id();
root_layout_manager_.reset(
new RootLayoutManager(view_manager, root, content_node_id_));
new RootLayoutManager(view_manager_, root, content_node_id_));
root->AddObserver(root_layout_manager_.get());
View* view = View::Create(view_manager);
View* view = View::Create(view_manager_);
node->SetActiveView(view);
view->SetColor(SK_ColorBLUE);
view->AddObserver(this);
CreateLauncherUI();
CreateControlPanel(node);
}
virtual void OnViewManagerDisconnected(ViewManager* view_manager) OVERRIDE {
DCHECK_EQ(view_manager_, view_manager);
view_manager_ = NULL;
base::MessageLoop::current()->Quit();
}
// Overridden from ViewEventDispatcher:
virtual void DispatchEvent(View* target, EventPtr event) OVERRIDE {
......
......@@ -11,7 +11,9 @@
extern "C" APPLICATION_EXPORT MojoResult CDECL MojoMain(
MojoHandle shell_handle) {
base::CommandLine::Init(0, NULL);
#if !defined(COMPONENT_BUILD)
base::AtExitManager at_exit;
#endif
base::MessageLoop loop;
scoped_ptr<mojo::ApplicationDelegate> delegate(
mojo::ApplicationDelegate::Create());
......
......@@ -137,12 +137,15 @@ class NativeViewportImpl
}
virtual void OnDestroyed() OVERRIDE {
command_buffer_.reset();
client()->OnDestroyed();
base::MessageLoop::current()->Quit();
client()->OnDestroyed(base::Bind(&NativeViewportImpl::AckDestroyed,
base::Unretained(this)));
}
private:
void AckDestroyed() {
command_buffer_.reset();
}
shell::Context* context_;
gfx::AcceleratedWidget widget_;
scoped_ptr<services::NativeViewport> native_viewport_;
......
......@@ -62,7 +62,9 @@ class Delegate : public mojo::ApplicationDelegate {
extern "C" APPLICATION_EXPORT MojoResult CDECL MojoMain(
MojoHandle shell_handle) {
base::CommandLine::Init(0, NULL);
#if !defined(COMPONENT_BUILD)
base::AtExitManager at_exit;
#endif
// The IO message loop allows us to use net::URLRequest on this thread.
base::MessageLoopForIO loop;
......
......@@ -194,26 +194,6 @@ class ScopedSetBoundsNotifier {
DISALLOW_COPY_AND_ASSIGN(ScopedSetBoundsNotifier);
};
class ScopedDestructionNotifier {
public:
explicit ScopedDestructionNotifier(Node* node)
: node_(node) {
FOR_EACH_OBSERVER(NodeObserver,
*NodePrivate(node_).observers(),
OnNodeDestroying(node_));
}
~ScopedDestructionNotifier() {
FOR_EACH_OBSERVER(NodeObserver,
*NodePrivate(node_).observers(),
OnNodeDestroyed(node_));
}
private:
Node* node_;
DISALLOW_COPY_AND_ASSIGN(ScopedDestructionNotifier);
};
// Some operations are only permitted in the connection that created the node.
bool OwnsNode(ViewManager* manager, Node* node) {
return !manager ||
......@@ -359,13 +339,14 @@ Node::Node()
active_view_(NULL) {}
Node::~Node() {
ScopedDestructionNotifier notifier(this);
FOR_EACH_OBSERVER(NodeObserver, observers_, OnNodeDestroying(this));
if (parent_)
parent_->LocalRemoveChild(this);
// TODO(beng): It'd be better to do this via a destruction observer in the
// ViewManagerClientImpl.
if (manager_)
static_cast<ViewManagerClientImpl*>(manager_)->RemoveNode(id_);
FOR_EACH_OBSERVER(NodeObserver, observers_, OnNodeDestroyed(this));
}
////////////////////////////////////////////////////////////////////////////////
......
......@@ -13,28 +13,6 @@
namespace mojo {
namespace view_manager {
namespace {
class ScopedDestructionNotifier {
public:
explicit ScopedDestructionNotifier(View* view)
: view_(view) {
FOR_EACH_OBSERVER(ViewObserver,
*ViewPrivate(view_).observers(),
OnViewDestroying(view_));
}
~ScopedDestructionNotifier() {
FOR_EACH_OBSERVER(ViewObserver,
*ViewPrivate(view_).observers(),
OnViewDestroyed(view_));
}
private:
View* view_;
DISALLOW_COPY_AND_ASSIGN(ScopedDestructionNotifier);
};
} // namespace
// static
View* View::Create(ViewManager* manager) {
View* view = new View(manager);
......@@ -80,11 +58,13 @@ View::View()
manager_(NULL) {}
View::~View() {
ScopedDestructionNotifier notifier(this);
FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroying(this));
// TODO(beng): It'd be better to do this via a destruction observer in the
// ViewManagerClientImpl.
if (manager_)
static_cast<ViewManagerClientImpl*>(manager_)->RemoveView(id_);
FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroyed(this));
}
void View::LocalDestroy() {
......
......@@ -97,6 +97,7 @@ class RootObserver : public NodeObserver {
DCHECK_EQ(node, root_);
static_cast<ViewManagerClientImpl*>(
NodePrivate(root_).view_manager())->RemoveRoot(root_);
node->RemoveObserver(this);
delete this;
}
......@@ -535,6 +536,7 @@ ViewManagerClientImpl::ViewManagerClientImpl(ApplicationConnection* connection,
dispatcher_(NULL) {}
ViewManagerClientImpl::~ViewManagerClientImpl() {
delegate_->OnViewManagerDisconnected(this);
while (!nodes_.empty()) {
IdToNodeMap::iterator it = nodes_.begin();
if (OwnsNode(it->second->id()))
......
......@@ -83,6 +83,7 @@ class ConnectServiceLoader : public ServiceLoader,
Node* root) OVERRIDE {
callback_.Run(view_manager, root);
}
virtual void OnViewManagerDisconnected(ViewManager* view_manager) OVERRIDE {}
ScopedVector<ApplicationImpl> apps_;
LoadedCallback callback_;
......
......@@ -13,7 +13,8 @@ class ViewManager;
class ViewManagerDelegate {
public:
virtual void OnRootAdded(ViewManager* view_manager, Node* root) {}
virtual void OnRootAdded(ViewManager* view_manager, Node* root) = 0;
virtual void OnViewManagerDisconnected(ViewManager* view_manager) = 0;
protected:
virtual ~ViewManagerDelegate() {}
......
......@@ -21,7 +21,7 @@ interface NativeViewport {
interface NativeViewportClient {
OnCreated();
OnBoundsChanged(Rect bounds);
OnDestroyed();
OnDestroyed() => ();
OnEvent(Event event) => ();
};
......
......@@ -42,12 +42,17 @@ RootNodeManager::Context::~Context() {
aura::Env::DeleteInstance();
}
RootNodeManager::RootNodeManager(ApplicationConnection* app_connection,
RootViewManagerDelegate* view_manager_delegate)
RootNodeManager::RootNodeManager(
ApplicationConnection* app_connection,
RootViewManagerDelegate* view_manager_delegate,
const Callback<void()>& native_viewport_closed_callback)
: app_connection_(app_connection),
next_connection_id_(1),
next_server_change_id_(1),
root_view_manager_(app_connection, this, view_manager_delegate),
root_view_manager_(app_connection,
this,
view_manager_delegate,
native_viewport_closed_callback),
root_(new Node(this, RootNodeId())),
current_change_(NULL) {
}
......
......@@ -82,7 +82,8 @@ class MOJO_VIEW_MANAGER_EXPORT RootNodeManager
};
RootNodeManager(ApplicationConnection* app_connection,
RootViewManagerDelegate* view_manager_delegate);
RootViewManagerDelegate* view_manager_delegate,
const Callback<void()>& native_viewport_closed_callback);
virtual ~RootNodeManager();
// Returns the id for the next ViewManagerServiceImpl.
......
......@@ -116,9 +116,11 @@ class WindowTreeClientImpl : public aura::client::WindowTreeClient {
DISALLOW_COPY_AND_ASSIGN(WindowTreeClientImpl);
};
RootViewManager::RootViewManager(ApplicationConnection* app_connection,
RootViewManager::RootViewManager(
ApplicationConnection* app_connection,
RootNodeManager* root_node,
RootViewManagerDelegate* delegate)
RootViewManagerDelegate* delegate,
const Callback<void()>& native_viewport_closed_callback)
: delegate_(delegate),
root_node_manager_(root_node),
in_setup_(false) {
......@@ -131,7 +133,8 @@ RootViewManager::RootViewManager(ApplicationConnection* app_connection,
viewport.Pass(),
gfx::Rect(800, 600),
base::Bind(&RootViewManager::OnCompositorCreated,
base::Unretained(this))));
base::Unretained(this)),
native_viewport_closed_callback));
}
RootViewManager::~RootViewManager() {
......
......@@ -9,6 +9,7 @@
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "mojo/public/cpp/bindings/callback.h"
#include "mojo/public/cpp/gles2/gles2.h"
#include "mojo/services/view_manager/view_manager_export.h"
......@@ -39,7 +40,8 @@ class MOJO_VIEW_MANAGER_EXPORT RootViewManager {
public:
RootViewManager(ApplicationConnection* app_connection,
RootNodeManager* root_node,
RootViewManagerDelegate* delegate);
RootViewManagerDelegate* delegate,
const Callback<void()>& native_viewport_closed_callback);
virtual ~RootViewManager();
// See description above field for details.
......
......@@ -4,6 +4,7 @@
#include "mojo/services/view_manager/view_manager_init_service_impl.h"
#include "base/bind.h"
#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
#include "mojo/services/view_manager/ids.h"
#include "mojo/services/view_manager/view_manager_service_impl.h"
......@@ -19,7 +20,11 @@ ViewManagerInitServiceImpl::ConnectParams::~ConnectParams() {}
ViewManagerInitServiceImpl::ViewManagerInitServiceImpl(
ApplicationConnection* connection)
: root_node_manager_(connection, this),
: root_node_manager_(
connection,
this,
base::Bind(&ViewManagerInitServiceImpl::OnNativeViewportDeleted,
base::Unretained(this))),
is_tree_host_ready_(false) {
}
......@@ -57,6 +62,13 @@ void ViewManagerInitServiceImpl::OnRootViewManagerWindowTreeHostCreated() {
MaybeEmbedRoot(connect_params_->url, connect_params_->callback);
}
void ViewManagerInitServiceImpl::OnNativeViewportDeleted() {
// TODO(beng): Should not have to rely on implementation detail of
// InterfaceImpl to close the connection. Instead should simply
// be able to delete this object.
internal_state()->router()->CloseMessagePipe();
}
} // namespace service
} // namespace view_manager
} // namespace mojo
......@@ -57,6 +57,8 @@ class MOJO_VIEW_MANAGER_EXPORT ViewManagerInitServiceImpl
// RootViewManagerDelegate overrides:
virtual void OnRootViewManagerWindowTreeHostCreated() OVERRIDE;
void OnNativeViewportDeleted();
ServiceProvider* service_provider_;
RootNodeManager root_node_manager_;
......
......@@ -68,9 +68,11 @@ void RootLayoutManager::SetChildBounds(aura::Window* child,
WindowTreeHostImpl::WindowTreeHostImpl(
NativeViewportPtr viewport,
const gfx::Rect& bounds,
const base::Callback<void()>& compositor_created_callback)
const Callback<void()>& compositor_created_callback,
const Callback<void()>& native_viewport_closed_callback)
: native_viewport_(viewport.Pass()),
compositor_created_callback_(compositor_created_callback),
native_viewport_closed_callback_(native_viewport_closed_callback),
bounds_(bounds) {
native_viewport_.set_client(this);
native_viewport_->Create(Rect::From(bounds));
......@@ -178,8 +180,11 @@ void WindowTreeHostImpl::OnBoundsChanged(RectPtr bounds) {
OnHostResized(bounds_.size());
}
void WindowTreeHostImpl::OnDestroyed() {
base::MessageLoop::current()->Quit();
void WindowTreeHostImpl::OnDestroyed(const mojo::Callback<void()>& callback) {
DestroyCompositor();
native_viewport_closed_callback_.Run();
// TODO(beng): quit the message loop once we are on our own thread.
callback.Run();
}
void WindowTreeHostImpl::OnEvent(EventPtr event,
......
......@@ -25,9 +25,11 @@ class WindowTreeHostImpl : public aura::WindowTreeHost,
public ui::EventSource,
public NativeViewportClient {
public:
WindowTreeHostImpl(NativeViewportPtr viewport,
WindowTreeHostImpl(
NativeViewportPtr viewport,
const gfx::Rect& bounds,
const base::Callback<void()>& compositor_created_callback);
const Callback<void()>& compositor_created_callback,
const Callback<void()>& native_viewport_closed_callback);
virtual ~WindowTreeHostImpl();
gfx::Rect bounds() const { return bounds_; }
......@@ -54,7 +56,7 @@ class WindowTreeHostImpl : public aura::WindowTreeHost,
// Overridden from NativeViewportClient:
virtual void OnCreated() OVERRIDE;
virtual void OnDestroyed() OVERRIDE;
virtual void OnDestroyed(const mojo::Callback<void()>& callback) OVERRIDE;
virtual void OnBoundsChanged(RectPtr bounds) OVERRIDE;
virtual void OnEvent(EventPtr event,
const mojo::Callback<void()>& callback) OVERRIDE;
......@@ -62,7 +64,8 @@ class WindowTreeHostImpl : public aura::WindowTreeHost,
static ContextFactoryImpl* context_factory_;
NativeViewportPtr native_viewport_;
base::Callback<void()> compositor_created_callback_;
Callback<void()> compositor_created_callback_;
Callback<void()> native_viewport_closed_callback_;
gfx::Rect bounds_;
......
......@@ -91,6 +91,7 @@ NativeWidgetViewManager::NativeWidgetViewManager(
views::internal::NativeWidgetDelegate* delegate, view_manager::Node* node)
: NativeWidgetAura(delegate),
node_(node) {
node_->AddObserver(this);
node_->active_view()->AddObserver(this);
window_tree_host_.reset(new WindowTreeHostMojo(node_, this));
window_tree_host_->InitHost();
......@@ -112,7 +113,9 @@ NativeWidgetViewManager::NativeWidgetViewManager(
}
NativeWidgetViewManager::~NativeWidgetViewManager() {
if (node_->active_view())
node_->active_view()->RemoveObserver(this);
node_->RemoveObserver(this);
}
void NativeWidgetViewManager::InitNativeWidget(
......@@ -129,6 +132,20 @@ void NativeWidgetViewManager::CompositorContentsChanged(
node_->active_view()->SetContents(bitmap);
}
void NativeWidgetViewManager::OnNodeDestroyed(view_manager::Node* node) {
window_tree_host_.reset();
}
void NativeWidgetViewManager::OnNodeActiveViewChanged(
view_manager::Node* node,
view_manager::View* old_view,
view_manager::View* new_view) {
if (old_view)
old_view->RemoveObserver(this);
if (new_view)
new_view->AddObserver(this);
}
void NativeWidgetViewManager::OnViewInputEvent(view_manager::View* view,
const EventPtr& event) {
scoped_ptr<ui::Event> ui_event(event.To<scoped_ptr<ui::Event> >());
......
......@@ -42,6 +42,12 @@ class NativeWidgetViewManager : public views::NativeWidgetAura,
// WindowTreeHostMojoDelegate:
virtual void CompositorContentsChanged(const SkBitmap& bitmap) OVERRIDE;
// view_manager::NodeObserver:
virtual void OnNodeDestroyed(view_manager::Node* node) OVERRIDE;
virtual void OnNodeActiveViewChanged(view_manager::Node* node,
view_manager::View* old_view,
view_manager::View* new_view) OVERRIDE;
// view_manager::ViewObserver
virtual void OnViewInputEvent(view_manager::View* view,
const EventPtr& event) OVERRIDE;
......
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