input proxy feature added for QskInputPanel
This commit is contained in:
parent
602e3748df
commit
7fe675d74d
@ -138,7 +138,8 @@ public:
|
||||
textInput3->setSizePolicy( Qt::Horizontal, QskSizePolicy::Preferred );
|
||||
|
||||
auto* textInput4 = new QskTextInput( this );
|
||||
textInput4->setEchoMode( QskTextInput::PasswordEchoOnEdit );
|
||||
textInput4->setEchoMode( QskTextInput::Password );
|
||||
textInput4->setPasswordMaskDelay( 1000 );
|
||||
textInput4->setMaxLength( 8 );
|
||||
textInput4->setText( "12345678" );
|
||||
textInput4->setSizePolicy( Qt::Horizontal, QskSizePolicy::Preferred );
|
||||
@ -248,8 +249,10 @@ int main( int argc, char* argv[] )
|
||||
QskObjectCounter counter( true );
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
qputenv( "QT_IM_MODULE", "skinny" );
|
||||
qputenv( "QT_PLUGIN_PATH", STRING( PLUGIN_PATH ) );
|
||||
#endif
|
||||
|
||||
QGuiApplication app( argc, argv );
|
||||
|
||||
|
@ -170,17 +170,14 @@ void qskForceActiveFocus( QQuickItem* item, Qt::FocusReason reason )
|
||||
}
|
||||
}
|
||||
|
||||
void qskUpdateInputMethod( const QQuickItem* item, Qt::InputMethodQueries queries )
|
||||
QQuickItem* qskInputContextItem()
|
||||
{
|
||||
if ( ( item == nullptr ) ||
|
||||
!( item->flags() & QQuickItem::ItemAcceptsInputMethod ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
/*
|
||||
The item that is connected to the input context.
|
||||
- often the item having the active focus.
|
||||
*/
|
||||
|
||||
auto inputMethod = QGuiApplication::inputMethod();
|
||||
|
||||
bool doUpdate = item->hasActiveFocus();
|
||||
QQuickItem* inputItem = nullptr;
|
||||
|
||||
/*
|
||||
We could also get the inputContext from QInputMethodPrivate
|
||||
@ -197,17 +194,32 @@ void qskUpdateInputMethod( const QQuickItem* item, Qt::InputMethodQueries querie
|
||||
without losing the connected input item
|
||||
*/
|
||||
|
||||
QQuickItem* inputItem = nullptr;
|
||||
|
||||
if ( QMetaObject::invokeMethod( inputContext, "inputItem",
|
||||
Qt::DirectConnection, Q_RETURN_ARG( QQuickItem*, inputItem ) ) )
|
||||
{
|
||||
doUpdate = ( item == inputItem );
|
||||
}
|
||||
QMetaObject::invokeMethod( inputContext, "inputItem",
|
||||
Qt::DirectConnection, Q_RETURN_ARG( QQuickItem*, inputItem ) );
|
||||
}
|
||||
|
||||
return inputItem;
|
||||
}
|
||||
|
||||
void qskUpdateInputMethod( const QQuickItem* item, Qt::InputMethodQueries queries )
|
||||
{
|
||||
if ( item == nullptr )
|
||||
return;
|
||||
|
||||
#if 1
|
||||
if ( !( item->flags() & QQuickItem::ItemAcceptsInputMethod ) )
|
||||
return;
|
||||
#endif
|
||||
|
||||
bool doUpdate;
|
||||
|
||||
if ( const QQuickItem* inputItem = qskInputContextItem() )
|
||||
doUpdate = ( item == inputItem );
|
||||
else
|
||||
doUpdate = item->hasActiveFocus();
|
||||
|
||||
if ( doUpdate )
|
||||
inputMethod->update( queries );
|
||||
QGuiApplication::inputMethod()->update( queries );
|
||||
}
|
||||
|
||||
QList< QQuickItem* > qskPaintOrderChildItems( const QQuickItem* item )
|
||||
|
@ -265,6 +265,7 @@ QSK_EXPORT void qskForceActiveFocus( QQuickItem*, Qt::FocusReason );
|
||||
QSK_EXPORT QList< QQuickItem* > qskPaintOrderChildItems( const QQuickItem* );
|
||||
|
||||
QSK_EXPORT void qskUpdateInputMethod( const QQuickItem*, Qt::InputMethodQueries );
|
||||
QSK_EXPORT QQuickItem* qskInputContextItem();
|
||||
|
||||
QSK_EXPORT const QSGNode* qskItemNode( const QQuickItem* );
|
||||
QSK_EXPORT const QSGNode* qskPaintNode( const QQuickItem* );
|
||||
|
@ -62,6 +62,12 @@ static inline void qskBindSignals( const QQuickTextInput* wrappedInput,
|
||||
QObject::connect( wrappedInput, &QQuickTextInput::echoModeChanged,
|
||||
input, [ input ] { input->Q_EMIT 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 );
|
||||
|
||||
@ -381,22 +387,18 @@ void QskTextInput::focusInEvent( QFocusEvent* event )
|
||||
|
||||
void QskTextInput::focusOutEvent( QFocusEvent* event )
|
||||
{
|
||||
#if 1
|
||||
if ( event->reason() != Qt::ActiveWindowFocusReason
|
||||
&& event->reason() != Qt::PopupFocusReason )
|
||||
switch( event->reason() )
|
||||
{
|
||||
m_data->textInput->deselect();
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( m_data->activationModes & ActivationOnFocus )
|
||||
{
|
||||
#if 0
|
||||
if ( !hasFocus() )
|
||||
case Qt::ActiveWindowFocusReason:
|
||||
case Qt::PopupFocusReason:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
m_data->textInput->deselect();
|
||||
setEditing( false );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Inherited::focusOutEvent( event );
|
||||
@ -655,6 +657,37 @@ void QskTextInput::setEchoMode( EchoMode mode )
|
||||
}
|
||||
}
|
||||
|
||||
QString QskTextInput::passwordCharacter() const
|
||||
{
|
||||
return m_data->textInput->passwordCharacter();
|
||||
}
|
||||
|
||||
void QskTextInput::setPasswordCharacter( const QString& text )
|
||||
{
|
||||
m_data->textInput->setPasswordCharacter( text );
|
||||
}
|
||||
|
||||
void QskTextInput::resetPasswordCharacter()
|
||||
{
|
||||
m_data->textInput->setPasswordCharacter(
|
||||
QGuiApplication::styleHints()->passwordMaskCharacter() );
|
||||
}
|
||||
|
||||
int QskTextInput::passwordMaskDelay() const
|
||||
{
|
||||
return m_data->textInput->passwordMaskDelay();
|
||||
}
|
||||
|
||||
void QskTextInput::setPasswordMaskDelay( int ms )
|
||||
{
|
||||
return m_data->textInput->setPasswordMaskDelay( ms );
|
||||
}
|
||||
|
||||
void QskTextInput::resetPasswordMaskDelay()
|
||||
{
|
||||
return m_data->textInput->resetPasswordMaskDelay();
|
||||
}
|
||||
|
||||
QString QskTextInput::displayText() const
|
||||
{
|
||||
return m_data->textInput->displayText();
|
||||
|
@ -31,6 +31,14 @@ class QSK_EXPORT QskTextInput : public QskControl
|
||||
Q_PROPERTY( bool editing READ isEditing
|
||||
WRITE setEditing NOTIFY editingChanged )
|
||||
|
||||
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:
|
||||
@ -103,8 +111,15 @@ public:
|
||||
EchoMode echoMode() const;
|
||||
void setEchoMode( EchoMode );
|
||||
|
||||
QString displayText() const;
|
||||
QString passwordCharacter() const;
|
||||
void setPasswordCharacter( const QString& );
|
||||
void resetPasswordCharacter();
|
||||
|
||||
int passwordMaskDelay() const;
|
||||
void setPasswordMaskDelay( int );
|
||||
void resetPasswordMaskDelay();
|
||||
|
||||
QString displayText() const;
|
||||
QString preeditText() const;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
|
||||
@ -152,7 +167,10 @@ Q_SIGNALS:
|
||||
#endif
|
||||
|
||||
void maximumLengthChanged( int );
|
||||
|
||||
void echoModeChanged( EchoMode );
|
||||
void passwordMaskDelayChanged();
|
||||
void passwordCharacterChanged();
|
||||
|
||||
void validatorChanged();
|
||||
void inputMaskChanged( const QString& );
|
||||
|
@ -278,7 +278,9 @@ void QskInputContext::showInputPanel()
|
||||
|
||||
inputPopup->setOverlay( hasInputProxy );
|
||||
|
||||
if ( !hasInputProxy )
|
||||
if ( hasInputProxy )
|
||||
box->setMargins( QMarginsF( 5, 5, 5, 5 ) );
|
||||
else
|
||||
box->setExtraSpacingAt( Qt::TopEdge | Qt::LeftEdge | Qt::RightEdge );
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,16 @@ static inline QString qskKeyString( int keyCode )
|
||||
return QChar( keyCode );
|
||||
}
|
||||
|
||||
static inline bool qskUsePrediction( Qt::InputMethodHints hints )
|
||||
{
|
||||
constexpr Qt::InputMethodHints mask =
|
||||
Qt::ImhNoPredictiveText | Qt::ImhHiddenText
|
||||
| Qt::ImhDialableCharactersOnly | Qt::ImhEmailCharactersOnly
|
||||
| Qt::ImhUrlCharactersOnly;
|
||||
|
||||
return ( hints & mask ) == 0;
|
||||
}
|
||||
|
||||
class QskInputEngine::PrivateData
|
||||
{
|
||||
public:
|
||||
@ -114,7 +124,7 @@ QskInputEngine::Result QskInputEngine::processKey( int key,
|
||||
auto& preedit = m_data->preedit;
|
||||
|
||||
QskTextPredictor* predictor = nullptr;
|
||||
if ( !( inputHints & Qt::ImhHiddenText ) )
|
||||
if ( qskUsePrediction( inputHints ) )
|
||||
predictor = m_data->predictor;
|
||||
|
||||
/*
|
||||
@ -175,6 +185,7 @@ QskInputEngine::Result QskInputEngine::processKey( int key,
|
||||
{
|
||||
if ( !preedit.isEmpty() && spaceLeft)
|
||||
{
|
||||
preedit += qskKeyString( key );
|
||||
preedit = preedit.left( spaceLeft );
|
||||
|
||||
result.text = preedit;
|
||||
|
@ -74,15 +74,59 @@ static inline void qskSendKey( QQuickItem* receiver, int key )
|
||||
QCoreApplication::sendEvent( receiver, &keyRelease );
|
||||
}
|
||||
|
||||
static inline void qskSyncInputProxy(
|
||||
QQuickItem* inputItem, QskTextInput* inputProxy )
|
||||
{
|
||||
int passwordMaskDelay = -1;
|
||||
QString passwordCharacter;
|
||||
|
||||
if ( auto textInput = qobject_cast< QskTextInput* >( inputItem ) )
|
||||
{
|
||||
passwordMaskDelay = textInput->passwordMaskDelay();
|
||||
passwordCharacter = textInput->passwordCharacter();
|
||||
|
||||
if ( inputProxy->echoMode() == QskTextInput::NoEcho )
|
||||
{
|
||||
/*
|
||||
Qt::ImhHiddenText does not provide information
|
||||
to decide between NoEcho/Password
|
||||
*/
|
||||
auto mode = textInput->echoMode();
|
||||
if ( mode == QskTextInput::Password )
|
||||
inputProxy->setEchoMode( mode );
|
||||
}
|
||||
}
|
||||
|
||||
if ( passwordMaskDelay >= 0 )
|
||||
inputProxy->setPasswordMaskDelay( passwordMaskDelay );
|
||||
else
|
||||
inputProxy->resetPasswordMaskDelay();
|
||||
|
||||
if ( !passwordCharacter.isEmpty() )
|
||||
inputProxy->setPasswordCharacter( passwordCharacter );
|
||||
else
|
||||
inputProxy->resetPasswordCharacter();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class TextInput : public QskTextInput
|
||||
class TextInput final : public QskTextInput
|
||||
{
|
||||
public:
|
||||
TextInput( QQuickItem* parentItem = nullptr ):
|
||||
QskTextInput( parentItem )
|
||||
{
|
||||
setObjectName( "InputPanelInputProxy" );
|
||||
setFocusPolicy( Qt::NoFocus );
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void focusInEvent( QFocusEvent* ) override final
|
||||
{
|
||||
}
|
||||
|
||||
virtual void focusOutEvent( QFocusEvent* ) override final
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -198,7 +242,19 @@ void QskInputPanel::attachInputItem( QQuickItem* item )
|
||||
processInputMethodQueries( queries );
|
||||
|
||||
if ( m_data->hasInputProxy )
|
||||
{
|
||||
m_data->inputProxy->setEditing( true );
|
||||
|
||||
// hiding the cursor in the real input item
|
||||
const QInputMethodEvent::Attribute attribute(
|
||||
QInputMethodEvent::Cursor, 0, 0, QVariant() );
|
||||
|
||||
QInputMethodEvent event( QString(), { attribute } );
|
||||
QCoreApplication::sendEvent( item, &event );
|
||||
|
||||
// not all information is available from the input method query
|
||||
qskSyncInputProxy( item, m_data->inputProxy );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -351,17 +407,17 @@ void QskInputPanel::processInputMethodQueries( Qt::InputMethodQueries queries )
|
||||
QInputMethodQueryEvent event( queries );
|
||||
QCoreApplication::sendEvent( m_data->inputItem, &event );
|
||||
|
||||
if ( queries & Qt::ImHints )
|
||||
if ( event.queries() & Qt::ImHints )
|
||||
{
|
||||
bool hasPrediction = true;
|
||||
bool hasEchoMode = false;
|
||||
QskTextInput::EchoMode echoMode = QskTextInput::Normal;
|
||||
|
||||
const auto hints = static_cast< Qt::InputMethodHints >(
|
||||
event.value( Qt::ImHints ).toInt() );
|
||||
|
||||
if ( hints & Qt::ImhHiddenText )
|
||||
{
|
||||
hasEchoMode = true;
|
||||
echoMode = QskTextInput::NoEcho;
|
||||
}
|
||||
|
||||
if ( hints & Qt::ImhSensitiveData )
|
||||
@ -415,11 +471,13 @@ void QskInputPanel::processInputMethodQueries( Qt::InputMethodQueries queries )
|
||||
if ( hints & Qt::ImhDigitsOnly )
|
||||
{
|
||||
// using a numpad instead of our virtual keyboard
|
||||
hasPrediction = false;
|
||||
}
|
||||
|
||||
if ( hints & Qt::ImhFormattedNumbersOnly )
|
||||
{
|
||||
// a numpad with decimal point and minus sign
|
||||
hasPrediction = false;
|
||||
}
|
||||
|
||||
if ( hints & Qt::ImhUppercaseOnly )
|
||||
@ -435,16 +493,19 @@ void QskInputPanel::processInputMethodQueries( Qt::InputMethodQueries queries )
|
||||
if ( hints & Qt::ImhDialableCharactersOnly )
|
||||
{
|
||||
// characters suitable for phone dialing
|
||||
hasPrediction = false;
|
||||
}
|
||||
|
||||
if ( hints & Qt::ImhEmailCharactersOnly )
|
||||
{
|
||||
// characters suitable for email addresses
|
||||
hasPrediction = false;
|
||||
}
|
||||
|
||||
if ( hints & Qt::ImhUrlCharactersOnly )
|
||||
{
|
||||
// characters suitable for URLs
|
||||
hasPrediction = false;
|
||||
}
|
||||
|
||||
if ( hints & Qt::ImhLatinOnly )
|
||||
@ -457,18 +518,17 @@ void QskInputPanel::processInputMethodQueries( Qt::InputMethodQueries queries )
|
||||
m_data->predictionBar->setVisible(
|
||||
hasPrediction && m_data->engine && m_data->engine->predictor() );
|
||||
|
||||
m_data->inputProxy->setEchoMode(
|
||||
hasEchoMode ? QskTextInput::PasswordEchoOnEdit : QskTextInput::Normal );
|
||||
m_data->inputProxy->setEchoMode( echoMode );
|
||||
|
||||
m_data->inputHints = hints;
|
||||
}
|
||||
|
||||
if ( queries & Qt::ImPreferredLanguage )
|
||||
if ( event.queries() & Qt::ImPreferredLanguage )
|
||||
{
|
||||
// already handled by the input context
|
||||
}
|
||||
|
||||
if ( queries & Qt::ImMaximumTextLength )
|
||||
if ( event.queries() & Qt::ImMaximumTextLength )
|
||||
{
|
||||
// needs to be handled before Qt::ImCursorPosition !
|
||||
|
||||
@ -483,7 +543,7 @@ void QskInputPanel::processInputMethodQueries( Qt::InputMethodQueries queries )
|
||||
}
|
||||
|
||||
|
||||
if ( queries & Qt::ImSurroundingText )
|
||||
if ( event.queries() & Qt::ImSurroundingText )
|
||||
{
|
||||
if ( m_data->hasInputProxy )
|
||||
{
|
||||
@ -492,7 +552,7 @@ void QskInputPanel::processInputMethodQueries( Qt::InputMethodQueries queries )
|
||||
}
|
||||
}
|
||||
|
||||
if ( queries & Qt::ImCursorPosition )
|
||||
if ( event.queries() & Qt::ImCursorPosition )
|
||||
{
|
||||
if ( m_data->hasInputProxy )
|
||||
{
|
||||
@ -501,7 +561,7 @@ void QskInputPanel::processInputMethodQueries( Qt::InputMethodQueries queries )
|
||||
}
|
||||
}
|
||||
|
||||
if ( queries & Qt::ImCurrentSelection )
|
||||
if ( event.queries() & Qt::ImCurrentSelection )
|
||||
{
|
||||
#if 0
|
||||
const auto text = event.value( Qt::ImCurrentSelection ).toString();
|
||||
|
Loading…
x
Reference in New Issue
Block a user