source: trunk/src/declarative/graphicsitems/qdeclarativetext.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 36.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtDeclarative module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qdeclarativetext_p.h"
43#include "private/qdeclarativetext_p_p.h"
44#include <qdeclarativestyledtext_p.h>
45#include <qdeclarativeinfo.h>
46#include <qdeclarativepixmapcache_p.h>
47
48#include <QSet>
49#include <QTextLayout>
50#include <QTextLine>
51#include <QTextDocument>
52#include <QTextCursor>
53#include <QGraphicsSceneMouseEvent>
54#include <QPainter>
55#include <QAbstractTextDocumentLayout>
56#include <qmath.h>
57
58QT_BEGIN_NAMESPACE
59
60extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled;
61
62class QTextDocumentWithImageResources : public QTextDocument {
63 Q_OBJECT
64
65public:
66 QTextDocumentWithImageResources(QDeclarativeText *parent);
67 virtual ~QTextDocumentWithImageResources();
68
69 void setText(const QString &);
70 int resourcesLoading() const { return outstanding; }
71
72protected:
73 QVariant loadResource(int type, const QUrl &name);
74
75private slots:
76 void requestFinished();
77
78private:
79 QHash<QUrl, QDeclarativePixmap *> m_resources;
80
81 int outstanding;
82 static QSet<QUrl> errors;
83};
84
85DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE);
86
87QDeclarativeTextPrivate::QDeclarativeTextPrivate()
88: color((QRgb)0), style(QDeclarativeText::Normal), hAlign(QDeclarativeText::AlignLeft),
89 vAlign(QDeclarativeText::AlignTop), elideMode(QDeclarativeText::ElideNone),
90 format(QDeclarativeText::AutoText), wrapMode(QDeclarativeText::NoWrap), imageCacheDirty(true),
91 updateOnComponentComplete(true), richText(false), singleline(false), cacheAllTextAsImage(true),
92 internalWidthUpdate(false), doc(0)
93{
94 cacheAllTextAsImage = enableImageCache();
95 QGraphicsItemPrivate::acceptedMouseButtons = Qt::LeftButton;
96 QGraphicsItemPrivate::flags = QGraphicsItemPrivate::flags & ~QGraphicsItem::ItemHasNoContents;
97}
98
99QTextDocumentWithImageResources::QTextDocumentWithImageResources(QDeclarativeText *parent)
100: QTextDocument(parent), outstanding(0)
101{
102}
103
104QTextDocumentWithImageResources::~QTextDocumentWithImageResources()
105{
106 if (!m_resources.isEmpty())
107 qDeleteAll(m_resources);
108}
109
110QVariant QTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
111{
112 QDeclarativeContext *context = qmlContext(parent());
113 QUrl url = context->resolvedUrl(name);
114
115 if (type == QTextDocument::ImageResource) {
116 QHash<QUrl, QDeclarativePixmap *>::Iterator iter = m_resources.find(url);
117
118 if (iter == m_resources.end()) {
119 QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url);
120 iter = m_resources.insert(name, p);
121
122 if (p->isLoading()) {
123 p->connectFinished(this, SLOT(requestFinished()));
124 outstanding++;
125 }
126 }
127
128 QDeclarativePixmap *p = *iter;
129 if (p->isReady()) {
130 return p->pixmap();
131 } else if (p->isError()) {
132 if (!errors.contains(url)) {
133 errors.insert(url);
134 qmlInfo(parent()) << p->error();
135 }
136 }
137 }
138
139 return QTextDocument::loadResource(type,url); // The *resolved* URL
140}
141
142void QTextDocumentWithImageResources::requestFinished()
143{
144 outstanding--;
145 if (outstanding == 0) {
146 QDeclarativeText *textItem = static_cast<QDeclarativeText*>(parent());
147 QString text = textItem->text();
148#ifndef QT_NO_TEXTHTMLPARSER
149 setHtml(text);
150#else
151 setPlainText(text);
152#endif
153 QDeclarativeTextPrivate *d = QDeclarativeTextPrivate::get(textItem);
154 d->updateLayout();
155 }
156}
157
158void QTextDocumentWithImageResources::setText(const QString &text)
159{
160 if (!m_resources.isEmpty()) {
161 qDeleteAll(m_resources);
162 m_resources.clear();
163 outstanding = 0;
164 }
165
166#ifndef QT_NO_TEXTHTMLPARSER
167 setHtml(text);
168#else
169 setPlainText(text);
170#endif
171}
172
173QSet<QUrl> QTextDocumentWithImageResources::errors;
174
175QDeclarativeTextPrivate::~QDeclarativeTextPrivate()
176{
177}
178
179void QDeclarativeTextPrivate::updateLayout()
180{
181 Q_Q(QDeclarativeText);
182 if (!q->isComponentComplete()) {
183 updateOnComponentComplete = true;
184 return;
185 }
186
187 // Setup instance of QTextLayout for all cases other than richtext
188 if (!richText) {
189 layout.clearLayout();
190 layout.setFont(font);
191 if (format != QDeclarativeText::StyledText) {
192 QString tmp = text;
193 tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
194 singleline = !tmp.contains(QChar::LineSeparator);
195 if (singleline && elideMode != QDeclarativeText::ElideNone && q->widthValid()) {
196 QFontMetrics fm(font);
197 tmp = fm.elidedText(tmp,(Qt::TextElideMode)elideMode,q->width()); // XXX still worth layout...?
198 }
199 layout.setText(tmp);
200 } else {
201 singleline = false;
202 QDeclarativeStyledText::parse(text, layout);
203 }
204 }
205
206 updateSize();
207}
208
209void QDeclarativeTextPrivate::updateSize()
210{
211 Q_Q(QDeclarativeText);
212
213 if (!q->isComponentComplete()) {
214 updateOnComponentComplete = true;
215 return;
216 }
217
218 invalidateImageCache();
219
220 QFontMetrics fm(font);
221 if (text.isEmpty()) {
222 q->setImplicitWidth(0);
223 q->setImplicitHeight(fm.height());
224 emit q->paintedSizeChanged();
225 q->update();
226 return;
227 }
228
229 int dy = q->height();
230 QSize size(0, 0);
231
232 //setup instance of QTextLayout for all cases other than richtext
233 if (!richText) {
234 size = setupTextLayout();
235 if (layedOutTextSize != size) {
236 q->prepareGeometryChange();
237 layedOutTextSize = size;
238 }
239 dy -= size.height();
240 } else {
241 singleline = false; // richtext can't elide or be optimized for single-line case
242 ensureDoc();
243 doc->setDefaultFont(font);
244 QTextOption option((Qt::Alignment)int(hAlign | vAlign));
245 option.setWrapMode(QTextOption::WrapMode(wrapMode));
246 doc->setDefaultTextOption(option);
247 if (wrapMode != QDeclarativeText::NoWrap && q->widthValid())
248 doc->setTextWidth(q->width());
249 else
250 doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
251 dy -= (int)doc->size().height();
252 QSize dsize = doc->size().toSize();
253 if (dsize != layedOutTextSize) {
254 q->prepareGeometryChange();
255 layedOutTextSize = dsize;
256 }
257 size = QSize(int(doc->idealWidth()),dsize.height());
258 }
259 int yoff = 0;
260
261 if (q->heightValid()) {
262 if (vAlign == QDeclarativeText::AlignBottom)
263 yoff = dy;
264 else if (vAlign == QDeclarativeText::AlignVCenter)
265 yoff = dy/2;
266 }
267 q->setBaselineOffset(fm.ascent() + yoff);
268
269 //### need to comfirm cost of always setting these for richText
270 internalWidthUpdate = true;
271 q->setImplicitWidth(size.width());
272 internalWidthUpdate = false;
273 q->setImplicitHeight(size.height());
274 emit q->paintedSizeChanged();
275 q->update();
276}
277
278/*!
279 Lays out the QDeclarativeTextPrivate::layout QTextLayout in the constraints of the QDeclarativeText.
280
281 Returns the size of the final text. This can be used to position the text vertically (the text is
282 already absolutely positioned horizontally).
283*/
284QSize QDeclarativeTextPrivate::setupTextLayout()
285{
286 // ### text layout handling should be profiled and optimized as needed
287 // what about QStackTextEngine engine(tmp, d->font.font()); QTextLayout textLayout(&engine);
288 Q_Q(QDeclarativeText);
289 layout.setCacheEnabled(true);
290
291 qreal height = 0;
292 qreal widthUsed = 0;
293 qreal lineWidth = 0;
294
295 //set manual width
296 if ((wrapMode != QDeclarativeText::NoWrap || elideMode != QDeclarativeText::ElideNone) && q->widthValid())
297 lineWidth = q->width();
298
299 QTextOption textOption = layout.textOption();
300 textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
301 layout.setTextOption(textOption);
302
303 layout.beginLayout();
304 forever {
305 QTextLine line = layout.createLine();
306 if (!line.isValid())
307 break;
308
309 if (lineWidth)
310 line.setLineWidth(lineWidth);
311 }
312 layout.endLayout();
313
314 for (int i = 0; i < layout.lineCount(); ++i) {
315 QTextLine line = layout.lineAt(i);
316 widthUsed = qMax(widthUsed, line.naturalTextWidth());
317 }
318
319 qreal layoutWidth = q->widthValid() ? q->width() : widthUsed;
320
321 qreal x = 0;
322 for (int i = 0; i < layout.lineCount(); ++i) {
323 QTextLine line = layout.lineAt(i);
324 line.setPosition(QPointF(0, height));
325 height += line.height();
326
327 if (!cacheAllTextAsImage) {
328 if (hAlign == QDeclarativeText::AlignLeft) {
329 x = 0;
330 } else if (hAlign == QDeclarativeText::AlignRight) {
331 x = layoutWidth - line.naturalTextWidth();
332 } else if (hAlign == QDeclarativeText::AlignHCenter) {
333 x = (layoutWidth - line.naturalTextWidth()) / 2;
334 }
335 line.setPosition(QPointF(x, line.y()));
336 }
337 }
338
339 return layout.boundingRect().toAlignedRect().size();
340}
341
342/*!
343 Returns a painted version of the QDeclarativeTextPrivate::layout QTextLayout.
344 If \a drawStyle is true, the style color overrides all colors in the document.
345*/
346QPixmap QDeclarativeTextPrivate::textLayoutImage(bool drawStyle)
347{
348 //do layout
349 QSize size = layedOutTextSize;
350
351 qreal x = 0;
352 for (int i = 0; i < layout.lineCount(); ++i) {
353 QTextLine line = layout.lineAt(i);
354 if (hAlign == QDeclarativeText::AlignLeft) {
355 x = 0;
356 } else if (hAlign == QDeclarativeText::AlignRight) {
357 x = size.width() - line.naturalTextWidth();
358 } else if (hAlign == QDeclarativeText::AlignHCenter) {
359 x = (size.width() - line.naturalTextWidth()) / 2;
360 }
361 line.setPosition(QPointF(x, line.y()));
362 }
363
364 //paint text
365 QPixmap img(size);
366 if (!size.isEmpty()) {
367 img.fill(Qt::transparent);
368#ifdef Q_WS_MAC
369 bool oldSmooth = qt_applefontsmoothing_enabled;
370 qt_applefontsmoothing_enabled = false;
371#endif
372 QPainter p(&img);
373#ifdef Q_WS_MAC
374 qt_applefontsmoothing_enabled = oldSmooth;
375#endif
376 drawTextLayout(&p, QPointF(0,0), drawStyle);
377 }
378 return img;
379}
380
381/*!
382 Paints the QDeclarativeTextPrivate::layout QTextLayout into \a painter at \a pos. If
383 \a drawStyle is true, the style color overrides all colors in the document.
384*/
385void QDeclarativeTextPrivate::drawTextLayout(QPainter *painter, const QPointF &pos, bool drawStyle)
386{
387 if (drawStyle)
388 painter->setPen(styleColor);
389 else
390 painter->setPen(color);
391 painter->setFont(font);
392 layout.draw(painter, pos);
393}
394
395/*!
396 Returns a painted version of the QDeclarativeTextPrivate::doc QTextDocument.
397 If \a drawStyle is true, the style color overrides all colors in the document.
398*/
399QPixmap QDeclarativeTextPrivate::textDocumentImage(bool drawStyle)
400{
401 QSize size = doc->size().toSize();
402
403 //paint text
404 QPixmap img(size);
405 img.fill(Qt::transparent);
406#ifdef Q_WS_MAC
407 bool oldSmooth = qt_applefontsmoothing_enabled;
408 qt_applefontsmoothing_enabled = false;
409#endif
410 QPainter p(&img);
411#ifdef Q_WS_MAC
412 qt_applefontsmoothing_enabled = oldSmooth;
413#endif
414
415 QAbstractTextDocumentLayout::PaintContext context;
416
417 QTextOption oldOption(doc->defaultTextOption());
418 if (drawStyle) {
419 context.palette.setColor(QPalette::Text, styleColor);
420 QTextOption colorOption(doc->defaultTextOption());
421 colorOption.setFlags(QTextOption::SuppressColors);
422 doc->setDefaultTextOption(colorOption);
423 } else {
424 context.palette.setColor(QPalette::Text, color);
425 }
426 doc->documentLayout()->draw(&p, context);
427 if (drawStyle)
428 doc->setDefaultTextOption(oldOption);
429 return img;
430}
431
432/*!
433 Mark the image cache as dirty.
434*/
435void QDeclarativeTextPrivate::invalidateImageCache()
436{
437 Q_Q(QDeclarativeText);
438
439 if(cacheAllTextAsImage || style != QDeclarativeText::Normal){//If actually using the image cache
440 if (imageCacheDirty)
441 return;
442
443 imageCacheDirty = true;
444 imageCache = QPixmap();
445 }
446 if (q->isComponentComplete())
447 q->update();
448}
449
450/*!
451 Tests if the image cache is dirty, and repaints it if it is.
452*/
453void QDeclarativeTextPrivate::checkImageCache()
454{
455 if (!imageCacheDirty)
456 return;
457
458 if (text.isEmpty()) {
459
460 imageCache = QPixmap();
461
462 } else {
463
464 QPixmap textImage;
465 QPixmap styledImage;
466
467 if (richText) {
468 textImage = textDocumentImage(false);
469 if (style != QDeclarativeText::Normal)
470 styledImage = textDocumentImage(true); //### should use styleColor
471 } else {
472 textImage = textLayoutImage(false);
473 if (style != QDeclarativeText::Normal)
474 styledImage = textLayoutImage(true); //### should use styleColor
475 }
476
477 switch (style) {
478 case QDeclarativeText::Outline:
479 imageCache = drawOutline(textImage, styledImage);
480 break;
481 case QDeclarativeText::Sunken:
482 imageCache = drawOutline(textImage, styledImage, -1);
483 break;
484 case QDeclarativeText::Raised:
485 imageCache = drawOutline(textImage, styledImage, 1);
486 break;
487 default:
488 imageCache = textImage;
489 break;
490 }
491
492 }
493
494 imageCacheDirty = false;
495}
496
497/*!
498 Ensures the QDeclarativeTextPrivate::doc variable is set to a valid text document
499*/
500void QDeclarativeTextPrivate::ensureDoc()
501{
502 if (!doc) {
503 Q_Q(QDeclarativeText);
504 doc = new QTextDocumentWithImageResources(q);
505 doc->setDocumentMargin(0);
506 }
507}
508
509/*!
510 Draw \a styleSource as an outline around \a source and return the new image.
511*/
512QPixmap QDeclarativeTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource)
513{
514 QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
515 img.fill(Qt::transparent);
516
517 QPainter ppm(&img);
518
519 QPoint pos(0, 0);
520 pos += QPoint(-1, 0);
521 ppm.drawPixmap(pos, styleSource);
522 pos += QPoint(2, 0);
523 ppm.drawPixmap(pos, styleSource);
524 pos += QPoint(-1, -1);
525 ppm.drawPixmap(pos, styleSource);
526 pos += QPoint(0, 2);
527 ppm.drawPixmap(pos, styleSource);
528
529 pos += QPoint(0, -1);
530 ppm.drawPixmap(pos, source);
531 ppm.end();
532
533 return img;
534}
535
536/*!
537 Draw \a styleSource below \a source at \a yOffset and return the new image.
538*/
539QPixmap QDeclarativeTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset)
540{
541 QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
542 img.fill(Qt::transparent);
543
544 QPainter ppm(&img);
545
546 ppm.drawPixmap(QPoint(0, yOffset), styleSource);
547 ppm.drawPixmap(0, 0, source);
548
549 ppm.end();
550
551 return img;
552}
553
554/*!
555 \qmlclass Text QDeclarativeText
556 \ingroup qml-basic-visual-elements
557 \since 4.7
558 \brief The Text item allows you to add formatted text to a scene.
559 \inherits Item
560
561 A Text item can display both plain and rich text. For example:
562
563 \qml
564 Text { text: "Hello World!"; font.family: "Helvetica"; font.pointSize: 24; color: "red" }
565 Text { text: "<b>Hello</b> <i>World!</i>" }
566 \endqml
567
568 \image declarative-text.png
569
570 If height and width are not explicitly set, Text will attempt to determine how
571 much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
572 prefer width to height (all text will be placed on a single line).
573
574 The \l elide property can alternatively be used to fit a single line of
575 plain text to a set width.
576
577 Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
578 HTML img tags that load remote images, the text is reloaded.
579
580 Text provides read-only text. For editable text, see \l TextEdit.
581
582 \sa {declarative/text/fonts}{Fonts example}
583*/
584QDeclarativeText::QDeclarativeText(QDeclarativeItem *parent)
585 : QDeclarativeItem(*(new QDeclarativeTextPrivate), parent)
586{
587}
588
589QDeclarativeText::~QDeclarativeText()
590{
591}
592
593/*!
594 \qmlproperty bool Text::clip
595 This property holds whether the text is clipped.
596
597 Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
598
599 If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
600*/
601
602/*!
603 \qmlproperty bool Text::smooth
604
605 This property holds whether the text is smoothly scaled or transformed.
606
607 Smooth filtering gives better visual quality, but is slower. If
608 the item is displayed at its natural size, this property has no visual or
609 performance effect.
610
611 \note Generally scaling artifacts are only visible if the item is stationary on
612 the screen. A common pattern when animating an item is to disable smooth
613 filtering at the beginning of the animation and reenable it at the conclusion.
614*/
615
616/*!
617 \qmlsignal Text::onLinkActivated(string link)
618
619 This handler is called when the user clicks on a link embedded in the text.
620 The link must be in rich text or HTML format and the
621 \a link string provides access to the particular link.
622
623 \snippet doc/src/snippets/declarative/text/onLinkActivated.qml 0
624
625 The example code will display the text
626 "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}."
627
628 Clicking on the highlighted link will output
629 \tt{http://qt.nokia.com link activated} to the console.
630*/
631
632/*!
633 \qmlproperty string Text::font.family
634
635 Sets the family name of the font.
636
637 The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
638 If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
639 If the family isn't available a family will be set using the font matching algorithm.
640*/
641
642/*!
643 \qmlproperty bool Text::font.bold
644
645 Sets whether the font weight is bold.
646*/
647
648/*!
649 \qmlproperty enumeration Text::font.weight
650
651 Sets the font's weight.
652
653 The weight can be one of:
654 \list
655 \o Font.Light
656 \o Font.Normal - the default
657 \o Font.DemiBold
658 \o Font.Bold
659 \o Font.Black
660 \endlist
661
662 \qml
663 Text { text: "Hello"; font.weight: Font.DemiBold }
664 \endqml
665*/
666
667/*!
668 \qmlproperty bool Text::font.italic
669
670 Sets whether the font has an italic style.
671*/
672
673/*!
674 \qmlproperty bool Text::font.underline
675
676 Sets whether the text is underlined.
677*/
678
679/*!
680 \qmlproperty bool Text::font.strikeout
681
682 Sets whether the font has a strikeout style.
683*/
684
685/*!
686 \qmlproperty real Text::font.pointSize
687
688 Sets the font size in points. The point size must be greater than zero.
689*/
690
691/*!
692 \qmlproperty int Text::font.pixelSize
693
694 Sets the font size in pixels.
695
696 Using this function makes the font device dependent.
697 Use \c pointSize to set the size of the font in a device independent manner.
698*/
699
700/*!
701 \qmlproperty real Text::font.letterSpacing
702
703 Sets the letter spacing for the font.
704
705 Letter spacing changes the default spacing between individual letters in the font.
706 A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
707*/
708
709/*!
710 \qmlproperty real Text::font.wordSpacing
711
712 Sets the word spacing for the font.
713
714 Word spacing changes the default spacing between individual words.
715 A positive value increases the word spacing by a corresponding amount of pixels,
716 while a negative value decreases the inter-word spacing accordingly.
717*/
718
719/*!
720 \qmlproperty enumeration Text::font.capitalization
721
722 Sets the capitalization for the text.
723
724 \list
725 \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
726 \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
727 \o Font.AllLowercase - This alters the text to be rendered in all lowercase type.
728 \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
729 \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
730 \endlist
731
732 \qml
733 Text { text: "Hello"; font.capitalization: Font.AllLowercase }
734 \endqml
735*/
736QFont QDeclarativeText::font() const
737{
738 Q_D(const QDeclarativeText);
739 return d->sourceFont;
740}
741
742void QDeclarativeText::setFont(const QFont &font)
743{
744 Q_D(QDeclarativeText);
745 if (d->sourceFont == font)
746 return;
747
748 d->sourceFont = font;
749 QFont oldFont = d->font;
750 d->font = font;
751 if (d->font.pointSizeF() != -1) {
752 // 0.5pt resolution
753 qreal size = qRound(d->font.pointSizeF()*2.0);
754 d->font.setPointSizeF(size/2.0);
755 }
756
757 if (oldFont != d->font)
758 d->updateLayout();
759
760 emit fontChanged(d->sourceFont);
761}
762
763/*!
764 \qmlproperty string Text::text
765
766 The text to display. Text supports both plain and rich text strings.
767
768 The item will try to automatically determine whether the text should
769 be treated as rich text. This determination is made using Qt::mightBeRichText().
770*/
771QString QDeclarativeText::text() const
772{
773 Q_D(const QDeclarativeText);
774 return d->text;
775}
776
777void QDeclarativeText::setText(const QString &n)
778{
779 Q_D(QDeclarativeText);
780 if (d->text == n)
781 return;
782
783 d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(n));
784 if (d->richText && isComponentComplete()) {
785 d->ensureDoc();
786 d->doc->setText(n);
787 }
788
789 d->text = n;
790 d->updateLayout();
791
792 emit textChanged(d->text);
793}
794
795
796/*!
797 \qmlproperty color Text::color
798
799 The text color.
800
801 \qml
802 //green text using hexadecimal notation
803 Text { color: "#00FF00"; ... }
804
805 //steelblue text using SVG color name
806 Text { color: "steelblue"; ... }
807 \endqml
808*/
809QColor QDeclarativeText::color() const
810{
811 Q_D(const QDeclarativeText);
812 return d->color;
813}
814
815void QDeclarativeText::setColor(const QColor &color)
816{
817 Q_D(QDeclarativeText);
818 if (d->color == color)
819 return;
820
821 d->color = color;
822 d->invalidateImageCache();
823 emit colorChanged(d->color);
824}
825
826/*!
827 \qmlproperty enumeration Text::style
828
829 Set an additional text style.
830
831 Supported text styles are:
832 \list
833 \o Text.Normal - the default
834 \o Text.Outline
835 \o Text.Raised
836 \o Text.Sunken
837 \endlist
838
839 \qml
840 Row {
841 Text { font.pointSize: 24; text: "Normal" }
842 Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
843 Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
844 Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
845 }
846 \endqml
847
848 \image declarative-textstyle.png
849*/
850QDeclarativeText::TextStyle QDeclarativeText::style() const
851{
852 Q_D(const QDeclarativeText);
853 return d->style;
854}
855
856void QDeclarativeText::setStyle(QDeclarativeText::TextStyle style)
857{
858 Q_D(QDeclarativeText);
859 if (d->style == style)
860 return;
861
862 // changing to/from Normal requires the boundingRect() to change
863 if (isComponentComplete() && (d->style == Normal || style == Normal))
864 prepareGeometryChange();
865 d->style = style;
866 d->invalidateImageCache();
867 emit styleChanged(d->style);
868}
869
870/*!
871 \qmlproperty color Text::styleColor
872
873 Defines the secondary color used by text styles.
874
875 \c styleColor is used as the outline color for outlined text, and as the
876 shadow color for raised or sunken text. If no style has been set, it is not
877 used at all.
878
879 \qml
880 Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
881 \endqml
882
883 \sa style
884 */
885QColor QDeclarativeText::styleColor() const
886{
887 Q_D(const QDeclarativeText);
888 return d->styleColor;
889}
890
891void QDeclarativeText::setStyleColor(const QColor &color)
892{
893 Q_D(QDeclarativeText);
894 if (d->styleColor == color)
895 return;
896
897 d->styleColor = color;
898 d->invalidateImageCache();
899 emit styleColorChanged(d->styleColor);
900}
901
902
903/*!
904 \qmlproperty enumeration Text::horizontalAlignment
905 \qmlproperty enumeration Text::verticalAlignment
906
907 Sets the horizontal and vertical alignment of the text within the Text items
908 width and height. By default, the text is top-left aligned.
909
910 The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight and
911 \c Text.AlignHCenter. The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
912 and \c Text.AlignVCenter.
913
914 Note that for a single line of text, the size of the text is the area of the text. In this common case,
915 all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
916 need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
917 that of the parent.
918*/
919QDeclarativeText::HAlignment QDeclarativeText::hAlign() const
920{
921 Q_D(const QDeclarativeText);
922 return d->hAlign;
923}
924
925void QDeclarativeText::setHAlign(HAlignment align)
926{
927 Q_D(QDeclarativeText);
928 if (d->hAlign == align)
929 return;
930
931 if (isComponentComplete())
932 prepareGeometryChange();
933
934 d->hAlign = align;
935 d->updateLayout();
936
937 emit horizontalAlignmentChanged(align);
938}
939
940QDeclarativeText::VAlignment QDeclarativeText::vAlign() const
941{
942 Q_D(const QDeclarativeText);
943 return d->vAlign;
944}
945
946void QDeclarativeText::setVAlign(VAlignment align)
947{
948 Q_D(QDeclarativeText);
949 if (d->vAlign == align)
950 return;
951
952 if (isComponentComplete())
953 prepareGeometryChange();
954 d->vAlign = align;
955 emit verticalAlignmentChanged(align);
956}
957
958/*!
959 \qmlproperty enumeration Text::wrapMode
960
961 Set this property to wrap the text to the Text item's width. The text will only
962 wrap if an explicit width has been set. wrapMode can be one of:
963
964 \list
965 \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width.
966 \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width.
967 \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
968 \o Text.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
969 \endlist
970*/
971QDeclarativeText::WrapMode QDeclarativeText::wrapMode() const
972{
973 Q_D(const QDeclarativeText);
974 return d->wrapMode;
975}
976
977void QDeclarativeText::setWrapMode(WrapMode mode)
978{
979 Q_D(QDeclarativeText);
980 if (mode == d->wrapMode)
981 return;
982
983 d->wrapMode = mode;
984 d->updateLayout();
985
986 emit wrapModeChanged();
987}
988
989
990/*!
991 \qmlproperty enumeration Text::textFormat
992
993 The way the text property should be displayed.
994
995 Supported text formats are:
996
997 \list
998 \o Text.AutoText (default)
999 \o Text.PlainText
1000 \o Text.RichText
1001 \o Text.StyledText
1002 \endlist
1003
1004 If the text format is \c Text.AutoText the text element
1005 will automatically determine whether the text should be treated as
1006 rich text. This determination is made using Qt::mightBeRichText().
1007
1008 Text.StyledText is an optimized format supporting some basic text
1009 styling markup, in the style of html 3.2:
1010
1011 \code
1012 <font size="4" color="#ff0000">font size and color</font>
1013 <b>bold</b>
1014 <i>italic</i>
1015 <br>
1016 &gt; &lt; &amp;
1017 \endcode
1018
1019 \c Text.StyledText parser is strict, requiring tags to be correctly nested.
1020
1021 \table
1022 \row
1023 \o
1024 \qml
1025Column {
1026 Text {
1027 font.pointSize: 24
1028 text: "<b>Hello</b> <i>World!</i>"
1029 }
1030 Text {
1031 font.pointSize: 24
1032 textFormat: Text.RichText
1033 text: "<b>Hello</b> <i>World!</i>"
1034 }
1035 Text {
1036 font.pointSize: 24
1037 textFormat: Text.PlainText
1038 text: "<b>Hello</b> <i>World!</i>"
1039 }
1040}
1041 \endqml
1042 \o \image declarative-textformat.png
1043 \endtable
1044*/
1045QDeclarativeText::TextFormat QDeclarativeText::textFormat() const
1046{
1047 Q_D(const QDeclarativeText);
1048 return d->format;
1049}
1050
1051void QDeclarativeText::setTextFormat(TextFormat format)
1052{
1053 Q_D(QDeclarativeText);
1054 if (format == d->format)
1055 return;
1056 d->format = format;
1057 bool wasRich = d->richText;
1058 d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text));
1059
1060 if (!wasRich && d->richText && isComponentComplete()) {
1061 d->ensureDoc();
1062 d->doc->setText(d->text);
1063 }
1064
1065 d->updateLayout();
1066
1067 emit textFormatChanged(d->format);
1068}
1069
1070/*!
1071 \qmlproperty enumeration Text::elide
1072
1073 Set this property to elide parts of the text fit to the Text item's width.
1074 The text will only elide if an explicit width has been set.
1075
1076 This property cannot be used with multi-line text or with rich text.
1077
1078 Eliding can be:
1079 \list
1080 \o Text.ElideNone - the default
1081 \o Text.ElideLeft
1082 \o Text.ElideMiddle
1083 \o Text.ElideRight
1084 \endlist
1085
1086 If the text is a multi-length string, and the mode is not \c Text.ElideNone,
1087 the first string that fits will be used, otherwise the last will be elided.
1088
1089 Multi-length strings are ordered from longest to shortest, separated by the
1090 Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
1091*/
1092QDeclarativeText::TextElideMode QDeclarativeText::elideMode() const
1093{
1094 Q_D(const QDeclarativeText);
1095 return d->elideMode;
1096}
1097
1098void QDeclarativeText::setElideMode(QDeclarativeText::TextElideMode mode)
1099{
1100 Q_D(QDeclarativeText);
1101 if (mode == d->elideMode)
1102 return;
1103
1104 d->elideMode = mode;
1105 d->updateLayout();
1106
1107 emit elideModeChanged(d->elideMode);
1108}
1109
1110/*! \internal */
1111QRectF QDeclarativeText::boundingRect() const
1112{
1113 Q_D(const QDeclarativeText);
1114
1115 int w = width();
1116 int h = height();
1117
1118 int x = 0;
1119 int y = 0;
1120
1121 QSize size = d->layedOutTextSize;
1122 if (d->style != Normal)
1123 size += QSize(2,2);
1124
1125 // Could include font max left/right bearings to either side of rectangle.
1126
1127 switch (d->hAlign) {
1128 case AlignLeft:
1129 x = 0;
1130 break;
1131 case AlignRight:
1132 x = w - size.width();
1133 break;
1134 case AlignHCenter:
1135 x = (w - size.width()) / 2;
1136 break;
1137 }
1138
1139 switch (d->vAlign) {
1140 case AlignTop:
1141 y = 0;
1142 break;
1143 case AlignBottom:
1144 y = h - size.height();
1145 break;
1146 case AlignVCenter:
1147 y = (h - size.height()) / 2;
1148 break;
1149 }
1150
1151 return QRectF(x,y,size.width(),size.height());
1152}
1153
1154/*! \internal */
1155void QDeclarativeText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1156{
1157 Q_D(QDeclarativeText);
1158 if ((!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width())
1159 && (d->wrapMode != QDeclarativeText::NoWrap
1160 || d->elideMode != QDeclarativeText::ElideNone
1161 || d->hAlign != QDeclarativeText::AlignLeft)) {
1162 if (d->singleline && d->elideMode != QDeclarativeText::ElideNone && widthValid()) {
1163 // We need to re-elide
1164 d->updateLayout();
1165 } else {
1166 // We just need to re-layout
1167 d->updateSize();
1168 }
1169 }
1170
1171 QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
1172}
1173
1174/*!
1175 \qmlproperty real Text::paintedWidth
1176
1177 Returns the width of the text, including width past the width
1178 which is covered due to insufficient wrapping if WrapMode is set.
1179*/
1180qreal QDeclarativeText::paintedWidth() const
1181{
1182 return implicitWidth();
1183}
1184
1185/*!
1186 \qmlproperty real Text::paintedHeight
1187
1188 Returns the height of the text, including height past the height
1189 which is covered due to there being more text than fits in the set height.
1190*/
1191qreal QDeclarativeText::paintedHeight() const
1192{
1193 return implicitHeight();
1194}
1195
1196/*!
1197 Returns the number of resources (images) that are being loaded asynchronously.
1198*/
1199int QDeclarativeText::resourcesLoading() const
1200{
1201 Q_D(const QDeclarativeText);
1202 return d->doc ? d->doc->resourcesLoading() : 0;
1203}
1204
1205/*! \internal */
1206void QDeclarativeText::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
1207{
1208 Q_D(QDeclarativeText);
1209
1210 if (d->cacheAllTextAsImage || d->style != Normal) {
1211 d->checkImageCache();
1212 if (d->imageCache.isNull())
1213 return;
1214
1215 bool oldAA = p->testRenderHint(QPainter::Antialiasing);
1216 bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform);
1217 if (d->smooth)
1218 p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth);
1219
1220 QRect br = boundingRect().toRect();
1221
1222 bool needClip = clip() && (d->imageCache.width() > width() ||
1223 d->imageCache.height() > height());
1224
1225 if (needClip)
1226 p->drawPixmap(0, 0, width(), height(), d->imageCache, -br.x(), -br.y(), width(), height());
1227 else
1228 p->drawPixmap(br.x(), br.y(), d->imageCache);
1229
1230 if (d->smooth) {
1231 p->setRenderHint(QPainter::Antialiasing, oldAA);
1232 p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth);
1233 }
1234 } else {
1235 QRectF bounds = boundingRect();
1236
1237 bool needClip = clip() && (d->layedOutTextSize.width() > width() ||
1238 d->layedOutTextSize.height() > height());
1239
1240 if (needClip) {
1241 p->save();
1242 p->setClipRect(0, 0, width(), height(), Qt::IntersectClip);
1243 }
1244 if (d->richText) {
1245 QAbstractTextDocumentLayout::PaintContext context;
1246 context.palette.setColor(QPalette::Text, d->color);
1247 p->translate(bounds.x(), bounds.y());
1248 d->doc->documentLayout()->draw(p, context);
1249 p->translate(-bounds.x(), -bounds.y());
1250 } else {
1251 d->drawTextLayout(p, QPointF(0, bounds.y()), false);
1252 }
1253
1254 if (needClip) {
1255 p->restore();
1256 }
1257 }
1258}
1259
1260/*! \internal */
1261void QDeclarativeText::componentComplete()
1262{
1263 Q_D(QDeclarativeText);
1264 QDeclarativeItem::componentComplete();
1265 if (d->updateOnComponentComplete) {
1266 d->updateOnComponentComplete = false;
1267 if (d->richText) {
1268 d->ensureDoc();
1269 d->doc->setText(d->text);
1270 }
1271 d->updateLayout();
1272 }
1273}
1274
1275/*! \internal */
1276void QDeclarativeText::mousePressEvent(QGraphicsSceneMouseEvent *event)
1277{
1278 Q_D(QDeclarativeText);
1279
1280 if (!d->richText || !d->doc || d->doc->documentLayout()->anchorAt(event->pos()).isEmpty()) {
1281 event->setAccepted(false);
1282 d->activeLink.clear();
1283 } else {
1284 d->activeLink = d->doc->documentLayout()->anchorAt(event->pos());
1285 }
1286
1287 // ### may malfunction if two of the same links are clicked & dragged onto each other)
1288
1289 if (!event->isAccepted())
1290 QDeclarativeItem::mousePressEvent(event);
1291
1292}
1293
1294/*! \internal */
1295void QDeclarativeText::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1296{
1297 Q_D(QDeclarativeText);
1298
1299 // ### confirm the link, and send a signal out
1300 if (d->richText && d->doc && d->activeLink == d->doc->documentLayout()->anchorAt(event->pos()))
1301 emit linkActivated(d->activeLink);
1302 else
1303 event->setAccepted(false);
1304
1305 if (!event->isAccepted())
1306 QDeclarativeItem::mouseReleaseEvent(event);
1307}
1308
1309QT_END_NAMESPACE
1310
1311#include "qdeclarativetext.moc"
Note: See TracBrowser for help on using the repository browser.