2024-12-03 16:52:39 +00:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) The authors
|
|
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include "QskMaterial3TextFieldSkinlet.h"
|
2025-02-07 10:35:36 +00:00
|
|
|
#include "QskMaterial3Skin.h"
|
|
|
|
|
|
|
|
#include <QskTextField.h>
|
|
|
|
#include <QskBoxBorderColors.h>
|
2025-03-10 14:01:29 +00:00
|
|
|
#include <QskBoxHints.h>
|
2025-02-07 10:35:36 +00:00
|
|
|
#include <QskFunctions.h>
|
|
|
|
|
|
|
|
#include <QFontMetricsF>
|
|
|
|
|
|
|
|
using Q = QskTextField;
|
|
|
|
|
2025-03-14 13:06:38 +00:00
|
|
|
static inline bool isOutlined( const QskTextField* textField )
|
|
|
|
{
|
|
|
|
return textField->effectiveVariation() == QskAspect::NoVariation;
|
|
|
|
}
|
|
|
|
|
2025-02-07 10:35:36 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2024-12-03 16:52:39 +00:00
|
|
|
|
|
|
|
QskMaterial3TextFieldSkinlet::QskMaterial3TextFieldSkinlet( QskSkin* skin )
|
|
|
|
: Inherited( skin )
|
|
|
|
{
|
2025-03-10 14:01:29 +00:00
|
|
|
appendNodeRoles( { CharacterCountRole } );
|
2024-12-03 16:52:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QskMaterial3TextFieldSkinlet::~QskMaterial3TextFieldSkinlet()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QRectF QskMaterial3TextFieldSkinlet::subControlRect( const QskSkinnable* skinnable,
|
|
|
|
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
|
|
|
|
{
|
2025-02-07 10:35:36 +00:00
|
|
|
const auto textField = static_cast< const Q* >( skinnable );
|
|
|
|
|
|
|
|
if ( subControl == Q::Header )
|
|
|
|
{
|
2025-03-10 14:01:29 +00:00
|
|
|
const auto text = effectiveText( textField, Q::Header );
|
2025-02-07 10:35:36 +00:00
|
|
|
if( text.isEmpty() )
|
|
|
|
return QRectF();
|
|
|
|
|
|
|
|
const QFontMetrics fm( textField->effectiveFont( Q::Header ) );
|
|
|
|
const auto textSize = fm.size( Qt::TextSingleLine | Qt::TextExpandTabs, text );
|
|
|
|
|
2025-03-14 13:06:38 +00:00
|
|
|
if ( isOutlined( textField ) )
|
2025-02-07 10:35:36 +00:00
|
|
|
{
|
2025-03-10 14:01:29 +00:00
|
|
|
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();
|
2025-02-07 10:35:36 +00:00
|
|
|
|
2025-03-10 14:01:29 +00:00
|
|
|
return QRectF( x, y, textSize.width(), textSize.height() );
|
2025-02-07 10:35:36 +00:00
|
|
|
}
|
2025-03-10 14:01:29 +00:00
|
|
|
else
|
2025-02-07 10:35:36 +00:00
|
|
|
{
|
2025-03-10 14:01:29 +00:00
|
|
|
const auto r = subControlRect( skinnable, contentsRect, Q::Text );
|
2025-02-07 10:35:36 +00:00
|
|
|
|
2025-03-10 14:01:29 +00:00
|
|
|
return QRectF( r.x(), r.top() - textSize.height(),
|
|
|
|
textSize.width(), textSize.height() );
|
2025-02-07 10:35:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-10 14:01:29 +00:00
|
|
|
if ( subControl == Q::TextPanel )
|
2025-02-07 10:35:36 +00:00
|
|
|
{
|
2025-03-10 14:01:29 +00:00
|
|
|
auto rect = skinnable->subControlRect( contentsRect, Q::Panel );
|
2025-02-07 10:35:36 +00:00
|
|
|
|
2025-03-14 13:06:38 +00:00
|
|
|
if ( isOutlined( textField ) )
|
2025-03-10 14:01:29 +00:00
|
|
|
{
|
|
|
|
const QFontMetrics fm( textField->effectiveFont( Q::Header ) );
|
|
|
|
rect.setTop( rect.top() + 0.5 * fm.height() );
|
2025-02-07 10:35:36 +00:00
|
|
|
}
|
|
|
|
|
2025-03-10 14:01:29 +00:00
|
|
|
const auto h = skinnable->strutSizeHint( Q::TextPanel ).height();
|
|
|
|
rect.setHeight( h );
|
|
|
|
|
|
|
|
return rect;
|
2025-02-07 10:35:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2024-12-03 16:52:39 +00:00
|
|
|
return Inherited::subControlRect( skinnable, contentsRect, subControl );
|
|
|
|
}
|
|
|
|
|
|
|
|
QSGNode* QskMaterial3TextFieldSkinlet::updateSubNode(
|
|
|
|
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
|
|
|
|
{
|
2025-02-07 10:35:36 +00:00
|
|
|
const auto textField = static_cast< const Q* >( skinnable );
|
|
|
|
|
|
|
|
switch ( nodeRole )
|
|
|
|
{
|
2025-03-10 14:01:29 +00:00
|
|
|
case TextPanelRole:
|
2025-02-07 10:35:36 +00:00
|
|
|
{
|
2025-03-10 14:01:29 +00:00
|
|
|
const auto rect = textField->subControlRect( Q::TextPanel );
|
|
|
|
|
|
|
|
auto hints = textField->boxHints( Q::TextPanel );
|
|
|
|
|
2025-03-14 13:06:38 +00:00
|
|
|
if ( isOutlined( textField ) )
|
2025-02-07 10:35:36 +00:00
|
|
|
{
|
2025-03-10 14:01:29 +00:00
|
|
|
const auto clipRect = textField->subControlRect( Q::Header );
|
2025-02-07 10:35:36 +00:00
|
|
|
if ( !clipRect.isEmpty() )
|
|
|
|
{
|
2025-03-10 14:01:29 +00:00
|
|
|
hints.borderColors = outlineColors(
|
|
|
|
hints.borderColors, rect, clipRect );
|
2025-02-07 10:35:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-10 14:01:29 +00:00
|
|
|
return updateBoxNode( skinnable, node, rect, hints );
|
2025-02-07 10:35:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case CharacterCountRole:
|
|
|
|
{
|
|
|
|
return updateTextNode( skinnable, node,
|
|
|
|
maxLengthString( textField ), Q::CharacterCount );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-03 16:52:39 +00:00
|
|
|
return Inherited::updateSubNode( skinnable, nodeRole, node );
|
|
|
|
}
|
|
|
|
|
2024-12-10 15:37:35 +00:00
|
|
|
QSizeF QskMaterial3TextFieldSkinlet::sizeHint( const QskSkinnable* skinnable,
|
|
|
|
Qt::SizeHint which, const QSizeF& constraint ) const
|
|
|
|
{
|
2025-03-10 14:01:29 +00:00
|
|
|
Q_UNUSED( constraint ); // TODO ...
|
|
|
|
|
2025-02-07 10:35:36 +00:00
|
|
|
if ( which != Qt::PreferredSize )
|
|
|
|
return QSizeF();
|
|
|
|
|
|
|
|
const auto textField = static_cast< const QskTextField* >( skinnable );
|
|
|
|
|
2025-03-10 14:01:29 +00:00
|
|
|
auto hint = textField->unwrappedTextSize();
|
|
|
|
hint = hint.expandedTo( skinnable->strutSizeHint( Q::TextPanel ) );
|
2025-02-07 10:35:36 +00:00
|
|
|
|
2025-03-14 13:06:38 +00:00
|
|
|
if ( isOutlined( textField ) )
|
2025-02-07 10:35:36 +00:00
|
|
|
{
|
2025-03-10 14:01:29 +00:00
|
|
|
const QFontMetrics fm( textField->effectiveFont( Q::Header ) );
|
|
|
|
hint.rheight() += 0.5 * fm.height();
|
2025-02-07 10:35:36 +00:00
|
|
|
}
|
|
|
|
|
2025-03-10 14:01:29 +00:00
|
|
|
hint.rheight() += effectiveFooterHeight( textField );
|
|
|
|
hint = hint.expandedTo( skinnable->strutSizeHint( Q::Panel ) );
|
|
|
|
|
2025-02-07 10:35:36 +00:00
|
|
|
return hint;
|
|
|
|
}
|
|
|
|
|
2025-03-10 14:01:29 +00:00
|
|
|
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 );
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal QskMaterial3TextFieldSkinlet::effectiveFooterHeight(
|
2025-02-07 10:35:36 +00:00
|
|
|
const QskTextField* textField ) const
|
|
|
|
{
|
2025-03-10 14:01:29 +00:00
|
|
|
if ( hasCharacterCount( textField ) )
|
2025-02-07 10:35:36 +00:00
|
|
|
{
|
2025-03-10 14:01:29 +00:00
|
|
|
const auto h = textField->effectiveFontHeight( Q::Footer );
|
|
|
|
const auto margins = textField->marginHint( Q::Footer );
|
2025-02-07 10:35:36 +00:00
|
|
|
|
2025-03-10 14:01:29 +00:00
|
|
|
return h + margins.top() + margins.bottom();
|
2025-02-07 10:35:36 +00:00
|
|
|
}
|
|
|
|
|
2025-03-10 14:01:29 +00:00
|
|
|
return Inherited::effectiveFooterHeight( textField );
|
2024-12-10 15:37:35 +00:00
|
|
|
}
|
|
|
|
|
2024-12-03 16:52:39 +00:00
|
|
|
#include "moc_QskMaterial3TextFieldSkinlet.cpp"
|