From e1b84c98ed843b6857c8b98fa52954cd0695eb3b Mon Sep 17 00:00:00 2001 From: Warwick Allison Date: Fri, 28 Aug 2009 10:32:25 +1000 Subject: Changes as per review. WebView: idealWidth -> preferredWidth (as per QSizePolicy) WebView: idealHeight -> preferredHeight WebView: status -> statusText WebView: mouseX -> clickX (parameter to onDoubleClick) WebView: mouseY -> clickY (parameter to onDoubleClick) WebView: cacheSize -> pixelCacheSize (may later go away) WebView: lost "interactive" property (always true now) --- demos/declarative/webbrowser/webbrowser.qml | 38 ++- examples/declarative/webview/autosize.qml | 10 +- examples/declarative/webview/evalandattach.qml | 1 - src/declarative/QmlChanges.txt | 7 + src/declarative/fx/qfxwebview.cpp | 454 +++++++++++++------------ src/declarative/fx/qfxwebview.h | 70 ++-- 6 files changed, 304 insertions(+), 276 deletions(-) diff --git a/demos/declarative/webbrowser/webbrowser.qml b/demos/declarative/webbrowser/webbrowser.qml index ec3a2a0..15c3943 100644 --- a/demos/declarative/webbrowser/webbrowser.qml +++ b/demos/declarative/webbrowser/webbrowser.qml @@ -17,10 +17,10 @@ Item { function zoomOut() { WebBrowser.state = "ZoomedOut"; } - function toggleZoom() { + function toggleZoom(x,y) { if(WebBrowser.state == "ZoomedOut") { - Flick.centerX = MyWebView.mouseX; - Flick.centerY = MyWebView.mouseY; + Flick.centerX = x + Flick.centerY = y WebBrowser.state = "Normal"; } else { zoomOut(); @@ -135,9 +135,9 @@ Item { text: WebBrowser.urlString label: "url:" - onConfirmed: { print ('OnConfirmed: '+EditUrl.text); WebBrowser.urlString = EditUrl.text; print (EditUrl.text); MyWebView.focus=true } + onConfirmed: { WebBrowser.urlString = EditUrl.text; MyWebView.focus=true } onCancelled: { MyWebView.focus=true } - onStartEdit: { print (EditUrl.text); MyWebView.focus=false } + onStartEdit: { MyWebView.focus=false } anchors.left: UrlBox.left anchors.right: UrlBox.right @@ -188,20 +188,36 @@ Item { WebView { id: MyWebView - cacheSize: 4000000 + pixelCacheSize: 4000000 - url: WebBrowser.urlString + Script { + function fixUrl(url) + { + if (url == "") return url + if (url[0] == "/") return "file://"+url + if (url.indexOf(":")<0) { + if (url.indexOf(".")<0 || url.indexOf(" ")>=0) { + // Fall back to a search engine; hard-code Wikipedia + return "http://en.wikipedia.org/w/index.php?search="+url + } else { + return "http://"+url + } + } + return url + } + } + + url: fixUrl(WebBrowser.urlString) smooth: !Flick.moving fillColor: "white" focus: true - interactive: true - idealWidth: Flick.width - idealHeight: Flick.height/scale + preferredWidth: Flick.width + preferredHeight: Flick.height/scale scale: (width > 0) ? Flick.width/width*zoomedOut+(1-zoomedOut) : 1 onUrlChanged: { if (url != null) { WebBrowser.urlString = url.toString(); } } - onDoubleClick: { toggleZoom() } + onDoubleClick: { toggleZoom(clickX,clickY) } property real zoomedOut : 1 } diff --git a/examples/declarative/webview/autosize.qml b/examples/declarative/webview/autosize.qml index 13004a8..dbd94e1 100644 --- a/examples/declarative/webview/autosize.qml +++ b/examples/declarative/webview/autosize.qml @@ -1,7 +1,7 @@ import Qt 4.6 // The WebView size is determined by the width, height, -// idealWidth, and idealHeight properties. +// preferredWidth, and preferredHeight properties. Rectangle { id: Rect color: "white" @@ -34,16 +34,16 @@ Rectangle { } } WebView { - idealWidth: Rect.width/2 - html: "The idealWidth is half." + preferredWidth: Rect.width/2 + html: "The preferredWidth is half." Rectangle { color: "#10000000" anchors.fill: parent } } WebView { - idealWidth: Rect.width/2 - html: "The_idealWidth_is_half." + preferredWidth: Rect.width/2 + html: "The_preferredWidth_is_half." Rectangle { color: "#10000000" anchors.fill: parent diff --git a/examples/declarative/webview/evalandattach.qml b/examples/declarative/webview/evalandattach.qml index bf7f25e..e755819 100644 --- a/examples/declarative/webview/evalandattach.qml +++ b/examples/declarative/webview/evalandattach.qml @@ -35,7 +35,6 @@ Item { anchors.left: parent.left anchors.right: parent.right focus: true - interactive: true settings.pluginsEnabled: true javaScriptWindowObjects: [ Object { diff --git a/src/declarative/QmlChanges.txt b/src/declarative/QmlChanges.txt index e01b9af..22d0470 100644 --- a/src/declarative/QmlChanges.txt +++ b/src/declarative/QmlChanges.txt @@ -42,6 +42,12 @@ Transition: operations -> animations Transition: fromState -> from Transition: toState -> to Follow: followValue -> value +WebView: idealWidth -> preferredWidth +WebView: idealHeight -> preferredHeight +WebView: status -> statusText +WebView: mouseX -> clickX (parameter to onDoubleClick) +WebView: mouseY -> clickY (parameter to onDoubleClick) +WebView: cacheSize -> pixelCacheSize Additions: MouseRegion: add "acceptedButtons" property @@ -53,6 +59,7 @@ Deletions: Column/VerticalPositioner: lost "margins" property Row/HorizontalPositioner: lost "margins" property Grid/Positioner/Layout: lost "margins" property +WebView: lost "interactive" property (always true now) Other Changes: Drag: axis becomes an enum with values "XAxis", "YAxis", "XandYAxis" diff --git a/src/declarative/fx/qfxwebview.cpp b/src/declarative/fx/qfxwebview.cpp index d3ce00c..46f1c1a 100644 --- a/src/declarative/fx/qfxwebview.cpp +++ b/src/declarative/fx/qfxwebview.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include "qml.h" #include "qmlengine.h" @@ -62,25 +63,24 @@ QT_BEGIN_NAMESPACE QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,WebView,QFxWebView) +QML_DEFINE_NOCREATE_TYPE(QAction) static const int MAX_DOUBLECLICK_TIME=500; // XXX need better gesture system class QFxWebSettings : public QObject { Q_OBJECT - /* - ### Add these - StandardFont, - FixedFont, - SerifFont, - SansSerifFont, - CursiveFont, - FantasyFont - - MinimumFontSize, - MinimumLogicalFontSize, - DefaultFontSize, - DefaultFixedFontSize - */ + + Q_PROPERTY(QString standardFontFamily READ standardFontFamily WRITE setStandardFontFamily) + Q_PROPERTY(QString fixedFontFamily READ fixedFontFamily WRITE setFixedFontFamily) + Q_PROPERTY(QString serifFontFamily READ serifFontFamily WRITE setSerifFontFamily) + Q_PROPERTY(QString sansSerifFontFamily READ sansSerifFontFamily WRITE setSansSerifFontFamily) + Q_PROPERTY(QString cursiveFontFamily READ cursiveFontFamily WRITE setCursiveFontFamily) + Q_PROPERTY(QString fantasyFontFamily READ fantasyFontFamily WRITE setFantasyFontFamily) + + Q_PROPERTY(int minimumFontSize READ minimumFontSize WRITE setMinimumFontSize) + Q_PROPERTY(int minimumLogicalFontSize READ minimumLogicalFontSize WRITE setMinimumLogicalFontSize) + Q_PROPERTY(int defaultFontSize READ defaultFontSize WRITE setDefaultFontSize) + Q_PROPERTY(int defaultFixedFontSize READ defaultFixedFontSize WRITE setDefaultFixedFontSize) Q_PROPERTY(bool autoLoadImages READ autoLoadImages WRITE setAutoLoadImages) Q_PROPERTY(bool javascriptEnabled READ javascriptEnabled WRITE setJavascriptEnabled) @@ -96,10 +96,33 @@ class QFxWebSettings : public QObject { Q_PROPERTY(bool offlineStorageDatabaseEnabled READ offlineStorageDatabaseEnabled WRITE setOfflineStorageDatabaseEnabled) Q_PROPERTY(bool offlineWebApplicationCacheEnabled READ offlineWebApplicationCacheEnabled WRITE setOfflineWebApplicationCacheEnabled) Q_PROPERTY(bool localStorageDatabaseEnabled READ localStorageDatabaseEnabled WRITE setLocalStorageDatabaseEnabled) + Q_PROPERTY(bool localContentCanAccessRemoteUrls READ localContentCanAccessRemoteUrls WRITE setLocalContentCanAccessRemoteUrls) public: QFxWebSettings() {} + QString standardFontFamily() const { return s->fontFamily(QWebSettings::StandardFont); } + void setStandardFontFamily(const QString& f) { s->setFontFamily(QWebSettings::StandardFont,f); } + QString fixedFontFamily() const { return s->fontFamily(QWebSettings::FixedFont); } + void setFixedFontFamily(const QString& f) { s->setFontFamily(QWebSettings::FixedFont,f); } + QString serifFontFamily() const { return s->fontFamily(QWebSettings::SerifFont); } + void setSerifFontFamily(const QString& f) { s->setFontFamily(QWebSettings::SerifFont,f); } + QString sansSerifFontFamily() const { return s->fontFamily(QWebSettings::SansSerifFont); } + void setSansSerifFontFamily(const QString& f) { s->setFontFamily(QWebSettings::SansSerifFont,f); } + QString cursiveFontFamily() const { return s->fontFamily(QWebSettings::CursiveFont); } + void setCursiveFontFamily(const QString& f) { s->setFontFamily(QWebSettings::CursiveFont,f); } + QString fantasyFontFamily() const { return s->fontFamily(QWebSettings::FantasyFont); } + void setFantasyFontFamily(const QString& f) { s->setFontFamily(QWebSettings::FantasyFont,f); } + + int minimumFontSize() const { return s->fontSize(QWebSettings::MinimumFontSize); } + void setMinimumFontSize(int size) { s->setFontSize(QWebSettings::MinimumFontSize,size); } + int minimumLogicalFontSize() const { return s->fontSize(QWebSettings::MinimumLogicalFontSize); } + void setMinimumLogicalFontSize(int size) { s->setFontSize(QWebSettings::MinimumLogicalFontSize,size); } + int defaultFontSize() const { return s->fontSize(QWebSettings::DefaultFontSize); } + void setDefaultFontSize(int size) { s->setFontSize(QWebSettings::DefaultFontSize,size); } + int defaultFixedFontSize() const { return s->fontSize(QWebSettings::DefaultFixedFontSize); } + void setDefaultFixedFontSize(int size) { s->setFontSize(QWebSettings::DefaultFixedFontSize,size); } + bool autoLoadImages() const { return s->testAttribute(QWebSettings::AutoLoadImages); } void setAutoLoadImages(bool on) { s->setAttribute(QWebSettings::AutoLoadImages, on); } bool javascriptEnabled() const { return s->testAttribute(QWebSettings::JavascriptEnabled); } @@ -128,6 +151,8 @@ public: void setOfflineWebApplicationCacheEnabled(bool on) { s->setAttribute(QWebSettings::OfflineWebApplicationCacheEnabled, on); } bool localStorageDatabaseEnabled() const { return s->testAttribute(QWebSettings::LocalStorageDatabaseEnabled); } void setLocalStorageDatabaseEnabled(bool on) { s->setAttribute(QWebSettings::LocalStorageDatabaseEnabled, on); } + bool localContentCanAccessRemoteUrls() const { return s->testAttribute(QWebSettings::LocalContentCanAccessRemoteUrls); } + void setLocalContentCanAccessRemoteUrls(bool on) { s->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, on); } QWebSettings *s; }; @@ -141,21 +166,19 @@ class QFxWebViewPrivate : public QFxPaintedItemPrivate public: QFxWebViewPrivate() - : QFxPaintedItemPrivate(), page(0), idealwidth(0), idealheight(0), interactive(true), lastPress(0), lastRelease(0), mouseX(0), mouseY(0), - progress(1.0), pending(PendingNone), windowObjects(this) + : QFxPaintedItemPrivate(), page(0), idealwidth(0), idealheight(0), + progress(1.0), status(QFxWebView::Null), pending(PendingNone), windowObjects(this) { } + QUrl url; // page url might be different if it has not loaded yet QWebPage *page; int idealwidth; int idealheight; - bool interactive; - QMouseEvent *lastPress, *lastRelease; - int mouseX, mouseY; qreal progress; - QBasicTimer dcTimer; - QString statusBarMessage; + QFxWebView::Status status; + QString statusText; enum { PendingNone, PendingUrl, PendingHtml, PendingContent } pending; QUrl pending_url; QString pending_string; @@ -188,12 +211,12 @@ public: dynamically adjust to a size appropriate for the content. This width may be large (eg. 980) for typical online web pages. - If the idealWidth is set, the width will be this amount or larger, - usually laying out the web content to fit the idealWidth. + If the preferredWidth is set, the width will be this amount or larger, + usually laying out the web content to fit the preferredWidth. - If the idealHeight is set, the height will be this amount or larger. + If the preferredHeight is set, the height will be this amount or larger. Due to WebKit limitations, the height may be more than necessary - if the idealHeight is changed after the content is loaded. + if the preferredHeight is changed after the content is loaded. \qml WebView { @@ -278,9 +301,18 @@ void QFxWebView::componentComplete() d->updateWindowObjects(); } +QFxWebView::Status QFxWebView::status() const +{ + Q_D(const QFxWebView); + return d->status; +} + + /*! \qmlproperty real WebView::progress This property holds the progress of loading the current URL, from 0 to 1. + + \sa onLoadFinished() onLoadFailed() */ qreal QFxWebView::progress() const { @@ -288,6 +320,16 @@ qreal QFxWebView::progress() const return d->progress; } +void QFxWebView::doLoadStarted() +{ + Q_D(QFxWebView); + if (!d->url.isEmpty()) { + d->status = Loading; + emit statusChanged(d->status); + } + emit loadStarted(); +} + void QFxWebView::doLoadProgress(int p) { Q_D(QFxWebView); @@ -298,116 +340,114 @@ void QFxWebView::doLoadProgress(int p) emit progressChanged(); } +void QFxWebView::pageUrlChanged() +{ + Q_D(QFxWebView); + if ((d->url.isEmpty() && page()->mainFrame()->url() != QUrl(QLatin1String("about:blank"))) + || d->url != page()->mainFrame()->url()) + { + d->url = page()->mainFrame()->url(); + if (d->url == QUrl(QLatin1String("about:blank"))) + d->url = QUrl(); + emit urlChanged(); + } +} + void QFxWebView::doLoadFinished(bool ok) { - // XXX bug 232556 - pages with no title never get this signal + Q_D(QFxWebView); if (title().isEmpty()) - emit urlChanged(); + pageUrlChanged(); // XXX bug 232556 - pages with no title never get urlChanged() if (ok) { + d->status = d->url.isEmpty() ? Null : Ready; emit loadFinished(); } else { + d->status = Error; emit loadFailed(); } + emit statusChanged(d->status); } /*! \qmlproperty url WebView::url - This property holds the URL to the page displayed in this item. + This property holds the URL to the page displayed in this item. It can be set, + but also can change spontaneously (eg. because of network redirection). + + If the url is empty, the page is blank. - Note that after this property is set, it may take some time - before the change is notified, as this only happens when - loading of the URL successfully starts. + The url is always absolute (QML will resolve relative URL strings in the context + of the containing QML document). */ QUrl QFxWebView::url() const { - return page()->mainFrame()->url(); + Q_D(const QFxWebView); + return d->url; } void QFxWebView::setUrl(const QUrl &url) { - if (url.isEmpty()) { - // Make absolute. - setUrl(QUrl("about:blank")); - return; - } - Q_D(QFxWebView); - if (url == page()->mainFrame()->url()) + if (url == d->url) return; - page()->setViewportSize(QSize( - d->idealwidth>0 ? d->idealwidth : width(), - d->idealheight>0 ? d->idealheight : height())); + if (isComponentComplete()) { + d->url = url; + page()->setViewportSize(QSize( + d->idealwidth>0 ? d->idealwidth : width(), + d->idealheight>0 ? d->idealheight : height())); - Q_ASSERT(!url.isRelative()); + QUrl seturl = url; + if (seturl.isEmpty()) + seturl = QUrl(QLatin1String("about:blank")); - if (isComponentComplete()) - page()->mainFrame()->load(url); - else { + Q_ASSERT(!seturl.isRelative()); + + page()->mainFrame()->load(seturl); + + emit urlChanged(); + } else { d->pending = d->PendingUrl; d->pending_url = url; } - - // emit urlChanged() - not until actually loaded } /*! - \qmlproperty int WebView::idealWidth + \qmlproperty int WebView::preferredWidth This property holds the ideal width for displaying the current URL. */ -int QFxWebView::idealWidth() const +int QFxWebView::preferredWidth() const { Q_D(const QFxWebView); return d->idealwidth; } -void QFxWebView::setIdealWidth(int iw) +void QFxWebView::setPreferredWidth(int iw) { Q_D(QFxWebView); if (d->idealwidth == iw) return; d->idealwidth = iw; expandToWebPage(); - emit idealWidthChanged(); + emit preferredWidthChanged(); } /*! - \qmlproperty int WebView::idealHeight + \qmlproperty int WebView::preferredHeight This property holds the ideal height for displaying the current URL. */ -int QFxWebView::idealHeight() const +int QFxWebView::preferredHeight() const { Q_D(const QFxWebView); return d->idealheight; } -void QFxWebView::setIdealHeight(int ih) +void QFxWebView::setPreferredHeight(int ih) { Q_D(QFxWebView); if (d->idealheight == ih) return; d->idealheight = ih; expandToWebPage(); - emit idealHeightChanged(); -} - -/*! - \qmlproperty bool WebView::interactive - - This property controls whether the item responds to mouse and - key events. -*/ -bool QFxWebView::interactive() const -{ - Q_D(const QFxWebView); - return d->interactive; -} - -void QFxWebView::setInteractive(bool i) -{ - Q_D(QFxWebView); - if (d->interactive == i) return; - d->interactive = i; - emit interactiveChanged(); + emit preferredHeightChanged(); } /*! @@ -464,19 +504,19 @@ void QFxWebView::paintPage(const QRect& r) } /*! - \qmlproperty int WebView::cacheSize + \qmlproperty int WebView::pixelCacheSize This property holds the maximum number of pixels of image cache to allow. The default is 0.1 megapixels. The cache will not be larger than the (unscaled) size of the WebView. */ -int QFxWebView::cacheSize() const +int QFxWebView::pixelCacheSize() const { Q_D(const QFxWebView); return d->max_imagecache_size; } -void QFxWebView::setCacheSize(int pixels) +void QFxWebView::setPixelCacheSize(int pixels) { Q_D(QFxWebView); if (pixels < d->max_imagecache_size) { @@ -612,85 +652,38 @@ static QMouseEvent *sceneHoverMoveEventToMouseEvent(QGraphicsSceneHoverEvent *e) } -void QFxWebView::timerEvent(QTimerEvent *event) -{ - Q_D(QFxWebView); - if (event->timerId() ==d->dcTimer.timerId()) { - d->dcTimer.stop(); - if (d->lastPress) { - page()->event(d->lastPress); - delete d->lastPress; - d->lastPress = 0; - } - if (d->lastRelease) { - page()->event(d->lastRelease); - delete d->lastRelease; - d->lastRelease = 0; - } - } -} - -int QFxWebView::mouseX() const -{ - Q_D(const QFxWebView); - if (d->lastPress) - return d->lastPress->x(); - if (d->lastRelease) - return d->lastRelease->x(); - return d->mouseX; -} +/*! + \qmlsignal WebView::onDoubleClick(clickx,clicky) -int QFxWebView::mouseY() const -{ - Q_D(const QFxWebView); - if (d->lastPress) - return d->lastPress->y(); - if (d->lastRelease) - return d->lastRelease->y(); - return d->mouseY; -} + The WebView does not pass double-click events to the engine, but rather + emits this signals. +*/ void QFxWebView::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) { QMouseEvent *me = sceneMouseEventToMouseEvent(event); - Q_D(QFxWebView); - d->dcTimer.stop(); - delete d->lastPress; - delete d->lastRelease; - d->lastPress = 0; - d->lastRelease = 0; - d->mouseX = me->x(); - d->mouseY = me->y(); - emit doubleClick(); - d->mouseX = 0; - d->mouseY = 0; + emit doubleClick(me->x(),me->y()); delete me; } void QFxWebView::mousePressEvent(QGraphicsSceneMouseEvent *event) { - Q_D(QFxWebView); - if (d->interactive) { - setFocus (true); - QMouseEvent *me = sceneMouseEventToMouseEvent(event); - if (d->lastPress) delete d->lastPress; - d->lastPress = me; - page()->event(me); - event->setAccepted( - /* - It is not correct to send the press event upwards, if it is not accepted by WebKit - e.g. push button does not work, if done so as QGraphicsScene will not send the release event at all to WebKit - Might be a bug in WebKit, though - */ + setFocus (true); + QMouseEvent *me = sceneMouseEventToMouseEvent(event); + page()->event(me); + event->setAccepted( +/* + It is not correct to send the press event upwards, if it is not accepted by WebKit + e.g. push button does not work, if done so as QGraphicsScene will not send the release event at all to WebKit + Might be a bug in WebKit, though + */ #if 1 //QT_VERSION <= 0x040500 // XXX see bug 230835 - true + true #else - me->isAccepted() + me->isAccepted() #endif - ); - } else { - event->setAccepted(false); - } + ); + delete me; if (!event->isAccepted()) { QFxPaintedItem::mousePressEvent(event); } @@ -698,27 +691,20 @@ void QFxWebView::mousePressEvent(QGraphicsSceneMouseEvent *event) void QFxWebView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { - Q_D(QFxWebView); - if (d->interactive) { - QMouseEvent *me = sceneMouseEventToMouseEvent(event); - if (d->lastRelease) delete d->lastRelease; - d->lastRelease = me; - page()->event(me); - d->dcTimer.start(MAX_DOUBLECLICK_TIME,this); - event->setAccepted( - /* - It is not correct to send the press event upwards, if it is not accepted by WebKit - e.g. push button does not work, if done so as QGraphicsScene will not send all the events to WebKit - */ + QMouseEvent *me = sceneMouseEventToMouseEvent(event); + page()->event(me); + event->setAccepted( +/* + It is not correct to send the press event upwards, if it is not accepted by WebKit + e.g. push button does not work, if done so as QGraphicsScene will not send all the events to WebKit + */ #if 1 //QT_VERSION <= 0x040500 // XXX see bug 230835 - true + true #else - me->isAccepted() + me->isAccepted() #endif - ); - } else { - event->setAccepted(false); - } + ); + delete me; if (!event->isAccepted()) { QFxPaintedItem::mouseReleaseEvent(event); } @@ -726,86 +712,67 @@ void QFxWebView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) void QFxWebView::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { - Q_D(const QFxWebView); - if (d->interactive && !d->dcTimer.isActive()) { - QMouseEvent *me = sceneMouseEventToMouseEvent(event); - page()->event(me); - event->setAccepted( - /* - It is not correct to send the press event upwards, if it is not accepted by WebKit - e.g. push button does not work, if done so as QGraphicsScene will not send the release event at all to WebKit - Might be a bug in WebKit, though - */ + QMouseEvent *me = sceneMouseEventToMouseEvent(event); + page()->event(me); + event->setAccepted( +/* + It is not correct to send the press event upwards, if it is not accepted by WebKit + e.g. push button does not work, if done so as QGraphicsScene will not send the release event at all to WebKit + Might be a bug in WebKit, though + */ #if 1 // QT_VERSION <= 0x040500 // XXX see bug 230835 - true + true #else - me->isAccepted() + me->isAccepted() #endif - ); - delete me; - } else { - event->setAccepted(false); - } + ); + delete me; if (!event->isAccepted()) QFxPaintedItem::mouseMoveEvent(event); } void QFxWebView::hoverMoveEvent (QGraphicsSceneHoverEvent * event) { - Q_D(const QFxWebView); - if (d->interactive) { - QMouseEvent *me = sceneHoverMoveEventToMouseEvent(event); - page()->event(me); - event->setAccepted( + QMouseEvent *me = sceneHoverMoveEventToMouseEvent(event); + page()->event(me); + event->setAccepted( #if QT_VERSION <= 0x040500 // XXX see bug 230835 - true + true #else - me->isAccepted() + me->isAccepted() #endif - ); - delete me; - } - else { - event->setAccepted(false); - } + ); + delete me; if (!event->isAccepted()) QFxPaintedItem::hoverMoveEvent(event); } void QFxWebView::keyPressEvent(QKeyEvent* event) { - Q_D(const QFxWebView); - if (d->interactive) - page()->event(event); + page()->event(event); if (!event->isAccepted()) QFxPaintedItem::keyPressEvent(event); } void QFxWebView::keyReleaseEvent(QKeyEvent* event) { - Q_D(const QFxWebView); - if (d->interactive) - page()->event(event); + page()->event(event); if (!event->isAccepted()) QFxPaintedItem::keyReleaseEvent(event); } bool QFxWebView::sceneEvent(QEvent *event) { - Q_D(const QFxWebView); - - if (d->interactive) { - if (event->type() == QEvent::KeyPress) { - QKeyEvent *k = static_cast(event); - if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) { - if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier? - page()->event(event); - if (event->isAccepted()) - return true; - } + if (event->type() == QEvent::KeyPress) { + QKeyEvent *k = static_cast(event); + if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) { + if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier? + page()->event(event); + if (event->isAccepted()) + return true; } } - } + } return QFxPaintedItem::sceneEvent(event); } @@ -909,11 +876,17 @@ qreal QFxWebView::zoomFactor() const return page()->mainFrame()->zoomFactor(); } -void QFxWebView::setStatusBarMessage(const QString& s) +/*! + \qmlproperty string WebView::statusText + + This property is the current status suggested by the current web page. In a web browser, + such status is often shown in some kind of status bar. +*/ +void QFxWebView::setStatusText(const QString& s) { Q_D(QFxWebView); - d->statusBarMessage = s; - emit statusChanged(); + d->statusText = s; + emit statusTextChanged(); } void QFxWebView::windowObjectCleared() @@ -922,10 +895,10 @@ void QFxWebView::windowObjectCleared() d->updateWindowObjects(); } -QString QFxWebView::status() const +QString QFxWebView::statusText() const { Q_D(const QFxWebView); - return d->statusBarMessage; + return d->statusText; } QWebPage *QFxWebView::page() const @@ -955,6 +928,18 @@ QWebPage *QFxWebView::page() const // The QObject interface to settings(). /*! + \qmlproperty string WebView::settings.standardFontFamily + \qmlproperty string WebView::settings.fixedFontFamily + \qmlproperty string WebView::settings.serifFontFamily + \qmlproperty string WebView::settings.sansSerifFontFamily + \qmlproperty string WebView::settings.cursiveFontFamily + \qmlproperty string WebView::settings.fantasyFontFamily + + \qmlproperty int WebView::settings.minimumFontSize + \qmlproperty int WebView::settings.minimumLogicalFontSize + \qmlproperty int WebView::settings.defaultFontSize + \qmlproperty int WebView::settings.defaultFixedFontSize + \qmlproperty bool WebView::settings.autoLoadImages \qmlproperty bool WebView::settings.javascriptEnabled \qmlproperty bool WebView::settings.javaEnabled @@ -969,14 +954,16 @@ QWebPage *QFxWebView::page() const \qmlproperty bool WebView::settings.offlineStorageDatabaseEnabled \qmlproperty bool WebView::settings.offlineWebApplicationCacheEnabled \qmlproperty bool WebView::settings.localStorageDatabaseEnabled + \qmlproperty bool WebView::settings.localContentCanAccessRemoteUrls These properties give access to the settings controlling the web view. - See QWebSettings for the list of sub-properties. + See QWebSettings for details of these properties. \qml WebView { settings.pluginsEnabled: true + settings.standardFontFamily: "Arial" ... } \endqml @@ -1007,18 +994,45 @@ void QFxWebView::setPage(QWebPage *page) d->page->mainFrame()->setScrollBarPolicy(Qt::Horizontal,Qt::ScrollBarAlwaysOff); d->page->mainFrame()->setScrollBarPolicy(Qt::Vertical,Qt::ScrollBarAlwaysOff); connect(d->page,SIGNAL(repaintRequested(QRect)),this,SLOT(paintPage(QRect))); - connect(d->page->mainFrame(),SIGNAL(urlChanged(QUrl)),this,SIGNAL(urlChanged())); + connect(d->page->mainFrame(),SIGNAL(urlChanged(QUrl)),this,SLOT(pageUrlChanged())); connect(d->page->mainFrame(), SIGNAL(titleChanged(QString)), this, SIGNAL(titleChanged(QString))); connect(d->page->mainFrame(), SIGNAL(iconChanged()), this, SIGNAL(iconChanged())); - connect(d->page,SIGNAL(loadStarted()),this,SIGNAL(loadStarted())); + connect(d->page,SIGNAL(loadStarted()),this,SLOT(doLoadStarted())); connect(d->page,SIGNAL(loadProgress(int)),this,SLOT(doLoadProgress(int))); connect(d->page,SIGNAL(loadFinished(bool)),this,SLOT(doLoadFinished(bool))); - connect(d->page,SIGNAL(statusBarMessage(QString)),this,SLOT(setStatusBarMessage(QString))); + connect(d->page,SIGNAL(statusBarMessage(QString)),this,SLOT(setStatusText(QString))); connect(d->page->mainFrame(),SIGNAL(javaScriptWindowObjectCleared()),this,SLOT(windowObjectCleared())); } +/*! + \qmlsignal WebView::onLoadStarted() + + This handler is called when the web engine begins loading + a page. + + \sa progress onLoadFinished() onLoadFailed() +*/ + +/*! + \qmlsignal WebView::onLoadFinished() + + This handler is called when the web engine finishes loading + a page, including any component content. + + \sa progress onLoadFailed() +*/ + +/*! + \qmlsignal WebView::onLoadFailed() + + This handler is called when the web engine fails loading + a page or any component content. + + \sa progress onLoadFinished() +*/ + void QFxWebView::load(const QNetworkRequest &request, QNetworkAccessManager::Operation operation, const QByteArray &body) diff --git a/src/declarative/fx/qfxwebview.h b/src/declarative/fx/qfxwebview.h index ef40912..5a42ec4 100644 --- a/src/declarative/fx/qfxwebview.h +++ b/src/declarative/fx/qfxwebview.h @@ -85,30 +85,27 @@ class Q_DECLARATIVE_EXPORT QFxWebView : public QFxPaintedItem { Q_OBJECT + Q_ENUMS(Status) + Q_PROPERTY(QString title READ title NOTIFY titleChanged) Q_PROPERTY(QPixmap icon READ icon NOTIFY iconChanged) Q_PROPERTY(qreal textSizeMultiplier READ textSizeMultiplier WRITE setTextSizeMultiplier DESIGNABLE false) Q_PROPERTY(qreal zoomFactor READ zoomFactor WRITE setZoomFactor NOTIFY zoomFactorChanged) - Q_PROPERTY(QString status READ status NOTIFY statusChanged) //### statusText - - Q_PROPERTY(int mouseX READ mouseX) //### remove and find way to handle double clicking - Q_PROPERTY(int mouseY READ mouseY) + Q_PROPERTY(QString statusText READ statusText NOTIFY statusTextChanged) Q_PROPERTY(QString html READ html WRITE setHtml) - Q_PROPERTY(int idealWidth READ idealWidth WRITE setIdealWidth NOTIFY idealWidthChanged) //### widthHint - Q_PROPERTY(int idealHeight READ idealHeight WRITE setIdealHeight NOTIFY idealHeightChanged) //### heightHint - Q_PROPERTY(int cacheSize READ cacheSize WRITE setCacheSize) //### remove - Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged) //### change immediately + Q_PROPERTY(int preferredWidth READ preferredWidth WRITE setPreferredWidth NOTIFY preferredWidthChanged) + Q_PROPERTY(int preferredHeight READ preferredHeight WRITE setPreferredHeight NOTIFY preferredHeightChanged) + Q_PROPERTY(int pixelCacheSize READ pixelCacheSize WRITE setPixelCacheSize) + Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged) Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) - //### status + Q_PROPERTY(Status status READ status NOTIFY statusChanged) - Q_PROPERTY(bool interactive READ interactive WRITE setInteractive NOTIFY interactiveChanged) //### Really needed here?? - - Q_PROPERTY(QObject* reload READ reloadAction CONSTANT) //### QAction - Q_PROPERTY(QObject* back READ backAction CONSTANT) //### QAction - Q_PROPERTY(QObject* forward READ forwardAction CONSTANT) //### QAction - Q_PROPERTY(QObject* stop READ stopAction CONSTANT) //### QAction + Q_PROPERTY(QAction* reload READ reloadAction CONSTANT) + Q_PROPERTY(QAction* back READ backAction CONSTANT) + Q_PROPERTY(QAction* forward READ forwardAction CONSTANT) + Q_PROPERTY(QAction* stop READ stopAction CONSTANT) Q_PROPERTY(QFxWebSettings* settings READ settingsObject CONSTANT) @@ -131,19 +128,15 @@ public: qreal zoomFactor() const; void setZoomFactor(qreal); - bool interactive() const; - void setInteractive(bool); - - int mouseX() const; - int mouseY() const; - - int idealWidth() const; - void setIdealWidth(int); - int idealHeight() const; - void setIdealHeight(int); - + int preferredWidth() const; + void setPreferredWidth(int); + int preferredHeight() const; + void setPreferredHeight(int); + enum Status { Null, Ready, Loading, Error }; + Status status() const; qreal progress() const; + QString statusText() const; QAction *reloadAction() const; QAction *backAction() const; @@ -166,32 +159,29 @@ public: QWebSettings *settings() const; QFxWebSettings *settingsObject() const; - QString status() const; - - int cacheSize() const; - void setCacheSize(int pixels); + int pixelCacheSize() const; + void setPixelCacheSize(int pixels); QmlList *javaScriptWindowObjects(); static QFxWebViewAttached *qmlAttachedProperties(QObject *); Q_SIGNALS: - void idealWidthChanged(); - void idealHeightChanged(); + void preferredWidthChanged(); + void preferredHeightChanged(); void urlChanged(); - void interactiveChanged(); void progressChanged(); + void statusChanged(Status); void titleChanged(const QString&); void iconChanged(); - void statusChanged(); + void statusTextChanged(); void zoomFactorChanged(); - void loadStarted(); //### document with \qmlsignal + void loadStarted(); void loadFinished(); void loadFailed(); - void singleClick(); - void doubleClick(); + void doubleClick(int clickX, int clickY); public Q_SLOTS: QVariant evaluateJavaScript(const QString&); @@ -199,10 +189,12 @@ public Q_SLOTS: private Q_SLOTS: void expandToWebPage(); void paintPage(const QRect&); + void doLoadStarted(); void doLoadProgress(int p); void doLoadFinished(bool ok); - void setStatusBarMessage(const QString&); + void setStatusText(const QString&); void windowObjectCleared(); + void pageUrlChanged(); protected: QFxWebView(QFxWebViewPrivate &dd, QFxItem *parent); @@ -216,7 +208,6 @@ protected: void hoverMoveEvent (QGraphicsSceneHoverEvent * event); void keyPressEvent(QKeyEvent* event); void keyReleaseEvent(QKeyEvent* event); - void timerEvent(QTimerEvent *event); virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); virtual void focusChanged(bool); @@ -232,6 +223,7 @@ private: QT_END_NAMESPACE QML_DECLARE_TYPE(QFxWebView) +QML_DECLARE_TYPE(QAction) QT_END_HEADER -- cgit v0.12 From 1a742b1a1c646d7941f3f714b21c2ef927a0390b Mon Sep 17 00:00:00 2001 From: Warwick Allison Date: Fri, 28 Aug 2009 14:54:46 +1000 Subject: New window creation in WebView. --- examples/declarative/webview/newwindows.html | 3 + examples/declarative/webview/newwindows.qml | 28 ++++++++ src/declarative/fx/qfxwebview.cpp | 103 ++++++++++++++++++++++++++- src/declarative/fx/qfxwebview.h | 12 ++++ 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 examples/declarative/webview/newwindows.html create mode 100644 examples/declarative/webview/newwindows.qml diff --git a/examples/declarative/webview/newwindows.html b/examples/declarative/webview/newwindows.html new file mode 100644 index 0000000..f169599 --- /dev/null +++ b/examples/declarative/webview/newwindows.html @@ -0,0 +1,3 @@ +

Multiple windows...

+ +Popup! diff --git a/examples/declarative/webview/newwindows.qml b/examples/declarative/webview/newwindows.qml new file mode 100644 index 0000000..7fd9d63 --- /dev/null +++ b/examples/declarative/webview/newwindows.qml @@ -0,0 +1,28 @@ +// Demonstrates opening new WebViews from HTML +// +// Note that to open windows from JavaScript, you will need to +// allow it with WebView.settings.javascriptCanOpenWindows: true + +import Qt 4.6 + +Row { + id: Pages + height: 200 + resources: [ + Component { + id: WebViewPage + Rectangle { + width: WV.width + height: WV.height + WebView { + id: WV + newWindowComponent: WebViewPage + newWindowParent: Pages + url: "newwindows.html" + } + } + } + ] + width: 500 + ComponentInstance { component: WebViewPage } +} diff --git a/src/declarative/fx/qfxwebview.cpp b/src/declarative/fx/qfxwebview.cpp index 46f1c1a..b150ca0 100644 --- a/src/declarative/fx/qfxwebview.cpp +++ b/src/declarative/fx/qfxwebview.cpp @@ -167,7 +167,9 @@ class QFxWebViewPrivate : public QFxPaintedItemPrivate public: QFxWebViewPrivate() : QFxPaintedItemPrivate(), page(0), idealwidth(0), idealheight(0), - progress(1.0), status(QFxWebView::Null), pending(PendingNone), windowObjects(this) + progress(1.0), status(QFxWebView::Null), pending(PendingNone), + newWindowComponent(0), newWindowParent(0), + windowObjects(this) { } @@ -184,6 +186,8 @@ public: QString pending_string; QByteArray pending_data; mutable QFxWebSettings settings; + QmlComponent *newWindowComponent; + QFxItem *newWindowParent; void updateWindowObjects(); class WindowObjectList : public QmlConcreteList @@ -1099,6 +1103,95 @@ QWebSettings *QFxWebView::settings() const return page()->settings(); } +QFxWebView *QFxWebView::createWindow(QWebPage::WebWindowType type) +{ + Q_D(QFxWebView); + switch (type) { + case QWebPage::WebBrowserWindow: { + if (!d->newWindowComponent && d->newWindowParent) + qWarning("WebView::newWindowComponent not set - WebView::newWindowParent ignored"); + else if (d->newWindowComponent && !d->newWindowParent) + qWarning("WebView::newWindowParent not set - WebView::newWindowComponent ignored"); + else if (d->newWindowComponent && d->newWindowParent) { + QFxWebView *webview = 0; + QmlContext *windowContext = new QmlContext(qmlContext(this)); + + QObject *nobj = d->newWindowComponent->create(windowContext); + if (nobj) { + windowContext->setParent(nobj); + QFxItem *item = qobject_cast(nobj); + if (!item) { + delete nobj; + } else { + webview = item->findChild(); + if (!webview) { + delete item; + } else { + item->setParent(d->newWindowParent); + } + } + } else { + delete windowContext; + } + + return webview; + } + } + break; + case QWebPage::WebModalDialog: { + // Not supported + } + } + return 0; +} + +/*! + \qmlproperty component WebView::newWindowComponent + + This property holds the component to use for new windows. + The component must have a WebView somewhere in its structure. + + When the web engine requests a new window, it will be an instance of + this component. + + The parent of the new window is set by newWindowParent. It must be set. +*/ +QmlComponent *QFxWebView::newWindowComponent() const +{ + Q_D(const QFxWebView); + return d->newWindowComponent; +} + +void QFxWebView::setNewWindowComponent(QmlComponent *newWindow) +{ + Q_D(QFxWebView); + delete d->newWindowComponent; + d->newWindowComponent = newWindow; +} + + +/*! + \qmlproperty item WebView::newWindowParent + + The parent item for new windows. + + \sa newWindowComponent +*/ +QFxItem *QFxWebView::newWindowParent() const +{ + Q_D(const QFxWebView); + return d->newWindowParent; +} + +void QFxWebView::setNewWindowParent(QFxItem *parent) +{ + Q_D(QFxWebView); + delete d->newWindowParent; + d->newWindowParent = parent; +} + + + /*! \internal \class QFxWebPage @@ -1206,6 +1299,14 @@ QObject *QFxWebPage::createPlugin(const QString &, const QUrl &url, const QStrin return new QWidget_Dummy_Plugin(comp,viewItem(),paramNames,paramValues); } +QWebPage *QFxWebPage::createWindow(WebWindowType type) +{ + QFxWebView *newView = viewItem()->createWindow(type); + if (newView) + return newView->page(); + return 0; +} + QT_END_NAMESPACE #include "qfxwebview.moc" diff --git a/src/declarative/fx/qfxwebview.h b/src/declarative/fx/qfxwebview.h index 5a42ec4..c8fd7a0 100644 --- a/src/declarative/fx/qfxwebview.h +++ b/src/declarative/fx/qfxwebview.h @@ -69,6 +69,8 @@ public: ~QFxWebPage(); protected: QObject *createPlugin(const QString &classid, const QUrl &url, const QStringList ¶mNames, const QStringList ¶mValues); + QWebPage *createWindow(WebWindowType type); + private: QFxWebView *viewItem(); }; @@ -111,6 +113,9 @@ class Q_DECLARATIVE_EXPORT QFxWebView : public QFxPaintedItem Q_PROPERTY(QmlList* javaScriptWindowObjects READ javaScriptWindowObjects CONSTANT) + Q_PROPERTY(QmlComponent* newWindowComponent READ newWindowComponent WRITE setNewWindowComponent) + Q_PROPERTY(QFxItem* newWindowParent READ newWindowParent WRITE setNewWindowParent) + public: QFxWebView(QFxItem *parent=0); ~QFxWebView(); @@ -166,6 +171,11 @@ public: static QFxWebViewAttached *qmlAttachedProperties(QObject *); + QmlComponent *newWindowComponent() const; + void setNewWindowComponent(QmlComponent *newWindow); + QFxItem *newWindowParent() const; + void setNewWindowParent(QFxItem *newWindow); + Q_SIGNALS: void preferredWidthChanged(); void preferredHeightChanged(); @@ -212,12 +222,14 @@ protected: const QRectF &oldGeometry); virtual void focusChanged(bool); virtual bool sceneEvent(QEvent *event); + QFxWebView *createWindow(QWebPage::WebWindowType type); private: void init(); virtual void componentComplete(); Q_DISABLE_COPY(QFxWebView) Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QFxWebView) + friend class QFxWebPage; }; QT_END_NAMESPACE -- cgit v0.12 From d52e5a6cf3fd4471027dd502a6c41ad03a1bdc43 Mon Sep 17 00:00:00 2001 From: Warwick Allison Date: Fri, 28 Aug 2009 15:17:04 +1000 Subject: doc --- examples/declarative/webview/newwindows.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/declarative/webview/newwindows.qml b/examples/declarative/webview/newwindows.qml index 7fd9d63..9ff902e2 100644 --- a/examples/declarative/webview/newwindows.qml +++ b/examples/declarative/webview/newwindows.qml @@ -1,7 +1,7 @@ // Demonstrates opening new WebViews from HTML // // Note that to open windows from JavaScript, you will need to -// allow it with WebView.settings.javascriptCanOpenWindows: true +// allow it on WebView with settings.javascriptCanOpenWindows: true import Qt 4.6 -- cgit v0.12 From 6167fdb50ba6d8b2d2592d42ba0055c2107141d4 Mon Sep 17 00:00:00 2001 From: Warwick Allison Date: Fri, 28 Aug 2009 15:19:49 +1000 Subject: doc --- src/declarative/fx/qfxwebview.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/declarative/fx/qfxwebview.h b/src/declarative/fx/qfxwebview.h index c8fd7a0..f084d61 100644 --- a/src/declarative/fx/qfxwebview.h +++ b/src/declarative/fx/qfxwebview.h @@ -79,7 +79,6 @@ private: class QFxWebViewAttached; class QFxWebSettings; -//### TODO: new web "windows" (popups) //### TODO: browser plugins //### TODO: smart zooming using e.g. DIV -- cgit v0.12 From 797162603f35af1f67dd5a7c35c7004eef1828a9 Mon Sep 17 00:00:00 2001 From: Warwick Allison Date: Fri, 28 Aug 2009 15:20:29 +1000 Subject: doc --- src/declarative/QmlChanges.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/declarative/QmlChanges.txt b/src/declarative/QmlChanges.txt index 22d0470..bb5d0d8 100644 --- a/src/declarative/QmlChanges.txt +++ b/src/declarative/QmlChanges.txt @@ -54,6 +54,7 @@ MouseRegion: add "acceptedButtons" property MouseRegion: add "hoverEnabled" property MouseRegion: add "pressedButtons" property Timer: add start() and stop() slots +WebView: add newWindowComponent and newWindowParent properties Deletions: Column/VerticalPositioner: lost "margins" property -- cgit v0.12 From 67ca33d53ff805ca33ba775524086a89cb975df9 Mon Sep 17 00:00:00 2001 From: Martin Jones Date: Fri, 28 Aug 2009 16:42:02 +1000 Subject: Make a number of Flickable API changes following review. --- demos/declarative/contacts/contacts.qml | 4 +- demos/declarative/flickr/common/ImageDetails.qml | 8 +- demos/declarative/webbrowser/webbrowser.qml | 18 ++--- doc/src/tutorials/declarative.qdoc | 2 +- examples/declarative/velocity/Day.qml | 2 +- src/declarative/QmlChanges.txt | 5 ++ src/declarative/fx/qfxflickable.cpp | 98 ++++++++++++------------ src/declarative/fx/qfxflickable.h | 55 ++++++------- src/declarative/fx/qfxflickable_p.h | 12 +-- src/declarative/fx/qfxgridview.cpp | 6 +- src/declarative/fx/qfxlistview.cpp | 6 +- 11 files changed, 113 insertions(+), 103 deletions(-) diff --git a/demos/declarative/contacts/contacts.qml b/demos/declarative/contacts/contacts.qml index ebcab69..81d8352 100644 --- a/demos/declarative/contacts/contacts.qml +++ b/demos/declarative/contacts/contacts.qml @@ -101,7 +101,7 @@ Rectangle { } PropertyChanges { target: contactListView - yPosition: wrapper.y + viewportY: wrapper.y } PropertyChanges { target: contactListView @@ -121,7 +121,7 @@ Rectangle { Transition { NumberAnimation { duration: 500 - properties: "yPosition,height,opacity" + properties: "viewportY,height,opacity" } } ] diff --git a/demos/declarative/flickr/common/ImageDetails.qml b/demos/declarative/flickr/common/ImageDetails.qml index 8098273..dcd44eb 100644 --- a/demos/declarative/flickr/common/ImageDetails.qml +++ b/demos/declarative/flickr/common/ImageDetails.qml @@ -121,12 +121,12 @@ Flipable { id: Slider; x: 25; y: 374; visible: { BigImage.status == 1 && maximum > minimum } onValueChanged: { if (BigImage.width * value > Flick.width) { - var xoff = (Flick.width/2 + Flick.xPosition) * value / prevScale; - Flick.xPosition = xoff - Flick.width/2; + var xoff = (Flick.width/2 + Flick.viewportX) * value / prevScale; + Flick.viewportX = xoff - Flick.width/2; } if (BigImage.height * value > Flick.height) { - var yoff = (Flick.height/2 + Flick.yPosition) * value / prevScale; - Flick.yPosition = yoff - Flick.height/2; + var yoff = (Flick.height/2 + Flick.viewportY) * value / prevScale; + Flick.viewportY = yoff - Flick.height/2; } prevScale = value; } diff --git a/demos/declarative/webbrowser/webbrowser.qml b/demos/declarative/webbrowser/webbrowser.qml index ec3a2a0..126fa87 100644 --- a/demos/declarative/webbrowser/webbrowser.qml +++ b/demos/declarative/webbrowser/webbrowser.qml @@ -48,8 +48,8 @@ Item { anchors.bottom: Footer.top } RectSoftShadow { - x: -Flick.xPosition - y: -Flick.yPosition + x: -Flick.viewportX + y: -Flick.viewportY width: MyWebView.width*MyWebView.scale height: Flick.y+MyWebView.height*MyWebView.scale } @@ -72,10 +72,10 @@ Item { width: parent.width height: 64 state: "Normal" - x: Flick.xPosition < 0 ? -Flick.xPosition : Flick.xPosition > Flick.viewportWidth-Flick.width - ? -Flick.xPosition+Flick.viewportWidth-Flick.width : 0 - y: Flick.yPosition < 0 ? -Flick.yPosition : progressOff* - (Flick.yPosition>height?-height:-Flick.yPosition) + x: Flick.viewportX < 0 ? -Flick.viewportX : Flick.viewportX > Flick.viewportWidth-Flick.width + ? -Flick.viewportX+Flick.viewportWidth-Flick.width : 0 + y: Flick.viewportY < 0 ? -Flick.viewportY : progressOff* + (Flick.viewportY>height?-height:-Flick.viewportY) Text { id: HeaderText @@ -335,8 +335,8 @@ Item { State { name: "Normal" PropertyChanges { target: MyWebView; zoomedOut: 0 } - PropertyChanges { target: Flick; explicit: true; xPosition: Math.min(MyWebView.width-Flick.width,Math.max(0,Flick.centerX-Flick.width/2)) } - PropertyChanges { target: Flick; explicit: true; yPosition: Math.min(MyWebView.height-Flick.height,Math.max(0,Flick.centerY-Flick.height/2)) } + PropertyChanges { target: Flick; explicit: true; viewportX: Math.min(MyWebView.width-Flick.width,Math.max(0,Flick.centerX-Flick.width/2)) } + PropertyChanges { target: Flick; explicit: true; viewportY: Math.min(MyWebView.height-Flick.height,Math.max(0,Flick.centerY-Flick.height/2)) } }, State { name: "ZoomedOut" @@ -360,7 +360,7 @@ Item { } NumberAnimation { target: Flick - properties: "xPosition,yPosition" + properties: "viewportX,viewportY" easing: "easeInOutQuad" duration: 200 } diff --git a/doc/src/tutorials/declarative.qdoc b/doc/src/tutorials/declarative.qdoc index dde0615..7780988 100644 --- a/doc/src/tutorials/declarative.qdoc +++ b/doc/src/tutorials/declarative.qdoc @@ -614,7 +614,7 @@ This defines the open state of the delegate. It changes the height of the delegate component to that of the whole list view, pushing the other items off each end of - the list. It sets the lists views scroll yPosition of the ListView to the + the list. It sets the listview's scroll viewportY of the ListView to the y value of the delegate so that the top of the delegate matches the top of the list view. The next step is to lock the list view. This prevents the user being able to flick the list view. The final to properties that are set should diff --git a/examples/declarative/velocity/Day.qml b/examples/declarative/velocity/Day.qml index 1a336b7..06d0bd4 100644 --- a/examples/declarative/velocity/Day.qml +++ b/examples/declarative/velocity/Day.qml @@ -32,7 +32,7 @@ Rectangle { y: Math.random() * 300 + 50 id: StickyPage rotation: Follow { - source: -Flick.xVelocity / 100 + source: -Flick.horizontalVelocity / 100 spring: 2.0 damping: 0.1 } diff --git a/src/declarative/QmlChanges.txt b/src/declarative/QmlChanges.txt index e01b9af..e760037 100644 --- a/src/declarative/QmlChanges.txt +++ b/src/declarative/QmlChanges.txt @@ -42,6 +42,11 @@ Transition: operations -> animations Transition: fromState -> from Transition: toState -> to Follow: followValue -> value +Flickable: xPosition -> viewportX +Flickable: yPosition -> viewportY +Flickable: xVelocity -> horizontalVelocity +Flickable: yVelocity -> verticalVelocity +Flickable: velocityDecay -> reportedVelocitySmoothing Additions: MouseRegion: add "acceptedButtons" property diff --git a/src/declarative/fx/qfxflickable.cpp b/src/declarative/fx/qfxflickable.cpp index ea38026..07df799 100644 --- a/src/declarative/fx/qfxflickable.cpp +++ b/src/declarative/fx/qfxflickable.cpp @@ -96,7 +96,7 @@ QFxFlickablePrivate::QFxFlickablePrivate() : _flick(new QFxItem), _moveX(_flick, &QFxItem::setX), _moveY(_flick, &QFxItem::setY) , vWidth(-1), vHeight(-1), overShoot(true), flicked(false), moving(false), stealMouse(false) , pressed(false), maxVelocity(-1), locked(false), dragMode(QFxFlickable::Hard) - , elasticY(_moveY), elasticX(_moveX), velocityDecay(100), xVelocity(this), yVelocity(this) + , elasticY(_moveY), elasticX(_moveX), reportedVelocitySmoothing(100), horizontalVelocity(this), verticalVelocity(this) , vTime(0), atXEnd(false), atXBeginning(true), pageXPosition(0.), pageWidth(0.) , atYEnd(false), atYBeginning(true), pageYPosition(0.), pageHeight(0.) { @@ -112,8 +112,8 @@ void QFxFlickablePrivate::init() QObject::connect(&_tl, SIGNAL(completed()), q, SLOT(movementEnding())); q->setAcceptedMouseButtons(Qt::LeftButton); q->setFiltersChildEvents(true); - QObject::connect(_flick, SIGNAL(xChanged()), q, SIGNAL(positionChanged())); - QObject::connect(_flick, SIGNAL(yChanged()), q, SIGNAL(positionChanged())); + QObject::connect(_flick, SIGNAL(xChanged()), q, SIGNAL(positionXChanged())); + QObject::connect(_flick, SIGNAL(yChanged()), q, SIGNAL(positionYChanged())); QObject::connect(&elasticX, SIGNAL(updated()), q, SLOT(ticked())); QObject::connect(&elasticY, SIGNAL(updated()), q, SLOT(ticked())); QObject::connect(q, SIGNAL(heightChanged()), q, SLOT(heightChange())); @@ -369,20 +369,20 @@ QFxFlickable::~QFxFlickable() } /*! - \qmlproperty int Flickable::xPosition - \qmlproperty int Flickable::yPosition + \qmlproperty int Flickable::viewportX + \qmlproperty int Flickable::viewportY These properties hold the surface coordinate currently at the top-left corner of the Flickable. For example, if you flick an image up 100 pixels, \c yPosition will be 100. */ -qreal QFxFlickable::xPosition() const +qreal QFxFlickable::viewportX() const { Q_D(const QFxFlickable); return -d->_moveX.value(); } -void QFxFlickable::setXPosition(qreal pos) +void QFxFlickable::setViewportX(qreal pos) { Q_D(QFxFlickable); pos = qRound(pos); @@ -393,13 +393,13 @@ void QFxFlickable::setXPosition(qreal pos) } } -qreal QFxFlickable::yPosition() const +qreal QFxFlickable::viewportY() const { Q_D(const QFxFlickable); return -d->_moveY.value(); } -void QFxFlickable::setYPosition(qreal pos) +void QFxFlickable::setViewportY(qreal pos) { Q_D(QFxFlickable); pos = qRound(pos); @@ -454,21 +454,25 @@ void QFxFlickable::setDragMode(DragMode mode) } /*! - \qmlproperty real Flickable::xVelocity - \qmlproperty real Flickable::yVelocity + \qmlproperty real Flickable::horizontalVelocity + \qmlproperty real Flickable::verticalVelocity + \qmlproperty real Flickable::reportedVelocitySmoothing The instantaneous velocity of movement along the x and y axes, in pixels/sec. + + The reported velocity is smoothed to avoid erratic output. + reportedVelocitySmoothing determines how much smoothing is applied. */ -qreal QFxFlickable::xVelocity() const +qreal QFxFlickable::horizontalVelocity() const { Q_D(const QFxFlickable); - return d->xVelocity.value(); + return d->horizontalVelocity.value(); } -qreal QFxFlickable::yVelocity() const +qreal QFxFlickable::verticalVelocity() const { Q_D(const QFxFlickable); - return d->yVelocity.value(); + return d->verticalVelocity.value(); } /*! @@ -760,18 +764,18 @@ void QFxFlickable::viewportMoved() qreal prevX = d->lastFlickablePosition.y(); d->velocityTimeline.clear(); if (d->pressed) { - qreal xVelocity = (prevX - d->_moveX.value()) * 1000 / elapsed; - qreal yVelocity = (prevY - d->_moveY.value()) * 1000 / elapsed; - d->velocityTimeline.move(d->xVelocity, xVelocity, d->velocityDecay); - d->velocityTimeline.move(d->xVelocity, 0, d->velocityDecay); - d->velocityTimeline.move(d->yVelocity, yVelocity, d->velocityDecay); - d->velocityTimeline.move(d->yVelocity, 0, d->velocityDecay); + qreal horizontalVelocity = (prevX - d->_moveX.value()) * 1000 / elapsed; + qreal verticalVelocity = (prevY - d->_moveY.value()) * 1000 / elapsed; + d->velocityTimeline.move(d->horizontalVelocity, horizontalVelocity, d->reportedVelocitySmoothing); + d->velocityTimeline.move(d->horizontalVelocity, 0, d->reportedVelocitySmoothing); + d->velocityTimeline.move(d->verticalVelocity, verticalVelocity, d->reportedVelocitySmoothing); + d->velocityTimeline.move(d->verticalVelocity, 0, d->reportedVelocitySmoothing); } else { if (d->_tl.time() != d->vTime) { - qreal xVelocity = (prevX - d->_moveX.value()) * 1000 / (d->_tl.time() - d->vTime); - qreal yVelocity = (prevY - d->_moveY.value()) * 1000 / (d->_tl.time() - d->vTime); - d->xVelocity.setValue(xVelocity); - d->yVelocity.setValue(yVelocity); + qreal horizontalVelocity = (prevX - d->_moveX.value()) * 1000 / (d->_tl.time() - d->vTime); + qreal verticalVelocity = (prevY - d->_moveY.value()) * 1000 / (d->_tl.time() - d->vTime); + d->horizontalVelocity.setValue(horizontalVelocity); + d->verticalVelocity.setValue(verticalVelocity); } d->vTime = d->_tl.time(); } @@ -787,21 +791,21 @@ void QFxFlickable::viewportMoved() if (d->velocityY > 0) { const qreal minY = minYExtent(); if (minY - d->_moveY.value() < height()/3 && minY != d->flickTargetY) - d->flickY(-d->yVelocity.value()); + d->flickY(-d->verticalVelocity.value()); } else { const qreal maxY = maxYExtent(); if (d->_moveY.value() - maxY < height()/3 && maxY != d->flickTargetY) - d->flickY(-d->yVelocity.value()); + d->flickY(-d->verticalVelocity.value()); } if (d->velocityX > 0) { const qreal minX = minXExtent(); if (minX - d->_moveX.value() < height()/3 && minX != d->flickTargetX) - d->flickX(-d->xVelocity.value()); + d->flickX(-d->horizontalVelocity.value()); } else { const qreal maxX = maxXExtent(); if (d->_moveX.value() - maxX < height()/3 && maxX != d->flickTargetX) - d->flickX(-d->xVelocity.value()); + d->flickX(-d->horizontalVelocity.value()); } } } @@ -892,13 +896,13 @@ void QFxFlickable::setOverShoot(bool o) } \endcode */ -int QFxFlickable::viewportWidth() const +qreal QFxFlickable::viewportWidth() const { Q_D(const QFxFlickable); return d->vWidth; } -void QFxFlickable::setViewportWidth(int w) +void QFxFlickable::setViewportWidth(qreal w) { Q_D(QFxFlickable); if (d->vWidth == w) @@ -935,13 +939,13 @@ void QFxFlickable::heightChange() } } -int QFxFlickable::viewportHeight() const +qreal QFxFlickable::viewportHeight() const { Q_D(const QFxFlickable); return d->vHeight; } -void QFxFlickable::setViewportHeight(int h) +void QFxFlickable::setViewportHeight(qreal h) { Q_D(QFxFlickable); if (d->vHeight == h) @@ -958,7 +962,7 @@ void QFxFlickable::setViewportHeight(int h) d->updateBeginningEnd(); } -int QFxFlickable::vWidth() const +qreal QFxFlickable::vWidth() const { Q_D(const QFxFlickable); if (d->vWidth < 0) @@ -967,7 +971,7 @@ int QFxFlickable::vWidth() const return d->vWidth; } -int QFxFlickable::vHeight() const +qreal QFxFlickable::vHeight() const { Q_D(const QFxFlickable); if (d->vHeight < 0) @@ -1049,15 +1053,15 @@ bool QFxFlickable::sceneEventFilter(QGraphicsItem *i, QEvent *e) /*! \qmlproperty int Flickable::maximumFlickVelocity - This property holds the maximum velocity that the user can flick the view. + This property holds the maximum velocity that the user can flick the view in pixels/second. */ -int QFxFlickable::maximumFlickVelocity() const +qreal QFxFlickable::maximumFlickVelocity() const { Q_D(const QFxFlickable); return d->maxVelocity; } -void QFxFlickable::setMaximumFlickVelocity(int v) +void QFxFlickable::setMaximumFlickVelocity(qreal v) { Q_D(QFxFlickable); if (v == d->maxVelocity) @@ -1071,20 +1075,20 @@ bool QFxFlickable::isFlicking() const return d->flicked; } -int QFxFlickable::velocityDecay() const +qreal QFxFlickable::reportedVelocitySmoothing() const { Q_D(const QFxFlickable); - return d->velocityDecay; + return d->reportedVelocitySmoothing; } -void QFxFlickable::setVelocityDecay(int decay) +void QFxFlickable::setReportedVelocitySmoothing(qreal reportedVelocitySmoothing) { Q_D(QFxFlickable); - Q_ASSERT(decay >= 0); - if (decay == d->velocityDecay) + Q_ASSERT(reportedVelocitySmoothing >= 0); + if (reportedVelocitySmoothing == d->reportedVelocitySmoothing) return; - d->velocityDecay = decay; - emit velocityDecayChanged(decay); + d->reportedVelocitySmoothing = reportedVelocitySmoothing; + emit reportedVelocitySmoothingChanged(reportedVelocitySmoothing); } bool QFxFlickable::isMoving() const @@ -1116,13 +1120,13 @@ void QFxFlickable::movementEnding() emit flickingChanged(); emit flickEnded(); } - d->xVelocity.setValue(0); + d->horizontalVelocity.setValue(0); } void QFxFlickablePrivate::updateVelocity() { Q_Q(QFxFlickable); - emit q->velocityChanged(q->xVelocity(), q->yVelocity()); + emit q->velocityChanged(q->horizontalVelocity(), q->verticalVelocity()); } QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxflickable.h b/src/declarative/fx/qfxflickable.h index 13d793f..c27b29f 100644 --- a/src/declarative/fx/qfxflickable.h +++ b/src/declarative/fx/qfxflickable.h @@ -55,17 +55,17 @@ class Q_DECLARATIVE_EXPORT QFxFlickable : public QFxItem { Q_OBJECT - Q_PROPERTY(bool overShoot READ overShoot WRITE setOverShoot) - Q_PROPERTY(int viewportWidth READ viewportWidth WRITE setViewportWidth NOTIFY viewportWidthChanged) //### qreal - Q_PROPERTY(int viewportHeight READ viewportHeight WRITE setViewportHeight NOTIFY viewportHeightChanged) - Q_PROPERTY(qreal xPosition READ xPosition WRITE setXPosition NOTIFY positionChanged) //### viewportX, positionXChannged - Q_PROPERTY(qreal yPosition READ yPosition WRITE setYPosition NOTIFY positionChanged) //### ^^^ + Q_PROPERTY(qreal viewportWidth READ viewportWidth WRITE setViewportWidth NOTIFY viewportWidthChanged) + Q_PROPERTY(qreal viewportHeight READ viewportHeight WRITE setViewportHeight NOTIFY viewportHeightChanged) + Q_PROPERTY(qreal viewportX READ viewportX WRITE setViewportX NOTIFY positionXChanged) + Q_PROPERTY(qreal viewportY READ viewportY WRITE setViewportY NOTIFY positionYChanged) - Q_PROPERTY(int velocityDecay READ velocityDecay WRITE setVelocityDecay NOTIFY velocityDecayChanged) //### qreal deceleration - Q_PROPERTY(int maximumFlickVelocity READ maximumFlickVelocity WRITE setMaximumFlickVelocity) ///### qreal, use same units as follow (pixels/s) + Q_PROPERTY(qreal horizontalVelocity READ horizontalVelocity NOTIFY horizontalVelocityChanged) + Q_PROPERTY(qreal verticalVelocity READ verticalVelocity NOTIFY verticalVelocityChanged) + Q_PROPERTY(qreal reportedVelocitySmoothing READ reportedVelocitySmoothing WRITE setReportedVelocitySmoothing NOTIFY reportedVelocitySmoothingChanged) - Q_PROPERTY(qreal xVelocity READ xVelocity NOTIFY velocityChanged) //### horizontalVelocity - Q_PROPERTY(qreal yVelocity READ yVelocity NOTIFY velocityChanged) //### verticalVelocity + Q_PROPERTY(bool overShoot READ overShoot WRITE setOverShoot) + Q_PROPERTY(qreal maximumFlickVelocity READ maximumFlickVelocity WRITE setMaximumFlickVelocity) Q_PROPERTY(bool moving READ isMoving NOTIFY movingChanged) Q_PROPERTY(bool flicking READ isFlicking NOTIFY flickingChanged) @@ -96,26 +96,26 @@ public: bool overShoot() const; void setOverShoot(bool); - int viewportWidth() const; - void setViewportWidth(int); + qreal viewportWidth() const; + void setViewportWidth(qreal); - int viewportHeight() const; - void setViewportHeight(int); + qreal viewportHeight() const; + void setViewportHeight(qreal); - qreal xPosition() const; - void setXPosition(qreal pos); + qreal viewportX() const; + void setViewportX(qreal pos); - qreal yPosition() const; - void setYPosition(qreal pos); + qreal viewportY() const; + void setViewportY(qreal pos); bool isMoving() const; bool isFlicking() const; - int velocityDecay() const; - void setVelocityDecay(int); + qreal reportedVelocitySmoothing() const; + void setReportedVelocitySmoothing(qreal); - int maximumFlickVelocity() const; - void setMaximumFlickVelocity(int); + qreal maximumFlickVelocity() const; + void setMaximumFlickVelocity(qreal); bool isLocked() const; void setLocked(bool); @@ -125,8 +125,8 @@ public: DragMode dragMode() const; void setDragMode(DragMode mode); - qreal xVelocity() const; - qreal yVelocity() const; + qreal horizontalVelocity() const; + qreal verticalVelocity() const; bool isAtXEnd() const; bool isAtXBeginning() const; @@ -143,14 +143,15 @@ public: Q_SIGNALS: void viewportWidthChanged(); void viewportHeightChanged(); - void positionChanged(); + void positionXChanged(); + void positionYChanged(); void movingChanged(); void flickingChanged(); void movementStarted(); void movementEnded(); void flickStarted(); void flickEnded(); - void velocityDecayChanged(int); + void reportedVelocitySmoothingChanged(int); void velocityChanged(qreal, qreal); void isAtBoundaryChanged(); void pageChanged(); @@ -176,8 +177,8 @@ protected: virtual qreal minYExtent() const; virtual qreal maxXExtent() const; virtual qreal maxYExtent() const; - int vWidth() const; - int vHeight() const; + qreal vWidth() const; + qreal vHeight() const; virtual void viewportMoved(); bool sendMouseEvent(QGraphicsSceneMouseEvent *event); diff --git a/src/declarative/fx/qfxflickable_p.h b/src/declarative/fx/qfxflickable_p.h index 00d5423..a0ac011 100644 --- a/src/declarative/fx/qfxflickable_p.h +++ b/src/declarative/fx/qfxflickable_p.h @@ -103,8 +103,8 @@ public: QmlTimeLineValueProxy _moveX; QmlTimeLineValueProxy _moveY; QmlTimeLine _tl; - int vWidth; - int vHeight; + qreal vWidth; + qreal vHeight; bool overShoot; bool flicked; bool moving; @@ -120,14 +120,14 @@ public: QTime pressTime; QmlTimeLineEvent fixupXEvent; QmlTimeLineEvent fixupYEvent; - int maxVelocity; + qreal maxVelocity; bool locked; QFxFlickable::DragMode dragMode; ElasticValue elasticY; ElasticValue elasticX; QTime velocityTime; QPointF lastFlickablePosition; - int velocityDecay; + qreal reportedVelocitySmoothing; int flickTargetX; int flickTargetY; @@ -142,8 +142,8 @@ public: } QFxFlickablePrivate *parent; }; - Velocity xVelocity; - Velocity yVelocity; + Velocity horizontalVelocity; + Velocity verticalVelocity; int vTime; QmlTimeLine velocityTimeline; bool atXEnd; diff --git a/src/declarative/fx/qfxgridview.cpp b/src/declarative/fx/qfxgridview.cpp index bf6e863..138a8ae 100644 --- a/src/declarative/fx/qfxgridview.cpp +++ b/src/declarative/fx/qfxgridview.cpp @@ -185,14 +185,14 @@ public: qreal position() const { Q_Q(const QFxGridView); - return flow == QFxGridView::LeftToRight ? q->yPosition() : q->xPosition(); + return flow == QFxGridView::LeftToRight ? q->viewportY() : q->viewportX(); } void setPosition(qreal pos) { Q_Q(QFxGridView); if (flow == QFxGridView::LeftToRight) - q->setYPosition(pos); + q->setViewportY(pos); else - q->setXPosition(pos); + q->setViewportX(pos); } int size() const { Q_Q(const QFxGridView); diff --git a/src/declarative/fx/qfxlistview.cpp b/src/declarative/fx/qfxlistview.cpp index c1e03dd..147c977 100644 --- a/src/declarative/fx/qfxlistview.cpp +++ b/src/declarative/fx/qfxlistview.cpp @@ -196,14 +196,14 @@ public: qreal position() const { Q_Q(const QFxListView); - return orient == Qt::Vertical ? q->yPosition() : q->xPosition(); + return orient == Qt::Vertical ? q->viewportY() : q->viewportX(); } void setPosition(qreal pos) { Q_Q(QFxListView); if (orient == Qt::Vertical) - q->setYPosition(pos); + q->setViewportY(pos); else - q->setXPosition(pos); + q->setViewportX(pos); } int size() const { Q_Q(const QFxListView); -- cgit v0.12 From c61b59a7d856b9c7783c87d2cccfc2ed913ef543 Mon Sep 17 00:00:00 2001 From: Martin Jones Date: Fri, 28 Aug 2009 16:56:05 +1000 Subject: Fix combined horizontal and vertical flicking --- src/declarative/fx/qfxflickable.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/declarative/fx/qfxflickable.cpp b/src/declarative/fx/qfxflickable.cpp index 07df799..27bfa27 100644 --- a/src/declarative/fx/qfxflickable.cpp +++ b/src/declarative/fx/qfxflickable.cpp @@ -142,7 +142,7 @@ void QFxFlickablePrivate::flickX(qreal velocity) else v = maxVelocity; } - _tl.clear(); + _tl.reset(_moveX); _tl.accel(_moveX, v, 500, maxDistance); _tl.execute(fixupXEvent); if (!flicked) { @@ -151,7 +151,7 @@ void QFxFlickablePrivate::flickX(qreal velocity) emit q->flickStarted(); } } else { - _tl.clear(); + _tl.reset(_moveX); fixupX(); } } @@ -178,7 +178,7 @@ void QFxFlickablePrivate::flickY(qreal velocity) else v = maxVelocity; } - _tl.clear(); + _tl.reset(_moveY); _tl.accel(_moveY, v, 500, maxDistance); _tl.execute(fixupYEvent); if (!flicked) { @@ -187,7 +187,7 @@ void QFxFlickablePrivate::flickY(qreal velocity) emit q->flickStarted(); } } else { - _tl.clear(); + _tl.reset(_moveY); fixupY(); } } -- cgit v0.12