6#include "QskInputPanel.h"
7#include "QskInputContext.h"
8#include "QskTextPredictor.h"
11#include <qtextformat.h>
24static void qskRegisterInputPanel()
26 qRegisterMetaType< Result >(
"Result" );
29Q_CONSTRUCTOR_FUNCTION( qskRegisterInputPanel )
31static inline QQuickItem* qskReceiverItem(
const QskInputPanel* panel )
33 if (
auto item = panel->inputProxy() )
36 return panel->inputItem();
39static inline bool qskUsePrediction( Qt::InputMethodHints hints )
41 constexpr Qt::InputMethodHints mask =
42 Qt::ImhNoPredictiveText | Qt::ImhExclusiveInputMask | Qt::ImhHiddenText;
44 return ( hints & mask ) == 0;
47static inline void qskSendText(
48 QQuickItem* receiver,
const QString& text,
bool isFinal )
50 if ( receiver ==
nullptr )
55 QInputMethodEvent event;
63 event.setCommitString( text, 0, 1 );
65 event.setCommitString( text );
67 QCoreApplication::sendEvent( receiver, &event );
71 QTextCharFormat format;
72 format.setFontUnderline(
true );
74 const QInputMethodEvent::Attribute attribute(
75 QInputMethodEvent::TextFormat, 0, text.length(), format );
77 QInputMethodEvent event( text, { attribute } );
79 QCoreApplication::sendEvent( receiver, &event );
83static inline void qskSendReplaceText( QQuickItem* receiver,
const QString& text )
85 if ( receiver ==
nullptr )
88 QInputMethodEvent::Attribute attribute(
89 QInputMethodEvent::Selection, 0, 32767, QVariant() );
91 QInputMethodEvent event( QString(), { attribute } );
92 QCoreApplication::sendEvent( receiver, &event );
94 qskSendText( receiver, text,
true );
97static inline void qskSendKey( QQuickItem* receiver,
int key )
99 if ( receiver ==
nullptr )
102 QKeyEvent keyPress( QEvent::KeyPress, key, Qt::NoModifier );
103 QCoreApplication::sendEvent( receiver, &keyPress );
105 QKeyEvent keyRelease( QEvent::KeyRelease, key, Qt::NoModifier );
106 QCoreApplication::sendEvent( receiver, &keyRelease );
111 class KeyProcessor :
public QObject
116 QString preedit()
const
122 int key, Qt::InputMethodHints inputHints,
QskInputPanel* panel,
126 m_currentResult.isFinal =
true;
127 m_currentResult.text.clear();
128 m_currentResult.key = 0;
130 m_predictor = predictor;
131 m_spaceLeft = spaceLeft;
137 case Qt::Key_Backspace:
138 case Qt::Key_Muhenkan:
140 if ( predictor && !m_preedit.isEmpty() )
144 m_currentResult.text = m_preedit;
145 m_currentResult.isFinal =
false;
147 Q_EMIT panel->predictionRequested( m_preedit );
150 Q_EMIT keyProcessingFinished( m_currentResult );
154 m_currentResult.key = Qt::Key_Backspace;
155 Q_EMIT keyProcessingFinished( m_currentResult );
164 if ( !m_preedit.isEmpty() )
168 m_currentResult.text = m_preedit.left( spaceLeft );
169 m_currentResult.isFinal =
true;
173 Q_EMIT keyProcessingFinished( m_currentResult );
178 if ( !( inputHints & Qt::ImhMultiLine ) )
180 m_currentResult.key = Qt::Key_Return;
181 Q_EMIT keyProcessingFinished( m_currentResult );
191 if ( !m_preedit.isEmpty() && spaceLeft )
193 m_preedit += keyString( key );
194 m_preedit = m_preedit.left( spaceLeft );
196 m_currentResult.text = m_preedit;
197 m_currentResult.isFinal =
true;
201 Q_EMIT keyProcessingFinished( m_currentResult );
213 m_currentResult.key = key;
214 Q_EMIT keyProcessingFinished( m_currentResult );
219 const QString text = keyString( key );
224 Q_EMIT panel->predictionRequested( m_preedit );
228 m_currentResult.text = text;
229 m_currentResult.isFinal =
true;
230 Q_EMIT keyProcessingFinished( m_currentResult );
239 void continueProcessingKey(
const QStringList& candidates )
243 if ( candidates.count() > 0 )
245 m_currentResult.text = m_preedit;
246 m_currentResult.isFinal =
false;
250 m_currentResult.text = m_preedit.left( m_spaceLeft );
251 m_currentResult.isFinal =
true;
257 Q_EMIT keyProcessingFinished( m_currentResult );
261 void keyProcessingFinished(
const Result& );
264 inline QString keyString(
int keyCode )
const
270 case Qt::Key_CapsLock:
271 case Qt::Key_Mode_switch:
272 case Qt::Key_Backspace:
273 case Qt::Key_Muhenkan:
278 return QChar( QChar::CarriageReturn );
281 return QChar( QChar::Space );
287 return QChar( keyCode );
291 int m_spaceLeft = -1;
293 Result m_currentResult;
297class QskInputPanel::PrivateData
305 KeyProcessor keyProcessor;
306 QPointer< QQuickItem > inputItem;
308 QLocale predictorLocale;
309 std::shared_ptr< QskTextPredictor > predictor;
310 QStringList candidates;
312 Qt::InputMethodHints inputHints;
313 bool hasPredictorLocale =
false;
316 void handleKeyProcessingFinished(
const Result& result )
318 switch ( result.key )
322 qskSendText( qskReceiverItem( panel ),
323 result.text, result.isFinal );
328 if (
auto proxy = panel->inputProxy() )
331 const auto value = proxy->property(
"text" );
332 if ( value.canConvert< QString >() )
334 qskSendReplaceText( inputItem, value.toString() );
338 qskSendKey( inputItem, result.key );
344 qskSendKey( inputItem, result.key );
349 qskSendKey( qskReceiverItem( panel ), result.key );
355QskInputPanel::QskInputPanel( QQuickItem* parent )
356 : Inherited( parent )
357 , m_data( new PrivateData( this ) )
359 setAutoLayoutChildren(
true );
360 initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Constrained );
362 connect(
this, &QskInputPanel::keySelected,
363 this, &QskInputPanel::commitKey );
365 connect(
this, &QskInputPanel::predictiveTextSelected,
366 this, &QskInputPanel::commitPredictiveText );
369 this, &QskInputPanel::updateLocale );
371 connect( &m_data->keyProcessor, &KeyProcessor::keyProcessingFinished,
372 this, [
this](
const Result& result ) { m_data->handleKeyProcessingFinished( result ); } );
374 updateLocale( locale() );
377QskInputPanel::~QskInputPanel()
381void QskInputPanel::attachInputItem( QQuickItem* item )
383 if ( item == m_data->inputItem )
386 if ( m_data->inputItem )
388 disconnect( m_data->inputItem, &QObject::destroyed,
389 this, &QskInputPanel::inputItemDestroyed );
392 m_data->inputItem = item;
396 if ( m_data->predictor )
397 Q_EMIT predictionReset();
399 m_data->keyProcessor.reset();
400 m_data->inputHints = Qt::InputMethodHints();
404 Qt::InputMethodQueries queries = Qt::ImQueryAll;
405 queries &= ~Qt::ImEnabled;
407 updateInputPanel( queries );
416 const QInputMethodEvent::Attribute attribute(
417 QInputMethodEvent::Cursor, 0, 0, QVariant() );
419 QCoreApplication::postEvent( item,
420 new QInputMethodEvent( QString(), { attribute } ) );
423 connect( item, &QObject::destroyed,
424 this, &QskInputPanel::inputItemDestroyed,
425 Qt::UniqueConnection );
429 attachItem(
nullptr );
433void QskInputPanel::updateInputPanel( Qt::InputMethodQueries queries )
435 if ( m_data->inputItem ==
nullptr )
438 QInputMethodQueryEvent event( queries );
439 QCoreApplication::sendEvent( m_data->inputItem, &event );
441 if ( queries & Qt::ImHints )
443 m_data->inputHints =
static_cast< Qt::InputMethodHints
>(
444 event.value( Qt::ImHints ).toInt() );
446 setPredictionEnabled(
447 m_data->predictor && qskUsePrediction( m_data->inputHints ) );
450 if ( queries & Qt::ImPreferredLanguage )
452 setLocale( event.value( Qt::ImPreferredLanguage ).toLocale() );
455 if ( queries & Qt::ImInputItemClipRectangle )
464void QskInputPanel::updateLocale(
const QLocale& locale )
466 if ( !m_data->hasPredictorLocale ||
locale != m_data->predictorLocale )
468 m_data->hasPredictorLocale =
true;
469 m_data->predictorLocale =
locale;
472 m_data->keyProcessor.reset();
476void QskInputPanel::resetPredictor(
const QLocale& locale )
478 auto predictor = QskInputContext::instance()->textPredictor(
locale );
480 if ( predictor == m_data->predictor )
483 m_data->predictor = predictor;
488 connect(
this, &QskInputPanel::predictionReset,
489 predictor.get(), &QskTextPredictor::reset );
490 connect(
this, &QskInputPanel::predictionRequested,
491 predictor.get(), &QskTextPredictor::request );
493 connect( predictor.get(), &QskTextPredictor::predictionChanged,
494 this, &QskInputPanel::updatePrediction );
497 setPredictionEnabled(
498 predictor && qskUsePrediction( m_data->inputHints ) );
501void QskInputPanel::commitPredictiveText(
int index )
505 if ( m_data->predictor )
507 text = m_data->candidates.at( index );
508 Q_EMIT predictionReset();
511 m_data->keyProcessor.reset();
515 qskSendText( qskReceiverItem(
this ), text,
true );
518void QskInputPanel::updatePrediction(
const QString& text,
const QStringList& candidates )
520 if ( m_data->predictor )
522 if( m_data->keyProcessor.preedit() != text )
528 setPrediction( candidates );
529 m_data->keyProcessor.continueProcessingKey( candidates );
533 qWarning() <<
"got prediction update, but no predictor. Something is wrong";
537QQuickItem* QskInputPanel::inputProxy()
const
542QQuickItem* QskInputPanel::inputItem()
const
544 return m_data->inputItem;
547void QskInputPanel::setPrompt(
const QString& )
551void QskInputPanel::setPredictionEnabled(
bool )
555void QskInputPanel::setPrediction(
const QStringList& candidates )
557 m_data->candidates = candidates;
560Qt::Alignment QskInputPanel::alignment()
const
567 return inputProxy() ? Qt::AlignVCenter : Qt::AlignBottom;
570QStringList QskInputPanel::candidates()
const
572 return m_data->candidates;
575void QskInputPanel::commitKey(
int key )
577 if ( m_data->inputItem ==
nullptr )
582 if ( !( m_data->inputHints & Qt::ImhMultiLine ) )
584 QInputMethodQueryEvent event1( Qt::ImMaximumTextLength );
585 QCoreApplication::sendEvent( m_data->inputItem, &event1 );
587 const int maxChars = event1.value( Qt::ImMaximumTextLength ).toInt();
590 QInputMethodQueryEvent event2( Qt::ImSurroundingText );
591 QCoreApplication::sendEvent( qskReceiverItem(
this ), &event2 );
593 const auto text = event2.value( Qt::ImSurroundingText ).toString();
594 spaceLeft = maxChars - text.length();
599 if ( qskUsePrediction( m_data->inputHints ) )
600 predictor = m_data->predictor.get();
602 m_data->keyProcessor.processKey(
603 key, m_data->inputHints,
this, predictor, spaceLeft );
606#include "moc_QskInputPanel.cpp"
607#include "QskInputPanel.moc"
void setLocale(const QLocale &)
void localeChanged(const QLocale &)