Add more SampleApp support to viewer.

Fixes loading a Sample as first slide.
Adds char input.
Adds --slide and --list options.

Change-Id: I34b66818e3673fcfdc649443e7d9dfb74b478062
Reviewed-on: https://skia-review.googlesource.com/8445
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
diff --git a/samplecode/SampleAndroidShadows.cpp b/samplecode/SampleAndroidShadows.cpp
index 0e0a4b4..803ea49 100644
--- a/samplecode/SampleAndroidShadows.cpp
+++ b/samplecode/SampleAndroidShadows.cpp
@@ -70,32 +70,43 @@
 
         SkUnichar uni;
         if (SampleCode::CharQ(*evt, &uni)) {
+            bool handled = false;
             switch (uni) {
                 case 'W':
                     fShowAmbient = !fShowAmbient;
+                    handled = true;
                     break;
                 case 'S':
                     fShowSpot = !fShowSpot;
+                    handled = true;
                     break;
                 case 'T':
                     fUseAlt = !fUseAlt;
+                    handled = true;
                     break;
                 case 'O':
                     fShowObject = !fShowObject;
+                    handled = true;
                     break;
                 case '>':
                     fZDelta += 0.5f;
+                    handled = true;
                     break;
                 case '<':
                     fZDelta -= 0.5f;
+                    handled = true;
                     break;
                 case '?':
                     fIgnoreShadowAlpha = !fIgnoreShadowAlpha;
+                    handled = true;
                     break;
                 default:
                     break;
             }
-            this->inval(nullptr);
+            if (handled) {
+                this->inval(nullptr);
+                return true;
+            }
         }
         return this->INHERITED::onQuery(evt);
     }
@@ -503,7 +514,7 @@
     }
 
 private:
-    typedef SkView INHERITED;
+    typedef SampleView INHERITED;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/tools/viewer/GMSlide.cpp b/tools/viewer/GMSlide.cpp
index 6096044..40a9c99 100644
--- a/tools/viewer/GMSlide.cpp
+++ b/tools/viewer/GMSlide.cpp
@@ -30,3 +30,7 @@
 bool GMSlide::animate(const SkAnimTimer& timer) {
     return fGM->animate(timer);
 }
+
+bool GMSlide::onChar(SkUnichar c) {
+    return fGM->handleKey(c);
+}
diff --git a/tools/viewer/GMSlide.h b/tools/viewer/GMSlide.h
index 6b03527..2a3faa9 100644
--- a/tools/viewer/GMSlide.h
+++ b/tools/viewer/GMSlide.h
@@ -21,6 +21,8 @@
     void draw(SkCanvas* canvas) override;
     bool animate(const SkAnimTimer&) override;
 
+    bool onChar(SkUnichar c) override;
+
 private:
     skiagm::GM* fGM;
 };
diff --git a/tools/viewer/SampleSlide.cpp b/tools/viewer/SampleSlide.cpp
index 5e02ff1..00ee534 100644
--- a/tools/viewer/SampleSlide.cpp
+++ b/tools/viewer/SampleSlide.cpp
@@ -36,6 +36,12 @@
     fView = nullptr;
 }
 
+bool SampleSlide::onChar(SkUnichar c) {
+    SkEvent evt(gCharEvtName);
+    evt.setFast32(c);
+    return fView->doQuery(&evt);
+}
+
 #if defined(SK_BUILD_FOR_ANDROID)
 // these are normally defined in SkOSWindow_unix, but we don't
 // want to include that
diff --git a/tools/viewer/SampleSlide.h b/tools/viewer/SampleSlide.h
index 3d772d0..ab45c01 100644
--- a/tools/viewer/SampleSlide.h
+++ b/tools/viewer/SampleSlide.h
@@ -26,6 +26,8 @@
         return false;
     }
 
+    bool onChar(SkUnichar c) override;
+
 private:
     const SkViewFactory*   fViewFactory;
     SkView*                fView;
diff --git a/tools/viewer/Slide.h b/tools/viewer/Slide.h
index bc0ffd4..9ec7a3d 100644
--- a/tools/viewer/Slide.h
+++ b/tools/viewer/Slide.h
@@ -28,6 +28,8 @@
     virtual void load(SkScalar winWidth, SkScalar winHeight) {}
     virtual void unload() {}
 
+    virtual bool onChar(SkUnichar c) { return false; }
+
     SkString getName() { return fName; }
 
 protected:
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index 1d5e033..fe3b0d9 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -113,6 +113,9 @@
                "If a bench does not match any list entry,\n"
                "it is skipped unless some list entry starts with ~");
 
+DEFINE_string(slide, "", "Start on this sample.");
+DEFINE_bool(list, false, "List samples?");
+
 #ifdef SK_VULKAN
 #    define BACKENDS_STR "\"sw\", \"gl\", and \"vk\""
 #else
@@ -169,6 +172,7 @@
 
 Viewer::Viewer(int argc, char** argv, void* platformData)
     : fCurrentMeasurement(0)
+    , fSetupFirstFrame(false)
     , fDisplayStats(false)
     , fRefresh(false)
     , fShowImGuiDebugWindow(false)
@@ -319,13 +323,13 @@
 
     // set up slides
     this->initSlides();
+    this->setStartupSlide();
+    if (FLAGS_list) {
+        this->listNames();
+    }
 
     fAnimTimer.run();
 
-    // set up first frame
-    fCurrentSlide = 0;
-    setupCurrentSlide(-1);
-
     // ImGui initialization:
     ImGuiIO& io = ImGui::GetIO();
     io.DisplaySize.x = static_cast<float>(fWindow->width());
@@ -469,6 +473,32 @@
     fWindow->setTitle(title.c_str());
 }
 
+void Viewer::setStartupSlide() {
+
+    if (!FLAGS_slide.isEmpty()) {
+        int count = fSlides.count();
+        for (int i = 0; i < count; i++) {
+            if (fSlides[i]->getName().equals(FLAGS_slide[0])) {
+                fCurrentSlide = i;
+                return;
+            }
+        }
+
+        fprintf(stderr, "Unknown slide \"%s\"\n", FLAGS_slide[0]);
+        this->listNames();
+    }
+
+    fCurrentSlide = 0;
+}
+
+void Viewer::listNames() {
+    int count = fSlides.count();
+    SkDebugf("All Slides:\n");
+    for (int i = 0; i < count; i++) {
+        SkDebugf("    %s\n", fSlides[i]->getName().c_str());
+    }
+}
+
 void Viewer::setupCurrentSlide(int previousSlide) {
     if (fCurrentSlide == previousSlide) {
         return; // no change; do nothing
@@ -613,6 +643,13 @@
 }
 
 void Viewer::onPaint(SkCanvas* canvas) {
+    // We have to wait until the first draw to make sure the window size is set correctly
+    if (!fSetupFirstFrame) {
+        // set up first frame
+        setupCurrentSlide(-1);
+        fSetupFirstFrame = true;
+    }
+
     // Update ImGui input
     ImGuiIO& io = ImGui::GetIO();
     io.DeltaTime = 1.0f / 60.0f;
@@ -949,5 +986,10 @@
 }
 
 bool Viewer::onChar(SkUnichar c, uint32_t modifiers) {
+    if (fSlides[fCurrentSlide]->onChar(c)) {
+        fWindow->inval();
+        return true;
+    }
+
     return fCommands.onChar(c, modifiers);
 }
diff --git a/tools/viewer/Viewer.h b/tools/viewer/Viewer.h
index 74d3f06..f4847e5 100644
--- a/tools/viewer/Viewer.h
+++ b/tools/viewer/Viewer.h
@@ -34,7 +34,9 @@
     void initSlides();
     void updateTitle();
     void setColorMode(SkColorType, sk_sp<SkColorSpace>);
+    void setStartupSlide();
     void setupCurrentSlide(int previousSlide);
+    void listNames();
 
     void updateUIState();
 
@@ -56,6 +58,7 @@
     SkAnimTimer            fAnimTimer;
     SkTArray<sk_sp<Slide>> fSlides;
     int                    fCurrentSlide;
+    bool                   fSetupFirstFrame;
 
     bool                   fDisplayStats;
     bool                   fRefresh; // whether to continuously refresh for measuring render time