ginebra2/TextEditItem.cpp
changeset 0 1450b09d0cfd
child 3 0954f5dd2cd0
--- /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 <QGraphicsTextItem>
+#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<QKeyEvent*>(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);
+  }
+
+}
+