1
0
mirror of https://github.com/Swordfish90/cool-retro-term.git synced 2025-02-16 01:48:38 +00:00

250 lines
7.2 KiB
C++

/******************************************************************************
* 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 <QtQuick/private/qsgadaptationlayer_p.h>
#include <QtQuick/private/qsgrenderer_p.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquicktextnode_p.h>
#include <QtGui/QTextLayout>
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<QGlyphRun> 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<QPointF> m_positions;
QLinkedList<QSGNode *> 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<MonoSGNode *>(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();
}