stable calculation of text base lines, so that text don't "wobble"
anymore during translations of margins/paddings
This commit is contained in:
parent
6108af2c84
commit
277d8dd8d6
@ -30,7 +30,7 @@ int main( int argc, char* argv[] )
|
|||||||
|
|
||||||
SkinnyFont::init( &app );
|
SkinnyFont::init( &app );
|
||||||
SkinnyShortcut::enable( SkinnyShortcut::Quit |
|
SkinnyShortcut::enable( SkinnyShortcut::Quit |
|
||||||
SkinnyShortcut::DebugShortcuts );
|
SkinnyShortcut::ChangeFonts | SkinnyShortcut::DebugShortcuts );
|
||||||
|
|
||||||
QQmlApplicationEngine engine( QUrl( "qrc:/qml/colorswitch.qml" ) );
|
QQmlApplicationEngine engine( QUrl( "qrc:/qml/colorswitch.qml" ) );
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QSGTransformNode>
|
#include <QSGTransformNode>
|
||||||
#include <QQuickWindow>
|
#include <QQuickWindow>
|
||||||
|
#include <QtMath>
|
||||||
|
|
||||||
QSK_QT_PRIVATE_BEGIN
|
QSK_QT_PRIVATE_BEGIN
|
||||||
#include <private/qsgadaptationlayer_p.h>
|
#include <private/qsgadaptationlayer_p.h>
|
||||||
@ -43,9 +44,10 @@ QRectF QskPlainTextRenderer::textRect( const QString& text,
|
|||||||
return fm.boundingRect( r, options.textFlags(), text );
|
return fm.boundingRect( r, options.textFlags(), text );
|
||||||
}
|
}
|
||||||
|
|
||||||
static qreal qskLayoutText( QTextLayout* layout, qreal lineWidth, const QskTextOptions& options )
|
static qreal qskLayoutText( QTextLayout* layout,
|
||||||
|
qreal lineWidth, const QskTextOptions& options )
|
||||||
{
|
{
|
||||||
const auto maxLineCount =( options.wrapMode() == QskTextOptions::NoWrap )
|
const auto maxLineCount = ( options.wrapMode() == QskTextOptions::NoWrap )
|
||||||
? 1 : options.maximumLineCount();
|
? 1 : options.maximumLineCount();
|
||||||
|
|
||||||
int lineNumber = 0;
|
int lineNumber = 0;
|
||||||
@ -60,7 +62,8 @@ static qreal qskLayoutText( QTextLayout* layout, qreal lineWidth, const QskTextO
|
|||||||
if ( lineNumber == maxLineCount )
|
if ( lineNumber == maxLineCount )
|
||||||
{
|
{
|
||||||
const auto elideMode = options.elideMode();
|
const auto elideMode = options.elideMode();
|
||||||
auto engine = layout->engine();
|
const auto engine = layout->engine();
|
||||||
|
|
||||||
const auto textLength = engine->text.length();
|
const auto textLength = engine->text.length();
|
||||||
if ( elideMode != Qt::ElideNone && textLength > characterPosition )
|
if ( elideMode != Qt::ElideNone && textLength > characterPosition )
|
||||||
{
|
{
|
||||||
@ -72,8 +75,9 @@ static qreal qskLayoutText( QTextLayout* layout, qreal lineWidth, const QskTextO
|
|||||||
|
|
||||||
auto elidedText = engine->elidedText(
|
auto elidedText = engine->elidedText(
|
||||||
elideMode, QFixed::fromReal( lineWidth ),
|
elideMode, QFixed::fromReal( lineWidth ),
|
||||||
Qt::TextShowMnemonic, characterPosition )
|
Qt::TextShowMnemonic, characterPosition );
|
||||||
.leftJustified( textLength - characterPosition );
|
|
||||||
|
elidedText = elidedText.leftJustified( textLength - characterPosition );
|
||||||
|
|
||||||
engine->text.replace( characterPosition, elidedText.length(), elidedText );
|
engine->text.replace( characterPosition, elidedText.length(), elidedText );
|
||||||
Q_ASSERT( engine->text.length() == textLength );
|
Q_ASSERT( engine->text.length() == textLength );
|
||||||
@ -88,19 +92,21 @@ static qreal qskLayoutText( QTextLayout* layout, qreal lineWidth, const QskTextO
|
|||||||
line.setPosition( QPointF( 0, y ) );
|
line.setPosition( QPointF( 0, y ) );
|
||||||
line.setLineWidth( lineWidth );
|
line.setLineWidth( lineWidth );
|
||||||
characterPosition = line.textStart() + line.textLength();
|
characterPosition = line.textStart() + line.textLength();
|
||||||
|
|
||||||
y += line.leading() + line.height();
|
y += line.leading() + line.height();
|
||||||
}
|
}
|
||||||
|
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qskRenderText(
|
static void qskRenderText(
|
||||||
QQuickItem* item, QSGNode* parentNode, QTextLayout* layout, const QPointF& position,
|
QQuickItem* item, QSGNode* parentNode, const QTextLayout& layout, qreal baseLine,
|
||||||
const QColor& color, QQuickText::TextStyle style, const QColor& styleColor )
|
const QColor& color, QQuickText::TextStyle style, const QColor& styleColor )
|
||||||
{
|
{
|
||||||
auto rc = RenderContext::from( QOpenGLContext::currentContext() );
|
auto renderContext = RenderContext::from( QOpenGLContext::currentContext() );
|
||||||
auto sgContext = rc->sceneGraphContext();
|
auto sgContext = renderContext->sceneGraphContext();
|
||||||
|
|
||||||
// Clear out foreign nodes (e.g. from QskTextRenderer)
|
// Clear out foreign nodes (e.g. from QskRichTextRenderer)
|
||||||
QSGNode* node = parentNode->firstChild();
|
QSGNode* node = parentNode->firstChild();
|
||||||
while ( node )
|
while ( node )
|
||||||
{
|
{
|
||||||
@ -114,28 +120,34 @@ static void qskRenderText(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto glyphNode = static_cast< QSGGlyphNode* >( parentNode->firstChild() );
|
auto glyphNode = static_cast< QSGGlyphNode* >( parentNode->firstChild() );
|
||||||
for ( int i = 0; i < layout->lineCount(); ++i )
|
|
||||||
|
const QPointF position( 0, baseLine );
|
||||||
|
|
||||||
|
for ( int i = 0; i < layout.lineCount(); ++i )
|
||||||
{
|
{
|
||||||
const auto glyphRuns = layout->lineAt( i ).glyphRuns();
|
const auto glyphRuns = layout.lineAt( i ).glyphRuns();
|
||||||
|
|
||||||
for ( const auto& glyphRun : glyphRuns )
|
for ( const auto& glyphRun : glyphRuns )
|
||||||
{
|
{
|
||||||
const bool doCreate = !glyphNode;
|
if ( glyphNode == nullptr )
|
||||||
if ( doCreate )
|
|
||||||
{
|
{
|
||||||
glyphNode = sgContext->createGlyphNode( rc, false ); // ### add native rendering to QskTextOptions?
|
const bool preferNativeGlyphNode = false; // QskTextOptions?
|
||||||
|
|
||||||
|
glyphNode = sgContext->createGlyphNode( renderContext, preferNativeGlyphNode );
|
||||||
glyphNode->setOwnerElement( item );
|
glyphNode->setOwnerElement( item );
|
||||||
glyphNode->setFlags( QSGNode::OwnedByParent | GlyphFlag );
|
glyphNode->setFlags( QSGNode::OwnedByParent | GlyphFlag );
|
||||||
}
|
}
|
||||||
|
|
||||||
glyphNode->setStyle( style );
|
glyphNode->setStyle( style );
|
||||||
glyphNode->setColor( color );
|
glyphNode->setColor( color );
|
||||||
glyphNode->setStyleColor( styleColor );
|
glyphNode->setStyleColor( styleColor );
|
||||||
glyphNode->setGlyphs( position, glyphRun );
|
glyphNode->setGlyphs( position, glyphRun );
|
||||||
glyphNode->update();
|
glyphNode->update();
|
||||||
if ( doCreate )
|
|
||||||
{
|
if ( glyphNode->parent() != parentNode )
|
||||||
parentNode->appendChildNode( glyphNode );
|
parentNode->appendChildNode( glyphNode );
|
||||||
}
|
|
||||||
glyphNode = static_cast< decltype( glyphNode ) >( glyphNode->nextSibling() );
|
glyphNode = static_cast< QSGGlyphNode* >( glyphNode->nextSibling() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,15 +179,26 @@ void QskPlainTextRenderer::updateNode( const QString& text,
|
|||||||
layout.setText( text );
|
layout.setText( text );
|
||||||
|
|
||||||
layout.beginLayout();
|
layout.beginLayout();
|
||||||
QPointF position;
|
const qreal textHeight = qskLayoutText( &layout, rect.width(), options );
|
||||||
position.ry() += qskLayoutText( &layout, rect.width(), options );
|
|
||||||
layout.endLayout();
|
layout.endLayout();
|
||||||
|
|
||||||
position.setX( 0 );
|
qreal yBaseline = QFontMetricsF( font ).ascent();
|
||||||
position.setY( QFontMetricsF( font ).ascent()
|
|
||||||
+ ( alignment & Qt::AlignVCenter ? ( rect.height() - position.y() ) * 0.5 : 0 ) );
|
|
||||||
|
|
||||||
qskRenderText( const_cast< QQuickItem* >( item ), node, &layout, position,
|
if ( alignment & Qt::AlignVCenter )
|
||||||
|
{
|
||||||
|
yBaseline += ( rect.height() - textHeight ) * 0.5;
|
||||||
|
|
||||||
|
/*
|
||||||
|
We need to have a stable algo for rounding the text base line,
|
||||||
|
so that texts don't start wobbling, when processing transitions
|
||||||
|
between margins/paddings.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const int bh = int( layout.boundingRect().height() );
|
||||||
|
yBaseline = ( bh % 2 ) ? qFloor( yBaseline ) : qCeil( yBaseline );
|
||||||
|
}
|
||||||
|
|
||||||
|
qskRenderText( const_cast< QQuickItem* >( item ), node, layout, yBaseline,
|
||||||
colors.textColor, static_cast< QQuickText::TextStyle >( style ), colors.styleColor );
|
colors.textColor, static_cast< QQuickText::TextStyle >( style ), colors.styleColor );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,6 +150,8 @@ void QskRichTextRenderer::updateNode( const QString& text,
|
|||||||
|
|
||||||
textItem.begin();
|
textItem.begin();
|
||||||
|
|
||||||
|
textItem.setBottomPadding( 0 );
|
||||||
|
textItem.setTopPadding( 0 );
|
||||||
textItem.setFont( font );
|
textItem.setFont( font );
|
||||||
textItem.setOptions( options );
|
textItem.setOptions( options );
|
||||||
textItem.setAlignment( alignment );
|
textItem.setAlignment( alignment );
|
||||||
@ -179,6 +181,28 @@ void QskRichTextRenderer::updateNode( const QString& text,
|
|||||||
|
|
||||||
textItem.end();
|
textItem.end();
|
||||||
|
|
||||||
|
if ( alignment & Qt::AlignVCenter )
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
We need to have a stable algo for rounding the text base line,
|
||||||
|
so that texts don't start wobbling, when processing transitions
|
||||||
|
between margins/paddings. We manipulate the layout code
|
||||||
|
by adding some padding, so that the position of base line
|
||||||
|
gets always floored.
|
||||||
|
*/
|
||||||
|
auto d = QQuickTextPrivate::get( &textItem );
|
||||||
|
|
||||||
|
const qreal h = d->layedOutTextRect.height() + d->lineHeightOffset();
|
||||||
|
|
||||||
|
if ( static_cast< int >( rect.height() - h ) % 2 )
|
||||||
|
{
|
||||||
|
if ( static_cast<int>( h ) % 2 )
|
||||||
|
d->extra.value().bottomPadding = 1;
|
||||||
|
else
|
||||||
|
d->extra.value().topPadding = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
textItem.updateTextNode( item->window(), node );
|
textItem.updateTextNode( item->window(), node );
|
||||||
textItem.setText( QString::null );
|
textItem.setText( QString::null );
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
|
|
||||||
#include <QRectF>
|
#include <QRectF>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Since Qt 5.7 QQuickTextNode is exported as Q_QUICK_PRIVATE_EXPORT
|
||||||
|
and could be used. TODO ...
|
||||||
|
*/
|
||||||
QSizeF QskTextRenderer::textSize( const QString& text,
|
QSizeF QskTextRenderer::textSize( const QString& text,
|
||||||
const QFont& font, const QskTextOptions& options )
|
const QFont& font, const QskTextOptions& options )
|
||||||
{
|
{
|
||||||
|
@ -36,6 +36,18 @@ void SkinnyShortcut::enable( Types types )
|
|||||||
cout << "CTRL-S to change the skin." << endl;
|
cout << "CTRL-S to change the skin." << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( types & ChangeFonts )
|
||||||
|
{
|
||||||
|
QskShortcutMap::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_F ),
|
||||||
|
false, &s_shortcut, [] { s_shortcut.changeFonts( +1 ); } );
|
||||||
|
|
||||||
|
QskShortcutMap::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_G ),
|
||||||
|
false, &s_shortcut, [] { s_shortcut.changeFonts( -1 ); } );
|
||||||
|
|
||||||
|
cout << "CTRL-F to increase the font size." << endl;
|
||||||
|
cout << "CTRL-G to decrease the font size." << endl;
|
||||||
|
}
|
||||||
|
|
||||||
if ( types & DebugBackground )
|
if ( types & DebugBackground )
|
||||||
{
|
{
|
||||||
QskShortcutMap::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_B ),
|
QskShortcutMap::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_B ),
|
||||||
@ -134,6 +146,29 @@ void SkinnyShortcut::showBackground()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkinnyShortcut::changeFonts( int increment )
|
||||||
|
{
|
||||||
|
auto skin = qskSetup->skin();
|
||||||
|
|
||||||
|
for ( int role = 0; role <= QskSkin::HugeFont; role++ )
|
||||||
|
{
|
||||||
|
auto font = skin->font( role );
|
||||||
|
|
||||||
|
if ( font.pixelSize() > 0 )
|
||||||
|
{
|
||||||
|
font.setPixelSize( font.pixelSize() + increment );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
font.setPointSize( font.pointSize() + increment );
|
||||||
|
}
|
||||||
|
|
||||||
|
skin->setFont( role, font );
|
||||||
|
}
|
||||||
|
|
||||||
|
qskSetup->Q_EMIT skinChanged( skin );
|
||||||
|
}
|
||||||
|
|
||||||
static inline void countNodes( const QSGNode* node, int& counter )
|
static inline void countNodes( const QSGNode* node, int& counter )
|
||||||
{
|
{
|
||||||
if ( node )
|
if ( node )
|
||||||
|
@ -16,13 +16,14 @@ class SKINNY_EXPORT SkinnyShortcut : public QObject
|
|||||||
public:
|
public:
|
||||||
enum Type
|
enum Type
|
||||||
{
|
{
|
||||||
Quit = 1,
|
Quit = 1 << 0,
|
||||||
RotateSkin = 2,
|
RotateSkin = 1 << 1,
|
||||||
DebugBackground = 4,
|
ChangeFonts = 1 << 2,
|
||||||
DebugStatistics = 8,
|
DebugBackground = 1 << 3,
|
||||||
|
DebugStatistics = 1 << 4,
|
||||||
|
|
||||||
DebugShortcuts = DebugBackground | DebugStatistics,
|
DebugShortcuts = DebugBackground | DebugStatistics,
|
||||||
AllShortcuts = Quit | RotateSkin | DebugBackground | DebugStatistics
|
AllShortcuts = Quit | RotateSkin | ChangeFonts | DebugBackground | DebugStatistics
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_ENUM( Type )
|
Q_ENUM( Type )
|
||||||
@ -33,8 +34,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
SkinnyShortcut( QObject* parent = nullptr );
|
SkinnyShortcut( QObject* parent = nullptr );
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void rotateSkin();
|
void rotateSkin();
|
||||||
|
void changeFonts( int increment );
|
||||||
void showBackground();
|
void showBackground();
|
||||||
void debugStatistics();
|
void debugStatistics();
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user