Create a Window::Layer interface to reduce sk_app glue code

This also makes it possible to manage other parts of viewer, etc (like
the stats screen, command set, even samples) as additional layers in the
stack. For now, it just removes a lot of boilerplate.

Bug: skia:
Change-Id: Ic2f80690fc76c683b3736287dc2b738c50d38614
Reviewed-on: https://skia-review.googlesource.com/82688
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/example/HelloWorld.cpp b/example/HelloWorld.cpp
index fba7f7e..5954e71 100644
--- a/example/HelloWorld.cpp
+++ b/example/HelloWorld.cpp
@@ -18,21 +18,6 @@
     return new HelloWorld(argc, argv, platformData);
 }
 
-static void on_backend_created_func(void* userData) {
-    HelloWorld* hw = reinterpret_cast<HelloWorld*>(userData);
-    return hw->onBackendCreated();
-}
-
-static void on_paint_handler(SkCanvas* canvas, void* userData) {
-    HelloWorld* hw = reinterpret_cast<HelloWorld*>(userData);
-    return hw->onPaint(canvas);
-}
-
-static bool on_char_handler(SkUnichar c, uint32_t modifiers, void* userData) {
-    HelloWorld* hw = reinterpret_cast<HelloWorld*>(userData);
-    return hw->onChar(c, modifiers);
-}
-
 HelloWorld::HelloWorld(int argc, char** argv, void* platformData)
         : fBackendType(Window::kNativeGL_BackendType)
         , fRotationAngle(0) {
@@ -42,9 +27,7 @@
     fWindow->setRequestedDisplayParams(DisplayParams());
 
     // register callbacks
-    fWindow->registerBackendCreatedFunc(on_backend_created_func, this);
-    fWindow->registerPaintFunc(on_paint_handler, this);
-    fWindow->registerCharFunc(on_char_handler, this);
+    fWindow->pushLayer(this);
 
     fWindow->attach(fBackendType);
 }
diff --git a/example/HelloWorld.h b/example/HelloWorld.h
index 44f1321..28715fe 100644
--- a/example/HelloWorld.h
+++ b/example/HelloWorld.h
@@ -13,15 +13,16 @@
 
 class SkCanvas;
 
-class HelloWorld : public sk_app::Application {
+class HelloWorld : public sk_app::Application, sk_app::Window::Layer {
 public:
     HelloWorld(int argc, char** argv, void* platformData);
     ~HelloWorld() override;
 
-    void onBackendCreated();
-    void onPaint(SkCanvas* canvas);
     void onIdle() override;
-    bool onChar(SkUnichar c, uint32_t modifiers);
+
+    void onBackendCreated() override;
+    void onPaint(SkCanvas* canvas) override;
+    bool onChar(SkUnichar c, uint32_t modifiers) override;
 
 private:
     void updateTitle();
diff --git a/tools/sk_app/Window.cpp b/tools/sk_app/Window.cpp
index d7904fd..436b6b9 100644
--- a/tools/sk_app/Window.cpp
+++ b/tools/sk_app/Window.cpp
@@ -13,45 +13,7 @@
 
 namespace sk_app {
 
-static void default_backend_created_func(void* userData) {}
-
-static bool default_char_func(SkUnichar c, uint32_t modifiers, void* userData) {
-    return false;
-}
-
-static bool default_key_func(Window::Key key, Window::InputState state, uint32_t modifiers,
-                             void* userData) {
-    return false;
-}
-
-static bool default_mouse_func(int x, int y, Window::InputState state, uint32_t modifiers,
-                               void* userData) {
-    return false;
-}
-
-static bool default_mouse_wheel_func(float delta, uint32_t modifiers, void* userData) {
-    return false;
-}
-
-static bool default_touch_func(intptr_t owner, Window::InputState state, float x, float y,
-                               void* userData) {
-    return false;
-}
-
-static void default_ui_state_changed_func(
-        const SkString& stateName, const SkString& stateValue, void* userData) {}
-
-static void default_paint_func(SkCanvas*, void* userData) {}
-
-Window::Window() : fBackendCreatedFunc(default_backend_created_func)
-                 , fCharFunc(default_char_func)
-                 , fKeyFunc(default_key_func)
-                 , fMouseFunc(default_mouse_func)
-                 , fMouseWheelFunc(default_mouse_wheel_func)
-                 , fTouchFunc(default_touch_func)
-                 , fUIStateChangedFunc(default_ui_state_changed_func)
-                 , fPaintFunc(default_paint_func) {
-}
+Window::Window() {}
 
 void Window::detach() {
     delete fWindowContext;
@@ -59,31 +21,60 @@
 }
 
 void Window::onBackendCreated() {
-    fBackendCreatedFunc(fBackendCreatedUserData);
+    for (int i = 0; i < fLayers.count(); ++i) {
+        fLayers[i]->onBackendCreated();
+    }
 }
 
 bool Window::onChar(SkUnichar c, uint32_t modifiers) {
-    return fCharFunc(c, modifiers, fCharUserData);
+    for (int i = fLayers.count() - 1; i >= 0; --i) {
+        if (fLayers[i]->onChar(c, modifiers)) {
+            return true;
+        }
+    }
+    return false;
 }
 
 bool Window::onKey(Key key, InputState state, uint32_t modifiers) {
-    return fKeyFunc(key, state, modifiers, fKeyUserData);
+    for (int i = fLayers.count() - 1; i >= 0; --i) {
+        if (fLayers[i]->onKey(key, state, modifiers)) {
+            return true;
+        }
+    }
+    return false;
 }
 
 bool Window::onMouse(int x, int y, InputState state, uint32_t modifiers) {
-    return fMouseFunc(x, y, state, modifiers, fMouseUserData);
+    for (int i = fLayers.count() - 1; i >= 0; --i) {
+        if (fLayers[i]->onMouse(x, y, state, modifiers)) {
+            return true;
+        }
+    }
+    return false;
 }
 
 bool Window::onMouseWheel(float delta, uint32_t modifiers) {
-    return fMouseWheelFunc(delta, modifiers, fMouseWheelUserData);
+    for (int i = fLayers.count() - 1; i >= 0; --i) {
+        if (fLayers[i]->onMouseWheel(delta, modifiers)) {
+            return true;
+        }
+    }
+    return false;
 }
 
 bool Window::onTouch(intptr_t owner, InputState state, float x, float y) {
-    return fTouchFunc(owner, state, x, y, fTouchUserData);
+    for (int i = fLayers.count() - 1; i >= 0; --i) {
+        if (fLayers[i]->onTouch(owner, state, x, y)) {
+            return true;
+        }
+    }
+    return false;
 }
 
 void Window::onUIStateChanged(const SkString& stateName, const SkString& stateValue) {
-    return fUIStateChangedFunc(stateName, stateValue, fUIStateChangedUserData);
+    for (int i = 0; i < fLayers.count(); ++i) {
+        fLayers[i]->onUIStateChanged(stateName, stateValue);
+    }
 }
 
 void Window::onPaint() {
@@ -96,7 +87,9 @@
         // draw into the canvas of this surface
         SkCanvas* canvas = backbuffer->getCanvas();
 
-        fPaintFunc(canvas, fPaintUserData);
+        for (int i = 0; i < fLayers.count(); ++i) {
+            fLayers[i]->onPaint(canvas);
+        }
 
         canvas->flush();
 
diff --git a/tools/sk_app/Window.h b/tools/sk_app/Window.h
index 4d407809..77478b5 100644
--- a/tools/sk_app/Window.h
+++ b/tools/sk_app/Window.h
@@ -130,55 +130,23 @@
         kMove_InputState   // only valid for mouse
     };
 
-    // return value of 'true' means 'I have handled this event'
-    typedef void(*OnBackendCreatedFunc)(void* userData);
-    typedef bool(*OnCharFunc)(SkUnichar c, uint32_t modifiers, void* userData);
-    typedef bool(*OnKeyFunc)(Key key, InputState state, uint32_t modifiers, void* userData);
-    typedef bool(*OnMouseFunc)(int x, int y, InputState state, uint32_t modifiers, void* userData);
-    typedef bool(*OnMouseWheelFunc)(float delta, uint32_t modifiers, void* userData);
-    typedef bool(*OnTouchFunc)(intptr_t owner, InputState state, float x, float y, void* userData);
-    typedef void(*OnUIStateChangedFunc)(
-            const SkString& stateName, const SkString& stateValue, void* userData);
-    typedef void(*OnPaintFunc)(SkCanvas*, void* userData);
+    class Layer {
+    public:
+        virtual ~Layer() = default;
 
-    void registerBackendCreatedFunc(OnBackendCreatedFunc func, void* userData) {
-        fBackendCreatedFunc = func;
-        fBackendCreatedUserData = userData;
-    }
+        // return value of 'true' means 'I have handled this event'
+        virtual void onBackendCreated() {}
+        virtual bool onChar(SkUnichar c, uint32_t modifiers) { return false; }
+        virtual bool onKey(Key key, InputState state, uint32_t modifiers) { return false; }
+        virtual bool onMouse(int x, int y, InputState state, uint32_t modifiers) { return false; }
+        virtual bool onMouseWheel(float delta, uint32_t modifiers) { return false; }
+        virtual bool onTouch(intptr_t owner, InputState state, float x, float y) { return false; }
+        virtual void onUIStateChanged(const SkString& stateName, const SkString& stateValue) {}
+        virtual void onPaint(SkCanvas*) {}
+    };
 
-    void registerCharFunc(OnCharFunc func, void* userData) {
-        fCharFunc = func;
-        fCharUserData = userData;
-    }
-
-    void registerKeyFunc(OnKeyFunc func, void* userData) {
-        fKeyFunc = func;
-        fKeyUserData = userData;
-    }
-
-    void registerMouseFunc(OnMouseFunc func, void* userData) {
-        fMouseFunc = func;
-        fMouseUserData = userData;
-    }
-
-    void registerMouseWheelFunc(OnMouseWheelFunc func, void* userData) {
-        fMouseWheelFunc = func;
-        fMouseWheelUserData = userData;
-    }
-
-    void registerPaintFunc(OnPaintFunc func, void* userData) {
-        fPaintFunc = func;
-        fPaintUserData = userData;
-    }
-
-    void registerTouchFunc(OnTouchFunc func, void* userData) {
-        fTouchFunc = func;
-        fTouchUserData = userData;
-    }
-
-    void registerUIStateChangedFunc(OnUIStateChangedFunc func, void* userData) {
-        fUIStateChangedFunc = func;
-        fUIStateChangedUserData = userData;
+    void pushLayer(Layer* layer) {
+        fLayers.push(layer);
     }
 
     void onBackendCreated();
@@ -207,22 +175,7 @@
 protected:
     Window();
 
-    OnBackendCreatedFunc   fBackendCreatedFunc;
-    void*                  fBackendCreatedUserData;
-    OnCharFunc             fCharFunc;
-    void*                  fCharUserData;
-    OnKeyFunc              fKeyFunc;
-    void*                  fKeyUserData;
-    OnMouseFunc            fMouseFunc;
-    void*                  fMouseUserData;
-    OnMouseWheelFunc       fMouseWheelFunc;
-    void*                  fMouseWheelUserData;
-    OnTouchFunc            fTouchFunc;
-    void*                  fTouchUserData;
-    OnUIStateChangedFunc   fUIStateChangedFunc;
-    void*                  fUIStateChangedUserData;
-    OnPaintFunc            fPaintFunc;
-    void*                  fPaintUserData;
+    SkTDArray<Layer*>      fLayers;
     DisplayParams          fRequestedDisplayParams;
 
     WindowContext* fWindowContext = nullptr;
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index ddef1da..a1b7baa 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -56,81 +56,6 @@
     return new Viewer(argc, argv, platformData);
 }
 
-static void on_backend_created_func(void* userData) {
-    Viewer* vv = reinterpret_cast<Viewer*>(userData);
-
-    return vv->onBackendCreated();
-}
-
-static void on_paint_handler(SkCanvas* canvas, void* userData) {
-    Viewer* vv = reinterpret_cast<Viewer*>(userData);
-
-    return vv->onPaint(canvas);
-}
-
-static bool on_touch_handler(intptr_t owner, Window::InputState state, float x, float y, void* userData)
-{
-    Viewer* viewer = reinterpret_cast<Viewer*>(userData);
-
-    return viewer->onTouch(owner, state, x, y);
-}
-
-static void on_ui_state_changed_handler(const SkString& stateName, const SkString& stateValue, void* userData) {
-    Viewer* viewer = reinterpret_cast<Viewer*>(userData);
-
-    return viewer->onUIStateChanged(stateName, stateValue);
-}
-
-static bool on_mouse_handler(int x, int y, Window::InputState state, uint32_t modifiers,
-                             void* userData) {
-    ImGuiIO& io = ImGui::GetIO();
-    io.MousePos.x = static_cast<float>(x);
-    io.MousePos.y = static_cast<float>(y);
-    if (Window::kDown_InputState == state) {
-        io.MouseDown[0] = true;
-    } else if (Window::kUp_InputState == state) {
-        io.MouseDown[0] = false;
-    }
-    if (io.WantCaptureMouse) {
-        return true;
-    } else {
-        Viewer* viewer = reinterpret_cast<Viewer*>(userData);
-        return viewer->onMouse(x, y, state, modifiers);
-    }
-}
-
-static bool on_mouse_wheel_handler(float delta, uint32_t modifiers, void* userData) {
-    ImGuiIO& io = ImGui::GetIO();
-    io.MouseWheel += delta;
-    return true;
-}
-
-static bool on_key_handler(Window::Key key, Window::InputState state, uint32_t modifiers,
-                           void* userData) {
-    ImGuiIO& io = ImGui::GetIO();
-    io.KeysDown[static_cast<int>(key)] = (Window::kDown_InputState == state);
-
-    if (io.WantCaptureKeyboard) {
-        return true;
-    } else {
-        Viewer* viewer = reinterpret_cast<Viewer*>(userData);
-        return viewer->onKey(key, state, modifiers);
-    }
-}
-
-static bool on_char_handler(SkUnichar c, uint32_t modifiers, void* userData) {
-    ImGuiIO& io = ImGui::GetIO();
-    if (io.WantTextInput) {
-        if (c > 0 && c < 0x10000) {
-            io.AddInputCharacter(c);
-        }
-        return true;
-    } else {
-        Viewer* viewer = reinterpret_cast<Viewer*>(userData);
-        return viewer->onChar(c, modifiers);
-    }
-}
-
 static DEFINE_bool2(fullscreen, f, true, "Run fullscreen.");
 
 static DEFINE_string2(match, m, nullptr,
@@ -325,14 +250,7 @@
 
     // register callbacks
     fCommands.attach(fWindow);
-    fWindow->registerBackendCreatedFunc(on_backend_created_func, this);
-    fWindow->registerPaintFunc(on_paint_handler, this);
-    fWindow->registerTouchFunc(on_touch_handler, this);
-    fWindow->registerUIStateChangedFunc(on_ui_state_changed_handler, this);
-    fWindow->registerMouseFunc(on_mouse_handler, this);
-    fWindow->registerMouseWheelFunc(on_mouse_wheel_handler, this);
-    fWindow->registerKeyFunc(on_key_handler, this);
-    fWindow->registerCharFunc(on_char_handler, this);
+    fWindow->pushLayer(this);
 
     // add key-bindings
     fCommands.addCommand(' ', "GUI", "Toggle Debug GUI", [this]() {
@@ -810,14 +728,7 @@
 
     // re-register callbacks
     fCommands.attach(fWindow);
-    fWindow->registerBackendCreatedFunc(on_backend_created_func, this);
-    fWindow->registerPaintFunc(on_paint_handler, this);
-    fWindow->registerTouchFunc(on_touch_handler, this);
-    fWindow->registerUIStateChangedFunc(on_ui_state_changed_handler, this);
-    fWindow->registerMouseFunc(on_mouse_handler, this);
-    fWindow->registerMouseWheelFunc(on_mouse_wheel_handler, this);
-    fWindow->registerKeyFunc(on_key_handler, this);
-    fWindow->registerCharFunc(on_char_handler, this);
+    fWindow->pushLayer(this);
     // Don't allow the window to re-attach. If we're in MSAA mode, the params we grabbed above
     // will still include our correct sample count. But the re-created fWindow will lose that
     // information. On Windows, we need to re-create the window when changing sample count,
@@ -1023,28 +934,46 @@
     return true;
 }
 
-bool Viewer::onMouse(float x, float y, Window::InputState state, uint32_t modifiers) {
-    if (!fSlides[fCurrentSlide]->onMouse(x, y, state, modifiers)) {
-        if (GestureDevice::kTouch == fGestureDevice) {
-            return false;
-        }
-        switch (state) {
-            case Window::kUp_InputState: {
-                fGesture.touchEnd(nullptr);
-                break;
-            }
-            case Window::kDown_InputState: {
-                fGesture.touchBegin(nullptr, x, y);
-                break;
-            }
-            case Window::kMove_InputState: {
-                fGesture.touchMoved(nullptr, x, y);
-                break;
-            }
-        }
-        fGestureDevice = fGesture.isBeingTouched() ? GestureDevice::kMouse : GestureDevice::kNone;
+bool Viewer::onMouse(int x, int y, Window::InputState state, uint32_t modifiers) {
+    ImGuiIO& io = ImGui::GetIO();
+    io.MousePos.x = static_cast<float>(x);
+    io.MousePos.y = static_cast<float>(y);
+    if (Window::kDown_InputState == state) {
+        io.MouseDown[0] = true;
+    } else if (Window::kUp_InputState == state) {
+        io.MouseDown[0] = false;
     }
-    fWindow->inval();
+    if (io.WantCaptureMouse) {
+        return true;
+    } else {
+        if (!fSlides[fCurrentSlide]->onMouse(x, y, state, modifiers)) {
+            if (GestureDevice::kTouch == fGestureDevice) {
+                return false;
+            }
+            switch (state) {
+                case Window::kUp_InputState: {
+                    fGesture.touchEnd(nullptr);
+                    break;
+                }
+                case Window::kDown_InputState: {
+                    fGesture.touchBegin(nullptr, x, y);
+                    break;
+                }
+                case Window::kMove_InputState: {
+                    fGesture.touchMoved(nullptr, x, y);
+                    break;
+                }
+            }
+            fGestureDevice = fGesture.isBeingTouched() ? GestureDevice::kMouse : GestureDevice::kNone;
+        }
+        fWindow->inval();
+        return true;
+    }
+}
+
+bool Viewer::onMouseWheel(float delta, uint32_t modifiers) {
+    ImGuiIO& io = ImGui::GetIO();
+    io.MouseWheel += delta;
     return true;
 }
 
@@ -1697,14 +1626,27 @@
 }
 
 bool Viewer::onKey(sk_app::Window::Key key, sk_app::Window::InputState state, uint32_t modifiers) {
-    return fCommands.onKey(key, state, modifiers);
+    ImGuiIO& io = ImGui::GetIO();
+    io.KeysDown[static_cast<int>(key)] = (Window::kDown_InputState == state);
+
+    if (io.WantCaptureKeyboard) {
+        return true;
+    } else {
+        return fCommands.onKey(key, state, modifiers);
+    }
 }
 
 bool Viewer::onChar(SkUnichar c, uint32_t modifiers) {
-    if (fSlides[fCurrentSlide]->onChar(c)) {
+    ImGuiIO& io = ImGui::GetIO();
+    if (io.WantTextInput) {
+        if (c > 0 && c < 0x10000) {
+            io.AddInputCharacter(c);
+        }
+        return true;
+    } else if (fSlides[fCurrentSlide]->onChar(c)) {
         fWindow->inval();
         return true;
+    } else {
+        return fCommands.onChar(c, modifiers);
     }
-
-    return fCommands.onChar(c, modifiers);
 }
diff --git a/tools/viewer/Viewer.h b/tools/viewer/Viewer.h
index ff47b35..1f5d1c5 100644
--- a/tools/viewer/Viewer.h
+++ b/tools/viewer/Viewer.h
@@ -20,19 +20,21 @@
 
 class SkCanvas;
 
-class Viewer : public sk_app::Application {
+class Viewer : public sk_app::Application, sk_app::Window::Layer {
 public:
     Viewer(int argc, char** argv, void* platformData);
     ~Viewer() override;
 
-    void onBackendCreated();
-    void onPaint(SkCanvas* canvas);
     void onIdle() override;
-    bool onTouch(intptr_t owner, sk_app::Window::InputState state, float x, float y);
-    bool onMouse(float x, float y, sk_app::Window::InputState state, uint32_t modifiers);
-    void onUIStateChanged(const SkString& stateName, const SkString& stateValue);
-    bool onKey(sk_app::Window::Key key, sk_app::Window::InputState state, uint32_t modifiers);
-    bool onChar(SkUnichar c, uint32_t modifiers);
+
+    void onBackendCreated() override;
+    void onPaint(SkCanvas* canvas) override;
+    bool onTouch(intptr_t owner, sk_app::Window::InputState state, float x, float y) override;
+    bool onMouse(int x, int y, sk_app::Window::InputState state, uint32_t modifiers) override;
+    bool onMouseWheel(float delta, uint32_t modifiers) override;
+    void onUIStateChanged(const SkString& stateName, const SkString& stateValue) override;
+    bool onKey(sk_app::Window::Key key, sk_app::Window::InputState state, uint32_t modifiers) override;
+    bool onChar(SkUnichar c, uint32_t modifiers) override;
 
 private:
     enum class ColorMode {