ginebra2/TextEditItem.cpp
changeset 0 1450b09d0cfd
child 3 0954f5dd2cd0
equal deleted inserted replaced
-1:000000000000 0:1450b09d0cfd
       
     1 /*
       
     2 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: 
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 #include <QGraphicsTextItem>
       
    20 #include "TextEditItem.h"
       
    21 #include "ChromeSnippet.h"
       
    22 #include "GreenChromeSnippet.h"
       
    23 
       
    24 namespace GVA {
       
    25   EditorWidget::EditorWidget(QGraphicsItem * parent)
       
    26     : QGraphicsTextItem(parent)
       
    27   {
       
    28     setText("");
       
    29   }
       
    30   void EditorWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem * option, QWidget* widget)
       
    31   {
       
    32     //Hack to get rid of the ugly selection ants. This should be tunable in QGraphicsTextItem!
       
    33     QStyleOptionGraphicsItem newOption = *option;
       
    34     newOption.state &= (!QStyle::State_Selected | !QStyle::State_HasFocus);
       
    35     painter->save();
       
    36     QGraphicsTextItem::paint(painter, &newOption, widget);
       
    37     painter->restore();
       
    38   }
       
    39 
       
    40   // The editor document signals cursor movements when the document modified (because characters
       
    41   // are being added or removed, but not when the cursor is simply being moved. Also, the
       
    42   // document's idea of cursor position is based on the character count, not the actual pixel
       
    43   // position. To implement scrolling, we need our own cursor change event that supplies the
       
    44   // pixel change in all cases.
       
    45   
       
    46   void EditorWidget::keyPressEvent(QKeyEvent *event)
       
    47   {   
       
    48     qreal oldX = cursorX();
       
    49     QGraphicsTextItem::keyPressEvent(event);
       
    50     emit cursorXChanged(cursorX(), oldX);
       
    51   }
       
    52  
       
    53   void EditorWidget::setText(const QString& text, bool html)
       
    54   {
       
    55     if(html)
       
    56       setHtml(text);
       
    57     else
       
    58       setPlainText(text);
       
    59     //All this just to get the first (and only) text line of the document!
       
    60     m_textLine = document()->begin().layout()->lineForTextPosition(0);
       
    61   }
       
    62 
       
    63   // Use QTextLine to compute the text metrics for the current cursor position.
       
    64 
       
    65   qreal EditorWidget::cursorX() {
       
    66      return m_textLine.cursorToX(textCursor().position());
       
    67   }
       
    68 
       
    69   TextEditItem::TextEditItem(ChromeSnippet * snippet, QGraphicsItem* parent)
       
    70     : NativeChromeItem(snippet, parent),
       
    71       m_textWidth(0),
       
    72       m_scrollPos(0)
       
    73   {
       
    74     setFlags(QGraphicsItem::ItemIsMovable);
       
    75     //The viewport clips the editor when text overflows
       
    76     m_viewPort = new QGraphicsWidget(this);
       
    77     m_viewPort->setFlags(QGraphicsItem::ItemClipsChildrenToShape);
       
    78     //The actual text editor item
       
    79     m_editor = new EditorWidget(m_viewPort);
       
    80     m_cursor = m_editor->textCursor();
       
    81     connect(m_editor, SIGNAL(cursorXChanged(qreal, qreal)), this, SLOT(onCursorXChanged(qreal, qreal)));
       
    82  
       
    83     //Force the editor to be a single text line 
       
    84     m_textOption = m_editor->document()->defaultTextOption();
       
    85     m_textOption.setWrapMode(QTextOption::NoWrap);
       
    86     m_editor->document()->setDefaultTextOption(m_textOption);
       
    87     
       
    88     //Not exactly well-documented, but this flag is needed to make cursor keys work
       
    89     m_editor->setTextInteractionFlags(Qt::TextEditorInteraction);
       
    90 
       
    91     //Non-default key handling for scrolling, etc.
       
    92     m_editor->installEventFilter(this);
       
    93  
       
    94     //Set text and background colors from element css
       
    95     QString cssVal = m_snippet->element().styleProperty("color", QWebElement::ComputedStyle);
       
    96     CSSToQColor(cssVal, m_textColor);
       
    97     m_editor->setDefaultTextColor(m_textColor);
       
    98     cssVal = m_snippet->element().styleProperty("background-color", QWebElement::ComputedStyle);
       
    99     CSSToQColor(cssVal, m_backgroundColor);
       
   100 
       
   101     //For border-related properties, we constrain all values (top, left, etc.) to be the same.
       
   102     //These can be set using the css shorthand (e.g. padding:10px), but the computed css style will be for
       
   103     //the four primitive values (padding-top, padding-left) etc, which will all be equal.
       
   104     //Hence we just use one of the computed primitive values (top) to represent the common value.
       
   105 
       
   106     cssVal = m_snippet->element().styleProperty("border-top-color", QWebElement::ComputedStyle);
       
   107     CSSToQColor(cssVal, m_borderColor);
       
   108     cssVal = m_snippet->element().styleProperty("padding-top", QWebElement::ComputedStyle);
       
   109     m_padding = cssVal.remove("px").toInt();
       
   110     cssVal = m_snippet->element().styleProperty("border-top-width", QWebElement::ComputedStyle);
       
   111     m_border = cssVal.remove("px").toInt();
       
   112 
       
   113     //Cool effect, but shadow, if any, should be set by js
       
   114     //m_shadow = new QGraphicsDropShadowEffect();
       
   115     //m_shadow->setOffset(3.0,3.0);
       
   116     //m_shadow->setBlurRadius(2.0);
       
   117     //setGraphicsEffect(m_shadow);
       
   118   }
       
   119 
       
   120   TextEditItem::~TextEditItem()
       
   121   {
       
   122     delete m_editor;
       
   123   }
       
   124   
       
   125   void TextEditItem::resizeEvent(QGraphicsSceneResizeEvent * ev)
       
   126   {
       
   127     NativeChromeItem::resizeEvent(ev);
       
   128     m_viewPortWidth = boundingRect().width()-m_padding*2;
       
   129     m_viewPort->setGeometry(m_padding,(boundingRect().height()-m_editor->boundingRect().height())/2,m_viewPortWidth, m_editor->boundingRect().height() );
       
   130     m_editor->setTextWidth(m_viewPortWidth);
       
   131     //Make a rectangular background with a cut-out for the text. The width of the surrounding
       
   132     //background is set by padding 
       
   133     m_background.addRect(boundingRect());
       
   134     m_background.addRoundedRect(m_padding, m_padding, m_viewPortWidth, boundingRect().height()-m_padding*2,4,4);
       
   135  }
       
   136 
       
   137   //Filter key events to emit activate signal absorb up, down keys
       
   138 
       
   139   bool TextEditItem::eventFilter(QObject * obj, QEvent *ev)
       
   140   {
       
   141     if(obj == m_editor){
       
   142       if(ev->type() == QEvent::KeyPress){
       
   143 	QKeyEvent *keyEvent = static_cast<QKeyEvent*>(ev);
       
   144 	if(keyEvent->key() == Qt::Key_Select || keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) {
       
   145           //Signal that a carriage return-like key-press happened
       
   146 	  emit activated();
       
   147 	  return true;
       
   148 	}
       
   149 	if(keyEvent->key() == Qt::Key_Down || keyEvent->key() == Qt::Key_Up)
       
   150 	  return true;
       
   151         //Otherwise, pass keypress to the text editor
       
   152 	return false;
       
   153       }
       
   154     }
       
   155     return NativeChromeItem::eventFilter(obj, ev);
       
   156   }
       
   157 
       
   158   void TextEditItem::internalScroll(qreal deltaX)
       
   159   {
       
   160     if(deltaX > -m_scrollPos)
       
   161       m_editor->moveBy(-m_scrollPos,0);
       
   162     else
       
   163       m_editor->moveBy(deltaX,0);
       
   164     m_scrollPos = m_editor->pos().x();
       
   165   }
       
   166 
       
   167   //Handle text scrolling
       
   168   //NB: Still needs some tweaking, for example to keep the last character visibile when
       
   169   //inserting! Rewrite as state machine?
       
   170 
       
   171   void TextEditItem::onCursorXChanged(qreal newX, qreal oldX)
       
   172   {
       
   173     qreal oldTextWidth = m_textWidth;
       
   174     m_textWidth = m_editor->document()->size().width(); 
       
   175     if(oldTextWidth == 0)
       
   176 	return;
       
   177     qreal textDelta = m_textWidth - oldTextWidth;
       
   178     qreal deltaX = oldX - newX;
       
   179     //Just moving the cursor, slide window as needed
       
   180     if(textDelta == 0){
       
   181       //NB: Currently slides by one character, in some browsers slides by multiple characters
       
   182       if((newX <= -m_scrollPos)||(newX >= (m_viewPortWidth - m_scrollPos))){
       
   183 	internalScroll(deltaX);
       
   184       }
       
   185     }
       
   186     //Inserting characters
       
   187     else if (textDelta > 0){
       
   188       if(newX >= (m_viewPortWidth - m_scrollPos)){
       
   189         internalScroll(deltaX);
       
   190       }
       
   191     }
       
   192     //Deleting characters. 
       
   193     else {
       
   194       if(m_scrollPos < 0){
       
   195         //Delete may be a selected block, in which case the cursor movement may be
       
   196         //different from the text delta.
       
   197         internalScroll(-textDelta);
       
   198       }
       
   199     }
       
   200   }
       
   201 
       
   202   // Paint background and any border
       
   203 
       
   204   void TextEditItem::paint(QPainter* painter, const QStyleOptionGraphicsItem * option, QWidget* widget){
       
   205     NativeChromeItem::paint(painter, option,widget);
       
   206     QPainterPath path;
       
   207     painter->save();
       
   208     painter->setRenderHint(QPainter::Antialiasing);
       
   209     painter->setBrush(m_backgroundColor);
       
   210     if(m_border > 0){
       
   211       QPen pen;
       
   212       pen.setWidth(m_border);
       
   213       pen.setBrush(m_borderColor);
       
   214       painter->setPen(pen);
       
   215     }
       
   216     painter->drawPath(m_background);
       
   217     painter->restore();
       
   218   }
       
   219 
       
   220   //NB: Move these slots to the containing snippet so they can be exported to JS
       
   221 
       
   222   QString TextEditItem::text(){
       
   223     return m_editor->toPlainText();
       
   224   }
       
   225   
       
   226   void TextEditItem::setText(const QString & text){
       
   227     m_editor->setText(text);
       
   228   }
       
   229 
       
   230 }
       
   231