/****************************************************************************** * Copyright (c) 2012 Jørgen Lind * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * ******************************************************************************/ #include "mono_text.h" #include #include #include #include #include class MonoSGNode : public QSGTransformNode { public: MonoSGNode(QQuickItem *owner) : m_owner(owner) { } void deleteContent() { QSGNode *subnode = firstChild(); while (subnode) { // We can't delete the node now as it might be in the preprocess list // It will be deleted in the next preprocess m_nodes_to_delete.append(subnode); subnode = subnode->nextSibling(); } removeAllChildNodes(); } void preprocess() { while (m_nodes_to_delete.count()) delete m_nodes_to_delete.takeLast(); } void setLatinText(const QString &text, const QFont &font, const QColor &color) { QRawFont raw_font = QRawFont::fromFont(font, QFontDatabase::Latin); if (raw_font != m_raw_font) { m_raw_font = raw_font; m_positions.clear(); } if (m_positions.size() < text.size()) { qreal x_pos = 0; qreal max_char_width = raw_font.averageCharWidth(); qreal ascent = raw_font.ascent(); if (m_positions.size()) x_pos = m_positions.last().x() + max_char_width; int to_add = text.size() - m_positions.size(); for (int i = 0; i < to_add; i++) { m_positions << QPointF(x_pos, ascent); x_pos += max_char_width; } } deleteContent(); QSGRenderContext *sgr = QQuickItemPrivate::get(m_owner)->sceneGraphRenderContext(); QSGGlyphNode *node = sgr->sceneGraphContext()->createGlyphNode(sgr); node->setOwnerElement(m_owner); node->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern); node->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern); node->setStyle(QQuickText::Normal); node->setColor(color); QGlyphRun glyphrun; glyphrun.setRawFont(raw_font); glyphrun.setGlyphIndexes(raw_font.glyphIndexesForString(text)); glyphrun.setPositions(m_positions); node->setGlyphs(QPointF(0, raw_font.ascent()), glyphrun); node->update(); appendChildNode(node); } void setUnicodeText(const QString &text, const QFont &font, const QColor &color) { deleteContent(); QRawFont raw_font = QRawFont::fromFont(font, QFontDatabase::Latin); qreal line_width = raw_font.averageCharWidth() * text.size(); QSGRenderContext *sgr = QQuickItemPrivate::get(m_owner)->sceneGraphRenderContext(); QTextLayout layout(text,font); layout.beginLayout(); QTextLine line = layout.createLine(); line.setLineWidth(line_width); //Q_ASSERT(!layout.createLine().isValid()); layout.endLayout(); QList glyphRuns = line.glyphRuns(); qreal xpos = 0; for (int i = 0; i < glyphRuns.size(); i++) { QSGGlyphNode *node = sgr->sceneGraphContext()->createGlyphNode(sgr); node->setOwnerElement(m_owner); node->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern); node->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern); node->setGlyphs(QPointF(xpos, raw_font.ascent()), glyphRuns.at(i)); node->setStyle(QQuickText::Normal); node->setColor(color); xpos += raw_font.averageCharWidth() * glyphRuns.at(i).positions().size(); node->update(); appendChildNode(node); } } private: QQuickItem *m_owner; QVector m_positions; QLinkedList m_nodes_to_delete; QRawFont m_raw_font; }; MonoText::MonoText(QQuickItem *parent) : QQuickItem(parent) , m_color_changed(false) , m_latin(true) , m_old_latin(true) { setFlag(ItemHasContents, true); } MonoText::~MonoText() { } QString MonoText::text() const { return m_text; } void MonoText::setText(const QString &text) { if (m_text != text) { m_text = text; emit textChanged(); polish(); } } QFont MonoText::font() const { return m_font; } void MonoText::setFont(const QFont &font) { if (font != m_font) { m_font = font; emit fontChanged(); polish(); } } QColor MonoText::color() const { return m_color; } void MonoText::setColor(const QColor &color) { if (m_color != color) { m_color = color; emit colorChanged(); update(); } } qreal MonoText::paintedWidth() const { return implicitWidth(); } qreal MonoText::paintedHeight() const { return implicitHeight(); } bool MonoText::latin() const { return m_latin; } void MonoText::setLatin(bool latin) { if (latin == m_latin) return; m_latin = latin; emit latinChanged(); } QSGNode *MonoText::updatePaintNode(QSGNode *old, UpdatePaintNodeData *) { if (m_text.size() == 0 || m_text.trimmed().size() == 0) { delete old; return 0; } MonoSGNode *node = static_cast(old); if (!node) { node = new MonoSGNode(this); } if (m_latin) { node->setLatinText(m_text, m_font, m_color); } else { node->setUnicodeText(m_text, m_font, m_color); } return node; } void MonoText::updatePolish() { QRawFont raw_font = QRawFont::fromFont(m_font, QFontDatabase::Latin); qreal height = raw_font.descent() + raw_font.ascent() + raw_font.lineThickness(); qreal width = raw_font.averageCharWidth() * m_text.size(); bool emit_text_width_changed = width != implicitWidth(); bool emit_text_height_changed = height != implicitHeight(); setImplicitSize(width, height); if (emit_text_width_changed) emit paintedWidthChanged(); if (emit_text_height_changed) emit paintedHeightChanged(); update(); }