material specific decorations generalized and moved to QskTextFieldSkinlet

This commit is contained in:
Uwe Rathmann 2025-05-15 14:00:07 +02:00
parent 7305502de0
commit c7012fe24a
5 changed files with 355 additions and 360 deletions

View File

@ -611,6 +611,8 @@ void Editor::setupTextField()
setAlignment( Q::Footer, Qt::AlignLeft | Qt::AlignVCenter );
setAlignment( Q::CharacterCount, Qt::AlignRight | Qt::AlignVCenter );
setFlag( Q::CharacterCount | A::Option, Qsk::Maybe );
}
void Editor::setupProgressBar()

View File

@ -6,243 +6,26 @@
#include "QskMaterial3TextFieldSkinlet.h"
#include "QskMaterial3Skin.h"
#include <QskTextField.h>
#include <QskBoxBorderColors.h>
#include <QskBoxHints.h>
#include <QskFunctions.h>
#include <QskSkinnable.h>
#include <QFontMetricsF>
using Q = QskTextField;
static inline bool isOutlined( const QskTextField* textField )
static inline bool isOutlined( const QskSkinnable* skinnable )
{
return textField->effectiveVariation() == QskAspect::NoVariation;
}
namespace
{
const int spacingV = 0; // skin hint !
inline bool hasCharacterCount( const QskTextField* textField )
{
// magic number hardcoded in qquicktextinput.cpp
return textField->maxLength() < 32767;
}
QString maxLengthString( const QskTextField* textField )
{
QString s = QString::number( textField->text().length() )
+ " / " + QString::number( textField->maxLength() );
return s;
}
// We need to "cut a hole" in the upper gradient for the label text:
QskBoxBorderColors outlineColors( const QskBoxBorderColors& colors,
const QRectF& rect, const QRectF& clipRect )
{
auto gradient = colors.gradientAt( Qt::TopEdge );
const auto margin = 6; // ->skin
auto s1 = ( clipRect.left() - margin - rect.left() ) / rect.width();
auto s2 = ( clipRect.right() - rect.left() ) / rect.width();
s1 = qBound( 0.0, s1, 1.0 );
s2 = qBound( 0.0, s2, 1.0 );
// not correct, when gradient is not monochrome !!!
gradient.setStops( {
{ 0.0, gradient.startColor() },
{ s1, gradient.startColor() },
{ s1, Qt::transparent },
{ s2, Qt::transparent },
{ s2, gradient.endColor() },
{ 1.0, gradient.endColor() }
} );
auto borderColors = colors;
borderColors.setGradientAt( Qt::TopEdge, gradient );
return borderColors;
}
return skinnable->effectiveVariation() == QskAspect::NoVariation;
}
QskMaterial3TextFieldSkinlet::QskMaterial3TextFieldSkinlet( QskSkin* skin )
: Inherited( skin )
{
appendNodeRoles( { CharacterCountRole } );
setRenderHints( UseHeaderAsPlaceholder );
}
QskMaterial3TextFieldSkinlet::~QskMaterial3TextFieldSkinlet()
{
}
QRectF QskMaterial3TextFieldSkinlet::subControlRect( const QskSkinnable* skinnable,
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
int QskMaterial3TextFieldSkinlet::panelMode( const QskSkinnable* skinnable ) const
{
const auto textField = static_cast< const Q* >( skinnable );
if ( subControl == Q::Header )
{
const auto text = effectiveText( textField, Q::Header );
if( text.isEmpty() )
return QRectF();
const QFontMetrics fm( textField->effectiveFont( Q::Header ) );
const auto textSize = fm.size( Qt::TextSingleLine | Qt::TextExpandTabs, text );
if ( isOutlined( textField ) )
{
const auto r = subControlRect( skinnable, contentsRect, Q::TextPanel );
const auto x = r.left() + skinnable->paddingHint( Q::TextPanel ).left();
const auto y = r.top() - 0.5 * textSize.height();
return QRectF( x, y, textSize.width(), textSize.height() );
}
else
{
const auto r = subControlRect( skinnable, contentsRect, Q::Text );
return QRectF( r.x(), r.top() - textSize.height(),
textSize.width(), textSize.height() );
}
}
if ( subControl == Q::TextPanel )
{
auto rect = skinnable->subControlRect( contentsRect, Q::Panel );
if ( isOutlined( textField ) )
{
const QFontMetrics fm( textField->effectiveFont( Q::Header ) );
rect.setTop( rect.top() + 0.5 * fm.height() );
}
const auto h = skinnable->strutSizeHint( Q::TextPanel ).height();
rect.setHeight( h );
return rect;
}
if ( subControl == Q::CharacterCount )
{
if( hasCharacterCount( textField ) )
{
auto rect = contentsRect;
const auto margins = textField->marginHint( subControl );
const auto h = textField->effectiveFontHeight( subControl )
+ margins.top() + margins.bottom();
rect.setTop( rect.bottom() - h );
const QFontMetricsF fm( textField->effectiveFont( subControl ) );
const auto w = qskHorizontalAdvance( fm, maxLengthString( textField ) );
rect.setRight( rect.right() - margins.right() );
rect.setLeft( rect.right() - ( margins.left() + w + margins.right() ) );
return rect;
}
return QRectF();
}
return Inherited::subControlRect( skinnable, contentsRect, subControl );
}
QSGNode* QskMaterial3TextFieldSkinlet::updateSubNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
{
const auto textField = static_cast< const Q* >( skinnable );
switch ( nodeRole )
{
case TextPanelRole:
{
const auto rect = textField->subControlRect( Q::TextPanel );
auto hints = textField->boxHints( Q::TextPanel );
if ( isOutlined( textField ) )
{
const auto clipRect = textField->subControlRect( Q::Header );
if ( !clipRect.isEmpty() )
{
hints.borderColors = outlineColors(
hints.borderColors, rect, clipRect );
}
}
return updateBoxNode( skinnable, node, rect, hints );
}
case CharacterCountRole:
{
return updateTextNode( skinnable, node,
maxLengthString( textField ), Q::CharacterCount );
}
}
return Inherited::updateSubNode( skinnable, nodeRole, node );
}
QSizeF QskMaterial3TextFieldSkinlet::sizeHint( const QskSkinnable* skinnable,
Qt::SizeHint which, const QSizeF& constraint ) const
{
Q_UNUSED( constraint ); // TODO ...
if ( which != Qt::PreferredSize )
return QSizeF();
const auto textField = static_cast< const QskTextField* >( skinnable );
auto hint = textField->unwrappedTextSize();
hint = hint.expandedTo( skinnable->strutSizeHint( Q::TextPanel ) );
if ( isOutlined( textField ) )
{
hint.rheight() += 0.5 * textField->effectiveFontHeight( Q::Header );
}
if ( hasCharacterCount( textField )
|| !effectiveText( textField, Q::Footer ).isEmpty() )
{
hint.rheight() += textHeight( textField, Q::Footer );
}
hint = hint.expandedTo( skinnable->strutSizeHint( Q::Panel ) );
return hint;
}
QString QskMaterial3TextFieldSkinlet::effectiveText(
const QskTextField* textField, QskAspect::Subcontrol subControl ) const
{
const bool showHeaderAsPlaceholder =
textField->text().isEmpty() && !textField->isEditing();
if ( subControl == Q::Header )
{
if ( showHeaderAsPlaceholder )
return QString();
}
else if ( subControl == Q::Placeholder )
{
if ( showHeaderAsPlaceholder )
{
auto text = Inherited::effectiveText( textField, Q::Placeholder );
if ( text.isEmpty() )
text = Inherited::effectiveText( textField, Q::Header );
return text;
}
return QString();
}
return Inherited::effectiveText( textField, subControl );
return isOutlined( skinnable ) ? 2 : 1;
}
#include "moc_QskMaterial3TextFieldSkinlet.cpp"

View File

@ -16,26 +16,11 @@ class QSK_MATERIAL3_EXPORT QskMaterial3TextFieldSkinlet : public QskTextFieldSki
using Inherited = QskTextFieldSkinlet;
public:
enum NodeRole : quint8
{
CharacterCountRole = Inherited::RoleCount
};
Q_INVOKABLE QskMaterial3TextFieldSkinlet( QskSkin* = nullptr );
~QskMaterial3TextFieldSkinlet() override;
QRectF subControlRect( const QskSkinnable*,
const QRectF& rect, QskAspect::Subcontrol ) const override;
QSizeF sizeHint( const QskSkinnable*,
Qt::SizeHint, const QSizeF& ) const override;
protected:
QSGNode* updateSubNode( const QskSkinnable*,
quint8 nodeRole, QSGNode* ) const override;
QString effectiveText( const QskTextField*,
QskAspect::Subcontrol ) const override;
private:
int panelMode( const QskSkinnable* skinnable ) const override final;
};
#endif

View File

@ -6,63 +6,72 @@
#include "QskTextFieldSkinlet.h"
#include "QskTextField.h"
#include "QskFunctions.h"
#include "QskBoxHints.h"
#include <qfontmetrics.h>
using Q = QskTextField;
static QString qskEffectiveCharacterCountText( const QskTextField* textField )
{
auto policy = textField->flagHint< Qsk::Policy >(
Q::CharacterCount | QskAspect::Option, Qsk::Never );
const auto maxLength = textField->maxLength();
const bool isLimited = maxLength < 32767; // magic number hardcoded in qquicktextinput.cpp
if ( policy == Qsk::Always || ( policy == Qsk::Maybe && isLimited ) )
{
auto s = QString::number( textField->text().length() );
if ( isLimited )
s += " / " + QString::number( maxLength );
return s;
}
return QString();
}
QskTextFieldSkinlet::QskTextFieldSkinlet( QskSkin* skin )
: Inherited( skin )
{
setNodeRoles( { TextPanelRole, IconRole, ButtonPanelRole, ButtonRole,
PlaceholderRole, HeaderRole, FooterRole } );
PlaceholderRole, HeaderRole, FooterRole, CharacterCountRole } );
}
QskTextFieldSkinlet::~QskTextFieldSkinlet()
{
}
void QskTextFieldSkinlet::setRenderHints( RenderHints hints )
{
m_renderHints = hints;
}
QskTextFieldSkinlet::RenderHints QskTextFieldSkinlet::renderHints() const
{
return m_renderHints;
}
QRectF QskTextFieldSkinlet::subControlRect( const QskSkinnable* skinnable,
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
{
const auto textField = static_cast< const QskTextField* >( skinnable );
if ( subControl == Q::Panel )
{
return contentsRect;
}
if ( subControl == Q::Header )
{
if ( auto h = effectiveHeaderHeight( textField ) )
{
const auto m = textField->marginHint( Q::Header );
const auto r = subControlRect( skinnable, contentsRect, Q::TextPanel );
return QRectF( r.left() + m.left(),
r.top() - m.bottom() - h, r.width() - m.left() - m.right(), h );
}
return QRectF();
}
return headerRect( skinnable, contentsRect );
if ( subControl == Q::Footer )
{
if ( const auto h = effectiveFooterHeight( textField ) )
{
const auto m = textField->marginHint( Q::Footer );
const auto r = subControlRect( skinnable, contentsRect, Q::TextPanel );
return alignedLabelRect( skinnable, contentsRect, subControl, Qt::AlignBottom );
return QRectF( r.left() + m.left(),
r.bottom() + m.top(), r.width() - m.left() - m.right(), h );
}
return QRectF();
}
if ( subControl == Q::CharacterCount )
return alignedLabelRect( skinnable, contentsRect, subControl, Qt::AlignBottom );
if ( subControl == Q::TextPanel )
{
const auto rect = textField->subControlContentsRect( contentsRect, Q::Panel );
return inputPanelRect( textField, rect );
}
return inputPanelRect( skinnable, contentsRect );
if ( subControl == Q::Text )
{
@ -85,7 +94,7 @@ QRectF QskTextFieldSkinlet::subControlRect( const QskSkinnable* skinnable,
if ( subControl == Q::Placeholder )
{
if( textField->text().isEmpty() )
if ( hasText( skinnable, Q::Placeholder ) )
return subControlRect( skinnable, contentsRect, Q::Text );
return QRectF();
@ -142,45 +151,22 @@ QRectF QskTextFieldSkinlet::subControlRect( const QskSkinnable* skinnable,
QSGNode* QskTextFieldSkinlet::updateSubNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
{
const auto textField = static_cast< const QskTextField* >( skinnable );
switch ( nodeRole )
{
case TextPanelRole:
{
return updateBoxNode( skinnable, node, Q::TextPanel );
}
return updateInputPanelNode( skinnable, node );
case PlaceholderRole:
{
const auto subControl = Q::Placeholder;
const auto text = effectiveText( textField, subControl );
if ( text.isEmpty() )
return nullptr;
QskSkinHintStatus status;
auto options = skinnable->textOptionsHint( subControl, &status );
if ( !status.isValid() )
options.setElideMode( Qt::ElideRight );
return updateTextNode( skinnable, node,
textField->subControlRect( subControl ),
textField->alignmentHint( subControl, Qt::AlignLeft ),
options, text, subControl );
}
return updateLabelNode( skinnable, node, Q::Placeholder );
case HeaderRole:
{
return updateTextNode( skinnable, node,
effectiveText( textField, Q::Header ), Q::Header );
}
return updateLabelNode( skinnable, node, Q::Header );
case FooterRole:
{
return updateTextNode( skinnable, node,
effectiveText( textField, Q::Footer ), Q::Footer );
}
return updateLabelNode( skinnable, node, Q::Footer );
case CharacterCountRole:
return updateLabelNode( skinnable, node, Q::CharacterCount );
case IconRole:
return updateSymbolNode( skinnable, node, Q::Icon );
@ -195,35 +181,157 @@ QSGNode* QskTextFieldSkinlet::updateSubNode(
return Inherited::updateSubNode( skinnable, nodeRole, node );
}
QRectF QskTextFieldSkinlet::inputPanelRect(
const QskTextField* textField, const QRectF& rect ) const
QSGNode* QskTextFieldSkinlet::updateInputPanelNode(
const QskSkinnable* skinnable, QSGNode* node ) const
{
qreal h = textField->effectiveFontHeight( Q::Text );
h = textField->outerBoxSize( Q::TextPanel, QSizeF( rect.width(), h ) ).height();
h = qMax( h, textField->strutSizeHint( Q::TextPanel ).height() );
const auto control = static_cast< const QskControl* >( skinnable );
/*
when having textfields in horizontal layouts you usually want
the text panels being vertically aligned - regardless of having
Q::Header/Q::Footer being available.
*/
const auto rect = control->subControlRect( Q::TextPanel );
auto top = textHeight( textField, Q::Header );
auto bottom = textHeight( textField, Q::Footer );
auto hints = skinnable->boxHints( Q::TextPanel );
if ( rect.height() < top + h + bottom )
if ( panelMode( skinnable ) == 2 )
{
if ( effectiveText( textField, Q::Footer ).isEmpty() )
bottom = 0.0;
const auto clipRect = control->subControlRect( Q::Header );
if ( !clipRect.isEmpty() )
{
// "cutting a hole" in the upper gradient for the header
const auto margin = 6; // ->skin
auto s1 = ( clipRect.left() - margin - rect.left() ) / rect.width();
auto s2 = ( clipRect.right() - rect.left() ) / rect.width();
auto gradient = hints.borderColors.top();
gradient.setStops( qskClippedGradientStops( gradient.stops(), s1, s2 ) );
hints.borderColors.setTop( gradient );
}
}
if ( rect.height() < top + h + bottom )
{
if ( effectiveText( textField, Q::Header ).isEmpty() )
top = 0.0;
}
return updateBoxNode( skinnable, node, rect, hints );
}
return QRectF( rect.left(), rect.top() + top, rect.width(), h );
QSGNode* QskTextFieldSkinlet::updateLabelNode( const QskSkinnable* skinnable,
QSGNode* node, QskAspect::Subcontrol subControl ) const
{
const auto text = effectiveText( skinnable, subControl );
if ( text.isEmpty() )
return nullptr;
QskSkinHintStatus status;
auto options = skinnable->textOptionsHint( subControl, &status );
if ( !status.isValid() )
options.setElideMode( Qt::ElideRight );
auto rect = skinnable->controlCast()->contentsRect();
rect = subControlRect( skinnable, rect, subControl );
return updateTextNode( skinnable, node, rect,
skinnable->alignmentHint( subControl, Qt::AlignLeft ),
options, text, subControl );
}
QRectF QskTextFieldSkinlet::headerRect(
const QskSkinnable* skinnable, const QRectF& contentsRect ) const
{
switch( panelMode( skinnable ) )
{
case 1:
{
const auto sz = effectiveTextSize( skinnable, Q::Header );
const auto r = subControlRect( skinnable, contentsRect, Q::Text );
return QRectF( r.x(), r.top() - sz.height(), sz.width(), sz.height() );
}
case 2:
{
const auto sz = effectiveTextSize( skinnable, Q::Header );
const auto r = subControlRect( skinnable, contentsRect, Q::TextPanel );
const auto x = r.left() + skinnable->paddingHint( Q::TextPanel ).left();
const auto y = r.top() - 0.5 * sz.height();
return QRectF( x, y, sz.width(), sz.height() );
}
default:
return alignedLabelRect( skinnable, contentsRect, Q::Header, Qt::AlignTop );
}
}
QRectF QskTextFieldSkinlet::inputPanelRect(
const QskSkinnable* skinnable, const QRectF& contentsRect ) const
{
auto rect = skinnable->subControlContentsRect( contentsRect, Q::Panel );
const auto mode = panelMode( skinnable );
if ( mode == 0 )
{
qreal h = skinnable->effectiveFontHeight( Q::Text );
h = skinnable->outerBoxSize( Q::TextPanel, QSizeF( rect.width(), h ) ).height();
h = qMax( h, skinnable->strutSizeHint( Q::TextPanel ).height() );
/*
when having textfields in horizontal layouts you usually want
the text panels being vertically aligned - regardless of having
Q::Header/Q::Footer being available.
*/
auto top = textHeight( skinnable, Q::Header );
auto bottom = textHeight( skinnable, Q::Footer );
if ( rect.height() < top + h + bottom )
{
if ( effectiveText( skinnable, Q::Footer ).isEmpty() )
bottom = 0.0;
}
if ( rect.height() < top + h + bottom )
{
if ( effectiveText( skinnable, Q::Header ).isEmpty() )
top = 0.0;
}
return QRectF( rect.left(), rect.top() + top, rect.width(), h );
}
else
{
if ( mode == 2 )
{
qreal h = 0.0;
#if 0
h = effectiveTextHeight( skinnable, Q::Header );
#else
if ( !text( skinnable, Q::Header ).isEmpty() )
h = skinnable->effectiveFontHeight( Q::Header );
#endif
rect.setTop( rect.top() + 0.5 * h );
}
const auto h = skinnable->strutSizeHint( Q::TextPanel ).height();
rect.setHeight( h );
return rect;
}
}
QRectF QskTextFieldSkinlet::alignedLabelRect( const QskSkinnable* skinnable,
const QRectF& contentsRect, QskAspect::Subcontrol subControl,
Qt::Alignment alignment ) const
{
const auto h = effectiveTextHeight( skinnable, subControl );
if ( h <= 0.0 )
return QRectF();
const auto m = skinnable->marginHint( subControl );
auto r = subControlRect( skinnable, contentsRect, Q::TextPanel );
const auto y = ( alignment & Qt::AlignTop )
? r.top() - m.bottom() - h : r.bottom() + m.top();
return QRectF( r.left() + m.left(), y,
r.width() - m.left() - m.right(), h );
}
QSizeF QskTextFieldSkinlet::sizeHint( const QskSkinnable* skinnable,
@ -237,75 +345,161 @@ QSizeF QskTextFieldSkinlet::sizeHint( const QskSkinnable* skinnable,
const auto textField = static_cast< const QskTextField* >( skinnable );
auto hint = textField->unwrappedTextSize();
hint = hint.expandedTo( skinnable->strutSizeHint( Q::Text ) );
if( !skinnable->symbolHint( Q::Icon ).isEmpty() )
{
const auto sz = skinnable->strutSizeHint( Q::Icon );
if ( sz.width() > 0.0 )
hint.rwidth() += sz.width();
hint.rheight() = qMax( hint.height(), sz.height() );
}
if( !skinnable->symbolHint( Q::Button ).isEmpty() )
{
const auto sz = skinnable->strutSizeHint( Q::Button );
if ( sz.width() > 0.0 )
hint.rwidth() += sz.width();
hint.rheight() = qMax( hint.height(), sz.height() );
}
hint = skinnable->outerBoxSize( Q::TextPanel, hint );
hint = hint.expandedTo( skinnable->strutSizeHint( Q::TextPanel ) );
switch( panelMode( skinnable ) )
{
case 0:
{
hint = skinnable->outerBoxSize( Q::TextPanel, hint );
hint = hint.expandedTo( skinnable->strutSizeHint( Q::TextPanel ) );
hint.rheight() += effectiveHeaderHeight( textField );
hint.rheight() += effectiveFooterHeight( textField );
hint.rheight() += effectiveTextHeight( skinnable, Q::Header );
break;
}
case 1:
{
hint = hint.expandedTo( skinnable->strutSizeHint( Q::TextPanel ) );
break;
}
case 2:
{
hint = hint.expandedTo( skinnable->strutSizeHint( Q::TextPanel ) );
hint.rheight() += 0.5 * skinnable->effectiveFontHeight( Q::Header );
break;
}
}
hint.rheight() += qMax(
effectiveTextHeight( skinnable, Q::Footer ),
effectiveTextHeight( skinnable, Q::CharacterCount )
);
hint = skinnable->outerBoxSize( Q::Panel, hint );
hint = hint.expandedTo( skinnable->strutSizeHint( Q::Panel ) );
return hint;
return hint.expandedTo( skinnable->strutSizeHint( Q::Panel ) );
}
qreal QskTextFieldSkinlet::textHeight( const QskTextField* textField,
qreal QskTextFieldSkinlet::textHeight( const QskSkinnable* skinnable,
QskAspect::Subcontrol subControl ) const
{
auto h = textField->effectiveFontHeight( subControl );
auto h = skinnable->effectiveFontHeight( subControl );
const auto margins = textField->marginHint( subControl );
const auto margins = skinnable->marginHint( subControl );
h += margins.top() + margins.bottom();
const auto sz = textField->strutSizeHint( subControl );
const auto sz = skinnable->strutSizeHint( subControl );
return qMax( h, sz.height() );
}
QString QskTextFieldSkinlet::effectiveText(
const QskTextField* textField, QskAspect::Subcontrol subcontrol ) const
QSizeF QskTextFieldSkinlet::effectiveTextSize( const QskSkinnable* skinnable,
QskAspect::Subcontrol subControl ) const
{
if ( subcontrol == Q::Text )
const auto text = effectiveText( skinnable, subControl );
const QFontMetricsF fm( skinnable->effectiveFont( subControl ) );
auto w = qskHorizontalAdvance( fm, effectiveText( skinnable, subControl ) );
auto h = fm.height();
const auto margins = skinnable->marginHint( subControl );
w += margins.left() + margins.right();
h += margins.top() + margins.bottom();
QSizeF sz( w, h );
sz = sz.expandedTo( skinnable->strutSizeHint( subControl ) );
return sz;
}
QString QskTextFieldSkinlet::text(
const QskSkinnable* skinnable, QskAspect::Subcontrol subControl ) const
{
const auto textField = static_cast< const QskTextField* >( skinnable );
if ( subControl == Q::Text )
return textField->text();
if ( subcontrol == Q::Placeholder )
{
if ( textField->text().isEmpty() &&
!( textField->isReadOnly() || textField->isEditing() ) )
{
return textField->placeholderText();
}
if ( subControl == Q::Placeholder )
return textField->placeholderText();
return QString();
}
if ( subcontrol == Q::Header )
if ( subControl == Q::Header )
return textField->headerText();
if ( subcontrol == Q::Footer )
if ( subControl == Q::Footer )
return textField->footerText();
if ( subControl == Q::CharacterCount )
return qskEffectiveCharacterCountText( textField );
return QString();
}
qreal QskTextFieldSkinlet::effectiveHeaderHeight( const QskTextField* textField ) const
bool QskTextFieldSkinlet::isPlaceholderVisible(
const QskSkinnable* skinnable ) const
{
const auto text = effectiveText( textField, Q::Header );
return text.isEmpty() ? 0.0 : textHeight( textField, Q::Header );
return !skinnable->hasSkinState( Q::Editing )
&& text( skinnable, Q::Text ).isEmpty();
}
qreal QskTextFieldSkinlet::effectiveFooterHeight( const QskTextField* textField ) const
bool QskTextFieldSkinlet::hasText(
const QskSkinnable* skinnable, QskAspect::Subcontrol subControl ) const
{
const auto text = effectiveText( textField, Q::Footer );
return text.isEmpty() ? 0.0 : textHeight( textField, Q::Footer );
return !effectiveText( skinnable, subControl ).isEmpty();
}
QString QskTextFieldSkinlet::effectiveText(
const QskSkinnable* skinnable, QskAspect::Subcontrol subControl ) const
{
if ( subControl == Q::Placeholder )
{
if ( !isPlaceholderVisible( skinnable ) )
return QString();
auto txt = text( skinnable, Q::Placeholder );
if ( txt.isEmpty() && ( renderHints() & UseHeaderAsPlaceholder ) )
txt = text( skinnable, Q::Header );
return txt;
}
if ( subControl == Q::Header )
{
if ( isPlaceholderVisible( skinnable )
&& ( renderHints() & UseHeaderAsPlaceholder ) )
{
return QString();
}
return text( skinnable, Q::Header );
}
return text( skinnable, subControl );
}
qreal QskTextFieldSkinlet::effectiveTextHeight(
const QskSkinnable* skinnable, QskAspect::Subcontrol subcontrol ) const
{
const auto text = effectiveText( skinnable, subcontrol );
return text.isEmpty() ? 0.0 : textHeight( skinnable, subcontrol );
}
#include "moc_QskTextFieldSkinlet.cpp"

View File

@ -23,6 +23,8 @@ class QSK_EXPORT QskTextFieldSkinlet : public QskSkinlet
HeaderRole,
FooterRole,
CharacterCountRole,
PlaceholderRole,
IconRole,
ButtonPanelRole,
@ -31,9 +33,20 @@ class QSK_EXPORT QskTextFieldSkinlet : public QskSkinlet
RoleCount
};
enum RenderHint
{
UseHeaderAsPlaceholder = 1 << 0
};
Q_ENUM( RenderHint )
Q_DECLARE_FLAGS( RenderHints, RenderHint )
Q_INVOKABLE QskTextFieldSkinlet( QskSkin* = nullptr );
~QskTextFieldSkinlet() override;
void setRenderHints( RenderHints );
RenderHints renderHints() const;
QRectF subControlRect( const QskSkinnable*,
const QRectF& rect, QskAspect::Subcontrol ) const override;
@ -44,17 +57,35 @@ class QSK_EXPORT QskTextFieldSkinlet : public QskSkinlet
QSGNode* updateSubNode( const QskSkinnable*,
quint8 nodeRole, QSGNode* ) const override;
virtual QString effectiveText( const QskTextField*,
QSGNode* updateLabelNode( const QskSkinnable*,
QSGNode*, QskAspect::Subcontrol ) const;
QString effectiveText( const QskSkinnable*,
QskAspect::Subcontrol ) const;
qreal textHeight( const QskTextField*, QskAspect::Subcontrol ) const;
QString text( const QskSkinnable*, QskAspect::Subcontrol ) const;
qreal textHeight( const QskSkinnable*, QskAspect::Subcontrol ) const;
bool hasText( const QskSkinnable*, QskAspect::Subcontrol ) const;
QSizeF effectiveTextSize( const QskSkinnable*, QskAspect::Subcontrol ) const;
qreal effectiveTextHeight( const QskSkinnable*, QskAspect::Subcontrol ) const;
private:
QRectF inputPanelRect( const QskTextField*, const QRectF& ) const;
QRectF headerRect( const QskSkinnable*, const QRectF& ) const;
QRectF inputPanelRect( const QskSkinnable*, const QRectF& ) const;
QSGNode* updateInputPanelNode( const QskSkinnable*, QSGNode* ) const;
qreal effectiveFooterHeight( const QskTextField* ) const;
qreal effectiveHeaderHeight( const QskTextField* ) const;
QRectF alignedLabelRect( const QskSkinnable*, const QRectF&,
QskAspect::Subcontrol, Qt::Alignment ) const;
bool isPlaceholderVisible( const QskSkinnable* ) const;
virtual int panelMode( const QskSkinnable* ) const { return 0; }
RenderHints m_renderHints;
};
Q_DECLARE_OPERATORS_FOR_FLAGS( QskTextFieldSkinlet::RenderHints )
#endif