ginebra2/UrlSearchSnippet.cpp
changeset 0 1450b09d0cfd
child 3 0954f5dd2cd0
child 5 0f2326c2a325
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ginebra2/UrlSearchSnippet.cpp	Tue May 04 12:39:35 2010 +0300
@@ -0,0 +1,445 @@
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: 
+*
+*/
+
+
+#include "UrlSearchSnippet.h"
+#include "Utilities.h"
+
+#include "ChromeRenderer.h"
+#include "ChromeWidget.h"
+#include "ViewController.h"
+#include "WebChromeSnippet.h"
+
+#include "webpagecontroller.h"
+
+namespace GVA {
+
+// Methods for class UrlEditorWidget
+
+UrlEditorWidget::UrlEditorWidget(QGraphicsItem * parent)
+: QGraphicsTextItem(parent)
+{
+    // Disable wrapping, force text to be stored and displayed
+    // as a single line.
+
+    QTextOption textOption = document()->defaultTextOption();
+    textOption.setWrapMode(QTextOption::NoWrap);
+    document()->setDefaultTextOption(textOption);
+
+    // Enable cursor keys.
+
+    setTextInteractionFlags(Qt::TextEditorInteraction);
+
+    // This is needed to initialize m_textLine.
+
+    setText("");
+}
+
+UrlEditorWidget::~UrlEditorWidget()
+{
+}
+
+void UrlEditorWidget::setText(const QString & text)
+{
+    setPlainText(text);
+    m_textLine = document()->begin().layout()->lineForTextPosition(0);
+}
+
+qreal UrlEditorWidget::cursorX()
+{
+    return m_textLine.cursorToX(textCursor().position());
+}
+
+void UrlEditorWidget::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget)
+{
+    // Paint without ugly selection ants (the dashed line that surrounds
+    // the selected text).
+
+    QStyleOptionGraphicsItem newOption = *option;
+    newOption.state &= (!QStyle::State_Selected | !QStyle::State_HasFocus);
+
+    painter->save();
+
+    QGraphicsTextItem::paint(painter, &newOption, widget);
+
+    painter->restore();
+}
+
+void UrlEditorWidget::keyPressEvent(QKeyEvent * event)
+{
+    // Signal horizontal cursor movement so UrlSearchSnippet can
+    // implement horizontal scrolling.
+
+    qreal oldX = cursorX();
+
+    QGraphicsTextItem::keyPressEvent(event);
+
+    qreal newX = cursorX();
+
+    if (newX != oldX) {
+        emit cursorXChanged(newX);
+    }
+}
+
+// Methods for class UrlSearchSnippet
+
+UrlSearchSnippet::UrlSearchSnippet(ChromeSnippet * snippet, ChromeWidget * chrome, QGraphicsItem * parent)
+: NativeChromeItem(snippet, parent)
+, m_chrome(chrome)
+, m_percent(0)
+, m_pendingClearCalls(0)
+, m_viewPortWidth(0.0)
+, m_viewPortHeight(0.0)
+{
+    setFlags(QGraphicsItem::ItemIsMovable);
+
+    // Extract style information from element CSS.
+
+    // For border-related properties, we constrain all values (top, left, etc.)
+    // to be the same.  These can be set using the css shorthand (e.g. padding:10px),
+    // but the computed css style will be for the four primitive values (padding-top,
+    // padding-left) etc, which will all be equal.  Hence we just use one of the
+    // computed primitive values (top) to represent the common value.
+
+    QWebElement we = m_snippet->element();
+
+    NativeChromeItem::CSSToQColor(
+            we.styleProperty("color", QWebElement::ComputedStyle),
+            m_textColor);
+
+    NativeChromeItem::CSSToQColor(
+            we.styleProperty("background-color", QWebElement::ComputedStyle),
+            m_backgroundColor);
+
+    NativeChromeItem::CSSToQColor(
+            we.styleProperty("border-top-color", QWebElement::ComputedStyle),
+            m_borderColor);
+
+    QString cssPadding = we.styleProperty("padding-top", QWebElement::ComputedStyle);
+    m_padding = cssPadding.remove("px").toInt();
+
+    QString cssBorder = we.styleProperty("border-top-width", QWebElement::ComputedStyle);
+    m_border = cssBorder.remove("px").toInt();
+
+    // The viewport clips the editor when text overflows
+
+    m_viewPort = new QGraphicsWidget(this);
+    m_viewPort->setFlags(QGraphicsItem::ItemClipsChildrenToShape);
+
+    // The actual text editor item
+
+    m_editor = new UrlEditorWidget(m_viewPort);
+    m_editor->setDefaultTextColor(m_textColor);
+    m_editor->installEventFilter(this);
+
+    // Monitor editor cursor position changes for horizontal scrolling.
+
+    safe_connect(m_editor, SIGNAL(cursorXChanged(qreal)),
+            this, SLOT(makeVisible(qreal)));
+
+    // Monitor resize events.
+
+    safe_connect(m_chrome->renderer(), SIGNAL(chromeResized()),
+            this, SLOT(resize()));
+
+    // Update state as soon as chrome completes loading.
+
+    safe_connect(m_chrome, SIGNAL(chromeComplete()),
+            this, SLOT(setStarted()));
+
+    // Monitor page loading.
+
+    WebPageController * pageController = WebPageController::getSingleton();
+
+    safe_connect(pageController, SIGNAL(pageUrlChanged(const QString)),
+            this, SLOT(setUrlText(const QString &)));
+
+    safe_connect(pageController, SIGNAL(pageLoadStarted()),
+            this, SLOT(setStarted()));
+
+    safe_connect(pageController, SIGNAL(pageLoadProgress(const int)),
+            this, SLOT(setProgress(int)));
+
+    safe_connect(pageController, SIGNAL(pageLoadFinished(bool)),
+            this, SLOT(setFinished(bool)));
+
+    // Monitor view changes.
+
+    ViewController * viewController = chrome->viewController();
+
+    safe_connect(viewController, SIGNAL(currentViewChanged()),
+            this, SLOT(viewChanged()));
+}
+
+UrlSearchSnippet::~UrlSearchSnippet()
+{
+}
+
+bool UrlSearchSnippet::eventFilter(QObject * object, QEvent * event)
+{
+    // Filter editor key events.
+
+    if (object != m_editor) {
+        return false;
+    }
+
+    if (event->type() != QEvent::KeyPress) {
+        return false;
+    }
+
+    QKeyEvent * keyEvent = static_cast<QKeyEvent*>(event);
+
+    switch (keyEvent->key()) {
+    case Qt::Key_Select:
+    case Qt::Key_Return:
+    case Qt::Key_Enter:
+        // Signal that a carriage return-like key-press happened.
+        emit activated();
+        return true;
+
+    case Qt::Key_Down:
+    case Qt::Key_Up:
+        // Swallow arrow up/down keys, editor has just one line.
+        return true;
+
+    default:
+        return false;
+    }
+}
+
+void UrlSearchSnippet::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget)
+{
+    // Make sure any required horizontal scrolling happens
+    // before rendering UrlEditorWidget.
+
+    makeVisible(m_editor->cursorX());
+
+    NativeChromeItem::paint(painter, option,widget);
+
+    painter->save();
+
+    painter->setRenderHint(QPainter::Antialiasing);
+    painter->setBrush(m_backgroundColor);
+
+    // First, do progress bar.
+
+    QRectF g = boundingRect();
+    g.setWidth(g.width() * m_percent / 100.0);
+    painter->fillRect(g, QColor::fromRgb(0, 200, 200, 50));
+
+    // Next, background matte.
+
+    if (m_border > 0) {
+        QPen pen;
+        pen.setWidth(m_border);
+        pen.setBrush(m_borderColor);
+        painter->setPen(pen);
+    }
+
+    QPainterPath background;
+    background.addRect(boundingRect());
+    background.addRoundedRect(
+            m_padding,
+            m_padding,
+            m_viewPortWidth,
+            m_viewPortHeight,
+            4,
+            4);
+    painter->drawPath(background);
+
+    painter->restore();
+}
+
+void UrlSearchSnippet::resizeEvent(QGraphicsSceneResizeEvent * event)
+{
+    QSizeF size = event->newSize();
+
+    m_viewPort->resize(size);
+
+    m_viewPortWidth  = size.width()  - m_padding * 2;
+    m_viewPortHeight = size.height() - m_padding * 2;
+
+    m_viewPort->setGeometry(
+            m_padding,
+            (size.height() - m_editor->boundingRect().height()) / 2,
+            m_viewPortWidth,
+            m_viewPortHeight);
+
+    m_editor->setTextWidth(m_viewPortWidth);
+}
+
+void UrlSearchSnippet::resize()
+{
+    QWebElement we = m_snippet->element();
+
+    QRectF g = we.geometry();
+
+    qreal newWidth  = g.width();
+
+    qreal newHeight = g.height();
+
+    QGraphicsWidget::resize(newWidth, newHeight);
+}
+
+void UrlSearchSnippet::setUrlText(const QString & text)
+{
+    m_editor->setText(text);
+    m_editor->setPos(0, m_editor->pos().y());
+
+    makeVisible(m_editor->cursorX());
+}
+
+void UrlSearchSnippet::setStarted()
+{
+    // Strictly speaking we should set progress to 0.
+    // But set it higher to give immediate visual feedback
+    // that something is happening.
+
+    int progress = 0;
+
+    WebPageController * pageController = WebPageController::getSingleton();
+
+    if (pageController->isPageLoading()) {
+        progress = 5;
+    }
+
+    setProgress(progress);
+}
+
+void UrlSearchSnippet::setProgress(int percent)
+{
+    m_percent = percent;
+    update();
+}
+
+// Wait a half-second before actually clearing the progress bar.
+//
+// We have to be careful of the following two use cases:
+//
+// 1. Another page starts loading between the call to setFinished()
+//    and the scheduled call to clearProgress().
+//
+//    We don't want to clear the progress bar if another page is
+//    loading.  WebPageController::isPageLoading() can tell us
+//    if that is the case.
+//
+// 2. Another page finishes loading between the call to setFinished()
+//    and the scheduled call to clearProgress().  The sequence here is:
+//
+//      setFinished(ok) // for URL #1
+//      setFinished(ok) // for URL #2
+//      clearProgress() // for URL #1
+//      clearProgress() // for URL #2
+//
+//    We don't want to clear the progress bar in the first call to
+//    clearProgress() because we want the progress bar to retain its
+//    appearance for the full timeout period.  We manage this by
+//    tracking the number of pending calls to clearProgress() and
+//    only clearing the progress bar when that number becomes 0.
+
+void UrlSearchSnippet::setFinished(bool ok)
+{
+    if (ok) {
+        setProgress(99);
+    }
+
+    ++m_pendingClearCalls;
+
+    QTimer::singleShot(500, this, SLOT(clearProgress()));
+}
+
+void UrlSearchSnippet::clearProgress()
+{
+    --m_pendingClearCalls;
+
+    if (m_pendingClearCalls > 0) {
+        return;
+    }
+
+    WebPageController * pageController = WebPageController::getSingleton();
+
+    if (pageController->isPageLoading()) {
+        return;
+    }
+
+    setProgress(0);
+}
+
+void UrlSearchSnippet::viewChanged()
+{
+    WebPageController * pageController = WebPageController::getSingleton();
+
+    setUrlText(pageController->currentDocUrl());
+
+    int progress = pageController->loadProgressValue();
+    if (progress >= 100) {
+        progress = 0;
+    }
+    setProgress(progress);
+}
+
+// We divide the viewport into 3 distinct regions:
+//
+//
+//        [ left | middle | right ]
+//
+// [ editor, shifted left by editorShift pixels ]
+//
+// When a cursor is in the middle section of the viewport we
+// leave the editor shift unchanged, to preserve stability.
+//
+// When a cursor is in the right section or beyond we shift
+// the editor left until the cursor appears at the border
+// between the middle and right sections.
+//
+// When a cursor is in the left section or beyond we shift
+// the editor right until the cursor appears at the border
+// between the left and middle sections.
+//
+// We never shift the editor right of the viewport.
+
+void UrlSearchSnippet::makeVisible(qreal cursorX)
+{
+    qreal leftScrollBorder  = 0;
+
+    qreal rightScrollBorder = m_viewPortWidth - 10;
+
+    qreal editorShift = -1 * m_editor->pos().x();
+
+    qreal localX = cursorX - editorShift;
+
+    if (localX < leftScrollBorder) {
+        // Before left section, scroll right.
+        // In left section, scroll right.
+        qreal shift = qMin(leftScrollBorder - localX, editorShift);
+        m_editor->moveBy(shift, 0);
+        return;
+    }
+
+    if (localX >= rightScrollBorder) {
+        // In right section, scroll left.
+        // After right section, scroll left.
+        qreal shift = localX - rightScrollBorder;
+        m_editor->moveBy(-shift, 0);
+        return;
+    }
+
+    // In middle section, no scroll needed.
+    return;
+}
+
+} // namespace GVA