Make navigating the picture view in debugger more functional.
https://codereview.appspot.com/7134049/


git-svn-id: http://skia.googlecode.com/svn/trunk@7253 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/debugger/QT/SkCanvasWidget.cpp b/debugger/QT/SkCanvasWidget.cpp
index 2f714d7..63a0e05 100644
--- a/debugger/QT/SkCanvasWidget.cpp
+++ b/debugger/QT/SkCanvasWidget.cpp
@@ -29,8 +29,7 @@
     fHorizontalLayout.addWidget(&fGLWidget);
 
     fPreviousPoint.set(0,0);
-    fUserOffset.set(0,0);
-    fUserScaleFactor = 1.0;
+    fUserMatrix.reset();
 
     setWidgetVisibility(kGPU_WidgetType, true);
     connect(&fRasterWidget, SIGNAL(drawComplete()),
@@ -48,9 +47,10 @@
 
 void SkCanvasWidget::mouseMoveEvent(QMouseEvent* event) {
     SkIPoint eventPoint = SkIPoint::Make(event->globalX(), event->globalY());
-    fUserOffset += eventPoint - fPreviousPoint;
+    SkIPoint eventOffset = eventPoint - fPreviousPoint;
     fPreviousPoint = eventPoint;
-    fDebugger->setUserOffset(fUserOffset);
+    fUserMatrix.postTranslate(eventOffset.fX, eventOffset.fY);
+    fDebugger->setUserMatrix(fUserMatrix);
     drawTo(fDebugger->index());
 }
 
@@ -61,15 +61,50 @@
 }
 
 void SkCanvasWidget::mouseDoubleClickEvent(QMouseEvent* event) {
-    resetWidgetTransform();
+    Qt::KeyboardModifiers modifiers = event->modifiers();
+    if (modifiers.testFlag(Qt::ControlModifier)) {
+        snapWidgetTransform();
+    } else {
+        resetWidgetTransform();
+    }
+}
+
+#define ZOOM_FACTOR (1.25f)
+
+void SkCanvasWidget::wheelEvent(QWheelEvent* event) {
+    Qt::KeyboardModifiers modifiers = event->modifiers();
+    if (modifiers.testFlag(Qt::ControlModifier)) {
+        zoom(event->delta() > 0 ? ZOOM_FACTOR : (1.0f / ZOOM_FACTOR), event->x(), event->y());
+    } else {
+        if (Qt::Horizontal == event->orientation()) {
+            fUserMatrix.postTranslate(event->delta(), 0.0f);
+        } else {
+            fUserMatrix.postTranslate(0.0f, event->delta());
+        }
+        fDebugger->setUserMatrix(fUserMatrix);
+        drawTo(fDebugger->index());
+    }
+}
+
+void SkCanvasWidget::zoom(int zoomCommand) {
+    zoom(kIn_ZoomCommand == zoomCommand ? ZOOM_FACTOR : (1.0f / ZOOM_FACTOR),
+         this->size().width() / 2, this->size().height() / 2);
+}
+
+void SkCanvasWidget::snapWidgetTransform() {
+    double x, y;
+    modf(fUserMatrix.getTranslateX(), &x);
+    modf(fUserMatrix.getTranslateY(), &y);
+    fUserMatrix[SkMatrix::kMTransX] = x;
+    fUserMatrix[SkMatrix::kMTransY] = y;
+    fDebugger->setUserMatrix(fUserMatrix);
+    drawTo(fDebugger->index());
 }
 
 void SkCanvasWidget::resetWidgetTransform() {
-    fUserOffset.set(0,0);
-    fUserScaleFactor = 1.0;
-    fDebugger->setUserOffset(fUserOffset);
-    fDebugger->setUserScale(fUserScaleFactor);
-    emit scaleFactorChanged(fUserScaleFactor);
+    fUserMatrix.reset();
+    fDebugger->setUserMatrix(fUserMatrix);
+    emit scaleFactorChanged(fUserMatrix.getScaleX());
     drawTo(fDebugger->index());
 }
 
@@ -81,17 +116,9 @@
     }
 }
 
-void SkCanvasWidget::zoom(float zoomIncrement) {
-    fUserScaleFactor += zoomIncrement;
-
-    /* The range of the fUserScaleFactor crosses over the range -1,0,1 frequently.
-    * Based on the code below, -1 and 1 both scale the image to it's original
-    * size we do the following to never have a registered wheel scroll
-    * not effect the fUserScaleFactor. */
-    if (fUserScaleFactor == 0) {
-        fUserScaleFactor = 2 * zoomIncrement;
-    }
-    emit scaleFactorChanged(fUserScaleFactor);
-    fDebugger->setUserScale(fUserScaleFactor);
+void SkCanvasWidget::zoom(float scale, int px, int py) {
+    fUserMatrix.postScale(scale, scale, px, py);
+    emit scaleFactorChanged(fUserMatrix.getScaleX());
+    fDebugger->setUserMatrix(fUserMatrix);
     drawTo(fDebugger->index());
 }
diff --git a/debugger/QT/SkCanvasWidget.h b/debugger/QT/SkCanvasWidget.h
index ab634f8..f6bc618 100644
--- a/debugger/QT/SkCanvasWidget.h
+++ b/debugger/QT/SkCanvasWidget.h
@@ -34,28 +34,34 @@
 
     void setWidgetVisibility(WidgetType type, bool isHidden);
 
-    void zoom(float zoomIncrement);
+    /** Zooms the canvas by scale with the transformation centered at the widget point (px, py). */
+    void zoom(float scale, int px, int py);
 
     void resetWidgetTransform();
 
+    enum ZoomCommandTypes {
+        kIn_ZoomCommand,
+        kOut_ZoomCommand,
+    };
+public slots:
+    /**
+     *  Zooms in or out (see ZoomCommandTypes) by the standard zoom factor
+     *  with the transformation centered in the middle of the widget.
+     */
+    void zoom(int zoomCommand);
+
 signals:
     void scaleFactorChanged(float newScaleFactor);
     void commandChanged(int newCommand);
     void hitChanged(int hit);
 
-private slots:
-    void keyZoom(int zoomIncrement) {
-        zoom(zoomIncrement);
-    }
-
 private:
     QHBoxLayout fHorizontalLayout;
     SkRasterWidget fRasterWidget;
     SkGLWidget fGLWidget;
     SkDebugger* fDebugger;
     SkIPoint fPreviousPoint;
-    SkIPoint fUserOffset;
-    float fUserScaleFactor;
+    SkMatrix fUserMatrix;
 
     void mouseMoveEvent(QMouseEvent* event);
 
@@ -63,9 +69,9 @@
 
     void mouseDoubleClickEvent(QMouseEvent* event);
 
-    void wheelEvent(QWheelEvent* event) {
-        zoom(event->delta()/120);
-    }
+    void wheelEvent(QWheelEvent* event);
+
+    void snapWidgetTransform();
 };
 
 
diff --git a/debugger/QT/SkDebuggerGUI.cpp b/debugger/QT/SkDebuggerGUI.cpp
index e445171..058cb5c 100644
--- a/debugger/QT/SkDebuggerGUI.cpp
+++ b/debugger/QT/SkDebuggerGUI.cpp
@@ -98,12 +98,12 @@
     connect(&fActionSaveAs, SIGNAL(triggered()), this, SLOT(actionSaveAs()));
     connect(&fActionSave, SIGNAL(triggered()), this, SLOT(actionSave()));
 
-    fMapper.setMapping(&fActionZoomIn, 1);
-    fMapper.setMapping(&fActionZoomOut, -1);
+    fMapper.setMapping(&fActionZoomIn, SkCanvasWidget::kIn_ZoomCommand);
+    fMapper.setMapping(&fActionZoomOut, SkCanvasWidget::kOut_ZoomCommand);
 
     connect(&fActionZoomIn, SIGNAL(triggered()), &fMapper, SLOT(map()));
     connect(&fActionZoomOut, SIGNAL(triggered()), &fMapper, SLOT(map()));
-    connect(&fMapper, SIGNAL(mapped(int)), &fCanvasWidget, SLOT(keyZoom(int)));
+    connect(&fMapper, SIGNAL(mapped(int)), &fCanvasWidget, SLOT(zoom(int)));
 
     fInspectorWidget.setDisabled(true);
     fMenuEdit.setDisabled(true);
@@ -144,10 +144,10 @@
                            const SkTDArray<size_t>& offsets,
                            const SkTDArray<bool>& deletedCommands)
         : INHERITED(stream, info, isValid, decoder)
-        , fTot(0.0)
-        , fCurCommand(0)
         , fOffsets(offsets)
-        , fSkipCommands(deletedCommands) {
+        , fSkipCommands(deletedCommands)
+        , fTot(0.0)
+        , fCurCommand(0) {
         fTimes.setCount(fOffsets.count());
         fTypeTimes.setCount(LAST_DRAWTYPE_ENUM+1);
         this->resetTimes();
diff --git a/debugger/QT/SkSettingsWidget.cpp b/debugger/QT/SkSettingsWidget.cpp
index d9268be..c74be40 100644
--- a/debugger/QT/SkSettingsWidget.cpp
+++ b/debugger/QT/SkSettingsWidget.cpp
@@ -151,12 +151,6 @@
     return &fVisibleOn;
 }
 
-void SkSettingsWidget::setZoomText(int scaleFactor) {
-    if(scaleFactor == 1 || scaleFactor == -1) {
-        fZoomBox.setText("100%");
-    } else if (scaleFactor > 1) {
-        fZoomBox.setText(QString::number(scaleFactor*100).append("%"));
-    } else if (scaleFactor < -1) {
-        fZoomBox.setText(QString::number(100 / pow(2.0f, (-scaleFactor - 1))).append("%"));
-    }
+void SkSettingsWidget::setZoomText(float scale) {
+    fZoomBox.setText(QString::number(scale*100, 'f', 0).append("%"));
 }
diff --git a/debugger/QT/SkSettingsWidget.h b/debugger/QT/SkSettingsWidget.h
index fdb99d1..1f75527 100644
--- a/debugger/QT/SkSettingsWidget.h
+++ b/debugger/QT/SkSettingsWidget.h
@@ -34,7 +34,8 @@
      */
     SkSettingsWidget();
 
-    void setZoomText(int scaleFactor);
+    /** Sets the displayed user zoom level. A scale of 1.0 represents no zoom. */
+    void setZoomText(float scale);
 
     QRadioButton* getVisibilityButton();
 
diff --git a/debugger/SkDebugCanvas.cpp b/debugger/SkDebugCanvas.cpp
index 5fcc076..e4cfec8 100644
--- a/debugger/SkDebugCanvas.cpp
+++ b/debugger/SkDebugCanvas.cpp
@@ -39,8 +39,7 @@
     fBm.setConfig(SkBitmap::kNo_Config, fWidth, fHeight);
     fFilter = false;
     fIndex = 0;
-    fUserOffset.set(0,0);
-    fUserScale = 1.0;
+    fUserMatrix.reset();
 }
 
 SkDebugCanvas::~SkDebugCanvas() {
@@ -63,13 +62,7 @@
 }
 
 void SkDebugCanvas::applyUserTransform(SkCanvas* canvas) {
-    canvas->translate(SkIntToScalar(fUserOffset.fX),
-                      SkIntToScalar(fUserOffset.fY));
-    if (fUserScale < 0) {
-        canvas->scale((1.0f / -fUserScale), (1.0f / -fUserScale));
-    } else if (fUserScale > 0) {
-        canvas->scale(fUserScale, fUserScale);
-    }
+    canvas->concat(fUserMatrix);
 }
 
 int SkDebugCanvas::getCommandAtPoint(int x, int y, int index) {
diff --git a/debugger/SkDebugCanvas.h b/debugger/SkDebugCanvas.h
index a94663d..aafa802 100644
--- a/debugger/SkDebugCanvas.h
+++ b/debugger/SkDebugCanvas.h
@@ -110,12 +110,8 @@
         fHeight = height;
     }
 
-    void setUserOffset(SkIPoint offset) {
-        fUserOffset = offset;
-    }
-
-    void setUserScale(float scale) {
-        fUserScale = scale;
+    void setUserMatrix(SkMatrix matrix) {
+        fUserMatrix = matrix;
     }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -212,8 +208,7 @@
     SkBitmap fBm;
     bool fFilter;
     int fIndex;
-    SkIPoint fUserOffset;
-    float fUserScale;
+    SkMatrix fUserMatrix;
     SkMatrix fMatrix;
     SkIRect fClip;
 
diff --git a/debugger/SkDebugger.h b/debugger/SkDebugger.h
index a0a1682..0e3906e 100644
--- a/debugger/SkDebugger.h
+++ b/debugger/SkDebugger.h
@@ -66,13 +66,9 @@
         return fDebugCanvas->getSize();
     }
 
-    void setUserOffset(SkIPoint userOffset) {
+    void setUserMatrix(SkMatrix userMatrix) {
         // Should this live in debugger instead?
-        fDebugCanvas->setUserOffset(userOffset);
-    }
-
-    void setUserScale(float userScale) {
-        fDebugCanvas->setUserScale(userScale);
+        fDebugCanvas->setUserMatrix(userMatrix);
     }
 
     int getCommandAtPoint(int x, int y, int index) {