diff -r 000000000000 -r 1450b09d0cfd ginebra2/TextEditItem.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ginebra2/TextEditItem.cpp Tue May 04 12:39:35 2010 +0300 @@ -0,0 +1,231 @@ +/* +* 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 +#include "TextEditItem.h" +#include "ChromeSnippet.h" +#include "GreenChromeSnippet.h" + +namespace GVA { + EditorWidget::EditorWidget(QGraphicsItem * parent) + : QGraphicsTextItem(parent) + { + setText(""); + } + void EditorWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem * option, QWidget* widget) + { + //Hack to get rid of the ugly selection ants. This should be tunable in QGraphicsTextItem! + QStyleOptionGraphicsItem newOption = *option; + newOption.state &= (!QStyle::State_Selected | !QStyle::State_HasFocus); + painter->save(); + QGraphicsTextItem::paint(painter, &newOption, widget); + painter->restore(); + } + + // The editor document signals cursor movements when the document modified (because characters + // are being added or removed, but not when the cursor is simply being moved. Also, the + // document's idea of cursor position is based on the character count, not the actual pixel + // position. To implement scrolling, we need our own cursor change event that supplies the + // pixel change in all cases. + + void EditorWidget::keyPressEvent(QKeyEvent *event) + { + qreal oldX = cursorX(); + QGraphicsTextItem::keyPressEvent(event); + emit cursorXChanged(cursorX(), oldX); + } + + void EditorWidget::setText(const QString& text, bool html) + { + if(html) + setHtml(text); + else + setPlainText(text); + //All this just to get the first (and only) text line of the document! + m_textLine = document()->begin().layout()->lineForTextPosition(0); + } + + // Use QTextLine to compute the text metrics for the current cursor position. + + qreal EditorWidget::cursorX() { + return m_textLine.cursorToX(textCursor().position()); + } + + TextEditItem::TextEditItem(ChromeSnippet * snippet, QGraphicsItem* parent) + : NativeChromeItem(snippet, parent), + m_textWidth(0), + m_scrollPos(0) + { + setFlags(QGraphicsItem::ItemIsMovable); + //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 EditorWidget(m_viewPort); + m_cursor = m_editor->textCursor(); + connect(m_editor, SIGNAL(cursorXChanged(qreal, qreal)), this, SLOT(onCursorXChanged(qreal, qreal))); + + //Force the editor to be a single text line + m_textOption = m_editor->document()->defaultTextOption(); + m_textOption.setWrapMode(QTextOption::NoWrap); + m_editor->document()->setDefaultTextOption(m_textOption); + + //Not exactly well-documented, but this flag is needed to make cursor keys work + m_editor->setTextInteractionFlags(Qt::TextEditorInteraction); + + //Non-default key handling for scrolling, etc. + m_editor->installEventFilter(this); + + //Set text and background colors from element css + QString cssVal = m_snippet->element().styleProperty("color", QWebElement::ComputedStyle); + CSSToQColor(cssVal, m_textColor); + m_editor->setDefaultTextColor(m_textColor); + cssVal = m_snippet->element().styleProperty("background-color", QWebElement::ComputedStyle); + CSSToQColor(cssVal, m_backgroundColor); + + //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. + + cssVal = m_snippet->element().styleProperty("border-top-color", QWebElement::ComputedStyle); + CSSToQColor(cssVal, m_borderColor); + cssVal = m_snippet->element().styleProperty("padding-top", QWebElement::ComputedStyle); + m_padding = cssVal.remove("px").toInt(); + cssVal = m_snippet->element().styleProperty("border-top-width", QWebElement::ComputedStyle); + m_border = cssVal.remove("px").toInt(); + + //Cool effect, but shadow, if any, should be set by js + //m_shadow = new QGraphicsDropShadowEffect(); + //m_shadow->setOffset(3.0,3.0); + //m_shadow->setBlurRadius(2.0); + //setGraphicsEffect(m_shadow); + } + + TextEditItem::~TextEditItem() + { + delete m_editor; + } + + void TextEditItem::resizeEvent(QGraphicsSceneResizeEvent * ev) + { + NativeChromeItem::resizeEvent(ev); + m_viewPortWidth = boundingRect().width()-m_padding*2; + m_viewPort->setGeometry(m_padding,(boundingRect().height()-m_editor->boundingRect().height())/2,m_viewPortWidth, m_editor->boundingRect().height() ); + m_editor->setTextWidth(m_viewPortWidth); + //Make a rectangular background with a cut-out for the text. The width of the surrounding + //background is set by padding + m_background.addRect(boundingRect()); + m_background.addRoundedRect(m_padding, m_padding, m_viewPortWidth, boundingRect().height()-m_padding*2,4,4); + } + + //Filter key events to emit activate signal absorb up, down keys + + bool TextEditItem::eventFilter(QObject * obj, QEvent *ev) + { + if(obj == m_editor){ + if(ev->type() == QEvent::KeyPress){ + QKeyEvent *keyEvent = static_cast(ev); + if(keyEvent->key() == Qt::Key_Select || keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) { + //Signal that a carriage return-like key-press happened + emit activated(); + return true; + } + if(keyEvent->key() == Qt::Key_Down || keyEvent->key() == Qt::Key_Up) + return true; + //Otherwise, pass keypress to the text editor + return false; + } + } + return NativeChromeItem::eventFilter(obj, ev); + } + + void TextEditItem::internalScroll(qreal deltaX) + { + if(deltaX > -m_scrollPos) + m_editor->moveBy(-m_scrollPos,0); + else + m_editor->moveBy(deltaX,0); + m_scrollPos = m_editor->pos().x(); + } + + //Handle text scrolling + //NB: Still needs some tweaking, for example to keep the last character visibile when + //inserting! Rewrite as state machine? + + void TextEditItem::onCursorXChanged(qreal newX, qreal oldX) + { + qreal oldTextWidth = m_textWidth; + m_textWidth = m_editor->document()->size().width(); + if(oldTextWidth == 0) + return; + qreal textDelta = m_textWidth - oldTextWidth; + qreal deltaX = oldX - newX; + //Just moving the cursor, slide window as needed + if(textDelta == 0){ + //NB: Currently slides by one character, in some browsers slides by multiple characters + if((newX <= -m_scrollPos)||(newX >= (m_viewPortWidth - m_scrollPos))){ + internalScroll(deltaX); + } + } + //Inserting characters + else if (textDelta > 0){ + if(newX >= (m_viewPortWidth - m_scrollPos)){ + internalScroll(deltaX); + } + } + //Deleting characters. + else { + if(m_scrollPos < 0){ + //Delete may be a selected block, in which case the cursor movement may be + //different from the text delta. + internalScroll(-textDelta); + } + } + } + + // Paint background and any border + + void TextEditItem::paint(QPainter* painter, const QStyleOptionGraphicsItem * option, QWidget* widget){ + NativeChromeItem::paint(painter, option,widget); + QPainterPath path; + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + painter->setBrush(m_backgroundColor); + if(m_border > 0){ + QPen pen; + pen.setWidth(m_border); + pen.setBrush(m_borderColor); + painter->setPen(pen); + } + painter->drawPath(m_background); + painter->restore(); + } + + //NB: Move these slots to the containing snippet so they can be exported to JS + + QString TextEditItem::text(){ + return m_editor->toPlainText(); + } + + void TextEditItem::setText(const QString & text){ + m_editor->setText(text); + } + +} +