QskTextField/QskTextInput seperated
This commit is contained in:
parent
74d14b6005
commit
a91dcd5a56
@ -1776,14 +1776,14 @@ void Editor::setupTextFieldMetrics()
|
||||
{
|
||||
using Q = QskTextField;
|
||||
|
||||
setStrutSize( Q::Panel, { -1, 30_px } );
|
||||
setPadding( Q::Panel, { 11_px, 0, 11_px, 0 } );
|
||||
setStrutSize( Q::TextPanel, { -1, 30_px } );
|
||||
setPadding( Q::TextPanel, { 11_px, 0, 11_px, 0 } );
|
||||
|
||||
setBoxBorderMetrics( Q::Panel, 1_px );
|
||||
setBoxBorderMetrics( Q::TextPanel, 1_px );
|
||||
for( const auto& state : { Q::Focused, Q::Editing } )
|
||||
setBoxBorderMetrics( Q::Panel | state, { 1_px, 1_px, 1_px, 2_px } );
|
||||
setBoxBorderMetrics( Q::TextPanel | state, { 1_px, 1_px, 1_px, 2_px } );
|
||||
|
||||
setBoxShape( Q::Panel, 3_px );
|
||||
setBoxShape( Q::TextPanel, 3_px );
|
||||
|
||||
setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter );
|
||||
setFontRole( Q::Text, Fluent2::Body );
|
||||
@ -1801,7 +1801,7 @@ void Editor::setupTextFieldColors(
|
||||
|
||||
const auto& pal = theme.palette;
|
||||
|
||||
setColor( Q::Panel | SK::Selected, pal.fillColor.accent.selectedTextBackground );
|
||||
setColor( Q::TextPanel | SK::Selected, pal.fillColor.accent.selectedTextBackground );
|
||||
setColor( Q::Text | SK::Selected, pal.fillColor.textOnAccent.selectedText );
|
||||
setColor( Q::PlaceholderText, pal.fillColor.text.secondary );
|
||||
|
||||
@ -1837,7 +1837,7 @@ void Editor::setupTextFieldColors(
|
||||
textColor = pal.fillColor.text.disabled;
|
||||
}
|
||||
|
||||
const auto panel = Q::Panel | section | state;
|
||||
const auto panel = Q::TextPanel | section | state;
|
||||
const auto text = Q::Text | section | state;
|
||||
|
||||
panelColor = rgbSolid( panelColor, pal.background.solid.base );
|
||||
|
@ -397,25 +397,25 @@ void Editor::setupTextField()
|
||||
{
|
||||
const auto colorGroup = ( state == A::NoState ) ? P::Active : P::Disabled;
|
||||
|
||||
setColor( Q::Panel | state, m_pal.color( colorGroup, P::Base ) );
|
||||
setColor( Q::Panel | SK::Selected | state, m_pal.color( colorGroup, P::Highlight ) );
|
||||
setColor( Q::TextPanel | state, m_pal.color( colorGroup, P::Base ) );
|
||||
setColor( Q::TextPanel | SK::Selected | state, m_pal.color( colorGroup, P::Highlight ) );
|
||||
|
||||
setColor( Q::Text | state, m_pal.color( colorGroup, P::Text ) );
|
||||
setColor( Q::Text | SK::Selected | state, m_pal.color( colorGroup, P::HighlightedText ) );
|
||||
setColor( Q::PlaceholderText, m_pal.color( colorGroup, P::PlaceholderText ) );
|
||||
}
|
||||
|
||||
setColor( Q::Panel | Q::ReadOnly, m_pal.disabled( P::Base ) );
|
||||
setColor( Q::TextPanel | Q::ReadOnly, m_pal.disabled( P::Base ) );
|
||||
|
||||
setBoxBorderMetrics( Q::Panel, 1_px );
|
||||
setBoxBorderMetrics( Q::TextPanel, 1_px );
|
||||
|
||||
setBoxBorderColors( Q::Panel, m_pal.outline );
|
||||
setBoxBorderColors( Q::TextPanel, m_pal.outline );
|
||||
#ifdef SHOW_FOCUS
|
||||
setBoxBorderColors( Q::Panel | Q::Focused, m_pal.highlightedOutline );
|
||||
setBoxBorderColors( Q::TextPanel | Q::Focused, m_pal.highlightedOutline );
|
||||
#endif
|
||||
|
||||
setBoxShape( Q::Panel, 2_px );
|
||||
setPadding( Q::Panel, 4_px );
|
||||
setBoxShape( Q::TextPanel, 2_px );
|
||||
setPadding( Q::TextPanel, 4_px );
|
||||
}
|
||||
|
||||
void Editor::setupProgressBar()
|
||||
|
@ -434,12 +434,12 @@ void Editor::setupTextLabel()
|
||||
void Editor::setupTextField()
|
||||
{
|
||||
using Q = QskTextField;
|
||||
using SK = QskTextFieldSkinlet;
|
||||
using SK = QskTextInputSkinlet;
|
||||
|
||||
setStrutSize( Q::Panel, -1.0, 56_px );
|
||||
setPadding( Q::Panel, { 12_px, 8_px, 12_px, 8_px } );
|
||||
setGradient( Q::Panel, m_pal.surfaceVariant );
|
||||
setColor( Q::Panel | SK::Selected, m_pal.primary12 );
|
||||
setColor( Q::TextPanel | SK::Selected, m_pal.primary12 );
|
||||
setBoxShape( Q::Panel, m_pal.shapeExtraSmallTop );
|
||||
setBoxBorderMetrics( Q::Panel, { 0, 0, 0, 1_px } );
|
||||
setBoxBorderColors( Q::Panel, m_pal.onSurfaceVariant );
|
||||
|
@ -281,6 +281,8 @@ list(APPEND HEADERS
|
||||
controls/QskTabViewSkinlet.h
|
||||
controls/QskTextField.h
|
||||
controls/QskTextFieldSkinlet.h
|
||||
controls/QskTextInput.h
|
||||
controls/QskTextInputSkinlet.h
|
||||
controls/QskTextLabel.h
|
||||
controls/QskTextLabelSkinlet.h
|
||||
controls/QskVariantAnimator.h
|
||||
@ -387,6 +389,8 @@ list(APPEND SOURCES
|
||||
controls/QskTabViewSkinlet.cpp
|
||||
controls/QskTextField.cpp
|
||||
controls/QskTextFieldSkinlet.cpp
|
||||
controls/QskTextInput.cpp
|
||||
controls/QskTextInputSkinlet.cpp
|
||||
controls/QskTextLabel.cpp
|
||||
controls/QskTextLabelSkinlet.cpp
|
||||
controls/QskVariantAnimator.cpp
|
||||
|
@ -4,514 +4,32 @@
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskTextField.h"
|
||||
#include "QskTextFieldSkinlet.h"
|
||||
#include "QskFontRole.h"
|
||||
#include "QskQuick.h"
|
||||
|
||||
QSK_QT_PRIVATE_BEGIN
|
||||
#include <private/qquicktextinput_p.h>
|
||||
#include <private/qquicktextinput_p_p.h>
|
||||
QSK_QT_PRIVATE_END
|
||||
|
||||
QSK_SUBCONTROL( QskTextField, Panel )
|
||||
QSK_SUBCONTROL( QskTextField, Text )
|
||||
QSK_SUBCONTROL( QskTextField, PlaceholderText )
|
||||
|
||||
QSK_SYSTEM_STATE( QskTextField, ReadOnly, QskAspect::FirstSystemState << 1 )
|
||||
QSK_SYSTEM_STATE( QskTextField, Editing, QskAspect::FirstSystemState << 2 )
|
||||
|
||||
static inline void qskPropagateReadOnly( QskTextField* input )
|
||||
{
|
||||
Q_EMIT input->readOnlyChanged( input->isReadOnly() );
|
||||
|
||||
QEvent event( QEvent::ReadOnlyChange );
|
||||
QCoreApplication::sendEvent( input, &event );
|
||||
}
|
||||
|
||||
static inline void qskBindSignals(
|
||||
const QQuickTextInput* input, QskTextField* field )
|
||||
{
|
||||
QObject::connect( input, &QQuickTextInput::textChanged,
|
||||
field, [ field ] { Q_EMIT field->textChanged( field->text() ); } );
|
||||
|
||||
QObject::connect( input, &QQuickTextInput::displayTextChanged,
|
||||
field, [ field ] { Q_EMIT field->displayTextChanged( field->displayText() ); } );
|
||||
|
||||
QObject::connect( input, &QQuickTextInput::textEdited,
|
||||
field, [ field ] { Q_EMIT field->textEdited( field->text() ); } );
|
||||
|
||||
QObject::connect( input, &QQuickTextInput::validatorChanged,
|
||||
field, &QskTextField::validatorChanged );
|
||||
|
||||
QObject::connect( input, &QQuickTextInput::inputMaskChanged,
|
||||
field, &QskTextField::inputMaskChanged );
|
||||
|
||||
QObject::connect( input, &QQuickTextInput::readOnlyChanged,
|
||||
field, [ field ] { qskPropagateReadOnly( field ); } );
|
||||
|
||||
QObject::connect( input, &QQuickTextInput::overwriteModeChanged,
|
||||
field, &QskTextField::overwriteModeChanged );
|
||||
|
||||
QObject::connect( input, &QQuickTextInput::maximumLengthChanged,
|
||||
field, &QskTextField::maximumLengthChanged );
|
||||
|
||||
QObject::connect( input, &QQuickTextInput::wrapModeChanged,
|
||||
field, [ field ] { Q_EMIT field->wrapModeChanged( field->wrapMode() ); } );
|
||||
|
||||
QObject::connect( input, &QQuickTextInput::echoModeChanged,
|
||||
field, [ field ] { Q_EMIT field->echoModeChanged( field->echoMode() ); } );
|
||||
|
||||
QObject::connect( input, &QQuickTextInput::passwordCharacterChanged,
|
||||
field, &QskTextField::passwordCharacterChanged );
|
||||
|
||||
QObject::connect( input, &QQuickTextInput::passwordMaskDelayChanged,
|
||||
field, &QskTextField::passwordMaskDelayChanged );
|
||||
|
||||
QObject::connect( input, &QQuickItem::implicitWidthChanged,
|
||||
field, &QskControl::resetImplicitSize );
|
||||
|
||||
QObject::connect( input, &QQuickItem::implicitHeightChanged,
|
||||
field, &QskControl::resetImplicitSize );
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class TextInput final : public QQuickTextInput
|
||||
{
|
||||
using Inherited = QQuickTextInput;
|
||||
|
||||
public:
|
||||
TextInput( QskTextField* );
|
||||
|
||||
void setEditing( bool on );
|
||||
|
||||
inline void setAlignment( Qt::Alignment alignment )
|
||||
{
|
||||
setHAlign( ( HAlignment ) ( int( alignment ) & 0x0f ) );
|
||||
setVAlign( ( VAlignment ) ( int( alignment ) & 0xf0 ) );
|
||||
}
|
||||
|
||||
bool fixup()
|
||||
{
|
||||
return QQuickTextInputPrivate::get( this )->fixup();
|
||||
}
|
||||
|
||||
bool hasAcceptableInput() const
|
||||
{
|
||||
/*
|
||||
we would like to call QQuickTextInputPrivate::hasAcceptableInput
|
||||
but unfortunately it is private, so we need to hack somthing
|
||||
together
|
||||
*/
|
||||
|
||||
auto that = const_cast< TextInput* >( this );
|
||||
auto d = QQuickTextInputPrivate::get( that );
|
||||
|
||||
if ( d->m_validator )
|
||||
{
|
||||
QString text = displayText();
|
||||
int pos = d->m_cursor;
|
||||
|
||||
const auto state = d->m_validator->validate( text, pos );
|
||||
if ( state != QValidator::Acceptable )
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( d->m_maskData )
|
||||
{
|
||||
/*
|
||||
We only want to do the check for the maskData here
|
||||
and have to disable d->m_validator temporarily
|
||||
*/
|
||||
|
||||
class Validator final : public QValidator
|
||||
{
|
||||
public:
|
||||
State validate( QString&, int& ) const override
|
||||
{
|
||||
return QValidator::Acceptable;
|
||||
}
|
||||
};
|
||||
|
||||
const auto validator = d->m_validator;
|
||||
|
||||
const auto validInput = d->m_validInput;
|
||||
const auto acceptableInput = d->m_acceptableInput;
|
||||
|
||||
d->m_acceptableInput = true;
|
||||
|
||||
static Validator noValidator;
|
||||
that->setValidator( &noValidator ); // implicitly checking maskData
|
||||
that->setValidator( d->m_validator );
|
||||
|
||||
const bool isAcceptable = d->m_acceptableInput;
|
||||
|
||||
// restoring old values
|
||||
d->m_validInput = validInput;
|
||||
d->m_acceptableInput = acceptableInput;
|
||||
|
||||
return isAcceptable;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void updateColors();
|
||||
void updateMetrics();
|
||||
|
||||
inline bool handleEvent( QEvent* event )
|
||||
{
|
||||
return this->event( event );
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
|
||||
void geometryChange(
|
||||
const QRectF& newGeometry, const QRectF& oldGeometry ) override
|
||||
{
|
||||
Inherited::geometryChange( newGeometry, oldGeometry );
|
||||
updateClip();
|
||||
}
|
||||
#else
|
||||
void geometryChanged(
|
||||
const QRectF& newGeometry, const QRectF& oldGeometry ) override
|
||||
{
|
||||
Inherited::geometryChanged( newGeometry, oldGeometry );
|
||||
updateClip();
|
||||
}
|
||||
#endif
|
||||
|
||||
void updateClip()
|
||||
{
|
||||
setClip( ( contentWidth() > width() ) ||
|
||||
( contentHeight() > height() ) );
|
||||
}
|
||||
|
||||
QSGNode* updatePaintNode(
|
||||
QSGNode* oldNode, UpdatePaintNodeData* data ) override
|
||||
{
|
||||
updateColors();
|
||||
return Inherited::updatePaintNode( oldNode, data );
|
||||
}
|
||||
};
|
||||
|
||||
TextInput::TextInput( QskTextField* textField )
|
||||
: QQuickTextInput( textField )
|
||||
{
|
||||
classBegin();
|
||||
|
||||
setActiveFocusOnTab( false );
|
||||
setFlag( ItemAcceptsInputMethod, false );
|
||||
setFocusOnPress( false );
|
||||
|
||||
componentComplete();
|
||||
|
||||
connect( this, &TextInput::contentSizeChanged,
|
||||
this, &TextInput::updateClip );
|
||||
}
|
||||
|
||||
void TextInput::setEditing( bool on )
|
||||
{
|
||||
auto d = QQuickTextInputPrivate::get( this );
|
||||
|
||||
if ( d->cursorVisible == on )
|
||||
return;
|
||||
|
||||
setCursorVisible( on );
|
||||
d->setBlinkingCursorEnabled( on );
|
||||
|
||||
if ( !on )
|
||||
{
|
||||
if ( d->m_passwordEchoEditing || d->m_passwordEchoTimer.isActive() )
|
||||
d->updatePasswordEchoEditing( false );
|
||||
}
|
||||
|
||||
polish();
|
||||
update();
|
||||
}
|
||||
|
||||
void TextInput::updateMetrics()
|
||||
{
|
||||
auto textField = static_cast< const QskTextField* >( parentItem() );
|
||||
|
||||
setAlignment( textField->alignment() );
|
||||
setFont( textField->font() );
|
||||
}
|
||||
|
||||
void TextInput::updateColors()
|
||||
{
|
||||
auto textField = static_cast< const QskTextField* >( parentItem() );
|
||||
auto d = QQuickTextInputPrivate::get( this );
|
||||
|
||||
bool isDirty = false;
|
||||
|
||||
QColor color;
|
||||
|
||||
color = textField->color( QskTextField::Text );
|
||||
if ( d->color != color )
|
||||
{
|
||||
d->color = color;
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
if ( d->hasSelectedText() )
|
||||
{
|
||||
color = textField->color( QskTextField::Panel | QskTextFieldSkinlet::Selected );
|
||||
if ( d->selectionColor != color )
|
||||
{
|
||||
d->selectionColor = color;
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
color = textField->color( QskTextField::Text | QskTextFieldSkinlet::Selected );
|
||||
if ( d->selectedTextColor != color )
|
||||
{
|
||||
d->selectedTextColor = color;
|
||||
isDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isDirty )
|
||||
{
|
||||
d->textLayoutDirty = true;
|
||||
d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class QskTextField::PrivateData
|
||||
{
|
||||
public:
|
||||
TextInput* textInput;
|
||||
QString placeholderText;
|
||||
|
||||
unsigned int activationModes : 3;
|
||||
bool hasPanel : 1;
|
||||
};
|
||||
|
||||
QskTextField::QskTextField( QQuickItem* parent )
|
||||
: Inherited( parent )
|
||||
, m_data( new PrivateData() )
|
||||
{
|
||||
m_data->activationModes = ActivationOnMouse | ActivationOnKey;
|
||||
m_data->hasPanel = true;
|
||||
|
||||
setPolishOnResize( true );
|
||||
|
||||
setAcceptHoverEvents( true );
|
||||
setFocusPolicy( Qt::StrongFocus );
|
||||
|
||||
setFlag( QQuickItem::ItemAcceptsInputMethod );
|
||||
|
||||
/*
|
||||
QQuickTextInput is a beast of almost 5k lines of code, we don't
|
||||
want to reimplement that - at least not now.
|
||||
So this is more or less a simple wrapper making everything
|
||||
conforming to qskinny.
|
||||
*/
|
||||
|
||||
m_data->textInput = new TextInput( this );
|
||||
qskBindSignals( m_data->textInput, this );
|
||||
|
||||
setAcceptedMouseButtons( m_data->textInput->acceptedMouseButtons() );
|
||||
m_data->textInput->setAcceptedMouseButtons( Qt::NoButton );
|
||||
|
||||
initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Fixed );
|
||||
}
|
||||
|
||||
QskTextField::QskTextField( const QString& text, QQuickItem* parent )
|
||||
: QskTextField( parent )
|
||||
{
|
||||
m_data->textInput->setText( text );
|
||||
setText( text );
|
||||
}
|
||||
|
||||
QskTextField::~QskTextField()
|
||||
{
|
||||
}
|
||||
|
||||
void QskTextField::setPanel( bool on )
|
||||
{
|
||||
if ( on != m_data->hasPanel )
|
||||
{
|
||||
m_data->hasPanel = on;
|
||||
|
||||
resetImplicitSize();
|
||||
polish();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
bool QskTextField::hasPanel() const
|
||||
{
|
||||
return m_data->hasPanel;
|
||||
}
|
||||
|
||||
bool QskTextField::event( QEvent* event )
|
||||
{
|
||||
if ( event->type() == QEvent::ShortcutOverride )
|
||||
{
|
||||
return m_data->textInput->handleEvent( event );
|
||||
}
|
||||
else if ( event->type() == QEvent::LocaleChange )
|
||||
{
|
||||
qskUpdateInputMethod( this, Qt::ImPreferredLanguage );
|
||||
}
|
||||
|
||||
return Inherited::event( event );
|
||||
}
|
||||
|
||||
void QskTextField::keyPressEvent( QKeyEvent* event )
|
||||
{
|
||||
if ( isEditing() )
|
||||
{
|
||||
switch ( event->key() )
|
||||
{
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
{
|
||||
if ( hasAcceptableInput() || fixup() )
|
||||
{
|
||||
QGuiApplication::inputMethod()->commit();
|
||||
|
||||
if ( !( inputMethodHints() & Qt::ImhMultiLine ) )
|
||||
{
|
||||
setEditing( false );
|
||||
|
||||
// When returning from a virtual keyboard
|
||||
qskForceActiveFocus( this, Qt::PopupFocusReason );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#if 1
|
||||
case Qt::Key_Escape:
|
||||
{
|
||||
setEditing( false );
|
||||
qskForceActiveFocus( this, Qt::PopupFocusReason );
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
m_data->textInput->handleEvent( event );
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ( m_data->activationModes & ActivationOnKey ) && !event->isAutoRepeat() )
|
||||
{
|
||||
if ( event->key() == Qt::Key_Select || event->key() == Qt::Key_Space )
|
||||
{
|
||||
setEditing( true );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Inherited::keyPressEvent( event );
|
||||
}
|
||||
|
||||
void QskTextField::keyReleaseEvent( QKeyEvent* event )
|
||||
{
|
||||
Inherited::keyReleaseEvent( event );
|
||||
}
|
||||
|
||||
void QskTextField::mousePressEvent( QMouseEvent* event )
|
||||
{
|
||||
m_data->textInput->handleEvent( event );
|
||||
|
||||
if ( !isReadOnly() && !qGuiApp->styleHints()->setFocusOnTouchRelease() )
|
||||
setEditing( true );
|
||||
}
|
||||
|
||||
void QskTextField::mouseMoveEvent( QMouseEvent* event )
|
||||
{
|
||||
m_data->textInput->handleEvent( event );
|
||||
}
|
||||
|
||||
void QskTextField::mouseReleaseEvent( QMouseEvent* event )
|
||||
{
|
||||
m_data->textInput->handleEvent( event );
|
||||
|
||||
if ( !isReadOnly() && qGuiApp->styleHints()->setFocusOnTouchRelease() )
|
||||
setEditing( true );
|
||||
}
|
||||
|
||||
void QskTextField::mouseDoubleClickEvent( QMouseEvent* event )
|
||||
{
|
||||
m_data->textInput->handleEvent( event );
|
||||
}
|
||||
|
||||
void QskTextField::inputMethodEvent( QInputMethodEvent* event )
|
||||
{
|
||||
m_data->textInput->handleEvent( event );
|
||||
}
|
||||
|
||||
void QskTextField::focusInEvent( QFocusEvent* event )
|
||||
{
|
||||
if ( m_data->activationModes & ActivationOnFocus )
|
||||
{
|
||||
switch ( event->reason() )
|
||||
{
|
||||
case Qt::ActiveWindowFocusReason:
|
||||
case Qt::PopupFocusReason:
|
||||
break;
|
||||
|
||||
default:
|
||||
#if 1
|
||||
// auto selecting the complete text ???
|
||||
#endif
|
||||
setEditing( true );
|
||||
}
|
||||
}
|
||||
|
||||
Inherited::focusInEvent( event );
|
||||
}
|
||||
|
||||
void QskTextField::focusOutEvent( QFocusEvent* event )
|
||||
{
|
||||
switch ( event->reason() )
|
||||
{
|
||||
case Qt::ActiveWindowFocusReason:
|
||||
case Qt::PopupFocusReason:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
m_data->textInput->deselect();
|
||||
setEditing( false );
|
||||
}
|
||||
}
|
||||
|
||||
Inherited::focusOutEvent( event );
|
||||
}
|
||||
|
||||
void QskTextField::updateLayout()
|
||||
{
|
||||
m_data->textInput->updateMetrics();
|
||||
qskSetItemGeometry( m_data->textInput, subControlRect( Text ) );
|
||||
}
|
||||
|
||||
void QskTextField::updateNode( QSGNode* node )
|
||||
{
|
||||
m_data->textInput->updateColors();
|
||||
Inherited::updateNode( node );
|
||||
}
|
||||
|
||||
QString QskTextField::text() const
|
||||
{
|
||||
return m_data->textInput->text();
|
||||
}
|
||||
|
||||
void QskTextField::setText( const QString& text )
|
||||
{
|
||||
m_data->textInput->setText( text );
|
||||
}
|
||||
|
||||
void QskTextField::setPlaceholderText( const QString& text )
|
||||
{
|
||||
if ( m_data->placeholderText != text )
|
||||
@ -526,437 +44,4 @@ QString QskTextField::placeholderText() const
|
||||
return m_data->placeholderText;
|
||||
}
|
||||
|
||||
QskTextField::ActivationModes QskTextField::activationModes() const
|
||||
{
|
||||
return static_cast< QskTextField::ActivationModes >( m_data->activationModes );
|
||||
}
|
||||
|
||||
void QskTextField::setActivationModes( ActivationModes modes )
|
||||
{
|
||||
if ( static_cast< ActivationModes >( m_data->activationModes ) != modes )
|
||||
{
|
||||
m_data->activationModes = modes;
|
||||
Q_EMIT activationModesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void qskUpdateInputMethodFont( const QskTextField* input )
|
||||
{
|
||||
const auto queries = Qt::ImCursorRectangle | Qt::ImFont | Qt::ImAnchorRectangle;
|
||||
qskUpdateInputMethod( input, queries );
|
||||
}
|
||||
|
||||
void QskTextField::setFontRole( const QskFontRole& role )
|
||||
{
|
||||
if ( setFontRoleHint( Text, role ) )
|
||||
{
|
||||
qskUpdateInputMethodFont( this );
|
||||
Q_EMIT fontRoleChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void QskTextField::resetFontRole()
|
||||
{
|
||||
if ( resetFontRoleHint( Text ) )
|
||||
{
|
||||
qskUpdateInputMethodFont( this );
|
||||
Q_EMIT fontRoleChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QskFontRole QskTextField::fontRole() const
|
||||
{
|
||||
return fontRoleHint( Text );
|
||||
}
|
||||
|
||||
void QskTextField::setAlignment( Qt::Alignment alignment )
|
||||
{
|
||||
if ( setAlignmentHint( Text, alignment ) )
|
||||
{
|
||||
m_data->textInput->setAlignment( alignment );
|
||||
Q_EMIT alignmentChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void QskTextField::resetAlignment()
|
||||
{
|
||||
if ( resetAlignmentHint( Text ) )
|
||||
{
|
||||
m_data->textInput->setAlignment( alignment() );
|
||||
Q_EMIT alignmentChanged();
|
||||
}
|
||||
}
|
||||
|
||||
Qt::Alignment QskTextField::alignment() const
|
||||
{
|
||||
return alignmentHint( Text, Qt::AlignLeft | Qt::AlignTop );
|
||||
}
|
||||
|
||||
void QskTextField::setWrapMode( QskTextOptions::WrapMode wrapMode )
|
||||
{
|
||||
m_data->textInput->setWrapMode(
|
||||
static_cast< QQuickTextInput::WrapMode >( wrapMode ) );
|
||||
}
|
||||
|
||||
QskTextOptions::WrapMode QskTextField::wrapMode() const
|
||||
{
|
||||
return static_cast< QskTextOptions::WrapMode >(
|
||||
m_data->textInput->wrapMode() );
|
||||
}
|
||||
|
||||
QFont QskTextField::font() const
|
||||
{
|
||||
return effectiveFont( QskTextField::Text );
|
||||
}
|
||||
|
||||
bool QskTextField::isReadOnly() const
|
||||
{
|
||||
return m_data->textInput->isReadOnly();
|
||||
}
|
||||
|
||||
void QskTextField::setReadOnly( bool on )
|
||||
{
|
||||
auto input = m_data->textInput;
|
||||
|
||||
if ( input->isReadOnly() == on )
|
||||
return;
|
||||
|
||||
#if 1
|
||||
// do we want to be able to restore the previous policy ?
|
||||
setFocusPolicy( Qt::NoFocus );
|
||||
#endif
|
||||
|
||||
input->setReadOnly( on );
|
||||
|
||||
// we are killing user settings here ?
|
||||
input->setFlag( QQuickItem::ItemAcceptsInputMethod, !on );
|
||||
qskUpdateInputMethod( this, Qt::ImEnabled );
|
||||
|
||||
setSkinStateFlag( ReadOnly, on );
|
||||
}
|
||||
|
||||
void QskTextField::setEditing( bool on )
|
||||
{
|
||||
if ( isReadOnly() || on == isEditing() )
|
||||
return;
|
||||
|
||||
setSkinStateFlag( Editing, on );
|
||||
m_data->textInput->setEditing( on );
|
||||
|
||||
if ( on )
|
||||
{
|
||||
#if 0
|
||||
updateInputMethod( Qt::ImCursorRectangle | Qt::ImAnchorRectangle );
|
||||
QGuiApplication::inputMethod()->inputDirection
|
||||
#endif
|
||||
qskInputMethodSetVisible( this, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( hasAcceptableInput() || fixup() )
|
||||
Q_EMIT m_data->textInput->editingFinished();
|
||||
|
||||
#if 0
|
||||
inputMethod->reset();
|
||||
#endif
|
||||
qskInputMethodSetVisible( this, false );
|
||||
}
|
||||
|
||||
Q_EMIT editingChanged( on );
|
||||
}
|
||||
|
||||
bool QskTextField::isEditing() const
|
||||
{
|
||||
return hasSkinState( Editing );
|
||||
}
|
||||
|
||||
void QskTextField::ensureVisible( int position )
|
||||
{
|
||||
m_data->textInput->ensureVisible( position );
|
||||
}
|
||||
|
||||
int QskTextField::cursorPosition() const
|
||||
{
|
||||
return m_data->textInput->cursorPosition();
|
||||
}
|
||||
|
||||
void QskTextField::setCursorPosition( int pos )
|
||||
{
|
||||
m_data->textInput->setCursorPosition( pos );
|
||||
}
|
||||
|
||||
int QskTextField::maxLength() const
|
||||
{
|
||||
return m_data->textInput->maxLength();
|
||||
}
|
||||
|
||||
void QskTextField::setMaxLength( int length )
|
||||
{
|
||||
m_data->textInput->setMaxLength( length );
|
||||
}
|
||||
|
||||
QValidator* QskTextField::validator() const
|
||||
{
|
||||
return m_data->textInput->validator();
|
||||
}
|
||||
|
||||
void QskTextField::setValidator( QValidator* validator )
|
||||
{
|
||||
m_data->textInput->setValidator( validator );
|
||||
}
|
||||
|
||||
QString QskTextField::inputMask() const
|
||||
{
|
||||
return m_data->textInput->inputMask();
|
||||
}
|
||||
|
||||
void QskTextField::setInputMask( const QString& mask )
|
||||
{
|
||||
m_data->textInput->setInputMask( mask );
|
||||
}
|
||||
|
||||
QskTextField::EchoMode QskTextField::echoMode() const
|
||||
{
|
||||
const auto mode = m_data->textInput->echoMode();
|
||||
return static_cast< QskTextField::EchoMode >( mode );
|
||||
}
|
||||
|
||||
void QskTextField::setEchoMode( EchoMode mode )
|
||||
{
|
||||
if ( mode != echoMode() )
|
||||
{
|
||||
m_data->textInput->setEchoMode(
|
||||
static_cast< QQuickTextInput::EchoMode >( mode ) );
|
||||
|
||||
qskUpdateInputMethod( this, Qt::ImHints );
|
||||
}
|
||||
}
|
||||
|
||||
QString QskTextField::passwordCharacter() const
|
||||
{
|
||||
return m_data->textInput->passwordCharacter();
|
||||
}
|
||||
|
||||
void QskTextField::setPasswordCharacter( const QString& text )
|
||||
{
|
||||
m_data->textInput->setPasswordCharacter( text );
|
||||
}
|
||||
|
||||
void QskTextField::resetPasswordCharacter()
|
||||
{
|
||||
m_data->textInput->setPasswordCharacter(
|
||||
QGuiApplication::styleHints()->passwordMaskCharacter() );
|
||||
}
|
||||
|
||||
int QskTextField::passwordMaskDelay() const
|
||||
{
|
||||
return m_data->textInput->passwordMaskDelay();
|
||||
}
|
||||
|
||||
void QskTextField::setPasswordMaskDelay( int ms )
|
||||
{
|
||||
m_data->textInput->setPasswordMaskDelay( ms );
|
||||
}
|
||||
|
||||
void QskTextField::resetPasswordMaskDelay()
|
||||
{
|
||||
m_data->textInput->resetPasswordMaskDelay();
|
||||
}
|
||||
|
||||
QString QskTextField::displayText() const
|
||||
{
|
||||
return m_data->textInput->displayText();
|
||||
}
|
||||
|
||||
QString QskTextField::preeditText() const
|
||||
{
|
||||
const auto d = QQuickTextInputPrivate::get( m_data->textInput );
|
||||
return d->m_textLayout.preeditAreaText();
|
||||
}
|
||||
|
||||
bool QskTextField::overwriteMode() const
|
||||
{
|
||||
return m_data->textInput->overwriteMode();
|
||||
}
|
||||
|
||||
void QskTextField::setOverwriteMode( bool overwrite )
|
||||
{
|
||||
m_data->textInput->setOverwriteMode( overwrite );
|
||||
}
|
||||
|
||||
bool QskTextField::hasAcceptableInput() const
|
||||
{
|
||||
return m_data->textInput->hasAcceptableInput();
|
||||
}
|
||||
|
||||
bool QskTextField::fixup()
|
||||
{
|
||||
return m_data->textInput->fixup();
|
||||
}
|
||||
|
||||
QVariant QskTextField::inputMethodQuery(
|
||||
Qt::InputMethodQuery property ) const
|
||||
{
|
||||
return inputMethodQuery( property, QVariant() );
|
||||
}
|
||||
|
||||
QVariant QskTextField::inputMethodQuery(
|
||||
Qt::InputMethodQuery query, const QVariant& argument ) const
|
||||
{
|
||||
switch ( query )
|
||||
{
|
||||
case Qt::ImEnabled:
|
||||
{
|
||||
return QVariant( ( bool ) ( flags() & ItemAcceptsInputMethod ) );
|
||||
}
|
||||
case Qt::ImFont:
|
||||
{
|
||||
return font();
|
||||
}
|
||||
case Qt::ImPreferredLanguage:
|
||||
{
|
||||
return locale();
|
||||
}
|
||||
case Qt::ImInputItemClipRectangle:
|
||||
case Qt::ImCursorRectangle:
|
||||
{
|
||||
QVariant v = m_data->textInput->inputMethodQuery( query, argument );
|
||||
#if 1
|
||||
if ( v.canConvert< QRectF >() )
|
||||
v.setValue( v.toRectF().translated( m_data->textInput->position() ) );
|
||||
#endif
|
||||
return v;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return m_data->textInput->inputMethodQuery( query, argument );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QskTextField::canUndo() const
|
||||
{
|
||||
return m_data->textInput->canUndo();
|
||||
}
|
||||
|
||||
bool QskTextField::canRedo() const
|
||||
{
|
||||
return m_data->textInput->canRedo();
|
||||
}
|
||||
|
||||
Qt::InputMethodHints QskTextField::inputMethodHints() const
|
||||
{
|
||||
return m_data->textInput->inputMethodHints();
|
||||
}
|
||||
|
||||
void QskTextField::setInputMethodHints( Qt::InputMethodHints hints )
|
||||
{
|
||||
if ( m_data->textInput->inputMethodHints() != hints )
|
||||
{
|
||||
m_data->textInput->setInputMethodHints( hints );
|
||||
qskUpdateInputMethod( this, Qt::ImHints );
|
||||
}
|
||||
}
|
||||
|
||||
void QskTextField::setupFrom( const QQuickItem* item )
|
||||
{
|
||||
if ( item == nullptr )
|
||||
return;
|
||||
|
||||
// finding attributes from the input hints of item
|
||||
|
||||
int maxCharacters = 32767;
|
||||
QskTextField::EchoMode echoMode = QskTextField::Normal;
|
||||
|
||||
Qt::InputMethodQueries queries = Qt::ImQueryAll;
|
||||
queries &= ~Qt::ImEnabled;
|
||||
|
||||
QInputMethodQueryEvent event( queries );
|
||||
QCoreApplication::sendEvent( const_cast< QQuickItem* >( item ), &event );
|
||||
|
||||
if ( event.queries() & Qt::ImHints )
|
||||
{
|
||||
const auto hints = static_cast< Qt::InputMethodHints >(
|
||||
event.value( Qt::ImHints ).toInt() );
|
||||
|
||||
if ( hints & Qt::ImhHiddenText )
|
||||
echoMode = QskTextField::NoEcho;
|
||||
}
|
||||
|
||||
if ( event.queries() & Qt::ImMaximumTextLength )
|
||||
{
|
||||
// needs to be handled before Qt::ImCursorPosition !
|
||||
|
||||
const auto max = event.value( Qt::ImMaximumTextLength ).toInt();
|
||||
maxCharacters = qBound( 0, max, maxCharacters );
|
||||
}
|
||||
|
||||
setMaxLength( maxCharacters );
|
||||
|
||||
if ( event.queries() & Qt::ImSurroundingText )
|
||||
{
|
||||
const auto text = event.value( Qt::ImSurroundingText ).toString();
|
||||
setText( text );
|
||||
}
|
||||
|
||||
if ( event.queries() & Qt::ImCursorPosition )
|
||||
{
|
||||
const auto pos = event.value( Qt::ImCursorPosition ).toInt();
|
||||
setCursorPosition( pos );
|
||||
}
|
||||
|
||||
if ( event.queries() & Qt::ImCurrentSelection )
|
||||
{
|
||||
#if 0
|
||||
const auto text = event.value( Qt::ImCurrentSelection ).toString();
|
||||
if ( !text.isEmpty() )
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int passwordMaskDelay = -1;
|
||||
QString passwordCharacter;
|
||||
|
||||
if ( echoMode == QskTextField::NoEcho )
|
||||
{
|
||||
/*
|
||||
Qt::ImhHiddenText does not provide information
|
||||
to decide between NoEcho/Password, or provides
|
||||
more details about how to deal with hidden inputs.
|
||||
So we try to find out more from trying some properties.
|
||||
*/
|
||||
|
||||
QVariant value;
|
||||
|
||||
value = item->property( "passwordMaskDelay" );
|
||||
if ( value.canConvert< int >() )
|
||||
passwordMaskDelay = value.toInt();
|
||||
|
||||
value = item->property( "passwordCharacter" );
|
||||
if ( value.canConvert< QString >() )
|
||||
passwordCharacter = value.toString();
|
||||
|
||||
value = item->property( "echoMode" );
|
||||
if ( value.canConvert< int >() )
|
||||
{
|
||||
const auto mode = value.toInt();
|
||||
if ( mode == QskTextField::Password )
|
||||
echoMode = QskTextField::Password;
|
||||
}
|
||||
}
|
||||
|
||||
if ( passwordMaskDelay >= 0 )
|
||||
setPasswordMaskDelay( passwordMaskDelay );
|
||||
else
|
||||
resetPasswordMaskDelay();
|
||||
|
||||
if ( !passwordCharacter.isEmpty() )
|
||||
setPasswordCharacter( passwordCharacter );
|
||||
else
|
||||
resetPasswordCharacter();
|
||||
|
||||
setEchoMode( echoMode );
|
||||
}
|
||||
|
||||
#include "moc_QskTextField.cpp"
|
||||
|
@ -6,218 +6,34 @@
|
||||
#ifndef QSK_TEXT_FIELD_H
|
||||
#define QSK_TEXT_FIELD_H
|
||||
|
||||
#include "QskControl.h"
|
||||
#include "QskTextOptions.h"
|
||||
#include "QskTextInput.h"
|
||||
|
||||
class QValidator;
|
||||
class QskFontRole;
|
||||
|
||||
class QSK_EXPORT QskTextField : public QskControl
|
||||
class QSK_EXPORT QskTextField : public QskTextInput
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY( QString text READ text WRITE setText NOTIFY textChanged USER true)
|
||||
|
||||
Q_PROPERTY( QString placeholderText READ placeholderText
|
||||
WRITE setPlaceholderText NOTIFY placeholderTextChanged )
|
||||
|
||||
Q_PROPERTY( QskFontRole fontRole READ fontRole
|
||||
WRITE setFontRole RESET resetFontRole NOTIFY fontRoleChanged )
|
||||
|
||||
Q_PROPERTY( QFont font READ font )
|
||||
|
||||
Q_PROPERTY( Qt::Alignment alignment READ alignment
|
||||
WRITE setAlignment RESET resetAlignment NOTIFY alignmentChanged )
|
||||
|
||||
Q_PROPERTY( QskTextOptions::WrapMode wrapMode READ wrapMode
|
||||
WRITE setWrapMode NOTIFY wrapModeChanged )
|
||||
|
||||
Q_PROPERTY( ActivationModes activationModes READ activationModes
|
||||
WRITE setActivationModes NOTIFY activationModesChanged )
|
||||
|
||||
Q_PROPERTY( bool editing READ isEditing
|
||||
WRITE setEditing NOTIFY editingChanged )
|
||||
|
||||
Q_PROPERTY( EchoMode echoMode READ echoMode
|
||||
WRITE setEchoMode NOTIFY echoModeChanged )
|
||||
|
||||
Q_PROPERTY( QString passwordCharacter READ passwordCharacter
|
||||
WRITE setPasswordCharacter RESET resetPasswordCharacter
|
||||
NOTIFY passwordCharacterChanged )
|
||||
|
||||
Q_PROPERTY( int passwordMaskDelay READ passwordMaskDelay
|
||||
WRITE setPasswordMaskDelay RESET resetPasswordMaskDelay
|
||||
NOTIFY passwordMaskDelayChanged )
|
||||
|
||||
Q_PROPERTY( bool panel READ hasPanel
|
||||
WRITE setPanel NOTIFY panelChanged )
|
||||
|
||||
using Inherited = QskControl;
|
||||
using Inherited = QskTextInput;
|
||||
|
||||
public:
|
||||
QSK_SUBCONTROLS( Panel, Text, PlaceholderText )
|
||||
QSK_STATES( ReadOnly, Editing )
|
||||
|
||||
enum ActivationMode : quint8
|
||||
{
|
||||
NoActivation,
|
||||
|
||||
ActivationOnFocus = 1 << 0,
|
||||
ActivationOnMouse = 1 << 1,
|
||||
ActivationOnKey = 1 << 2,
|
||||
|
||||
ActivationOnInput = ActivationOnMouse | ActivationOnKey,
|
||||
ActivationOnAll = ActivationOnFocus | ActivationOnMouse | ActivationOnKey
|
||||
};
|
||||
|
||||
Q_ENUM( ActivationMode )
|
||||
Q_DECLARE_FLAGS( ActivationModes, ActivationMode )
|
||||
|
||||
enum EchoMode : quint8
|
||||
{
|
||||
Normal,
|
||||
NoEcho,
|
||||
Password,
|
||||
PasswordEchoOnEdit
|
||||
};
|
||||
|
||||
Q_ENUM( EchoMode )
|
||||
QSK_SUBCONTROLS( Panel, PlaceholderText )
|
||||
|
||||
QskTextField( QQuickItem* parent = nullptr );
|
||||
QskTextField( const QString& text, QQuickItem* parent = nullptr );
|
||||
|
||||
~QskTextField() override;
|
||||
|
||||
void setupFrom( const QQuickItem* );
|
||||
|
||||
QString text() const;
|
||||
|
||||
void setPlaceholderText( const QString& );
|
||||
QString placeholderText() const;
|
||||
|
||||
void setPanel( bool );
|
||||
bool hasPanel() const;
|
||||
|
||||
void setFontRole( const QskFontRole& role );
|
||||
void resetFontRole();
|
||||
QskFontRole fontRole() const;
|
||||
|
||||
void setAlignment( Qt::Alignment );
|
||||
void resetAlignment();
|
||||
Qt::Alignment alignment() const;
|
||||
|
||||
void setWrapMode( QskTextOptions::WrapMode );
|
||||
QskTextOptions::WrapMode wrapMode() const;
|
||||
|
||||
void setActivationModes( ActivationModes );
|
||||
ActivationModes activationModes() const;
|
||||
|
||||
bool isEditing() const;
|
||||
|
||||
QFont font() const;
|
||||
|
||||
bool isReadOnly() const;
|
||||
void setReadOnly( bool );
|
||||
|
||||
int cursorPosition() const;
|
||||
void setCursorPosition( int );
|
||||
|
||||
int maxLength() const;
|
||||
void setMaxLength( int );
|
||||
|
||||
QValidator* validator() const;
|
||||
void setValidator( QValidator* );
|
||||
|
||||
QString inputMask() const;
|
||||
void setInputMask( const QString& );
|
||||
|
||||
EchoMode echoMode() const;
|
||||
void setEchoMode( EchoMode );
|
||||
|
||||
QString passwordCharacter() const;
|
||||
void setPasswordCharacter( const QString& );
|
||||
void resetPasswordCharacter();
|
||||
|
||||
int passwordMaskDelay() const;
|
||||
void setPasswordMaskDelay( int );
|
||||
void resetPasswordMaskDelay();
|
||||
|
||||
QString displayText() const;
|
||||
QString preeditText() const;
|
||||
|
||||
bool overwriteMode() const;
|
||||
void setOverwriteMode( bool );
|
||||
|
||||
bool hasAcceptableInput() const;
|
||||
bool fixup();
|
||||
|
||||
QVariant inputMethodQuery( Qt::InputMethodQuery ) const override;
|
||||
QVariant inputMethodQuery( Qt::InputMethodQuery, const QVariant& argument ) const;
|
||||
|
||||
bool canUndo() const;
|
||||
bool canRedo() const;
|
||||
|
||||
Qt::InputMethodHints inputMethodHints() const;
|
||||
void setInputMethodHints( Qt::InputMethodHints );
|
||||
|
||||
void ensureVisible( int position );
|
||||
|
||||
public Q_SLOTS:
|
||||
void setText( const QString& );
|
||||
void setEditing( bool );
|
||||
|
||||
Q_SIGNALS:
|
||||
void editingChanged( bool );
|
||||
|
||||
void activationModesChanged();
|
||||
void readOnlyChanged( bool );
|
||||
void panelChanged( bool );
|
||||
|
||||
void textChanged( const QString& );
|
||||
void displayTextChanged( const QString& );
|
||||
|
||||
void textEdited( const QString& );
|
||||
void placeholderTextChanged( const QString& );
|
||||
|
||||
void fontRoleChanged();
|
||||
void alignmentChanged();
|
||||
void wrapModeChanged( QskTextOptions::WrapMode );
|
||||
|
||||
void overwriteModeChanged( bool );
|
||||
void maximumLengthChanged( int );
|
||||
|
||||
void echoModeChanged( EchoMode );
|
||||
void passwordMaskDelayChanged();
|
||||
void passwordCharacterChanged();
|
||||
|
||||
void validatorChanged();
|
||||
void inputMaskChanged( const QString& );
|
||||
|
||||
protected:
|
||||
bool event( QEvent* ) override;
|
||||
|
||||
void inputMethodEvent( QInputMethodEvent* ) override;
|
||||
|
||||
void focusInEvent( QFocusEvent* ) override;
|
||||
void focusOutEvent( QFocusEvent* ) override;
|
||||
|
||||
void mousePressEvent( QMouseEvent* ) override;
|
||||
void mouseMoveEvent( QMouseEvent* ) override;
|
||||
void mouseReleaseEvent( QMouseEvent* ) override;
|
||||
void mouseDoubleClickEvent( QMouseEvent* ) override;
|
||||
|
||||
void keyPressEvent( QKeyEvent* ) override;
|
||||
void keyReleaseEvent( QKeyEvent* ) override;
|
||||
|
||||
void updateLayout() override;
|
||||
void updateNode( QSGNode* ) override;
|
||||
|
||||
private:
|
||||
class PrivateData;
|
||||
std::unique_ptr< PrivateData > m_data;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS( QskTextField::ActivationModes )
|
||||
Q_DECLARE_METATYPE( QskTextField::ActivationModes )
|
||||
|
||||
#endif
|
||||
|
@ -6,27 +6,12 @@
|
||||
#include "QskTextFieldSkinlet.h"
|
||||
#include "QskTextField.h"
|
||||
|
||||
#include <qfontmetrics.h>
|
||||
|
||||
using Q = QskTextField;
|
||||
|
||||
QSK_SYSTEM_STATE( QskTextFieldSkinlet, Selected, QskAspect::FirstSystemState << 3 )
|
||||
|
||||
static QString qskEffectivePlaceholderText( const QskTextField* textField )
|
||||
{
|
||||
if ( textField->text().isEmpty() &&
|
||||
!( textField->isReadOnly() || textField->isEditing() ) )
|
||||
{
|
||||
return textField->placeholderText();
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QskTextFieldSkinlet::QskTextFieldSkinlet( QskSkin* skin )
|
||||
: Inherited( skin )
|
||||
{
|
||||
setNodeRoles( { PanelRole, PlaceholderTextRole, } );
|
||||
setNodeRoles( { PanelRole, TextPanelRole, PlaceholderTextRole } );
|
||||
}
|
||||
|
||||
QskTextFieldSkinlet::~QskTextFieldSkinlet()
|
||||
@ -37,14 +22,12 @@ QRectF QskTextFieldSkinlet::subControlRect( const QskSkinnable* skinnable,
|
||||
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
|
||||
{
|
||||
if ( subControl == Q::Panel )
|
||||
{
|
||||
return contentsRect;
|
||||
}
|
||||
else if ( subControl == Q::Text )
|
||||
{
|
||||
|
||||
if ( subControl == Q::TextPanel )
|
||||
return skinnable->subControlContentsRect( contentsRect, Q::Panel );
|
||||
}
|
||||
else if ( subControl == Q::PlaceholderText )
|
||||
|
||||
if ( subControl == Q::PlaceholderText )
|
||||
{
|
||||
const auto textField = static_cast< const QskTextField* >( skinnable );
|
||||
if( textField->text().isEmpty() )
|
||||
@ -65,14 +48,12 @@ QSGNode* QskTextFieldSkinlet::updateSubNode(
|
||||
{
|
||||
case PanelRole:
|
||||
{
|
||||
if ( !textField->hasPanel() )
|
||||
return nullptr;
|
||||
|
||||
return updateBoxNode( skinnable, node, Q::Panel );
|
||||
}
|
||||
|
||||
case PlaceholderTextRole:
|
||||
{
|
||||
const auto text = qskEffectivePlaceholderText( textField );
|
||||
const auto text = effectivePlaceholderText( textField );
|
||||
if ( text.isEmpty() )
|
||||
return nullptr;
|
||||
|
||||
@ -95,24 +76,28 @@ QSGNode* QskTextFieldSkinlet::updateSubNode(
|
||||
}
|
||||
|
||||
QSizeF QskTextFieldSkinlet::sizeHint( const QskSkinnable* skinnable,
|
||||
Qt::SizeHint which, const QSizeF& ) const
|
||||
Qt::SizeHint which, const QSizeF& constraint ) const
|
||||
{
|
||||
if ( which != Qt::PreferredSize )
|
||||
return QSizeF();
|
||||
|
||||
const auto textField = static_cast< const QskTextField* >( skinnable );
|
||||
|
||||
const QFontMetricsF fm( textField->effectiveFont( Q::Text ) );
|
||||
|
||||
auto hint = fm.size( Qt::TextSingleLine | Qt::TextExpandTabs, textField->text() );
|
||||
|
||||
if ( textField->hasPanel() )
|
||||
{
|
||||
hint = textField->outerBoxSize( Q::Panel, hint );
|
||||
hint = hint.expandedTo( textField->strutSizeHint( Q::Panel ) );
|
||||
}
|
||||
auto hint = Inherited::sizeHint( skinnable, which, constraint );
|
||||
hint = skinnable->outerBoxSize( Q::Panel, hint );
|
||||
hint = hint.expandedTo( skinnable->strutSizeHint( Q::Panel ) );
|
||||
|
||||
return hint;
|
||||
}
|
||||
|
||||
QString QskTextFieldSkinlet::effectivePlaceholderText(
|
||||
const QskTextField* textField ) const
|
||||
{
|
||||
if ( textField->text().isEmpty() &&
|
||||
!( textField->isReadOnly() || textField->isEditing() ) )
|
||||
{
|
||||
return textField->placeholderText();
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
#include "moc_QskTextFieldSkinlet.cpp"
|
||||
|
@ -6,20 +6,21 @@
|
||||
#ifndef QSK_TEXT_FIELD_SKINLET_H
|
||||
#define QSK_TEXT_FIELD_SKINLET_H
|
||||
|
||||
#include "QskSkinlet.h"
|
||||
#include "QskTextInputSkinlet.h"
|
||||
|
||||
class QSK_EXPORT QskTextFieldSkinlet : public QskSkinlet
|
||||
class QskTextField;
|
||||
|
||||
class QSK_EXPORT QskTextFieldSkinlet : public QskTextInputSkinlet
|
||||
{
|
||||
Q_GADGET
|
||||
|
||||
using Inherited = QskSkinlet;
|
||||
using Inherited = QskTextInputSkinlet;
|
||||
|
||||
public:
|
||||
QSK_STATES( Selected )
|
||||
|
||||
enum NodeRole : quint8
|
||||
{
|
||||
PanelRole,
|
||||
PanelRole = QskTextInputSkinlet::RoleCount,
|
||||
|
||||
PlaceholderTextRole,
|
||||
RoleCount
|
||||
};
|
||||
@ -36,6 +37,8 @@ class QSK_EXPORT QskTextFieldSkinlet : public QskSkinlet
|
||||
protected:
|
||||
QSGNode* updateSubNode( const QskSkinnable*,
|
||||
quint8 nodeRole, QSGNode* ) const override;
|
||||
|
||||
virtual QString effectivePlaceholderText( const QskTextField* ) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
917
src/controls/QskTextInput.cpp
Normal file
917
src/controls/QskTextInput.cpp
Normal file
@ -0,0 +1,917 @@
|
||||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskTextInput.h"
|
||||
#include "QskTextInputSkinlet.h"
|
||||
#include "QskFontRole.h"
|
||||
#include "QskQuick.h"
|
||||
|
||||
QSK_QT_PRIVATE_BEGIN
|
||||
#include <private/qquicktextinput_p.h>
|
||||
#include <private/qquicktextinput_p_p.h>
|
||||
QSK_QT_PRIVATE_END
|
||||
|
||||
QSK_SUBCONTROL( QskTextInput, Text )
|
||||
QSK_SUBCONTROL( QskTextInput, TextPanel )
|
||||
|
||||
QSK_SYSTEM_STATE( QskTextInput, ReadOnly, QskAspect::FirstSystemState << 1 )
|
||||
QSK_SYSTEM_STATE( QskTextInput, Editing, QskAspect::FirstSystemState << 2 )
|
||||
QSK_SYSTEM_STATE( QskTextInput, Error, QskAspect::FirstSystemState << 4 )
|
||||
|
||||
static inline void qskPropagateReadOnly( QskTextInput* input )
|
||||
{
|
||||
Q_EMIT input->readOnlyChanged( input->isReadOnly() );
|
||||
|
||||
QEvent event( QEvent::ReadOnlyChange );
|
||||
QCoreApplication::sendEvent( input, &event );
|
||||
}
|
||||
|
||||
static inline void qskBindSignals(
|
||||
const QQuickTextInput* wrappedInput, QskTextInput* input )
|
||||
{
|
||||
QObject::connect( wrappedInput, &QQuickTextInput::textChanged,
|
||||
input, [ input ] { Q_EMIT input->textChanged( input->text() ); } );
|
||||
|
||||
QObject::connect( wrappedInput, &QQuickTextInput::displayTextChanged,
|
||||
input, [ input ] { Q_EMIT input->displayTextChanged( input->displayText() ); } );
|
||||
|
||||
QObject::connect( wrappedInput, &QQuickTextInput::textEdited,
|
||||
input, [ input ] { Q_EMIT input->textEdited( input->text() ); } );
|
||||
|
||||
QObject::connect( wrappedInput, &QQuickTextInput::validatorChanged,
|
||||
input, &QskTextInput::validatorChanged );
|
||||
|
||||
QObject::connect( wrappedInput, &QQuickTextInput::inputMaskChanged,
|
||||
input, &QskTextInput::inputMaskChanged );
|
||||
|
||||
QObject::connect( wrappedInput, &QQuickTextInput::readOnlyChanged,
|
||||
input, [ input ] { qskPropagateReadOnly( input ); } );
|
||||
|
||||
QObject::connect( wrappedInput, &QQuickTextInput::overwriteModeChanged,
|
||||
input, &QskTextInput::overwriteModeChanged );
|
||||
|
||||
QObject::connect( wrappedInput, &QQuickTextInput::maximumLengthChanged,
|
||||
input, &QskTextInput::maximumLengthChanged );
|
||||
|
||||
QObject::connect( wrappedInput, &QQuickTextInput::wrapModeChanged,
|
||||
input, [ input ] { Q_EMIT input->wrapModeChanged( input->wrapMode() ); } );
|
||||
|
||||
QObject::connect( wrappedInput, &QQuickTextInput::echoModeChanged,
|
||||
input, [ input ] { Q_EMIT input->echoModeChanged( input->echoMode() ); } );
|
||||
|
||||
QObject::connect( wrappedInput, &QQuickTextInput::passwordCharacterChanged,
|
||||
input, &QskTextInput::passwordCharacterChanged );
|
||||
|
||||
QObject::connect( wrappedInput, &QQuickTextInput::passwordMaskDelayChanged,
|
||||
input, &QskTextInput::passwordMaskDelayChanged );
|
||||
|
||||
QObject::connect( wrappedInput, &QQuickItem::implicitWidthChanged,
|
||||
input, &QskControl::resetImplicitSize );
|
||||
|
||||
QObject::connect( wrappedInput, &QQuickItem::implicitHeightChanged,
|
||||
input, &QskControl::resetImplicitSize );
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class QuickTextInput final : public QQuickTextInput
|
||||
{
|
||||
using Inherited = QQuickTextInput;
|
||||
|
||||
public:
|
||||
QuickTextInput( QskTextInput* );
|
||||
|
||||
void setEditing( bool on );
|
||||
|
||||
inline void setAlignment( Qt::Alignment alignment )
|
||||
{
|
||||
setHAlign( ( HAlignment ) ( int( alignment ) & 0x0f ) );
|
||||
setVAlign( ( VAlignment ) ( int( alignment ) & 0xf0 ) );
|
||||
}
|
||||
|
||||
bool fixup()
|
||||
{
|
||||
return QQuickTextInputPrivate::get( this )->fixup();
|
||||
}
|
||||
|
||||
bool hasAcceptableInput() const
|
||||
{
|
||||
/*
|
||||
we would like to call QQuickTextInputPrivate::hasAcceptableInput
|
||||
but unfortunately it is private, so we need to hack somthing
|
||||
together
|
||||
*/
|
||||
|
||||
auto that = const_cast< QuickTextInput* >( this );
|
||||
auto d = QQuickTextInputPrivate::get( that );
|
||||
|
||||
if ( d->m_validator )
|
||||
{
|
||||
QString text = displayText();
|
||||
int pos = d->m_cursor;
|
||||
|
||||
const auto state = d->m_validator->validate( text, pos );
|
||||
if ( state != QValidator::Acceptable )
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( d->m_maskData )
|
||||
{
|
||||
/*
|
||||
We only want to do the check for the maskData here
|
||||
and have to disable d->m_validator temporarily
|
||||
*/
|
||||
|
||||
class Validator final : public QValidator
|
||||
{
|
||||
public:
|
||||
State validate( QString&, int& ) const override
|
||||
{
|
||||
return QValidator::Acceptable;
|
||||
}
|
||||
};
|
||||
|
||||
const auto validator = d->m_validator;
|
||||
|
||||
const auto validInput = d->m_validInput;
|
||||
const auto acceptableInput = d->m_acceptableInput;
|
||||
|
||||
d->m_acceptableInput = true;
|
||||
|
||||
static Validator noValidator;
|
||||
that->setValidator( &noValidator ); // implicitly checking maskData
|
||||
that->setValidator( d->m_validator );
|
||||
|
||||
const bool isAcceptable = d->m_acceptableInput;
|
||||
|
||||
// restoring old values
|
||||
d->m_validInput = validInput;
|
||||
d->m_acceptableInput = acceptableInput;
|
||||
|
||||
return isAcceptable;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void updateColors();
|
||||
void updateMetrics();
|
||||
|
||||
inline bool handleEvent( QEvent* event )
|
||||
{
|
||||
return this->event( event );
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
|
||||
void geometryChange(
|
||||
const QRectF& newGeometry, const QRectF& oldGeometry ) override
|
||||
{
|
||||
Inherited::geometryChange( newGeometry, oldGeometry );
|
||||
updateClip();
|
||||
}
|
||||
#else
|
||||
void geometryChanged(
|
||||
const QRectF& newGeometry, const QRectF& oldGeometry ) override
|
||||
{
|
||||
Inherited::geometryChanged( newGeometry, oldGeometry );
|
||||
updateClip();
|
||||
}
|
||||
#endif
|
||||
|
||||
void updateClip()
|
||||
{
|
||||
setClip( ( contentWidth() > width() ) ||
|
||||
( contentHeight() > height() ) );
|
||||
}
|
||||
|
||||
QSGNode* updatePaintNode(
|
||||
QSGNode* oldNode, UpdatePaintNodeData* data ) override
|
||||
{
|
||||
updateColors();
|
||||
return Inherited::updatePaintNode( oldNode, data );
|
||||
}
|
||||
};
|
||||
|
||||
QuickTextInput::QuickTextInput( QskTextInput* textInput )
|
||||
: QQuickTextInput( textInput )
|
||||
{
|
||||
classBegin();
|
||||
|
||||
setActiveFocusOnTab( false );
|
||||
setFlag( ItemAcceptsInputMethod, false );
|
||||
setFocusOnPress( false );
|
||||
|
||||
componentComplete();
|
||||
|
||||
connect( this, &QQuickTextInput::contentSizeChanged,
|
||||
this, &QuickTextInput::updateClip );
|
||||
}
|
||||
|
||||
void QuickTextInput::setEditing( bool on )
|
||||
{
|
||||
auto d = QQuickTextInputPrivate::get( this );
|
||||
|
||||
if ( d->cursorVisible == on )
|
||||
return;
|
||||
|
||||
setCursorVisible( on );
|
||||
d->setBlinkingCursorEnabled( on );
|
||||
|
||||
if ( !on )
|
||||
{
|
||||
if ( d->m_passwordEchoEditing || d->m_passwordEchoTimer.isActive() )
|
||||
d->updatePasswordEchoEditing( false );
|
||||
}
|
||||
|
||||
polish();
|
||||
update();
|
||||
}
|
||||
|
||||
void QuickTextInput::updateMetrics()
|
||||
{
|
||||
auto textInput = static_cast< const QskTextInput* >( parentItem() );
|
||||
|
||||
setAlignment( textInput->alignment() );
|
||||
setFont( textInput->font() );
|
||||
}
|
||||
|
||||
void QuickTextInput::updateColors()
|
||||
{
|
||||
auto textInput = static_cast< const QskTextInput* >( parentItem() );
|
||||
auto d = QQuickTextInputPrivate::get( this );
|
||||
|
||||
bool isDirty = false;
|
||||
|
||||
QColor color;
|
||||
|
||||
color = textInput->color( QskTextInput::Text );
|
||||
if ( d->color != color )
|
||||
{
|
||||
d->color = color;
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
if ( d->hasSelectedText() )
|
||||
{
|
||||
QskAspect::States states = QskTextInputSkinlet::Selected;
|
||||
#if 0
|
||||
states |= textInput->skinStates();
|
||||
#endif
|
||||
|
||||
color = textInput->color( QskTextInput::TextPanel | states );
|
||||
if ( d->selectionColor != color )
|
||||
{
|
||||
d->selectionColor = color;
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
color = textInput->color( QskTextInput::Text | states );
|
||||
if ( d->selectedTextColor != color )
|
||||
{
|
||||
d->selectedTextColor = color;
|
||||
isDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isDirty )
|
||||
{
|
||||
d->textLayoutDirty = true;
|
||||
d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class QskTextInput::PrivateData
|
||||
{
|
||||
public:
|
||||
QuickTextInput* wrappedInput;
|
||||
|
||||
ActivationModes activationModes;
|
||||
};
|
||||
|
||||
QskTextInput::QskTextInput( QQuickItem* parent )
|
||||
: Inherited( parent )
|
||||
, m_data( new PrivateData() )
|
||||
{
|
||||
m_data->activationModes = ActivationOnMouse | ActivationOnKey;
|
||||
|
||||
setPolishOnResize( true );
|
||||
|
||||
setAcceptHoverEvents( true );
|
||||
setFocusPolicy( Qt::StrongFocus );
|
||||
|
||||
setFlag( QQuickItem::ItemAcceptsInputMethod );
|
||||
|
||||
/*
|
||||
QQuickTextInput is a beast of almost 5k lines of code, we don't
|
||||
want to reimplement that - at least not now.
|
||||
So QskTextInput is more or less a simple wrapper making everything
|
||||
conforming to qskinny.
|
||||
*/
|
||||
|
||||
m_data->wrappedInput = new QuickTextInput( this );
|
||||
qskBindSignals( m_data->wrappedInput, this );
|
||||
|
||||
setAcceptedMouseButtons( m_data->wrappedInput->acceptedMouseButtons() );
|
||||
m_data->wrappedInput->setAcceptedMouseButtons( Qt::NoButton );
|
||||
|
||||
initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Fixed );
|
||||
}
|
||||
|
||||
QskTextInput::~QskTextInput()
|
||||
{
|
||||
}
|
||||
|
||||
bool QskTextInput::event( QEvent* event )
|
||||
{
|
||||
if ( event->type() == QEvent::ShortcutOverride )
|
||||
{
|
||||
return m_data->wrappedInput->handleEvent( event );
|
||||
}
|
||||
else if ( event->type() == QEvent::LocaleChange )
|
||||
{
|
||||
qskUpdateInputMethod( this, Qt::ImPreferredLanguage );
|
||||
}
|
||||
|
||||
return Inherited::event( event );
|
||||
}
|
||||
|
||||
void QskTextInput::keyPressEvent( QKeyEvent* event )
|
||||
{
|
||||
if ( isEditing() )
|
||||
{
|
||||
switch ( event->key() )
|
||||
{
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
{
|
||||
if ( hasAcceptableInput() || fixup() )
|
||||
{
|
||||
QGuiApplication::inputMethod()->commit();
|
||||
|
||||
if ( !( inputMethodHints() & Qt::ImhMultiLine ) )
|
||||
{
|
||||
setEditing( false );
|
||||
|
||||
// When returning from a virtual keyboard
|
||||
qskForceActiveFocus( this, Qt::PopupFocusReason );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#if 1
|
||||
case Qt::Key_Escape:
|
||||
{
|
||||
setEditing( false );
|
||||
qskForceActiveFocus( this, Qt::PopupFocusReason );
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
m_data->wrappedInput->handleEvent( event );
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ( m_data->activationModes & ActivationOnKey ) && !event->isAutoRepeat() )
|
||||
{
|
||||
if ( event->key() == Qt::Key_Select || event->key() == Qt::Key_Space )
|
||||
{
|
||||
setEditing( true );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Inherited::keyPressEvent( event );
|
||||
}
|
||||
|
||||
void QskTextInput::keyReleaseEvent( QKeyEvent* event )
|
||||
{
|
||||
Inherited::keyReleaseEvent( event );
|
||||
}
|
||||
|
||||
void QskTextInput::mousePressEvent( QMouseEvent* event )
|
||||
{
|
||||
m_data->wrappedInput->handleEvent( event );
|
||||
|
||||
if ( !isReadOnly() && !qGuiApp->styleHints()->setFocusOnTouchRelease() )
|
||||
setEditing( true );
|
||||
}
|
||||
|
||||
void QskTextInput::mouseMoveEvent( QMouseEvent* event )
|
||||
{
|
||||
m_data->wrappedInput->handleEvent( event );
|
||||
}
|
||||
|
||||
void QskTextInput::mouseReleaseEvent( QMouseEvent* event )
|
||||
{
|
||||
m_data->wrappedInput->handleEvent( event );
|
||||
|
||||
if ( !isReadOnly() && qGuiApp->styleHints()->setFocusOnTouchRelease() )
|
||||
setEditing( true );
|
||||
}
|
||||
|
||||
void QskTextInput::mouseDoubleClickEvent( QMouseEvent* event )
|
||||
{
|
||||
m_data->wrappedInput->handleEvent( event );
|
||||
}
|
||||
|
||||
void QskTextInput::inputMethodEvent( QInputMethodEvent* event )
|
||||
{
|
||||
m_data->wrappedInput->handleEvent( event );
|
||||
}
|
||||
|
||||
void QskTextInput::focusInEvent( QFocusEvent* event )
|
||||
{
|
||||
if ( m_data->activationModes & ActivationOnFocus )
|
||||
{
|
||||
switch ( event->reason() )
|
||||
{
|
||||
case Qt::ActiveWindowFocusReason:
|
||||
case Qt::PopupFocusReason:
|
||||
break;
|
||||
|
||||
default:
|
||||
#if 1
|
||||
// auto selecting the complete text ???
|
||||
#endif
|
||||
setEditing( true );
|
||||
}
|
||||
}
|
||||
|
||||
Inherited::focusInEvent( event );
|
||||
}
|
||||
|
||||
void QskTextInput::focusOutEvent( QFocusEvent* event )
|
||||
{
|
||||
switch ( event->reason() )
|
||||
{
|
||||
case Qt::ActiveWindowFocusReason:
|
||||
case Qt::PopupFocusReason:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
m_data->wrappedInput->deselect();
|
||||
setEditing( false );
|
||||
}
|
||||
}
|
||||
|
||||
Inherited::focusOutEvent( event );
|
||||
}
|
||||
|
||||
void QskTextInput::updateLayout()
|
||||
{
|
||||
m_data->wrappedInput->updateMetrics();
|
||||
qskSetItemGeometry( m_data->wrappedInput, subControlRect( Text ) );
|
||||
}
|
||||
|
||||
void QskTextInput::updateNode( QSGNode* node )
|
||||
{
|
||||
m_data->wrappedInput->updateColors();
|
||||
Inherited::updateNode( node );
|
||||
}
|
||||
|
||||
QString QskTextInput::text() const
|
||||
{
|
||||
return m_data->wrappedInput->text();
|
||||
}
|
||||
|
||||
void QskTextInput::setText( const QString& text )
|
||||
{
|
||||
m_data->wrappedInput->setText( text );
|
||||
}
|
||||
|
||||
QskTextInput::ActivationModes QskTextInput::activationModes() const
|
||||
{
|
||||
return static_cast< QskTextInput::ActivationModes >( m_data->activationModes );
|
||||
}
|
||||
|
||||
void QskTextInput::setActivationModes( ActivationModes modes )
|
||||
{
|
||||
if ( static_cast< ActivationModes >( m_data->activationModes ) != modes )
|
||||
{
|
||||
m_data->activationModes = modes;
|
||||
Q_EMIT activationModesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void qskUpdateInputMethodFont( const QskTextInput* input )
|
||||
{
|
||||
const auto queries = Qt::ImCursorRectangle | Qt::ImFont | Qt::ImAnchorRectangle;
|
||||
qskUpdateInputMethod( input, queries );
|
||||
}
|
||||
|
||||
void QskTextInput::setFontRole( const QskFontRole& role )
|
||||
{
|
||||
if ( setFontRoleHint( Text, role ) )
|
||||
{
|
||||
qskUpdateInputMethodFont( this );
|
||||
Q_EMIT fontRoleChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void QskTextInput::resetFontRole()
|
||||
{
|
||||
if ( resetFontRoleHint( Text ) )
|
||||
{
|
||||
qskUpdateInputMethodFont( this );
|
||||
Q_EMIT fontRoleChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QskFontRole QskTextInput::fontRole() const
|
||||
{
|
||||
return fontRoleHint( Text );
|
||||
}
|
||||
|
||||
void QskTextInput::setAlignment( Qt::Alignment alignment )
|
||||
{
|
||||
if ( setAlignmentHint( Text, alignment ) )
|
||||
{
|
||||
m_data->wrappedInput->setAlignment( alignment );
|
||||
Q_EMIT alignmentChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void QskTextInput::resetAlignment()
|
||||
{
|
||||
if ( resetAlignmentHint( Text ) )
|
||||
{
|
||||
m_data->wrappedInput->setAlignment( alignment() );
|
||||
Q_EMIT alignmentChanged();
|
||||
}
|
||||
}
|
||||
|
||||
Qt::Alignment QskTextInput::alignment() const
|
||||
{
|
||||
return alignmentHint( Text, Qt::AlignLeft | Qt::AlignTop );
|
||||
}
|
||||
|
||||
void QskTextInput::setWrapMode( QskTextOptions::WrapMode wrapMode )
|
||||
{
|
||||
m_data->wrappedInput->setWrapMode(
|
||||
static_cast< QQuickTextInput::WrapMode >( wrapMode ) );
|
||||
}
|
||||
|
||||
QskTextOptions::WrapMode QskTextInput::wrapMode() const
|
||||
{
|
||||
return static_cast< QskTextOptions::WrapMode >(
|
||||
m_data->wrappedInput->wrapMode() );
|
||||
}
|
||||
|
||||
QFont QskTextInput::font() const
|
||||
{
|
||||
return effectiveFont( QskTextInput::Text );
|
||||
}
|
||||
|
||||
bool QskTextInput::isReadOnly() const
|
||||
{
|
||||
return m_data->wrappedInput->isReadOnly();
|
||||
}
|
||||
|
||||
void QskTextInput::setReadOnly( bool on )
|
||||
{
|
||||
auto input = m_data->wrappedInput;
|
||||
|
||||
if ( input->isReadOnly() == on )
|
||||
return;
|
||||
|
||||
#if 1
|
||||
// do we want to be able to restore the previous policy ?
|
||||
setFocusPolicy( Qt::NoFocus );
|
||||
#endif
|
||||
|
||||
input->setReadOnly( on );
|
||||
|
||||
// we are killing user settings here ?
|
||||
input->setFlag( QQuickItem::ItemAcceptsInputMethod, !on );
|
||||
qskUpdateInputMethod( this, Qt::ImEnabled );
|
||||
|
||||
setSkinStateFlag( ReadOnly, on );
|
||||
}
|
||||
|
||||
void QskTextInput::setEditing( bool on )
|
||||
{
|
||||
if ( isReadOnly() || on == isEditing() )
|
||||
return;
|
||||
|
||||
setSkinStateFlag( Editing, on );
|
||||
m_data->wrappedInput->setEditing( on );
|
||||
|
||||
if ( on )
|
||||
{
|
||||
#if 0
|
||||
updateInputMethod( Qt::ImCursorRectangle | Qt::ImAnchorRectangle );
|
||||
QGuiApplication::inputMethod()->inputDirection
|
||||
#endif
|
||||
qskInputMethodSetVisible( this, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( hasAcceptableInput() || fixup() )
|
||||
Q_EMIT m_data->wrappedInput->editingFinished();
|
||||
|
||||
#if 0
|
||||
inputMethod->reset();
|
||||
#endif
|
||||
qskInputMethodSetVisible( this, false );
|
||||
}
|
||||
|
||||
Q_EMIT editingChanged( on );
|
||||
}
|
||||
|
||||
bool QskTextInput::isEditing() const
|
||||
{
|
||||
return hasSkinState( Editing );
|
||||
}
|
||||
|
||||
void QskTextInput::ensureVisible( int position )
|
||||
{
|
||||
m_data->wrappedInput->ensureVisible( position );
|
||||
}
|
||||
|
||||
int QskTextInput::cursorPosition() const
|
||||
{
|
||||
return m_data->wrappedInput->cursorPosition();
|
||||
}
|
||||
|
||||
void QskTextInput::setCursorPosition( int pos )
|
||||
{
|
||||
m_data->wrappedInput->setCursorPosition( pos );
|
||||
}
|
||||
|
||||
int QskTextInput::maxLength() const
|
||||
{
|
||||
return m_data->wrappedInput->maxLength();
|
||||
}
|
||||
|
||||
void QskTextInput::setMaxLength( int length )
|
||||
{
|
||||
m_data->wrappedInput->setMaxLength( length );
|
||||
}
|
||||
|
||||
QValidator* QskTextInput::validator() const
|
||||
{
|
||||
return m_data->wrappedInput->validator();
|
||||
}
|
||||
|
||||
void QskTextInput::setValidator( QValidator* validator )
|
||||
{
|
||||
m_data->wrappedInput->setValidator( validator );
|
||||
}
|
||||
|
||||
QString QskTextInput::inputMask() const
|
||||
{
|
||||
return m_data->wrappedInput->inputMask();
|
||||
}
|
||||
|
||||
void QskTextInput::setInputMask( const QString& mask )
|
||||
{
|
||||
m_data->wrappedInput->setInputMask( mask );
|
||||
}
|
||||
|
||||
QskTextInput::EchoMode QskTextInput::echoMode() const
|
||||
{
|
||||
const auto mode = m_data->wrappedInput->echoMode();
|
||||
return static_cast< QskTextInput::EchoMode >( mode );
|
||||
}
|
||||
|
||||
void QskTextInput::setEchoMode( EchoMode mode )
|
||||
{
|
||||
if ( mode != echoMode() )
|
||||
{
|
||||
m_data->wrappedInput->setEchoMode(
|
||||
static_cast< QQuickTextInput::EchoMode >( mode ) );
|
||||
|
||||
qskUpdateInputMethod( this, Qt::ImHints );
|
||||
}
|
||||
}
|
||||
|
||||
QString QskTextInput::passwordCharacter() const
|
||||
{
|
||||
return m_data->wrappedInput->passwordCharacter();
|
||||
}
|
||||
|
||||
void QskTextInput::setPasswordCharacter( const QString& text )
|
||||
{
|
||||
m_data->wrappedInput->setPasswordCharacter( text );
|
||||
}
|
||||
|
||||
void QskTextInput::resetPasswordCharacter()
|
||||
{
|
||||
m_data->wrappedInput->setPasswordCharacter(
|
||||
QGuiApplication::styleHints()->passwordMaskCharacter() );
|
||||
}
|
||||
|
||||
int QskTextInput::passwordMaskDelay() const
|
||||
{
|
||||
return m_data->wrappedInput->passwordMaskDelay();
|
||||
}
|
||||
|
||||
void QskTextInput::setPasswordMaskDelay( int ms )
|
||||
{
|
||||
m_data->wrappedInput->setPasswordMaskDelay( ms );
|
||||
}
|
||||
|
||||
void QskTextInput::resetPasswordMaskDelay()
|
||||
{
|
||||
m_data->wrappedInput->resetPasswordMaskDelay();
|
||||
}
|
||||
|
||||
QString QskTextInput::displayText() const
|
||||
{
|
||||
return m_data->wrappedInput->displayText();
|
||||
}
|
||||
|
||||
QString QskTextInput::preeditText() const
|
||||
{
|
||||
const auto d = QQuickTextInputPrivate::get( m_data->wrappedInput );
|
||||
return d->m_textLayout.preeditAreaText();
|
||||
}
|
||||
|
||||
bool QskTextInput::overwriteMode() const
|
||||
{
|
||||
return m_data->wrappedInput->overwriteMode();
|
||||
}
|
||||
|
||||
void QskTextInput::setOverwriteMode( bool overwrite )
|
||||
{
|
||||
m_data->wrappedInput->setOverwriteMode( overwrite );
|
||||
}
|
||||
|
||||
bool QskTextInput::hasAcceptableInput() const
|
||||
{
|
||||
return m_data->wrappedInput->hasAcceptableInput();
|
||||
}
|
||||
|
||||
bool QskTextInput::fixup()
|
||||
{
|
||||
return m_data->wrappedInput->fixup();
|
||||
}
|
||||
|
||||
QVariant QskTextInput::inputMethodQuery(
|
||||
Qt::InputMethodQuery property ) const
|
||||
{
|
||||
return inputMethodQuery( property, QVariant() );
|
||||
}
|
||||
|
||||
QVariant QskTextInput::inputMethodQuery(
|
||||
Qt::InputMethodQuery query, const QVariant& argument ) const
|
||||
{
|
||||
switch ( query )
|
||||
{
|
||||
case Qt::ImEnabled:
|
||||
{
|
||||
return QVariant( ( bool ) ( flags() & ItemAcceptsInputMethod ) );
|
||||
}
|
||||
case Qt::ImFont:
|
||||
{
|
||||
return font();
|
||||
}
|
||||
case Qt::ImPreferredLanguage:
|
||||
{
|
||||
return locale();
|
||||
}
|
||||
case Qt::ImInputItemClipRectangle:
|
||||
case Qt::ImCursorRectangle:
|
||||
{
|
||||
QVariant v = m_data->wrappedInput->inputMethodQuery( query, argument );
|
||||
#if 1
|
||||
if ( v.canConvert< QRectF >() )
|
||||
v.setValue( v.toRectF().translated( m_data->wrappedInput->position() ) );
|
||||
#endif
|
||||
return v;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return m_data->wrappedInput->inputMethodQuery( query, argument );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Qt::InputMethodHints QskTextInput::inputMethodHints() const
|
||||
{
|
||||
return m_data->wrappedInput->inputMethodHints();
|
||||
}
|
||||
|
||||
void QskTextInput::setInputMethodHints( Qt::InputMethodHints hints )
|
||||
{
|
||||
if ( m_data->wrappedInput->inputMethodHints() != hints )
|
||||
{
|
||||
m_data->wrappedInput->setInputMethodHints( hints );
|
||||
qskUpdateInputMethod( this, Qt::ImHints );
|
||||
}
|
||||
}
|
||||
|
||||
void QskTextInput::setupFrom( const QQuickItem* item )
|
||||
{
|
||||
if ( item == nullptr )
|
||||
return;
|
||||
|
||||
// finding attributes from the input hints of item
|
||||
|
||||
int maxCharacters = 32767;
|
||||
QskTextInput::EchoMode echoMode = QskTextInput::Normal;
|
||||
|
||||
Qt::InputMethodQueries queries = Qt::ImQueryAll;
|
||||
queries &= ~Qt::ImEnabled;
|
||||
|
||||
QInputMethodQueryEvent event( queries );
|
||||
QCoreApplication::sendEvent( const_cast< QQuickItem* >( item ), &event );
|
||||
|
||||
if ( event.queries() & Qt::ImHints )
|
||||
{
|
||||
const auto hints = static_cast< Qt::InputMethodHints >(
|
||||
event.value( Qt::ImHints ).toInt() );
|
||||
|
||||
if ( hints & Qt::ImhHiddenText )
|
||||
echoMode = QskTextInput::NoEcho;
|
||||
}
|
||||
|
||||
if ( event.queries() & Qt::ImMaximumTextLength )
|
||||
{
|
||||
// needs to be handled before Qt::ImCursorPosition !
|
||||
|
||||
const auto max = event.value( Qt::ImMaximumTextLength ).toInt();
|
||||
maxCharacters = qBound( 0, max, maxCharacters );
|
||||
}
|
||||
|
||||
setMaxLength( maxCharacters );
|
||||
|
||||
if ( event.queries() & Qt::ImSurroundingText )
|
||||
{
|
||||
const auto text = event.value( Qt::ImSurroundingText ).toString();
|
||||
setText( text );
|
||||
}
|
||||
|
||||
if ( event.queries() & Qt::ImCursorPosition )
|
||||
{
|
||||
const auto pos = event.value( Qt::ImCursorPosition ).toInt();
|
||||
setCursorPosition( pos );
|
||||
}
|
||||
|
||||
if ( event.queries() & Qt::ImCurrentSelection )
|
||||
{
|
||||
#if 0
|
||||
const auto text = event.value( Qt::ImCurrentSelection ).toString();
|
||||
if ( !text.isEmpty() )
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int passwordMaskDelay = -1;
|
||||
QString passwordCharacter;
|
||||
|
||||
if ( echoMode == QskTextInput::NoEcho )
|
||||
{
|
||||
/*
|
||||
Qt::ImhHiddenText does not provide information
|
||||
to decide between NoEcho/Password, or provides
|
||||
more details about how to deal with hidden inputs.
|
||||
So we try to find out more from trying some properties.
|
||||
*/
|
||||
|
||||
QVariant value;
|
||||
|
||||
value = item->property( "passwordMaskDelay" );
|
||||
if ( value.canConvert< int >() )
|
||||
passwordMaskDelay = value.toInt();
|
||||
|
||||
value = item->property( "passwordCharacter" );
|
||||
if ( value.canConvert< QString >() )
|
||||
passwordCharacter = value.toString();
|
||||
|
||||
value = item->property( "echoMode" );
|
||||
if ( value.canConvert< int >() )
|
||||
{
|
||||
const auto mode = value.toInt();
|
||||
if ( mode == QskTextInput::Password )
|
||||
echoMode = QskTextInput::Password;
|
||||
}
|
||||
}
|
||||
|
||||
if ( passwordMaskDelay >= 0 )
|
||||
setPasswordMaskDelay( passwordMaskDelay );
|
||||
else
|
||||
resetPasswordMaskDelay();
|
||||
|
||||
if ( !passwordCharacter.isEmpty() )
|
||||
setPasswordCharacter( passwordCharacter );
|
||||
else
|
||||
resetPasswordCharacter();
|
||||
|
||||
setEchoMode( echoMode );
|
||||
}
|
||||
|
||||
#include "moc_QskTextInput.cpp"
|
207
src/controls/QskTextInput.h
Normal file
207
src/controls/QskTextInput.h
Normal file
@ -0,0 +1,207 @@
|
||||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef QSK_TEXT_INPUT_H
|
||||
#define QSK_TEXT_INPUT_H
|
||||
|
||||
#include "QskControl.h"
|
||||
#include "QskTextOptions.h"
|
||||
|
||||
class QValidator;
|
||||
class QskFontRole;
|
||||
|
||||
class QSK_EXPORT QskTextInput : public QskControl
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY( QString text READ text
|
||||
WRITE setText NOTIFY textChanged USER true )
|
||||
|
||||
Q_PROPERTY( QskFontRole fontRole READ fontRole
|
||||
WRITE setFontRole RESET resetFontRole NOTIFY fontRoleChanged )
|
||||
|
||||
Q_PROPERTY( QFont font READ font )
|
||||
|
||||
Q_PROPERTY( Qt::Alignment alignment READ alignment
|
||||
WRITE setAlignment RESET resetAlignment NOTIFY alignmentChanged )
|
||||
|
||||
Q_PROPERTY( QskTextOptions::WrapMode wrapMode READ wrapMode
|
||||
WRITE setWrapMode NOTIFY wrapModeChanged )
|
||||
|
||||
Q_PROPERTY( ActivationModes activationModes READ activationModes
|
||||
WRITE setActivationModes NOTIFY activationModesChanged )
|
||||
|
||||
Q_PROPERTY( bool editing READ isEditing
|
||||
WRITE setEditing NOTIFY editingChanged )
|
||||
|
||||
Q_PROPERTY( EchoMode echoMode READ echoMode
|
||||
WRITE setEchoMode NOTIFY echoModeChanged )
|
||||
|
||||
Q_PROPERTY( QString passwordCharacter READ passwordCharacter
|
||||
WRITE setPasswordCharacter RESET resetPasswordCharacter
|
||||
NOTIFY passwordCharacterChanged )
|
||||
|
||||
Q_PROPERTY( int passwordMaskDelay READ passwordMaskDelay
|
||||
WRITE setPasswordMaskDelay RESET resetPasswordMaskDelay
|
||||
NOTIFY passwordMaskDelayChanged )
|
||||
|
||||
using Inherited = QskControl;
|
||||
|
||||
public:
|
||||
QSK_SUBCONTROLS( TextPanel, Text )
|
||||
|
||||
QSK_STATES( ReadOnly, Editing, Error )
|
||||
|
||||
enum ActivationMode : quint8
|
||||
{
|
||||
NoActivation,
|
||||
|
||||
ActivationOnFocus = 1 << 0,
|
||||
ActivationOnMouse = 1 << 1,
|
||||
ActivationOnKey = 1 << 2,
|
||||
|
||||
ActivationOnInput = ActivationOnMouse | ActivationOnKey,
|
||||
ActivationOnAll = ActivationOnFocus | ActivationOnMouse | ActivationOnKey
|
||||
};
|
||||
|
||||
Q_ENUM( ActivationMode )
|
||||
Q_DECLARE_FLAGS( ActivationModes, ActivationMode )
|
||||
|
||||
enum EchoMode : quint8
|
||||
{
|
||||
Normal,
|
||||
NoEcho,
|
||||
Password,
|
||||
PasswordEchoOnEdit
|
||||
};
|
||||
|
||||
Q_ENUM( EchoMode )
|
||||
|
||||
~QskTextInput() override;
|
||||
|
||||
void setupFrom( const QQuickItem* );
|
||||
|
||||
QString text() const;
|
||||
|
||||
void setFontRole( const QskFontRole& role );
|
||||
void resetFontRole();
|
||||
QskFontRole fontRole() const;
|
||||
|
||||
void setAlignment( Qt::Alignment );
|
||||
void resetAlignment();
|
||||
Qt::Alignment alignment() const;
|
||||
|
||||
void setWrapMode( QskTextOptions::WrapMode );
|
||||
QskTextOptions::WrapMode wrapMode() const;
|
||||
|
||||
void setActivationModes( ActivationModes );
|
||||
ActivationModes activationModes() const;
|
||||
|
||||
bool isEditing() const;
|
||||
|
||||
QFont font() const;
|
||||
|
||||
bool isReadOnly() const;
|
||||
void setReadOnly( bool );
|
||||
|
||||
int cursorPosition() const;
|
||||
void setCursorPosition( int );
|
||||
|
||||
int maxLength() const;
|
||||
void setMaxLength( int );
|
||||
|
||||
QValidator* validator() const;
|
||||
void setValidator( QValidator* );
|
||||
|
||||
QString inputMask() const;
|
||||
void setInputMask( const QString& );
|
||||
|
||||
EchoMode echoMode() const;
|
||||
void setEchoMode( EchoMode );
|
||||
|
||||
QString passwordCharacter() const;
|
||||
void setPasswordCharacter( const QString& );
|
||||
void resetPasswordCharacter();
|
||||
|
||||
int passwordMaskDelay() const;
|
||||
void setPasswordMaskDelay( int );
|
||||
void resetPasswordMaskDelay();
|
||||
|
||||
QString displayText() const;
|
||||
QString preeditText() const;
|
||||
|
||||
bool overwriteMode() const;
|
||||
void setOverwriteMode( bool );
|
||||
|
||||
bool hasAcceptableInput() const;
|
||||
bool fixup();
|
||||
|
||||
QVariant inputMethodQuery( Qt::InputMethodQuery ) const override;
|
||||
QVariant inputMethodQuery( Qt::InputMethodQuery, const QVariant& argument ) const;
|
||||
|
||||
Qt::InputMethodHints inputMethodHints() const;
|
||||
void setInputMethodHints( Qt::InputMethodHints );
|
||||
|
||||
void ensureVisible( int position );
|
||||
|
||||
public Q_SLOTS:
|
||||
void setText( const QString& );
|
||||
void setEditing( bool );
|
||||
|
||||
Q_SIGNALS:
|
||||
void editingChanged( bool );
|
||||
|
||||
void activationModesChanged();
|
||||
void readOnlyChanged( bool );
|
||||
|
||||
void textChanged( const QString& );
|
||||
void displayTextChanged( const QString& );
|
||||
|
||||
void textEdited( const QString& );
|
||||
|
||||
void fontRoleChanged();
|
||||
void alignmentChanged();
|
||||
void wrapModeChanged( QskTextOptions::WrapMode );
|
||||
|
||||
void overwriteModeChanged( bool );
|
||||
void maximumLengthChanged( int );
|
||||
|
||||
void echoModeChanged( EchoMode );
|
||||
void passwordMaskDelayChanged();
|
||||
void passwordCharacterChanged();
|
||||
|
||||
void validatorChanged();
|
||||
void inputMaskChanged( const QString& );
|
||||
|
||||
protected:
|
||||
QskTextInput( QQuickItem* parent = nullptr );
|
||||
|
||||
bool event( QEvent* ) override;
|
||||
|
||||
void inputMethodEvent( QInputMethodEvent* ) override;
|
||||
|
||||
void focusInEvent( QFocusEvent* ) override;
|
||||
void focusOutEvent( QFocusEvent* ) override;
|
||||
|
||||
void mousePressEvent( QMouseEvent* ) override;
|
||||
void mouseMoveEvent( QMouseEvent* ) override;
|
||||
void mouseReleaseEvent( QMouseEvent* ) override;
|
||||
void mouseDoubleClickEvent( QMouseEvent* ) override;
|
||||
|
||||
void keyPressEvent( QKeyEvent* ) override;
|
||||
void keyReleaseEvent( QKeyEvent* ) override;
|
||||
|
||||
void updateLayout() override;
|
||||
void updateNode( QSGNode* ) override;
|
||||
|
||||
private:
|
||||
class PrivateData;
|
||||
std::unique_ptr< PrivateData > m_data;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS( QskTextInput::ActivationModes )
|
||||
Q_DECLARE_METATYPE( QskTextInput::ActivationModes )
|
||||
|
||||
#endif
|
74
src/controls/QskTextInputSkinlet.cpp
Normal file
74
src/controls/QskTextInputSkinlet.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskTextInputSkinlet.h"
|
||||
#include "QskTextInput.h"
|
||||
|
||||
#include <qfontmetrics.h>
|
||||
|
||||
using Q = QskTextInput;
|
||||
|
||||
QSK_SYSTEM_STATE( QskTextInputSkinlet, Selected, QskAspect::FirstSystemState << 3 )
|
||||
|
||||
QskTextInputSkinlet::QskTextInputSkinlet( QskSkin* skin )
|
||||
: Inherited( skin )
|
||||
{
|
||||
setNodeRoles( { TextPanelRole } );
|
||||
}
|
||||
|
||||
QskTextInputSkinlet::~QskTextInputSkinlet()
|
||||
{
|
||||
}
|
||||
|
||||
QRectF QskTextInputSkinlet::subControlRect( const QskSkinnable* skinnable,
|
||||
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
|
||||
{
|
||||
if ( subControl == Q::TextPanel )
|
||||
return contentsRect;
|
||||
|
||||
if ( subControl == Q::Text )
|
||||
{
|
||||
auto rect = skinnable->subControlContentsRect( contentsRect, Q::TextPanel );
|
||||
|
||||
const auto h = skinnable->effectiveFontHeight( Q::Text );
|
||||
rect.setTop( rect.center().y() - 0.5 * h );
|
||||
rect.setHeight( h );
|
||||
rect = rect.marginsAdded( skinnable->marginHint( Q::Text ) );
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
return Inherited::subControlRect( skinnable, contentsRect, subControl );
|
||||
}
|
||||
|
||||
QSGNode* QskTextInputSkinlet::updateSubNode(
|
||||
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
|
||||
{
|
||||
switch ( nodeRole )
|
||||
{
|
||||
case TextPanelRole:
|
||||
return updateBoxNode( skinnable, node, Q::TextPanel );
|
||||
}
|
||||
|
||||
return Inherited::updateSubNode( skinnable, nodeRole, node );
|
||||
}
|
||||
|
||||
QSizeF QskTextInputSkinlet::sizeHint( const QskSkinnable* skinnable,
|
||||
Qt::SizeHint which, const QSizeF& ) const
|
||||
{
|
||||
if ( which != Qt::PreferredSize )
|
||||
return QSizeF();
|
||||
|
||||
const auto text = static_cast< const QskTextInput* >( skinnable )->text();
|
||||
|
||||
const QFontMetricsF fm( skinnable->effectiveFont( Q::Text ) );
|
||||
|
||||
auto hint = fm.size( Qt::TextSingleLine | Qt::TextExpandTabs, text );
|
||||
|
||||
hint = skinnable->outerBoxSize( Q::TextPanel, hint );
|
||||
hint = hint.expandedTo( skinnable->strutSizeHint( Q::TextPanel ) );
|
||||
|
||||
return hint;
|
||||
}
|
39
src/controls/QskTextInputSkinlet.h
Normal file
39
src/controls/QskTextInputSkinlet.h
Normal file
@ -0,0 +1,39 @@
|
||||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef QSK_TEXT_INPUT_SKINLET_H
|
||||
#define QSK_TEXT_INPUT_SKINLET_H
|
||||
|
||||
#include "QskSkinlet.h"
|
||||
|
||||
class QSK_EXPORT QskTextInputSkinlet : public QskSkinlet
|
||||
{
|
||||
using Inherited = QskSkinlet;
|
||||
|
||||
public:
|
||||
QSK_STATES( Selected )
|
||||
|
||||
enum NodeRole : quint8
|
||||
{
|
||||
TextPanelRole,
|
||||
RoleCount
|
||||
};
|
||||
|
||||
~QskTextInputSkinlet() override;
|
||||
|
||||
QRectF subControlRect( const QskSkinnable*,
|
||||
const QRectF& rect, QskAspect::Subcontrol ) const override;
|
||||
|
||||
QSizeF sizeHint( const QskSkinnable*,
|
||||
Qt::SizeHint, const QSizeF& ) const override;
|
||||
|
||||
protected:
|
||||
QskTextInputSkinlet( QskSkin* = nullptr );
|
||||
|
||||
QSGNode* updateSubNode( const QskSkinnable*,
|
||||
quint8 nodeRole, QSGNode* ) const override;
|
||||
};
|
||||
|
||||
#endif
|
@ -32,7 +32,7 @@ namespace
|
||||
QskAspect::Subcontrol substitutedSubcontrol(
|
||||
QskAspect::Subcontrol subControl ) const override
|
||||
{
|
||||
if ( subControl == QskTextField::Panel )
|
||||
if ( subControl == QskTextField::TextPanel )
|
||||
return m_panelBox->effectiveSubcontrol( QskInputPanelBox::ProxyPanel );
|
||||
|
||||
if ( subControl == QskTextField::Text )
|
||||
@ -184,7 +184,7 @@ QskAspect::Subcontrol QskInputPanelBox::substitutedSubcontrol(
|
||||
#if 1
|
||||
// TODO ...
|
||||
if ( subControl == QskInputPanelBox::ProxyPanel )
|
||||
return QskTextField::Panel;
|
||||
return QskTextField::TextPanel;
|
||||
|
||||
if ( subControl == QskInputPanelBox::ProxyText )
|
||||
return QskTextField::Text;
|
||||
|
Loading…
x
Reference in New Issue
Block a user