material specific decorations generalized and moved to QskTextFieldSkinlet
This commit is contained in:
parent
7305502de0
commit
c7012fe24a
|
|
@ -611,6 +611,8 @@ void Editor::setupTextField()
|
||||||
|
|
||||||
setAlignment( Q::Footer, Qt::AlignLeft | Qt::AlignVCenter );
|
setAlignment( Q::Footer, Qt::AlignLeft | Qt::AlignVCenter );
|
||||||
setAlignment( Q::CharacterCount, Qt::AlignRight | Qt::AlignVCenter );
|
setAlignment( Q::CharacterCount, Qt::AlignRight | Qt::AlignVCenter );
|
||||||
|
|
||||||
|
setFlag( Q::CharacterCount | A::Option, Qsk::Maybe );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Editor::setupProgressBar()
|
void Editor::setupProgressBar()
|
||||||
|
|
|
||||||
|
|
@ -6,243 +6,26 @@
|
||||||
#include "QskMaterial3TextFieldSkinlet.h"
|
#include "QskMaterial3TextFieldSkinlet.h"
|
||||||
#include "QskMaterial3Skin.h"
|
#include "QskMaterial3Skin.h"
|
||||||
|
|
||||||
#include <QskTextField.h>
|
#include <QskSkinnable.h>
|
||||||
#include <QskBoxBorderColors.h>
|
|
||||||
#include <QskBoxHints.h>
|
|
||||||
#include <QskFunctions.h>
|
|
||||||
|
|
||||||
#include <QFontMetricsF>
|
static inline bool isOutlined( const QskSkinnable* skinnable )
|
||||||
|
|
||||||
using Q = QskTextField;
|
|
||||||
|
|
||||||
static inline bool isOutlined( const QskTextField* textField )
|
|
||||||
{
|
{
|
||||||
return textField->effectiveVariation() == QskAspect::NoVariation;
|
return skinnable->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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QskMaterial3TextFieldSkinlet::QskMaterial3TextFieldSkinlet( QskSkin* skin )
|
QskMaterial3TextFieldSkinlet::QskMaterial3TextFieldSkinlet( QskSkin* skin )
|
||||||
: Inherited( skin )
|
: Inherited( skin )
|
||||||
{
|
{
|
||||||
appendNodeRoles( { CharacterCountRole } );
|
setRenderHints( UseHeaderAsPlaceholder );
|
||||||
}
|
}
|
||||||
|
|
||||||
QskMaterial3TextFieldSkinlet::~QskMaterial3TextFieldSkinlet()
|
QskMaterial3TextFieldSkinlet::~QskMaterial3TextFieldSkinlet()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QRectF QskMaterial3TextFieldSkinlet::subControlRect( const QskSkinnable* skinnable,
|
int QskMaterial3TextFieldSkinlet::panelMode( const QskSkinnable* skinnable ) const
|
||||||
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
|
|
||||||
{
|
{
|
||||||
const auto textField = static_cast< const Q* >( skinnable );
|
return isOutlined( skinnable ) ? 2 : 1;
|
||||||
|
|
||||||
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 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "moc_QskMaterial3TextFieldSkinlet.cpp"
|
#include "moc_QskMaterial3TextFieldSkinlet.cpp"
|
||||||
|
|
|
||||||
|
|
@ -16,26 +16,11 @@ class QSK_MATERIAL3_EXPORT QskMaterial3TextFieldSkinlet : public QskTextFieldSki
|
||||||
using Inherited = QskTextFieldSkinlet;
|
using Inherited = QskTextFieldSkinlet;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum NodeRole : quint8
|
|
||||||
{
|
|
||||||
CharacterCountRole = Inherited::RoleCount
|
|
||||||
};
|
|
||||||
|
|
||||||
Q_INVOKABLE QskMaterial3TextFieldSkinlet( QskSkin* = nullptr );
|
Q_INVOKABLE QskMaterial3TextFieldSkinlet( QskSkin* = nullptr );
|
||||||
~QskMaterial3TextFieldSkinlet() override;
|
~QskMaterial3TextFieldSkinlet() override;
|
||||||
|
|
||||||
QRectF subControlRect( const QskSkinnable*,
|
private:
|
||||||
const QRectF& rect, QskAspect::Subcontrol ) const override;
|
int panelMode( const QskSkinnable* skinnable ) const override final;
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -6,63 +6,72 @@
|
||||||
#include "QskTextFieldSkinlet.h"
|
#include "QskTextFieldSkinlet.h"
|
||||||
#include "QskTextField.h"
|
#include "QskTextField.h"
|
||||||
#include "QskFunctions.h"
|
#include "QskFunctions.h"
|
||||||
|
#include "QskBoxHints.h"
|
||||||
|
|
||||||
|
#include <qfontmetrics.h>
|
||||||
|
|
||||||
using Q = QskTextField;
|
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 )
|
QskTextFieldSkinlet::QskTextFieldSkinlet( QskSkin* skin )
|
||||||
: Inherited( skin )
|
: Inherited( skin )
|
||||||
{
|
{
|
||||||
setNodeRoles( { TextPanelRole, IconRole, ButtonPanelRole, ButtonRole,
|
setNodeRoles( { TextPanelRole, IconRole, ButtonPanelRole, ButtonRole,
|
||||||
PlaceholderRole, HeaderRole, FooterRole } );
|
PlaceholderRole, HeaderRole, FooterRole, CharacterCountRole } );
|
||||||
}
|
}
|
||||||
|
|
||||||
QskTextFieldSkinlet::~QskTextFieldSkinlet()
|
QskTextFieldSkinlet::~QskTextFieldSkinlet()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QskTextFieldSkinlet::setRenderHints( RenderHints hints )
|
||||||
|
{
|
||||||
|
m_renderHints = hints;
|
||||||
|
}
|
||||||
|
|
||||||
|
QskTextFieldSkinlet::RenderHints QskTextFieldSkinlet::renderHints() const
|
||||||
|
{
|
||||||
|
return m_renderHints;
|
||||||
|
}
|
||||||
|
|
||||||
QRectF QskTextFieldSkinlet::subControlRect( const QskSkinnable* skinnable,
|
QRectF QskTextFieldSkinlet::subControlRect( const QskSkinnable* skinnable,
|
||||||
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
|
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
|
||||||
{
|
{
|
||||||
const auto textField = static_cast< const QskTextField* >( skinnable );
|
const auto textField = static_cast< const QskTextField* >( skinnable );
|
||||||
|
|
||||||
if ( subControl == Q::Panel )
|
if ( subControl == Q::Panel )
|
||||||
{
|
|
||||||
return contentsRect;
|
return contentsRect;
|
||||||
}
|
|
||||||
|
|
||||||
if ( subControl == Q::Header )
|
if ( subControl == Q::Header )
|
||||||
{
|
return headerRect( skinnable, contentsRect );
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( subControl == Q::Footer )
|
if ( subControl == Q::Footer )
|
||||||
{
|
return alignedLabelRect( skinnable, contentsRect, subControl, Qt::AlignBottom );
|
||||||
if ( const auto h = effectiveFooterHeight( textField ) )
|
|
||||||
{
|
|
||||||
const auto m = textField->marginHint( Q::Footer );
|
|
||||||
const auto r = subControlRect( skinnable, contentsRect, Q::TextPanel );
|
|
||||||
|
|
||||||
return QRectF( r.left() + m.left(),
|
if ( subControl == Q::CharacterCount )
|
||||||
r.bottom() + m.top(), r.width() - m.left() - m.right(), h );
|
return alignedLabelRect( skinnable, contentsRect, subControl, Qt::AlignBottom );
|
||||||
}
|
|
||||||
|
|
||||||
return QRectF();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( subControl == Q::TextPanel )
|
if ( subControl == Q::TextPanel )
|
||||||
{
|
return inputPanelRect( skinnable, contentsRect );
|
||||||
const auto rect = textField->subControlContentsRect( contentsRect, Q::Panel );
|
|
||||||
return inputPanelRect( textField, rect );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( subControl == Q::Text )
|
if ( subControl == Q::Text )
|
||||||
{
|
{
|
||||||
|
|
@ -85,7 +94,7 @@ QRectF QskTextFieldSkinlet::subControlRect( const QskSkinnable* skinnable,
|
||||||
|
|
||||||
if ( subControl == Q::Placeholder )
|
if ( subControl == Q::Placeholder )
|
||||||
{
|
{
|
||||||
if( textField->text().isEmpty() )
|
if ( hasText( skinnable, Q::Placeholder ) )
|
||||||
return subControlRect( skinnable, contentsRect, Q::Text );
|
return subControlRect( skinnable, contentsRect, Q::Text );
|
||||||
|
|
||||||
return QRectF();
|
return QRectF();
|
||||||
|
|
@ -142,45 +151,22 @@ QRectF QskTextFieldSkinlet::subControlRect( const QskSkinnable* skinnable,
|
||||||
QSGNode* QskTextFieldSkinlet::updateSubNode(
|
QSGNode* QskTextFieldSkinlet::updateSubNode(
|
||||||
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
|
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
|
||||||
{
|
{
|
||||||
const auto textField = static_cast< const QskTextField* >( skinnable );
|
|
||||||
|
|
||||||
switch ( nodeRole )
|
switch ( nodeRole )
|
||||||
{
|
{
|
||||||
case TextPanelRole:
|
case TextPanelRole:
|
||||||
{
|
return updateInputPanelNode( skinnable, node );
|
||||||
return updateBoxNode( skinnable, node, Q::TextPanel );
|
|
||||||
}
|
|
||||||
case PlaceholderRole:
|
case PlaceholderRole:
|
||||||
{
|
return updateLabelNode( skinnable, node, Q::Placeholder );
|
||||||
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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
case HeaderRole:
|
case HeaderRole:
|
||||||
{
|
return updateLabelNode( skinnable, node, Q::Header );
|
||||||
return updateTextNode( skinnable, node,
|
|
||||||
effectiveText( textField, Q::Header ), Q::Header );
|
|
||||||
}
|
|
||||||
|
|
||||||
case FooterRole:
|
case FooterRole:
|
||||||
{
|
return updateLabelNode( skinnable, node, Q::Footer );
|
||||||
return updateTextNode( skinnable, node,
|
|
||||||
effectiveText( textField, Q::Footer ), Q::Footer );
|
case CharacterCountRole:
|
||||||
}
|
return updateLabelNode( skinnable, node, Q::CharacterCount );
|
||||||
|
|
||||||
case IconRole:
|
case IconRole:
|
||||||
return updateSymbolNode( skinnable, node, Q::Icon );
|
return updateSymbolNode( skinnable, node, Q::Icon );
|
||||||
|
|
@ -195,35 +181,157 @@ QSGNode* QskTextFieldSkinlet::updateSubNode(
|
||||||
return Inherited::updateSubNode( skinnable, nodeRole, node );
|
return Inherited::updateSubNode( skinnable, nodeRole, node );
|
||||||
}
|
}
|
||||||
|
|
||||||
QRectF QskTextFieldSkinlet::inputPanelRect(
|
QSGNode* QskTextFieldSkinlet::updateInputPanelNode(
|
||||||
const QskTextField* textField, const QRectF& rect ) const
|
const QskSkinnable* skinnable, QSGNode* node ) const
|
||||||
{
|
{
|
||||||
qreal h = textField->effectiveFontHeight( Q::Text );
|
const auto control = static_cast< const QskControl* >( skinnable );
|
||||||
h = textField->outerBoxSize( Q::TextPanel, QSizeF( rect.width(), h ) ).height();
|
|
||||||
h = qMax( h, textField->strutSizeHint( Q::TextPanel ).height() );
|
|
||||||
|
|
||||||
/*
|
const auto rect = control->subControlRect( Q::TextPanel );
|
||||||
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( textField, Q::Header );
|
auto hints = skinnable->boxHints( Q::TextPanel );
|
||||||
auto bottom = textHeight( textField, Q::Footer );
|
|
||||||
|
|
||||||
if ( rect.height() < top + h + bottom )
|
if ( panelMode( skinnable ) == 2 )
|
||||||
{
|
{
|
||||||
if ( effectiveText( textField, Q::Footer ).isEmpty() )
|
const auto clipRect = control->subControlRect( Q::Header );
|
||||||
bottom = 0.0;
|
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 )
|
return updateBoxNode( skinnable, node, rect, hints );
|
||||||
{
|
}
|
||||||
if ( effectiveText( textField, Q::Header ).isEmpty() )
|
|
||||||
top = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
QSizeF QskTextFieldSkinlet::sizeHint( const QskSkinnable* skinnable,
|
||||||
|
|
@ -237,75 +345,161 @@ QSizeF QskTextFieldSkinlet::sizeHint( const QskSkinnable* skinnable,
|
||||||
const auto textField = static_cast< const QskTextField* >( skinnable );
|
const auto textField = static_cast< const QskTextField* >( skinnable );
|
||||||
|
|
||||||
auto hint = textField->unwrappedTextSize();
|
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() )
|
if( !skinnable->symbolHint( Q::Button ).isEmpty() )
|
||||||
{
|
{
|
||||||
const auto sz = skinnable->strutSizeHint( Q::Button );
|
const auto sz = skinnable->strutSizeHint( Q::Button );
|
||||||
if ( sz.width() > 0.0 )
|
if ( sz.width() > 0.0 )
|
||||||
hint.rwidth() += sz.width();
|
hint.rwidth() += sz.width();
|
||||||
|
|
||||||
|
hint.rheight() = qMax( hint.height(), sz.height() );
|
||||||
}
|
}
|
||||||
|
|
||||||
hint = skinnable->outerBoxSize( Q::TextPanel, hint );
|
switch( panelMode( skinnable ) )
|
||||||
hint = hint.expandedTo( skinnable->strutSizeHint( Q::TextPanel ) );
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
hint = skinnable->outerBoxSize( Q::TextPanel, hint );
|
||||||
|
hint = hint.expandedTo( skinnable->strutSizeHint( Q::TextPanel ) );
|
||||||
|
|
||||||
hint.rheight() += effectiveHeaderHeight( textField );
|
hint.rheight() += effectiveTextHeight( skinnable, Q::Header );
|
||||||
hint.rheight() += effectiveFooterHeight( textField );
|
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 = skinnable->outerBoxSize( Q::Panel, hint );
|
||||||
hint = hint.expandedTo( skinnable->strutSizeHint( Q::Panel ) );
|
return hint.expandedTo( skinnable->strutSizeHint( Q::Panel ) );
|
||||||
|
|
||||||
return hint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal QskTextFieldSkinlet::textHeight( const QskTextField* textField,
|
qreal QskTextFieldSkinlet::textHeight( const QskSkinnable* skinnable,
|
||||||
QskAspect::Subcontrol subControl ) const
|
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();
|
h += margins.top() + margins.bottom();
|
||||||
|
|
||||||
const auto sz = textField->strutSizeHint( subControl );
|
const auto sz = skinnable->strutSizeHint( subControl );
|
||||||
|
|
||||||
return qMax( h, sz.height() );
|
return qMax( h, sz.height() );
|
||||||
}
|
}
|
||||||
|
|
||||||
QString QskTextFieldSkinlet::effectiveText(
|
QSizeF QskTextFieldSkinlet::effectiveTextSize( const QskSkinnable* skinnable,
|
||||||
const QskTextField* textField, QskAspect::Subcontrol subcontrol ) const
|
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();
|
return textField->text();
|
||||||
|
|
||||||
if ( subcontrol == Q::Placeholder )
|
if ( subControl == Q::Placeholder )
|
||||||
{
|
return textField->placeholderText();
|
||||||
if ( textField->text().isEmpty() &&
|
|
||||||
!( textField->isReadOnly() || textField->isEditing() ) )
|
|
||||||
{
|
|
||||||
return textField->placeholderText();
|
|
||||||
}
|
|
||||||
|
|
||||||
return QString();
|
if ( subControl == Q::Header )
|
||||||
}
|
|
||||||
|
|
||||||
if ( subcontrol == Q::Header )
|
|
||||||
return textField->headerText();
|
return textField->headerText();
|
||||||
|
|
||||||
if ( subcontrol == Q::Footer )
|
if ( subControl == Q::Footer )
|
||||||
return textField->footerText();
|
return textField->footerText();
|
||||||
|
|
||||||
|
if ( subControl == Q::CharacterCount )
|
||||||
|
return qskEffectiveCharacterCountText( textField );
|
||||||
|
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal QskTextFieldSkinlet::effectiveHeaderHeight( const QskTextField* textField ) const
|
bool QskTextFieldSkinlet::isPlaceholderVisible(
|
||||||
|
const QskSkinnable* skinnable ) const
|
||||||
{
|
{
|
||||||
const auto text = effectiveText( textField, Q::Header );
|
return !skinnable->hasSkinState( Q::Editing )
|
||||||
return text.isEmpty() ? 0.0 : textHeight( textField, Q::Header );
|
&& 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 !effectiveText( skinnable, subControl ).isEmpty();
|
||||||
return text.isEmpty() ? 0.0 : textHeight( textField, Q::Footer );
|
}
|
||||||
|
|
||||||
|
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"
|
#include "moc_QskTextFieldSkinlet.cpp"
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ class QSK_EXPORT QskTextFieldSkinlet : public QskSkinlet
|
||||||
|
|
||||||
HeaderRole,
|
HeaderRole,
|
||||||
FooterRole,
|
FooterRole,
|
||||||
|
CharacterCountRole,
|
||||||
|
|
||||||
PlaceholderRole,
|
PlaceholderRole,
|
||||||
IconRole,
|
IconRole,
|
||||||
ButtonPanelRole,
|
ButtonPanelRole,
|
||||||
|
|
@ -31,9 +33,20 @@ class QSK_EXPORT QskTextFieldSkinlet : public QskSkinlet
|
||||||
RoleCount
|
RoleCount
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum RenderHint
|
||||||
|
{
|
||||||
|
UseHeaderAsPlaceholder = 1 << 0
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_ENUM( RenderHint )
|
||||||
|
Q_DECLARE_FLAGS( RenderHints, RenderHint )
|
||||||
|
|
||||||
Q_INVOKABLE QskTextFieldSkinlet( QskSkin* = nullptr );
|
Q_INVOKABLE QskTextFieldSkinlet( QskSkin* = nullptr );
|
||||||
~QskTextFieldSkinlet() override;
|
~QskTextFieldSkinlet() override;
|
||||||
|
|
||||||
|
void setRenderHints( RenderHints );
|
||||||
|
RenderHints renderHints() const;
|
||||||
|
|
||||||
QRectF subControlRect( const QskSkinnable*,
|
QRectF subControlRect( const QskSkinnable*,
|
||||||
const QRectF& rect, QskAspect::Subcontrol ) const override;
|
const QRectF& rect, QskAspect::Subcontrol ) const override;
|
||||||
|
|
||||||
|
|
@ -44,17 +57,35 @@ class QSK_EXPORT QskTextFieldSkinlet : public QskSkinlet
|
||||||
QSGNode* updateSubNode( const QskSkinnable*,
|
QSGNode* updateSubNode( const QskSkinnable*,
|
||||||
quint8 nodeRole, QSGNode* ) const override;
|
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;
|
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:
|
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;
|
QRectF alignedLabelRect( const QskSkinnable*, const QRectF&,
|
||||||
qreal effectiveHeaderHeight( const QskTextField* ) const;
|
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
|
#endif
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue