diff -r 000000000000 -r 1450b09d0cfd ginebra2/UrlSearchSnippet.cpp --- /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(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